clickzetta-dbutils 1.0.4__py3-none-any.whl → 1.0.6__py3-none-any.whl
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.
- clickzetta_dbutils/__init__.py +2 -2
- clickzetta_dbutils/db_utils.py +86 -37
- clickzetta_dbutils/version.py +1 -1
- {clickzetta_dbutils-1.0.4.dist-info → clickzetta_dbutils-1.0.6.dist-info}/METADATA +1 -6
- clickzetta_dbutils-1.0.6.dist-info/RECORD +7 -0
- clickzetta_dbutils-1.0.4.dist-info/RECORD +0 -7
- {clickzetta_dbutils-1.0.4.dist-info → clickzetta_dbutils-1.0.6.dist-info}/WHEEL +0 -0
- {clickzetta_dbutils-1.0.4.dist-info → clickzetta_dbutils-1.0.6.dist-info}/top_level.txt +0 -0
clickzetta_dbutils/__init__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
from .db_utils import get_active_engine,
|
1
|
+
from .db_utils import get_active_engine, get_lakehouse_connection, DatabaseConnectionManager, ConnectionConfig, \
|
2
2
|
DatabaseConnectionError
|
3
3
|
|
4
|
-
__all__ = ["get_active_engine", "
|
4
|
+
__all__ = ["get_active_engine", "get_lakehouse_connection", "DatabaseConnectionManager", "ConnectionConfig",
|
5
5
|
"DatabaseConnectionError"]
|
clickzetta_dbutils/db_utils.py
CHANGED
@@ -2,12 +2,15 @@ import json
|
|
2
2
|
import os
|
3
3
|
import urllib.parse
|
4
4
|
from dataclasses import dataclass, field
|
5
|
+
from logging import getLogger
|
5
6
|
from typing import Optional, Dict
|
6
7
|
|
7
8
|
from sqlalchemy import create_engine as sa_create_engine
|
8
9
|
from sqlalchemy.engine import URL
|
9
10
|
from sqlalchemy.engine.base import Engine
|
10
11
|
|
12
|
+
_log = getLogger(__name__)
|
13
|
+
|
11
14
|
|
12
15
|
class DatabaseConnectionError(Exception):
|
13
16
|
"""Custom exception for database connection errors."""
|
@@ -18,14 +21,16 @@ class DatabaseConnectionError(Exception):
|
|
18
21
|
class ConnectionConfig:
|
19
22
|
dsName: str
|
20
23
|
dsType: int
|
21
|
-
schema: str
|
24
|
+
schema: str = "public"
|
25
|
+
workspaceName: str = "default"
|
26
|
+
table: Optional[str] = None
|
22
27
|
host: Optional[str] = None
|
23
28
|
magicToken: Optional[str] = None
|
24
29
|
username: Optional[str] = None
|
25
30
|
password: Optional[str] = None
|
26
31
|
instanceName: Optional[str] = None
|
27
|
-
workspaceName: Optional[str] = None
|
28
32
|
options: Dict[str, str] = field(default_factory=dict)
|
33
|
+
query: Dict[str, str] = field(default_factory=dict)
|
29
34
|
|
30
35
|
|
31
36
|
class DatabaseConnectionManager:
|
@@ -33,16 +38,18 @@ class DatabaseConnectionManager:
|
|
33
38
|
Manages database connections with flexible configuration options.
|
34
39
|
"""
|
35
40
|
|
36
|
-
def __init__(self):
|
41
|
+
def __init__(self, ds_name: str):
|
37
42
|
"""
|
38
43
|
Initialize a database connection for a specific data source.
|
39
44
|
"""
|
45
|
+
self._ds_name = ds_name
|
40
46
|
self._vcluster: Optional[str] = None
|
41
47
|
self._workspace: Optional[str] = None
|
42
48
|
self._driver: Optional[str] = None
|
43
49
|
self._schema: Optional[str] = None
|
44
50
|
self._engine: Optional[Engine] = None
|
45
51
|
self._options = {}
|
52
|
+
self._query: Dict[str, str] = {}
|
46
53
|
|
47
54
|
@classmethod
|
48
55
|
def _load_connection_configs(cls) -> Dict[str, ConnectionConfig]:
|
@@ -55,6 +62,9 @@ class DatabaseConnectionManager:
|
|
55
62
|
if not hasattr(DatabaseConnectionManager, '_connection_cache'):
|
56
63
|
# Retrieve and decode connection info from environment variable
|
57
64
|
conn_info_str = os.environ.get('connectionInfos', '[]')
|
65
|
+
if not conn_info_str:
|
66
|
+
raise DatabaseConnectionError(
|
67
|
+
"No connection information found in environment variable 'connectionInfos'")
|
58
68
|
decoded_info = urllib.parse.unquote(conn_info_str)
|
59
69
|
conn_list = json.loads(decoded_info)
|
60
70
|
|
@@ -63,6 +73,10 @@ class DatabaseConnectionManager:
|
|
63
73
|
info.get('dsName'): ConnectionConfig(**info)
|
64
74
|
for info in conn_list
|
65
75
|
}
|
76
|
+
|
77
|
+
resource_names = [f"{conn.dsName} ({conn.workspaceName})" for conn in cls._connection_cache.values()]
|
78
|
+
_log.info(f"Successfully loaded connection configurations: {', '.join(resource_names)}")
|
79
|
+
|
66
80
|
return cls._connection_cache
|
67
81
|
|
68
82
|
def get_connection_info(self, ds_name: str) -> ConnectionConfig:
|
@@ -77,8 +91,16 @@ class DatabaseConnectionManager:
|
|
77
91
|
|
78
92
|
config = connections.get(ds_name)
|
79
93
|
config.options.update(self._options)
|
94
|
+
if self._query:
|
95
|
+
config.query.update(self._query)
|
80
96
|
return config
|
81
97
|
|
98
|
+
def get_connection_infos(self):
|
99
|
+
"""
|
100
|
+
Get all connection infos
|
101
|
+
"""
|
102
|
+
return self._load_connection_configs()
|
103
|
+
|
82
104
|
def use_workspace(self, workspace: str) -> 'DatabaseConnectionManager':
|
83
105
|
"""
|
84
106
|
Set workspace for the connection.
|
@@ -143,15 +165,27 @@ class DatabaseConnectionManager:
|
|
143
165
|
"""
|
144
166
|
if options:
|
145
167
|
self._options.update(options)
|
168
|
+
return self
|
169
|
+
|
170
|
+
def use_query(self, query: dict) -> 'DatabaseConnectionManager':
|
171
|
+
"""
|
172
|
+
Set query for the connection.
|
173
|
+
Args:
|
174
|
+
query (str): Query string
|
175
|
+
Returns:
|
176
|
+
self: For method chaining
|
177
|
+
"""
|
178
|
+
if query:
|
179
|
+
self._query.update(query)
|
180
|
+
return self
|
146
181
|
|
147
|
-
def
|
182
|
+
def build(self, *args, **kwargs) -> Engine:
|
148
183
|
"""
|
149
184
|
Create SQLAlchemy engine based on data source name and optional schema
|
150
185
|
|
151
|
-
:param ds_name: Name of the data source
|
152
186
|
:return: SQLAlchemy Engine
|
153
187
|
"""
|
154
|
-
conn_info: ConnectionConfig = self.get_connection_info(
|
188
|
+
conn_info: ConnectionConfig = self.get_connection_info(self._ds_name)
|
155
189
|
|
156
190
|
if not conn_info.host:
|
157
191
|
raise DatabaseConnectionError("Missing connection host for MySQL data source")
|
@@ -160,15 +194,14 @@ class DatabaseConnectionManager:
|
|
160
194
|
options = conn_info.options or {}
|
161
195
|
schema = self._schema or conn_info.schema
|
162
196
|
host_parts = conn_info.host.split(':')
|
163
|
-
|
164
|
-
|
197
|
+
connect_args = {}
|
165
198
|
|
166
199
|
# Construct connection URL based on data source type
|
167
200
|
if ds_type == 5: # Mysql
|
168
201
|
if not conn_info.username or not conn_info.password:
|
169
202
|
raise DatabaseConnectionError("Missing username or password for MySQL data source")
|
170
|
-
# Split host into host and port if provided
|
171
203
|
|
204
|
+
options.update(conn_info.query)
|
172
205
|
url = URL.create(
|
173
206
|
drivername=self._driver or 'mysql+mysqlconnector',
|
174
207
|
username=conn_info.username,
|
@@ -187,8 +220,11 @@ class DatabaseConnectionManager:
|
|
187
220
|
password=conn_info.password,
|
188
221
|
host=host_parts[0],
|
189
222
|
port=host_parts[1] if len(host_parts) > 1 else None,
|
190
|
-
database=schema
|
223
|
+
database=schema,
|
224
|
+
query=conn_info.query
|
191
225
|
)
|
226
|
+
connect_args = {'options': self._convert_options(options)}
|
227
|
+
|
192
228
|
elif ds_type == 1: # ClickZetta
|
193
229
|
if not conn_info.workspaceName or not conn_info.instanceName:
|
194
230
|
raise DatabaseConnectionError("Missing required parameters 'workspace_name', "
|
@@ -196,41 +232,53 @@ class DatabaseConnectionManager:
|
|
196
232
|
if not self._vcluster:
|
197
233
|
raise DatabaseConnectionError("Missing virtual cluster for ClickZetta data source")
|
198
234
|
|
235
|
+
# Generate base parameters
|
236
|
+
query_params = {
|
237
|
+
"virtualcluster": self._vcluster
|
238
|
+
}
|
239
|
+
|
240
|
+
if schema:
|
241
|
+
query_params["schema"] = schema
|
242
|
+
|
243
|
+
query_params.update(options)
|
244
|
+
query_params.update(conn_info.query)
|
245
|
+
|
246
|
+
full_host = f"{conn_info.instanceName}.{conn_info.host}"
|
247
|
+
|
199
248
|
if conn_info.username and conn_info.password:
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
249
|
+
url = URL.create(
|
250
|
+
drivername="clickzetta",
|
251
|
+
username=conn_info.username,
|
252
|
+
password=conn_info.password,
|
253
|
+
host=full_host,
|
254
|
+
database=conn_info.workspaceName,
|
255
|
+
query=query_params
|
256
|
+
)
|
205
257
|
elif conn_info.magicToken:
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
258
|
+
query_params["magic_token"] = conn_info.magicToken
|
259
|
+
url = URL.create(
|
260
|
+
drivername="clickzetta",
|
261
|
+
host=full_host,
|
262
|
+
database=conn_info.workspaceName,
|
263
|
+
query=query_params
|
264
|
+
)
|
211
265
|
else:
|
212
266
|
raise ValueError("username and password or token must be specified")
|
213
|
-
|
214
|
-
|
215
|
-
# Add schema if provided
|
216
|
-
if schema:
|
217
|
-
base_url += f"&schema={schema}"
|
218
|
-
|
219
|
-
url = base_url
|
220
267
|
else:
|
221
268
|
raise ValueError(f"Unsupported data source type: {ds_type}")
|
222
269
|
|
223
|
-
|
270
|
+
self._engine = sa_create_engine(url, connect_args=connect_args, *args, **kwargs)
|
271
|
+
return self._engine
|
224
272
|
|
225
273
|
@staticmethod
|
226
274
|
def _convert_options(options):
|
227
275
|
if not options:
|
228
276
|
return ''
|
229
|
-
return ' '.join([f'-c
|
277
|
+
return ' '.join([f'-c{k}={v}' for k, v in options.items()])
|
230
278
|
|
231
279
|
|
232
|
-
def
|
233
|
-
return conn.connection.connection
|
280
|
+
def get_lakehouse_connection(conn):
|
281
|
+
return conn.connection.connection
|
234
282
|
|
235
283
|
|
236
284
|
def get_active_engine(
|
@@ -245,15 +293,16 @@ def get_active_engine(
|
|
245
293
|
Convenience function to create a database engine.
|
246
294
|
|
247
295
|
Args:
|
248
|
-
ds_name (str): Data source name
|
249
|
-
|
250
|
-
|
251
|
-
|
296
|
+
ds_name (str): Data source name. Required.
|
297
|
+
vcluster (str, optional): Virtual cluster name for ClickZetta data source. Required for ClickZetta.
|
298
|
+
workspace (str, optional): Workspace name. Default is 'default'.
|
299
|
+
schema (str, optional): Schema name for the connection. Default is 'public'.
|
300
|
+
options (dict, optional): Additional connection options.
|
252
301
|
|
253
302
|
Returns:
|
254
303
|
SQLAlchemy Engine instance
|
255
304
|
"""
|
256
|
-
manager = DatabaseConnectionManager()
|
305
|
+
manager = DatabaseConnectionManager(ds_name)
|
257
306
|
|
258
307
|
if workspace:
|
259
308
|
manager.use_workspace(workspace)
|
@@ -264,4 +313,4 @@ def get_active_engine(
|
|
264
313
|
if options:
|
265
314
|
manager.use_options(options)
|
266
315
|
|
267
|
-
return manager.
|
316
|
+
return manager.build(*args, **kwargs)
|
clickzetta_dbutils/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "1.0.
|
1
|
+
__version__ = "1.0.6"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: clickzetta-dbutils
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.6
|
4
4
|
Summary: clickzetta dbutils
|
5
5
|
Author-email: "lin.zhang" <lin.zhang@clickzetta.com>
|
6
6
|
Project-URL: documentation, https://www.yunqi.tech/
|
@@ -14,13 +14,8 @@ Requires-Dist: psycopg2-binary
|
|
14
14
|
Requires-Dist: mysql-connector-python
|
15
15
|
Provides-Extra: dev
|
16
16
|
Requires-Dist: pytest==8.2.1; extra == "dev"
|
17
|
-
Requires-Dist: sqlparse; extra == "dev"
|
18
|
-
Requires-Dist: grpcio; extra == "dev"
|
19
|
-
Requires-Dist: grpcio-tools; extra == "dev"
|
20
17
|
Requires-Dist: build; extra == "dev"
|
21
18
|
Requires-Dist: pytest-xdist; extra == "dev"
|
22
|
-
Requires-Dist: pytz; extra == "dev"
|
23
|
-
Requires-Dist: apache-superset==4.0.2; extra == "dev"
|
24
19
|
Provides-Extra: cliclzetta
|
25
20
|
Requires-Dist: clickzetta-connector; extra == "cliclzetta"
|
26
21
|
Requires-Dist: clickzetta-sqlalchemy; extra == "cliclzetta"
|
@@ -0,0 +1,7 @@
|
|
1
|
+
clickzetta_dbutils/__init__.py,sha256=Q_6kas0RvZ0767qlaA_xGESmXxm0dks1YQ8DqCX8LV0,290
|
2
|
+
clickzetta_dbutils/db_utils.py,sha256=1iVaiYIQosukrQBAbgjNXJJW51p7k1ihQKBIcl_MvQM,10166
|
3
|
+
clickzetta_dbutils/version.py,sha256=fCDDAyG3nMZcE_hvt1RHdxkiFN3DfNSyU_k-rLUDrpE,21
|
4
|
+
clickzetta_dbutils-1.0.6.dist-info/METADATA,sha256=mpfK67NkOGCBN_wu5NcTvMLLTsVGlLxmnNdcX3KTiFw,726
|
5
|
+
clickzetta_dbutils-1.0.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
+
clickzetta_dbutils-1.0.6.dist-info/top_level.txt,sha256=8o5KqMSg9pxnPNejHjMaqZV2vEDvwvsz2GdChZI0N6I,19
|
7
|
+
clickzetta_dbutils-1.0.6.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
clickzetta_dbutils/__init__.py,sha256=OevYNnzvgLUw0-KDJKG7loCONkN_7DxtmqZAmzCHGAg,282
|
2
|
-
clickzetta_dbutils/db_utils.py,sha256=L170MOzkMH4cfEwYJcHPz7_aQcTi96e9BqkF6CCRfc4,8506
|
3
|
-
clickzetta_dbutils/version.py,sha256=O-a1T6uLdX0DYXelAnzqY4NDGktopbWdpKZloec7oxY,21
|
4
|
-
clickzetta_dbutils-1.0.4.dist-info/METADATA,sha256=5Q0gbUScv8hzvjZxf_gaJaZJiGqakvkFIilkrauCgbw,938
|
5
|
-
clickzetta_dbutils-1.0.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
-
clickzetta_dbutils-1.0.4.dist-info/top_level.txt,sha256=8o5KqMSg9pxnPNejHjMaqZV2vEDvwvsz2GdChZI0N6I,19
|
7
|
-
clickzetta_dbutils-1.0.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|