chdb 3.6.0__cp38-abi3-macosx_11_0_arm64.whl → 3.7.0__cp38-abi3-macosx_11_0_arm64.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.

Potentially problematic release.


This version of chdb might be problematic. Click here for more details.

chdb/dbapi/cursors.py CHANGED
@@ -13,14 +13,32 @@ RE_INSERT_VALUES = re.compile(
13
13
 
14
14
 
15
15
  class Cursor(object):
16
- """
17
- This is the object you use to interact with the database.
18
-
19
- Do not create an instance of a Cursor yourself. Call
20
- connections.Connection.cursor().
21
-
22
- See `Cursor <https://www.python.org/dev/peps/pep-0249/#cursor-objects>`_ in
23
- the specification.
16
+ """DB-API 2.0 cursor for executing queries and fetching results.
17
+
18
+ The cursor provides methods for executing SQL statements, managing query results,
19
+ and navigating through result sets. It supports parameter binding, bulk operations,
20
+ and follows DB-API 2.0 specifications.
21
+
22
+ Do not create Cursor instances directly. Use Connection.cursor() instead.
23
+
24
+ Attributes:
25
+ description (tuple): Column metadata for the last query result
26
+ rowcount (int): Number of rows affected by the last query (-1 if unknown)
27
+ arraysize (int): Default number of rows to fetch at once (default: 1)
28
+ lastrowid: ID of the last inserted row (if applicable)
29
+ max_stmt_length (int): Maximum statement size for executemany() (default: 1024000)
30
+
31
+ Examples:
32
+ >>> conn = Connection()
33
+ >>> cur = conn.cursor()
34
+ >>> cur.execute("SELECT 1 as id, 'test' as name")
35
+ >>> result = cur.fetchone()
36
+ >>> print(result) # (1, 'test')
37
+ >>> cur.close()
38
+
39
+ Note:
40
+ See `DB-API 2.0 Cursor Objects <https://www.python.org/dev/peps/pep-0249/#cursor-objects>`_
41
+ for complete specification details.
24
42
  """
25
43
 
26
44
  #: Max statement size which :meth:`executemany` generates.
@@ -29,6 +47,11 @@ class Cursor(object):
29
47
  max_stmt_length = 1024000
30
48
 
31
49
  def __init__(self, connection):
50
+ """Initialize cursor for the given connection.
51
+
52
+ Args:
53
+ connection (Connection): Database connection to use
54
+ """
32
55
  self.connection = connection
33
56
  self._cursor = connection._conn.cursor()
34
57
  self.description = None
@@ -38,58 +61,89 @@ class Cursor(object):
38
61
  self._executed = None
39
62
 
40
63
  def __enter__(self):
64
+ """Enter context manager and return self.
65
+
66
+ Returns:
67
+ Cursor: This cursor instance
68
+ """
41
69
  return self
42
70
 
43
71
  def __exit__(self, *exc_info):
72
+ """Exit context manager and close cursor.
73
+
74
+ Args:
75
+ *exc_info: Exception information (ignored)
76
+ """
44
77
  del exc_info
45
78
  self.close()
46
79
 
47
80
  def __iter__(self):
81
+ """Make cursor iterable over result rows.
82
+
83
+ Returns:
84
+ iterator: Iterator yielding rows until None is returned
85
+
86
+ Example:
87
+ >>> cur.execute("SELECT id FROM users")
88
+ >>> for row in cur:
89
+ ... print(row[0])
90
+ """
48
91
  return iter(self.fetchone, None)
49
92
 
50
93
  def callproc(self, procname, args=()):
51
- """Execute stored procedure procname with args
52
-
53
- procname -- string, name of procedure to execute on server
54
-
55
- args -- Sequence of parameters to use with procedure
56
-
57
- Returns the original args.
58
-
59
- Compatibility warning: PEP-249 specifies that any modified
60
- parameters must be returned. This is currently impossible
61
- as they are only available by storing them in a server
62
- variable and then retrieved by a query. Since stored
63
- procedures return zero or more result sets, there is no
64
- reliable way to get at OUT or INOUT parameters via callproc.
65
- The server variables are named @_procname_n, where procname
66
- is the parameter above and n is the position of the parameter
67
- (from zero). Once all result sets generated by the procedure
68
- have been fetched, you can issue a SELECT @_procname_0, ...
69
- query using .execute() to get any OUT or INOUT values.
70
-
71
- Compatibility warning: The act of calling a stored procedure
72
- itself creates an empty result set. This appears after any
73
- result sets generated by the procedure. This is non-standard
74
- behavior with respect to the DB-API. Be sure to use nextset()
75
- to advance through all result sets; otherwise you may get
76
- disconnected.
94
+ """Execute a stored procedure (placeholder implementation).
95
+
96
+ Args:
97
+ procname (str): Name of stored procedure to execute
98
+ args (sequence): Parameters to pass to the procedure
99
+
100
+ Returns:
101
+ sequence: The original args parameter (unmodified)
102
+
103
+ Note:
104
+ chDB/ClickHouse does not support stored procedures in the traditional sense.
105
+ This method is provided for DB-API 2.0 compliance but does not perform
106
+ any actual operation. Use execute() for all SQL operations.
107
+
108
+ Compatibility Warning:
109
+ This is a placeholder implementation. Traditional stored procedure
110
+ features like OUT/INOUT parameters, multiple result sets, and server
111
+ variables are not supported by the underlying ClickHouse engine.
77
112
  """
78
113
 
79
114
  return args
80
115
 
81
116
  def close(self):
82
- """
83
- Closing a cursor just exhausts all remaining data.
117
+ """Close the cursor and free associated resources.
118
+
119
+ After closing, the cursor becomes unusable and any operation will raise an exception.
120
+ Closing a cursor exhausts all remaining data and releases the underlying cursor.
84
121
  """
85
122
  self._cursor.close()
86
123
 
87
124
  def _get_db(self):
125
+ """Internal method to get the database connection.
126
+
127
+ Returns:
128
+ Connection: The database connection
129
+
130
+ Raises:
131
+ ProgrammingError: If cursor is closed
132
+ """
88
133
  if not self.connection:
89
134
  raise err.ProgrammingError("Cursor closed")
90
135
  return self.connection
91
136
 
92
137
  def _escape_args(self, args, conn):
138
+ """Internal method to escape query arguments.
139
+
140
+ Args:
141
+ args (tuple/list/dict): Arguments to escape
142
+ conn (Connection): Database connection for escaping
143
+
144
+ Returns:
145
+ Escaped arguments in the same structure as input
146
+ """
93
147
  if isinstance(args, (tuple, list)):
94
148
  return tuple(conn.escape(arg) for arg in args)
95
149
  elif isinstance(args, dict):
@@ -100,7 +154,22 @@ class Cursor(object):
100
154
  return conn.escape(args)
101
155
 
102
156
  def _format_query(self, query, args, conn):
103
- """Format query with arguments supporting ? and % placeholders."""
157
+ """Format SQL query by substituting parameter placeholders.
158
+
159
+ This internal method handles parameter binding for both question mark (?) and
160
+ format (%s) style placeholders, with proper escaping for SQL injection prevention.
161
+
162
+ Args:
163
+ query (str): SQL query with parameter placeholders
164
+ args (tuple/list): Parameter values to substitute
165
+ conn (Connection): Database connection for escaping values
166
+
167
+ Returns:
168
+ str: SQL query with parameters substituted and properly escaped
169
+
170
+ Note:
171
+ This is an internal method. Use execute() or mogrify() instead.
172
+ """
104
173
  if args is None or ('?' not in query and '%' not in query):
105
174
  return query
106
175
 
@@ -143,29 +212,59 @@ class Cursor(object):
143
212
  return ''.join(result)
144
213
 
145
214
  def mogrify(self, query, args=None):
146
- """
147
- Returns the exact string that is sent to the database by calling the
148
- execute() method.
215
+ """Return the exact query string that would be sent to the database.
149
216
 
150
- This method follows the extension to the DB API 2.0 followed by Psycopg.
151
- """
152
- conn = self._get_db()
153
- return self._format_query(query, args, conn)
217
+ This method shows the final SQL query after parameter substitution,
218
+ which is useful for debugging and logging purposes.
154
219
 
155
- def execute(self, query, args=None):
156
- """Execute a query
220
+ Args:
221
+ query (str): SQL query with parameter placeholders
222
+ args (tuple/list/dict, optional): Parameters to substitute
157
223
 
158
- :param str query: Query to execute.
224
+ Returns:
225
+ str: The final SQL query string with parameters substituted
159
226
 
160
- :param args: parameters used with query. (optional)
161
- :type args: tuple, list or dict
227
+ Example:
228
+ >>> cur.mogrify("SELECT * FROM users WHERE id = ?", (123,))
229
+ "SELECT * FROM users WHERE id = 123"
162
230
 
163
- :return: Number of affected rows
164
- :rtype: int
231
+ Note:
232
+ This method follows the extension to DB-API 2.0 used by Psycopg.
233
+ """
234
+ conn = self._get_db()
235
+ return self._format_query(query, args, conn)
165
236
 
166
- If args is a list or tuple, ? can be used as a placeholder in the query.
167
- If args is a dict, %(name)s can be used as a placeholder in the query.
168
- Also supports %s placeholder for backward compatibility.
237
+ def execute(self, query, args=None):
238
+ """Execute a SQL query with optional parameter binding.
239
+
240
+ This method executes a single SQL statement with optional parameter substitution.
241
+ It supports multiple parameter placeholder styles for flexibility.
242
+
243
+ Args:
244
+ query (str): SQL query to execute
245
+ args (tuple/list/dict, optional): Parameters to bind to placeholders
246
+
247
+ Returns:
248
+ int: Number of affected rows (-1 if unknown)
249
+
250
+ Parameter Styles:
251
+ - Question mark style: "SELECT * FROM users WHERE id = ?"
252
+ - Named style: "SELECT * FROM users WHERE name = %(name)s"
253
+ - Format style: "SELECT * FROM users WHERE age = %s" (legacy)
254
+
255
+ Examples:
256
+ >>> # Question mark parameters
257
+ >>> cur.execute("SELECT * FROM users WHERE id = ? AND age > ?", (123, 18))
258
+ >>>
259
+ >>> # Named parameters
260
+ >>> cur.execute("SELECT * FROM users WHERE name = %(name)s", {'name': 'Alice'})
261
+ >>>
262
+ >>> # No parameters
263
+ >>> cur.execute("SELECT COUNT(*) FROM users")
264
+
265
+ Raises:
266
+ ProgrammingError: If cursor is closed or query is malformed
267
+ InterfaceError: If database error occurs during execution
169
268
  """
170
269
  query = self._format_query(query, args, self.connection)
171
270
  self._cursor.execute(query)
@@ -189,16 +288,36 @@ class Cursor(object):
189
288
  return self.rowcount
190
289
 
191
290
  def executemany(self, query, args):
192
- # type: (str, list) -> int
193
- """Run several data against one query
194
-
195
- :param query: query to execute on server
196
- :param args: Sequence of sequences or mappings. It is used as parameter.
197
- :return: Number of rows affected, if any.
198
-
199
- This method improves performance on multiple-row INSERT and
200
- REPLACE. Otherwise, it is equivalent to looping over args with
201
- execute().
291
+ """Execute a query multiple times with different parameter sets.
292
+
293
+ This method efficiently executes the same SQL query multiple times with
294
+ different parameter values. It's particularly useful for bulk INSERT operations.
295
+
296
+ Args:
297
+ query (str): SQL query to execute multiple times
298
+ args (sequence): Sequence of parameter tuples/dicts/lists for each execution
299
+
300
+ Returns:
301
+ int: Total number of affected rows across all executions
302
+
303
+ Examples:
304
+ >>> # Bulk insert with question mark parameters
305
+ >>> users_data = [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie')]
306
+ >>> cur.executemany("INSERT INTO users VALUES (?, ?)", users_data)
307
+ >>>
308
+ >>> # Bulk insert with named parameters
309
+ >>> users_data = [
310
+ ... {'id': 1, 'name': 'Alice'},
311
+ ... {'id': 2, 'name': 'Bob'}
312
+ ... ]
313
+ >>> cur.executemany(
314
+ ... "INSERT INTO users VALUES (%(id)s, %(name)s)",
315
+ ... users_data
316
+ ... )
317
+
318
+ Note:
319
+ This method improves performance for multiple-row INSERT and UPDATE operations
320
+ by optimizing the query execution process.
202
321
  """
203
322
  if not args:
204
323
  return 0
@@ -318,34 +437,109 @@ class Cursor(object):
318
437
  return rows
319
438
 
320
439
  def _check_executed(self):
440
+ """Internal method to verify that execute() has been called.
441
+
442
+ Raises:
443
+ ProgrammingError: If no query has been executed yet
444
+ """
321
445
  if not self._executed:
322
446
  raise err.ProgrammingError("execute() first")
323
447
 
324
448
  def fetchone(self):
325
- """Fetch the next row"""
449
+ """Fetch the next row from the query result.
450
+
451
+ Returns:
452
+ tuple or None: Next row as a tuple, or None if no more rows available
453
+
454
+ Raises:
455
+ ProgrammingError: If execute() has not been called first
456
+
457
+ Example:
458
+ >>> cursor.execute("SELECT id, name FROM users LIMIT 3")
459
+ >>> row = cursor.fetchone()
460
+ >>> print(row) # (1, 'Alice')
461
+ >>> row = cursor.fetchone()
462
+ >>> print(row) # (2, 'Bob')
463
+ """
326
464
  if not self._executed:
327
465
  raise err.ProgrammingError("execute() first")
328
466
  return self._cursor.fetchone()
329
467
 
330
468
  def fetchmany(self, size=1):
331
- """Fetch several rows"""
469
+ """Fetch multiple rows from the query result.
470
+
471
+ Args:
472
+ size (int, optional): Number of rows to fetch. Defaults to 1.
473
+ If not specified, uses cursor.arraysize.
474
+
475
+ Returns:
476
+ list: List of tuples representing the fetched rows
477
+
478
+ Raises:
479
+ ProgrammingError: If execute() has not been called first
480
+
481
+ Example:
482
+ >>> cursor.execute("SELECT id, name FROM users")
483
+ >>> rows = cursor.fetchmany(3)
484
+ >>> print(rows) # [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie')]
485
+ """
332
486
  if not self._executed:
333
487
  raise err.ProgrammingError("execute() first")
334
488
  return self._cursor.fetchmany(size)
335
489
 
336
490
  def fetchall(self):
337
- """Fetch all the rows"""
491
+ """Fetch all remaining rows from the query result.
492
+
493
+ Returns:
494
+ list: List of tuples representing all remaining rows
495
+
496
+ Raises:
497
+ ProgrammingError: If execute() has not been called first
498
+
499
+ Warning:
500
+ This method can consume large amounts of memory for big result sets.
501
+ Consider using fetchmany() for large datasets.
502
+
503
+ Example:
504
+ >>> cursor.execute("SELECT id, name FROM users")
505
+ >>> all_rows = cursor.fetchall()
506
+ >>> print(len(all_rows)) # Number of total rows
507
+ """
338
508
  if not self._executed:
339
509
  raise err.ProgrammingError("execute() first")
340
510
  return self._cursor.fetchall()
341
511
 
342
512
  def nextset(self):
343
- """Get the next query set"""
513
+ """Move to the next result set (not supported).
514
+
515
+ Returns:
516
+ None: Always returns None as multiple result sets are not supported
517
+
518
+ Note:
519
+ chDB/ClickHouse does not support multiple result sets from a single query.
520
+ This method is provided for DB-API 2.0 compliance but always returns None.
521
+ """
344
522
  # Not support for now
345
523
  return None
346
524
 
347
525
  def setinputsizes(self, *args):
348
- """Does nothing, required by DB API."""
526
+ """Set input sizes for parameters (no-op implementation).
527
+
528
+ Args:
529
+ *args: Parameter size specifications (ignored)
530
+
531
+ Note:
532
+ This method does nothing but is required by DB-API 2.0 specification.
533
+ chDB automatically handles parameter sizing internally.
534
+ """
349
535
 
350
536
  def setoutputsizes(self, *args):
351
- """Does nothing, required by DB API."""
537
+ """Set output column sizes (no-op implementation).
538
+
539
+ Args:
540
+ *args: Column size specifications (ignored)
541
+
542
+ Note:
543
+ This method does nothing but is required by DB-API 2.0 specification.
544
+ chDB automatically handles output sizing internally.
545
+ """