Skip to content

[KYUUBI #7774][AUTHZ] Resolve the table owner for v1 (Hive) tables - #7775

Closed
attilapiros wants to merge 3 commits into
apache:masterfrom
attilapiros:7774
Closed

attilapiros wants to merge 3 commits into
apache:masterfrom
attilapiros:7774

Conversation

@attilapiros

Copy link
Copy Markdown
Contributor

Why are the changes needed?

Granting ownership privileges needs the table owner on the privilege object, but it came back empty for Hive tables.

TableExtractor.getOwner assumed its argument wraps a connector Table in a table member. V1Table, which V2SessionCatalog.loadTable returns for a session-catalog (Hive) table, carries the properties itself and has no table accessor, so the lookup threw, was swallowed by the surrounding catch, and the owner was lost.

DROP TABLE on a Hive table reaches exactly this path. With spark.sql.legacy.useV1Command at its default of false the plan is not downgraded to DropTableCommand; it stays as the v2 DropTable(ResolvedIdentifier(...)), whose first table descriptor resolves the owner through loadTable.

  • getOwner picks the properties holder -- V1Table itself, or the wrapped table otherwise -- and then reads the owner once, instead of duplicating the lookup.
  • Empty owners are filtered out. V1Table.addV2TableProperties sets PROP_OWNER unconditionally, so a table with no owner yielded Some(""). The other owner lookups in this file already filter on nonEmpty.
  • A debug line when the catalog lookup fails, so a genuinely absent owner can be told apart from a failed load.

V1Table is private[sql], so it is matched by class name; it cannot be referenced as a type from this package.

How was this patch tested?

A new test in HiveCatalogRangerSparkExtensionSuite asserts the owner on the privilege object built for DROP TABLE. It fails without this change: the owner is None, because getOwner throws on a V1Table. The test asserts the owner value rather than isDefined -- since PROP_OWNER is always set, isDefined would also hold for an empty owner.

Was this patch assisted by generative AI tooling?

Assisted-by: Claude Opus 5

Granting ownership privileges needs the table owner on the privilege object, but it
came back empty for Hive tables.

`TableExtractor.getOwner` assumed its argument wraps a connector `Table` in a `table`
member. `V1Table`, which `V2SessionCatalog.loadTable` returns for a session-catalog
(Hive) table, carries the properties itself and has no `table` accessor, so the
lookup threw, was swallowed by the surrounding `catch`, and the owner was lost.

`DROP TABLE` on a Hive table reaches exactly this path. With
`spark.sql.legacy.useV1Command` at its default of `false` the plan is not downgraded
to `DropTableCommand`; it stays as the v2 `DropTable(ResolvedIdentifier(...))`, whose
first table descriptor resolves the owner through `loadTable`.

- `getOwner` picks the properties holder -- `V1Table` itself, or the wrapped table
  otherwise -- and then reads the owner once, instead of duplicating the lookup.
- Empty owners are filtered out. `V1Table.addV2TableProperties` sets `PROP_OWNER`
  unconditionally, so a table with no owner yielded `Some("")`. The other owner
  lookups in this file already filter on `nonEmpty`.
- A debug line when the catalog lookup fails, so a genuinely absent owner can be told
  apart from a failed load.

`V1Table` is `private[sql]`, so it is matched by class name; it cannot be referenced
as a type from this package.

```
    build/mvn test -Dcdpd.build -pl extensions/spark/kyuubi-spark-authz -am
```

A new test in `HiveCatalogRangerSparkExtensionSuite` asserts the owner on the
privilege object built for `DROP TABLE`. It fails without this change: the owner is
`None`, because `getOwner` throws on a `V1Table`. The test asserts the owner value
rather than `isDefined` -- since `PROP_OWNER` is always set, `isDefined` would also
hold for an empty owner.
val table = invokeAs[AnyRef](v, "table")
val properties = invokeAs[JMap[String, String]](table, "properties").asScala
properties.get("owner")
// It holds the properties itself, rather than wrapping another table.

@pan3793 pan3793 Oct 14, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should move this comment to the V1Table branch?

@pan3793

pan3793 commented Oct 14, 2026

Copy link
Copy Markdown
Member

Match on the public org.apache.spark.sql.connector.catalog.Table type instead of the V1Table class name, so this handles connector tables generally:

import org.apache.spark.sql.connector.catalog.{Identifier, Table => V2Table}

val maybeTable = v match {
  case table: V2Table => table
  case _ => invokeAs[AnyRef](v, "table")
}

For example, Delta Lake\s DeltaCatalog.loadTable returns a DeltaTableV2, which exposes the owner through properties() but has no table accessor. The fallback preserves unwrapping for ResolvedTable and DataSourceV2Relation.

@attilapiros

Copy link
Copy Markdown
Contributor Author

@pan3793 I just would not call it V2Table (by the import rename Table => V2Table) especially because it handles the V1Table so it would be a bit misleading. What about using the following rename:Table => ConnectorTable (as CatalogTable is already taken)?

@pan3793

pan3793 commented Oct 15, 2026

Copy link
Copy Markdown
Member

@attilapiros

Copy link
Copy Markdown
Contributor Author

@pan3793 ok, I have done the rename

@pan3793 pan3793 added this to the v1.12.1 milestone Oct 15, 2026
@pan3793 pan3793 closed this in e130c3e Oct 16, 2026
pan3793 pushed a commit that referenced this pull request Oct 16, 2026
### Why are the changes needed?

Granting ownership privileges needs the table owner on the privilege object, but it came back empty for Hive tables.

`TableExtractor.getOwner` assumed its argument wraps a connector `Table` in a `table` member. `V1Table`, which `V2SessionCatalog.loadTable` returns for a session-catalog (Hive) table, carries the properties itself and has no `table` accessor, so the lookup threw, was swallowed by the surrounding `catch`, and the owner was lost.

`DROP TABLE` on a Hive table reaches exactly this path. With `spark.sql.legacy.useV1Command` at its default of `false` the plan is not downgraded to `DropTableCommand`; it stays as the v2 `DropTable(ResolvedIdentifier(...))`, whose first table descriptor resolves the owner through `loadTable`.

- `getOwner` picks the properties holder -- `V1Table` itself, or the wrapped table otherwise -- and then reads the owner once, instead of duplicating the lookup.
- Empty owners are filtered out. `V1Table.addV2TableProperties` sets `PROP_OWNER` unconditionally, so a table with no owner yielded `Some("")`. The other owner lookups in this file already filter on `nonEmpty`.
- A debug line when the catalog lookup fails, so a genuinely absent owner can be told apart from a failed load.

`V1Table` is `private[sql]`, so it is matched by class name; it cannot be referenced as a type from this package.

### How was this patch tested?

A new test in `HiveCatalogRangerSparkExtensionSuite` asserts the owner on the privilege object built for `DROP TABLE`. It fails without this change: the owner is `None`, because `getOwner` throws on a `V1Table`. The test asserts the owner value rather than `isDefined` -- since `PROP_OWNER` is always set, `isDefined` would also hold for an empty owner.

### Was this patch assisted by generative AI tooling?

Assisted-by: Claude Opus 5

Closes #7775 from attilapiros/7774.

Closes #7774

304aafd [attilapiros] renaming ConnectorTable to V2Table
86d9108 [attilapiros] Applying review comments
389b555 [attilapiros] [KYUUBI #7774][AUTHZ] Resolve the table owner for v1 (Hive) tables

Authored-by: Attila Zsolt Piros <piros.attila.zsolt@gmail.com>
Signed-off-by: Cheng Pan <chengpan@apache.org>
(cherry picked from commit e130c3e)
Signed-off-by: Cheng Pan <chengpan@apache.org>
@pan3793

pan3793 commented Oct 16, 2026

Copy link
Copy Markdown
Member

Merge Summary:

Posted by merge_kyuubi_pr.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants