Skip to content

Commit 37bc5f0

Browse files
committed
fix(kernel): honor use_cloud_fetch
Signed-off-by: Vu Anh Phung <vu.phung@databricks.com>
1 parent 8f4daee commit 37bc5f0

4 files changed

Lines changed: 86 additions & 6 deletions

File tree

src/databricks/sql/backend/kernel/client.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ def __init__(
230230
self._use_arrow_native_complex_types = kwargs.get(
231231
"_use_arrow_native_complex_types", True
232232
)
233+
self._use_cloud_fetch = kwargs.get("use_cloud_fetch", True)
233234
# NB: don't call ``kernel_auth_kwargs`` here. That call
234235
# materialises the bearer token in-process; keeping a
235236
# cleartext copy on a long-lived connector object that may
@@ -293,12 +294,16 @@ def open_session(
293294
) -> SessionId:
294295
if self._kernel_session is not None:
295296
raise InterfaceError("KernelDatabricksClient already has an open session.")
296-
# ``session_configuration`` flows through to the kernel's
297-
# ``session_conf`` map verbatim; the SEA endpoint enforces
298-
# its own allow-list and rejects unknown keys.
299-
session_conf: Optional[Dict[str, str]] = None
300-
if session_configuration:
301-
session_conf = {k: str(v) for k, v in session_configuration.items()}
297+
# Convert server session confs to strings, then add the kernel's
298+
# client-side CloudFetch knob to the same boundary map.
299+
session_conf = (
300+
{k: str(v) for k, v in session_configuration.items()}
301+
if session_configuration
302+
else {}
303+
)
304+
# The kernel consumes this before filtering the server confs and
305+
# selects INLINE when CloudFetch is disabled.
306+
session_conf["cloudfetch_enabled"] = str(self._use_cloud_fetch).lower()
302307
# The kwarg builds run INSIDE the try so the ``finally`` scrub
303308
# below always fires — including when ``kernel_auth_kwargs``
304309
# itself raises mid-build (e.g. an OAuth token-exchange failure

src/databricks/sql/session.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ def _create_backend(
204204
http_client=self.http_client,
205205
catalog=kwargs.get("catalog"),
206206
schema=kwargs.get("schema"),
207+
use_cloud_fetch=kwargs.get("use_cloud_fetch", True),
207208
_use_arrow_native_complex_types=_use_arrow_native_complex_types,
208209
auth_options=kernel_auth_options,
209210
retry_options=kernel_retry_options,

tests/unit/test_kernel_client.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,42 @@ def fake_session(**kw):
344344
assert captured.get("complex_types_as_json") is expected_flag
345345

346346

347+
@pytest.mark.parametrize(
348+
"use_cloud_fetch, expected",
349+
[(True, "true"), (False, "false")],
350+
)
351+
def test_open_session_passes_cloud_fetch_setting_to_kernel(
352+
monkeypatch, use_cloud_fetch, expected
353+
):
354+
captured = {}
355+
356+
def fake_session(**kw):
357+
captured.update(kw)
358+
sess = MagicMock()
359+
sess.session_id = "sess-id"
360+
return sess
361+
362+
monkeypatch.setattr(kernel_client._kernel, "Session", fake_session)
363+
364+
c = kernel_client.KernelDatabricksClient(
365+
server_hostname="example.cloud.databricks.com",
366+
http_path="/sql/1.0/warehouses/abc",
367+
auth_provider=AccessTokenAuthProvider("dapi-test"),
368+
ssl_options=None,
369+
use_cloud_fetch=use_cloud_fetch,
370+
)
371+
c.open_session(
372+
session_configuration={"ANSI_MODE": "false"},
373+
catalog=None,
374+
schema=None,
375+
)
376+
377+
assert captured["session_conf"] == {
378+
"ANSI_MODE": "false",
379+
"cloudfetch_enabled": expected,
380+
}
381+
382+
347383
def test_execute_command_forwards_parameters_to_bind_param():
348384
"""``execute_command(parameters=[...])`` routes each parameter
349385
through ``bind_tspark_params`` onto the kernel statement before

tests/unit/test_session.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,44 @@ def test_retry_kwargs_threaded_into_kernel_client(self):
478478
conn.close()
479479

480480

481+
class TestKernelCloudFetchThreading:
482+
def test_use_cloud_fetch_threaded_into_kernel_client(self):
483+
import sys
484+
import types
485+
486+
pytest.importorskip(
487+
"pyarrow",
488+
reason="kernel client module imports pyarrow at load",
489+
)
490+
491+
fake = types.ModuleType("databricks_sql_kernel")
492+
fake.KernelError = type("KernelError", (Exception,), {})
493+
fake.Session = MagicMock()
494+
495+
with patch.dict(sys.modules, {"databricks_sql_kernel": fake}), patch(
496+
"databricks.sql.backend.kernel.client.KernelDatabricksClient"
497+
) as mock_kernel_client, patch(
498+
"databricks.sql.session.get_python_sql_connector_auth_provider"
499+
):
500+
instance = mock_kernel_client.return_value
501+
instance.open_session.return_value = SessionId(
502+
BackendType.SEA, "sess-id", None
503+
)
504+
505+
conn = databricks.sql.connect(
506+
server_hostname="foo",
507+
http_path="/sql/1.0/warehouses/abc",
508+
use_kernel=True,
509+
use_cloud_fetch=False,
510+
access_token="dapi-xyz",
511+
enable_telemetry=False,
512+
)
513+
try:
514+
assert mock_kernel_client.call_args.kwargs["use_cloud_fetch"] is False
515+
finally:
516+
conn.close()
517+
518+
481519
class TestKernelUserAgentForwarding:
482520
"""user_agent_entry must reach the kernel on the use_kernel path —
483521
session.py folds it into the composed User-Agent and includes it in

0 commit comments

Comments
 (0)