Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Config/DatabasePrimitives/DatabaseObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ public bool IsAnyColumnNullable(List<string> columnsToCheck)

return null;
}

public virtual int? GetLengthForParam(string paramName)
{
if (Columns.TryGetValue(paramName, out ColumnDefinition? columnDefinition))
{
return columnDefinition.Length;
}

return null;
}
}

/// <summary>
Expand Down Expand Up @@ -270,6 +280,7 @@ public class ColumnDefinition
public bool IsNullable { get; set; }
public bool IsReadOnly { get; set; }
public object? DefaultValue { get; set; }
public int? Length { get; set; }

public ColumnDefinition() { }

Expand Down
6 changes: 5 additions & 1 deletion src/Core/Models/DbConnectionParam.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ namespace Azure.DataApiBuilder.Core.Models;
/// </summary>
public class DbConnectionParam
{
public DbConnectionParam(object? value, DbType? dbType = null, SqlDbType? sqlDbType = null)
public DbConnectionParam(object? value, DbType? dbType = null, SqlDbType? sqlDbType = null, int? length = null)
{
Value = value;
DbType = dbType;
SqlDbType = sqlDbType;
Length = length;
}

/// <summary>
Expand All @@ -31,4 +32,7 @@ public DbConnectionParam(object? value, DbType? dbType = null, SqlDbType? sqlDbT
// This is being made nullable
// because it's not populated for DB's other than MSSQL.
public SqlDbType? SqlDbType { get; set; }

// Nullable integer parameter representing length. nullable for back compatibility and for where its not needed
public int? Length { get; set; }
}
23 changes: 16 additions & 7 deletions src/Core/Models/GraphQLFilterParsers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ private static Predicate ParseScalarType(
string schemaName,
string tableName,
string tableAlias,
Func<object, string?, string> processLiterals,
Func<object, string?, bool, string> processLiterals,
bool isListType = false)
{
Column column = new(schemaName, tableName, columnName: fieldName, tableAlias);
Expand Down Expand Up @@ -611,7 +611,7 @@ public static Predicate Parse(
IInputValueDefinition argumentSchema,
Column column,
List<ObjectFieldNode> fields,
Func<object, string?, string> processLiterals,
Func<object, string?, bool, string> processLiterals,
bool isListType = false)
{
List<PredicateOperand> predicates = new();
Expand All @@ -626,6 +626,7 @@ public static Predicate Parse(
variables: ctx.Variables);

bool processLiteral = true;
bool lengthOverride = false;

if (value is null)
{
Expand Down Expand Up @@ -671,6 +672,7 @@ public static Predicate Parse(
{
op = PredicateOperation.LIKE;
value = $"%{EscapeLikeString((string)value)}%";
lengthOverride = true;
}

break;
Expand All @@ -683,16 +685,19 @@ public static Predicate Parse(
{
op = PredicateOperation.NOT_LIKE;
value = $"%{EscapeLikeString((string)value)}%";
lengthOverride = true;
}

break;
case "startsWith":
op = PredicateOperation.LIKE;
value = $"{EscapeLikeString((string)value)}%";
lengthOverride = true;
break;
case "endsWith":
op = PredicateOperation.LIKE;
value = $"%{EscapeLikeString((string)value)}";
lengthOverride = true;
break;
case "isNull":
processLiteral = false;
Expand All @@ -707,7 +712,7 @@ public static Predicate Parse(
predicates.Push(new PredicateOperand(new Predicate(
new PredicateOperand(column),
op,
GenerateRightOperand(ctx, argumentObject, name, processLiterals, value, processLiteral) // right operand
GenerateRightOperand(ctx, argumentObject, name, column, processLiterals, value, processLiteral, lengthOverride)
)));
}

Expand Down Expand Up @@ -758,17 +763,21 @@ public static Predicate Parse(
/// <param name="ctx">The GraphQL middleware context, used to resolve variable values.</param>
/// <param name="argumentObject">The input object type describing the argument schema.</param>
/// <param name="operationName">The name of the filter operation (e.g., "eq", "in").</param>
/// <param name="column">The target column, used to derive parameter type/size metadata.</param>
/// <param name="processLiterals">A function to encode or parameterize literal values for database queries.</param>
/// <param name="value">The value to be used as the right operand in the predicate.</param>
/// <param name="processLiteral">Indicates whether to process the value as a literal using processLiterals, or use its string representation directly.</param>
/// <param name="lengthOverride">When true, indicates the parameter length should not be constrained to the column length (used for LIKE operations).</param>
/// <returns>A <see cref="PredicateOperand"/> representing the right operand for the predicate.</returns>
private static PredicateOperand GenerateRightOperand(
IMiddlewareContext ctx,
InputObjectType argumentObject,
string operationName,
Func<object, string?, string> processLiterals,
Column column,
Func<object, string?, bool, string> processLiterals,
object value,
bool processLiteral)
bool processLiteral,
bool lengthOverride)
{
if (operationName.Equals("in", StringComparison.OrdinalIgnoreCase))
{
Expand All @@ -778,13 +787,13 @@ private static PredicateOperand GenerateRightOperand(
argumentObject.Fields[operationName],
ctx.Variables))
.Where(inValue => inValue is not null)
.Select(inValue => processLiterals(inValue!, null))
.Select(inValue => processLiterals(inValue!, column.ColumnName, false))
.ToList();

return new PredicateOperand("(" + string.Join(", ", encodedParams) + ")");
}

return new PredicateOperand(processLiteral ? processLiterals(value, null) : value.ToString());
return new PredicateOperand(processLiteral ? $"{processLiterals(value, column.ColumnName, lengthOverride)}" : value.ToString());
}

private static string EscapeLikeString(string input)
Expand Down
5 changes: 3 additions & 2 deletions src/Core/Resolvers/BaseQueryStructure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,16 @@ public BaseQueryStructure(
/// </summary>
/// <param name="value">Value to be assigned to parameter, which can be null for nullable columns.</param>
/// <param name="paramName"> The name of the parameter - backing column name for table/views or parameter name for stored procedures.</param>
public virtual string MakeDbConnectionParam(object? value, string? paramName = null)
public virtual string MakeDbConnectionParam(object? value, string? paramName = null, bool lengthOverride = false)
{
string encodedParamName = GetEncodedParamName(Counter.Next());
if (!string.IsNullOrEmpty(paramName))
{
Parameters.Add(encodedParamName,
new(value,
dbType: GetUnderlyingSourceDefinition().GetDbTypeForParam(paramName),
sqlDbType: GetUnderlyingSourceDefinition().GetSqlDbTypeForParam(paramName)));
sqlDbType: GetUnderlyingSourceDefinition().GetSqlDbTypeForParam(paramName),
length: lengthOverride ? -1 : GetUnderlyingSourceDefinition().GetLengthForParam(paramName)));
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Resolvers/CosmosQueryStructure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public CosmosQueryStructure(
}

/// <inheritdoc/>
public override string MakeDbConnectionParam(object? value, string? columnName = null)
public override string MakeDbConnectionParam(object? value, string? columnName = null, bool lengthOverride = false)
{
string encodedParamName = $"{PARAM_NAME_PREFIX}param{Counter.Next()}";
Parameters.Add(encodedParamName, new(value));
Expand Down
11 changes: 10 additions & 1 deletion src/Core/Resolvers/MsSqlQueryExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,17 @@ public override SqlCommand PrepareDbCommand(
{
SqlParameter parameter = cmd.CreateParameter();
parameter.ParameterName = parameterEntry.Key;
parameter.Value = parameterEntry.Value.Value ?? DBNull.Value;
parameter.Value = parameterEntry.Value?.Value ?? DBNull.Value;

PopulateDbTypeForParameter(parameterEntry, parameter);

//if sqldbtype is varchar, nvarchar then set the length when explicitly provided
if (parameter.SqlDbType is SqlDbType.VarChar or SqlDbType.NVarChar or SqlDbType.Char or SqlDbType.NChar
&& parameterEntry.Value?.Length is not null)
{
parameter.Size = parameterEntry.Value.Length.Value;
}

cmd.Parameters.Add(parameter);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ private static void VerifyColumnDefinitionSerializationDeserialization(ColumnDef
{
// test number of properties/fields defined in Column Definition
int fields = typeof(ColumnDefinition).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Length;
Assert.AreEqual(fields, 8);
Assert.AreEqual(fields, 9);

// test values
expectedColumnDefinition.Equals(deserializedColumnDefinition);
Expand Down
Loading