Skip to content

[fix](search) Bind SEARCH fields before predicate pushdown - #69344

Open
feiniaofeiafei wants to merge 6 commits into
apache:masterfrom
feiniaofeiafei:codex/fix-search-cte-pushdown
Open

feiniaofeiafei wants to merge 6 commits into
apache:masterfrom
feiniaofeiafei:codex/fix-search-cte-pushdown

Conversation

@feiniaofeiafei

@feiniaofeiafei feiniaofeiafei commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

What problem does this PR solve?

SEARCH combined with a NOT IN subquery inside a CTE can fail analysis after subquery expansion introduces an Apply. SEARCH fields are initially embedded in a DSL string, so predicate pushdown cannot see their column dependencies until they are bound. Requiring a single-scan shape before rewriting also rejects predicates that could reach a valid scan through a join, projection or UNION.

Bind SEARCH fields top-down to the filter's visible input slots during analysis, before subquery expansion and predicate pushdown. Each field must resolve to exactly one input column. Existing rewrite rules can then substitute aliases, map UNION branches and push predicates using their actual column dependencies.

Consolidate early SEARCH validation in CheckAnalysis: SEARCH is allowed only in filter predicates, and empty or whitespace-only DSL reports Invalid search expression: empty search DSL. Remove the standalone CheckSearchUsage rule. After rewriting, require every surviving SEARCH to be in a filter directly above an OLAP scan, reference supported scan-column expressions and have the required inverted indexes. Variant subcolumn checks use the original parent column. SEARCH eliminated during rewriting no longer requires an index. Allow temporary expression-child replacements used by optimizer inference, with executable children validated by the final check.

The new regression suites use EXPLAIN to validate FE plans for inline, materialized and reused CTEs, NOT IN, joins, projected and shadowed aliases, Variant paths and UNION branches. Negative cases validate field resolution, placement, empty DSL and index errors. Additional result-set regression covers aliased Variant cache isolation, absent-path segments, constant equalities and computed SEARCH arguments, and INTERSECT/EXCEPT with unindexed sibling columns.

Review follow-up preserves the final physical field bindings through these rewrites:

  • Fold constant DSL/options before parsing and protect executable SEARCH and multi_match column dependencies from constant substitution.
  • Introduce NonInferableExpression for Match, MultiMatch, Search and SearchExpression. Check the trait in equivalent-replacement inference and both sibling-predicate insertion paths, including INTERSECT/EXCEPT and JOINs.
  • Serialize Variant parent/path metadata from final scan descriptors or retained element_at children while preserving the logical DSL key.
  • Derive NESTED array owners from final physical Variant bindings, and pass physical leaf paths to nested document mapping and row-count callbacks. This covers root aliases and projected Variant prefixes while keeping logical DSL lookup keys unchanged.
  • Include sorted logical-to-storage field bindings in the BE bitmap cache signature, with length-prefixed names to avoid ambiguity.

Release note

Fix SEARCH predicate handling with CTEs and NOT IN subqueries. Allow unambiguous SEARCH predicates over joins, projected aliases and UNION outputs when rewriting can push them to indexed OLAP scan columns. Reject empty SEARCH DSL with an explicit analysis error.

Check List (For Author)

  • Test
    • Regression test
    • Unit Test
    • Manual test

Validation of the earlier review fixes (7ef8f50):

  • 216 FE tests passed across SearchCteRewriteTest, SearchPredicateTest, RewriteSearchToSlotsTest, SearchExpressionTest, CheckSearchUsageTest, CheckAnalysisTest, BindScoreToOlapScanTest, ConstantPropagationTest, and InferPredicatesTest.
  • SEARCH BE unit suites: 111 tests passed across FunctionSearchTest and FunctionSearchNullTest using run-be-ut.sh.
  • Built FE and ASAN BE using build.sh, deployed both, and verified deployed artifact checksums.
  • All 19 targeted regression suites passed, covering the new result-set suite and existing CTE, binding, index checks, syntax, cache, score, Boolean/NULL, and Variant missing/historical-path behavior. The new .out was generated by run-regression-test.sh, checked for correct rows, and rerun for comparison.
  • The original 24-query cross-connection cache reproduction produced zero mismatches after the fix.
  • FE Checkstyle, clang-format 16, clang-tidy, and BE build-hygiene checks passed.

NESTED alias follow-up validation:

  • Reproduced three FE failures on the previous code (including real SQL planning) and one BE callback-path failure; all pass with the fix.
  • 40 FE tests and 180 BE tests passed. FE and ASAN BE builds, Checkstyle, clang-format 16, clang-tidy, and build hygiene passed.
  • Updated both deployed libraries, verified artifact checksums, and reran all 19 targeted regression suites: zero failures and zero skips.
  • Full NESTED result-set execution remains unverified because the available community build lacks the NestedGroup module. The new tests validate SQL planning/serialization and the BE nested callback paths.

Non-inference trait and multi_match follow-up validation:

  • Reproduced incorrect MATCH/multi_match INTERSECT and EXCEPT results with equal strings indexed using different analyzers, and the multi_match execution error caused by replacing a searched column with an equality constant.

  • Added trait-based inference tests, set-operation/JOIN planning tests, and single-column/partially constant multi_match tests. All 164 FE tests passed.

  • FE build and Checkstyle passed. Deployed the FE library and verified its checksum. Generated the new regression .out with the test script, inspected the eight results, and passed all 21 targeted regression suites with zero failures or skips.

  • The new trait only controls predicate inference; ordinary pushdown, alias substitution, and pruning keep their existing behavior. ConstantPropagation separately preserves multi_match column arguments.

  • Behavior changed:

    • Yes. Bind SEARCH dependencies before pushdown and validate final scan placement and indexes after rewriting, while retaining early filter-position and unique-field checks and explicitly rejecting empty DSL.
  • Does this need documentation?

    • No.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

@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?

@feiniaofeiafei feiniaofeiafei changed the title [fix](fe) Bind SEARCH fields before predicate pushdown [fix](search) Bind SEARCH fields before predicate pushdown Oct 20, 2026
@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28638 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 5e3a61c3167459de1d0eb05cb18715109054c068, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17579	5112	5040	5040
q2	2137	318	253	253
q3	10332	1382	826	826
q4	4680	459	334	334
q5	7436	804	530	530
q6	181	182	156	156
q7	738	802	600	600
q8	9258	1369	1075	1075
q9	5534	4161	4322	4161
q10	6827	1292	1022	1022
q11	422	247	231	231
q12	633	394	276	276
q13	18102	3626	3006	3006
q14	280	277	251	251
q15	q16	727	709	672	672
q17	1190	766	577	577
q18	7007	6194	6128	6128
q19	1118	958	580	580
q20	396	339	230	230
q21	5127	2483	2417	2417
q22	368	323	273	273
Total cold run time: 100072 ms
Total hot run time: 28638 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5988	5863	6116	5863
q2	735	596	587	587
q3	5439	5793	5398	5398
q4	2018	2147	1543	1543
q5	5683	5598	5780	5598
q6	235	181	138	138
q7	2225	2002	1872	1872
q8	2992	2588	2548	2548
q9	8003	7968	7717	7717
q10	3653	3569	3167	3167
q11	602	408	382	382
q12	642	677	499	499
q13	3199	3606	3018	3018
q14	281	290	260	260
q15	q16	670	677	633	633
q17	7729	7017	6841	6841
q18	12979	12129	12859	12129
q19	875	773	759	759
q20	2139	2138	1899	1899
q21	5580	4611	4737	4611
q22	499	448	417	417
Total cold run time: 72166 ms
Total hot run time: 65879 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150587 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 5e3a61c3167459de1d0eb05cb18715109054c068, data reload: false

query5	4323	585	424	424
query6	431	210	193	193
query7	4811	455	225	225
query8	322	168	147	147
query9	8795	3081	3105	3081
query10	510	331	253	253
query11	5862	3872	3572	3572
query12	153	89	84	84
query13	1251	449	335	335
query14	6057	4024	3643	3643
query14_1	3377	3391	3380	3380
query15	203	193	182	182
query16	975	468	428	428
query17	868	660	557	557
query18	2406	424	300	300
query19	193	154	132	132
query20	83	86	81	81
query21	207	127	103	103
query22	12931	13082	12798	12798
query23	13960	12762	12466	12466
query23_1	12552	12683	12504	12504
query24	7366	804	448	448
query24_1	466	463	461	461
query25	505	403	340	340
query26	1245	254	132	132
query27	2781	436	269	269
query28	4634	1913	1915	1913
query29	1551	564	435	435
query30	300	219	184	184
query31	999	914	811	811
query32	138	91	88	88
query33	545	296	230	230
query34	900	854	495	495
query35	736	747	647	647
query36	817	843	735	735
query37	121	105	81	81
query38	1758	1710	1676	1676
query39	791	798	747	747
query39_1	733	722	741	722
query40	215	112	94	94
query41	71	69	68	68
query42	85	85	83	83
query43	340	364	308	308
query44	1309	693	710	693
query45	179	182	161	161
query46	841	961	566	566
query47	2787	2938	2821	2821
query48	320	315	233	233
query49	591	417	298	298
query50	674	279	204	204
query51	10677	10665	10334	10334
query52	81	79	71	71
query53	199	207	154	154
query54	264	206	203	203
query55	75	70	66	66
query56	230	224	226	224
query57	1672	1603	1581	1581
query58	288	273	262	262
query59	2135	2199	1987	1987
query60	306	253	235	235
query61	174	165	174	165
query62	391	335	288	288
query63	192	160	161	160
query64	2908	1091	921	921
query65	3502	3304	3240	3240
query66	1776	417	294	294
query67	20025	20052	19879	19879
query68	3005	996	606	606
query69	375	275	246	246
query70	838	805	750	750
query71	289	231	211	211
query72	2804	2596	2287	2287
query73	502	519	290	290
query74	4646	4552	4321	4321
query75	2359	2296	1930	1930
query76	2310	1001	572	572
query77	345	385	271	271
query78	9721	9715	9122	9122
query79	994	834	519	519
query80	1436	411	351	351
query81	592	314	275	275
query82	615	135	103	103
query83	282	192	171	171
query84	313	129	102	102
query85	1097	421	371	371
query86	398	239	231	231
query87	2009	1953	1806	1806
query88	3684	2658	2672	2658
query89	332	282	239	239
query90	1822	170	166	166
query91	168	153	127	127
query92	98	90	88	88
query93	1049	1022	570	570
query94	616	292	261	261
query95	568	331	368	331
query96	660	486	230	230
query97	2469	2439	2301	2301
query98	169	150	147	147
query99	781	760	652	652
Total cold run time: 236010 ms
Total hot run time: 150587 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.5 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 5e3a61c3167459de1d0eb05cb18715109054c068, data reload: false

