Skip to content

[improvement](datasource) Version Iceberg partition views and SQL cache metadata - #69299

Merged
924060929 merged 5 commits into
apache:masterfrom
924060929:codex/lake-pr-iceberg-versions
Oct 26, 2026
Merged

924060929 merged 5 commits into
apache:masterfrom
924060929:codex/lake-pr-iceberg-versions

Conversation

@924060929

@924060929 924060929 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Problem Summary: Ordinary Iceberg scans and SQL result-cache validation materialize and copy the full Doris partition view even when native file pruning or a pinned metadata version is sufficient. Cache immutable engine partition views and their pruning index by connector metadata generation, defer conversion for native planning, and obtain SQL cache versions through a source-neutral version provider. Record distinct current, historical and branch/tag references when a statement reads several versions of one table, and revalidate them through the existing snapshot resolver. Reuse statement-retained table handles and combine relation-name discovery through the connector SPI to avoid repeated remote existence and listing requests. Preserve MV/partition metadata consumers, refresh invalidation and frozen-reader schema consistency.

Main benefit

Remove partition-count-proportional work from ordinary Iceberg planning and SQL Cache validation. On the independently measured 120,000-partition fixture, warm LIMIT 10 falls from 389–463 ms to 9.69 ms, and warm EXPLAIN from 188–189 ms to 3.45 ms. On the 1,000-Manifest fixture, cold LIMIT falls from 925–934 ms to 26.50 ms. The independent A/B table and cache protocol are below; both variants use the same Release BE.

Ordinary scans use native Iceberg pruning without first converting every partition into a Doris representation. Consumers that need an internal partition view reuse an immutable, generation-specific view and index. SQL Cache validates the actual selected table/snapshot/schema version without enumerating partitions, including independently tracked historical, branch and tag references. DDL existence probes remain live, so cached query handles cannot make CREATE/DROP decisions stale.

The 120,000-partition experiment deliberately avoids SQL result-cache hits, so the improvement measures planning and metadata validation rather than returning cached query results. Function-heavy partition predicates that still require value evaluation are not claimed to improve.

Release note

Iceberg ordinary scans use native file pruning by default. The session variable enable_iceberg_internal_partition_pruning enables Doris internal partition pruning when needed. SQL result caching supports independently versioned Iceberg snapshot and branch/tag references without enumerating partitions.

Check List (For Author)

Latest DDL review-repair validation at b44b384627e: all 90 DDL-routing tests and a full build.sh --fe -j4 with Checkstyle passed. Tests cover CREATE/CREATE IF NOT EXISTS after an external drop and reject a stale cached handle as proof of a concurrent creator. Earlier metadata/SPI/live SQL checks listed below cover their respective implementation revisions; the performance table predates the subsequent rebase and review repairs and is not a fresh A/B of this head. CI status remains tracked by the checks on this PR.

  • Test: Unit Test
    • run-fe-ut.sh: 325 targeted metadata, MVCC, partition view, SQL cache and dependent test cases passed
    • run-fe-ut.sh: all 85 connector SPI tests passed separately
    • build.sh --fe -j4 passed, including Checkstyle and connector architecture gates
  • Behavior changed: Yes, defer ordinary Iceberg partition conversion by default and support version-aware external SQL caching
  • Does this need documentation: Yes, updated fe/fe-connector/README.md

Performance validation

Measured independently against unoptimized master e0bf4874070, with identical Linux AArch64 Release BE, data/footer prewarming, and session settings. Numbers are complete SQL medians, in milliseconds. The baseline column shows two separate baseline runs.

Scenario Baseline runs This PR
120,000 partitions, warm LIMIT 10 389.38–463.29 9.69
120,000 partitions, warm EXPLAIN 188.19–189.07 3.45
120,000 partitions, warm LIMIT 10 with internal pruning enabled 333.18–391.80 9.76
1,000 manifests / 10,000 files, cold LIMIT 10 925.44–933.77 26.50

The large-partition protocol enables SQL Cache validation but varies an always-true predicate to avoid result-cache hits. Ordinary LIMIT cases have 40 measured samples; EXPLAIN has 20. Baseline EXPLAIN retention is bounded by using equivalent SELECT warmup, identically for all variants. Function-heavy partition predicates remain approximately 5.5 seconds in this fixture and are not claimed as a benefit.

Additional validation

  • Live SQL Cache checks: 170 assertions for current reads, snapshots, timestamps, branch/tag movement, multiple versions of one table, schema evolution/rename, snapshot expiry and ref removal/recreation.

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

@924060929

Copy link
Copy Markdown
Contributor Author

run buildall

@924060929

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.

Reviewed all 61 changed paths at a45d9bf using the supplied aggregate diff. Two P2 issues warrant fixes: eager sorting can reject valid mixed-spec Iceberg partition displays, and weighted admission prevents large partition views from being cached. Both have inline evidence and concrete triggers. No P0/P1 finding was substantiated; there were no existing inline threads to duplicate or carry as blockers.

The reviewed flow is relation discovery -> statement snapshot/schema pin -> deferred native planning or partition-view conversion -> scan request -> per-reference SQL-cache recording and revalidation.

