Skip to content

Port PostgreSQL GraphQL groupby/aggregation + related features to release/2.0 - #3767

Merged
naxing123 merged 6 commits into
release/2.0from
port/pg-graphql-features
Aug 12, 2026
Merged

Port PostgreSQL GraphQL groupby/aggregation + related features to release/2.0#3767
naxing123 merged 6 commits into
release/2.0from
port/pg-graphql-features

Conversation

@naxing123

Copy link
Copy Markdown
Contributor

Ports the PostgreSQL GraphQL groupby/aggregation and related feature commits from main (released in v2.1.0-rc) to release/2.0 via cherry-pick.

Commits ported (chronological)

PR Title
#3450 Fix GraphQL aggregation features disabled when runtime.graphql config section is absent
#3694 Database policy support for PUT/PATCH operations - PostgreSQL
#3728 Add support for DateTime filters in PostgreSQL
#3750 Fix column mapping in GroupBy and aggregation queries
#3741 Add groupby/aggregation support for PostgreSQL in GraphQL
#3753 Enhance test coverage for GraphQL queries by adding orderBy clause

Notes

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Ports PostgreSQL GraphQL groupBy/aggregation support (plus related config/runtime fixes), PostgreSQL PUT/PATCH database policy behavior, and PostgreSQL DateTime filter handling from main back onto release/2.0, with corresponding updates to core query generation/execution and the test suite.

Changes:

  • Enable/extend GraphQL aggregation (groupBy + aggregate functions) support to PostgreSQL, including deterministic ordering for groupBy results and correct mapped-column handling.
  • Add PostgreSQL support for database policies on PUT/PATCH upsert paths (distinguishing “row missing” vs “policy blocked” via multi-result-set handling).
  • Add PostgreSQL-specific parameter type casting for date/time filters and expand tests/snapshots/config/schema to cover the new behaviors.

