morningman opened a new pull request, #68874:
URL: https://github.com/apache/doris/pull/68874
### What problem does this PR solve?
Issue Number: None
Related PR: #65868
Problem Summary:
**In short.** This ports the static-partition part of branch-4.1 #65868 to
the Paimon connector. An `INSERT OVERWRITE ... PARTITION (...)` named the
Paimon partition by the literal as the user wrote it, while the rows carry that
literal cast to the column type. Overwrites whose literal is not spelled the
column type's way failed, and overwrites of a TIMESTAMP WITH LOCAL TIME ZONE
partition failed whenever the session zone differed from the FE JVM zone; when
no rows were written to trip Paimon's check, the overwrite silently deleted the
wrong partition. Connectors now get the cast values through a new write-handle
getter, and Paimon uses them.
**Background.** On a Paimon table, the bind step materializes the literal of
`PARTITION (col = <literal>)` into every row with `castIfNotSameType`, so the
rows carry the value in the column's type. The static partition spec handed to
connectors is built separately: `PluginDrivenInsertCommandContext` keeps
`Literal.getStringValue()` of the literal as written, and `PaimonWriteBinding`
passed that string to Paimon's `withOverwrite`. Paimon parses the string with
the column type to find the partition to replace, parses an LTZ value as local
time in the FE JVM zone, and reads the table's `partition.default-name` as the
NULL partition. branch-4.1 #65868 casts each literal to its column type first,
moves an LTZ value from the session zone to the JVM zone, and rejects a value
equal to `partition.default-name`; master has none of this.
**The problem, and what it cost.** On a local FE + BE with a filesystem
Paimon catalog, session zone Asia/Shanghai, FE JVM zone Europe/London:
| Statement | master |
|---|---|
| `INSERT OVERWRITE t PARTITION (p = true) VALUES (...)`, `p INT` | fails:
`NumberFormatException: For input string: "true"` |
| `... PARTITION (dt = 20240101) ...`, `dt DATE` | fails: `the changes in
dt=19723/ does not belong to this partition` (Paimon reads 20240101 as a day
number) |
| `... PARTITION (ts = '2024-01-15 08:30:45') ...`, LTZ `ts` | fails: `the
changes in ts=2024-01-15T00%3A30%3A45/ does not belong to this partition` |
| the same LTZ overwrite with no rows (`SELECT ... LIMIT 0`) | succeeds,
deletes the partition `2024-01-15 16:30:45` (session time) and keeps `08:30:45`
|
| an overwrite with no rows of `region = '<partition.default-name>'` |
succeeds, deletes the NULL partition |
| `INSERT INTO t PARTITION (dt = '2024-13-45') ...` | succeeds, writes `dt =
NULL` |
The two empty overwrites are silent data loss: the statement reports success
and the deleted partition is not the one the user named.
**How this PR fixes it.**
- `ConnectorWriteHandle#getCastStaticPartitionSpec` (new): the static
partition values cast to the types of their columns, keyed like
`getStaticPartitionSpec`, which still carries the literals as written. SQL NULL
keys stay in `getStaticPartitionNullKeys` only; a key that names no column (an
Iceberg partition field) keeps its literal. The default, for a handle that does
not cast, is the spec as written without its null keys.
- fe-core: `InsertIntoTableCommand` records the sink's bound target schema
on `PluginDrivenInsertCommandContext`, and the write handle computes the cast
values on demand with `checkedCastTo`, the cast constant folding applies to the
row value. Only a connector that asks pays for the cast and can fail on it, so
Iceberg, MaxCompute and Hive, which keep reading the spec as written, see no
change. A value that does not fit its column type fails the statement instead
of becoming NULL, as on branch-4.x.
- Paimon: `PaimonWriteBinding` resolves the static partition from the cast
values. An LTZ value is moved to the FE JVM zone and written with a space
between date and time, which Paimon's parser needs: a DATETIMEV2 value from the
session zone, and a TIMESTAMPTZ value (`enable.mapping.timestamp_tz`) from its
own UTC offset. An overwrite value equal to `partition.default-name` is
rejected. Rejecting LTZ values that fall into a DST overlap of the JVM zone,
which branch-4.2 adds with its TIMESTAMPTZ mapping work, is left to that port.
**Results.** The table above, with this PR:
| Statement | this PR |
|---|---|
| `PARTITION (p = true)` | replaces `p = 1` |
| `PARTITION (dt = 20240101)` | replaces `dt = 2024-01-01` |
| LTZ overwrite, DATETIMEV2 or TIMESTAMPTZ mapping | replaces the named
partition |
| LTZ overwrite with no rows | deletes the named partition only |
| `region = '<partition.default-name>'`, with or without rows | fails: `...
cannot be represented in a static overwrite`, no data changes |
| `INSERT INTO ... PARTITION (dt = '2024-13-45')` | fails: `date/datetime
literal [2024-13-45T00:00:00] is invalid` |
Original author: @suxiaogang223
### Release note
Fix INSERT OVERWRITE ... PARTITION on Paimon tables failing, or replacing
the wrong partition, when a static partition value is not spelled in its column
type (`p = true` on an INT column, `dt = 20240101` on a DATE column) or the
column is TIMESTAMP WITH LOCAL TIME ZONE. A static partition value equal to the
table's `partition.default-name`, or one that is not a valid value of its
column type, is now rejected.
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [x] Regression test
- `test_paimon_write_transaction` gains the INT, DATE, LTZ and
`partition.default-name` cases. It needs the Paimon docker environment and was
not run locally; the same statements on a local filesystem Paimon catalog give
the asserted rows with this PR and fail, or delete the wrong partition, on
master.
- [x] Unit Test
- `PaimonWriteBindingTest` ports the branch-4.x cases for the cast
value, the `partition.default-name` rejection and the LTZ zone move, and adds
the TIMESTAMPTZ mapping and SQL NULL. `PluginDrivenTableSinkBindingTest` checks
the cast values on the write handle, the session-zone forms of DATETIMEV2 and
TIMESTAMPTZ values, and that an invalid value fails only when cast.
`ConnectorWriteHandleTest` pins the SPI default, and the plugin surface
baseline is refreshed. Reading the spec as written, dropping the default-name
check, skipping either zone move, or skipping the cast in fe-core makes them
fail.
- The fe-connector-spi suite (147), the Paimon connector tests (683)
and `InsertIntoTableCommandTest` / `InsertOverwriteTableCommandTest` pass.
- [x] Manual test (add detailed scripts or steps below)
- The before/after tables 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. Paimon static partition values are matched to the rows by
their cast value; an invalid static partition value, or one equal to
`partition.default-name` in an overwrite, is rejected.
- 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]