Critical checkpoint conclusions:

  1. Goal and proof: native planning and immutable metadata generations reduce repeated partition/existence work while retaining selected read versions. Added component tests support the main flow; the two inline cases remain gaps.
  2. Scope and clarity: changes stay within FE connector, MVCC, planner and result-cache plumbing; the binlog test change is a mechanical dependency-key adaptation.
  3. Concurrency and locks: request planning, split workers and refresh/loaders share concurrent statement memos, generation-aware cache publication and retained leases. No additional lock-order or deadlock issue was substantiated.
  4. Lifecycle: frozen metadata and resource leases remain statement-owned across refresh; deferred loading uses the pinned handle/schema. No premature close, ownership cycle or static initialization problem was substantiated.
  5. Configuration: enable_iceberg_internal_partition_pruning defaults to false and is captured from session state; later statements observe session changes without restart. Partition-aware consumers retain eager planning.
  6. Compatibility: default SPI methods preserve other connector behavior; API 18.0 advances to 19.0 with both surface baselines updated. No persisted storage format changes occur.
  7. Thrift/protobuf: no definitions or field IDs changed.
  8. Parallel paths: latest, historical, branch/tag, empty/system reads, DDL/CTAS, synchronous/streaming scans, COUNT pushdown, MV compensation and partition rules were traced; unsupported options/incremental cache paths remain excluded.
  9. Conditional/error paths: native unknown remains distinct from an empty read; newly installed partition rules reject unknown counts. Version acquisition failures disable caching. Eager mixed-spec index construction is the exception covered inline.
  10. Coverage: reviewed new unit cases for pins, selectors, schema/ref changes, cache identity, refresh and concurrency, plus the regression suite. Mixed-spec sorting and large weighted admission need coverage. Builds and tests were prohibited and were not run.
  11. Changed expected results: cold/warm rows, branch/tag/main aggregates, moved branch, refresh and recreation outputs agree with the scripted operations. Generation of the supplied output file was not independently rerun.
  12. Observability: native EXPLAIN identifies unknown counts; existing cache rejection metrics and load-failure logging cover the changed paths. No additional logging/metrics defect was substantiated.
  13. Transactions and persistence: no EditLog, replay, commit or failover protocol changes occur.
  14. Writes and crash atomicity: no data-write or transaction-atomicity logic changed; existing DDL paths continue to use writable metadata.
  15. FE/BE variables: no transmitted field or BE protocol addition; the new session preference affects FE planning.
  16. Performance and memory: native pruning, view reuse and deferred MV partition unions remove repeated work. The weighted-cache issue defeats reuse under supported memory limits; eager sorting also affects display-only views.
  17. Other invariants: selected snapshot/schema identity is retained, and no new rowset visibility, delete-bitmap or BE allocation behavior is involved. No additional verified issue remained after the missed-issue sweeps.

User focus: no additional focus points were supplied. Coverage limit: this is a static review; the new integration regression disables SQL cache, so end-to-end cache behavior and the PR's performance/test claims were not independently executed. No changed path or concrete candidate remains unreviewed or unresolved.

Review converged after 2 round(s).

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17574	5202	5100	5100
q2	2088	312	266	266
q3	10289	1387	807	807
q4	4694	465	334	334
q5	7424	815	529	529
q6	183	190	160	160
q7	741	800	585	585
q8	9268	1349	1101	1101
q9	5405	4244	4213	4213
q10	6856	1275	1039	1039
q11	444	244	221	221
q12	654	404	282	282
q13	18050	3583	3005	3005
q14	280	273	250	250
q15	q16	739	731	667	667
q17	1185	746	584	584
q18	6996	6170	6117	6117
q19	1111	983	599	599
q20	407	336	225	225
q21	5583	2283	2754	2283
q22	396	326	286	286
Total cold run time: 100367 ms
Total hot run time: 28653 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5966	5866	5934	5866
q2	720	633	576	576
q3	5613	5710	5273	5273
q4	2018	2165	1612	1612
q5	5967	5766	5524	5524
q6	243	191	155	155
q7	2365	2031	1820	1820
q8	3013	2663	2660	2660
q9	7512	7422	7475	7422
q10	3651	3564	3138	3138
q11	592	410	400	400
q12	664	674	521	521
q13	3286	3636	3061	3061
q14	277	283	257	257
q15	q16	662	680	613	613
q17	7744	7076	7006	7006
q18	12993	12193	12867	12193
q19	917	816	835	816
q20	2137	2133	1928	1928
q21	5756	4898	4998	4898
q22	506	468	434	434
Total cold run time: 72602 ms
Total hot run time: 66173 ms

@hello-stephen

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

query5	4335	602	423	423
query6	433	207	200	200
query7	4820	468	234	234
query8	314	180	147	147
query9	8766	3116	3112	3112
query10	473	295	257	257
query11	5923	3835	3559	3559
query12	146	91	85	85
query13	1242	452	313	313
query14	6115	3958	3786	3786
query14_1	3426	3515	3438	3438
query15	210	203	177	177
query16	955	433	428	428
query17	874	672	541	541
query18	2424	429	311	311
query19	203	167	137	137
query20	84	97	84	84
query21	212	128	108	108
query22	13012	13083	13004	13004
query23	13439	12662	12539	12539
query23_1	12479	12565	12510	12510
query24	7421	839	462	462
query24_1	469	478	474	474
query25	530	419	370	370
query26	1246	253	136	136
query27	2768	433	268	268
query28	4599	1923	1927	1923
query29	1553	601	422	422
query30	292	216	186	186
query31	1020	913	821	821
query32	135	87	89	87
query33	521	291	236	236
query34	892	833	491	491
query35	738	751	641	641
query36	808	814	734	734
query37	124	99	81	81
query38	1773	1757	1674	1674
query39	789	795	739	739
query39_1	729	721	735	721
query40	218	111	95	95
query41	67	63	61	61
query42	87	91	85	85
query43	349	369	320	320
query44	1337	690	685	685
query45	178	190	159	159
query46	864	964	557	557
query47	2919	2863	2896	2863
query48	296	302	222	222
query49	566	432	296	296
query50	682	266	220	220
query51	10511	10437	10175	10175
query52	76	78	68	68
query53	184	205	152	152
query54	251	187	175	175
query55	69	73	64	64
query56	239	224	222	222
query57	1664	1618	1596	1596
query58	284	266	252	252
query59	2097	2160	1934	1934
query60	289	244	226	226
query61	148	141	143	141
query62	388	340	286	286
query63	186	159	163	159
query64	2729	940	772	772
query65	3331	3275	3249	3249
query66	1770	419	305	305
query67	20134	20286	19977	19977
query68	3008	1001	653	653
query69	396	286	244	244
query70	878	780	755	755
query71	303	235	209	209
query72	2707	2677	2450	2450
query73	517	541	301	301
query74	4644	4547	4354	4354
query75	2332	2292	1925	1925
query76	2320	1012	624	624
query77	361	387	280	280
query78	9712	9687	9203	9203
query79	1009	927	520	520
query80	590	406	331	331
query81	541	313	271	271
query82	581	137	104	104
query83	304	198	179	179
query84	315	126	104	104
query85	824	423	368	368
query86	317	241	228	228
query87	1999	1973	1801	1801
query88	3694	2676	2665	2665
query89	327	282	241	241
query90	1896	178	176	176
query91	165	151	131	131
query92	101	91	93	91
query93	1043	996	568	568
query94	440	294	293	293
query95	568	325	304	304
query96	682	502	231	231
query97	2420	2452	2275	2275
query98	158	152	146	146
query99	775	760	641	641
Total cold run time: 233934 ms
Total hot run time: 151208 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.09	0.05	0.05
query3	0.26	0.09	0.08
query4	1.61	0.09	0.10
query5	0.23	0.24	0.23
query6	1.17	0.69	0.68
query7	0.04	0.00	0.01
query8	0.05	0.04	0.04
query9	0.40	0.36	0.35
query10	0.57	0.57	0.63
query11	0.20	0.15	0.14
query12	0.18	0.15	0.14
query13	0.48	0.50	0.48
query14	1.01	1.01	1.00
query15	0.63	0.60	0.61
query16	0.36	0.35	0.36
query17	1.12	1.16	1.11
query18	0.21	0.19	0.20
query19	2.11	1.97	1.91
query20	0.02	0.01	0.01
query21	15.44	0.20	0.14
query22	4.91	0.05	0.06
query23	16.26	0.32	0.12
query24	2.85	0.62	0.31
query25	0.12	0.04	0.07
query26	1.18	0.22	0.16
query27	0.05	0.03	0.05
query28	3.91	0.75	0.35
query29	12.49	4.12	3.25
query30	0.28	0.14	0.12
query31	2.77	0.57	0.31
query32	3.22	0.60	0.51
query33	3.27	3.16	3.16
query34	15.31	3.99	3.38
query35	3.36	3.30	3.32
query36	0.56	0.44	0.42
query37	0.09	0.06	0.06
query38	0.05	0.04	0.04
query39	0.03	0.03	0.03
query40	0.18	0.14	0.14
query41	0.08	0.02	0.02
query42	0.04	0.02	0.02
query43	0.04	0.04	0.03
Total cold run time: 97.24 s
Total hot run time: 24.01 s

@924060929
924060929 force-pushed the codex/lake-pr-iceberg-versions branch from a45d9bf to 12ba5ca Compare October 20, 2026 04:00
@924060929

Copy link
Copy Markdown
Contributor Author

run buildall

@924060929

Copy link
Copy Markdown
Contributor Author

/review

@924060929

Copy link
Copy Markdown
Contributor Author

CI failure triage for the rebased head:

  • Fixed the owned Hive streaming UT fixture (missing SqlBlockRuleMgr) and opted the two Iceberg EXPLAIN regressions that require exact partition metadata into internal partition pruning.
  • The Paimon CHAR complex MAP-key test repair is already upstream in [fix](paimon) Authenticate deferred metadata reads and normalized HDFS storage #69247 and is included by the rebase.
  • The bloom_filter_p0 failure log shows Is Cached: No and Profile Completion State: COLLECTING when the test checks the Bloom-filter counter. The legacy profile{} path reads the asynchronous profile once; its failure branch also calls a nonexistent Suite.fail(String). This is an independent native-scan regression-test issue, left outside this external-metadata PR rather than changing unrelated production paths.
  • The coverage job log was not accessible through the guest artifact endpoints, so its cause is not claimed fixed. Existing muted TSO/expiry/timezone failures are not claimed fixed either.

Local validation on this repaired head: 210 scoped tests, an overlapping 21-test weighted-cache/runtime pass, and build.sh --fe with Checkstyle passed. New buildall and review runs have been requested; remote CI completion is still pending.

@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 head 12ba5caef8d92a09526c8172f567d40052d6d5b6 using the supplied aggregate diff and all 67 changed paths. One new P2 regression is reported inline: a cached Iceberg table handle breaks HMS DROP TABLE IF EXISTS after an external drop. No new P0/P1 finding was verified.

The existing weighted partition-view admission report and heterogeneous partition-key sorting report are addressed on this head: the dedicated estimator reaches catalog runtimes, and mixed LIST key shapes retain their maps without constructing an invalid sorted index. Neither is reposted. No additional user focus was supplied.

Critical checkpoint conclusions:

  • Goal and evidence: native Iceberg scans can defer engine partition conversion; immutable views/indexes are reused by generation, and SQL-cache dependencies retain each original selector. Focused unit and handle/ref/schema/refresh regressions support those paths; the inline DDL edge remains uncovered.
  • Scope and abstraction: changes consistently address metadata reuse, planning, and result-cache identity. Generic engine code uses SPI defaults and source-neutral carriers.
  • Concurrency and locks: statement lookup uses ConcurrentHashMap; deferred views are memoized and published after construction; existing cache generation fences and borrower reference counting protect refresh/eviction. No new lock-order defect was verified.
  • Lifecycle: statement-frozen metadata and retained schemas feed binding and scans; leases survive cache retirement and release at statement cleanup. No new static-initialization issue was identified.
  • Configuration: enable_iceberg_internal_partition_pruning defaults to false, propagates through captured session properties, and is observed without restart. MV candidates and partition-aware block rules retain eager metadata.
  • Compatibility: connector API advances from 19.0 to 20.0 with both recorded baselines and the version assertion updated. No persisted storage format or FE/BE wire change is introduced.
  • Thrift/protobuf IDs: no definitions changed.
  • Parallel paths: checked latest/historical/branch/tag reads, empty/no-scan cache results, synchronous/streaming scans, system tables, LIST/RANGE views, cache-disabled/offline calls, and DDL. The new issue is in the DDL existence path.
  • Conditional semantics: NATIVE is unknown/all, not prune-to-zero; predicates remain on the scan. A partition rule introduced during native planning fails with a retry instead of treating an unknown count as zero.
  • Tests and negative cases: inspected selector immutability, expired refs, schema-only changes, recreation, deferred-load concurrency/failure, cache invalidation and 12,000-partition weighted admission. No build or test was run under this static-only contract.
  • Changed expected output: counts, sums, branch/tag contents, refresh and recreation results agree with the regression statements. Output generation was not independently executed.
  • Observability: native partition counts are explicitly shown as unknown; existing cache metrics and contextual failures remain available. No additional observability defect was verified.
  • Transactions, persistence and failover: no transaction protocol or EditLog format changes; caches and pins remain transient. No new persistence gap was verified.
  • Data writes and atomicity: write transaction machinery is unchanged; mutation preflight is affected by the inline stale-existence issue.
  • FE/BE variable propagation: the new session switch controls FE planning and requires no new BE field.
  • Performance: versioned keys avoid partition fingerprints for Iceberg, views share conversions and indexes, partition collection avoids name-set copies/hashing, and MV unions and duplicate relation listings are deferred/eliminated. Benchmark figures were not independently reproduced.
  • Additional risk sweep: metadata-generation/schema/ref identity, cache-miss fallback, shared-item readers, and resource cleanup yielded no further verified new issue.

The reviewed execution chain is CollectRelation -> snapshot/schema pin -> native or eager partition selection -> scan request, with original selector + captured version -> SQL-cache revalidation. Findings are based on static control-flow evidence; runtime integration and timing were not executed.

Review converged after 2 round(s).

@924060929
924060929 force-pushed the codex/lake-pr-iceberg-versions branch from 12ba5ca to 740783d Compare October 20, 2026 04:56
@924060929

Copy link
Copy Markdown
Contributor Author

run buildall

@924060929

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.

Static review of all 68 changed paths at 740783d found one new P2: CREATE TABLE's separate existence probes still consume cached read handles after an external drop. The inline comment describes the concrete cache-expiry ordering and fix direction. No new P0/P1 finding was confirmed.

The three existing P2 threads were independently revalidated as fixed: heterogeneous LIST keys now omit an incompatible sorted index (4317051884); partition views use a scalable entry-specific estimator through catalog runtimes (4317051894); DROP TABLE IF EXISTS uses a live existence preflight (4321436027). The new finding concerns the separate CREATE consumers, not the repaired DROP path. No additional user focus was supplied.

Critical checkpoint conclusions:

  • Goal and proof: pinned metadata versions, reused partition projections and deferred native planning implement the stated optimization. Tests cover the principal contracts; CREATE's stale-cache case needs coverage.
  • Scope and reuse: changes stay within connector SPI, shared caches and their planning consumers; common selector logic replaces duplicate key construction.
  • Concurrency and locks: immutable projections, generation-fenced invalidation, per-pin memoization and existing table leases preserve active readers. No new lock-order cycle or shared-map traversal defect was substantiated.
  • Lifecycle: frozen read metadata survives refresh through statement-owned leases; cleanup follows split-pump completion. Prepared executions reset their metadata ownership; no static-initialization dependency was introduced.
  • Configuration: the new session boolean defaults to false and is captured per connector session. MV candidates and enabled partition rules retain eager metadata; later partition-rule changes reject unknown counts with a retry.
  • Compatibility: connector API advances from 19.0 to 20.0 with both recorded surfaces and the pinned version test updated. Legacy connectors retain default behavior. No persisted format change was found.
  • Thrift/protobuf: no changed definitions or field IDs.
  • Parallel paths: current, snapshot/time/ref/branch/tag, empty/system tables, LIST/RANGE, native/eager, synchronous/streaming/COUNT, MV/block-rule, legacy freshness and DDL/write routes were traced. CREATE is the identified exception.
  • Conditions and errors: native unknown partitions remain distinct from a prune-to-empty result; snapshot/version errors invalidate result-cache reuse, and deferred-load failures do not publish partial views.
  • Test coverage: supplied unit tests cover immutable selectors, exact versions, schema/rollback/ref changes, weighted admission, invalidation, concurrent loading, native filters, counts and rule/MV fallback. No builds or tests were run, as required by this review contract.
  • Test results: the new regression's expected rows agree with its insert/ref/refresh/recreation sequence and use ordered queries. Output-generation provenance was not verified. That suite disables SQL caching, so it does not itself establish end-to-end cache-hit invalidation.
  • Observability: native EXPLAIN exposes unknown counts; eager counts, contextual errors and existing cache metrics remain available. No additional required instrumentation was identified.
  • Transactions and persistence: the new caches, selectors and projections are transient; no journal/replay or failover protocol change was introduced.
  • Writes and crash behavior: actual DDL uses live catalog operations, while data writes use separate mutable metadata and generation checks. No new atomicity or resource-release defect was confirmed beyond CREATE preflight routing.
  • FE/BE values: no new wire variable; existing predicates, snapshot handles and PCache fields carry the behavior.
  • Performance: native version lookup avoids partition enumeration, versioned views reuse conversion/index work, and partition collection avoids unnecessary copies/hashes. Published benchmark claims were not rerun.
  • Other and final recheck: all candidates and four initial risk focuses have explicit dispositions; the deferred-worker timezone hypothesis lacked a reachable production trigger and was dismissed. Historical cache probes cannot enable an incompatible MV rewrite because selector-bearing scans are rejected by the existing table-query-operator gate.

Review limits: static analysis only; no runtime, benchmark, build or generated-result provenance verification. No unresolved static candidate or uncovered changed path remains. Delivery and convergence are tracked separately; the final convergence result follows the bounded reviewer recheck.

Converged after 2 review rounds: all three reviewers returned NO_NEW_VALUABLE_FINDINGS on the identical frozen comment set; all changed paths, candidates and risk focuses have dispositions. One P2 is delivered inline; there are no confirmed existing P0/P1 blockers.

Review converged after 2 round(s).

@924060929
924060929 force-pushed the codex/lake-pr-iceberg-versions branch from 740783d to b44b384 Compare October 20, 2026 05:58
@924060929

Copy link
Copy Markdown
Contributor Author

run buildall

@924060929

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.

Reviewed all 68 changed paths and their relevant callers at b44b384. No new substantiated findings.

The four existing P2 concerns are fixed on this head: heterogeneous LIST keys skip invalid sorted indexes (4317051884); partition views use an entry-specific scalable estimator propagated into the catalog runtime (4317051894); DROP uses a live existence preflight (4321436027); both CREATE existence probes use the live API (4321658993). These concerns are not reposted. No existing P0/P1 finding requires a blocking comment ID.

Critical checkpoint conclusions:

Checkpoint Conclusion
Goal and supporting tests The code implements generation-specific partition-view reuse, native Iceberg planning and independent SQL-cache dependencies for current/historical/branch/tag references. Added tests target these mechanisms; execution was not independently verified.
Scope, clarity and reuse The changes follow the existing connector SPI, statement-scope and shared-cache infrastructure; selector keys have one implementation. No unrelated production change identified.
Data and version correctness Scalar snapshot, retained schema, partition projection and scan handle describe the same frozen read. SQL-cache identity includes table UUID, snapshot, schema and metadata publication; original selectors remain separate dependencies and revalidate through MVCC resolution. No new visibility defect found.
Concurrency and locks Concurrent scope lookup, memoized complete projection publication, generation-fenced invalidation and existing lease ownership preserve in-flight readers. No new lock-order problem or heavy I/O under a newly added metadata lock found.
Lifecycle and static initialization Statement borrowers survive shared-cache retirement and close after scan work; offline existence probes do not acquire an unowned lease. New selector/version values are immutable. No static initialization change.
Configuration and dynamic behavior enable_iceberg_internal_partition_pruning defaults false and is captured from session properties; later planning observes SET without restart. Existing SQL-cache opt-in remains required.
Compatibility SPI major version and test pin advance to 20.0 and both recorded surfaces reflect the new API. No persisted format change.
Thrift, Protobuf and FE-BE propagation No gensrc definitions, wire fields, plan-node kinds or newly transmitted variables change.
Parallel paths and conditions Reviewed latest/time travel, branch/tag, view/base/system metadata, native/eager, RANGE/LIST, empty/missing, synchronous/streaming/batch and COUNT paths. Native selection remains an unknown universe, retains filters and cannot become a pruned-empty scan.
MV and planner interactions Candidate collection precedes normal pinning; partition-aware MV consumers remain eager. Non-latest references retained after SQL-cache misses are rejected by the actual query MV structural-validity gate. Existing rule stage order and property behavior are preserved.
Error behavior Unreliable cache versions disable caching; revalidation failures invalidate entries. Late partition rules reject unknown counts. CREATE/DROP liveness is separate from cached query handles. No new silent-success path found.
Test coverage Inspected tests for refresh/lease races, deferred loading, weighted admission, mixed specs, selector immutability, cache identity/revalidation, DDL liveness, partition counts and MV bookkeeping, plus the changed regression suites.
Changed expected results The handle-reuse output matches the fixture: latest count/sum 3/3000000003, original branch/tag 2/3, moved branch follows latest, and recreated table contains 42. Output-generation provenance remains unverified.
Observability EXPLAIN identifies unknown native partition counts; contextual metadata errors and existing cache statistics remain available. No additional instrumentation requirement established.
Persistence, transactions, writes and failover No new durable state or transaction protocol; CREATE/DROP retain existing EditLog/invalidation paths. Read pins remain separate from writable metadata. No new crash-related write/ownership issue found.
Performance and memory Certified generation keys and scalar cache versions avoid partition enumeration; maps/indexes share immutable projection objects, fallback content keys remain linear, and per-partition sizing avoids whole-view traversal limits. Reported performance numbers were not reproduced. BE allocation, MoW and delete-bitmap invariants are unaffected.

No additional user-provided focus was specified. Coverage is complete for the static review; no unresolved candidate remains. Two complete-scope reviewers and the additional risk reviewer all returned NO_NEW_VALUABLE_FINDINGS on the unchanged empty finding set. Review converged after 1 round.

Validation limits: static analysis only, as required. No builds, tests or source modifications were performed; runtime behavior, benchmark claims and regression output-generation provenance were not independently validated.

Review converged after 1 round(s).

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 84.13% (371/441) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17624	5281	5372	5281
q2	2150	346	250	250
q3	10188	1394	849	849
q4	4683	460	334	334
q5	7437	794	540	540
q6	187	186	157	157
q7	753	805	610	610
q8	9259	1348	1104	1104
q9	5525	4208	4203	4203
q10	6832	1301	1041	1041
q11	448	252	228	228
q12	639	409	302	302
q13	18134	3677	3023	3023
q14	285	275	253	253
q15	q16	729	721	675	675
q17	1200	748	569	569
q18	7061	6183	6130	6130
q19	1119	959	625	625
q20	398	353	226	226
q21	5510	2492	2497	2492
q22	380	347	265	265
Total cold run time: 100541 ms
Total hot run time: 29157 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5973	5962	6123	5962
q2	718	610	571	571
q3	5606	5731	5259	5259
q4	2059	2156	1605	1605
q5	5694	5912	5554	5554
q6	243	187	143	143
q7	2196	2040	1829	1829
q8	2977	2688	2824	2688
q9	7935	7794	7508	7508
q10	3641	3570	3158	3158
q11	591	420	402	402
q12	666	655	504	504
q13	3312	3609	3022	3022
q14	287	288	261	261
q15	q16	690	675	603	603
q17	7874	7097	6938	6938
q18	13013	12256	12871	12256
q19	980	864	844	844
q20	2144	2150	1954	1954
q21	5695	4675	4777	4675
q22	514	442	395	395
Total cold run time: 72808 ms
Total hot run time: 66131 ms

@hello-stephen

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

query5	4314	607	450	450
query6	447	225	195	195
query7	4812	442	228	228
query8	332	173	149	149
query9	8785	3183	3149	3149
query10	498	311	263	263
query11	5928	3892	3557	3557
query12	147	90	87	87
query13	1267	465	319	319
query14	6117	3896	3770	3770
query14_1	3433	3388	3393	3388
query15	207	197	179	179
query16	938	458	388	388
query17	835	641	519	519
query18	2407	423	295	295
query19	197	165	130	130
query20	86	82	86	82
query21	221	129	105	105
query22	12989	12967	12670	12670
query23	13385	12870	12449	12449
query23_1	12478	12588	12553	12553
query24	7361	845	455	455
query24_1	457	462	471	462
query25	514	396	349	349
query26	1258	253	142	142
query27	2764	432	273	273
query28	4650	1933	1945	1933
query29	1572	583	417	417
query30	292	220	184	184
query31	1021	918	809	809
query32	172	92	89	89
query33	534	305	241	241
query34	933	852	515	515
query35	737	747	644	644
query36	814	828	721	721
query37	134	100	86	86
query38	1764	1773	1643	1643
query39	792	790	743	743
query39_1	727	719	751	719
query40	227	121	98	98
query41	76	70	70	70
query42	91	85	87	85
query43	350	372	313	313
query44	1350	702	725	702
query45	179	183	175	175
query46	823	953	560	560
query47	2782	2758	2750	2750
query48	314	323	225	225
query49	588	410	307	307
query50	685	285	221	221
query51	10635	10625	10653	10625
query52	85	82	72	72
query53	192	213	155	155
query54	262	219	206	206
query55	76	68	64	64
query56	252	225	225	225
query57	1648	1627	1542	1542
query58	302	281	266	266
query59	2082	2166	1928	1928
query60	296	249	237	237
query61	175	164	174	164
query62	396	336	290	290
query63	197	163	166	163
query64	2846	1040	803	803
query65	3357	3286	3279	3279
query66	1785	423	304	304
query67	20061	20291	19821	19821
query68	3042	1007	615	615
query69	371	295	251	251
query70	852	799	784	784
query71	314	230	227	227
query72	2797	2565	2269	2269
query73	521	536	297	297
query74	4647	4479	4349	4349
query75	2346	2297	1887	1887
query76	2314	1004	601	601
query77	342	379	273	273
query78	9751	9813	9206	9206
query79	945	892	511	511
query80	783	416	326	326
query81	562	319	271	271
query82	747	132	109	109
query83	275	197	189	189
query84	312	123	102	102
query85	982	425	372	372
query86	356	237	229	229
query87	2024	1947	1801	1801
query88	3622	2671	2663	2663
query89	337	278	241	241
query90	1701	171	170	170
query91	159	150	129	129
query92	93	91	80	80
query93	998	981	575	575
query94	505	312	283	283
query95	579	330	309	309
query96	671	480	230	230
query97	2461	2429	2318	2318
query98	161	152	154	152
query99	771	777	645	645
Total cold run time: 234538 ms
Total hot run time: 150879 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.09	0.05	0.05
query3	0.26	0.09	0.09
query4	1.60	0.10	0.11
query5	0.24	0.23	0.25
query6	1.17	0.68	0.68
query7	0.04	0.01	0.00
query8	0.06	0.04	0.04
query9	0.40	0.36	0.35
query10	0.59	0.58	0.61
query11	0.19	0.15	0.14
query12	0.18	0.14	0.14
query13	0.49	0.50	0.50
query14	1.06	1.03	1.02
query15	0.65	0.60	0.61
query16	0.35	0.36	0.33
query17	1.13	1.17	1.12
query18	0.21	0.20	0.20
query19	2.06	1.96	2.01
query20	0.02	0.02	0.01
query21	15.44	0.19	0.14
query22	4.95	0.06	0.05
query23	16.28	0.34	0.13
query24	3.00	0.65	0.54
query25	0.12	0.07	0.07
query26	1.20	0.22	0.16
query27	0.04	0.03	0.03
query28	3.84	0.81	0.35
query29	12.51	4.17	3.27
query30	0.28	0.13	0.10
query31	2.77	0.55	0.32
query32	3.22	0.59	0.51
query33	3.17	3.33	3.28
query34	15.44	4.03	3.41
query35	3.34	3.34	3.37
query36	0.55	0.44	0.41
query37	0.09	0.06	0.07
query38	0.05	0.04	0.03
query39	0.04	0.03	0.02
query40	0.18	0.15	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.04
Total cold run time: 97.48 s
Total hot run time: 24.6 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 70.00% (322/460) 🎉
Increment coverage report
Complete coverage report

@924060929
924060929 merged commit c11706e into apache:master Oct 26, 2026
40 checks passed
@924060929
924060929 deleted the codex/lake-pr-iceberg-versions branch October 26, 2026 08:52
@github-actions

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 Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

morningman added a commit that referenced this pull request Nov 2, 2026
…hema (#69488)

### What problem does this PR solve?

Issue Number: None

Related PR: #66538, #57531, #65851

Problem Summary:

**In short.** On an Iceberg table, `DEFAULT(column)` anywhere except
`UPDATE ... SET` and `MERGE ... VALUES` reads the default from the read
schema, which carries no Iceberg write-default. This PR resolves it
against the write schema that the statement pinned for its target, as
branch-4.1 and branch-4.2 do.

**Background.** An Iceberg `write-default` applies to rows written after
it is set. So it lives in the writer schema that a write statement pins
(`IcebergWriteSchemaContext`), not in the columns of the read schema.
#65851 resolves `DEFAULT(column)` against that pinned schema in `UPDATE
... SET`, `MERGE ... UPDATE SET` and `MERGE ... INSERT VALUES`
(`ConnectorWriteSchemaUtils`). Every other `DEFAULT(column)` either
fails at binding or reaches `RewriteDefaultExpression` (#57531), which
reads the default from the bound column. branch-4.1 #66538, also in
branch-4.2, resolves these against the pinned write target too. It
rejects `DEFAULT(column)` on an Iceberg column in a statement that does
not write the table.

**The problem, and what it cost.** Reproduced on a local cluster with a
`file://` format-version 3 table: `CREATE TABLE t (id INT, c INT)`,
`ALTER TABLE t ADD COLUMN d INT DEFAULT 42`, one row `(0, 0)`, so `d =
42`.

| Statement | Master today | After this PR (same as branch-4.2) |
|---|---|---|
| `INSERT INTO t (id, c, d) VALUES (1, 1, DEFAULT(d))` | `Unknown column
'd' in 'table list'` | writes 42 |
| `INSERT INTO t (id, c, d) SELECT 2, 2, DEFAULT(d) FROM t WHERE id = 0`
| writes NULL | writes 42 |
| `INSERT INTO t (id, c, d) SELECT 3, 3, DEFAULT(d)` | `Unknown column
'd'` | writes 42 |
| `UPDATE t SET c = 100 WHERE d = DEFAULT(d)` | updates nothing: `d =
NULL` makes the plan an empty set | updates every row, `d = 42` is
pushed down to the scan |
| `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` | matches nothing |
updates row 0 |
| `SELECT DEFAULT(d) FROM t` | NULL | `DEFAULT(d) must reference a
column of the table that this statement writes` |

The `UPDATE` and `MERGE` cases fail silently: the statement succeeds and
changes nothing.

**How this PR fixes it.**

- fe-core cannot tell from a column whether its connector keeps defaults
in the read schema. Hive does: an HMS default shows up in both schemas.
Iceberg does not. The new connector capability
`SUPPORTS_WRITE_ONLY_DEFAULTS` says which kind a table is. The Iceberg
connector declares it. The Hive connector passes it on to iceberg-on-HMS
tables, like the other per-table capabilities, but only through the
two-argument `getTableSchema`. Since #69299, a statement that pins the
table at its latest snapshot reads the schema from the snapshot
overload, which reflects none of them, so on an iceberg-on-HMS table
such a statement keeps master's results (for example, `UPDATE t SET c =
100 WHERE d = DEFAULT(d)` still changes no row). Reflecting the
sibling's capabilities there changes planning for all of them and is
left to a follow-up PR.
- For a target that declares it, `InsertUtils.normalizePlan` resolves
`DEFAULT(column)` in `INSERT ... VALUES` and in the select lists of
`INSERT ... SELECT`, including the replacements of `SELECT *
REPLACE(...)`, against the pinned write schema. It does this by name,
before the source relations are bound, so the name always refers to a
target column, as in branch-4.2. In MySQL, `DEFAULT(column)` in a
`SELECT` refers to a source column instead. A derived table in `FROM` is
a query of its own and is left alone: `DEFAULT(column)` in it still
names a column of its own `FROM` clause.
- `RewriteDefaultExpression` takes the default for a `DEFAULT(column)`
bound to such a table from the pinned write column with the same field
id. That covers the `WHERE` of `UPDATE` and the `ON` of `MERGE`. A MERGE
resolves it whatever its WHEN branches, also when they all delete, as
branch-4.2 does. If the statement pinned no write schema for that table,
it fails. A `DELETE` pins the writer schema too, for its delete sink,
but it writes no row values, so `RowLevelDmlCommand` marks its target
and `DEFAULT(column)` on it fails with `DEFAULT(d) cannot be used in a
DELETE: the defaults of this table apply only to the rows that a
statement writes`, as in branch-4.2, whose DELETE installs no write
schema context. Master reads NULL there, so `DELETE FROM t WHERE d =
DEFAULT(d)` deletes nothing; resolved to the write-default it would
delete every row with `d = 42`.
- A resolved default is cast to the type of its writer column before the
expression around it is bound, as `RewriteDefaultExpression` already
does for a bound `DEFAULT(column)`. The parsed literal `-1` is a
TINYINT, so without the cast `bit_count(DEFAULT(c))` on an INT column
would count 8 bits instead of 32. The external `UPDATE`, `MERGE` and
changelog paths use the same helper. `RewriteDefaultExpression` rejects
a bound `DEFAULT(column)` whose writer column has another type than the
read column the statement was bound with, which happens only when the
table changed outside Doris after Doris cached it, and asks to refresh
the table: the expressions around it were bound against the read type.
- Hive, Paimon and internal tables do not declare the capability, so
they behave as before.
- The new enum constant changes the connector plugin API surface, so
`connector.plugin.api.version` goes from 26.0 to 27.0, as for the other
recent changes to that surface (#69299, #69588, #69298, #69683 and
#69835 took 22.0 to 26.0).

**Where `DEFAULT(column)` resolves on an Iceberg target (direct Iceberg
catalogs).**

| Statement | Position of `DEFAULT(column)` | Result | branch-4.2 |
|---|---|---|---|
| `INSERT` / `INSERT OVERWRITE` | `VALUES` rows | the target column's
write-default, typed as the column | same |
| | the INSERT's own select lists (with or without `FROM`, set operation
branches, `SELECT * REPLACE`) | the target column's write-default, by
name, before binding | same, except `* REPLACE`, which branch-4.2 binds
to the source |
| | `GROUP BY` / `ROLLUP` outputs, a derived table in `FROM`, `WITH`
definitions, subqueries, `WHERE` / `ON` | bound like any
`DEFAULT(column)` (last row) | same, except derived tables, which
branch-4.2 resolves against the target |
| | a `VALUES` branch of a set operation | fails with an unknown column,
as on master | same |
| | a qualifier other than the target (`DEFAULT(src.c)`,
`DEFAULT(ctl.db.t.c)`) in the positions above | fails: `column in
assignment list is invalid` | same |
| `UPDATE` | `SET` / `WHERE` | write-default | same |
| `MERGE` | `ON` and `WHEN ... AND` conditions, whatever the WHEN
branches, also when they all delete | write-default | same |
| | `UPDATE SET` / `INSERT VALUES` | write-default | same |
| `DELETE` (also `DELETE ... USING`) | anywhere | fails: `cannot be used
in a DELETE` | fails: its DELETE has no write schema context |
| any | a bound `DEFAULT(x.c)` | `x` is the written table: its
write-default by field id; an OLAP or Hive table: that column's default;
another Iceberg table: fails with `must reference a column of the table
that this statement writes` | same |

A bound `DEFAULT(column)` whose writer column has another type than the
read column fails and asks to refresh the table. Where `DEFAULT(column)`
is bound rather than resolved by name (the third row), it keeps its
meaning from before this PR and names a column of its own `FROM` clause,
as on master and branch-4.2: for example in `INSERT INTO t SELECT id,
DEFAULT(d) FROM src GROUP BY id, d` with an OLAP or Hive `src`, it takes
the default of `src.d`.

**Results.**

- Local cluster, with the table above: the table shows what each
statement did before and after. `EXPLAIN UPDATE t SET c = 5 WHERE d =
DEFAULT(d)` goes from an empty set to a scan with `d = 42` pushed down
to Iceberg.
- Unit tests: 6 new tests in `InsertUtilsTest`, 6 in the new
`RewriteDefaultExpressionTest` and 1 in `RowLevelDmlCommandTest`. They
cover VALUES, and INSERT ... SELECT both from a staging relation whose
own column is also `name` and without FROM: each resolves to the pinned
default, and an unknown column fails. `DEFAULT(column)` inside a derived
table, qualified or not and also under a UNION branch, is left to
binding. A `SELECT * REPLACE(DEFAULT(name) AS name)` replacement
resolves like the rest of the select list and keeps the star's qualifier
and `EXCEPT` list. The pinned write column wins over the read column. A
statement that writes nothing fails. A table without the capability is
unchanged. With master's `InsertUtils` and `RewriteDefaultExpression`,
the 5 tests of the fix fail and the 2 tests of the unchanged path pass.
The `SELECT * REPLACE` test fails with this PR's previous `InsertUtils`,
where the replacement stays `DEFAULT(name)`. `IcebergConnectorTest`,
`HiveConnectorMetadataSiblingDelegationTest` and
`ConnectorPluginSurfaceTest` pass. So do the fe-core tests around the
write schema: `ConnectorWriteSchemaUtilsTest`,
`ExternalRowLevelUpdatePlanBuilderTest`,
`ExternalRowLevelMergePlanBuilderTest`,
`ChangelogRowLevelDmlTransformTest` and `PluginDrivenExternalTableTest`.
Two of the tests pin the typing: `bit_count(DEFAULT(bits))` gets the
default as an INT in VALUES and in SELECT, and fails without the cast; a
BIGINT writer column over an INT read column is rejected, directly and
under an IF bound to INT. `DEFAULT(column)` on the target of a DELETE
fails even with a pinned write-default, and only a DELETE marks its
target (an UPDATE does not); skipping the check, or marking UPDATE
instead of DELETE, makes exactly these two tests fail.
`PositionDeleteRowLevelDmlTransformTest`,
`PhysicalPlanTranslatorIcebergRowLevelDmlTest`, `DeleteFromCommandTest`
and `UpdateCommandTest` pass.
- Regression: `test_iceberg_initial_defaults` now writes rows 20 to 22
with `DEFAULT(default_int)` in VALUES and in INSERT ... SELECT with and
without FROM. It then updates them with `WHERE default_int =
DEFAULT(default_int)` and `MERGE ... ON ... = DEFAULT(...)`, for Parquet
and ORC, and checks the rows in the `.out`. A plain `SELECT
DEFAULT(default_int)` must fail, and so must a `DELETE ... WHERE
default_int = DEFAULT(default_int)` on rows 20 to 22, which hold the
write-default. The suite passed locally before the DELETE block was
added, and the whole suite, DELETE block included, has since passed in
two External regression runs on CI. With master's FE it fails at the
first of these statements: `Unknown column 'default_int' in 'table
list'`.
924060929 added a commit to 924060929/incubator-doris that referenced this pull request Nov 2, 2026
### What problem does this PR solve?

Issue Number: None

Related PR: apache#69299

Problem Summary: The snapshot-id test passed a null partition-item map because it only exercised partition aliases. Iceberg partition projections now copy their maps before cache publication, so this invalid fixture fails before reaching its snapshot assertions. Supply an empty map and retain all snapshot-selection assertions. No production behavior changes.

### Release note

None

### Check List (For Author)

- Test: Unit Test: reproduced the original IcebergPartitionInfoTest failure, then passed its 2 tests and all 25 FrontendServiceImplTest tests; full build.sh --fe package build and Checkstyle passed. The remote authorization-test failure was not reproduced locally.
- Behavior changed: No
- Does this need documentation: No
924060929 added a commit to 924060929/incubator-doris that referenced this pull request Nov 2, 2026
### What problem does this PR solve?

Issue Number: None

Related PR: apache#69299

Problem Summary: Moving Iceberg partition views to a separate cache caused an MV refresh to enumerate the complete view once per requested partition whenever caching was disabled or weight admission rejected the view. Table-generation replacement also left obsolete partition views behind, and a previously pinned projection could republish an old view after retirement. Load one view for the native/HMS Iceberg bulk MV operation, share the existing version fallback, and preserve per-partition failures. Retire obsolete physical-generation entries during replacement and prevent stale lazy loads from republishing them. Same-generation reuse and cache accounting remain intact.

### Release note

Reduce repeated Iceberg metadata loading during materialized-view refresh and retire obsolete partition-cache views after table metadata refresh.

### Check List (For Author)

- Test: Unit Test
    - Reproduced all four lifecycle/load-count regressions before the fix.
    - All 197 related FE tests pass, including native/HMS bulk reads, disabled/rejected cache admission, replacement and late loads, version fallback and per-partition errors.
    - Full FE packaging and Checkstyle pass through build.sh --fe -j4 (UI build disabled).
- Behavior changed: Yes (reuse operation-local partition views and retire obsolete cached generations)
- Does this need documentation: No
924060929 added a commit to 924060929/incubator-doris that referenced this pull request Nov 2, 2026
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#69299

Problem Summary: Iceberg partition views are loaded lazily from a retained table generation. Catalog reset can remove its cache group before projection creation or the first partition read. Looking up current partition and schema entries then fails pinned work despite its live table lease. Capture only an existing shared partition entry when applicable and derive partition columns from the same retained table/schema under captured authentication. Session-catalog and retired-group paths load directly without resolving another catalog generation, while existing cache admission and retirement fences remain in place.

### Release note

Keep retained Iceberg partition metadata usable after catalog cache reset.

### Check List (For Author)

- Test: Unit Test: 204 related FE tests; full FE packaging and Checkstyle via build.sh --fe
- Behavior changed: Yes; retained partition projections can finish lazy metadata loading after catalog reset
- Does this need documentation: No
924060929 added a commit to 924060929/incubator-doris that referenced this pull request Nov 2, 2026
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#69299

Problem Summary: Repeated Iceberg dictionary freshness checks detach the latest snapshot by eagerly loading its partition view. When the partition entry is disabled, rejected by its weight limit or retired, three checks of the same immutable generation invoke the complete view loader three times. Use the existing scoped snapshot callback and retain only the exact maximum partition-update timestamp in that generation. A synchronized lazy getter computes it once, including an empty view's zero result; a new metadata generation computes its own value. Reserve the fixed scalar/flag fields in snapshot accounting and keep complete partition maps subject to the existing admission policy. Focused tests reduce three loader invocations to one without retaining an unadmitted full view; this is a loader-count result, not a latency benchmark.

### Release note

Avoid repeated partition metadata enumeration during Iceberg dictionary freshness checks.

### Check List (For Author)

- Test: Unit Test: 207 related FE tests; full FE packaging and Checkstyle via build.sh --fe
- Behavior changed: Yes; reuse the same freshness value within one immutable metadata generation without retaining full partition views
- Does this need documentation: No
924060929 added a commit to 924060929/incubator-doris that referenced this pull request Nov 2, 2026
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#69299, apache#69983

Problem Summary: Reset-safe lazy partition metadata loading parses a retained Iceberg table directly instead of using the live schema cache. It therefore bypassed SchemaCacheValue.validateSchema(), allowing case-insensitive duplicate columns into partition-range and MV/dictionary snapshot derivation even though normal schema loading rejects them. Reuse the same pure validator before deriving partition columns, inside the captured authenticator and existing table/snapshot AnalysisException wrapper. Preserve the retained table/schema generation and avoid a live catalog/cache lookup after reset.

### Release note

Reject case-insensitive duplicate Iceberg columns consistently when deriving lazy partition metadata for materialized views and dictionaries.

### Check List (For Author)

- Test: Unit tests and packaging
    - Two real Iceberg metadata tests reproduced the missing rejection for active and retired catalog projections before repair.
    - All 173 related schema, snapshot, partition and cache FE tests passed, including existing reset/session success paths.
    - Full FE packaging and Checkstyle passed.
    - git diff --check passed; no local full P0 or external-service regression in this cycle.
- Behavior changed: Yes. Lazy retained partition loads reject ambiguous schemas consistently and keep table/snapshot error context. Snapshot identity, cache admission and generation-retirement semantics remain unchanged.
- Does this need documentation: No
yiguolei pushed a commit that referenced this pull request Nov 3, 2026
morningman added a commit that referenced this pull request Nov 3, 2026
…t a snapshot (#69999)

### 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.
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. dev/4.2.0-merged reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants