This is an automated email from the ASF dual-hosted git repository.

leonardBang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-cdc.git


The following commit(s) were added to refs/heads/master by this push:
     new 504a4b491 [FLINK-39759][pipeline-connector/starrocks] Fix CHAR/VARCHAR 
mapping for utf8mb4 characters (#4447)
504a4b491 is described below

commit 504a4b491a8f7111fafce6477e0bece932ec128c
Author: haruki <[email protected]>
AuthorDate: Wed Jun 24 18:17:31 2026 +0800

    [FLINK-39759][pipeline-connector/starrocks] Fix CHAR/VARCHAR mapping for 
utf8mb4 characters (#4447)
    
    This closes  #4447.
    
    Co-authored-by: 春栖 <[email protected]>
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../connectors/pipeline-connectors/starrocks.md    | 26 +++++----
 .../connectors/pipeline-connectors/starrocks.md    | 28 +++++-----
 .../starrocks/sink/StarRocksDataSinkFactory.java   |  1 +
 .../starrocks/sink/StarRocksDataSinkOptions.java   | 10 ++++
 .../starrocks/sink/StarRocksMetadataApplier.java   |  8 ++-
 .../connectors/starrocks/sink/StarRocksUtils.java  | 64 +++++++++++++++++-----
 .../starrocks/sink/TableCreateConfig.java          | 30 +++++++++-
 .../starrocks/sink/CdcDataTypeTransformerTest.java | 52 ++++++++++++++++++
 .../sink/StarRocksMetadataApplierITCase.java       | 34 ++++++++++++
 .../starrocks/sink/StarRocksUtilsTest.java         | 30 ++++++++++
 10 files changed, 243 insertions(+), 40 deletions(-)

diff --git a/docs/content.zh/docs/connectors/pipeline-connectors/starrocks.md 
b/docs/content.zh/docs/connectors/pipeline-connectors/starrocks.md
index 5e69fde7e..39e4b5d48 100644
--- a/docs/content.zh/docs/connectors/pipeline-connectors/starrocks.md
+++ b/docs/content.zh/docs/connectors/pipeline-connectors/starrocks.md
@@ -215,6 +215,13 @@ pipeline:
       <td>Duration</td>
       <td>StarRocks 侧执行 schema change 的超时时间,必须是秒的整数倍。超时后 StarRocks 将会取消 schema 
change,从而导致作业失败。</td>
     </tr>
+    <tr>
+      <td>unicode-char.max-bytes</td>
+      <td>optional</td>
+      <td style="word-wrap: break-word;">3</td>
+      <td>Integer</td>
+      <td>将上游 CHAR 和 VARCHAR 类型映射到 StarRocks 时,为每个字符分配的最大字节数。由于 StarRocks 
的长度以字节为单位,如果上游使用 utf8mb4,建议将该选项设置为 4,以避免低估目标列长度。默认值仍为 3,以保持向后兼容。</td>
+    </tr>
     <tr>
       <td>sink.socket.timeout-ms</td>
       <td>optional</td>
@@ -320,22 +327,19 @@ pipeline:
       <td></td>
     </tr>
     <tr>
-      <td>CHAR(n) where n <= 85</td>
-      <td>CHAR(n * 3)</td>
-      <td>CDC 中长度表示字符数,而 StarRocks 中长度表示字节数。根据 UTF-8 编码,一个中文字符占用三个字节,因此 CDC 
中的长度对应到 StarRocks
-          中为 n * 3。由于 StarRocks CHAR 类型的最大长度为255,所以只有当 CDC 中长度不超过85时,才将 CDC 
CHAR 映射到 StarRocks CHAR。</td>
+      <td>CHAR(n),且 n * unicode-char.max-bytes <= 255,并且不是主键列</td>
+      <td>CHAR(n * unicode-char.max-bytes)</td>
+      <td>CDC 中长度表示字符数,而 StarRocks 中长度表示字节数。StarRocks 的长度按 n * 
unicode-char.max-bytes 计算。由于 StarRocks CHAR 类型的最大长度为 255,只有当计算后的长度不超过 255 时,才将 
CDC CHAR 映射到 StarRocks CHAR。如果该列是主键列,则会映射为 VARCHAR。</td>
     </tr>
     <tr>
-      <td>CHAR(n) where n > 85</td>
-      <td>VARCHAR(n * 3)</td>
-      <td>CDC 中长度表示字符数,而 StarRocks 中长度表示字节数。根据 UTF-8 编码,一个中文字符占用三个字节,因此 CDC 
中的长度对应到 StarRocks
-          中为 n * 3。由于 StarRocks CHAR 类型的最大长度为255,所以当 CDC 中长度超过85时,才将 CDC CHAR 
映射到 StarRocks VARCHAR。</td>
+      <td>CHAR(n),且 n * unicode-char.max-bytes > 255,或主键列</td>
+      <td>VARCHAR(min(n * unicode-char.max-bytes, 1048576))</td>
+      <td>CDC 中长度表示字符数,而 StarRocks 中长度表示字节数。StarRocks 的长度按 n * 
unicode-char.max-bytes 计算。由于 StarRocks CHAR 类型的最大长度为 255,当计算后的长度超过 255 时,会将 CDC 
CHAR 映射到 StarRocks VARCHAR。主键 CHAR 列也会映射为 VARCHAR。</td>
     </tr>
     <tr>
       <td>VARCHAR(n)</td>
-      <td>VARCHAR(n * 3)</td>
-      <td>CDC 中长度表示字符数,而 StarRocks 中长度表示字节数。根据 UTF-8 编码,一个中文字符占用三个字节,因此 CDC 
中的长度对应到 StarRocks
-          中为 n * 3。</td>
+      <td>VARCHAR(min(n * unicode-char.max-bytes, 1048576))</td>
+      <td>CDC 中长度表示字符数,而 StarRocks 中长度表示字节数。StarRocks 的长度按 n * 
unicode-char.max-bytes 计算,并且最大不会超过 1048576。</td>
     </tr>
     <tr>
       <td>BINARY(n)</td>
diff --git a/docs/content/docs/connectors/pipeline-connectors/starrocks.md 
b/docs/content/docs/connectors/pipeline-connectors/starrocks.md
index 13297d3af..b36303be3 100644
--- a/docs/content/docs/connectors/pipeline-connectors/starrocks.md
+++ b/docs/content/docs/connectors/pipeline-connectors/starrocks.md
@@ -222,6 +222,13 @@ pipeline:
           seconds. StarRocks will cancel the schema change after timeout which 
will
           cause the sink failure. </td>
     </tr>
+    <tr>
+      <td>unicode-char.max-bytes</td>
+      <td>optional</td>
+      <td style="word-wrap: break-word;">3</td>
+      <td>Integer</td>
+      <td>The maximum number of bytes allocated for each upstream character 
when mapping CHAR and VARCHAR types to StarRocks, whose length is measured in 
bytes. If the upstream source uses utf8mb4, set this option to 4 to avoid 
underestimating column lengths. The default value of 3 is retained for backward 
compatibility.</td>
+    </tr>
     <tr>
       <td>sink.socket.timeout-ms</td>
       <td>optional</td>
@@ -329,24 +336,19 @@ pipeline:
       <td></td>
     </tr>
     <tr>
-      <td>CHAR(n) where n <= 85</td>
-      <td>CHAR(n * 3)</td>
-      <td>CDC defines the length by characters, and StarRocks defines it by 
bytes. According to UTF-8, one Chinese 
-        character is equal to three bytes, so the length for StarRocks is n * 
3. Because the max length of StarRocks
-        CHAR is 255, map CDC CHAR to StarRocks CHAR only when the CDC length 
is no larger than 85.</td>
+      <td>CHAR(n) where n * unicode-char.max-bytes <= 255 and not primary 
key</td>
+      <td>CHAR(n * unicode-char.max-bytes)</td>
+      <td>CDC defines the length by characters, and StarRocks defines it by 
bytes. The StarRocks length is calculated as n * unicode-char.max-bytes. 
Because the max length of StarRocks CHAR is 255, map CDC CHAR to StarRocks CHAR 
only when the calculated length is no larger than 255. If the column is part of 
the primary key, it is mapped to VARCHAR instead.</td>
     </tr>
     <tr>
-      <td>CHAR(n) where n > 85</td>
-      <td>VARCHAR(n * 3)</td>
-      <td>CDC defines the length by characters, and StarRocks defines it by 
bytes. According to UTF-8, one Chinese 
-        character is equal to three bytes, so the length for StarRocks is n * 
3. Because the max length of StarRocks
-        CHAR is 255, map CDC CHAR to StarRocks VARCHAR if the CDC length is 
larger than 85.</td>
+      <td>CHAR(n) where n * unicode-char.max-bytes > 255, or primary key</td>
+      <td>VARCHAR(min(n * unicode-char.max-bytes, 1048576))</td>
+      <td>CDC defines the length by characters, and StarRocks defines it by 
bytes. The StarRocks length is calculated as n * unicode-char.max-bytes. 
Because the max length of StarRocks CHAR is 255, map CDC CHAR to StarRocks 
VARCHAR when the calculated length exceeds 255. Primary key CHAR columns are 
also mapped to VARCHAR.</td>
     </tr>
     <tr>
       <td>VARCHAR(n)</td>
-      <td>VARCHAR(n * 3)</td>
-      <td>CDC defines the length by characters, and StarRocks defines it by 
bytes. According to UTF-8, one Chinese 
-        character is equal to three bytes, so the length for StarRocks is n * 
3.</td>
+      <td>VARCHAR(min(n * unicode-char.max-bytes, 1048576))</td>
+      <td>CDC defines the length by characters, and StarRocks defines it by 
bytes. The StarRocks length is calculated as n * unicode-char.max-bytes and 
capped at 1048576.</td>
     </tr>
     <tr>
       <td>BINARY(n)</td>
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkFactory.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkFactory.java
index fbb34cd51..373594b00 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkFactory.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkFactory.java
@@ -182,6 +182,7 @@ public class StarRocksDataSinkFactory implements 
DataSinkFactory {
         
optionalOptions.add(StarRocksDataSinkOptions.SINK_METRIC_HISTOGRAM_WINDOW_SIZE);
         optionalOptions.add(StarRocksDataSinkOptions.TABLE_CREATE_NUM_BUCKETS);
         
optionalOptions.add(StarRocksDataSinkOptions.TABLE_SCHEMA_CHANGE_TIMEOUT);
+        optionalOptions.add(StarRocksDataSinkOptions.UNICODE_CHAR_MAX_BYTES);
         return optionalOptions;
     }
 }
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkOptions.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkOptions.java
index 5f7b7d8ab..6afd470aa 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkOptions.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksDataSinkOptions.java
@@ -157,4 +157,14 @@ public class StarRocksDataSinkOptions {
                             "Timeout for a schema change on StarRocks side, 
and must be an integral multiple of "
                                     + "seconds. StarRocks will cancel the 
schema change after timeout which will "
                                     + "cause the sink failure.");
+
+    public static final ConfigOption<Integer> UNICODE_CHAR_MAX_BYTES =
+            ConfigOptions.key("unicode-char.max-bytes")
+                    .intType()
+                    .defaultValue(3)
+                    .withDescription(
+                            "Specifies how many bytes are allocated for each 
upstream character when mapping "
+                                    + "CHAR and VARCHAR types to StarRocks, 
whose length is measured in bytes. "
+                                    + "Valid values are within [1, 4]. If the 
upstream source uses utf8mb4, "
+                                    + "set this option to 4 to avoid 
underestimating column lengths.");
 }
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplier.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplier.java
index 00dcb67b6..7f0208441 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplier.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplier.java
@@ -162,7 +162,7 @@ public class StarRocksMetadataApplier implements 
MetadataApplier {
                             .setDefaultValue(
                                     
StarRocksUtils.convertInvalidTimestampDefaultValue(
                                             
column.getDefaultValueExpression(), column.getType()));
-            toStarRocksDataType(column, false, builder);
+            toStarRocksDataType(column, false, builder, 
tableCreateConfig.getUnicodeCharMaxBytes());
             addColumns.add(builder.build());
         }
 
@@ -321,7 +321,11 @@ public class StarRocksMetadataApplier implements 
MetadataApplier {
             for (Map.Entry<String, DataType> entry : typeMapping.entrySet()) {
                 StarRocksColumn.Builder builder =
                         new 
StarRocksColumn.Builder().setColumnName(entry.getKey());
-                toStarRocksDataType(entry.getValue(), false, builder);
+                toStarRocksDataType(
+                        entry.getValue(),
+                        false,
+                        builder,
+                        tableCreateConfig.getUnicodeCharMaxBytes());
                 catalog.alterColumnType(
                         tableId.getSchemaName(), tableId.getTableName(), 
builder.build());
             }
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtils.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtils.java
index f72f5ddc5..d2a50d4ab 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtils.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtils.java
@@ -95,7 +95,11 @@ public class StarRocksUtils {
                             .setDefaultValue(
                                     convertInvalidTimestampDefaultValue(
                                             
column.getDefaultValueExpression(), column.getType()));
-            toStarRocksDataType(column, i < primaryKeyCount, builder);
+            toStarRocksDataType(
+                    column,
+                    i < primaryKeyCount,
+                    builder,
+                    tableCreateConfig.getUnicodeCharMaxBytes());
             starRocksColumns.add(builder.build());
         }
 
@@ -119,14 +123,38 @@ public class StarRocksUtils {
     /** Convert CDC data type to StarRocks data type. */
     public static void toStarRocksDataType(
             DataType cdcDataType, boolean isPrimaryKeys, 
StarRocksColumn.Builder builder) {
+        toStarRocksDataType(
+                cdcDataType,
+                isPrimaryKeys,
+                builder,
+                
StarRocksDataSinkOptions.UNICODE_CHAR_MAX_BYTES.defaultValue());
+    }
+
+    public static void toStarRocksDataType(
+            DataType cdcDataType,
+            boolean isPrimaryKeys,
+            StarRocksColumn.Builder builder,
+            int unicodeCharMaxBytes) {
         CdcDataTypeTransformer dataTypeTransformer =
-                new CdcDataTypeTransformer(isPrimaryKeys, builder);
+                new CdcDataTypeTransformer(isPrimaryKeys, builder, 
unicodeCharMaxBytes);
         cdcDataType.accept(dataTypeTransformer);
     }
 
     public static void toStarRocksDataType(
             Column cdcColumn, boolean isPrimaryKeys, StarRocksColumn.Builder 
builder) {
-        toStarRocksDataType(cdcColumn.getType(), isPrimaryKeys, builder);
+        toStarRocksDataType(
+                cdcColumn,
+                isPrimaryKeys,
+                builder,
+                
StarRocksDataSinkOptions.UNICODE_CHAR_MAX_BYTES.defaultValue());
+    }
+
+    public static void toStarRocksDataType(
+            Column cdcColumn,
+            boolean isPrimaryKeys,
+            StarRocksColumn.Builder builder,
+            int unicodeCharMaxBytes) {
+        toStarRocksDataType(cdcColumn.getType(), isPrimaryKeys, builder, 
unicodeCharMaxBytes);
     }
 
     /** Format DATE type data. */
@@ -297,10 +325,20 @@ public class StarRocksUtils {
 
         private final StarRocksColumn.Builder builder;
         private final boolean isPrimaryKeys;
+        private final int unicodeCharMaxBytes;
 
         public CdcDataTypeTransformer(boolean isPrimaryKeys, 
StarRocksColumn.Builder builder) {
+            this(
+                    isPrimaryKeys,
+                    builder,
+                    
StarRocksDataSinkOptions.UNICODE_CHAR_MAX_BYTES.defaultValue());
+        }
+
+        public CdcDataTypeTransformer(
+                boolean isPrimaryKeys, StarRocksColumn.Builder builder, int 
unicodeCharMaxBytes) {
             this.isPrimaryKeys = isPrimaryKeys;
             this.builder = builder;
+            this.unicodeCharMaxBytes = unicodeCharMaxBytes;
         }
 
         @Override
@@ -379,13 +417,13 @@ public class StarRocksUtils {
         @Override
         public StarRocksColumn.Builder visit(CharType charType) {
             // CDC and StarRocks use different units for the length. It's the 
number
-            // of characters in CDC, and the number of bytes in StarRocks. One 
chinese
-            // character will use 3 bytes because it uses UTF-8, so the length 
of StarRocks
-            // char type should be three times as that of CDC char type. 
Specifically, if
-            // the length of StarRocks exceeds the MAX_CHAR_SIZE, map CDC char 
type to StarRocks
-            // varchar type
+            // of characters in CDC, and the number of bytes in StarRocks. The 
number
+            // of bytes needed for each character depends on the upstream 
encoding, so
+            // the length of StarRocks char type should be scaled by 
unicodeCharMaxBytes.
+            // Specifically, if the length of StarRocks exceeds the 
MAX_CHAR_SIZE, map
+            // CDC char type to StarRocks varchar type.
             int length = charType.getLength();
-            long starRocksLength = length * 3L;
+            long starRocksLength = (long) length * unicodeCharMaxBytes;
             // In the StarRocks, The primary key columns can be any of the 
following data types:
             // BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, STRING, 
VARCHAR, DATE, and
             // DATETIME, But it doesn't include CHAR. When a char type appears 
in the primary key of
@@ -405,11 +443,11 @@ public class StarRocksUtils {
         @Override
         public StarRocksColumn.Builder visit(VarCharType varCharType) {
             // CDC and StarRocks use different units for the length. It's the 
number
-            // of characters in CDC, and the number of bytes in StarRocks. One 
chinese
-            // character will use 3 bytes because it uses UTF-8, so the length 
of StarRocks
-            // varchar type should be three times as that of CDC varchar type.
+            // of characters in CDC, and the number of bytes in StarRocks. The 
number
+            // of bytes needed for each character depends on the upstream 
encoding, so
+            // the length of StarRocks varchar type should be scaled by 
unicodeCharMaxBytes.
             int length = varCharType.getLength();
-            long starRocksLength = length * 3L;
+            long starRocksLength = (long) length * unicodeCharMaxBytes;
             builder.setDataType(VARCHAR);
             builder.setNullable(varCharType.isNullable());
             builder.setColumnSize((int) Math.min(starRocksLength, 
MAX_VARCHAR_SIZE));
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/TableCreateConfig.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/TableCreateConfig.java
index b3bae0f7b..7eaeb477f 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/TableCreateConfig.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/main/java/org/apache/flink/cdc/connectors/starrocks/sink/TableCreateConfig.java
@@ -18,6 +18,7 @@
 package org.apache.flink.cdc.connectors.starrocks.sink;
 
 import org.apache.flink.cdc.common.configuration.Configuration;
+import org.apache.flink.cdc.common.utils.Preconditions;
 
 import javax.annotation.Nullable;
 
@@ -44,9 +45,29 @@ public class TableCreateConfig implements Serializable {
     /** Properties for the table. */
     private final Map<String, String> properties;
 
+    /** Maximum number of bytes a single character can take in UTF-8 encoding. 
*/
+    public static final int MAX_UNICODE_CHAR_BYTES = 4;
+
+    /** Max bytes allocated for each upstream Unicode character during schema 
mapping. */
+    private final int unicodeCharMaxBytes;
+
     public TableCreateConfig(@Nullable Integer numBuckets, Map<String, String> 
properties) {
+        this(
+                numBuckets,
+                properties,
+                
StarRocksDataSinkOptions.UNICODE_CHAR_MAX_BYTES.defaultValue());
+    }
+
+    public TableCreateConfig(
+            @Nullable Integer numBuckets, Map<String, String> properties, int 
unicodeCharMaxBytes) {
+        Preconditions.checkArgument(
+                unicodeCharMaxBytes >= 1 && unicodeCharMaxBytes <= 
MAX_UNICODE_CHAR_BYTES,
+                "unicode-char.max-bytes must be between 1 and %s, but actually 
is %s",
+                MAX_UNICODE_CHAR_BYTES,
+                unicodeCharMaxBytes);
         this.numBuckets = numBuckets;
         this.properties = new HashMap<>(properties);
+        this.unicodeCharMaxBytes = unicodeCharMaxBytes;
     }
 
     public Optional<Integer> getNumBuckets() {
@@ -57,6 +78,10 @@ public class TableCreateConfig implements Serializable {
         return Collections.unmodifiableMap(properties);
     }
 
+    public int getUnicodeCharMaxBytes() {
+        return unicodeCharMaxBytes;
+    }
+
     public static TableCreateConfig from(Configuration config) {
         Integer numBuckets = 
config.get(StarRocksDataSinkOptions.TABLE_CREATE_NUM_BUCKETS);
         Map<String, String> tableProperties =
@@ -77,6 +102,9 @@ public class TableCreateConfig implements Serializable {
                                                                         
.length())
                                                         .toLowerCase(),
                                         Map.Entry::getValue));
-        return new TableCreateConfig(numBuckets, tableProperties);
+        return new TableCreateConfig(
+                numBuckets,
+                tableProperties,
+                config.get(StarRocksDataSinkOptions.UNICODE_CHAR_MAX_BYTES));
     }
 }
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/CdcDataTypeTransformerTest.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/CdcDataTypeTransformerTest.java
index 8fe51a1c4..cdc34529a 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/CdcDataTypeTransformerTest.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/CdcDataTypeTransformerTest.java
@@ -150,6 +150,58 @@ class CdcDataTypeTransformerTest {
         Assertions.assertThat(largeLengthColumn.isNullable()).isTrue();
     }
 
+    @Test
+    void testCharTypeWithUnicodeCharMaxBytes() {
+        // With unicode-char.max-bytes = 4, a CHAR(17) should be sized 4 * 17 
= 68 (<=
+        // MAX_CHAR_SIZE)
+        StarRocksColumn.Builder charBuilder =
+                new 
StarRocksColumn.Builder().setColumnName("utf8mb4_char").setOrdinalPosition(0);
+        new CharType(17).accept(new 
StarRocksUtils.CdcDataTypeTransformer(false, charBuilder, 4));
+        StarRocksColumn charColumn = charBuilder.build();
+        
Assertions.assertThat(charColumn.getDataType()).isEqualTo(StarRocksUtils.CHAR);
+        Assertions.assertThat(charColumn.getColumnSize()).hasValue(68);
+
+        // 4 * 100 = 400 > MAX_CHAR_SIZE, so it should fall back to VARCHAR
+        StarRocksColumn.Builder overflowBuilder =
+                new 
StarRocksColumn.Builder().setColumnName("overflow_char").setOrdinalPosition(1);
+        new CharType(100)
+                .accept(new StarRocksUtils.CdcDataTypeTransformer(false, 
overflowBuilder, 4));
+        StarRocksColumn overflowColumn = overflowBuilder.build();
+        
Assertions.assertThat(overflowColumn.getDataType()).isEqualTo(StarRocksUtils.VARCHAR);
+        Assertions.assertThat(overflowColumn.getColumnSize()).hasValue(400);
+
+        // Primary key CHAR is always mapped to VARCHAR, scaled by 
unicode-char.max-bytes
+        StarRocksColumn.Builder pkBuilder =
+                new 
StarRocksColumn.Builder().setColumnName("pk_char").setOrdinalPosition(2);
+        new CharType(17).accept(new 
StarRocksUtils.CdcDataTypeTransformer(true, pkBuilder, 4));
+        StarRocksColumn pkColumn = pkBuilder.build();
+        
Assertions.assertThat(pkColumn.getDataType()).isEqualTo(StarRocksUtils.VARCHAR);
+        Assertions.assertThat(pkColumn.getColumnSize()).hasValue(68);
+    }
+
+    @Test
+    void testVarCharTypeWithUnicodeCharMaxBytes() {
+        // With unicode-char.max-bytes = 4, a VARCHAR(17) should be sized 4 * 
17 = 68
+        StarRocksColumn.Builder builder =
+                new StarRocksColumn.Builder()
+                        .setColumnName("utf8mb4_varchar")
+                        .setOrdinalPosition(0);
+        new VarCharType(17).accept(new 
StarRocksUtils.CdcDataTypeTransformer(false, builder, 4));
+        StarRocksColumn column = builder.build();
+        
Assertions.assertThat(column.getDataType()).isEqualTo(StarRocksUtils.VARCHAR);
+        Assertions.assertThat(column.getColumnSize()).hasValue(68);
+
+        // The result should still be capped at MAX_VARCHAR_SIZE
+        StarRocksColumn.Builder largeBuilder =
+                new 
StarRocksColumn.Builder().setColumnName("large_varchar").setOrdinalPosition(1);
+        new VarCharType(StarRocksUtils.MAX_VARCHAR_SIZE)
+                .accept(new StarRocksUtils.CdcDataTypeTransformer(false, 
largeBuilder, 4));
+        StarRocksColumn largeColumn = largeBuilder.build();
+        
Assertions.assertThat(largeColumn.getDataType()).isEqualTo(StarRocksUtils.VARCHAR);
+        Assertions.assertThat(largeColumn.getColumnSize())
+                .hasValue(StarRocksUtils.MAX_VARCHAR_SIZE);
+    }
+
     @Test
     void testBinaryType() {
         StarRocksColumn.Builder builder =
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplierITCase.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplierITCase.java
index fd1d3c068..d9164d60c 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplierITCase.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksMetadataApplierITCase.java
@@ -270,6 +270,35 @@ class StarRocksMetadataApplierITCase extends 
StarRocksSinkTestBase {
         assertEqualsInOrder(expected, actual);
     }
 
+    @Test
+    void testStarRocksDataTypeWithUnicodeCharMaxBytes() throws Exception {
+        TableId tableId =
+                TableId.tableId(
+                        StarRocksContainer.STARROCKS_DATABASE_NAME,
+                        StarRocksContainer.STARROCKS_TABLE_NAME);
+        Schema schema =
+                Schema.newBuilder()
+                        .column(new PhysicalColumn("id", 
DataTypes.INT().notNull(), "ID"))
+                        .column(new PhysicalColumn("char", DataTypes.CHAR(17), 
"Char"))
+                        .column(new PhysicalColumn("varchar", 
DataTypes.VARCHAR(17), "Var Char"))
+                        .primaryKey("id")
+                        .build();
+
+        runJobWithEvents(
+                Collections.singletonList(new CreateTableEvent(tableId, 
schema)),
+                new 
Configuration().set(StarRocksDataSinkOptions.UNICODE_CHAR_MAX_BYTES, 4));
+
+        List<String> actual = inspectTableSchema(tableId);
+        List<String> expected =
+                Arrays.asList(
+                        "id | int | NO | true | null",
+                        // 4 bytes per character instead of the default 3
+                        "char | char(68) | YES | false | null",
+                        "varchar | varchar(68) | YES | false | null");
+
+        assertEqualsInOrder(expected, actual);
+    }
+
     @Test
     void testStarRocksAddColumn() throws Exception {
         TableId tableId =
@@ -426,6 +455,10 @@ class StarRocksMetadataApplierITCase extends 
StarRocksSinkTestBase {
     }
 
     private void runJobWithEvents(List<Event> events) throws Exception {
+        runJobWithEvents(events, new Configuration());
+    }
+
+    private void runJobWithEvents(List<Event> events, Configuration 
extraConfig) throws Exception {
         DataStream<Event> stream = env.fromData(events, new 
EventTypeInfo()).setParallelism(1);
 
         Configuration config =
@@ -434,6 +467,7 @@ class StarRocksMetadataApplierITCase extends 
StarRocksSinkTestBase {
                         .set(JDBC_URL, STARROCKS_CONTAINER.getJdbcUrl())
                         .set(USERNAME, StarRocksContainer.STARROCKS_USERNAME)
                         .set(PASSWORD, StarRocksContainer.STARROCKS_PASSWORD);
+        config.addAll(extraConfig);
 
         DataSink starRocksSink = createStarRocksDataSink(config);
 
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtilsTest.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtilsTest.java
index cc99b3bc4..e1caaba50 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtilsTest.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-starrocks/src/test/java/org/apache/flink/cdc/connectors/starrocks/sink/StarRocksUtilsTest.java
@@ -194,6 +194,36 @@ class StarRocksUtilsTest {
         assertThat(table.getProperties()).containsEntry("replication_num", 
"3");
     }
 
+    @Test
+    void testToStarRocksTableWithUnicodeCharMaxBytes() {
+        Schema schema =
+                Schema.newBuilder()
+                        .physicalColumn("id", DataTypes.BIGINT().notNull())
+                        .physicalColumn("name", DataTypes.VARCHAR(17))
+                        .physicalColumn("code", DataTypes.CHAR(17))
+                        .primaryKey("id")
+                        .build();
+
+        TableId tableId = TableId.tableId("db", "table");
+        // unicode-char.max-bytes = 4 to fit utf8mb4 characters
+        TableCreateConfig config = new TableCreateConfig(null, 
Collections.emptyMap(), 4);
+
+        StarRocksTable table = StarRocksUtils.toStarRocksTable(tableId, 
schema, config);
+
+        List<StarRocksColumn> columns = table.getColumns();
+        assertThat(columns).hasSize(3);
+
+        StarRocksColumn nameColumn = columns.get(1);
+        assertThat(nameColumn.getColumnName()).isEqualTo("name");
+        assertThat(nameColumn.getDataType()).isEqualTo(StarRocksUtils.VARCHAR);
+        assertThat(nameColumn.getColumnSize()).hasValue(68);
+
+        StarRocksColumn codeColumn = columns.get(2);
+        assertThat(codeColumn.getColumnName()).isEqualTo("code");
+        assertThat(codeColumn.getDataType()).isEqualTo(StarRocksUtils.CHAR);
+        assertThat(codeColumn.getColumnSize()).hasValue(68);
+    }
+
     @Test
     void testToStarRocksTableWithComment() {
         Schema schema =

Reply via email to