clickzetta-dbutils 1.0.4__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.
@@ -1,5 +1,5 @@
1
- from .db_utils import get_active_engine, get_lakehouse_client, DatabaseConnectionManager, ConnectionConfig, \
1
+ from .db_utils import get_active_engine, get_lakehouse_connection, DatabaseConnectionManager, ConnectionConfig, \
2
2
  DatabaseConnectionError
3
3
 
4
- __all__ = ["get_active_engine", "get_lakehouse_client", "DatabaseConnectionManager", "ConnectionConfig",
4
+ __all__ = ["get_active_engine", "get_lakehouse_connection", "DatabaseConnectionManager", "ConnectionConfig",
5
5
  "DatabaseConnectionError"]
@@ -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 connect(self, ds_name: str, *args, **kwargs) -> Engine:
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(ds_name)
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
- return sa_create_engine(url, connect_args={'options': self._convert_options(options)}, *args, **kwargs)
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 get_lakehouse_client(conn):
233
- return conn.connection.connection._client
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
- workspace (str, optional): Workspace name
250
- schema (str, optional): Schema name
251
- vcluster (str, optional): Virtual cluster name
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.connect(ds_name, *args, **kwargs)
308
+ return manager.build(*args, **kwargs)
@@ -1 +1 @@
1
- __version__ = "1.0.4"
1
+ __version__ = "1.0.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: clickzetta-dbutils
3
- Version: 1.0.4
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/
@@ -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=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,,