From 44dddb15ca169c0bb5c955426f4e1077678155d5 Mon Sep 17 00:00:00 2001 From: Gengliang Wang Date: Tue, 28 Apr 2026 11:52:41 -0700 Subject: [PATCH 1/2] Rename MetadataOnlyTable -> MetadataTable, RelationCatalog -> TableViewCatalog, loadRelation -> loadTableOrView - MetadataTable drops the "Only" qualifier; reads naturally as the Table backed by a TableInfo (and ViewInfo via subclass). - TableViewCatalog explicitly names what the interface covers and avoids overloading Spark's "Relation" terminology (BaseRelation, LogicalRelation, etc.). - TableViewCatalog.loadTableOrView is the single-RPC perf entry point; the new name matches the contract on the interface ("returns a Table or a view"). The unrelated helpers CatalogV2Util.loadRelation and the private RelationResolution.loadRelation (V2TableReference -> LogicalPlan) keep their names. Test fixture TestingRelationCatalog -> TestingTableViewCatalog. Suites DataSourceV2MetadataOnlyTableSuite/DataSourceV2MetadataOnlyViewSuite -> drop "Only" to mirror the class. --- ...adataOnlyTable.java => MetadataTable.java} | 16 ++-- .../sql/connector/catalog/TableCatalog.java | 2 +- ...tionCatalog.java => TableViewCatalog.java} | 44 +++++------ .../sql/connector/catalog/ViewCatalog.java | 4 +- .../spark/sql/connector/catalog/ViewInfo.java | 6 +- .../sql/catalyst/analysis/Analyzer.scala | 14 ++-- .../analysis/RelationResolution.scala | 23 +++--- .../sql/catalyst/catalog/SessionCatalog.scala | 2 +- .../sql/catalyst/catalog/interface.scala | 2 +- .../sql/connector/catalog/Catalogs.scala | 12 +-- .../spark/sql/connector/catalog/V1Table.scala | 2 +- .../analysis/ResolveSessionCatalog.scala | 2 +- ...a => DataSourceV2MetadataTableSuite.scala} | 20 ++--- ...la => DataSourceV2MetadataViewSuite.scala} | 74 +++++++++---------- 14 files changed, 112 insertions(+), 111 deletions(-) rename sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/{MetadataOnlyTable.java => MetadataTable.java} (82%) rename sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/{RelationCatalog.java => TableViewCatalog.java} (85%) rename sql/core/src/test/scala/org/apache/spark/sql/connector/{DataSourceV2MetadataOnlyTableSuite.scala => DataSourceV2MetadataTableSuite.scala} (90%) rename sql/core/src/test/scala/org/apache/spark/sql/connector/{DataSourceV2MetadataOnlyViewSuite.scala => DataSourceV2MetadataViewSuite.scala} (95%) diff --git a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/MetadataOnlyTable.java b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/MetadataTable.java similarity index 82% rename from sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/MetadataOnlyTable.java rename to sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/MetadataTable.java index b20a9b566646f..36af9f999d352 100644 --- a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/MetadataOnlyTable.java +++ b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/MetadataTable.java @@ -33,17 +33,17 @@ * (for views) at read time. *

* Catalogs build the metadata via {@link TableInfo.Builder} (for data-source tables) or - * {@link ViewInfo.Builder} (for views). A {@code MetadataOnlyTable} wrapping a + * {@link ViewInfo.Builder} (for views). A {@code MetadataTable} wrapping a * {@link TableInfo} can be returned from {@link TableCatalog#loadTable(Identifier)} for a - * data-source table; a {@code MetadataOnlyTable} wrapping a {@link ViewInfo} can be returned - * from {@link RelationCatalog#loadRelation(Identifier)} as the single-RPC perf opt-in for a view. - * Downstream consumers distinguish the two by checking + * data-source table; a {@code MetadataTable} wrapping a {@link ViewInfo} can be returned + * from {@link TableViewCatalog#loadTableOrView(Identifier)} as the single-RPC perf opt-in + * for a view. Downstream consumers distinguish the two by checking * {@code getTableInfo() instanceof ViewInfo}. * * @since 4.2.0 */ @Evolving -public class MetadataOnlyTable implements Table { +public class MetadataTable implements Table { private final TableInfo info; private final String name; @@ -51,12 +51,12 @@ public class MetadataOnlyTable implements Table { * @param info metadata for the table or view. Pass a {@link ViewInfo} for a view. * @param name human-readable name for this table, used by places that read {@link #name()} * (e.g. the {@code Name} row of {@code DESCRIBE TABLE EXTENDED}). Catalogs - * returning a {@code MetadataOnlyTable} from {@link TableCatalog#loadTable} or - * {@link RelationCatalog#loadRelation} should typically pass + * returning a {@code MetadataTable} from {@link TableCatalog#loadTable} or + * {@link TableViewCatalog#loadTableOrView} should typically pass * {@code ident.toString()}, matching the quoted multi-part form used elsewhere * for v2 identifiers. */ - public MetadataOnlyTable(TableInfo info, String name) { + public MetadataTable(TableInfo info, String name) { this.info = Objects.requireNonNull(info, "info should not be null"); this.name = Objects.requireNonNull(name, "name should not be null"); } diff --git a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalog.java b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalog.java index 55894357f19d1..a6f51342aef59 100644 --- a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalog.java +++ b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCatalog.java @@ -34,7 +34,7 @@ * Catalog API for connectors that expose tables. *

* Connectors that expose only tables implement this interface. Connectors that expose - * both tables and views must implement {@link RelationCatalog} (which extends both this + * both tables and views must implement {@link TableViewCatalog} (which extends both this * interface and {@link ViewCatalog} and adds the cross-cutting contract for the combined * case); the methods on this interface remain table-only -- they do not interact with views. *

diff --git a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/RelationCatalog.java b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableViewCatalog.java similarity index 85% rename from sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/RelationCatalog.java rename to sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableViewCatalog.java index bb674faa10ac5..fadb3c4de8038 100644 --- a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/RelationCatalog.java +++ b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableViewCatalog.java @@ -27,15 +27,15 @@ * Catalog API for connectors that expose both tables and views in a single shared identifier * namespace. *

- * Connectors that expose both tables and views must implement {@code RelationCatalog}; + * Connectors that expose both tables and views must implement {@code TableViewCatalog}; * implementing {@link TableCatalog} and {@link ViewCatalog} directly without - * {@code RelationCatalog} is rejected at catalog initialization. Connectors that expose only + * {@code TableViewCatalog} is rejected at catalog initialization. Connectors that expose only * tables implement just {@link TableCatalog}; connectors that expose only views implement just * {@link ViewCatalog}; this interface is not relevant to them. * *

Two principles

* - * A {@code RelationCatalog} follows two rules that, taken together, define every cross-cutting + * A {@code TableViewCatalog} follows two rules that, taken together, define every cross-cutting * subtlety: *
    *
  1. Orthogonal interfaces. Every {@link TableCatalog} method behaves as if views did @@ -97,11 +97,11 @@ *

    Single-RPC perf entry points

    * * The orthogonal {@link TableCatalog} and {@link ViewCatalog} answer two cross-cutting - * questions in two round trips each. {@code RelationCatalog} adds dedicated methods so a + * questions in two round trips each. {@code TableViewCatalog} adds dedicated methods so a * catalog can answer both in one round trip: *
      - *
    • {@link #loadRelation(Identifier)} -- the resolver's per-identifier read path. Returns - * a regular {@link Table} for a table, or a {@link MetadataOnlyTable} wrapping a + *
    • {@link #loadTableOrView(Identifier)} -- the resolver's per-identifier read path. Returns + * a regular {@link Table} for a table, or a {@link MetadataTable} wrapping a * {@link ViewInfo} for a view. Saves the {@code loadTable} -> {@code loadView} fallback * on a cold cache.
    • *
    • {@link #listRelationSummaries(String[])} -- a unified listing of tables and views with the @@ -113,22 +113,22 @@ * @since 4.2.0 */ @Evolving -public interface RelationCatalog extends TableCatalog, ViewCatalog { +public interface TableViewCatalog extends TableCatalog, ViewCatalog { /** * Load metadata for an identifier that may resolve to either a table or a view. *

      * For a table, returns the table's {@link Table}. For a view, returns a - * {@link MetadataOnlyTable} wrapping a {@link ViewInfo}; callers discriminate via + * {@link MetadataTable} wrapping a {@link ViewInfo}; callers discriminate via * {@code getTableInfo() instanceof ViewInfo}. This lets the resolver answer in a single RPC * instead of falling back from {@link TableCatalog#loadTable} to {@link ViewCatalog#loadView}. * * @param ident the identifier - * @return a {@link Table} for tables, or a {@link MetadataOnlyTable} wrapping a + * @return a {@link Table} for tables, or a {@link MetadataTable} wrapping a * {@link ViewInfo} for views * @throws NoSuchTableException if neither a table nor a view exists at {@code ident} */ - Table loadRelation(Identifier ident) throws NoSuchTableException; + Table loadTableOrView(Identifier ident) throws NoSuchTableException; /** * List the tables and views in a namespace, returned as {@link TableSummary} entries with @@ -162,14 +162,14 @@ default TableSummary[] listRelationSummaries(String[] namespace) /** * {@inheritDoc} *

      - * The default implementation derives from {@link #loadRelation}: a {@link MetadataOnlyTable} + * The default implementation derives from {@link #loadTableOrView}: a {@link MetadataTable} * wrapping a {@link ViewInfo} is rejected as not-a-table; anything else is returned. Override * only if a tables-only path is materially cheaper than the unified one. */ @Override default Table loadTable(Identifier ident) throws NoSuchTableException { - Table t = loadRelation(ident); - if (t instanceof MetadataOnlyTable mot && mot.getTableInfo() instanceof ViewInfo) { + Table t = loadTableOrView(ident); + if (t instanceof MetadataTable mt && mt.getTableInfo() instanceof ViewInfo) { throw new NoSuchTableException(ident); } return t; @@ -178,7 +178,7 @@ default Table loadTable(Identifier ident) throws NoSuchTableException { /** * {@inheritDoc} *

      - * The default implementation derives from {@link #loadRelation}: a {@link MetadataOnlyTable} + * The default implementation derives from {@link #loadTableOrView}: a {@link MetadataTable} * wrapping a {@link ViewInfo} is unwrapped and returned; anything else (table or absent) is * surfaced as {@link NoSuchViewException}. Override only if a views-only path is materially * cheaper than the unified one. @@ -187,11 +187,11 @@ default Table loadTable(Identifier ident) throws NoSuchTableException { default ViewInfo loadView(Identifier ident) throws NoSuchViewException { Table t; try { - t = loadRelation(ident); + t = loadTableOrView(ident); } catch (NoSuchTableException e) { throw new NoSuchViewException(ident); } - if (t instanceof MetadataOnlyTable mot && mot.getTableInfo() instanceof ViewInfo vi) { + if (t instanceof MetadataTable mt && mt.getTableInfo() instanceof ViewInfo vi) { return vi; } throw new NoSuchViewException(ident); @@ -200,14 +200,14 @@ default ViewInfo loadView(Identifier ident) throws NoSuchViewException { /** * {@inheritDoc} *

      - * The default implementation derives from {@link #loadRelation}: returns {@code true} only if + * The default implementation derives from {@link #loadTableOrView}: returns {@code true} only if * the entry exists and is not a view. Override only if a cheaper existence-check path exists. */ @Override default boolean tableExists(Identifier ident) { try { - Table t = loadRelation(ident); - return !(t instanceof MetadataOnlyTable mot && mot.getTableInfo() instanceof ViewInfo); + Table t = loadTableOrView(ident); + return !(t instanceof MetadataTable mt && mt.getTableInfo() instanceof ViewInfo); } catch (NoSuchTableException e) { return false; } @@ -216,14 +216,14 @@ default boolean tableExists(Identifier ident) { /** * {@inheritDoc} *

      - * The default implementation derives from {@link #loadRelation}: returns {@code true} only if + * The default implementation derives from {@link #loadTableOrView}: returns {@code true} only if * the entry exists and is a view. Override only if a cheaper existence-check path exists. */ @Override default boolean viewExists(Identifier ident) { try { - Table t = loadRelation(ident); - return t instanceof MetadataOnlyTable mot && mot.getTableInfo() instanceof ViewInfo; + Table t = loadTableOrView(ident); + return t instanceof MetadataTable mt && mt.getTableInfo() instanceof ViewInfo; } catch (NoSuchTableException e) { return false; } diff --git a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewCatalog.java b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewCatalog.java index 184676023d7c4..be2bc8dacc4da 100644 --- a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewCatalog.java +++ b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewCatalog.java @@ -25,7 +25,7 @@ * Catalog API for connectors that expose views. *

      * Connectors that expose only views implement this interface. Connectors that expose - * both tables and views must implement {@link RelationCatalog} (which extends both this + * both tables and views must implement {@link TableViewCatalog} (which extends both this * interface and {@link TableCatalog} and adds the cross-cutting contract for the combined * case); the methods on this interface remain view-only -- they do not interact with tables. *

      @@ -126,7 +126,7 @@ ViewInfo createView(Identifier ident, ViewInfo info) * concurrent {@code CREATE VIEW} won the race in the * default impl's gap between {@link #replaceView} and * the fallback {@link #createView}, or, in a - * {@link RelationCatalog}, a table sits at {@code ident} + * {@link TableViewCatalog}, a table sits at {@code ident} * @throws NoSuchNamespaceException if the identifier's namespace does not exist (optional) */ default ViewInfo createOrReplaceView(Identifier ident, ViewInfo info) diff --git a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewInfo.java b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewInfo.java index da82de01f8e4d..f70e3649b3f6c 100644 --- a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewInfo.java +++ b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/ViewInfo.java @@ -31,9 +31,9 @@ * query output column names. Schema and user TBLPROPERTIES are inherited from {@link TableInfo} * via the typed builder. *

      - * {@code ViewInfo} extends {@link TableInfo} so that a {@link RelationCatalog} can opt into the - * single-RPC perf path by returning a {@link MetadataOnlyTable} wrapping a {@code ViewInfo} - * from {@link RelationCatalog#loadRelation} for a view identifier. Pure {@link ViewCatalog} + * {@code ViewInfo} extends {@link TableInfo} so that a {@link TableViewCatalog} can opt into the + * single-RPC perf path by returning a {@link MetadataTable} wrapping a {@code ViewInfo} + * from {@link TableViewCatalog#loadTableOrView} for a view identifier. Pure {@link ViewCatalog} * implementations never see {@code TableInfo}; the typed setters on {@link Builder} cover * everything they need to construct a {@code ViewInfo}. * diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala index a72824f953c08..9bbe4e65faf82 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala @@ -1131,8 +1131,8 @@ class Analyzer( * so surfacing a downstream "view not found" would hide the real reason. * * Lookup order against a non-session catalog: - * 1. If the catalog is a [[RelationCatalog]], [[RelationCatalog.loadRelation]] is called - * once. A returned [[MetadataOnlyTable]] wrapping a [[ViewInfo]] is interpreted as a + * 1. If the catalog is a [[TableViewCatalog]], [[TableViewCatalog.loadTableOrView]] is + * called once. A returned [[MetadataTable]] wrapping a [[ViewInfo]] is interpreted as a * view; other results are tables. * 2. Otherwise, [[TableCatalog.loadTable]] is tried (when implemented), then * [[ViewCatalog.loadView]] as the fallback view-resolution path (when implemented). @@ -1150,13 +1150,13 @@ class Analyzer( throw QueryCompilationErrors.missingCatalogViewsAbilityError(catalog) } catalog match { - case mc: RelationCatalog => - // Single-RPC perf path: loadRelation returns a Table for a table or a - // MetadataOnlyTable wrapping a ViewInfo for a view. NoSuchTable means + case mc: TableViewCatalog => + // Single-RPC perf path: loadTableOrView returns a Table for a table or a + // MetadataTable wrapping a ViewInfo for a view. NoSuchTable means // neither exists. try { - Some(mc.loadRelation(ident) match { - case t: MetadataOnlyTable if t.getTableInfo.isInstanceOf[ViewInfo] => + Some(mc.loadTableOrView(ident) match { + case t: MetadataTable if t.getTableInfo.isInstanceOf[ViewInfo] => ResolvedPersistentView( catalog, ident, V1Table.toCatalogTable(catalog, ident, t)) case table => diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RelationResolution.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RelationResolution.scala index 7a5077a8a3e11..e2ed60533cde1 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RelationResolution.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/RelationResolution.scala @@ -37,10 +37,10 @@ import org.apache.spark.sql.connector.catalog.{ ChangelogInfo, Identifier, LookupCatalog, - MetadataOnlyTable, - RelationCatalog, + MetadataTable, Table, TableCatalog, + TableViewCatalog, V1Table, V2TableWithV1Fallback, ViewCatalog, @@ -262,8 +262,8 @@ class RelationResolution( .orElse { val writePrivileges = u.options.get(UnresolvedRelation.REQUIRED_WRITE_PRIVILEGES) val finalOptions = u.clearWritePrivileges.options - // For a `RelationCatalog` with no time-travel / write privileges, the single-RPC - // `loadRelation` answers both "is there a table?" and "is there a view?" in one + // For a `TableViewCatalog` with no time-travel / write privileges, the single-RPC + // `loadTableOrView` answers both "is there a table?" and "is there a view?" in one // call. Time-travel and write privileges apply to tables only, so for those the // lookup falls through to the table-only `loadTable` path below; views are not // reachable via the v2 fallback in those cases. @@ -272,9 +272,10 @@ class RelationResolution( // mixin): `CatalogV2Util.loadTable` would call `asTableCatalog` and throw // MISSING_CATALOG_ABILITY.TABLES, masking the legitimate view-resolution path. val tableOrView: Option[Table] = catalog match { - case mc: RelationCatalog if finalTimeTravelSpec.isEmpty && writePrivileges == null => + case mc: TableViewCatalog + if finalTimeTravelSpec.isEmpty && writePrivileges == null => try { - Some(mc.loadRelation(ident)) + Some(mc.loadTableOrView(ident)) } catch { case _: NoSuchTableException => None } @@ -299,7 +300,7 @@ class RelationResolution( catalog match { case vc: ViewCatalog => try { - Some(new MetadataOnlyTable(vc.loadView(ident), ident.toString)) + Some(new MetadataTable(vc.loadView(ident), ident.toString)) } catch { case _: NoSuchViewException => None } @@ -313,7 +314,7 @@ class RelationResolution( // `table` is `tableOrView` filtered to tables only -- used for cache lookup since // we don't share-cache views. val table: Option[Table] = tableOrView.filter { - case t: MetadataOnlyTable if t.getTableInfo.isInstanceOf[ViewInfo] => false + case t: MetadataTable if t.getTableInfo.isInstanceOf[ViewInfo] => false case _ => true } @@ -426,10 +427,10 @@ class RelationResolution( || !v1Table.catalogTable.tracksPartitionsInCatalog => createDataSourceV1Scan(v1Table.v1Table) - // MetadataOnlyTable is a sentinel meaning "interpret via v1", so unlike the V1Table + // MetadataTable is a sentinel meaning "interpret via v1", so unlike the V1Table // case above we apply no session-catalog / tracksPartitionsInCatalog guard -- any catalog - // returning MetadataOnlyTable has opted into v1 read semantics. - case t: MetadataOnlyTable => + // returning MetadataTable has opted into v1 read semantics. + case t: MetadataTable => createDataSourceV1Scan(V1Table.toCatalogTable(catalog, ident, t)) case table => diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/SessionCatalog.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/SessionCatalog.scala index af398eb8527e9..a3efade9b9a1c 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/SessionCatalog.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/SessionCatalog.scala @@ -1058,7 +1058,7 @@ class SessionCatalog( // so the SubqueryAlias qualifier reflects the real catalog + multi-part namespace. // Fall back to the historical 3-part form for v1 session-catalog tables -- we intentionally // always include `SESSION_CATALOG_NAME` here and ignore - // `LEGACY_NON_IDENTIFIER_OUTPUT_CATALOG_NAME` to preserve pre-v2-MetadataOnlyTable behavior. + // `LEGACY_NON_IDENTIFIER_OUTPUT_CATALOG_NAME` to preserve pre-v2-MetadataTable behavior. val multiParts = metadata.multipartIdentifier.getOrElse { val qualifiedIdent = qualifyIdentifier(metadata.identifier) Seq(CatalogManager.SESSION_CATALOG_NAME, qualifiedIdent.database.get, qualifiedIdent.table) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/interface.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/interface.scala index 981b2ac96a37a..1c4362bfd3ed7 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/interface.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/interface.scala @@ -447,7 +447,7 @@ case class CatalogTable( ignoredProperties: Map[String, String] = Map.empty, viewOriginalText: Option[String] = None, // Multi-part identifier [catalog, namespace..., name] for tables synthesized from a v2 - // `MetadataOnlyTable` whose namespace has more than one part -- the v1 `identifier: + // `MetadataTable` whose namespace has more than one part -- the v1 `identifier: // TableIdentifier` (single-string database) cannot carry that losslessly. `None` for // v1-native tables; callers should use `fullIdent` which falls back to `identifier.nameParts`. multipartIdentifier: Option[Seq[String]] = None) diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/Catalogs.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/Catalogs.scala index 03addeb170697..c40d5ab679190 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/Catalogs.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/Catalogs.scala @@ -64,7 +64,7 @@ private[sql] object Catalogs { } val plugin = pluginClass.getDeclaredConstructor().newInstance().asInstanceOf[CatalogPlugin] plugin.initialize(name, catalogOptions(name, conf)) - validateRelationCatalog(name, plugin) + validateTableViewCatalog(name, plugin) plugin } catch { case e: ClassNotFoundException => @@ -110,17 +110,17 @@ private[sql] object Catalogs { /** * Reject catalogs that implement both [[TableCatalog]] and [[ViewCatalog]] without - * extending [[RelationCatalog]]. The combined case has cross-cutting rules (single namespace, - * cross-type collision rejection, perf opt-ins) that live on [[RelationCatalog]]; implementing + * extending [[TableViewCatalog]]. The combined case has cross-cutting rules (single namespace, + * cross-type collision rejection, perf opt-ins) that live on [[TableViewCatalog]]; implementing * the two interfaces directly would skip that contract. */ - private def validateRelationCatalog(name: String, plugin: CatalogPlugin): Unit = { + private def validateTableViewCatalog(name: String, plugin: CatalogPlugin): Unit = { if (plugin.isInstanceOf[TableCatalog] && plugin.isInstanceOf[ViewCatalog] && - !plugin.isInstanceOf[RelationCatalog]) { + !plugin.isInstanceOf[TableViewCatalog]) { throw new IllegalArgumentException( s"Catalog '$name' (${plugin.getClass.getName}) implements both TableCatalog and " + s"ViewCatalog directly. Catalogs that expose both tables and views must implement " + - s"RelationCatalog instead, which centralizes the cross-cutting rules (shared " + + s"TableViewCatalog instead, which centralizes the cross-cutting rules (shared " + s"identifier namespace, cross-type collision rejection, single-RPC perf entry " + s"points).") } diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/V1Table.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/V1Table.scala index 079b2639aa2b9..a1fb2c1c84e40 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/V1Table.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/connector/catalog/V1Table.scala @@ -112,7 +112,7 @@ private[sql] object V1Table { def toCatalogTable( catalog: CatalogPlugin, ident: Identifier, - t: MetadataOnlyTable): CatalogTable = t.getTableInfo match { + t: MetadataTable): CatalogTable = t.getTableInfo match { case viewInfo: ViewInfo => toCatalogTable(catalog, ident, viewInfo) case tableInfo => toCatalogTable(catalog, ident, tableInfo) } diff --git a/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala b/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala index 94523dd313b43..f8a9ce1d40d9c 100644 --- a/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala +++ b/sql/core/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveSessionCatalog.scala @@ -786,7 +786,7 @@ class ResolveSessionCatalog(val catalogManager: CatalogManager) object ResolvedViewIdentifier { // Only matches session-catalog persistent views. Non-session-catalog persistent views - // (produced for `MetadataOnlyTable`) fall through; `AlterViewAs` is picked up by the v2 + // (produced for `MetadataTable`) fall through; `AlterViewAs` is picked up by the v2 // strategy, and the remaining view DDL / inspection plans (SET/UNSET TBLPROPERTIES, // ALTER VIEW ... WITH SCHEMA, RENAME TO, SHOW CREATE TABLE, SHOW TBLPROPERTIES, SHOW // COLUMNS, DESCRIBE [COLUMN]) are rejected with `UNSUPPORTED_FEATURE.TABLE_OPERATION` by diff --git a/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataOnlyTableSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataTableSuite.scala similarity index 90% rename from sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataOnlyTableSuite.scala rename to sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataTableSuite.scala index 8d3ad19419dff..c520ea451ec48 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataOnlyTableSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataTableSuite.scala @@ -20,18 +20,18 @@ package org.apache.spark.sql.connector import org.apache.spark.SparkConf import org.apache.spark.sql.{QueryTest, Row} import org.apache.spark.sql.catalyst.analysis.NoSuchTableException -import org.apache.spark.sql.connector.catalog.{Identifier, MetadataOnlyTable, Table, TableCatalog, TableChange, TableInfo, TableSummary} +import org.apache.spark.sql.connector.catalog.{Identifier, MetadataTable, Table, TableCatalog, TableChange, TableInfo, TableSummary} import org.apache.spark.sql.connector.expressions.LogicalExpressions import org.apache.spark.sql.test.SharedSparkSession import org.apache.spark.sql.types.StructType import org.apache.spark.sql.util.CaseInsensitiveStringMap /** - * Tests for the data-source-table side of [[MetadataOnlyTable]]: a v2 catalog returns + * Tests for the data-source-table side of [[MetadataTable]]: a v2 catalog returns * metadata-only tables and Spark reads / writes them via the V1 data-source path. - * View-related paths live in [[DataSourceV2MetadataOnlyViewSuite]]. + * View-related paths live in [[DataSourceV2MetadataViewSuite]]. */ -class DataSourceV2MetadataOnlyTableSuite extends QueryTest with SharedSparkSession { +class DataSourceV2MetadataTableSuite extends QueryTest with SharedSparkSession { import testImplicits._ override def sparkConf: SparkConf = super.sparkConf @@ -83,8 +83,8 @@ class DataSourceV2MetadataOnlyTableSuite extends QueryTest with SharedSparkSessi checkAnswer(spark.table(tableName), 0.until(10).map(i => Row(i, -i))) } - test("DESCRIBE TABLE EXTENDED on a non-view MetadataOnlyTable shows the real identifier") { - // MetadataOnlyTable.name() is read by DescribeTableExec's "Name" row. Pin that it + test("DESCRIBE TABLE EXTENDED on a non-view MetadataTable shows the real identifier") { + // MetadataTable.name() is read by DescribeTableExec's "Name" row. Pin that it // reflects the catalog-supplied identifier (here TestingDataSourceTableCatalog passes // `ident.toString`) rather than a generic placeholder, so the DESCRIBE output is // meaningful for users. @@ -129,7 +129,7 @@ class DataSourceV2MetadataOnlyTableSuite extends QueryTest with SharedSparkSessi } /** - * A read-only [[TableCatalog]] that returns [[MetadataOnlyTable]] for a small set of canned + * A read-only [[TableCatalog]] that returns [[MetadataTable]] for a small set of canned * table fixtures. Used to drive the data-source-table read path (file source + v2 provider) * through Spark's V1 data-source machinery. */ @@ -142,7 +142,7 @@ class TestingDataSourceTableCatalog extends TableCatalog { .withLocation(ident.namespace().head) .withTableType(TableSummary.EXTERNAL_TABLE_TYPE) .build() - new MetadataOnlyTable(info, ident.toString) + new MetadataTable(info, ident.toString) case "test_partitioned_json" => val partitioning = LogicalExpressions.identity(LogicalExpressions.reference(Seq("c2"))) val info = new TableInfo.Builder() @@ -152,13 +152,13 @@ class TestingDataSourceTableCatalog extends TableCatalog { .withTableType(TableSummary.EXTERNAL_TABLE_TYPE) .withPartitions(Array(partitioning)) .build() - new MetadataOnlyTable(info, ident.toString) + new MetadataTable(info, ident.toString) case "test_v2" => val info = new TableInfo.Builder() .withSchema(FakeV2Provider.schema) .withProvider(classOf[FakeV2Provider].getName) .build() - new MetadataOnlyTable(info, ident.toString) + new MetadataTable(info, ident.toString) case _ => throw new NoSuchTableException(ident) } diff --git a/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataOnlyViewSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataViewSuite.scala similarity index 95% rename from sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataOnlyViewSuite.scala rename to sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataViewSuite.scala index 0851e6d2df765..c0cd09cd85187 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataOnlyViewSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataViewSuite.scala @@ -20,31 +20,31 @@ package org.apache.spark.sql.connector import org.apache.spark.SparkConf import org.apache.spark.sql.{AnalysisException, QueryTest, Row} import org.apache.spark.sql.catalyst.analysis.{NoSuchTableException, NoSuchViewException, TableAlreadyExistsException, ViewAlreadyExistsException} -import org.apache.spark.sql.connector.catalog.{Identifier, MetadataOnlyTable, RelationCatalog, Table, TableCatalog, TableChange, TableInfo, TableSummary, V1Table, ViewCatalog, ViewInfo} +import org.apache.spark.sql.connector.catalog.{Identifier, MetadataTable, Table, TableCatalog, TableChange, TableInfo, TableSummary, TableViewCatalog, V1Table, ViewCatalog, ViewInfo} import org.apache.spark.sql.internal.SQLConf import org.apache.spark.sql.test.SharedSparkSession import org.apache.spark.sql.types.StructType import org.apache.spark.sql.util.CaseInsensitiveStringMap /** - * Tests for the view side of [[MetadataOnlyTable]]: view-text expansion on read, and + * Tests for the view side of [[MetadataTable]]: view-text expansion on read, and * CREATE VIEW / ALTER VIEW ... AS going through the v2 write path * (`CreateV2ViewExec` / `AlterV2ViewExec`). View writes route through * [[ViewCatalog#createView]] / [[ViewCatalog#replaceView]]. * Data-source-table read paths live in - * [[org.apache.spark.sql.connector.DataSourceV2MetadataOnlyTableSuite]]. + * [[org.apache.spark.sql.connector.DataSourceV2MetadataTableSuite]]. * * TODO: once the remaining v2 view DDL is implemented (SET/UNSET TBLPROPERTIES, SHOW CREATE * VIEW, RENAME TO, SCHEMA BINDING, DESCRIBE / SHOW TBLPROPERTIES on v2 views), register a - * `MetadataOnlyTable`-backed `DelegatingCatalogExtension` as `spark.sql.catalog.spark_catalog` + * `MetadataTable`-backed `DelegatingCatalogExtension` as `spark.sql.catalog.spark_catalog` * and run the shared [[org.apache.spark.sql.execution.PersistedViewTestSuite]] body against * the v2 path for full parity with the v1 persisted-view coverage. */ -class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSession { +class DataSourceV2MetadataViewSuite extends QueryTest with SharedSparkSession { import testImplicits._ override def sparkConf: SparkConf = super.sparkConf - .set("spark.sql.catalog.view_catalog", classOf[TestingRelationCatalog].getName) + .set("spark.sql.catalog.view_catalog", classOf[TestingTableViewCatalog].getName) // --- View read path ----------------------------------------------------- @@ -72,7 +72,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio // End-to-end coverage of the v2 encoder -> parser round-trip: test_unqualified_multi is a // view whose captured catalog+namespace is view_catalog.ns1.ns2 (two-part namespace) and // whose body references `t` unqualified. At read time the unqualified `t` must expand to - // view_catalog.ns1.ns2.t via the captured context -- which TestingRelationCatalog resolves to + // view_catalog.ns1.ns2.t via the captured context -- which TestingTableViewCatalog resolves to // its own `t` fixture at that namespace. checkAnswer( spark.table("view_catalog.outer_ns.test_unqualified_multi"), @@ -93,11 +93,11 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio .withCurrentCatalog("my_cat") .withCurrentNamespace(Array("db1", "db2")) .build() - val motTable = new MetadataOnlyTable(info, "v") + val mt = new MetadataTable(info, "v") // Any CatalogPlugin works here; toCatalogTable only reads `catalog.name()`. val catalog = spark.sessionState.catalogManager.catalog("view_catalog") val ct = V1Table.toCatalogTable( - catalog, Identifier.of(Array("ns"), "v"), motTable) + catalog, Identifier.of(Array("ns"), "v"), mt) assert(ct.viewCatalogAndNamespace == Seq("my_cat", "db1", "db2")) // Namespace parts containing dots flow through structurally (no string encoding). @@ -108,7 +108,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio .withCurrentNamespace(Array("weird.db", "normal")) .build() val ctWeird = V1Table.toCatalogTable( - catalog, Identifier.of(Array("ns"), "v"), new MetadataOnlyTable(infoWeird, "v")) + catalog, Identifier.of(Array("ns"), "v"), new MetadataTable(infoWeird, "v")) assert(ctWeird.viewCatalogAndNamespace == Seq("my_cat", "weird.db", "normal")) } @@ -117,9 +117,9 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio .withSchema(new StructType().add("col", "string")) .withQueryText("SELECT * FROM spark_catalog.default.t") .build() - val motTable = new MetadataOnlyTable(info, "v") + val mt = new MetadataTable(info, "v") val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - val ct = V1Table.toCatalogTable(catalog, Identifier.of(Array("ns"), "v"), motTable) + val ct = V1Table.toCatalogTable(catalog, Identifier.of(Array("ns"), "v"), mt) assert(ct.viewCatalogAndNamespace.isEmpty) } @@ -232,10 +232,10 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio Seq("a", "b").toDF("col").write.saveAsTable("spark_catalog.default.t") sql("CREATE VIEW view_catalog.default.v_coll DEFAULT COLLATION UTF8_BINARY AS " + "SELECT col FROM spark_catalog.default.t") - // TestingRelationCatalog stores the TableInfo verbatim, so the collation property is + // TestingTableViewCatalog stores the TableInfo verbatim, so the collation property is // observable via the catalog-stored builder output. val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] val info = catalog.getStoredView(Array("default"), "v_coll") assert(info.properties().get(TableCatalog.PROP_COLLATION) == "UTF8_BINARY") } @@ -258,7 +258,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio test("CREATE VIEW over a non-view table entry is rejected (plain TableCatalog)") { val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] val tableIdent = Identifier.of(Array("default"), "v_existing_table") val tableInfo = new TableInfo.Builder() .withSchema(new StructType().add("col", "string")) @@ -374,7 +374,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio "SELECT x + 1 AS x FROM spark_catalog.default.t") val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] val info = catalog.getStoredView(Array("default"), "v_preserve") assert(info.properties().get("mykey") == "myvalue") } @@ -387,7 +387,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio "SELECT x FROM spark_catalog.default.t") val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] val info = catalog.getStoredView(Array("default"), "v_owner_create") // v2 CREATE VIEW stamps the current user into PROP_OWNER, matching v2 CREATE TABLE // (via CatalogV2Util.withDefaultOwnership) and v1 CREATE VIEW (via CatalogTable.owner's @@ -400,7 +400,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio test("ALTER VIEW preserves PROP_OWNER (v1-parity)") { val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] val viewIdent = Identifier.of(Array("default"), "v_owner") // Pre-seed a view whose stored ViewInfo carries an explicit owner. val initialInfo = new ViewInfo.Builder() @@ -435,7 +435,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio "SELECT x + 1 AS x FROM spark_catalog.default.t") val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] assert(catalog.getStoredView(Array("default"), "v_evo").schemaMode() == "EVOLUTION") } } @@ -448,7 +448,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio "SELECT col FROM spark_catalog.default.t") } val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] assert(catalog.getStoredView(Array("default"), "v_configs") .sqlConfigs().get(SQLConf.ANSI_ENABLED.key) == "true") @@ -480,7 +480,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio // ALTER VIEW's identifier is resolved via `UnresolvedView`, whose `viewOnly=true` path // in `Analyzer.lookupTableOrView` rejects non-ViewCatalog catalogs up front with the // expected error class -- before `loadTable` is even called. `TestingTableOnlyCatalog` - // happens to round-trip `default.v` as a view-typed MetadataOnlyTable, but that fixture + // happens to round-trip `default.v` as a view-typed MetadataTable, but that fixture // is not actually consulted on this path. CREATE VIEW's capability check lives in // `CheckViewReferences`; ALTER VIEW's lives in the analyzer gate. Both yield // `MISSING_CATALOG_ABILITY.VIEWS`. @@ -571,7 +571,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio // `unsupportedCreateOrReplaceViewOnTableError`. Pre-seed a non-view entry at a // multi-level-namespace identifier to exercise the rendering. val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] val tblIdent = Identifier.of(Array("ns1", "inner"), "t_err") catalog.createTable( tblIdent, @@ -742,7 +742,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio test("DROP VIEW on a ViewCatalog drops the view") { val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] withTable("spark_catalog.default.t") { Seq(1, 2, 3).toDF("x").write.saveAsTable("spark_catalog.default.t") sql("CREATE VIEW view_catalog.default.v_drop AS " + @@ -764,7 +764,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio // `wrongCommandForObjectTypeError`. The v2 path must also refuse -- otherwise // `DROP VIEW view_catalog.default.` would silently destroy the table's entry. val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] val tableIdent = Identifier.of(Array("default"), "t_not_a_view") catalog.createTable( tableIdent, @@ -799,7 +799,7 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio private def seedV2Table(name: String): Unit = { val catalog = spark.sessionState.catalogManager.catalog("view_catalog") - .asInstanceOf[TestingRelationCatalog] + .asInstanceOf[TestingTableViewCatalog] catalog.createTable( Identifier.of(Array("default"), name), new TableInfo.Builder() @@ -869,26 +869,26 @@ class DataSourceV2MetadataOnlyViewSuite extends QueryTest with SharedSparkSessio } /** - * A [[RelationCatalog]]: round-trips [[MetadataOnlyTable]] for created views and tables and + * A [[TableViewCatalog]]: round-trips [[MetadataTable]] for created views and tables and * exposes a few canned read-only view fixtures (`test_view`, `test_unqualified_view`, * `test_unqualified_multi`, plus an unqualified-target view at `ns1.ns2.t`) used by the * view-read tests. Entries created via `createTable` / `createView` are distinguished by the * stored value's runtime type (ViewInfo vs TableInfo). The single-RPC perf entry point - * [[loadRelation]] returns either kind; [[loadTable]] is tables-only per the + * [[loadTableOrView]] returns either kind; [[loadTable]] is tables-only per the * [[TableCatalog#loadTable]] contract. */ -class TestingRelationCatalog extends RelationCatalog { +class TestingTableViewCatalog extends TableViewCatalog { // Holds entries (views and tables) created via createTable / createView within the session. // Keyed by (namespace, name); the stored value's runtime type (ViewInfo vs TableInfo) // distinguishes views from tables. Mixed-catalog: shared identifier namespace per the - // RelationCatalog contract. + // TableViewCatalog contract. private val createdViews = new java.util.concurrent.ConcurrentHashMap[(Seq[String], String), TableInfo]() - // Canned read-only view fixtures, exposed only via the perf path (loadRelation). loadView - // does not need to expose them because the resolver routes RelationCatalog reads through - // loadRelation. + // Canned read-only view fixtures, exposed only via the perf path (loadTableOrView). loadView + // does not need to expose them because the resolver routes TableViewCatalog reads through + // loadTableOrView. private def fixtureView(ident: Identifier): Option[ViewInfo] = ident.name() match { case "test_view" => Some(new ViewInfo.Builder() @@ -925,15 +925,15 @@ class TestingRelationCatalog extends RelationCatalog { case _ => None } - override def loadRelation(ident: Identifier): Table = { - // Single-RPC perf path: returns tables AND views (as MetadataOnlyTable). Stored entries + override def loadTableOrView(ident: Identifier): Table = { + // Single-RPC perf path: returns tables AND views (as MetadataTable). Stored entries // win over fixture views (the fixture namespace is read-only and disjoint from // createdViews in practice). loadTable, loadView, tableExists, viewExists all derive - // from this via the RelationCatalog default impls. + // from this via the TableViewCatalog default impls. val key = (ident.namespace().toSeq, ident.name()) Option(createdViews.get(key)) .orElse(fixtureView(ident)) - .map(new MetadataOnlyTable(_, ident.toString)) + .map(new MetadataTable(_, ident.toString)) .getOrElse(throw new NoSuchTableException(ident)) } @@ -945,7 +945,7 @@ class TestingRelationCatalog extends RelationCatalog { if (createdViews.putIfAbsent(key, info) != null) { throw new TableAlreadyExistsException(ident) } - new MetadataOnlyTable(info, ident.toString) + new MetadataTable(info, ident.toString) } /** Test-only accessor: returns the stored TableInfo (table or view) for the identifier. */ From 9e3ddda259fc7fccfbfedfb55f63f933a21b058c Mon Sep 17 00:00:00 2001 From: Gengliang Wang Date: Tue, 28 Apr 2026 20:37:07 +0000 Subject: [PATCH 2/2] Rename listRelationSummaries -> listTableAndViewSummaries --- .../spark/sql/connector/catalog/TableViewCatalog.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableViewCatalog.java b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableViewCatalog.java index fadb3c4de8038..6a702c58a7434 100644 --- a/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableViewCatalog.java +++ b/sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableViewCatalog.java @@ -104,8 +104,8 @@ * a regular {@link Table} for a table, or a {@link MetadataTable} wrapping a * {@link ViewInfo} for a view. Saves the {@code loadTable} -> {@code loadView} fallback * on a cold cache. - *
    • {@link #listRelationSummaries(String[])} -- a unified listing of tables and views with the - * kind preserved on each {@link TableSummary}. Default impl performs both + *
    • {@link #listTableAndViewSummaries(String[])} -- a unified listing of tables and views + * with the kind preserved on each {@link TableSummary}. Default impl performs both * {@link TableCatalog#listTableSummaries} and {@link ViewCatalog#listViews}; override to * fetch in one round trip.
    • * @@ -144,7 +144,7 @@ public interface TableViewCatalog extends TableCatalog, ViewCatalog { * @throws NoSuchTableException if a table listed by the underlying enumeration disappears * before its summary can be assembled (default impl only) */ - default TableSummary[] listRelationSummaries(String[] namespace) + default TableSummary[] listTableAndViewSummaries(String[] namespace) throws NoSuchNamespaceException, NoSuchTableException { TableSummary[] tableSummaries = listTableSummaries(namespace); Identifier[] viewIdentifiers = listViews(namespace);