Skip to content

[backport release/2.0] fix: don't enforce many-to-one/one-to-many relationships for DWSQL (#3770) - #3775

Merged
Ritika Dhawan (ritikadhawan) merged 2 commits into
release/2.0from
dev/ritikadhawan/backport-3770-to-2.0
Aug 18, 2026
Merged

[backport release/2.0] fix: don't enforce many-to-one/one-to-many relationships for DWSQL (#3770)#3775
Ritika Dhawan (ritikadhawan) merged 2 commits into
release/2.0from
dev/ritikadhawan/backport-3770-to-2.0

Conversation

@ritikadhawan

Copy link
Copy Markdown
Contributor

What's the problem?

When Data API builder serves a Fabric Warehouse (DWSQL) database, a GraphQL query that follows a relationship between two tables could fail with this error:

"Cannot return null for non-nullable field." (HC0018)

...even when the query was perfectly valid and the data was fine. Instead of returning the row, the whole query blew up.

Why does it happen?

Most databases guarantee that if a row points to a "parent" row (a foreign key), that parent actually exists. DAB relies on that guarantee: if a foreign-key column is marked "required" (NOT NULL), DAB assumes the related object is always there, and tells GraphQL "this related field will never be null."

Fabric Warehouse is different — it does not enforce foreign keys. So a child row can happily point at a parent that doesn't exist (an "orphaned" row). When GraphQL asks for that missing parent, DAB has nothing to return, but it already promised the field would never be null. GraphQL sees a broken promise and throws HC0018.

Example: an Enrollment row has a studentId that no Student matches. Asking for enrollment.student returns nothing, and the query fails.

What does this change do?

For Fabric Warehouse (DWSQL) only, DAB no longer assumes related rows always exist. It marks the related fields on many-to-one and one-to-many relationships as nullable. That way, when a related row is genuinely missing, the query simply returns null for that field instead of failing the entire request.

Behavior for all other databases (SQL Server, PostgreSQL, MySQL) is unchanged, since they do enforce foreign keys.

How was it verified?

  • Added unit tests covering both DWSQL (field is now nullable) and SQL Server (field stays non-nullable) for both relationship directions.
  • Manually validated end-to-end against a Fabric Warehouse: the query that previously failed now returns the row with student: null.

Before

Screenshot 2026-08-12 143540

After

Screenshot 2026-08-12 141456

@ritikadhawan Ritika Dhawan (ritikadhawan) changed the title [backport release/2.0] fix: don't enforce many-to-one/one-to-many relationships for DWSQL (#3770) [backport release/2.0] fix: don't enforce many-to-one/one-to-many relationships for DWSQL Aug 17, 2026
@ritikadhawan
Ritika Dhawan (ritikadhawan) marked this pull request as ready for review August 17, 2026 18:08
Copilot AI lite review requested due to automatic review settings August 17, 2026 18:08
@ritikadhawan Ritika Dhawan (ritikadhawan) changed the title [backport release/2.0] fix: don't enforce many-to-one/one-to-many relationships for DWSQL [backport release/2.0] fix: don't enforce many-to-one/one-to-many relationships for DWSQL (#3770) Aug 17, 2026

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

This backport fixes an incorrect GraphQL schema nullability assumption for Fabric Warehouse (DWSQL): because DWSQL does not enforce foreign keys, relationship fields must not be marked non-nullable based solely on NOT NULL FK metadata, otherwise valid queries can fail with HC0018 (“Cannot return null for non-nullable field.”).

Changes:

  • Thread DatabaseType through SQL GraphQL schema generation so relationship nullability decisions can be database-specific.
  • For DatabaseType.DWSQL, force relationship fields to be nullable when generating the GraphQL schema.
  • Add/extend unit tests to validate DWSQL vs MSSQL relationship-field nullability behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs Adds coverage asserting DWSQL relationship fields become nullable even when FK metadata indicates NOT NULL, while MSSQL remains unchanged.
src/Service.GraphQLBuilder/Sql/SchemaConverter.cs Adds databaseType plumbing and makes FindNullabilityOfRelationship return nullable for DWSQL relationships.
src/Core/Services/GraphQLSchemaCreator.cs Passes the metadata provider’s DatabaseType into SchemaConverter during schema construction (including linking entities).

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

@ritikadhawan

Copy link
Copy Markdown
Contributor Author

/azp run

1 similar comment
@ritikadhawan

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 6 pipeline(s).

@ritikadhawan

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 6 pipeline(s).

…3770)

## What's the problem?

When Data API builder serves a Fabric Warehouse (DWSQL) database, a
GraphQL query that follows a relationship between two tables could fail
with this error:

```
"Cannot return null for non-nullable field." (HC0018)
```

