clickzetta-dbutils 1.0.3__py3-none-any.whl → 1.0.5__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 +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
|