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.
@@ -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,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 connect(self, ds_name: str, *args, **kwargs) -> Engine:
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(ds_name)
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
- base_url = (f"clickzetta://{conn_info.username}:{conn_info.password}@{conn_info.instanceName}."
201
- f"{conn_info.host}/"
202
- f"{conn_info.workspaceName}"
203
- f"?virtualcluster={self._vcluster}"
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
- base_url = (f"clickzetta://{conn_info.instanceName}.{conn_info.host}/"
207
- f"{conn_info.workspaceName}"
208
- f"?magic_token={conn_info.magicToken}"
209
- f"&virtualcluster={self._vcluster}"
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
- return sa_create_engine(url, connect_args={'options': self._convert_options(options)}, *args, **kwargs)
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 {k}={v}' for k, v in options.items()])
277
+ return ' '.join([f'-c{k}={v}' for k, v in options.items()])
230
278
 
231
279
 
232
- def get_lakehouse_client(conn):
233
- return conn.connection.connection._client
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
- workspace (str, optional): Workspace name
250
- schema (str, optional): Schema name
251
- vcluster (str, optional): Virtual cluster name
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.connect(ds_name, *args, **kwargs)
316
+ return manager.build(*args, **kwargs)
@@ -1 +1 @@
1
- __version__ = "1.0.4"
1
+ __version__ = "1.0.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: clickzetta-dbutils
3
- Version: 1.0.4
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,,