Skip to content

Honor null-on-empty for-each quantifiers outside select boxes - #4489

Open
robert-brunel wants to merge 1 commit into
mainfrom
apple/robert-brunel/issue3431
Open

Honor null-on-empty for-each quantifiers outside select boxes#4489
robert-brunel wants to merge 1 commit into
mainfrom
apple/robert-brunel/issue3431

Conversation

@robert-brunel

Copy link
Copy Markdown
Contributor

A ForEach quantifier with the isNullOnEmpty() flag flows a single NULL row when the sub-graph it ranges over is empty. However, so far, the flag is only honored if it sits directly under a SelectExpression, since only ImplementSimpleSelectRule and ImplementNestedLoopJoinRule ever inspect it. Anywhere else in a query graph it is silently ignored, which would lead to incorrect results if NoE ever makes it there.

To address this issue:

  • Change RemoveSortRule, ImplementDistinctRule, ImplementUniqueRule, ImplementDistinctUnionRule and ImplementUnorderedUnionRule to correctly honor NoE quantifiers.
  • Prevent PredicateToLogicalUnionRule from firing on a NoE quantifier, since the rewrite wouldn’t correctly preserve the semantics.
  • Add helpers to facilitate the handling of NoE quantifiers; notably, forNullOnEmpty() utility in RecordQueryDefaultOnEmptyPlan to construct the ON EMPTY NULL wrapper node from a given quantifier, and Quantifiers.implementNullOnEmptyIfPresent() for rules that need to establish those semantics.
  • Add a validation pass, Quantifiers.verifyNullOnEmptyAwareness(), that runs from Reference.insertUnchecked() and Traversal.addExpression() if sanity checks are enabled. This pass ensures that expressions can only have a NoE quantifier if they are known to honor it.

Resolves #3431.

A `ForEach` quantifier with the `isNullOnEmpty()` flag flows a single `NULL` row when the sub-graph it ranges over is empty. However, so far, the flag is only honored if it sits directly under a `SelectExpression`, since only `ImplementSimpleSelectRule` and `ImplementNestedLoopJoinRule` ever inspect it. Anywhere else in a query graph it is silently ignored, which would lead to incorrect results if NoE ever makes it there.

To address this issue:

* Change **RemoveSortRule**, **ImplementDistinctRule**, **ImplementUniqueRule**, **ImplementDistinctUnionRule** and **ImplementUnorderedUnionRule** to correctly honor NoE quantifiers.
* Prevent **PredicateToLogicalUnionRule** from firing on a NoE quantifier, since the rewrite wouldn’t correctly preserve the semantics.
* Add helpers to facilitate the handling of NoE quantifiers; notably, `forNullOnEmpty()` utility in `RecordQueryDefaultOnEmptyPlan` to construct the `ON EMPTY NULL` wrapper node from a given quantifier, and `Quantifiers.implementNullOnEmptyIfPresent()` for rules that need to establish those semantics.
* Add a **validation pass**, `Quantifiers.verifyNullOnEmptyAwareness()`, that runs from `Reference.insertUnchecked()` and `Traversal.addExpression()` if sanity checks are enabled. This pass ensures that expressions can only have a NoE quantifier if they are known to honor it.

Resolves #3431.
@robert-brunel robert-brunel self-assigned this Aug 21, 2026
@robert-brunel robert-brunel added the enhancement New feature or request label Aug 21, 2026
@github-actions

Copy link
Copy Markdown

📊 Metrics Diff Analysis Report

Summary

  • New queries: 1
  • Dropped queries: 0
  • Plan changed + metrics changed: 0
  • Plan unchanged + metrics changed: 0
ℹ️ About this analysis

This automated analysis compares query planner metrics between the base branch and this PR. It categorizes changes into:

  • New queries: Queries added in this PR
  • Dropped queries: Queries removed in this PR. These should be reviewed to ensure we are not losing coverage.
  • Plan changed + metrics changed: The query plan has changed along with planner metrics.
  • Metrics only changed: Same plan but different metrics

The last category in particular may indicate planner regressions that should be investigated.

New Queries

Count of new queries by file:

  • yaml-tests/src/test/resources/join-tests-outer.metrics.yaml: 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

For-each quantifiers with null-on-empty semantics only work as input to SelectExpressions

1 participant