Skip to content

[improvement](subquery) Improve mark join slot inference to eliminate redundant mark joins - #66482

Open
starocean999 wants to merge 6 commits into
apache:masterfrom
starocean999:master_markjoin
Open

[improvement](subquery) Improve mark join slot inference to eliminate redundant mark joins#66482
starocean999 wants to merge 6 commits into
apache:masterfrom
starocean999:master_markjoin

Conversation

@starocean999

@starocean999 starocean999 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

close #66435
Problem Summary:

The old ExpressionUtils.canInferNotNullForMarkSlot only returned a single boolean telling whether every mark join slot in a predicate could be treated as non-nullable (its null value could be replaced by false). That coarse, predicate-level answer limited how far the optimizer could go in eliminating redundant mark joins.

This PR replaces it with ExpressionUtils.inferMarkSlotNotNullMap, which returns per-mark-slot information as a
Map<MarkJoinSlotReference, Pair<Boolean, Boolean>>:

  • Pair.first is computed on the predicate simplified by TrySimplifyPredicateWithMarkJoinSlot (conjuncts without any mark slot in And are replaced by true, in Or by false): it is true when the simplified predicate taking false or null always evaluates to false or null, meaning the mark slot's null value can be replaced by false (the mark slot can be non-nullable).
  • Pair.second is computed on the original predicate: it is true when taking false or null always evaluates to false or null on the original predicate.

SubqueryToApply now consumes this map: when Pair.second is true, the mark slot conjunct is replaced by the true literal and the mark slot is eliminated from the LogicalApply, so an IN subquery used directly as a filter or join ON conjunct is unnested into a plain LEFT_SEMI_JOIN, and a NOT IN into a plain NULL_AWARE_LEFT_ANTI_JOIN, without materializing a mark column. The duplicated inference and replacement logic is extracted into simplifyConjunctWithMarkJoinSlot.

Release note

None

Check List (For Author)

  • Test: Unit Test
  • Behavior changed: No
  • Does this need documentation: No

What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

Release note

None

Check List (For Author)

  • Test

    • Regression test
    • 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
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

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?

@starocean999

Copy link
Copy Markdown
Contributor Author

/review

@starocean999

Copy link
Copy Markdown
Contributor Author

run buildall

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: All Codex review accounts are usage-limited; earliest retry is 2026-08-08T03:32:00Z.
Workflow run: https://github.com/apache/doris/actions/runs/30993172234

Please trigger /review again after that time.

@starocean999

Copy link
Copy Markdown
Contributor Author

/review

@starocean999

Copy link
Copy Markdown
Contributor Author

run buildall

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: All Codex review accounts are usage-limited; earliest retry is 2026-08-08T03:32:00Z.
Workflow run: https://github.com/apache/doris/actions/runs/31062124745

Please trigger /review again after that time.

@starocean999

Copy link
Copy Markdown
Contributor Author

/review

@starocean999

Copy link
Copy Markdown
Contributor Author

run buildall

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: All Codex review accounts are usage-limited; earliest retry is 2026-08-08T03:32:00Z.
Workflow run: https://github.com/apache/doris/actions/runs/31068116341

Please trigger /review again after that time.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 46.94% (69/147) 🎉
Increment coverage report
Complete coverage report

@starocean999

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 41.86% (72/172) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17693	3972	3990	3972
q2	2027	328	201	201
q3	10335	1370	812	812
q4	4680	470	338	338
q5	7509	822	582	582
q6	179	172	134	134
q7	746	823	605	605
q8	9359	1538	1632	1538
q9	5398	4108	4072	4072
q10	6748	1654	1350	1350
q11	508	367	326	326
q12	757	579	466	466
q13	18098	3298	2725	2725
q14	265	250	243	243
q15	q16	725	731	666	666
q17	1068	921	1031	921
q18	6562	5655	5559	5559
q19	1156	1259	1056	1056
q20	842	667	545	545
q21	6050	2768	2694	2694
q22	460	373	337	337
Total cold run time: 101165 ms
Total hot run time: 29142 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5019	4703	4669	4669
q2	283	329	213	213
q3	4868	5344	4662	4662
q4	2201	2291	1414	1414
q5	4671	4469	4388	4388
q6	232	179	130	130
q7	1943	1706	1443	1443
q8	2321	2059	2004	2004
q9	7108	6721	6675	6675
q10	4278	4210	3814	3814
q11	512	369	339	339
q12	706	709	486	486
q13	2918	3232	2745	2745
q14	273	280	245	245
q15	q16	649	681	598	598
q17	1237	1229	1215	1215
q18	12088	11018	11769	11018
q19	1118	1086	1081	1081
q20	2192	2198	1912	1912
q21	5271	4758	4606	4606
q22	501	461	421	421
Total cold run time: 60389 ms
Total hot run time: 54078 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 166247 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 f86f0fe8076b1dfeb2478cc7dd75ea202d881da2, data reload: false

