This is an automated email from the ASF dual-hosted git repository.
yuqi1129 pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new 8d1c6bdb99 [Cherry-pick to branch-1.3] [#13943] fix(oceanbase): Escape
table and column comments (#13944) (#13960)
8d1c6bdb99 is described below
commit 8d1c6bdb99e9adb3c8ea3f0ebea740053621aed9
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Oct 20 11:07:07 2026 +0800
[Cherry-pick to branch-1.3] [#13943] fix(oceanbase): Escape table and
column comments (#13944) (#13960)
**Cherry-pick Information:**
- Original commit: 409d1c4013306c091c440e9bd3603c73d8b4788e
- Target branch: `branch-1.3`
- Status: ⚠️ **Has conflicts - manual resolution required**
**Do not merge** until conflict markers are resolved and the
`cherry-pick-conflict` label is removed.
Please review and resolve the conflicts before merging.
Co-authored-by: Qi Yu <[email protected]>
---
.../operation/OceanBaseTableOperations.java | 12 ++-
.../operation/TestOceanBaseTableOperations.java | 54 ++++++++++++
.../TestOceanBaseTableOperationsSqlGeneration.java | 96 ++++++++++++++++++++++
3 files changed, 158 insertions(+), 4 deletions(-)
diff --git
a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
index 87399f8165..f05fcfada3 100644
---
a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
+++
b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java
@@ -18,6 +18,7 @@
*/
package org.apache.gravitino.catalog.oceanbase.operation;
+import static
org.apache.gravitino.catalog.jdbc.utils.JdbcConnectorUtils.escapeSqlLiteral;
import static org.apache.gravitino.rel.Column.DEFAULT_VALUE_NOT_SET;
import com.google.common.annotations.VisibleForTesting;
@@ -108,7 +109,7 @@ public class OceanBaseTableOperations extends
JdbcTableOperations {
// Add table comment if specified
if (StringUtils.isNotEmpty(comment)) {
- sqlBuilder.append(" COMMENT='").append(comment).append("'");
+ sqlBuilder.append(" COMMENT='").append(escapeSqlLiteral(comment,
'\'')).append("'");
}
// Add table properties
@@ -311,7 +312,7 @@ public class OceanBaseTableOperations extends
JdbcTableOperations {
newComment = StringIdentifier.addToComment(identifier, newComment);
}
}
- alterSql.add("COMMENT '" + newComment + "'");
+ alterSql.add("COMMENT '" + escapeSqlLiteral(newComment, '\'') + "'");
}
if (!setProperties.isEmpty()) {
@@ -476,7 +477,10 @@ public class OceanBaseTableOperations extends
JdbcTableOperations {
}
// Append comment if available
if (StringUtils.isNotEmpty(addColumn.getComment())) {
- columnDefinition.append("COMMENT
'").append(addColumn.getComment()).append("' ");
+ columnDefinition
+ .append("COMMENT '")
+ .append(escapeSqlLiteral(addColumn.getComment(), '\''))
+ .append("' ");
}
// Append default value if available
@@ -647,7 +651,7 @@ public class OceanBaseTableOperations extends
JdbcTableOperations {
// Add column comment if specified
if (StringUtils.isNotEmpty(column.comment())) {
- sqlBuilder.append("COMMENT '").append(column.comment()).append("' ");
+ sqlBuilder.append("COMMENT '").append(escapeSqlLiteral(column.comment(),
'\'')).append("' ");
}
return sqlBuilder;
}
diff --git
a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperations.java
b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperations.java
index 2a063d8a94..06fc9101ad 100644
---
a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperations.java
+++
b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperations.java
@@ -45,6 +45,8 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
@Tag("gravitino-docker-test")
public class TestOceanBaseTableOperations extends TestOceanBase {
@@ -56,6 +58,58 @@ public class TestOceanBaseTableOperations extends
TestOceanBase {
DATABASE_OPERATIONS.create(TEST_DB_NAME, null, new HashMap<>());
}
+ @ParameterizedTest
+ @ValueSource(strings = {"customer's id", "quote\\' and \\new\\path\\;
\"double\"\n中文"})
+ void testCommentRoundTrips(String comment) {
+ String tableName = RandomNameUtils.genRandomName("comment_round_trip");
+ JdbcColumn column =
+ JdbcColumn.builder()
+ .withName("col1")
+ .withType(VARCHAR)
+ .withNullable(true)
+ .withComment(comment)
+ .build();
+ try {
+ TABLE_OPERATIONS.create(
+ TEST_DB_NAME,
+ tableName,
+ new JdbcColumn[] {column},
+ comment,
+ Collections.emptyMap(),
+ Transforms.EMPTY_TRANSFORM,
+ Distributions.NONE,
+ Indexes.EMPTY_INDEXES);
+ JdbcTable table = TABLE_OPERATIONS.load(TEST_DB_NAME, tableName);
+ Assertions.assertEquals(comment, table.comment());
+ Assertions.assertEquals(comment, table.columns()[0].comment());
+
+ String updated = comment + " updated";
+ TABLE_OPERATIONS.alterTable(TEST_DB_NAME, tableName,
TableChange.updateComment(updated));
+ Assertions.assertEquals(updated, TABLE_OPERATIONS.load(TEST_DB_NAME,
tableName).comment());
+
+ TABLE_OPERATIONS.alterTable(
+ TEST_DB_NAME, tableName, TableChange.addColumn(new String[]
{"col2"}, VARCHAR, comment));
+ table = TABLE_OPERATIONS.load(TEST_DB_NAME, tableName);
+ Assertions.assertEquals(comment, table.columns()[1].comment());
+
+ TABLE_OPERATIONS.alterTable(
+ TEST_DB_NAME, tableName, TableChange.updateColumnComment(new
String[] {"col1"}, updated));
+ table = TABLE_OPERATIONS.load(TEST_DB_NAME, tableName);
+ Assertions.assertEquals(updated, table.columns()[0].comment());
+
+ // Rebuilding a column definition must also preserve an existing escaped
comment.
+ TABLE_OPERATIONS.alterTable(
+ TEST_DB_NAME,
+ tableName,
+ TableChange.updateColumnType(new String[] {"col1"},
Types.VarCharType.of(512)));
+ table = TABLE_OPERATIONS.load(TEST_DB_NAME, tableName);
+ Assertions.assertEquals(updated, table.columns()[0].comment());
+ Assertions.assertEquals(updated, table.comment());
+ } finally {
+ TABLE_OPERATIONS.drop(TEST_DB_NAME, tableName);
+ }
+ }
+
@Test
public void testOperationTable() {
String tableName = RandomStringUtils.randomAlphabetic(16).toLowerCase() +
"_op_table";
diff --git
a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java
b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java
index fb15beb030..aff7fb637b 100644
---
a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java
+++
b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java
@@ -20,11 +20,14 @@ package org.apache.gravitino.catalog.oceanbase.operation;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Stream;
import org.apache.gravitino.catalog.jdbc.JdbcColumn;
+import org.apache.gravitino.catalog.jdbc.JdbcTable;
import
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
import org.apache.gravitino.catalog.jdbc.utils.ConnectionCountingDataSource;
import org.apache.gravitino.catalog.oceanbase.converter.OceanBaseTypeConverter;
+import org.apache.gravitino.rel.TableChange;
import org.apache.gravitino.rel.expressions.distributions.Distributions;
import org.apache.gravitino.rel.expressions.literals.Literals;
import org.apache.gravitino.rel.expressions.transforms.Transforms;
@@ -32,6 +35,9 @@ import org.apache.gravitino.rel.indexes.Indexes;
import org.apache.gravitino.rel.types.Types;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
public class TestOceanBaseTableOperationsSqlGeneration {
@@ -59,6 +65,61 @@ public class TestOceanBaseTableOperationsSqlGeneration {
}
}
+ @ParameterizedTest
+ @MethodSource("comments")
+ void testCreateEscapesTableAndColumnComments(String comment, String escaped)
{
+ TestableOceanBaseTableOperations ops = new
TestableOceanBaseTableOperations();
+ String sql =
+ ops.generateCreateTableSql(
+ "test_table",
+ new JdbcColumn[] {column(comment)},
+ comment,
+ Collections.emptyMap(),
+ Transforms.EMPTY_TRANSFORM,
+ Distributions.NONE,
+ Indexes.EMPTY_INDEXES);
+
+ Assertions.assertTrue(sql.contains("COMMENT '" + escaped + "' "), sql);
+ Assertions.assertTrue(sql.endsWith("COMMENT='" + escaped + "';"), sql);
+ }
+
+ @ParameterizedTest
+ @MethodSource("comments")
+ void testUpdateTableCommentEscapesLiteral(String comment, String escaped) {
+ TestableOceanBaseTableOperations ops = operationsWithTable();
+ String sql =
+ ops.generateAlterTableSql("database", "test_table",
TableChange.updateComment(comment));
+
+ Assertions.assertEquals("ALTER TABLE `test_table`\nCOMMENT '" + escaped +
"';", sql);
+ }
+
+ @ParameterizedTest
+ @MethodSource("comments")
+ void testAddColumnEscapesComment(String comment, String escaped) {
+ TestableOceanBaseTableOperations ops = operationsWithTable();
+ String sql =
+ ops.generateAlterTableSql(
+ "database",
+ "test_table",
+ TableChange.addColumn(new String[] {"new_col"},
Types.IntegerType.get(), comment));
+
+ Assertions.assertTrue(sql.contains("COMMENT '" + escaped + "' "), sql);
+ }
+
+ @ParameterizedTest
+ @MethodSource("comments")
+ void testModifyColumnEscapesComment(String comment, String escaped) {
+ TestableOceanBaseTableOperations ops = operationsWithTable();
+ String sql =
+ ops.generateAlterTableSql(
+ "database",
+ "test_table",
+ TableChange.updateColumnComment(new String[] {"col1"}, comment));
+
+ Assertions.assertTrue(sql.contains("MODIFY COLUMN `col1`"), sql);
+ Assertions.assertTrue(sql.contains("COMMENT '" + escaped + "' "), sql);
+ }
+
@Test
void testPurgeIsRejectedWithoutExecutingSql() {
ConnectionCountingDataSource connections = new
ConnectionCountingDataSource();
@@ -124,4 +185,39 @@ public class TestOceanBaseTableOperationsSqlGeneration {
sql.contains("DEFAULT " +
converter.fromGravitino(col1.defaultValue())),
"Should contain DEFAULT value but was: " + sql);
}
+
+ private static JdbcColumn column(String comment) {
+ return JdbcColumn.builder()
+ .withName("col1")
+ .withType(Types.IntegerType.get())
+ .withNullable(true)
+ .withComment(comment)
+ .build();
+ }
+
+ private static TestableOceanBaseTableOperations operationsWithTable() {
+ JdbcTable table =
+ JdbcTable.builder()
+ .withName("test_table")
+ .withComment("old comment")
+ .withColumns(new JdbcColumn[] {column("old comment")})
+ .build();
+ return new TestableOceanBaseTableOperations() {
+ /** Returns the table fixture without accessing a database. */
+ @Override
+ public JdbcTable load(String databaseName, String tableName) {
+ Assertions.assertEquals("database", databaseName);
+ Assertions.assertEquals("test_table", tableName);
+ return table;
+ }
+ };
+ }
+
+ private static Stream<Arguments> comments() {
+ return Stream.of(
+ Arguments.of("customer's id", "customer''s id"),
+ Arguments.of("C:\\new\\trailing\\", "C:\\\\new\\\\trailing\\\\"),
+ Arguments.of("backslash\\'quote; --", "backslash\\\\''quote; --"),
+ Arguments.of("line1\n中文 \"double\"", "line1\n中文 \"double\""));
+ }
}