Skip to content

[fix](expr) Canonicalize logical OR results - #68308

Merged
Mryange merged 3 commits into
apache:masterfrom
Mryange:fix-nullable-or-payload
Sep 22, 2026
Merged

Mryange merged 3 commits into
apache:masterfrom
Mryange:fix-nullable-or-payload

Conversation

@Mryange

@Mryange Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Logical OR used bitwise OR on the nested bytes of Boolean columns. Nullable rows may carry arbitrary nested payloads, so NULL OR TRUE could expose a non-canonical non-NULL Boolean byte instead of 1. Downstream expressions such as multi-branch CASE WHEN could then use that byte as an invalid branch index and crash the BE.

Root cause: both the generic logical function and the VCompoundPred fast path preserved input bits instead of producing a canonical Boolean result. This change normalizes every logical OR result to 0 or 1 while preserving SQL three-valued NULL semantics. It also adds focused BE coverage and SQL regressions for nullable payloads, CASE WHEN, short-circuit evaluation settings, and the complete TRUE/FALSE/NULL OR truth table.

The resulting nullable Boolean representation follows this truth table. NULL(any) means that the nested byte of a NULL row may contain any value; it is ignored whenever res_null is 1.

Left Right res_null res_data Final result
FALSE FALSE 0 0 FALSE
FALSE TRUE 0 1 TRUE
TRUE FALSE 0 1 TRUE
TRUE TRUE 0 1 TRUE
NULL(any) FALSE 1 0 or 1 NULL
FALSE NULL(any) 1 0 or 1 NULL
NULL(any) TRUE 0 1 TRUE
TRUE NULL(any) 0 1 TRUE
NULL(any) NULL(any) 1 0 or 1 NULL

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?

@Mryange

Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

/review

@Mryange

Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor 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.

Request changes — capped/incomplete static review (round 3 of 3)

I reviewed the authoritative five-file diff at exact head e358d870510ae9fc6fc73a98a078cdc73f952342. Four blocking findings are attached inline: the compound unit test cannot reach its intended kernel, OR identity/direct consumers remain unsafe for legacy noncanonical Booleans, the OR null-mask formulas still test the low bit rather than truthiness, and logical XOR remains an independently wrong mixed-version consumer. A new valuable issue surfaced in the third and final permitted round, so this review is explicitly capped/incomplete; the workflow forbids a fourth convergence round.

Critical checkpoint conclusions:

  • Goal/correctness: Rows that enter the patched OR data loops are canonicalized, and canonical TRUE/FALSE/NULL inputs retain the right SQL truth table. The end-to-end goal is not complete because the four inline cases remain.
  • Scope/clarity: The diff is small and its local intent is clear, but it does not cover the identity returns, null-mask calculation, or parallel logical consumers required by the same Boolean invariant.
  • Concurrency/thread safety: The production changes are stateless row-local arithmetic. They introduce no shared state, locks, threads, or deadlock surface.
  • Lifecycle/ownership: Production ownership is unchanged. The new test has a concrete lifecycle/dispatch defect: its childless fake expression is classified constant and enters an unprepared generic fallback.
  • Configuration: No configuration item or dynamic-update path is added.
  • Compatibility: Existing nullable/numeric PBlock serialization preserves raw Boolean bytes across BE exchange. Mixed-version execution is therefore applicable and is not safely handled by OR-only normalization; the identity, CASE, null-mask, and XOR cases are blockers.
  • Parallel paths and conditions: Generic/compound, nullable/non-nullable, constants, selectors, index materialization, identity returns, regular/short-circuit CASE, and both operand orders were traced. Constants use the generic path and index results are canonical; the unsafe conditions are identified inline.
  • Test coverage/results: BE test discovery is valid, and the two direct generic tests exercise their intended paths. The compound test does not. The regression labels and outputs are internally consistent: 34,000 true rows, 66,000 NULL rows, 100,000 non-NULL CASE results, and the nine-row truth table all recompute correctly. No test covers the mixed-version identity, even-truthy null-mask, direct CASE, or XOR shapes.
  • Observability: No new long-running or operational path needs logs or metrics.
  • Transactions/persistence/data writes: No transaction, edit-log, storage-format, visibility, MoW, or atomic-write behavior changes.
  • FE-BE variables/protocol fields: No new variable or wire field is introduced; the relevant existing PBlock value compatibility is the failing concern above.
  • Performance: Work remains O(rows), vector-friendly, and allocation-free in the changed loops; no material CPU or memory regression was found.
  • Other: The PR describes a user-visible behavior fix but leaves the release note as None and the behavior/test checklist unchecked; please update the metadata to match the actual change and validation. No additional user review focus was supplied.

Validation is static only by task restriction: I did not build or run unit/regression tests, and CI/author claims are not treated as independent execution evidence.

class ColumnExpr final : public VExpr {
public:
ColumnExpr(ColumnPtr column, DataTypePtr type)
: VExpr(std::move(type), false), _column(std::move(column)) {}

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] Keep this synthetic column expression off the constant path. Because it has no children, inherited VExpr::is_constant() returns true, so VCompoundPred::_has_const_child() delegates this test to VectorizedFnCall instead of reaching do_null_pred<false>. This helper never prepares/opens the expression, leaving _function unset; the fallback fails in _do_execute(), and the test provides no coverage for the compound change.

Suggested change
: VExpr(std::move(type), false), _column(std::move(column)) {}
: VExpr(std::move(type), false), _column(std::move(column)) {}
bool is_constant() const override { return false; }

Comment thread be/src/exprs/vcompound_pred.h Outdated
} else {
lhs[i] |= rhs[i];
// Logical OR must produce a canonical Boolean instead of preserving input bits.
lhs[i] = (lhs[i] != 0) || (rhs[i] != 0);

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 compatibility for OR results that bypass these loops. The block-wide shortcuts at lines 521-536 return an operand unchanged, so during a rolling upgrade an old BE can emit byte 65 for NULL(payload=65) OR TRUE, PBlock exchange preserves it, and a new BE can pass 65 into regular CASE branch indexing. Canonicalizing those returned operands fixes this route, but an exchanged legacy OR result can also feed CASE directly; please additionally interpret CASE conditions by truthiness (or canonicalize at a defined compatibility boundary) and cover the mixed-version shape.

static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a | b; }
// A NULL row may carry an arbitrary nested byte. Canonicalize logical OR to 0 or 1 so that
// such a byte cannot become visible when the other operand makes the result non-NULL.
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return (a != 0) || (b != 0); }

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] Normalize the null decision as well as the data byte. The unchanged apply_null below tests the low bit via 1 ^ a, not truthiness. A supported rolling-upgrade path can produce an even true byte: old OR emits {65,1}, XOR with TRUE yields {64,0}, and PBlock preserves it; on the new BE, 64 OR NULL reaches this formula and is marked NULL instead of TRUE. Please derive nullness with a == 0 / b == 0 in both OrImpl and VCompoundPred, and test both operand orders.

static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a | b; }
// A NULL row may carry an arbitrary nested byte. Canonicalize logical OR to 0 or 1 so that
// such a byte cannot become visible when the other operand makes the result non-NULL.
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return (a != 0) || (b != 0); }

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] This normalization leaves another rolling-upgrade consumer incorrect. An old BE can send the semantically true byte 65 produced by pre-fix OR; PBlock preserves it, and the new BE's registered XorImpl still computes 65 ^ 1 = 64. Doris interprets 64 as true, so TRUE XOR TRUE returns TRUE before any later OR or CASE. Please normalize XOR by operand truthiness ((a != 0) != (b != 0)) or define one Boolean canonicalization boundary, and add a mixed-version case.

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17794	3912	3929	3912
q2	2230	365	314	314
q3	10021	1413	799	799
q4	4683	479	360	360
q5	7464	833	545	545
q6	183	168	143	143
q7	759	813	601	601
q8	9298	1550	1567	1550
q9	5434	4224	4152	4152
q10	6831	1341	996	996
q11	446	278	245	245
q12	644	420	305	305
q13	18014	2618	1998	1998
q14	265	262	232	232
q15	q16	739	710	658	658
q17	1711	1072	1032	1032
q18	6485	5589	5514	5514
q19	1168	1258	1104	1104
q20	457	393	264	264
q21	5080	3135	2928	2928
q22	450	376	317	317
Total cold run time: 100156 ms
Total hot run time: 27969 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5035	4769	4352	4352
q2	739	580	531	531
q3	4936	5133	4653	4653
q4	2231	2326	1427	1427
q5	4615	4357	4559	4357
q6	229	179	132	132
q7	1831	1689	1518	1518
q8	2330	2051	1957	1957
q9	7331	7419	7181	7181
q10	3677	3618	3082	3082
q11	512	399	372	372
q12	710	704	513	513
q13	2248	2593	1993	1993
q14	260	274	248	248
q15	q16	654	686	603	603
q17	7284	6689	6655	6655
q18	11918	11049	11686	11049
q19	1075	974	1009	974
q20	2205	2176	1897	1897
q21	4981	4097	4291	4097
q22	500	468	399	399
Total cold run time: 65301 ms
Total hot run time: 57990 ms

@hello-stephen

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

query5	4317	583	444	444
query6	421	210	189	189
query7	4822	551	295	295
query8	323	179	170	170
query9	8799	4002	3965	3965
query10	490	303	247	247
query11	5815	3519	3212	3212
query12	132	86	84	84
query13	1247	560	394	394
query14	6541	4485	4215	4215
query14_1	3940	3912	3921	3912
query15	197	195	171	171
query16	975	443	422	422
query17	900	625	520	520
query18	2406	446	334	334
query19	195	179	138	138
query20	79	78	88	78
query21	219	129	114	114
query22	12963	12990	12882	12882
query23	13930	13037	12437	12437
query23_1	12677	12412	12506	12412
query24	7379	1099	682	682
query24_1	682	646	695	646
query25	588	445	374	374
query26	1253	299	159	159
query27	2749	556	328	328
query28	4642	1948	1942	1942
query29	1628	710	496	496
query30	298	221	178	178
query31	865	767	618	618
query32	149	93	91	91
query33	488	301	240	240
query34	1200	1115	626	626
query35	718	742	635	635
query36	823	824	700	700
query37	157	103	86	86
query38	1822	1749	1696	1696
query39	713	668	668	668
query39_1	647	644	614	614
query40	220	116	102	102
query41	70	66	61	61
query42	93	91	92	91
query43	331	340	297	297
query44	1337	719	709	709
query45	176	173	161	161
query46	1085	1217	706	706
query47	1507	1513	1391	1391
query48	400	393	303	303
query49	586	394	289	289
query50	1017	339	264	264
query51	10409	10565	10612	10565
query52	87	92	92	92
query53	248	245	178	178
query54	251	194	184	184
query55	76	72	70	70
query56	227	219	240	219
query57	1388	1410	1304	1304
query58	297	259	244	244
query59	1983	2044	1849	1849
query60	282	246	224	224
query61	145	144	142	142
query62	406	313	265	265
query63	219	174	172	172
query64	2771	958	785	785
query65	3464	3420	3438	3420
query66	1799	427	307	307
query67	20417	20174	19908	19908
query68	3377	1501	889	889
query69	422	289	257	257
query70	889	817	785	785
query71	290	220	204	204
query72	2825	2671	2299	2299
query73	830	739	434	434
query74	4596	4491	4292	4292
query75	2299	2276	1938	1938
query76	2375	1114	708	708
query77	353	401	290	290
query78	8967	8984	8428	8428
query79	1308	1100	742	742
query80	555	435	353	353
query81	539	322	277	277
query82	631	157	127	127
query83	313	216	200	200
query84	312	144	119	119
query85	836	478	373	373
query86	334	232	224	224
query87	1996	1939	1849	1849
query88	3580	2707	2689	2689
query89	344	285	240	240
query90	1900	178	182	178
query91	175	155	131	131
query92	103	91	85	85
query93	1432	1389	852	852
query94	528	360	301	301
query95	651	472	339	339
query96	995	804	331	331
query97	2425	2413	2302	2302
query98	160	153	142	142
query99	719	719	622	622
Total cold run time: 236189 ms
Total hot run time: 151992 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.09	0.04	0.04
query3	0.26	0.14	0.13
query4	1.61	0.13	0.13
query5	0.23	0.23	0.21
query6	1.15	0.92	0.93
query7	0.04	0.00	0.00
query8	0.05	0.04	0.04
query9	0.38	0.33	0.33
query10	0.54	0.55	0.54
query11	0.19	0.14	0.14
query12	0.18	0.15	0.15
query13	0.46	0.46	0.48
query14	0.94	0.96	0.97
query15	0.62	0.58	0.59
query16	0.30	0.30	0.34
query17	1.07	1.09	1.09
query18	0.22	0.20	0.19
query19	2.01	1.94	1.93
query20	0.02	0.01	0.01
query21	15.44	0.20	0.15
query22	4.86	0.05	0.06
query23	16.12	0.30	0.13
query24	2.97	0.44	0.34
query25	0.13	0.04	0.03
query26	0.74	0.21	0.16
query27	0.05	0.04	0.04
query28	3.48	0.81	0.35
query29	12.50	4.13	3.29
query30	0.28	0.15	0.16
query31	2.77	0.55	0.31
query32	3.22	0.59	0.48
query33	3.32	3.17	3.23
query34	15.69	3.91	3.26
query35	3.21	3.22	3.21
query36	0.56	0.43	0.42
query37	0.09	0.07	0.06
query38	0.05	0.04	0.03
query39	0.04	0.03	0.03
query40	0.18	0.15	0.14
query41	0.09	0.03	0.02
query42	0.05	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.25 s
Total hot run time: 24.04 s

@Mryange

Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

/review 重新review一下代码。请注意,我这是一个fix,不需要考虑升降级兼容性。我们为什么要兼容错误的行为呢?

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Selected model is at capacity. Please try a different model.
Workflow run: https://github.com/apache/doris/actions/runs/35567032027

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

@Mryange

Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17666	3819	3856	3819
q2	2176	362	321	321
q3	10040	1438	788	788
q4	4683	474	349	349
q5	7484	816	543	543
q6	182	166	135	135
q7	744	785	589	589
q8	9298	1497	1538	1497
q9	5365	4164	4141	4141
q10	6815	1324	1008	1008
q11	435	272	245	245
q12	631	413	287	287
q13	18053	2603	2004	2004
q14	272	257	237	237
q15	q16	730	710	655	655
q17	1696	1141	1020	1020
q18	6408	5636	5571	5571
q19	1180	1237	948	948
q20	477	382	265	265
q21	5489	2957	2947	2947
q22	457	362	307	307
Total cold run time: 100281 ms
Total hot run time: 27676 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4598	4371	4545	4371
q2	774	543	531	531
q3	4746	5067	4796	4796
q4	2217	2353	1475	1475
q5	4638	4576	4693	4576
q6	234	177	129	129
q7	1806	1725	1575	1575
q8	2573	2082	2012	2012
q9	7405	7308	6837	6837
q10	3614	3534	3073	3073
q11	545	373	346	346
q12	693	709	514	514
q13	2287	2596	1980	1980
q14	269	278	242	242
q15	q16	674	688	597	597
q17	7282	6833	6617	6617
q18	11953	11019	11845	11019
q19	1078	1007	1013	1007
q20	2222	2191	1908	1908
q21	4977	4088	4331	4088
q22	533	472	408	408
Total cold run time: 65118 ms
Total hot run time: 58101 ms

@hello-stephen

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

query5	4317	599	473	473
query6	429	214	204	204
query7	4805	526	289	289
query8	319	180	163	163
query9	8799	3931	3927	3927
query10	451	311	263	263
query11	5843	3569	3226	3226
query12	148	93	89	89
query13	1295	599	428	428
query14	6540	4545	4189	4189
query14_1	3952	3926	3924	3924
query15	205	196	175	175
query16	984	452	455	452
query17	932	667	545	545
query18	2428	476	339	339
query19	204	185	169	169
query20	84	80	80	80
query21	221	135	117	117
query22	12898	12952	12954	12952
query23	13823	13100	12318	12318
query23_1	12697	12553	12466	12466
query24	7223	1127	677	677
query24_1	686	679	672	672
query25	577	440	364	364
query26	1248	310	167	167
query27	2702	559	336	336
query28	4550	1979	1944	1944
query29	1660	739	531	531
query30	301	224	189	189
query31	896	755	643	643
query32	161	104	96	96
query33	536	330	262	262
query34	1181	1098	624	624
query35	731	745	666	666
query36	802	834	721	721
query37	150	111	110	110
query38	1863	1760	1675	1675
query39	709	672	656	656
query39_1	651	638	654	638
query40	227	128	107	107
query41	71	67	68	67
query42	99	98	101	98
query43	331	349	299	299
query44	1347	709	752	709
query45	182	175	160	160
query46	1032	1192	713	713
query47	1490	1490	1379	1379
query48	411	416	279	279
query49	599	398	287	287
query50	906	357	251	251
query51	10328	10308	10556	10308
query52	87	86	79	79
query53	241	254	180	180
query54	255	200	197	197
query55	80	73	70	70
query56	214	205	226	205
query57	1533	1512	1399	1399
query58	277	257	249	249
query59	2365	2054	1871	1871
query60	273	236	229	229
query61	153	147	146	146
query62	397	318	268	268
query63	213	173	178	173
query64	2822	1037	822	822
query65	3452	3414	3406	3406
query66	1787	428	314	314
query67	20140	19979	20028	19979
query68	3003	1570	902	902
query69	408	296	260	260
query70	906	819	828	819
query71	288	233	210	210
query72	2638	2087	2225	2087
query73	820	758	408	408
query74	4645	4503	4302	4302
query75	2288	2280	1947	1947
query76	2286	1095	726	726
query77	376	396	300	300
query78	9230	9084	8515	8515
query79	1334	1178	761	761
query80	578	439	381	381
query81	541	321	276	276
query82	646	166	123	123
query83	303	220	194	194
query84	319	148	118	118
query85	852	458	380	380
query86	326	236	227	227
query87	2012	1978	1857	1857
query88	3640	2712	2708	2708
query89	366	286	245	245
query90	1955	174	171	171
query91	168	160	132	132
query92	98	91	85	85
query93	1519	1391	814	814
query94	514	319	294	294
query95	670	464	339	339
query96	1090	770	324	324
query97	2423	2411	2301	2301
query98	168	166	149	149
query99	727	726	633	633
Total cold run time: 236063 ms
Total hot run time: 152257 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.10	0.05	0.05
query3	0.26	0.13	0.14
query4	1.61	0.14	0.14
query5	0.24	0.23	0.22
query6	1.15	0.92	0.93
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.38	0.33	0.34
query10	0.55	0.52	0.54
query11	0.20	0.14	0.13
query12	0.19	0.15	0.16
query13	0.46	0.46	0.46
query14	0.94	0.95	0.95
query15	0.60	0.59	0.58
query16	0.31	0.32	0.30
query17	1.09	1.06	1.07
query18	0.21	0.20	0.20
query19	2.01	1.92	1.98
query20	0.02	0.01	0.01
query21	15.40	0.22	0.14
query22	4.86	0.05	0.06
query23	16.10	0.30	0.12
query24	3.03	0.43	0.32
query25	0.12	0.06	0.04
query26	0.72	0.20	0.15
query27	0.05	0.05	0.04
query28	3.48	0.78	0.36
query29	12.48	4.01	3.22
query30	0.28	0.15	0.18
query31	2.78	0.57	0.31
query32	3.22	0.59	0.49
query33	3.10	3.27	3.26
query34	15.49	3.92	3.27
query35	3.22	3.25	3.22
query36	0.56	0.43	0.43
query37	0.08	0.07	0.06
query38	0.05	0.04	0.03
query39	0.03	0.02	0.03
query40	0.18	0.14	0.14
query41	0.09	0.04	0.03
query42	0.04	0.02	0.03
query43	0.04	0.04	0.04
Total cold run time: 95.83 s
Total hot run time: 24.03 s

@Mryange

Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Selected model is at capacity. Please try a different model.
Workflow run: https://github.com/apache/doris/actions/runs/35572836908

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

@Mryange

Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

run external

@yiguolei yiguolei added usercase Important user case type label dev/4.1.x labels Sep 21, 2026
Comment thread be/src/exprs/vcompound_pred.h Outdated
@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (3/3) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.24% (34340/45043)
Line Coverage 61.15% (385501/630398)
Region Coverage 57.54% (324591/564065)
Branch Coverage 58.33% (147788/253373)

@Mryange

Mryange commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17696	3802	3791	3791
q2	2182	381	319	319
q3	10037	1378	790	790
q4	4683	472	351	351
q5	7551	819	550	550
q6	179	168	135	135
q7	732	786	602	602
q8	9342	1511	1632	1511
q9	5436	4199	4214	4199
q10	6823	1318	1026	1026
q11	436	272	245	245
q12	640	423	291	291
q13	18040	2622	1981	1981
q14	259	254	236	236
q15	q16	725	717	665	665
q17	1739	1047	982	982
q18	6499	5640	5543	5543
q19	1309	1267	1074	1074
q20	498	396	267	267
q21	5727	3280	2959	2959
q22	456	369	309	309
Total cold run time: 100989 ms
Total hot run time: 27826 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4579	4616	4314	4314
q2	746	572	526	526
q3	5274	5102	4629	4629
q4	2237	2326	1438	1438
q5	4579	4368	4669	4368
q6	230	171	126	126
q7	1780	1686	1528	1528
q8	2319	1962	2013	1962
q9	7235	7342	7199	7199
q10	3696	3600	3087	3087
q11	514	368	350	350
q12	697	706	508	508
q13	2277	2583	2009	2009
q14	267	271	251	251
q15	q16	668	678	595	595
q17	7255	6713	6625	6625
q18	11804	11038	11783	11038
q19	1079	997	1005	997
q20	2224	2178	1913	1913
q21	5023	4101	4327	4101
q22	503	456	401	401
Total cold run time: 64986 ms
Total hot run time: 57965 ms

@hello-stephen

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

query5	4314	601	451	451
query6	421	206	195	195
query7	4976	528	296	296
query8	317	186	169	169
query9	8805	3988	4000	3988
query10	431	304	251	251
query11	5815	3565	3234	3234
query12	145	86	86	86
query13	1295	588	418	418
query14	6547	4506	4238	4238
query14_1	3956	3941	3927	3927
query15	208	203	177	177
query16	977	437	413	413
query17	920	691	541	541
query18	2424	463	335	335
query19	195	185	146	146
query20	83	80	78	78
query21	219	133	115	115
query22	13042	12931	12782	12782
query23	14044	12992	12308	12308
query23_1	12580	12606	12469	12469
query24	7252	1158	647	647
query24_1	700	741	668	668
query25	559	437	378	378
query26	1259	305	164	164
query27	2715	581	351	351
query28	4561	1978	1966	1966
query29	1610	748	490	490
query30	292	217	177	177
query31	879	757	626	626
query32	148	87	85	85
query33	503	311	239	239
query34	1188	1136	635	635
query35	727	759	635	635
query36	798	804	711	711
query37	143	100	86	86
query38	1830	1754	1670	1670
query39	702	692	664	664
query39_1	690	673	660	660
query40	215	126	101	101
query41	67	62	62	62
query42	95	89	90	89
query43	335	342	297	297
query44	1410	706	699	699
query45	182	195	178	178
query46	1091	1191	761	761
query47	1493	1495	1385	1385
query48	393	395	316	316
query49	609	419	291	291
query50	928	355	257	257
query51	10393	10712	10352	10352
query52	92	88	83	83
query53	242	257	178	178
query54	254	209	181	181
query55	77	80	74	74
query56	221	219	223	219
query57	1448	1475	1381	1381
query58	286	264	278	264
query59	1971	2062	1858	1858
query60	277	244	226	226
query61	152	147	144	144
query62	392	314	266	266
query63	219	176	180	176
query64	2807	1001	816	816
query65	3481	3423	3452	3423
query66	1834	423	309	309
query67	20156	20154	19814	19814
query68	3297	1471	919	919
query69	402	305	255	255
query70	871	828	839	828
query71	299	242	217	217
query72	2672	2512	2155	2155
query73	829	729	414	414
query74	4641	4485	4279	4279
query75	2311	2278	1933	1933
query76	2324	1115	741	741
query77	369	404	311	311
query78	9021	8931	8468	8468
query79	1227	1200	739	739
query80	590	495	396	396
query81	543	339	278	278
query82	475	170	124	124
query83	317	233	205	205
query84	328	150	123	123
query85	911	566	374	374
query86	345	247	224	224
query87	1988	1964	1843	1843
query88	3619	2715	2691	2691
query89	357	284	242	242
query90	1874	183	177	177
query91	172	156	126	126
query92	95	87	90	87
query93	1502	1538	865	865
query94	537	360	291	291
query95	669	373	330	330
query96	1103	756	342	342
query97	2464	2429	2295	2295
query98	166	151	142	142
query99	718	725	607	607
Total cold run time: 235988 ms
Total hot run time: 152029 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.10	0.05	0.05
query3	0.26	0.14	0.14
query4	1.61	0.14	0.14
query5	0.24	0.22	0.22
query6	1.16	0.95	0.90
query7	0.04	0.01	0.00
query8	0.06	0.04	0.04
query9	0.39	0.36	0.33
query10	0.54	0.56	0.55
query11	0.20	0.14	0.14
query12	0.18	0.14	0.15
query13	0.46	0.47	0.46
query14	0.96	0.95	0.94
query15	0.60	0.58	0.59
query16	0.32	0.33	0.34
query17	1.10	1.11	1.07
query18	0.21	0.20	0.19
query19	2.02	1.95	1.86
query20	0.02	0.02	0.01
query21	15.48	0.21	0.12
query22	4.88	0.05	0.05
query23	16.15	0.31	0.12
query24	2.92	0.41	0.33
query25	0.13	0.05	0.04
query26	0.74	0.20	0.14
query27	0.04	0.04	0.04
query28	3.56	0.73	0.33
query29	12.50	4.09	3.16
query30	0.28	0.15	0.15
query31	2.77	0.57	0.32
query32	3.22	0.59	0.48
query33	3.11	3.17	3.12
query34	15.49	3.94	3.28
query35	3.25	3.21	3.24
query36	0.56	0.44	0.44
query37	0.09	0.06	0.07
query38	0.05	0.04	0.03
query39	0.04	0.03	0.03
query40	0.18	0.15	0.15
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 96.09 s
Total hot run time: 23.79 s

morningman added a commit that referenced this pull request Sep 22, 2026
… the query; stop the regression framework leaking connections (#68338)

### What problem does this PR solve?

Related PR: #68101 (the one connection pool, which made the leak
visible), #68266 (the bearer token as the session's credential, which
removed its last cap), #67503 / #62259 (the Arrow Flight deferral gate
this generalizes)

Problem Summary:

**Context.** A Remote Doris catalog with `use_arrow_flight = true` reads
another Doris cluster over Arrow Flight SQL: for every scan,
`RemoteDorisScanNode` on the local FE performs a Flight SQL handshake
against a remote FE (`authenticateBasicToken`), runs the query there
(`GetFlightInfo`), and hands the endpoints - a ticket per remote BE - to
the local BE, which reads the rows with `DoGet` straight from the remote
BEs.

The handshake is not free on the remote side: since #68101 a Flight SQL
session is a connection in the remote FE's one connection pool, counted
against `qe_max_connection`, the Arrow Flight SQL sub-quota and the
catalog user's `max_user_connections`; since #68266 the bearer token is
that session's name in the pool and nothing else, so the session ends
only on `CloseSession`, `KILL CONNECTION`, `wait_timeout` (8h by
default) or an FE restart.

The regression framework has a leak of its own of the same shape: a
suite's Doris connections are `ThreadLocal` to the thread that opened
them, and only the suite thread and `Suite.thread()` close theirs; a
`sql` on any other thread opens a connection nobody closes.

**1. The problem, and what it cost**

- `RemoteDorisScanNode.executeFlightSqlQuery` closed the gRPC channel
and the allocator in a try-with-resources but never sent `CloseSession`.
Every scan of a Remote Doris table therefore left one Flight SQL session
behind on the remote FE, under the catalog user, until `wait_timeout`.
Before #68101 this was invisible: Flight sessions had a pool of their
own, were not counted per user, and a per-user LRU of
`max_user_connections / 2` tokens evicted the oldest. After #68101 the
leaked sessions eat the catalog user's quota on the remote FE; after
#68266 nothing caps them at all. A hundred scans within 8h and the user
- MySQL clients included - is refused there with `Reach limit of
connections`.
- This is what broke the external regression pipeline on 2026-09-21
(TeamCity 1053416 on #68308): the `remote_doris` suites point the
catalog at the FE under test with user `root`; 49 scans left 48 Flight
sessions (`Arrow Flight SQL: 512 (current: 48)` in the refusal), which
took half of root's 100; the other half was taken by the framework leak
below, and 14 MySQL connections were refused in a six-second window.
- The framework leak: `Awaitility.await()...until { sql ... }` evaluates
the condition on Awaitility's own thread, which dies with the `await()`.
Each call leaked one root connection until the client JVM
garbage-collected it (the FE logs those as `No more data to be read.
Close connection`). 230 suites call `Awaitility.await()` directly; in
the failing run one suite opened 22 such connections in 46 seconds, and
29 of them were collected in one GC at the moment the refusals stopped.
Suites that call `sql` from threads of their own (`Thread.start {
streamLoad }`, an `Executors` pool) leak the same way - a P0 run of the
same day shows 76 Awaitility connections and 89 own-thread connections
opened by root within one minute, all left to the garbage collector -
and the two docker helpers dropped the connection their action opened
without closing it.

**2. What this PR does, and why it helps**

FE:

- `RemoteDorisFlightSession` (new): the session as an object -
handshake, `execute`, and `close()` = `CloseSession` (bounded to 5s so a
remote FE that stopped answering cannot hang the local query's teardown;
the session is then left to its `wait_timeout` as before) followed by
the channel and the allocator. `open()` leaves nothing behind when the
handshake is refused; a query that fails closes its session at once, so
the retry on the next node leaves nothing behind either. Idempotent.
- `RemoteDorisScanNode` keeps the session from `getSplits` until
`stop()`, which the coordinator calls when the local query closes or is
cancelled - i.e. when the local BE is done with the remote query's
endpoints. It cannot be closed right after `GetFlightInfo`: the remote
FE cancels whatever a closed session was still running, and when the
remote table is itself an external table scanned in batch mode the
remote query is deferred there and still running while the local BE
reads.
- For the same reason the local coordinator has to outlive dispatch when
the local query is itself an Arrow Flight SQL query (otherwise #67503
closes it right after `exec()`, while the local BE may still be
reading). The deferral gate's predicate is generalized from "has a batch
split source" to "the BE still depends on this scan after dispatch":
`ScanNode.hasBatchSplitSource()` -> `coordinatorMustOutliveDispatch()`,
`Coordinator.hasBatchSplitSource()` -> `mustOutliveDispatch()`;
`RemoteDorisScanNode` adds its open session as the second reason.
Batch-mode external scans behave exactly as before.
- The statement is the fallback owner. The session is opened while the
plan is translated, before any coordinator exists, and not every plan
gets a coordinator or gets one that is closed: a statement that fails
between planning and dispatch (a SQL block rule on the scan, an `INSERT`
whose transaction cannot begin - a re-used `WITH LABEL`, the per-db txn
limit), the plan `INSERT OVERWRITE` and every materialized-view refresh
run only to locate the sink and discard, a load job created from the
plan. `keepFlightSession` registers the node with the
`StatementContext`, whose `close()` (the per-statement finally of
`ConnectProcessor`, `TaskProcessor`, `MTMVTask`) stops what is still
registered; the deferral gate hands the nodes over to the deferred
coordinator before `deferForArrowFlight`, so a Flight query kept alive
for DoGet is untouched. `INSERT OVERWRITE` releases its probe plan's
scan nodes as soon as the plan has been read.
- A same-plan retry must not reuse a plan whose scan node released what
the BE scans with: `handleQueryWithRetry` re-dispatches the failed
attempt's plan after its `cancel()` stopped the scan nodes, and the
endpoints of a remote Doris scan belong to the query of a session that
is then gone (the remote FE tears down a query it had deferred).
`ScanNode.cannotBeRedispatched()` (true for a remote Doris scan once
`stop()` ended its session) makes the retry rethrow the original error
instead.

Regression framework:

- `Awaitility.pollInSameThread()` at framework start-up: every `until {
}` now runs on the suite thread and reuses the suite's connection, as
`Suite.awaitUntil` already did. The trade-off: an `atMost()` no longer
bounds a condition that blocks (the poll runs to completion before the
bound is checked). A condition that runs statements is bounded by their
timeouts; a condition that waits on anything else bounds the wait itself
- `SuiteCluster` now runs its `doris-compose` subprocess waits on a
helper thread joined with the command's timeout and destroys the process
on expiry (they used to rely on `atMost()`).
- `SuiteContext` records every connection its thread-local accessors
open, with the thread that opened it. On every statement it closes the
connections of threads that have finished (a suite that starts a thread
per step - `Thread.start { streamLoad }; join`, as the mow flexible
suites do 72 times - now holds at most the connections of the threads
still running, deterministically, where before the population depended
on the JVM's next GC), and when the suite ends it closes whatever is
left, with a warning naming the suite.
- The two docker helpers (`docker`, `dockers`) close the connection
their action opened before restoring the original one (and the
multi-cluster one now types that original as the `ConnectionInfo` it
is).

Tests:

- `RemoteDorisScanNodeTest`: an in-process Flight SQL server counts the
sessions it is asked to close. The session lives from the query until
`stop()`; `stop()` twice closes once; a failed query closes at once; a
refused handshake opens nothing; a session handed over after `stop()` is
closed at once; the coordinator of a query with such a scan
`mustOutliveDispatch()`; a session no coordinator takes ends with the
statement, one handed to a deferred coordinator does not; and a node
whose `stop()` ended a session `cannotBeRedispatched()`.
- `ArrowFlightDeferralGateTest` follows the rename.
- Regression
`external_table_p0/remote_doris/test_remote_doris_flight_session`: a
catalog logging in as a user of its own scans a table five times, then
`INSERT OVERWRITE`s from it, then runs an `INSERT ... WITH LABEL` twice
(the second is refused after planning), and asserts after each statement
that `information_schema.processlist` holds no `ArrowFlightSQL` session
of that user - assertions that fail on master.

What it buys: a Remote Doris scan costs the remote FE one session for
exactly the duration of the local query, whatever the protocol of the
local client; the catalog user's quota on the remote FE is no longer
consumed by history; and the regression framework no longer manufactures
the MySQL half of the pressure.

**3. The classes, and how they call each other**

- `RemoteDorisScanNode` (existing): `getSplits` -> `executeQuery` ->
`executeFlightSqlQuery(host, user, password, sql, timeout)`:
`RemoteDorisFlightSession.open` + `execute`, then `keepFlightSession`
(which also registers the node with
`StatementContext.stopScanNodeAtClose`). `stop()` (from
`Coordinator.close()` / `cancel()`, or `StatementContext.close()` as the
fallback) closes the session; `coordinatorMustOutliveDispatch()` is true
while one is held; `cannotBeRedispatched()` once `stop()` ended one.
- `RemoteDorisFlightSession` (new): `open` (FlightClient +
`authenticateBasicToken`), `execute` (`FlightSqlClient.execute`),
`close` (`closeSession` with a 5s deadline, then client and allocator).
- `ScanNode.coordinatorMustOutliveDispatch()` (renamed from
`hasBatchSplitSource`): `splitAssignment != null`, overridable.
`ScanNode.cannotBeRedispatched()` (new): false by default.
- `Coordinator.mustOutliveDispatch()` (renamed): any scan node's
`coordinatorMustOutliveDispatch()`.
- `StmtExecutor.executeAndSendResult`: the deferral gate now reads
`coord.mustOutliveDispatch()`; the deferral gate calls
`StatementContext.handOverScanNodesToDeferredCoordinator` before
`deferForArrowFlight`; `handleQueryWithRetry` rethrows instead of
retrying when `planCannotBeRedispatched()`.
- `StatementContext`: `stopScanNodeAtClose`,
`handOverScanNodesToDeferredCoordinator`, and `close()` stopping what is
left (after the table locks, before the connector scope).
- `InsertOverwriteTableCommand.run`: stops the scan nodes of the plan it
probes and discards.
- `SuiteCluster.waitForDorisCompose`: the bounded subprocess wait
`runCmd` / `runCmdList` use instead of `Awaitility.await().atMost(...)`.
- Remote FE, untouched: `DorisFlightSqlProducer.closeSession` ->
`FlightSessionsInConnectPool.closeConnectContext` ->
`ConnectContext.cleanup()` + `cancelQuery`.
- `RegressionTest.initGroovyEnv`: `Awaitility.pollInSameThread()`.
- `SuiteContext`: `openedDorisConnections` (connection -> opening
thread), `trackDorisConnection`, `closeConnectionsOfFinishedThreads`
(from `getConnection()`, i.e. every statement), `closeDorisConnection`,
`closeLeftoverDorisConnections` (from `close()`); `Suite.dockerImpl` /
`dockers` call `closeDorisConnection`.

```
local FE                                                  remote FE                         remote BE
RemoteDorisScanNode.getSplits
  '- executeFlightSqlQuery
       |- RemoteDorisFlightSession.open ---- handshake --> openSession (pool: +1 for the catalog user)
       |- session.execute ----------------- GetFlightInfo --> runs the query ---------------> result buffered
       '- keepFlightSession                                                                   (ticket per BE)
Coordinator.exec  -> local BE ------------------------------ DoGet(ticket) --------------------> rows
  (Arrow Flight local client: coordinator kept alive, mustOutliveDispatch() == true)
Coordinator.close / cancel
  '- scanNode.stop
       '- session.close ------------------- CloseSession --> closeConnectContext (pool: -1)
```
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#68280

Problem Summary: Logical OR used bitwise OR on the nested bytes of Boolean columns. Nullable rows may carry arbitrary nested payloads, so a NULL operand combined with TRUE could expose a non-canonical non-NULL Boolean value instead of 1. Downstream expressions such as CASE could then treat that byte as an invalid branch index. Canonicalize OR results to 0 or 1 in both the generic logical function and VCompoundPred paths.

### Release note

Fix logical OR to return canonical Boolean values for nullable inputs.

### Check List (For Author)

- Test: Regression test and manual test
- Behavior changed: Yes. Logical OR now canonicalizes non-NULL results to 0 or 1.
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: N/A

Problem Summary: The synthetic column expression in the logical OR unit test inherited the default constant-expression classification because it has no children. This made VCompoundPred fall back to the generic function path instead of exercising the nullable compound OR kernel. Mark the test expression as non-constant so the test covers the intended implementation.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - `./run-be-ut.sh --run --filter='FunctionsLogicalTest.*' -j48`
- Behavior changed: No. Test-only correction.
- Does this need documentation: No
@morningman
morningman force-pushed the fix-nullable-or-payload branch from 3da0332 to 22b2cf5 Compare September 22, 2026 02:54
@morningman

Copy link
Copy Markdown
Contributor

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17671	3939	3842	3842
q2	2600	359	289	289
q3	10454	1325	755	755
q4	4686	468	346	346
q5	7676	846	539	539
q6	179	170	136	136
q7	729	799	600	600
q8	9338	1409	1501	1409
q9	5872	4186	4178	4178
q10	7620	1332	1019	1019
q11	628	263	236	236
q12	648	418	289	289
q13	19039	2585	2005	2005
q14	265	260	238	238
q15	q16	733	716	654	654
q17	1819	1101	988	988
q18	6482	5575	5552	5552
q19	1268	1201	950	950
q20	471	382	268	268
q21	5577	3072	2823	2823
q22	435	369	292	292
Total cold run time: 104190 ms
Total hot run time: 27408 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4576	4411	4396	4396
q2	712	536	518	518
q3	4760	5093	4644	4644
q4	2206	2293	1429	1429
q5	4353	4351	4308	4308
q6	222	174	139	139
q7	1830	1743	1499	1499
q8	2404	2002	2099	2002
q9	7300	7300	7337	7300
q10	3713	3676	3185	3185
q11	529	390	397	390
q12	764	738	515	515
q13	2445	2771	1994	1994
q14	275	279	239	239
q15	q16	658	682	601	601
q17	7296	6759	6655	6655
q18	11854	10993	11720	10993
q19	1090	991	1020	991
q20	2206	2179	1895	1895
q21	5021	4148	4339	4148
q22	494	438	397	397
Total cold run time: 64708 ms
Total hot run time: 58238 ms

@hello-stephen

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

query5	4307	612	479	479
query6	431	204	188	188
query7	4809	568	296	296
query8	320	178	189	178
query9	8780	3995	3925	3925
query10	452	297	252	252
query11	5799	3543	3235	3235
query12	140	87	82	82
query13	1253	576	412	412
query14	6513	4558	4253	4253
query14_1	4030	3966	3977	3966
query15	202	205	177	177
query16	3247	487	426	426
query17	926	670	550	550
query18	2203	456	337	337
query19	208	179	144	144
query20	81	83	83	83
query21	533	140	122	122
query22	13057	12997	12749	12749
query23	13953	13034	12429	12429
query23_1	12602	12478	12455	12455
query24	7462	1134	672	672
query24_1	671	690	723	690
query25	554	435	388	388
query26	1299	314	167	167
query27	2638	549	332	332
query28	4555	1986	1959	1959
query29	1594	718	531	531
query30	363	216	179	179
query31	890	783	645	645
query32	148	99	92	92
query33	558	312	251	251
query34	1214	1073	620	620
query35	746	753	651	651
query36	801	795	725	725
query37	153	105	97	97
query38	1831	1764	1696	1696
query39	703	691	664	664
query39_1	668	678	662	662
query40	251	120	104	104
query41	72	68	68	68
query42	99	95	93	93
query43	343	349	301	301
query44	1365	702	698	698
query45	186	178	169	169
query46	1075	1227	709	709
query47	1489	1486	1396	1396
query48	415	419	300	300
query49	648	421	304	304
query50	976	380	252	252
query51	10619	10583	10478	10478
query52	91	88	76	76
query53	238	252	175	175
query54	255	204	181	181
query55	80	79	69	69
query56	241	228	213	213
query57	1496	1475	1374	1374
query58	283	260	277	260
query59	1986	2088	1838	1838
query60	272	236	224	224
query61	147	149	150	149
query62	401	318	264	264
query63	209	173	170	170
query64	2382	994	827	827
query65	3469	3420	3440	3420
query66	1692	392	301	301
query67	20327	20014	20253	20014
query68	3359	1490	958	958
query69	519	302	265	265
query70	901	749	787	749
query71	297	229	213	213
query72	2726	2563	2475	2475
query73	847	828	427	427
query74	4614	4476	4291	4291
query75	2300	2301	1904	1904
query76	1894	1090	747	747
query77	359	396	289	289
query78	9019	9001	8537	8537
query79	1395	1155	716	716
query80	1176	452	384	384
query81	590	321	280	280
query82	623	172	129	129
query83	299	222	191	191
query84	312	147	119	119
query85	910	474	383	383
query86	441	237	235	235
query87	2005	1968	1846	1846
query88	3606	2739	2725	2725
query89	381	282	242	242
query90	1977	181	173	173
query91	167	149	126	126
query92	102	91	88	88
query93	1510	1413	801	801
query94	724	348	300	300
query95	689	383	420	383
query96	1035	847	318	318
query97	2415	2417	2307	2307
query98	162	152	143	143
query99	800	732	612	612
Total cold run time: 240249 ms
Total hot run time: 152816 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.15	0.05	0.05
query3	0.39	0.14	0.14
query4	1.61	0.15	0.14
query5	0.24	0.23	0.23
query6	1.22	0.89	0.94
query7	0.04	0.01	0.01
query8	0.05	0.04	0.04
query9	0.54	0.33	0.33
query10	0.54	0.54	0.54
query11	0.26	0.13	0.14
query12	0.23	0.15	0.15
query13	0.47	0.47	0.47
query14	0.95	0.94	0.96
query15	0.62	0.57	0.59
query16	0.31	0.34	0.32
query17	1.11	1.09	1.10
query18	0.21	0.19	0.20
query19	2.10	1.98	1.95
query20	0.02	0.02	0.01
query21	15.84	0.20	0.13
query22	4.78	0.06	0.05
query23	16.32	0.30	0.12
query24	8.60	0.41	0.34
query25	0.12	0.06	0.03
query26	0.72	0.20	0.14
query27	0.04	0.03	0.04
query28	3.57	0.78	0.35
query29	12.71	4.07	3.22
query30	0.29	0.15	0.14
query31	2.80	0.57	0.31
query32	3.35	0.58	0.50
query33	3.17	3.17	3.24
query34	15.49	4.03	3.31
query35	3.25	3.21	3.25
query36	0.57	0.42	0.42
query37	0.26	0.07	0.06
query38	0.05	0.04	0.04
query39	0.11	0.03	0.03
query40	0.22	0.15	0.13
query41	0.21	0.04	0.03
query42	0.11	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 103.69 s
Total hot run time: 24.01 s

@yiguolei

Copy link
Copy Markdown
Contributor

/review

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 66.67% (2/3) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 63.78% (29667/46517)
Line Coverage 48.46% (309037/637745)
Region Coverage 44.01% (249286/566414)
Branch Coverage 45.58% (115877/254200)

@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 two modified row loops now canonicalize OR with the focused, SIMD-friendly (a | b) != 0 form, and the new unit/regression cases are statically discoverable, reach the intended registered/compound paths, and have coherent deterministic outputs. Two additional P1 issues still block the fix: the nullable compound path can read a freed synthetic null map, and a valid legacy noncanonical TRUE still breaks nullable non-short-circuit IF.

I did not duplicate the live threads already covering OR shortcut/regular-CASE compatibility, the nullable OR decision formula, or XOR. The test-helper issue is addressed on this head, and the packed-expression performance concern is resolved.

Checkpoint conclusions: the change is focused and the primary reproduction is covered, but the overall rolling-compatible Boolean goal is incomplete. Registered/compound, constant/vector, nullable/non-nullable, shortcut, planned-versus-physical nullability, CASE/IF, parallel logical operators, and filter consumers were traced. No new concurrency, locking, initialization-order, configuration, transaction/EditLog, durable-format, FE-BE-field, data-write, ABI, observability, or error-propagation concern was found. Raw PBlock Boolean preservation remains the relevant compatibility boundary. The changed tests and expected results are statically sound, but neither blocking case below is covered. This was a static-only review as required: I did not build or run tests, so author/CI test claims are not independent reviewer validation. No additional user focus was supplied.

res_data[i] = lhs_data[i] | rhs_data[i];
// A NULL row may carry an arbitrary nested byte. If the result remains NULL the
// byte is ignored; otherwise normalization prevents it from becoming visible.
res_data[i] = (lhs_data[i] | rhs_data[i]) != 0;

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] Keep both synthesized null maps alive here. A planned Nullable(Boolean) child may legally return a physical non-nullable ColumnUInt8 for an all-non-null batch (VExpr explicitly permits this). If both children do so with mixed values, no shortcut fires and vector_vector_null calls create_null_map_column twice through the same temp_null_map owner. The second assignment destroys the first zero map, leaving lhs_null_map_tmp dangling before do_null_pred reads it in this loop. Under ASAN this is a use-after-free; otherwise result nullness comes from freed memory. Please retain two owners or deliberately share one live zero map, and add a planned-nullable case with two mixed physical non-nullable children.

END) IS NOT NULL
"""

order_qt_case_branches_no_short_circuit """

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] Please cover the nullable IF consumer as part of this compatibility fix. A legacy OR can validly expose mixed condition bytes {65,0} (65 is SQL TRUE), and PBlock preserves them. With a nullable non-NULL THEN value and NULL ELSE, both non-short-circuit IF implementations call apply_negated_null_map; it computes 1 ^ 65 = 64, so the true row becomes NULL instead of returning THEN. This is distinct from the existing regular-CASE thread: a local CASE truthiness fix leaves FunctionIf and VectorizedIfExpr wrong. Canonicalize before using Boolean bytes as null maps (or enforce the boundary earlier) and test this shape with both short-circuit settings.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (3/3) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.18% (34315/45043)
Line Coverage 61.10% (385207/630469)
Region Coverage 57.48% (324253/564130)
Branch Coverage 58.29% (147715/253413)

@Mryange

Mryange commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

run vault_p0

@Mryange
Mryange dismissed github-actions[bot]’s stale review September 22, 2026 07:13

第一条review意见是他过去就这样,
第二条他要我fix的PR去考虑兼容性?

@Mryange
Mryange merged commit f00f110 into apache:master Sep 22, 2026
38 checks passed
@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Sep 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

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

Labels

approved Indicates a PR has been approved by one committer. dev/4.1.4-merged usercase Important user case type label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants