databricks-sql-connector 4.3.0__tar.gz → 4.4.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.
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/CHANGELOG.md +5 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/PKG-INFO +5 -8
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/pyproject.toml +28 -17
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/__init__.py +1 -1
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/auth.py +6 -4
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/authenticators.py +6 -8
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/oauth.py +3 -3
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/retry.py +1 -1
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/thrift_http_client.py +5 -3
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/token_federation.py +3 -3
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/utils/filters.py +2 -4
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/utils/http_client.py +5 -3
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/thrift_backend.py +31 -11
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/types.py +4 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/client.py +47 -11
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/unified_http_client.py +7 -5
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/exc.py +3 -1
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/result_set.py +4 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/telemetry_client.py +9 -9
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/utils.py +18 -2
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/LICENSE +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/README.md +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/auth_utils.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/common.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/endpoint.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/databricks_client.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/kernel/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/kernel/_errors.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/kernel/auth_bridge.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/kernel/client.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/kernel/result_set.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/kernel/type_mapping.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/backend.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/models/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/models/base.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/models/requests.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/models/responses.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/queue.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/result_set.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/utils/constants.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/utils/conversion.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/sea/utils/normalize.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/utils/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/backend/utils/guid_utils.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/cloudfetch/download_manager.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/cloudfetch/downloader.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/agent.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/feature_flag.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/http.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/http_utils.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/url_utils.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/experimental/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/parameters/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/parameters/native.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/parameters/py.typed +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/py.typed +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/session.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/circuit_breaker_manager.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/latency_logger.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/models/endpoint_models.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/models/enums.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/models/event.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/models/frontend_logs.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/telemetry_push_client.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/telemetry/utils.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/thrift_api/TCLIService/ttypes.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/thrift_api/__init__.py +0 -0
- {databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/types.py +0 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
# 4.4.0 (2026-07-22)
|
|
4
|
+
- Raised the minimum supported Python version to 3.10, dropping the end-of-life 3.8/3.9, to update the lockfile and clear CVE-flagged dependencies in the repo (databricks/databricks-sql-python#798)
|
|
5
|
+
- Fix: `REMOVE` staging operations no longer require `staging_allowed_local_path` to be set, since removing a remote file does not touch the local filesystem (databricks/databricks-sql-python#726)
|
|
6
|
+
- Report `cursor.rowcount` for DML on the Thrift backend: INSERT/UPDATE/DELETE/MERGE now set `rowcount` to the server's affected-row count instead of the hardcoded `-1`; SELECT (and statements the server does not report a count for) still return `-1`. `executemany` aggregates the count across all parameter sets per PEP 249 ([#784](https://github.com/databricks/databricks-sql-python/issues/784))
|
|
7
|
+
|
|
3
8
|
# 4.3.0 (2026-06-12)
|
|
4
9
|
- **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
10
|
- 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)
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: databricks-sql-connector
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: Databricks SQL Connector for Python
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Author: Databricks
|
|
8
8
|
Author-email: databricks-sql-connector-maintainers@databricks.com
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -19,21 +17,20 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
19
17
|
Provides-Extra: kernel
|
|
20
18
|
Provides-Extra: pyarrow
|
|
21
19
|
Requires-Dist: databricks-sql-kernel (>=0.2.0,<0.3.0) ; (python_version >= "3.10") and (extra == "kernel")
|
|
22
|
-
Requires-Dist: lz4 (>=4.0.2,<5.0.0) ; python_version >= "3.
|
|
20
|
+
Requires-Dist: lz4 (>=4.0.2,<5.0.0) ; python_version >= "3.10" and python_version < "3.14"
|
|
23
21
|
Requires-Dist: lz4 (>=4.4.5,<5.0.0) ; python_version >= "3.14"
|
|
24
22
|
Requires-Dist: oauthlib (>=3.1.0,<4.0.0)
|
|
25
23
|
Requires-Dist: openpyxl (>=3.0.10,<4.0.0)
|
|
26
|
-
Requires-Dist: pandas (>=1.2.5,<4.0.0) ; python_version >= "3.
|
|
24
|
+
Requires-Dist: pandas (>=1.2.5,<4.0.0) ; python_version >= "3.10" and python_version < "3.13"
|
|
27
25
|
Requires-Dist: pandas (>=2.2.3,<4.0.0) ; python_version >= "3.13"
|
|
28
26
|
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")
|
|
30
27
|
Requires-Dist: pyarrow (>=18.0.0) ; (python_version == "3.13") and (extra == "pyarrow")
|
|
31
28
|
Requires-Dist: pyarrow (>=22.0.0) ; (python_version >= "3.14") and (extra == "pyarrow")
|
|
32
29
|
Requires-Dist: pybreaker (>=1.0.0,<2.0.0)
|
|
33
30
|
Requires-Dist: pyjwt (>=2.0.0,<3.0.0)
|
|
34
31
|
Requires-Dist: python-dateutil (>=2.8.0,<3.0.0)
|
|
35
32
|
Requires-Dist: requests (>=2.18.1,<3.0.0)
|
|
36
|
-
Requires-Dist: thrift (>=0.
|
|
33
|
+
Requires-Dist: thrift (>=0.24.0,<0.25.0)
|
|
37
34
|
Requires-Dist: urllib3 (>=1.26)
|
|
38
35
|
Project-URL: Bug Tracker, https://github.com/databricks/databricks-sql-python/issues
|
|
39
36
|
Project-URL: Homepage, https://github.com/databricks/databricks-sql-python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "databricks-sql-connector"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.4.0"
|
|
4
4
|
description = "Databricks SQL Connector for Python"
|
|
5
5
|
authors = ["Databricks <databricks-sql-connector-maintainers@databricks.com>"]
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -9,14 +9,28 @@ packages = [{ include = "databricks", from = "src" }]
|
|
|
9
9
|
include = ["CHANGELOG.md"]
|
|
10
10
|
|
|
11
11
|
[tool.poetry.dependencies]
|
|
12
|
-
python = "^3.
|
|
13
|
-
|
|
12
|
+
python = "^3.10"
|
|
13
|
+
# Floor is 0.24.0 -- the first release that both clears the open Apache Thrift
|
|
14
|
+
# CVEs (CVE-2025-48431 + the CVE-2026-41602..41636 set, all fixed in 0.23.0)
|
|
15
|
+
# AND is safe to install on DBR LTS. History: 0.23.0 fixed the CVEs but ships
|
|
16
|
+
# sdist-only and its setup.py calls sys.exit(0) on the build-success path,
|
|
17
|
+
# killing the PEP 517 backend on the OLD setuptools bundled by DBR LTS -- the
|
|
18
|
+
# SEV0 ES-1960554 (4.2.7 widened to <0.24.0 and was yanked; PR #840), which is
|
|
19
|
+
# why we held at ~=0.22.0. thrift 0.24.0 (THRIFT-6067) resolves this: it ships
|
|
20
|
+
# prebuilt manylinux2014 wheels (cp310-cp314) + macOS/musl/Windows, so pip uses
|
|
21
|
+
# a wheel and never runs setup.py on DBR LTS -- the build-time break cannot
|
|
22
|
+
# trigger. The `DBR LTS Install` CI check (.github/workflows/dbr-lts-install.yml)
|
|
23
|
+
# installs the built artifact on real DBR LTS clusters and is the authoritative
|
|
24
|
+
# gate for this. Cap at <0.25.0: thrift is pre-1.0, each 0.x minor can carry
|
|
25
|
+
# breaking changes or packaging regressions (see 0.23.0), so bump this
|
|
26
|
+
# deliberately once a new minor ships and the DBR-LTS gate proves it safe.
|
|
27
|
+
thrift = "~=0.24.0"
|
|
14
28
|
pandas = [
|
|
15
|
-
{ version = ">=1.2.5,<4.0.0", python = ">=3.
|
|
29
|
+
{ version = ">=1.2.5,<4.0.0", python = ">=3.10,<3.13" },
|
|
16
30
|
{ version = ">=2.2.3,<4.0.0", python = ">=3.13" }
|
|
17
31
|
]
|
|
18
32
|
lz4 = [
|
|
19
|
-
{ version = "^4.0.2", python = ">=3.
|
|
33
|
+
{ version = "^4.0.2", python = ">=3.10,<3.14" },
|
|
20
34
|
{ version = "^4.4.5", python = ">=3.14" }
|
|
21
35
|
]
|
|
22
36
|
requests = "^2.18.1"
|
|
@@ -25,15 +39,6 @@ openpyxl = "^3.0.10"
|
|
|
25
39
|
urllib3 = ">=1.26"
|
|
26
40
|
python-dateutil = "^2.8.0"
|
|
27
41
|
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
42
|
{ version = ">=14.0.1", python = ">=3.10,<3.13", optional=true },
|
|
38
43
|
{ version = ">=18.0.0", python = ">=3.13,<3.14", optional=true },
|
|
39
44
|
{ version = ">=22.0.0", python = ">=3.14", optional=true }
|
|
@@ -77,15 +82,21 @@ pyarrow = ["pyarrow"]
|
|
|
77
82
|
kernel = ["databricks-sql-kernel"]
|
|
78
83
|
|
|
79
84
|
[tool.poetry.group.dev.dependencies]
|
|
80
|
-
pytest = "^
|
|
85
|
+
pytest = "^9.0.3"
|
|
81
86
|
mypy = "^1.10.1"
|
|
82
87
|
pylint = ">=2.12.0"
|
|
83
|
-
black = "^
|
|
88
|
+
black = "^26.3.1"
|
|
84
89
|
pytest-dotenv = "^0.5.2"
|
|
85
90
|
pytest-cov = "^4.0.0"
|
|
86
91
|
pytest-xdist = "^3.0.0"
|
|
92
|
+
# tests/unit/test_parameters.py imports pytz directly. It used to arrive
|
|
93
|
+
# transitively via pandas, but pandas 3.0 (resolved on Python >=3.11)
|
|
94
|
+
# dropped pytz as a required dependency -- it is now only a pandas extra.
|
|
95
|
+
# Declare it explicitly here so the test suite does not depend on pandas's
|
|
96
|
+
# transitive graph, which differs across the Python matrix.
|
|
97
|
+
pytz = ">=2020.1"
|
|
87
98
|
numpy = [
|
|
88
|
-
{ version = ">=1.16.6", python = ">=3.
|
|
99
|
+
{ version = ">=1.16.6", python = ">=3.10,<3.11" },
|
|
89
100
|
{ version = ">=1.23.4", python = ">=3.11" },
|
|
90
101
|
]
|
|
91
102
|
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/auth.py
RENAMED
|
@@ -102,7 +102,7 @@ def get_python_sql_connector_auth_provider(hostname: str, http_client, **kwargs)
|
|
|
102
102
|
# TODO : unify all the auth mechanisms with the Python SDK
|
|
103
103
|
|
|
104
104
|
auth_type = kwargs.get("auth_type")
|
|
105
|
-
|
|
105
|
+
client_id, redirect_port_range = get_client_id_and_redirect_port(
|
|
106
106
|
auth_type == AuthType.AZURE_OAUTH.value
|
|
107
107
|
)
|
|
108
108
|
|
|
@@ -124,9 +124,11 @@ def get_python_sql_connector_auth_provider(hostname: str, http_client, **kwargs)
|
|
|
124
124
|
azure_client_secret=kwargs.get("azure_client_secret"),
|
|
125
125
|
azure_tenant_id=kwargs.get("azure_tenant_id"),
|
|
126
126
|
azure_workspace_resource_id=kwargs.get("azure_workspace_resource_id"),
|
|
127
|
-
oauth_redirect_port_range=
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
oauth_redirect_port_range=(
|
|
128
|
+
[kwargs["oauth_redirect_port"]]
|
|
129
|
+
if kwargs.get("oauth_client_id") and kwargs.get("oauth_redirect_port")
|
|
130
|
+
else redirect_port_range
|
|
131
|
+
),
|
|
130
132
|
oauth_persistence=kwargs.get("experimental_oauth_persistence"),
|
|
131
133
|
credentials_provider=kwargs.get("credentials_provider"),
|
|
132
134
|
identity_federation_client_id=kwargs.get("identity_federation_client_id"),
|
|
@@ -33,12 +33,10 @@ class CredentialsProvider(abc.ABC):
|
|
|
33
33
|
for authenticating requests to Databricks REST APIs"""
|
|
34
34
|
|
|
35
35
|
@abc.abstractmethod
|
|
36
|
-
def auth_type(self) -> str:
|
|
37
|
-
...
|
|
36
|
+
def auth_type(self) -> str: ...
|
|
38
37
|
|
|
39
38
|
@abc.abstractmethod
|
|
40
|
-
def __call__(self, *args, **kwargs) -> HeaderFactory:
|
|
41
|
-
...
|
|
39
|
+
def __call__(self, *args, **kwargs) -> HeaderFactory: ...
|
|
42
40
|
|
|
43
41
|
|
|
44
42
|
# Private API: this is an evolving interface and it will change in the future.
|
|
@@ -109,7 +107,7 @@ class DatabricksOAuthProvider(AuthProvider):
|
|
|
109
107
|
if self._access_token and self._refresh_token:
|
|
110
108
|
self._update_token_if_expired()
|
|
111
109
|
else:
|
|
112
|
-
|
|
110
|
+
access_token, refresh_token = self.oauth_manager.get_tokens(
|
|
113
111
|
hostname=self._hostname, scope=self._scopes_as_str
|
|
114
112
|
)
|
|
115
113
|
self._access_token = access_token
|
|
@@ -231,9 +229,9 @@ class AzureServicePrincipalCredentialProvider(CredentialsProvider):
|
|
|
231
229
|
}
|
|
232
230
|
|
|
233
231
|
if self.azure_workspace_resource_id:
|
|
234
|
-
headers[
|
|
235
|
-
self.
|
|
236
|
-
|
|
232
|
+
headers[self.DATABRICKS_AZURE_WORKSPACE_RESOURCE_ID_HEADER] = (
|
|
233
|
+
self.azure_workspace_resource_id
|
|
234
|
+
)
|
|
237
235
|
|
|
238
236
|
return headers
|
|
239
237
|
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/oauth.py
RENAMED
|
@@ -45,7 +45,7 @@ class Token:
|
|
|
45
45
|
exp_time = decoded_token.get("exp")
|
|
46
46
|
current_time = time.time()
|
|
47
47
|
buffer_time = 30 # 30 seconds buffer
|
|
48
|
-
return exp_time and (exp_time - buffer_time) <= current_time
|
|
48
|
+
return exp_time is not None and (exp_time - buffer_time) <= current_time
|
|
49
49
|
except Exception as e:
|
|
50
50
|
logger.error("Failed to decode token: %s", e)
|
|
51
51
|
raise e
|
|
@@ -134,7 +134,7 @@ class OAuthManager:
|
|
|
134
134
|
try:
|
|
135
135
|
with HTTPServer(("", port), handler) as httpd:
|
|
136
136
|
redirect_url = OAuthManager.__get_redirect_url(port)
|
|
137
|
-
|
|
137
|
+
auth_req_uri, _, _ = client.prepare_authorization_request(
|
|
138
138
|
authorization_url=auth_url,
|
|
139
139
|
redirect_url=redirect_url,
|
|
140
140
|
scope=scope,
|
|
@@ -269,7 +269,7 @@ class OAuthManager:
|
|
|
269
269
|
auth_url = self.idp_endpoint.get_authorization_url(hostname)
|
|
270
270
|
|
|
271
271
|
state = OAuthManager.__token_urlsafe(16)
|
|
272
|
-
|
|
272
|
+
verifier, challenge = OAuthManager.__get_challenge()
|
|
273
273
|
client = oauthlib.oauth2.WebApplicationClient(self.client_id)
|
|
274
274
|
|
|
275
275
|
try:
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/retry.py
RENAMED
|
@@ -245,7 +245,7 @@ class DatabricksRetryPolicy(Retry):
|
|
|
245
245
|
return self._command_type
|
|
246
246
|
|
|
247
247
|
@command_type.setter
|
|
248
|
-
def command_type(self, value: CommandType) -> None:
|
|
248
|
+
def command_type(self, value: Optional[CommandType]) -> None:
|
|
249
249
|
self._command_type = value
|
|
250
250
|
|
|
251
251
|
@property
|
|
@@ -122,9 +122,11 @@ class THttpClient(thrift.transport.THttpClient.THttpClient):
|
|
|
122
122
|
pool_class = HTTPSConnectionPool
|
|
123
123
|
_pool_kwargs.update(
|
|
124
124
|
{
|
|
125
|
-
"cert_reqs":
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
"cert_reqs": (
|
|
126
|
+
ssl.CERT_REQUIRED
|
|
127
|
+
if self._ssl_options.tls_verify
|
|
128
|
+
else ssl.CERT_NONE
|
|
129
|
+
),
|
|
128
130
|
"ca_certs": self._ssl_options.tls_trusted_ca_file,
|
|
129
131
|
"cert_file": self._ssl_options.tls_client_cert_file,
|
|
130
132
|
"key_file": self._ssl_options.tls_client_cert_key_file,
|
|
@@ -111,9 +111,9 @@ class TokenFederationProvider(AuthProvider):
|
|
|
111
111
|
"""Add authentication headers to the request."""
|
|
112
112
|
|
|
113
113
|
if self._cached_token and not self._cached_token.is_expired():
|
|
114
|
-
request_headers[
|
|
115
|
-
"
|
|
116
|
-
|
|
114
|
+
request_headers["Authorization"] = (
|
|
115
|
+
f"{self._cached_token.token_type} {self._cached_token.access_token}"
|
|
116
|
+
)
|
|
117
117
|
return
|
|
118
118
|
|
|
119
119
|
# Get the external headers first to check if we need token federation
|
|
@@ -227,10 +227,8 @@ class ResultSetFilter:
|
|
|
227
227
|
if not case_sensitive:
|
|
228
228
|
allowed_values = [v.upper() for v in allowed_values]
|
|
229
229
|
# Helper lambda to get column value based on case sensitivity
|
|
230
|
-
get_column_value = (
|
|
231
|
-
|
|
232
|
-
if not case_sensitive
|
|
233
|
-
else row[column_index]
|
|
230
|
+
get_column_value = lambda row: (
|
|
231
|
+
row[column_index].upper() if not case_sensitive else row[column_index]
|
|
234
232
|
)
|
|
235
233
|
|
|
236
234
|
# Filter rows based on allowed values
|
|
@@ -161,9 +161,11 @@ class SeaHttpClient:
|
|
|
161
161
|
pool_class = HTTPSConnectionPool
|
|
162
162
|
pool_kwargs.update(
|
|
163
163
|
{
|
|
164
|
-
"cert_reqs":
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
"cert_reqs": (
|
|
165
|
+
ssl.CERT_REQUIRED
|
|
166
|
+
if self.ssl_options.tls_verify
|
|
167
|
+
else ssl.CERT_NONE
|
|
168
|
+
),
|
|
167
169
|
"ca_certs": self.ssl_options.tls_trusted_ca_file,
|
|
168
170
|
"cert_file": self.ssl_options.tls_client_cert_file,
|
|
169
171
|
"key_file": self.ssl_options.tls_client_cert_key_file,
|
|
@@ -12,7 +12,6 @@ from databricks.sql.common.unified_http_client import UnifiedHttpClient
|
|
|
12
12
|
from databricks.sql.result_set import ThriftResultSet
|
|
13
13
|
from databricks.sql.telemetry.models.event import StatementType
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
if TYPE_CHECKING:
|
|
17
16
|
from databricks.sql.client import Cursor
|
|
18
17
|
from databricks.sql.result_set import ResultSet
|
|
@@ -680,7 +679,10 @@ class ThriftDatabricksClient(DatabricksClient):
|
|
|
680
679
|
num_rows,
|
|
681
680
|
) = convert_column_based_set_to_arrow_table(t_row_set.columns, description)
|
|
682
681
|
elif t_row_set.arrowBatches is not None:
|
|
683
|
-
(
|
|
682
|
+
(
|
|
683
|
+
arrow_table,
|
|
684
|
+
num_rows,
|
|
685
|
+
) = convert_arrow_based_set_to_arrow_table(
|
|
684
686
|
t_row_set.arrowBatches, lz4_compressed, schema_bytes
|
|
685
687
|
)
|
|
686
688
|
else:
|
|
@@ -801,7 +803,9 @@ class ThriftDatabricksClient(DatabricksClient):
|
|
|
801
803
|
for col in t_table_schema.columns
|
|
802
804
|
]
|
|
803
805
|
|
|
804
|
-
def _results_message_to_execute_response(
|
|
806
|
+
def _results_message_to_execute_response(
|
|
807
|
+
self, resp, operation_state, num_modified_rows=None
|
|
808
|
+
):
|
|
805
809
|
if resp.directResults and resp.directResults.resultSetMetadata:
|
|
806
810
|
t_result_set_metadata_resp = resp.directResults.resultSetMetadata
|
|
807
811
|
else:
|
|
@@ -864,6 +868,7 @@ class ThriftDatabricksClient(DatabricksClient):
|
|
|
864
868
|
is_staging_operation=t_result_set_metadata_resp.isStagingOperation,
|
|
865
869
|
arrow_schema_bytes=schema_bytes,
|
|
866
870
|
result_format=t_result_set_metadata_resp.resultFormat,
|
|
871
|
+
num_modified_rows=num_modified_rows,
|
|
867
872
|
)
|
|
868
873
|
|
|
869
874
|
return execute_response, has_more_rows
|
|
@@ -945,6 +950,7 @@ class ThriftDatabricksClient(DatabricksClient):
|
|
|
945
950
|
self._check_command_not_in_error_or_closed_state(
|
|
946
951
|
op_handle, initial_operation_status_resp
|
|
947
952
|
)
|
|
953
|
+
final_status_resp = initial_operation_status_resp
|
|
948
954
|
operation_state = (
|
|
949
955
|
initial_operation_status_resp
|
|
950
956
|
and initial_operation_status_resp.operationState
|
|
@@ -956,7 +962,10 @@ class ThriftDatabricksClient(DatabricksClient):
|
|
|
956
962
|
poll_resp = self._poll_for_status(op_handle)
|
|
957
963
|
operation_state = poll_resp.operationState
|
|
958
964
|
self._check_command_not_in_error_or_closed_state(op_handle, poll_resp)
|
|
959
|
-
|
|
965
|
+
final_status_resp = poll_resp
|
|
966
|
+
# Return the terminal status response (not just the state) so callers
|
|
967
|
+
# can read ``numModifiedRows`` — the DML affected-row count — from it.
|
|
968
|
+
return operation_state, final_status_resp
|
|
960
969
|
|
|
961
970
|
def get_query_state(self, command_id: CommandId) -> CommandState:
|
|
962
971
|
thrift_handle = command_id.to_thrift_handle()
|
|
@@ -1046,11 +1055,13 @@ class ThriftDatabricksClient(DatabricksClient):
|
|
|
1046
1055
|
statement=operation,
|
|
1047
1056
|
runAsync=True,
|
|
1048
1057
|
# For async operation we don't want the direct results
|
|
1049
|
-
getDirectResults=
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1058
|
+
getDirectResults=(
|
|
1059
|
+
None
|
|
1060
|
+
if async_op
|
|
1061
|
+
else ttypes.TSparkGetDirectResults(
|
|
1062
|
+
maxRows=max_rows,
|
|
1063
|
+
maxBytes=max_bytes,
|
|
1064
|
+
)
|
|
1054
1065
|
),
|
|
1055
1066
|
canReadArrowResult=True if pyarrow else False,
|
|
1056
1067
|
canDecompressLZ4Result=lz4_compression,
|
|
@@ -1274,12 +1285,21 @@ class ThriftDatabricksClient(DatabricksClient):
|
|
|
1274
1285
|
cursor.active_command_id = command_id
|
|
1275
1286
|
self._check_direct_results_for_error(resp.directResults, self._host)
|
|
1276
1287
|
|
|
1277
|
-
final_operation_state = self._wait_until_command_done(
|
|
1288
|
+
final_operation_state, final_status_resp = self._wait_until_command_done(
|
|
1278
1289
|
resp.operationHandle,
|
|
1279
1290
|
resp.directResults and resp.directResults.operationStatus,
|
|
1280
1291
|
)
|
|
1281
1292
|
|
|
1282
|
-
|
|
1293
|
+
# ``numModifiedRows`` is populated by the server for DML statements
|
|
1294
|
+
# (INSERT/UPDATE/DELETE/MERGE) and is None for SELECT. Surface it so it
|
|
1295
|
+
# can flow to ``cursor.rowcount``.
|
|
1296
|
+
num_modified_rows = (
|
|
1297
|
+
final_status_resp.numModifiedRows if final_status_resp else None
|
|
1298
|
+
)
|
|
1299
|
+
|
|
1300
|
+
return self._results_message_to_execute_response(
|
|
1301
|
+
resp, final_operation_state, num_modified_rows
|
|
1302
|
+
)
|
|
1283
1303
|
|
|
1284
1304
|
def _handle_execute_response_async(self, resp, cursor):
|
|
1285
1305
|
command_id = CommandId.from_thrift_handle(resp.operationHandle)
|
|
@@ -425,3 +425,7 @@ class ExecuteResponse:
|
|
|
425
425
|
is_staging_operation: bool = False
|
|
426
426
|
arrow_schema_bytes: Optional[bytes] = None
|
|
427
427
|
result_format: Optional[Any] = None
|
|
428
|
+
# Number of rows modified by a DML statement (INSERT/UPDATE/DELETE/MERGE),
|
|
429
|
+
# surfaced as ``cursor.rowcount``. ``None`` for SELECT and any statement
|
|
430
|
+
# for which the server does not report a count → ``rowcount`` stays at -1.
|
|
431
|
+
num_modified_rows: Optional[int] = None
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/client.py
RENAMED
|
@@ -340,9 +340,9 @@ class Connection:
|
|
|
340
340
|
http_path=http_path,
|
|
341
341
|
port=kwargs.get("_port", 443),
|
|
342
342
|
client_context=client_context,
|
|
343
|
-
user_agent=
|
|
344
|
-
|
|
345
|
-
|
|
343
|
+
user_agent=(
|
|
344
|
+
self.session.useragent_header if hasattr(self, "session") else None
|
|
345
|
+
),
|
|
346
346
|
enable_telemetry=enable_telemetry,
|
|
347
347
|
)
|
|
348
348
|
raise e
|
|
@@ -390,9 +390,11 @@ class Connection:
|
|
|
390
390
|
|
|
391
391
|
driver_connection_params = DriverConnectionParameters(
|
|
392
392
|
http_path=http_path,
|
|
393
|
-
mode=
|
|
394
|
-
|
|
395
|
-
|
|
393
|
+
mode=(
|
|
394
|
+
DatabricksClientType.SEA
|
|
395
|
+
if self.session.use_sea
|
|
396
|
+
else DatabricksClientType.THRIFT
|
|
397
|
+
),
|
|
396
398
|
host_info=HostDetails(host_url=server_hostname, port=self.session.port),
|
|
397
399
|
auth_mech=TelemetryHelper.get_auth_mechanism(self.session.auth_provider),
|
|
398
400
|
auth_flow=TelemetryHelper.get_auth_flow(self.session.auth_provider),
|
|
@@ -874,7 +876,10 @@ class Cursor:
|
|
|
874
876
|
|
|
875
877
|
self.connection: Connection = connection
|
|
876
878
|
|
|
877
|
-
|
|
879
|
+
# -1 until a statement runs. Set to the affected-row count after a DML
|
|
880
|
+
# statement (INSERT/UPDATE/DELETE/MERGE); stays -1 for SELECT and any
|
|
881
|
+
# statement the server does not report a modified-row count for.
|
|
882
|
+
self.rowcount: int = -1
|
|
878
883
|
self.buffer_size_bytes: int = result_buffer_size_bytes
|
|
879
884
|
self.active_result_set: Union[ResultSet, None] = None
|
|
880
885
|
self.arraysize: int = arraysize
|
|
@@ -1039,6 +1044,9 @@ class Cursor:
|
|
|
1039
1044
|
self.active_result_set.close()
|
|
1040
1045
|
finally:
|
|
1041
1046
|
self.active_result_set = None
|
|
1047
|
+
# Reset rowcount to its -1 default so a prior DML's count never
|
|
1048
|
+
# leaks into a subsequent SELECT (or unreported) statement.
|
|
1049
|
+
self.rowcount = -1
|
|
1042
1050
|
|
|
1043
1051
|
def _check_not_closed(self):
|
|
1044
1052
|
if not self.open:
|
|
@@ -1081,6 +1089,14 @@ class Cursor:
|
|
|
1081
1089
|
headers=headers,
|
|
1082
1090
|
)
|
|
1083
1091
|
|
|
1092
|
+
# REMOVE deletes a remote resource and never touches the local
|
|
1093
|
+
# filesystem, so it does not require staging_allowed_local_path.
|
|
1094
|
+
if row.operation == "REMOVE":
|
|
1095
|
+
return self._handle_staging_remove(
|
|
1096
|
+
presigned_url=row.presignedUrl,
|
|
1097
|
+
headers=headers,
|
|
1098
|
+
)
|
|
1099
|
+
|
|
1084
1100
|
# For non-streaming operations, validate staging_allowed_local_path
|
|
1085
1101
|
if isinstance(staging_allowed_local_path, type(str())):
|
|
1086
1102
|
_staging_allowed_local_paths = [staging_allowed_local_path]
|
|
@@ -1133,14 +1149,12 @@ class Cursor:
|
|
|
1133
1149
|
)
|
|
1134
1150
|
|
|
1135
1151
|
# TODO: Create a retry loop here to re-attempt if the request times out or fails
|
|
1152
|
+
# REMOVE is handled above, before staging_allowed_local_path validation,
|
|
1153
|
+
# since it does not touch the local filesystem.
|
|
1136
1154
|
if row.operation == "GET":
|
|
1137
1155
|
return self._handle_staging_get(**handler_args)
|
|
1138
1156
|
elif row.operation == "PUT":
|
|
1139
1157
|
return self._handle_staging_put(**handler_args)
|
|
1140
|
-
elif row.operation == "REMOVE":
|
|
1141
|
-
# Local file isn't needed to remove a remote resource
|
|
1142
|
-
handler_args.pop("local_file")
|
|
1143
|
-
return self._handle_staging_remove(**handler_args)
|
|
1144
1158
|
else:
|
|
1145
1159
|
raise ProgrammingError(
|
|
1146
1160
|
f"Operation {row.operation} is not supported. "
|
|
@@ -1367,6 +1381,14 @@ class Cursor:
|
|
|
1367
1381
|
query_tags=query_tags,
|
|
1368
1382
|
)
|
|
1369
1383
|
|
|
1384
|
+
# Surface the affected-row count for DML (INSERT/UPDATE/DELETE/MERGE) as
|
|
1385
|
+
# cursor.rowcount instead of the hardcoded -1. num_modified_rows is None
|
|
1386
|
+
# for SELECT (and statements the server does not report a count for) →
|
|
1387
|
+
# leave rowcount at its -1 default.
|
|
1388
|
+
num_modified_rows = getattr(self.active_result_set, "num_modified_rows", None)
|
|
1389
|
+
if num_modified_rows is not None:
|
|
1390
|
+
self.rowcount = num_modified_rows
|
|
1391
|
+
|
|
1370
1392
|
if self.active_result_set and self.active_result_set.is_staging_operation:
|
|
1371
1393
|
self._handle_staging_operation(
|
|
1372
1394
|
staging_allowed_local_path=self.connection.staging_allowed_local_path,
|
|
@@ -1505,8 +1527,22 @@ class Cursor:
|
|
|
1505
1527
|
|
|
1506
1528
|
:returns self
|
|
1507
1529
|
"""
|
|
1530
|
+
# Per PEP 249, rowcount after executemany reflects the total rows
|
|
1531
|
+
# affected across all parameter sets (or -1 when undeterminable). Each
|
|
1532
|
+
# execute() resets self.rowcount and sets it from its own statement, so
|
|
1533
|
+
# we accumulate the reported counts here. If no statement reports a
|
|
1534
|
+
# count (e.g. all SELECT, or the server does not report one), rowcount
|
|
1535
|
+
# stays at its -1 default.
|
|
1536
|
+
total_rowcount = -1
|
|
1508
1537
|
for parameters in seq_of_parameters:
|
|
1509
1538
|
self.execute(operation, parameters, query_tags=query_tags)
|
|
1539
|
+
if self.rowcount >= 0:
|
|
1540
|
+
total_rowcount = (
|
|
1541
|
+
self.rowcount
|
|
1542
|
+
if total_rowcount < 0
|
|
1543
|
+
else total_rowcount + self.rowcount
|
|
1544
|
+
)
|
|
1545
|
+
self.rowcount = total_rowcount
|
|
1510
1546
|
return self
|
|
1511
1547
|
|
|
1512
1548
|
@log_latency(StatementType.METADATA)
|
|
@@ -148,11 +148,13 @@ class UnifiedHttpClient:
|
|
|
148
148
|
"num_pools": self.config.pool_connections,
|
|
149
149
|
"maxsize": self.config.pool_maxsize,
|
|
150
150
|
"retries": self._retry_policy,
|
|
151
|
-
"timeout":
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
"timeout": (
|
|
152
|
+
urllib3.Timeout(
|
|
153
|
+
connect=self.config.socket_timeout, read=self.config.socket_timeout
|
|
154
|
+
)
|
|
155
|
+
if self.config.socket_timeout
|
|
156
|
+
else None
|
|
157
|
+
),
|
|
156
158
|
"ssl_context": ssl_context,
|
|
157
159
|
}
|
|
158
160
|
|
|
@@ -3,6 +3,7 @@ import logging
|
|
|
3
3
|
|
|
4
4
|
logger = logging.getLogger(__name__)
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
### PEP-249 Mandated ###
|
|
7
8
|
# https://peps.python.org/pep-0249/#exceptions
|
|
8
9
|
class Error(Exception):
|
|
@@ -155,7 +156,8 @@ class CursorAlreadyClosedError(RequestError):
|
|
|
155
156
|
|
|
156
157
|
class TelemetryRateLimitError(Exception):
|
|
157
158
|
"""Raised when telemetry endpoint returns 429 or 503, indicating rate limiting or service unavailable.
|
|
158
|
-
This exception is used exclusively by the circuit breaker to track telemetry rate limiting events.
|
|
159
|
+
This exception is used exclusively by the circuit breaker to track telemetry rate limiting events.
|
|
160
|
+
"""
|
|
159
161
|
|
|
160
162
|
|
|
161
163
|
class TelemetryNonRateLimitError(Exception):
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/result_set.py
RENAMED
|
@@ -50,6 +50,7 @@ class ResultSet(ABC):
|
|
|
50
50
|
is_staging_operation: bool = False,
|
|
51
51
|
lz4_compressed: bool = False,
|
|
52
52
|
arrow_schema_bytes: Optional[bytes] = None,
|
|
53
|
+
num_modified_rows: Optional[int] = None,
|
|
53
54
|
):
|
|
54
55
|
"""
|
|
55
56
|
A ResultSet manages the results of a single command.
|
|
@@ -82,6 +83,8 @@ class ResultSet(ABC):
|
|
|
82
83
|
self._is_staging_operation = is_staging_operation
|
|
83
84
|
self.lz4_compressed = lz4_compressed
|
|
84
85
|
self._arrow_schema_bytes = arrow_schema_bytes
|
|
86
|
+
# Affected-row count for DML; None for SELECT / unreported.
|
|
87
|
+
self.num_modified_rows = num_modified_rows
|
|
85
88
|
|
|
86
89
|
def __iter__(self):
|
|
87
90
|
while True:
|
|
@@ -264,6 +267,7 @@ class ThriftResultSet(ResultSet):
|
|
|
264
267
|
is_staging_operation=execute_response.is_staging_operation,
|
|
265
268
|
lz4_compressed=execute_response.lz4_compressed,
|
|
266
269
|
arrow_schema_bytes=execute_response.arrow_schema_bytes,
|
|
270
|
+
num_modified_rows=execute_response.num_modified_rows,
|
|
267
271
|
)
|
|
268
272
|
|
|
269
273
|
# Initialize results queue if not provided
|
|
@@ -490,9 +490,9 @@ class TelemetryClientFactory:
|
|
|
490
490
|
on the telemetry endpoint.
|
|
491
491
|
"""
|
|
492
492
|
|
|
493
|
-
_clients: Dict[
|
|
494
|
-
|
|
495
|
-
|
|
493
|
+
_clients: Dict[str, _TelemetryClientHolder] = (
|
|
494
|
+
{}
|
|
495
|
+
) # Map of host_url -> TelemetryClientHolder
|
|
496
496
|
_executor: Optional[ThreadPoolExecutor] = None
|
|
497
497
|
_initialized: bool = False
|
|
498
498
|
_lock = threading.RLock() # Thread safety for factory operations
|
|
@@ -639,13 +639,13 @@ class TelemetryClientFactory:
|
|
|
639
639
|
client_context=client_context,
|
|
640
640
|
extra_headers=extra_headers,
|
|
641
641
|
)
|
|
642
|
-
TelemetryClientFactory._clients[
|
|
643
|
-
|
|
644
|
-
|
|
642
|
+
TelemetryClientFactory._clients[host_url] = (
|
|
643
|
+
_TelemetryClientHolder(client)
|
|
644
|
+
)
|
|
645
645
|
else:
|
|
646
|
-
TelemetryClientFactory._clients[
|
|
647
|
-
|
|
648
|
-
|
|
646
|
+
TelemetryClientFactory._clients[host_url] = (
|
|
647
|
+
_TelemetryClientHolder(NoopTelemetryClient())
|
|
648
|
+
)
|
|
649
649
|
except Exception as e:
|
|
650
650
|
logger.debug("Failed to initialize telemetry client: %s", e)
|
|
651
651
|
# Fallback to NoopTelemetryClient to ensure connection doesn't fail
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/utils.py
RENAMED
|
@@ -876,7 +876,7 @@ def _create_python_tuple(t_col_value_wrapper):
|
|
|
876
876
|
|
|
877
877
|
|
|
878
878
|
def concat_table_chunks(
|
|
879
|
-
table_chunks: List[Union["pyarrow.Table", ColumnTable]]
|
|
879
|
+
table_chunks: List[Union["pyarrow.Table", ColumnTable]],
|
|
880
880
|
) -> Union["pyarrow.Table", ColumnTable]:
|
|
881
881
|
if len(table_chunks) == 0:
|
|
882
882
|
return table_chunks
|
|
@@ -894,11 +894,27 @@ def concat_table_chunks(
|
|
|
894
894
|
result_table[j].extend(table_chunks[i].column_table[j])
|
|
895
895
|
return ColumnTable(result_table, table_chunks[0].column_names)
|
|
896
896
|
else:
|
|
897
|
+
return _concat_arrow_tables(table_chunks)
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
def _concat_arrow_tables(table_chunks: List["pyarrow.Table"]) -> "pyarrow.Table":
|
|
901
|
+
"""Concatenate Arrow tables, tolerant of the installed pyarrow version.
|
|
902
|
+
|
|
903
|
+
``promote_options`` was added in pyarrow 14.0.0, replacing the older
|
|
904
|
+
``promote`` boolean. The connector's declared floor is pyarrow>=14, but a
|
|
905
|
+
base install (no ``[pyarrow]`` extra) can run against a runtime's older
|
|
906
|
+
bundled pyarrow -- e.g. DBR 13.3 (12.x) / 14.3 -- where ``promote_options``
|
|
907
|
+
raises ``TypeError: unexpected keyword argument``. Fall back to the legacy
|
|
908
|
+
``promote=True`` there (equivalent to ``promote_options="default"``).
|
|
909
|
+
"""
|
|
910
|
+
try:
|
|
897
911
|
return pyarrow.concat_tables(table_chunks, promote_options="default")
|
|
912
|
+
except TypeError:
|
|
913
|
+
return pyarrow.concat_tables(table_chunks, promote=True)
|
|
898
914
|
|
|
899
915
|
|
|
900
916
|
def serialize_query_tags(
|
|
901
|
-
query_tags: Optional[Dict[str, Optional[str]]]
|
|
917
|
+
query_tags: Optional[Dict[str, Optional[str]]],
|
|
902
918
|
) -> Optional[str]:
|
|
903
919
|
"""
|
|
904
920
|
Serialize query_tags dictionary to a string format.
|
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/auth/common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/agent.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/common/http.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/py.typed
RENAMED
|
File without changes
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/session.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-4.3.0 → databricks_sql_connector-4.4.0}/src/databricks/sql/types.py
RENAMED
|
File without changes
|