in src/Microsoft.Health.Fhir.SqlServer/Features/Search/Expressions/Visitors/QueryGenerators/SearchParameterQueryGenerator.cs [177:252]
protected static SearchParameterQueryGeneratorContext VisitSimpleString(StringExpression expression, SearchParameterQueryGeneratorContext context, StringColumn column, string value)
{
if (expression.StringOperator != StringOperator.LeftSideStartsWith)
{
AppendColumnName(context, column, expression);
}
bool needsEscaping = false;
switch (expression.StringOperator)
{
case StringOperator.Contains:
needsEscaping = TryEscapeValueForLike(ref value);
SqlParameter containsParameter = context.Parameters.AddParameter(column, $"%{value}%", true);
context.StringBuilder.Append(" LIKE ").Append(containsParameter.ParameterName);
break;
case StringOperator.EndsWith:
needsEscaping = TryEscapeValueForLike(ref value);
SqlParameter endWithParameter = context.Parameters.AddParameter(column, $"%{value}", true);
context.StringBuilder.Append(" LIKE ").Append(endWithParameter.ParameterName);
break;
case StringOperator.Equals:
SqlParameter equalsParameter = context.Parameters.AddParameter(column, value, true);
context.StringBuilder.Append(" = ").Append(equalsParameter.ParameterName);
break;
case StringOperator.NotContains:
context.StringBuilder.Append(" NOT ");
goto case StringOperator.Contains;
case StringOperator.NotEndsWith:
context.StringBuilder.Append(" NOT ");
goto case StringOperator.EndsWith;
case StringOperator.NotStartsWith:
context.StringBuilder.Append(" NOT ");
goto case StringOperator.StartsWith;
case StringOperator.StartsWith:
needsEscaping = TryEscapeValueForLike(ref value);
SqlParameter startsWithParameter = context.Parameters.AddParameter(column, $"{value}%", true);
context.StringBuilder.Append(" LIKE ").Append(startsWithParameter.ParameterName);
break;
case StringOperator.LeftSideStartsWith:
needsEscaping = TryEscapeValueForLike(ref value);
SqlParameter leftParameter = context.Parameters.AddParameter(column, $"{value}", true);
context.StringBuilder.Append(leftParameter.ParameterName).Append(" LIKE ");
AppendColumnName(context, column, expression);
context.StringBuilder.Append("+'%'");
break;
default:
throw new ArgumentOutOfRangeException(expression.StringOperator.ToString());
}
if (needsEscaping)
{
context.StringBuilder.Append(" ESCAPE '!'");
}
if (column.IsAcentSensitive == null || column.IsCaseSensitive == null ||
column.IsAcentSensitive == expression.IgnoreCase ||
column.IsCaseSensitive == expression.IgnoreCase)
{
if (!expression.IgnoreCase && expression.StringOperator == StringOperator.Equals && column.IsAcentSensitive != null && column.IsCaseSensitive != null)
{
// We are doing a case/accent sensitive query over a column that is case/accent insensitive.
// We can improve efficiency of the query by including an accent/case insensitive predicate
// in addition to the sensitive one. This allows the optimizer choose an index seek.
context.StringBuilder.Append(" AND ");
AppendColumnName(context, column, expression);
SqlParameter equalsParameter = context.Parameters.AddParameter(column, value, true);
context.StringBuilder.Append(" = ").Append(equalsParameter.ParameterName);
}
context.StringBuilder.Append(" COLLATE ").Append(expression.IgnoreCase ? DefaultCaseInsensitiveCollation : DefaultCaseSensitiveCollation);
}
return context;
}