Skip to content

[fix](complex) Support literal defaults for complex columns - #63528

Open
mrhhsg wants to merge 6 commits into
apache:masterfrom
mrhhsg:fix/reject-complex-type-default
Open

mrhhsg wants to merge 6 commits into
apache:masterfrom
mrhhsg:fix/reject-complex-type-default

Conversation

@mrhhsg

@mrhhsg mrhhsg commented May 22, 2026 •

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: ARRAY, MAP, and STRUCT columns previously only supported DEFAULT NULL or very limited empty ARRAY defaults. This prevented valid literal defaults such as [], {}, [1, 2], {"a": 1}, or {1, "x"} from being used consistently when creating a table, adding a column, or reading default values from older rowsets. This change validates non-null complex defaults as type-appropriate literals in FE, teaches BE complex SerDes to parse these literal strings into typed Fields, and lets default iterators and direct schema change parse explicit defaults through non-nullable SerDes so type-mismatched nested values fail instead of being silently converted to NULL. Explicit nested NULL values are now accepted only when the nested SerDe is nullable, so non-nullable nested struct fields reject NULL defaults during parsing instead of reaching column insertion as a null Field. The regression coverage avoids asserting local-storage rewrite cancellation in cloud mode, where add-column schema change may finish as a metadata-only operation. INSERT fills unmentioned columns through a FE-side cast of the default literal, which is non-strict by default, so the regression exercises BE default parsing by reading rows written before a column with an invalid default was added through light schema change. JSON and VARIANT columns still only support DEFAULT NULL.

FE stores the default in a canonical text rather than the raw SQL spelling: the value is parsed as a SQL literal, every nested value is cast to the declared nested type with strict checking, and the result is rendered with unquoted numbers (booleans as 1/0), double quoted strings/dates/ips, nested brackets and NULL. Type mismatches, typed or exponent literals such as [DATEV2 "2024-01-01"] or [1e3], and repeated map keys are therefore resolved at DDL time, and BE parses the same canonical text through the default value iterator, schema change, and the INSERT cast. Nested string values that contain quotes or backslashes are rejected because neither BE parser decodes escapes and the DDL parser keeps default text verbatim when SHOW CREATE TABLE output is replayed. SHOW CREATE TABLE wraps a default that contains double quotes in single quotes so CREATE TABLE LIKE can replay it, and load default_value / replace_value mappings validate complex columns through the same canonicalizer.

Release note

Support literal default values for ARRAY, MAP, and STRUCT columns.

Check List (For Author)

  • Test:
    • Unit Test: ./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.info.ColumnDefinitionTest,org.apache.doris.catalog.ColumnTest
    • Unit Test: ./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.AlterTableCommandTest
    • Unit Test: ./run-be-ut.sh --run --filter='DataTypeSerDeFromStringTest.*' -j 32
    • Unit Test: ./run-be-ut.sh --run --filter=DataTypeSerDeFromStringTest.*:ColumnReaderTest.DefaultValue* -j 64
    • Build: ./build.sh --be --fe
    • Format: build-support/clang-format.sh; build-support/check-format.sh; git diff --check
    • Regression test: doris-local-regression --network 10.26.20.3/24 run -d datatype_p0/complex_types -s test_complex_default_value (-genOut first, then compare mode)
    • Regression test: doris-local-regression --network 10.26.20.3/24 run -d schema_change_p0 -s test_dup_schema_key_add,test_unique_schema_key_change_add,test_modify_struct
    • Regression test: doris-local-regression --network 10.26.20.3/24 run -d external_table_p0/iceberg -s test_iceberg_schema_change_complex_types
  • Behavior changed: Yes. ARRAY, MAP, and STRUCT non-null literal defaults are accepted, type checked at DDL time, stored in canonical form and materialized in both the default-iterator and direct schema-change paths; nested string defaults may not contain quotes or backslashes. JSON and VARIANT non-null defaults remain rejected.
  • Does this need documentation: No

@mrhhsg

mrhhsg commented May 22, 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.

No blocking issues found.

Checkpoint conclusions:

  • Goal and tests: The PR rejects non-null defaults for ARRAY, MAP, STRUCT, JSON, and VARIANT columns while preserving omitted defaults and explicit DEFAULT NULL. The new unit test and regression case cover create-table failures, allowed null/no-default cases, and ALTER ADD COLUMN rejection; existing expected outputs were updated for the new NULL default behavior.
  • Scope and clarity: The main validation change is small and focused in ColumnDefinition, with broad test DDL updates required by the behavior change.
  • Concurrency and lifecycle: Not applicable; this is DDL validation and test data update logic with no new shared mutable state, locks, or lifecycle-sensitive objects.
  • Configuration and compatibility: No new configuration items. Existing persisted tables/defaults are not migrated here; the change affects new DDL validation only.
  • Parallel paths: Create table and schema-change ADD COLUMN paths both flow through ColumnDefinition validation. Legacy ColumnDef translation also delegates into ColumnDefinition, so the old and Nereids paths appear covered.
  • Data correctness: Rejecting string literals for complex defaults avoids storing untyped complex defaults and aligns DEFAULT() / partial-update behavior with nullable complex columns.
  • Test coverage: Coverage is reasonable for the changed behavior. I did not run the test suite locally in this review runner; I reviewed the author-listed test coverage and checked the patch for whitespace issues with git diff --check.
  • Observability: Not applicable; this is user-facing analysis validation with explicit error messages.
  • User focus: No additional user-provided review focus was present.

@mrhhsg

mrhhsg commented May 22, 2026

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17766	4071	4018	4018
q2	q3	10831	1365	806	806
q4	4682	477	348	348
q5	7583	2349	2126	2126
q6	250	186	144	144
q7	960	761	646	646
q8	9487	1802	1552	1552
q9	5152	4947	4945	4945
q10	6356	2082	1780	1780
q11	432	270	244	244
q12	627	429	295	295
q13	18118	3431	2778	2778
q14	261	255	248	248
q15	q16	817	774	706	706
q17	1010	951	996	951
q18	7041	5631	5439	5439
q19	1296	1335	1078	1078
q20	650	486	288	288
q21	6314	2968	2625	2625
q22	459	521	342	342
Total cold run time: 100092 ms
Total hot run time: 31359 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	4821	4756	4816	4756
q2	q3	4906	5248	4632	4632
q4	2155	2224	1399	1399
q5	5014	4541	4621	4541
q6	254	180	128	128
q7	1917	1721	1550	1550
q8	2518	2159	2160	2159
q9	7634	7235	7293	7235
q10	4485	4480	3970	3970
q11	522	406	347	347
q12	714	734	514	514
q13	3097	3383	2805	2805
q14	284	281	260	260
q15	q16	678	698	613	613
q17	1280	1263	1256	1256
q18	7295	6813	6850	6813
q19	1134	1112	1129	1112
q20	2214	2190	1926	1926
q21	5420	4654	4540	4540
q22	530	474	402	402
Total cold run time: 56872 ms
Total hot run time: 50958 ms

@hello-stephen

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

query5	4334	665	513	513
query6	336	221	203	203
query7	4250	564	305	305
query8	326	234	215	215
query9	8837	3967	3970	3967
query10	463	338	295	295
query11	5598	2402	2236	2236
query12	182	126	123	123
query13	1262	645	461	461
query14	6018	5394	5023	5023
query14_1	4356	4359	4315	4315
query15	212	200	182	182
query16	1016	450	447	447
query17	1131	719	588	588
query18	2614	489	354	354
query19	213	197	158	158
query20	134	126	126	126
query21	208	143	124	124
query22	13765	13620	13350	13350
query23	17288	16409	15938	15938
query23_1	16170	16079	16051	16051
query24	7407	1748	1293	1293
query24_1	1308	1302	1267	1267
query25	557	471	416	416
query26	1313	320	173	173
query27	2678	579	349	349
query28	4428	1984	1929	1929
query29	1030	652	530	530
query30	306	240	202	202
query31	1138	1090	959	959
query32	98	79	79	79
query33	549	359	305	305
query34	1176	1166	671	671
query35	773	788	702	702
query36	1362	1319	1221	1221
query37	159	109	91	91
query38	3218	3121	3100	3100
query39	933	912	885	885
query39_1	873	871	876	871
query40	244	152	132	132
query41	72	70	68	68
query42	116	112	111	111
query43	327	334	297	297
query44	
query45	217	208	203	203
query46	1114	1187	738	738
query47	2280	2348	2113	2113
query48	409	417	293	293
query49	648	517	407	407
query50	1037	358	257	257
query51	4298	4266	4157	4157
query52	108	109	95	95
query53	262	281	207	207
query54	335	290	266	266
query55	98	92	100	92
query56	322	316	320	316
query57	1396	1389	1278	1278
query58	308	284	280	280
query59	1563	1638	1424	1424
query60	335	336	324	324
query61	182	177	190	177
query62	690	627	563	563
query63	246	201	203	201
query64	2430	839	645	645
query65	
query66	1691	477	354	354
query67	30192	30064	29947	29947
query68	
query69	461	337	293	293
query70	1044	945	1009	945
query71	311	280	271	271
query72	3086	2774	2443	2443
query73	828	767	414	414
query74	5094	4890	4744	4744
query75	2658	2590	2275	2275
query76	2325	1146	817	817
query77	402	403	339	339
query78	12172	12206	11661	11661
query79	1489	1043	760	760
query80	905	555	455	455
query81	489	277	240	240
query82	1353	172	123	123
query83	359	285	251	251
query84	261	147	109	109
query85	939	557	469	469
query86	430	335	323	323
query87	3451	3352	3199	3199
query88	3582	2668	2669	2668
query89	443	384	335	335
query90	1886	186	180	180
query91	183	167	155	155
query92	78	88	80	80
query93	1474	1613	865	865
query94	625	335	303	303
query95	653	465	353	353
query96	1057	857	352	352
query97	2740	2735	2622	2622
query98	239	229	240	229
query99	1172	1145	1039	1039
Total cold run time: 254207 ms
Total hot run time: 169370 ms

@mrhhsg

mrhhsg commented May 22, 2026

Copy link
Copy Markdown
Member Author

run cloud_p0

@github-actions

Copy link
Copy Markdown
Contributor

Possible file(s) that should be tracked in LFS detected: 🚨

The following file(s) exceeds the file size limit: 1048576 bytes, as set in the .yml configuration files:

  • regression-test/data/nereids_function_p0/scalar_function/Array1.out

Consider using git-lfs to manage large files.

@github-actions github-actions Bot added the lfs-detected! Warning Label for use when LFS is detected in the commits of a Pull Request label May 22, 2026
@mrhhsg

mrhhsg commented May 22, 2026

Copy link
Copy Markdown
Member Author

run compile

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 66.67% (4/6) 🎉
Increment coverage report
Complete coverage report

@mrhhsg
mrhhsg force-pushed the fix/reject-complex-type-default branch from bab2f97 to a5b7348 Compare May 27, 2026 04:01
@mrhhsg mrhhsg changed the title [fix](fe) Reject non-null defaults for complex columns [fix](complex) Support literal defaults for complex columns May 27, 2026
@github-actions github-actions Bot removed the lfs-detected! Warning Label for use when LFS is detected in the commits of a Pull Request label May 27, 2026
@mrhhsg

mrhhsg commented May 27, 2026

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

I found one blocking correctness issue in the new complex-default parsing path.

Critical checkpoint conclusions:

  • Goal/test: The PR aims to support ARRAY/MAP/STRUCT literal defaults and adds FE unit/regression coverage for valid literals, NULL defaults, and shape-invalid literals. It does not cover type-invalid nested literal values.
  • Scope/focus: The change is focused on complex default validation/parsing and default iterators.
  • Concurrency/lifecycle/config/compatibility: No new concurrency, lifecycle-sensitive globals, configs, storage-format changes, or FE-BE protocol fields were found.
  • Parallel paths: Nereids validation and BE default materialization were reviewed together; the identified issue is in BE nested parsing and can affect create/alter defaults once FE accepts a complex literal shape.
  • Tests: Positive cases are covered, but negative cases for nested element/key/value/field type conversion are missing, which exposes the bug below.
  • Observability/transactions/data writes: No new transaction or persistence logic was added; the risk is data correctness for materialized default values.
  • User focus: No additional user-provided review focus was supplied.

Comment thread be/src/core/data_type_serde/complex_type_deserialize_util.h Outdated
@mrhhsg
mrhhsg force-pushed the fix/reject-complex-type-default branch from a5b7348 to dc36a4b Compare May 27, 2026 06:19
@mrhhsg

mrhhsg commented May 27, 2026

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

I found one correctness issue that should be fixed before approval.

Critical checkpoint conclusions:

  • Goal/test coverage: The PR mostly implements literal defaults for ARRAY/MAP/STRUCT and adds FE unit plus regression coverage, but nested NULL spelling coverage is missing.
  • Scope/focus: The change is focused on complex default validation/materialization.
  • Concurrency/lifecycle: No new concurrency, locks, or non-trivial lifecycle management found in the touched paths.
  • Config/compatibility: No config or storage-format change; metadata remains string defaults, but BE parsing must accept all FE-accepted literal forms.
  • Parallel paths: FE validation and BE materialization paths were both updated, but their NULL handling is inconsistent for nested literals.
  • Error handling/data correctness: One FE/BE mismatch can let valid DDL fail later during default materialization.
  • Tests/results: Added outputs look deterministic, but missing a case for uppercase/mixed-case nested NULL literals.
  • Observability/performance: No additional observability required; no material hot-path performance concern found.

User focus: No additional user-provided review focus was present.

Comment thread be/src/core/data_type_serde/complex_type_deserialize_util.h
@mrhhsg
mrhhsg force-pushed the fix/reject-complex-type-default branch from dc36a4b to c5101b1 Compare May 27, 2026 07:19
@mrhhsg

mrhhsg commented May 27, 2026

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

Summary

No additional blocking issues found in this pass. The current head addresses the already-raised nested strict conversion concern by unwrapping nullable nested serdes for non-NULL literals, and addresses the already-raised nested NULL casing concern with case-insensitive matching and regression coverage.

Critical checkpoint conclusions:

  • Goal and tests: The PR supports literal defaults for ARRAY, MAP, and STRUCT in FE validation and BE default materialization. It includes FE unit coverage and regression coverage for create, insert, nullable defaults, alter-add-column defaults, nested NULL casing, and nested type mismatch materialization failures.
  • Scope and clarity: The change is focused on complex default validation/parsing and the default iterator path.
  • Concurrency and lifecycle: No new shared mutable state, locks, threads, or non-trivial lifecycle management were introduced.
  • Configuration and compatibility: No new configuration items or storage/protocol format changes were introduced. Existing persisted default strings are interpreted through SerDe at materialization time.
  • Parallel paths: The default iterator now receives the tablet column SerDe, covering complex defaults that could not be reconstructed from only scalar type metadata.
  • Error handling: BE parsing returns non-OK Status on malformed complex defaults; the previously silent nested nullable conversion issue is fixed in the current head.
  • Tests and results: New regression output is deterministic with ordered queries. I did not run tests in this review environment.
  • Observability, transactions, persistence, FE-BE variables, performance: No new concerns found for these checkpoints.

User focus: No additional user-provided review focus was specified.

@mrhhsg

