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/.flake8 +5 -0
- chdb/__init__.py +132 -11
- chdb/_chdb.abi3.so +0 -0
- chdb/build-musl.sh +166 -0
- chdb/build.sh +370 -0
- chdb/build_linux_arm64.sh +63 -0
- chdb/build_mac_arm64.sh +121 -0
- chdb/build_pybind11.sh +131 -0
- chdb/dataframe/__init__.py +7 -2
- chdb/dataframe/query.py +211 -23
- chdb/dbapi/__init__.py +57 -2
- chdb/dbapi/connections.py +169 -12
- chdb/dbapi/converters.py +352 -34
- chdb/dbapi/cursors.py +264 -70
- chdb/dbapi/err.py +269 -30
- chdb/dbapi/times.py +171 -0
- chdb/libpybind11nonlimitedapi_chdb_3.10.dylib +0 -0
- chdb/libpybind11nonlimitedapi_chdb_3.11.dylib +0 -0
- chdb/libpybind11nonlimitedapi_chdb_3.12.dylib +0 -0
- chdb/libpybind11nonlimitedapi_chdb_3.13.dylib +0 -0
- chdb/libpybind11nonlimitedapi_chdb_3.8.dylib +0 -0
- chdb/libpybind11nonlimitedapi_chdb_3.9.dylib +0 -0
- chdb/libpybind11nonlimitedapi_stubs.dylib +0 -0
- chdb/session/state.py +167 -4
- chdb/state/sqlitelike.py +608 -34
- chdb/test_smoke.sh +32 -0
- chdb/udf/__init__.py +7 -0
- chdb/udf/udf.py +41 -25
- chdb/utils/__init__.py +6 -0
- chdb/utils/trace.py +31 -0
- chdb/utils/types.py +62 -64
- chdb/vars.sh +48 -0
- {chdb-3.6.0.dist-info → chdb-3.7.0.dist-info}/METADATA +29 -18
- chdb-3.7.0.dist-info/RECORD +43 -0
- chdb-3.6.0.dist-info/RECORD +0 -35
- {chdb-3.6.0.dist-info → chdb-3.7.0.dist-info}/LICENSE.txt +0 -0
- {chdb-3.6.0.dist-info → chdb-3.7.0.dist-info}/WHEEL +0 -0
- {chdb-3.6.0.dist-info → chdb-3.7.0.dist-info}/top_level.txt +0 -0
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Returns
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
156
|
-
|
|
220
|
+
Args:
|
|
221
|
+
query (str): SQL query with parameter placeholders
|
|
222
|
+
args (tuple/list/dict, optional): Parameters to substitute
|
|
157
223
|
|
|
158
|
-
:
|
|
224
|
+
Returns:
|
|
225
|
+
str: The final SQL query string with parameters substituted
|
|
159
226
|
|
|
160
|
-
:
|
|
161
|
-
|
|
227
|
+
Example:
|
|
228
|
+
>>> cur.mogrify("SELECT * FROM users WHERE id = ?", (123,))
|
|
229
|
+
"SELECT * FROM users WHERE id = 123"
|
|
162
230
|
|
|
163
|
-
:
|
|
164
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
|
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
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
+
"""
|