Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
bdd15a9
Add cursor support to Spark SQL Scripting
srielau Dec 18, 2025
c28dd8f
Initial implementation
srielau Dec 18, 2025
eab7cd3
[SPARK-54759][SQL][WIP] Cursor support
srielau Dec 18, 2025
b64942e
Complete MVP fucntion.
srielau Dec 19, 2025
9678674
Parameterized cursor support
srielau Dec 19, 2025
8144b3d
Paramneterized cursor support
srielau Dec 19, 2025
87874ef
Fix error messages
srielau Dec 20, 2025
701f070
Fix keywords
srielau Dec 20, 2025
e7502e6
Fix more bugs
srielau Dec 20, 2025
bdfda8c
more fixes
srielau Dec 20, 2025
ca980ef
add tests for session variables
srielau Dec 20, 2025
8368ed6
more fixes
srielau Dec 20, 2025
8d782d6
proper NOT FOUND handler
srielau Dec 20, 2025
81c8eb4
Merge branch 'master' into cursors
srielau Dec 20, 2025
ea28537
more tests
srielau Dec 20, 2025
cc8419c
Fix parameter marker bind in
srielau Dec 21, 2025
7d5633e
Update sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plan…
srielau Dec 27, 2025
69d76df
Address review comments and add config.
srielau Dec 27, 2025
f5f4b77
Fix identifier processing for cursors
srielau Dec 27, 2025
883492f
Fix errro message order, pass multipart names, add case isnensitivity…
srielau Dec 27, 2025
07530cf
Init commit
miland-db Jan 10, 2026
376b8ee
Add isInCondition override in WhileStatementExec
miland-db Jan 10, 2026
6006876
Merge branch 'master' into cursors
srielau Jan 11, 2026
23070e7
[SPARK-XXXXX][SQL] Address cursor PR review comments
srielau Jan 11, 2026
f519917
[SPARK-XXXXX][SQL][WIP] Refactor cursor commands to use CursorReferen…
srielau Jan 11, 2026
f71d1e5
[SPARK-XXXXX][SQL] Refactor cursor commands to use CursorReference ex…
srielau Jan 11, 2026
bdf3bb7
[SPARK-XXXXX][SQL] Refactor cursor lifecycle management with explicit…
srielau Jan 11, 2026
b501120
[SPARK-XXXXX][SQL] Code quality improvements for cursor implementation
srielau Jan 11, 2026
80965e4
[SPARK-XXXXX][SQL] Add invariant assertion to OpenCursor logical plan
srielau Jan 11, 2026
52d07bc
[SPARK-XXXXX][SQL] Fix cursor state lookup to respect scope qualifica…
srielau Jan 11, 2026
9e9f47f
[SPARK-55005][SQL] Merge PR#53759: Fix CONTINUE HANDLER to continue l…
srielau Jan 11, 2026
d8fe820
Merge branch 'master' into cursors
srielau Jan 12, 2026
d67f761
[SPARK-XXXXX][SQL] Change CursorReference.scopePath from Seq[String] …
srielau Jan 12, 2026
c951aa0
[SPARK-XXXXX][SQL] Add utility method to get scripting context for cu…
srielau Jan 12, 2026
061ffff
[SPARK-XXXXX][SQL] Move cursor definition lookup to analysis time
srielau Jan 12, 2026
512490c
[SPARK-XXXXX][SQL] Code review: Improve comments and documentation
srielau Jan 12, 2026
11a06de
[SPARK-XXXXX][SQL] Add comprehensive tests for NO DATA / NOT FOUND ha…
srielau Jan 12, 2026
f910215
[SPARK-XXXXX][SQL] Regenerate cursors.sql golden file to fix whitespace
srielau Jan 12, 2026
70a81cb
Fix goldenfiles
srielau Jan 12, 2026
e7b1185
[SPARK-XXXXX][SQL] Move CursorDefinition to CursorReference.scala and…
srielau Jan 13, 2026
da83661
[SPARK-XXXXX][SQL] Update comments to reflect delayed query parsing
srielau Jan 13, 2026
b3110c3
[SPARK-XXXXX][SQL] Refactor completion condition check to avoid empty…
srielau Jan 14, 2026
3b1dccf
[SPARK-XXXXX][SQL] Remove dead code: setReturnCurrentWithoutAdvancing
srielau Jan 14, 2026
7be6f7b
[SPARK-XXXXX][SQL] Move CursorState to separate file
srielau Jan 14, 2026
383071e
[SPARK-XXXXX][SQL] Remove redundant resolved check in ResolveCursors
srielau Jan 14, 2026
9749ca4
[SPARK-XXXXX][SQL] Validate cursor reference qualifiers
srielau Jan 14, 2026
cfe8126
[SPARK-XXXXX][SQL] Normalize scope label for case-insensitive cursor …
srielau Jan 14, 2026
fe7a21a
[SPARK-XXXXX][SQL] Validate cursor qualifier at parse time
srielau Jan 14, 2026
cf4a340
[SPARK-XXXXX][SQL] Replace reflection with extension API for cursor l…
srielau Jan 14, 2026
e647a68
[SPARK-XXXXX][SQL] Fix style issues in ResolveCursors
srielau Jan 14, 2026
dd2de65
[SPARK-XXXXX][SQL] Use toSQLId for cursor name in error message
srielau Jan 14, 2026
a648b1c
[SPARK-XXXXX][SQL] Format CloseCursor case class on single line
srielau Jan 14, 2026
54afdf4
[SPARK-XXXXX][SQL] Add ResolveCursors to RuleIdCollection
srielau Jan 14, 2026
c057c0d
[SPARK-XXXXX][SQL] Import CursorReference instead of using fully qual…
srielau Jan 14, 2026
47b6e55
[SPARK-XXXXX][SQL] Add tests for cursor access in CONTINUE handlers
srielau Jan 14, 2026
fbb587e
[SPARK-XXXXX][SQL] Fix cursor access across execution frames
srielau Jan 14, 2026
b0e31f9
[SPARK-XXXXX][SQL] Add tests for variable and cursor access in handlers
srielau Jan 14, 2026
dad9930
[SPARK-XXXXX][SQL] Fix FETCH variable resolution regression
srielau Jan 14, 2026
8f61f3c
[SPARK-XXXXX][SQL] Address cursor PR review comments from cloud-fan
srielau Jan 14, 2026
a5515ed
[SPARK-XXXXX][SQL] Fix cursor bugs: case-insensitive labels, statemen…
srielau Jan 14, 2026
0b85d6e
Revert "[SPARK-XXXXX][SQL] Remove dead code: setReturnCurrentWithoutA…
srielau Jan 14, 2026
3a272c9
[SPARK-XXXXX][SQL] Restore returnCurrentWithoutAdvancing logic for RE…
srielau Jan 14, 2026
bc4d002
[SPARK-XXXXX][SQL] Fix cursor reopen after CLOSE + review cursors.sql…
srielau Jan 14, 2026
273d106
[SPARK-XXXXX][SQL] Apply scalafmt formatting to QueryParsingErrors
srielau Jan 14, 2026
59d5bba
[SPARK-XXXXX][SQL] Fix cursor shadowing in handlers
srielau Jan 15, 2026
581a874
Minor cleanup
srielau Jan 15, 2026
edacf57
Update cursors.sql.out golden file with toSQLId() backticks
srielau Jan 15, 2026
0755379
Address davidm-db review comments: Fix FETCH grammar and simplify Res…
srielau Jan 15, 2026
fa0fb53
Address davidm-db review: Extract duplicate resolution logic in Resol…
srielau Jan 15, 2026
9c7da87
Address davidm-db review: Move cursor statements to statement rule fo…
srielau Jan 15, 2026
85cb7cb
[SPARK-XXXXX][SQL] Create Scala test suite for cursors (16/86 tests, …
srielau Jan 15, 2026
c9ebf5a
[SPARK-XXXXX][SQL] Fix Tests 27-31: Use DECLARE for session variables…
srielau Jan 15, 2026
0a82b25
[SPARK-XXXXX][SQL] Regenerate golden files after Tests 27-31 structur…
srielau Jan 15, 2026
ff87bcf
[SPARK-XXXXX][SQL] Complete comprehensive review of cursors.sql.out g…
srielau Jan 15, 2026
1f8b342
debugging, reorg test suite, unify FETCH INTO with SET
srielau Jan 15, 2026
4f09d53
Add new svcala suite
srielau Jan 16, 2026
b8acbc0
Merge branch 'master' into cursors
srielau Jan 16, 2026
b8e4f9c
remove sql script tests
srielau Jan 16, 2026
5245576
More review comments
srielau Jan 16, 2026
b441e47
Fix cursor resolution across frames
srielau Jan 19, 2026
2e5aa99
Refactor to share traversal logic for frame resolution
srielau Jan 20, 2026
f9d447a
Merge branch 'master' into cursors
srielau Jan 24, 2026
5a0a1ea
Merge branch 'master' into cursors
srielau Jan 27, 2026
ba277f5
Backout bad continue handler foix
srielau Jan 27, 2026
13aa89c
Danko's fixes to continue handler
srielau Jan 27, 2026
fea234d
Merge branch 'master' into cursors
srielau Jan 28, 2026
62dfa5d
Update sql/core/src/main/scala/org/apache/spark/sql/scripting/SqlScri…
srielau Jan 28, 2026
9749cfc
Update sql/core/src/main/scala/org/apache/spark/sql/scripting/SqlScri…
srielau Jan 28, 2026
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
55 changes: 54 additions & 1 deletion common/utils/src/main/resources/error/error-conditions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,48 @@
},
"sqlState" : "21S01"
},
"CURSOR_ALREADY_EXISTS" : {
"message" : [
"Cannot declare cursor <cursorName> because it already exists in the current scope."
],
"sqlState" : "42723"
},
"CURSOR_ALREADY_OPEN" : {
"message" : [
"Cannot open cursor <cursorName> because it is already open."
],
"sqlState" : "24502"
},
"CURSOR_NOT_FOUND" : {
"message" : [
"Cursor <cursorName> not found in the current scope."
],
"sqlState" : "42883"
},
"CURSOR_NOT_OPEN" : {
"message" : [
"Cannot fetch from or close cursor <cursorName> because it is not open."
],
"sqlState" : "24501"
},
"CURSOR_NO_MORE_ROWS" : {
"message" : [
"No more rows available to fetch from cursor <cursorName>."
],
"sqlState" : "02000"
},
"CURSOR_OUTSIDE_SCRIPT" : {
"message" : [
"Cursor operations can only be used within SQL scripts."
],
"sqlState" : "0A000"
},
"CURSOR_REFERENCE_INVALID_QUALIFIER" : {
"message" : [
"Cursor reference <cursorName> is invalid. Cursor references can only have at most one qualifier (e.g., label.cursor)."
],
"sqlState" : "42601"
},
"CYCLIC_FUNCTION_REFERENCE" : {
"message" : [
"Cyclic function reference detected: <path>."
Expand Down Expand Up @@ -2796,6 +2838,12 @@
},
"sqlState" : "HY109"
},
"INVALID_CURSOR_DECLARATION" : {
"message" : [
"Cursors must be declared after variable/condition declarations, and before handlers and other statements."
],
"sqlState" : "42601"
},
"INVALID_DATETIME_PATTERN" : {
"message" : [
"Unrecognized datetime pattern: <pattern>."
Expand Down Expand Up @@ -3155,7 +3203,7 @@
},
"WRONG_PLACE_OF_DECLARATION" : {
"message" : [
"Handlers must be declared after variable/condition declaration, and before other statements."
"Handlers must be declared after variable/condition/cursor declarations, and before other statements."
]
}
},
Expand Down Expand Up @@ -7046,6 +7094,11 @@
"<variableName> is a VARIABLE and cannot be updated using the SET statement. Use SET VARIABLE <variableName> = ... instead."
]
},
"SQL_CURSOR" : {
"message" : [
"SQL cursor operations (DECLARE CURSOR, OPEN, FETCH, CLOSE) are not supported."
]
},
"SQL_SCRIPTING" : {
"message" : [
"SQL Scripting is under development and not all features are supported. SQL Scripting enables users to write procedural SQL including control flow and error handling. To enable existing features set <sqlScriptingEnabled> to `true`."
Expand Down
19 changes: 13 additions & 6 deletions docs/sql-ref-ansi-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ license: |
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand Down Expand Up @@ -63,7 +63,7 @@ By default, `spark.sql.storeAssignmentPolicy` is `ANSI` and Spark SQL complies w
</tr>
</table>

The following subsections present behaviour changes in arithmetic operations, type conversions, and SQL parsing when the ANSI mode enabled. For type conversions in Spark SQL, there are three kinds of them and this article will introduce them one by one: cast, store assignment and type coercion.
The following subsections present behaviour changes in arithmetic operations, type conversions, and SQL parsing when the ANSI mode enabled. For type conversions in Spark SQL, there are three kinds of them and this article will introduce them one by one: cast, store assignment and type coercion.

### Arithmetic Operations

Expand Down Expand Up @@ -129,7 +129,7 @@ In the table above, all the `CAST`s with new syntax are marked as red <span styl
* CAST(Numeric AS Numeric): raise an overflow exception if the value is out of the target data type's range.
* CAST(String AS (Numeric/Date/Timestamp/Timestamp_NTZ/Interval/Boolean)): raise a runtime exception if the value can't be parsed as the target data type.
* CAST(Timestamp AS Numeric): raise an overflow exception if the number of seconds since epoch is out of the target data type's range.
* CAST(Numeric AS Timestamp): raise an overflow exception if numeric value times 1000000(microseconds per second) is out of the range of Long type.
* CAST(Numeric AS Timestamp): raise an overflow exception if numeric value times 1000000(microseconds per second) is out of the range of Long type.
* CAST(Array AS Array): raise an exception if there is any on the conversion of the elements.
* CAST(Map AS Map): raise an exception if there is any on the conversion of the keys and the values.
* CAST(Struct AS Struct): raise an exception if there is any on the conversion of the struct fields.
Expand Down Expand Up @@ -290,9 +290,9 @@ Note, arithmetic operations have special rules to calculate the least common typ
| e1 % e2 | min(p1 - s1, p2 - s2) + max(s1, s2) | max(s1, s2) |

The truncation rule is also different for arithmetic operations: they retain at least 6 digits in the fractional part, which means we can only reduce `scale` to 6. Overflow may happen in this case.

```sql
-- The coalesce function accepts any set of argument types as long as they share a least common type.
-- The coalesce function accepts any set of argument types as long as they share a least common type.
-- The result type is the least common type of the arguments.
> SET spark.sql.ansi.enabled=true;
> SELECT typeof(coalesce(1Y, 1L, NULL));
Expand Down Expand Up @@ -424,6 +424,7 @@ Below is a list of all the keywords in Spark SQL.
|ARRAY|non-reserved|non-reserved|reserved|
|AS|reserved|non-reserved|reserved|
|ASC|non-reserved|non-reserved|non-reserved|
|ASENSITIVE|non-reserved|non-reserved|non-reserved|
|AT|non-reserved|non-reserved|reserved|
|ATOMIC|non-reserved|non-reserved|non-reserved|
|AUTHORIZATION|reserved|non-reserved|reserved|
Expand Down Expand Up @@ -451,6 +452,7 @@ Below is a list of all the keywords in Spark SQL.
|CHARACTER|non-reserved|non-reserved|reserved|
|CHECK|reserved|non-reserved|reserved|
|CLEAR|non-reserved|non-reserved|non-reserved|
|CLOSE|non-reserved|non-reserved|non-reserved|
|CLUSTER|non-reserved|non-reserved|non-reserved|
|CLUSTERED|non-reserved|non-reserved|non-reserved|
|CODEGEN|non-reserved|non-reserved|non-reserved|
Expand Down Expand Up @@ -479,6 +481,7 @@ Below is a list of all the keywords in Spark SQL.
|CURRENT_TIME|reserved|non-reserved|reserved|
|CURRENT_TIMESTAMP|reserved|non-reserved|reserved|
|CURRENT_USER|reserved|non-reserved|reserved|
|CURSOR|non-reserved|non-reserved|non-reserved|
|DATA|non-reserved|non-reserved|non-reserved|
|DATE|non-reserved|non-reserved|reserved|
|DATABASE|non-reserved|non-reserved|non-reserved|
Expand Down Expand Up @@ -577,6 +580,7 @@ Below is a list of all the keywords in Spark SQL.
|INPUT|non-reserved|non-reserved|non-reserved|
|INPUTFORMAT|non-reserved|non-reserved|non-reserved|
|INSERT|non-reserved|non-reserved|reserved|
|INSENSITIVE|non-reserved|non-reserved|non-reserved|
|INT|non-reserved|non-reserved|reserved|
|INTEGER|non-reserved|non-reserved|reserved|
|INTERSECT|reserved|strict-non-reserved|reserved|
Expand Down Expand Up @@ -636,6 +640,7 @@ Below is a list of all the keywords in Spark SQL.
|NANOSECOND|non-reserved|non-reserved|non-reserved|
|NANOSECONDS|non-reserved|non-reserved|non-reserved|
|NATURAL|reserved|strict-non-reserved|reserved|
|NEXT|non-reserved|non-reserved|non-reserved|
|NO|non-reserved|non-reserved|reserved|
|NONE|non-reserved|non-reserved|reserved|
|NORELY|non-reserved|non-reserved|not a keyword|
Expand All @@ -647,6 +652,7 @@ Below is a list of all the keywords in Spark SQL.
|OFFSET|reserved|non-reserved|reserved|
|ON|reserved|strict-non-reserved|reserved|
|ONLY|reserved|non-reserved|reserved|
|OPEN|non-reserved|non-reserved|reserved|
|OPTION|non-reserved|non-reserved|non-reserved|
|OPTIONS|non-reserved|non-reserved|non-reserved|
|OR|reserved|non-reserved|reserved|
Expand Down Expand Up @@ -675,6 +681,7 @@ Below is a list of all the keywords in Spark SQL.
|QUARTER|non-reserved|non-reserved|non-reserved|
|QUERY|non-reserved|non-reserved|non-reserved|
|RANGE|non-reserved|non-reserved|reserved|
|READ|non-reserved|non-reserved|non-reserved|
|READS|non-reserved|non-reserved|non-reserved|
|REAL|non-reserved|non-reserved|reserved|
|RECORDREADER|non-reserved|non-reserved|non-reserved|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ ARCHIVE: 'ARCHIVE';
ARRAY: 'ARRAY' {incComplexTypeLevelCounter();};
AS: 'AS';
ASC: 'ASC';
ASENSITIVE: 'ASENSITIVE';
AT: 'AT';
ATOMIC: 'ATOMIC';
AUTHORIZATION: 'AUTHORIZATION';
Expand Down Expand Up @@ -170,6 +171,7 @@ CHAR: 'CHAR';
CHARACTER: 'CHARACTER';
CHECK: 'CHECK';
CLEAR: 'CLEAR';
CLOSE: 'CLOSE';
CLUSTER: 'CLUSTER';
CLUSTERED: 'CLUSTERED';
CODEGEN: 'CODEGEN';
Expand Down Expand Up @@ -198,6 +200,7 @@ CURRENT_DATE: 'CURRENT_DATE';
CURRENT_TIME: 'CURRENT_TIME';
CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP';
CURRENT_USER: 'CURRENT_USER';
CURSOR: 'CURSOR';
DAY: 'DAY';
DAYS: 'DAYS';
DAYOFYEAR: 'DAYOFYEAR';
Expand Down Expand Up @@ -296,6 +299,7 @@ INPATH: 'INPATH';
INPUT: 'INPUT';
INPUTFORMAT: 'INPUTFORMAT';
INSERT: 'INSERT';
INSENSITIVE: 'INSENSITIVE';
INTERSECT: 'INTERSECT';
INTERVAL: 'INTERVAL';
INT: 'INT';
Expand Down Expand Up @@ -354,6 +358,7 @@ NAMESPACES: 'NAMESPACES';
NANOSECOND: 'NANOSECOND';
NANOSECONDS: 'NANOSECONDS';
NATURAL: 'NATURAL';
NEXT: 'NEXT';
NO: 'NO';
NONE: 'NONE';
NOT: 'NOT';
Expand All @@ -365,6 +370,7 @@ OF: 'OF';
OFFSET: 'OFFSET';
ON: 'ON';
ONLY: 'ONLY';
OPEN: 'OPEN';
OPTION: 'OPTION';
OPTIONS: 'OPTIONS';
OR: 'OR';
Expand Down Expand Up @@ -393,6 +399,7 @@ PURGE: 'PURGE';
QUARTER: 'QUARTER';
QUERY: 'QUERY';
RANGE: 'RANGE';
READ: 'READ';
READS: 'READS';
REAL: 'REAL';
RECORDREADER: 'RECORDREADER';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ options { tokenVocab = SqlBaseLexer; }
return false;
}
int la = _input.LA(2); // Look ahead 2 tokens (current is PIPE, check what follows)
return la == SELECT || la == EXTEND || la == SET || la == DROP ||
return la == SELECT || la == EXTEND || la == SET || la == DROP ||
la == AS || la == WHERE || la == PIVOT || la == UNPIVOT ||
la == TABLESAMPLE || la == INNER || la == CROSS || la == LEFT ||
la == RIGHT || la == FULL || la == NATURAL || la == SEMI ||
la == ANTI || la == JOIN || la == UNION || la == EXCEPT ||
la == RIGHT || la == FULL || la == NATURAL || la == SEMI ||
la == ANTI || la == JOIN || la == UNION || la == EXCEPT ||
la == SETMINUS || la == INTERSECT || la == ORDER || la == CLUSTER ||
la == DISTRIBUTE || la == SORT || la == LIMIT || la == OFFSET ||
la == AGGREGATE || la == WINDOW || la == LATERAL;
Expand Down Expand Up @@ -350,6 +350,13 @@ statement
(COMMA identifierReferences+=identifierReference)*
dataType? variableDefaultExpression? #createVariable
| DROP TEMPORARY variable (IF EXISTS)? identifierReference #dropVariable
| DECLARE name=errorCapturingIdentifier (ASENSITIVE | INSENSITIVE)? CURSOR FOR query (FOR READ ONLY)?
#declareCursorStatement
| OPEN multipartIdentifier (USING (LEFT_PAREN params=namedExpressionSeq RIGHT_PAREN | params=namedExpressionSeq))?
#openCursorStatement
| FETCH ((NEXT? FROM) | FROM)? cursorName=multipartIdentifier INTO targets=multipartIdentifierList
#fetchCursorStatement
| CLOSE multipartIdentifier #closeCursorStatement
| EXPLAIN (LOGICAL | FORMATTED | EXTENDED | CODEGEN | COST)?
(statement|setResetStatement) #explain
| SHOW TABLES ((FROM | IN) identifierReference)?
Expand Down Expand Up @@ -1880,6 +1887,7 @@ ansiNonReserved
| ARCHIVE
| ARRAY
| ASC
| ASENSITIVE
| AT
| ATOMIC
| BEGIN
Expand All @@ -1902,6 +1910,7 @@ ansiNonReserved
| CHAR
| CHARACTER
| CLEAR
| CLOSE
| CLUSTER
| CLUSTERED
| CODEGEN
Expand All @@ -1918,6 +1927,7 @@ ansiNonReserved
| CONTAINS
| CONTINUE
| COST
| CURSOR
| CUBE
| CURRENT
| DATA
Expand Down Expand Up @@ -2000,6 +2010,7 @@ ansiNonReserved
| INPUT
| INPUTFORMAT
| INSERT
| INSENSITIVE
| INT
| INTEGER
| INTERVAL
Expand Down Expand Up @@ -2050,12 +2061,14 @@ ansiNonReserved
| NAMESPACES
| NANOSECOND
| NANOSECONDS
| NEXT
| NO
| NONE
| NORELY
| NULLS
| NUMERIC
| OF
| OPEN
| OPTION
| OPTIONS
| OUT
Expand All @@ -2079,6 +2092,7 @@ ansiNonReserved
| QUARTER
| QUERY
| RANGE
| READ
| READS
| REAL
| RECORDREADER
Expand Down Expand Up @@ -2239,6 +2253,7 @@ nonReserved
| ARRAY
| AS
| ASC
| ASENSITIVE
| AT
| ATOMIC
| AUTHORIZATION
Expand Down Expand Up @@ -2267,6 +2282,7 @@ nonReserved
| CHARACTER
| CHECK
| CLEAR
| CLOSE
| CLUSTER
| CLUSTERED
| CODEGEN
Expand All @@ -2290,6 +2306,7 @@ nonReserved
| CREATE
| CUBE
| CURRENT
| CURSOR
| CURRENT_DATE
| CURRENT_TIME
| CURRENT_TIMESTAMP
Expand Down Expand Up @@ -2389,6 +2406,7 @@ nonReserved
| INPUT
| INPUTFORMAT
| INSERT
| INSENSITIVE
| INT
| INTEGER
| INTERVAL
Expand Down Expand Up @@ -2443,6 +2461,7 @@ nonReserved
| NAMESPACES
| NANOSECOND
| NANOSECONDS
| NEXT
| NO
| NONE
| NORELY
Expand All @@ -2453,6 +2472,7 @@ nonReserved
| OF
| OFFSET
| ONLY
| OPEN
| OPTION
| OPTIONS
| OR
Expand Down Expand Up @@ -2481,6 +2501,7 @@ nonReserved
| QUARTER
| QUERY
| RANGE
| READ
| READS
| REAL
| RECORDREADER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ class SubstituteParmsAstBuilder extends SqlBaseParserBaseVisitor[AnyRef] {
override def visit(tree: ParseTree): AnyRef = {
if (tree == null) return null

// Skip cursor query definitions - parameter markers in cursor queries
// should not be substituted until OPEN time
tree match {
case ctx: DeclareCursorStatementContext =>
// Don't visit the query() child, only visit other parts if needed
return null
case _ => // Continue with normal processing
}

// Check if this is a parameter literal
tree match {
case ctx: NamedParameterLiteralContext =>
Expand Down
Loading