Reviewed changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Service.Tests/UnitTests/ConfigValidationUnitTests.cs Updates config validation expectations for PostgreSQL create policy support and minor comment cleanup.
src/Service.Tests/SqlTests/SqlTestBase.cs Fixes REST API wording and “Postgres” naming in doc comments.
src/Service.Tests/SqlTests/RestApiTests/Put/PostgreSqlPutApiTests.cs Enables PUT database policy insert coverage for PostgreSQL (removes ignored stubs; adds expected SQL).
src/Service.Tests/SqlTests/RestApiTests/Patch/PostgreSqlPatchApiTests.cs Enables PATCH database policy insert coverage for PostgreSQL (removes ignored stubs; adds expected SQL).
src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs Normalizes REST API phrasing and trims trailing whitespace in comments.
src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs Adjusts PostgreSQL JSON aggregation handling (COALESCE) and enables DateTime-related coverage by removing a skip.
src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs Enables/extends PostgreSQL aggregation & groupBy GraphQL tests and adds mapped-column groupBy/aggregation scenarios.
src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLQueryTests.cs Adds mapped-column regression tests for groupBy/aggregations and makes groupBy ordering deterministic.
src/Service.Tests/SqlTests/GraphQLQueryTests/DwSqlGraphQLQueryTests.cs Adds mapped-column groupBy regression tests and formatting/order determinism improvements.
src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForPostgreSql.verified.txt Updates runtime config snapshot for PostgreSQL database policies and new DateOnlyTable entity.
src/Service.Tests/GraphQLBuilder/QueryBuilderTests.cs Adds unit coverage for aggregation enablement (including PostgreSQL) and groupBy field presence/absence.
src/Service.Tests/DatabaseSchema-PostgreSql.sql Adds date_only_table seed data and small formatting fixes.
src/Service.Tests/DatabaseSchema-MsSql.sql Adds SET QUOTED_IDENTIFIER ON; and formatting fixes for seed scripts.
src/Service.Tests/dab-config.PostgreSql.json Updates PostgreSQL config to include create database policy and adds DateOnlyTable entity.
src/Service.Tests/Configuration/RuntimeConfigLoaderTests.cs Adds unit tests for EnableAggregation defaulting behavior and explicit config handling.
src/Service.Tests/Configuration/ConfigurationTests.cs Updates assertions to match improved metadata-provider error messages.
src/Service.GraphQLBuilder/Sql/SchemaConverter.cs Clarifies authorize-directive parameter docs.
src/Service.GraphQLBuilder/Queries/QueryBuilder.cs Enables aggregation schema features for PostgreSQL via AggregationEnabledDatabaseTypes.
src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs Refines supported datetime type modeling/comments and datetimeoffset handling.
src/Core/Services/MetadataProviders/SqlMetadataProvider.cs Improves exception messages (“database object … not inferred”) and clarifies schema-fill doc comment.
src/Core/Services/GraphQLSchemaCreator.cs Fixes aggregation flag hot-reload, refactors role lookup loop, and uses safer DocumentNode definition merging.
src/Core/Resolvers/SqlPaginationUtil.cs Fixes groupBy response shaping to distinguish fields vs aggregations by aggregation aliases.
src/Core/Resolvers/SqlMutationEngine.cs Refines PUT/PATCH mutation result handling and error pathways for policy/not-found scenarios.
src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs Ensures groupBy selects use backing column names with exposed labels and adds deterministic order defaulting for groupBy.
src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs Adds PostgreSQL-only parameter type coercion (notably DateTime handling for timestamp comparisons).
src/Core/Resolvers/PostgresQueryBuilder.cs Adds PostgreSQL groupBy/aggregation query generation and extends upsert/insert generation for create/update policy semantics.
src/Core/Resolvers/PostgreSqlExecutor.cs Implements multi-result-set handling for PostgreSQL upsert to return correct 403/404 outcomes for policies vs missing rows.
src/Core/Resolvers/BaseTSqlQueryBuilder.cs Removes duplicated groupBy/having/order/aggregation helpers (now centralized).
src/Core/Resolvers/BaseSqlQueryBuilder.cs Centralizes shared groupBy/having/order/aggregation SQL builders (used across engines).
src/Core/Configurations/RuntimeConfigValidator.cs Adds PostgreSQL to the set of database types supporting create policies.
src/Config/ObjectModel/RuntimeConfig.cs Fixes EnableAggregation default logic (enabled when runtime/graphql sections are absent) and updates docs.
schemas/dab.draft.schema.json Adds runtime.graphql.enable-aggregation to schema and documents it.
config-generators/postgresql-commands.txt Updates generator commands to reflect separate create/update policies and adds DateOnlyTable.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread schemas/dab.draft.schema.json
@naxing123
naxing123 enabled auto-merge (squash) August 12, 2026 02:14
Copilot AI and others added 6 commits August 11, 2026 19:28
… section is absent (#3450)

## Why make this change?

GraphQL aggregation features (`groupBy`, `sum`, `avg`, `min`, `max`,
`count`) were silently disabled for users whose config lacked an
explicit `runtime.graphql` section, even though `EnableAggregation`
defaults to `true`.

## What is this change?

**Bug fix: `RuntimeConfig.EnableAggregation` logic inversion**

The property used `&&` logic, returning `false` when `Runtime` or
`Runtime.GraphQL` was `null`. This is inconsistent with every analogous
property (e.g. `IsGraphQLEnabled`) which use `||` logic to treat absence
as "use default (enabled)":

```csharp
// Before (broken): returns false when runtime.graphql section is absent
public bool EnableAggregation =>
    Runtime is not null &&
    Runtime.GraphQL is not null &&
    Runtime.GraphQL.EnableAggregation;

// After (fixed): returns true when section is absent, consistent with IsGraphQLEnabled
public bool EnableAggregation =>
    Runtime is null ||
    Runtime.GraphQL is null ||
    Runtime.GraphQL.EnableAggregation;
```

**Additional fixes:**
- `GraphQLSchemaCreator.OnConfigChanged` now updates
`_isAggregationEnabled` on hot-reload (was omitted, causing stale state)
- `schemas/dab.draft.schema.json`: added `enable-aggregation` to
`runtime.graphql` properties — it was missing despite
`additionalProperties: false`, meaning any config explicitly setting the
flag would fail schema validation

**Test refactoring (per code review feedback):**
- Extracted a `LoadConfig` private helper in `RuntimeConfigLoaderTests`
to eliminate repeated mock-filesystem setup across all
`EnableAggregation` tests
- Merged `EnableAggregation_WhenExplicitlyDisabled_ReturnsFalse` and
`EnableAggregation_WhenExplicitlyEnabled_ReturnsTrue` into a single
parameterized `[DataTestMethod]`
(`EnableAggregation_WhenExplicitlySet_ReturnsConfiguredValue`) using
`[DataRow(true)]` and `[DataRow(false)]`
- Merged
`Build_WithMssqlAndAggregationEnabled_AddsGroupByToConnectionType` and
`Build_WithPostgreSqlAndAggregationEnabled_DoesNotAddGroupByToConnectionType`
into a single parameterized `[DataTestMethod]`
(`Build_WithAggregationEnabled_GroupByPresenceMatchesDatabaseSupport`)
covering MSSQL, DWSQL (groupBy present), PostgreSQL, and MySQL (groupBy
absent)

## How was this tested?

- [ ] Integration Tests
- [x] Unit Tests
- `RuntimeConfigLoaderTests`: `EnableAggregation` defaults to `true`
when `runtime` or `runtime.graphql` sections are absent; a single
parameterized test
(`EnableAggregation_WhenExplicitlySet_ReturnsConfiguredValue`) covers
both `true` and `false` explicit values using `[DataRow]`. A shared
`LoadConfig` helper eliminates repeated mock-filesystem setup.
- `QueryBuilderTests`: a single parameterized test
(`Build_WithAggregationEnabled_GroupByPresenceMatchesDatabaseSupport`)
covers MSSQL and DWSQL (groupBy present) and PostgreSQL and MySQL
(groupBy absent), plus a test confirming groupBy is omitted when
aggregation is disabled.

## Sample Request(s)

With a config that has no explicit `runtime.graphql` section, the
following now works as expected for MSSQL/DWSQL entities:

```graphql
{
  books {
    groupBy(fields: [price]) {
      fields { price }
      aggregations {
        count
        sum(field: price)
        avg(field: price)
        max(field: price)
        min(field: price)
      }
    }
  }
}
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com>
Co-authored-by: Aniruddh Munde <anmunde@microsoft.com>
Co-authored-by: Souvik Ghosh <souvikofficial04@gmail.com>
(cherry picked from commit 9391685)
## Why make this change?
As per the behaviour expected from PUT/PATCH operations with database
policies discussed in #1430, implement db policy support for PostgreSQL
to fix #1372.

## What is this change?
1. Prior to this change, there was only one database policy for each
operation. Since now database policies will be supported for both insert
(or create)/update actions via PUT/PATCH operations, these 2 operations
can have 2 database policies defined for them, one for each action.

2. The query generated by
`PostgresQueryBuilder.Build(SqlUpsertQueryStructure structure)` is
modified to accommodate create/update policies while also keeping intact
the normal upsert behavior expected (try update, then insert).

3. The method `IQueryExecutor.GetMultipleResultSetsIfAnyAsync` has been
provided another implementation specific to PostgreSql in
`PostgreSqlExecutor`. The `DbDataReader` instance for the query being
executed for the PUT/PATCH operation will always contain two result
sets.

4. Different scenarios are added to the method
`PostgreSqlExecutor.GetMultipleResultSetsIfAnyAsync` to throw
appropriate exceptions (Forbidden/Authorization failure - 403 and
NotFound - 404). Appropriate comments are added within the code to
demonstrate each case.

## How was this tested?
Integration Tests - Done

---------

Co-authored-by: RubenCerna2079 <32799214+RubenCerna2079@users.noreply.github.com>
(cherry picked from commit 57a195c)
## Why make this change?

This pull request is to address
#3094, which is a bug
that happens because certain date/time SQL parameters are not being
parsed to their appropriate .NET type (`DateTime` or `DateTimeOffset`)
and are instead being treated as raw text. This is leading to type
conversion issues when executing the underlying SQL query.

## What is this change?

I have provided an overridden implementation of `MakeDbConnectionParam`
in `BaseSqlQueryStructure.cs`. This method does some extra
pre-processing for PostgreSQL parameters specifically by parsing each
parameter into its appropriate system type before calling the base class
implementation of `MakeDbConnectionParam` in `BaseQueryStructure.cs`.
The system type of each parameter is retrieved from metadata of the
underlying column that this parameter corresponds to via the method
`GetColumnSystemType`. This change fixes the relevant issue because now
parameters which correspond to an underlying `datetime` column in the
schema are cast to their appropriate .NET type instead of falling
through as text before building the final SQL query. Furthermore, this
change is limited in scope to apply this parameter casting only when the
underlying database is PostgreSQL. This ensures we do not see unexpected
behavior with other database types where this sort of parameter casting
may be unnecessary and/or erroneous.

My original thought was to modify the method
`ExtractValueFromIValueNode` in `ExecutionHelper.cs` to explicitly
handle an `IValueNode` with a GraphQL scalar type of
`SupportedHotChocolateTypes.DATETIME_TYPE` by parsing the `IValueNode`
as a `DateTime` system type before being returned. For all practical
purposes, this has the same desired effect as parsing the parameter in
`MakeDbConnectionParam`, but the call to `ExtractValueFromIValueNode`
happens upstream of the call to `MakeDbConnectionParam`.

I believe that casting the parameters to their appropriate .NET type in
`MakeDbConnectionParam` is appropriate because the method literally
builds each parameter for the SQL query and the .NET type casting can be
considered a part of this parameter building process. I do not think
.NET type casting is as appropriate in `ExtractValueFromIValueNode`
because this method serves the broader purpose of extracting a value
from a GraphQL `IValueNode`, where the `IValueNode` could contain a
scalar but it could also contain a variable that needs to be resolved
recursively. For parameters that need to be cast to a .NET type, the
control flow will go from `ExtractValueFromIValueNode` to eventually
`MakeDbConnectionParam`, but again, `MakeDbConnectionParam` serves the
sole purpose of building the parameter while
`ExtractValueFromIValueNode` serves a broader purpose.

## How was this tested?

- [ X] Integration Tests

## Sample Request(s)

With query:

```
query {
  assignments(filter: { assignment_due_date: { gte: "2026-03-23T00:00:00.000Z" } }) {
    items {
      assignment_id
      assignment_name
      assignment_due_date
    }
  }
}
```

Before:

```
SELECT * FROM public."Assignments"
WHERE "Assignments"."assignment_due_date" >= '2026-03-23T00:00:00.000Z'::text;
```

After:

```
SELECT * FROM public."Assignments"
WHERE "Assignments"."assignment_due_date" >= '2026-03-23T00:00:00.000Z';
```

(cherry picked from commit eb3a58b)
#3750)

## Why make this change?
There is a bug when running aggregation queries, wherein if a mapping
backed column is present, the aggregation fails since it references the
surfaced column and not the backed column.

This pull request improves how group-by queries with mapped (aliased)
columns are handled in SQL generation and adds a regression test to
ensure correct behavior. The main focus is to ensure that SQL queries
reference the backing (database) column names in the SELECT and GROUP BY
clauses, while projecting the correct exposed (mapped) names in the
results.

## What is this change?

* Updated `ProcessGroupByField` and `ProcessGroupByFieldSelections` in
`SqlQueryStructure.cs` to ensure that the backing (database) column name
is used in the SQL query, while the exposed (mapped) field name is used
as the label in the results. This resolves previous issues where the
SELECT clause referenced the exposed name instead of the actual database
column.
[[1]](diffhunk://#diff-4b3df270873038f36ec2f627091120b7aafa271c0e2a2d257380f90602f5a298L907-R908)
[[2]](diffhunk://#diff-4b3df270873038f36ec2f627091120b7aafa271c0e2a2d257380f90602f5a298L956-R956)

## How was this tested?

- [x] Integration Tests
- [X] Unit Tests

* Added a new test method
`TestSupportForGroupByAggregationWithMappedColumns` in
`MsSqlGraphQLQueryTests.cs` to verify that group-by fields and
aggregations both resolve to the correct backing columns, and that
results are projected under the mapped names. This prevents regressions
on mapped column handling in group-by queries.
* Added missing import for
`Azure.DataApiBuilder.Service.GraphQLBuilder.Queries` in
`MsSqlGraphQLQueryTests.cs` to support the new test.

---------

Co-authored-by: ARPIT GUPTA <guptaar@microsoft.com>
(cherry picked from commit 032eff9)
## Why make this change?

Closes #2850. This PR adds support for group by and aggregation in
PostgreSQL in GraphQL queries.

## What is this change?

Groupby and aggregation types/enums were added to the GraphQL schema in
PostgreSQL, but they essentially were orphaned because the user couldn't
utilize them in their GraphQL query. This PR hooks up the
groupby/aggregation schema types/enums into a GraphQL input query.

## How was this tested?

- [X] Integration Tests

## Sample Request(s)

```
query {
  stocks_prices {
    groupBy(fields: [categoryid, pieceid]) {
      fields {
        categoryid
        pieceid
      }
      aggregations {
        total: sum(field: price)
        count: count(field: pieceid, having: { gt: 5 })
      }
    }
  }
}
```

Previously, a query such as the one above would fail because the
`groupBy` field would not have been attached to the `stocks_prices`
object type. This PR adds the `groupBy` field to the relevant list query
entity types. The `groupBy` field serves as a window to the rest of the
groupBy/aggregation query.

---------

Co-authored-by: Anusha Kolan <anushakolan10@gmail.com>
(cherry picked from commit 7ad4f49)
#3753)

## Why make this change?

In a previously merged PR, there were comments about some gaps in
testing scenarios, which is addressed as part of this change.

## What is this change?

This pull request adds comprehensive regression tests for groupBy
queries involving mapped (aliased) columns in both MS SQL and DWSQL
environments, and ensures deterministic query results by explicitly
specifying `orderBy` in all relevant test GraphQL queries.

**Test Coverage Improvements:**

* Added two new end-to-end regression tests to
`DwSqlGraphQLQueryTests.cs`:
* One test verifies that grouping and projecting only a mapped column
works as expected, ensuring the backing column is used for grouping and
the alias is used in the response.
* Another test covers grouping, selecting, aggregating, applying HAVING
filters, and ordering on a mapped column, confirming consistent use of
the backing column in all SQL clauses while projecting the mapped names
in the response.

## How was this tested?

- [X] Integration Tests
- [X] Unit Tests

Co-authored-by: ARPIT GUPTA <guptaar@microsoft.com>
(cherry picked from commit 3cfb270)
@naxing123
naxing123 force-pushed the port/pg-graphql-features branch from 030f6bf to fa2765d Compare August 12, 2026 02:30
@naxing123
naxing123 merged commit 53c70a7 into release/2.0 Aug 12, 2026
12 checks passed
@naxing123
naxing123 deleted the port/pg-graphql-features branch August 12, 2026 03:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants