[KYUUBI #7673] Fix thrift GetSchemas/GetTables with custom session catalog - #7718
[KYUUBI #7673] Fix thrift GetSchemas/GetTables with custom session catalog#7718maomaodev wants to merge 2 commits into
Conversation
…ion catalog The thrift GetSchemas and GetTables operations assumed `spark_catalog` is the builtin V1 session catalog and always listed databases/tables through `spark.sessionState.catalog`. When a custom session catalog is configured via `spark.sql.catalog.spark_catalog` (e.g. Iceberg's SparkSessionCatalog), the operations bypass the DSv2 catalog APIs and return wrong results. - getSchemas: detect custom session catalogs via the `builtin` magic value (SPARK-50700) and list namespaces through SupportsNamespaces - getCatalogTablesOrViews: match TableCatalog before the builtin session catalog case so a custom session catalog takes the DSv2 listing path - run SparkIcebergOperationSuite against a real HMS via testcontainers (WithSimpleHMSContainer), with catalogImplementation=hive, an Iceberg SparkSessionCatalog as session catalog and a hadoop-type Iceberg catalog as default catalog; the get tables test now covers both spark_catalog and the v2 catalog - make format/catalog/warehouse/extraJars vals in the DataLake test mixins so they are computed once
pan3793
left a comment
There was a problem hiding this comment.
Review of #7718 (head 8ffa541). Verdict: request changes. The #7673 diagnosis and the Iceberg fix mechanism are verified real; the listing refactor introduces the regressions below in the same configurations the PR targets.
-
[Blocker, regression] Local temp views leak into the new v2 listing.
catalog.listTables(ns)(SparkCatalogUtils.scala:202-204) reachesV2SessionCatalog.listTables->SessionCatalog.listTables(db)withincludeLocalTempViews = true, so temp views come back asIdentifier.of(Array(), name); they are typed TABLE withTABLE_SCHEM=""(:217-232) andgetTempViews(:350-359) emits the same view a second time as VIEW. Master usedincludeLocalTempViews = falsehere. Fix: drop identifiers with an empty namespace before typing/loading. -
[Blocker, regression]
catalog.loadTable(ident)at :229 is unguarded and runs per row whenignoreTableProperties=false(the default), so the namespace-less identifier from (1) throws (requiresSinglePartNamespace/NoSuchTable) and fails the whole GetTables; any unloadable, dropped, or corrupt table does the same. It also forces per-table format I/O (Delta_delta_log, Iceberg metadata JSON) where master did one bulk HMS call. Fix: wrap inTryand degrade REMARKS to "" (or keep the session-catalog listing load-free). -
[Blocker, regression] The view probe keys
listViewsresults by the outer db (:211-215) whileSessionCatalog.listViewsalso returns local temp views, so a temp viewvmarks permanent tabledb.vas VIEW and removes it undertableTypes={TABLE}. Fix: only keepTableIdentifiers with a database. -
[Major, regression]
getSchemas("spark_catalog", ...)with a custom session catalog now returns backtick-quoted namespaces and matches the pattern against the quoted form (:126, :170-173); a DB like2024_dbis returned as`2024_db`and no longer matchable by its own pattern. Master returned raw v1 names. -
[Major, regression]
listAllNamespacesis a non-exhaustive match (:153-163) and is now reachable for the session catalog, so aTableCatalog-only session catalog throwsMatchErrorin GetSchemas where master returned v1 rows. -
[Major, new failure mode] The per-namespace
spark.sessionState.catalog.listViews(db, ...)probe callsrequireDbExists, so a custom session catalog whose namespaces are not v1-backed now fails the whole GetTables withNoSuchDatabaseException. Guard withdatabaseExistsor catch and skip the probe for that namespace. -
[Major]
hasCustomSessionCatalog(:106-109) compares case-sensitively; Spark 4.x normalizesBUILTINtobuiltin, so such a value is treated as custom. UseequalsIgnoreCase(mirrorsV2SessionCatalog.hasCustomSessionCatalog). -
[Major, behavior change]
tableTypesis now applied to ordinary v2 catalogs (:225) while view detection exists only for the session catalog, sogetTables(<v2 catalog>, ..., Array("VIEW"))goes from all-rows-typed-TABLE to empty. DSv2ViewCatalogis still immature, so covering v2 tables only is fine for this PR - please just document the limitation (non-session v2 catalogs report tables only, VIEW-only filters return empty) and drop the filter or add a test pinning the empty-result behavior. -
[Major, scope/claim] The fix guarantees only "the custom catalog can load the table". Tables delegated back to v1 (e.g.
DeltaCatalog+catalogImplementation=hive, or non-Iceberg tables under Iceberg's session catalog) still hitHiveExternalCatalog/HiveClientImpl.getStorageHandlerand the sameClassNotFoundException. Narrow the claim, or make the listing independent of per-catalog loading. -
[Major]
TABLE_CATis inconsistent within one result:catalog.name()at :231 vs the raw request string ingetTempViews(:357) andgetColumnsByCatalog, sogetTables(null, ...)mixes"spark_catalog"withnull. Align all row producers and tighten the assertions. -
[Test]
DeltaMetadataTests#get tables and viewsexercises the new path (DeltaCatalog is a custom session catalog) but passes on master, so it does not discriminate the fix. Add a case that fails without the change, or drop it. -
[Test] Nothing covers GetSchemas: reverting only the getSchemas change keeps the suites green, because the test session catalogs delegate namespaces to the v1 catalog. Add a test-only
CatalogExtensionexposing a namespace the v1 catalog cannot see. -
[Test]
IcebergMetadataTestscreatesspark_catalog.<db>namespaces and never drops them (withDatabasesonly drops unqualified names), making the exact-countget schemasassertion order-dependent. Drop them in thefinallyblocks. -
[Test]
kyuubi.operation.getTables.ignoreTablePropertieshas no coverage repo-wide; the new path has three distinct behaviors for it (:208, :225, :228). Add a test. -
[Minor] The new tests never assert
TABLE_SCHEM/TABLE_CAT; the docstring at :186-191 overstates ("returns both v1 and v2 views", "never loads the Hive storage handler") - it only covers views registered in the v1 catalog;catalog.name()andcatalog.nameare mixed in one file. -
[Minor] The new
WithSimpleHMSContainerduplicatesWithSecuredHMSContainer(drop the unusedenv, consider sharing the container def),hmsThriftUrishas no null guard, andHostPortWaitStrategyhas no startup-timeout margin. -
[Hygiene] Commit
80255d65eis tagged #7064 while the PR head/title use #7673, and the description does not describe the final diff or list the commands run. Please update the description for the final state.
Why are the changes needed?
The thrift
GetSchemas/GetTablesoperations assumedspark_catalogis always the builtin V1 session catalog and listed databases/tables viaspark.sessionState.catalog. When a custom session catalog is configured, e.g.these operations bypass the DSv2 catalog APIs (
SupportsNamespaces/TableCatalog) and the V1 session catalog path cannot even parse the V2 table metadata the custom catalog persists in HMS - it may return wrong results, or throw and fail the whole request. #7673 is a real-world example:GetTablesfails withClassNotFoundException: org.apache.iceberg.mr.hive.HiveIcebergSerDewhen the V1HiveExternalCatalogtries to load tables registered by Iceberg withstorage_handler = org.apache.iceberg.mr.hive.HiveIcebergStorageHandler.How was this patch tested?
DeltaMetadataTests#get tables and views(v1 view path),IcebergMetadataTests#get tables and views(v2 view path), andIcebergMetadataTests#get tables(bothspark_catalogandhadoop_prod). A testcontainers-basedWithSimpleHMSContaineris wired intoSparkIcebergOperationSuiteandIcebergOperationSuiteso these run against a real Hive metastore (catalogImplementation=hive).iceberg-hive-runtime):beeline ... -e "!tables"previously threwClassNotFoundException: org.apache.iceberg.mr.hive.HiveIcebergStorageHandler, and now returns the table asTABLEand v1/v2 views asVIEW.Was this patch assisted by generative AI tooling?
Assisted-by: DeepSeek-V4-Pro