github-actions[bot] commented on code in PR #69295:
URL: https://github.com/apache/doris/pull/69295#discussion_r4315766354


##########
fe/be-java-extensions/paimon-scanner/src/main/java/org/apache/doris/paimon/PaimonWriteSchema.java:
##########
@@ -210,8 +208,7 @@ static PaimonWriteSchema create(RowType tableType, String[] 
columnNames, boolean
             // Paimon writer return its normal constraint diagnostic instead 
of reading a default
             // primitive value from a non-null getter.
             inputFields.add(field.newType(field.type().nullable()));
-            if (charPadding && !field.name().equals(rowKindField)
-                    && (!orcFiles || partitionKeys.contains(field.name()))) {
+            if (charPadding && !field.name().equals(rowKindField)) {

Review Comment:
   [P1] Normalize ORC keys in the global-index bootstrap too
   
   Padding incoming ORC CHAR keys also changes the keys passed to 
`GlobalIndexAssigner.processInput`, but 
`PaimonJniWriter.bootstrapGlobalIndexKey` still indexes the unpadded keys 
returned by Paimon's 
[IndexBootstrap](https://github.com/apache/paimon/blob/release-1.4.2/paimon-core/src/main/java/org/apache/paimon/crosspartition/IndexBootstrap.java#L66-L115).
 On a fresh ORC table partitioned by `pt`, with `c CHAR(3)` as the primary key 
and `bucket=-1` (KEY_DYNAMIC), execute two statements inserting `(pt='p1', 
c='k')` and then `(pt='p2', c='k')`. The second writer bootstraps `'k'`, looks 
up the newly padded `'k  '`, and takes `processNewRecord` instead of 
`ExistingProcessor`, leaving the old row in `p1` and inserting another in `p2`. 
This affects rows written by this version too. Normalize the bootstrap keys 
consistently with incoming keys and cover a cross-partition CHAR upsert in a 
second writer session.



##########
fe/be-java-extensions/paimon-scanner/src/main/java/org/apache/doris/paimon/PaimonWriteSchema.java:
##########
@@ -327,24 +334,49 @@ private void padRow(GenericRow row) {
             if (value == null) {
                 continue;
             }
-            // In a changelog write only MERGE inserts rows, and MERGE has 
matched them to the table and checked them
-            // for duplicate keys by the key as it is: Doris compares CHAR 
values unpadded. Padding a shorter key
-            // here would make 'k' and 'k ' one key, or let a NOT MATCHED row 
replace the stored 'k  ' it did not
-            // match. A key as long as the column is not changed by padding.
-            if (insertedKeyCharLengths[i] > 0 && ((BinaryString) 
value).numChars() < insertedKeyCharLengths[i]) {
-                throw new IllegalArgumentException(String.format(
-                        "MERGE cannot insert '%s' into the CHAR(%d) primary 
key column '%s' of a Paimon table: "
-                                + "the value is shorter than the column, and 
padding it to the column "
-                                + "length would change the key that MERGE 
matched. Pad the key in the "
-                                + "MERGE, with rpad(<key>, %d, ' ') in both 
the ON condition and the "
-                                + "inserted values, or set 
enable_paimon_write_char_padding = false",
-                        value, insertedKeyCharLengths[i], 
inputType.getFields().get(i).name(),
-                        insertedKeyCharLengths[i]));
+            if (insertedKeyCharLengths[i] > 0 && row.getRowKind() == 
RowKind.INSERT) {
+                checkInsertedKey((BinaryString) value, i);
             }
             row.setField(tableFieldIndexes[i], charPadders[i].pad(value));
         }
     }
 
+    /**
+     * In a changelog write only MERGE inserts rows, and MERGE has matched 
them to the table and checked them for
+     * duplicate keys by the key as it is: Doris compares CHAR values 
unpadded. A table that writes no ORC files gives
+     * Doris its keys back padded, so padding a shorter key here would make 
'k' and 'k ' one key, or let a NOT MATCHED
+     * row replace the stored 'k  ' it did not match; a key as long as the 
column is not changed by padding. ORC gives
+     * the keys back without their trailing spaces, so there MERGE matched 'k' 
to the stored 'k  ', and only a key
+     * that ends with a space, such as 'k ', matches no stored key yet pads to 
one.
+     */
+    private void checkInsertedKey(BinaryString key, int column) {
+        int length = insertedKeyCharLengths[column];
+        String name = inputType.getFields().get(column).name();
+        if (orcFiles) {

Review Comment:
   [P1] Preserve the MERGE guard for keys read back with padding
   
   `orcFiles` is true when *any* level uses ORC; it does not mean every target 
key was read from ORC. For example, with `file.format=parquet` and 
`file.format.per.level='5:orc'`, an initial INSERT stores `'k  '` in level-0 
Parquet. A subsequent `MERGE ... ON t.c = s.c WHEN NOT MATCHED THEN INSERT 
...`, with source key `'k'`, does not match that padded key. This branch 
nevertheless accepts `'k'`, pads it to `'k  '`, and silently replaces the 
existing row even though no MATCHED action selected it. CHAR partition keys 
have the same problem because scans restore their padded values from partition 
metadata, including on ORC tables. Retain the short-key rejection for these 
cases, or make MERGE matching and writing use the same canonical key, and add 
mixed-level/CHAR-partition regression coverage.



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