-
Notifications
You must be signed in to change notification settings - Fork 4k
branch-4.2: [fix](nereids) fix limit + offset overflow when pushing down TopN/Limit #64633 #68186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: branch-4.2
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,11 +17,13 @@ | |
|
|
||
| package org.apache.doris.nereids.rules.rewrite; | ||
|
|
||
| import org.apache.doris.nereids.exceptions.AnalysisException; | ||
| import org.apache.doris.nereids.properties.OrderKey; | ||
| import org.apache.doris.nereids.rules.Rule; | ||
| import org.apache.doris.nereids.rules.RuleType; | ||
| import org.apache.doris.nereids.trees.plans.Plan; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalTopN; | ||
| import org.apache.doris.nereids.util.Utils; | ||
|
|
||
| import java.util.List; | ||
|
|
||
|
|
@@ -58,6 +60,10 @@ public Rule build() { | |
| long limit = topN.getLimit(); | ||
| long childOffset = childTopN.getOffset(); | ||
| long childLimit = childTopN.getLimit(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Keep individually valid nested TopNs executable Compatible nested TopNs can hit this guard even when each node is valid. For example, two TopNs with limit 1 and offset 9223372036854775806 each have limit + offset == Long.MAX_VALUE; the outer one must return zero rows, but their combined offsets overflow and this code throws before lines 68-73 derive newLimit = 0. The ordered derived-table shape is already known to reach MERGE_TOP_N in test_merge_topn_offset.groovy. Please canonicalize the empty result with a representable offset before this check, and add a boundary case for it. |
||
| if (Utils.addOverflows(offset, childOffset)) { | ||
| throw new AnalysisException( | ||
| "offset overflows long range when merging TopNs: " + offset + " + " + childOffset); | ||
| } | ||
| long newOffset = offset + childOffset; | ||
| // The parent's offset is applied on top of the child's output, so only | ||
| // (childLimit - offset) of the child's rows survive. Clamp the merged limit | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,7 @@ | |
| import org.apache.doris.nereids.trees.plans.logical.LogicalProject; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalTopN; | ||
| import org.apache.doris.nereids.util.ExpressionUtils; | ||
| import org.apache.doris.nereids.util.Utils; | ||
| import org.apache.doris.thrift.TExprOpcode; | ||
|
|
||
| import com.google.common.collect.ImmutableList; | ||
|
|
@@ -189,10 +190,9 @@ private Plan pushDown( | |
| } | ||
|
|
||
| // When limit + offset overflows the long range, the pushed scan limit would wrap to a | ||
| // negative value. Fail with the same error as ordinary TopN instead of leaving score() | ||
| // unmaterialized and reporting an unrelated score() usage error. | ||
| if (topN.getLimit() > Long.MAX_VALUE - topN.getOffset()) { | ||
| throw new AnalysisException("limit + offset overflows the long range"); | ||
| // negative value; skip the push-down and let the TopN above the scan apply limit/offset. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Preserve the overflow error for score() queries Returning null here leaves score() unmaterialized. The same rewrite batch immediately runs CheckScoreUsage, so a query that has MATCH, ORDER BY, and LIMIT now fails with the unrelated "score() function requires ..." message before LogicalTopNToPhysicalTopN can report the overflow. This also contradicts the existing regression at test_search_score_topn_predicates.groovy:191-201, and the base version of this hunk explicitly threw the standard overflow error to avoid exactly this path. Please keep the fail-fast AnalysisException here (or validate overflow before CheckScoreUsage). |
||
| if (Utils.addOverflows(topN.getLimit(), topN.getOffset())) { | ||
| return null; | ||
| } | ||
|
|
||
| long scoreLimit = topN.getLimit() + topN.getOffset(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Preserve valid nested LIMITs when only the merged offset overflows
Two nested LIMIT 1 OFFSET 9223372036854775806 nodes are individually representable because each limit + offset equals Long.MAX_VALUE, and the outer node necessarily returns zero rows because its offset exceeds the child's one-row output. This guard nevertheless turns that valid query into an analysis error even though mergeLimit already derives a zero limit. Please canonicalize this case to a representable empty limit; PhysicalPlanTranslator's Exchange fold at lines 2263-2265 needs the same zero-limit handling so it does not call mergeOffset after deriving zero.