...even when the query was perfectly valid and the data was fine.
Instead of returning the row, the whole query blew up.

## Why does it happen?

Most databases guarantee that if a row points to a "parent" row (a
foreign key), that parent actually exists. DAB relies on that guarantee:
if a foreign-key column is marked "required" (NOT NULL), DAB assumes the
related object is always there, and tells GraphQL "this related field
will never be null."

**Fabric Warehouse is different — it does not enforce foreign keys.** So
a child row can happily point at a parent that doesn't exist (an
"orphaned" row). When GraphQL asks for that missing parent, DAB has
nothing to return, but it already promised the field would never be
null. GraphQL sees a broken promise and throws HC0018.

Example: an `Enrollment` row has a `studentId` that no `Student`
matches. Asking for `enrollment.student` returns nothing, and the query
fails.

## What does this change do?

For Fabric Warehouse (DWSQL) only, DAB no longer assumes related rows
always exist. It marks the related fields on many-to-one and one-to-many
relationships as nullable. That way, when a related row is genuinely
missing, the query simply returns `null` for that field instead of
failing the entire request.

Behavior for all other databases (SQL Server, PostgreSQL, MySQL) is
unchanged, since they do enforce foreign keys.

## How was it verified?

- Added unit tests covering both DWSQL (field is now nullable) and SQL
Server (field stays non-nullable) for both relationship directions.
- Manually validated end-to-end against a Fabric Warehouse: the query
that previously failed now returns the row with `student: null`.

#### Before
<img width="854" height="730" alt="Screenshot 2026-08-12 143540"
src="https://github.com/user-attachments/assets/9b4585e5-5cb8-456f-8efb-0eda22f68994"
/>


#### After
<img width="1053" height="514" alt="Screenshot 2026-08-12 141456"
src="https://github.com/user-attachments/assets/cbb42b63-c867-4804-bc10-6d7a4c1c643b"
/>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: aaronburtle <93220300+aaronburtle@users.noreply.github.com>
@ritikadhawan

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 6 pipeline(s).

@ritikadhawan

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 6 pipeline(s).

@ritikadhawan

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 6 pipeline(s).

…3770)

## What's the problem?

When Data API builder serves a Fabric Warehouse (DWSQL) database, a
GraphQL query that follows a relationship between two tables could fail
with this error:

```
"Cannot return null for non-nullable field." (HC0018)
```

...even when the query was perfectly valid and the data was fine.
Instead of returning the row, the whole query blew up.

## Why does it happen?

Most databases guarantee that if a row points to a "parent" row (a
foreign key), that parent actually exists. DAB relies on that guarantee:
if a foreign-key column is marked "required" (NOT NULL), DAB assumes the
related object is always there, and tells GraphQL "this related field
will never be null."

**Fabric Warehouse is different — it does not enforce foreign keys.** So
a child row can happily point at a parent that doesn't exist (an
"orphaned" row). When GraphQL asks for that missing parent, DAB has
nothing to return, but it already promised the field would never be
null. GraphQL sees a broken promise and throws HC0018.

Example: an `Enrollment` row has a `studentId` that no `Student`
matches. Asking for `enrollment.student` returns nothing, and the query
fails.

## What does this change do?

For Fabric Warehouse (DWSQL) only, DAB no longer assumes related rows
always exist. It marks the related fields on many-to-one and one-to-many
relationships as nullable. That way, when a related row is genuinely
missing, the query simply returns `null` for that field instead of
failing the entire request.

Behavior for all other databases (SQL Server, PostgreSQL, MySQL) is
unchanged, since they do enforce foreign keys.

## How was it verified?

- Added unit tests covering both DWSQL (field is now nullable) and SQL
Server (field stays non-nullable) for both relationship directions.
- Manually validated end-to-end against a Fabric Warehouse: the query
that previously failed now returns the row with `student: null`.

#### Before
<img width="854" height="730" alt="Screenshot 2026-08-12 143540"
src="https://github.com/user-attachments/assets/9b4585e5-5cb8-456f-8efb-0eda22f68994"
/>


#### After
<img width="1053" height="514" alt="Screenshot 2026-08-12 141456"
src="https://github.com/user-attachments/assets/cbb42b63-c867-4804-bc10-6d7a4c1c643b"
/>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: aaronburtle <93220300+aaronburtle@users.noreply.github.com>

Backport of #3770 to release/2.0.
@ritikadhawan

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 6 pipeline(s).

@ritikadhawan
Ritika Dhawan (ritikadhawan) merged commit 0b38aa7 into release/2.0 Aug 18, 2026
12 checks passed
@ritikadhawan
Ritika Dhawan (ritikadhawan) deleted the dev/ritikadhawan/backport-3770-to-2.0 branch August 18, 2026 05:27
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.

4 participants