morningman opened a new pull request, #69229:
URL: https://github.com/apache/doris/pull/69229

   ### What problem does this PR solve?
   
   Issue Number: None
   
   Related PR: #68481, #68124
   
   Problem Summary:
   
   **In short.** Ports the rest of branch-4.1 #68481 to master. When the source 
column of a historical Iceberg partition field has been dropped, DELETE and 
UPDATE on master commit a delete file under partition NULL, after which Doris 
can no longer read the table. They now fail with a clear error before anything 
is written, ordinary reads keep working, and TIME partition values can be 
parsed back at commit.
   
   **Background.** #68481 fixed Iceberg row-level DML after a table evolves to 
an unpartitioned spec. Its main part, keeping each file's own spec ID and 
partition tuple in the scan range once any spec is partitioned, reached master 
with #68124; the regression added here confirms that REPLACE to an 
unpartitioned table and dropping the last partition field work on master. Row 
IDs carry each file's `partition_data_json`, and BE writes the position-delete 
file of a DELETE, UPDATE or MERGE under that partition.
   
   **The problem, and what it cost.**
   
   - Dropping the source column of a historical partition field leaves the 
field with no type, so the file's partition tuple cannot be serialized. 
`IcebergPartitionUtils.getPartitionValues` swallowed the 
`UnsupportedOperationException` and wrote a null entry. A DELETE on such a 
table then committed a delete file under partition NULL for a data file in 
partition 7. Spark still reads the table, but every later Doris query of it 
fails:
   
     `Mismatched partition tuples (PartitionData{partition_key=null}, 
PartitionData{partition_key=7}) for delete file s3://.../delete_pos_....parquet 
and data file s3a://.../partition_key=7/00000-....parquet`
   
   - `parsePartitionValueFromString` had no TIME branch, so the value 
`serializePartitionValue` renders for a TIME partition could not be parsed back 
when the delete file is committed (`convertToPartitionData`).
   
   **How this PR fixes it.**
   
   - `getPartitionValues` throws instead of returning a null entry.
   - When a data file's partition tuple cannot be serialized, its range carries 
no `partition_data_json` and is marked. Ordinary reads do not use the payload, 
so they are unaffected.
   - A scan that produces row IDs over such a range is rejected: `Cannot 
produce Iceberg row IDs with unsupported partition types in spec N. Rewrite 
historical data files before DELETE or UPDATE.` The check runs on every 
`planScan` call, including one served by the statement scan reuse (its key 
leaves out the projection, as for the metadata-column reader check), and on 
each range of a streaming split source.
   - `getColumnHandles` returns a handle for the hidden row-id column, as it 
does for `_file` and `_pos`, so a DML scan request carries it. Before, the 
column had no handle and never reached the connector. The field-id schema 
dictionary leaves it out, since it is not a table column.
   - `parsePartitionValueFromString` parses TIME (microseconds since midnight).
   
   Branch-4.x renders `partition_data_json` only for scans that produce row 
IDs. Master keeps rendering it for every scan, because 
`specId|partitionDataJson` is the partition identity behind EXPLAIN 
`partition=N/M` and `sql_block_rule` `partition_num`.
   
   **Results.** Local cluster with the Iceberg docker environment (tables 
written by Spark):
   
   | Case | Before | After |
   |---|---|---|
   | Spark REPLACE a partitioned table with an unpartitioned one, then two 
Doris DELETEs | Works (#68124) | Same |
   | Drop the last partition field, then DELETE and UPDATE across both specs | 
Works | Same |
   | Drop the partition field, then its source column; Doris DELETE (Parquet 
and ORC) | Commits a delete file under partition NULL; Doris reads of the table 
then fail with `Mismatched partition tuples` | Rejected with the error above; 
no delete file, rows unchanged in Doris and Spark |
   | Same table, Doris UPDATE | Same as DELETE | Rejected |
   | DELETE on historical binary partitions; DELETE and UPDATE on pre-1970 
timestamp partitions | Works | Same |
   
   Original author: @Gabriel39 (#68481)
   
   ### Release note
   
   Iceberg DELETE, UPDATE and MERGE now fail with a clear error, instead of 
writing delete files that make the table unreadable from Doris, when a data 
file belongs to a partition field whose source column was dropped.
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [x] Regression test
       - [x] Unit Test
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] 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?  -->
       - `IcebergScanPlanProviderTest`: an ordinary read of such a file and the 
rejection of row-ID scans through `planScan`, statement reuse and 
`streamSplits`; the row-id handle stays out of the field-id dictionary. 
`IcebergPartitionUtilsTest`: `getPartitionDataJson` throws for a field without 
a type, and TIME parses back what serialize renders. 
`IcebergConnectorMetadataTest`: the row-id handle. Mutations each turn the 
intended tests red: restoring the null fallback (4 tests), skipping the check 
for reused ranges, skipping it in the streaming source, dropping the TIME 
branch, and keeping the row-id name in the dictionary. All 1592 
fe-connector-iceberg tests pass.
       - Regression: `test_iceberg_delete_unpartitioned_evolution` (ported from 
branch-4.x) passes. Of the 33 Iceberg suites that run DELETE / UPDATE / MERGE, 
31 pass. The other two fail the same way without this change: 
`test_iceberg_write_binary_partitions` needs a BE newer than my local one (exec 
version 19), and `test_iceberg_deletion_vector` hits `Cannot obtain metadata` 
in the Trino container.
   
   - Behavior changed:
       - [ ] No.
       - [x] Yes. Row-level DML over data files of a partition field whose 
source column was dropped is rejected instead of writing a NULL delete 
partition.
   
   - 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