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

lidongdai pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git


The following commit(s) were added to refs/heads/dev by this push:
     new 5880c3ee84 [Fix][Connector-v2][Clickhouse] Handle special characters 
in column comments  (#10128)
5880c3ee84 is described below

commit 5880c3ee84f65d99f880611e010db1b975827e6e
Author: Jast <[email protected]>
AuthorDate: Mon Dec 8 18:38:46 2025 +0800

    [Fix][Connector-v2][Clickhouse] Handle special characters in column 
comments  (#10128)
---
 .github/workflows/backend.yml                      |  6 +--
 .../seatunnel/common/util/CatalogUtil.java         | 27 ++++++----
 .../seatunnel/clickhouse/ClickhouseIT.java         | 40 +++++++++++++++
 ...ickhouse_auto_create_with_special_comments.conf | 58 ++++++++++++++++++++++
 4 files changed, 119 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
index 07e19fc13b..322707e8c8 100644
--- a/.github/workflows/backend.yml
+++ b/.github/workflows/backend.yml
@@ -449,7 +449,7 @@ jobs:
       matrix:
         java: [ '8', '11' ]
         os: [ 'ubuntu-latest' ]
-    timeout-minutes: 120
+    timeout-minutes: 180
     steps:
       - uses: actions/checkout@v2
       - name: Set up JDK ${{ matrix.java }}
@@ -510,7 +510,7 @@ jobs:
       matrix:
         java: [ '8', '11' ]
         os: [ 'ubuntu-latest' ]
-    timeout-minutes: 120
+    timeout-minutes: 180
     steps:
       - uses: actions/checkout@v2
       - name: Set up JDK ${{ matrix.java }}
@@ -540,7 +540,7 @@ jobs:
       matrix:
         java: [ '8', '11' ]
         os: [ 'ubuntu-latest' ]
-    timeout-minutes: 120
+    timeout-minutes: 180
     steps:
       - uses: actions/checkout@v2
       - name: Set up JDK ${{ matrix.java }}
diff --git 
a/seatunnel-connectors-v2/connector-common/src/main/java/org/apache/seatunnel/connectors/seatunnel/common/util/CatalogUtil.java
 
b/seatunnel-connectors-v2/connector-common/src/main/java/org/apache/seatunnel/connectors/seatunnel/common/util/CatalogUtil.java
index dc351e239f..2970700430 100644
--- 
a/seatunnel-connectors-v2/connector-common/src/main/java/org/apache/seatunnel/connectors/seatunnel/common/util/CatalogUtil.java
+++ 
b/seatunnel-connectors-v2/connector-common/src/main/java/org/apache/seatunnel/connectors/seatunnel/common/util/CatalogUtil.java
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.function.Function;
+import java.util.regex.Matcher;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -70,7 +71,7 @@ public abstract class CatalogUtil {
         template =
                 template.replaceAll(
                         
SaveModePlaceHolder.ROWTYPE_PRIMARY_KEY.getReplacePlaceHolder(),
-                        primaryKey);
+                        Matcher.quoteReplacement(primaryKey));
         SqlTemplate.canHandledByTemplateWithPlaceholder(
                 template,
                 SaveModePlaceHolder.ROWTYPE_UNIQUE_KEY.getPlaceHolder(),
@@ -80,7 +81,8 @@ public abstract class CatalogUtil {
 
         template =
                 template.replaceAll(
-                        
SaveModePlaceHolder.ROWTYPE_UNIQUE_KEY.getReplacePlaceHolder(), uniqueKey);
+                        
SaveModePlaceHolder.ROWTYPE_UNIQUE_KEY.getReplacePlaceHolder(),
+                        Matcher.quoteReplacement(uniqueKey));
         Map<String, CreateTableParser.ColumnInfo> columnInTemplate =
                 CreateTableParser.getColumnList(template);
         template = mergeColumnInTemplate(columnInTemplate, tableSchema, 
template);
@@ -95,20 +97,27 @@ public abstract class CatalogUtil {
             // TODO: Remove this compatibility config
             template =
                     template.replaceAll(
-                            
SaveModePlaceHolder.TABLE_NAME.getReplacePlaceHolder(), table);
+                            
SaveModePlaceHolder.TABLE_NAME.getReplacePlaceHolder(),
+                            Matcher.quoteReplacement(table));
             log.warn(
                     "The variable placeholder `${table_name}` has been marked 
as deprecated and will be removed soon, please use `${table}`");
         }
 
-        return 
template.replaceAll(SaveModePlaceHolder.DATABASE.getReplacePlaceHolder(), 
database)
-                .replaceAll(SaveModePlaceHolder.TABLE.getReplacePlaceHolder(), 
table)
+        return template.replaceAll(
+                        SaveModePlaceHolder.DATABASE.getReplacePlaceHolder(),
+                        Matcher.quoteReplacement(database))
                 .replaceAll(
-                        
SaveModePlaceHolder.ROWTYPE_FIELDS.getReplacePlaceHolder(), rowTypeFields)
+                        SaveModePlaceHolder.TABLE.getReplacePlaceHolder(),
+                        Matcher.quoteReplacement(table))
+                .replaceAll(
+                        
SaveModePlaceHolder.ROWTYPE_FIELDS.getReplacePlaceHolder(),
+                        Matcher.quoteReplacement(rowTypeFields))
                 .replaceAll(
                         SaveModePlaceHolder.COMMENT.getReplacePlaceHolder(),
-                        Objects.isNull(comment)
-                                ? ""
-                                : comment.replace("'", "''").replace("\\", 
"\\\\"));
+                        Matcher.quoteReplacement(
+                                Objects.isNull(comment)
+                                        ? ""
+                                        : comment.replace("'", 
"''").replace("\\", "\\\\")));
     }
 
     private String mergeColumnInTemplate(
diff --git 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-clickhouse-e2e/src/test/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/ClickhouseIT.java
 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-clickhouse-e2e/src/test/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/ClickhouseIT.java
index 092c7cd9ad..47bd9672f9 100644
--- 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-clickhouse-e2e/src/test/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/ClickhouseIT.java
+++ 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-clickhouse-e2e/src/test/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/ClickhouseIT.java
@@ -131,6 +131,46 @@ public class ClickhouseIT extends TestSuiteBase implements 
TestResource {
         Assertions.assertEquals(0, execResult.getExitCode());
     }
 
+    @TestTemplate
+    public void testClickhouseAutoCreateTableWithSpecialCharactersInComments(
+            TestContainer testContainer) throws Exception {
+        String testTableName = "test_special_chars_comments_table";
+
+        String createSourceTableSql =
+                String.format(
+                        "CREATE TABLE IF NOT EXISTS %s.%s ("
+                                + "id UInt64, "
+                                + "col_with_dollar_comment String COMMENT 
'Comment with $1 and $2 special chars', "
+                                + "col_with_backslash_comment String COMMENT 
'Comment with \\\\ backslash', "
+                                + "col_with_mixed_chars String COMMENT 
'~`!@#$%%^&*()_+-*/-=[]{}', "
+                                + "col_with_chinese_chars String COMMENT 
'这是特殊符号测试英文键盘:~`!@#$%%^&*()_+-*/-=[]{}'"
+                                + ") ENGINE = MergeTree() ORDER BY id",
+                        DATABASE, testTableName);
+
+        String sinkTableName = testTableName + "_sink";
+
+        try (Statement statement = connection.createStatement()) {
+            statement.execute(createSourceTableSql);
+
+            String insertSql =
+                    String.format(
+                            "INSERT INTO %s.%s VALUES "
+                                    + "(1, 'value1', 'value2', 'value3', 
'value4')",
+                            DATABASE, testTableName);
+            statement.execute(insertSql);
+        }
+
+        Container.ExecResult execResult =
+                
testContainer.executeJob("/clickhouse_auto_create_with_special_comments.conf");
+
+        Assertions.assertEquals(0, execResult.getExitCode(), 
execResult.getStderr());
+
+        Assertions.assertEquals(1, countData(sinkTableName));
+
+        dropTable(DATABASE + "." + testTableName);
+        dropTable(DATABASE + "." + sinkTableName);
+    }
+
     @TestTemplate
     public void clickhouseWithCreateSchemaWhenNotExist(TestContainer 
container) throws Exception {
         String tableName = "default.sink_table_for_schema";
diff --git 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-clickhouse-e2e/src/test/resources/clickhouse_auto_create_with_special_comments.conf
 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-clickhouse-e2e/src/test/resources/clickhouse_auto_create_with_special_comments.conf
new file mode 100644
index 0000000000..3b5c7a3039
--- /dev/null
+++ 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-clickhouse-e2e/src/test/resources/clickhouse_auto_create_with_special_comments.conf
@@ -0,0 +1,58 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+######
+###### This config file tests auto create table with special characters in 
column comments
+###### Testing regex special characters like $ and \ are properly handled by 
Matcher.quoteReplacement
+######
+
+env {
+  parallelism = 1
+  job.mode = "BATCH"
+  checkpoint.interval = 10
+}
+
+source {
+  Clickhouse {
+    host = "clickhouse:8123"
+    table_path = "default.test_special_chars_comments_table"
+    sql = "select * from default.test_special_chars_comments_table"
+    username = "default"
+    password = ""
+    plugin_output = "source_table"
+  }
+}
+
+sink {
+  Clickhouse {
+    host = "clickhouse:8123"
+    database = "default"
+    table = "test_special_chars_comments_table_sink"
+    username = "default"
+    password = ""
+    "schema_save_mode" = "CREATE_SCHEMA_WHEN_NOT_EXIST"
+    "data_save_mode" = "APPEND_DATA"
+    "save_mode_create_template" = """
+     CREATE TABLE IF NOT EXISTS `${database}`.`${table}` (
+    ${rowtype_fields}
+    ) ENGINE = MergeTree()
+    ORDER BY (id)
+    COMMENT '${comment}';
+    """
+    support_upsert = true
+    allow_experimental_lightweight_delete = true
+  }
+}

Reply via email to