Skip to content

[SPARK-56681][SQL] PATH cleanup#55647

Closed
srielau wants to merge 12 commits into
apache:masterfrom
srielau:SPARK-56681-patch-clean-up
Closed

[SPARK-56681][SQL] PATH cleanup#55647
srielau wants to merge 12 commits into
apache:masterfrom
srielau:SPARK-56681-patch-clean-up

Conversation

@srielau
Copy link
Copy Markdown
Contributor

@srielau srielau commented May 1, 2026

What changes were proposed in this pull request?

Address the open follow-ups from SPARK-56681 (umbrella for PATH / SPARK-56605 cleanup) in a single cleanup PR. Items #1 and #2 were already wired by SPARK-56639; this PR covers the remainder.

# Item Resolution
#1 FunctionResolution.resolveProcedure was dead code Already wired by SPARK-56639 (no action).
#2 Frozen view / SQL-function PATH wiring unfinished Already done by SPARK-56639 (no action).
#3 AnalysisContext.resolutionPathEntries threadlocal Audit only: confirmed withNewAnalysisContext / reset() correctly clear it. Full removal needs a coordinated refactor to plumb the path through RelationResolution / FunctionResolution method calls; flagged as a follow-up.
#4 Analyzer.executeAndCheck clobbers outer SQLConf.withExistingConf Extracted runWithSessionConf helper, added SQLConf.getExistingConfIfSet. executeAndCheck and executeSameContext now share one path that yields to any outer scope.
#5 VariableResolution.allowUnqualifiedSessionTempVariableLookup force-loads default catalog Replaced the hot-path catalog read with CatalogManager.isSystemSessionOnPath, which inspects stored session-path entries directly. No catalog load on column resolution.
#6 DROP VARIABLE PATH gate asymmetric with DECLARE / CREATE Removed the gate. DDL on session variables (DECLARE / CREATE / DROP) always targets system.session directly; only DML (SET VAR, SELECT @x) goes through PATH.
#7 lookupFunctionType exception swallow too broad Narrowed from NonFatal to the explicit not-found list (NoSuchFunctionException, NoSuchNamespaceException, CatalogNotFoundException, FORBIDDEN_OPERATION). Other exceptions propagate.
#8 lookupFunctionType fan-out had wasteful system.* candidates Filtered them out — system.session, system.builtin, system.ai are already resolved earlier in the same method.
#9 Three near-duplicate path-resolution helpers Lifted into CatalogManager.resolutionPathEntriesForAnalysis(pinnedEntries, viewCatalogAndNamespace). Relation, routine, and procedure resolution all route through it.
#10 Tests for the new error paths and gates Added a DECLARE / SET VAR / DROP cycle test under non-default PATH and a struct-variable field-vs-qualified ambiguity test in sql-session-variables.sql.
#11 ProtoToParsedPlanTestSuite.analyzerIsolationConf was a bare SQLConf Clone spark.sessionState.conf and only override PATH_ENABLED=false, so all sparkConf overrides (ANSI, alias config, ...) propagate automatically.
Bonus ResolveSetVariable hardcoded SYSTEM.SESSION regardless of actual PATH unresolvedVariableError now takes Seq[Seq[String]] path entries with required Origin (no overloads). DML lookup failures (SET VAR, FETCH ... INTO) report the full SQL path as a bracketed list, byte-for-byte consistent with UNRESOLVED_ROUTINE and TABLE_OR_VIEW_NOT_FOUND. DDL name validation in ResolveCatalogs continues to report [system.session] since PATH does not apply there. Origin is plumbed through VariableManager.set so all error sites carry a queryContext pointing at the offending variable identifier (parser opt-ins via withOrigin(identifierReference) so the highlight is the variable name, not the whole statement).

Why are the changes needed?

These are the cleanup items called out on SPARK-56681 from the post-merge source review of SPARK-56605. They eliminate dead code paths, plug user-visible bugs (force-loading a misconfigured default catalog on column resolution; clobbering pinned session configs; swallowing real catalog errors as UNRESOLVED_ROUTINE), remove the asymmetry between DDL and DML on session variables, and make UNRESOLVED_VARIABLE self-consistent with the other "not found" errors.

Does this PR introduce any user-facing change?

Yes.

  • UNRESOLVED_VARIABLE.searchPath is now rendered as a bracketed list. For DML lookups (SET VAR, FETCH ... INTO), the list reflects the actual SQL PATH that was consulted instead of a hardcoded SYSTEM.SESSION. For DDL name validation (DECLARE / DROP with a non-session namespace), the list is [`` system.session ``] since PATH does not apply.
  • UNRESOLVED_VARIABLE now always carries a queryContext that highlights just the offending variable identifier (e.g. "builtin.var1", "ses.var1"), not the whole DECLARE / SET VAR statement.
  • DROP TEMPORARY VARIABLE no longer raises UNRESOLVED_VARIABLE when the SQL PATH does not contain system.session. DDL on session variables ignores PATH, matching the existing behaviour of DECLARE OR REPLACE VARIABLE.
  • lookupFunctionType no longer swallows non–NotFound errors. A catalog reporting PERMISSION_DENIED (or similar) for a function lookup now propagates instead of silently producing UNRESOLVED_ROUTINE.

How was this patch tested?

  • Added sql-session-variables.sql regression test for the struct-variable field-vs-qualified ambiguity (DECLARE VARIABLE session STRUCT<a INT>SELECT session.a succeeds → DROPSELECT session.a falls through to UNRESOLVED_COLUMN).
  • Updated SetPathSuite: DECLARE / SET VAR / DROP cycle under a non-default PATH; bonus test asserts the actual rendered search path and the variable-identifier queryContext.
  • Updated SqlScriptingExecutionSuite for the new bracketed searchPath and identifier-pinned queryContext.
  • Regenerated sql-session-variables.sql.out for the new error shape.
  • Added resolutionPathEntriesForAnalysis stubs to mocked CatalogManager instances in PlanResolutionSuite, AlignAssignmentsSuiteBase, and TableLookupCacheSuite.
  • Ran focused suites locally; all pass:
    • build/sbt 'sql/testOnly *SetPathSuite *SqlScriptingExecutionSuite *ExecuteImmediateEndToEndSuite'
    • build/sbt 'sql/testOnly *SimpleSQLViewSuite *SQLFunctionSuite'
    • build/sbt 'sql/testOnly *PlanResolutionSuite *UpdateTableAlignAssignmentsSuite *MergeIntoTableAlignAssignmentsSuite'
    • build/sbt 'catalyst/testOnly *TableLookupCacheSuite *AnalysisSuite *AnalysisErrorSuite *LookupFunctionsSuite'
    • build/sbt 'sql/testOnly *FunctionQualificationSuite *RelationQualificationSuite *DataSourceV2FunctionSuite'
    • build/sbt 'sql/testOnly *SQLQuerySuite'
    • build/sbt 'connect/testOnly *ProtoToParsedPlanTestSuite'
    • build/sbt 'sql/testOnly *SQLQueryTestSuite -- -z sql-session-variables.sql'
    • Full org.apache.spark.sql.catalyst.analysis.*, org.apache.spark.sql.catalyst.parser.*, and org.apache.spark.sql.analysis.resolver.* suites.
  • scalastyle and scalafmt clean across catalyst, sql, and connect modules.

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

Generated-by: Cursor Claude Opus 4.7

srielau added 10 commits May 1, 2026 07:52
### What changes were proposed in this pull request?

Address the open follow-ups from SPARK-56681 (umbrella for PATH /
SPARK-56605 cleanup) in a single cleanup PR. Items #1 and #2 were
already wired by SPARK-56639; this PR covers the remainder:

- #3 (audit only): document the lifecycle of
  `AnalysisContext.resolutionPathEntries` and confirm
  `withNewAnalysisContext` / `reset()` correctly clear it. The full
  removal of the field requires plumbing the path through
  `RelationResolution` / `FunctionResolution` method calls and is
  flagged as a follow-up in the docstring.
- apache#4: extract `Analyzer.runWithSessionConf` and add
  `SQLConf.getExistingConfIfSet`. `executeAndCheck` and
  `executeSameContext` now share one helper that yields to any outer
  `SQLConf.withExistingConf` scope (so persisted view / SQL-UDF configs
  are honored) instead of unconditionally clobbering it.
- apache#5: short-circuit
  `VariableResolution.allowUnqualifiedSessionTempVariableLookup` when
  PATH is not explicitly set, so column resolution does not force-load
  the configured default catalog (which would raise
  `CATALOG_NOT_FOUND` if it is misconfigured).
- apache#6: remove the `DROP VARIABLE` PATH gate in `ResolveCatalogs`. DDL on
  session variables (`DECLARE` / `CREATE` / `DROP`) always targets
  `system.session` directly, regardless of PATH; only DML (`SET VAR`,
  `SELECT @x`) goes through PATH.
- apache#7: narrow the exception swallow in
  `FunctionResolution.lookupFunctionType` from `NonFatal` to the
  explicit not-found list (`NoSuchFunctionException`,
  `NoSuchNamespaceException`, `CatalogNotFoundException`,
  `FORBIDDEN_OPERATION`). `PERMISSION_DENIED`, transient catalog
  errors, and unrelated bugs now surface instead of being masked as
  `UNRESOLVED_ROUTINE`.
- apache#8: filter `system.*` candidates out of the persistent fan-out in
  `lookupFunctionType`. Routines under `system.session`,
  `system.builtin`, and `system.ai` are already resolved by
  `lookupBuiltinOrTempFunction` / `lookupBuiltinOrTempTableFunction`
  earlier in the same method, so the extra `functionExists` calls were
  wasteful catalog round-trips.
- apache#9: lift `resolutionPathEntriesForAnalysis(pinnedEntries,
  viewCatalogAndNamespace)` onto `CatalogManager` as the single source
  of truth used by relation, routine, and procedure resolution. The
  near-duplicate helpers in `RelationResolution` and
  `FunctionResolution` now route through it.
- apache#11: clone `spark.sessionState.conf` for
  `ProtoToParsedPlanTestSuite.analyzerIsolationConf` instead of
  starting from a bare `new SQLConf()`. All `sparkConf` overrides
  (ANSI, alias config, ...) propagate automatically; only the genuine
  isolation knob (`PATH_ENABLED=false`) is overridden explicitly.
- Bonus (umbrella comment): `UNRESOLVED_VARIABLE` now reports the
  actual SQL PATH searched. `unresolvedVariableError` is collapsed
  into a single method that takes `Seq[Seq[String]]` path entries with
  an optional `Origin` and renders `searchPath` as
  `[entry1, entry2, ...]` -- byte-for-byte consistent with
  `UNRESOLVED_ROUTINE` and `TABLE_OR_VIEW_NOT_FOUND`.

### Why are the changes needed?

These are the cleanup items called out on SPARK-56681 from the
post-merge source review of SPARK-56605. They eliminate dead code
paths, plug user-visible bugs (force-loading a misconfigured default
catalog on column resolution; clobbering pinned session configs;
swallowing real catalog errors as `UNRESOLVED_ROUTINE`), remove the
asymmetry between DDL and DML on session variables, and make
`UNRESOLVED_VARIABLE` self-consistent with the other "not found"
errors.

### Does this PR introduce _any_ user-facing change?

Yes:

- `UNRESOLVED_VARIABLE.searchPath` is now always rendered as a
  bracketed list (`` [`system`.`session`] ``), matching
  `UNRESOLVED_ROUTINE` and `TABLE_OR_VIEW_NOT_FOUND`. For unqualified
  `SET VAR` failures the list reflects the actual SQL PATH that was
  consulted instead of a hardcoded `SYSTEM.SESSION`. The error also
  carries a `queryContext` for `SET VAR` since `ResolveSetVariable`
  now passes the current origin.
- `DROP TEMPORARY VARIABLE` no longer raises `UNRESOLVED_VARIABLE`
  when the SQL PATH does not contain `system.session`. DDL on session
  variables ignores PATH; this matches the existing behaviour of
  `DECLARE OR REPLACE VARIABLE`.
- `lookupFunctionType` no longer swallows non-`NotFound` errors. A
  catalog reporting `PERMISSION_DENIED` (or similar) for a function
  lookup now propagates instead of silently producing
  `UNRESOLVED_ROUTINE`.

### How was this patch tested?

- Updated `SetPathSuite` (DECLARE / SET VAR / DROP cycle under a
  non-default PATH; updated bonus test to assert the actual rendered
  search path).
- Updated `SqlScriptingExecutionSuite` (local-variable SET error
  expectation) and the `sql-session-variables.sql` golden files
  (regenerated) for the new bracketed `searchPath` format.
- Added `resolutionPathEntriesForAnalysis` stubs to mocked
  `CatalogManager` instances in `PlanResolutionSuite`,
  `AlignAssignmentsSuiteBase`, and `TableLookupCacheSuite`.
- Ran focused suites locally:
  - `build/sbt 'sql/testOnly *SetPathSuite *SqlScriptingExecutionSuite *ExecuteImmediateEndToEndSuite'`
  - `build/sbt 'sql/testOnly *SimpleSQLViewSuite *SQLFunctionSuite'`
  - `build/sbt 'sql/testOnly *PlanResolutionSuite *UpdateTableAlignAssignmentsSuite *MergeIntoTableAlignAssignmentsSuite'`
  - `build/sbt 'catalyst/testOnly *TableLookupCacheSuite *AnalysisSuite *AnalysisErrorSuite *LookupFunctionsSuite'`
  - `build/sbt 'sql/testOnly *FunctionQualificationSuite *RelationQualificationSuite *DataSourceV2FunctionSuite'`
  - `build/sbt 'sql/testOnly *SQLQuerySuite'`
  - `build/sbt 'connect/testOnly *ProtoToParsedPlanTestSuite'`
  - `build/sbt 'sql/testOnly *SQLQueryTestSuite -- -z sql-session-variables.sql'`
  - All catalyst analysis suites and single-pass resolver suites.
- `scalastyle` clean across catalyst, sql, and connect modules.

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

Generated-by: Cursor Claude Opus 4.7
Replace comments that describe how the code came to be (history /
JIRA pointers) with comments that describe what the code does:

- `ResolveSetVariable`: drop the rendering rationale at the throw
  site -- documented at `unresolvedVariableError` /
  `searchPathEntriesForError`.
- `FunctionResolution.lookupFunctionType`: tighten the system-prefix
  filter and exception-list comments.
- `ResolveCatalogs`: tighten the DROP VARIABLE policy comment.
- `Analyzer.AnalysisContext.resolutionPathEntries`: condense lifecycle
  paragraph and replace prose follow-up with a single TODO marker.
- `FunctionResolution.sqlResolutionPathEntriesForAnalysis`: state the
  invariant (relation/routine share path entries) instead of narrating
  the routing.
- `SetPathSuite`, `SqlScriptingExecutionSuite`: drop test-side prose
  about why the rendering changed.

No behavior change.
Variables only live in `system.session` -- there is no persistent
variable catalog -- so the gate and the search-path-for-error are
both simply "is `system.session` on the SQL path?".

- Replace `CatalogManager.sessionScopeUnqualifiedAllowed(catalog,
  namespace)` with `CatalogManager.isSystemSessionOnPath`, which
  inspects the stored session path entries directly. No catalog
  load, no marker expansion: `CurrentSchemaEntry` resolves to the
  current schema and can never be `system.session`, so only literal
  entries matter.
- `VariableResolution.allowUnqualifiedSessionTempVariableLookup`
  collapses to a one-liner: pass through qualified names, gate
  unqualified ones on `isSystemSessionOnPath`.
- `VariableResolution.searchPathEntriesForError` always returns
  `[Seq(SYSTEM, SESSION)]` -- there is nowhere else variables can
  live, regardless of name-part count or PATH contents.

Test expectation updates for the new always-`[system.session]` shape:
`SetPathSuite` and `SqlScriptingExecutionSuite`. Golden files
unchanged (already render `[system.session]` from the qualified path).

No behavior change beyond the error message: variable lookup still
only consults `system.session`.
…sion is off PATH

If the user removes `system.session` from the SQL path, an unqualified
SET VAR lookup never actually consults any namespace. Reporting
`[system.session]` as the search path in that case would be a lie:
nothing was searched. Return `[]` instead so the error reflects what
the resolver actually did.

`searchPathEntriesForError`:
- `system.session` on PATH -> `[Seq("system", "session")]`
- `system.session` off PATH -> `Seq.empty`

`SetPathSuite` updated to expect `[]` for the off-PATH case.
…IABLE

Resolution and search-path reporting are separate concerns:

- The resolver may *optimize* by skipping path entries where the
  looked-up object kind cannot live (e.g. `RelationResolution` skips
  `system.builtin`; the variable gate skips everything except
  `system.session`).
- The reported `searchPath` is the full path the resolver *would*
  consult: `TABLE_OR_VIEW_NOT_FOUND` keeps `system.builtin` in its
  list even though no tables can be there.

`UNRESOLVED_VARIABLE` should follow the same convention. For
unqualified names, report the full resolved SQL path; for qualified
names, report `[system.session]` (PATH does not apply).

`isSystemSessionOnPath` is still used as the lookup gate, so the hot
path never loads the configured default catalog. The full-path read
in `searchPathEntriesForError` only happens at error time.

Test expectations updated: `SetPathSuite` and
`SqlScriptingExecutionSuite` now assert the actual full SQL PATH.
Reduce diff against `apache/spark master` by undoing changes that did
not carry their weight:

- Drop `unresolvedVariableError(name, pathEntries, origin)` overload
  and the corresponding `CurrentOrigin.get` pass in
  `ResolveSetVariable`. Master never had a caller for the origin form,
  and historical `UNRESOLVED_VARIABLE` errors don't carry a
  `queryContext`. Single 2-arg overload is enough.
- Restore master's docstrings on `AnalysisContext.resolutionPathEntries`,
  `FunctionResolution.sqlResolutionPathEntriesForAnalysis`,
  `RelationResolution.relationResolutionEntries`, and
  `VariableResolution.allowUnqualifiedSessionTempVariableLookup`.
  The behaviour each describes is unchanged; the new prose was just
  narration.
- Remove the now-unused doc on `unresolvedVariableError` and tighten
  `CatalogManager.isSystemSessionOnPath`.
- Regenerated golden files have a single mechanical change per hunk:
  add brackets to `searchPath`. No more added `queryContext` blocks.

Net effect: ~25% fewer lines added vs master while keeping all 11
items + bonus addressed and all tests passing.
…bleError

Every variable-resolution failure now carries a `queryContext`
pointing at the SQL fragment that triggered it, matching what
`TABLE_OR_VIEW_NOT_FOUND` and `UNRESOLVED_ROUTINE` already do.

- Collapse `unresolvedVariableError` to a single 3-arg signature with
  required `Origin`. No optional default, no two-overload pattern.
- `VariableManager.set` now takes an `origin: Origin` parameter that
  it forwards to `unresolvedVariableError`. Both implementations
  (`TempVariableManager`, `SqlScriptingLocalVariableManager`) and all
  three callers (`SetVariableExec`, `FetchCursorExec`,
  `VariableAssignmentUtils`) updated to thread the calling
  `VariableReference`'s origin.
- Analyzer-rule sites pass `CurrentOrigin.get`:
  `ResolveSetVariable`, `ResolveCatalogs.assertValidSessionVariableNameParts`,
  `ResolveFetchCursor`.

Regenerated `sql-session-variables.sql.out` shows every
UNRESOLVED_VARIABLE error now lists `searchPath` as a bracketed list
and carries a `queryContext` with the offending SQL fragment.
Previously `UNRESOLVED_VARIABLE.queryContext` highlighted the entire
SQL statement (e.g. `DECLARE OR REPLACE VARIABLE builtin.var1 INT`)
because `withIdentClause` deliberately does not wrap its builder in
`withOrigin(ctx)`. The identifier-specific origin per the
`withIdentClause` doc comment is opt-in per call site.

Opt in for the three variable parser paths so the error highlights
just the variable identifier:
- `visitCreateVariable`: wrap each `UnresolvedIdentifier` with
  `withOrigin(identifierReference)`.
- `visitDropVariable`: wrap the `UnresolvedIdentifier` with
  `withOrigin(ctx.identifierReference())`.
- `visitSetVariableImpl`: wrap each `UnresolvedAttribute` with
  `withOrigin(variableIdent)` / `withOrigin(assign.key)`.

Plumb the inner identifier's origin through the analyzer rules
(`u.origin` instead of `CurrentOrigin.get`):
- `ResolveSetVariable` (`UnresolvedAttribute`).
- `ResolveFetchCursor` (`UnresolvedAttribute`).
- `ResolveCatalogs.assertValidSessionVariableNameParts` now takes an
  explicit `origin: Origin`; both call sites pass the matched
  `UnresolvedIdentifier`'s origin.

Regenerated `sql-session-variables.sql.out` confirms the fragment is
now the variable identifier (e.g. `"builtin.var1"`, `"ses.var1"`)
instead of the whole statement. `SetPathSuite` /
`SqlScriptingExecutionSuite` `ExpectedContext` updated to match.
…_VARIABLE

DML lookup failures (`SET VAR`, `FETCH ... INTO`) now report the full
SQL path regardless of how the name was qualified, matching the
convention used by `TABLE_OR_VIEW_NOT_FOUND` and
`UNRESOLVED_ROUTINE`.

Qualification-independent rendering also future-proofs the error
shape: if Spark ever grows struct-field assignment, 2-part forms like
`SET VAR session.foo = ...` become genuinely ambiguous between
qualified-variable and field-access interpretations -- and the
resolver tries multiple interpretations through PATH-gated lookup.
The error format will not have to change.

DDL (`DECLARE` / `DROP` name validation) is unchanged: it does not
consult the SQL path and continues to report `[system.session]`
directly from `ResolveCatalogs.assertValidSessionVariableNameParts`.

Added regression test `Dropped struct variable -- field access vs
qualified name` to `sql-session-variables.sql` demonstrating the
field-vs-qualified ambiguity and that `SELECT session.a` after
dropping a struct variable falls through to `UNRESOLVED_COLUMN`.

`searchPathEntriesForError` now takes no arguments and always
returns the full path from `CatalogManager.resolutionPathEntriesForAnalysis`.
…uite

CI complained about an unformatted scaladoc line. Fix is purely a
line-wrap change emitted by

  ./build/mvn scalafmt:format -Dscalafmt.skip=false \
    -Dscalafmt.validateOnly=false -Dscalafmt.changedOnly=false \
    -pl sql/api -pl sql/connect/common -pl sql/connect/server \
    -pl sql/connect/shims -pl sql/connect/client/jvm
Copy link
Copy Markdown
Contributor

@cloud-fan cloud-fan left a comment

Choose a reason for hiding this comment

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

Summary

Prior state and problem. SPARK-56605 introduced the SQL PATH machinery (search path for routines, relations, variables) and SPARK-56639 wired most of it. The SPARK-56681 umbrella enumerated 11 follow-ups from the post-merge review: dead code, an unfinished gate, three near-duplicate path-resolution helpers, an over-broad NonFatal swallow that hid permission errors as UNRESOLVED_ROUTINE, a hot-path currentCatalog load that could fault on misconfigured DEFAULT_CATALOG, an asymmetric DROP TEMPORARY VARIABLE PATH gate (DDL behaved differently than DECLARE/CREATE), executeAndCheck clobbering an outer SQLConf.withExistingConf (e.g. SQL UDF / view body's pinned configs), a bare SQLConf in ProtoToParsedPlanTestSuite that diverged from sparkConf, and UNRESOLVED_VARIABLE hardcoding SYSTEM.SESSION regardless of the actual SQL path.

Design approach. Centralize the three near-duplicate path helpers into one shared method CatalogManager.resolutionPathEntriesForAnalysis(pinnedEntries, viewCatalogAndNamespace) that all of routine, relation, and variable resolution route through. Replace the hot-path catalog read in unqualified variable lookup with CatalogManager.isSystemSessionOnPath which inspects stored session-path entries directly. Lift conf-yielding logic out of executeAndCheck / executeSameContext into a runWithSessionConf helper that yields to any outer withExistingConf scope (via the new SQLConf.getExistingConfIfSet peek). For variable errors, change UNRESOLVED_VARIABLE.searchPath from a single dotted path to a bracketed list of path entries (matching TABLE_OR_VIEW_NOT_FOUND / UNRESOLVED_ROUTINE); make Origin mandatory on unresolvedVariableError and plumb it through VariableManager.set so all error sites carry a queryContext pointing at the variable identifier.

Key design decisions.

  • resolutionPathEntriesForAnalysis lives in CatalogManager (Coordination) and takes AnalysisContext data as parameters rather than reading the threadlocal directly — preserves the package boundary so connector.catalog doesn't depend on analysis.
  • runWithSessionConf uses getExistingConfIfSet (threadlocal peek) instead of SQLConf.get. When there's no outer withExistingConf scope but SQLConf.get differs from the analyzer's pinned conf, the new code installs the pinned conf where the old executeSameContext would yield. This is the deliberate fix for #4: analyzer isolation should win over the active session conf when no explicit outer scope was opened.
  • DROP TEMPORARY VARIABLE's PATH gate is removed entirely (rather than mirroring it onto DECLARE/CREATE): DDL on session variables targets system.session directly, symmetric with DECLARE / CREATE OR REPLACE.
  • UNRESOLVED_VARIABLE.searchPath rendering switches to Seq[Seq[String]] + bracketed mkString. Old single-arg overload removed (no overloads).

Implementation sketch.

  • Coordination (CatalogManager): adds isSystemSessionOnPath and resolutionPathEntriesForAnalysis; removes sessionScopeUnqualifiedAllowed.
  • Strategy (FunctionResolution, RelationResolution, VariableResolution): all three delegate to the new shared method. FunctionResolution.lookupFunctionType additionally narrows the swallowed exception list (matches the pre-existing pattern at lines 146-153) and filters system.* candidates from the persistent loop.
  • Resolution rules (ResolveCatalogs, ResolveSetVariable, ResolveFetchCursor): DROP path gate removed; Origin plumbed through assertValidSessionVariableNameParts.
  • Storage (TempVariableManager, SqlScriptingLocalVariableManager, VariableManager trait): set adds an origin: Origin parameter, mechanically threaded to error sites.
  • Analyzer: runWithSessionConf helper used by both executeAndCheck and executeSameContext; SQLConf.getExistingConfIfSet exposes the threadlocal peek.
  • Parser (AstBuilder): withOrigin(identifierReference) opt-ins for CreateVariable / DropVariable / SetVariable so error highlights pin to the variable identifier.
  • Tests: golden file regen for new bracketed searchPath and identifier-pinned queryContext; new struct-variable-vs-column ambiguity test; DECLARE/SET VAR/DROP cycle test; mocked CatalogManager stubs in three suites.

One real bug below (ResolveFetchCursor still hardcodes the path, contradicting the bonus item) plus a few clarity questions on the design.

- ResolveFetchCursor: route through `variableResolution.searchPathEntriesForError`
  instead of hardcoding `Seq(Seq("SYSTEM", "SESSION"))`, so `FETCH ... INTO`
  miss reports the full SQL path. Bonus item now consistent for SET VAR and
  FETCH INTO.
- CatalogManager.isSystemSessionOnPath: spell out the load-bearing assumption
  ("`system` is not a registered catalog") so a future change that registers
  a real catalog under `system` won't silently break the short-circuit.
- FunctionResolution.lookupFunctionType: tighten the `system.*` filter to
  match the actually-resolved set (`system.session`, `system.builtin`)
  rather than any prefix beginning with `system`. A future namespace under
  `system.<x>` not covered by `identifierFromSystemNameParts` will now go
  through persistent lookup as expected.
Copy link
Copy Markdown
Contributor

@cloud-fan cloud-fan left a comment

Choose a reason for hiding this comment

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

Re-review at c2f8f64 — 4 addressed, 0 remaining, 1 new (late catch).

All four prior findings addressed:

  • ResolveFetchCursor now routes through variableResolution.searchPathEntriesForError.
  • FunctionResolution filter narrowed to system.session/system.builtin only — tracks exactly what identifierFromSystemNameParts accepts.
  • CatalogManager Scaladoc spells out the load-bearing assumption (system is not a registered catalog).
  • VariableResolution searchPath rendering confirmed deliberate (full-path matches TABLE_OR_VIEW_NOT_FOUND/UNRESOLVED_ROUTINE; the existing doc on searchPathEntriesForError already captures the rationale).

One late catch inline below: the FETCH … INTO fix is uncovered by tests — adding a symmetric SetPathSuite (or SqlScriptingCursorE2eSuite) case would lock in the bracketed-list rendering for the path the bonus item explicitly promises.

LGTM otherwise.

Symmetric coverage for the bonus item alongside the existing SET VAR
test in SetPathSuite. Verifies that under a non-default PATH that
excludes system.session, an unqualified FETCH ... INTO target fails
with UNRESOLVED_VARIABLE and the searchPath is the bracketed list of
the actual SQL path -- locking in the behavior wired up in the
previous commit (FETCH ... INTO routes through
`variableResolution.searchPathEntriesForError`).
@dtenedor
Copy link
Copy Markdown
Contributor

dtenedor commented May 4, 2026

LGTM, the one CI failure looks flaky/unrelated. Merging to master

@dtenedor dtenedor closed this in 3a3e4ed May 4, 2026
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.

3 participants