Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #19311 +/- ##
============================================
+ Coverage 67.83% 67.91% +0.07%
Complexity 1450 1450
============================================
Files 3504 3517 +13
Lines 226581 228103 +1522
Branches 35804 36124 +320
============================================
+ Hits 153699 154912 +1213
- Misses 60754 60912 +158
- Partials 12128 12279 +151
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
ededa2b to
3435400
Compare
7ebcfdc to
5f27d70
Compare
xiangfu0
left a comment
There was a problem hiding this comment.
Review of current head 5f27d70c.
The correctness, rolling-upgrade, and resource-behavior issues below should be addressed before merge. I did not duplicate the two existing unresolved threads about v2/lite rejection and _closedPartitionKeys cardinality.
Process follow-ups:
- This is a 56-file, 7.5K-line change spanning protobuf, planning, execution, configuration, and integration. Please either link the reviewed design/maintainer agreement for keeping it as one vertical slice, or split it into reviewable stacked PRs.
- Please link the sender-side-sorting TODO to a tracking issue.
- Both commits contain AI
Co-Authored-Bytrailers; repository guidance says to omit those, so please rewrite the commit messages before merge.
9623e5c to
ccb8dea
Compare
ccb8dea to
b612fab
Compare
|
Addressed the process follow-ups on head
Local validation passed: the 342-test focused suite, a 32-test matcher/operator rerun after the final retained-state changes, 13/13 two-server integration cases, the full 63-module |
e25bf6f to
c05a54b
Compare
|
The rebased PR head is now Coverage added in this follow-up:
The comparison remains pinned to Trino Validation:
Fresh hosted CI is complete: 12/12 checks passed for |
6575e4c to
3034c6d
Compare
3034c6d to
b6ed53c
Compare
Adds row pattern recognition to the multi-stage query engine, following the same shape as the UNNEST support added in apache#17168: a new plan node, an exchange-insertion rule, and an intermediate-stage operator. Pinot's parser already accepted the full MATCH_RECOGNIZE grammar (Parser.jj carries Calcite's Babel production). What was missing was operator-table registration, validation, a plan node, and all of execution. Front end - Register PREV/NEXT/FIRST/LAST/CLASSIFIER/MATCH_NUMBER/RUNNING/FINAL in PinotOperatorTable, which is a strict allow-list. - New MatchRecognizeValidator runs on the SqlNode tree before conversion. It rewrites an OMITTED AFTER MATCH clause to SKIP PAST LAST ROW: Calcite substitutes SKIP TO NEXT ROW, but SQL:2016, Trino, Snowflake and Oracle all default to SKIP PAST LAST ROW. The two differ in whether matches overlap, so a query ported from another engine would otherwise silently return different rows. Conversion erases the omitted-vs-explicit distinction, so the rewrite has to happen here. - Deferred constructs are rejected at planning time with actionable messages rather than silently mis-executing: ALL ROWS PER MATCH, SUBSET, PERMUTE, pattern exclusions, WITHIN, aggregates in DEFINE, NULLS FIRST/LAST, and ORDER BY / PARTITION BY on expressions (the last of which otherwise fail inside SqlToRelConverter with AssertionError or ClassCastException). Plan and wire format - MatchNode at plan.proto tag 19, encoding the pattern as a self-contained recursive RowPattern with a symbol table rather than a RexCall tree, with field numbers reserved for SUBSET and WITHIN. - PatternFieldRef in expressions.proto, so RexPatternFieldRef can no longer degrade to a plain InputRef and produce wrong-but-type-correct results. - PinotMatchExchangeNodeInsertRule hash-distributes on the PARTITION BY keys and prepends them to the sort collation, so rows arrive clustered and the operator can match and flush one partition at a time. A missing PARTITION BY is rejected by default, since it collapses the table onto one worker. Runtime - PatternToNfaCompiler builds an NFA with prioritized transitions (alternation in source order; greedy takes the loop edge first, reluctant the exit edge; {n,m} via counter registers rather than state unrolling), so depth-first traversal yields the SQL:2016 preferred match first. - MatchOperator evaluates DEFINE predicates over a classifier tape supporting PREV/NEXT/FIRST/LAST/CLASSIFIER/MATCH_NUMBER, emits MEASURES, and advances per the AFTER MATCH SKIP mode. - Guardrails throw rather than truncate, since a truncated pattern result is a silently wrong one: maxRowsInMatch, maxStepsPerMatchAttempt, and an empty-cycle guard. v1 covers PARTITION BY, mandatory ORDER BY, MEASURES, ONE ROW PER MATCH, all four AFTER MATCH SKIP modes, the full pattern algebra including reluctant quantifiers and anchors, and single-variable aggregates in MEASURES. MATCH_RECOGNIZE is not yet supported under the v2 physical optimizer or lite mode; queries there are covered by ignore flags rather than silently wrong results. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Upstream now enforces `///` markdown doc comments (JEP 467) over `/** */`
Javadoc via a checkstyle RegexpCheck, and this feature branch predates that
rule. Converts all 168 Javadoc blocks across the 23 MATCH_RECOGNIZE files, and
removes an `org.apache.calcite.sql.SqlLiteral` import that the feature commit
added without ever using (UnusedImports flags it independently).
Formatting only: `{@link X}` becomes `[X]`, `{@link X label}` becomes
`[label][X]`, `{@code x}` becomes a backtick span, `<p>` becomes an empty ///
line, and the remaining HTML becomes its markdown equivalent. No documentation
text was reworded or dropped, and the removed import is the only non-comment
line changed.
Verified: checkstyle reports 0 violations in each of pinot-spi, pinot-common,
pinot-query-planner, pinot-query-runtime and pinot-integration-tests (run
per-module, since a combined reactor stops at the first failure and hides the
rest); `javadoc -Xdoclint:reference,syntax` is clean over the changed sources;
pinot-query-planner 1598 tests and pinot-query-runtime 4611 tests still pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b6ed53c to
d252451
Compare
Adds SQL:2016 row pattern recognition to Pinot's multi-stage query engine: validation, logical/wire plans, exchange planning, and an intermediate-stage matcher.
Pinot's parser already accepts the
MATCH_RECOGNIZEgrammar through Calcite's Babel parser. This PR adds the missing supported-subset validation and execution path.Design and delivery boundary
The parser validation,
MatchNodewire representation, exchange rule, runtime operator, and expression semantics form one end-to-end query operator and must land atomically. Splitting those layers would either expose syntax that cannot execute or dispatch plans that servers cannot interpret. Existing non-MATCH_RECOGNIZEqueries keep their existing plan and execution behavior; unsupportedMATCH_RECOGNIZEmodes fail closed with actionable validation errors. The PR remains labeleddesign-reviewfor maintainer sign-off before landing.Rollback is likewise atomic: revert the feature commits and stop issuing
MATCH_RECOGNIZEqueries. There is no persisted-data or table-schema migration.Front end and planner
PREV/NEXT/FIRST/LAST/CLASSIFIER/MATCH_NUMBER/RUNNING/FINALin the strictPinotOperatorTableallow-list.MatchRecognizeValidator, including SQL:2016's omittedAFTER MATCHdefault (SKIP PAST LAST ROW) and actionable rejection of deferred constructs such asALL ROWS PER MATCH,SUBSET,PERMUTE, exclusions,WITHIN, aggregates inDEFINE, explicit null ordering, expression partition/order keys, and multi-value partition or aggregate inputs.RUNNINGinDEFINEas the clause's intrinsic mode and rejects unsupportedFINALsemantics there.CLASSIFIER()as nullable so Calcite preservesCOUNT(classifier_measure)instead of incorrectly rewriting it toCOUNT(*)for empty matches.MatchNodeand a self-contained recursiveRowPattern;PatternFieldRefpreserves pattern-variable identity instead of degrading to a plain input reference.PARTITION BY, then sorts by the exchange's actual partition-key order plus the requested order keys. Queries withoutPARTITION BYrequire the explicitallowMatchRecognizeWithoutPartitionByquery option and execute as one global partition.SET usePhysicalOptimizer = falseselects the supported path.Runtime and resource safety
DEFINEpredicates andMEASURESover a classifier tape, including navigation aroundCLASSIFIER(), bounded navigation, and exact decimal/integer aggregation behavior.Resource-limit precedence is node hint, query option, server config, then default:
maxRowsInMatchPartitionmax_rows_in_match_partitionpinot.query.match.max.rows.per.partitionmaxStepsPerMatchAttemptmax_steps_per_match_attemptpinot.query.match.max.steps.per.attemptScope
v1 covers
PARTITION BY, mandatoryORDER BY,MEASURES,ONE ROW PER MATCH, all four supportedAFTER MATCH SKIPmodes, pattern alternation/concatenation/quantifiers/anchors, navigation and classifier functions, and single-variable aggregates in measures.Trino comparison
The supported v1 behavior and tests were compared against Trino at commit
19b4cebac145, especially its row-pattern query suite, aggregation suite, analyzer suite, and feature documentation.Coverage added from that comparison includes:
CLASSIFIER()nullability, andCOUNT(classifier_measure)versusCOUNT(*)semantics;FIRST/LAST/PREV/NEXTnavigation aroundCLASSIFIER(), including out-of-match nulls and composition of non-zero logical and physical offsets;DEFINEexpressions and rejection ofFINALinDEFINE;DESC/ASCkeys.This comparison targets Pinot's explicitly supported v1 subset and does not claim full Trino feature parity.
ALL ROWS PER MATCH,SUBSET,PERMUTE, exclusions,WITHIN, aggregates inDEFINE, optional measures/order, and row-pattern window frames remain deferred and fail validation where applicable.Rolling-upgrade boundary
MatchNodeis plan oneof field 19. Older servers preserve that protobuf field as unknown but see the node oneof as unset (NODE_NOT_SET), so they cannot execute a dispatchedMATCH_RECOGNIZEstage. Upgrade all servers before issuing these queries; there is no mixed-version fallback. Existing query node kinds remain wire-compatible and are unaffected.Testing
MatchNodesemantic field.PlanNodeMerger.visitMatchand all 12 inEquivalentStagesFinder.visitMatchare exercised, with no missed or partial lines in either visitor.MatchRecognizeIntegrationTest: 21/21 passed against a real two-server cluster through the full 63-module dependency reactor, including empty matches under all skip modes, nullable classifier measures and classifier navigation,RUNNING/FINALvalidation, unquoted identifier and alias isolation, aggregate/null semantics, multi-key ordering, empty input, partition order, cross-segment isolation, and gated global execution withoutPARTITION BY.3034c6d21fef; final Codecov reports 81.91882% patch coverage with 343 changed lines missing and 67.86% project coverage (+0.16% versus base), with all expected uploads present.git diff --checkpassed for all affected modules on JDK 25.