From f1f136700bd601984686328f5e648c4567efa8b8 Mon Sep 17 00:00:00 2001 From: Arjun Narendra Date: Tue, 28 Jul 2026 23:47:49 -0700 Subject: [PATCH 1/6] Add functionality for groupby+aggregation in GraphQL for PostgreSQL --- src/Core/Resolvers/BaseSqlQueryBuilder.cs | 92 ++++++++++++++++++- src/Core/Resolvers/BaseTSqlQueryBuilder.cs | 85 ----------------- src/Core/Resolvers/PostgresQueryBuilder.cs | 8 +- .../Queries/QueryBuilder.cs | 1 + 4 files changed, 96 insertions(+), 90 deletions(-) diff --git a/src/Core/Resolvers/BaseSqlQueryBuilder.cs b/src/Core/Resolvers/BaseSqlQueryBuilder.cs index a509e9d842..ec4d799a57 100644 --- a/src/Core/Resolvers/BaseSqlQueryBuilder.cs +++ b/src/Core/Resolvers/BaseSqlQueryBuilder.cs @@ -193,6 +193,92 @@ protected virtual string Build(AggregationColumn column, bool useAlias = false) return $"{column.Type.ToString()}({columnName}) {appendAlias}"; } + /// + /// Build the Group By Clause needed to append to the main query + /// + /// Sql query structure to build query on + /// SQL query with group-by clause + protected virtual string BuildGroupBy(SqlQueryStructure structure) + { + // Add GROUP BY clause if there are any group by columns + if (structure.GroupByMetadata.Fields.Any()) + { + return $" GROUP BY {string.Join(", ", structure.GroupByMetadata.Fields.Values.Select(c => Build(c)))}"; + } + + return string.Empty; + } + + /// + /// Build the Having clause needed to append to the main query + /// + /// Sql query structure to build query on + /// SQL query with having clause + protected virtual string BuildHaving(SqlQueryStructure structure) + { + if (structure.GroupByMetadata.Aggregations.Count > 0) + { + List? havingPredicates = structure.GroupByMetadata.Aggregations + .SelectMany(aggregation => aggregation.HavingPredicates ?? new List()) + .ToList(); + + if (havingPredicates.Any()) + { + return $" HAVING {Build(havingPredicates)}"; + } + } + + return string.Empty; + } + + /// + /// Build the aggregation columns needed to append to the main query + /// + /// Sql query structure to build query on + /// SQL query with aggregation columns + protected virtual string BuildAggregationColumns(SqlQueryStructure structure) + { + string aggregations = string.Empty; + if (structure.GroupByMetadata.Aggregations.Count > 0) + { + if (structure.Columns.Any()) + { + aggregations = $",{BuildAggregationColumns(structure.GroupByMetadata)}"; + } + else + { + aggregations = $"{BuildAggregationColumns(structure.GroupByMetadata)}"; + } + } + + return aggregations; + } + + /// + /// Build the aggregation columns needed to append to the main query + /// + /// GroupByMetadata + /// SQL query with aggregation columns + protected virtual string BuildAggregationColumns(GroupByMetadata metadata) + { + return string.Join(", ", metadata.Aggregations.Select(aggregation => Build(aggregation.Column, useAlias: true))); + } + + /// + /// Build the Order By clause needed to append to the main query + /// + /// Sql query structure to build query on + /// SQL query with order-by clause + protected virtual string BuildOrderBy(SqlQueryStructure structure) + { + if (structure.OrderByColumns.Any()) + { + return $" ORDER BY {Build(structure.OrderByColumns)}"; + } + + return string.Empty; + } + /// /// Build orderby column as /// {SourceAlias}.{ColumnName} {direction} @@ -447,7 +533,7 @@ public virtual string BuildForeignKeyInfoQuery(int numberOfParameters) // constraint columns - one inner join for the columns from the 'Referencing table' // and the other join for the columns from the 'Referenced Table'. string foreignKeyQuery = $@" -SELECT +SELECT ReferentialConstraints.CONSTRAINT_NAME {QuoteIdentifier(nameof(ForeignKeyDefinition))}, ReferencingColumnUsage.TABLE_SCHEMA {QuoteIdentifier($"Referencing{nameof(DatabaseObject.SchemaName)}")}, @@ -457,9 +543,9 @@ public virtual string BuildForeignKeyInfoQuery(int numberOfParameters) {QuoteIdentifier($"Referenced{nameof(DatabaseObject.SchemaName)}")}, ReferencedColumnUsage.TABLE_NAME {QuoteIdentifier($"Referenced{nameof(SourceDefinition)}")}, ReferencedColumnUsage.COLUMN_NAME {QuoteIdentifier(nameof(ForeignKeyDefinition.ReferencedColumns))} -FROM +FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ReferentialConstraints - INNER JOIN + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ReferencingColumnUsage ON ReferentialConstraints.CONSTRAINT_CATALOG = ReferencingColumnUsage.CONSTRAINT_CATALOG AND ReferentialConstraints.CONSTRAINT_SCHEMA = ReferencingColumnUsage.CONSTRAINT_SCHEMA diff --git a/src/Core/Resolvers/BaseTSqlQueryBuilder.cs b/src/Core/Resolvers/BaseTSqlQueryBuilder.cs index 1bf7e73ef4..5254f68198 100644 --- a/src/Core/Resolvers/BaseTSqlQueryBuilder.cs +++ b/src/Core/Resolvers/BaseTSqlQueryBuilder.cs @@ -43,90 +43,5 @@ protected virtual string BuildPredicates(SqlQueryStructure structure) Build(structure.PaginationMetadata.PaginationPredicate)); } - /// - /// Build the Group By Clause needed to append to the main query - /// - /// Sql query structure to build query on - /// SQL query with group-by clause - protected virtual string BuildGroupBy(SqlQueryStructure structure) - { - // Add GROUP BY clause if there are any group by columns - if (structure.GroupByMetadata.Fields.Any()) - { - return $" GROUP BY {string.Join(", ", structure.GroupByMetadata.Fields.Values.Select(c => Build(c)))}"; - } - - return string.Empty; - } - - /// - /// Build the Having clause needed to append to the main query - /// - /// Sql query structure to build query on - /// SQL query with having clause - protected virtual string BuildHaving(SqlQueryStructure structure) - { - if (structure.GroupByMetadata.Aggregations.Count > 0) - { - List? havingPredicates = structure.GroupByMetadata.Aggregations - .SelectMany(aggregation => aggregation.HavingPredicates ?? new List()) - .ToList(); - - if (havingPredicates.Any()) - { - return $" HAVING {Build(havingPredicates)}"; - } - } - - return string.Empty; - } - - /// - /// Build the Order By clause needed to append to the main query - /// - /// Sql query structure to build query on - /// SQL query with order-by clause - protected virtual string BuildOrderBy(SqlQueryStructure structure) - { - if (structure.OrderByColumns.Any()) - { - return $" ORDER BY {Build(structure.OrderByColumns)}"; - } - - return string.Empty; - } - - /// - /// Build the aggregation columns needed to append to the main query - /// - /// Sql query structure to build query on - /// SQL query with aggregation columns - protected virtual string BuildAggregationColumns(SqlQueryStructure structure) - { - string aggregations = string.Empty; - if (structure.GroupByMetadata.Aggregations.Count > 0) - { - if (structure.Columns.Any()) - { - aggregations = $",{BuildAggregationColumns(structure.GroupByMetadata)}"; - } - else - { - aggregations = $"{BuildAggregationColumns(structure.GroupByMetadata)}"; - } - } - - return aggregations; - } - - /// - /// Build the aggregation columns needed to append to the main query - /// - /// GroupByMetadata - /// SQL query with aggregation columns - protected virtual string BuildAggregationColumns(GroupByMetadata metadata) - { - return string.Join(", ", metadata.Aggregations.Select(aggregation => Build(aggregation.Column, useAlias: true))); - } } } diff --git a/src/Core/Resolvers/PostgresQueryBuilder.cs b/src/Core/Resolvers/PostgresQueryBuilder.cs index 00ac571149..9a768f82a0 100644 --- a/src/Core/Resolvers/PostgresQueryBuilder.cs +++ b/src/Core/Resolvers/PostgresQueryBuilder.cs @@ -41,10 +41,14 @@ public string Build(SqlQueryStructure structure) Build(structure.Predicates), Build(structure.PaginationMetadata.PaginationPredicate)); - string query = $"SELECT {MakeSelectColumns(structure)}" + string aggregations = BuildAggregationColumns(structure); + + string query = $"SELECT {MakeSelectColumns(structure)}{aggregations}" + $" FROM {fromSql}" + $" WHERE {predicates}" - + $" ORDER BY {Build(structure.OrderByColumns)}" + + BuildGroupBy(structure) + + BuildHaving(structure) + + BuildOrderBy(structure) + $" LIMIT {structure.Limit()}"; string subqueryName = QuoteIdentifier($"subq{structure.Counter.Next()}"); diff --git a/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs b/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs index a2cc63b2c2..d744dc549e 100644 --- a/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs +++ b/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs @@ -36,6 +36,7 @@ public static class QueryBuilder { DatabaseType.MSSQL, DatabaseType.DWSQL, + DatabaseType.PostgreSQL, }; /// From 79d34fc6fbb8ac15bd1eb83be24a196435d96866 Mon Sep 17 00:00:00 2001 From: Arjun Narendra Date: Wed, 29 Jul 2026 10:25:34 -0700 Subject: [PATCH 2/6] Unignore aggregation/group by tests for PostgreSQL --- .../PostgreSqlGraphQLQueryTests.cs | 195 +++++++++--------- 1 file changed, 97 insertions(+), 98 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs index 9136d2f5a6..6999fb33d8 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs @@ -438,21 +438,21 @@ await TestConfigTakesPrecedenceForRelationshipFieldsOverDB( /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForAggregationsWithAliases() { - string msSqlQuery = @" - SELECT - MAX(categoryid) AS max, - MAX(price) AS max_price, - MIN(price) AS min_price, - AVG(price) AS avg_price, - SUM(price) AS sum_price - FROM stocks_price - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + MAX(categoryid) AS max, + MAX(price) AS max_price, + MIN(price) AS min_price, + AVG(price) AS avg_price, + SUM(price) AS sum_price + FROM stocks_price + ) AS table0"; // Execute the test for the SQL query - await TestSupportForAggregationsWithAliases(msSqlQuery); + await TestSupportForAggregationsWithAliases(postgresQuery); } /// @@ -460,23 +460,23 @@ FROM stocks_price /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForGroupByAggregationsWithAliases() { - string msSqlQuery = @" - SELECT - MAX(categoryid) AS max, - MAX(price) AS max_price, - MIN(price) AS min_price, - AVG(price) AS avg_price, - SUM(price) AS sum_price, - COUNT(categoryid) AS count - FROM stocks_price - GROUP BY categoryid - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + MAX(categoryid) AS max, + MAX(price) AS max_price, + MIN(price) AS min_price, + AVG(price) AS avg_price, + SUM(price) AS sum_price, + COUNT(categoryid) AS count + FROM stocks_price + GROUP BY categoryid + ) AS table0"; // Execute the test for the SQL query - await TestSupportForGroupByAggregationsWithAliases(msSqlQuery); + await TestSupportForGroupByAggregationsWithAliases(postgresQuery); } /// @@ -484,17 +484,17 @@ GROUP BY categoryid /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForMinAggregation() { - string msSqlQuery = @" - SELECT - MIN(price) AS min_price - FROM stocks_price - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + MIN(price) AS min_price + FROM stocks_price + ) AS table0"; // Execute the test for the SQL query - await TestSupportForMinAggregation(msSqlQuery); + await TestSupportForMinAggregation(postgresQuery); } /// @@ -502,17 +502,17 @@ FROM stocks_price /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForMaxAggregation() { - string msSqlQuery = @" - SELECT - MAX(price) AS max_price - FROM stocks_price - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + MAX(price) AS max_price + FROM stocks_price + ) AS table0"; // Execute the test for the SQL query - await TestSupportForMaxAggregation(msSqlQuery); + await TestSupportForMaxAggregation(postgresQuery); } /// @@ -520,17 +520,17 @@ FROM stocks_price /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForAvgAggregation() { - string msSqlQuery = @" - SELECT - AVG(price) AS avg_price - FROM stocks_price - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + AVG(price) AS avg_price + FROM stocks_price + ) AS table0"; // Execute the test for the SQL query - await TestSupportForAvgAggregation(msSqlQuery); + await TestSupportForAvgAggregation(postgresQuery); } /// @@ -538,17 +538,17 @@ FROM stocks_price /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForSumAggregation() { - string msSqlQuery = @" - SELECT - SUM(price) AS sum_price - FROM stocks_price - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + SUM(price) AS sum_price + FROM stocks_price + ) AS table0"; // Execute the test for the SQL query - await TestSupportForSumAggregation(msSqlQuery); + await TestSupportForSumAggregation(postgresQuery); } /// @@ -556,17 +556,17 @@ FROM stocks_price /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForCountAggregation() { - string msSqlQuery = @" - SELECT - COUNT(categoryid) AS count_categoryid - FROM stocks_price - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + COUNT(categoryid) AS count_categoryid + FROM stocks_price + ) AS table0"; // Execute the test for the SQL query - await TestSupportForCountAggregation(msSqlQuery); + await TestSupportForCountAggregation(postgresQuery); } /// @@ -574,18 +574,18 @@ FROM stocks_price /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForHavingAggregation() { - string msSqlQuery = @" - SELECT - SUM(price) AS sum_price - FROM stocks_price - HAVING SUM(price) > 50 - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + MAX(id) AS max + FROM publishers + HAVING MAX(id) > 2346 + ) AS table0"; // Execute the test for the SQL query - await TestSupportForHavingAggregation(msSqlQuery); + await TestSupportForHavingAggregation(postgresQuery); } /// @@ -593,19 +593,19 @@ HAVING SUM(price) > 50 /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForGroupByHavingAggregation() { - string msSqlQuery = @" - SELECT - SUM(price) AS sum_price - FROM stocks_price - GROUP BY categoryid, pieceid - HAVING SUM(price) > 50 - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + SUM(price) AS sum_price + FROM stocks_price + GROUP BY categoryid, pieceid + HAVING SUM(price) > 50 + ) AS table0"; // Execute the test for the SQL query - await TestSupportForGroupByHavingAggregation(msSqlQuery); + await TestSupportForGroupByHavingAggregation(postgresQuery); } /// @@ -613,22 +613,22 @@ HAVING SUM(price) > 50 /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForGroupByHavingFieldsAggregation() { - string msSqlQuery = @" - SELECT - categoryid, - pieceid, - SUM(price) AS sum_price, - COUNT(pieceid) AS count_piece - FROM stocks_price - GROUP BY categoryid, pieceid - HAVING SUM(price) > 50 AND COUNT(pieceid) <= 100 - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + categoryid, + pieceid, + SUM(price) AS sum_price, + COUNT(pieceid) AS count_piece + FROM stocks_price + GROUP BY categoryid, pieceid + HAVING SUM(price) > 50 AND COUNT(pieceid) <= 100 + ) AS table0"; // Execute the test for the SQL query - await TestSupportForGroupByHavingFieldsAggregation(msSqlQuery); + await TestSupportForGroupByHavingFieldsAggregation(postgresQuery); } /// @@ -636,23 +636,22 @@ HAVING SUM(price) > 50 AND COUNT(pieceid) <= 100 /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. /// [TestMethod] - [Ignore] public async Task TestSupportForGroupByNoAggregation() { - string msSqlQuery = @" - SELECT - categoryid, - pieceid - FROM stocks_price - GROUP BY categoryid, pieceid - FOR JSON PATH, INCLUDE_NULL_VALUES"; + string postgresQuery = @" + SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT + categoryid, + pieceid + FROM stocks_price + GROUP BY categoryid, pieceid + ) AS table0"; // Execute the test for the SQL query - await TestSupportForGroupByNoAggregation(msSqlQuery); + await TestSupportForGroupByNoAggregation(postgresQuery); } [TestMethod] - [Ignore] public override async Task TestNoAggregationOptionsForTableWithoutNumericFields() { await base.TestNoAggregationOptionsForTableWithoutNumericFields(); From a322bc100a3040355d134a3f52bf3c311893db4b Mon Sep 17 00:00:00 2001 From: Arjun Narendra Date: Thu, 30 Jul 2026 13:14:01 -0700 Subject: [PATCH 3/6] Modify aggregation/groupby tests to account for differences between MsSQL and PostgreSQL behavior --- .../GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs index 6999fb33d8..abafea60db 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs @@ -458,6 +458,9 @@ FROM stocks_price /// /// Test to check GraphQL support for aggregations with aliases and groupby. /// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format. + /// Note: an explicit ORDER BY categoryid is required here because DAB's actual generated query selects + /// categoryid as an extra column, which changes Postgres's query plan (and therefore GROUP BY row order) + /// compared to a query that only selects the aggregates. /// [TestMethod] public async Task TestSupportForGroupByAggregationsWithAliases() @@ -473,6 +476,7 @@ SELECT json_agg(to_jsonb(table0)) FROM ( COUNT(categoryid) AS count FROM stocks_price GROUP BY categoryid + ORDER BY categoryid ) AS table0"; // Execute the test for the SQL query @@ -576,8 +580,9 @@ FROM stocks_price [TestMethod] public async Task TestSupportForHavingAggregation() { + // HAVING may exclude the only row; json_agg over zero rows returns NULL, not []. string postgresQuery = @" - SELECT json_agg(to_jsonb(table0)) FROM ( + SELECT COALESCE(json_agg(to_jsonb(table0)), '[]') FROM ( SELECT MAX(id) AS max FROM publishers From e72d3d3d16f2feb0f959d1ed81ab129d5f2bd6b9 Mon Sep 17 00:00:00 2001 From: Arjun Narendra Date: Thu, 30 Jul 2026 13:14:43 -0700 Subject: [PATCH 4/6] Add a date_only_table to the PostgreSQL test schema --- .../DatabaseSchema-PostgreSql.sql | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Service.Tests/DatabaseSchema-PostgreSql.sql b/src/Service.Tests/DatabaseSchema-PostgreSql.sql index bd36c551b0..77edd4a823 100644 --- a/src/Service.Tests/DatabaseSchema-PostgreSql.sql +++ b/src/Service.Tests/DatabaseSchema-PostgreSql.sql @@ -41,6 +41,7 @@ DROP TABLE IF EXISTS default_with_function_table; DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS user_profiles; DROP TABLE IF EXISTS dimaccount; +DROP TABLE IF EXISTS date_only_table; DROP FUNCTION IF EXISTS insertCompositeView; DROP SCHEMA IF EXISTS foo; @@ -361,14 +362,14 @@ INSERT INTO bookmarks (id, bkname) SELECT value, CONCAT('Test Item #' , value) -FROM +FROM GENERATE_SERIES(1, 10000, 1) as value; INSERT INTO mappedbookmarks (id, bkname) SELECT value, CONCAT('Test Item #' , value) -FROM +FROM GENERATE_SERIES(1, 10000, 1) as value; INSERT INTO GQLmappings(__column1, __column2, column3) VALUES (1, 'Incompatible GraphQL Name', 'Compatible GraphQL Name'); @@ -378,7 +379,7 @@ INSERT INTO GQLmappings(__column1, __column2, column3) VALUES (5, 'Filtered Reco INSERT INTO publishers(id, name) VALUES (1234, 'Big Company'), (2345, 'Small Town Publisher'), (2323, 'TBD Publishing One'), (2324, 'TBD Publishing Two Ltd'), (1940, 'Policy Publisher 01'), (1941, 'Policy Publisher 02'), (1156, 'The First Publisher'); INSERT INTO clubs(id, name) VALUES (1111, 'Manchester United'), (1112, 'FC Barcelona'), (1113, 'Real Madrid'); INSERT INTO players(id, name, current_club_id, new_club_id) - VALUES + VALUES (1, 'Cristiano Ronaldo', 1113, 1111), (2, 'Leonel Messi', 1112, 1113); INSERT INTO authors(id, name, birthdate) VALUES (123, 'Jelte', '2001-01-01'), (124, 'Aniruddh', '2002-02-02'), (125, 'Aniruddh', '2001-01-01'), (126, 'Aaron', '2001-01-01'); @@ -487,3 +488,14 @@ $$ LANGUAGE plpgsql; CREATE TRIGGER insertCompositeViewTrigger INSTEAD OF INSERT ON books_publishers_view_composite_insertable FOR EACH ROW EXECUTE PROCEDURE insertCompositeView(); + +CREATE TABLE date_only_table ( + event_date date NOT NULL, + event_time time NOT NULL, + event_timestamp timestamp NOT NULL +); + +INSERT INTO date_only_table(event_date, event_time, event_timestamp) +VALUES ('2023-01-01', '08:30:00', '2023-01-01 08:30:00'), + ('2023-02-15', '12:45:00', '2023-02-15 12:45:00'), + ('2023-03-30', '17:15:00', '2023-03-30 17:15:00'); From ae989b603123ae511360cc548eaed0bb74959b00 Mon Sep 17 00:00:00 2001 From: Arjun Narendra Date: Thu, 30 Jul 2026 23:07:41 -0700 Subject: [PATCH 5/6] Remove unnecessary using directive --- src/Core/Resolvers/BaseTSqlQueryBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Core/Resolvers/BaseTSqlQueryBuilder.cs b/src/Core/Resolvers/BaseTSqlQueryBuilder.cs index 5254f68198..7d0d2ef4b7 100644 --- a/src/Core/Resolvers/BaseTSqlQueryBuilder.cs +++ b/src/Core/Resolvers/BaseTSqlQueryBuilder.cs @@ -1,5 +1,4 @@ using Azure.DataApiBuilder.Config.ObjectModel; -using Azure.DataApiBuilder.Core.Models; namespace Azure.DataApiBuilder.Core.Resolvers { From 707b5c6f5a064bef5ce074eb1d24462fc8a327a1 Mon Sep 17 00:00:00 2001 From: Arjun Narendra Date: Thu, 30 Jul 2026 23:21:04 -0700 Subject: [PATCH 6/6] Add DateOnlyTable entity to PostgreSQL test config --- src/Service.Tests/dab-config.PostgreSql.json | 31 +++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Service.Tests/dab-config.PostgreSql.json b/src/Service.Tests/dab-config.PostgreSql.json index 86bed1a352..fd4a34084a 100644 --- a/src/Service.Tests/dab-config.PostgreSql.json +++ b/src/Service.Tests/dab-config.PostgreSql.json @@ -2337,6 +2337,35 @@ } ] }, + "DateOnlyTable": { + "source": { + "object": "date_only_table", + "type": "table", + "key-fields": [ + "event_date" + ] + }, + "graphql": { + "enabled": true, + "type": { + "singular": "DateOnlyTable", + "plural": "DateOnlyTables" + } + }, + "rest": { + "enabled": true + }, + "permissions": [ + { + "role": "anonymous", + "actions": [ + { + "action": "*" + } + ] + } + ] + }, "GQLmappings": { "source": { "object": "gqlmappings", @@ -2891,4 +2920,4 @@ } } } -} \ No newline at end of file +}