Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a0bd291
chore(deps): upgrade to .NET 10 and Microsoft.Data.SqlClient 6.x
souvikghosh04 Jun 10, 2026
1064d7f
Update SqlClient 6.1.5
RubenCerna2079 Jun 18, 2026
96f5324
Address PR review feedback and align ASP.NET Core packages to 10.x
souvikghosh04 Jun 18, 2026
3f5b597
Merge branch 'main' into Usr/sogh/upgrade-net10-sqlclient6
souvikghosh04 Jun 18, 2026
280151b
Pin MessagePack to 2.5.301 to fix NU1903 transitive vulnerability
souvikghosh04 Jun 18, 2026
1a7d309
Formatting fixes
souvikghosh04 Jun 18, 2026
a6ecbb9
Formatting fixes
souvikghosh04 Jun 18, 2026
5544ef4
Remove unused variable
souvikghosh04 Jun 18, 2026
5e3175b
Fix SqlTestHelper for Microsoft.Data.SqlClient 6.x SqlError construct…
souvikghosh04 Jun 18, 2026
f840076
Treat empty ASPNETCORE_URLS as valid in ValidateAspNetCoreUrls
souvikghosh04 Jun 18, 2026
ed820fb
Revert unrelated changes to match main (PR review feedback)
souvikghosh04 Jun 19, 2026
3735ff4
Fix .NET 10 formatter whitespace/style on Core mutation files
souvikghosh04 Jun 19, 2026
a808e13
remove unused variable due to strict check by .Net 10
souvikghosh04 Jun 19, 2026
abb49d1
Merge branch 'main' into Usr/sogh/upgrade-net10-sqlclient6
souvikghosh04 Jun 22, 2026
fbb8159
try adding DBTYPE_RESOLUTION_ERROR back
souvikghosh04 Jun 22, 2026
f3f5583
Removing unused variable which fails in pipeline
souvikghosh04 Jun 22, 2026
bd87fa0
Suppress unused variable warning
souvikghosh04 Jun 22, 2026
d19a55f
Update SqlClient to 6.1.5
RubenCerna2079 Jun 8, 2026
3cc1c0b
First additions to vector data type
RubenCerna2079 Jun 9, 2026
2fea5ce
New changes
RubenCerna2079 Jun 10, 2026
1e49376
Make vector array type
RubenCerna2079 Jun 17, 2026
98e6434
Read
RubenCerna2079 Jun 19, 2026
0137653
More changes
RubenCerna2079 Jun 22, 2026
e7d72c8
Add write abilities
RubenCerna2079 Jun 22, 2026
36ff6eb
Add writing capabilities
RubenCerna2079 Jun 22, 2026
8a0edd2
Add tests
RubenCerna2079 Jun 24, 2026
c657910
Merge branch 'main' into dev/rubencerna/first-changes-vector-type
RubenCerna2079 Jun 24, 2026
ebe6b28
Fix tests
RubenCerna2079 Jun 24, 2026
7e45fe2
Fix syntax
RubenCerna2079 Jun 24, 2026
aef7af9
Fix syntax error
RubenCerna2079 Jun 24, 2026
e479927
Changes based on copilot
RubenCerna2079 Jun 25, 2026
0e96e32
Upgrade docker sql version
RubenCerna2079 Jun 25, 2026
84b73b1
Fix utf8
RubenCerna2079 Jun 25, 2026
99954f6
Upgrade docker sql version
RubenCerna2079 Jun 25, 2026
6404bdc
Change to sql 2025 in pipeline
RubenCerna2079 Jun 25, 2026
786f98f
Merge remote-tracking branch 'refs/remotes/origin/dev/rubencerna/firs…
RubenCerna2079 Jun 25, 2026
f96fefb
Fixed how result is returned in insert
RubenCerna2079 Jun 26, 2026
8f2eb92
Changes based on comments
RubenCerna2079 Jul 1, 2026
c357a59
Fix syntax
RubenCerna2079 Jul 1, 2026
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
4 changes: 2 additions & 2 deletions .pipelines/mssql-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ jobs:
# for the linux job above.
data-source.connection-string: Server=(localdb)\MSSQLLocalDB;Persist Security Info=False;Integrated Security=True;MultipleActiveResultSets=False;Connection Timeout=30;TrustServerCertificate=True;
InstallerUrl: https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi
SqlVersionCode: '15.0'
SqlVersionCode: '17.0'

