Skip to content

Commit

Permalink
[SPARK-48353][SQL] Introduction of Exception Handling mechanism in SQ…
Browse files Browse the repository at this point in the history
…L Scripting

### What changes were proposed in this pull request?
This pull request introduces the logic of error handling inside SQL Scripting language. Now, it is possible to:
- declare conditions for specific SQL States (currently only valid in scope where they are defined)
- declare handlers to catch and process errors that are risen during statement execution

Rules for selecting the most appropriate handler:
- Named condition handlers are most specific.
- SQLSTATE handlers are next in specificity.
- Generic NOT FOUND and SQLEXCEPTION handlers are least specific.

Note: Handlers defined in the innermost compound statement where the exception was raised are considered.

### Why are the changes needed?
The intent is to add the possibility for user to handle SQL errors in a custom defined way.

### Limitations

- Currently, only `EXIT` handler is supported. Support for `CONTINUE` handlers will come in the future.
- It is only possible to declare condition in its full form by specifying SQLSTATE. Short form with default SQLSTATE is not yet supported.

### Does this PR introduce any user-facing change?
No.

### How was this patch tested?
There are already existing test suites for SQL scripting that have been improved to test new functionalities:

- `SqlScriptingParserSuite`
- `SqlScriptingExecutionSuite`
- `SqlScriptingE2eSuite`

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

Closes apache#49427 from miland-db/milan-dankovic_data/refactor-execution-4-condition-handlers.

Authored-by: Milan Dankovic <milan.dankovic@databricks.com>
Signed-off-by: Wenchen Fan <wenchen@databricks.com>
  • Loading branch information
miland-db authored and cloud-fan committed Feb 4, 2025
1 parent 9d6fb58 commit 0f163a5
Show file tree
Hide file tree
Showing 22 changed files with 2,069 additions and 99 deletions.
91 changes: 91 additions & 0 deletions common/utils/src/main/resources/error/error-conditions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,30 @@
],
"sqlState" : "42614"
},
"DUPLICATE_CONDITION_IN_SCOPE" : {
"message" : [
"Found duplicate condition <condition> in the scope. Please, remove one of them."
],
"sqlState" : "42734"
},
"DUPLICATE_EXCEPTION_HANDLER" : {
"message" : [
"Found duplicate handlers. Please, remove one of them."
],
"subClass" : {
"CONDITION" : {
"message" : [
"Found duplicate handlers for the same condition <condition>."
]
},
"SQLSTATE" : {
"message" : [
"Found duplicate handlers for the same SQLSTATE <sqlState>."
]
}
},
"sqlState" : "42734"
},
"DUPLICATE_KEY" : {
"message" : [
"Found duplicate keys <keyColumn>."
Expand Down Expand Up @@ -2440,6 +2464,29 @@
],
"sqlState" : "42K05"
},
"INVALID_ERROR_CONDITION_DECLARATION" : {
"message" : [
"Invalid condition declaration."
],
"subClass" : {
"ONLY_AT_BEGINNING" : {
"message" : [
"Condition <conditionName> can only be declared at the beginning of the compound."
]
},
"QUALIFIED_CONDITION_NAME" : {
"message" : [
"Condition <conditionName> cannot be qualified."
]
},
"SPECIAL_CHARACTER_FOUND" : {
"message" : [
"Special character found in condition name <conditionName>. Only alphanumeric characters and underscores are allowed."
]
}
},
"sqlState" : "42K0R"
},
"INVALID_ESC" : {
"message" : [
"Found an invalid escape string: <invalidEscape>. The escape string must contain only one character."
Expand Down Expand Up @@ -2608,6 +2655,39 @@
},
"sqlState" : "HY000"
},
"INVALID_HANDLER_DECLARATION" : {
"message" : [
"Invalid handler declaration."
],
"subClass" : {
"CONDITION_NOT_FOUND" : {
"message" : [
"Condition <condition> not found."
]
},
"DUPLICATE_CONDITION_IN_HANDLER_DECLARATION" : {
"message" : [
"Found duplicate condition <condition> in the handler declaration. Please, remove one of them."
]
},
"DUPLICATE_SQLSTATE_IN_HANDLER_DECLARATION" : {
"message" : [
"Found duplicate sqlState <sqlState> in the handler declaration. Please, remove one of them."
]
},
"INVALID_CONDITION_COMBINATION" : {
"message" : [
"Invalid combination of conditions in the handler declaration. SQLEXCEPTION and NOT FOUND cannot be used together with other condition/sqlstate values."
]
},
"WRONG_PLACE_OF_DECLARATION" : {
"message" : [
"Handlers must be declared after variable/condition declaration, and before other statements."
]
}
},
"sqlState" : "42K0Q"
},
"INVALID_IDENTIFIER" : {
"message" : [
"The unquoted identifier <ident> is invalid and must be back quoted as: `<ident>`.",
Expand Down Expand Up @@ -3264,6 +3344,12 @@
},
"sqlState" : "42616"
},
"INVALID_SQLSTATE" : {
"message" : [
"Invalid SQLSTATE value: '<sqlState>'. SQLSTATE must be exactly 5 characters long and contain only A-Z and 0-9. SQLSTATE must not start with '00', '01', or 'XX'."
],
"sqlState" : "428B3"
},
"INVALID_SQL_ARG" : {
"message" : [
"The argument <name> of `sql()` is invalid. Consider to replace it either by a SQL literal or by collection constructor functions such as `map()`, `array()`, `struct()`."
Expand Down Expand Up @@ -5492,6 +5578,11 @@
"Attach a comment to the namespace <namespace>."
]
},
"CONTINUE_EXCEPTION_HANDLER" : {
"message" : [
"CONTINUE exception handler is not supported. Use EXIT handler."
]
},
"DESC_TABLE_COLUMN_JSON" : {
"message" : [
"DESC TABLE COLUMN AS JSON not supported for individual columns."
Expand Down
12 changes: 12 additions & 0 deletions common/utils/src/main/resources/error/error-states.json
Original file line number Diff line number Diff line change
Expand Up @@ -4643,6 +4643,18 @@
"standard": "N",
"usedBy": ["Spark"]
},
"42K0Q": {
"description": "Invalid handler declaration.",
"origin": "Spark",
"standard": "N",
"usedBy": ["Spark"]
},
"42K0R": {
"description": "Invalid condition declaration.",
"origin": "Spark",
"standard": "N",
"usedBy": ["Spark"]
},
"42KD0": {
"description": "Ambiguous name reference.",
"origin": "Databricks",
Expand Down
8 changes: 8 additions & 0 deletions docs/sql-ref-ansi-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,10 @@ Below is a list of all the keywords in Spark SQL.
|COMPENSATION|non-reserved|non-reserved|non-reserved|
|COMPUTE|non-reserved|non-reserved|non-reserved|
|CONCATENATE|non-reserved|non-reserved|non-reserved|
|CONDITION|non-reserved|non-reserved|non-reserved|
|CONSTRAINT|reserved|non-reserved|reserved|
|CONTAINS|non-reserved|non-reserved|non-reserved|
|CONTINUE|non-reserved|non-reserved|non-reserved|
|COST|non-reserved|non-reserved|non-reserved|
|CREATE|reserved|non-reserved|reserved|
|CROSS|reserved|strict-non-reserved|reserved|
Expand Down Expand Up @@ -513,6 +515,7 @@ Below is a list of all the keywords in Spark SQL.
|EXCLUDE|non-reserved|non-reserved|non-reserved|
|EXECUTE|reserved|non-reserved|reserved|
|EXISTS|non-reserved|non-reserved|reserved|
|EXIT|non-reserved|non-reserved|non-reserved|
|EXPLAIN|non-reserved|non-reserved|non-reserved|
|EXPORT|non-reserved|non-reserved|non-reserved|
|EXTEND|non-reserved|non-reserved|non-reserved|
Expand All @@ -531,6 +534,7 @@ Below is a list of all the keywords in Spark SQL.
|FOREIGN|reserved|non-reserved|reserved|
|FORMAT|non-reserved|non-reserved|non-reserved|
|FORMATTED|non-reserved|non-reserved|non-reserved|
|FOUND|non-reserved|non-reserved|non-reserved|
|FROM|reserved|non-reserved|reserved|
|FULL|reserved|strict-non-reserved|reserved|
|FUNCTION|non-reserved|non-reserved|reserved|
Expand All @@ -540,6 +544,7 @@ Below is a list of all the keywords in Spark SQL.
|GRANT|reserved|non-reserved|reserved|
|GROUP|reserved|non-reserved|reserved|
|GROUPING|non-reserved|non-reserved|reserved|
|HANDLER|non-reserved|non-reserved|non-reserved|
|HAVING|reserved|non-reserved|reserved|
|HOUR|non-reserved|non-reserved|non-reserved|
|HOURS|non-reserved|non-reserved|non-reserved|
Expand Down Expand Up @@ -701,6 +706,8 @@ Below is a list of all the keywords in Spark SQL.
|SOURCE|non-reserved|non-reserved|non-reserved|
|SPECIFIC|non-reserved|non-reserved|reserved|
|SQL|reserved|non-reserved|reserved|
|SQLEXCEPTION|non-reserved|non-reserved|non-reserved|
|SQLSTATE|non-reserved|non-reserved|non-reserved|
|START|non-reserved|non-reserved|reserved|
|STATISTICS|non-reserved|non-reserved|non-reserved|
|STORED|non-reserved|non-reserved|non-reserved|
Expand Down Expand Up @@ -754,6 +761,7 @@ Below is a list of all the keywords in Spark SQL.
|USE|non-reserved|non-reserved|non-reserved|
|USER|reserved|non-reserved|reserved|
|USING|reserved|strict-non-reserved|reserved|
|VALUE|non-reserved|non-reserved|non-reserved|
|VALUES|non-reserved|non-reserved|reserved|
|VARCHAR|non-reserved|non-reserved|reserved|
|VAR|non-reserved|non-reserved|non-reserved|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,10 @@ COMPACTIONS: 'COMPACTIONS';
COMPENSATION: 'COMPENSATION';
COMPUTE: 'COMPUTE';
CONCATENATE: 'CONCATENATE';
CONDITION: 'CONDITION';
CONSTRAINT: 'CONSTRAINT';
CONTAINS: 'CONTAINS';
CONTINUE: 'CONTINUE';
COST: 'COST';
CREATE: 'CREATE';
CROSS: 'CROSS';
Expand Down Expand Up @@ -227,6 +229,7 @@ EXCEPT: 'EXCEPT';
EXCHANGE: 'EXCHANGE';
EXCLUDE: 'EXCLUDE';
EXISTS: 'EXISTS';
EXIT: 'EXIT';
EXPLAIN: 'EXPLAIN';
EXPORT: 'EXPORT';
EXTEND: 'EXTEND';
Expand All @@ -245,6 +248,7 @@ FOR: 'FOR';
FOREIGN: 'FOREIGN';
FORMAT: 'FORMAT';
FORMATTED: 'FORMATTED';
FOUND: 'FOUND';
FROM: 'FROM';
FULL: 'FULL';
FUNCTION: 'FUNCTION';
Expand All @@ -254,6 +258,7 @@ GLOBAL: 'GLOBAL';
GRANT: 'GRANT';
GROUP: 'GROUP';
GROUPING: 'GROUPING';
HANDLER: 'HANDLER';
HAVING: 'HAVING';
BINARY_HEX: 'X';
HOUR: 'HOUR';
Expand Down Expand Up @@ -415,6 +420,8 @@ SORTED: 'SORTED';
SOURCE: 'SOURCE';
SPECIFIC: 'SPECIFIC';
SQL: 'SQL';
SQLEXCEPTION: 'SQLEXCEPTION';
SQLSTATE: 'SQLSTATE';
START: 'START';
STATISTICS: 'STATISTICS';
STORED: 'STORED';
Expand Down Expand Up @@ -468,6 +475,7 @@ UPDATE: 'UPDATE';
USE: 'USE';
USER: 'USER';
USING: 'USING';
VALUE: 'VALUE';
VALUES: 'VALUES';
VARCHAR: 'VARCHAR';
VAR: 'VAR';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ compoundStatement
: statement
| setStatementWithOptionalVarKeyword
| beginEndCompoundBlock
| declareConditionStatement
| declareHandlerStatement
| ifElseStatement
| caseStatement
| whileStatement
Expand All @@ -79,6 +81,29 @@ setStatementWithOptionalVarKeyword
LEFT_PAREN query RIGHT_PAREN #setVariableWithOptionalKeyword
;

sqlStateValue
: stringLit
;

declareConditionStatement
: DECLARE multipartIdentifier CONDITION (FOR SQLSTATE VALUE? sqlStateValue)?
;

conditionValue
: SQLSTATE VALUE? sqlStateValue
| SQLEXCEPTION
| NOT FOUND
| multipartIdentifier
;

conditionValues
: cvList+=conditionValue (COMMA cvList+=conditionValue)*
;

declareHandlerStatement
: DECLARE (CONTINUE | EXIT) HANDLER FOR conditionValues (beginEndCompoundBlock | statement | setStatementWithOptionalVarKeyword)
;

whileStatement
: beginLabel? WHILE booleanExpression DO compoundBody END WHILE endLabel?
;
Expand Down Expand Up @@ -1607,7 +1632,9 @@ ansiNonReserved
| COMPENSATION
| COMPUTE
| CONCATENATE
| CONDITION
| CONTAINS
| CONTINUE
| COST
| CUBE
| CURRENT
Expand Down Expand Up @@ -1648,6 +1675,7 @@ ansiNonReserved
| EXCHANGE
| EXCLUDE
| EXISTS
| EXIT
| EXPLAIN
| EXPORT
| EXTEND
Expand All @@ -1661,11 +1689,13 @@ ansiNonReserved
| FOLLOWING
| FORMAT
| FORMATTED
| FOUND
| FUNCTION
| FUNCTIONS
| GENERATED
| GLOBAL
| GROUPING
| HANDLER
| HOUR
| HOURS
| IDENTIFIER_KW
Expand Down Expand Up @@ -1798,6 +1828,8 @@ ansiNonReserved
| SORTED
| SOURCE
| SPECIFIC
| SQLEXCEPTION
| SQLSTATE
| START
| STATISTICS
| STORED
Expand Down Expand Up @@ -1840,6 +1872,7 @@ ansiNonReserved
| UNTIL
| UPDATE
| USE
| VALUE
| VALUES
| VARCHAR
| VAR
Expand Down Expand Up @@ -1945,8 +1978,10 @@ nonReserved
| COMPENSATION
| COMPUTE
| CONCATENATE
| CONDITION
| CONSTRAINT
| CONTAINS
| CONTINUE
| COST
| CREATE
| CUBE
Expand Down Expand Up @@ -1997,6 +2032,7 @@ nonReserved
| EXCLUDE
| EXECUTE
| EXISTS
| EXIT
| EXPLAIN
| EXPORT
| EXTEND
Expand All @@ -2016,13 +2052,15 @@ nonReserved
| FORMAT
| FORMATTED
| FROM
| FOUND
| FUNCTION
| FUNCTIONS
| GENERATED
| GLOBAL
| GRANT
| GROUP
| GROUPING
| HANDLER
| HAVING
| HOUR
| HOURS
Expand Down Expand Up @@ -2174,6 +2212,8 @@ nonReserved
| SOURCE
| SPECIFIC
| SQL
| SQLEXCEPTION
| SQLSTATE
| START
| STATISTICS
| STORED
Expand Down Expand Up @@ -2224,6 +2264,7 @@ nonReserved
| UPDATE
| USE
| USER
| VALUE
| VALUES
| VARCHAR
| VAR
Expand Down
Loading

0 comments on commit 0f163a5

Please sign in to comment.