Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2ed877c
Redesign EXECUTE IMMEDIATE
srielau Aug 29, 2025
eb92a0c
Named parameter support
srielau Aug 29, 2025
ab76d3f
INTO clause support
srielau Aug 29, 2025
0e151c8
Phase 3
srielau Aug 29, 2025
f8d1920
Complete function for EXECUTE IMMEDIATE REWORK
srielau Aug 30, 2025
8a8b3e4
Update common/utils/src/main/resources/error/error-conditions.json
srielau Aug 30, 2025
1aeaac9
Rework
srielau Aug 30, 2025
7bf196c
Fix testcases
srielau Aug 30, 2025
0198a3d
Fix more testcases
srielau Aug 30, 2025
dd3d2bc
Cleanup
srielau Aug 30, 2025
65b31dd
Cleanup 2
srielau Aug 30, 2025
b7c3aef
re-add columnResolutionHelper
srielau Aug 31, 2025
ea5d87e
Enable SQL Scripting with EXECUTE IMMEDIATE
srielau Aug 31, 2025
2e44eb0
Add another testcase
srielau Aug 31, 2025
e69e214
Clean up
srielau Aug 31, 2025
0c39ea2
Update sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/anal…
srielau Sep 2, 2025
9b67877
Update sql/core/src/test/resources/sql-tests/inputs/execute-immediate…
srielau Sep 2, 2025
d3d4511
Update sql/core/src/test/resources/sql-tests/inputs/execute-immediate…
srielau Sep 2, 2025
c1270d6
Update sql/hive/src/main/scala/org/apache/spark/sql/hive/HiveSessionS…
srielau Sep 2, 2025
b18c99c
Update sql/core/src/test/resources/sql-tests/inputs/execute-immediate…
srielau Sep 2, 2025
04f91ab
Update sql/core/src/test/resources/sql-tests/inputs/execute-immediate…
srielau Sep 2, 2025
bde8617
Address code review comments by David and Wenchen
srielau Sep 2, 2025
a7ab0d4
More code review comments
srielau Sep 2, 2025
bf1e7c8
Fox error-condiditions
srielau Sep 3, 2025
928c5d9
Code cleanup
srielau Sep 4, 2025
77d7268
Reduce complexity around INTO clause
srielau Sep 4, 2025
0588b93
Fixed parameter tests in parameters.scala
srielau Sep 4, 2025
b501c51
simplify parameters.scala
srielau Sep 4, 2025
af6f403
more fixes
srielau Sep 5, 2025
769cadc
Move testcases
srielau Sep 5, 2025
ecbc859
Fixes
srielau Sep 5, 2025
37e927f
code review
srielau Sep 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions common/utils/src/main/resources/error/error-conditions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2737,6 +2737,12 @@
],
"sqlState" : "42001"
},
"INVALID_EXPR_TYPE_FOR_QUERY_EXECUTE_IMMEDIATE" : {
"message" : [
"Expression type must be string type but got <exprType>."
],
"sqlState" : "42K09"
},
"INVALID_EXTERNAL_TYPE" : {
"message" : [
"The external type <externalType> is not valid for the type <type> at the expression <expr>."
Expand Down Expand Up @@ -3914,12 +3920,6 @@
},
"sqlState" : "42K0M"
},
"INVALID_VARIABLE_TYPE_FOR_QUERY_EXECUTE_IMMEDIATE" : {
"message" : [
"Variable type must be string type but got <varType>."
],
"sqlState" : "42K09"
},
"INVALID_VARIANT_CAST" : {
"message" : [
"The variant value `<value>` cannot be cast into `<dataType>`. Please use `try_variant_get` instead."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,27 +397,14 @@ setResetStatement
;

executeImmediate
: EXECUTE IMMEDIATE queryParam=executeImmediateQueryParam (INTO targetVariable=multipartIdentifierList)? executeImmediateUsing?
: EXECUTE IMMEDIATE queryParam=expression (INTO targetVariable=multipartIdentifierList)? executeImmediateUsing?
;

executeImmediateUsing
: USING LEFT_PAREN params=namedExpressionSeq RIGHT_PAREN
| USING params=namedExpressionSeq
;

executeImmediateQueryParam
: stringLit
| multipartIdentifier
;

executeImmediateArgument
: (constant|multipartIdentifier) (AS name=errorCapturingIdentifier)?
;

executeImmediateArgumentSeq
: executeImmediateArgument (COMMA executeImmediateArgument)*
;

timezone
: stringLit
| LOCAL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ object FakeV2SessionCatalog extends TableCatalog with FunctionCatalog with Suppo
* if `t` was a permanent table when the current view was created, it
* should still be a permanent table when resolving the current view,
* even if a temp view `t` has been created.
* @param isExecuteImmediate Whether the current plan is created by EXECUTE IMMEDIATE. Used when
* resolving variables, as SQL Scripting local variables should not be
* visible from EXECUTE IMMEDIATE.
* @param outerPlan The query plan from the outer query that can be used to resolve star
* expressions in a subquery.
*/
Expand All @@ -155,7 +152,6 @@ case class AnalysisContext(
referredTempFunctionNames: mutable.Set[String] = mutable.Set.empty,
referredTempVariableNames: Seq[Seq[String]] = Seq.empty,
outerPlan: Option[LogicalPlan] = None,
isExecuteImmediate: Boolean = false,
collation: Option[String] = None,

/**
Expand Down Expand Up @@ -212,19 +208,11 @@ object AnalysisContext {
viewDesc.viewReferredTempViewNames,
mutable.Set(viewDesc.viewReferredTempFunctionNames: _*),
viewDesc.viewReferredTempVariableNames,
isExecuteImmediate = originContext.isExecuteImmediate,
collation = viewDesc.collation)
set(context)
try f finally { set(originContext) }
}

def withExecuteImmediateContext[A](f: => A): A = {
val originContext = value.get()
val context = originContext.copy(isExecuteImmediate = true)

set(context)
try f finally { set(originContext) }
}

def withNewAnalysisContext[A](f: => A): A = {
val originContext = value.get()
Expand Down Expand Up @@ -444,6 +432,7 @@ class Analyzer(override val catalogManager: CatalogManager) extends RuleExecutor
AddMetadataColumns ::
DeduplicateRelations ::
ResolveCollationName ::
new ResolveExecuteImmediate(catalogManager)::
ResolveMergeIntoSchemaEvolution ::
new ResolveReferences(catalogManager) ::
// Please do not insert any other rules in between. See the TODO comments in rule
Expand Down Expand Up @@ -495,10 +484,6 @@ class Analyzer(override val catalogManager: CatalogManager) extends RuleExecutor
RewriteMergeIntoTable ::
MoveParameterizedQueriesDown ::
BindParameters ::
new SubstituteExecuteImmediate(
catalogManager,
resolveChild = executeSameContext,
checkAnalysis = checkAnalysis) ::
typeCoercionRules() ++
Seq(
ResolveWithCTE,
Expand Down Expand Up @@ -1802,8 +1787,6 @@ class Analyzer(override val catalogManager: CatalogManager) extends RuleExecutor
case s: Sort if !s.resolved || s.missingInput.nonEmpty =>
resolveReferencesInSort(s)

// Pass for Execute Immediate as arguments will be resolved by [[SubstituteExecuteImmediate]].
case e : ExecuteImmediateQuery => e

case d: DataFrameDropColumns if !d.resolved =>
resolveDataFrameDropColumns(d)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ trait ColumnResolutionHelper extends Logging with DataTypeErrorsBase {
variableResolution.resolveMultipartName(
nameParts = nameParts,
resolvingView = AnalysisContext.get.catalogAndNamespace.nonEmpty,
resolvingExecuteImmediate = AnalysisContext.get.isExecuteImmediate,
referredTempVariableNames = AnalysisContext.get.referredTempVariableNames
).map(e => Alias(e, nameParts.last)())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ class ResolveCatalogs(val catalogManager: CatalogManager)
}

private def withinSqlScript: Boolean =
SqlScriptingContextManager.get().map(_.getVariableManager).isDefined &&
!AnalysisContext.get.isExecuteImmediate
SqlScriptingContextManager.get().map(_.getVariableManager).isDefined

private def assertValidSessionVariableNameParts(
nameParts: Seq[String],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ class ResolveSetVariable(val catalogManager: CatalogManager) extends Rule[Logica
val resolvedVars = setVariable.targetVariables.map {
case u: UnresolvedAttribute =>
variableResolution.lookupVariable(
nameParts = u.nameParts,
resolvingExecuteImmediate = AnalysisContext.get.isExecuteImmediate
nameParts = u.nameParts
) match {
case Some(variable) => variable.copy(canFold = false)
case _ => throw unresolvedVariableError(u.nameParts, Seq("SYSTEM", "SESSION"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ class VariableResolution(tempVariableManager: TempVariableManager) extends SQLCo
* (e.g., ["catalog", "schema", "variable", "field1", "field2"])
* @param resolvingView Whether this resolution is happening within a view context.
* When true, only variables explicitly referred to in the view definition are accessible.
* @param resolvingExecuteImmediate Whether this resolution is happening within an
* EXECUTE IMMEDIATE context. When true, local variables are not accessible, only session
* variables.
* @param referredTempVariableNames When resolving within a view, this contains the list of
* variable names that the view explicitly references and should have access to.
*
Expand All @@ -65,7 +62,6 @@ class VariableResolution(tempVariableManager: TempVariableManager) extends SQLCo
def resolveMultipartName(
nameParts: Seq[String],
resolvingView: Boolean,
resolvingExecuteImmediate: Boolean,
referredTempVariableNames: Seq[Seq[String]]): Option[Expression] = {
var resolvedVariable: Option[Expression] = None
// We only support temp variables for now, so the variable name can at most have 3 parts.
Expand All @@ -76,7 +72,6 @@ class VariableResolution(tempVariableManager: TempVariableManager) extends SQLCo
resolvedVariable = resolveVariable(
nameParts = nameParts.dropRight(numInnerFields),
resolvingView = resolvingView,
resolvingExecuteImmediate = resolvingExecuteImmediate,
referredTempVariableNames = referredTempVariableNames
)

Expand All @@ -99,16 +94,12 @@ class VariableResolution(tempVariableManager: TempVariableManager) extends SQLCo

/**
* Look up variable by nameParts.
* If in SQL Script, first check local variables, unless in EXECUTE IMMEDIATE
* (EXECUTE IMMEDIATE generated query cannot access local variables).
* if not found fall back to session variables.
* If in SQL Script, first check local variables.
* If not found fall back to session variables.
* @param nameParts NameParts of the variable.
* @param resolvingExecuteImmediate Whether the current context is in EXECUTE IMMEDIATE.
* @return Reference to the variable.
*/
def lookupVariable(
nameParts: Seq[String],
resolvingExecuteImmediate: Boolean): Option[VariableReference] = {
def lookupVariable(nameParts: Seq[String]): Option[VariableReference] = {
val namePartsCaseAdjusted = if (conf.caseSensitiveAnalysis) {
nameParts
} else {
Expand All @@ -118,8 +109,6 @@ class VariableResolution(tempVariableManager: TempVariableManager) extends SQLCo
SqlScriptingContextManager
.get()
.map(_.getVariableManager)
// If we are in EXECUTE IMMEDIATE lookup only session variables.
.filterNot(_ => resolvingExecuteImmediate)
// If variable name is qualified with session.<varName> treat it as a session variable.
.filterNot(
_ =>
Expand Down Expand Up @@ -156,16 +145,15 @@ class VariableResolution(tempVariableManager: TempVariableManager) extends SQLCo
private def resolveVariable(
nameParts: Seq[String],
resolvingView: Boolean,
resolvingExecuteImmediate: Boolean,
referredTempVariableNames: Seq[Seq[String]]): Option[Expression] = {
if (resolvingView) {
if (referredTempVariableNames.contains(nameParts)) {
lookupVariable(nameParts = nameParts, resolvingExecuteImmediate = resolvingExecuteImmediate)
lookupVariable(nameParts = nameParts)
} else {
None
}
} else {
lookupVariable(nameParts = nameParts, resolvingExecuteImmediate = resolvingExecuteImmediate)
lookupVariable(nameParts = nameParts)
}
}

Expand Down
Loading