From e09bb9a62b20a8b87891da1240e09d8aea37f8fc Mon Sep 17 00:00:00 2001 From: Dongjoon Hyun Date: Sun, 14 Jun 2026 12:26:09 -0700 Subject: [PATCH] [SPARK-57446][SQL] Apply escapeSql to comment in JDBC table/schema comment queries --- .../org/apache/spark/sql/jdbc/JdbcDialects.scala | 4 ++-- .../org/apache/spark/sql/jdbc/MySQLDialect.scala | 2 +- .../scala/org/apache/spark/sql/jdbc/JDBCSuite.scala | 13 +++++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala index a34d23512e99..b0b10f1d09f2 100644 --- a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala +++ b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala @@ -716,11 +716,11 @@ abstract class JdbcDialect extends Serializable with Logging { } def getTableCommentQuery(table: String, comment: String): String = { - s"COMMENT ON TABLE $table IS '$comment'" + s"COMMENT ON TABLE $table IS '${escapeSql(comment)}'" } def getSchemaCommentQuery(schema: String, comment: String): String = { - s"COMMENT ON SCHEMA ${quoteIdentifier(schema)} IS '$comment'" + s"COMMENT ON SCHEMA ${quoteIdentifier(schema)} IS '${escapeSql(comment)}'" } def removeSchemaCommentQuery(schema: String): String = { diff --git a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MySQLDialect.scala b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MySQLDialect.scala index a047085a3537..fb7a1c6e617e 100644 --- a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MySQLDialect.scala +++ b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MySQLDialect.scala @@ -266,7 +266,7 @@ private case class MySQLDialect() extends JdbcDialect with SQLConfHelper with No // See https://dev.mysql.com/doc/refman/8.0/en/alter-table.html override def getTableCommentQuery(table: String, comment: String): String = { - s"ALTER TABLE $table COMMENT = '$comment'" + s"ALTER TABLE $table COMMENT = '${escapeSql(comment)}'" } override def getJDBCType(dt: DataType): Option[JdbcType] = dt match { diff --git a/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala index ea730abf67d6..9fcbc8b52d79 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala @@ -943,6 +943,19 @@ class JDBCSuite extends SharedSparkSession { assert(mySQLSQL(StringStartsWith("c", "a%b_")) === """`c` LIKE 'a\\%b\\_%' ESCAPE '\\'""") } + test("SPARK-57446: escape single quotes in JDBC comment queries") { + val defaultDialect = JdbcDialects.get("jdbc:") + assert(defaultDialect.getTableCommentQuery("t", "a'b") === + "COMMENT ON TABLE t IS 'a''b'") + assert(defaultDialect.getSchemaCommentQuery("s", "a'b") === + """COMMENT ON SCHEMA "s" IS 'a''b'""") + + // MySQL overrides getTableCommentQuery with its own ALTER TABLE syntax. + val mySQLDialect = JdbcDialects.get("jdbc:mysql://127.0.0.1/db") + assert(mySQLDialect.getTableCommentQuery("t", "a'b") === + "ALTER TABLE t COMMENT = 'a''b'") + } + test("Dialect unregister") { JdbcDialects.unregisterDialect(H2Dialect()) try {