This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/dev/pr-12503-ce4071f0eacffb95c5c5d650273ab731931c58b5 in repository https://gitbox.apache.org/repos/asf/seatunnel.git
commit ed13548892eba42843cbde62d723c7786e3a1885 Author: hutiefang76 <[email protected]> AuthorDate: Sun Oct 4 22:59:41 2026 +0000 [Fix][Connector-V2] Escape DuckDB catalog metadata literals (#12503) --- .../jdbc/catalog/duckdb/DuckDBCatalog.java | 12 +++++++++-- .../jdbc/catalog/duckdb/DuckDBCatalogTest.java | 23 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalog.java b/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalog.java index c3742bdc62..e9652f129d 100644 --- a/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalog.java +++ b/seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalog.java @@ -159,7 +159,9 @@ public class DuckDBCatalog extends AbstractJdbcCatalog { protected String getSelectColumnsSql(TablePath tablePath) { return String.format( - SELECT_COLUMNS_SQL_TEMPLATE, tablePath.getSchemaName(), tablePath.getTableName()); + SELECT_COLUMNS_SQL_TEMPLATE, + escapeSqlLiteral(tablePath.getSchemaName()), + escapeSqlLiteral(tablePath.getTableName())); } @Override @@ -228,7 +230,13 @@ public class DuckDBCatalog extends AbstractJdbcCatalog { return String.format( "SELECT table_schema, table_name FROM information_schema.tables " + "WHERE table_schema = '%s' AND table_name = '%s'", - tablePath.getSchemaName(), tablePath.getTableName()); + escapeSqlLiteral(tablePath.getSchemaName()), + escapeSqlLiteral(tablePath.getTableName())); + } + + private String escapeSqlLiteral(String value) { + // Preserve the existing String.format behavior for a missing schema. + return value == null ? null : value.replace("'", "''"); } @Override diff --git a/seatunnel-connectors-v2/connector-jdbc/src/test/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalogTest.java b/seatunnel-connectors-v2/connector-jdbc/src/test/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalogTest.java index 49bec53042..90f171603f 100644 --- a/seatunnel-connectors-v2/connector-jdbc/src/test/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalogTest.java +++ b/seatunnel-connectors-v2/connector-jdbc/src/test/java/org/apache/seatunnel/connectors/seatunnel/jdbc/catalog/duckdb/DuckDBCatalogTest.java @@ -211,6 +211,29 @@ public class DuckDBCatalogTest { Assertions.assertFalse(catalog.tableExists(copyPath)); } + @Test + @Order(8) + public void testMetadataLookupWithApostrophes() throws Exception { + TablePath tablePath = TablePath.of(DATABASE_NAME, "odd'schema", "odd'table"); + try (Statement statement = catalog.getConnection(jdbcUrl).createStatement()) { + statement.execute("CREATE SCHEMA \"odd'schema\""); + statement.execute("CREATE TABLE \"odd'schema\".\"odd'table\" (id INTEGER)"); + try { + Assertions.assertTrue(catalog.tableExists(tablePath)); + Assertions.assertFalse( + catalog.tableExists(TablePath.of(DATABASE_NAME, null, "odd'table"))); + CatalogTable table = catalog.getTable(tablePath); + Assertions.assertEquals("id", table.getTableSchema().getColumns().get(0).getName()); + Assertions.assertFalse( + catalog.tableExists( + TablePath.of(DATABASE_NAME, "odd'schema", "x' OR 1=1 --"))); + } finally { + statement.execute("DROP TABLE \"odd'schema\".\"odd'table\""); + statement.execute("DROP SCHEMA \"odd'schema\""); + } + } + } + private void createTestTable(String tableName) throws Exception { Connection connection = catalog.getConnection(jdbcUrl); try (Statement statement = connection.createStatement()) {