query1	0.00	0.01	0.01
query2	0.10	0.05	0.05
query3	0.26	0.09	0.08
query4	1.60	0.11	0.11
query5	0.27	0.24	0.24
query6	1.17	0.68	0.68
query7	0.04	0.01	0.01
query8	0.05	0.04	0.03
query9	0.39	0.36	0.34
query10	0.61	0.58	0.58
query11	0.20	0.16	0.15
query12	0.20	0.15	0.14
query13	0.48	0.52	0.48
query14	1.01	1.01	1.02
query15	0.62	0.60	0.60
query16	0.30	0.36	0.35
query17	1.17	1.14	1.16
query18	0.21	0.20	0.19
query19	2.07	2.05	2.03
query20	0.02	0.01	0.02
query21	15.43	0.20	0.13
query22	4.74	0.06	0.05
query23	16.38	0.32	0.13
query24	3.11	0.46	0.37
query25	0.10	0.07	0.05
query26	1.02	0.22	0.16
query27	0.07	0.05	0.03
query28	3.38	0.76	0.39
query29	12.49	4.17	3.37
query30	0.28	0.12	0.09
query31	2.77	0.56	0.32
query32	3.23	0.59	0.49
query33	3.17	3.31	3.16
query34	15.34	3.99	3.42
query35	3.36	3.35	3.36
query36	0.55	0.47	0.40
query37	0.09	0.07	0.06
query38	0.06	0.04	0.04
query39	0.04	0.03	0.03
query40	0.19	0.15	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.74 s
Total hot run time: 24.5 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 80.00% (44/55) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 81.82% (45/55) 🎉
Increment coverage report
Complete coverage report

@feiniaofeiafei
feiniaofeiafei force-pushed the codex/fix-search-cte-pushdown branch from 073b20b to 83ed119 Compare October 27, 2026 06:29
@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28486 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 83ed1195a04d79d2944c7f1ece293aad9fcbf361, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17610	5175	5066	5066
q2	2117	362	265	265
q3	10182	1385	829	829
q4	4678	468	345	345
q5	7455	811	529	529
q6	180	184	156	156
q7	734	802	599	599
q8	9259	1348	1073	1073
q9	5463	4168	4164	4164
q10	6819	1290	1042	1042
q11	424	244	232	232
q12	633	404	277	277
q13	18114	3660	3026	3026
q14	275	280	250	250
q15	q16	746	706	681	681
q17	1209	751	588	588
q18	7042	6173	6115	6115
q19	1104	948	591	591
q20	407	332	221	221
q21	5305	2180	2268	2180
q22	347	295	257	257
Total cold run time: 100103 ms
Total hot run time: 28486 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5610	5573	5569	5569
q2	676	599	525	525
q3	5240	5369	4989	4989
q4	1962	2140	1533	1533
q5	5318	5137	5647	5137
q6	250	206	155	155
q7	2339	2126	1933	1933
q8	3073	2881	2652	2652
q9	8143	8147	8047	8047
q10	3740	3689	3220	3220
q11	606	433	403	403
q12	666	708	539	539
q13	3552	3892	3258	3258
q14	301	313	289	289
q15	q16	735	713	663	663
q17	8283	7575	6922	6922
q18	12971	12165	12843	12165
q19	903	772	772	772
q20	2146	2139	1907	1907
q21	5568	4590	4693	4590
q22	500	436	407	407
Total cold run time: 72582 ms
Total hot run time: 65675 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150810 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 83ed1195a04d79d2944c7f1ece293aad9fcbf361, data reload: false

query5	4312	569	440	440
query6	428	205	195	195
query7	4803	442	229	229
query8	336	165	147	147
query9	8782	3088	3112	3088
query10	472	303	285	285
query11	5975	3830	3559	3559
query12	161	91	88	88
query13	1254	433	326	326
query14	6118	3903	3637	3637
query14_1	3394	3407	3394	3394
query15	211	197	179	179
query16	951	458	445	445
query17	873	673	546	546
query18	2425	431	327	327
query19	208	162	138	138
query20	86	86	82	82
query21	209	128	109	109
query22	12966	12974	12762	12762
query23	13449	12918	12428	12428
query23_1	12580	12642	12459	12459
query24	7382	822	459	459
query24_1	470	485	468	468
query25	540	430	370	370
query26	1279	259	140	140
query27	2754	440	279	279
query28	4633	1967	1925	1925
query29	1561	608	448	448
query30	299	218	184	184
query31	1007	925	822	822
query32	141	96	96	96
query33	545	309	248	248
query34	899	854	495	495
query35	743	752	646	646
query36	817	809	735	735
query37	131	103	82	82
query38	1769	1738	1667	1667
query39	788	783	747	747
query39_1	744	729	751	729
query40	214	117	99	99
query41	72	70	68	68
query42	89	87	84	84
query43	341	364	310	310
query44	1289	687	691	687
query45	173	172	168	168
query46	832	966	565	565
query47	2924	2882	2805	2805
query48	294	306	217	217
query49	569	395	291	291
query50	681	270	222	222
query51	11120	10407	10516	10407
query52	78	79	74	74
query53	184	211	147	147
query54	249	186	190	186
query55	76	66	62	62
query56	228	220	220	220
query57	1627	1596	1571	1571
query58	288	250	264	250
query59	2262	2225	2038	2038
query60	304	245	222	222
query61	149	151	149	149
query62	385	332	305	305
query63	184	157	160	157
query64	2757	922	744	744
query65	3343	3259	3282	3259
query66	1779	420	294	294
query67	20022	20013	20157	20013
query68	2985	1002	614	614
query69	391	292	248	248
query70	834	759	763	759
query71	291	233	213	213
query72	2776	2561	2262	2262
query73	500	517	289	289
query74	4593	4507	4315	4315
query75	2304	2309	1916	1916
query76	2320	1015	587	587
query77	344	370	271	271
query78	9882	9747	9235	9235
query79	984	896	515	515
query80	970	418	331	331
query81	585	320	266	266
query82	575	133	107	107
query83	274	196	181	181
query84	312	130	93	93
query85	1013	429	375	375
query86	358	237	230	230
query87	2004	1931	1796	1796
query88	3619	2676	2642	2642
query89	334	291	241	241
query90	1704	166	165	165
query91	160	149	128	128
query92	102	88	80	80
query93	992	990	584	584
query94	515	303	284	284
query95	575	410	315	315
query96	649	475	231	231
query97	2434	2441	2365	2365
query98	168	154	146	146
query99	767	774	661	661
Total cold run time: 235307 ms
Total hot run time: 150810 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.24 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 83ed1195a04d79d2944c7f1ece293aad9fcbf361, data reload: false

query1	0.01	0.00	0.01
query2	0.10	0.05	0.05
query3	0.27	0.08	0.08
query4	1.61	0.10	0.10
query5	0.23	0.24	0.25
query6	1.17	0.69	0.68
query7	0.04	0.01	0.00
query8	0.06	0.04	0.04
query9	0.41	0.36	0.36
query10	0.57	0.57	0.60
query11	0.21	0.14	0.14
query12	0.18	0.14	0.14
query13	0.49	0.50	0.49
query14	1.03	1.03	1.01
query15	0.62	0.60	0.61
query16	0.36	0.34	0.32
query17	1.09	1.12	1.10
query18	0.21	0.22	0.21
query19	2.05	2.02	1.98
query20	0.02	0.02	0.01
query21	15.48	0.19	0.13
query22	4.94	0.06	0.05
query23	16.24	0.32	0.12
query24	3.12	0.84	0.35
query25	0.11	0.06	0.06
query26	1.82	0.21	0.15
query27	0.07	0.05	0.04
query28	2.55	0.74	0.37
query29	12.51	4.21	3.28
query30	0.28	0.15	0.10
query31	2.78	0.56	0.33
query32	3.22	0.60	0.53
query33	3.10	3.30	3.17
query34	15.38	3.97	3.35
query35	3.36	3.34	3.31
query36	0.55	0.47	0.43
query37	0.10	0.07	0.06
query38	0.06	0.03	0.03
query39	0.04	0.03	0.03
query40	0.17	0.15	0.14
query41	0.08	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.77 s
Total hot run time: 24.24 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 7.15% (51/713) 🎉
Increment coverage report
Complete coverage report

### Background

SEARCH fields are bound during analysis before score binding runs in rewrite. Bound SEARCH predicates use SearchExpression instead of the scalar Search function.