query5	4340	602	456	456
query6	478	214	217	214
query7	4835	571	339	339
query8	319	161	146	146
query9	8772	4074	4064	4064
query10	465	360	317	317
query11	5865	2189	2002	2002
query12	150	100	94	94
query13	1235	602	435	435
query14	6106	4268	3962	3962
query14_1	3795	3788	3777	3777
query15	200	195	173	173
query16	990	464	474	464
query17	921	695	544	544
query18	2427	465	336	336
query19	203	185	159	159
query20	107	103	102	102
query21	231	160	138	138
query22	13014	12926	12833	12833
query23	15984	15022	14700	14700
query23_1	14822	14745	14704	14704
query24	7718	1732	1242	1242
query24_1	1269	1272	1254	1254
query25	557	466	399	399
query26	1337	347	211	211
query27	2600	586	378	378
query28	4553	2034	2038	2034
query29	1089	651	502	502
query30	362	273	232	232
query31	1213	1128	1055	1055
query32	119	62	63	62
query33	538	324	254	254
query34	1211	1167	642	642
query35	743	752	651	651
query36	788	776	728	728
query37	161	111	94	94
query38	1832	1764	1686	1686
query39	834	823	804	804
query39_1	791	790	779	779
query40	254	175	152	152
query41	74	71	70	70
query42	99	98	101	98
query43	324	329	282	282
query44	1501	788	775	775
query45	190	178	172	172
query46	1052	1152	718	718
query47	1524	1531	1413	1413
query48	415	441	298	298
query49	593	413	303	303
query50	1077	438	339	339
query51	10632	10605	10532	10532
query52	90	97	85	85
query53	267	293	195	195
query54	318	244	233	233
query55	81	76	74	74
query56	310	317	323	317
query57	1011	986	930	930
query58	292	275	264	264
query59	1550	1593	1339	1339
query60	322	287	273	273
query61	179	178	197	178
query62	405	318	263	263
query63	233	206	200	200
query64	2906	1031	889	889
query65	3854	3701	3771	3701
query66	1836	502	367	367
query67	28102	28111	27924	27924
query68	3169	1710	988	988
query69	415	304	262	262
query70	915	787	784	784
query71	373	343	303	303
query72	3105	2687	2343	2343
query73	902	759	450	450
query74	4632	4522	4279	4279
query75	2402	2344	1976	1976
query76	2348	1152	767	767
query77	348	360	287	287
query78	11131	11185	10532	10532
query79	1380	1231	755	755
query80	1240	560	478	478
query81	562	325	284	284
query82	642	183	134	134
query83	376	346	299	299
query84	335	161	132	132
query85	970	631	529	529
query86	403	243	231	231
query87	2002	1980	1845	1845
query88	3795	2859	2799	2799
query89	399	323	294	294
query90	1903	207	197	197
query91	213	200	164	164
query92	69	63	59	59
query93	1785	1472	969	969
query94	702	352	314	314
query95	812	598	492	492
query96	1151	792	355	355
query97	2462	2487	2327	2327
query98	196	189	188	188
query99	738	729	612	612
Total cold run time: 254447 ms
Total hot run time: 166247 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.09	0.04	0.05
query3	0.26	0.14	0.13
query4	1.61	0.14	0.14
query5	0.24	0.22	0.22
query6	1.17	0.80	0.79
query7	0.03	0.00	0.00
query8	0.06	0.04	0.04
query9	0.38	0.31	0.31
query10	0.55	0.54	0.58
query11	0.19	0.14	0.15
query12	0.18	0.15	0.14
query13	0.47	0.46	0.47
query14	1.00	0.99	1.00
query15	0.60	0.59	0.61
query16	0.31	0.33	0.30
query17	1.10	1.05	1.11
query18	0.20	0.19	0.19
query19	2.04	1.93	1.98
query20	0.02	0.01	0.01
query21	15.45	0.20	0.13
query22	4.94	0.04	0.05
query23	16.13	0.31	0.12
query24	3.02	0.41	0.34
query25	0.11	0.04	0.06
query26	0.74	0.21	0.14
query27	0.06	0.03	0.04
query28	3.55	0.73	0.33
query29	12.54	4.09	3.25
query30	0.27	0.16	0.16
query31	2.77	0.55	0.32
query32	3.22	0.59	0.48
query33	3.20	3.12	3.25
query34	15.73	3.93	3.30
query35	3.24	3.23	3.22
query36	0.55	0.44	0.43
query37	0.09	0.07	0.06
query38	0.04	0.03	0.04
query39	0.04	0.03	0.02
query40	0.18	0.16	0.14
query41	0.08	0.04	0.04
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.54 s
Total hot run time: 23.82 s

@starocean999

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17848	3993	3952	3952
q2	2013	313	205	205
q3	10310	1379	801	801
q4	4682	475	342	342
q5	7480	829	561	561
q6	173	167	137	137
q7	768	806	591	591
q8	9328	1603	1567	1567
q9	5351	4121	4088	4088
q10	6709	1597	1368	1368
q11	496	360	320	320
q12	719	574	457	457
q13	18088	3282	2705	2705
q14	260	259	248	248
q15	q16	730	731	656	656
q17	997	1035	966	966
q18	6451	5581	5533	5533
q19	1376	1207	1012	1012
q20	817	667	557	557
q21	5809	2639	2319	2319
q22	424	354	288	288
Total cold run time: 100829 ms
Total hot run time: 28673 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4289	4196	4182	4182
q2	272	316	203	203
q3	4524	4886	4337	4337
q4	2132	2249	1398	1398
q5	4220	4063	4068	4063
q6	223	172	124	124
q7	1658	1605	1405	1405
q8	2452	2301	2079	2079
q9	7397	7380	7423	7380
q10	4353	4230	3795	3795
q11	532	398	360	360
q12	716	728	499	499
q13	3197	3436	2965	2965
q14	299	326	265	265
q15	q16	686	714	640	640
q17	1311	1301	1288	1288
q18	12192	11022	12009	11022
q19	1181	1236	1150	1150
q20	2225	2205	1923	1923
q21	5701	4823	4804	4804
q22	540	482	462	462
Total cold run time: 60100 ms
Total hot run time: 54344 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 165997 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 86ce3f1ace3e8c3e847d50c08245eb02583e86a7, data reload: false

query5	4333	578	440	440
query6	458	223	197	197
query7	4838	585	331	331
query8	321	161	140	140
query9	8774	4014	4014	4014
query10	461	354	300	300
query11	5690	2178	1998	1998
query12	155	97	95	95
query13	1237	618	423	423
query14	6045	4224	3936	3936
query14_1	3764	3725	3741	3725
query15	195	193	179	179
query16	978	454	430	430
query17	886	673	548	548
query18	2415	458	337	337
query19	207	185	152	152
query20	104	102	101	101
query21	237	159	134	134
query22	12956	12946	12722	12722
query23	15719	15034	14650	14650
query23_1	15384	15040	15067	15040
query24	7923	1776	1223	1223
query24_1	1221	1252	1231	1231
query25	519	400	345	345
query26	1313	347	218	218
query27	2586	567	385	385
query28	4561	2027	2016	2016
query29	1041	618	476	476
query30	341	250	220	220
query31	1168	1106	1040	1040
query32	107	58	56	56
query33	506	297	232	232
query34	1191	1173	635	635
query35	723	732	617	617
query36	771	777	669	669
query37	154	111	87	87
query38	1828	1762	1684	1684
query39	817	814	796	796
query39_1	773	796	778	778
query40	242	159	136	136
query41	63	60	59	59
query42	93	88	91	88
query43	314	325	265	265
query44	1479	772	770	770
query45	180	172	164	164
query46	1077	1154	694	694
query47	1494	1557	1445	1445
query48	406	387	272	272
query49	598	403	295	295
query50	1008	425	332	332
query51	10510	10451	10626	10451
query52	87	84	79	79
query53	267	281	203	203
query54	283	237	207	207
query55	74	71	64	64
query56	317	298	305	298
query57	1072	1016	901	901
query58	291	261	271	261
query59	1514	1586	1387	1387
query60	318	292	271	271
query61	178	176	173	173
query62	396	323	276	276
query63	234	197	192	192
query64	2947	1019	842	842
query65	3878	3771	3769	3769
query66	1836	448	342	342
query67	28219	27567	28030	27567
query68	3133	1568	1005	1005
query69	419	312	260	260
query70	863	757	796	757
query71	377	312	315	312
query72	2987	2647	2393	2393
query73	857	791	425	425
query74	4629	4483	4277	4277
query75	2395	2340	1984	1984
query76	2348	1173	764	764
query77	344	367	287	287
query78	11153	11055	10547	10547
query79	1387	1112	749	749
query80	686	585	493	493
query81	463	329	295	295
query82	632	174	156	156
query83	434	349	301	301
query84	335	168	140	140
query85	1012	696	600	600
query86	315	227	218	218
query87	1993	1964	1819	1819
query88	3754	2800	2833	2800
query89	402	316	272	272
query90	1924	218	185	185
query91	202	194	158	158
query92	60	62	52	52
query93	1611	1625	1068	1068
query94	543	357	294	294
query95	778	599	473	473
query96	1015	862	348	348
query97	2477	2481	2354	2354
query98	185	186	185	185
query99	727	721	612	612
Total cold run time: 252454 ms
Total hot run time: 165997 ms

@hello-stephen

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

query1	0.01	0.00	0.01
query2	0.08	0.05	0.05
query3	0.25	0.14	0.13
query4	1.60	0.14	0.14
query5	0.24	0.22	0.22
query6	1.16	0.85	0.80
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.38	0.30	0.30
query10	0.54	0.55	0.59
query11	0.19	0.14	0.13
query12	0.18	0.15	0.14
query13	0.46	0.47	0.47
query14	1.00	1.00	0.99
query15	0.60	0.59	0.58
query16	0.32	0.32	0.32
query17	1.10	1.08	1.06
query18	0.21	0.20	0.19
query19	2.06	1.97	1.91
query20	0.02	0.01	0.02
query21	15.56	0.20	0.13
query22	4.94	0.05	0.05
query23	16.14	0.31	0.11
query24	2.95	0.42	0.30
query25	0.11	0.05	0.04
query26	0.72	0.22	0.15
query27	0.05	0.04	0.03
query28	3.49	0.75	0.35
query29	12.50	4.00	3.19
query30	0.26	0.15	0.15
query31	2.77	0.55	0.32
query32	3.22	0.58	0.48
query33	3.23	3.19	3.12
query34	15.66	3.92	3.29
query35	3.24	3.21	3.23
query36	0.54	0.42	0.42
query37	0.09	0.06	0.06
query38	0.05	0.03	0.04
query39	0.04	0.02	0.02
query40	0.17	0.16	0.15
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.39 s
Total hot run time: 23.73 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 97.30% (72/74) 🎉
Increment coverage report
Complete coverage report

@starocean999
starocean999 marked this pull request as ready for review August 7, 2026 03:07
@morrySnow morrySnow changed the title [improvement](fe) Improve mark join slot inference to eliminate redundant mark joins [improvement](subquery) Improve mark join slot inference to eliminate redundant mark joins Aug 7, 2026
@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 61.86% (73/118) 🎉
Increment coverage report
Complete coverage report

boolean sameResultForFalseAndNull = true;
boolean simplifiedForFalseAndNull = true;
for (int i = 0; i < loopCount; ++i) {
replaceMap.clear();

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.

Optimization: missing early exit when both booleans are already false.

Once both sameResultForFalseAndNull and simplifiedForFalseAndNull have been set to false, no further iteration can flip them back to true. Adding if (!sameResultForFalseAndNull && !simplifiedForFalseAndNull) break; (or return Pair.of(false, false)) would short-circuit the remaining loop iterations.

In the worst case (4 mark slots → 27 inner iterations), the current code always runs all 27 iterations per target slot (108 iterations total for 4 targets), doing 4 FoldConstantRule.evaluate + 4 ExpressionUtils.replace calls per iteration (432 evaluations total). In many practical predicates both flags will be set to false by the first few iterations. An early exit could significantly reduce this.

(predicate.collect(MarkJoinSlotReference.class::isInstance)));
int markSlotSize = markJoinSlotReferenceList.size();
int maxMarkSlotCount = 4;
// if the conjunct has mark slot, and maximum 4 mark slots(for performance)

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.

Style: maxMarkSlotCount = 4 should be a private static final class-level constant.

As a local variable inside inferMarkSlotNotNullMap, the rationale for the limit (performance guard against 3^k combinatorial explosion) and the specific value 4 are harder to discover. Extracting it to a named constant at the class level, e.g. private static final int MAX_MARK_SLOT_COUNT_FOR_INFERENCE = 4, with a brief comment explaining the 3^k scaling, would make the performance contract explicit.

