Skip to content

[fix](iceberg) Honor partial name mappings for legacy files#65784

Open
Gabriel39 wants to merge 3 commits into
apache:masterfrom
Gabriel39:ai/doris-27020-strict-partial-name-mapping
Open

[fix](iceberg) Honor partial name mappings for legacy files#65784
Gabriel39 wants to merge 3 commits into
apache:masterfrom
Gabriel39:ai/doris-27020-strict-partial-name-mapping

Conversation

@Gabriel39

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: Legacy Iceberg Parquet and ORC files without field IDs are resolved through name mapping. With a partial mapping, FE omitted the optional mapping metadata for unmapped fields, so both V1 and V2 BE readers fell back to the current physical name and read unrelated data.

This change preserves table-level mapping presence by transporting explicit empty per-field lists and makes those lists authoritative in both readers. Unmapped fields now materialize their default or NULL, while mapped aliases and scans without name mapping retain their existing behavior.

Release note

Fix reads of migrated Iceberg files with partial name mapping so fields omitted from the mapping materialize their default value or NULL instead of matching a physical column by the current name.

Check List (For Author)

  • Test: Unit Test
    • ExternalUtilTest (6 tests)
    • Focused V1 and V2 name-mapping BE unit tests under ASAN (10 tests)
  • Behavior changed: Yes. Partial Iceberg name mappings are now authoritative for legacy files without field IDs.
  • Does this need documentation: No

### What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: Legacy Iceberg Parquet and ORC files without field IDs are resolved through name mapping. With a partial mapping, FE omitted the optional mapping metadata for unmapped fields, so both V1 and V2 BE readers fell back to the current physical name and read unrelated data. Preserve table-level mapping presence by transporting explicit empty per-field lists and make those lists authoritative in both readers, so unmapped fields materialize their default or NULL while mapped aliases and scans without name mapping retain their existing behavior.

### Release note

Fix reads of migrated Iceberg files with partial name mapping so fields omitted from the mapping materialize their default value or NULL instead of matching a physical column by the current name.

### Check List (For Author)

- Test: Unit Test
    - ExternalUtilTest
    - Focused V1 and V2 name-mapping BE unit tests under ASAN
- Behavior changed: Yes. Partial Iceberg name mappings are now authoritative for legacy files without field IDs.
- Does this need documentation: No
@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?

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@Gabriel39
Gabriel39 marked this pull request as ready for review July 18, 2026 13:03
@Gabriel39
Gabriel39 requested a review from yiguolei as a code owner July 18, 2026 13:03
@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17697	4015	4010	4010
q2	2031	317	197	197
q3	10257	1418	797	797
q4	4686	465	340	340
q5	7491	831	558	558
q6	176	170	136	136
q7	726	824	616	616
q8	9939	1499	1520	1499
q9	6239	4341	4346	4341
q10	6825	1734	1456	1456
q11	515	361	317	317
q12	732	570	452	452
q13	18119	3314	2726	2726
q14	267	258	248	248
q15	q16	782	777	701	701
q17	1037	1030	1094	1030
q18	6712	5729	5420	5420
q19	1516	1358	1047	1047
q20	784	668	541	541
q21	5957	2627	2274	2274
q22	424	352	292	292
Total cold run time: 102912 ms
Total hot run time: 28998 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4412	4373	4386	4373
q2	309	319	213	213
q3	4679	5034	4400	4400
q4	2049	2180	1366	1366
q5	4368	4246	4242	4242
q6	236	181	122	122
q7	1715	2077	1730	1730
q8	2454	2176	2134	2134
q9	7792	7679	7691	7679
q10	4680	4654	4214	4214
q11	629	407	369	369
q12	868	834	576	576
q13	3303	3572	2945	2945
q14	316	307	259	259
q15	q16	721	722	648	648
q17	1333	1334	1295	1295
q18	7983	7339	6860	6860
q19	1128	1080	1061	1061
q20	2229	2190	1944	1944
q21	5213	4492	4420	4420
q22	523	455	402	402
Total cold run time: 56940 ms
Total hot run time: 51252 ms

@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 completed against the current head after three convergence rounds. Two blocking correctness issues remain.

Checkpoint conclusions:

  • Goal/minimality: the patch fixes top-level non-empty partial mappings, but the same authoritative-presence contract is incomplete for nested V2 projections and for a present globally empty mapping. The changes are otherwise focused.
  • Flow/parallel paths: traced FE extraction and Thrift transport through V1 Parquet/ORC, V2 field-id/BY_NAME mapping, nested projection, equality-delete hidden dependencies, and position-delete/deletion-vector paths. No additional reachable defect survived convergence.
  • Compatibility/state: no wire type, persisted format, configuration, concurrency, locking, lifecycle, or ownership contract changes were introduced. Mixed-version descriptors retain the pre-existing behavior rather than creating a new serialization incompatibility.
  • Tests: the added tests cover scalar top-level non-empty partial mappings, but not whole/pruned nested materialization or absent-versus-present-[] transport/results.
  • Performance/observability: the added metadata bit has negligible cost and is included in debug output; no per-row hot-path regression was found.
  • Validation: static review only, as required by the review task. Remote compile, CheckStyle, clang-format, license, secret, and dependency checks pass. The macOS BE UT workflow failed because it selected JDK 25 while the build requires JDK 17; Doris BE/FE and regression jobs were still running at submission.

Requesting changes because both findings can silently bind unrelated legacy values instead of producing the Iceberg initial default/NULL.

context->schema_column = build_schema_column_from_external_field(*schema_field, column->type);
column->identifier = context->schema_column->identifier;
column->name_mapping = context->schema_column->name_mapping;
column->has_name_mapping = context->schema_column->has_name_mapping;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Preserve authoritative mapping state on nested projections

This preserves has_name_mapping only on the projected root. build_schema_column_from_external_field() does populate it recursively, but FileScannerV2 then rebuilds complex children through AccessPathParser::build_nested_children(), whose inherit_schema_metadata() copies only name_mapping. For s STRUCT<b INT> where nested field b has an explicit empty names list, both SELECT s and SELECT s.b therefore recreate b with has_name_mapping == false; an ID-less legacy file containing an unrelated physical s.b is then accepted by the current-name fallback instead of materializing the table child as NULL/default. Please copy the presence bit with the aliases in AccessPathParser and add whole/pruned nested coverage (including struct descendants under array/map).

if (nameMapping != null && nameMapping.containsKey(dorisColumn.getUniqueId())) {
// for iceberg set name mapping.
root.setNameMapping(new ArrayList<>(nameMapping.get(dorisColumn.getUniqueId())));
if (nameMapping != null && !nameMapping.isEmpty()) {

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] Keep table mapping presence when the parsed mapping is empty

!nameMapping.isEmpty() still conflates “no Iceberg name-mapping property” with a present valid mapping whose JSON list is empty ([]). IcebergScanNode.extractNameMapping() returns an empty HashMap in both cases (and the bundled Iceberg 1.10.1 parser accepts an empty array), so this branch leaves TField.name_mapping unset for every current field. For an ID-less imported file, V1 and V2 then fall back to a same-current-name physical column instead of treating the field as missing and producing its initial default/NULL. Please preserve property presence separately from map contents (while retaining fallback when the property is absent) and add an absent-vs-[] transport/result test.

@hello-stephen

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

query5	4312	618	476	476
query6	475	230	215	215
query7	4858	566	333	333
query8	338	189	177	177
query9	8809	4046	4061	4046
query10	474	373	305	305
query11	6117	2354	2126	2126
query12	162	103	104	103
query13	1323	606	436	436
query14	6576	5221	4932	4932
query14_1	4285	4259	4251	4251
query15	219	212	185	185
query16	1016	467	405	405
query17	1128	715	584	584
query18	2717	482	347	347
query19	217	189	149	149
query20	114	109	108	108
query21	240	156	134	134
query22	13552	13613	13430	13430
query23	17286	16503	16084	16084
query23_1	16233	16262	16202	16202
query24	7454	1730	1292	1292
query24_1	1317	1248	1247	1247
query25	586	479	396	396
query26	1330	354	223	223
query27	2615	602	381	381
query28	4458	1983	2009	1983
query29	1157	630	508	508
query30	344	273	233	233
query31	1117	1090	975	975
query32	114	63	63	63
query33	536	313	256	256
query34	1149	1127	665	665
query35	761	797	663	663
query36	1193	1190	1094	1094
query37	152	104	87	87
query38	1892	1699	1666	1666
query39	887	871	831	831
query39_1	825	840	882	840
query40	247	163	142	142
query41	64	61	62	61
query42	96	93	90	90
query43	326	323	285	285
query44	1409	781	756	756
query45	195	187	172	172
query46	1047	1216	714	714
query47	2126	2099	2042	2042
query48	411	387	302	302
query49	583	414	307	307
query50	1040	440	339	339
query51	10528	10835	10537	10537
query52	85	87	79	79
query53	261	279	201	201
query54	281	234	219	219
query55	78	70	65	65
query56	310	287	289	287
query57	1326	1300	1257	1257
query58	287	269	236	236
query59	1558	1640	1424	1424
query60	338	272	276	272
query61	146	148	146	146
query62	535	496	428	428
query63	240	204	204	204
query64	2759	1026	888	888
query65	4715	4682	4645	4645
query66	1780	526	381	381
query67	29408	29263	29087	29087
query68	3043	1475	966	966
query69	400	287	300	287
query70	1054	974	942	942
query71	382	343	316	316
query72	3003	2667	2319	2319
query73	850	737	440	440
query74	5068	4911	4687	4687
query75	2521	2503	2136	2136
query76	2368	1176	765	765
query77	349	375	284	284
query78	11927	11937	11290	11290
query79	1478	1156	730	730
query80	1296	550	461	461
query81	576	332	283	283
query82	606	159	126	126
query83	373	331	298	298
query84	281	168	131	131
query85	987	625	519	519
query86	436	287	274	274
query87	1824	1825	1756	1756
query88	3733	2805	2778	2778
query89	434	374	318	318
query90	1910	193	198	193
query91	197	187	161	161
query92	59	61	52	52
query93	1663	1463	999	999
query94	740	352	308	308
query95	771	515	477	477
query96	1061	852	351	351
query97	2625	2630	2474	2474
query98	217	212	202	202
query99	1105	1114	976	976
Total cold run time: 264178 ms
Total hot run time: 177114 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.09	0.04	0.05
query3	0.26	0.14	0.13
query4	1.62	0.13	0.14
query5	0.24	0.22	0.22
query6	1.29	1.05	1.05
query7	0.04	0.00	0.00
query8	0.06	0.04	0.03
query9	0.37	0.33	0.33
query10	0.57	0.55	0.54
query11	0.19	0.14	0.15
query12	0.19	0.14	0.14
query13	0.45	0.47	0.47
query14	1.02	1.01	1.02
query15	0.61	0.59	0.60
query16	0.31	0.33	0.32
query17	1.11	1.07	1.09
query18	0.22	0.20	0.20
query19	2.03	1.92	1.98
query20	0.02	0.02	0.01
query21	15.41	0.19	0.13
query22	4.93	0.06	0.05
query23	16.16	0.31	0.11
query24	3.08	0.42	0.31
query25	0.12	0.05	0.03
query26	0.74	0.21	0.14
query27	0.05	0.03	0.03
query28	3.51	0.97	0.54
query29	12.49	4.12	3.29
query30	0.28	0.15	0.15
query31	2.77	0.60	0.30
query32	3.23	0.59	0.49
query33	3.18	3.22	3.18
query34	15.65	4.20	3.52
query35	3.51	3.50	3.53
query36	0.56	0.43	0.43
query37	0.10	0.06	0.07
query38	0.04	0.04	0.03
query39	0.04	0.03	0.03
query40	0.19	0.16	0.16
query41	0.08	0.03	0.02
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.9 s
Total hot run time: 24.91 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (13/13) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 57.44% (23945/41688)
Line Coverage 41.17% (235514/572026)
Region Coverage 37.00% (186115/502950)
Branch Coverage 38.20% (83567/218786)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 100.00% (3/3) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (13/13) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.41% (29854/40666)
Line Coverage 57.24% (325419/568507)
Region Coverage 53.73% (271233/504842)
Branch Coverage 54.66% (119658/218928)

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17630	4132	4081	4081
q2	1992	334	211	211
q3	10262	1418	841	841
q4	4677	467	336	336
q5	7477	835	553	553
q6	189	183	141	141
q7	754	818	615	615
q8	9786	1670	1537	1537
q9	6167	4312	4313	4312
q10	6808	1743	1477	1477
q11	513	347	319	319
q12	745	578	459	459
q13	18120	3346	2728	2728
q14	266	279	247	247
q15	q16	787	778	711	711
q17	1036	935	908	908
q18	6925	5781	5479	5479
q19	1364	1284	1057	1057
q20	789	725	643	643
q21	5862	2602	2454	2454
q22	440	368	294	294
Total cold run time: 102589 ms
Total hot run time: 29403 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4466	4381	4321	4321
q2	281	313	208	208
q3	4584	4938	4413	4413
q4	2074	2148	1371	1371
q5	4429	4339	4331	4331
q6	233	177	134	134
q7	1725	2177	1736	1736
q8	2504	2206	2228	2206
q9	7888	7689	7721	7689
q10	4680	4666	4188	4188
q11	566	425	385	385
q12	860	816	530	530
q13	3346	3534	3072	3072
q14	304	316	278	278
q15	q16	690	722	665	665
q17	1363	1332	1341	1332
q18	7947	7330	6873	6873
q19	1137	1074	1073	1073
q20	2214	2211	1935	1935
q21	5231	4563	4509	4509
q22	505	455	424	424
Total cold run time: 57027 ms
Total hot run time: 51673 ms

@hello-stephen

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

query5	4321	615	495	495
query6	469	225	213	213
query7	4844	608	350	350
query8	332	184	167	167
query9	8808	4035	4056	4035
query10	479	381	312	312
query11	5926	2342	2139	2139
query12	154	104	103	103
query13	1281	601	425	425
query14	6256	5178	4876	4876
query14_1	4203	4219	4256	4219
query15	210	201	181	181
query16	1008	477	456	456
query17	1135	746	566	566
query18	2486	495	348	348
query19	207	227	148	148
query20	115	113	113	113
query21	229	159	138	138
query22	13443	13647	13234	13234
query23	17280	16482	16113	16113
query23_1	16200	16280	16195	16195
query24	7680	1753	1276	1276
query24_1	1331	1280	1282	1280
query25	578	458	385	385
query26	1350	363	216	216
query27	2676	576	385	385
query28	4431	2015	1986	1986
query29	1100	615	500	500
query30	339	264	233	233
query31	1114	1094	1001	1001
query32	109	59	58	58
query33	506	313	237	237
query34	1152	1153	639	639
query35	753	802	661	661
query36	1169	1163	1054	1054
query37	154	104	94	94
query38	1873	1700	1672	1672
query39	870	866	848	848
query39_1	842	836	835	835
query40	245	168	139	139
query41	69	64	62	62
query42	92	94	96	94
query43	327	321	275	275
query44	1418	753	737	737
query45	188	184	175	175
query46	1060	1162	726	726
query47	2132	2173	1997	1997
query48	384	391	295	295
query49	574	420	301	301
query50	1065	441	335	335
query51	10449	10571	10577	10571
query52	86	87	75	75
query53	258	279	199	199
query54	275	223	218	218
query55	76	71	68	68
query56	319	295	288	288
query57	1318	1281	1209	1209
query58	272	270	252	252
query59	1556	1639	1434	1434
query60	293	270	259	259
query61	150	148	142	142
query62	545	495	433	433
query63	244	206	200	200
query64	2827	1037	876	876
query65	4752	4632	4604	4604
query66	1817	512	377	377
query67	29189	29140	29017	29017
query68	2986	1489	1004	1004
query69	402	303	270	270
query70	1081	946	977	946
query71	393	330	334	330
query72	3018	2645	2355	2355
query73	858	771	423	423
query74	5042	4895	4713	4713
query75	2495	2481	2129	2129
query76	2336	1153	778	778
query77	354	374	279	279
query78	11901	11907	11160	11160
query79	1506	1150	792	792
query80	1271	544	457	457
query81	570	339	283	283
query82	617	154	120	120
query83	359	319	309	309
query84	292	158	137	137
query85	960	602	513	513
query86	433	280	273	273
query87	1827	1822	1732	1732
query88	3655	2782	2780	2780
query89	437	378	320	320
query90	1960	204	196	196
query91	206	193	171	171
query92	63	62	55	55
query93	1693	1473	996	996
query94	717	364	309	309
query95	802	518	565	518
query96	1133	777	337	337
query97	2622	2623	2528	2528
query98	218	206	196	196
query99	1090	1112	966	966
Total cold run time: 262890 ms
Total hot run time: 177192 ms

@hello-stephen

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

query1	0.01	0.00	0.01
query2	0.09	0.05	0.05
query3	0.25	0.14	0.13
query4	1.62	0.14	0.14
query5	0.24	0.24	0.22
query6	1.23	1.07	1.08
query7	0.04	0.01	0.00
query8	0.06	0.03	0.04
query9	0.38	0.31	0.32
query10	0.56	0.56	0.54
query11	0.20	0.15	0.14
query12	0.18	0.14	0.14
query13	0.47	0.48	0.47
query14	1.03	1.02	1.01
query15	0.63	0.61	0.60
query16	0.32	0.31	0.32
query17	1.17	1.10	1.15
query18	0.22	0.20	0.20
query19	2.10	1.92	1.93
query20	0.02	0.01	0.02
query21	15.43	0.21	0.13
query22	4.85	0.05	0.05
query23	16.13	0.30	0.13
query24	2.90	0.45	0.34
query25	0.10	0.05	0.04
query26	0.73	0.22	0.14
query27	0.04	0.03	0.03
query28	3.54	0.87	0.53
query29	12.47	4.18	3.30
query30	0.30	0.15	0.15
query31	2.77	0.61	0.32
query32	3.22	0.59	0.50
query33	3.16	3.19	3.22
query34	15.61	4.19	3.54
query35	3.51	3.48	3.57
query36	0.56	0.42	0.43
query37	0.09	0.06	0.07
query38	0.05	0.05	0.04
query39	0.04	0.03	0.03
query40	0.18	0.16	0.16
query41	0.10	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.04
Total cold run time: 96.68 s
Total hot run time: 25.07 s

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

Requesting changes: two P1 correctness blockers remain.

Critical checkpoint conclusions

  • Goal: The patch correctly preserves authoritative Iceberg mapping presence for ordinary same-version ID-less legacy reads, but it does not preserve matching IDs in mixed-ID files and is not semantically stable across the supported rolling-upgrade phases.
  • Scope and minimality: The implementation is otherwise focused on FE transport plus V1/V2 name matching; the two existing review threads for valid root [] presence and nested presence propagation are fixed on the current head.
  • Concurrency: No new threads, shared mutable state, locks, or lock-order concerns are introduced.
  • Lifecycle: No static-initialization, ownership, or special resource-lifecycle behavior changes.
  • Configuration: No configuration item is added or changed.
  • Compatibility: TField.name_mapping remains wire-decodable, but its interpretation changes without version negotiation. Old/new BEs can split one scan during BE-first rollout, and old/new FEs can produce different plans during the following FE rollout (second inline finding).
  • Parallel paths: FE regular/system-table schemas, hidden equality-delete dependencies, V1/V2, Parquet/ORC, whole/pruned complex fields, and position-delete paths were traced. V1 and V2 both globally downgrade mixed-ID schemas and lose an authoritative matching ID (first inline finding).
  • Conditional logic: The new mapping-presence condition is documented and propagated through the changed builders, but the surrounding all-fields-have-IDs condition is too coarse for the strict branch. No other distinct conditional-path issue survived review.
  • Test coverage: Added FE and BE unit tests cover absent, empty, partial, and nested presence/helper state, but miss mixed-ID Parquet/ORC behavior, old-FE/mixed-BE and mixed-FE execution, and the resulting observable value/default behavior.
  • Test results: No regression output file is changed. This review was static-only as required by the task; no local build or test was run.
  • Observability: Existing parse-failure logging is sufficient for the new FE parse path; the accepted failures are silent wrong-result cases that require correctness fixes rather than additional logging.
  • Persistence and transactions: No EditLog, persisted format, transaction, or failover behavior changes.
  • Data writes: This is a read-path change and introduces no write atomicity or crash-recovery concern.
  • FE/BE state transport: All same-version mapping send/copy sites were checked. The existing optional list and new local presence bit are propagated, but the missing semantic version is the rolling-compatibility blocker above.
  • Performance and memory: The metadata boolean and small mapping copies add no material CPU, memory, allocation, or hot-path concern.
  • Other issues, focus, and completion: No additional user focus was supplied. After three normal/risk-focused convergence rounds, every candidate is accepted, deduplicated, or dismissed and all final reviewers returned NO_NEW_VALUABLE_FINDINGS on this exact two-comment set. The full review is complete.

}
// An explicit empty or unmatched Iceberg mapping means the legacy field is absent;
// falling back to its current name would bind an unrelated physical column.
return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Preserve ID matches when only part of a file schema has IDs

This strict return is used for every field once all_have_field_id sees any ID-less sibling (and V2 similarly downgrades the whole file through IcebergTableReader::_has_field_id). For a partial mapping with a (ID 1, names=[]) and a file containing a (ID 1) plus b (no ID), both scanners now enter name mode and materialize a as missing/default even though its physical ID matches. Iceberg 1.10.1's ParquetSchemaUtil.hasIds and ORCSchemaUtil.hasIds select ID projection when any field has an ID, so that ID must remain authoritative. Please preserve ID-first matching before applying strict empty/unmatched mappings in both V1 and V2 (including nested paths), and add mixed-ID Parquet/ORC coverage.

}
if (lhs.has_identifier_name() && column_has_name(rhs, lhs.get_identifier_name())) {
return true;
if (!lhs.has_name_mapping) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Keep mapping semantics consistent through rolling upgrades

This matcher (and the parallel V1 early return) changes the meaning of the existing optional name_mapping field without a version signal. During Doris's documented BE-first rollout, the old FE already sends present-empty or unmatched mappings for explicit mapped-field entries; old BEs fall back to a matching current-name column while new BEs materialize missing/default, so one scan can combine unrelated values and NULL/defaults. After all BEs are new, the FE rollout still mixes old FEs that omit unmapped entries from partial/[] mappings with new FEs that send empty lists, making identical queries depend on the FE endpoint. Please version/gate this contract or enforce homogeneous compatible execution in both phases, and add old-FE/mixed-BE plus mixed-FE coverage.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (14/14) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 57.48% (23963/41688)
Line Coverage 41.23% (235862/572027)
Region Coverage 37.02% (186200/502950)
Branch Coverage 38.23% (83647/218786)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (14/14) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 74.96% (30485/40666)
Line Coverage 59.12% (336086/568508)
Region Coverage 55.85% (281962/504842)
Branch Coverage 57.17% (125155/218928)

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

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.

Requesting changes: three P1 correctness blockers remain.

Critical checkpoint conclusions

  • Goal and tests: The patch moves V1 and V2 toward Iceberg's any-ID rule and preserves explicit mapping presence, but ID-less nested wrappers still diverge by scanner/format, nested binary initial defaults are lost during projection, and an unchanged V2 equality-delete test is deterministically contradicted by the new mode. The added tests primarily assert helper or transport state and do not cover these observable results.
  • Scope and minimality: The implementation is focused on FE mapping extraction/Thrift transport and BE matching. No unrelated production changes were found.
  • Concurrency, lifecycle, configuration, persistence, and writes: No new thread, lock, ownership, startup/shutdown, configuration, persisted-state, transaction, or data-write contract is introduced.
  • Compatibility and protocol: The optional Thrift presence marker is produced and consumed consistently on same-version paths, and absent old-FE state retains legacy behavior. The mixed-version semantic issue remains covered by the existing rolling-upgrade thread and is not duplicated here.
  • Parallel paths: V1/V2, Parquet/ORC, whole/pruned complex projections, schema-history reconstruction, hidden equality-delete dependencies, and both position-delete paths were traced. The position-delete candidate was dismissed because supported Iceberg position-delete schemas require fixed/matching IDs and the metadata-table scan does not transport the claimed authoritative mapping state.
  • Conditional and null/default behavior: V1's per-struct any-ID condition and V2's global current-node matcher do not implement each format's wrapper projection contract. Separately, recursive Base64 initial-default metadata is dropped before a newly missing nested child is materialized.
  • FE/BE transport: Mapping authority and nested Base64 defaults reach BE schema reconstruction. Authority now survives the changed access-path copy, but the nested default value/encoding metadata does not, which causes the second inline failure.
  • Error and nullability handling: No new status-propagation defect survived review. The accepted failures are silent wrong-result/default-materialization cases rather than missing diagnostics.
  • Observability: Existing debug-state additions are adequate; no separate logging or metric concern was found.
  • Performance and memory: Recursive schema scans and one metadata boolean are planning/setup work with negligible cost; no per-row hot-path or allocation regression was found.
  • User focus and completion: No additional user focus was supplied. After the final wording correction, all three reviewers independently returned NO_NEW_VALUABLE_FINDINGS on this exact three-comment set and anchors.
  • Validation: Static review only, because the review task prohibited builds and test execution. At submission, formatting, CheckStyle, license, dependency, secret, and Cloud UT checks pass; BE UT, FE UT, compile, code-review, and performance checks are pending. The macOS BE UT workflow failed during environment setup because it selected JDK 25 while the build requires JDK 17.

The review remains blocking because all three findings can change returned Iceberg row values or leave the checked-in reader test contract inconsistent with the implementation.

bool all_have_field_id = true;
// Iceberg considers the schema ID-bearing when any field has an ID; requiring all IDs would
// discard authoritative matches merely because an unrelated sibling is ID-less.
bool has_field_id = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Match ID-less wrapper projection to each file format

This computes the any-ID decision independently for each V1 struct, while V2 switches globally but matches only the current root ID. For a physical ID-less wrapper s containing legacy_a with ID 1, and table s ID 10 containing a ID 1, V1 name-matches the wrapper and returns a, whereas V2 cannot match root ID 10 and materializes the struct as missing/default. Iceberg Parquet retains the selected physical child and its nonempty wrapper; Iceberg ORC instead synthesizes the absent parent ID rather than reusing the physical wrapper, so V1's fallback is inconsistent there. Please make both scanner paths honor the format-specific projection contract, with Parquet/ORC whole and pruned nested result tests.

column->name_mapping = schema_column->name_mapping;
// The presence bit is part of the mapping contract: an explicit empty mapping must remain
// authoritative after access-path pruning instead of enabling current-name fallback.
column->has_name_mapping = schema_column->has_name_mapping;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Preserve nested Iceberg binary initial defaults

getBase64EncodedInitialDefaults() indexes nested UUID/BINARY/FIXED field IDs, ExternalUtil recursively writes the value and Base64 marker, and schema reconstruction restores them. This pruning copy then carries only aliases and authority. Once an explicit empty nested mapping correctly rejects a physical child, the V2 child mapping has no default expression and inserts the generic type default; V1's missing-child node does the same. An older file missing such an s.b therefore returns NULL/empty data instead of its decoded initial default. Please carry and materialize recursive initial defaults for struct/array/map children in both scanners, with binary/Base64 result coverage.

}
if (!field.has_identifier_field_id()) {
return false;
if (field.has_identifier_field_id()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Reconcile the unchanged stale-ID equality-delete test

With this new existential switch, IcebergEqualityDeleteByNameIgnoresStaleFileFieldId can no longer produce its asserted [1, 3]. Its file has ID-less legacy_id=[1,2,3] plus stale_key=[100,200,300] carrying ID 0; table current_id also has ID 0. This helper now selects BY_FIELD_ID, so both projection and delete-key lookup bind to stale_key, value 2 deletes nothing, and the result is [100,200,300]. Please explicitly resolve this compatibility contract and update the fixture/expectation (or implement the supported exception); the current full V2 reader suite is deterministically inconsistent with the patch.

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17853	4086	4035	4035
q2	2029	335	205	205
q3	10301	1481	843	843
q4	4687	476	358	358
q5	7500	845	574	574
q6	177	170	138	138
q7	744	822	612	612
q8	9323	1656	1699	1656
q9	5714	4417	4353	4353
q10	6741	1735	1492	1492
q11	509	353	330	330
q12	738	587	462	462
q13	18095	3388	2781	2781
q14	271	267	246	246
q15	q16	796	788	705	705
q17	1044	920	1016	920
q18	6966	5781	5499	5499
q19	1306	1310	1183	1183
q20	828	691	553	553
q21	5924	2662	2426	2426
q22	434	361	303	303
Total cold run time: 101980 ms
Total hot run time: 29674 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4432	4369	4303	4303
q2	283	314	216	216
q3	4553	4940	4440	4440
q4	2062	2150	1389	1389
q5	4401	4262	4265	4262
q6	236	176	152	152
q7	1776	2054	1705	1705
q8	2466	2242	2111	2111
q9	7774	7736	7809	7736
q10	4723	4691	4283	4283
q11	590	455	405	405
q12	897	814	536	536
q13	3219	3556	3018	3018
q14	291	307	278	278
q15	q16	713	751	659	659
q17	1376	1389	1332	1332
q18	8125	7474	6987	6987
q19	1112	1053	1075	1053
q20	2236	2222	1927	1927
q21	5277	4674	4485	4485
q22	514	453	407	407
Total cold run time: 57056 ms
Total hot run time: 51684 ms

@hello-stephen

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

query5	4331	620	475	475
query6	466	227	198	198
query7	4933	615	341	341
query8	350	192	170	170
query9	8787	4068	4015	4015
query10	482	364	319	319
query11	5924	2362	2085	2085
query12	163	106	105	105
query13	1301	623	437	437
query14	6251	5185	4871	4871
query14_1	4251	4212	4266	4212
query15	222	204	185	185
query16	1053	490	451	451
query17	1145	705	576	576
query18	2502	480	355	355
query19	212	197	163	163
query20	114	114	106	106
query21	236	164	139	139
query22	13510	13537	13287	13287
query23	17295	16503	16132	16132
query23_1	16287	16266	16286	16266
query24	7675	1791	1246	1246
query24_1	1336	1298	1275	1275
query25	581	472	398	398
query26	1355	344	224	224
query27	2589	613	390	390
query28	4471	1989	2032	1989
query29	1123	645	520	520
query30	344	271	226	226
query31	1115	1093	980	980
query32	111	65	61	61
query33	539	328	272	272
query34	1182	1136	644	644
query35	781	790	671	671
query36	1202	1237	1082	1082
query37	155	109	99	99
query38	1880	1702	1670	1670
query39	879	887	866	866
query39_1	853	832	836	832
query40	287	163	140	140
query41	65	62	64	62
query42	99	89	90	89
query43	318	320	282	282
query44	1402	761	747	747
query45	194	183	176	176
query46	1100	1172	731	731
query47	2172	2145	2084	2084
query48	397	384	291	291
query49	576	423	316	316
query50	1048	460	333	333
query51	11068	11102	10853	10853
query52	87	83	78	78
query53	265	277	203	203
query54	274	234	229	229
query55	78	71	66	66
query56	292	305	298	298
query57	1321	1289	1201	1201
query58	296	273	254	254
query59	1571	1664	1471	1471
query60	306	268	254	254
query61	157	155	150	150
query62	547	494	440	440
query63	252	208	197	197
query64	2850	1071	912	912
query65	4697	4632	4624	4624
query66	1812	518	377	377
query67	29282	29166	29027	29027
query68	3089	1630	942	942
query69	412	305	258	258
query70	1082	966	936	936
query71	364	331	327	327
query72	3113	2712	2407	2407
query73	805	736	440	440
query74	5080	4896	4698	4698
query75	2546	2498	2133	2133
query76	2377	1197	797	797
query77	347	381	280	280
query78	11944	12055	11207	11207
query79	1527	1162	782	782
query80	1294	566	468	468
query81	576	329	291	291
query82	598	155	119	119
query83	371	325	295	295
query84	293	162	133	133
query85	980	622	519	519
query86	427	297	294	294
query87	1810	1822	1755	1755
query88	3711	2851	2771	2771
query89	441	378	332	332
query90	1895	208	197	197
query91	203	191	166	166
query92	61	59	58	58
query93	1755	1565	965	965
query94	719	369	309	309
query95	799	610	481	481
query96	1005	782	334	334
query97	2623	2644	2499	2499
query98	219	207	195	195
query99	1093	1103	980	980
Total cold run time: 264695 ms
Total hot run time: 177702 ms

@hello-stephen

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

query1	0.01	0.00	0.01
query2	0.14	0.08	0.08
query3	0.40	0.25	0.24
query4	1.61	0.23	0.24
query5	0.34	0.32	0.32
query6	1.15	0.67	0.67
query7	0.03	0.01	0.01
query8	0.09	0.07	0.07
query9	0.52	0.39	0.39
query10	0.61	0.60	0.60
query11	0.31	0.18	0.19
query12	0.32	0.19	0.19
query13	0.53	0.54	0.53
query14	0.95	0.94	0.93
query15	0.67	0.59	0.60
query16	0.39	0.39	0.40
query17	1.03	1.03	1.16
query18	0.32	0.31	0.30
query19	1.91	1.86	1.80
query20	0.02	0.02	0.01
query21	15.40	0.36	0.32
query22	4.94	0.14	0.14
query23	15.84	0.49	0.30
query24	2.48	0.61	0.44
query25	0.16	0.10	0.10
query26	0.74	0.27	0.22
query27	0.10	0.10	0.10
query28	3.46	0.96	0.51
query29	12.50	4.26	3.34
query30	0.37	0.27	0.26
query31	2.76	0.61	0.32
query32	3.23	0.60	0.48
query33	2.98	2.93	2.90
query34	15.68	4.13	3.39
query35	3.33	3.30	3.24
query36	0.65	0.53	0.50
query37	0.12	0.09	0.10
query38	0.08	0.06	0.07
query39	0.08	0.06	0.06
query40	0.20	0.19	0.18
query41	0.13	0.08	0.08
query42	0.08	0.06	0.06
query43	0.08	0.07	0.07
Total cold run time: 96.74 s
Total hot run time: 25.72 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