Skip to content

Commit a5e0a08

Browse files
ai: apply changes for #915 (5 review threads)
Addresses: - #3800053148 at src/databricks/sql/session.py:48 - #3800053178 at src/databricks/sql/session.py:52 - #3800053209 at src/databricks/sql/session.py:69 - #3800056285 at src/databricks/sql/session.py:245 - #3800209433 at src/databricks/sql/session.py:245 Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>
1 parent 7fc2d5f commit a5e0a08

2 files changed

Lines changed: 170 additions & 4 deletions

File tree

src/databricks/sql/session.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,19 @@ def _kernel_host_and_path(
4444
"""
4545
connection_uri = kwargs.get("_connection_uri")
4646
if connection_uri:
47-
# Ensure a scheme so urlsplit populates netloc rather than path; the
48-
# Thrift backend defaults a scheme-less URI to https, so do the same.
47+
# Ensure a scheme so urlsplit populates netloc rather than path. A
48+
# scheme-less URI is ambiguous here, so default to https to match the
49+
# connector's default transport.
4950
uri = connection_uri if "://" in connection_uri else "https://" + connection_uri
5051
parts = urlsplit(uri)
52+
if not parts.netloc:
53+
# A missing authority (e.g. a value like ``//foo`` or ``https:///p``)
54+
# would otherwise yield a scheme-only host such as ``https://`` and
55+
# silently connect to the wrong endpoint. Fail loudly instead.
56+
raise ValueError(
57+
"Invalid _connection_uri {!r}: could not determine host "
58+
"authority (expected scheme://host[:port]/path)".format(connection_uri)
59+
)
5160
host = "{}://{}".format(parts.scheme, parts.netloc)
5261
path = parts.path or http_path
5362
if parts.query:
@@ -58,9 +67,15 @@ def _kernel_host_and_path(
5867
if port is not None:
5968
# server_hostname is a bare host on this path (e.g.
6069
# ``dbc-123.cloud.databricks.com``); the kernel adds the scheme.
61-
# Append the port unless the host already carries one.
70+
# Append the port unless the host already carries one. Detect an
71+
# existing port via ``urlsplit`` rather than a naive ``":" in host``
72+
# check, so IPv6 literals (whose authority legitimately contains ``:``
73+
# even without a port, e.g. ``[::1]``) are handled correctly.
74+
# ``urlsplit`` only populates ``netloc``/``port`` when a scheme is
75+
# present, so add a temporary one when the host is scheme-less.
6276
host = server_hostname.rstrip("/")
63-
if ":" not in host:
77+
probe = host if "://" in host else "https://" + host
78+
if urlsplit(probe).port is None:
6479
host = "{}:{}".format(host, port)
6580
return host, http_path
6681

@@ -245,6 +260,23 @@ def _create_backend(
245260
kernel_host, kernel_http_path = _kernel_host_and_path(
246261
server_hostname, http_path, kwargs
247262
)
263+
# The SPOG ``x-databricks-org-id`` header in ``all_headers`` was
264+
# derived in ``__init__`` from the *original* ``http_path``. A
265+
# ``_connection_uri`` override can rewrite the kernel path to a
266+
# different workspace (a different ``?o=`` or cluster path), so
267+
# re-derive the routing header from the resolved path and swap it
268+
# in — otherwise the kernel would receive an org-id that points at
269+
# the pre-override workspace (a silent mis-routing). A caller-set
270+
# header still wins: ``_spog_headers`` is empty in that case, so the
271+
# explicit header is left untouched below.
272+
if kernel_http_path != http_path and self._spog_headers:
273+
base_headers = [
274+
h for h in all_headers if h not in self._spog_headers.items()
275+
]
276+
self._spog_headers = self._extract_spog_headers(
277+
kernel_http_path, base_headers
278+
)
279+
all_headers = base_headers + list(self._spog_headers.items())
248280
return KernelDatabricksClient(
249281
server_hostname=kernel_host,
250282
http_path=kernel_http_path,

tests/unit/test_session.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,52 @@ def test_retry_kwargs_threaded_into_kernel_client(self):
477477
finally:
478478
conn.close()
479479

480+
def test_remapped_host_and_path_threaded_into_kernel_client(self):
481+
"""The remapped ``server_hostname``/``http_path`` from
482+
``_kernel_host_and_path`` must reach ``KernelDatabricksClient``.
483+
Guards against a regression that dropped or reordered the call so
484+
the raw (pre-override) values leaked through — the silent-ignore
485+
bug this PR fixes, which the pure-function tests alone can't catch.
486+
"""
487+
import sys
488+
import types
489+
490+
pytest.importorskip(
491+
"pyarrow",
492+
reason="kernel client module imports pyarrow at load",
493+
)
494+
495+
fake = types.ModuleType("databricks_sql_kernel")
496+
fake.KernelError = type("KernelError", (Exception,), {})
497+
fake.Session = MagicMock()
498+
499+
with patch.dict(sys.modules, {"databricks_sql_kernel": fake}), patch(
500+
"databricks.sql.backend.kernel.client.KernelDatabricksClient"
501+
) as mock_kernel_client, patch(
502+
"%s.session.get_python_sql_connector_auth_provider" % self.PACKAGE
503+
):
504+
instance = mock_kernel_client.return_value
505+
instance.open_session.return_value = SessionId(
506+
BackendType.SEA, "sess-id", None
507+
)
508+
509+
conn = databricks.sql.connect(
510+
server_hostname="foo.cloud.databricks.com",
511+
http_path="/sql/1.0/warehouses/abc",
512+
use_kernel=True,
513+
access_token="dapi-xyz",
514+
enable_telemetry=False,
515+
_connection_uri="https://direct.example.com:8443/sql/1.0/warehouses/xyz",
516+
)
517+
try:
518+
_, kwargs = mock_kernel_client.call_args
519+
# The _connection_uri override must be split and remapped
520+
# onto the kernel client, not passed through raw.
521+
assert kwargs["server_hostname"] == "https://direct.example.com:8443"
522+
assert kwargs["http_path"] == "/sql/1.0/warehouses/xyz"
523+
finally:
524+
conn.close()
525+
480526

481527
class TestKernelUserAgentForwarding:
482528
"""user_agent_entry must reach the kernel on the use_kernel path —
@@ -527,6 +573,79 @@ def test_user_agent_entry_reaches_kernel_client_http_headers(self):
527573
conn.close()
528574

529575

576+
class TestKernelSpogHeaderReDerivedFromResolvedPath:
577+
"""On the kernel path a ``_connection_uri`` override can rewrite the
578+
http_path to a different workspace. The SPOG ``x-databricks-org-id``
579+
header is computed in ``__init__`` from the *original* http_path, so it
580+
must be re-derived from the resolved kernel path — otherwise the kernel
581+
would receive an org-id pointing at the pre-override workspace."""
582+
583+
PACKAGE = "databricks.sql"
584+
585+
def _connect_and_get_kernel_headers(self, connect_kwargs):
586+
import sys
587+
import types
588+
589+
pytest.importorskip(
590+
"pyarrow", reason="kernel client module imports pyarrow at load"
591+
)
592+
593+
fake = types.ModuleType("databricks_sql_kernel")
594+
fake.KernelError = type("KernelError", (Exception,), {})
595+
fake.Session = MagicMock()
596+
597+
with patch.dict(sys.modules, {"databricks_sql_kernel": fake}), patch(
598+
"databricks.sql.backend.kernel.client.KernelDatabricksClient"
599+
) as mock_kernel_client, patch(
600+
"%s.session.get_python_sql_connector_auth_provider" % self.PACKAGE
601+
):
602+
instance = mock_kernel_client.return_value
603+
instance.open_session.return_value = SessionId(
604+
BackendType.SEA, "sess-id", None
605+
)
606+
607+
conn = databricks.sql.connect(
608+
server_hostname="foo.cloud.databricks.com",
609+
use_kernel=True,
610+
access_token="dapi-xyz",
611+
enable_telemetry=False,
612+
**connect_kwargs,
613+
)
614+
try:
615+
_, kwargs = mock_kernel_client.call_args
616+
return dict(kwargs["http_headers"])
617+
finally:
618+
conn.close()
619+
620+
def test_org_id_re_derived_from_connection_uri_override(self):
621+
# Original path routes to workspace 111; the _connection_uri override
622+
# points at workspace 222 — the kernel must see org-id 222.
623+
headers = self._connect_and_get_kernel_headers(
624+
{
625+
"http_path": "/sql/1.0/warehouses/abc?o=111",
626+
"_connection_uri": "https://direct.example.com/sql/1.0/warehouses/xyz?o=222",
627+
}
628+
)
629+
assert headers.get("x-databricks-org-id") == "222"
630+
631+
def test_org_id_dropped_when_override_has_no_workspace(self):
632+
# The override points at a path with no workspace routing info, so the
633+
# stale org-id (from the original path) must not be carried over.
634+
headers = self._connect_and_get_kernel_headers(
635+
{
636+
"http_path": "/sql/1.0/warehouses/abc?o=111",
637+
"_connection_uri": "https://direct.example.com/sql/1.0/warehouses/xyz",
638+
}
639+
)
640+
assert "x-databricks-org-id" not in headers
641+
642+
def test_org_id_preserved_when_no_override(self):
643+
headers = self._connect_and_get_kernel_headers(
644+
{"http_path": "/sql/1.0/warehouses/abc?o=111"}
645+
)
646+
assert headers.get("x-databricks-org-id") == "111"
647+
648+
530649
@pytest.mark.realkernel
531650
class TestUseKernelRoutesThroughRealWheel:
532651
"""No-network proof that ``sql.connect(use_kernel=True)`` actually
@@ -638,6 +757,11 @@ def test_connection_uri_wins_over_port(self):
638757
assert host == "https://direct.example.com:9999"
639758
assert path == "/p"
640759

760+
def test_connection_uri_without_authority_raises(self):
761+
for bad in ("//no-scheme-authority", "https:///only-path"):
762+
with pytest.raises(ValueError, match="could not determine host authority"):
763+
_kernel_host_and_path(self.HOST, self.PATH, {"_connection_uri": bad})
764+
641765
def test_port_folded_into_bare_host(self):
642766
host, path = _kernel_host_and_path(self.HOST, self.PATH, {"_port": 8443})
643767
assert host == "{}:8443".format(self.HOST)
@@ -646,3 +770,13 @@ def test_port_folded_into_bare_host(self):
646770
def test_port_not_double_appended_when_host_has_port(self):
647771
host, _ = _kernel_host_and_path(self.HOST + ":7000", self.PATH, {"_port": 8443})
648772
assert host == self.HOST + ":7000"
773+
774+
def test_port_folded_into_ipv6_literal_without_port(self):
775+
# An IPv6 authority contains ``:`` even without a port, so a naive
776+
# ``":" in host`` check would wrongly skip appending _port.
777+
host, _ = _kernel_host_and_path("[::1]", self.PATH, {"_port": 8443})
778+
assert host == "[::1]:8443"
779+
780+
def test_port_not_double_appended_for_ipv6_literal_with_port(self):
781+
host, _ = _kernel_host_and_path("[::1]:7000", self.PATH, {"_port": 8443})
782+
assert host == "[::1]:7000"

0 commit comments

Comments
 (0)