clickzetta-dbutils 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- clickzetta_dbutils/__init__.py +2 -2
- clickzetta_dbutils/db_utils.py +61 -20
- clickzetta_dbutils/version.py +1 -1
- {clickzetta_dbutils-1.0.3.dist-info → clickzetta_dbutils-1.0.5.dist-info}/METADATA +2 -2
- clickzetta_dbutils-1.0.5.dist-info/RECORD +7 -0
- clickzetta_dbutils-1.0.3.dist-info/RECORD +0 -7
- {clickzetta_dbutils-1.0.3.dist-info → clickzetta_dbutils-1.0.5.dist-info}/WHEEL +0 -0
- {clickzetta_dbutils-1.0.3.dist-info → clickzetta_dbutils-1.0.5.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,15 @@ 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"
|
22
26
|
host: Optional[str] = None
|
23
27
|
magicToken: Optional[str] = None
|
24
28
|
username: Optional[str] = None
|
25
29
|
password: Optional[str] = None
|
26
30
|
instanceName: Optional[str] = None
|
27
|
-
workspaceName: Optional[str] = None
|
28
31
|
options: Dict[str, str] = field(default_factory=dict)
|
32
|
+
query: Dict[str, str] = field(default_factory=dict)
|
29
33
|
|
30
34
|
|
31
35
|
class DatabaseConnectionManager:
|
@@ -33,16 +37,18 @@ class DatabaseConnectionManager:
|
|
33
37
|
Manages database connections with flexible configuration options.
|
34
38
|
"""
|
35
39
|
|
36
|
-
def __init__(self):
|
40
|
+
def __init__(self, ds_name: str):
|
37
41
|
"""
|
38
42
|
Initialize a database connection for a specific data source.
|
39
43
|
"""
|
44
|
+
self._ds_name = ds_name
|
40
45
|
self._vcluster: Optional[str] = None
|
41
46
|
self._workspace: Optional[str] = None
|
42
47
|
self._driver: Optional[str] = None
|
43
48
|
self._schema: Optional[str] = None
|
44
49
|
self._engine: Optional[Engine] = None
|
45
50
|
self._options = {}
|
51
|
+
self._query: Dict[str, str] = {}
|
46
52
|
|
47
53
|
@classmethod
|
48
54
|
def _load_connection_configs(cls) -> Dict[str, ConnectionConfig]:
|
@@ -55,6 +61,9 @@ class DatabaseConnectionManager:
|
|
55
61
|
if not hasattr(DatabaseConnectionManager, '_connection_cache'):
|
56
62
|
# Retrieve and decode connection info from environment variable
|
57
63
|
conn_info_str = os.environ.get('connectionInfos', '[]')
|
64
|
+
if not conn_info_str:
|
65
|
+
raise DatabaseConnectionError(
|
66
|
+
"No connection information found in environment variable 'connectionInfos'")
|
58
67
|
decoded_info = urllib.parse.unquote(conn_info_str)
|
59
68
|
conn_list = json.loads(decoded_info)
|
60
69
|
|
@@ -63,6 +72,10 @@ class DatabaseConnectionManager:
|
|
63
72
|
info.get('dsName'): ConnectionConfig(**info)
|
64
73
|
for info in conn_list
|
65
74
|
}
|
75
|
+
|
76
|
+
resource_names = [f"{conn.dsName} ({conn.workspaceName})" for conn in cls._connection_cache.values()]
|
77
|
+
_log.info(f"Successfully loaded connection configurations: {', '.join(resource_names)}")
|
78
|
+
|
66
79
|
return cls._connection_cache
|
67
80
|
|
68
81
|
def get_connection_info(self, ds_name: str) -> ConnectionConfig:
|
@@ -77,8 +90,16 @@ class DatabaseConnectionManager:
|
|
77
90
|
|
78
91
|
config = connections.get(ds_name)
|
79
92
|
config.options.update(self._options)
|
93
|
+
if self._query:
|
94
|
+
config.query.update(self._query)
|
80
95
|
return config
|
81
96
|
|
97
|
+
def get_connection_infos(self):
|
98
|
+
"""
|
99
|
+
Get all connection infos
|
100
|
+
"""
|
101
|
+
return self._load_connection_configs()
|
102
|
+
|
82
103
|
def use_workspace(self, workspace: str) -> 'DatabaseConnectionManager':
|
83
104
|
"""
|
84
105
|
Set workspace for the connection.
|
@@ -143,15 +164,27 @@ class DatabaseConnectionManager:
|
|
143
164
|
"""
|
144
165
|
if options:
|
145
166
|
self._options.update(options)
|
167
|
+
return self
|
168
|
+
|
169
|
+
def use_query(self, query: dict) -> 'DatabaseConnectionManager':
|
170
|
+
"""
|
171
|
+
Set query for the connection.
|
172
|
+
Args:
|
173
|
+
query (str): Query string
|
174
|
+
Returns:
|
175
|
+
self: For method chaining
|
176
|
+
"""
|
177
|
+
if query:
|
178
|
+
self._query.update(query)
|
179
|
+
return self
|
146
180
|
|
147
|
-
def
|
181
|
+
def build(self, *args, **kwargs) -> Engine:
|
148
182
|
"""
|
149
183
|
Create SQLAlchemy engine based on data source name and optional schema
|
150
184
|
|
151
|
-
:param ds_name: Name of the data source
|
152
185
|
:return: SQLAlchemy Engine
|
153
186
|
"""
|
154
|
-
conn_info: ConnectionConfig = self.get_connection_info(
|
187
|
+
conn_info: ConnectionConfig = self.get_connection_info(self._ds_name)
|
155
188
|
|
156
189
|
if not conn_info.host:
|
157
190
|
raise DatabaseConnectionError("Missing connection host for MySQL data source")
|
@@ -160,15 +193,14 @@ class DatabaseConnectionManager:
|
|
160
193
|
options = conn_info.options or {}
|
161
194
|
schema = self._schema or conn_info.schema
|
162
195
|
host_parts = conn_info.host.split(':')
|
163
|
-
|
164
|
-
|
196
|
+
connect_args = {}
|
165
197
|
|
166
198
|
# Construct connection URL based on data source type
|
167
199
|
if ds_type == 5: # Mysql
|
168
200
|
if not conn_info.username or not conn_info.password:
|
169
201
|
raise DatabaseConnectionError("Missing username or password for MySQL data source")
|
170
|
-
# Split host into host and port if provided
|
171
202
|
|
203
|
+
options.update(conn_info.query)
|
172
204
|
url = URL.create(
|
173
205
|
drivername=self._driver or 'mysql+mysqlconnector',
|
174
206
|
username=conn_info.username,
|
@@ -187,8 +219,10 @@ class DatabaseConnectionManager:
|
|
187
219
|
password=conn_info.password,
|
188
220
|
host=host_parts[0],
|
189
221
|
port=host_parts[1] if len(host_parts) > 1 else None,
|
190
|
-
database=schema
|
222
|
+
database=schema,
|
223
|
+
query=conn_info.query
|
191
224
|
)
|
225
|
+
connect_args = {'options': self._convert_options(options)}
|
192
226
|
elif ds_type == 1: # ClickZetta
|
193
227
|
if not conn_info.workspaceName or not conn_info.instanceName:
|
194
228
|
raise DatabaseConnectionError("Missing required parameters 'workspace_name', "
|
@@ -211,16 +245,22 @@ class DatabaseConnectionManager:
|
|
211
245
|
else:
|
212
246
|
raise ValueError("username and password or token must be specified")
|
213
247
|
|
214
|
-
|
215
248
|
# Add schema if provided
|
216
249
|
if schema:
|
217
250
|
base_url += f"&schema={schema}"
|
218
251
|
|
252
|
+
options.update(conn_info.query)
|
253
|
+
|
254
|
+
# Add query string if provided
|
255
|
+
if options:
|
256
|
+
base_url += f"&{urllib.parse.urlencode(options)}"
|
257
|
+
|
219
258
|
url = base_url
|
220
259
|
else:
|
221
260
|
raise ValueError(f"Unsupported data source type: {ds_type}")
|
222
261
|
|
223
|
-
|
262
|
+
self._engine = sa_create_engine(url, connect_args=connect_args, *args, **kwargs)
|
263
|
+
return self._engine
|
224
264
|
|
225
265
|
@staticmethod
|
226
266
|
def _convert_options(options):
|
@@ -229,8 +269,8 @@ class DatabaseConnectionManager:
|
|
229
269
|
return ' '.join([f'-c {k}={v}' for k, v in options.items()])
|
230
270
|
|
231
271
|
|
232
|
-
def
|
233
|
-
return conn.connection.connection
|
272
|
+
def get_lakehouse_connection(conn):
|
273
|
+
return conn.connection.connection
|
234
274
|
|
235
275
|
|
236
276
|
def get_active_engine(
|
@@ -245,15 +285,16 @@ def get_active_engine(
|
|
245
285
|
Convenience function to create a database engine.
|
246
286
|
|
247
287
|
Args:
|
248
|
-
ds_name (str): Data source name
|
249
|
-
|
250
|
-
|
251
|
-
|
288
|
+
ds_name (str): Data source name. Required.
|
289
|
+
vcluster (str, optional): Virtual cluster name for ClickZetta data source. Required for ClickZetta.
|
290
|
+
workspace (str, optional): Workspace name. Default is 'default'.
|
291
|
+
schema (str, optional): Schema name for the connection. Default is 'public'.
|
292
|
+
options (dict, optional): Additional connection options.
|
252
293
|
|
253
294
|
Returns:
|
254
295
|
SQLAlchemy Engine instance
|
255
296
|
"""
|
256
|
-
manager = DatabaseConnectionManager()
|
297
|
+
manager = DatabaseConnectionManager(ds_name)
|
257
298
|
|
258
299
|
if workspace:
|
259
300
|
manager.use_workspace(workspace)
|
@@ -264,4 +305,4 @@ def get_active_engine(
|
|
264
305
|
if options:
|
265
306
|
manager.use_options(options)
|
266
307
|
|
267
|
-
return manager.
|
308
|
+
return manager.build(*args, **kwargs)
|
clickzetta_dbutils/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "1.0.
|
1
|
+
__version__ = "1.0.5"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: clickzetta-dbutils
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.5
|
4
4
|
Summary: clickzetta dbutils
|
5
5
|
Author-email: "lin.zhang" <lin.zhang@clickzetta.com>
|
6
6
|
Project-URL: documentation, https://www.yunqi.tech/
|
@@ -10,7 +10,7 @@ Classifier: Development Status :: 3 - Alpha
|
|
10
10
|
Requires-Python: >=3.7
|
11
11
|
Requires-Dist: sqlalchemy<2.0.0,>=1.4.0
|
12
12
|
Requires-Dist: numpy==1.24.4
|
13
|
-
Requires-Dist: psycopg2
|
13
|
+
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"
|
@@ -0,0 +1,7 @@
|
|
1
|
+
clickzetta_dbutils/__init__.py,sha256=Q_6kas0RvZ0767qlaA_xGESmXxm0dks1YQ8DqCX8LV0,290
|
2
|
+
clickzetta_dbutils/db_utils.py,sha256=TQobsTlxNTzUwIFgWZvVMtydNUPk_7AG82ChWQ4BCQY,10085
|
3
|
+
clickzetta_dbutils/version.py,sha256=ZR1VA9cGs0vIK6cWK4YKLfBTnmUCAcDaaP9ARPPYxEs,21
|
4
|
+
clickzetta_dbutils-1.0.5.dist-info/METADATA,sha256=2QYdvLVx4oQCI0J727G0ACdKrwuNAlYXjPpUKuSS6mI,938
|
5
|
+
clickzetta_dbutils-1.0.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
+
clickzetta_dbutils-1.0.5.dist-info/top_level.txt,sha256=8o5KqMSg9pxnPNejHjMaqZV2vEDvwvsz2GdChZI0N6I,19
|
7
|
+
clickzetta_dbutils-1.0.5.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=-PYTKPEkq2aGJHOdzkgu5oKZIPKSq6MCqGrFyy4QQXc,21
|
4
|
-
clickzetta_dbutils-1.0.3.dist-info/METADATA,sha256=Io3EEH8-lVA9DMp33N7x8WDgMPeeWHkzY9Qi4GDwPg4,931
|
5
|
-
clickzetta_dbutils-1.0.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
-
clickzetta_dbutils-1.0.3.dist-info/top_level.txt,sha256=8o5KqMSg9pxnPNejHjMaqZV2vEDvwvsz2GdChZI0N6I,19
|
7
|
-
clickzetta_dbutils-1.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|