steps:
- template: templates/mssql-test-steps.yml
Expand All @@ -200,7 +200,7 @@ jobs:
# for the linux job above.
data-source.connection-string: Server=(localdb)\MSSQLLocalDB;Persist Security Info=False;Integrated Security=True;MultipleActiveResultSets=False;Connection Timeout=30;TrustServerCertificate=True;
InstallerUrl: https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi
SqlVersionCode: '15.0'
SqlVersionCode: '17.0'

steps:
- template: templates/mssql-test-steps.yml
Expand Down
2 changes: 2 additions & 0 deletions config-generators/mssql-commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ add Broker --config "dab-config.MsSql.json" --source brokers --permissions "anon
add WebsiteUser --config "dab-config.MsSql.json" --source website_users --permissions "anonymous:create,read,delete,update"
add WebsiteUser_MM --config "dab-config.MsSql.json" --source website_users_mm --graphql "websiteuser_mm:websiteusers_mm" --permissions "anonymous:*"
add SupportedType --config "dab-config.MsSql.json" --source type_table --permissions "anonymous:create,read,delete,update"
add VectorType --config "dab-config.MsSql.json" --source vector_type_table --rest true --graphql false --permissions "anonymous:create,read,delete,update"
update VectorType --config "dab-config.MsSql.json" --permissions "authenticated:create,read,delete,update"
add stocks_price --config "dab-config.MsSql.json" --source stocks_price --permissions "authenticated:create,read,update,delete"
update stocks_price --config "dab-config.MsSql.json" --permissions "anonymous:read"
update stocks_price --config "dab-config.MsSql.json" --permissions "TestNestedFilterFieldIsNull_ColumnForbidden:read" --fields.exclude "price"
Expand Down
2 changes: 1 addition & 1 deletion scripts/start-mssql-server.bash
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ echo "forceencryption = 1" >> $CERT_DIR/mssql.conf
cat $CERT_DIR/mssql.conf

# Start mssql-server by volume mounting the cert, key and conf files.
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$DOCKER_SQL_PASS" -p 1433:1433 --name customerdb -h customerdb -v $CERT_DIR/mssql.conf:/var/opt/mssql/mssql.conf -v $CERT_DIR/mssql.pem:/var/opt/mssql/mssql.pem -v $CERT_DIR/mssql.key:/var/opt/mssql/mssql.key -d mcr.microsoft.com/mssql/server:2019-latest
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$DOCKER_SQL_PASS" -p 1433:1433 --name customerdb -h customerdb -v $CERT_DIR/mssql.conf:/var/opt/mssql/mssql.conf -v $CERT_DIR/mssql.pem:/var/opt/mssql/mssql.pem -v $CERT_DIR/mssql.key:/var/opt/mssql/mssql.key -d mcr.microsoft.com/mssql/server:2025-latest
sleep 30
docker logs customerdb

Expand Down
3 changes: 2 additions & 1 deletion src/Core/Models/SqlTypeConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static class SqlTypeConstants
{ "datetime2", true }, // SqlDbType.DateTime2
{ "datetimeoffset", true }, // SqlDbType.DateTimeOffset
{ "", false }, // SqlDbType.Udt and SqlDbType.Structured provided by SQL as empty strings (unsupported)
{ "numeric", true} // Not present in SqlDbType, however can be returned by sql functions like LAG and should map to decimal.
{ "numeric", true}, // Not present in SqlDbType, however can be returned by sql functions like LAG and should map to decimal.
{ "vector", true } // SqlDbType.Vector
};
}
6 changes: 2 additions & 4 deletions src/Core/Resolvers/MsSqlQueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -666,8 +666,7 @@ AND ty.name IN
N'hierarchyid',
N'sql_variant',
N'xml',
N'rowversion',
N'vector'
N'rowversion'
)
) THEN 1
ELSE 0
Expand Down Expand Up @@ -712,8 +711,7 @@ AND ty.name IN
N'hierarchyid',
N'sql_variant',
N'xml',
N'rowversion',
N'vector'
N'rowversion'
)
)
)
Expand Down
14 changes: 14 additions & 0 deletions src/Core/Resolvers/MsSqlQueryExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Azure.Identity;
using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlClient;
using Microsoft.Data.SqlTypes;
using Microsoft.Extensions.Logging;

namespace Azure.DataApiBuilder.Core.Resolvers
Expand Down Expand Up @@ -695,6 +696,19 @@ public override SqlCommand PrepareDbCommand(
parameter.Size = parameterEntry.Value.Length.Value;
}

// if sqldbtype is vector then set the value as an SqlVector object
if (parameter.SqlDbType is SqlDbType.Vector)
{
List<float> values = new();
foreach (float val in (Array)parameter.Value)
Comment thread
RubenCerna2079 marked this conversation as resolved.
{
values.Add(val);
}

SqlVector<float> value = new(values.ToArray());
parameter.Value = value;
}
Comment thread
RubenCerna2079 marked this conversation as resolved.

cmd.Parameters.Add(parameter);
}
}
Expand Down
23 changes: 20 additions & 3 deletions src/Core/Resolvers/QueryExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Azure.DataApiBuilder.Core.Models;
using Azure.DataApiBuilder.Service.Exceptions;
using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlTypes;
using Microsoft.Extensions.Logging;
using Polly;
using Polly.Retry;
Expand Down Expand Up @@ -502,7 +503,7 @@ public async Task<DbResultSet>
{
if (!ConfigProvider.GetConfig().MaxResponseSizeLogicEnabled())
{
dbResultSetRow.Columns.Add(columnName, dbDataReader[columnName]);
dbResultSetRow.Columns.Add(columnName, GetColumnInformation(dbDataReader, columnName));
}
else
{
Expand Down Expand Up @@ -554,7 +555,7 @@ public DbResultSet
{
if (!ConfigProvider.GetConfig().MaxResponseSizeLogicEnabled())
{
dbResultSetRow.Columns.Add(columnName, dbDataReader[columnName]);
dbResultSetRow.Columns.Add(columnName, GetColumnInformation(dbDataReader, columnName));
}
else
{
Expand Down Expand Up @@ -822,7 +823,7 @@ internal int StreamDataIntoDbResultSetRow(DbDataReader dbDataReader, DbResultSet
{
dataRead = columnSize;
ValidateSize(availableBytes, dataRead);
dbResultSetRow.Columns.Add(columnName, dbDataReader[columnName]);
dbResultSetRow.Columns.Add(columnName, GetColumnInformation(dbDataReader, columnName));
}

return dataRead;
Expand Down Expand Up @@ -885,6 +886,22 @@ private void ValidateSize(long availableSizeBytes, long sizeToBeReadBytes)
}
}

/// <summary>
/// Helper function to get column information from the DbDataReader and handle special cases like SqlVector<float>.
/// </summary>
/// <param name="dbDataReader"></param>
/// <param name="columnName"></param>
/// <returns></returns>
private static object GetColumnInformation(DbDataReader dbDataReader, string columnName)
Comment thread
RubenCerna2079 marked this conversation as resolved.
{
if (dbDataReader[columnName] is SqlVector<float> columnValue)
{
return columnValue.Memory;
}

return dbDataReader[columnName];
}

internal virtual void AddDbExecutionTimeToMiddlewareContext(long time)
{
HttpContext? httpContext = HttpContextAccessor?.HttpContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Data;
using System.Globalization;
using System.Net;
using System.Text.Json;
using Azure.DataApiBuilder.Auth;
using Azure.DataApiBuilder.Config.DatabasePrimitives;
using Azure.DataApiBuilder.Config.ObjectModel;
Expand Down Expand Up @@ -452,10 +453,50 @@ protected static object ParseParamAsSystemType(string param, Type systemType)
"Guid" => Guid.Parse(param),
"TimeOnly" => TimeOnly.Parse(param),
"TimeSpan" => TimeOnly.Parse(param),
"Single[]" => ParseArrayIntoSystemType(param, systemType),
_ => throw new NotSupportedException($"{systemType.Name} is not supported")
};
}

/// <summary>
/// Takes the array of the parameter we are going to parse and converts each element to the specified system type.
/// </summary>
/// <param name="param"></param>
/// <param name="systemType"></param>
/// <returns></returns>
/// <exception cref="NotSupportedException"></exception>
/// <exception cref="FormatException"></exception>
private static object ParseArrayIntoSystemType(string param, Type systemType)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this pattern we can also write to other array values such as the ones found in PostgreSQL. Let me know if you think this is viable.

Comment thread
RubenCerna2079 marked this conversation as resolved.
{
Type typeOfArray;
switch (systemType.Name)
{
case "Single[]":
typeOfArray = typeof(Single);
break;

default:
throw new NotSupportedException($"{systemType.Name} is not supported");
}

try
{
List<object> list = new();
object[] values = JsonSerializer.Deserialize<object[]>(param) ?? Array.Empty<object>();
for (int i = 0; i < values.Length; i++)
{
string stringValue = values[i]?.ToString() ?? string.Empty;
values[i] = ParseParamAsSystemType(stringValue, typeOfArray);
}

return values;
}
catch
{
throw new FormatException($"Expected an array for {systemType.Name} but got an unexpected value");
}
}

/// <summary>
/// Very similar to GQLArgumentToDictParams but only extracts the argument names from
/// the specified field which means that the method does not need a middleware context
Expand Down
10 changes: 10 additions & 0 deletions src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Azure.DataApiBuilder.Service.Exceptions;
using Azure.DataApiBuilder.Service.GraphQLBuilder;
using Microsoft.Data.SqlClient;
using Microsoft.Data.SqlTypes;
using Microsoft.Extensions.Logging;
using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming;

Expand Down Expand Up @@ -114,6 +115,15 @@ protected override void PopulateColumnDefinitionWithHasDefaultAndDbType(
columnDefinition.DbType = TypeHelper.GetDbTypeFromSystemType(columnDefinition.SystemType);

string sqlDbTypeName = (string)columnInfo["DATA_TYPE"];

if (columnDefinition.SystemType == typeof(SqlVector<Single>))
{
sqlDbTypeName = "vector"; // Currently the "DATA_TYPE" column returns "varbinary" for vector type columns. This is a known issue https://learn.microsoft.com/en-us/sql/t-sql/data-types/vector-data-type?view=sql-server-ver17&tabs=csharp#known-issues
columnDefinition.IsArrayType = true;
columnDefinition.ElementSystemType = typeof(Single);
Comment thread
RubenCerna2079 marked this conversation as resolved.
columnDefinition.SystemType = columnDefinition.ElementSystemType.MakeArrayType();
}

if (Enum.TryParse(sqlDbTypeName, ignoreCase: true, out SqlDbType sqlDbType))
{
// The DbType enum in .NET does not distinguish between VarChar and NVarChar. Both are mapped to DbType.String.
Expand Down
9 changes: 8 additions & 1 deletion src/Core/Services/OpenAPI/OpenApiDocumentor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1484,12 +1484,18 @@ private static OpenApiSchema CreateComponentSchema(
if (metadataProvider.TryGetBackingColumn(entityName, field, out string? backingColumnValue) && !string.IsNullOrEmpty(backingColumnValue))
{
string typeMetadata = string.Empty;
string subTypeMetadata = string.Empty;
string formatMetadata = string.Empty;
string? fieldDescription = null;

if (dbObject.SourceDefinition.Columns.TryGetValue(backingColumnValue, out ColumnDefinition? columnDef))
{
typeMetadata = TypeHelper.GetJsonDataTypeFromSystemType(columnDef.SystemType).ToString().ToLower();

if (string.Equals(typeMetadata, JsonDataType.Array.ToString().ToLower(), StringComparison.OrdinalIgnoreCase))
{
subTypeMetadata = TypeHelper.GetJsonDataTypeFromSystemType(columnDef.ElementSystemType!).ToString().ToLower();
}
}

if (entityConfig?.Fields != null)
Expand All @@ -1502,7 +1508,8 @@ private static OpenApiSchema CreateComponentSchema(
{
Type = typeMetadata,
Format = formatMetadata,
Description = fieldDescription
Description = fieldDescription,
Items = !string.IsNullOrWhiteSpace(subTypeMetadata) ? new OpenApiSchema() { Type = subTypeMetadata } : null
});
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/Core/Services/TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Azure.DataApiBuilder.Core.Services.OpenAPI;
using Azure.DataApiBuilder.Service.Exceptions;
using HotChocolate.Language;
using Microsoft.Data.SqlTypes;
using Microsoft.OData.Edm;

namespace Azure.DataApiBuilder.Core.Services
Expand Down Expand Up @@ -46,7 +47,8 @@ public static class TypeHelper
[typeof(byte[])] = DbType.Binary,
[typeof(TimeOnly)] = DbType.Time,
[typeof(TimeSpan)] = DbType.Time,
[typeof(object)] = DbType.Object
[typeof(object)] = DbType.Object,
[typeof(SqlVector<Single>)] = DbType.Single
};

/// <summary>
Expand Down Expand Up @@ -77,7 +79,8 @@ public static class TypeHelper
[typeof(TimeOnly)] = JsonDataType.String,
[typeof(object)] = JsonDataType.Object,
[typeof(DateTime)] = JsonDataType.String,
[typeof(DateTimeOffset)] = JsonDataType.String
[typeof(DateTimeOffset)] = JsonDataType.String,
[typeof(Single[])] = JsonDataType.Array
};

/// <summary>
Expand Down Expand Up @@ -111,7 +114,8 @@ public static class TypeHelper
[SqlDbType.TinyInt] = typeof(byte),
[SqlDbType.UniqueIdentifier] = typeof(Guid),
[SqlDbType.VarBinary] = typeof(byte[]),
[SqlDbType.VarChar] = typeof(string)
[SqlDbType.VarChar] = typeof(string),
[SqlDbType.Vector] = typeof(float)
};

private static Dictionary<SqlDbType, DbType> _sqlDbDateTimeTypeToDbType = new()
Expand Down
24 changes: 24 additions & 0 deletions src/Service.Tests/DatabaseSchema-MsSql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ DROP TABLE IF EXISTS stocks;
DROP TABLE IF EXISTS comics;
DROP TABLE IF EXISTS brokers;
DROP TABLE IF EXISTS type_table;
DROP TABLE IF EXISTS vector_type_table;
DROP TABLE IF EXISTS trees;
DROP TABLE IF EXISTS fungi;
DROP TABLE IF EXISTS empty_table;
Expand Down Expand Up @@ -232,6 +233,12 @@ CREATE TABLE type_table(
uuid_types uniqueidentifier DEFAULT newid()
);

CREATE TABLE vector_type_table(
id int IDENTITY(5001, 1) PRIMARY KEY,
vector_data vector(3),
vector_data_max vector(1998)
);
Comment thread
RubenCerna2079 marked this conversation as resolved.

CREATE TABLE trees (
treeId int PRIMARY KEY,
species varchar(max),
Expand Down Expand Up @@ -608,6 +615,23 @@ VALUES
INSERT INTO type_table(id, uuid_types) values(10, 'D1D021A8-47B4-4AE4-B718-98E89C41A161');
SET IDENTITY_INSERT type_table OFF

SET IDENTITY_INSERT vector_type_table ON
INSERT INTO vector_type_table(id, vector_data)
VALUES
(1, '[0.5, 0.25, 0.75]'),
(2, '[1.5, -2.5, 3.5]'),
(3, NULL),
(4, '[1.0, 2.0, 3.0]'),
(5, '[4.0, 5.0, 6.0]'),
(6, '[7.0, 8.0, 9.0]');

INSERT INTO vector_type_table(id, vector_data_max)
VALUES (7, CAST('[' + (
SELECT STRING_AGG(CAST(value AS NVARCHAR(MAX)), ',') WITHIN GROUP (ORDER BY value)
FROM GENERATE_SERIES(1, 1998)
) + ']' AS vector(1998)));
SET IDENTITY_INSERT vector_type_table OFF

SET IDENTITY_INSERT sales ON
INSERT INTO sales(id, item_name, subtotal, tax) VALUES (1, 'Watch', 249.00, 20.59), (2, 'Montior', 120.50, 11.12);
SET IDENTITY_INSERT sales OFF
Expand Down
Loading
Loading