public static Map<MarkJoinSlotReference, Pair<Boolean, Boolean>> inferMarkSlotNotNullMap(
Expression predicate, ExpressionRewriteContext ctx) {
ExpressionRewriteContext rewriteContext = new ExpressionRewriteContext(ctx.cascadesContext);
Expression simplifiedPredicate = TrySimplifyPredicateWithMarkJoinSlot.INSTANCE.rewrite(predicate,

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.

Minor: the ExpressionRewriteContext created here discards the plan from the caller's context.

ExpressionRewriteContext rewriteContext = new ExpressionRewriteContext(ctx.cascadesContext);
Expression simplifiedPredicate = TrySimplifyPredicateWithMarkJoinSlot.INSTANCE.rewrite(predicate,
        rewriteContext);

The passed-in ctx may have been constructed with a Plan (see simplifyConjunctWithMarkJoinSlot at SubqueryToApply.java line 157-158: plan == null ? new ExpressionRewriteContext(cascadesContext) : new ExpressionRewriteContext(plan, cascadesContext)). Here a fresh context is created from just cascadesContext, discarding the plan. While TrySimplifyPredicateWithMarkJoinSlot is a purely structural rewrite that doesn't depend on plan state today, the plan-less context is then also what flows into the fold-constant evaluation (via the original ctx). The two-context pattern is confusing — consider either reusing the caller's context directly for the simplify step, or adding a comment explaining why a fresh plan-less context is intentionally used here.

: false;
Map<MarkJoinSlotReference, Pair<Boolean, Boolean>> markSlotsInfo;
if (join.getJoinType().isInnerOrCrossJoin() || join.getJoinType().isSemiJoin()) {
Pair<Expression, Map<MarkJoinSlotReference, Pair<Boolean, Boolean>>> simplifyResult =

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.

Question: why are outer/anti/asof joins excluded from mark-slot simplification?

The condition join.getJoinType().isInnerOrCrossJoin() || join.getJoinType().isSemiJoin() excludes LEFT/RIGHT OUTER, ANTI, and ASOF joins from the simplifyConjunctWithMarkJoinSlot call, falling back to an empty markSlotsInfo map. This means mark join elimination (info.second) and non-nullable mark inference (info.first) are both skipped for those join types.

For ANTI joins: the buildRules filter path (line 30-43) does call simplifyConjunctWithMarkJoinSlot unconditionally. But in the join-ON path, a NOT IN subquery in an ANTI join's ON clause would skip the simplification entirely. While this is conservative and correct, a brief comment explaining why these join types can't be simplified would help future readers (e.g., "outer joins preserve NULLs from the nullable side, making mark slot three-valued semantics observable").

…dant mark joins

### What problem does this PR solve?

Problem Summary:

The old `ExpressionUtils.canInferNotNullForMarkSlot` only returned a single
boolean telling whether every mark join slot in a predicate could be treated as
non-nullable (its null value could be replaced by false). That coarse,
predicate-level answer limited how far the optimizer could go in eliminating
redundant mark joins.

This PR replaces it with `ExpressionUtils.inferMarkSlotNotNullMap`, which returns
per-mark-slot information as a
`Map<MarkJoinSlotReference, Pair<Boolean, Boolean>>`:
- `Pair.first` is computed on the predicate simplified by
  `TrySimplifyPredicateWithMarkJoinSlot` (conjuncts without any mark slot in `And`
  are replaced by true, in `Or` by false): it is true when the simplified
  predicate taking false or null always evaluates to false or null, meaning the
  mark slot's null value can be replaced by false (the mark slot can be
  non-nullable).
- `Pair.second` is computed on the original predicate: it is true when taking
  false or null always evaluates to false or null AND taking true always
  evaluates to true, meaning the predicate is equivalent to the mark slot being
  true.

`SubqueryToApply` now consumes this map: when `Pair.second` is true, the mark
slot conjunct is replaced by the true literal and the mark slot is eliminated
from the `LogicalApply`, so an `IN` subquery used directly as a filter or join
ON conjunct is unnested into a plain `LEFT_SEMI_JOIN`, and a `NOT IN` into a
plain `NULL_AWARE_LEFT_ANTI_JOIN`, without materializing a mark column. The
duplicated inference and replacement logic is extracted into
`simplifyConjunctWithMarkJoinSlot`.

Tests:
- New `InferMarkSlotNotNullMapTest` covers And/Or/IsNull/IsNotNull/Nvl and
  multi-mark-slot predicates, including cases that distinguish the simplified
  predicate (pair.first) from the original predicate (pair.second). The old
  `CanInferNotNullForMarkSlotTest` is removed as it is superseded.
- New `EliminateMarkJoinTest` verifies IN / NOT IN in join ON conditions are
  turned into plain semi / null-aware anti joins, while the mark join is kept
  when the mark is projected to the query output or when NULL semantics are
  observable (`is null`, `or`).

### Release note

None

### Check List (For Author)

- Test: Unit Test
- Behavior changed: No
- Does this need documentation: No
@starocean999

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17630	3917	3894	3894
q2	2041	331	201	201
q3	10246	1357	811	811
q4	4683	469	343	343
q5	7524	826	565	565
q6	179	184	137	137
q7	727	776	601	601
q8	9336	1586	1539	1539
q9	5309	4088	4077	4077
q10	6768	1606	1350	1350
q11	510	348	323	323
q12	733	597	455	455
q13	18080	3269	2768	2768
q14	255	270	239	239
q15	q16	731	727	657	657
q17	1017	1000	961	961
q18	6606	5613	5581	5581
q19	1166	1247	1114	1114
q20	755	664	611	611
q21	5852	2819	2578	2578
q22	457	364	310	310
Total cold run time: 100605 ms
Total hot run time: 29115 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4955	4630	4659	4630
q2	268	330	220	220
q3	4814	5216	4689	4689
q4	2172	2261	1421	1421
q5	4509	4704	4389	4389
q6	226	173	126	126
q7	1798	1716	1537	1537
q8	2332	2043	2024	2024
q9	7163	6887	6714	6714
q10	4186	4146	3796	3796
q11	500	364	339	339
q12	700	709	489	489
q13	2972	3270	2744	2744
q14	264	276	244	244
q15	q16	659	683	607	607
q17	1237	1222	1218	1218
q18	12109	11036	11886	11036
q19	1081	1066	1039	1039
q20	2198	2171	1903	1903
q21	5268	4488	4517	4488
q22	503	485	416	416
Total cold run time: 59914 ms
Total hot run time: 54069 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 158615 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 be24f2e13c2b81c83ea41679ad2781e23cf88222, data reload: false

query5	4296	577	440	440
query6	456	215	197	197
query7	4852	564	336	336
query8	314	158	149	149
query9	8753	4014	4055	4014
query10	463	358	295	295
query11	5814	2160	1990	1990
query12	147	99	92	92
query13	1241	563	439	439
query14	6075	4266	3977	3977
query14_1	3857	3840	3920	3840
query15	203	197	180	180
query16	1018	517	473	473
query17	932	698	559	559
query18	2439	469	357	357
query19	207	191	153	153
query20	108	100	101	100
query21	231	160	141	141
query22	13077	13001	12854	12854
query23	15882	14967	14591	14591
query23_1	14875	14821	14746	14746
query24	7601	1722	1228	1228
query24_1	1268	1281	1249	1249
query25	553	455	386	386
query26	1330	352	221	221
query27	2603	602	376	376
query28	4559	2081	2038	2038
query29	1081	611	465	465
query30	341	263	221	221
query31	1169	1109	1040	1040
query32	112	69	59	59
query33	522	306	236	236
query34	1222	1153	649	649
query35	789	747	636	636
query36	775	796	730	730
query37	147	102	95	95
query38	1829	1757	1711	1711
query39	804	810	824	810
query39_1	786	785	787	785
query40	250	169	136	136
query41	64	63	62	62
query42	91	91	89	89
query43	321	321	266	266
query44	1405	782	774	774
query45	183	176	170	170
query46	1082	1180	699	699
query47	1523	1555	1464	1464
query48	395	423	305	305
query49	574	414	292	292
query50	1129	432	332	332
query51	10675	10563	10457	10457
query52	89	84	75	75
query53	261	271	196	196
query54	288	227	233	227
query55	74	76	65	65
query56	299	307	288	288
query57	1013	993	949	949
query58	284	271	264	264
query59	1593	1563	1399	1399
query60	310	275	258	258
query61	153	148	142	142
query62	405	314	263	263
query63	233	204	197	197
query64	2816	995	812	812
query65	3876	3795	3805	3795
query66	1831	475	361	361
query67	20272	20346	19950	19950
query68	3373	1640	1041	1041
query69	421	302	258	258
query70	841	781	785	781
query71	371	360	302	302
query72	3006	2790	2551	2551
query73	869	870	428	428
query74	4702	4494	4281	4281
query75	2372	2326	2003	2003
query76	2411	1117	766	766
query77	345	369	272	272
query78	11204	11271	10490	10490
query79	1320	1166	735	735
query80	900	547	466	466
query81	515	333	283	283
query82	631	170	136	136
query83	381	325	298	298
query84	332	163	135	135
query85	970	599	499	499
query86	374	238	222	222
query87	1995	1961	1823	1823
query88	3677	2802	2776	2776
query89	390	317	284	284
query90	1839	203	190	190
query91	206	191	161	161
query92	60	57	58	57
query93	1633	1625	926	926
query94	601	351	341	341
query95	804	595	458	458
query96	1146	781	356	356
query97	2474	2450	2392	2392
query98	199	192	196	192
query99	753	721	610	610
Total cold run time: 245459 ms
Total hot run time: 158615 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.09	0.05	0.05
query3	0.25	0.14	0.14
query4	1.61	0.14	0.14
query5	0.23	0.22	0.22
query6	1.16	0.78	0.79
query7	0.03	0.01	0.00
query8	0.05	0.04	0.03
query9	0.38	0.31	0.32
query10	0.54	0.53	0.54
query11	0.19	0.14	0.14
query12	0.18	0.14	0.14
query13	0.46	0.46	0.47
query14	0.98	0.98	0.98
query15	0.60	0.58	0.59
query16	0.32	0.31	0.34
query17	1.05	1.08	1.06
query18	0.20	0.18	0.20
query19	2.05	1.95	2.03
query20	0.01	0.01	0.01
query21	15.45	0.22	0.15
query22	4.74	0.05	0.05
query23	16.09	0.31	0.12
query24	2.96	0.42	0.30
query25	0.10	0.04	0.04
query26	0.76	0.21	0.15
query27	0.04	0.04	0.04
query28	3.46	0.82	0.35
query29	12.47	4.02	3.20
query30	0.27	0.16	0.15
query31	2.77	0.57	0.32
query32	3.22	0.59	0.48
query33	3.12	3.17	3.24
query34	15.52	3.91	3.27
query35	3.22	3.23	3.22
query36	0.55	0.45	0.44
query37	0.08	0.06	0.07
query38	0.05	0.03	0.03
query39	0.04	0.03	0.02
query40	0.18	0.15	0.16
query41	0.09	0.03	0.04
query42	0.04	0.03	0.03
query43	0.05	0.04	0.04
Total cold run time: 95.66 s
Total hot run time: 23.83 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 92.21% (71/77) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 53.15% (76/143) 🎉
Increment coverage report
Complete coverage report

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants