peter-toth opened a new pull request, #57599:
URL: https://github.com/apache/spark/pull/57599

   ### What changes were proposed in this pull request?
   
   Backport of #57563 to `branch-4.1`.
   
   `RelationResolution.resolveRelation` answers a hit in the per-query 
`AnalysisContext.relationCache` before reaching the branch that calls 
`CatalogV2Util.loadTable(catalog, ident, timeTravelSpec, writePrivileges)`. 
This PR stops serving a reference that carries `REQUIRED_WRITE_PRIVILEGES` from 
that cache, so a write target always goes through 
`TableCatalog.loadTable(ident, writePrivileges)`:
   
   ```scala
   val writePrivileges = 
u.options.get(UnresolvedRelation.REQUIRED_WRITE_PRIVILEGES)
   val cached = if (writePrivileges == null) relationCache.get(key) else None
   cached.map(adaptCachedRelation(_, planId)).orElse { ... }
   ```
   
   Loaded relations are still *stored* in the cache, so a self-MERGE's source 
keeps sharing the target's `Table` instance (one snapshot).
   
   ### Backport notes
   
   The cherry-pick of `a1c3f310a8c` does not apply cleanly, so the change was 
re-applied by hand:
   
   - On `master` the affected code lives in 
`RelationResolution.tryResolvePersistent`, extracted alongside 
`relationResolutionSteps` and the `RelationCatalog.loadRelation` fast path. 
None of that exists on `branch-4.1`, where the same cache-then-load sequence 
still sits inline in `resolveRelation`, one nesting level deeper inside 
`resolveTempView(...).orElse { expandIdentifier(...) match ... }`. The change 
itself is unchanged: hoist `writePrivileges` above the cache lookup and skip 
the lookup when it is set.
   - The comment block is rewrapped, because the extra two spaces of 
indentation push one line past 100 characters.
   
   The test hunk applies unchanged.
   
   ### Why are the changes needed?
   
   `TableCatalog.loadTable(Identifier, Set<TableWritePrivilege>)` (`@since 
3.5.3`) is where a catalog authorizes a write; the only other caller in the 
tree is `ResolveSchemaEvolution`. A write target hits the relation cache 
whenever another reference to the same table was resolved earlier in the same 
analyzer run, which is exactly the case for `InsertIntoStatement` and 
`V2WriteCommand` - their target is resolved at the command node *after* its 
query. So the catalog is never asked to authorize these statements:
   
   ```sql
   INSERT INTO t SELECT * FROM t
   INSERT OVERWRITE t SELECT * FROM t WHERE 1 = 0   -- empties the table
   INSERT INTO t REPLACE WHERE i = 0 SELECT * FROM t
   INSERT INTO t SELECT i FROM (SELECT i FROM t) x
   WITH c AS (SELECT i FROM t) INSERT INTO t SELECT i FROM c
   ```
   
   ```scala
   spark.table("t").writeTo("t").append()            // DataFrameWriterV2 
passes the unanalyzed plan
   ```
   
   `INSERT INTO t SELECT 1` on the same table is authorized normally, and 
`UPDATE` / `DELETE` / `MERGE` are unaffected (their target is a plain child, 
resolved before the source, so it is a cache miss), as is 
`DataFrameWriter.insertInto` (it passes the already-analyzed plan).
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes. On a catalog that enforces privileges in `loadTable(ident, 
writePrivileges)`, the statements above are now authorized like every other 
write, so a user without the required privilege gets the catalog's error 
instead of silently writing. Catalogs that ignore the privileges argument 
(including Spark's built-in `V2SessionCatalog`) are unaffected. The write 
target of a self-referencing INSERT is now loaded once more, which is what a 
non-self-referencing INSERT already does.
   
   ### How was this patch tested?
   
   New test in `DataSourceV2SQLSuite`, identical to the one on `master`. It 
fails on `branch-4.1` without the fix ("Expected exception 
java.lang.RuntimeException to be thrown, but no exception was thrown") and 
passes with it.
   
   Also green on this branch: 17 suites / 1162 tests - `DataSourceV2SQLSuite` 
(V1+V2 filter), `DataSourceV2DataFrameSuite`, `DataSourceV2OptionSuite`, 
`PlanResolutionSuite`, `CachedTableSuite`, `*ViewTestSuite`, group/delta-based 
MERGE/UPDATE/DELETE suites, `MergeIntoDataFrameSuite`, `InsertSuite`. 
`dev/lint-scala` clean.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code (Claude Opus 5)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to