Skip to content

[SPARK-58311][SQL] Gate generated column values on write with a table capability#57481

Open
szehon-ho wants to merge 1 commit into
apache:masterfrom
szehon-ho:dsv2-generated-column-gate
Open

[SPARK-58311][SQL] Gate generated column values on write with a table capability#57481
szehon-ho wants to merge 1 commit into
apache:masterfrom
szehon-ho:dsv2-generated-column-gate

Conversation

@szehon-ho

Copy link
Copy Markdown
Member

What changes were proposed in this pull request?

This PR moves the gate that decides whether Spark auto-fills and enforces generated column values on a DSv2 write from a catalog-level capability to a table-level capability.

Specifically:

  • Adds TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE, checked via Table.capabilities().
  • Removes TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE (added by SPARK-57644 and still unreleased).
  • GeneratedColumn.supportsGeneratedColumnsOnWrite now takes a Table and checks the table's capabilities instead of the TableCatalog's.
  • Updates the four call sites to consult the table:
    • Analyzer (exposing generation-expression metadata to TableOutputResolver so missing values are auto-filled),
    • ResolveTableConstraints (adding the generated-column CheckInvariant constraints),
    • RewriteRowLevelCommand (blocking MERGE/UPDATE),
    • ResolveWriteToStream (blocking streaming writes).
  • Test infra: the in-memory test table advertises the new capability, gated by a generate-column-values-on-write table property (default true), mirroring the existing accept-any-schema / auto-schema-evolution toggles.

Why are the changes needed?

Whether Spark should generate/enforce generated column values is a property of an individual table, not of the whole catalog. A single catalog can expose tables from formats or protocol versions with differing generated-column support, so a catalog-wide flag is too coarse.

This also aligns with how Delta Lake gates the feature: it is gated per-table on the table's protocol. GeneratedColumn.satisfyGeneratedColumnProtocol(protocol) returns protocol.isFeatureSupported(GeneratedColumnsTableFeature) -- i.e. it checks whether the GeneratedColumnsTableFeature is enabled in that specific table's protocol. A per-table TableCapability is the natural DSv2 analogue and matches the existing pattern for other per-table write behaviors (BATCH_WRITE, OVERWRITE_DYNAMIC, TRUNCATE, ...).

Does this PR introduce any user-facing change?

No change relative to a released version. The catalog capability being removed (SUPPORT_GENERATED_COLUMN_ON_WRITE) was introduced by SPARK-57644 and has not shipped in any release, so this only reshapes an unreleased API within the development branch. Connectors opt in by having their Table advertise TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE.

How was this patch tested?

GeneratedColumnWriteSuite (69 tests) passes. The existing "connector without the write capability does not auto-fill or enforce generated columns" coverage is retained (renamed to "table without write capability ..."), now driven by creating a table with TBLPROPERTIES ('generate-column-values-on-write' = 'false') instead of a dedicated no-capability catalog.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Cursor (Opus 4.8)

… capability

Move the gate that decides whether Spark auto-fills and enforces generated
column values on a DSv2 write from the catalog-level
`TableCatalogCapability.SUPPORT_GENERATED_COLUMN_ON_WRITE` to a new per-table
`TableCapability.GENERATE_COLUMN_VALUES_ON_WRITE`.

@cloud-fan cloud-fan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

0 blocking, 1 non-blocking, 0 nits.
The implementation is coherent, but the new public capability contract promises broader auto-fill behavior than the resolver currently provides.

Correctness (1)

  • sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/TableCapability.java:117: The capability contract overstates auto-fill behavior. Ordinary by-position writes reject a short input before resolving generated expressions, so this bullet should scope auto-computation to by-name writes (or the implementation should add equivalent by-position support). -- see inline

Verification

Traced both resolution modes in TableOutputResolver: by-name resolution substitutes a missing generated expression, while ordinary by-position resolution rejects a short input before column resolution.

PR description suggestions

  • Clarify that missing generated values are currently auto-filled for by-name writes, while ordinary by-position writes still require matching arity.

* <p>
* When this capability is present, Spark will:
* <ul>
* <li>Auto-compute missing generated column values using the generation expression.</li>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please scope this promise to by-name writes. Ordinary by-position resolution rejects a short input before a missing generated column can be filled.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants