e6data-python-connector 2.2.2__py3-none-any.whl → 2.2.3__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.
- e6data_python_connector/cluster_manager.py +46 -22
- e6data_python_connector/e6data_grpc.py +365 -57
- {e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/METADATA +2 -2
- {e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/RECORD +8 -8
- {e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/LICENSE +0 -0
- {e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/WHEEL +0 -0
- {e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/entry_points.txt +0 -0
- {e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/top_level.txt +0 -0
|
@@ -8,6 +8,19 @@ import multiprocessing
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def _get_grpc_header(engine_ip=None, cluster=None):
|
|
11
|
+
"""
|
|
12
|
+
Generate gRPC metadata headers for the request.
|
|
13
|
+
|
|
14
|
+
This function creates a list of metadata headers to be used in gRPC requests.
|
|
15
|
+
It includes optional headers for the engine IP and cluster UUID.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
engine_ip (str, optional): The IP address of the engine. Defaults to None.
|
|
19
|
+
cluster (str, optional): The UUID of the cluster. Defaults to None.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
list: A list of tuples representing the gRPC metadata headers.
|
|
23
|
+
"""
|
|
11
24
|
metadata = []
|
|
12
25
|
if engine_ip:
|
|
13
26
|
metadata.append(('plannerip', engine_ip))
|
|
@@ -123,7 +136,7 @@ class ClusterManager:
|
|
|
123
136
|
cluster_uuid (str): The unique identifier for the target cluster.
|
|
124
137
|
"""
|
|
125
138
|
|
|
126
|
-
def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 3, cluster_uuid=None):
|
|
139
|
+
def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 3, cluster_uuid=None, grpc_options=None):
|
|
127
140
|
"""
|
|
128
141
|
Initializes a new instance of the ClusterManager class.
|
|
129
142
|
|
|
@@ -147,6 +160,9 @@ class ClusterManager:
|
|
|
147
160
|
self._timeout = time.time() + timeout
|
|
148
161
|
self._secure_channel = secure_channel
|
|
149
162
|
self.cluster_uuid = cluster_uuid
|
|
163
|
+
self._grpc_options = grpc_options
|
|
164
|
+
if grpc_options is None:
|
|
165
|
+
self._grpc_options = dict()
|
|
150
166
|
|
|
151
167
|
@property
|
|
152
168
|
def _get_connection(self):
|
|
@@ -161,14 +177,34 @@ class ClusterManager:
|
|
|
161
177
|
if self._secure_channel:
|
|
162
178
|
self._channel = grpc.secure_channel(
|
|
163
179
|
target='{}:{}'.format(self._host, self._port),
|
|
180
|
+
options=self._grpc_options,
|
|
164
181
|
credentials=grpc.ssl_channel_credentials()
|
|
165
182
|
)
|
|
166
183
|
else:
|
|
167
184
|
self._channel = grpc.insecure_channel(
|
|
168
|
-
target='{}:{}'.format(self._host, self._port)
|
|
185
|
+
target='{}:{}'.format(self._host, self._port),
|
|
186
|
+
options=self._grpc_options
|
|
169
187
|
)
|
|
170
188
|
return cluster_pb2_grpc.ClusterServiceStub(self._channel)
|
|
171
189
|
|
|
190
|
+
def _check_cluster_status(self):
|
|
191
|
+
while True:
|
|
192
|
+
try:
|
|
193
|
+
# Create a status request payload with user credentials
|
|
194
|
+
status_payload = cluster_pb2.ClusterStatusRequest(
|
|
195
|
+
user=self._user,
|
|
196
|
+
password=self._password
|
|
197
|
+
)
|
|
198
|
+
# Send the status request to the cluster service
|
|
199
|
+
response = self._get_connection.status(
|
|
200
|
+
status_payload,
|
|
201
|
+
metadata=_get_grpc_header(cluster=self.cluster_uuid)
|
|
202
|
+
)
|
|
203
|
+
# Yield the current status
|
|
204
|
+
yield response.status
|
|
205
|
+
except _InactiveRpcError as e:
|
|
206
|
+
yield None
|
|
207
|
+
|
|
172
208
|
def resume(self) -> bool:
|
|
173
209
|
"""
|
|
174
210
|
Resumes the cluster if it is currently suspended or not in the 'active' state.
|
|
@@ -229,27 +265,15 @@ class ClusterManager:
|
|
|
229
265
|
"""
|
|
230
266
|
return False
|
|
231
267
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
response = self._get_connection.status(
|
|
240
|
-
status_payload,
|
|
241
|
-
metadata=_get_grpc_header(cluster=self.cluster_uuid)
|
|
242
|
-
)
|
|
243
|
-
if response.status == 'active':
|
|
244
|
-
lock.set_active()
|
|
245
|
-
return True
|
|
246
|
-
if response.status in ['suspended', 'failed']:
|
|
247
|
-
return False
|
|
248
|
-
if time.time() > self._timeout:
|
|
249
|
-
return False
|
|
250
|
-
except _InactiveRpcError as e:
|
|
251
|
-
pass
|
|
268
|
+
for status in self._check_cluster_status():
|
|
269
|
+
if status == 'active':
|
|
270
|
+
lock.set_active()
|
|
271
|
+
return True
|
|
272
|
+
elif status == 'failed' or time.time() > self._timeout:
|
|
273
|
+
return False
|
|
274
|
+
# Wait for 5 seconds before the next status check
|
|
252
275
|
time.sleep(5)
|
|
276
|
+
return False
|
|
253
277
|
|
|
254
278
|
def suspend(self):
|
|
255
279
|
"""
|
|
@@ -11,6 +11,7 @@ import datetime
|
|
|
11
11
|
import logging
|
|
12
12
|
import re
|
|
13
13
|
import sys
|
|
14
|
+
import time
|
|
14
15
|
from decimal import Decimal
|
|
15
16
|
from io import BytesIO
|
|
16
17
|
from ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
|
@@ -68,18 +69,22 @@ TYPES_CONVERTER = {
|
|
|
68
69
|
|
|
69
70
|
def re_auth(func):
|
|
70
71
|
def wrapper(self, *args, **kwargs):
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
print(f'RE_AUTH: Error Found {e}')
|
|
76
|
-
if e.code() == grpc.StatusCode.INTERNAL and 'Access denied' in e.details():
|
|
77
|
-
print('RE_AUTH: Initialising re-authentication.')
|
|
78
|
-
self.connection.get_re_authenticate_session_id()
|
|
79
|
-
print(f'RE_AUTH: Re-auth successful.')
|
|
72
|
+
max_retry = 5
|
|
73
|
+
current_retry = 0
|
|
74
|
+
while current_retry < max_retry:
|
|
75
|
+
try:
|
|
80
76
|
return func(self, *args, **kwargs)
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
except _InactiveRpcError as e:
|
|
78
|
+
current_retry += 1
|
|
79
|
+
if current_retry == max_retry:
|
|
80
|
+
raise e
|
|
81
|
+
if e.code() == grpc.StatusCode.INTERNAL and 'Access denied' in e.details():
|
|
82
|
+
time.sleep(0.2)
|
|
83
|
+
_logger.info(f'RE_AUTH: Function Name: {func}')
|
|
84
|
+
_logger.info(f'RE_AUTH: Error Found {e}')
|
|
85
|
+
self.connection.get_re_authenticate_session_id()
|
|
86
|
+
else:
|
|
87
|
+
raise e
|
|
83
88
|
|
|
84
89
|
return wrapper
|
|
85
90
|
|
|
@@ -169,6 +174,7 @@ class Connection(object):
|
|
|
169
174
|
- max_receive_message_length: This parameter sets the maximum allowed size (in bytes) for incoming messages on the gRPC server.
|
|
170
175
|
- max_send_message_length: Similar to max_receive_message_length, this parameter sets the maximum allowed size (in bytes) for outgoing messages from the gRPC client
|
|
171
176
|
- grpc_prepare_timeout: Timeout for prepare statement API call (default to 10 minutes).
|
|
177
|
+
- keepalive_time_ms: This parameter defines the time, in milliseconds, Default to 30 seconds
|
|
172
178
|
"""
|
|
173
179
|
if not username or not password:
|
|
174
180
|
raise ValueError("username or password cannot be empty.")
|
|
@@ -188,43 +194,88 @@ class Connection(object):
|
|
|
188
194
|
|
|
189
195
|
self._auto_resume = auto_resume
|
|
190
196
|
|
|
191
|
-
self.
|
|
192
|
-
self.
|
|
193
|
-
|
|
194
|
-
self.grpc_prepare_timeout = 10 * 60 # 10 minutes
|
|
195
|
-
|
|
196
|
-
if isinstance(grpc_options, dict):
|
|
197
|
-
self._keepalive_timeout_ms = grpc_options.get('keepalive_timeout_ms') or self._keepalive_timeout_ms
|
|
198
|
-
self._max_receive_message_length = grpc_options.get(
|
|
199
|
-
'max_receive_message_length') or self._max_receive_message_length
|
|
200
|
-
self._max_send_message_length = grpc_options.get('max_send_message_length') or self._max_send_message_length
|
|
201
|
-
self.grpc_prepare_timeout = grpc_options.get('grpc_prepare_timeout') or self.grpc_prepare_timeout
|
|
197
|
+
self._grpc_options = grpc_options
|
|
198
|
+
if self._grpc_options is None:
|
|
199
|
+
self._grpc_options = dict()
|
|
200
|
+
self.grpc_prepare_timeout = self._grpc_options.get('grpc_prepare_timeout') or 10 * 60 # 10 minutes
|
|
202
201
|
self._create_client()
|
|
203
202
|
|
|
203
|
+
@property
|
|
204
|
+
def _get_grpc_options(self):
|
|
205
|
+
"""
|
|
206
|
+
Property to get gRPC options for the connection.
|
|
207
|
+
|
|
208
|
+
This method checks if the gRPC options are already cached. If not, it creates a copy of the
|
|
209
|
+
provided gRPC options and merges them with the default options. The merged options are then
|
|
210
|
+
cached for future use.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
list: A list of tuples containing gRPC options.
|
|
214
|
+
"""
|
|
215
|
+
if not hasattr(self, '_cached_grpc_options'):
|
|
216
|
+
grpc_options = self._grpc_options.copy()
|
|
217
|
+
default_options = {
|
|
218
|
+
"keepalive_timeout_ms": 900000, # Time in milliseconds to keep the connection alive.
|
|
219
|
+
"max_receive_message_length": -1, # Maximum size of received messages.
|
|
220
|
+
"max_send_message_length": 300 * 1024 * 1024, # Maximum size of sent messages (300 MB).
|
|
221
|
+
"grpc_prepare_timeout": self.grpc_prepare_timeout, # Timeout for prepare statement API call.
|
|
222
|
+
"keepalive_time_ms": 30000, # Time in milliseconds between keep-alive pings.
|
|
223
|
+
"keepalive_permit_without_calls": 1, # Allow keep-alives with no active RPCs.
|
|
224
|
+
"http2.max_pings_without_data": 0, # Unlimited pings without data.
|
|
225
|
+
"http2.min_time_between_pings_ms": 15000, # Minimum time between pings (15 seconds).
|
|
226
|
+
"http2.min_ping_interval_without_data_ms": 15000, # Minimum interval between pings without data (15 seconds).
|
|
227
|
+
}
|
|
228
|
+
if grpc_options:
|
|
229
|
+
for key, value in grpc_options.items():
|
|
230
|
+
default_options[key] = value
|
|
231
|
+
|
|
232
|
+
self._cached_grpc_options = [(f'grpc.{key}', value) for key, value in default_options.items()]
|
|
233
|
+
|
|
234
|
+
return self._cached_grpc_options
|
|
235
|
+
|
|
204
236
|
def _create_client(self):
|
|
237
|
+
"""
|
|
238
|
+
Creates a gRPC client for the connection.
|
|
239
|
+
|
|
240
|
+
This method initializes a gRPC channel based on whether a secure channel is required or not.
|
|
241
|
+
It then creates a client stub for the QueryEngineService.
|
|
242
|
+
|
|
243
|
+
If the secure channel is enabled, it uses `grpc.secure_channel` with SSL credentials.
|
|
244
|
+
Otherwise, it uses `grpc.insecure_channel`.
|
|
245
|
+
|
|
246
|
+
The gRPC options are retrieved from the `_get_grpc_options` property.
|
|
247
|
+
|
|
248
|
+
Raises:
|
|
249
|
+
grpc.RpcError: If there is an error in creating the gRPC channel or client stub.
|
|
250
|
+
"""
|
|
205
251
|
if self._secure_channel:
|
|
206
252
|
self._channel = grpc.secure_channel(
|
|
207
253
|
target='{}:{}'.format(self._host, self._port),
|
|
208
|
-
options=
|
|
209
|
-
("grpc.keepalive_timeout_ms", self._keepalive_timeout_ms),
|
|
210
|
-
('grpc.max_send_message_length', self._max_send_message_length),
|
|
211
|
-
('grpc.max_receive_message_length', self._max_receive_message_length)
|
|
212
|
-
],
|
|
254
|
+
options=self._get_grpc_options,
|
|
213
255
|
credentials=grpc.ssl_channel_credentials()
|
|
214
256
|
)
|
|
215
257
|
else:
|
|
216
258
|
self._channel = grpc.insecure_channel(
|
|
217
259
|
target='{}:{}'.format(self._host, self._port),
|
|
218
|
-
options=
|
|
219
|
-
("grpc.keepalive_timeout_ms", self._keepalive_timeout_ms),
|
|
220
|
-
('grpc.max_send_message_length', self._max_send_message_length),
|
|
221
|
-
('grpc.max_receive_message_length', self._max_receive_message_length)
|
|
222
|
-
]
|
|
260
|
+
options=self._get_grpc_options
|
|
223
261
|
)
|
|
224
262
|
self._client = e6x_engine_pb2_grpc.QueryEngineServiceStub(self._channel)
|
|
225
263
|
|
|
226
264
|
def get_re_authenticate_session_id(self):
|
|
227
|
-
|
|
265
|
+
"""
|
|
266
|
+
Re-authenticates the session by closing the current connection and creating a new client.
|
|
267
|
+
|
|
268
|
+
This method is used to re-establish the session ID by closing the existing gRPC channel,
|
|
269
|
+
creating a new client, and then retrieving a new session ID.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
str: The new session ID after re-authentication.
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
Exception: If there is an error during the re-authentication process.
|
|
276
|
+
"""
|
|
277
|
+
self.close()
|
|
278
|
+
self._create_client()
|
|
228
279
|
return self.get_session_id
|
|
229
280
|
|
|
230
281
|
@property
|
|
@@ -277,35 +328,58 @@ class Connection(object):
|
|
|
277
328
|
raise e
|
|
278
329
|
return self._session_id
|
|
279
330
|
|
|
280
|
-
def
|
|
281
|
-
self.client.updateUsers(userInfo=user_info)
|
|
282
|
-
|
|
283
|
-
def set_prop_map(self, prop_map: str):
|
|
284
|
-
"""
|
|
285
|
-
To enable to disable the caches.
|
|
286
|
-
:param prop_map: To set engine props
|
|
331
|
+
def __enter__(self):
|
|
287
332
|
"""
|
|
288
|
-
|
|
289
|
-
self._client.setProps(set_props_request)
|
|
333
|
+
Enters the runtime context related to this object.
|
|
290
334
|
|
|
291
|
-
|
|
292
|
-
|
|
335
|
+
This method is called when the execution flow enters the context of the `with` statement.
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
Connection: The current instance of the connection.
|
|
339
|
+
"""
|
|
293
340
|
return self
|
|
294
341
|
|
|
295
342
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
296
|
-
"""
|
|
343
|
+
"""
|
|
344
|
+
Exits the runtime context related to this object.
|
|
345
|
+
|
|
346
|
+
This method is called when the execution flow exits the context of the `with` statement.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
exc_type (Type[BaseException]): The type of exception raised (if any).
|
|
350
|
+
exc_val (BaseException): The exception instance raised (if any).
|
|
351
|
+
exc_tb (Traceback): The traceback object of the exception (if any).
|
|
352
|
+
"""
|
|
297
353
|
self.close()
|
|
298
354
|
|
|
299
355
|
def close(self):
|
|
356
|
+
"""
|
|
357
|
+
Closes the gRPC channel and resets the session ID.
|
|
358
|
+
|
|
359
|
+
This method ensures that the gRPC channel is properly closed and the session ID is reset to None.
|
|
360
|
+
"""
|
|
300
361
|
if self._channel is not None:
|
|
301
362
|
self._channel.close()
|
|
302
363
|
self._channel = None
|
|
303
364
|
self._session_id = None
|
|
304
365
|
|
|
305
366
|
def check_connection(self):
|
|
367
|
+
"""
|
|
368
|
+
Checks if the gRPC channel is still open.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
bool: True if the gRPC channel is open, False otherwise.
|
|
372
|
+
"""
|
|
306
373
|
return self._channel is not None
|
|
307
374
|
|
|
308
375
|
def clear(self, query_id, engine_ip=None):
|
|
376
|
+
"""
|
|
377
|
+
Clears the query results from the server.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
query_id (str): The ID of the query to be cleared.
|
|
381
|
+
engine_ip (str, optional): The IP address of the engine. Defaults to None.
|
|
382
|
+
"""
|
|
309
383
|
clear_request = e6x_engine_pb2.ClearRequest(
|
|
310
384
|
sessionId=self.get_session_id,
|
|
311
385
|
queryId=query_id,
|
|
@@ -317,10 +391,22 @@ class Connection(object):
|
|
|
317
391
|
)
|
|
318
392
|
|
|
319
393
|
def reopen(self):
|
|
394
|
+
"""
|
|
395
|
+
Reopens the gRPC channel by closing the current channel and creating a new client.
|
|
396
|
+
|
|
397
|
+
This method is useful for re-establishing the connection if it was previously closed.
|
|
398
|
+
"""
|
|
320
399
|
self._channel.close()
|
|
321
400
|
self._create_client()
|
|
322
401
|
|
|
323
402
|
def query_cancel(self, engine_ip, query_id):
|
|
403
|
+
"""
|
|
404
|
+
Cancels the execution of a query on the server.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
engine_ip (str): The IP address of the engine.
|
|
408
|
+
query_id (str): The ID of the query to be canceled.
|
|
409
|
+
"""
|
|
324
410
|
cancel_query_request = e6x_engine_pb2.CancelQueryRequest(
|
|
325
411
|
engineIP=engine_ip,
|
|
326
412
|
sessionId=self.get_session_id,
|
|
@@ -332,6 +418,15 @@ class Connection(object):
|
|
|
332
418
|
)
|
|
333
419
|
|
|
334
420
|
def dry_run(self, query):
|
|
421
|
+
"""
|
|
422
|
+
Performs a dry run of the query to validate its syntax and structure.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
query (str): The SQL query to be validated.
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
str: The result of the dry run validation.
|
|
429
|
+
"""
|
|
335
430
|
dry_run_request = e6x_engine_pb2.DryRunRequest(
|
|
336
431
|
sessionId=self.get_session_id,
|
|
337
432
|
schema=self.database,
|
|
@@ -344,6 +439,16 @@ class Connection(object):
|
|
|
344
439
|
return dry_run_response.dryrunValue
|
|
345
440
|
|
|
346
441
|
def get_tables(self, catalog, database):
|
|
442
|
+
"""
|
|
443
|
+
Retrieves the list of tables from the specified catalog and database.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
catalog (str): The catalog name.
|
|
447
|
+
database (str): The database name.
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
list: A list of table names.
|
|
451
|
+
"""
|
|
347
452
|
get_table_request = e6x_engine_pb2.GetTablesV2Request(
|
|
348
453
|
sessionId=self.get_session_id,
|
|
349
454
|
schema=database,
|
|
@@ -356,6 +461,17 @@ class Connection(object):
|
|
|
356
461
|
return list(get_table_response.tables)
|
|
357
462
|
|
|
358
463
|
def get_columns(self, catalog, database, table):
|
|
464
|
+
"""
|
|
465
|
+
Retrieves the list of columns for the specified table in the given catalog and database.
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
catalog (str): The catalog name.
|
|
469
|
+
database (str): The database name.
|
|
470
|
+
table (str): The table name.
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
list: A list of dictionaries containing column information.
|
|
474
|
+
"""
|
|
359
475
|
get_columns_request = e6x_engine_pb2.GetColumnsV2Request(
|
|
360
476
|
sessionId=self.get_session_id,
|
|
361
477
|
schema=database,
|
|
@@ -369,6 +485,15 @@ class Connection(object):
|
|
|
369
485
|
return [{'fieldName': row.fieldName, 'fieldType': row.fieldType} for row in get_columns_response.fieldInfo]
|
|
370
486
|
|
|
371
487
|
def get_schema_names(self, catalog):
|
|
488
|
+
"""
|
|
489
|
+
Retrieves the list of schema names from the specified catalog.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
catalog (str): The catalog name.
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
list: A list of schema names.
|
|
496
|
+
"""
|
|
372
497
|
get_schema_request = e6x_engine_pb2.GetSchemaNamesV2Request(
|
|
373
498
|
sessionId=self.get_session_id,
|
|
374
499
|
catalog=catalog
|
|
@@ -380,29 +505,65 @@ class Connection(object):
|
|
|
380
505
|
return list(get_schema_response.schemas)
|
|
381
506
|
|
|
382
507
|
def commit(self):
|
|
383
|
-
"""
|
|
508
|
+
"""
|
|
509
|
+
Commits the current transaction.
|
|
510
|
+
|
|
511
|
+
Note:
|
|
512
|
+
This method does nothing as transactions are not supported.
|
|
513
|
+
"""
|
|
384
514
|
pass
|
|
385
515
|
|
|
386
516
|
def cursor(self, catalog_name=None, db_name=None):
|
|
387
|
-
"""
|
|
517
|
+
"""
|
|
518
|
+
Creates a new cursor object for executing queries.
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
catalog_name (str, optional): The catalog name. Defaults to None.
|
|
522
|
+
db_name (str, optional): The database name. Defaults to None.
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
Cursor: A new cursor object.
|
|
526
|
+
"""
|
|
388
527
|
return Cursor(self, database=db_name, catalog_name=catalog_name)
|
|
389
528
|
|
|
390
529
|
def rollback(self):
|
|
391
|
-
|
|
530
|
+
"""
|
|
531
|
+
Rolls back the current transaction.
|
|
532
|
+
|
|
533
|
+
Raises:
|
|
534
|
+
Exception: Always raises an exception as transactions are not supported.
|
|
535
|
+
"""
|
|
536
|
+
raise Exception("e6data does not support transactions") # pragma: no cover
|
|
392
537
|
|
|
393
538
|
@property
|
|
394
539
|
def client(self):
|
|
540
|
+
"""
|
|
541
|
+
Returns the gRPC client stub for interacting with the server.
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
e6x_engine_pb2_grpc.QueryEngineServiceStub: The gRPC client stub.
|
|
545
|
+
"""
|
|
395
546
|
return self._client
|
|
396
547
|
|
|
397
548
|
|
|
398
549
|
class Cursor(DBAPICursor):
|
|
399
|
-
"""
|
|
550
|
+
"""
|
|
551
|
+
These objects represent a database cursor, which is used to manage the context of a fetch
|
|
400
552
|
operation.
|
|
401
553
|
Cursors are not isolated, i.e., any changes done to the database by a cursor are immediately
|
|
402
554
|
visible by other cursors or connections.
|
|
403
555
|
"""
|
|
404
556
|
|
|
405
557
|
def __init__(self, connection: Connection, array_size=1000, database=None, catalog_name=None):
|
|
558
|
+
"""
|
|
559
|
+
Initialize a new Cursor object.
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
connection (Connection): The connection object to the database.
|
|
563
|
+
array_size (int, optional): The number of rows to fetch at a time. Defaults to 1000.
|
|
564
|
+
database (str, optional): The database name. Defaults to None.
|
|
565
|
+
catalog_name (str, optional): The catalog name. Defaults to None.
|
|
566
|
+
"""
|
|
406
567
|
super(Cursor, self).__init__()
|
|
407
568
|
self._array_size = array_size
|
|
408
569
|
self.connection = connection
|
|
@@ -423,15 +584,32 @@ class Cursor(DBAPICursor):
|
|
|
423
584
|
|
|
424
585
|
@property
|
|
425
586
|
def metadata(self):
|
|
587
|
+
"""
|
|
588
|
+
Get the gRPC metadata for the current query.
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
list: A list of tuples containing gRPC metadata.
|
|
592
|
+
"""
|
|
426
593
|
return _get_grpc_header(engine_ip=self._engine_ip, cluster=self.connection.cluster_uuid)
|
|
427
594
|
|
|
428
595
|
@property
|
|
429
596
|
def arraysize(self):
|
|
597
|
+
"""
|
|
598
|
+
Get the array size for fetching rows.
|
|
599
|
+
|
|
600
|
+
Returns:
|
|
601
|
+
int: The number of rows to fetch at a time.
|
|
602
|
+
"""
|
|
430
603
|
return self._arraysize
|
|
431
604
|
|
|
432
605
|
@arraysize.setter
|
|
433
606
|
def arraysize(self, value):
|
|
434
|
-
"""
|
|
607
|
+
"""
|
|
608
|
+
Set the array size for fetching rows.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
value (int): The number of rows to fetch at a time.
|
|
612
|
+
"""
|
|
435
613
|
default_arraysize = 1000
|
|
436
614
|
try:
|
|
437
615
|
self._arraysize = int(value) or default_arraysize
|
|
@@ -467,14 +645,29 @@ class Cursor(DBAPICursor):
|
|
|
467
645
|
return self._description
|
|
468
646
|
|
|
469
647
|
def __enter__(self):
|
|
648
|
+
"""
|
|
649
|
+
Enter the runtime context related to this object.
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
Cursor: The current instance of the cursor.
|
|
653
|
+
"""
|
|
470
654
|
return self
|
|
471
655
|
|
|
472
656
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
657
|
+
"""
|
|
658
|
+
Exit the runtime context related to this object.
|
|
659
|
+
|
|
660
|
+
Args:
|
|
661
|
+
exc_type (Type[BaseException]): The type of exception raised (if any).
|
|
662
|
+
exc_val (BaseException): The exception instance raised (if any).
|
|
663
|
+
exc_tb (Traceback): The traceback object of the exception (if any).
|
|
664
|
+
"""
|
|
473
665
|
self.close()
|
|
474
666
|
|
|
475
667
|
def close(self):
|
|
476
|
-
"""
|
|
477
|
-
|
|
668
|
+
"""
|
|
669
|
+
Close the operation handle and reset the cursor state.
|
|
670
|
+
"""
|
|
478
671
|
try:
|
|
479
672
|
self.clear()
|
|
480
673
|
except:
|
|
@@ -491,17 +684,44 @@ class Cursor(DBAPICursor):
|
|
|
491
684
|
self._database = None
|
|
492
685
|
|
|
493
686
|
def get_tables(self):
|
|
687
|
+
"""
|
|
688
|
+
Retrieve the list of tables from the current database.
|
|
689
|
+
|
|
690
|
+
Returns:
|
|
691
|
+
list: A list of table names.
|
|
692
|
+
"""
|
|
494
693
|
schema = self.connection.database
|
|
495
694
|
return self.connection.get_tables(catalog=self._catalog_name, database=schema)
|
|
496
695
|
|
|
497
696
|
def get_columns(self, table):
|
|
697
|
+
"""
|
|
698
|
+
Retrieve the list of columns for the specified table.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
table (str): The table name.
|
|
702
|
+
|
|
703
|
+
Returns:
|
|
704
|
+
list: A list of dictionaries containing column information.
|
|
705
|
+
"""
|
|
498
706
|
schema = self.connection.database
|
|
499
707
|
return self.connection.get_columns(catalog=self._catalog_name, database=schema, table=table)
|
|
500
708
|
|
|
501
709
|
def get_schema_names(self):
|
|
710
|
+
"""
|
|
711
|
+
Retrieve the list of schema names from the current catalog.
|
|
712
|
+
|
|
713
|
+
Returns:
|
|
714
|
+
list: A list of schema names.
|
|
715
|
+
"""
|
|
502
716
|
return self.connection.get_schema_names(catalog=self._catalog_name)
|
|
503
717
|
|
|
504
718
|
def clear(self, query_id=None):
|
|
719
|
+
"""
|
|
720
|
+
Clear the query results from the server.
|
|
721
|
+
|
|
722
|
+
Args:
|
|
723
|
+
query_id (str, optional): The ID of the query to be cleared. Defaults to None.
|
|
724
|
+
"""
|
|
505
725
|
if not query_id:
|
|
506
726
|
query_id = self._query_id
|
|
507
727
|
clear_request = e6x_engine_pb2.ClearOrCancelQueryRequest(
|
|
@@ -512,9 +732,24 @@ class Cursor(DBAPICursor):
|
|
|
512
732
|
return self.connection.client.clearOrCancelQuery(clear_request, metadata=self.metadata)
|
|
513
733
|
|
|
514
734
|
def cancel(self, query_id):
|
|
735
|
+
"""
|
|
736
|
+
Cancel the execution of a query on the server.
|
|
737
|
+
|
|
738
|
+
Args:
|
|
739
|
+
query_id (str): The ID of the query to be canceled.
|
|
740
|
+
"""
|
|
515
741
|
self.connection.query_cancel(engine_ip=self._engine_ip, query_id=query_id)
|
|
516
742
|
|
|
517
743
|
def status(self, query_id):
|
|
744
|
+
"""
|
|
745
|
+
Get the status of the specified query.
|
|
746
|
+
|
|
747
|
+
Args:
|
|
748
|
+
query_id (str): The ID of the query.
|
|
749
|
+
|
|
750
|
+
Returns:
|
|
751
|
+
StatusResponse: The status response of the query.
|
|
752
|
+
"""
|
|
518
753
|
status_request = e6x_engine_pb2.StatusRequest(
|
|
519
754
|
sessionId=self.connection.get_session_id,
|
|
520
755
|
queryId=query_id,
|
|
@@ -524,12 +759,17 @@ class Cursor(DBAPICursor):
|
|
|
524
759
|
|
|
525
760
|
@re_auth
|
|
526
761
|
def execute(self, operation, parameters=None, **kwargs):
|
|
527
|
-
"""Prepare and execute a database operation (query or command).
|
|
528
|
-
Return values are not defined.
|
|
529
762
|
"""
|
|
763
|
+
Prepare and execute a database operation (query or command).
|
|
764
|
+
|
|
765
|
+
Args:
|
|
766
|
+
operation (str): The SQL query or command to execute.
|
|
767
|
+
parameters (dict, optional): The parameters to bind to the query. Defaults to None.
|
|
768
|
+
|
|
769
|
+
Returns:
|
|
770
|
+
str: The query ID of the executed query.
|
|
530
771
|
"""
|
|
531
|
-
Semicolon is now not supported. So removing it from query end.
|
|
532
|
-
"""
|
|
772
|
+
# Semicolon is now not supported. So removing it from query end.
|
|
533
773
|
operation = operation.strip() # Remove leading and trailing whitespaces.
|
|
534
774
|
if operation.endswith(';'):
|
|
535
775
|
operation = operation[:-1]
|
|
@@ -593,10 +833,19 @@ class Cursor(DBAPICursor):
|
|
|
593
833
|
|
|
594
834
|
@property
|
|
595
835
|
def rowcount(self):
|
|
836
|
+
"""
|
|
837
|
+
Get the number of rows affected by the last execute operation.
|
|
838
|
+
|
|
839
|
+
Returns:
|
|
840
|
+
int: The number of rows affected.
|
|
841
|
+
"""
|
|
596
842
|
self.update_mete_data()
|
|
597
843
|
return self._rowcount
|
|
598
844
|
|
|
599
845
|
def update_mete_data(self):
|
|
846
|
+
"""
|
|
847
|
+
Update the metadata for the current query.
|
|
848
|
+
"""
|
|
600
849
|
result_meta_data_request = e6x_engine_pb2.GetResultMetadataRequest(
|
|
601
850
|
engineIP=self._engine_ip,
|
|
602
851
|
sessionId=self.connection.get_session_id,
|
|
@@ -611,6 +860,12 @@ class Cursor(DBAPICursor):
|
|
|
611
860
|
self._is_metadata_updated = True
|
|
612
861
|
|
|
613
862
|
def _fetch_more(self):
|
|
863
|
+
"""
|
|
864
|
+
Fetch more rows from the server.
|
|
865
|
+
|
|
866
|
+
Returns:
|
|
867
|
+
list: A list of rows fetched from the server.
|
|
868
|
+
"""
|
|
614
869
|
batch_size = self._arraysize
|
|
615
870
|
self._data = list()
|
|
616
871
|
for i in range(batch_size):
|
|
@@ -621,6 +876,12 @@ class Cursor(DBAPICursor):
|
|
|
621
876
|
return self._data
|
|
622
877
|
|
|
623
878
|
def _fetch_all(self):
|
|
879
|
+
"""
|
|
880
|
+
Fetch all rows from the server.
|
|
881
|
+
|
|
882
|
+
Returns:
|
|
883
|
+
list: A list of all rows fetched from the server.
|
|
884
|
+
"""
|
|
624
885
|
self._data = list()
|
|
625
886
|
while True:
|
|
626
887
|
rows = self.fetch_batch()
|
|
@@ -632,6 +893,15 @@ class Cursor(DBAPICursor):
|
|
|
632
893
|
return rows
|
|
633
894
|
|
|
634
895
|
def fetchall_buffer(self, query_id=None):
|
|
896
|
+
"""
|
|
897
|
+
Fetch all rows from the server in a buffered manner.
|
|
898
|
+
|
|
899
|
+
Args:
|
|
900
|
+
query_id (str, optional): The ID of the query. Defaults to None.
|
|
901
|
+
|
|
902
|
+
Yields:
|
|
903
|
+
list: A list of rows fetched from the server.
|
|
904
|
+
"""
|
|
635
905
|
if query_id:
|
|
636
906
|
self._query_id = query_id
|
|
637
907
|
while True:
|
|
@@ -641,6 +911,12 @@ class Cursor(DBAPICursor):
|
|
|
641
911
|
yield rows
|
|
642
912
|
|
|
643
913
|
def fetch_batch(self):
|
|
914
|
+
"""
|
|
915
|
+
Fetch a batch of rows from the server.
|
|
916
|
+
|
|
917
|
+
Returns:
|
|
918
|
+
list: A list of rows fetched from the server.
|
|
919
|
+
"""
|
|
644
920
|
client = self.connection.client
|
|
645
921
|
get_next_result_batch_request = e6x_engine_pb2.GetNextResultBatchRequest(
|
|
646
922
|
engineIP=self._engine_ip,
|
|
@@ -660,9 +936,24 @@ class Cursor(DBAPICursor):
|
|
|
660
936
|
return read_rows_from_chunk(self._query_columns_description, buffer)
|
|
661
937
|
|
|
662
938
|
def fetchall(self):
|
|
939
|
+
"""
|
|
940
|
+
Fetch all rows from the server.
|
|
941
|
+
|
|
942
|
+
Returns:
|
|
943
|
+
list: A list of all rows fetched from the server.
|
|
944
|
+
"""
|
|
663
945
|
return self._fetch_all()
|
|
664
946
|
|
|
665
947
|
def fetchmany(self, size: int = None):
|
|
948
|
+
"""
|
|
949
|
+
Fetch a specified number of rows from the server.
|
|
950
|
+
|
|
951
|
+
Args:
|
|
952
|
+
size (int, optional): The number of rows to fetch. Defaults to None.
|
|
953
|
+
|
|
954
|
+
Returns:
|
|
955
|
+
list: A list of rows fetched from the server.
|
|
956
|
+
"""
|
|
666
957
|
if size is None:
|
|
667
958
|
size = self.arraysize
|
|
668
959
|
if self._data is None:
|
|
@@ -681,13 +972,24 @@ class Cursor(DBAPICursor):
|
|
|
681
972
|
return rows
|
|
682
973
|
|
|
683
974
|
def fetchone(self):
|
|
684
|
-
|
|
975
|
+
"""
|
|
976
|
+
Fetch a single row from the server.
|
|
977
|
+
|
|
978
|
+
Returns:
|
|
979
|
+
list: A single row fetched from the server.
|
|
980
|
+
"""
|
|
685
981
|
rows = self.fetchmany(1)
|
|
686
982
|
if rows is None or len(rows) == 0:
|
|
687
983
|
return None
|
|
688
984
|
return rows
|
|
689
985
|
|
|
690
986
|
def explain(self):
|
|
987
|
+
"""
|
|
988
|
+
Get the execution plan for the current query.
|
|
989
|
+
|
|
990
|
+
Returns:
|
|
991
|
+
str: The execution plan of the query.
|
|
992
|
+
"""
|
|
691
993
|
explain_request = e6x_engine_pb2.ExplainRequest(
|
|
692
994
|
engineIP=self._engine_ip,
|
|
693
995
|
sessionId=self.connection.get_session_id,
|
|
@@ -700,6 +1002,12 @@ class Cursor(DBAPICursor):
|
|
|
700
1002
|
return explain_response.explain
|
|
701
1003
|
|
|
702
1004
|
def explain_analyse(self):
|
|
1005
|
+
"""
|
|
1006
|
+
Get the execution plan for the current query.
|
|
1007
|
+
|
|
1008
|
+
Returns:
|
|
1009
|
+
dict: The execution plan of the query.
|
|
1010
|
+
"""
|
|
703
1011
|
explain_analyze_request = e6x_engine_pb2.ExplainAnalyzeRequest(
|
|
704
1012
|
engineIP=self._engine_ip,
|
|
705
1013
|
sessionId=self.connection.get_session_id,
|
{e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: e6data-python-connector
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Summary: Client for the e6data distributed SQL Engine.
|
|
5
5
|
Home-page: https://github.com/e6x-labs/e6data-python-connector
|
|
6
6
|
Author: e6data, Inc.
|
|
@@ -38,7 +38,7 @@ Dynamic: summary
|
|
|
38
38
|
|
|
39
39
|
# e6data Python Connector
|
|
40
40
|
|
|
41
|
-

|
|
42
42
|
|
|
43
43
|
## Introduction
|
|
44
44
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
e6data_python_connector/__init__.py,sha256=x_VwhPQ7XLlthR-NJL9Vl7lkLBRCG2pSjmyZSBHOBpM,103
|
|
2
|
-
e6data_python_connector/cluster_manager.py,sha256=
|
|
2
|
+
e6data_python_connector/cluster_manager.py,sha256=rg-eEYP-tQF4Yoj7w0fMORlO3nl2iqXThd168zqFGBs,11060
|
|
3
3
|
e6data_python_connector/common.py,sha256=nk0CDQ5j1iu8DctBOXX4QypPYmxSgIgoA-Cn1UZXLic,9976
|
|
4
4
|
e6data_python_connector/constants.py,sha256=h5w0lWiFsxz__zUZRlo1f72-8mT_vt9gcWvtWh0viFI,682
|
|
5
5
|
e6data_python_connector/datainputstream.py,sha256=2XKW__PCGfTQ55Puqgie2LZdk_WU-YKwpEIUOQEF-sY,12592
|
|
6
6
|
e6data_python_connector/date_time_utils.py,sha256=zmIwRhEGJwDvCOUjAl6VTDWGppluA-DPl_ioHTNpJKI,14413
|
|
7
7
|
e6data_python_connector/dialect.py,sha256=hkVRjXxx9DhS8-aHpSoyMVhhHxVs3jdTE60x2aOAf5I,11312
|
|
8
|
-
e6data_python_connector/e6data_grpc.py,sha256=
|
|
8
|
+
e6data_python_connector/e6data_grpc.py,sha256=qFLpaG0cceY_HiuFxOgNON8IknHkSH1uZTDS02RR_KY,36279
|
|
9
9
|
e6data_python_connector/exceptions.py,sha256=vrUqdzMq3LjbCmvr5_TcRq9eKmdqGfe-uVjR0BJQf5U,382
|
|
10
10
|
e6data_python_connector/typeId.py,sha256=2EMNWK2SO5CBReNuJphma6xp-69r9GDwwys66t-kSk0,1777
|
|
11
11
|
e6data_python_connector/cluster_server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -21,9 +21,9 @@ e6data_python_connector/server/e6x_engine_pb2.py,sha256=BxJjbijNrEqXQTWEhzLm54Fq
|
|
|
21
21
|
e6data_python_connector/server/e6x_engine_pb2.pyi,sha256=_aQDLFbSCQ7ubK0Z5ZKh9_VAHFaXbMV55MXYXr85srg,18693
|
|
22
22
|
e6data_python_connector/server/e6x_engine_pb2_grpc.py,sha256=n-eTCsAjsmpgPA_bDansKZq_zMmqkK6TlQGn6IdT6L4,46671
|
|
23
23
|
e6data_python_connector/server/ttypes.py,sha256=uWxwSp4m6e-7SeSEckbZtSduGYmfzXWM09iy5eltBPA,21227
|
|
24
|
-
e6data_python_connector-2.2.
|
|
25
|
-
e6data_python_connector-2.2.
|
|
26
|
-
e6data_python_connector-2.2.
|
|
27
|
-
e6data_python_connector-2.2.
|
|
28
|
-
e6data_python_connector-2.2.
|
|
29
|
-
e6data_python_connector-2.2.
|
|
24
|
+
e6data_python_connector-2.2.3.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
25
|
+
e6data_python_connector-2.2.3.dist-info/METADATA,sha256=3YC7YniJCrZ2lIKHoRFVYe1PfDJpdFCz6hN9uJjN2Ok,7309
|
|
26
|
+
e6data_python_connector-2.2.3.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
27
|
+
e6data_python_connector-2.2.3.dist-info/entry_points.txt,sha256=MDK4cAOND0preLvq2Tlpnz00wXnP_Y_UopqJsAf3U7Y,77
|
|
28
|
+
e6data_python_connector-2.2.3.dist-info/top_level.txt,sha256=ChOS6qLf-SQycXr0uBiVxomIfYfbH2SgwrOWhW79ab0,24
|
|
29
|
+
e6data_python_connector-2.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{e6data_python_connector-2.2.2.dist-info → e6data_python_connector-2.2.3.dist-info}/top_level.txt
RENAMED
|
File without changes
|