Skip to content

[fix](function) Do not mutate shared nested column when IF normalizes a nullable condition - #67931

Open
mrhhsg wants to merge 1 commit into
apache:masterfrom
mrhhsg:fix/if-null-condition-shared-column
Open

mrhhsg wants to merge 1 commit into
apache:masterfrom
mrhhsg:fix/if-null-condition-shared-column

Conversation

@mrhhsg

@mrhhsg mrhhsg commented Sep 14, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: None

Problem Summary:

When the condition of IF is a Nullable(Boolean) column, both
VectorizedIfExpr and FunctionIf normalize it by treating NULL as false.
They did this by writing nested[i] &= !null_map[i] directly into the
nested column of the nullable condition.

That nested column can be shared with other columns of the same block.
NULLIF(b, p) is implemented as if(b = p, NULL, b) and wraps b itself
as the nested column of its Nullable(Boolean) result, so
IF(NULLIF(b, p), f, b) overwrote b in place: the else branch read the
polluted values and returned a wrong result, and any other projection of
b in the same block was polluted as well. With
short_circuit_evaluation=true the expression goes through a different
path and returned the correct result, which hid the bug.

Build a fresh condition column from nested & !null_map instead of
mutating the shared nested column in place.

Release note

None

Check List (For Author)

  • Test:
    • Unit Test: VConditionExprIfTest.NullableCondition_NotPolluteSharedNestedColumn, FunctionIfTest.NullableConditionNotPolluteSharedNestedColumn (both fail without the fix)
    • Regression test: test_if_nullable_condition
  • Behavior changed: No
  • Does this need documentation: No

https://claude.ai/code/session_013PjVxV5VYPRduC2fubqCzD

… a nullable condition

### What problem does this PR solve?

Issue Number: None

Problem Summary:

When the condition of `IF` is a `Nullable(Boolean)` column, both
`VectorizedIfExpr` and `FunctionIf` normalize it by treating NULL as false.
They did this by writing `nested[i] &= !null_map[i]` directly into the
nested column of the nullable condition.

That nested column can be shared with other columns of the same block.
`NULLIF(b, p)` is implemented as `if(b = p, NULL, b)` and wraps `b` itself
as the nested column of its `Nullable(Boolean)` result, so
`IF(NULLIF(b, p), f, b)` overwrote `b` in place: the else branch read the
polluted values and returned a wrong result, and any other projection of
`b` in the same block was polluted as well. With
`short_circuit_evaluation=true` the expression goes through a different
path and returned the correct result, which hid the bug.

Build a fresh condition column from `nested & !null_map` instead of
mutating the shared nested column in place.

### Release note

None

### Check List (For Author)

- Test:
    - Unit Test: `VConditionExprIfTest.NullableCondition_NotPolluteSharedNestedColumn`, `FunctionIfTest.NullableConditionNotPolluteSharedNestedColumn` (both fail without the fix)
    - Regression test: `test_if_nullable_condition`
- Behavior changed: No
- Does this need documentation: No

Claude-Session: https://claude.ai/code/session_013PjVxV5VYPRduC2fubqCzD
@mrhhsg

mrhhsg commented Sep 14, 2026

Copy link
Copy Markdown
Member Author

/review

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

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

Automated review summary — no actionable findings

Reviewed PR #67931 at exact head edbca7c7950eed388e92c35683e0909d3a6f49e5 against base df36e174b99557a004bc3ad57faa019da4a7d91f. The review is summary-only because no distinct inline issue remains after duplicate fencing, candidate adjudication, and two convergence rounds.

Critical checkpoint conclusions:

  • Goal and data correctness: The change prevents nullable IF normalization from mutating a shared nested Boolean column. The fresh row-aligned condition implements nested AND NOT null, preserves SQL NULL-as-false behavior, and fixes the production-reachable IF(NULLIF(b, p), f, b) alias path.
  • Scope and clarity: The production change is focused in the two non-short-circuit IF implementations, with targeted unit and regression coverage. No unrelated source behavior is changed.
  • Concurrency: Expression blocks and temporary columns are evaluation-local. The new implementation removes the shared mutation and introduces no locks, atomics, thread entry points, or cross-thread lifecycle.
  • Lifecycle and static initialization: The detached condition is owned by a local evaluation block and released with it. Recursive dispatch appends at most once because the replacement condition is non-nullable. No static initialization or ownership cycle is introduced.
  • Configuration: No configuration item is added or changed. The regression suite only switches the existing short-circuit setting to cover both execution modes.
  • Compatibility: No symbol, storage format, serialization, RPC, FE-BE protocol, or rolling-upgrade contract changes.
  • Parallel paths: FunctionIf and VectorizedIfExpr are both corrected. ShortCircuitIfExpr was traced separately and already routes NULL through the false selector without mutating the condition.
  • Conditional and error handling: Top-level constants are materialized before nullable dispatch; all-NULL, nullable then/else, scalar, and generic paths remain consistent. Production Status propagation is unchanged and checked.
  • Tests and results: The new FunctionIf and VectorizedIfExpr unit tests directly retain a shared nested owner and assert both the selected result and source preservation. The ordered regression queries cover short-circuit off/on and projected-source preservation; the checked .out rows match the SQL semantics. Test discovery is automatic through the BE test glob, and no source .cpp inclusion or unity-skip update is involved.
  • Observability: No new distributed or failure-prone operation is introduced, so additional logging or metrics are not needed.
  • Transactions, persistence, and writes: Not applicable; there is no transaction, EditLog, metadata persistence, storage write, or crash-recovery change.
  • FE-BE variables: No new transmitted variable or FE/BE type contract is introduced.
  • Performance and memory: The fresh byte column uses the tracked Doris column allocator and has bounded local lifetime. A possible conditional-reuse idea was investigated and dismissed as a current-patch issue because nested uniqueness does not prove whole-tree COW exclusivity in either actual caller ownership graph.
  • Additional review: Header hygiene, test determinism, null/const shapes, recursive termination, block reallocation, and result aliasing were checked; no further issue was substantiated.

User focus: no additional focus was provided; the complete PR was reviewed.

Verification scope: static review only, as required by the review runner contract. No build or test was executed independently. At submission time the available formatter, checkstyle, license, secrets, title, and changed-file checks were passing, while broad build/test jobs were skipped.

@mrhhsg

mrhhsg commented Sep 14, 2026

Copy link
Copy Markdown
Member Author

Local pipeline review — ✅ PASS

schema: doris-repo-review/v1
status: PASS
pr: apache/doris#67931
commit: edbca7c7950eed388e92c35683e0909d3a6f49e5
base: df36e174b99557a004bc3ad57faa019da4a7d91f
reviewed_at: 2026-09-14T14:42+08:00
reviewer: mrhhsg
model: gpt-6-astra
effort: xhigh
findings: {blocker: 0, major: 0, minor: 0, nit: 0}
rounds: 1
converged: true

Notes for maintainers

  • 本次为固定提交的静态源码评审;已核对两条 IF 修复路径及测试覆盖,未运行 build、UT、regression 或 benchmark。

Reviewed locally with the doris-repo-review pipeline. Repository policy may accept this receipt for the matching commit; it is not a human Apache approval.

@mrhhsg

mrhhsg commented Sep 14, 2026

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17567	3000	3016	3000
q2	2077	247	216	216
q3	10287	882	517	517
q4	4668	251	206	206
q5	7670	573	388	388
q6	135	121	94	94
q7	528	503	394	394
q8	9233	899	891	891
q9	3490	2404	2434	2404
q10	6500	886	709	709
q11	407	195	182	182
q12	617	265	191	191
q13	18175	1540	1180	1180
q14	160	152	142	142
q15	q16	449	401	378	378
q17	1398	906	828	828
q18	3153	2334	2317	2317
q19	1288	921	696	696
q20	400	280	199	199
q21	5582	1753	1866	1753
q22	343	273	235	235
Total cold run time: 94127 ms
Total hot run time: 16920 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3360	3310	3291	3291
q2	501	404	367	367
q3	2239	2403	2168	2168
q4	1220	1197	895	895
q5	2221	2123	2164	2123
q6	167	121	87	87
q7	1059	925	859	859
q8	1583	1403	1382	1382
q9	3216	3206	3163	3163
q10	1943	1858	1659	1659
q11	358	273	250	250
q12	461	441	351	351
q13	1512	1583	1165	1165
q14	178	166	160	160
q15	q16	396	396	355	355
q17	3640	3420	3242	3242
q18	5068	4498	5079	4498
q19	953	853	861	853
q20	1040	971	858	858
q21	3842	3243	3217	3217
q22	395	333	317	317
Total cold run time: 35352 ms
Total hot run time: 31260 ms

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 63.32% (29665/46847)
Line Coverage 48.26% (310204/642776)
Region Coverage 43.80% (249825/570404)
Branch Coverage 45.39% (116308/256266)

@hello-stephen

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

query5	4238	422	330	330
query6	386	140	145	140
query7	4919	403	238	238
query8	291	131	126	126
query9	8700	2876	2871	2871
query10	387	219	182	182
query11	5394	1075	942	942
query12	122	70	68	68
query13	1190	452	330	330
query14	6059	2262	2125	2125
query14_1	2026	2019	2029	2019
query15	172	129	115	115
query16	922	361	352	352
query17	805	433	346	346
query18	2318	324	227	227
query19	157	129	102	102
query20	70	67	67	67
query21	196	100	83	83
query22	5662	5423	5366	5366
query23	6826	6309	6122	6122
query23_1	6201	6318	6331	6318
query24	7281	1079	765	765
query24_1	759	753	768	753
query25	402	293	238	238
query26	1232	237	125	125
query27	2796	400	247	247
query28	4710	1483	1514	1483
query29	919	430	329	329
query30	251	154	133	133
query31	832	407	334	334
query32	126	77	73	73
query33	460	213	158	158
query34	991	829	482	482
query35	401	400	356	356
query36	555	540	536	536
query37	118	85	68	68
query38	1004	850	826	826
query39	498	511	471	471
query39_1	461	468	446	446
query40	195	92	78	78
query41	63	56	55	55
query42	75	76	72	72
query43	251	281	206	206
query44	973	532	541	532
query45	112	100	104	100
query46	744	810	532	532
query47	752	752	705	705
query48	316	303	210	210
query49	540	239	186	186
query50	708	262	190	190
query51	7960	7920	8003	7920
query52	64	66	61	61
query53	189	191	148	148
query54	209	175	168	168
query55	67	57	55	55
query56	191	165	261	165
query57	688	669	661	661
query58	203	167	162	162
query59	1254	1274	1121	1121
query60	240	193	199	193
query61	130	146	125	125
query62	364	217	176	176
query63	170	144	146	144
query64	2799	768	707	707
query65	1675	1672	1666	1666
query66	1828	249	195	195
query67	10009	9950	10127	9950
query68	2998	1179	721	721
query69	332	219	190	190
query70	649	629	612	612
query71	244	173	166	166
query72	2299	1689	1517	1517
query73	634	616	341	341
query74	1985	1244	1156	1156
query75	1187	1115	997	997
query76	2352	722	500	500
query77	249	264	209	209
query78	4085	3905	3342	3342
query79	2786	794	591	591
query80	1580	324	291	291
query81	515	159	133	133
query82	680	132	96	96
query83	281	212	188	188
query84	297	112	89	89
query85	798	329	279	279
query86	479	173	156	156
query87	1046	1003	925	925
query88	3194	2110	2098	2098
query89	282	189	174	174
query90	2181	130	125	125
query91	129	114	95	95
query92	100	72	66	66
query93	2655	1064	751	751
query94	644	245	202	202
query95	505	322	232	232
query96	840	559	249	249
query97	1070	1121	1007	1007
query98	176	133	132	132
query99	423	358	317	317
Total cold run time: 180682 ms
Total hot run time: 82944 ms

@hello-stephen

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

query1	0.00	0.01	0.00
query2	0.07	0.03	0.04
query3	0.24	0.10	0.11
query4	1.60	0.10	0.10
query5	0.18	0.16	0.17
query6	1.24	0.69	0.70
query7	0.03	0.01	0.00
query8	0.04	0.03	0.03
query9	0.28	0.21	0.22
query10	0.36	0.34	0.37
query11	0.16	0.12	0.12
query12	0.15	0.13	0.12
query13	0.30	0.30	0.31
query14	0.46	0.45	0.46
query15	0.38	0.36	0.36
query16	0.22	0.23	0.21
query17	0.67	0.66	0.68
query18	0.18	0.16	0.18
query19	1.17	1.13	1.18
query20	0.02	0.01	0.01
query21	15.44	0.16	0.12
query22	5.07	0.04	0.04
query23	16.16	0.26	0.11
query24	2.97	0.33	0.27
query25	0.11	0.04	0.04
query26	0.83	0.16	0.11
query27	0.04	0.04	0.03
query28	3.68	0.56	0.28
query29	12.51	3.15	2.54
query30	0.26	0.11	0.11
query31	2.75	0.38	0.17
query32	3.52	0.32	0.24
query33	1.53	1.42	1.58
query34	15.37	2.20	1.78
query35	1.81	1.74	1.74
query36	0.50	0.30	0.28
query37	0.06	0.04	0.04
query38	0.04	0.03	0.03
query39	0.04	0.02	0.03
query40	0.12	0.08	0.07
query41	0.08	0.03	0.02
query42	0.03	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 90.71 s
Total hot run time: 14.7 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.37% (34658/45380)
Line Coverage 61.29% (389498/635495)
Region Coverage 57.58% (327193/568199)
Branch Coverage 58.37% (149145/255526)

@mrhhsg
mrhhsg marked this pull request as ready for review September 15, 2026 02:14
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