This is an automated email from the ASF dual-hosted git repository.
szehon-ho pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git
The following commit(s) were added to refs/heads/main by this push:
new 4eb9cb7c3d Spark 4.2: Fix view rename target namespace (#18255)
4eb9cb7c3d is described below
commit 4eb9cb7c3d53cb8080724eebf6e6898871ee23b0
Author: Manu Zhang <[email protected]>
AuthorDate: Fri Oct 9 09:33:58 2026 +0800
Spark 4.2: Fix view rename target namespace (#18255)
---
.../v2/ExtendedDataSourceV2Strategy.scala | 25 ++++++++++++++-----
.../apache/iceberg/spark/extensions/TestViews.java | 28 ++++++++++++++++++++++
.../java/org/apache/iceberg/spark/Spark3Util.java | 3 +++
3 files changed, 50 insertions(+), 6 deletions(-)
diff --git
a/spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/ExtendedDataSourceV2Strategy.scala
b/spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/ExtendedDataSourceV2Strategy.scala
index 7d46b2e045..17d734deec 100644
---
a/spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/ExtendedDataSourceV2Strategy.scala
+++
b/spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/ExtendedDataSourceV2Strategy.scala
@@ -125,12 +125,8 @@ case class ExtendedDataSourceV2Strategy(spark:
SparkSession) extends Strategy wi
ResolvedV2View(oldCatalog: ViewCatalog, oldIdent, _),
newName,
isView @ true) =>
- val newIdent = Spark3Util.catalogAndIdentifier(spark,
newName.toList.asJava)
- if (oldCatalog.name != newIdent.catalog().name()) {
- throw new IcebergAnalysisException(
- s"Cannot move view between catalogs: from=${oldCatalog.name} and
to=${newIdent.catalog().name()}")
- }
- IcebergRenameV2ViewExec(oldCatalog, oldIdent, newIdent.identifier()) ::
Nil
+ val targetIdent = resolveViewRenameTarget(oldCatalog, newName)
+ IcebergRenameV2ViewExec(oldCatalog, oldIdent, targetIdent) :: Nil
case DropIcebergView(ResolvedIdentifier(viewCatalog: ViewCatalog, ident),
ifExists) =>
DropV2ViewExec(viewCatalog, ident, ifExists) :: Nil
@@ -189,6 +185,23 @@ case class ExtendedDataSourceV2Strategy(spark:
SparkSession) extends Strategy wi
case _ => Nil
}
+ private def resolveViewRenameTarget(
+ sourceCatalog: ViewCatalog,
+ targetName: Seq[String]): Identifier = {
+ if (targetName.length == 1) {
+ // An unqualified target keeps the source view's namespace
+ Identifier.of(Array.empty[String], targetName.head)
+ } else {
+ val target = Spark3Util.catalogAndIdentifier(spark,
targetName.toList.asJava, sourceCatalog)
+ if (sourceCatalog.name != target.catalog().name()) {
+ throw new IcebergAnalysisException(
+ s"Cannot move view between catalogs: from=${sourceCatalog.name} and
to=${target.catalog().name()}")
+ }
+
+ target.identifier()
+ }
+ }
+
private object IcebergCatalogAndIdentifier {
def unapply(identifier: Seq[String]): Option[(TableCatalog, Identifier)] =
{
val catalogAndIdentifier = Spark3Util.catalogAndIdentifier(spark,
identifier.asJava)
diff --git
a/spark/v4.2/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
b/spark/v4.2/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
index 254c45f64d..2d3953f549 100644
---
a/spark/v4.2/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
+++
b/spark/v4.2/spark-extensions/src/test/java/org/apache/iceberg/spark/extensions/TestViews.java
@@ -893,6 +893,34 @@ public class TestViews extends ExtensionsTestBase {
assertThat(viewCatalog().viewExists(TableIdentifier.of(NAMESPACE,
renamedView))).isTrue();
}
+ @TestTemplate
+ public void renameQualifiedViewToUnqualifiedTargetKeepsSourceNamespace() {
+ Namespace sourceNamespace = Namespace.of(viewName("rename_source_ns"));
+ Namespace currentNamespace = Namespace.of(viewName("rename_current_ns"));
+ String viewName = viewName("originalView");
+ String renamedView = viewName("renamedView");
+ String sql = String.format("SELECT id FROM %s.%s.%s", catalogName,
NAMESPACE, tableName);
+
+ sql("CREATE NAMESPACE IF NOT EXISTS %s.%s", catalogName, sourceNamespace);
+ sql("CREATE NAMESPACE IF NOT EXISTS %s.%s", catalogName, currentNamespace);
+
+ ViewCatalog viewCatalog = viewCatalog();
+ viewCatalog
+ .buildView(TableIdentifier.of(sourceNamespace, viewName))
+ .withQuery("spark", sql)
+ .withDefaultNamespace(sourceNamespace)
+ .withDefaultCatalog(catalogName)
+ .withSchema(schema(sql))
+ .create();
+
+ sql("USE %s.%s", catalogName, currentNamespace);
+ sql("ALTER VIEW %s.%s RENAME TO %s", sourceNamespace, viewName,
renamedView);
+
+ assertThat(viewCatalog.viewExists(TableIdentifier.of(sourceNamespace,
viewName))).isFalse();
+ assertThat(viewCatalog.viewExists(TableIdentifier.of(sourceNamespace,
renamedView))).isTrue();
+ assertThat(viewCatalog.viewExists(TableIdentifier.of(currentNamespace,
renamedView))).isFalse();
+ }
+
@TestTemplate
public void renameViewHiddenByTempView() throws NoSuchTableException {
insertRows(10);
diff --git
a/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java
b/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java
index 92ff7926cc..567743927e 100644
--- a/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java
+++ b/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java
@@ -821,6 +821,9 @@ public class Spark3Util {
* A modified version of Spark's LookupCatalog.CatalogAndIdentifier.unapply
Attempts to find the
* catalog and identifier a multipart identifier represents
*
+ * <p>A single-part name is resolved in the session's current namespace, or
in the default
+ * namespace of {@code defaultCatalog} if it is not the current catalog.
+ *
* @param spark Spark session to use for resolution
* @param nameParts Multipart identifier representing a table
* @param defaultCatalog Catalog to use if none is specified