morningman commented on code in PR #69083:
URL: https://github.com/apache/doris/pull/69083#discussion_r4275700626


##########
fe/be-java-extensions/paimon-scanner/src/main/java/org/apache/doris/paimon/PaimonWriteSchema.java:
##########
@@ -163,13 +214,135 @@ GenericRow tableRow(InternalRow columnValues) {
                 row.setRowKind(toRowKind(value));
                 continue;
             }
+            // Only an inserted row is padded; its row kind, if any, is the 
first input column. An UPDATE or
+            // DELETE row carries the key Doris read back from the table, 
which has to stay byte-equal to the
+            // stored key for Paimon to match it, padded or not.
+            if (charPadders[i] != null && value != null && row.getRowKind() == 
RowKind.INSERT) {
+                value = charPadders[i].pad(value);

Review Comment:
   Fixed in 45e6e5d2eff. This reproduced on a local cluster. Into an empty 
table keyed by `c CHAR(3)`, `MERGE INTO t USING s ON t.c = s.c WHEN NOT MATCHED 
THEN INSERT (c, v) VALUES (s.c, s.v)` with the source rows `('k', 1)` and `('k 
', 2)` committed and left the single row `'k  '`. With a row `'k  '` that a 
padded INSERT had written, a NOT MATCHED-only MERGE of `('k', 9)` replaced it, 
while a MATCHED-only MERGE of the same source changed nothing.
   
   MERGE matches its source rows, and checks the keys it inserts for 
duplicates, by the key as Doris holds it, unpadded, and the writer padded the 
keys of the rows MERGE inserts afterwards. Normalizing the keys during planning 
would mean comparing CHAR keys padded in ON, the read-side change this PR 
leaves out: Doris compares CHAR values as they are, as it does for the data 
Spark writes. So `PaimonWriteSchema` now fails a row that a MERGE inserts when 
one of its CHAR primary-key values is shorter than the column, which is the 
only case where padding changes a key, and `PaimonJniWriter` passes it the 
table's primary keys. A full-length key is written as it is, so MERGE's 
matching and its duplicate-insert check see the stored key. A MERGE can pad the 
key itself, `ON t.c = rpad(s.c, 3, ' ') ... INSERT (c, v) VALUES (rpad(s.c, 3, 
' '), s.v)`: that matches the padded row, and the duplicate-insert check then 
rejects `'m'` and `'m '`. With `enable_paimon_write_char_padding = false`, 
MERGE w
 rites the keys unpadded, as before.
   
   Tests:
   - `PaimonWriteSchemaTest.testMergeInsertOfAShortCharKeyFails` (`'k'`, `'k '` 
and `'中中'` into a CHAR(3) key), `testMergeInsertPadsTheRestOfAFullLengthKeyRow` 
(a full-length key, `'中中中'` included, and a longer one are written as they are, 
and the row's other CHAR column is padded), and 
`testShortCharKeysThatMergeDoesNotInsertAreWrittenAsBefore` (UPDATE and DELETE 
rows, padding off, and a plain INSERT). Removing the check, counting bytes, or 
checking UPDATE and DELETE rows too turns them red.
   - `test_paimon_write_char_padding` now runs a MERGE from a Doris table: the 
short key fails and nothing is written; the `rpad` MERGE inserts `'k  '` and, 
run again, matches it; the `rpad` MERGE of `'m'` and `'m '` fails the 
duplicate-insert check; and with padding off MERGE writes `'m'` and `'m '` as 
two rows. With the jars from before this fix the suite fails at the MERGE that 
must fail.
   



##########
fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonWriteBinding.java:
##########
@@ -126,10 +153,13 @@ static Map<String, String> 
resolveStaticPartition(FileStoreTable table, Connecto
             }
             String value = Objects.requireNonNull(castValues.get(key),
                     () -> "missing the cast value of static partition column " 
+ key);
-            if (table.rowType().getField(canonicalName).type().getTypeRoot()
-                    == DataTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
+            DataType type = table.rowType().getField(canonicalName).type();
+            if (type.getTypeRoot() == 
DataTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE) {
                 value = toSdkLocalTime(canonicalName, value);
             }
+            if (charPadding && type.getTypeRoot() == DataTypeRoot.CHAR) {
+                value = padChar(value, ((CharType) type).getLength());

Review Comment:
   Fixed in 45e6e5d2eff. This reproduced too: in a table `(id INT, rk CHAR(3))` 
keyed by `(id, rk)`, partitioned by `rk`, with `'rowkind.field' = 'rk'` and two 
`'+I'` rows, `INSERT OVERWRITE TABLE t PARTITION (rk = '+I') SELECT 3` failed 
with `Trying to overwrite partition {rk=+I }, but the changes in rk=+I/ does 
not belong to this partition`, and an overwrite whose source returned no rows 
at run time returned OK and left both rows. `resolveStaticPartition` now leaves 
the `rowkind.field` value unpadded, as the writer does (both compare the name 
case-sensitively), so the first overwrite replaces the partition with the row 
of id 3 and the empty one clears it.
   
   A source the planner already proves empty, such as `SELECT 3 LIMIT 0`, 
behaves differently on this table, and not because of padding: with padding on 
the table is written by one writer, the sink then sits directly on the empty 
set, and fe-core skips the write altogether, as it does for any connector table 
with such a plan (a dynamic-bucket primary-key table, for one). The regression 
therefore uses a source that is empty at run time.
   
   Tests: 
`PaimonWriteBindingTest.staticRowKindFieldPartitionIsLeftUnpaddedAsTheRowsAre` 
(the `rowkind.field` partition value stays `'+I'` while another CHAR partition 
value is padded), and `test_paimon_write_char_padding`, which overwrites the 
`'+I'` partition of such a table with one row and then with a source that is 
empty at run time.
   



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