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.
@@ -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
- # Wait for the cluster to become active
233
- while True:
234
- try:
235
- status_payload = cluster_pb2.ClusterStatusRequest(
236
- user=self._user,
237
- password=self._password
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
- try:
72
- return func(self, *args, **kwargs)
73
- except _InactiveRpcError as e:
74
- print(f'RE_AUTH: Function Name: {func}')
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
- else:
82
- raise e
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._keepalive_timeout_ms = 900000
192
- self._max_receive_message_length = -1
193
- self._max_send_message_length = 300 * 1024 * 1024 # mb
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
- self._session_id = None
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 update_users(self, user_info):
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
- set_props_request = e6x_engine_pb2.SetPropsRequest(sessionId=self.get_session_id, props=prop_map)
289
- self._client.setProps(set_props_request)
333
+ Enters the runtime context related to this object.
290
334
 
291
- def __enter__(self):
292
- """Transport should already be opened by __init__"""
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
- """Call close"""
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
- """We do not support transactions, so this does nothing."""
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
- """Return a new :py:class:`Cursor` object using the connection."""
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
- raise Exception("e6xdb does not support transactions") # pragma: no cover
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
- """These objects represent a database cursor, which is used to manage the context of a fetch
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
- """Array size cannot be None, and should be an integer"""
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
- """Close the operation handle"""
477
- # self.connection.close()
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
- # _logger.info("fetch One returning the batch itself which is limited by predefined no.of rows")
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,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: e6data-python-connector
3
- Version: 2.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
- ![version](https://img.shields.io/badge/version-2.2.2-blue.svg)
41
+ ![version](https://img.shields.io/badge/version-2.2.3-blue.svg)
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=ZKxsMDgUuEKuRxNxo6vFhV8q4-Il3Rg-hX8niaACMso,10163
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=B5MsAKsI6Syx9QGDpDOXxQn4tjEwiByA4HevrSczbFs,26981
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.2.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
25
- e6data_python_connector-2.2.2.dist-info/METADATA,sha256=SDpjML-yRccpZOxJSqLr_L1O9Ph7YHkokCdwCYrjRzk,7309
26
- e6data_python_connector-2.2.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
27
- e6data_python_connector-2.2.2.dist-info/entry_points.txt,sha256=MDK4cAOND0preLvq2Tlpnz00wXnP_Y_UopqJsAf3U7Y,77
28
- e6data_python_connector-2.2.2.dist-info/top_level.txt,sha256=ChOS6qLf-SQycXr0uBiVxomIfYfbH2SgwrOWhW79ab0,24
29
- e6data_python_connector-2.2.2.dist-info/RECORD,,
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,,