Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,8 @@ case class ExtendedDataSourceV2Strategy(spark: SparkSession) extends Strategy wi
OrderAwareCoalesceExec(numPartitions, coalescer, planLater(child)) :: Nil

case RenameTable(ResolvedV2View(oldCatalog: ViewCatalog, oldIdent), newName, isView @ true) =>
val newIdent = Spark3Util.catalogAndIdentifier(spark, newName.toList.asJava)
if (oldCatalog.name != newIdent.catalog().name()) {
throw new AnalysisException(
s"Cannot move view between catalogs: from=${oldCatalog.name} and to=${newIdent.catalog().name()}")
}
RenameV2ViewExec(oldCatalog, oldIdent, newIdent.identifier()) :: Nil
val targetIdent = resolveViewRenameTarget(oldCatalog, newName)
RenameV2ViewExec(oldCatalog, oldIdent, targetIdent) :: Nil

case DropIcebergView(ResolvedIdentifier(viewCatalog: ViewCatalog, ident), ifExists) =>
DropV2ViewExec(viewCatalog, ident, ifExists) :: Nil
Expand Down Expand Up @@ -192,6 +188,23 @@ case class ExtendedDataSourceV2Strategy(spark: SparkSession) extends Strategy wi
new GenericInternalRow(values)
}

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 AnalysisException(
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ case class RenameV2ViewExec(catalog: ViewCatalog, oldIdent: Identifier, newIdent
override lazy val output: Seq[Attribute] = Nil

override protected def run(): Seq[InternalRow] = {
catalog.renameView(oldIdent, newIdent)
val qualifiedNewIdent = if (newIdent.namespace.isEmpty) {
Identifier.of(oldIdent.namespace, newIdent.name)
} else {
newIdent
}

catalog.renameView(oldIdent, qualifiedNewIdent)

Seq.empty
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,34 @@ public void renameView() throws NoSuchTableException {
.containsExactlyInAnyOrderElementsOf(expected);
}

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,9 @@ public static CatalogAndIdentifier catalogAndIdentifier(
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,8 @@ case class ExtendedDataSourceV2Strategy(spark: SparkSession) extends Strategy wi
OrderAwareCoalesceExec(numPartitions, coalescer, planLater(child)) :: Nil

case RenameTable(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()}")
}
RenameV2ViewExec(oldCatalog, oldIdent, newIdent.identifier()) :: Nil
val targetIdent = resolveViewRenameTarget(oldCatalog, newName)
RenameV2ViewExec(oldCatalog, oldIdent, targetIdent) :: Nil

case DropIcebergView(ResolvedIdentifier(viewCatalog: ViewCatalog, ident), ifExists) =>
DropV2ViewExec(viewCatalog, ident, ifExists) :: Nil
Expand Down Expand Up @@ -176,6 +172,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ case class RenameV2ViewExec(catalog: ViewCatalog, oldIdent: Identifier, newIdent
override lazy val output: Seq[Attribute] = Nil

override protected def run(): Seq[InternalRow] = {
catalog.renameView(oldIdent, newIdent)
val qualifiedNewIdent = if (newIdent.namespace.isEmpty) {
Identifier.of(oldIdent.namespace, newIdent.name)
} else {
newIdent
}

catalog.renameView(oldIdent, qualifiedNewIdent)

Seq.empty
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,34 @@ public void renameView() throws NoSuchTableException {
.containsExactlyInAnyOrderElementsOf(expected);
}

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,9 @@ public static CatalogAndIdentifier catalogAndIdentifier(
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,8 @@ case class ExtendedDataSourceV2Strategy(spark: SparkSession) extends Strategy wi
OrderAwareCoalesceExec(numPartitions, coalescer, planLater(child)) :: Nil

case RenameTable(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()}")
}
RenameV2ViewExec(oldCatalog, oldIdent, newIdent.identifier()) :: Nil
val targetIdent = resolveViewRenameTarget(oldCatalog, newName)
RenameV2ViewExec(oldCatalog, oldIdent, targetIdent) :: Nil

case DropIcebergView(ResolvedIdentifier(viewCatalog: ViewCatalog, ident), ifExists) =>
DropV2ViewExec(viewCatalog, ident, ifExists) :: Nil
Expand Down Expand Up @@ -176,6 +172,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ case class RenameV2ViewExec(catalog: ViewCatalog, oldIdent: Identifier, newIdent
override lazy val output: Seq[Attribute] = Nil

override protected def run(): Seq[InternalRow] = {
catalog.renameView(oldIdent, newIdent)
val qualifiedNewIdent = if (newIdent.namespace.isEmpty) {
Identifier.of(oldIdent.namespace, newIdent.name)
} else {
newIdent
}

catalog.renameView(oldIdent, qualifiedNewIdent)

Seq.empty
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,34 @@ public void renameView() throws NoSuchTableException {
.containsExactlyInAnyOrderElementsOf(expected);
}

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,9 @@ public static CatalogAndIdentifier catalogAndIdentifier(
* 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
Expand Down
Loading