fix(sql): an empty group's aggregate is NULL in HAVING; MATCH in HAVING is refused by name - #408
Merged
Merged
Conversation
…NG is refused by name For a group in which no document has the column, Elasticsearch hands the HAVING filter NaN for its MIN, MAX, AVG or percentile. The filter now reads null, NaN and +/-Infinity as NULL and follows SQL three-valued logic, so <>, a negated comparison, IS [NOT] NULL and COALESCE keep the groups SQL keeps. A NULL-handling function is checked on its result. No aggregation is added. Search and materialized views build the filter through one function, nullAwareSelectorScript, and metricSelector delegates to it. A MATCH in HAVING over an aggregate, or over a column that is neither an aggregate nor a GROUP BY key, was silently ignored. It is now refused by name, with the remedy to put it in WHERE. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fupelaqu
marked this pull request as ready for review
October 1, 2026 19:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
An empty group's aggregate is NULL in
HAVING. For a group in which no document has the column, Elasticsearch hands the group filter (bucket_selector)NaNfor itsMIN,MAX,AVGor percentile, not NULL. So<>, a negated comparison,IS [NOT] NULLandCOALESCEkept or dropped the wrong groups, with HTTP 200 and no error.null,NaNand±Infinityas NULL and applies SQL three-valued logic: a comparison with NULL is UNKNOWN,NOTUNKNOWN stays UNKNOWN, and a group is kept only when the condition is TRUE. This is exact for indexed data: Elasticsearch 6.8–9.0 index only finite numbers.COALESCE(MAX(a), MIN(b))is NULL only when both are.COUNTandSUMare read as before (0 and 0.0).Having.script) build the filter through one function,MetricSelectorScript.nullAwareSelectorScript, andmetricSelectordelegates to it.MATCHinHAVINGis refused by name. AMATCHinHAVINGover an aggregate, or over a column that is neither an aggregate nor aGROUP BYkey, was silently ignored. It is now refused with the remedy "Put the MATCH in WHERE." AMATCHover aGROUP BYkey is unchanged.Docs:
documentation/sql/dql_statements.md(theHAVINGrules and a "Changed in 0.24.0" note).Measured (ES 8.18.3 through the gateway unless stated)
MATCHstatements; on main each answer equalled the answer without theMATCH(12/12)buckets_pathvariablerequest_cache=false, interleavedtookmedians ×0.998; none slower beyond noiseSuites:
sql1765,es6bridge256,softclient4es8-sql-bridge263,core1148. ES 8.18.3: the population spec (53 tests) and 5 HAVING integration suites (261). Lint (headerCheck scalafmtSbtCheck scalafmtCheck test:scalafmtCheck) clean.Not run locally, left to CI: ES 6.8 / 7.17 / 9.0 (the population spec runs on every major), the Scala 2.12 cross-compile, and the es7 / es9 bridges.
Behaviour changes (release notes)
HAVINGover an empty group'sMIN/MAX/AVG/ percentile follows SQL NULL semantics. Statements using<>, a negated comparison,IS [NOT] NULLorCOALESCEmay return other groups than 0.23.0, which returned wrong ones.MATCHinHAVINGover an aggregate, or over a column that is neither an aggregate nor aGROUP BYkey. Remedy: put it inWHERE.HAVINGfilter script changes for these aggregates. A view picks it up when it is (re)deployed.MetricSelectorScript.metricSelectornow returns the NULL-aware script, andHaving.script's deprecation points toMetricSelectorScript.nullAwareSelectorScript. No case-class arity change.Other pre-existing
HAVINGlimitations found during this work are unchanged here and kept for later.🤖 Generated with Claude Code