Repository navigation
[improvement](datasource) Version Iceberg partition views and SQL cache metadata - #69299
Conversation
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
|
/review |
There was a problem hiding this comment.
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:
- 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.
- Scope and clarity: changes stay within FE connector, MVCC, planner and result-cache plumbing; the binlog test change is a mechanical dependency-key adaptation.
- 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.
- 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.
- 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.
- 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.
- Thrift/protobuf: no definitions or field IDs changed.
- 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.
- 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.
- 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.
- 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.
- 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.
- Transactions and persistence: no EditLog, replay, commit or failover protocol changes occur.
- Writes and crash atomicity: no data-write or transaction-atomicity logic changed; existing DDL paths continue to use writable metadata.
- FE/BE variables: no transmitted field or BE protocol addition; the new session preference affects FE planning.
- 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.
- 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).
TPC-H: Total hot run time: 28653 ms |
TPC-DS: Total hot run time: 151208 ms |
ClickBench: Total hot run time: 24.01 s |
a45d9bf to
12ba5ca
Compare
|
run buildall |
|
/review |
|
CI failure triage for the rebased head:
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. |
There was a problem hiding this comment.
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_pruningdefaults 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).
12ba5ca to
740783d
Compare
|
run buildall |
|
/review |
There was a problem hiding this comment.
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).
740783d to
b44b384
Compare
|
run buildall |
|
/review |
There was a problem hiding this comment.
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).
FE UT Coverage ReportIncrement line coverage |
TPC-H: Total hot run time: 29157 ms |
TPC-DS: Total hot run time: 150879 ms |
ClickBench: Total hot run time: 24.6 s |
FE Regression Coverage ReportIncrement line coverage |
|
PR approved by anyone and no changes requested. |
|
PR approved by at least one committer and no changes requested. |
…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'`.
### 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
### 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
### 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
### 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
### 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
…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.
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 fullbuild.sh --fe -j4with 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.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.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