### What problem does this PR solve?

Issue Number: None
Related PR: apache#69344 (early SEARCH binding), apache#69292 (score binding)
PR Type: Bugfix

Problem Summary: Valid SEARCH queries that call score() are rejected because score binding only recognizes the unbound Search type. Recognize SearchExpression in the WHERE predicate check, scoring alias propagation and HAVING/QUALIFY validation.

### Details

- **Frequency:** SEARCH scoring queries after early SEARCH binding.
- **Root cause:** The downstream score binder checks the expression type from the previous rule ordering.
- **Why existing tests missed it:** The targeted SEARCH suites did not cover score(); existing score regression suites expose the failure.
- **Fix:** Replace all three Search type checks with SearchExpression and update the import.
- **Other paths and branches:** MATCH recognition and score usage restrictions remain unchanged.
- **Behavior change:** Valid SEARCH scoring queries can pass score binding.

### Testing

FE Checkstyle and git diff --check passed. Built FE with DISABLE_BUILD_UI=ON ./build.sh --fe and deployed it to the development cluster.

The five targeted FE test classes executed 147 tests with zero failures, errors or skips, including 82 BindScoreToOlapScanTest cases.

Ran 21 targeted regression suites: 20 passed, one failed because the existing BE lacks the keyword MATCH scoring change from apache#69790. Its actual 0.290716 score matches the BE-version expected output, while the current expected output is 0.983863. Re-running that suite against the existing generated expected output from the running BE commit passed. Repository expected outputs were not changed. The previous SEARCH score binding error was not reproduced.

The initial local FE unit-test attempt stopped during dependency resolution due to Maven-cache sandbox permissions; remote tests provide the validation above.

### Release note

Fix planning failures when using score() with SEARCH predicates.

### Check List (For Author)

- Behavior changed: Yes (see Details)
- Does this need documentation: No
- Pre-submission status: Partially verified: current keyword MATCH expected scores require a BE containing apache#69790; validation used the existing BE

### AI Disclosure

- Tool: OpenAI Codex
- Generated: Code and commit description
- Human review: Pending
@feiniaofeiafei
feiniaofeiafei force-pushed the codex/fix-search-cte-pushdown branch from 83ed119 to 00fbcd2 Compare November 6, 2026 03:00
@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

run buildall

@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes: five independently verified findings (one P1, four P2) affect SEARCH result correctness and valid-query admission at head 00fbcd2fd54cf021c50c704cbb866add9eace6dd. Each finding is inline. No existing inline findings were present to deduplicate or retain as blockers.

The intended CTE/NOT IN fix is supported by the early dependency-binding path and the added FE plan checks. The remaining regressions are: alias Variant-path bitmap-cache collisions, executable constant propagation of SEARCH children, parsing constant SQL arguments before folding, missing Variant metadata for extracted aliases, and INTERSECT/EXCEPT inference copying SEARCH into an unindexed sibling.

Critical checkpoint conclusions:

Checkpoint Conclusion
Goal and proof Early binding exposes SEARCH dependencies before Apply/pushdown. Added tests cover the reported plan, but the five findings prevent correctness approval; EXPLAIN does not prove returned rows.
Focus, clarity and reuse Focused SEARCH changes reuse existing pushdown and consolidate validation; no unrelated production edits found.
Concurrency and locks Synchronous query-local planner state; no new threads, shared mutable state, heavy locked operation or lock-order change.
Lifecycle and initialization No new resource ownership, release path or static initialization dependency. Retained QsPlan versus rewritten children causes the two alias metadata findings.
Configuration No added or changed configuration. Existing inverted-index caching enables the P1 case.
Compatibility and rolling upgrades No binary/storage-format change. SQL compatibility regresses in the constant-expression, constant-propagation and set-operation cases.
Thrift/protobuf IDs No gensrc or field-ID changes. Existing SEARCH serialization omits physical path identity and misclassifies extracted aliases.
Parallel paths Inspected CTE producers/consumers, joins including mark/semi/anti/outer paths, projections, UNION/INTERSECT/EXCEPT, score and Variant paths. The inline findings identify the concrete gaps.
Special conditions and errors Early position/empty-DSL checks and final placement/index checks are intentional. Eliminated predicates need no index; several valid surviving predicates are incorrectly rejected as described inline.
Test coverage Inspected all three changed FE test files and six changed regression suites, including negative cases. Constant SQL arguments, executable constant substitution, set-operation sibling inference and actual alias cache/absent-path results need coverage.
Test results No result files changed. New suites primarily assert EXPLAIN structure; sparse-Variant result checks are explicitly deferred. No builds, tests or runtime reproduction were executed during this static review. Author-reported runs were not treated as reviewer execution.
Observability Existing analysis errors identify SEARCH/field/index context. No new distributed operation requiring additional metrics found.
Transactions and persistence No transaction, EditLog, replay or failover path changed. CREATE VIEW retains rewritten original SQL; no new options-persistence issue substantiated.
Writes and crash atomicity No data-write or storage-lifecycle change. The P1 issue affects query bitmap reuse and returned rows.
FE/BE variables and send paths No new variables. Existing child translation and binding serialization were traced into BE iterator selection, missing-path handling and cache lookup.
Performance and memory Per-field binding/index checks remain bounded by query/table metadata. No separate material CPU, allocation or memory-ownership defect substantiated.
Other/core invariants Visibility versions and delete bitmaps are untouched. Unsupported paths fail explicitly; the inline findings identify wrong-result or valid-query failures.

User focus: no additional focus was supplied; the complete 18-file PR was reviewed. All initial risk items and candidates have explicit dispositions in the shared ledger. Static inspection provides the evidence; execution behavior was not tested.

Convergence: converged after 3 cumulative rounds. All three reviewers returned NO_NEW_VALUABLE_FINDINGS in the final round, required static coverage is complete, and no candidate or risk question remains unresolved. The findings still require changes; convergence describes review coverage, not approval.

Review converged after 3 round(s).

originalFieldName, search.getDslString()));
}
checkInvertedIndexExists(scan.getTable(), slot.getName(), search.getDslString(), false);
Slot slot = findSlotByName(originalFieldName, input);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Include the final Variant path in the SEARCH cache identity

Binding to a visible alias leaves QsPlan unchanged when project pushdown and VariantSubPathPruning replace its children. Consequently Filter(search('label:hello', child=v.city)) and the same plan with child=v.country serialize identical DSL, binding names, descriptor IDs and inherited index properties when each scan requests one path. FunctionSearch::dsl_cache_signature hashes only those TSearchParam contents plus max_expansions, under a shared segment prefix; it does not include the actual path/iterator, and a cache hit returns before field resolution.

For indexed, fully materialized string paths and rows (1,{city:hello,country:world}), (2,{city:world,country:hello}), run SELECT id FROM (SELECT id,v['city'] AS label FROM t) x WHERE search('label:hello'), then substitute v['country'] AS label. With inverted-index caching enabled and no scoring, the correct results are 1 then 2, but the second query can reuse the first bitmap and return 1. Include final physical column/path identity in the cache key or execution metadata, and cover this with result-set cache checks.

"SearchExpression children must be SlotReference or ElementAt instances");
}
}
// Rewrites such as null-rejection inference temporarily replace slots with literals.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Protect bound SEARCH slots from executable constant propagation

Consider Project(id) -> Filter(title#1='hello' AND SearchExpression(children=[title#1])) -> OlapScan(t), with an inverted index on title. ConstantPropagation.replaceAndConstants infers title#1 -> 'hello'; its canReplaceExpression protects Match only, so this newly permissive method installs the literal into SEARCH. SEARCH is nonfoldable, and CheckAfterRewrite then raises search() fields must reference OLAP scan columns directly.

Thus the ordinary valid query SELECT id FROM t WHERE title='hello' AND search('title:hello') now fails planning; binding previously ran after constant propagation. Shield bound SEARCH dependencies from executable constant substitution as Match already does, while allowing temporary null-rejection inference replacements.

// run CheckSearchUsage before CheckAnalysis to detect search() in GROUP BY before it gets optimized
bottomUp(new CheckSearchUsage()),
// Bind hidden DSL column dependencies before subquery expansion and predicate pushdown.
topDown(new RewriteSearchToSlots()),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Fold constant SEARCH arguments before parsing them

This pass now executes before expression normalization. For example, SELECT id FROM t WHERE search('hello', concat('{"default_field":', '"title"}')) on an indexed title column reaches Search.getOptionsJson() with a Concat child. That method returns the expression's toString(), so SearchDslParser.parseOptions receives concat(...) and throws Invalid search options JSON instead of parsing the evaluated JSON string.

Previously expression normalization folded this constant before the late binding pass. Computed constant DSL arguments such as search(concat('title:', 'hello')) have the same regression. Fold the DSL/options arguments before parsing while retaining early dependency binding.

}
// VariantSubPathPruning replaces ElementAt with a slot that retains its parent column.
checkInvertedIndexExists(scan.getTable(), slot.getOriginalColumn().get().getName(),
search.getDslString(), child instanceof ElementAt || slot.hasSubColPath());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve Variant binding metadata for extracted aliases

This admits an extracted Variant slot, but its QsPlan still names the visible alias. In the reduced tree Filter(search('city:hello')) -> Project(id,v['city'] AS city) -> Scan(t), pushdown produces a v.city child while ExprToThriftVisitor sets is_variant_subcolumn=false because binding city has no dot, and emits no parent/subpath metadata.

For SELECT id FROM (SELECT id,v['city'] AS city FROM t) x WHERE search('city:hello'), a segment containing only v.country has a null v.city iterator. collect_slot_search_input then throws SEARCH index unavailable instead of calling verify_missing_variant_path and returning no matches as direct search('v.city:hello') does. This fails even with caching disabled. Derive Variant parent/path metadata from the final child while retaining the logical alias key, and cover absent-path segments with result-set checks.

// run CheckSearchUsage before CheckAnalysis to detect search() in GROUP BY before it gets optimized
bottomUp(new CheckSearchUsage()),
// Bind hidden DSL column dependencies before subquery expansion and predicate pushdown.
topDown(new RewriteSearchToSlots()),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Exclude SEARCH from cross-branch set-operation inference

With Intersect(Project(title) -> Filter(search(title)) -> Scan(indexed_t), Project(body) -> Scan(plain_t)), PullUpPredicates remaps the left SEARCH onto the set output. InferPredicates.visitLogicalIntersect then remaps it onto body and adds a right-side filter. Its admission check only rejects volatile/nonmovable expressions, so an unindexed body now fails the final index check.

With spill disabled and matching string column types, SELECT title FROM indexed_t WHERE search('title:hello') INTERSECT SELECT body FROM plain_t was valid before early binding: scalar Search had no column inputs, so sibling inference skipped it. EXCEPT has the same regression. Prevent these direct sibling-branch substitutions from synthesizing index-dependent SEARCH predicates; InferPredicateByReplace's existing SEARCH exclusion does not cover this route.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28703 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 00fbcd2fd54cf021c50c704cbb866add9eace6dd, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17609	5146	5049	5049
q2	2128	353	252	252
q3	10184	1390	799	799
q4	4678	478	341	341
q5	7439	803	524	524
q6	186	184	154	154
q7	751	800	606	606
q8	9259	1362	1086	1086
q9	5518	4230	4227	4227
q10	6807	1314	1023	1023
q11	435	247	226	226
q12	638	400	275	275
q13	18097	3643	3032	3032
q14	277	276	247	247
q15	q16	718	734	650	650
q17	1183	747	589	589
q18	7004	6194	6102	6102
q19	1099	966	599	599
q20	380	347	226	226
q21	4888	2436	2419	2419
q22	364	346	277	277
Total cold run time: 99642 ms
Total hot run time: 28703 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5947	5923	6137	5923
q2	764	600	580	580
q3	5646	5871	5256	5256
q4	2030	2142	1549	1549
q5	5725	5813	5609	5609
q6	245	187	144	144
q7	2259	2095	1807	1807
q8	2983	2807	2662	2662
q9	7926	7633	7507	7507
q10	3646	3609	3131	3131
q11	606	410	400	400
q12	674	670	504	504
q13	3330	3597	3054	3054
q14	297	290	259	259
q15	q16	664	675	618	618
q17	7771	7019	6874	6874
q18	13017	12232	12859	12232
q19	911	776	783	776
q20	2138	2157	1914	1914
q21	5607	4698	4668	4668
q22	515	455	432	432
Total cold run time: 72701 ms
Total hot run time: 65899 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 154227 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 00fbcd2fd54cf021c50c704cbb866add9eace6dd, data reload: false

query5	4311	583	447	447
query6	433	207	192	192
query7	4796	442	229	229
query8	319	166	151	151
query9	8805	3198	3210	3198
query10	479	306	256	256
query11	6042	3866	3584	3584
query12	154	90	89	89
query13	1248	440	323	323
query14	6075	3929	3651	3651
query14_1	3443	3452	3467	3452
query15	209	207	179	179
query16	947	410	433	410
query17	840	635	505	505
query18	2414	404	302	302
query19	187	161	128	128
query20	85	92	79	79
query21	208	125	104	104
query22	13071	12951	12843	12843
query23	13476	12886	12398	12398
query23_1	12601	12637	12626	12626
query24	7354	851	480	480
query24_1	481	472	483	472
query25	525	392	337	337
query26	1266	262	138	138
query27	2772	439	275	275
query28	4615	2034	2022	2022
query29	1555	573	439	439
query30	296	216	175	175
query31	1023	937	816	816
query32	145	97	84	84
query33	507	305	234	234
query34	891	875	504	504
query35	760	751	650	650
query36	825	790	737	737
query37	136	100	78	78
query38	1766	1743	1641	1641
query39	792	795	750	750
query39_1	730	725	751	725
query40	228	113	96	96
query41	65	64	61	61
query42	86	88	82	82
query43	344	354	309	309
query44	1380	714	709	709
query45	181	175	168	168
query46	912	983	574	574
query47	2872	2782	2712	2712
query48	297	316	223	223
query49	579	398	294	294
query50	713	282	215	215
query51	10505	10610	10513	10513
query52	78	80	71	71
query53	193	222	153	153
query54	228	189	177	177
query55	72	70	65	65
query56	239	206	205	205
query57	1686	1609	1568	1568
query58	292	267	253	253
query59	2093	2115	1942	1942
query60	298	250	221	221
query61	147	151	151	151
query62	389	334	283	283
query63	195	159	155	155
query64	2735	1066	908	908
query65	3323	3276	3250	3250
query66	1804	440	357	357
query67	22659	22626	22426	22426
query68	3003	1027	623	623
query69	394	283	247	247
query70	876	836	794	794
query71	297	241	194	194
query72	2707	2569	2370	2370
query73	538	548	302	302
query74	4648	4525	4380	4380
query75	2357	2292	1926	1926
query76	2333	1041	660	660
query77	363	380	273	273
query78	9781	9642	9203	9203
query79	905	874	551	551
query80	502	418	337	337
query81	521	312	266	266
query82	218	140	125	125
query83	198	212	187	187
query84	294	128	102	102
query85	776	423	379	379
query86	262	239	230	230
query87	1942	1947	1804	1804
query88	3810	2797	2792	2792
query89	340	281	247	247
query90	2125	179	167	167
query91	171	155	129	129
query92	99	94	93	93
query93	1032	1012	586	586
query94	460	311	301	301
query95	592	331	364	331
query96	690	508	241	241
query97	2463	2446	2374	2374
query98	161	154	148	148
query99	773	759	644	644
Total cold run time: 236588 ms
Total hot run time: 154227 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.08 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 00fbcd2fd54cf021c50c704cbb866add9eace6dd, data reload: false

query1	0.01	0.01	0.00
query2	0.10	0.05	0.05
query3	0.26	0.10	0.09
query4	1.61	0.10	0.11
query5	0.23	0.25	0.24
query6	1.18	0.68	0.67
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.40	0.35	0.35
query10	0.57	0.59	0.61
query11	0.20	0.15	0.14
query12	0.18	0.15	0.15
query13	0.52	0.49	0.49
query14	1.04	1.02	1.00
query15	0.65	0.60	0.60
query16	0.34	0.34	0.34
query17	1.14	1.10	1.11
query18	0.20	0.18	0.19
query19	2.12	1.98	1.96
query20	0.02	0.01	0.01
query21	15.48	0.18	0.13
query22	4.88	0.06	0.05
query23	16.29	0.32	0.12
query24	1.10	0.22	0.16
query25	0.08	0.06	0.05
query26	0.75	0.22	0.15
query27	0.03	0.03	0.03
query28	3.74	0.81	0.39
query29	12.51	4.19	3.29
query30	0.28	0.12	0.10
query31	2.78	0.56	0.33
query32	3.23	0.59	0.49
query33	3.25	3.18	3.25
query34	15.35	4.00	3.39
query35	3.37	3.33	3.35
query36	0.55	0.45	0.43
query37	0.09	0.07	0.06
query38	0.05	0.04	0.03
query39	0.03	0.03	0.03
query40	0.17	0.15	0.14
query41	0.08	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.04
Total cold run time: 95.04 s
Total hot run time: 24.08 s

### Background

SEARCH binds its hidden DSL fields before predicate pushdown. The resulting dependencies can be renamed, propagated, and replaced by extracted Variant scan slots before BE execution.

### What problem does this PR solve?

Issue Number: None
Related PR: apache#69344 (review follow-up)
PR Type: Bugfix

Problem Summary:

Fix all five review findings: aliased Variant paths could share cached bitmaps, executable constant propagation could replace SEARCH fields with literals, computed DSL/options were parsed before folding, extracted aliases lost missing-path metadata, and set-operation inference could copy SEARCH into an unindexed sibling. Preserve physical bindings and source-index dependencies while retaining early binding.

### Details

- **Change map:** 111 changed production lines, 215 changed test-source lines, and 49 generated result lines.
- **Frequency:** Triggered by the described SQL shapes; cache wrong results require bitmap cache reuse without scoring.
- **Root cause:** The earlier binding stage exposed SEARCH to normalization and inference while serialization and cache identity still relied on logical DSL names.
- **Why existing tests missed it:** Plan-only alias and CTE checks did not validate cache reuse or missing-path segment results; constant arguments and direct set-operation sibling inference lacked coverage.
- **Fix:** Fold SEARCH arguments before parsing; protect executable SEARCH dependencies from constant substitution and inferred cloning; derive Variant metadata from final scan descriptors or element_at children; encode sorted logical-to-storage bindings in cache signatures using length prefixes.
- **Other paths and branches:** Cover materialized and retained element_at Variant paths, INTERSECT/EXCEPT and the corresponding join inference gate. Preserve temporary inference replacements and the scoring cache bypass. No protocol IDs, storage formats, or configurations change.

### Testing

- Built FE and ASAN BE with build.sh and deployed the artifacts on the development cluster.
- 216 FE tests passed across nine targeted classes.
- FunctionSearchTest and FunctionSearchNullTest passed using run-be-ut.sh.
- 19 related regression suites passed; generated the new result file using run-regression-test.sh and reran it for comparison.
- The original 24-query cross-connection cache reproduction produced zero mismatches after the fix.
- FE Checkstyle, clang-format 16, clang-tidy, and BE build-hygiene checks passed.

### Release note

Fix wrong SEARCH results when cached searches use the same alias for different Variant paths. Allow valid SEARCH queries with constant expressions, equalities, missing Variant paths through aliases, and INTERSECT/EXCEPT against unindexed sibling columns.

### Check List (For Author)

- Behavior changed: Yes (see Details).
- Does this need documentation: No.
- Pre-submission status: Ready.

### AI Disclosure

- Tool: Codex.
- Generated: Code, tests, and this description.
- Human review: Pending author review.
@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review of head 7ef8f50: one new P2 finding, reported inline. Aliased Variant roots are admitted for NESTED SEARCH, but logical nested ownership and final physical leaf metadata disagree, causing execution failure in NestedGroup-capable builds.

All five existing reports are addressed for their stated scenarios: 4488661491 (physical cache identity), 4488661665 (constant propagation), 4488662116 (computed arguments), 4488662248 (ordinary alias missing-path metadata), and 4488662377 (INTERSECT/EXCEPT inference). No previously reported P0/P1 remains independently confirmed, so none is reposted or carried as blocking. The new NESTED-owner issue is distinct from ordinary leaf missing-path metadata.

Required checkpoint conclusions:

  1. Goal and evidence: early binding exposes dependencies before subquery expansion and supports the intended CTE/NOT IN, alias, join and UNION plans. Tests cover those planning paths and selected result sets; NESTED alias execution remains incomplete as reported inline.
  2. Scope and reuse: changes are focused on SEARCH binding/validation, dependent optimizer rules, physical metadata/cache identity and tests; existing folding, slot and index APIs are reused.
  3. Concurrency and locks: no new worker, lock or shared mutable state; query-owned FE structures and existing BE cache handles remain in use.
  4. Lifecycle and static initialization: no new retained resource, ownership cycle or cross-translation-unit initializer; signature buffers are local and scan descriptors keep their existing lifetime.
  5. Configuration: no configuration item added; tests use existing settings.
  6. Compatibility: no persistent storage format change or new wire field. Additional cache-key material is process-local. Artifact ABI and mixed-version execution were not run.
  7. Thrift/protobuf IDs: no gensrc definitions changed; cross-branch ID comparison is not applicable.
  8. Parallel paths: traced project/CTE/set mappings, extracted and retained ElementAt Variant children, ordinary missing-path handling, index-dependent inference and scoring. NESTED owner mapping is the identified gap.
  9. Conditions and errors: new inference/constant-substitution guards and final shape/index checks have concrete failure scenarios; errors propagate through existing exception/Status boundaries. No other guard defect substantiated.
  10. Test coverage: inspected all changed FE/BE tests and regression suites. New tests cover cache order/path/swap separation, absent segments, computed arguments, equality and set operations. Aliased NESTED execution lacks coverage.
  11. Expected results: all 16 new result blocks agree with inserted rows: city=1, country=2, equality/computed arguments=1, INTERSECT/EXCEPT=hello, absent-city queries=1. Output generation and author-reported passing runs were not independently executed.
  12. Observability: field-specific analysis errors and existing BE binding diagnostics remain; no additional metric requirement identified for this metadata change.
  13. Persistence and failover: no journal/replay or persistent metadata change.
  14. Writes and atomicity: no production data-write or transaction path change; committed visibility, visible-version filtering and delete bitmap rules are unchanged.
  15. FE-to-BE passing: final physical leaf metadata uses existing SEARCH bindings through the shared translator; the separate NESTED root still needs consistent resolution, as reported.
  16. Performance and memory: new cache identity work is O(F log F) for F query fields, with local metadata copies and no new row-wise processing or retained data-sized allocation. No benchmarks or memory measurements were run.
  17. Other concerns and focus: no additional user focus was supplied. All 29 changed paths and both initial risk focuses were reviewed; the dotted-key suspicion was dismissed against the existing writer/reader canonicalization contract.

Review converged in 2 rounds after complete coverage, candidate disposition, and a final sweep. Validation scope is static inspection only: no builds, tests, benchmarks, service startup, source edits or submodule initialization were performed. Complex CTE and several alias/join/UNION regressions are EXPLAIN-only; optional NestedGroup execution and runtime results were not exercised here.

Review converged after 2 round(s).

