clickzetta-dbutils 1.0.4__py3-none-any.whl → 1.0.6__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,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,,