[SPARK-58093][SQL] Add ASOF JOIN SQL syntax with MATCH_CONDITION#57194
Open
srielau wants to merge 19 commits into
Open
[SPARK-58093][SQL] Add ASOF JOIN SQL syntax with MATCH_CONDITION#57194srielau wants to merge 19 commits into
srielau wants to merge 19 commits into
Conversation
Wire the SQL Reference Spec ASOF JOIN surface into the existing AsOfJoin logical operator, reusing the DataFrame execution path (RewriteAsOfJoin / SortMergeAsOfJoinExec). Adds parser grammar, analysis resolution for USING and match operands, spec error conditions, and parser/SQL integration tests.
…pile errors Update case-class pattern matches for the two new AsOfJoin fields, fix conf.resolver usage, replace expr.preOrder with expr.collect, and drop a duplicate RowOrdering import.
… arity The error helper only accepts the two MATCH_CONDITION operands; drop the unused left/right AttributeSet arguments from the throw site.
Rule.ruleId is a lazy val; overriding it with a non-lazy val fails compilation. The base Rule implementation already derives the same id from the class name, so drop the override entirely.
Wrap long lines and reorder expression imports so the braced import precedes expressions.aggregate.
Store parsed MATCH_CONDITION operands in AsOfMatchCondition so mapExpressions does not corrupt the tuple shape. Resolve operands in ResolveReferences and materialize asOfCondition in ResolveAsOfJoin, including USING column projection.
…enabled Default the new SQL syntax off while under development. AstBuilder rejects ASOF JOIN at parse time when disabled; tests opt in via SQLConf.
…iance doc SQLKeywordSuite requires new lexer keywords to be listed in sql-ref-ansi-compliance.md with correct reserved/non-reserved status.
ASOF is a Spark extension, not SQL standard, so it belongs in ansiNonReserved rather than being reserved under ANSI mode.
…sing Restrict matchComparison to valueExpression so boolean AND is rejected at parse time. Tighten type validation for incompatible operand pairs and prepare struct/tuple ordering for field-wise distance expressions.
…expressions Expose parsed MATCH_CONDITION operand trees to analysis pruning and expression rewriting so functions, interval arithmetic, and ON predicates all resolve before ResolveAsOfJoin materializes the join condition.
…erands Recursively build min_by order expressions for nested structs (leaf flattening) and arrays (ZipWith over element-wise diffs). Use signed comparison distance for orderable non-numeric types like STRING. Add AsOfJoinSQLSuite coverage for nested struct, ARRAY<INT>, and struct tuple operands on the min_by rewrite path.
Place TreePattern before TreePattern._ and keep DataTypeUtils imports adjacent per scalastyle rules.
Regenerate keywords golden files to include ASOF and MATCH_CONDITION from the lexer vocabulary. Update bitwise parse-error goldens after the ASOF JOIN grammar change shifts the reported token for `> >>` syntax.
Add ASOF and MATCH_CONDITION to the SPARK-43119 hardcoded keyword list returned by GetInfo(CLI_ODBC_KEYWORDS).
Reorder ASOF_JOIN_MATCH_CONDITION_* entries before ASSIGNMENT_ARITY_MISMATCH and AS_OF_JOIN in error-conditions.json to satisfy SparkThrowableSuite key ordering (ORDER_MAP_ENTRIES_BY_KEYS).
Add ASOF and MATCH_CONDITION to the hardcoded getSQLKeywords expectation after the new ASOF JOIN grammar keywords were registered.
Parse MATCH_CONDITION comparisons as booleanExpression instead of a dedicated matchComparison rule, which disturbed unrelated expression error reporting (e.g. CHECK constraints). Move ASOF JOIN to a trailing grammar alternative and validate allowed comparison operators in AstBuilder. Update the join_ parse-error golden after the new ASOF alternative changes ANTLR error recovery.
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.
Wire the SQL Reference Spec ASOF JOIN surface into the existing
AsOfJoinlogical operator, reusing the DataFrame execution path (RewriteAsOfJoin/SortMergeAsOfJoinExec). Adds parser grammar, analysis resolution forUSINGandMATCH_CONDITIONoperands, spec error conditions, and parser/SQL integration tests.What changes were proposed in this pull request?
This PR adds SQL syntax for ASOF JOIN with a required
MATCH_CONDITIONclause and optionalON/USING, per the SQL Language Reference spec (Snowflake / CalciteMATCH_CONDITIONfamily).Syntax supported (v1):
where
comparison_operatoris one of>=,>,<=,<.Example:
Implementation layers (new vs reused):
ASOF,MATCH_CONDITIONkeywords;asofJoinType,asofJoinCriteria,matchComparisongrammar ruleswithAsOfJoin()buildsAsOfJoin.fromMatchCondition(...)ResolveAsOfJoinrule: expandsUSINGviaNaturalAndUsingJoinResolution, normalizesMATCH_CONDITIONoperands intoasOfCondition+orderExpression, validates operand expressions/typesAsOfJoinwith deferredmatchComparison/usingColumnsfields; addsMatchComparisonOperatorandAsOfJoin.fromMatchConditionRewriteAsOfJoinoptimizer rule andSortMergeAsOfJoinExecphysical operator already used byDataFrame.joinAsOfASOF_JOIN_MATCH_CONDITION_INVALID_EXPRESSION,..._INVALID_OPERATOR,..._INVALID_TYPE,..._TABLE_REFERENCEExplicitly out of scope (deferred per spec):
TOLERANCE,NEARESTdirection,RIGHT/FULL/CROSS/SEMI/ANTIASOF forms.Related prior art in Spark:
DataFrame.joinAsOf(PySpark/Scala),AsOfJoinlogical node,SortMergeAsOfJoinSuite,DataFrameAsOfJoinSuite.Why are the changes needed?
ASOF (as-of / closest-match) join is a standard temporal/analytics pattern — attaching the nearest preceding quote to each trade, finding the next maintenance window for an alert, etc. Spark already implements the semantics via the DataFrame API, but SQL users must hand-write
LEFT JOIN+ROW_NUMBER+QUALIFYrewrites. Native SQL syntax lowers the barrier and aligns with industry dialects (Snowflake, Calcite/Pinot/DorisMATCH_CONDITIONfamily).Does this PR introduce any user-facing change?
Yes.
Before:
ASOF JOINwas not valid SQL; closest-match joins were only available throughDataFrame.joinAsOf.After: SQL queries may use
ASOF JOIN/LEFT ASOF JOINwithMATCH_CONDITION. Behavior matches the spec: asymmetric left-driven lookup, at most one right row per left row,INNERdrops unmatched left rows,LEFTpreserves them with NULL right-side columns.Operand order inside
MATCH_CONDITIONis immaterial (t.ts >= q.ts≡q.ts <= t.ts); the analyzer normalizes to a canonical left-table-on-left form before planning.How was this patch tested?
Added unit and integration tests; full compile/test suite to run on CI (local build blocked by missing
netty 4.2.16artifacts on the dev Maven proxy after rebasing onto latest master).Parser tests (
PlanParserSuite):INNER/LEFT ASOF JOINwithMATCH_CONDITIONandONUSINGclauseu.a <= t.a)=inMATCH_CONDITION(parse error)SQL integration tests (
AsOfJoinSQLSuite):ON,LEFT ASOFpreserving unmatched rows,USINGequivalence, first-following match with<=Existing coverage reused (unchanged):
DataFrameAsOfJoinSuite,SortMergeAsOfJoinSuite,RewriteAsOfJoinSuiteexercise the shared execution path.Suggested CI commands for reviewers:
Was this patch authored or co-authored using generative AI tooling?
Yes. Generated-by: Cursor (Claude agent), with human review and editing by the PR author.