Skip to content

[fix](fe) Keep sibling capabilities on iceberg-on-HMS tables pinned at a snapshot - #69999

Merged
morningman merged 1 commit into
apache:masterfrom
morningman:b2m-t35y-hive-snapshot-capabilities
Nov 3, 2026
Merged

morningman merged 1 commit into
apache:masterfrom
morningman:b2m-t35y-hive-snapshot-capabilities

Conversation

@morningman

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: #69299, #69488

Problem Summary:

In short

An Iceberg table stored in HMS and read through an HMS catalog ('type'='hms') loses every capability it inherits from the embedded Iceberg connector in any statement that pins the table at a snapshot. Since #69299 that is every statement that reads it: it gets no Top-N lazy materialization and no nested column pruning, and DEFAULT(column) in the conditions of UPDATE and MERGE silently matches nothing. This PR makes the snapshot overload of HiveConnectorMetadata#getTableSchema pass the inherited capabilities on, as the latest-schema overload already does. It is a master-only fix (branch-4.x has no connector SPI), left over from #69488 (review comment 4355508100).

Background

fe-core resolves a table-scoped capability (PluginDrivenExternalTable.hasCapability) from the catalog's connector or from the per-table set in the table's schema. It never asks the embedded connector that built the schema of an iceberg-on-HMS table. So HiveConnectorMetadata copies the capabilities that fe-core resolves per table (SIBLING_INHERITABLE_CAPABILITIES: auto and sampled analyze, Top-N lazy materialization, nested column pruning, field-id access paths, storage predicate pruning, nested column DDL and write-only defaults) from the owning connector into the per-table set of each schema it delegates. Iceberg declares seven of the eight; it does not declare sampled analyze.

It did this only in getTableSchema(session, handle). getTableSchema(session, handle, snapshot) returned the sibling's schema unchanged. fe-core uses that overload for explicit time travel. Since #69299 it also uses it for the latest-snapshot pin of every statement that reads an Iceberg table: the pin retains its schema (retainSchema(true)), PluginDrivenMvccExternalTable builds the schema at that pin, and getSchemaCacheValue(), where hasCapability reads the per-table set, returns it for the rest of the statement.

The problem, and what it cost

Reproduced on a local cluster with a standalone Hive 3.1.2 metastore. Two identical format-version 3 tables (id INT, c INT, s STRUCT<a:INT, b:STRING>) get two rows, then ADD COLUMN d INT DEFAULT 42, RENAME COLUMN s.a TO a2 and a third row with d = 7. The same statements then run against one table through the Iceberg catalog and against the other through the HMS catalog.

Statement Iceberg catalog HMS catalog, master HMS catalog, this PR
EXPLAIN SELECT * FROM t ORDER BY id LIMIT 1, also with FOR VERSION AS OF VMaterializeNode no lazy materialization VMaterializeNode
EXPLAIN SELECT struct_element(s, 'a2') FROM t all access paths: [s.a2] no pruning all access paths: [s.a2]
UPDATE t SET c = 100 WHERE d = DEFAULT(d) updates rows 0 and 1 updates nothing updates rows 0 and 1
MERGE INTO t USING (SELECT 0 AS id) s ON t.id = s.id AND t.d = DEFAULT(t.d) WHEN MATCHED THEN UPDATE SET c = 300 updates row 0 updates nothing updates row 0
DELETE FROM t WHERE d = DEFAULT(d) fails: cannot be used in a DELETE deletes nothing fails the same way
SELECT DEFAULT(d) FROM t fails: must reference a column of the table that this statement writes returns NULL fails the same way

The UPDATE, MERGE and DELETE rows fail silently: the statement succeeds and changes nothing. Reads of the renamed field return the right values in all three columns: on master the HMS catalog does not prune the struct at all, and with this PR it prunes it with field-id access paths, as the Iceberg catalog does. INSERT ... VALUES (DEFAULT(d)) and INSERT ... SELECT DEFAULT(d) already wrote 42 through both catalogs.

How this PR fixes it

Both overloads now go through one private helper, delegatedTableSchema. It resolves the owning sibling once, asks it for the schema (at the pin, for the snapshot overload), and passes the result through the existing reflectSiblingCapabilities. The snapshot reaches the sibling unchanged. A hudi-on-HMS table is unaffected: the Hudi connector declares no capabilities, so its schema comes back as it was. A plain Hive table never reaches this branch.

Results

Through the HMS catalog, every statement in the table above now plans and answers as it does through the Iceberg catalog.

Release note

Fix Iceberg tables read through an HMS catalog losing Top-N lazy materialization and nested column pruning, and DEFAULT(column) in UPDATE / MERGE conditions on them reading NULL instead of the column's write-default.

Check List (For Author)

  • Test: Unit Test / Regression test / Manual test
    • FE UT: in HiveConnectorMetadataSiblingDelegationTest the per-table capability cases now check both getTableSchema overloads, and a new case checks that the snapshot overload forwards the pin unchanged and resolves the owner once. All 49 fe-connector-hive test classes pass (502 tests); fe-connector-hive Checkstyle 0 violations. Three mutations (the snapshot overload without the reflection, asking the sibling for its latest schema, resolving the owner a second time) each fail only their target cases.
    • Regression: new test_iceberg_on_hms_gateway_pinned_capabilities (hive3 docker) runs the probe above through both catalogs; its .out holds both results under paired tags. I ran it locally against a standalone Hive 3.1.2 metastore with a file:// warehouse, with the catalog properties adapted, not against the hive3 docker environment: without this change it fails on the HMS catalog's first Top-N check, with it it passes.
    • Manual: the table above, on a local single-BE cluster.
  • Behavior changed: Yes. Statements on Iceberg tables read through an HMS catalog plan with the capabilities they have through an Iceberg catalog, and DEFAULT(column) on them resolves as it does there.
  • Does this need documentation: No

…t a snapshot

### What problem does this PR solve?

Issue Number: None

Related PR: apache#69299, apache#69488

Problem Summary:

An Iceberg table stored in HMS can be read through a dedicated Iceberg
catalog or through an HMS catalog ('type'='hms'), whose embedded Iceberg
connector builds the table's schema. fe-core resolves the table-scoped
capabilities (Top-N lazy materialization, nested column pruning, field-id
access paths, storage predicate pruning, nested column DDL, write-only
defaults, auto analyze) from the catalog's connector or from the per-table
set in the table's schema, never from the embedded connector. So the Hive
connector copies the ones Iceberg declares into the per-table set of every
schema it delegates, but it did that only in getTableSchema(session,
handle), not in getTableSchema(session, handle, snapshot).

fe-core uses the second one for explicit time travel and, since apache#69299,
for every statement that reads an Iceberg table: the latest-snapshot pin
retains its schema, and the table's capabilities are then read from that
schema. On an iceberg-on-HMS table such a statement therefore saw none of
the inherited capabilities. Reproduced on a local cluster with a
standalone Hive 3.1.2 metastore, comparing the same statements on twin
format-version 3 tables, one through each catalog:

| Statement | Iceberg catalog | HMS catalog before | HMS catalog after |
|---|---|---|---|
| EXPLAIN ... ORDER BY id LIMIT 1 (latest, and FOR VERSION AS OF) | VMaterializeNode | no lazy materialization | VMaterializeNode |
| EXPLAIN SELECT struct_element(s, 'a2') | all access paths: [s.a2] | no pruning | all access paths: [s.a2] |
| UPDATE t SET c = 100 WHERE d = DEFAULT(d) | updates the rows with d = 42 | updates nothing | updates the rows with d = 42 |
| MERGE ... ON t.id = s.id AND t.d = DEFAULT(t.d) | updates row 0 | updates nothing | updates row 0 |
| DELETE FROM t WHERE d = DEFAULT(d) | error: cannot be used in a DELETE | deletes nothing | same error |
| SELECT DEFAULT(d) FROM t | error: must reference the written table | NULL | same error |

The snapshot overload now builds the delegated schema through the same
helper as the latest-schema overload, which resolves the owning sibling
once, asks it for the schema (at the pin, for the snapshot overload) and
reflects the inherited capabilities onto it. A hudi-on-HMS table is
unchanged: the Hudi connector declares no capabilities.

### Release note

Fix iceberg-on-HMS tables read through an HMS catalog losing Iceberg
optimizations (Top-N lazy materialization, nested column pruning) and
write-default resolution of DEFAULT(column) in UPDATE / MERGE conditions.

### Check List (For Author)

- Test: Unit Test / Regression test / Manual test
    - HiveConnectorMetadataSiblingDelegationTest: the per-table capability
      cases now check both getTableSchema overloads; a new case checks that
      the snapshot overload forwards the pin unchanged and resolves the
      owner once. All 49 fe-connector-hive test classes pass (502 tests).
      Three mutations (no reflection in the snapshot overload, asking the
      sibling for its latest schema, resolving the owner twice) each fail
      only their target cases.
    - New regression test_iceberg_on_hms_gateway_pinned_capabilities (hive3
      docker). Run locally against a standalone Hive 3.1.2 metastore with a
      file:// warehouse (catalog properties adapted): fails on the HMS
      catalog's first Top-N check without this change, passes with it.
- Behavior changed: Yes. Statements on iceberg-on-HMS tables read through
  an HMS catalog plan with the same capabilities as through an Iceberg
  catalog.
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@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?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17640	5188	5073	5073
q2	2160	311	273	273
q3	10220	1381	839	839
q4	4677	468	333	333
q5	7415	809	511	511
q6	180	187	156	156
q7	737	810	601	601
q8	9252	1342	1074	1074
q9	5425	4167	4198	4167
q10	6811	1289	1029	1029
q11	422	249	228	228
q12	642	403	279	279
q13	18115	3617	3092	3092
q14	277	277	247	247
q15	q16	723	711	638	638
q17	1204	745	598	598
q18	7007	6171	6083	6083
q19	1137	963	580	580
q20	388	336	229	229
q21	5105	2148	2292	2148
q22	347	295	255	255
Total cold run time: 99884 ms
Total hot run time: 28433 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5613	5617	5586	5586
q2	679	594	537	537
q3	5195	5380	4912	4912
q4	1977	2137	1555	1555
q5	5318	5163	5629	5163
q6	271	202	152	152
q7	2361	2103	1886	1886
q8	3053	2904	2731	2731
q9	8103	8094	8062	8062
q10	3755	3653	3329	3329
q11	623	434	412	412
q12	680	699	514	514
q13	3707	3839	3290	3290
q14	314	330	286	286
q15	q16	716	754	679	679
q17	8300	7534	6888	6888
q18	12969	12185	12776	12185
q19	885	773	762	762
q20	2161	2159	1908	1908
q21	5559	4543	4666	4543
q22	492	471	414	414
Total cold run time: 72731 ms
Total hot run time: 65794 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.

Reviewed PR 69999 at the exact bundle/live head 6d547c0. The change keeps sibling capabilities on both latest and pinned snapshot schemas by forwarding the owning handle and snapshot through one shared helper, while preserving schema columns, properties, and write metadata identity. I traced the FE pinned-schema publication path, Iceberg schema-id selection and applySnapshot flow, Hive Iceberg/Hudi owner routing, per-table capability resolution, statement-scoped metadata memoization, and the parallel Hive-handle path. The changed unit tests and the new Iceberg/HMS gateway regression suite cover both overloads, explicit time travel, nested field-id access, Top-N lazy materialization, write-only defaults, and negative cases; no concrete defect was verified. All three required independent review passes reported NO_NEW_VALUABLE_FINDINGS, and RISK-1 through RISK-3 were dismissed with evidence. No existing inline review threads or blocking findings apply, and there was no additional user focus. This was a static review; builds and tests were not executed per the review contract. The review converged in round 1.

Review converged after 1 round(s).

@hello-stephen

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

query5	4322	580	426	426
query6	420	214	214	214
query7	4822	455	237	237
query8	317	168	153	153
query9	8790	3192	3144	3144
query10	481	313	245	245
query11	6026	3870	3565	3565
query12	152	92	85	85
query13	1250	437	314	314
query14	6146	3935	3630	3630
query14_1	3428	3386	3403	3386
query15	215	205	185	185
query16	942	464	442	442
query17	884	623	503	503
query18	2419	410	298	298
query19	187	169	133	133
query20	81	85	84	84
query21	202	130	107	107
query22	13087	12941	12829	12829
query23	13520	12785	12421	12421
query23_1	12490	12553	12599	12553
query24	7366	823	477	477
query24_1	478	471	462	462
query25	525	393	337	337
query26	1259	256	139	139
query27	2769	436	267	267
query28	4632	2048	2061	2048
query29	1528	618	448	448
query30	295	226	184	184
query31	1015	936	823	823
query32	139	91	93	91
query33	512	286	231	231
query34	895	860	506	506
query35	724	747	636	636
query36	851	806	733	733
query37	127	94	87	87
query38	1766	1721	1679	1679
query39	774	806	744	744
query39_1	735	731	738	731
query40	232	115	99	99
query41	73	69	67	67
query42	87	81	91	81
query43	343	368	312	312
query44	1323	714	737	714
query45	183	183	175	175
query46	846	966	596	596
query47	2874	2753	2608	2608
query48	306	326	232	232
query49	591	416	298	298
query50	702	287	218	218
query51	10765	10740	10450	10450
query52	78	85	70	70
query53	191	211	156	156
query54	243	200	220	200
query55	72	71	67	67
query56	239	232	220	220
query57	1643	1639	1556	1556
query58	285	259	257	257
query59	2151	2117	1985	1985
query60	279	246	236	236
query61	147	140	143	140
query62	396	329	293	293
query63	186	154	157	154
query64	2751	951	773	773
query65	3306	3291	3269	3269
query66	1785	413	304	304
query67	20046	20216	20065	20065
query68	3014	1010	607	607
query69	388	281	253	253
query70	830	826	752	752
query71	290	227	210	210
query72	2733	2617	2067	2067
query73	513	523	291	291
query74	4600	4505	4361	4361
query75	2342	2286	1937	1937
query76	2305	1005	621	621
query77	349	384	280	280
query78	9793	9674	9195	9195
query79	905	867	531	531
query80	484	445	334	334
query81	529	311	280	280
query82	207	130	105	105
query83	282	195	191	191
query84	302	125	101	101
query85	780	414	364	364
query86	252	254	234	234
query87	1984	1935	1793	1793
query88	3736	2788	2786	2786
query89	321	272	242	242
query90	2125	170	166	166
query91	163	152	128	128
query92	96	87	92	87
query93	986	999	579	579
query94	450	328	254	254
query95	583	391	295	295
query96	669	483	240	240
query97	2446	2424	2339	2339
query98	162	151	147	147
query99	760	766	654	654
Total cold run time: 233827 ms
Total hot run time: 150940 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.09	0.05	0.05
query3	0.26	0.09	0.09
query4	1.60	0.10	0.11
query5	0.23	0.24	0.24
query6	1.16	0.67	0.68
query7	0.03	0.01	0.01
query8	0.05	0.04	0.04
query9	0.40	0.33	0.36
query10	0.56	0.63	0.56
query11	0.19	0.14	0.14
query12	0.19	0.14	0.14
query13	0.48	0.48	0.49
query14	1.02	1.02	1.00
query15	0.63	0.60	0.60
query16	0.31	0.35	0.34
query17	1.13	1.14	1.12
query18	0.21	0.19	0.19
query19	2.05	1.95	1.94
query20	0.02	0.01	0.01
query21	15.49	0.19	0.13
query22	4.93	0.05	0.05
query23	16.28	0.32	0.12
query24	2.87	0.69	0.42
query25	0.10	0.06	0.05
query26	1.23	0.21	0.14
query27	0.04	0.04	0.04
query28	3.21	0.70	0.37
query29	12.53	4.07	3.20
query30	0.28	0.13	0.10
query31	2.77	0.55	0.34
query32	3.22	0.59	0.50
query33	3.26	3.20	3.22
query34	15.21	4.00	3.42
query35	3.34	3.31	3.35
query36	0.55	0.44	0.43
query37	0.08	0.06	0.06
query38	0.05	0.04	0.03
query39	0.03	0.04	0.03
query40	0.17	0.15	0.15
query41	0.09	0.03	0.03
query42	0.03	0.02	0.03
query43	0.05	0.04	0.03
Total cold run time: 96.42 s
Total hot run time: 24.22 s

@924060929 924060929 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Iceberg tables accessed through an HMS catalog need their owning connector's table-scoped capabilities on both latest and pinned schemas. This change applies the existing capability reflection through one shared delegation helper.

The independent static review found no blocking defects in the FE changes or the necessary FE/BE scan contracts. Snapshot forwarding, schema fields, write metadata identity, statement-scoped metadata reuse, and Hive/Hudi/system-table boundaries remain consistent. The added tests cover both overloads, capability filtering, Top-N, nested-field reads, and write-default behavior.

APPROVE for head 6d547c0. No builds or tests were run during this review. The supplied CI snapshot reports successful P0, external regression, FE UT, and compilation checks, but FE coverage failed; merge eligibility requires a separate check.

Automated FE-focused static review (gpt-6.1-sol, high). No build or tests were run.

@morningman
morningman merged commit 1f71a66 into apache:master Nov 3, 2026
37 of 38 checks passed
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

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

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

Labels

approved Indicates a PR has been approved by one committer. backport/4.2-master reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants