airborne12 opened a new pull request, #69399:
URL: https://github.com/apache/doris/pull/69399

   ### What problem does this PR solve?
   
   Issue Number: N/A
   
   Related PR: #69292 (depends on this fix)
   
   Problem Summary:
   
   A CTE fails to plan when the optimizer inlines it after one of its scans got 
a virtual column:
   
   ```sql
   WITH c AS (
       SELECT id, score() AS s FROM t WHERE c1 MATCH 'apple'
       ORDER BY s DESC LIMIT 2
   )
   SELECT * FROM c;
   -- ERROR: A expression contains slot not from children
   ```
   
   A score or ANN TopN pushed into a scan, and a MATCH in the select list, add 
a virtual column to the scan. After rewrite, the optimizer inlines a CTE that 
has one consumer left (`Optimizer.normalizeCtePlan` → `CTEInliner`) by deep 
copying its rewritten body, so the copy includes these scans:
   
   - `score()` and `l2_distance_approximate()` are nondeterministic, so the 
rewrite does not inline a CTE that calls them, even with one consumer. Once the 
TopN pushdown has replaced them with a virtual column of the scan, the 
optimizer inlines the CTE. So every CTE with `ORDER BY score() LIMIT n` or an 
ANN TopN fails, with default settings.
   - A CTE with MATCH in its select list reaches the optimizer when the rewrite 
removes all but one of its consumers, e.g. `... UNION ALL SELECT * FROM c WHERE 
1 = 0`.
   
   `LogicalPlanDeepCopier` copied a scan with virtual columns wrongly:
   
   - it gave each virtual column two new ids: the slots above the scan were 
mapped to the first one, and the copied scan output the second one;
   - the copied scan still output the original virtual column, with its 
original id, next to the new one;
   - the score and ANN order keys of the copied scan still used the original 
slots.
   
   So the plan above the copied scan read a slot that the scan does not output.
   
   The fix:
   
   - The copier gives each virtual column one new id, maps the original id to 
it, and maps the score and ANN order keys to the new slots. Relations without 
virtual columns are copied as before.
   - `LogicalOlapScan.withVirtualColumns` replaces the virtual columns of the 
scan, but kept their old slots in the output next to the new ones. It now 
replaces them there too and keeps the other output slots, which the copier has 
already mapped. Computing the output of the copy again would not work: it gives 
VARIANT subpath slots new ids. `AdjustNullable`, the other caller that replaces 
existing virtual columns, no longer keeps two slots for a virtual column whose 
nullability changes.
   
   #69292 binds `score()` to its scan before CTEs are inlined, so it needs this 
fix; it carries the same change until this PR is merged.
   
   ### Release note
   
   Fix "A expression contains slot not from children" for a CTE that orders by 
`score()` or by an ANN distance with a LIMIT, or that has MATCH in its select 
list and is inlined after rewrite.
   
   ### Check List (For Author)
   
   - Test
       - [x] Regression test
       - [x] Unit Test
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] No need to test or manual test. Explain why:
           - [ ] This is a refactor/code format and no logic has been changed.
           - [ ] Previous test can cover this change.
           - [ ] No code files have been changed.
           - [ ] Other reason
   
       - Unit tests: 
`LogicalPlanDeepCopierTest.testDeepCopyOlapScanWithVirtualColumn` and 
`CTEInlineTest.inlineCteWhoseScanHasVirtualColumns` (CTEs with a score TopN, an 
ANN TopN, a MATCH projection, and a VARIANT subpath with a MATCH projection, 
with one consumer and with a second consumer removed by `WHERE 1 = 0`). Both 
fail on master (4 output slots instead of 3; `A expression contains slot not 
from children`) and pass with this change. `OrExpansionTest`, 
`SplitMultiDistinctTest`, `DecomposeRepeatWithPreAggregationTest`, 
`LogicalOlapScanTest`, `IvmDeltaRewriteHelperTest` and 
`PushDownVirtualColumnsIntoOlapScanTest` pass. Checkstyle passes.
       - Regression test: 
`nereids_rules_p0/cte/test_cte_inline_virtual_column`, with its `.out` 
generated by the framework, on an FE built from this branch and a BE built from 
41a58afba2 plus #69292's `SegmentIterator` change (this change is FE only). The 
VARIANT case is only in the unit test, because that BE reads `v['x']` as NULL 
even without a CTE.
   
   - Behavior changed:
       - [x] No.
       - [ ] Yes.
   
   - Does this need documentation?
       - [x] No.
       - [ ] Yes.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to