public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (Equals(member, _isClosed)) { SqlExpression sqlExpression = _sqlExpressionFactory.NullableFunction( "ST_IsClosed", new [] { instance }, returnType, false); // ST_IsRing and others returns TRUE for a NULL value in MariaDB, which is inconsistent with NTS' implementation. // We return the following instead: // CASE // WHEN instance IS NULL THEN NULL // ELSE expression // END if (returnType == typeof(bool)) { sqlExpression = _sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNull(instance), _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping)) }, sqlExpression); } return(sqlExpression); } return(null); }
public virtual SqlExpression Translate( SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (_indexOfMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options) .MakeIndexOfExpression(instance, arguments[0])); } if (_replaceMethodInfo.Equals(method)) { var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, arguments[0], arguments[1]); var replacementArgument = _sqlExpressionFactory.ApplyTypeMapping(arguments[1], stringTypeMapping); var replaceCall = _sqlExpressionFactory.NullableFunction( "REPLACE", new[] { _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping), _sqlExpressionFactory.ApplyTypeMapping(arguments[0], stringTypeMapping), _sqlExpressionFactory.ApplyTypeMapping(arguments[1], stringTypeMapping) }, method.ReturnType, stringTypeMapping); // Due to a bug in all versions of MariaDB and all MySQL versions below 8.0.x (exact version that fixed the issue is // currently unclear), using `null` as the replacement argument in a REPLACE() call leads to unexpected results, in which // the call returns the original string, instead of `null`. // See https://jira.mariadb.org/browse/MDEV-24263 return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(replacementArgument), replaceCall) }, _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping))); } if (_toLowerMethodInfo.Equals(method) || _toUpperMethodInfo.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( _toLowerMethodInfo.Equals(method) ? "LOWER" : "UPPER", new[] { instance }, method.ReturnType, instance.TypeMapping)); } if (_substringMethodInfoWithOneArg.Equals(method)) { return(_sqlExpressionFactory.Function( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Constant(1)), _sqlExpressionFactory.NullableFunction( "CHAR_LENGTH", new[] { instance }, typeof(int)) }, nullable: true, argumentsPropagateNullability: new[] { true, true, true }, method.ReturnType, instance.TypeMapping)); } if (_substringMethodInfoWithTwoArgs.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Constant(1)), arguments[1] }, method.ReturnType, instance.TypeMapping)); } if (_isNullOrWhiteSpaceMethodInfo.Equals(method)) { return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.IsNull(arguments[0]), _sqlExpressionFactory.Equal( ProcessTrimMethod(arguments[0], null, null), _sqlExpressionFactory.Constant(string.Empty)))); } if (_trimStartMethodInfoWithoutArgs?.Equals(method) == true || _trimStartMethodInfoWithCharArg?.Equals(method) == true || _trimStartMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, "LEADING")); } if (_trimEndMethodInfoWithoutArgs?.Equals(method) == true || _trimEndMethodInfoWithCharArg?.Equals(method) == true || _trimEndMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, "TRAILING")); } if (_trimMethodInfoWithoutArgs?.Equals(method) == true || _trimMethodInfoWithCharArg?.Equals(method) == true || _trimMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, null)); } if (_containsMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options) .MakeContainsExpression(instance, arguments[0])); } if (_startsWithMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options) .MakeStartsWithExpression(instance, arguments[0])); } if (_endsWithMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options) .MakeEndsWithExpression(instance, arguments[0])); } if (_padLeftWithOneArg.Equals(method)) { return(TranslatePadLeftRight( true, instance, arguments[0], _sqlExpressionFactory.Constant(" "), method.ReturnType)); } if (_padRightWithOneArg.Equals(method)) { return(TranslatePadLeftRight( false, instance, arguments[0], _sqlExpressionFactory.Constant(" "), method.ReturnType)); } if (_padLeftWithTwoArgs.Equals(method)) { return(TranslatePadLeftRight( true, instance, arguments[0], arguments[1], method.ReturnType)); } if (_padRightWithTwoArgs.Equals(method)) { return(TranslatePadLeftRight( false, instance, arguments[0], arguments[1], method.ReturnType)); } if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { arguments[0], _sqlExpressionFactory.Constant(1), _sqlExpressionFactory.Constant(1) }, method.ReturnType)); } if (_lastOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { arguments[0], _sqlExpressionFactory.NullableFunction( "CHAR_LENGTH", new[] { arguments[0] }, typeof(int)), _sqlExpressionFactory.Constant(1) }, method.ReturnType)); } if (_removeMethodInfoWithOneArg.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Constant(1), arguments[0], }, method.ReturnType, instance.TypeMapping)); } if (_removeMethodInfoWithTwoArgs.Equals(method)) { var firstSubString = _sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Constant(1), arguments[0] }, method.ReturnType, instance.TypeMapping); var secondSubString = _sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Add( _sqlExpressionFactory.Add( arguments[0], arguments[1]), _sqlExpressionFactory.Constant(1)), _sqlExpressionFactory.Subtract( _sqlExpressionFactory.NullableFunction( "CHAR_LENGTH", new[] { instance }, typeof(int)), _sqlExpressionFactory.Add( arguments[0], arguments[1])), }, method.ReturnType, instance.TypeMapping); var concat = _sqlExpressionFactory.NullableFunction( "CONCAT", new[] { firstSubString, secondSubString }, method.ReturnType, instance.TypeMapping); return(concat); } if (_concatMethodInfos.Contains( (method.IsGenericMethod ? method.GetGenericMethodDefinition() : null) ?? method)) { // Handle // string[] // IEnumerable<string> // object[] // IEnumerable<T> // and // string, ... // object, ... // // Some call signature variants can never reach this code, because they will be directly called and thus only their result // is translated. var concatArguments = arguments[0] is MySqlComplexFunctionArgumentExpression mySqlComplexFunctionArgumentExpression ? new SqlExpression[] { mySqlComplexFunctionArgumentExpression } : arguments.Select( e => e switch { SqlConstantExpression c => _sqlExpressionFactory.Constant(c.Value.ToString()), SqlParameterExpression p => p.ApplyTypeMapping( ((MySqlStringTypeMapping)_typeMappingSource.GetMapping(typeof(string))).Clone(forceToString: true)), _ => e, })
public virtual SqlExpression Translate( SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (_indexOfMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory) .MakeIndexOfExpression(instance, arguments[0])); } if (_replaceMethodInfo.Equals(method)) { var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, arguments[0], arguments[1]); var replacementArgument = _sqlExpressionFactory.ApplyTypeMapping(arguments[1], stringTypeMapping); var replaceCall = _sqlExpressionFactory.NullableFunction( "REPLACE", new[] { _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping), _sqlExpressionFactory.ApplyTypeMapping(arguments[0], stringTypeMapping), _sqlExpressionFactory.ApplyTypeMapping(arguments[1], stringTypeMapping) }, method.ReturnType, stringTypeMapping); // Due to a bug in all versions of MariaDB and all MySQL versions below 8.0.x (exact version that fixed the issue is // currently unclear), using `null` as the replacement argument in a REPLACE() call leads to unexpected results, in which // the call returns the original string, instead of `null`. // See https://jira.mariadb.org/browse/MDEV-24263 return(_sqlExpressionFactory.Case( new[] { new CaseWhenClause( _sqlExpressionFactory.IsNotNull(replacementArgument), replaceCall) }, _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping))); } if (_toLowerMethodInfo.Equals(method) || _toUpperMethodInfo.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( _toLowerMethodInfo.Equals(method) ? "LOWER" : "UPPER", new[] { instance }, method.ReturnType, instance.TypeMapping)); } if (_substringMethodInfo.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Add( arguments[0], _sqlExpressionFactory.Constant(1)), arguments[1] }, method.ReturnType, instance.TypeMapping)); } if (_isNullOrWhiteSpaceMethodInfo.Equals(method)) { return(_sqlExpressionFactory.OrElse( _sqlExpressionFactory.IsNull(arguments[0]), _sqlExpressionFactory.Equal( ProcessTrimMethod(arguments[0], null, null), _sqlExpressionFactory.Constant(string.Empty)))); } if (_trimStartMethodInfoWithoutArgs?.Equals(method) == true || _trimStartMethodInfoWithCharArg?.Equals(method) == true || _trimStartMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, "LEADING")); } if (_trimEndMethodInfoWithoutArgs?.Equals(method) == true || _trimEndMethodInfoWithCharArg?.Equals(method) == true || _trimEndMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, "TRAILING")); } if (_trimMethodInfoWithoutArgs?.Equals(method) == true || _trimMethodInfoWithCharArg?.Equals(method) == true || _trimMethodInfoWithCharArrayArg.Equals(method)) { return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, null)); } if (_containsMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory) .MakeContainsExpression(instance, arguments[0])); } if (_startsWithMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory) .MakeStartsWithExpression(instance, arguments[0])); } if (_endsWithMethodInfo.Equals(method)) { return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory) .MakeEndsWithExpression(instance, arguments[0])); } if (_padLeftWithOneArg.Equals(method)) { return(TranslatePadLeftRight( true, instance, arguments[0], _sqlExpressionFactory.Constant(" "), method.ReturnType)); } if (_padRightWithOneArg.Equals(method)) { return(TranslatePadLeftRight( false, instance, arguments[0], _sqlExpressionFactory.Constant(" "), method.ReturnType)); } if (_padLeftWithTwoArgs.Equals(method)) { return(TranslatePadLeftRight( true, instance, arguments[0], arguments[1], method.ReturnType)); } if (_padRightWithTwoArgs.Equals(method)) { return(TranslatePadLeftRight( false, instance, arguments[0], arguments[1], method.ReturnType)); } if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { arguments[0], _sqlExpressionFactory.Constant(1), _sqlExpressionFactory.Constant(1) }, method.ReturnType)); } if (_lastOrDefaultMethodInfoWithoutArgs.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { arguments[0], _sqlExpressionFactory.NullableFunction( "CHAR_LENGTH", new[] { arguments[0] }, typeof(int)), _sqlExpressionFactory.Constant(1) }, method.ReturnType)); } if (_removeMethodInfoWithOneArg.Equals(method)) { return(_sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Constant(1), arguments[0], }, method.ReturnType, instance.TypeMapping)); } if (_removeMethodInfoWithTwoArgs.Equals(method)) { var firstSubString = _sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Constant(1), arguments[0] }, method.ReturnType, instance.TypeMapping); var secondSubString = _sqlExpressionFactory.NullableFunction( "SUBSTRING", new[] { instance, _sqlExpressionFactory.Add( _sqlExpressionFactory.Add( arguments[0], arguments[1]), _sqlExpressionFactory.Constant(1)), _sqlExpressionFactory.Subtract( _sqlExpressionFactory.NullableFunction( "CHAR_LENGTH", new[] { instance }, typeof(int)), _sqlExpressionFactory.Add( arguments[0], arguments[1])), }, method.ReturnType, instance.TypeMapping); var concat = _sqlExpressionFactory.NullableFunction( "CONCAT", new[] { firstSubString, secondSubString }, method.ReturnType, instance.TypeMapping); return(concat); } return(null); }