diff --git a/connector/docker-integration-tests/src/test/scala/org/apache/spark/sql/jdbc/v2/V2JDBCTest.scala b/connector/docker-integration-tests/src/test/scala/org/apache/spark/sql/jdbc/v2/V2JDBCTest.scala index c78e87d0b8463..ac1294c2804a4 100644 --- a/connector/docker-integration-tests/src/test/scala/org/apache/spark/sql/jdbc/v2/V2JDBCTest.scala +++ b/connector/docker-integration-tests/src/test/scala/org/apache/spark/sql/jdbc/v2/V2JDBCTest.scala @@ -942,4 +942,19 @@ private[v2] trait V2JDBCTest extends SharedSparkSession with DockerIntegrationFu assert(row(2).getDouble(0) === 0.0) } } + + test("SPARK-48618: Renaming the table to the name of an existing table") { + withTable(s"$catalogName.tbl1", s"$catalogName.tbl2") { + sql(s"CREATE TABLE $catalogName.tbl1 (col1 INT, col2 INT)") + sql(s"CREATE TABLE $catalogName.tbl2 (col3 INT, col4 INT)") + + checkError( + exception = intercept[AnalysisException] { + sql(s"ALTER TABLE $catalogName.tbl2 RENAME TO tbl1") + }, + errorClass = "TABLE_OR_VIEW_ALREADY_EXISTS", + parameters = Map("relationName" -> "`tbl1`") + ) + } + } } diff --git a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/DB2Dialect.scala b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/DB2Dialect.scala index 934ccdb51aa39..91dec4d700a77 100644 --- a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/DB2Dialect.scala +++ b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/DB2Dialect.scala @@ -28,6 +28,7 @@ import org.apache.spark.sql.catalyst.SQLConfHelper import org.apache.spark.sql.catalyst.analysis.NonEmptyNamespaceException import org.apache.spark.sql.connector.catalog.Identifier import org.apache.spark.sql.connector.expressions.Expression +import org.apache.spark.sql.errors.QueryCompilationErrors import org.apache.spark.sql.execution.datasources.jdbc.JDBCOptions import org.apache.spark.sql.types._ @@ -167,6 +168,9 @@ private case class DB2Dialect() extends JdbcDialect with SQLConfHelper { namespace = messageParameters.get("namespace").toArray, details = sqlException.getMessage, cause = Some(e)) + case "42710" if errorClass == "FAILED_JDBC.RENAME_TABLE" => + val newTable = messageParameters("newName") + throw QueryCompilationErrors.tableAlreadyExistsError(newTable) case _ => super.classifyException(e, errorClass, messageParameters, description) } case _ => super.classifyException(e, errorClass, messageParameters, description) diff --git a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MsSqlServerDialect.scala b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MsSqlServerDialect.scala index e341bf3720f46..ce0d528c6f48a 100644 --- a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MsSqlServerDialect.scala +++ b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/MsSqlServerDialect.scala @@ -27,7 +27,7 @@ import org.apache.spark.sql.catalyst.analysis.NonEmptyNamespaceException import org.apache.spark.sql.connector.catalog.Identifier import org.apache.spark.sql.connector.expressions.{Expression, NullOrdering, SortDirection} import org.apache.spark.sql.connector.expressions.filter.Predicate -import org.apache.spark.sql.errors.QueryExecutionErrors +import org.apache.spark.sql.errors.{QueryCompilationErrors, QueryExecutionErrors} import org.apache.spark.sql.execution.datasources.jdbc.JDBCOptions import org.apache.spark.sql.internal.SQLConf import org.apache.spark.sql.jdbc.MsSqlServerDialect.{GEOGRAPHY, GEOMETRY} @@ -216,6 +216,9 @@ private case class MsSqlServerDialect() extends JdbcDialect { namespace = messageParameters.get("namespace").toArray, details = sqlException.getMessage, cause = Some(e)) + case 15335 if errorClass == "FAILED_JDBC.RENAME_TABLE" => + val newTable = messageParameters("newName") + throw QueryCompilationErrors.tableAlreadyExistsError(newTable) case _ => super.classifyException(e, errorClass, messageParameters, description) } case _ => super.classifyException(e, errorClass, messageParameters, description) 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 d2034812cdd3a..aaca93bbeac09 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 @@ -31,7 +31,7 @@ import org.apache.spark.sql.catalyst.analysis.{IndexAlreadyExistsException, NoSu import org.apache.spark.sql.connector.catalog.Identifier import org.apache.spark.sql.connector.catalog.index.TableIndex import org.apache.spark.sql.connector.expressions.{Expression, FieldReference, NamedReference, NullOrdering, SortDirection} -import org.apache.spark.sql.errors.QueryExecutionErrors +import org.apache.spark.sql.errors.{QueryCompilationErrors, QueryExecutionErrors} import org.apache.spark.sql.execution.datasources.jdbc.{JDBCOptions, JdbcUtils} import org.apache.spark.sql.types._ @@ -337,6 +337,9 @@ private case class MySQLDialect() extends JdbcDialect with SQLConfHelper { case sqlException: SQLException => sqlException.getErrorCode match { // ER_DUP_KEYNAME + case 1050 if errorClass == "FAILED_JDBC.RENAME_TABLE" => + val newTable = messageParameters("newName") + throw QueryCompilationErrors.tableAlreadyExistsError(newTable) case 1061 if errorClass == "FAILED_JDBC.CREATE_INDEX" => val indexName = messageParameters("indexName") val tableName = messageParameters("tableName") diff --git a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/OracleDialect.scala b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/OracleDialect.scala index 38fee89bf726a..6cc82cce16145 100644 --- a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/OracleDialect.scala +++ b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/OracleDialect.scala @@ -17,14 +17,16 @@ package org.apache.spark.sql.jdbc -import java.sql.{Date, Timestamp, Types} +import java.sql.{Date, SQLException, Timestamp, Types} import java.util.Locale import scala.util.control.NonFatal import org.apache.spark.SparkUnsupportedOperationException +import org.apache.spark.sql.AnalysisException import org.apache.spark.sql.catalyst.SQLConfHelper import org.apache.spark.sql.connector.expressions.Expression +import org.apache.spark.sql.errors.QueryCompilationErrors import org.apache.spark.sql.execution.datasources.jdbc.JDBCOptions import org.apache.spark.sql.jdbc.OracleDialect._ import org.apache.spark.sql.types._ @@ -229,6 +231,23 @@ private case class OracleDialect() extends JdbcDialect with SQLConfHelper { override def supportsLimit: Boolean = true override def supportsOffset: Boolean = true + + override def classifyException( + e: Throwable, + errorClass: String, + messageParameters: Map[String, String], + description: String): AnalysisException = { + e match { + case sqlException: SQLException => + sqlException.getErrorCode match { + case 955 if errorClass == "FAILED_JDBC.RENAME_TABLE" => + val newTable = messageParameters("newName") + throw QueryCompilationErrors.tableAlreadyExistsError(newTable) + case _ => super.classifyException(e, errorClass, messageParameters, description) + } + case _ => super.classifyException(e, errorClass, messageParameters, description) + } + } } private[jdbc] object OracleDialect {