databricks-sql-connector 3.0.3b1__tar.gz → 3.1.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-3.0.3b1 → databricks_sql_connector-3.1.0}/CHANGELOG.md +30 -8
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/PKG-INFO +2 -1
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/pyproject.toml +2 -1
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/__init__.py +34 -1
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/auth.py +10 -7
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/authenticators.py +4 -6
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/endpoint.py +30 -7
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/retry.py +27 -41
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/client.py +11 -11
- databricks_sql_connector-3.1.0/src/databricks/sqlalchemy/test_local/conftest.py +44 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test_local/e2e/test_basic.py +26 -27
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test_local/test_parsing.py +4 -4
- databricks_sql_connector-3.0.3b1/src/databricks/sqlalchemy/_pytest.ini +0 -3
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/LICENSE +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/README.md +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/oauth.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/thrift_http_client.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/cloudfetch/download_manager.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/cloudfetch/downloader.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/exc.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/experimental/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/parameters/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/parameters/native.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/parameters/py.typed +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/thrift_api/TCLIService/ttypes.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/thrift_api/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/thrift_backend.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/types.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/utils.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/README.sqlalchemy.md +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/README.tests.md +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/_ddl.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/_parse.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/_types.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/base.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/requirements.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/setup.cfg +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/_extra.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/_future.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/_regression.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/_unsupported.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/conftest.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/overrides/_componentreflectiontest.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/overrides/_ctetest.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test/test_suite.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test_local/__init__.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test_local/e2e/MOCK_DATA.xlsx +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test_local/test_ddl.py +0 -0
- {databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sqlalchemy/test_local/test_types.py +0 -0
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
-
# 3.0
|
|
3
|
+
# 3.1.0 (2024-02-16)
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- Revert retry-after behavior to be exponential backoff (#349)
|
|
6
|
+
- Support Databricks OAuth on Azure (#351)
|
|
7
|
+
- Support Databricks OAuth on GCP (#338)
|
|
8
|
+
|
|
9
|
+
# 3.0.3 (2024-02-02)
|
|
10
|
+
|
|
11
|
+
- Revised docstrings and examples for OAuth (#339)
|
|
12
|
+
- Redact the URL query parameters from the urllib3.connectionpool logs (#341)
|
|
6
13
|
|
|
7
14
|
# 3.0.2 (2024-01-25)
|
|
8
15
|
|
|
@@ -48,7 +55,7 @@
|
|
|
48
55
|
|
|
49
56
|
## 2.9.2 (2023-08-17)
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
**Note: this release was yanked from Pypi on 13 September 2023 due to compatibility issues with environments where `urllib3<=2.0.0` were installed. The log changes are incorporated into version 2.9.3 and greater.**
|
|
52
59
|
|
|
53
60
|
- Other: Add `examples/v3_retries_query_execute.py` (#199)
|
|
54
61
|
- Other: suppress log message when `_enable_v3_retries` is not `True` (#199)
|
|
@@ -56,7 +63,7 @@ __Note: this release was yanked from Pypi on 13 September 2023 due to compatibil
|
|
|
56
63
|
|
|
57
64
|
## 2.9.1 (2023-08-11)
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
**Note: this release was yanked from Pypi on 13 September 2023 due to compatibility issues with environments where `urllib3<=2.0.0` were installed.**
|
|
60
67
|
|
|
61
68
|
- Other: Explicitly pin urllib3 to ^2.0.0 (#191)
|
|
62
69
|
|
|
@@ -109,6 +116,7 @@ __Note: this release was yanked from Pypi on 13 September 2023 due to compatibil
|
|
|
109
116
|
- Other: Relax sqlalchemy required version as it was unecessarily strict.
|
|
110
117
|
|
|
111
118
|
## 2.5.0 (2023-04-14)
|
|
119
|
+
|
|
112
120
|
- Add support for External Auth providers
|
|
113
121
|
- Fix: Python HTTP proxies were broken
|
|
114
122
|
- Other: All Thrift requests that timeout during connection will be automatically retried
|
|
@@ -130,8 +138,8 @@ __Note: this release was yanked from Pypi on 13 September 2023 due to compatibil
|
|
|
130
138
|
|
|
131
139
|
## 2.2.2 (2023-01-03)
|
|
132
140
|
|
|
133
|
-
- Support custom oauth client id and redirect port
|
|
134
|
-
- Fix: Add none check on _oauth_persistence in DatabricksOAuthProvider
|
|
141
|
+
- Support custom oauth client id and redirect port
|
|
142
|
+
- Fix: Add none check on \_oauth_persistence in DatabricksOAuthProvider
|
|
135
143
|
|
|
136
144
|
## 2.2.1 (2022-11-29)
|
|
137
145
|
|
|
@@ -163,57 +171,71 @@ Huge thanks to @dbaxa for contributing this change!
|
|
|
163
171
|
|
|
164
172
|
- Add retry logic for `GetOperationStatus` requests that fail with an `OSError`
|
|
165
173
|
- Reorganised code to use Poetry for dependency management.
|
|
174
|
+
|
|
166
175
|
## 2.0.2 (2022-05-04)
|
|
176
|
+
|
|
167
177
|
- Better exception handling in automatic connection close
|
|
168
178
|
|
|
169
179
|
## 2.0.1 (2022-04-21)
|
|
180
|
+
|
|
170
181
|
- Fixed Pandas dependency in setup.cfg to be >= 1.2.0
|
|
171
182
|
|
|
172
183
|
## 2.0.0 (2022-04-19)
|
|
184
|
+
|
|
173
185
|
- Initial stable release of V2
|
|
174
|
-
- Added better support for complex types, so that in Databricks runtime 10.3+, Arrays, Maps and Structs will get
|
|
186
|
+
- Added better support for complex types, so that in Databricks runtime 10.3+, Arrays, Maps and Structs will get
|
|
175
187
|
deserialized as lists, lists of tuples and dicts, respectively.
|
|
176
188
|
- Changed the name of the metadata arg to http_headers
|
|
177
189
|
|
|
178
190
|
## 2.0.b2 (2022-04-04)
|
|
191
|
+
|
|
179
192
|
- Change import of collections.Iterable to collections.abc.Iterable to make the library compatible with Python 3.10
|
|
180
193
|
- Fixed bug with .tables method so that .tables works as expected with Unity-Catalog enabled endpoints
|
|
181
194
|
|
|
182
195
|
## 2.0.0b1 (2022-03-04)
|
|
196
|
+
|
|
183
197
|
- Fix packaging issue (dependencies were not being installed properly)
|
|
184
198
|
- Fetching timestamp results will now return aware instead of naive timestamps
|
|
185
199
|
- The client will now default to using simplified error messages
|
|
186
200
|
|
|
187
201
|
## 2.0.0b (2022-02-08)
|
|
202
|
+
|
|
188
203
|
- Initial beta release of V2. V2 is an internal re-write of large parts of the connector to use Databricks edge features. All public APIs from V1 remain.
|
|
189
|
-
- Added Unity Catalog support (pass catalog and / or
|
|
204
|
+
- Added Unity Catalog support (pass catalog and / or schema key word args to the .connect method to select initial schema and catalog)
|
|
190
205
|
|
|
191
206
|
---
|
|
192
207
|
|
|
193
208
|
**Note**: The code for versions prior to `v2.0.0b` is not contained in this repository. The below entries are included for reference only.
|
|
194
209
|
|
|
195
210
|
---
|
|
211
|
+
|
|
196
212
|
## 1.0.0 (2022-01-20)
|
|
213
|
+
|
|
197
214
|
- Add operations for retrieving metadata
|
|
198
215
|
- Add the ability to access columns by name on result rows
|
|
199
216
|
- Add the ability to provide configuration settings on connect
|
|
200
217
|
|
|
201
218
|
## 0.9.4 (2022-01-10)
|
|
219
|
+
|
|
202
220
|
- Improved logging and error messages.
|
|
203
221
|
|
|
204
222
|
## 0.9.3 (2021-12-08)
|
|
223
|
+
|
|
205
224
|
- Add retries for 429 and 503 HTTP responses.
|
|
206
225
|
|
|
207
226
|
## 0.9.2 (2021-12-02)
|
|
227
|
+
|
|
208
228
|
- (Bug fix) Increased Thrift requirement from 0.10.0 to 0.13.0 as 0.10.0 was in fact incompatible
|
|
209
229
|
- (Bug fix) Fixed error message after query execution failed -SQLSTATE and Error message were misplaced
|
|
210
230
|
|
|
211
231
|
## 0.9.1 (2021-09-01)
|
|
232
|
+
|
|
212
233
|
- Public Preview release, Experimental tag removed
|
|
213
234
|
- minor updates in internal build/packaging
|
|
214
235
|
- no functional changes
|
|
215
236
|
|
|
216
237
|
## 0.9.0 (2021-08-04)
|
|
238
|
+
|
|
217
239
|
- initial (Experimental) release of pyhive-forked connector
|
|
218
240
|
- Python DBAPI 2.0 (PEP-0249), thrift based
|
|
219
241
|
- see docs for more info: https://docs.databricks.com/dev-tools/python-sql-connector.html
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: databricks-sql-connector
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Databricks SQL Connector for Python
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: Databricks
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
16
|
Provides-Extra: alembic
|
|
16
17
|
Provides-Extra: sqlalchemy
|
|
17
18
|
Requires-Dist: alembic (>=1.0.11,<2.0.0) ; extra == "alembic"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "databricks-sql-connector"
|
|
3
|
-
version = "3.0
|
|
3
|
+
version = "3.1.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"
|
|
@@ -58,6 +58,7 @@ exclude = ['ttypes\.py$', 'TCLIService\.py$']
|
|
|
58
58
|
exclude = '/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist|thrift_api)/'
|
|
59
59
|
|
|
60
60
|
[tool.pytest.ini_options]
|
|
61
|
+
markers = {"reviewed" = "Test case has been reviewed by Databricks"}
|
|
61
62
|
minversion = "6.0"
|
|
62
63
|
log_cli = "false"
|
|
63
64
|
log_cli_level = "INFO"
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/__init__.py
RENAMED
|
@@ -8,6 +8,39 @@ threadsafety = 1 # Threads may share the module, but not connections.
|
|
|
8
8
|
|
|
9
9
|
paramstyle = "named"
|
|
10
10
|
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RedactUrlQueryParamsFilter(logging.Filter):
|
|
15
|
+
pattern = re.compile(r"(\?|&)([\w-]+)=([^&]+)")
|
|
16
|
+
mask = r"\1\2=<REDACTED>"
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
def redact(self, string):
|
|
22
|
+
return re.sub(self.pattern, self.mask, str(string))
|
|
23
|
+
|
|
24
|
+
def filter(self, record):
|
|
25
|
+
record.msg = self.redact(str(record.msg))
|
|
26
|
+
if isinstance(record.args, dict):
|
|
27
|
+
for k in record.args.keys():
|
|
28
|
+
record.args[k] = (
|
|
29
|
+
self.redact(record.args[k])
|
|
30
|
+
if isinstance(record.arg[k], str)
|
|
31
|
+
else record.args[k]
|
|
32
|
+
)
|
|
33
|
+
else:
|
|
34
|
+
record.args = tuple(
|
|
35
|
+
(self.redact(arg) if isinstance(arg, str) else arg)
|
|
36
|
+
for arg in record.args
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
logging.getLogger("urllib3.connectionpool").addFilter(RedactUrlQueryParamsFilter())
|
|
43
|
+
|
|
11
44
|
|
|
12
45
|
class DBAPITypeObject(object):
|
|
13
46
|
def __init__(self, *values):
|
|
@@ -29,7 +62,7 @@ DATETIME = DBAPITypeObject("timestamp")
|
|
|
29
62
|
DATE = DBAPITypeObject("date")
|
|
30
63
|
ROWID = DBAPITypeObject()
|
|
31
64
|
|
|
32
|
-
__version__ = "3.0
|
|
65
|
+
__version__ = "3.1.0"
|
|
33
66
|
USER_AGENT_NAME = "PyDatabricksSqlConnector"
|
|
34
67
|
|
|
35
68
|
# These two functions are pyhive legacy
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/auth.py
RENAMED
|
@@ -8,12 +8,11 @@ from databricks.sql.auth.authenticators import (
|
|
|
8
8
|
ExternalAuthProvider,
|
|
9
9
|
DatabricksOAuthProvider,
|
|
10
10
|
)
|
|
11
|
-
from databricks.sql.auth.endpoint import infer_cloud_from_host, CloudType
|
|
12
|
-
from databricks.sql.experimental.oauth_persistence import OAuthPersistence
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
class AuthType(Enum):
|
|
16
14
|
DATABRICKS_OAUTH = "databricks-oauth"
|
|
15
|
+
AZURE_OAUTH = "azure-oauth"
|
|
17
16
|
# other supported types (access_token, user/pass) can be inferred
|
|
18
17
|
# we can add more types as needed later
|
|
19
18
|
|
|
@@ -51,7 +50,7 @@ class ClientContext:
|
|
|
51
50
|
def get_auth_provider(cfg: ClientContext):
|
|
52
51
|
if cfg.credentials_provider:
|
|
53
52
|
return ExternalAuthProvider(cfg.credentials_provider)
|
|
54
|
-
if cfg.auth_type
|
|
53
|
+
if cfg.auth_type in [AuthType.DATABRICKS_OAUTH.value, AuthType.AZURE_OAUTH.value]:
|
|
55
54
|
assert cfg.oauth_redirect_port_range is not None
|
|
56
55
|
assert cfg.oauth_client_id is not None
|
|
57
56
|
assert cfg.oauth_scopes is not None
|
|
@@ -62,6 +61,7 @@ def get_auth_provider(cfg: ClientContext):
|
|
|
62
61
|
cfg.oauth_redirect_port_range,
|
|
63
62
|
cfg.oauth_client_id,
|
|
64
63
|
cfg.oauth_scopes,
|
|
64
|
+
cfg.auth_type,
|
|
65
65
|
)
|
|
66
66
|
elif cfg.access_token is not None:
|
|
67
67
|
return AccessTokenAuthProvider(cfg.access_token)
|
|
@@ -87,19 +87,22 @@ def normalize_host_name(hostname: str):
|
|
|
87
87
|
return f"{maybe_scheme}{hostname}{maybe_trailing_slash}"
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def get_client_id_and_redirect_port(
|
|
90
|
+
def get_client_id_and_redirect_port(use_azure_auth: bool):
|
|
91
91
|
return (
|
|
92
92
|
(PYSQL_OAUTH_CLIENT_ID, PYSQL_OAUTH_REDIRECT_PORT_RANGE)
|
|
93
|
-
if
|
|
93
|
+
if not use_azure_auth
|
|
94
94
|
else (PYSQL_OAUTH_AZURE_CLIENT_ID, PYSQL_OAUTH_AZURE_REDIRECT_PORT_RANGE)
|
|
95
95
|
)
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def get_python_sql_connector_auth_provider(hostname: str, **kwargs):
|
|
99
|
-
|
|
99
|
+
auth_type = kwargs.get("auth_type")
|
|
100
|
+
(client_id, redirect_port_range) = get_client_id_and_redirect_port(
|
|
101
|
+
auth_type == AuthType.AZURE_OAUTH.value
|
|
102
|
+
)
|
|
100
103
|
cfg = ClientContext(
|
|
101
104
|
hostname=normalize_host_name(hostname),
|
|
102
|
-
auth_type=
|
|
105
|
+
auth_type=auth_type,
|
|
103
106
|
access_token=kwargs.get("access_token"),
|
|
104
107
|
username=kwargs.get("_username"),
|
|
105
108
|
password=kwargs.get("_password"),
|
|
@@ -18,6 +18,7 @@ class AuthProvider:
|
|
|
18
18
|
|
|
19
19
|
HeaderFactory = Callable[[], Dict[str, str]]
|
|
20
20
|
|
|
21
|
+
|
|
21
22
|
# In order to keep compatibility with SDK
|
|
22
23
|
class CredentialsProvider(abc.ABC):
|
|
23
24
|
"""CredentialsProvider is the protocol (call-side interface)
|
|
@@ -69,16 +70,13 @@ class DatabricksOAuthProvider(AuthProvider):
|
|
|
69
70
|
redirect_port_range: List[int],
|
|
70
71
|
client_id: str,
|
|
71
72
|
scopes: List[str],
|
|
73
|
+
auth_type: str = "databricks-oauth",
|
|
72
74
|
):
|
|
73
75
|
try:
|
|
74
|
-
|
|
75
|
-
if not cloud_type:
|
|
76
|
-
raise NotImplementedError("Cannot infer the cloud type from hostname")
|
|
77
|
-
|
|
78
|
-
idp_endpoint = get_oauth_endpoints(cloud_type)
|
|
76
|
+
idp_endpoint = get_oauth_endpoints(hostname, auth_type == "azure-oauth")
|
|
79
77
|
if not idp_endpoint:
|
|
80
78
|
raise NotImplementedError(
|
|
81
|
-
f"OAuth is not supported for
|
|
79
|
+
f"OAuth is not supported for host ${hostname}"
|
|
82
80
|
)
|
|
83
81
|
|
|
84
82
|
# Convert to the corresponding scopes in the corresponding IdP
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#
|
|
2
2
|
# It implements all the cloud specific OAuth configuration/metadata
|
|
3
3
|
#
|
|
4
|
-
# Azure:
|
|
4
|
+
# Azure: It uses Databricks internal IdP or Azure AD
|
|
5
5
|
# AWS: It uses Databricks internal IdP
|
|
6
|
-
# GCP:
|
|
6
|
+
# GCP: It uses Databricks internal IdP
|
|
7
7
|
#
|
|
8
8
|
from abc import ABC, abstractmethod
|
|
9
9
|
from enum import Enum
|
|
@@ -21,6 +21,7 @@ class OAuthScope:
|
|
|
21
21
|
class CloudType(Enum):
|
|
22
22
|
AWS = "aws"
|
|
23
23
|
AZURE = "azure"
|
|
24
|
+
GCP = "gcp"
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
DATABRICKS_AWS_DOMAINS = [
|
|
@@ -34,6 +35,10 @@ DATABRICKS_AZURE_DOMAINS = [
|
|
|
34
35
|
".databricks.azure.cn",
|
|
35
36
|
".databricks.azure.us",
|
|
36
37
|
]
|
|
38
|
+
DATABRICKS_GCP_DOMAINS = [".gcp.databricks.com"]
|
|
39
|
+
|
|
40
|
+
# Domain supported by Databricks InHouse OAuth
|
|
41
|
+
DATABRICKS_OAUTH_AZURE_DOMAINS = [".azuredatabricks.net"]
|
|
37
42
|
|
|
38
43
|
|
|
39
44
|
# Infer cloud type from Databricks SQL instance hostname
|
|
@@ -45,10 +50,20 @@ def infer_cloud_from_host(hostname: str) -> Optional[CloudType]:
|
|
|
45
50
|
return CloudType.AZURE
|
|
46
51
|
elif any(e for e in DATABRICKS_AWS_DOMAINS if host.endswith(e)):
|
|
47
52
|
return CloudType.AWS
|
|
53
|
+
elif any(e for e in DATABRICKS_GCP_DOMAINS if host.endswith(e)):
|
|
54
|
+
return CloudType.GCP
|
|
48
55
|
else:
|
|
49
56
|
return None
|
|
50
57
|
|
|
51
58
|
|
|
59
|
+
def is_supported_databricks_oauth_host(hostname: str) -> bool:
|
|
60
|
+
host = hostname.lower().replace("https://", "").split("/")[0]
|
|
61
|
+
domains = (
|
|
62
|
+
DATABRICKS_AWS_DOMAINS + DATABRICKS_GCP_DOMAINS + DATABRICKS_OAUTH_AZURE_DOMAINS
|
|
63
|
+
)
|
|
64
|
+
return any(e for e in domains if host.endswith(e))
|
|
65
|
+
|
|
66
|
+
|
|
52
67
|
def get_databricks_oidc_url(hostname: str):
|
|
53
68
|
maybe_scheme = "https://" if not hostname.startswith("https://") else ""
|
|
54
69
|
maybe_trailing_slash = "/" if not hostname.endswith("/") else ""
|
|
@@ -94,7 +109,7 @@ class AzureOAuthEndpointCollection(OAuthEndpointCollection):
|
|
|
94
109
|
return "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration"
|
|
95
110
|
|
|
96
111
|
|
|
97
|
-
class
|
|
112
|
+
class InHouseOAuthEndpointCollection(OAuthEndpointCollection):
|
|
98
113
|
def get_scopes_mapping(self, scopes: List[str]) -> List[str]:
|
|
99
114
|
# No scope mapping in AWS
|
|
100
115
|
return scopes.copy()
|
|
@@ -108,10 +123,18 @@ class AwsOAuthEndpointCollection(OAuthEndpointCollection):
|
|
|
108
123
|
return f"{idp_url}/.well-known/oauth-authorization-server"
|
|
109
124
|
|
|
110
125
|
|
|
111
|
-
def get_oauth_endpoints(
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
def get_oauth_endpoints(
|
|
127
|
+
hostname: str, use_azure_auth: bool
|
|
128
|
+
) -> Optional[OAuthEndpointCollection]:
|
|
129
|
+
cloud = infer_cloud_from_host(hostname)
|
|
130
|
+
|
|
131
|
+
if cloud in [CloudType.AWS, CloudType.GCP]:
|
|
132
|
+
return InHouseOAuthEndpointCollection()
|
|
114
133
|
elif cloud == CloudType.AZURE:
|
|
115
|
-
return
|
|
134
|
+
return (
|
|
135
|
+
InHouseOAuthEndpointCollection()
|
|
136
|
+
if is_supported_databricks_oauth_host(hostname) and not use_azure_auth
|
|
137
|
+
else AzureOAuthEndpointCollection()
|
|
138
|
+
)
|
|
116
139
|
else:
|
|
117
140
|
return None
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/retry.py
RENAMED
|
@@ -118,13 +118,13 @@ class DatabricksRetryPolicy(Retry):
|
|
|
118
118
|
_total: int = urllib3_kwargs.pop("total")
|
|
119
119
|
_attempts_remaining = _total
|
|
120
120
|
|
|
121
|
-
_urllib_kwargs_we_care_about =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
_urllib_kwargs_we_care_about = dict(
|
|
122
|
+
total=_attempts_remaining,
|
|
123
|
+
respect_retry_after_header=True,
|
|
124
|
+
backoff_factor=self.delay_min,
|
|
125
|
+
allowed_methods=["POST"],
|
|
126
|
+
status_forcelist=[429, 503, *self.force_dangerous_codes],
|
|
127
|
+
)
|
|
128
128
|
|
|
129
129
|
urllib3_kwargs.update(**_urllib_kwargs_we_care_about)
|
|
130
130
|
|
|
@@ -132,22 +132,6 @@ class DatabricksRetryPolicy(Retry):
|
|
|
132
132
|
**urllib3_kwargs, # type: ignore
|
|
133
133
|
)
|
|
134
134
|
|
|
135
|
-
@property
|
|
136
|
-
def _allowed_methods_alias(self) -> str:
|
|
137
|
-
"""Returns `allowed_methods` if installed urllib3 is >=1.26.0
|
|
138
|
-
Returns `method_whitelist` otherwise."""
|
|
139
|
-
|
|
140
|
-
if not hasattr(self, "__allowed_methods_alias"):
|
|
141
|
-
import urllib3
|
|
142
|
-
from distutils.version import LooseVersion
|
|
143
|
-
|
|
144
|
-
if LooseVersion(urllib3.__version__) >= LooseVersion("1.26"):
|
|
145
|
-
self.__allowed_methods_alias = "allowed_methods"
|
|
146
|
-
else:
|
|
147
|
-
self.__allowed_methods_alias = "method_whitelist"
|
|
148
|
-
|
|
149
|
-
return self.__allowed_methods_alias
|
|
150
|
-
|
|
151
135
|
@classmethod
|
|
152
136
|
def __private_init__(
|
|
153
137
|
cls, retry_start_time: float, command_type: Optional[CommandType], **init_kwargs
|
|
@@ -217,22 +201,22 @@ class DatabricksRetryPolicy(Retry):
|
|
|
217
201
|
# Gather urllib3's current retry state _before_ increment was called
|
|
218
202
|
# These arguments match the function signature for Retry.__init__
|
|
219
203
|
# Note: if we update urllib3 we may need to add/remove arguments from this dict
|
|
220
|
-
urllib3_init_params =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
self.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
204
|
+
urllib3_init_params = dict(
|
|
205
|
+
total=self.total,
|
|
206
|
+
connect=self.connect,
|
|
207
|
+
read=self.read,
|
|
208
|
+
redirect=self.redirect,
|
|
209
|
+
status=self.status,
|
|
210
|
+
other=self.other,
|
|
211
|
+
allowed_methods=self.allowed_methods,
|
|
212
|
+
status_forcelist=self.status_forcelist,
|
|
213
|
+
backoff_factor=self.backoff_factor, # type: ignore
|
|
214
|
+
raise_on_redirect=self.raise_on_redirect,
|
|
215
|
+
raise_on_status=self.raise_on_status,
|
|
216
|
+
history=self.history,
|
|
217
|
+
remove_headers_on_redirect=self.remove_headers_on_redirect,
|
|
218
|
+
respect_retry_after_header=self.respect_retry_after_header,
|
|
219
|
+
)
|
|
236
220
|
|
|
237
221
|
# Update urllib3's current state to reflect the incremented counters
|
|
238
222
|
urllib3_init_params.update(**urllib3_incremented_counters)
|
|
@@ -299,8 +283,10 @@ class DatabricksRetryPolicy(Retry):
|
|
|
299
283
|
"""
|
|
300
284
|
retry_after = self.get_retry_after(response)
|
|
301
285
|
if retry_after:
|
|
302
|
-
self.
|
|
303
|
-
|
|
286
|
+
backoff = self.get_backoff_time()
|
|
287
|
+
proposed_wait = max(backoff, retry_after)
|
|
288
|
+
self.check_proposed_wait(proposed_wait)
|
|
289
|
+
time.sleep(proposed_wait)
|
|
304
290
|
return True
|
|
305
291
|
|
|
306
292
|
return False
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/client.py
RENAMED
|
@@ -96,11 +96,8 @@ class Connection:
|
|
|
96
96
|
legacy purposes and will be deprecated in a future release. When this parameter is `True` you will see
|
|
97
97
|
a warning log message. To suppress this log message, set `use_inline_params="silent"`.
|
|
98
98
|
auth_type: `str`, optional
|
|
99
|
-
`databricks-oauth` : to use
|
|
100
|
-
|
|
101
|
-
This supports User to Machine OAuth authentication for Databricks on AWS with
|
|
102
|
-
any IDP configured. This is only for interactive python applications and open a browser window.
|
|
103
|
-
Note this is beta (private preview)
|
|
99
|
+
`databricks-oauth` : to use Databricks OAuth with fine-grained permission scopes, set to `databricks-oauth`.
|
|
100
|
+
`azure-oauth` : to use Microsoft Entra ID OAuth flow, set to `azure-oauth`.
|
|
104
101
|
|
|
105
102
|
oauth_client_id: `str`, optional
|
|
106
103
|
custom oauth client_id. If not specified, it will use the built-in client_id of databricks-sql-python.
|
|
@@ -111,9 +108,9 @@ class Connection:
|
|
|
111
108
|
|
|
112
109
|
experimental_oauth_persistence: configures preferred storage for persisting oauth tokens.
|
|
113
110
|
This has to be a class implementing `OAuthPersistence`.
|
|
114
|
-
When `auth_type` is set to `databricks-oauth` without persisting the oauth token in a
|
|
115
|
-
the oauth tokens will only be maintained in memory and if the python process
|
|
116
|
-
will have to login again.
|
|
111
|
+
When `auth_type` is set to `databricks-oauth` or `azure-oauth` without persisting the oauth token in a
|
|
112
|
+
persistence storage the oauth tokens will only be maintained in memory and if the python process
|
|
113
|
+
restarts the end user will have to login again.
|
|
117
114
|
Note this is beta (private preview)
|
|
118
115
|
|
|
119
116
|
For persisting the oauth token in a prod environment you should subclass and implement OAuthPersistence
|
|
@@ -609,12 +606,15 @@ class Cursor:
|
|
|
609
606
|
"Local file operations are restricted to paths within the configured staging_allowed_local_path"
|
|
610
607
|
)
|
|
611
608
|
|
|
612
|
-
#
|
|
613
|
-
|
|
609
|
+
# May be real headers, or could be json string
|
|
610
|
+
headers = (
|
|
611
|
+
json.loads(row.headers) if isinstance(row.headers, str) else row.headers
|
|
612
|
+
)
|
|
613
|
+
|
|
614
614
|
handler_args = {
|
|
615
615
|
"presigned_url": row.presignedUrl,
|
|
616
616
|
"local_file": abs_localFile,
|
|
617
|
-
"headers":
|
|
617
|
+
"headers": dict(headers) or {},
|
|
618
618
|
}
|
|
619
619
|
|
|
620
620
|
logger.debug(
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@pytest.fixture(scope="session")
|
|
6
|
+
def host():
|
|
7
|
+
return os.getenv("DATABRICKS_SERVER_HOSTNAME")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(scope="session")
|
|
11
|
+
def http_path():
|
|
12
|
+
return os.getenv("DATABRICKS_HTTP_PATH")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture(scope="session")
|
|
16
|
+
def access_token():
|
|
17
|
+
return os.getenv("DATABRICKS_TOKEN")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture(scope="session")
|
|
21
|
+
def ingestion_user():
|
|
22
|
+
return os.getenv("DATABRICKS_USER")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture(scope="session")
|
|
26
|
+
def catalog():
|
|
27
|
+
return os.getenv("DATABRICKS_CATALOG")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture(scope="session")
|
|
31
|
+
def schema():
|
|
32
|
+
return os.getenv("DATABRICKS_SCHEMA", "default")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
36
|
+
def connection_details(host, http_path, access_token, ingestion_user, catalog, schema):
|
|
37
|
+
return {
|
|
38
|
+
"host": host,
|
|
39
|
+
"http_path": http_path,
|
|
40
|
+
"access_token": access_token,
|
|
41
|
+
"ingestion_user": ingestion_user,
|
|
42
|
+
"catalog": catalog,
|
|
43
|
+
"schema": schema,
|
|
44
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import decimal
|
|
3
|
-
import os
|
|
4
3
|
from typing import Tuple, Union, List
|
|
5
4
|
from unittest import skipIf
|
|
6
5
|
|
|
@@ -19,7 +18,7 @@ from sqlalchemy.engine import Engine
|
|
|
19
18
|
from sqlalchemy.engine.reflection import Inspector
|
|
20
19
|
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
|
|
21
20
|
from sqlalchemy.schema import DropColumnComment, SetColumnComment
|
|
22
|
-
from sqlalchemy.types import BOOLEAN, DECIMAL, Date,
|
|
21
|
+
from sqlalchemy.types import BOOLEAN, DECIMAL, Date, Integer, String
|
|
23
22
|
|
|
24
23
|
try:
|
|
25
24
|
from sqlalchemy.orm import declarative_base
|
|
@@ -49,12 +48,12 @@ def version_agnostic_select(object_to_select, *args, **kwargs):
|
|
|
49
48
|
return select(object_to_select, *args, **kwargs)
|
|
50
49
|
|
|
51
50
|
|
|
52
|
-
def version_agnostic_connect_arguments(
|
|
53
|
-
HOST =
|
|
54
|
-
HTTP_PATH =
|
|
55
|
-
ACCESS_TOKEN =
|
|
56
|
-
CATALOG =
|
|
57
|
-
SCHEMA =
|
|
51
|
+
def version_agnostic_connect_arguments(connection_details) -> Tuple[str, dict]:
|
|
52
|
+
HOST = connection_details["host"]
|
|
53
|
+
HTTP_PATH = connection_details["http_path"]
|
|
54
|
+
ACCESS_TOKEN = connection_details["access_token"]
|
|
55
|
+
CATALOG = connection_details["catalog"]
|
|
56
|
+
SCHEMA = connection_details["schema"]
|
|
58
57
|
|
|
59
58
|
ua_connect_args = {"_user_agent_entry": USER_AGENT_TOKEN}
|
|
60
59
|
|
|
@@ -77,8 +76,8 @@ def version_agnostic_connect_arguments(catalog=None, schema=None) -> Tuple[str,
|
|
|
77
76
|
|
|
78
77
|
|
|
79
78
|
@pytest.fixture
|
|
80
|
-
def db_engine() -> Engine:
|
|
81
|
-
conn_string, connect_args = version_agnostic_connect_arguments()
|
|
79
|
+
def db_engine(connection_details) -> Engine:
|
|
80
|
+
conn_string, connect_args = version_agnostic_connect_arguments(connection_details)
|
|
82
81
|
return create_engine(conn_string, connect_args=connect_args)
|
|
83
82
|
|
|
84
83
|
|
|
@@ -92,10 +91,11 @@ def run_query(db_engine: Engine, query: Union[str, Text]):
|
|
|
92
91
|
|
|
93
92
|
|
|
94
93
|
@pytest.fixture
|
|
95
|
-
def samples_engine() -> Engine:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
def samples_engine(connection_details) -> Engine:
|
|
95
|
+
details = connection_details.copy()
|
|
96
|
+
details["catalog"] = "samples"
|
|
97
|
+
details["schema"] = "nyctaxi"
|
|
98
|
+
conn_string, connect_args = version_agnostic_connect_arguments(details)
|
|
99
99
|
return create_engine(conn_string, connect_args=connect_args)
|
|
100
100
|
|
|
101
101
|
|
|
@@ -141,7 +141,7 @@ def test_connect_args(db_engine):
|
|
|
141
141
|
def test_pandas_upload(db_engine, metadata_obj):
|
|
142
142
|
import pandas as pd
|
|
143
143
|
|
|
144
|
-
SCHEMA =
|
|
144
|
+
SCHEMA = "default"
|
|
145
145
|
try:
|
|
146
146
|
df = pd.read_excel(
|
|
147
147
|
"src/databricks/sqlalchemy/test_local/e2e/demo_data/MOCK_DATA.xlsx"
|
|
@@ -409,7 +409,9 @@ def test_get_table_names_smoke_test(samples_engine: Engine):
|
|
|
409
409
|
_names is not None, "get_table_names did not succeed"
|
|
410
410
|
|
|
411
411
|
|
|
412
|
-
def test_has_table_across_schemas(
|
|
412
|
+
def test_has_table_across_schemas(
|
|
413
|
+
db_engine: Engine, samples_engine: Engine, catalog: str, schema: str
|
|
414
|
+
):
|
|
413
415
|
"""For this test to pass these conditions must be met:
|
|
414
416
|
- Table samples.nyctaxi.trips must exist
|
|
415
417
|
- Table samples.tpch.customer must exist
|
|
@@ -426,9 +428,6 @@ def test_has_table_across_schemas(db_engine: Engine, samples_engine: Engine):
|
|
|
426
428
|
)
|
|
427
429
|
|
|
428
430
|
# 3) Check for a table within a different catalog
|
|
429
|
-
other_catalog = os.environ.get("catalog")
|
|
430
|
-
other_schema = os.environ.get("schema")
|
|
431
|
-
|
|
432
431
|
# Create a table in a different catalog
|
|
433
432
|
with db_engine.connect() as conn:
|
|
434
433
|
conn.execute(text("CREATE TABLE test_has_table (numbers_are_cool INT);"))
|
|
@@ -442,8 +441,8 @@ def test_has_table_across_schemas(db_engine: Engine, samples_engine: Engine):
|
|
|
442
441
|
assert samples_engine.dialect.has_table(
|
|
443
442
|
connection=conn,
|
|
444
443
|
table_name="test_has_table",
|
|
445
|
-
schema=
|
|
446
|
-
catalog=
|
|
444
|
+
schema=schema,
|
|
445
|
+
catalog=catalog,
|
|
447
446
|
)
|
|
448
447
|
finally:
|
|
449
448
|
conn.execute(text("DROP TABLE test_has_table;"))
|
|
@@ -503,12 +502,12 @@ def test_get_columns(db_engine, sample_table: str):
|
|
|
503
502
|
|
|
504
503
|
class TestCommentReflection:
|
|
505
504
|
@pytest.fixture(scope="class")
|
|
506
|
-
def engine(self):
|
|
507
|
-
HOST =
|
|
508
|
-
HTTP_PATH =
|
|
509
|
-
ACCESS_TOKEN =
|
|
510
|
-
CATALOG =
|
|
511
|
-
SCHEMA =
|
|
505
|
+
def engine(self, connection_details: dict):
|
|
506
|
+
HOST = connection_details["host"]
|
|
507
|
+
HTTP_PATH = connection_details["http_path"]
|
|
508
|
+
ACCESS_TOKEN = connection_details["access_token"]
|
|
509
|
+
CATALOG = connection_details["catalog"]
|
|
510
|
+
SCHEMA = connection_details["schema"]
|
|
512
511
|
|
|
513
512
|
connection_string = f"databricks://token:{ACCESS_TOKEN}@{HOST}?http_path={HTTP_PATH}&catalog={CATALOG}&schema={SCHEMA}"
|
|
514
513
|
connect_args = {"_user_agent_entry": USER_AGENT_TOKEN}
|
|
@@ -64,16 +64,16 @@ def test_extract_3l_namespace_from_bad_constraint_string():
|
|
|
64
64
|
extract_three_level_identifier_from_constraint_string(input)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
@pytest.mark.parametrize("
|
|
68
|
-
def test_build_fk_dict(
|
|
67
|
+
@pytest.mark.parametrize("tschema", [None, "some_schema"])
|
|
68
|
+
def test_build_fk_dict(tschema):
|
|
69
69
|
fk_constraint_string = "FOREIGN KEY (`parent_user_id`) REFERENCES `main`.`some_schema`.`users` (`user_id`)"
|
|
70
70
|
|
|
71
|
-
result = build_fk_dict("some_fk_name", fk_constraint_string, schema_name=
|
|
71
|
+
result = build_fk_dict("some_fk_name", fk_constraint_string, schema_name=tschema)
|
|
72
72
|
|
|
73
73
|
assert result == {
|
|
74
74
|
"name": "some_fk_name",
|
|
75
75
|
"constrained_columns": ["parent_user_id"],
|
|
76
|
-
"referred_schema":
|
|
76
|
+
"referred_schema": tschema,
|
|
77
77
|
"referred_table": "users",
|
|
78
78
|
"referred_columns": ["user_id"],
|
|
79
79
|
}
|
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/auth/oauth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/exc.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
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/types.py
RENAMED
|
File without changes
|
{databricks_sql_connector-3.0.3b1 → databricks_sql_connector-3.1.0}/src/databricks/sql/utils.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
|