List<String> subPath = descriptor.getSubColPath();
if (subPath != null && !subPath.isEmpty()) {
thriftBinding.setIsVariantSubcolumn(true);
thriftBinding.setParentFieldName(descriptor.getColumn().getName());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Bind NESTED owners through the final physical field mapping

In a build with NestedGroup search enabled and an indexed v.items.msg, this newly admitted plan fails execution:

Filter(search('NESTED(doc.items, msg:hello)'))
  Project(id, v AS doc)
    Scan(t)

After pushdown the leaf is v.items.msg, so this emits parent_field_name=v, while convertQsNodeToThrift retains nested_path=doc.items. bind_nested_column in vsearch.cpp compares those roots (v != doc) and leaves nested_column unset. VariantNestedSearchEvaluator::evaluate then returns Column 'doc' not bound to current read schema for nested query, although the direct NESTED(v.items, ...) form can execute. Resolve the NESTED owner and full array path through the final physical binding while retaining logical leaf keys, and add an executing alias regression. The previous missing-path report concerns ordinary leaf metadata; this failure is in NESTED owner resolution.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 84.85% (56/66) 🎉
Increment coverage report
Complete coverage report

### Background

SEARCH dependencies can be renamed or replaced with extracted Variant slots during predicate pushdown.

### What problem does this PR solve?

Issue Number: None
Related PR: apache#69344
PR Type: Bugfix

Problem Summary:

NESTED queries through projected Variant aliases retained the logical array owner even after their leaves were bound to a physical scan column. The owner could not bind to the read schema. Nested leaf mapping and document-count callbacks also used logical paths and could miss deeper physical groups.

### Details

- **Frequency:** NESTED over a renamed Variant root or projected Variant prefix.
- **Root cause:** nested_path and nested traversal callbacks did not follow the final physical field bindings.
- **Why existing tests missed it:** Owner and leaf names matched scan names; alias tests covered ordinary SEARCH rather than NESTED serialization and nested callbacks.
- **Fix:** Derive the physical nested owner from a normalized leaf binding during serialization, and translate callback paths through the same binding metadata in BE.
- **Other paths and branches:** Cover materialized slots and retained element_at children, root aliases and projected prefixes, and both nested leaf mapping and row counts. Logical DSL lookup keys remain unchanged.
- **Behavior change:** Aliased NESTED predicates use the final physical Variant owner and paths.

### Testing

- Old code: three new FE tests failed with doc.items instead of v.items/v.payload.items; the new BE test failed with doc.items.tags.name instead of v.payload.items.tags.name.
- Fixed code: 40 FE tests and 180 BE tests passed using run-fe-ut.sh and run-be-ut.sh.
- Built FE and ASAN BE with build.sh and passed FE Checkstyle, clang-format 16, clang-tidy, and BE build hygiene.
- Deployed FE/BE libraries, verified checksums, and passed all 19 targeted SEARCH/score/Variant regression suites with zero failures or skips.
- Full NESTED result-set execution remains unverified because the available community build lacks the NestedGroup module.

### Release note

Fix physical Variant path binding for NESTED SEARCH predicates through projected aliases.

### Check List (For Author)

- Behavior changed: Yes (see Details).
- Does this need documentation: No.
- Pre-submission status: Partially verified: full NESTED result-set execution requires a NestedGroup-capable build; the available community build does not provide that module.

### AI Disclosure

- Tool: OpenAI Codex.
- Generated: Code, tests, and this description.
- Human review: Pending.
@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review of #69344 at 9fe2b52: no new actionable findings. Review converged in round 1. Complete FE and BE reviewers and a separate NESTED risk reviewer all returned NO_NEW_VALUABLE_FINDINGS; all 32 changed paths and related call chains were covered, and all candidates and risk focuses have evidence-backed dispositions.

Existing review context: all six prior reports are addressed at this head. The P1 cache report (4488661491) is fixed by including deterministic logical-to-physical storage bindings before bitmap-cache lookup. The P2 reports for executable constant substitution (4488661665), computed DSL/options folding (4488662116), missing-path Variant alias metadata (4488662248), INTERSECT/EXCEPT sibling inference (4488662377), and NESTED physical owner resolution (4489149834) have matching fixes in the current code. None is reposted; no independently confirmed existing P0/P1 remains blocking.

Critical checkpoint conclusions:

  • Goal and proof: binding now exposes SEARCH dependencies before subquery expansion/pushdown, with final OLAP scan/index checks. Unit and regression sources cover CTE/NOT IN, aliases, joins and set operations; assertions were inspected, not executed.
  • Scope and reuse: changes stay within SEARCH binding, validation, physical metadata/cache identity and their tests; existing substitution, folding and index checks are reused.
  • Concurrency and locks: no new threads, shared mutation or locks; cache-key inputs and callback mappings are query scoped. No changed lock ordering or work under locks was identified.
  • Lifecycle and static initialization: resolver-owned bindings back its pointers, borrowed scan/provider objects remain in scope, and callback cleanup is guarded. No new cross-translation-unit static dependency.
  • Configuration: no new configuration or dynamic-setting contract.
  • Compatibility: existing optional SEARCH metadata is reused; no persisted-format or plugin ABI change identified. Mixed-version runtime behavior was not executed.
  • Thrift/protobuf: no gensrc definitions, field IDs or enum values change.
  • Parallel paths: extracted slots and retained element_at paths, scalar and Variant fields, ordinary and NESTED callbacks, scoring/cache paths, and CTE/project/join/set-operation substitutions were inspected. Updated signature callers are consistent.
  • Conditional checks and failures: new checks have concrete placement, constant-argument or physical-path invariants and explanatory comments. Unsupported executable shapes/index absence fail explicitly; eliminated SEARCH predicates avoid index checks.
  • Test coverage: FE tests cover binding, ambiguity/missing fields, invalid placement, empty DSL, index checks, constants and inference. BE tests cover cache mapping/order isolation and both physical NESTED callbacks. Regression sources include result-set and negative cases; complex CTE and several new shapes use EXPLAIN.
  • Changed results: binding_results.out agrees with inserted rows: city/country select 1/2, constant cases select 1, set-operation cases produce hello, and absent-city alias/direct cases select 1. Generation provenance was not independently reproduced.
  • Observability: existing binding diagnostics, explicit errors and cache/timing statistics cover these paths; no new distributed operation requiring added metrics/logs.
  • Transactions and persistence: no transaction, EditLog/replay, failover or persisted metadata write path changes.
  • Data writes and atomicity: production changes affect query planning/index reads/cache identity; no new data mutation or crash-recovery protocol.
  • FE/BE propagation: final physical parent/subpath metadata accompanies logical field keys; NESTED owner and both descendant callbacks use physical paths. No new wire variable is required.
  • Performance and memory: binding occurs once during analysis; deterministic signature construction sorts by field count, not row count. No new material allocation, ownership leak, redundant row-wise work or memory-accounting defect substantiated by inspection.
  • Other concerns: temporary NULL substitutions leave nonfoldable SEARCH unevaluated, so the suspected erroneous child NOT NULL inference was dismissed. No additional substantiated issue remained after the final sweep.

User focus: no additional focus was supplied; the whole PR was reviewed.

Validation limits: static inspection only; no builds, tests, benchmarks, source edits, submodule initialization or runtime validation were performed. The original complex sparse-Variant CTE regression is plan-only. Full enterprise NestedGroup result execution and its unavailable provider implementation remain outside the available evidence; serialization/callback tests do not prove those runtime results. These are explicit validation limits, not unresolved code candidates or uncovered changed paths.

Review converged after 1 round(s).

@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

run buildall

### Background

Search predicates depend on source-column indexes and analyzers even when column values are equivalent.

### What problem does this PR solve?

Issue Number: None
Related PR: apache#69344
PR Type: Bugfix

Problem Summary:

INTERSECT and EXCEPT can propagate MATCH or multi_match predicates to sibling columns with different analyzers, changing query results. Constant propagation can also replace multi_match column arguments with literals and leave an expression that cannot execute through an inverted index.

### Details

- **Frequency:** Search predicates across set-operation branches, or multi_match with an equality fixing a searched column.
- **Root cause:** Equivalent-replacement inference enumerated search classes, while sibling insertion excluded only SearchExpression. ConstantPropagation protected Match and SearchExpression but omitted MultiMatch.
- **Why existing tests missed it:** Inference cases did not combine equal-valued columns with different analyzers; multi_match was not tested with constant-equality filters.
- **Fix:** Add NonInferableExpression to Match, MultiMatch, Search and SearchExpression, and use that trait at all three inference admission checks. Separately preserve MultiMatch column arguments during constant propagation.
- **Other paths and branches:** Preserve ordinary predicate inference, alias substitution, and normal pushdown. Tests cover JOIN variants, INTERSECT/EXCEPT, expression wrappers, and a custom trait-bearing expression.
- **Behavior change:** Prevent incorrect search predicate propagation and retain executable multi_match scan dependencies.

### Testing

- Reproduced wrong INTERSECT/EXCEPT results for MATCH and multi_match on equal-valued columns with different analyzers before the fix.
- Reproduced the multi_match constant-propagation execution error before the fix.
- 164 FE tests passed using run-fe-ut.sh across six inference, constant-propagation, and SEARCH test classes.
- FE build and Checkstyle passed using build.sh --fe; deployed the resulting FE library and verified its checksum.
- Generated the new regression .out with run-regression-test.sh, checked all eight results, then passed 21 targeted regression suites with zero failures or skips.

### Release note

Fix incorrect MATCH and multi_match results across INTERSECT/EXCEPT branches with different analyzers, and fix multi_match execution errors when an equality fixes a searched column.

### Check List (For Author)

- Behavior changed: Yes (see Details).
- Does this need documentation: No.
- Pre-submission status: Ready.

### AI Disclosure

- Tool: OpenAI Codex.
- Generated: Code, tests, and this description.
- Human review: Pending.
@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes for one new P1: ordinary pushdown of an outer SEARCH into EXCEPT's subtracting input can turn an empty result into a returned row when the inputs use different index analyzers. The inline includes a concrete query and reduced plan. This is distinct from the earlier inner-branch predicate-inference report, whose specific route is fixed.

Static review covers all 42 changed files at 7846086ee011db502cd1be13da4412238568fb80, the relevant planner-to-storage invocation chains, and the existing review threads. No additional user-provided focus was specified.

Required checkpoint Conclusion
Goal and supporting proof Early slot binding addresses the CTE/NOT IN dependency problem and admits aliases/joins/UNION. Added planning and result tests support those paths, but the new outer-EXCEPT case remains incorrect.
Scope and clarity Changes stay within SEARCH binding/validation, related index-dependent predicate inference, score adaptation, and physical binding/cache handling.
Concurrency and locking No new threads, locks, shared metadata mutation, or blocking work under metadata locks was introduced. Rewrites and signature construction use query-local state.
Lifecycle and initialization No new static initialization dependency or ownership cycle found. Nested callback cleanup and cache-handle lifetimes remain scoped; physical column pointers remain borrowed from the scan schema. No new memory-safety or unchecked-Status defect confirmed.
Configuration No configuration item added; existing cache, scoring and folding controls remain in use.
Compatibility No persisted-format or wire-definition change. Cache signature changes cause misses. Existing optional binding metadata now carries final physical paths. Mixed-version execution was not run.
Thrift/protobuf IDs No gensrc/thrift or gensrc/proto changes.
Parallel paths Checked CTE producers/consumers, subqueries, projections, joins, UNION, INTERSECT/EXCEPT, direct/extracted Variant fields, score binding, missing paths and NESTED callbacks. Direct outer EXCEPT pushdown is the uncovered correctness case.
Conditional checks New branches have concrete missing/ambiguous/unsupported-input failure paths; comments explain delayed checks, alias identity and temporary inference replacements.
Test coverage Inspected positive and negative FE/BE tests plus regression suites for binding, placement, indexes, constant arguments/equalities, analyzer-dependent inference and cache isolation. Add an executing outer-EXCEPT regression for the P1.
Expected results Both new .out files agree with their fixtures: city/country cache queries select rows 1/2, missing-city paths add no matches, and inner-branch set-operation/analyzer checks have consistent results. Generation claims in the PR description were not independently executed.
Observability Existing field/index analysis errors, logical/physical binding diagnostics, cache counters and query timers remain available; no additional instrumentation requirement identified.
Transactions and persistence No EditLog, commit/replay, visible-version or delete-bitmap mutation; no new failover requirement.
Data writes and crash atomicity Production write paths are unchanged. Result bitmap ownership and cache copies remain intact.
FE/BE variable propagation No new transmitted field. Final descriptor parent/subpath metadata, physical nested callbacks and storage bindings were traced through their consumers.
Performance Binding is per filter; signature sorting is per segment/query over referenced fields. No new per-row copy, unbounded retry or material redundant work identified.
Other issues No additional verified finding after the repeated missed-path checks.

Earlier reports were revalidated against this code and are addressed: physical Variant cache identity (4488661491), executable constant propagation (4488661665), computed DSL/options (4488662116), extracted-alias missing-path metadata (4488662248), inner-branch sibling inference (4488662377), and NESTED physical-owner binding (4489149834). They are not reposted; no existing P0/P1 was confirmed still applicable.

Validation was static only: no builds, tests, benchmarks, source edits, or submodule initialization. The original sparse-Variant CTE regression is explicitly EXPLAIN-only; full NestedGroup execution and runtime compatibility remain unverified. These are execution limits, with no unresolved static candidate beyond the accepted P1.

Converged after 2 rounds: all three latest reviewer results are NO_NEW_VALUABLE_FINDINGS, required static coverage is complete, and every candidate/risk focus has an evidence-backed disposition. One new P1 is accepted; no unresolved candidate or uncovered changed path remains.

Review converged after 2 round(s).

@Override
public Rule build() {
return logicalFilter(logicalOlapScan())
return logicalFilter()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Preserve EXCEPT subtraction when pushing down outer SEARCH

The generalized binding now admits this reduced plan:

Filter(SEARCH(label:hello))
  Except
    Project(title AS label) -> Scan(t, title index: unicode)
    Project(body)           -> Scan(t, body index: keyword)

Using the new test_search_non_inferable fixture (both columns are VARCHAR(100) and both contain hello world), with spill disabled:

SELECT label FROM (
  SELECT title AS label FROM search_non_inferable
  EXCEPT SELECT body FROM search_non_inferable
) s WHERE search('label:hello');

The inner EXCEPT is empty, so the result must stay empty. However, PushDownFilterThroughSetOperation.addFiltersToNewChildren copies the bound SEARCH into both branches and removes the outer filter; project substitution maps its children to title/body. The Unicode index matches token hello, while the keyword index contains only hello world, so the rewritten EXCEPT returns hello world. Both final scan predicates pass index validation.

This is a separate route from the earlier inner-branch inference report: the new NonInferableExpression checks in InferPredicates never run for this ordinary pushdown. Preserve the subtracting input when pushing this predicate (or reject this unsupported outer placement), and add a result regression for the outer-SEARCH form.

### Background

Search predicates depend on the source column's index and analyzer, not only on its value. Set-operation pushdown substitutes each branch's output columns.

### What problem does this PR solve?

Issue Number: None
Related PR: apache#69344 (follow-up)
PR Type: Bugfix

Problem Summary:
Outer SEARCH predicates could be copied to branches with different analyzers, changing EXCEPT results. Rename NonInferableExpression to SourceDependentExpression and retain marked conjuncts above every set operation, including UNION ALL. Existing final SEARCH validation rejects unsupported placement.

### Details

- **Frequency:** Outer SEARCH on set operations with source-dependent matching.
- **Root cause:** Ordinary set-operation pushdown bypassed inference guards; UNION ALL had an unconditional fast path.
- **Why existing tests missed it:** Tests expected outer UNION SEARCH to be copied into each branch.
- **Fix:** Classify all conjuncts using the source-dependent marker while preserving existing volatile-expression handling.
- **Other paths and branches:** Inference guards and implementations use the renamed interface. Same-source alias pushdown remains allowed. MATCH/MultiMatch final validation is deferred.
- **Behavior change:** Outer SEARCH retained above set operations is rejected by existing FE validation; branch-local SEARCH remains supported.

### Testing

On ssh dev: 160 FE unit tests passed across CheckSearchUsageTest, PushDownFilterThroughSetOperationTest, InferPredicateByReplaceTest and InferPredicatesTest. Standard build.sh --fe with DISABLE_BUILD_UI=ON passed, including Checkstyle. Updated FE deployed.
Generated regression output using run-regression-test.sh. SEARCH directory: 47/48 suites passed, including changed suites. test_search_inverted_index_profile failed and failed independently on retry because InvertedIndexSearcherCacheHit was 0 instead of positive; its single-table query does not exercise set-operation pushdown.

### Release note

SEARCH predicates over set-operation outputs are rejected instead of being evaluated with different branch indexes or analyzers. SEARCH inside individual branches remains supported.

### Check List (For Author)

- Behavior changed: Yes (see Details)
- Does this need documentation: No
- Pre-submission status: Partially verified: one cache-profile regression remains failing as described in Testing.

### AI Disclosure

- Tool: Codex
- Generated: Code, tests, and commit description.
- Human review: Pending.
@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28315 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 9fe2b52da5ca3eadb36803edf2b8080803b32354, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17611	5137	5049	5049
q2	2153	308	253	253
q3	10237	1384	807	807
q4	4679	471	338	338
q5	7431	802	535	535
q6	175	186	154	154
q7	759	784	622	622
q8	9253	1334	1051	1051
q9	5623	4183	4176	4176
q10	6800	1303	1027	1027
q11	430	237	233	233
q12	633	400	273	273
q13	18080	3539	3036	3036
q14	278	272	245	245
q15	q16	719	706	662	662
q17	1185	737	565	565
q18	6964	6212	6123	6123
q19	1119	952	590	590
q20	379	332	223	223
q21	4823	2102	2227	2102
q22	343	296	251	251
Total cold run time: 99674 ms
Total hot run time: 28315 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5570	5724	5579	5579
q2	681	581	520	520
q3	5264	5434	4956	4956
q4	1986	2159	1518	1518
q5	5306	5218	5612	5218
q6	257	206	150	150
q7	2359	2086	1957	1957
q8	3082	2675	2717	2675
q9	8241	8130	8079	8079
q10	3819	3684	3204	3204
q11	621	434	418	418
q12	693	701	505	505
q13	3552	3899	3321	3321
q14	308	317	290	290
q15	q16	696	714	649	649
q17	8138	7064	6891	6891
q18	13004	12197	12858	12197
q19	882	768	782	768
q20	2144	2118	1924	1924
q21	5562	4579	4663	4579
q22	489	461	485	461
Total cold run time: 72654 ms
Total hot run time: 65859 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 154280 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 9fe2b52da5ca3eadb36803edf2b8080803b32354, data reload: false

query5	4312	513	367	367
query6	430	222	188	188
query7	4803	432	236	236
query8	327	164	160	160
query9	8822	3227	3239	3227
query10	482	311	258	258
query11	5918	3869	3576	3576
query12	150	92	89	89
query13	1270	442	356	356
query14	6092	3923	3663	3663
query14_1	3471	3485	3429	3429
query15	210	201	180	180
query16	923	445	432	432
query17	839	684	535	535
query18	2396	415	307	307
query19	188	171	130	130
query20	86	96	80	80
query21	209	130	105	105
query22	13007	13029	12964	12964
query23	13452	12947	12361	12361
query23_1	12557	12560	12636	12560
query24	7382	824	484	484
query24_1	478	468	472	468
query25	513	400	331	331
query26	1258	257	148	148
query27	2771	438	269	269
query28	4629	2070	2042	2042
query29	1563	586	439	439
query30	290	219	190	190
query31	1011	931	821	821
query32	145	90	86	86
query33	538	291	240	240
query34	874	850	499	499
query35	748	752	665	665
query36	829	831	743	743
query37	123	97	86	86
query38	1778	1756	1656	1656
query39	790	809	746	746
query39_1	729	730	738	730
query40	217	119	110	110
query41	66	64	68	64
query42	90	88	86	86
query43	347	362	316	316
query44	1300	703	726	703
query45	179	185	170	170
query46	849	983	566	566
query47	2810	3067	2601	2601
query48	307	315	227	227
query49	587	403	301	301
query50	694	286	214	214
query51	10527	10658	10348	10348
query52	83	83	78	78
query53	193	211	160	160
query54	253	204	188	188
query55	79	67	65	65
query56	252	231	242	231
query57	1683	1609	1567	1567
query58	290	274	267	267
query59	2146	2200	1986	1986
query60	303	259	246	246
query61	171	169	169	169
query62	393	357	289	289
query63	198	166	169	166
query64	2902	1077	964	964
query65	3319	3279	3283	3279
query66	1771	440	308	308
query67	22668	22630	22554	22554
query68	2951	989	615	615
query69	390	300	251	251
query70	880	792	782	782
query71	296	241	216	216
query72	2731	2609	2313	2313
query73	516	517	295	295
query74	4652	4542	4364	4364
query75	2367	2295	1926	1926
query76	2312	1036	605	605
query77	354	372	287	287
query78	9805	9759	9226	9226
query79	1008	889	536	536
query80	1324	426	359	359
query81	591	321	265	265
query82	608	140	121	121
query83	294	209	196	196
query84	322	123	101	101
query85	1004	421	379	379
query86	383	238	216	216
query87	1996	1956	1827	1827
query88	3780	2774	2781	2774
query89	339	286	242	242
query90	1887	167	166	166
query91	163	154	133	133
query92	93	86	84	84
query93	1006	995	577	577
query94	557	298	269	269
query95	578	329	309	309
query96	680	504	241	241
query97	2457	2439	2318	2318
query98	158	153	149	149
query99	771	756	664	664
Total cold run time: 238006 ms
Total hot run time: 154280 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 9fe2b52da5ca3eadb36803edf2b8080803b32354, data reload: false

query1	0.00	0.00	0.00
query2	0.10	0.05	0.05
query3	0.26	0.09	0.09
query4	1.60	0.10	0.11
query5	0.23	0.24	0.23
query6	1.17	0.68	0.68
query7	0.04	0.01	0.01
query8	0.05	0.03	0.04
query9	0.42	0.36	0.36
query10	0.58	0.58	0.58
query11	0.21	0.15	0.14
query12	0.18	0.15	0.16
query13	0.50	0.49	0.50
query14	1.05	1.03	1.00
query15	0.64	0.63	0.60
query16	0.35	0.36	0.35
query17	1.15	1.13	1.14
query18	0.20	0.18	0.18
query19	2.07	2.04	1.93
query20	0.02	0.02	0.01
query21	15.50	0.18	0.13
query22	4.91	0.05	0.06
query23	16.27	0.34	0.13
query24	1.07	0.28	0.16
query25	0.10	0.06	0.04
query26	0.76	0.21	0.17
query27	0.03	0.03	0.03
query28	3.63	0.85	0.38
query29	12.46	4.08	3.21
query30	0.28	0.13	0.10
query31	2.77	0.57	0.33
query32	3.23	0.59	0.50
query33	3.24	3.17	3.32
query34	15.38	4.05	3.38
query35	3.36	3.34	3.34
query36	0.56	0.47	0.43
query37	0.10	0.06	0.07
query38	0.05	0.04	0.03
query39	0.04	0.03	0.03
query40	0.17	0.15	0.14
query41	0.09	0.03	0.02
query42	0.04	0.03	0.03
query43	0.05	0.04	0.03
Total cold run time: 94.91 s
Total hot run time: 24 s

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Review recovery stopped: shared time budget or helper timeout exhausted
Workflow run: https://github.com/apache/doris/actions/runs/42191195834

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (4/4) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 67.69% (32418/47895)
Line Coverage 52.58% (342750/651840)
Region Coverage 48.08% (278796/579868)
Branch Coverage 50.13% (132367/264045)

@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Review setup stopped before context preparation ran; inspect the first failed or cancelled setup step.
Workflow run: https://github.com/apache/doris/actions/runs/42741384263

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Review setup stopped before context preparation ran; inspect the first failed or cancelled setup step.
Workflow run: https://github.com/apache/doris/actions/runs/42767420749

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes: the existing P1 outer SEARCH/EXCEPT finding still applies to head 7846086ee011db502cd1be13da4412238568fb80. Early binding exposes the set-output slot, and ordinary PushDownFilterThroughSetOperation copies SEARCH into every EXCEPT input. With equal hello world strings, the Unicode title branch matches hello while the keyword body branch does not, so filtering an empty EXCEPT can incorrectly produce a row. The new inference guards do not protect this ordinary pushdown. This invocation adds no duplicate inline finding.

All 42 changed paths were reviewed statically by the main reviewer and two complete-scope reviewers, with an additional independent reviewer for the identified risks. All three returned NO_NEW_VALUABLE_FINDINGS; no candidate or risk question remains unclassified. The earlier cache-identity, constant-propagation, computed-argument, missing-Variant-path, sibling-inference and NESTED-owner reports are addressed at this head.

Critical checkpoint Conclusion
Goal and evidence Early slot binding supports CTE/NOT IN, visible aliases and UNION dependencies; inspected FE/regression tests cover these shapes. The existing EXCEPT correctness defect prevents approval.
Scope, clarity and reuse Changes stay within binding, validation, inference and physical field identity. The shared non-inference trait replaces duplicated concrete-type checks.
Concurrency and locking Query-local rewrites introduce no threads or new shared mutable state/locks. Existing cache synchronization is reused; no new lock-order or heavy-lock work identified.
Lifecycle and initialization Resolver owns copied bindings; nested callbacks are reset before borrowed readers/providers expire. No new ownership cycle or cross-translation-unit static dependency.
Configuration No new configuration or dynamic-refresh obligation.
Compatibility Existing wire fields are reused; no persistent format change. Updated BE accepts direct-name bindings; final FE metadata supplies physical aliases. Cache signatures intentionally separate storage identities. Mixed-version runtime behavior was not executed.
Thrift/protobuf IDs No definition, field-ID or enum-value change.
Parallel paths Checked MATCH/multi_match/SEARCH, both sibling-inference admission paths, SCORE binding, extracted/retained Variant paths and shared cloud/local query paths. Ordinary set-operation pushdown remains the known exception above.
Conditional checks and errors Constant-string, unique visible-field, Variant-parent, final scan-shape and index checks have concrete failure cases. FE AnalysisException and existing BE Status conversion remain in use.
Test coverage Inspected positive/negative unit and regression coverage for aliases, CTE thresholds, joins, UNION, empty DSL, index timing, cache isolation, absent paths and inference. Outer SEARCH/EXCEPT needs the regression described in the existing finding.
Changed expected results Both new .out files (16 binding sections and eight non-inference sections) agree with their fixtures and analyzer semantics by static inspection.
Observability Existing field-specific analysis errors and logical/storage binding diagnostics remain useful; no additional critical metric requirement identified.
Transactions and persistence No EditLog, transaction, failover or persistent metadata modification.
Data writes and atomicity No write-path change. Visible-version and delete-bitmap invariants are unaffected; query result correctness remains affected by the existing EXCEPT issue.
FE/BE propagation Traced final descriptor/element_at metadata through Thrift, vsearch input collection, cache signatures and NESTED owner/leaf callbacks. No new transmitted variable or omitted sending path identified.
Performance and memory Cache binding canonicalization costs O(F log F) for F fields with local copies. No substantial new allocation, ownership leak or demonstrated hot-path regression identified; no performance measurements run.
Other invariants Preserved SEARCH bitmap/NULL handling, checked temporary NULL substitutions and final executable-child validation. No BE include/PCH/template/unity or new libc dependency change.

No additional user focus was supplied. Validation limits: this was a static-only review; no builds, tests, benchmarks or SQL execution ran. The original complex sparse-Variant CTE case is mainly covered by EXPLAIN, and full enterprise NestedGroup result execution remains unverified; the new tests inspect serialization and callbacks. These runtime limits are distinct from completed static coverage. Review coverage converged in one round, with the existing P1 retained and no new findings.

Review converged after 1 round(s).

Existing P0/P1 findings confirmed for this head: #69344 (comment)

@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

/review

@feiniaofeiafei

Copy link
Copy Markdown
Collaborator Author

run buildall

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review of head b5be6ee found one new P2: the blanket source-dependent set-operation guard rejects existing MATCH queries above UNION ALL even when both branches use the same indexed source. The inline finding gives the query and rewrite chain. No new P0/P1 was verified.

All seven existing threads were independently rechecked and are addressed at this head: Variant cache identity (4488661491) now includes final physical metadata and sorted storage bindings; executable constant propagation (4488661665) preserves SEARCH/multi_match dependencies; computed arguments (4488662116) fold before parsing; extracted Variant aliases (4488662248) retain physical metadata; sibling inference (4488662377) is trait-guarded; NESTED aliases (4489149834) remap physical owners and callbacks; outer SEARCH over EXCEPT (4490192381) is retained and rejected instead of changing subtraction. Neither prior P1 remains applicable, so existing_blocking_comment_ids is empty. No duplicate finding is posted.

The required checkpoints were assessed as follows:

Checkpoint Conclusion
Goal and proof Early visible-slot binding precedes subquery expansion and preserves CTE/NOT IN dependencies. Added positive and negative tests exercise this, but the new MATCH compatibility regression remains. Tests were inspected only.
Scope and clarity Changes stay within SEARCH binding, source-preservation rules, physical field mapping, cache identity and their tests; the broad set-operation guard needs refinement.
Concurrency and locking No new thread entry, shared-state synchronization or lock ordering is introduced. Evaluation-local maps and existing cache handles retain their ownership model.
Lifecycle and static initialization Resolver binding storage and synchronous callback lifetimes remain valid; existing scoped reset and cache-handle cleanup remain intact. No new cross-translation-unit static dependency.
Configuration No configuration items are added. Existing folding/cache/scoring settings are respected in the inspected paths.
Compatibility Existing thrift fields are reused; no persisted format changes. Same-source MATCH-over-UNION-ALL SQL compatibility regresses as reported. Mixed-version execution was not run.
Thrift/protobuf IDs No gensrc/thrift or gensrc/proto definitions change.
Parallel paths MATCH, multi_match, scalar/bound SEARCH, sibling JOIN/set inference, aliases, spill conversion and Variant materialized/element_at paths were traced. The shared pushdown guard causes the reported regression.
Conditions and failures Added conditions explain source/index or physical-path assumptions; unsupported executable SEARCH shapes fail analysis. The retained MATCH fails the existing final scan-placement check.
Test coverage FE, BE and regression sources cover binding, ambiguity, invalid placement, eliminated predicates, constants, cache isolation, missing paths and set inference. Positive outer MATCH-over-UNION-ALL coverage is missing.
Changed expected results Both new .out files were checked against fixture rows, analyzer semantics and ordering; their expected results are consistent. Generation provenance was not independently established.
Observability Existing analysis errors, index binding diagnostics and cache counters cover these paths; no additional logging or metrics requirement was identified.
Transactions and persistence No transaction, EditLog, replay or master-failover behavior changes. Committed-version visibility and MoW delete-bitmap handling are untouched.
Writes and atomicity No data-write, publish, compaction or crash-recovery path changes.
FE/BE values Final physical Variant parent/path is serialized for extracted slots and retained element_at; NESTED owner/leaf mapping and storage-aware cache identity were traced through BE consumers.
Performance and memory Stable cache identity adds O(F log F) sorting for F bound fields per lookup. No new long-lived allocation, ownership cycle or unbounded loop was found; no benchmark was run.
Other issues No additional substantiated issue after the missed-issue recheck.

No additional user focus was supplied. All changed files, existing threads and risk items have dispositions; there is no remaining static changed-file coverage gap. Validation is limited to source/diff/test-output inspection: no build, tests, sanitizer, benchmark, final ELF audit, mixed-version cluster or enterprise NestedGroup execution ran. Enterprise NestedGroup internals are outside this checkout; the available serialization/callback code and tests were inspected. Author-reported runtime results are not independent validation.

Review converged after 2 round(s).

Set<Expression> keptAboveConjuncts = new LinkedHashSet<>();
for (Expression conjunct : origFilter.getConjuncts()) {
// Equal values in different branches may use different indexes or analyzers.
if (conjunct.containsType(SourceDependentExpression.class)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve MATCH pushdown through same-source UNION ALL

Match now implements SourceDependentExpression, so this guard also strands previously supported MATCH predicates above UNION ALL. For example, with an inverted index on t.title:

SELECT label FROM (
  SELECT title AS label FROM t WHERE id = 1
  UNION ALL
  SELECT title FROM t WHERE id = 2
) s WHERE label MATCH 'hello';

The reduced tree is Filter(Match(label)) -> UnionAll(Project(title AS label) -> Scan(t), Project(title) -> Scan(t)). Previously, addFiltersToNewChildren and project substitution moved MATCH to both scans. Now the rule retains it above the union, and CheckAfterRewrite.checkMatchIsUsedCorrectly throws only support in olapScan filter. Both branches use the same physical column/index/analyzer, so the previous pushdown preserves source semantics. Preserve this safe existing case and add a positive MATCH-over-UNION-ALL regression. This is distinct from the earlier outer-SEARCH/EXCEPT finding, which involved different analyzers and incorrect subtraction.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out. I agree that pushing MATCH into both UNION ALL branches is safe in this example because both branches read the same indexed column. This PR keeps the conservative behavior, so the outer MATCH in your example will be rejected. I'll defer support for same-source MATCH pushdown through UNION ALL to a follow-up, where we can verify the column source in every branch and add a positive regression test.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants