morningman opened a new pull request, #69488:
URL: https://github.com/apache/doris/pull/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.
   - For a target that declares it, `InsertUtils.normalizePlan` resolves 
`DEFAULT(column)` in `INSERT ... VALUES` and in the select lists of `INSERT ... 
SELECT` 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.
   - `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`. If the statement pinned 
no write schema for that table, it fails.
   - 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 19.0 to 20.0, as for the other recent 
changes to that surface.
   
   **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: 3 new tests in `InsertUtilsTest` and 3 in the new 
`RewriteDefaultExpressionTest`. 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. 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 4 tests of the fix fail and the 2 tests of the 
unchanged path pass. `IcebergConnectorTest`, 
`HiveConnectorMetadataSiblingDelegationTest` and `ConnectorPluginSurfaceTest` 
pass. So do the fe-core tests around the write schema: 
`ConnectorWriteSchemaUtilsTest`, `ExternalRowLevelUpdatePlanBuilderTest`, 
`ExternalRowLevelMergePlanBuilderTest`, `ChangelogRowLevelDmlTransformTest` and 
`PluginDrivenExternalTableTest`.
   - 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. The suite passes 
locally. With master's FE it fails at the first of these statements: `Unknown 
column 'default_int' in 'table list'`.
   
   ### Release note
   
   Fix: On Iceberg tables, `DEFAULT(column)` in `INSERT ... VALUES`, `INSERT 
... SELECT` and the `WHERE` / `ON` conditions of `UPDATE` and `MERGE` now 
resolves to the column's write-default. Before, it failed, wrote NULL, or 
matched no row. `SELECT DEFAULT(column)` on an Iceberg table now fails instead 
of returning NULL.
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [x] Regression test
       - [x] Unit Test
       - [x] Manual test (add detailed scripts or steps below)
         On a local cluster with a `file://` Iceberg catalog: `CREATE TABLE t 
(id INT, c INT) PROPERTIES ('format-version'='3')`, `ALTER TABLE t ADD COLUMN d 
INT DEFAULT 42`, then the statements in the table above.
       - [ ] No need to test or manual test. Explain why:
           - [ ] This is a refactor/code format and no logic has been changed.
           - [ ] Previous test can cover this change.
           - [ ] No code files have been changed.
           - [ ] Other reason <!-- Add your reason?  -->
   
   - Behavior changed:
       - [ ] No.
       - [x] Yes. <!-- Explain the behavior change -->
         See the release note. `INSERT ... SELECT DEFAULT(column)` into an 
Iceberg table names a target column, even when the source has a column of that 
name.
   
   - Does this need documentation?
       - [x] No.
       - [ ] Yes. <!-- Add document PR link here. eg: 
https://github.com/apache/doris-website/pull/1214 -->
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label <!-- Add branch pick label that this PR should 
merge into -->
   


-- 
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]

Reply via email to