Skip to content

branch-4.2: [fix](rbo) Give the constants materialized by constant propagation a fresh exprId #69111 - #69406

Merged
yiguolei merged 2 commits into
branch-4.2from
auto-pick-69111-branch-4.2
Oct 23, 2026
Merged

yiguolei merged 2 commits into
branch-4.2from
auto-pick-69111-branch-4.2

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Cherry-picked from #69111

…fresh exprId (#69111)

### What problem does this PR solve?

Problem Summary:
When ConstantPropagation replaces an aggregate output by a constant, it
materializes the constant in a
project above the aggregate, and the project item reused the exprId of
the replaced output:

    LogicalProject ( projects=[..., 1 AS `id`#1, ...] )
    +-- LogicalAggregate ( groupByExpr=[id#1], outputExpr=[id#1] )

So one exprId had two definitions, the aggregate output slot and the
constant, whose nullable and data
type may differ. Rules which compare exprIds only then take them as the
same column. For RQG case
11000258898 EliminateUnnecessaryProject removed such a project after the
aggregate had been rewritten
into `Limit 1`, the plan above fell back to the nullable output slot of
the set operation, and the last
AdjustNullable pass reported

AdjustNullable convert slot 2004-04-28#55 from not-nullable to nullable

Fix:
Give the constant a fresh exprId only when it really shadows a column,
i.e. when the exprId is still
produced by the plan below; otherwise keep it:

* the constant gets a fresh exprId, and the references above are
refreshed with ExprIdRewriter, when the
aggregate still outputs the replaced column (its grouping key is kept
because all grouping keys are
constant) or when a project item re-defines a column which its child
still outputs. That is the shadow
  the old code created, e.g. the RQG case 11000258898 of DORIS-28416.
* the constant keeps the exprId when the aggregate removed the constant
grouping key from its outputs:
the project item is then the only definition of the column, nothing
below references the exprId, and
consumers which rely on the identity of the column, e.g. the
materialized view matching, keep working.
The plan of such a query is byte identical to the plan before this
change, including the exprIds
(verified with the query of `agg_optimize_when_uniform/mv2_0`, for the
query side and for the MV side
  which is generated with constant propagation disabled).

The sink also has to follow the fresh exprIds, otherwise it would
reference a slot which its child does
not output any more; only exprIds are remapped there, the name and the
data type declared by the sink are
kept (that is why the rule does not replace the slots of the sink by
literals).

A CTE is rewritten in separate subtree contexts (producer body and
consumers) and the consumers, which
map their slots to the producer outputs by exprId, are rewritten before
the producer. The renamed exprIds
are published per CTE (`StatementContext#producerExprIdRemap`), and
`RewriteCteChildren` refreshes both
maps of every consumer with the last exprId of the chain
(`ExprIdRewriter#lastExprId`).

Reproduction: ConstantPropagationFreshExprIdTest runs this rule alone,
so the project above the aggregate
is still visible in the plan, and asserts that it does not reuse the
exprId of the aggregate output and
that the plan above references the fresh exprIds.
ConstantPropagationTest used to pin the reused exprIds
in its constant project expectations; it now asserts fresh exprIds. Both
fail before this change.

### Release note

None

### Check List (For Author)

- Test: Unit Test
- ConstantPropagationFreshExprIdTest (new): fails before with "the
constant project reuses the exprId
      of the aggregate output".
- ConstantPropagationTest: expectations updated, fails before with "the
constant project item must not
      reuse the exprId of the replaced output slot".
- 31 related FE unit test classes (constant propagation, constant cte,
set operation, column pruning,
fd, uniform, sql plan suite, runtime filter, ...) rerun: 385 tests, 0
failures.
- Behavior changed: Yes. A constant materialized above an aggregate has
its own exprId and the plan above
references it, so one exprId no longer describes two different columns.
- Does this need documentation: No
@github-actions
github-actions Bot requested a review from yiguolei as a code owner October 21, 2026 15:08
@hello-stephen

Copy link
Copy Markdown
Contributor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@hello-stephen

Copy link
Copy Markdown
Contributor

run buildall

The auto pick brought the master test files over verbatim and two of them do not compile on branch-4.2:

- ExprIdRewriterTest#testGuardedBranchIsRewritten builds its assignment with MergeUtils.selectBranch, which
  returns a ShortCircuitIf. branch-4.2's MergeUtils only has buildMergeJoin and there is no ShortCircuitIf
  class (MERGE INTO does not wrap its assignments that way here), so the subtree that fix made reachable
  does not exist on this branch: the test is dropped, the structural exprId replacement it exercises is
  ported unchanged.
- The two plan-rewrite tests of ExprIdRewriterTest use the 2-arg JobContext constructor; branch-4.2 has the
  3-arg one (ScheduleContext, PhysicalProperties, double).

Also drop the two RQG reproductions of ConstantPropagationFreshExprIdTest: both queries already trip
"AdjustNullable convert slot 2004-04-28#55 from not-nullable to nullable" (and "cKToNmsxok#45") on the
pristine branch-4.2, i.e. before this change, and the failure is unchanged by it. ConstantPropagation does
not modify the plans of those queries on this branch at all (verified by dumping the plan with the rule
applied alone), so the failure comes from another rule here, while on master the rule does rewrite them.
The tables those queries need are still created by the test.

Verified on branch-4.2: 105 tests of the 15 related FE unit test classes pass, and the
nereids_rules_p0/constant_propagation and nereids_rules_p0/mv/agg_optimize_when_uniform regression suites
pass.
@englefly

Copy link
Copy Markdown
Contributor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 77.87% (95/122) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 42.03% (58/138) 🎉
Increment coverage report
Complete coverage report

@yiguolei

Copy link
Copy Markdown
Contributor

skip buildall

@yiguolei
yiguolei merged commit 1b182a2 into branch-4.2 Oct 23, 2026
32 of 34 checks passed
@github-actions

Copy link
Copy Markdown
Contributor Author

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Oct 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor Author

PR approved by at least one committer and no changes requested.

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

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants