Skip to content

Commit e40dc30

Browse files
daniel-lunin-dbMaxGekk
authored andcommitted
[SPARK-57756][SQL] Move SQL UDF resolution SQLConf construction to common code
SessionCatalog.makeSQLFunctionPlan and the ResolveSQLFunctions / ResolveSQLTableFunctions analyzer rules each inline the same throwaway-SQLConf construction used to resolve a SQL UDF body: new SQLConf, seed from the function's stored configs, then either set ANSI unconditionally or overlay the session's retained resolution configs. Move it into a common Analyzer.buildSQLFunctionConf helper, parameterized by alwaysSetAnsiValue and applySessionOverrides, and have the three call sites delegate to it. Behavior-preserving. ### What changes were proposed in this pull request? `SessionCatalog.makeSQLFunctionPlan` and the `ResolveSQLFunctions` / `ResolveSQLTableFunctions` analyzer rules each inline the same throwaway-`SQLConf` construction used to resolve a SQL UDF body. This moves it into a common `Analyzer.buildSQLFunctionConf` helper, parameterized by `alwaysSetAnsiValue` and `applySessionOverrides`, and has the three call sites delegate to it. ### Why are the changes needed? The three call sites carried the same conf-seeding logic. Unifying it removes the duplication and gives one place to reason about a SQL UDF body's resolution conf. ### Does this PR introduce _any_ user-facing change? No. The helper produces the same `SQLConf` the inline code did. ### How was this patch tested? Behavior-preserving extraction, covered by existing SQL UDF resolution tests; CI on this PR compiles and runs them. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Code (Anthropic Claude Opus) Closes #56895 from daniel-lunin-db/SPARK-57756. Authored-by: daniel-lunin-db <daniel.lunin@databricks.com> Signed-off-by: Max Gekk <max.gekk@gmail.com>
1 parent ad958d4 commit e40dc30

2 files changed

Lines changed: 39 additions & 13 deletions

File tree

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,33 @@ object Analyzer {
327327
}
328328
}
329329
}
330+
331+
/**
332+
* Builds the throwaway [[SQLConf]] used to resolve a SQL UDF body, seeded from the function's
333+
* stored configs. Call sites share this seeding but differ in how they apply ANSI and session
334+
* overrides, so both are parameters:
335+
* - `alwaysSetAnsiValue` calls [[trySetAnsiValue]] unconditionally (the SessionCatalog plan
336+
* builders); when false, ANSI is set only as part of the session overlay below (the
337+
* [[Analyzer#ResolveSQLFunctions]] / [[Analyzer#ResolveSQLTableFunctions]] rules).
338+
* - `applySessionOverrides` overlays the active session's retained resolution configs via
339+
* [[retainResolutionConfigsForAnalysis]], gated on
340+
* [[SQLConf.APPLY_SESSION_CONF_OVERRIDES_TO_FUNCTION_RESOLUTION]].
341+
*/
342+
def buildSQLFunctionConf(
343+
function: SQLFunction,
344+
applySessionOverrides: Boolean,
345+
alwaysSetAnsiValue: Boolean): SQLConf = {
346+
val functionConf = new SQLConf()
347+
function.getSQLConfigs.foreach { case (k, v) => functionConf.settings.put(k, v) }
348+
if (alwaysSetAnsiValue) {
349+
trySetAnsiValue(functionConf)
350+
}
351+
if (applySessionOverrides &&
352+
conf.getConf(SQLConf.APPLY_SESSION_CONF_OVERRIDES_TO_FUNCTION_RESOLUTION)) {
353+
retainResolutionConfigsForAnalysis(newConf = functionConf, existingConf = conf)
354+
}
355+
functionConf
356+
}
330357
}
331358

332359
/**
@@ -2695,11 +2722,10 @@ class Analyzer(
26952722
val plan = v1SessionCatalog.makeSQLFunctionPlan(f.name, f.function, f.inputs)
26962723
val resolved = SQLFunctionContext.withSQLFunction {
26972724
// Resolve the SQL function plan using its context.
2698-
val newConf = new SQLConf()
2699-
f.function.getSQLConfigs.foreach { case (k, v) => newConf.settings.put(k, v) }
2700-
if (conf.getConf(SQLConf.APPLY_SESSION_CONF_OVERRIDES_TO_FUNCTION_RESOLUTION)) {
2701-
Analyzer.retainResolutionConfigsForAnalysis(newConf = newConf, existingConf = conf)
2702-
}
2725+
val newConf = Analyzer.buildSQLFunctionConf(
2726+
function = f.function,
2727+
applySessionOverrides = true,
2728+
alwaysSetAnsiValue = false)
27032729
SQLConf.withExistingConf(newConf) {
27042730
AnalysisContext.withAnalysisContext(f.function) {
27052731
executeSameContext(plan)
@@ -3005,11 +3031,10 @@ class Analyzer(
30053031
_.containsPattern(SQL_TABLE_FUNCTION)) {
30063032
case SQLTableFunction(name, function, inputs, output) =>
30073033
// Resolve the SQL table function plan using its function context.
3008-
val newConf = new SQLConf()
3009-
function.getSQLConfigs.foreach { case (k, v) => newConf.settings.put(k, v) }
3010-
if (conf.getConf(SQLConf.APPLY_SESSION_CONF_OVERRIDES_TO_FUNCTION_RESOLUTION)) {
3011-
Analyzer.retainResolutionConfigsForAnalysis(newConf = newConf, existingConf = conf)
3012-
}
3034+
val newConf = Analyzer.buildSQLFunctionConf(
3035+
function = function,
3036+
applySessionOverrides = true,
3037+
alwaysSetAnsiValue = false)
30133038
val resolved = SQLConf.withExistingConf(newConf) {
30143039
val plan = v1SessionCatalog.makeSQLTableFunctionPlan(name, function, inputs, output)
30153040
SQLFunctionContext.withSQLFunction {

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/SessionCatalog.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,9 +1893,10 @@ class SessionCatalog(
18931893
val funcName = function.name.funcName
18941894

18951895
// Use captured SQL configs when parsing a SQL function.
1896-
val conf = new SQLConf()
1897-
function.getSQLConfigs.foreach { case (k, v) => conf.settings.put(k, v) }
1898-
Analyzer.trySetAnsiValue(conf)
1896+
val conf = Analyzer.buildSQLFunctionConf(
1897+
function = function,
1898+
applySessionOverrides = false,
1899+
alwaysSetAnsiValue = true)
18991900
SQLConf.withExistingConf(conf) {
19001901
val inputParam = function.inputParam
19011902
val returnType = function.getScalarFuncReturnType

0 commit comments

Comments
 (0)