mrhhsg commented May 27, 2026

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 83.87% (26/31) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

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

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17636	3981	3958	3958
q2	q3	10834	1458	817	817
q4	4686	480	342	342
q5	7595	2277	2158	2158
q6	246	177	139	139
q7	934	785	645	645
q8	9400	1557	1531	1531
q9	5358	4997	4951	4951
q10	6399	2201	1899	1899
q11	434	270	242	242
q12	627	417	297	297
q13	18118	3359	2712	2712
q14	267	258	240	240
q15	q16	814	775	708	708
q17	1008	984	960	960
q18	7085	5818	5573	5573
q19	1141	1248	1029	1029
q20	536	525	303	303
q21	6044	2854	2784	2784
q22	472	379	307	307
Total cold run time: 99634 ms
Total hot run time: 31595 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	4973	4721	4657	4657
q2	q3	5179	5276	4622	4622
q4	2112	2206	1385	1385
q5	5026	4657	4638	4638
q6	231	178	128	128
q7	1924	1745	1577	1577
q8	2386	2122	2196	2122
q9	7945	7437	7415	7415
q10	4748	4653	4210	4210
q11	530	385	351	351
q12	735	737	522	522
q13	3016	3319	2775	2775
q14	277	270	258	258
q15	q16	681	700	608	608
q17	1284	1255	1250	1250
q18	7387	6892	6968	6892
q19	1109	1083	1086	1083
q20	2207	2237	1931	1931
q21	5305	4570	4368	4368
q22	549	448	428	428
Total cold run time: 57604 ms
Total hot run time: 51220 ms

@hello-stephen

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

query5	4305	660	527	527
query6	328	214	208	208
query7	4237	585	331	331
query8	322	234	215	215
query9	8791	4008	4022	4008
query10	442	337	296	296
query11	5763	2406	2295	2295
query12	181	130	123	123
query13	1266	606	445	445
query14	6092	5457	5114	5114
query14_1	4447	4420	4431	4420
query15	214	201	181	181
query16	993	438	437	437
query17	953	718	591	591
query18	2434	478	351	351
query19	216	206	177	177
query20	131	134	147	134
query21	219	141	118	118
query22	13616	13559	13368	13368
query23	17405	16470	16284	16284
query23_1	16476	16478	16422	16422
query24	7523	1772	1330	1330
query24_1	1338	1300	1330	1300
query25	593	510	452	452
query26	1322	354	178	178
query27	2658	572	349	349
query28	4521	2007	1986	1986
query29	1028	645	522	522
query30	308	243	203	203
query31	1165	1085	973	973
query32	92	76	79	76
query33	543	366	305	305
query34	1216	1186	687	687
query35	777	799	703	703
query36	1429	1433	1206	1206
query37	158	105	89	89
query38	3231	3158	3062	3062
query39	935	918	896	896
query39_1	883	893	883	883
query40	232	153	130	130
query41	74	68	68	68
query42	113	113	107	107
query43	328	331	297	297
query44	
query45	227	211	202	202
query46	1106	1219	735	735
query47	2392	2436	2274	2274
query48	425	436	302	302
query49	649	514	404	404
query50	1029	352	251	251
query51	4297	4341	4228	4228
query52	110	107	99	99
query53	260	290	212	212
query54	325	286	271	271
query55	100	99	92	92
query56	323	323	327	323
query57	1461	1428	1350	1350
query58	318	278	282	278
query59	1594	1676	1532	1532
query60	320	332	298	298
query61	162	152	156	152
query62	698	644	584	584
query63	240	201	201	201
query64	2470	815	658	658
query65	
query66	1704	470	369	369
query67	29816	29796	29587	29587
query68	
query69	454	354	299	299
query70	1052	1024	999	999
query71	315	281	270	270
query72	3131	2783	2494	2494
query73	846	817	432	432
query74	5160	4932	4790	4790
query75	2705	2608	2260	2260
query76	2289	1122	757	757
query77	404	416	331	331
query78	12404	12464	11887	11887
query79	1492	1073	787	787
query80	680	579	473	473
query81	468	282	236	236
query82	1437	155	119	119
query83	360	286	254	254
query84	311	144	114	114
query85	911	547	455	455
query86	403	365	315	315
query87	3421	3372	3202	3202
query88	3614	2752	2752	2752
query89	443	393	340	340
query90	1991	181	191	181
query91	182	166	141	141
query92	85	75	71	71
query93	1487	1537	907	907
query94	528	328	278	278
query95	663	396	440	396
query96	1074	775	317	317
query97	2735	2736	2625	2625
query98	231	226	224	224
query99	1172	1145	1024	1024
Total cold run time: 254726 ms
Total hot run time: 172518 ms

@mrhhsg
mrhhsg force-pushed the fix/reject-complex-type-default branch from c5101b1 to feb5503 Compare May 27, 2026 09:59
@mrhhsg

mrhhsg commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17627	5723	5583	5583
q2	2073	305	279	279
q3	10248	1406	856	856
q4	4681	475	357	357
q5	7434	805	547	547
q6	188	190	162	162
q7	766	814	618	618
q8	9252	1369	1071	1071
q9	5842	4484	4465	4465
q10	6821	1296	1006	1006
q11	438	256	239	239
q12	629	400	296	296
q13	18036	3067	2387	2387
q14	277	275	252	252
q15	q16	733	727	689	689
q17	1185	749	584	584
q18	7133	6212	6181	6181
q19	1103	950	614	614
q20	399	335	230	230
q21	5837	2533	2552	2533
q22	398	370	332	332
Total cold run time: 101100 ms
Total hot run time: 29281 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6570	6466	6503	6466
q2	762	616	568	568
q3	5534	5650	5389	5389
q4	2130	2172	1649	1649
q5	5880	5645	5747	5645
q6	254	201	151	151
q7	2256	2048	1848	1848
q8	3156	2808	2828	2808
q9	8222	8013	7720	7720
q10	3659	3616	3240	3240
q11	617	432	402	402
q12	715	704	502	502
q13	2720	3101	2393	2393
q14	289	289	268	268
q15	q16	666	683	616	616
q17	7861	6993	6889	6889
q18	13191	12265	12987	12265
q19	951	841	838	838
q20	2161	2187	1944	1944
q21	5970	4964	4968	4964
q22	515	471	424	424
Total cold run time: 74079 ms
Total hot run time: 66989 ms

@hello-stephen

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

query5	4319	583	471	471
query6	441	233	206	206
query7	4816	449	225	225
query8	322	190	168	168
query9	8772	4068	4047	4047
query10	497	315	270	270
query11	5950	3432	3187	3187
query12	153	91	86	86
query13	1263	454	321	321
query14	6560	4877	4581	4581
query14_1	4449	4307	4293	4293
query15	207	201	186	186
query16	947	451	433	433
query17	873	675	560	560
query18	2420	431	312	312
query19	197	162	141	141
query20	86	82	78	78
query21	213	140	123	123
query22	13105	13029	12808	12808
query23	13090	12397	12009	12009
query23_1	12135	12218	12183	12183
query24	7113	866	477	477
query24_1	476	487	468	468
query25	534	434	373	373
query26	1265	256	138	138
query27	2755	444	264	264
query28	4595	1932	1932	1932
query29	1543	604	469	469
query30	313	222	181	181
query31	883	783	653	653
query32	149	92	92	92
query33	515	312	258	258
query34	915	854	501	501
query35	753	768	694	694
query36	832	818	742	742
query37	140	111	95	95
query38	1821	1730	1701	1701
query39	725	723	692	692
query39_1	674	669	672	669
query40	228	125	143	125
query41	68	67	62	62
query42	90	85	82	82
query43	365	381	347	347
query44	1357	705	710	705
query45	181	179	166	166
query46	855	955	554	554
query47	2971	3028	2939	2939
query48	288	308	212	212
query49	574	398	294	294
query50	680	265	205	205
query51	10507	10286	10567	10286
query52	78	91	70	70
query53	188	219	151	151
query54	253	187	173	173
query55	80	73	65	65
query56	240	240	204	204
query57	1640	1594	1605	1594
query58	299	255	251	251
query59	2362	2421	2138	2138
query60	284	248	227	227
query61	148	151	147	147
query62	405	341	287	287
query63	206	167	172	167
query64	2616	940	786	786
query65	3461	3378	3374	3374
query66	1754	404	310	310
query67	20183	20136	20141	20136
query68	2975	987	610	610
query69	385	289	257	257
query70	944	846	885	846
query71	321	236	207	207
query72	2735	2574	2304	2304
query73	533	519	295	295
query74	4632	4495	4316	4316
query75	2350	2313	1974	1974
query76	2300	1037	651	651
query77	364	399	305	305
query78	9057	9055	8356	8356
query79	976	895	507	507
query80	732	424	360	360
query81	548	322	279	279
query82	577	145	107	107
query83	282	200	183	183
query84	325	130	104	104
query85	895	431	406	406
query86	336	242	239	239
query87	2005	1980	1864	1864
query88	3651	2693	2675	2675
query89	336	301	258	258
query90	1742	193	186	186
query91	163	146	126	126
query92	99	88	89	88
query93	1011	1000	559	559
query94	470	312	301	301
query95	591	413	320	320
query96	670	533	236	236
query97	2435	2469	2301	2301
query98	159	153	140	140
query99	775	762	642	642
Total cold run time: 234259 ms
Total hot run time: 152532 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.09	0.04	0.05
query3	0.26	0.11	0.12
query4	1.60	0.13	0.13
query5	0.29	0.25	0.23
query6	1.17	0.69	0.68
query7	0.04	0.01	0.00
query8	0.05	0.04	0.03
query9	0.43	0.37	0.38
query10	0.61	0.65	0.62
query11	0.21	0.14	0.14
query12	0.18	0.14	0.14
query13	0.50	0.50	0.51
query14	0.98	0.97	0.97
query15	0.66	0.61	0.61
query16	0.37	0.32	0.32
query17	1.15	1.13	1.16
query18	0.24	0.22	0.22
query19	2.10	1.99	2.02
query20	0.02	0.01	0.02
query21	15.48	0.29	0.14
query22	4.38	0.05	0.06
query23	16.23	0.33	0.13
query24	2.60	0.69	0.76
query25	0.10	0.06	0.04
query26	1.81	0.23	0.14
query27	0.05	0.04	0.04
query28	2.92	0.62	0.40
query29	12.51	4.13	3.32
query30	0.28	0.15	0.14
query31	2.78	0.58	0.34
query32	3.22	0.63	0.54
query33	3.13	3.21	3.15
query34	15.40	4.06	3.36
query35	3.38	3.34	3.35
query36	0.54	0.45	0.42
query37	0.10	0.06	0.06
query38	0.05	0.04	0.03
query39	0.04	0.03	0.03
query40	0.19	0.14	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.31 s
Total hot run time: 24.83 s

@mrhhsg
mrhhsg force-pushed the fix/reject-complex-type-default branch from f0b6e20 to 0196930 Compare October 9, 2026 12:37
@mrhhsg

mrhhsg commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

/review-light

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

Complete static review of PR #63528 at head 01969307d554e8bb57380a5381e7832867245ea9. No new inline findings. Existing P2 threads 4228442634 (signed floating zero changes on DDL replay) and 4228442626 (nested VARCHAR defaults truncate overlength input) still apply and were not reposted. Previously reported P1 issues 4226698280, 4226698283, 4226698290, 4227387142, and 4227894831 are addressed by the current DDL quoting, canonical literal validation, parser, and load mapping paths, so there are no still-applicable existing P0/P1 blockers.

Critical checkpoints:

  • Goal and proof: The change adds typed literal defaults for ARRAY, MAP, and STRUCT. FE canonicalizes nested values; BE parses them in the old-rowset default iterator and direct schema change; omitted-column INSERT and load mappings use the canonical text. Added FE/BE unit tests and a regression suite exercise these paths, including errors and SHOW CREATE/LIKE replay. This invocation inspected the tests and author-provided results; it did not execute them.
  • Scope and clarity: Changes stay within default validation, serialization text, materialization, load mappings, and their tests. The new parser helper is shared by DDL and explicit mapping values.
  • Concurrency and lifecycle: No new threads, locks, cross-TU globals, or asynchronous ownership were introduced. The iterator holds a SerDe only when a non-NULL default needs parsing; its Field is reused during the iterator lifetime.
  • Configuration and compatibility: No configuration or FE/BE protocol field was added. Canonical default text is persisted in existing column metadata and replayed through SHOW CREATE/LIKE. Old BE code cannot read newly permitted nonempty complex defaults, but the repository's documented rolling upgrade order is BE before FE; no separate supported-rollout defect was substantiated.
  • Parallel paths and guards: CREATE/ALTER, light missing-column reads, direct schema change, omitted-column INSERT, and explicit/defaulted load mappings were traced. Top-level nullable SerDes are removed for explicit non-NULL materialization so nested parse failures propagate. Nested NULL is accepted according to actual child nullability. SQL MAP keys are nullable here, resolving the initial null-key suspicion.
  • Tests and result data: Changed FE and BE unit tests cover canonical casts, rejection, nested NULL, three complex SerDes, and iterator behavior. Regression cases cover CREATE, ALTER, direct rewrite, load mapping, and DDL replay; their expected output matches the reviewed queries. No build, test, or result generation was performed in this review runner.
  • Observability and persistence: No new distributed operation, metric, log path, EditLog record, or transaction protocol was added. Defaults use the existing column metadata and schema-change/write machinery; no new crash or atomicity path was identified in the changed code.
  • Writes and performance: Default Fields are parsed at iterator setup or schema change and then inserted through established column paths. The caller skips recursive SerDe construction for NULL or absent defaults. No distinct CPU, memory-tracking, or output correctness defect remained beyond the two existing P2 threads.
  • Special conditions: NULL checks precede quote removal, so quoted "null" remains a value; the non-NULL SerDe branch is selected only when parsing is required. The conditions follow the two materialization paths and have local explanations.
  • FE/BE variables: No new variable is passed between FE and BE, so there is no additional thrift send site to update.
  • Transaction and data-write safety: The PR adds default values to existing column write and schema-change paths; it changes neither commit/publication order nor rowset visibility. The direct rewrite regression triggers the actual rewrite strategy with a delete predicate. No new crash recovery or concurrency boundary was introduced.
  • Remaining issues: The signed-zero and nested VARCHAR P2s are already live inline threads. I found no additional substantiated bug after deduplication and final sweep.

Focus file: -light supplied no additional technical target; all 33 changed files were nevertheless reviewed. The main risk scan, normal BE pass, normal FE/test pass, separate risk-focused pass, and final changed-file sweep converged in one round with no unresolved candidate. Review status: COMPLETE. Static inspection only.

@mrhhsg

mrhhsg commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

run buildall

@mrhhsg

mrhhsg commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

run compile

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17586	5639	5525	5525
q2	2098	333	253	253
q3	10205	1443	835	835
q4	4686	481	355	355
q5	7449	834	550	550
q6	186	185	155	155
q7	761	801	616	616
q8	9241	1332	1065	1065
q9	5794	4476	4461	4461
q10	6816	1307	1030	1030
q11	440	250	240	240
q12	625	405	293	293
q13	18055	3067	2377	2377
q14	283	275	246	246
q15	q16	747	727	657	657
q17	1193	743	598	598
q18	7088	6232	6194	6194
q19	1256	985	618	618
q20	399	333	232	232
q21	5987	2338	2389	2338
q22	367	313	268	268
Total cold run time: 101262 ms
Total hot run time: 28906 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6140	6122	6151	6122
q2	693	579	555	555
q3	5299	5431	4968	4968
q4	2007	2146	1532	1532
q5	5333	5506	5992	5506
q6	272	204	162	162
q7	2378	2145	1877	1877
q8	3292	2995	2817	2817
q9	8303	8316	8199	8199
q10	3756	3691	3313	3313
q11	625	430	402	402
q12	725	724	518	518
q13	2863	3262	2497	2497
q14	303	309	300	300
q15	q16	713	716	691	691
q17	8414	7469	7446	7446
q18	13150	12351	13048	12351
q19	1025	853	835	835
q20	2260	2215	1975	1975
q21	6096	4697	4782	4697
q22	504	440	391	391
Total cold run time: 74151 ms
Total hot run time: 67154 ms

@hello-stephen

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

query5	4323	627	470	470
query6	455	226	206	206
query7	4818	445	224	224
query8	325	181	168	168
query9	8809	4012	3988	3988
query10	457	308	260	260
query11	5952	3407	3187	3187
query12	139	87	83	83
query13	1245	426	311	311
query14	6526	4685	4416	4416
query14_1	4237	4185	4198	4185
query15	201	197	175	175
query16	954	429	413	413
query17	842	660	553	553
query18	2427	438	313	313
query19	201	174	141	141
query20	82	78	78	78
query21	213	136	111	111
query22	12882	12976	12750	12750
query23	13224	12578	12255	12255
query23_1	12417	12193	12175	12175
query24	7120	843	467	467
query24_1	471	480	488	480
query25	506	400	338	338
query26	1293	252	135	135
query27	2768	433	264	264
query28	4664	1897	1926	1897
query29	1575	569	443	443
query30	293	220	180	180
query31	881	763	636	636
query32	143	88	86	86
query33	500	292	236	236
query34	977	856	505	505
query35	758	754	640	640
query36	805	827	744	744
query37	133	108	86	86
query38	1779	1733	1674	1674
query39	719	717	680	680
query39_1	651	659	652	652
query40	211	118	95	95
query41	65	62	62	62
query42	80	79	81	79
query43	359	372	339	339
query44	1299	688	696	688
query45	171	191	162	162
query46	826	956	569	569
query47	2973	2917	2933	2917
query48	290	308	214	214
query49	570	394	291	291
query50	669	269	227	227
query51	10627	10252	10501	10252
query52	72	84	69	69
query53	199	205	149	149
query54	229	196	180	180
query55	73	69	64	64
query56	240	239	211	211
query57	1597	1587	1572	1572
query58	288	278	266	266
query59	2346	2354	2156	2156
query60	289	270	231	231
query61	177	175	172	172
query62	391	343	289	289
query63	192	172	175	172
query64	2905	1046	881	881
query65	3901	3376	3390	3376
query66	1819	432	317	317
query67	19936	20136	19797	19797
query68	2995	993	608	608
query69	397	288	264	264
query70	927	834	850	834
query71	317	245	213	213
query72	2816	2546	2275	2275
query73	515	550	304	304
query74	4555	4447	4262	4262
query75	2345	2269	1949	1949
query76	2295	1014	581	581
query77	367	391	293	293
query78	9088	9040	8477	8477
query79	988	835	507	507
query80	1223	428	334	334
query81	577	316	275	275
query82	582	138	106	106
query83	298	197	177	177
query84	300	124	102	102
query85	840	460	380	380
query86	388	240	232	232
query87	2001	1942	1834	1834
query88	3682	2681	2683	2681
query89	345	294	253	253
query90	1877	183	178	178
query91	156	145	125	125
query92	96	87	85	85
query93	1086	976	558	558
query94	659	305	280	280
query95	584	417	328	328
query96	673	521	231	231
query97	2439	2465	2308	2308
query98	152	153	142	142
query99	788	767	644	644
Total cold run time: 235620 ms
Total hot run time: 151546 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.09	0.04	0.05
query3	0.27	0.11	0.11
query4	1.60	0.12	0.12
query5	0.24	0.25	0.25
query6	1.16	0.68	0.68
query7	0.04	0.01	0.00
query8	0.05	0.04	0.03
query9	0.41	0.34	0.35
query10	0.58	0.57	0.56
query11	0.20	0.14	0.14
query12	0.17	0.14	0.15
query13	0.49	0.48	0.50
query14	0.98	0.98	0.97
query15	0.63	0.61	0.61
query16	0.35	0.34	0.35
query17	1.17	1.11	1.09
query18	0.23	0.21	0.21
query19	2.08	2.03	1.93
query20	0.02	0.01	0.00
query21	15.49	0.29	0.14
query22	4.42	0.06	0.05
query23	16.30	0.32	0.12
query24	2.99	1.11	0.40
query25	0.11	0.06	0.04
query26	1.45	0.26	0.14
query27	0.05	0.05	0.04
query28	1.73	0.59	0.40
query29	12.52	4.15	3.27
query30	0.28	0.15	0.13
query31	2.79	0.58	0.33
query32	3.22	0.61	0.52
query33	3.24	3.12	3.31
query34	15.57	4.00	3.39
query35	3.33	3.32	3.32
query36	0.56	0.47	0.47
query37	0.10	0.06	0.06
query38	0.05	0.03	0.04
query39	0.04	0.04	0.03
query40	0.18	0.15	0.15
query41	0.09	0.03	0.02
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 95.36 s
Total hot run time: 24.27 s

@mrhhsg

mrhhsg commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

run check_coverage

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (23/23) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 65.24% (30950/47439)
Line Coverage 50.04% (325563/650543)
Region Coverage 45.49% (263176/578594)
Branch Coverage 47.16% (123512/261918)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (23/23) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.58% (35166/45918)
Line Coverage 61.78% (397382/643210)
Region Coverage 58.17% (335159/576214)
Branch Coverage 59.06% (154228/261119)

Issue Number: None

Related PR: None

Problem Summary: ARRAY, MAP, and STRUCT columns previously only supported DEFAULT NULL or very limited empty ARRAY defaults. This prevented valid literal defaults such as [], {}, [1, 2], {"a": 1}, or {1, "x"} from being used consistently when creating a table, adding a column, or reading default values from older rowsets. This change validates non-null complex defaults as type-appropriate literals in FE, teaches BE complex SerDes to parse these literal strings into typed Fields, and lets default iterators and direct schema change parse explicit defaults through non-nullable SerDes so type-mismatched nested values fail instead of being silently converted to NULL. Explicit nested NULL values are now accepted only when the nested SerDe is nullable, so non-nullable nested struct fields reject NULL defaults during parsing instead of reaching column insertion as a null Field. The regression coverage avoids asserting local-storage rewrite cancellation in cloud mode, where add-column schema change may finish as a metadata-only operation. INSERT fills unmentioned columns through a FE-side cast of the default literal, which is non-strict by default, so the regression exercises BE default parsing by reading rows written before a column with an invalid default was added through light schema change. JSON and VARIANT columns still only support DEFAULT NULL.

Support literal default values for ARRAY, MAP, and STRUCT columns.

- Test:
    - Unit Test: ./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.info.ColumnDefinitionTest
    - Unit Test: ./run-be-ut.sh --run --filter=DataTypeSerDeFromStringTest.*FromFeString -j 32
    - Unit Test: ./run-be-ut.sh --run --filter=DataTypeSerDeFromStringTest.structFromFeString -j 32
    - Unit Test: ./run-be-ut.sh --run --filter=DataTypeSerDeFromStringTest.*:ColumnReaderTest.DefaultValue* -j 64
    - Unit Test: ./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.info.ColumnDefinitionTest,org.apache.doris.nereids.trees.plans.commands.AlterTableCommandTest
    - Build: ./build.sh --be
    - Build: ninja -C be/ut_build_ASAN src/storage/CMakeFiles/Storage.dir/schema_change/schema_change.cpp.o
    - Build and Regression test: doris-local-regression --network 10.26.20.3/24 all -d datatype_p0/complex_types -s test_complex_default_value
    - Format: build-support/clang-format.sh; build-support/check-format.sh; git diff --check
    - Regression test: doris-local-regression --network 10.26.20.3/24 run -d datatype_p0/complex_types -s test_complex_default_value -forceGenOut
    - Regression test: doris-local-regression --network 10.26.20.3/24 run -d datatype_p0/complex_types -s test_complex_default_value
    - Regression test: doris-local-regression --network 10.26.20.3/24 run -d schema_change_p0 -s test_dup_schema_key_add
    - Regression test: doris-local-regression --network 10.26.20.3/24 run -d schema_change_p0 -s test_unique_schema_key_change_add
    - Regression test: doris-local-regression --network 10.26.20.3/24 run -d schema_change_p0 -s test_modify_struct
    - Regression test (after rebase): doris-local-regression run -d datatype_p0/complex_types -s test_complex_default_value; run -d schema_change_p0 -s test_dup_schema_key_add,test_unique_schema_key_change_add,test_modify_struct
    - Regression test: doris-local-regression --network 10.26.20.3/24 run -d external_table_p0/iceberg -s test_iceberg_schema_change_complex_types
- Behavior changed: Yes. ARRAY, MAP, and STRUCT non-null literal defaults are accepted and materialized; invalid explicit defaults are rejected in both default-iterator and direct schema-change materialization paths. JSON and VARIANT non-null defaults remain rejected.
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: FE validated an ARRAY/MAP/STRUCT default only by its literal shape and stored the raw SQL text, while BE parsed that text with the plain string-to-complex grammar. Values that are valid SQL literals but not plain text therefore read back wrong or failed late: `ARRAY<DATEV2> DEFAULT '[DATEV2 "2024-01-01"]'` materialized `MIN_DATE_V2`, `ARRAY<INT> DEFAULT '[1e3]'` failed when old rows were read, `ARRAY<STRING> DEFAULT '["a""b"]'` stored `a""b`, and `MAP<STRING,INT> DEFAULT '{"a":1,"a":2}'` kept both pairs although the FE literal keeps `{"a":2}`. `SHOW CREATE TABLE` also wrapped the raw default in double quotes without escaping, so `CREATE TABLE LIKE` could not replay a MAP default, and a one-argument `replace_value` load mapping on such a column hit the scalar-only precondition of `ColumnDef.validateDefaultValue`.

FE now parses the default as a SQL literal, casts every nested value to the declared nested type with strict checking, and stores a canonical text (unquoted numbers, bool as 1/0, double quoted strings/dates/ips, nested brackets, NULL). Type mismatches, typed or exponent literals and repeated map keys are therefore resolved at DDL time, and BE only ever parses the canonical form through the default value iterator, schema change, and the INSERT cast. Nested string values that contain quotes or backslashes are rejected, because neither BE parser decodes escapes and the DDL parser keeps the default text verbatim. `Column.toSql()` wraps a default that contains double quotes in single quotes so SHOW CREATE TABLE output replays through CREATE TABLE LIKE, and load `default_value`/`replace_value` mappings validate complex columns through the same canonicalizer.

### Release note

None

### Check List (For Author)

- Test: Regression test / Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.info.ColumnDefinitionTest,org.apache.doris.catalog.ColumnTest
    - ./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.AlterTableCommandTest
    - ./run-be-ut.sh --run --filter='DataTypeSerDeFromStringTest.*'
    - doris-local-regression run -d datatype_p0/complex_types -s test_complex_default_value
    - doris-local-regression run -d schema_change_p0 -s test_dup_schema_key_add,test_unique_schema_key_change_add,test_modify_struct
- Behavior changed: Yes. Complex column defaults are type checked at DDL time and stored in canonical form; nested string defaults may not contain quotes or backslashes.
- Does this need documentation: No
…ng map keys

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

Follow-up fixes for literal defaults of ARRAY/MAP/STRUCT columns.

1. `default_value('[1e3]')` and `replace_value(null, '[1e3]')` mappings on an
   ARRAY<INT> column passed FE validation (which canonicalizes the text to
   `[1000]`) but the original literal was still sent to BE, whose string cast
   writes a NULL element in non-strict load and fails in strict load.
   `NereidsLoadScanProvider.transformHadoopFunctionExpr` now replaces the
   validated argument with the canonical text.
2. `MAP<INT, INT> DEFAULT '{"01": 1, "1": 2}'` rendered two entries with key
   `1`; BE materialized both for old rows while replaying the text as a SQL
   literal kept only the last one. Keys that collide after the cast to the key
   type are rejected at DDL time.
3. `Segment::new_default_iterator` built a (recursive) SerDe for every missing
   column of every old segment even when the column has no default or
   `DEFAULT NULL`, which never parses anything. The SerDe is now built only for
   a non-NULL default text and `DefaultValueColumnIterator` asserts it exists
   when it must parse; the iterator's unused FieldType/precision/scale/len
   fields are removed.
4. The regression case labelled as direct schema change actually took BE's
   linked strategy; a delete predicate is now added before the ALTER so BE
   selects `sc_directly` and materializes the complex defaults.

None

- Test: Unit Test / Regression test
    - FE UT: ColumnDefinitionTest, NereidsLoadScanProviderTest
    - BE UT: ColumnReaderTest, DataTypeSerDeFromStringTest
    - Regression: datatype_p0/complex_types/test_complex_default_value,
      schema_change_p0 (test_dup_schema_key_add, test_modify_struct,
      test_unique_schema_key_change_add)
- Behavior changed: Yes (map defaults whose keys collide after the cast are rejected; explicit complex load mapping values are canonicalized)
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

Follow-up fix for literal defaults of ARRAY/MAP/STRUCT columns.

`ComplexTypeDefaultValue.canonicalize` parsed the default text with the
regular SQL literal builder, which first casts the items of an array literal
and the keys and values of a map literal to a common type inferred from the
items. With the default `enable_strict_cast=false` an item that cannot be
cast is silently replaced by NULL, so `ARRAY<DATEV2> DEFAULT
'[DATEV2 "2024-01-01", 1]'` was accepted and stored as `["2024-01-01", NULL]`:
old rows and INSERTs that omit the column got a NULL the user never wrote.

The default text is now parsed with a builder that keeps every array item and
map key/value exactly as written; `render` casts each of them to the declared
nested type with `checkedCastTo`, which rejects the invalid item at DDL time
regardless of the session setting. `NereidsParser.parseExpression` gains an
overload taking the builder, mirroring `parseSingle`.

### Release note

None

### Check List (For Author)

- Test: Unit Test / Regression test
    - FE UT: ColumnDefinitionTest, NereidsLoadScanProviderTest, AlterTableCommandTest
    - Regression: datatype_p0/complex_types/test_complex_default_value
- Behavior changed: Yes (a nested default value that the SQL parser would have coerced to NULL is now rejected)
- Does this need documentation: No
…n test

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: Rebasing onto master picked up apache#65684, which deleted
`DefaultValueColumnIterator` and moved default-value parsing into
`Segment::get_default_value_field`, together with a new
`constant_column_iterator_test.cpp`. That file's
`NonEmptyComplexTextDefaultsAreRejected` asserted the pre-PR restriction that
a non-empty ARRAY/MAP/STRUCT literal default is rejected
(`NOT_IMPLEMENTED_ERROR`), which no longer holds once this branch's earlier
commits teach the complex SerDes to parse such literals. Replace it with
`NonEmptyComplexTextDefaultsAreParsed`, asserting that `[1]`, `{"k":7}`, and a
positional struct literal are parsed into the matching typed `Field` through
`Segment::get_default_value_field`.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - `./run-be-ut.sh --run --filter="ConstantColumnIteratorTest.*" -j 32` (34 tests, all pass)
- Behavior changed: No (test-only; the behavior change itself is in the earlier commits)
- Does this need documentation: No
@mrhhsg
mrhhsg force-pushed the fix/reject-complex-type-default branch from 0196930 to 5629205 Compare October 13, 2026 10:15
@mrhhsg

mrhhsg commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

/review-light

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

Reviewed pinned head 56292050219133897db92ad055709554f3e16736 against the supplied authoritative diff, covering all 30 changed files and their relevant callers/consumers. Request changes for two new P1 inline findings; the new P2 inline finding should also be addressed.

One P1 affects light schema change: selecting one field of a nonempty defaulted STRUCT on an old rowset inserts the full default into a pruned destination and fails. The other P1 affects MoW partial-update new keys: a nested STRING NULL default is persisted as the string "NULL". The P2 concerns recursive default buffers and retained iterator copies that bypass memory accounting. For the supplied -light focus, light-schema old-row reads, post-ALTER INSERT and the direct-rewrite counterpart were specifically traced.

Existing P1 concerns about permissive nested-item coercion, load mapping canonicalization, typed literals, quoted strings and DDL replay are fixed at this head. No previously reported P0/P1 was independently confirmed to remain, so existing_blocking_comment_ids is empty. Existing P2 nested VARCHAR truncation and negative-zero replay still apply and were not reposted.

Critical checkpoint conclusions:

  1. Goal and proof: Typed ARRAY/MAP/STRUCT literal defaults are implemented, but pruned old-row queries fail and partial updates can persist an incorrect nested value. FE/BE unit and regression coverage exists; no tests were executed in this static review.
  2. Scope/reuse: Changes are focused on defaults and reuse the parser, scalar SerDes and schema-change mapping.
  3. Concurrency/locking: No new threads, shared mutable state or locks; existing storage/schema-change lock and visibility ordering is unchanged.
  4. Lifecycle: Fields own their nested data and request/job lifetimes are coherent; no new static-initialization or COW issue. Their owned-memory accounting is the P2 finding.
  5. Configuration: No new configuration; nested pruning is already enabled by default.
  6. Compatibility: No wire/block layout change; existing NULL/empty ARRAY handling remains. The new feature requires upgraded BEs; no supported rolling-upgrade regression was substantiated.
  7. Thrift/protobuf: No definitions changed; field-ID checks are inapplicable.
  8. Parallel paths: CREATE/ADD/MODIFY, INSERT/DEFAULT, load replacements/default mappings, old-row readers, schema-change strategies, cloud reuse, partial updates, the row-store JSON fallback and connector restrictions were traced. Pruned scan defaults and partial-update JSON default parsing are the missed parallel consumers.
  9. Conditions/errors: Invalid shapes, casts, key collisions and nonnullable nested NULL are rejected; Status/analysis errors propagate. Empty STRUCT child defaults agree across ordinary consumers.
  10. Coverage: Tests cover normal literals, invalid input, replay, light old-row reads, a delete-predicate-triggered direct rewrite and load mappings. They miss nonempty pruned STRUCT/container reads, partial-update new keys with nested STRING NULL defaults, and large-default accounting; empty STRUCT ADD/rewrite, sorted and cloud/MoW execution are not demonstrated.
  11. Result files: New goldens and load CSVs were inspected and are consistent with the covered examples; generation provenance and runtime success were not verified.
  12. Observability: Existing parse/type diagnostics and schema-change logs suffice for these paths; the P2 buffers are absent from tracker observability.
  13. Persistence/failover: Canonical text uses existing Column/TabletColumn metadata. No EditLog or failover protocol change was found.
  14. Writes/atomicity: Existing rewrite/publication/version and MoW sequencing remains; parsing errors propagate before rewrite publication. No new crash-lifetime issue was identified, but partial-update default fills can persist wrong values as reported inline.
  15. FE-BE variables: Existing default text and nested type transport is reused; no new variable was added.
  16. Performance: Parsing is linear, and NULL/absent defaults avoid SerDe construction. Expanded Field storage/copies require the memory-accounting fix.
  17. Other concerns: No further distinct verified issue beyond the three new inline findings and the two existing P2 threads.

Review limits: static inspection only; no builds, product tests, source edits or executed reproductions. Author-reported validation and supplied goldens are not test execution by this review. Convergence is recorded separately by the submission helper.

Converged after 3 review rounds: all three latest-round reviewers returned NO_NEW_VALUABLE_FINDINGS, required static coverage is complete, and every candidate/risk is accepted, deduplicated or dismissed with evidence. No unresolved review candidate remains.

Review converged after 3 round(s).

Comment thread be/src/storage/segment/segment.cpp
Comment thread be/src/core/data_type_serde/data_type_array_serde.cpp
…mplex defaults

### What problem does this PR solve?

Issue Number: None

Related PR: apache#63528

Problem Summary:

Follow-up fixes for literal defaults of ARRAY/MAP/STRUCT columns.

1. Nested column pruning reads only the accessed STRUCT fields, so after
   `ADD COLUMN s STRUCT<a:INT,b:STRING> DEFAULT '{7, "x"}'` on a table with
   old rows, `SELECT struct_element(s, 'b')` created a STRUCT<b:STRING>
   destination while the old segment's constant default was parsed with the
   declared type and failed with `Cannot insert value of different size into
   struct`; STRUCT items of an ARRAY and STRUCT values of a MAP failed the
   same way. `Segment::new_column_iterator` now receives the materialized
   read type (`ReadSchema::data_type`) and, when access paths exist for the
   column, projects a `ConstantColumnIterator`'s Field onto it
   (`ConstantColumnIterator::project_to_read_type`): STRUCT fields are
   selected by the read type's names, ARRAY items and MAP values recurse,
   keys, scalars, NULL and the `{}` default are kept.

2. A partial update of a new key fills omitted columns through the BE JSON
   parser (`default_from_string`), whose nullable SerDe only recognizes a
   lower case `null`, so the canonical `[NULL]` text was stored as `["NULL"]`.
   The canonical text now renders a nested NULL as `null`, which every BE
   consumer of the text accepts.

3. A nested VARCHAR(n)/CHAR(n) value longer than n was truncated by the cast
   to VARCHAR(n); it is now rejected with `Default value is too long`, like
   the scalar default check.

4. `ARRAY<DOUBLE> DEFAULT '["-0"]'` stored `[-0.0]`, but replaying the SHOW
   CREATE TABLE text parsed `-0.0` through BigDecimal and stored `[0.0]`. A
   SQL numeric literal has no negative zero, so a float zero is always
   rendered as `0.0`.

### Release note

None

### Check List (For Author)

- Test: Unit Test / Regression test
    - BE UT: `ConstantColumnIteratorTest.*` (new `ComplexDefaultIsProjectedToPrunedReadType`), `*SegmentIteratorExprZonemap*`, `*IndexBuilder*`
    - FE UT: `ColumnDefinitionTest`
    - Regression: `datatype_p0/complex_types/test_complex_default_value` (pruned reads of light-added defaults, MoW partial update of a new key, VARCHAR/CHAR length rejects, negative zero replay)
- Behavior changed: Yes (nested NULL in a complex default is stored as `null`; overlength nested VARCHAR/CHAR defaults are rejected; a float `-0.0` default is stored as `0.0`)
- Does this need documentation: No
@mrhhsg

mrhhsg commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

/review-light

1 similar comment
@mrhhsg

mrhhsg commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

/review-light

@mrhhsg

mrhhsg commented Oct 13, 2026

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

Reviewed all 38 changed files at 0d9f0c992a87a85708cbfd7a25baf8de6aba0b8c by static inspection. One new P2 finding remains: generated light ALTER SQL escapes canonical nested quotes, so CCR replay rejects otherwise valid complex defaults. The inline comment identifies the separate ALTER serializer/consumer; the prior SHOW CREATE/LIKE issue is fixed.

The existing P1 reports for SQL-literal coercion, nested escapes/types/NULLs, explicit load mappings, DDL quoting and pruned old-row defaults are addressed at this head; no existing P0/P1 blocking IDs remain. Existing P2 thread 4257770328 about untracked recursive Field buffers still applies and was deduplicated.

Critical checkpoint conclusions:

  1. Goal and proof: ARRAY/MAP/STRUCT literal defaults now have target-aware canonicalization and BE parsing; test sources cover the principal materializers. ALTER binlog replay remains incomplete as described inline.
  2. Scope and clarity: changes are focused on defaults, required iterator type propagation, and related tests.
  3. Concurrency and locks: parsing is local and projection changes scan-owned values; existing cache initialization, schema-change locking and lock order are preserved.
  4. Lifecycle and static initialization: recursive type owners outlive projection; no new ownership cycle, worker lifetime or cross-TU initializer dependency found.
  5. Configuration: no configuration added.
  6. Compatibility: existing NULL/empty/scalar defaults and metadata layouts are preserved; new literals require feature-capable BE. The generated ALTER replay failure is the verified compatibility issue.
  7. Thrift/protobuf: no definition or field-ID changes.
  8. Parallel paths: ordinary INSERT, explicit/fallback load mappings, missing-segment scans, direct schema change, partial updates, pruned reads and rowid fetch were traced; the distinct ALTER text path has the inline finding.
  9. Conditions: shape/type/nullability, ambiguous quoting, key collisions, length and finite-number checks address concrete inputs; recursive projection assumptions were checked against FE pruning.
  10. Test coverage: FE/BE unit sources and CREATE/LIKE, light/direct ALTER, load mapping and partial-update regressions were reviewed. Serialized ALTER replay coverage is missing.
  11. Test results: new expected values were reconciled with SQL/canonical forms; execution and golden-generation provenance were not independently verified.
  12. Observability: parse failures reach existing Status/AnalysisException boundaries; no new distributed lifecycle needs additional metrics or INFO logging.
  13. Persistence and failover: existing catalog/tablet default fields and metadata replay are reused; exported light-schema rawSql has the reported replay defect.
  14. Writes and atomicity: default materialization remains before existing commit boundaries; visible-version, rowset publication and MoW delete-bitmap phases are unchanged.
  15. FE-BE values: canonical text travels through existing default/type metadata; no new wire variable introduced.
  16. Performance and memory: recursive work follows literal size; the previously reported untracked Field allocations remain applicable, without a duplicate inline comment.
  17. Other issues: all initial risks and candidate dispositions have source evidence; no additional verified issue remains outside the inline set and known thread.

For the supplied -light focus, old-row default materialization and nested projection were inspected, and the new finding specifically covers light ADD COLUMN binlog replay. Validation was static only: no builds, tests, running cluster, mixed-version deployment or source edits were performed.

Review converged after 2 rounds: all three latest reviewers returned NO_NEW_VALUABLE_FINDINGS, all changed-file coverage is complete, and all candidate/risk dispositions are recorded. No unresolved static coverage question remains.

Review converged after 2 round(s).

throw new AnalysisException("Struct type column default value just support null");
if (type.isArrayType() || type.isMapType() || type.isStructType()) {
defaultValue = Optional.of(new DefaultValue(
ComplexTypeDefaultValue.canonicalize(type, defaultValue.get().getValue())));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Preserve complex defaults in ALTER binlog SQL

With light schema change, ALTER TABLE t ADD COLUMN m MAP<STRING,INT> DEFAULT '{"a":1}' now succeeds, but ColumnDefinition.toSql() still applies SqlUtils.escapeQuota and emits DEFAULT "{\"a\":1}". Alter.processAlterOlapTable passes this text into TableAddOrDropColumnsInfo.rawSql, and CCR re-executes it. The DDL parser only removes the outer quotes, so replay passes {\"a\":1} to the canonicalizer and rejects it, stopping synchronization. Apply the replay-safe quoting used by Column.toSql() to this ALTER serializer and cover a serialized ADD COLUMN round trip. This is a separate consumer from the already-fixed SHOW CREATE/LIKE thread.

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

Reviewed pinned head 0d9f0c992a87a85708cbfd7a25baf8de6aba0b8c. No new verified findings. All 38 changed files and related consumers were reviewed; the FE, BE and independent risk reviewers each returned NO_NEW_VALUABLE_FINDINGS. Review converged in 1 round, with no unresolved candidate or uncovered changed-file scope.

Two previously reported P2 concerns still apply and are not reposted: ALTER binlog SQL quoting still uses the escaping serializer in ColumnDefinition.toSql, and parsed complex-default memory accounting still retains untracked recursive Field buffers/copies. The seven existing explicitly P1 reports were independently rechecked: SHOW CREATE/LIKE quoting and unsafe strings, typed literal conversion, explicit load mappings, inferred-item coercion, pruned default projection and partial-update nested NULL now have their concrete fixes. No existing P0/P1 finding was confirmed to remain applicable, so the blocking-ID set is empty.

The reviewed value flow is:

flowchart LR
    D[CREATE / ALTER / mapping validation] --> C[Strict FE canonical literal]
    C --> M[Existing Column default metadata]
    M --> I[INSERT / load / MERGE string cast]
    M --> F[Old-segment Field parsing and projection]
    M --> S[Direct schema-change Field backfill]
    M --> J[Partial-update and row-store JSON defaults]
Loading

Critical checkpoint conclusions:

Checkpoint Conclusion
Goal and proof Literal ARRAY/MAP/STRUCT defaults are canonicalized and recursively materialized across the inspected flows. FE/BE unit and regression source covers the principal positive/negative cases; execution was not performed.
Scope and reuse Changes are focused on defaults, reused canonicalization/strict-conversion helpers, necessary iterator type propagation and related tests.
Concurrency and locks Parsing state is local; cached default Fields are copied before iterator-local projection. No new thread, shared mutation, lock acquisition or lock-order change found.
Lifecycle and initialization Fields/iterators have owning lifetimes; no new ownership cycle or cross-TU static initialization dependency. Memory accounting remains the known P2 above.
Configuration No configuration added or dynamic-setting propagation introduced.
Compatibility Existing default metadata and block/wire/storage layouts are reused. Newly enabled nonempty defaults need supporting BE binaries; mixed-version behavior was not executed.
Thrift/protobuf IDs No files under gensrc/thrift or gensrc/proto changed.
Parallel paths CREATE/ALTER, SHOW CREATE/LIKE, mappings, INSERT/DEFAULT, MERGE, old-segment scans, direct backfill, partial updates and row-store fallback traced. Statistics, row-id and normal iterator callers pass materialized read types. The existing ALTER SQL concern remains.
Conditions and errors Shape/type/arity/key-collision/length/finite-value checks have concrete failure scenarios. Non-NULL nested defaults use strict parsing; explicit NULL requires a nullable child. New Status results propagate; declared/read projection assertions match upstream shapes.
Test coverage Source covers nullable/non-nullable cases, nesting, empty containers, malformed values, casts, old/new rows, field pruning, loads, LIKE and partial updates. The delete predicate forces direct rewrite in the local-mode test; cloud execution was not verified.
Expected results All 11 ordered query labels match the new golden sections, row widths and inspected literal/mapping values agree, and changed error expectations match FE validation. Golden generation provenance was not independently established.
Observability Existing parse/validation errors and schema-change diagnostics cover these paths; no new background workflow needing additional metrics identified.
Persistence and failover Canonical text uses existing Column/TabletColumn fields and schema maps. Light-schema-change EditLog writes and catalog replay preserve those maps; no new journal object or transaction transition.
Writes, atomicity and crash behavior Existing rowset/schema-change commit and publish boundaries remain. No new transaction, visible-version or delete-bitmap ordering change; no distinct new crash/leak/deadlock mechanism substantiated.
FE-BE variables No new transmitted variable. Existing default text is reused, and the new read_type argument is internal BE API plumbing at every inventoried caller.
Performance and memory Parsing/projection scales with literal size during planning/iterator setup, with no new per-row parser. Recursive allocation/copy tracking remains the previously reported P2; no distinct additional performance finding.
Other risks Root STRUCT reorder/drop, stale positional defaults after field append and isolated size/cardinality/IS NULL pruning were rechecked: existing name/order/arity/default-equality and type-preservation guards block the suspected failures.

For the supplied -light focus, light schema change and old-row default materialization received particular attention, including recursive pruned STRUCTs, metadata-only access paths and retained defaults during later schema changes. No additional issue was found beyond the two known P2 reports.

This was a static-only review: no builds, test runs or source edits. Inspected unit tests, regression goldens and author-reported validation are not executed evidence from this invocation. Runtime, external-service/cloud and rolling-upgrade behavior remain validation limits; no concrete unresolved review question remains.

Review converged after 1 round(s).

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