databricks-sql-connector 4.2.6__tar.gz → 4.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/CHANGELOG.md +13 -0
  2. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/PKG-INFO +31 -4
  3. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/README.md +24 -0
  4. databricks_sql_connector-4.3.0/pyproject.toml +135 -0
  5. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/__init__.py +1 -1
  6. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/common.py +2 -0
  7. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/retry.py +16 -2
  8. databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/__init__.py +25 -0
  9. databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/_errors.py +185 -0
  10. databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/auth_bridge.py +268 -0
  11. databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/client.py +1069 -0
  12. databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/result_set.py +274 -0
  13. databricks_sql_connector-4.3.0/src/databricks/sql/backend/kernel/type_mapping.py +248 -0
  14. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/http_client.py +4 -0
  15. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/thrift_backend.py +4 -0
  16. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/client.py +36 -6
  17. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/unified_http_client.py +1 -0
  18. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/result_set.py +35 -5
  19. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/session.py +142 -26
  20. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/telemetry_client.py +5 -0
  21. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/utils.py +6 -4
  22. databricks_sql_connector-4.2.6/pyproject.toml +0 -95
  23. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/LICENSE +0 -0
  24. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/__init__.py +0 -0
  25. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/__init__.py +0 -0
  26. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/auth.py +0 -0
  27. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/auth_utils.py +0 -0
  28. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/authenticators.py +0 -0
  29. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/endpoint.py +0 -0
  30. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/oauth.py +0 -0
  31. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
  32. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/thrift_http_client.py +0 -0
  33. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/auth/token_federation.py +0 -0
  34. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/databricks_client.py +0 -0
  35. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/backend.py +0 -0
  36. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/__init__.py +0 -0
  37. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/base.py +0 -0
  38. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/requests.py +0 -0
  39. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/models/responses.py +0 -0
  40. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/queue.py +0 -0
  41. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/result_set.py +0 -0
  42. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/constants.py +0 -0
  43. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/conversion.py +0 -0
  44. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/filters.py +0 -0
  45. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/sea/utils/normalize.py +0 -0
  46. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/types.py +0 -0
  47. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/utils/__init__.py +0 -0
  48. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/backend/utils/guid_utils.py +0 -0
  49. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/cloudfetch/download_manager.py +0 -0
  50. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/cloudfetch/downloader.py +0 -0
  51. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/agent.py +0 -0
  52. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/feature_flag.py +0 -0
  53. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/http.py +0 -0
  54. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/http_utils.py +0 -0
  55. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/common/url_utils.py +0 -0
  56. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/exc.py +0 -0
  57. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/experimental/__init__.py +0 -0
  58. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
  59. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/parameters/__init__.py +0 -0
  60. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/parameters/native.py +0 -0
  61. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/parameters/py.typed +0 -0
  62. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/py.typed +0 -0
  63. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/circuit_breaker_manager.py +0 -0
  64. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/latency_logger.py +0 -0
  65. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/endpoint_models.py +0 -0
  66. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/enums.py +0 -0
  67. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/event.py +0 -0
  68. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/models/frontend_logs.py +0 -0
  69. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/telemetry_push_client.py +0 -0
  70. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/telemetry/utils.py +0 -0
  71. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
  72. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
  73. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
  74. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
  75. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/TCLIService/ttypes.py +0 -0
  76. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/thrift_api/__init__.py +0 -0
  77. {databricks_sql_connector-4.2.6 → databricks_sql_connector-4.3.0}/src/databricks/sql/types.py +0 -0
@@ -1,5 +1,18 @@
1
1
  # Release History
2
2
 
3
+ # 4.3.0 (2026-06-12)
4
+ - **New: optional Rust kernel backend (`use_kernel=True`).** Adds an alternative connection path backed by the native [`databricks-sql-kernel`](https://pypi.org/project/databricks-sql-kernel/) client (a Rust core exposed via PyO3), installable with the new `databricks-sql-connector[kernel]` extra. The kernel talks to Databricks over the **SEA (Statement Execution API) HTTP transport** — not Thrift — with CloudFetch and inline-Arrow result fetching, so `use_kernel=True` gives you a modern SEA-native client through the same DB-API surface. Supports PAT, OAuth M2M, and OAuth U2M auth. Requires Python >= 3.10 (the kernel wheel is `cp310-abi3`); on older interpreters the extra is a no-op and `use_kernel=True` raises a clear `ImportError`. The default backend remains Thrift — opt in per connection.
5
+ - Kernel backend behavior is aligned with the Thrift backend so application code works the same either way: consistent cursor-state tracking (`query_id` / `get_query_state`), metadata (catalogs/schemas/tables/columns with JDBC-style filter semantics and case-insensitive `table_types`), DML `rowcount`, server-sourced async execution state, sync `cancel()`, fail-loud staging/volume operations, and structured error context (SQLSTATE, diagnostic info). Kernel logs surface through Python `logging` under the `databricks.sql.kernel` logger (databricks/databricks-sql-python#824, #825, #830, #838, #839 by @vikrantpuppala)
6
+ - Revert the thrift 0.23.0 bump that broke installation on DBR LTS (ES-1960554) (databricks/databricks-sql-python#840 by @vikrantpuppala)
7
+
8
+ # 4.2.7 (2026-06-02)
9
+ - Extract SPOG org-id from cluster http_path for non-Thrift requests (databricks/databricks-sql-python#817 by @msrathore-db)
10
+ - Remove empty chunks in CloudFetch concatenation (databricks/databricks-sql-python#814 by @jprakash-db)
11
+ - Add `_retry_server_directed_only` mode for Retry-After header compliance (databricks/databricks-sql-python#756 by @sd-db)
12
+ - Bump thrift to 0.23.0 (databricks/databricks-sql-python#796 by @leoromanovsky)
13
+ - Allow pandas 3.x in dependency constraints (databricks/databricks-sql-python#768 by @moomindani)
14
+ - Telemetry: unwrap TokenFederationProvider to report inner auth mechanism/flow (databricks/databricks-sql-python#781 by @samikshya-db)
15
+
3
16
  # 4.2.6 (2026-04-22)
4
17
  - Add SPOG routing support for account-level vanity URLs (databricks/databricks-sql-python#767 by @msrathore-db)
5
18
  - Fix dependency_manager: handle PEP 440 ~= compatible release syntax (databricks/databricks-sql-python#776 by @vikrantpuppala)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-sql-connector
3
- Version: 4.2.6
3
+ Version: 4.3.0
4
4
  Summary: Databricks SQL Connector for Python
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -16,14 +16,17 @@ Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Programming Language :: Python :: 3.14
19
+ Provides-Extra: kernel
19
20
  Provides-Extra: pyarrow
21
+ Requires-Dist: databricks-sql-kernel (>=0.2.0,<0.3.0) ; (python_version >= "3.10") and (extra == "kernel")
20
22
  Requires-Dist: lz4 (>=4.0.2,<5.0.0) ; python_version >= "3.8" and python_version < "3.14"
21
23
  Requires-Dist: lz4 (>=4.4.5,<5.0.0) ; python_version >= "3.14"
22
24
  Requires-Dist: oauthlib (>=3.1.0,<4.0.0)
23
25
  Requires-Dist: openpyxl (>=3.0.10,<4.0.0)
24
- Requires-Dist: pandas (>=1.2.5,<2.4.0) ; python_version >= "3.8" and python_version < "3.13"
25
- Requires-Dist: pandas (>=2.2.3,<2.4.0) ; python_version >= "3.13"
26
- Requires-Dist: pyarrow (>=14.0.1) ; (python_version >= "3.8" and python_version < "3.13") and (extra == "pyarrow")
26
+ Requires-Dist: pandas (>=1.2.5,<4.0.0) ; python_version >= "3.8" and python_version < "3.13"
27
+ Requires-Dist: pandas (>=2.2.3,<4.0.0) ; python_version >= "3.13"
28
+ Requires-Dist: pyarrow (>=14.0.1) ; (python_version >= "3.10" and python_version < "3.13") and (extra == "pyarrow")
29
+ Requires-Dist: pyarrow (>=14.0.1,<23) ; (python_version >= "3.8" and python_version < "3.10") and (extra == "pyarrow")
27
30
  Requires-Dist: pyarrow (>=18.0.0) ; (python_version == "3.13") and (extra == "pyarrow")
28
31
  Requires-Dist: pyarrow (>=22.0.0) ; (python_version >= "3.14") and (extra == "pyarrow")
29
32
  Requires-Dist: pybreaker (>=1.0.0,<2.0.0)
@@ -68,6 +71,30 @@ Install using `pip install databricks-sql-connector`
68
71
  ### Installing the core library with PyArrow
69
72
  Install using `pip install databricks-sql-connector[pyarrow]`
70
73
 
74
+ ### Installing with the Rust kernel backend (`use_kernel=True`)
75
+ Install using `pip install databricks-sql-connector[kernel]`
76
+
77
+ This adds the optional [`databricks-sql-kernel`](https://pypi.org/project/databricks-sql-kernel/)
78
+ extension (a native Rust client core, exposed via PyO3). Pass
79
+ `use_kernel=True` to `sql.connect(...)` to route the connection through it
80
+ instead of the default Thrift backend:
81
+
82
+ ```python
83
+ connection = sql.connect(
84
+ server_hostname=host,
85
+ http_path=http_path,
86
+ access_token=token,
87
+ use_kernel=True,
88
+ )
89
+ ```
90
+
91
+ Notes:
92
+ - Requires **Python >= 3.10** (the kernel wheel is published as
93
+ `cp310-abi3`). On older interpreters the `[kernel]` extra installs
94
+ nothing and `use_kernel=True` raises an `ImportError`.
95
+ - The extra also pulls in PyArrow, which the kernel result path requires.
96
+ - Authentication supports PAT (`access_token`), OAuth M2M, and OAuth U2M.
97
+
71
98
 
72
99
  ```bash
73
100
  export DATABRICKS_HOST=********.databricks.com
@@ -30,6 +30,30 @@ Install using `pip install databricks-sql-connector`
30
30
  ### Installing the core library with PyArrow
31
31
  Install using `pip install databricks-sql-connector[pyarrow]`
32
32
 
33
+ ### Installing with the Rust kernel backend (`use_kernel=True`)
34
+ Install using `pip install databricks-sql-connector[kernel]`
35
+
36
+ This adds the optional [`databricks-sql-kernel`](https://pypi.org/project/databricks-sql-kernel/)
37
+ extension (a native Rust client core, exposed via PyO3). Pass
38
+ `use_kernel=True` to `sql.connect(...)` to route the connection through it
39
+ instead of the default Thrift backend:
40
+
41
+ ```python
42
+ connection = sql.connect(
43
+ server_hostname=host,
44
+ http_path=http_path,
45
+ access_token=token,
46
+ use_kernel=True,
47
+ )
48
+ ```
49
+
50
+ Notes:
51
+ - Requires **Python >= 3.10** (the kernel wheel is published as
52
+ `cp310-abi3`). On older interpreters the `[kernel]` extra installs
53
+ nothing and `use_kernel=True` raises an `ImportError`.
54
+ - The extra also pulls in PyArrow, which the kernel result path requires.
55
+ - Authentication supports PAT (`access_token`), OAuth M2M, and OAuth U2M.
56
+
33
57
 
34
58
  ```bash
35
59
  export DATABRICKS_HOST=********.databricks.com
@@ -0,0 +1,135 @@
1
+ [tool.poetry]
2
+ name = "databricks-sql-connector"
3
+ version = "4.3.0"
4
+ description = "Databricks SQL Connector for Python"
5
+ authors = ["Databricks <databricks-sql-connector-maintainers@databricks.com>"]
6
+ license = "Apache-2.0"
7
+ readme = "README.md"
8
+ packages = [{ include = "databricks", from = "src" }]
9
+ include = ["CHANGELOG.md"]
10
+
11
+ [tool.poetry.dependencies]
12
+ python = "^3.8.0"
13
+ thrift = "~=0.22.0"
14
+ pandas = [
15
+ { version = ">=1.2.5,<4.0.0", python = ">=3.8,<3.13" },
16
+ { version = ">=2.2.3,<4.0.0", python = ">=3.13" }
17
+ ]
18
+ lz4 = [
19
+ { version = "^4.0.2", python = ">=3.8,<3.14" },
20
+ { version = "^4.4.5", python = ">=3.14" }
21
+ ]
22
+ requests = "^2.18.1"
23
+ oauthlib = "^3.1.0"
24
+ openpyxl = "^3.0.10"
25
+ urllib3 = ">=1.26"
26
+ python-dateutil = "^2.8.0"
27
+ pyarrow = [
28
+ # The <3.10 band is capped at <23 because pyarrow>=23 dropped
29
+ # Python 3.9 (it requires >=3.10). Without the cap, poetry tries to
30
+ # unify this entry with the kernel's transitive pyarrow>=23.0.1,<24
31
+ # across the 3.8–3.10 slice and `poetry lock` fails ("pyarrow is
32
+ # forbidden"). The cap removes no installable version — the newest
33
+ # pyarrow with a 3.9 wheel is 21.x — it just makes that explicit to
34
+ # the solver so the optional [kernel] extra (python>=3.10) can
35
+ # coexist. See the kernel dep + [kernel] extra below.
36
+ { version = ">=14.0.1,<23", python = ">=3.8,<3.10", optional=true },
37
+ { version = ">=14.0.1", python = ">=3.10,<3.13", optional=true },
38
+ { version = ">=18.0.0", python = ">=3.13,<3.14", optional=true },
39
+ { version = ">=22.0.0", python = ">=3.14", optional=true }
40
+ ]
41
+ pyjwt = "^2.0.0"
42
+ pybreaker = "^1.0.0"
43
+ requests-kerberos = {version = "^0.15.0", optional = true}
44
+ # Optional Rust kernel backend for ``use_kernel=True`` (PyO3 wheel).
45
+ # Pulled in only via the ``[kernel]`` extra below. The published wheel
46
+ # is ``abi3`` with ``Requires-Python: >=3.10`` (built ``abi3-py310``),
47
+ # so the dependency is gated to Python >= 3.10: on 3.8/3.9 the
48
+ # ``[kernel]`` extra resolves to nothing and ``use_kernel=True`` raises
49
+ # a clear ImportError at runtime (see backend/kernel/_errors.py).
50
+ #
51
+ # Floor is 0.2.0 (``^0.2.0`` == ``>=0.2.0,<0.3.0``). The kernel is
52
+ # pre-1.0, so each 0.x minor may carry breaking changes — the ``<0.3.0``
53
+ # cap means we bump this deliberately when the kernel ships 0.3.0 rather
54
+ # than letting a potentially-breaking minor flow in automatically. 0.2.0
55
+ # keeps the same Requires-Python (>=3.10) and pyarrow (>=23.0.1,<24) pin
56
+ # as 0.1.x, so the gating below is unchanged.
57
+ databricks-sql-kernel = {version = "^0.2.0", optional = true, python = ">=3.10"}
58
+
59
+
60
+ [tool.poetry.extras]
61
+ pyarrow = ["pyarrow"]
62
+ # ``pip install databricks-sql-connector[kernel]`` adds the Rust kernel
63
+ # backend so ``use_kernel=True`` works. No-op on Python < 3.10 (the
64
+ # wheel's floor) — those users get a runtime ImportError if they pass
65
+ # ``use_kernel=True``.
66
+ #
67
+ # The kernel result path (``backend/kernel/result_set.py``) needs
68
+ # pyarrow, but it is NOT listed in this extra on purpose: the published
69
+ # kernel wheel declares ``pyarrow>=23.0.1,<24`` as a hard runtime
70
+ # dependency, so ``pip install ...[kernel]`` already pulls a compatible
71
+ # pyarrow transitively. Listing bare ``pyarrow`` here additionally
72
+ # forces poetry to co-resolve an unconstrained pyarrow against the
73
+ # kernel's ``>=23.0.1,<24`` (which itself requires Python >=3.10) across
74
+ # the connector's full 3.8–3.14 support matrix, which is unsatisfiable
75
+ # on 3.8/3.9 and breaks ``poetry lock``. The kernel's own dependency
76
+ # metadata is the single source of truth for the pyarrow floor.
77
+ kernel = ["databricks-sql-kernel"]
78
+
79
+ [tool.poetry.group.dev.dependencies]
80
+ pytest = "^7.1.2"
81
+ mypy = "^1.10.1"
82
+ pylint = ">=2.12.0"
83
+ black = "^22.3.0"
84
+ pytest-dotenv = "^0.5.2"
85
+ pytest-cov = "^4.0.0"
86
+ pytest-xdist = "^3.0.0"
87
+ numpy = [
88
+ { version = ">=1.16.6", python = ">=3.8,<3.11" },
89
+ { version = ">=1.23.4", python = ">=3.11" },
90
+ ]
91
+
92
+ [tool.poetry.urls]
93
+ "Homepage" = "https://github.com/databricks/databricks-sql-python"
94
+ "Bug Tracker" = "https://github.com/databricks/databricks-sql-python/issues"
95
+
96
+ [build-system]
97
+ requires = ["poetry-core>=1.0.0"]
98
+ build-backend = "poetry.core.masonry.api"
99
+
100
+ [tool.mypy]
101
+ ignore_missing_imports = "true"
102
+ exclude = ['ttypes\.py$', 'TCLIService\.py$']
103
+
104
+ [tool.black]
105
+ exclude = '/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist|thrift_api)/'
106
+
107
+ [tool.pytest.ini_options]
108
+ markers = [
109
+ "reviewed: Test case has been reviewed by Databricks",
110
+ "serial: Tests that must run serially (not parallelized)",
111
+ "realkernel: Requires the real databricks-sql-kernel wheel and an unpolluted sys.modules (no fake kernel stub); must run in a separate pytest invocation from tests that fake databricks_sql_kernel (deselect with -m 'not realkernel', run alone with -m realkernel).",
112
+ ]
113
+ minversion = "6.0"
114
+ log_cli = "false"
115
+ log_cli_level = "INFO"
116
+ testpaths = ["tests"]
117
+ env_files = ["test.env"]
118
+
119
+ [tool.coverage.run]
120
+ source = ["src"]
121
+ branch = true
122
+ omit = [
123
+ "*/tests/*",
124
+ "*/test_*",
125
+ "*/__pycache__/*",
126
+ "*/thrift_api/*",
127
+ ]
128
+
129
+ [tool.coverage.report]
130
+ precision = 2
131
+ show_missing = true
132
+ skip_covered = false
133
+
134
+ [tool.coverage.xml]
135
+ output = "coverage.xml"
@@ -71,7 +71,7 @@ DATETIME = DBAPITypeObject("timestamp")
71
71
  DATE = DBAPITypeObject("date")
72
72
  ROWID = DBAPITypeObject()
73
73
 
74
- __version__ = "4.2.6"
74
+ __version__ = "4.3.0"
75
75
  USER_AGENT_NAME = "PyDatabricksSqlConnector"
76
76
 
77
77
  # These two functions are pyhive legacy
@@ -47,6 +47,7 @@ class ClientContext:
47
47
  retry_stop_after_attempts_duration: Optional[float] = None,
48
48
  retry_delay_default: Optional[float] = None,
49
49
  retry_dangerous_codes: Optional[List[int]] = None,
50
+ respect_server_retry_after_header: Optional[bool] = None,
50
51
  proxy_auth_method: Optional[str] = None,
51
52
  pool_connections: Optional[int] = None,
52
53
  pool_maxsize: Optional[int] = None,
@@ -80,6 +81,7 @@ class ClientContext:
80
81
  )
81
82
  self.retry_delay_default = retry_delay_default or 5.0
82
83
  self.retry_dangerous_codes = retry_dangerous_codes or []
84
+ self.respect_server_retry_after_header = bool(respect_server_retry_after_header)
83
85
  self.proxy_auth_method = proxy_auth_method
84
86
  self.pool_connections = pool_connections or 10
85
87
  self.pool_maxsize = pool_maxsize or 20
@@ -94,6 +94,7 @@ class DatabricksRetryPolicy(Retry):
94
94
  stop_after_attempts_duration: float,
95
95
  delay_default: float,
96
96
  force_dangerous_codes: List[int],
97
+ respect_server_retry_after_header: bool = False,
97
98
  urllib3_kwargs: dict = {},
98
99
  ):
99
100
  # These values do not change from one command to the next
@@ -103,6 +104,7 @@ class DatabricksRetryPolicy(Retry):
103
104
  self.stop_after_attempts_duration = stop_after_attempts_duration
104
105
  self._delay_default = delay_default
105
106
  self.force_dangerous_codes = force_dangerous_codes
107
+ self.respect_server_retry_after_header = respect_server_retry_after_header
106
108
 
107
109
  # the urllib3 kwargs are a mix of configuration (some of which we override)
108
110
  # and counters like `total` or `connect` which may change between successive retries
@@ -202,6 +204,7 @@ class DatabricksRetryPolicy(Retry):
202
204
  stop_after_attempts_duration=self.stop_after_attempts_duration,
203
205
  delay_default=self.delay_default,
204
206
  force_dangerous_codes=self.force_dangerous_codes,
207
+ respect_server_retry_after_header=self.respect_server_retry_after_header,
205
208
  urllib3_kwargs={},
206
209
  )
207
210
 
@@ -323,7 +326,9 @@ class DatabricksRetryPolicy(Retry):
323
326
 
324
327
  return proposed_backoff
325
328
 
326
- def should_retry(self, method: str, status_code: int) -> Tuple[bool, str]:
329
+ def should_retry(
330
+ self, method: str, status_code: int, has_retry_after: bool = False
331
+ ) -> Tuple[bool, str]:
327
332
  """This method encapsulates the connector's approach to retries.
328
333
 
329
334
  We always retry a request unless one of these conditions is met:
@@ -388,6 +393,15 @@ class DatabricksRetryPolicy(Retry):
388
393
  if not self._is_method_retryable(method):
389
394
  return False, "Only POST requests are retried"
390
395
 
396
+ # When respect_server_retry_after_header is enabled, only retry when the
397
+ # server explicitly signals it's safe via a Retry-After header. This prevents
398
+ # duplicate side effects for non-idempotent operations.
399
+ if self.respect_server_retry_after_header and not has_retry_after:
400
+ return (
401
+ False,
402
+ "respect_server_retry_after_header mode: no Retry-After header present",
403
+ )
404
+
391
405
  # Request failed, was an ExecuteStatement and the command may have reached the server
392
406
  if (
393
407
  self.command_type == CommandType.EXECUTE_STATEMENT
@@ -430,7 +444,7 @@ class DatabricksRetryPolicy(Retry):
430
444
  Logs a debug message if the request will be retried
431
445
  """
432
446
 
433
- should_retry, msg = self.should_retry(method, status_code)
447
+ should_retry, msg = self.should_retry(method, status_code, has_retry_after)
434
448
 
435
449
  if should_retry:
436
450
  logger.debug(msg)
@@ -0,0 +1,25 @@
1
+ """Backend that delegates to the Databricks SQL Kernel (Rust) via PyO3.
2
+
3
+ Routed when ``use_kernel=True`` is passed to ``databricks.sql.connect``.
4
+ The module's identity is "delegates to the kernel" — not the wire
5
+ protocol the kernel happens to use today (SEA REST). The kernel may
6
+ switch its default transport (SEA REST → SEA gRPC → …) without
7
+ renaming this module.
8
+
9
+ This ``__init__`` deliberately does **not** re-export
10
+ ``KernelDatabricksClient`` from ``.client``. Importing ``.client``
11
+ loads the ``databricks_sql_kernel`` PyO3 extension at module-import
12
+ time; doing that eagerly here would make ``import
13
+ databricks.sql.backend.kernel.type_mapping`` (used by tests / by
14
+ ``KernelResultSet`` consumers) require the kernel wheel even when
15
+ the caller never plans to open a kernel-backed session. Callers
16
+ that need the client import it directly:
17
+
18
+ from databricks.sql.backend.kernel.client import KernelDatabricksClient
19
+
20
+ ``session.py::_create_backend`` already does this lazy import under
21
+ the ``use_kernel=True`` branch.
22
+
23
+ See ``docs/designs/pysql-kernel-integration.md`` in
24
+ ``databricks-sql-kernel`` for the full integration design.
25
+ """
@@ -0,0 +1,185 @@
1
+ """Shared error-mapping primitives for the kernel backend.
2
+
3
+ The PyO3 boundary can produce two flavours of exception:
4
+
5
+ - ``databricks_sql_kernel.KernelError`` — the kernel's own
6
+ structured error type. Carries ``code`` / ``message`` /
7
+ ``sql_state`` / ``query_id`` / ``http_status`` / ``retryable`` /
8
+ ``vendor_code`` / ``error_code`` as attributes; mapped to a PEP
9
+ 249 exception class via ``_CODE_TO_EXCEPTION`` with the
10
+ attributes forwarded onto the re-raised exception so callers can
11
+ branch on ``err.code`` / ``err.sql_state`` without reaching
12
+ through ``__cause__``.
13
+ - Anything else — ``TypeError`` / ``OverflowError`` /
14
+ ``ValueError`` from PyO3 argument conversion, or arbitrary
15
+ extension-internal Python errors. These would otherwise propagate
16
+ raw to connector callers, breaking the DB-API contract that says
17
+ "only PEP 249 exception types cross the boundary". Wrapped in
18
+ ``OperationalError`` here.
19
+
20
+ These primitives live in their own module so both ``client.py``
21
+ (which orchestrates PyO3 calls) and ``result_set.py`` (which calls
22
+ ``fetch_next_batch`` on the same kernel handles) can share them
23
+ without ``result_set.py`` importing from ``client.py``.
24
+
25
+ Usage at every PyO3 call site is a plain try/except:
26
+
27
+ try:
28
+ stmt.execute()
29
+ except Exception as exc:
30
+ raise wrap_kernel_exception("execute_command", exc) from exc
31
+
32
+ The helper returns the mapped exception; callers raise it. Plain
33
+ ``try/except`` is preferred over a context manager: the control
34
+ flow is visible at the call site, the helper is a pure function
35
+ (trivial to test), and tracebacks don't carry an extra
36
+ ``__exit__`` frame.
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ import logging
42
+
43
+ from databricks.sql.exc import (
44
+ DatabaseError,
45
+ Error,
46
+ OperationalError,
47
+ ProgrammingError,
48
+ ServerOperationError,
49
+ )
50
+
51
+ try:
52
+ import databricks_sql_kernel as _kernel # type: ignore[import-not-found]
53
+ except ImportError as exc: # pragma: no cover - same hint as client.py
54
+ raise ImportError(
55
+ "use_kernel=True requires the optional databricks-sql-kernel "
56
+ "extension, which is not installed. Install it with:\n"
57
+ ' pip install "databricks-sql-connector[kernel]"\n'
58
+ "The kernel wheel requires Python >= 3.10; on older interpreters "
59
+ "use_kernel is unavailable. For local kernel development you can "
60
+ "instead build it from the databricks-sql-kernel repo:\n"
61
+ " cd databricks-sql-kernel/pyo3 && maturin develop --release"
62
+ ) from exc
63
+
64
+ # Route the kernel's Rust-side logs into Python's ``logging`` as soon as
65
+ # the extension loads. The kernel emits under the ``databricks.sql.kernel``
66
+ # logger (a child of the connector's ``databricks.sql`` namespace), so a
67
+ # customer who configures ``databricks.sql`` logging gets kernel logs for
68
+ # free with no extra setup.
69
+ #
70
+ # This is a best-effort, non-essential feature: it must never take down
71
+ # ``use_kernel=True`` for a process. ``getattr`` guards against an older
72
+ # kernel wheel that predates the function. The ``try`` guards against the
73
+ # call itself throwing — note ``except BaseException`` is deliberate: a
74
+ # panic raised across the PyO3 boundary surfaces as
75
+ # ``pyo3_runtime.PanicException``, which derives from ``BaseException``
76
+ # (not ``Exception``), so a narrower clause would let it escape module
77
+ # import and fail every kernel-backed connection. The kernel side is
78
+ # idempotent and returns rather than panics on a double install, but we
79
+ # do not rely on that here — the guard holds regardless of the Rust impl.
80
+ _kernel_init_logging = getattr(_kernel, "init_logging", None)
81
+ if _kernel_init_logging is not None:
82
+ try:
83
+ _kernel_init_logging()
84
+ except BaseException as exc: # noqa: BLE001 - see comment above re: PanicException
85
+ logging.getLogger(__name__).debug(
86
+ "kernel log bridge init failed; continuing without it: %r", exc
87
+ )
88
+
89
+
90
+ # Map a kernel `code` slug to the PEP 249 exception class that best
91
+ # captures it. The match isn't a perfect 1:1 — PEP 249 has a
92
+ # narrower taxonomy than the kernel — so several kernel codes
93
+ # collapse onto the same Python exception. This table is the only
94
+ # place that mapping lives.
95
+ _CODE_TO_EXCEPTION = {
96
+ "InvalidArgument": ProgrammingError,
97
+ "Unauthenticated": OperationalError,
98
+ "PermissionDenied": OperationalError,
99
+ "NotFound": ProgrammingError,
100
+ "ResourceExhausted": OperationalError,
101
+ "Unavailable": OperationalError,
102
+ "Timeout": OperationalError,
103
+ "Cancelled": OperationalError,
104
+ "DataLoss": DatabaseError,
105
+ "Internal": DatabaseError,
106
+ "InvalidStatementHandle": ProgrammingError,
107
+ "NetworkError": OperationalError,
108
+ # `SqlError` is a server-side query failure (syntax error, missing
109
+ # object, etc.) — exactly what the Thrift backend surfaces as
110
+ # `ServerOperationError`. Match Thrift's contract so user code that
111
+ # catches `ServerOperationError` (a subclass of `DatabaseError`)
112
+ # works equivalently with `use_kernel=True`.
113
+ "SqlError": ServerOperationError,
114
+ "Unknown": DatabaseError,
115
+ }
116
+
117
+
118
+ def reraise_kernel_error(exc: "_kernel.KernelError") -> "Error":
119
+ """Convert a ``databricks_sql_kernel.KernelError`` to a PEP 249
120
+ exception with the kernel's structured attributes forwarded onto
121
+ the new instance.
122
+
123
+ The returned exception is raised by callers with ``raise ... from
124
+ exc``; the ``from`` clause is what sets ``__cause__``, so we don't
125
+ touch it here.
126
+ """
127
+ code = getattr(exc, "code", "Unknown")
128
+ cls = _CODE_TO_EXCEPTION.get(code, DatabaseError)
129
+
130
+ # For ServerOperationError, reproduce the Thrift backend's
131
+ # ``context`` dict so callers that read
132
+ # ``err.context["diagnostic-info"]`` (the Spark stack trace) /
133
+ # ``err.context["operation-id"]`` get the same shape on the kernel
134
+ # path. ``diagnostic_info`` is forwarded from the kernel error (it
135
+ # now crosses the PyO3 boundary; older wheels return ``None`` via
136
+ # ``getattr``, so this degrades gracefully). Matches
137
+ # thrift_backend.py's ServerOperationError construction.
138
+ context = None
139
+ if cls is ServerOperationError:
140
+ context = {
141
+ "operation-id": getattr(exc, "query_id", None),
142
+ "diagnostic-info": getattr(exc, "diagnostic_info", None),
143
+ }
144
+ new = cls(getattr(exc, "message", str(exc)), context)
145
+
146
+ for attr in (
147
+ "code",
148
+ "sql_state",
149
+ "error_code",
150
+ "vendor_code",
151
+ "http_status",
152
+ "retryable",
153
+ "query_id",
154
+ # Extended server status now forwarded across the PyO3 boundary
155
+ # (kernel #121). ``getattr(..., None)`` keeps this forward-safe
156
+ # against an older wheel that doesn't set these attrs.
157
+ "display_message",
158
+ "diagnostic_info",
159
+ "error_details_json",
160
+ ):
161
+ setattr(new, attr, getattr(exc, attr, None))
162
+ return new
163
+
164
+
165
+ def wrap_kernel_exception(what: str, exc: BaseException) -> "Error":
166
+ """Map any exception from a PyO3 call site to a PEP 249 exception.
167
+
168
+ - ``KernelError`` → mapped class with structured attrs forwarded.
169
+ - Already-PEP-249 ``Error`` (e.g. raised by an inner caller that
170
+ already mapped) → passed through unchanged.
171
+ - Anything else (``TypeError`` / ``ValueError`` / etc. from PyO3
172
+ argument conversion, extension-internal errors) → wrapped in
173
+ ``OperationalError``.
174
+
175
+ Returned, not raised — the caller decides whether to ``raise``
176
+ or ``raise ... from exc``. ``what`` is a short tag (the calling
177
+ method name) used only in the ``OperationalError`` message.
178
+ """
179
+ if isinstance(exc, _kernel.KernelError):
180
+ return reraise_kernel_error(exc)
181
+ if isinstance(exc, Error):
182
+ return exc
183
+ return OperationalError(
184
+ f"Unexpected error from databricks_sql_kernel during {what}: {exc!r}"
185
+ )