From 255b59b125d82d00b1ae9fef9ea7c823643cafd0 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 22 Jan 2026 13:37:51 -0800 Subject: [PATCH 01/12] Changes for autoentities --- src/Core/Resolvers/MsSqlQueryBuilder.cs | 36 ++++++++++++++++++- src/Core/Resolvers/MsSqlQueryExecutor.cs | 4 +-- .../MsSqlMetadataProvider.cs | 1 + .../MetadataProviders/SqlMetadataProvider.cs | 21 +++++++++++ 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/Core/Resolvers/MsSqlQueryBuilder.cs b/src/Core/Resolvers/MsSqlQueryBuilder.cs index 798c20975a..a4b98b7338 100644 --- a/src/Core/Resolvers/MsSqlQueryBuilder.cs +++ b/src/Core/Resolvers/MsSqlQueryBuilder.cs @@ -506,7 +506,7 @@ public string BuildStoredProcedureResultDetailsQuery(string databaseObjectName) /// 2. are computed based on other columns, /// are considered as read only columns. The query combines both the types of read-only columns and returns the list. /// - /// Param name of the schema/database. + /// Param name of the schema/database. /// Param name of the table. /// public string BuildQueryToGetReadOnlyColumns(string schemaParamName, string tableParamName) @@ -560,5 +560,39 @@ protected override string BuildPredicates(SqlQueryStructure structure) // contains LIKE and add the ESCAPE clause accordingly. return AddEscapeToLikeClauses(predicates); } + + /// + /// Builds the query used to get the list of tables with the SQL LIKE + /// syntax that will be transformed into entities. + /// + /// Pattern for tables that will be included. + /// Pattern for tables that will be excluded. + /// Pattern for naming the entities. + /// + public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) + { + string query = $"DECLARE @include_pattern NVARCHAR(MAX) = {include} DECLARE @exclude_pattern NVARCHAR(MAX) = {exclude} DECLARE @name_pattern NVARCHAR(255) = {namePattern}" + + "DECLARE @exclude_invalid_types BIT = 1 SET NOCOUNT ON; WITH include_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern " + + "FROM STRING_SPLIT(ISNULL(@include_pattern, N''), N',') WHERE LTRIM(RTRIM(value)) <> N'' ), " + + "exclude_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern FROM STRING_SPLIT(ISNULL(@exclude_pattern, N''), N',') " + + "WHERE LTRIM(RTRIM(value)) <> N'' ), all_tables AS ( SELECT s.name AS schema_name, t.name AS object_name, s.name + N'.' " + + "+ t.name AS full_name, N'table' AS object_type, t.object_id FROM sys.tables AS t JOIN sys.schemas AS s ON t.schema_id = s.schema_id " + + "WHERE EXISTS ( SELECT 1 FROM sys.key_constraints AS kc WHERE kc.parent_object_id = t.object_id AND kc.type = 'PK' ) ), eligible_tables AS " + + "( SELECT o.schema_name, o.object_name, o.full_name, o.object_type, o.object_id, CASE WHEN so.is_ms_shipped = 1 THEN 1 WHEN o.schema_name IN " + + "(N'sys', N'INFORMATION_SCHEMA') THEN 1 WHEN o.object_name IN ( N'__EFMigrationsHistory', N'__MigrationHistory', N'__FlywayHistory', N'sysdiagrams' ) THEN 1 " + + "WHEN o.object_name LIKE N'service_broker_%' THEN 1 WHEN o.object_name LIKE N'queue_messages_%' THEN 1 WHEN o.object_name LIKE N'MSmerge_%' " + + "THEN 1 WHEN o.object_name LIKE N'MSreplication_%' THEN 1 WHEN o.object_name LIKE N'FileTableUpdates$%' THEN 1 WHEN o.object_name LIKE N'graph_%' THEN 1 WHEN EXISTS " + + "( SELECT 1 FROM sys.tables AS t WHERE t.object_id = o.object_id AND ( t.is_tracked_by_cdc = 1 OR t.temporal_type > 0 OR t.is_filetable = 1 OR t.is_memory_optimized = 1 ) ) " + + "THEN 1 ELSE 0 END AS is_system_object FROM all_tables AS o JOIN sys.objects AS so ON so.object_id = o.object_id ) SELECT a.schema_name AS [schema], a.object_name AS [object], " + + "CASE WHEN LTRIM(RTRIM(ISNULL(@name_pattern, N''))) = N'' THEN a.object_name ELSE REPLACE( REPLACE(@name_pattern, N'{schema}', a.schema_name), N'{object}', a.object_name ) " + + "END AS entity_name, CASE WHEN EXISTS ( SELECT 1 FROM sys.columns AS c JOIN sys.types AS ty ON c.user_type_id = ty.user_type_id WHERE c.object_id = a.object_id AND ty.name IN " + + "( N'geography', N'geometry', N'hierarchyid', N'sql_variant', N'xml', N'rowversion', N'vector' ) ) THEN 1 ELSE 0 END AS contains_invalid_types FROM eligible_tables AS a WHERE " + + "a.is_system_object = 0 AND ( NOT EXISTS (SELECT 1 FROM exclude_patterns) OR NOT EXISTS ( SELECT 1 FROM exclude_patterns AS ep WHERE a.full_name LIKE ep.pattern " + + "COLLATE DATABASE_DEFAULT ESCAPE '\' ) ) AND ( NOT EXISTS (SELECT 1 FROM include_patterns) OR EXISTS ( SELECT 1 FROM include_patterns AS ip WHERE a.full_name LIKE ip.pattern " + + "COLLATE DATABASE_DEFAULT ESCAPE '\' ) ) AND ( @exclude_invalid_types = 0 OR NOT EXISTS ( SELECT 1 FROM sys.columns AS c JOIN sys.types AS ty ON c.user_type_id = ty.user_type_id " + + "WHERE c.object_id = a.object_id AND ty.name IN ( N'geography', N'geometry', N'hierarchyid', N'sql_variant', N'xml', N'rowversion', N'vector' ) ) ) ORDER BY a.schema_name, a.object_name;"; + + return query; + } } } diff --git a/src/Core/Resolvers/MsSqlQueryExecutor.cs b/src/Core/Resolvers/MsSqlQueryExecutor.cs index 5cbe9f6a76..6a900fb7ff 100644 --- a/src/Core/Resolvers/MsSqlQueryExecutor.cs +++ b/src/Core/Resolvers/MsSqlQueryExecutor.cs @@ -82,7 +82,7 @@ public MsSqlQueryExecutor( _dataSourceToSessionContextUsage = new Dictionary(); _accessTokensFromConfiguration = runtimeConfigProvider.ManagedIdentityAccessToken; _runtimeConfigProvider = runtimeConfigProvider; - ConfigureMsSqlQueryEecutor(); + ConfigureMsSqlQueryExecutor(); } /// @@ -138,7 +138,7 @@ public override SqlConnection CreateConnection(string dataSourceName) /// /// Configure during construction or a hot-reload scenario. /// - private void ConfigureMsSqlQueryEecutor() + private void ConfigureMsSqlQueryExecutor() { IEnumerable> mssqldbs = _runtimeConfigProvider.GetConfig().GetDataSourceNamesToDataSourcesIterator().Where(x => x.Value.DatabaseType is DatabaseType.MSSQL || x.Value.DatabaseType is DatabaseType.DWSQL); diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 7d02798427..391a01da25 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -60,6 +60,7 @@ public override Type SqlToCLRType(string sqlType) /// public override async Task PopulateTriggerMetadataForTable(string entityName, string schemaName, string tableName, SourceDefinition sourceDefinition) { + string enumerateEnabledTriggers = SqlQueryBuilder.BuildFetchEnabledTriggersQuery(); Dictionary parameters = new() { diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 8553e08136..8a675c086d 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -307,6 +307,7 @@ public string GetEntityName(string graphQLType) public async Task InitializeAsync() { System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew(); + GenerateAutoentitiesIntoEntities(); GenerateDatabaseObjectForEntities(); if (_isValidateOnly) { @@ -686,6 +687,26 @@ private void GenerateDatabaseObjectForEntities() } } + /// + /// + /// + private void GenerateAutoentitiesIntoEntities() + { + if (GetDatabaseType() != DatabaseType.MSSQL) + { + return; + } + + RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); + if (runtimeConfig.Autoentities is not null) + { + foreach ((string name, Autoentity autoentity) in runtimeConfig.Autoentities.AutoEntities) + { + + } + } + } + protected void PopulateDatabaseObjectForEntity( Entity entity, string entityName, From 12ee205a03e148fc771be8e64221c585b181a196 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Mon, 26 Jan 2026 15:28:51 -0800 Subject: [PATCH 02/12] Add query and its execution --- src/Core/Resolvers/IQueryBuilder.cs | 2 ++ src/Core/Resolvers/MsSqlQueryBuilder.cs | 6 ++-- .../MsSqlMetadataProvider.cs | 29 ++++++++++++++++++ .../MetadataProviders/SqlMetadataProvider.cs | 25 ++++++---------- .../UnitTests/SqlMetadataProviderUnitTests.cs | 30 +++++++++++++++++++ 5 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/Core/Resolvers/IQueryBuilder.cs b/src/Core/Resolvers/IQueryBuilder.cs index 3a14e472cd..d4016fbba6 100644 --- a/src/Core/Resolvers/IQueryBuilder.cs +++ b/src/Core/Resolvers/IQueryBuilder.cs @@ -90,5 +90,7 @@ public interface IQueryBuilder /// DB Connection Param. /// public string QuoteTableNameAsDBConnectionParam(string param); + + public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) => throw new NotImplementedException(); } } diff --git a/src/Core/Resolvers/MsSqlQueryBuilder.cs b/src/Core/Resolvers/MsSqlQueryBuilder.cs index a4b98b7338..74fe6db9be 100644 --- a/src/Core/Resolvers/MsSqlQueryBuilder.cs +++ b/src/Core/Resolvers/MsSqlQueryBuilder.cs @@ -571,7 +571,7 @@ protected override string BuildPredicates(SqlQueryStructure structure) /// public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) { - string query = $"DECLARE @include_pattern NVARCHAR(MAX) = {include} DECLARE @exclude_pattern NVARCHAR(MAX) = {exclude} DECLARE @name_pattern NVARCHAR(255) = {namePattern}" + + string query = $"DECLARE @include_pattern NVARCHAR(MAX) = '{include}' DECLARE @exclude_pattern NVARCHAR(MAX) = '{exclude}' DECLARE @name_pattern NVARCHAR(255) = '{namePattern}' " + "DECLARE @exclude_invalid_types BIT = 1 SET NOCOUNT ON; WITH include_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern " + "FROM STRING_SPLIT(ISNULL(@include_pattern, N''), N',') WHERE LTRIM(RTRIM(value)) <> N'' ), " + "exclude_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern FROM STRING_SPLIT(ISNULL(@exclude_pattern, N''), N',') " + @@ -588,8 +588,8 @@ public string BuildGetAutoentitiesQuery(string include, string exclude, string n "END AS entity_name, CASE WHEN EXISTS ( SELECT 1 FROM sys.columns AS c JOIN sys.types AS ty ON c.user_type_id = ty.user_type_id WHERE c.object_id = a.object_id AND ty.name IN " + "( N'geography', N'geometry', N'hierarchyid', N'sql_variant', N'xml', N'rowversion', N'vector' ) ) THEN 1 ELSE 0 END AS contains_invalid_types FROM eligible_tables AS a WHERE " + "a.is_system_object = 0 AND ( NOT EXISTS (SELECT 1 FROM exclude_patterns) OR NOT EXISTS ( SELECT 1 FROM exclude_patterns AS ep WHERE a.full_name LIKE ep.pattern " + - "COLLATE DATABASE_DEFAULT ESCAPE '\' ) ) AND ( NOT EXISTS (SELECT 1 FROM include_patterns) OR EXISTS ( SELECT 1 FROM include_patterns AS ip WHERE a.full_name LIKE ip.pattern " + - "COLLATE DATABASE_DEFAULT ESCAPE '\' ) ) AND ( @exclude_invalid_types = 0 OR NOT EXISTS ( SELECT 1 FROM sys.columns AS c JOIN sys.types AS ty ON c.user_type_id = ty.user_type_id " + + "COLLATE DATABASE_DEFAULT ESCAPE '\\' ) ) AND ( NOT EXISTS (SELECT 1 FROM include_patterns) OR EXISTS ( SELECT 1 FROM include_patterns AS ip WHERE a.full_name LIKE ip.pattern " + + "COLLATE DATABASE_DEFAULT ESCAPE '\\' ) ) AND ( @exclude_invalid_types = 0 OR NOT EXISTS ( SELECT 1 FROM sys.columns AS c JOIN sys.types AS ty ON c.user_type_id = ty.user_type_id " + "WHERE c.object_id = a.object_id AND ty.name IN ( N'geography', N'geometry', N'hierarchyid', N'sql_variant', N'xml', N'rowversion', N'vector' ) ) ) ORDER BY a.schema_name, a.object_name;"; return query; diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 391a01da25..38666074c5 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -291,5 +291,34 @@ private bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) return false; } } + + /// + protected override async Task GenerateAutoentitiesIntoEntities() + { + RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); + if (runtimeConfig.Autoentities is not null) + { + foreach ((string name, Autoentity autoentity) in runtimeConfig.Autoentities.AutoEntities) + { + JsonArray? resultArray = await QueryAutoentitiesConfiguration(autoentity); + } + } + } + + public async Task QueryAutoentitiesConfiguration(Autoentity autoentity) + { + string include = string.Join(",", autoentity.Patterns.Include); + string exclude = string.Join(",", autoentity.Patterns.Exclude); + string namePattern = autoentity.Patterns.Name; + string getAutoentitiesQuery = SqlQueryBuilder.BuildGetAutoentitiesQuery(include, exclude, namePattern); + + JsonArray? resultArray = await QueryExecutor.ExecuteQueryAsync( + sqltext: getAutoentitiesQuery, + parameters: null!, + dataReaderHandler: QueryExecutor.GetJsonArrayAsync, + dataSourceName: _dataSourceName); + + return resultArray; + } } } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 8a675c086d..310760e499 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -307,7 +307,11 @@ public string GetEntityName(string graphQLType) public async Task InitializeAsync() { System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew(); - GenerateAutoentitiesIntoEntities(); + if (GetDatabaseType() == DatabaseType.MSSQL) + { + await GenerateAutoentitiesIntoEntities(); + } + GenerateDatabaseObjectForEntities(); if (_isValidateOnly) { @@ -688,23 +692,12 @@ private void GenerateDatabaseObjectForEntities() } /// - /// + /// Creates entities for each table that is found, based on the autoentity configuration. + /// This method is only called for tables in MsSql. /// - private void GenerateAutoentitiesIntoEntities() + protected virtual Task GenerateAutoentitiesIntoEntities() { - if (GetDatabaseType() != DatabaseType.MSSQL) - { - return; - } - - RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); - if (runtimeConfig.Autoentities is not null) - { - foreach ((string name, Autoentity autoentity) in runtimeConfig.Autoentities.AutoEntities) - { - - } - } + throw new NotImplementedException(); } protected void PopulateDatabaseObjectForEntity( diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 8b4ed68f60..6094807459 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -588,5 +588,35 @@ private static async Task SetupTestFixtureAndInferMetadata() await ResetDbStateAsync(); await _sqlMetadataProvider.InitializeAsync(); } + + /// + /// Ensures that the query that returns the tables that will be generated + /// into entities from the autoentites configuration return the expected result. + /// + [TestMethod, TestCategory(TestCategory.MSSQL)] + public async Task CheckAutoentitiesQuery() + { + DatabaseEngine = TestCategory.MSSQL; + TestHelper.SetupDatabaseEnvironment(DatabaseEngine); + RuntimeConfig runtimeConfig = SqlTestHelper.SetupRuntimeConfig(); + RuntimeConfigProvider runtimeConfigProvider = TestHelper.GenerateInMemoryRuntimeConfigProvider(runtimeConfig); + SetUpSQLMetadataProvider(runtimeConfigProvider); + + await _sqlMetadataProvider.InitializeAsync(); + + MsSqlMetadataProvider metadataProvider = (MsSqlMetadataProvider)_sqlMetadataProvider; + JsonArray resultArray = metadataProvider.QueryAutoentitiesConfiguration(); + + Assert.IsNotNull(resultArray); + + JsonObject resultObject = (JsonObject)resultArray[0]; + Assert.AreEqual(expected: entitySchema, actual: resultObject[0]); + Assert.AreEqual(expected: entityObject, actual: resultObject[1]); + Assert.AreEqual(expected: entityName, actual: resultObject[2]); + + // Check that the values inside of the + + TestHelper.UnsetAllDABEnvironmentVariables(); + } } } From b6f5f5b522f3bb1946d56ee5118809a03887dd15 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Mon, 26 Jan 2026 21:26:04 -0800 Subject: [PATCH 03/12] Add testing --- .../UnitTests/SqlMetadataProviderUnitTests.cs | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 6094807459..f168eabb53 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -19,6 +19,7 @@ using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.Tests.Configuration; using Azure.DataApiBuilder.Service.Tests.SqlTests; +using HotChocolate.Execution.Processing; using Microsoft.AspNetCore.Http; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; @@ -593,28 +594,56 @@ private static async Task SetupTestFixtureAndInferMetadata() /// Ensures that the query that returns the tables that will be generated /// into entities from the autoentites configuration return the expected result. /// - [TestMethod, TestCategory(TestCategory.MSSQL)] - public async Task CheckAutoentitiesQuery() + [DataTestMethod, TestCategory(TestCategory.MSSQL)] + [DataRow(new string[] { "dbo.%book%" }, new string[] { }, "{schema}.{object}.books", new string[] { "book" }, "")] + [DataRow(new string[] { "dbo.%publish%" }, new string[] { }, "{schema}.{object}", new string[] { "publish" }, "")] + [DataRow(new string[] { "dbo.%book%" }, new string[] { "dbo.%books%" }, "{schema}_{object}_exclude_books", new string[] { "book" }, "books")] + [DataRow(new string[] { "dbo.%book%", "dbo.%publish%" }, new string[] { }, "{object}", new string[] { "book", "publish" }, "")] + public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, string name, string[] includeObject, string excludeObject) { + // Arrange DatabaseEngine = TestCategory.MSSQL; TestHelper.SetupDatabaseEnvironment(DatabaseEngine); RuntimeConfig runtimeConfig = SqlTestHelper.SetupRuntimeConfig(); - RuntimeConfigProvider runtimeConfigProvider = TestHelper.GenerateInMemoryRuntimeConfigProvider(runtimeConfig); + Autoentity autoentity = new(new AutoentityPatterns(include, exclude, name), null, null); + Dictionary dictAutoentity = new() + { + { "autoentity", autoentity } + }; + RuntimeConfig configWithAutoentity = runtimeConfig with + { + Autoentities = new RuntimeAutoentities(dictAutoentity) + }; + RuntimeConfigProvider runtimeConfigProvider = TestHelper.GenerateInMemoryRuntimeConfigProvider(configWithAutoentity); SetUpSQLMetadataProvider(runtimeConfigProvider); await _sqlMetadataProvider.InitializeAsync(); + // Act MsSqlMetadataProvider metadataProvider = (MsSqlMetadataProvider)_sqlMetadataProvider; - JsonArray resultArray = metadataProvider.QueryAutoentitiesConfiguration(); + JsonArray resultArray = await metadataProvider.QueryAutoentitiesConfiguration(autoentity); + // Assert Assert.IsNotNull(resultArray); + foreach(JsonObject resultObject in resultArray) + { + bool includedObjectExists = false; + foreach (string included in includeObject) + { + if (resultObject[1].ToString().Contains(included)) + { + includedObjectExists = true; + Assert.AreEqual(expected: "dbo", actual: resultObject[0].ToString(), "Query does not return expected schema."); + Assert.AreNotEqual(name, resultObject[3].ToString(), "Name returned by query should not include {schema} or {object}."); + if (exclude.Length > 0) + { + Assert.IsTrue(!resultObject[1].ToString().Contains(excludeObject), "Query returns pattern that should be excluded."); + } + } + } - JsonObject resultObject = (JsonObject)resultArray[0]; - Assert.AreEqual(expected: entitySchema, actual: resultObject[0]); - Assert.AreEqual(expected: entityObject, actual: resultObject[1]); - Assert.AreEqual(expected: entityName, actual: resultObject[2]); - - // Check that the values inside of the + Assert.IsTrue(includedObjectExists, "Query does not return expected object."); + } TestHelper.UnsetAllDABEnvironmentVariables(); } From 97eaf7a6da02828769d2270911f1e85a42a617a6 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 28 Jan 2026 16:49:13 -0800 Subject: [PATCH 04/12] Changes based on comments --- .../MetadataProviders/MsSqlMetadataProvider.cs | 1 + .../UnitTests/SqlMetadataProviderUnitTests.cs | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 38666074c5..ac64defee1 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -301,6 +301,7 @@ protected override async Task GenerateAutoentitiesIntoEntities() foreach ((string name, Autoentity autoentity) in runtimeConfig.Autoentities.AutoEntities) { JsonArray? resultArray = await QueryAutoentitiesConfiguration(autoentity); + // TODO: Finish implementation of autoentities generation in task #3052 } } } diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index f168eabb53..c7dc48dad9 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -19,7 +19,6 @@ using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.Tests.Configuration; using Azure.DataApiBuilder.Service.Tests.SqlTests; -using HotChocolate.Execution.Processing; using Microsoft.AspNetCore.Http; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; @@ -592,7 +591,7 @@ private static async Task SetupTestFixtureAndInferMetadata() /// /// Ensures that the query that returns the tables that will be generated - /// into entities from the autoentites configuration return the expected result. + /// into entities from the autoentities configuration returns the expected result. /// [DataTestMethod, TestCategory(TestCategory.MSSQL)] [DataRow(new string[] { "dbo.%book%" }, new string[] { }, "{schema}.{object}.books", new string[] { "book" }, "")] @@ -630,14 +629,14 @@ public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, str bool includedObjectExists = false; foreach (string included in includeObject) { - if (resultObject[1].ToString().Contains(included)) + if (resultObject["object"].ToString().Contains(included)) { includedObjectExists = true; - Assert.AreEqual(expected: "dbo", actual: resultObject[0].ToString(), "Query does not return expected schema."); - Assert.AreNotEqual(name, resultObject[3].ToString(), "Name returned by query should not include {schema} or {object}."); + Assert.AreEqual(expected: "dbo", actual: resultObject["schema"].ToString(), "Query does not return expected schema."); + Assert.AreNotEqual(name, resultObject["entity_name"].ToString(), "Name returned by query should not include {schema} or {object}."); if (exclude.Length > 0) { - Assert.IsTrue(!resultObject[1].ToString().Contains(excludeObject), "Query returns pattern that should be excluded."); + Assert.IsTrue(!resultObject["object"].ToString().Contains(excludeObject), "Query returns pattern that should be excluded."); } } } From e1aa3a5fe3fb2ed24b05cb42d3e931e74d66b743 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 28 Jan 2026 17:26:57 -0800 Subject: [PATCH 05/12] Changes based on comments --- src/Core/Resolvers/IQueryBuilder.cs | 2 +- src/Core/Resolvers/MsSqlQueryBuilder.cs | 6 +++--- .../Services/MetadataProviders/MsSqlMetadataProvider.cs | 9 +++++++-- .../Services/MetadataProviders/SqlMetadataProvider.cs | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Core/Resolvers/IQueryBuilder.cs b/src/Core/Resolvers/IQueryBuilder.cs index d4016fbba6..75a4620097 100644 --- a/src/Core/Resolvers/IQueryBuilder.cs +++ b/src/Core/Resolvers/IQueryBuilder.cs @@ -91,6 +91,6 @@ public interface IQueryBuilder /// public string QuoteTableNameAsDBConnectionParam(string param); - public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) => throw new NotImplementedException(); + public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) => throw new NotSupportedException($"{GetType().Name} does not support Autoentities yet."); } } diff --git a/src/Core/Resolvers/MsSqlQueryBuilder.cs b/src/Core/Resolvers/MsSqlQueryBuilder.cs index 74fe6db9be..85bbb621ec 100644 --- a/src/Core/Resolvers/MsSqlQueryBuilder.cs +++ b/src/Core/Resolvers/MsSqlQueryBuilder.cs @@ -564,15 +564,15 @@ protected override string BuildPredicates(SqlQueryStructure structure) /// /// Builds the query used to get the list of tables with the SQL LIKE /// syntax that will be transformed into entities. + /// NOTE: Currently this query only returns Tables, support for Views will come later. /// /// Pattern for tables that will be included. /// Pattern for tables that will be excluded. /// Pattern for naming the entities. - /// public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) { - string query = $"DECLARE @include_pattern NVARCHAR(MAX) = '{include}' DECLARE @exclude_pattern NVARCHAR(MAX) = '{exclude}' DECLARE @name_pattern NVARCHAR(255) = '{namePattern}' " + - "DECLARE @exclude_invalid_types BIT = 1 SET NOCOUNT ON; WITH include_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern " + + string query = $"DECLARE @include_pattern NVARCHAR(MAX) = '{include}'; DECLARE @exclude_pattern NVARCHAR(MAX) = '{exclude}'; DECLARE @name_pattern NVARCHAR(255) = '{namePattern}'; " + + "DECLARE @exclude_invalid_types BIT = 1; SET NOCOUNT ON; WITH include_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern " + "FROM STRING_SPLIT(ISNULL(@include_pattern, N''), N',') WHERE LTRIM(RTRIM(value)) <> N'' ), " + "exclude_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern FROM STRING_SPLIT(ISNULL(@exclude_pattern, N''), N',') " + "WHERE LTRIM(RTRIM(value)) <> N'' ), all_tables AS ( SELECT s.name AS schema_name, t.name AS object_name, s.name + N'.' " + diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index ac64defee1..82ef65ff2b 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -300,19 +300,24 @@ protected override async Task GenerateAutoentitiesIntoEntities() { foreach ((string name, Autoentity autoentity) in runtimeConfig.Autoentities.AutoEntities) { - JsonArray? resultArray = await QueryAutoentitiesConfiguration(autoentity); + JsonArray? resultArray = await QueryAutoentitiesAsync(autoentity); // TODO: Finish implementation of autoentities generation in task #3052 } } } - public async Task QueryAutoentitiesConfiguration(Autoentity autoentity) + public async Task QueryAutoentitiesAsync(Autoentity autoentity) { string include = string.Join(",", autoentity.Patterns.Include); string exclude = string.Join(",", autoentity.Patterns.Exclude); string namePattern = autoentity.Patterns.Name; string getAutoentitiesQuery = SqlQueryBuilder.BuildGetAutoentitiesQuery(include, exclude, namePattern); + _logger.LogInformation("Query for Autoentities is being executed with the following parameters."); + _logger.LogInformation($"Autoentities include pattern: {include}"); + _logger.LogInformation($"Autoentities exclude pattern: {exclude}"); + _logger.LogInformation($"Autoentities name pattern: {namePattern}"); + JsonArray? resultArray = await QueryExecutor.ExecuteQueryAsync( sqltext: getAutoentitiesQuery, parameters: null!, diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 310760e499..c9a62ca470 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -697,7 +697,7 @@ private void GenerateDatabaseObjectForEntities() /// protected virtual Task GenerateAutoentitiesIntoEntities() { - throw new NotImplementedException(); + throw new NotSupportedException($"{GetType().Name} does not support Autoentities yet."); } protected void PopulateDatabaseObjectForEntity( From c72e20e9e3d0f864116daa575e774a597f2ab75f Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 28 Jan 2026 17:32:40 -0800 Subject: [PATCH 06/12] Changes based on comments --- src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index c7dc48dad9..583a777397 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -620,7 +620,7 @@ public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, str // Act MsSqlMetadataProvider metadataProvider = (MsSqlMetadataProvider)_sqlMetadataProvider; - JsonArray resultArray = await metadataProvider.QueryAutoentitiesConfiguration(autoentity); + JsonArray resultArray = await metadataProvider.QueryAutoentitiesAsync(autoentity); // Assert Assert.IsNotNull(resultArray); From 90cdf7b9855483cfbcf9d98be3227f4df24a088f Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Wed, 28 Jan 2026 18:14:23 -0800 Subject: [PATCH 07/12] Changes based on comments --- .../UnitTests/SqlMetadataProviderUnitTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 583a777397..85834e68d3 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -598,6 +598,7 @@ private static async Task SetupTestFixtureAndInferMetadata() [DataRow(new string[] { "dbo.%publish%" }, new string[] { }, "{schema}.{object}", new string[] { "publish" }, "")] [DataRow(new string[] { "dbo.%book%" }, new string[] { "dbo.%books%" }, "{schema}_{object}_exclude_books", new string[] { "book" }, "books")] [DataRow(new string[] { "dbo.%book%", "dbo.%publish%" }, new string[] { }, "{object}", new string[] { "book", "publish" }, "")] + [DataRow(new string[] { }, new string[] { "dbo.%book%" }, "{object}", new string[] { "" }, "book")] public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, string name, string[] includeObject, string excludeObject) { // Arrange @@ -632,8 +633,12 @@ public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, str if (resultObject["object"].ToString().Contains(included)) { includedObjectExists = true; - Assert.AreEqual(expected: "dbo", actual: resultObject["schema"].ToString(), "Query does not return expected schema."); Assert.AreNotEqual(name, resultObject["entity_name"].ToString(), "Name returned by query should not include {schema} or {object}."); + if (include.Length > 0) + { + Assert.AreEqual(expected: "dbo", actual: resultObject["schema"].ToString(), "Query does not return expected schema."); + } + if (exclude.Length > 0) { Assert.IsTrue(!resultObject["object"].ToString().Contains(excludeObject), "Query returns pattern that should be excluded."); From a75be75c84972f985b3a403180d4a1b5430ab578 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 29 Jan 2026 17:19:58 -0800 Subject: [PATCH 08/12] Added changes based on comments --- src/Core/Resolvers/IQueryBuilder.cs | 2 +- src/Core/Resolvers/MsSqlQueryBuilder.cs | 193 ++++++++++++++++-- .../MsSqlMetadataProvider.cs | 10 +- 3 files changed, 180 insertions(+), 25 deletions(-) diff --git a/src/Core/Resolvers/IQueryBuilder.cs b/src/Core/Resolvers/IQueryBuilder.cs index 75a4620097..f6a6222ddc 100644 --- a/src/Core/Resolvers/IQueryBuilder.cs +++ b/src/Core/Resolvers/IQueryBuilder.cs @@ -91,6 +91,6 @@ public interface IQueryBuilder /// public string QuoteTableNameAsDBConnectionParam(string param); - public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) => throw new NotSupportedException($"{GetType().Name} does not support Autoentities yet."); + public string BuildGetAutoentitiesQuery() => throw new NotSupportedException($"{GetType().Name} does not support Autoentities yet."); } } diff --git a/src/Core/Resolvers/MsSqlQueryBuilder.cs b/src/Core/Resolvers/MsSqlQueryBuilder.cs index 85bbb621ec..7adedc64d4 100644 --- a/src/Core/Resolvers/MsSqlQueryBuilder.cs +++ b/src/Core/Resolvers/MsSqlQueryBuilder.cs @@ -506,7 +506,7 @@ public string BuildStoredProcedureResultDetailsQuery(string databaseObjectName) /// 2. are computed based on other columns, /// are considered as read only columns. The query combines both the types of read-only columns and returns the list. /// - /// Param name of the schema/database. + /// Param name of the schema. /// Param name of the table. /// public string BuildQueryToGetReadOnlyColumns(string schemaParamName, string tableParamName) @@ -569,28 +569,177 @@ protected override string BuildPredicates(SqlQueryStructure structure) /// Pattern for tables that will be included. /// Pattern for tables that will be excluded. /// Pattern for naming the entities. - public string BuildGetAutoentitiesQuery(string include, string exclude, string namePattern) + public string BuildGetAutoentitiesQuery() { - string query = $"DECLARE @include_pattern NVARCHAR(MAX) = '{include}'; DECLARE @exclude_pattern NVARCHAR(MAX) = '{exclude}'; DECLARE @name_pattern NVARCHAR(255) = '{namePattern}'; " + - "DECLARE @exclude_invalid_types BIT = 1; SET NOCOUNT ON; WITH include_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern " + - "FROM STRING_SPLIT(ISNULL(@include_pattern, N''), N',') WHERE LTRIM(RTRIM(value)) <> N'' ), " + - "exclude_patterns AS ( SELECT LTRIM(RTRIM(value)) AS pattern FROM STRING_SPLIT(ISNULL(@exclude_pattern, N''), N',') " + - "WHERE LTRIM(RTRIM(value)) <> N'' ), all_tables AS ( SELECT s.name AS schema_name, t.name AS object_name, s.name + N'.' " + - "+ t.name AS full_name, N'table' AS object_type, t.object_id FROM sys.tables AS t JOIN sys.schemas AS s ON t.schema_id = s.schema_id " + - "WHERE EXISTS ( SELECT 1 FROM sys.key_constraints AS kc WHERE kc.parent_object_id = t.object_id AND kc.type = 'PK' ) ), eligible_tables AS " + - "( SELECT o.schema_name, o.object_name, o.full_name, o.object_type, o.object_id, CASE WHEN so.is_ms_shipped = 1 THEN 1 WHEN o.schema_name IN " + - "(N'sys', N'INFORMATION_SCHEMA') THEN 1 WHEN o.object_name IN ( N'__EFMigrationsHistory', N'__MigrationHistory', N'__FlywayHistory', N'sysdiagrams' ) THEN 1 " + - "WHEN o.object_name LIKE N'service_broker_%' THEN 1 WHEN o.object_name LIKE N'queue_messages_%' THEN 1 WHEN o.object_name LIKE N'MSmerge_%' " + - "THEN 1 WHEN o.object_name LIKE N'MSreplication_%' THEN 1 WHEN o.object_name LIKE N'FileTableUpdates$%' THEN 1 WHEN o.object_name LIKE N'graph_%' THEN 1 WHEN EXISTS " + - "( SELECT 1 FROM sys.tables AS t WHERE t.object_id = o.object_id AND ( t.is_tracked_by_cdc = 1 OR t.temporal_type > 0 OR t.is_filetable = 1 OR t.is_memory_optimized = 1 ) ) " + - "THEN 1 ELSE 0 END AS is_system_object FROM all_tables AS o JOIN sys.objects AS so ON so.object_id = o.object_id ) SELECT a.schema_name AS [schema], a.object_name AS [object], " + - "CASE WHEN LTRIM(RTRIM(ISNULL(@name_pattern, N''))) = N'' THEN a.object_name ELSE REPLACE( REPLACE(@name_pattern, N'{schema}', a.schema_name), N'{object}', a.object_name ) " + - "END AS entity_name, CASE WHEN EXISTS ( SELECT 1 FROM sys.columns AS c JOIN sys.types AS ty ON c.user_type_id = ty.user_type_id WHERE c.object_id = a.object_id AND ty.name IN " + - "( N'geography', N'geometry', N'hierarchyid', N'sql_variant', N'xml', N'rowversion', N'vector' ) ) THEN 1 ELSE 0 END AS contains_invalid_types FROM eligible_tables AS a WHERE " + - "a.is_system_object = 0 AND ( NOT EXISTS (SELECT 1 FROM exclude_patterns) OR NOT EXISTS ( SELECT 1 FROM exclude_patterns AS ep WHERE a.full_name LIKE ep.pattern " + - "COLLATE DATABASE_DEFAULT ESCAPE '\\' ) ) AND ( NOT EXISTS (SELECT 1 FROM include_patterns) OR EXISTS ( SELECT 1 FROM include_patterns AS ip WHERE a.full_name LIKE ip.pattern " + - "COLLATE DATABASE_DEFAULT ESCAPE '\\' ) ) AND ( @exclude_invalid_types = 0 OR NOT EXISTS ( SELECT 1 FROM sys.columns AS c JOIN sys.types AS ty ON c.user_type_id = ty.user_type_id " + - "WHERE c.object_id = a.object_id AND ty.name IN ( N'geography', N'geometry', N'hierarchyid', N'sql_variant', N'xml', N'rowversion', N'vector' ) ) ) ORDER BY a.schema_name, a.object_name;"; + string query = @$" + DECLARE @exclude_invalid_types BIT = 1; + + SET NOCOUNT ON; + + WITH + {IncludeAndExcludeSplitQuery(true)}, + {IncludeAndExcludeSplitQuery(false)}, + all_tables AS + ( + SELECT + s.name AS schema_name, + t.name AS object_name, + s.name + N'.' + t.name AS full_name, + N'table' AS object_type, + t.object_id + FROM sys.tables AS t + JOIN sys.schemas AS s + ON t.schema_id = s.schema_id + WHERE EXISTS + ( + SELECT 1 + FROM sys.key_constraints AS kc + WHERE kc.parent_object_id = t.object_id + AND kc.type = 'PK' + ) + ), + eligible_tables AS + ( + SELECT + o.schema_name, + o.object_name, + o.full_name, + o.object_type, + o.object_id, + CASE + WHEN so.is_ms_shipped = 1 THEN 1 + WHEN o.schema_name IN (N'sys', N'INFORMATION_SCHEMA') THEN 1 + WHEN o.object_name IN + ( + N'__EFMigrationsHistory', + N'__MigrationHistory', + N'__FlywayHistory', + N'sysdiagrams' + ) THEN 1 + WHEN o.object_name LIKE N'service_broker_%' THEN 1 + WHEN o.object_name LIKE N'queue_messages_%' THEN 1 + WHEN o.object_name LIKE N'MSmerge_%' THEN 1 + WHEN o.object_name LIKE N'MSreplication_%' THEN 1 + WHEN o.object_name LIKE N'FileTableUpdates$%' THEN 1 + WHEN o.object_name LIKE N'graph_%' THEN 1 + WHEN EXISTS + ( + SELECT 1 + FROM sys.tables AS t + WHERE t.object_id = o.object_id + AND + ( + t.is_tracked_by_cdc = 1 + OR t.temporal_type > 0 + OR t.is_filetable = 1 + OR t.is_memory_optimized = 1 + ) + ) THEN 1 + ELSE 0 + END AS is_system_object + FROM all_tables AS o + JOIN sys.objects AS so + ON so.object_id = o.object_id + ) + SELECT + a.schema_name AS [schema], + a.object_name AS [object], + CASE + WHEN LTRIM(RTRIM(ISNULL(@name_pattern, N''))) = N'' THEN a.object_name + ELSE REPLACE( + REPLACE(@name_pattern, N'{{schema}}', a.schema_name), + N'{{object}}', a.object_name + ) + END AS entity_name, + CASE + WHEN EXISTS + ( + SELECT 1 + FROM sys.columns AS c + JOIN sys.types AS ty + ON c.user_type_id = ty.user_type_id + WHERE c.object_id = a.object_id + AND ty.name IN + ( + N'geography', + N'geometry', + N'hierarchyid', + N'sql_variant', + N'xml', + N'rowversion', + N'vector' + ) + ) THEN 1 + ELSE 0 + END AS contains_invalid_types + FROM eligible_tables AS a + WHERE + a.is_system_object = 0 + AND + ( + NOT EXISTS (SELECT 1 FROM exclude_patterns) + OR NOT EXISTS + ( + SELECT 1 + FROM exclude_patterns AS ep + WHERE a.full_name LIKE ep.pattern COLLATE DATABASE_DEFAULT ESCAPE '\' + ) + ) + AND + ( + NOT EXISTS (SELECT 1 FROM include_patterns) + OR EXISTS + ( + SELECT 1 + FROM include_patterns AS ip + WHERE a.full_name LIKE ip.pattern COLLATE DATABASE_DEFAULT ESCAPE '\' + ) + ) + AND + ( + @exclude_invalid_types = 0 + OR NOT EXISTS + ( + SELECT 1 + FROM sys.columns AS c + JOIN sys.types AS ty + ON c.user_type_id = ty.user_type_id + WHERE c.object_id = a.object_id + AND ty.name IN + ( + N'geography', + N'geometry', + N'hierarchyid', + N'sql_variant', + N'xml', + N'rowversion', + N'vector' + ) + ) + ) + ORDER BY + a.schema_name, + a.object_name;"; + + return query; + } + + /// + /// Generates a SQL query segment for splitting include or exclude patterns. + /// + /// Indicates whether to generate the include or exclude pattern query. + /// An SQL query segment as a string. + public static string IncludeAndExcludeSplitQuery(bool isInclude) + { + string pattern = isInclude ? "include" : "exclude"; + + string query = $@" + {pattern}_patterns AS + ( + SELECT LTRIM(RTRIM(value)) AS pattern + FROM STRING_SPLIT(ISNULL(@{pattern}_pattern, N''), N',') + WHERE LTRIM(RTRIM(value)) <> N'' + )"; return query; } diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 82ef65ff2b..ebea0c0992 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -311,7 +311,13 @@ protected override async Task GenerateAutoentitiesIntoEntities() string include = string.Join(",", autoentity.Patterns.Include); string exclude = string.Join(",", autoentity.Patterns.Exclude); string namePattern = autoentity.Patterns.Name; - string getAutoentitiesQuery = SqlQueryBuilder.BuildGetAutoentitiesQuery(include, exclude, namePattern); + string getAutoentitiesQuery = SqlQueryBuilder.BuildGetAutoentitiesQuery(); + Dictionary parameters = new() + { + { $"{BaseQueryStructure.PARAM_NAME_PREFIX}include_pattern", new(include, null, SqlDbType.NVarChar) }, + { $"{BaseQueryStructure.PARAM_NAME_PREFIX}exclude_pattern", new(exclude, null, SqlDbType.NVarChar) }, + { $"{BaseQueryStructure.PARAM_NAME_PREFIX}name_pattern", new(namePattern, null, SqlDbType.NVarChar) } + }; _logger.LogInformation("Query for Autoentities is being executed with the following parameters."); _logger.LogInformation($"Autoentities include pattern: {include}"); @@ -320,7 +326,7 @@ protected override async Task GenerateAutoentitiesIntoEntities() JsonArray? resultArray = await QueryExecutor.ExecuteQueryAsync( sqltext: getAutoentitiesQuery, - parameters: null!, + parameters: parameters, dataReaderHandler: QueryExecutor.GetJsonArrayAsync, dataSourceName: _dataSourceName); From e93a8b0eb917361ca283d33f4004e89676be111b Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 29 Jan 2026 17:24:38 -0800 Subject: [PATCH 09/12] Comment out failing section --- .../Services/MetadataProviders/MsSqlMetadataProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index ebea0c0992..fb49e0d33a 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -293,7 +293,8 @@ private bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) } /// - protected override async Task GenerateAutoentitiesIntoEntities() + // TODO: Finish implementation of autoentities generation in task #3052 + /*protected override async Task GenerateAutoentitiesIntoEntities() { RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); if (runtimeConfig.Autoentities is not null) @@ -301,10 +302,9 @@ protected override async Task GenerateAutoentitiesIntoEntities() foreach ((string name, Autoentity autoentity) in runtimeConfig.Autoentities.AutoEntities) { JsonArray? resultArray = await QueryAutoentitiesAsync(autoentity); - // TODO: Finish implementation of autoentities generation in task #3052 } } - } + }*/ public async Task QueryAutoentitiesAsync(Autoentity autoentity) { From 06762d094e1124670549c3b58145a33bf8a31804 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 29 Jan 2026 17:30:33 -0800 Subject: [PATCH 10/12] Fix test failures --- .../MetadataProviders/MsSqlMetadataProvider.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index fb49e0d33a..a22452b2f1 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -294,17 +294,19 @@ private bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) /// // TODO: Finish implementation of autoentities generation in task #3052 - /*protected override async Task GenerateAutoentitiesIntoEntities() + protected override async Task GenerateAutoentitiesIntoEntities() { - RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); + await Task.CompletedTask; // Temporary await to suppress build errors. + + /*RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig(); if (runtimeConfig.Autoentities is not null) { foreach ((string name, Autoentity autoentity) in runtimeConfig.Autoentities.AutoEntities) { JsonArray? resultArray = await QueryAutoentitiesAsync(autoentity); } - } - }*/ + }*/ + } public async Task QueryAutoentitiesAsync(Autoentity autoentity) { From aa3116a2c0b4e098ffa8a8279100aff6f280f729 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 29 Jan 2026 17:48:50 -0800 Subject: [PATCH 11/12] Fix formatting issues --- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 1 - src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index a22452b2f1..4e3be0548a 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -60,7 +60,6 @@ public override Type SqlToCLRType(string sqlType) /// public override async Task PopulateTriggerMetadataForTable(string entityName, string schemaName, string tableName, SourceDefinition sourceDefinition) { - string enumerateEnabledTriggers = SqlQueryBuilder.BuildFetchEnabledTriggersQuery(); Dictionary parameters = new() { diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index 85834e68d3..fc61730d57 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -616,7 +616,7 @@ public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, str }; RuntimeConfigProvider runtimeConfigProvider = TestHelper.GenerateInMemoryRuntimeConfigProvider(configWithAutoentity); SetUpSQLMetadataProvider(runtimeConfigProvider); - + await _sqlMetadataProvider.InitializeAsync(); // Act From 05e3d9bccb026b3a38779bea53c40afc7c2ca644 Mon Sep 17 00:00:00 2001 From: Ruben Cerna Date: Thu, 29 Jan 2026 17:55:15 -0800 Subject: [PATCH 12/12] Fix formatting issues --- src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs index fc61730d57..30969918c0 100644 --- a/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs +++ b/src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs @@ -625,7 +625,7 @@ public async Task CheckAutoentitiesQuery(string[] include, string[] exclude, str // Assert Assert.IsNotNull(resultArray); - foreach(JsonObject resultObject in resultArray) + foreach (JsonObject resultObject in resultArray) { bool includedObjectExists = false; foreach (string included in includeObject)