Skip to content

Commit 0f391f7

Browse files
test(volume): verify option and error boundaries
Signed-off-by: Aakash Saravanan <aakash.saravanan@databricks.com>
1 parent 317bb1a commit 0f391f7

3 files changed

Lines changed: 36 additions & 15 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Release History
22

33
# Unreleased
4-
- Support SQL PUT, GET, and REMOVE file transfers with a native kernel exposing `Statement.set_staging_allowed_local_paths`; older wheels reject transfers and continue to support ordinary queries. Streaming PUT remains unsupported in kernel mode.
4+
- Support SQL PUT, GET, and REMOVE file transfers with a native kernel built from `KERNEL_REV`. Published kernel 1.4.0 does not support these transfers; the release version and dependency floor are pending [kernel #443](https://github.com/databricks/databricks-sql-kernel/issues/443). Older wheels reject transfers and continue to support ordinary queries. Streaming PUT remains unsupported in kernel mode.
55
- Fix inline parameters (`use_inline_params=True`) so a sequence directly after `IN` renders as a list again: `x IN %(ids)s` renders as `x IN (1,2,3)` instead of `x IN ARRAY(1,2,3)`, which Spark rejects. Inside the list, tuples render as rows for a multi-column `IN`, and a `set` or `frozenset` is accepted again. Sequences anywhere else still render as `ARRAY(...)` ([#629](https://github.com/databricks/databricks-sql-python/issues/629))
66

77
# 4.9.0 (2026-11-04)

‎CONNECTION_PARAMETERS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ to change without notice.
171171
| `use_inline_params` | `bool` \| `"silent"` | ✅ | ✅ | `False` | Render parameters inline (legacy) vs. native bound params (DBR 14.1+). Shared cursor code performs the rendering before dispatch, so both backends honor it identically. |
172172
| `ignore_transactions` | `bool` | ✅ | ✅ | `True` | When `True`: `commit()` is a no-op, `rollback()` raises `NotSupportedError`, and setting `autocommit` is a no-op. |
173173
| `fetch_autocommit_from_server`| `bool` | ✅ | ✅ | `False` | Query the server (`SET AUTOCOMMIT`) for autocommit state instead of returning the cached value. |
174-
| `staging_allowed_local_path` | `str` \| `List[str]` | ✅ | ✅ | `None` | Local path(s) permitted for Unity Catalog Volume `PUT`/`GET`. Kernel transfers require a wheel exposing `Statement.set_staging_allowed_local_paths` (kernel [#439](https://github.com/databricks/databricks-sql-kernel/pull/439)); older wheels reject transfer commands. Kernel mode does not support streaming PUT. |
174+
| `staging_allowed_local_path` | `str` \| `List[str]` | ✅ | Source build | `None` | Local path(s) permitted for Unity Catalog Volume `PUT`/`GET`. Requires a native kernel built from `KERNEL_REV` with [kernel #439](https://github.com/databricks/databricks-sql-kernel/pull/439). Published 1.4.0 lacks this capability; the release version and dependency floor are pending [kernel #443](https://github.com/databricks/databricks-sql-kernel/issues/443). Older wheels reject transfers. Kernel mode does not support streaming PUT. |
175175

176176
## Telemetry
177177

‎tests/unit/test_kernel_client.py‎

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,19 @@ def test_execute_command_forwards_row_limit(row_limit):
816816
],
817817
)
818818
@pytest.mark.parametrize("async_op", [False, True])
819-
def test_execute_command_forwards_allowed_local_paths(allowed, expected, async_op):
819+
@pytest.mark.parametrize(
820+
"operation, uses_paths",
821+
[
822+
("PUT '/tmp/files/input' INTO '/Volumes/c/s/v/input'", True),
823+
("GET '/Volumes/c/s/v/input' TO '/tmp/files/output'", True),
824+
("/* download */ GET '/Volumes/c/s/v/input' TO '/tmp/files/output'", True),
825+
("REMOVE '/Volumes/c/s/v/input'", False),
826+
("SELECT 1", False),
827+
],
828+
)
829+
def test_execute_command_forwards_allowed_local_paths(
830+
allowed, expected, async_op, operation, uses_paths
831+
):
820832
c = _make_client()
821833
c._kernel_session = MagicMock()
822834
cursor = MagicMock()
@@ -831,7 +843,7 @@ def test_execute_command_forwards_allowed_local_paths(allowed, expected, async_o
831843
stmt.execute.return_value = executed
832844
stmt.submit.return_value = executed
833845
c.execute_command(
834-
operation="PUT '/tmp/files/input' INTO '/Volumes/c/s/v/input'",
846+
operation=operation,
835847
session_id=MagicMock(),
836848
max_rows=1,
837849
max_bytes=1,
@@ -842,7 +854,9 @@ def test_execute_command_forwards_allowed_local_paths(allowed, expected, async_o
842854
async_op=async_op,
843855
enforce_embedded_schema_correctness=False,
844856
)
845-
stmt.set_staging_allowed_local_paths.assert_called_once_with(expected)
857+
stmt.set_staging_allowed_local_paths.assert_called_once_with(
858+
expected if uses_paths else []
859+
)
846860
(stmt.submit if async_op else stmt.execute).assert_called_once()
847861

848862

@@ -2657,20 +2671,27 @@ def test_only_reserved_headers_omits_kwarg(self, monkeypatch):
26572671

26582672

26592673
@pytest.mark.parametrize(
2660-
"code, http_status, expected",
2674+
"code, http_status, error_code, expected",
26612675
[
2662-
("PermissionDenied", None, ProgrammingError),
2663-
("InvalidArgument", None, ProgrammingError),
2664-
("PermissionDenied", 403, OperationalError),
2665-
("NotFound", None, FileNotFoundError),
2666-
("NotFound", 404, OperationalError),
2667-
("NetworkError", None, OperationalError),
2676+
("PermissionDenied", None, "FILE_TRANSFER_ERROR", ProgrammingError),
2677+
("InvalidArgument", None, "FILE_TRANSFER_ERROR", ProgrammingError),
2678+
("InvalidArgument", 400, "FILE_TRANSFER_ERROR", OperationalError),
2679+
("PermissionDenied", 403, "FILE_TRANSFER_ERROR", OperationalError),
2680+
("NotFound", None, "FILE_TRANSFER_ERROR", FileNotFoundError),
2681+
("NotFound", 404, "FILE_TRANSFER_ERROR", OperationalError),
2682+
("NetworkError", None, "FILE_TRANSFER_ERROR", OperationalError),
2683+
("Internal", None, "FILE_TRANSFER_ERROR", OperationalError),
2684+
("Internal", None, None, DatabaseError),
26682685
],
26692686
)
2670-
def test_file_transfer_error_matches_thrift_exception(code, http_status, expected):
2687+
def test_file_transfer_error_matches_thrift_exception(
2688+
code, http_status, error_code, expected
2689+
):
26712690
error = _FakeKernelError(code=code, message="File transfer failed")
2672-
error.error_code = "FILE_TRANSFER_ERROR"
2691+
error.error_code = error_code
26732692
error.http_status = http_status
26742693
result = kernel_client._reraise_kernel_error(error)
26752694
assert type(result) is expected
2676-
assert result.error_code == "FILE_TRANSFER_ERROR"
2695+
assert result.error_code == error_code
2696+
assert result.code == code
2697+
assert result.http_status == http_status

0 commit comments

Comments
 (0)