morningman opened a new pull request, #69999:
URL: https://github.com/apache/doris/pull/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.
### Release note
Fix Iceberg tables read through an HMS catalog losing Top-N lazy
materialization and nested column pruning, and `DEFAULT(column)` in `UPDATE` /
`MERGE` conditions on them reading NULL instead of the column's write-default.
### Check List (For Author)
- Test: Unit Test / Regression test / Manual test
- FE UT: in `HiveConnectorMetadataSiblingDelegationTest` the per-table
capability cases now check both `getTableSchema` overloads, and a new case
checks that the snapshot overload forwards the pin unchanged and resolves the
owner once. All 49 fe-connector-hive test classes pass (502 tests);
fe-connector-hive Checkstyle 0 violations. Three mutations (the snapshot
overload without the reflection, asking the sibling for its latest schema,
resolving the owner a second time) each fail only their target cases.
- Regression: new `test_iceberg_on_hms_gateway_pinned_capabilities`
(hive3 docker) runs the probe above through both catalogs; its `.out` holds
both results under paired tags. I ran it locally against a standalone Hive
3.1.2 metastore with a `file://` warehouse, with the catalog properties
adapted, not against the hive3 docker environment: without this change it fails
on the HMS catalog's first Top-N check, with it it passes.
- Manual: the table above, on a local single-BE cluster.
- Behavior changed: Yes. Statements on Iceberg tables read through an HMS
catalog plan with the capabilities they have through an Iceberg catalog, and
`DEFAULT(column)` on them resolves as it does there.
- Does this need documentation: No
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]