Skip to content

[fix](rbo) Preserve union constant rows across rewrites - #67790

Merged
morrySnow merged 1 commit into
apache:masterfrom
morrySnow:fix/preserve-union-constants
Sep 15, 2026
Merged

morrySnow merged 1 commit into
apache:masterfrom
morrySnow:fix/preserve-union-constants

Conversation

@morrySnow

@morrySnow morrySnow commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A UNION ALL could lose constant rows when a later rewrite exposed another one-row child. Queries that combine an existing constant branch with a single-row GROUP BY or DISTINCT branch returned only the newly exposed row.

Root cause

MergeOneRowRelationIntoUnion rebuilt the union constant-expression list from only the current children. If an earlier rewrite pass had already moved a constant child into the union metadata, a later pass replaced that metadata instead of retaining it.

Reproduction

SELECT 1 AS c
UNION ALL
SELECT c FROM (SELECT 2 AS c) s GROUP BY c;

The result incorrectly omitted the row containing 1. The same behavior occurred with DISTINCT and with the branches reversed.

Fix

Seed the rebuilt constant-expression list with the union existing constant rows before appending newly converted one-row children. This preserves row multiplicity across repeated rewrite passes.

Add a focused rewrite unit test and named regression queries with runner-generated golden output for GROUP BY, DISTINCT, reversed branch order, and duplicate constant rows.

Tests

  • SetOperationOutputMappingTest: 4 tests passed
  • merge_one_row_relation_into_union: named golden regression passed in both force-generation and normal comparison modes
  • Full FE build with UI disabled: passed
  • Checkstyle: passed

@morrySnow

Copy link
Copy Markdown
Contributor Author

run buildall

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

@morrySnow morrySnow changed the title [fix](nereids) Preserve union constant rows across rewrites [fix](rbo) Preserve union constant rows across rewrites Sep 10, 2026
@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17583	3096	3093	3093
q2	2066	244	212	212
q3	10273	858	513	513
q4	4673	252	201	201
q5	7678	565	386	386
q6	138	112	92	92
q7	518	532	383	383
q8	9234	879	871	871
q9	3424	2367	2336	2336
q10	6505	853	687	687
q11	413	196	176	176
q12	651	260	201	201
q13	18098	1518	1151	1151
q14	154	151	138	138
q15	q16	433	389	376	376
q17	1341	858	801	801
q18	3097	2206	2207	2206
q19	1290	825	741	741
q20	390	290	205	205
q21	5588	1694	1806	1694
q22	325	265	226	226
Total cold run time: 93872 ms
Total hot run time: 16689 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3473	3394	3401	3394
q2	513	388	368	368
q3	2229	2219	2191	2191
q4	1177	1167	890	890
q5	2190	2128	2099	2099
q6	168	123	87	87
q7	1004	951	841	841
q8	1587	1399	1403	1399
q9	3120	3102	3069	3069
q10	1860	1798	1615	1615
q11	356	268	252	252
q12	468	426	342	342
q13	1465	1534	1169	1169
q14	185	171	164	164
q15	q16	386	402	366	366
q17	3596	3338	3187	3187
q18	4783	4341	4705	4341
q19	847	780	894	780
q20	1004	956	841	841
q21	3874	3082	3338	3082
q22	393	341	329	329
Total cold run time: 34678 ms
Total hot run time: 30806 ms

@hello-stephen

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

query5	4250	416	330	330
query6	384	140	130	130
query7	4952	420	227	227
query8	287	123	119	119
query9	8685	2903	2878	2878
query10	403	221	179	179
query11	5369	1029	908	908
query12	121	72	69	69
query13	1208	443	319	319
query14	6090	2181	2078	2078
query14_1	1966	1937	1946	1937
query15	178	117	109	109
query16	931	393	348	348
query17	798	449	365	365
query18	2326	325	252	252
query19	168	141	107	107
query20	74	73	70	70
query21	202	99	86	86
query22	5397	5425	5395	5395
query23	6759	6109	5848	5848
query23_1	5968	5984	6063	5984
query24	7283	1047	773	773
query24_1	784	743	775	743
query25	429	311	248	248
query26	1235	227	134	134
query27	2790	411	257	257
query28	4699	1497	1491	1491
query29	933	428	353	353
query30	258	154	132	132
query31	834	398	328	328
query32	139	80	71	71
query33	467	226	179	179
query34	981	842	461	461
query35	410	425	346	346
query36	575	565	551	551
query37	127	80	74	74
query38	1013	845	824	824
query39	484	495	481	481
query39_1	465	454	447	447
query40	201	92	80	80
query41	61	57	59	57
query42	77	72	72	72
query43	242	245	212	212
query44	998	524	558	524
query45	114	113	100	100
query46	761	831	539	539
query47	752	773	702	702
query48	309	303	231	231
query49	549	229	178	178
query50	732	263	191	191
query51	8279	7970	7873	7873
query52	66	66	56	56
query53	196	201	145	145
query54	203	149	137	137
query55	68	61	58	58
query56	185	152	157	152
query57	699	672	606	606
query58	199	166	259	166
query59	1196	1243	1093	1093
query60	245	176	176	176
query61	106	110	117	110
query62	425	209	171	171
query63	171	140	134	134
query64	2804	685	585	585
query65	1648	1548	1589	1548
query66	1832	276	192	192
query67	9806	9697	9751	9697
query68	2753	1257	762	762
query69	337	228	188	188
query70	651	598	604	598
query71	241	187	165	165
query72	2286	1644	1496	1496
query73	638	577	333	333
query74	1567	1213	1146	1146
query75	1175	1098	953	953
query76	2294	715	512	512
query77	252	263	227	227
query78	3890	3734	3257	3257
query79	2430	866	576	576
query80	1588	340	266	266
query81	492	155	134	134
query82	796	120	95	95
query83	281	206	191	191
query84	292	107	93	93
query85	782	343	278	278
query86	406	185	165	165
query87	1021	970	890	890
query88	2775	2095	2125	2095
query89	284	200	177	177
query90	1988	137	134	134
query91	130	118	96	96
query92	76	62	63	62
query93	1757	1061	720	720
query94	635	249	220	220
query95	526	334	226	226
query96	793	630	275	275
query97	1080	1066	986	986
query98	171	135	134	134
query99	430	350	305	305
Total cold run time: 177594 ms
Total hot run time: 81351 ms

@hello-stephen

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

query1	0.01	0.00	0.00
query2	0.08	0.03	0.04
query3	0.25	0.11	0.10
query4	1.60	0.10	0.10
query5	0.16	0.15	0.16
query6	1.28	0.68	0.68
query7	0.03	0.00	0.01
query8	0.04	0.03	0.03
query9	0.29	0.21	0.24
query10	0.34	0.34	0.34
query11	0.17	0.12	0.11
query12	0.15	0.12	0.12
query13	0.30	0.30	0.32
query14	0.45	0.45	0.46
query15	0.36	0.34	0.34
query16	0.21	0.23	0.21
query17	0.71	0.69	0.71
query18	0.18	0.17	0.15
query19	1.20	1.16	1.13
query20	0.01	0.02	0.02
query21	15.44	0.16	0.12
query22	5.10	0.05	0.04
query23	16.21	0.25	0.10
query24	2.99	0.31	0.28
query25	0.10	0.04	0.03
query26	0.77	0.16	0.12
query27	0.03	0.03	0.02
query28	3.66	0.59	0.30
query29	12.42	3.21	2.56
query30	0.26	0.11	0.13
query31	2.75	0.38	0.17
query32	3.52	0.32	0.22
query33	1.41	1.47	1.40
query34	15.35	2.20	1.75
query35	1.78	1.75	1.69
query36	0.46	0.31	0.29
query37	0.07	0.04	0.04
query38	0.04	0.03	0.03
query39	0.04	0.02	0.02
query40	0.11	0.08	0.07
query41	0.08	0.02	0.02
query42	0.03	0.02	0.02
query43	0.03	0.03	0.03
Total cold run time: 90.47 s
Total hot run time: 14.59 s

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

The rewrite fix itself looks correct. I found one test-framework issue that should be addressed.

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: A UNION ALL could lose existing constant rows when a later rewrite exposed another one-row relation. MergeOneRowRelationIntoUnion rebuilt the union constant-expression list only from current children, so constant rows already stored on the union were omitted. Preserve the existing constant-expression list before appending newly merged rows. Unit tests cover output mapping across rewrites, and named regression queries with generated golden baselines cover GROUP BY, DISTINCT, reverse-order, and multiple-constant-row cases.

### Release note

Fix UNION ALL rewrites to preserve existing constant rows when merging one-row relations.

### Check List (For Author)

- Test: Unit Test and Regression Test
    - SetOperationOutputMappingTest
    - merge_one_row_relation_into_union
- Behavior changed: Yes. UNION ALL rewrites no longer drop pre-existing constant rows.
- Does this need documentation: No
@morrySnow
morrySnow force-pushed the fix/preserve-union-constants branch from ab49394 to a905b43 Compare September 14, 2026 03:22
@morrySnow

Copy link
Copy Markdown
Contributor Author

run buildall

@morrySnow

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review conclusion: no new valuable findings. The change is suitable for a comment-only positive review.

The one-line fix preserves the disjoint UNION representation across rewrite-stage re-entry. A production-reachable tree can have an existing metadata row plus a newly exposed one-row child:

Union ALL outputs=[c#U], constant rows=[[1#A]]
  OneRowRelation[2 AS c#B]

MergeOneRowRelationIntoUnion now retains [1#A], maps the live child through regularChildOutput, appends [2#B] once, and removes that child. A repeated pass is inapplicable (or returns the original node when only a duplicate-use volatile child remains), so rows are neither lost nor doubled.

Critical checkpoint conclusions:

  • Goal and proof: the change directly fixes loss of pre-existing UNION constant rows. The focused unit test fails under the prior behavior and checks identity, duplicate-row multiplicity, child removal, and immediate idempotence. The ordered golden regression cases cover GROUP BY and DISTINCT exposure, reversed branches, and three equal UNION ALL rows.
  • Scope and clarity: the implementation is the minimum state-preservation change and uses the existing immutable constant-row representation.
  • Parallel paths and stage order: checked whole-plan and CTE rewrites, repeated PUSH_DOWN_FILTERS, MergeSetOperations, group-by elimination, filter/project-through-union, column pruning, nullable adjustment, and DISTINCT lowering. Every source row remains represented exactly once.
  • Output correctness: existing constant rows already follow the union output by position and intentionally keep row-owned ExprIds; only live children need remapping. Type/nullability handling, logical-property recomputation, statistics, logical-to-physical conversion, and physical translation remain consistent.
  • Qualifiers and special conditions: UNION ALL preserves duplicates. UNION DISTINCT is lowered to an aggregate over the same union changed to ALL, so retaining every input before aggregate deduplication is correct. Volatile-expression evaluation count and rewrite convergence are unchanged.
  • Concurrency and lifecycle: no shared mutable state, locking, thread, asynchronous lifecycle, static initialization, or resource-ownership behavior is introduced.
  • Configuration, compatibility, persistence, and writes: no configuration, FE-BE field, storage format, rolling-upgrade contract, transaction, persistence, or data-write path changes apply.
  • Error handling and observability: no error boundary or invariant handling is weakened, and the narrow planner-state fix does not require new logging or metrics.
  • Performance: copying the existing small metadata-row list is linear only when an eligible one-row child is absorbed; no material hot-path regression was found.
  • Other risks: the final changed-file and unresolved-candidate sweep found no remaining correctness, compatibility, performance, or coverage issue.

The existing inline request to use named golden-output regression cases is addressed by the current qt_* cases and .out baseline, so it was not duplicated. There was no additional user-provided review focus.

This was a static review as required by the runner contract; I did not independently run builds or tests. All normal and risk-focused review agents converged on NO_NEW_VALUABLE_FINDINGS in round 1. Live submission gate: base df36e174b99557a004bc3ad57faa019da4a7d91f, head a905b4372042c2c2ce8b15f6d0e0f21849b0df0c.

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17621	3044	3035	3035
q2	2096	255	235	235
q3	10224	963	532	532
q4	4665	250	201	201
q5	7677	574	381	381
q6	142	114	93	93
q7	532	511	384	384
q8	9233	933	867	867
q9	3441	2352	2409	2352
q10	6514	846	699	699
q11	393	198	181	181
q12	617	267	203	203
q13	18127	1534	1147	1147
q14	161	146	146	146
q15	q16	425	396	373	373
q17	1389	912	818	818
q18	3077	2264	2224	2224
q19	1266	893	786	786
q20	372	289	197	197
q21	5663	1824	1868	1824
q22	331	269	238	238
Total cold run time: 93966 ms
Total hot run time: 16916 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3401	3322	3331	3322
q2	514	394	373	373
q3	2201	2290	2151	2151
q4	1186	1165	894	894
q5	2177	2128	2088	2088
q6	163	120	88	88
q7	1041	937	898	898
q8	1573	1374	1382	1374
q9	3113	3076	3093	3076
q10	1866	1780	1643	1643
q11	348	267	247	247
q12	451	431	348	348
q13	1457	1537	1148	1148
q14	177	169	158	158
q15	q16	391	390	360	360
q17	3558	3254	3300	3254
q18	4805	4391	4717	4391
q19	869	874	902	874
q20	983	963	855	855
q21	3872	3204	3189	3189
q22	391	346	318	318
Total cold run time: 34537 ms
Total hot run time: 31049 ms

@hello-stephen

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

query5	4258	431	341	341
query6	380	137	121	121
query7	4936	407	232	232
query8	297	130	128	128
query9	8693	2897	2895	2895
query10	415	243	185	185
query11	5381	1024	916	916
query12	121	69	71	69
query13	1186	441	336	336
query14	6135	2305	2174	2174
query14_1	2060	2041	2035	2035
query15	175	127	115	115
query16	915	382	354	354
query17	806	460	364	364
query18	2323	332	234	234
query19	170	139	109	109
query20	75	71	73	71
query21	201	101	87	87
query22	5391	5357	5395	5357
query23	6838	6321	6203	6203
query23_1	6179	6227	6136	6136
query24	7231	1111	787	787
query24_1	774	811	782	782
query25	447	311	288	288
query26	1232	214	131	131
query27	2803	398	255	255
query28	4694	1503	1488	1488
query29	928	447	350	350
query30	253	156	126	126
query31	819	404	337	337
query32	129	76	75	75
query33	464	233	187	187
query34	977	841	486	486
query35	417	404	367	367
query36	558	560	527	527
query37	172	85	77	77
query38	1043	877	814	814
query39	498	476	481	476
query39_1	476	468	487	468
query40	203	96	81	81
query41	63	56	57	56
query42	81	75	77	75
query43	247	247	214	214
query44	1004	549	548	548
query45	116	105	103	103
query46	813	804	515	515
query47	759	774	702	702
query48	296	307	237	237
query49	540	250	198	198
query50	757	299	193	193
query51	8254	8089	8201	8089
query52	99	73	61	61
query53	200	205	146	146
query54	207	175	166	166
query55	73	58	55	55
query56	199	160	167	160
query57	705	667	641	641
query58	198	180	165	165
query59	1260	1258	1140	1140
query60	247	181	170	170
query61	114	125	119	119
query62	353	212	185	185
query63	171	142	143	142
query64	2626	743	592	592
query65	1666	1594	1592	1592
query66	1770	269	197	197
query67	9679	9792	9588	9588
query68	2766	1200	767	767
query69	322	235	191	191
query70	697	623	617	617
query71	266	182	166	166
query72	2280	1678	1410	1410
query73	634	595	338	338
query74	1564	1262	1138	1138
query75	1167	1094	964	964
query76	2306	698	537	537
query77	265	269	210	210
query78	3983	3728	3169	3169
query79	2848	877	555	555
query80	1633	319	280	280
query81	512	154	138	138
query82	628	125	94	94
query83	289	218	200	200
query84	295	109	89	89
query85	806	376	293	293
query86	468	175	169	169
query87	1042	1004	895	895
query88	2899	2098	2098	2098
query89	294	198	176	176
query90	1965	131	131	131
query91	129	115	99	99
query92	89	71	72	71
query93	1734	1039	711	711
query94	630	244	234	234
query95	543	241	293	241
query96	825	593	268	268
query97	1076	1100	1015	1015
query98	162	133	136	133
query99	419	350	316	316
Total cold run time: 178501 ms
Total hot run time: 82539 ms

@hello-stephen

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

query1	0.01	0.00	0.01
query2	0.08	0.03	0.04
query3	0.26	0.11	0.11
query4	1.60	0.09	0.08
query5	0.17	0.15	0.15
query6	1.30	0.70	0.71
query7	0.03	0.01	0.00
query8	0.04	0.03	0.03
query9	0.28	0.21	0.21
query10	0.34	0.33	0.36
query11	0.16	0.12	0.11
query12	0.15	0.12	0.12
query13	0.31	0.31	0.31
query14	0.46	0.45	0.46
query15	0.35	0.34	0.35
query16	0.22	0.22	0.22
query17	0.69	0.71	0.72
query18	0.19	0.17	0.17
query19	1.19	1.14	1.19
query20	0.01	0.01	0.02
query21	15.43	0.16	0.11
query22	5.07	0.05	0.05
query23	16.18	0.26	0.11
query24	2.99	0.31	0.25
query25	0.12	0.04	0.03
query26	0.78	0.17	0.13
query27	0.04	0.02	0.03
query28	3.65	0.57	0.27
query29	12.42	3.19	2.56
query30	0.25	0.12	0.13
query31	2.75	0.36	0.17
query32	3.53	0.31	0.23
query33	1.50	1.43	1.49
query34	15.41	2.19	1.78
query35	1.73	1.76	1.76
query36	0.45	0.29	0.28
query37	0.07	0.04	0.04
query38	0.05	0.03	0.02
query39	0.04	0.02	0.03
query40	0.12	0.08	0.07
query41	0.08	0.02	0.03
query42	0.03	0.02	0.02
query43	0.03	0.03	0.03
Total cold run time: 90.56 s
Total hot run time: 14.74 s

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

Re-reviewed at a905b43. The previous regression-test concern is addressed: the deterministic cases now use named ordered qt_* queries with a generated .out baseline. The production change correctly preserves existing UNION constant rows before appending newly rewritten one-row children, and the focused unit/regression coverage checks row preservation, duplicate multiplicity, order, and rewrite idempotence. The current cloud_p0 deployment failure (missing libjvm.so/Maven) and NonConcurrent BE LSan/muted routine-load failure are unrelated to this FE planner change.

@morrySnow
morrySnow merged commit 8ea383a into apache:master Sep 15, 2026
35 of 36 checks passed
@morrySnow
morrySnow deleted the fix/preserve-union-constants branch September 15, 2026 03:04
github-actions Bot pushed a commit that referenced this pull request Sep 18, 2026
## Problem

A UNION ALL could lose constant rows when a later rewrite exposed
another one-row child. Queries that combine an existing constant branch
with a single-row GROUP BY or DISTINCT branch returned only the newly
exposed row.

## Root cause

MergeOneRowRelationIntoUnion rebuilt the union constant-expression list
from only the current children. If an earlier rewrite pass had already
moved a constant child into the union metadata, a later pass replaced
that metadata instead of retaining it.

## Reproduction

```sql
SELECT 1 AS c
UNION ALL
SELECT c FROM (SELECT 2 AS c) s GROUP BY c;
```

The result incorrectly omitted the row containing 1. The same behavior
occurred with DISTINCT and with the branches reversed.

## Fix

Seed the rebuilt constant-expression list with the union existing
constant rows before appending newly converted one-row children. This
preserves row multiplicity across repeated rewrite passes.

Add a focused rewrite unit test and named regression queries with
runner-generated golden output for GROUP BY, DISTINCT, reversed branch
order, and duplicate constant rows.

## Tests

- `SetOperationOutputMappingTest`: 4 tests passed
- `merge_one_row_relation_into_union`: named golden regression passed in
both force-generation and normal comparison modes
- Full FE build with UI disabled: passed
- Checkstyle: passed
yiguolei pushed a commit that referenced this pull request Sep 19, 2026
…67790 (#68172)

Cherry-picked from #67790

Co-authored-by: morrySnow <zhangwenxin@selectdb.com>
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.

4 participants