diff --git a/docs/syntax-coverage.md b/docs/syntax-coverage.md index cb22d3beb..5986eac54 100644 --- a/docs/syntax-coverage.md +++ b/docs/syntax-coverage.md @@ -170,13 +170,15 @@ that edits or removes a committed one. `unsupported_feature`. - `phase_basis`: why the importer chose the phase. The importer applies these rules in order: 1. An uncaught exception raised while a block ran is a `runtime_error`, unless its errno is - 1003, which is refused. + 1002 or 1003, which is refused. 2. Errno 2 `Unsupported feature` is an `unsupported_feature`. 3. An errno of 100000 or more is a `runtime_error`, unless its SQLSTATE is in class 42. Then it is refused unless the errno is a reviewed one, which is a `semantic_error`. 4. Errno 1003 under the `SQL compilation error` prefix is a `syntax_error`. Any other errno - under that prefix is a `semantic_error`, a compilation error after parsing. - 5. Any other error is refused unless its errno is a reviewed one, which is a `semantic_error`. + under that prefix except 1002 is a `semantic_error`, a compilation error after parsing. + 5. Errno 1002 is a `syntax_error` when its message itself starts with `syntax error`, in any + case and whatever its SQLSTATE; any other errno 1002 is refused. + 6. Any other error is refused unless its errno is a reviewed one, which is a `semantic_error`. A reviewed errno is one whose message shows that Snowflake parsed the statement and then refused it. Examples are a duplicate secret name (509008), a missing object to undrop (90025), diff --git a/scripts/syntax_receipts/_helpers/receipts.py b/scripts/syntax_receipts/_helpers/receipts.py index a7fb264eb..36fa13176 100644 --- a/scripts/syntax_receipts/_helpers/receipts.py +++ b/scripts/syntax_receipts/_helpers/receipts.py @@ -11,11 +11,13 @@ MISSING_OBJECT, MISSING_OBJECT_MESSAGE, OTHER_REASON, + PARSER_SYNTAX_ERRNO, REASON_BY_ERRNO, RECEIPT_FORMAT, REVIEWED_SEMANTIC_ERRNOS, RUNTIME_ERRNO_FLOOR, SYNTAX_ERRNO, + SYNTAX_MESSAGE_PREFIX, SYNTAX_OR_ACCESS_SQLSTATE_CLASS, UNCATEGORIZED_PHASES, UNCAUGHT_PREFIX, @@ -32,7 +34,7 @@ def classify( ) -> tuple[str, str] | None: """The phase of a Snowflake error, or None when the recorded fields do not establish one.""" if message.startswith(UNCAUGHT_PREFIX): - if errno == SYNTAX_ERRNO: + if errno in (SYNTAX_ERRNO, PARSER_SYNTAX_ERRNO): return None return "runtime_error", "uncaught exception raised while the block ran" if errno == UNSUPPORTED_ERRNO and message.startswith(UNSUPPORTED_PREFIX): @@ -46,6 +48,10 @@ def classify( compiled: bool = message.lower().startswith(COMPILATION_PREFIX.lower()) if errno == SYNTAX_ERRNO and compiled: return "syntax_error", "errno 1003 SQL compilation syntax error" + if errno == PARSER_SYNTAX_ERRNO: + if message.lower().startswith(SYNTAX_MESSAGE_PREFIX): + return "syntax_error", "errno 1002 parser syntax error" + return None if errno is not None and compiled: return "semantic_error", f"errno {errno} compilation error after parsing" if errno in REVIEWED_SEMANTIC_ERRNOS: diff --git a/scripts/syntax_receipts/constants.py b/scripts/syntax_receipts/constants.py index 532584a10..d11e01afa 100644 --- a/scripts/syntax_receipts/constants.py +++ b/scripts/syntax_receipts/constants.py @@ -7,6 +7,8 @@ {"accepted", "syntax_error", "semantic_error", "runtime_error", "unsupported_feature"} ) SYNTAX_ERRNO: int = 1003 +PARSER_SYNTAX_ERRNO: int = 1002 +SYNTAX_MESSAGE_PREFIX: str = "syntax error" UNSUPPORTED_ERRNO: int = 2 COMPILATION_PREFIX: str = "SQL compilation error" UNCAUGHT_PREFIX: str = "Uncaught exception of type" diff --git a/tests/unit/scripts/syntax_receipts/test_import_runs.py b/tests/unit/scripts/syntax_receipts/test_import_runs.py index 699475472..57580308d 100644 --- a/tests/unit/scripts/syntax_receipts/test_import_runs.py +++ b/tests/unit/scripts/syntax_receipts/test_import_runs.py @@ -194,6 +194,37 @@ message="External table identifier cannot be empty", expected_classification=None, ), + ClassifyTestCase( + description="errno 1002 whose message says syntax error is a syntax error", + errno=1002, + sqlstate="42601", + message=( + "syntax error line At least one table name is required for ADD TABLES operations" + " at position {1} unexpected '{2}'." + ), + expected_classification=("syntax_error", "errno 1002 parser syntax error"), + ), + ClassifyTestCase( + description="errno 1002 under the compilation prefix is not established", + errno=1002, + sqlstate="42601", + message="SQL compilation error:\nsyntax error line 1 at position 4", + expected_classification=None, + ), + ClassifyTestCase( + description="errno 1002 raised while a block runs is not established", + errno=1002, + sqlstate="42601", + message="Uncaught exception of type 'STATEMENT_ERROR' : syntax error line 1", + expected_classification=None, + ), + ClassifyTestCase( + description="errno 1002 with any other message is not established", + errno=1002, + sqlstate="42601", + message="Invalid argument for the operation.", + expected_classification=None, + ), ClassifyTestCase( description="a syntax error without the compilation prefix is not established", errno=1003,