Skip to content

Commit 6015540

Browse files
fix(kernel): preserve mapped transfer errors through nested calls
Signed-off-by: Aakash Saravanan <aakash.saravanan@databricks.com>
1 parent 1598dc0 commit 6015540

2 files changed

Lines changed: 66 additions & 2 deletions

File tree

‎src/databricks/sql/backend/kernel/_errors.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ def wrap_kernel_exception(what: str, exc: BaseException) -> "Union[Error, OSErro
201201
- ``KernelError`` → mapped class with structured attrs forwarded.
202202
A local missing transfer file maps to ``FileNotFoundError`` without
203203
the OS ``errno`` or ``filename`` attributes.
204-
- Already-PEP-249 ``Error`` (e.g. raised by an inner caller that
205-
already mapped) → passed through unchanged.
204+
- Already-mapped PEP 249 or local transfer errors → passed through
205+
unchanged so nested callers preserve their class and attributes.
206206
- Anything else (``TypeError`` / ``ValueError`` / etc. from PyO3
207207
argument conversion, extension-internal errors) → wrapped in
208208
``OperationalError``.
@@ -215,6 +215,10 @@ def wrap_kernel_exception(what: str, exc: BaseException) -> "Union[Error, OSErro
215215
return reraise_kernel_error(exc)
216216
if isinstance(exc, Error):
217217
return exc
218+
if isinstance(exc, (FileNotFoundError, PermissionError)) and getattr(
219+
exc, "error_code", None
220+
) in ("FILE_TRANSFER_ERROR", "FILE_TRANSFER_PERMISSION_ERROR"):
221+
return exc
218222
return OperationalError(
219223
f"Unexpected error from databricks_sql_kernel during {what}: {exc!r}"
220224
)

‎tests/unit/test_kernel_client.py‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,34 @@ def execute():
900900
stmt.execute.assert_called_once()
901901

902902

903+
@pytest.mark.parametrize(
904+
"operation, expected",
905+
[
906+
("PUT '/f' INTO '/v'", "PUT"),
907+
("get '/v' to '/f'", "GET"),
908+
("REMOVE '/v'", "REMOVE"),
909+
("-- upload the file\nPUT '/f' INTO '/v'", "PUT"),
910+
("/* staging */ PUT '/f' INTO '/v'", "PUT"),
911+
("/* c1 */\n -- c2\n get '/v' to '/f'", "GET"),
912+
(" \n\t PUT '/f' INTO '/v'", "PUT"),
913+
("SELECT 'GET' AS x", None),
914+
("SELECT * FROM puts", None),
915+
("-- PUT in a comment\nSELECT 1", None),
916+
("/* PUT */ SELECT 1", None),
917+
("INSERT INTO t VALUES (1)", None),
918+
("", None),
919+
("-- just a comment", None),
920+
("/* just a comment */", None),
921+
("PUTTER", None),
922+
("GETTING", None),
923+
("REMOVED", None),
924+
],
925+
)
926+
def test_staging_verb_matches_only_the_leading_statement(operation, expected):
927+
stripped = kernel_client._strip_leading_sql_comments(operation)
928+
assert kernel_client._staging_verb(stripped) == expected
929+
930+
903931
@pytest.mark.parametrize(
904932
"query",
905933
[
@@ -2701,3 +2729,35 @@ def test_file_transfer_error_matches_thrift_exception(
27012729
assert result.error_code == error_code
27022730
assert result.code == code
27032731
assert result.http_status == http_status
2732+
2733+
2734+
@pytest.mark.parametrize(
2735+
"code,error_code",
2736+
[
2737+
("NotFound", "FILE_TRANSFER_ERROR"),
2738+
("PermissionDenied", "FILE_TRANSFER_PERMISSION_ERROR"),
2739+
],
2740+
)
2741+
def test_nested_wrapping_preserves_mapped_local_transfer_error(code, error_code):
2742+
from databricks.sql.backend.kernel._errors import wrap_kernel_exception
2743+
2744+
error = _FakeKernelError(code=code, sql_state="HY000", query_id="transfer-id")
2745+
error.error_code = error_code
2746+
mapped = wrap_kernel_exception("inner", error)
2747+
wrapped = wrap_kernel_exception("outer", mapped)
2748+
assert wrapped is mapped
2749+
assert wrapped.code == code
2750+
assert wrapped.error_code == error_code
2751+
assert wrapped.sql_state == "HY000"
2752+
assert wrapped.query_id == "transfer-id"
2753+
2754+
2755+
@pytest.mark.parametrize(
2756+
"error", [FileNotFoundError("missing"), PermissionError("denied")]
2757+
)
2758+
def test_raw_local_errors_keep_unexpected_error_wrapping(error):
2759+
from databricks.sql.backend.kernel._errors import wrap_kernel_exception
2760+
2761+
wrapped = wrap_kernel_exception("execute_command", error)
2762+
assert type(wrapped) is OperationalError
2763+
assert "execute_command" in str(wrapped)

0 commit comments

Comments
 (0)