sqlspec 0.10.1__py3-none-any.whl → 0.11.1__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.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/adapters/adbc/config.py +1 -1
- sqlspec/adapters/adbc/driver.py +340 -192
- sqlspec/adapters/aiosqlite/driver.py +183 -129
- sqlspec/adapters/asyncmy/driver.py +168 -88
- sqlspec/adapters/asyncpg/config.py +3 -1
- sqlspec/adapters/asyncpg/driver.py +208 -259
- sqlspec/adapters/bigquery/driver.py +184 -264
- sqlspec/adapters/duckdb/driver.py +172 -110
- sqlspec/adapters/oracledb/driver.py +274 -160
- sqlspec/adapters/psqlpy/driver.py +274 -211
- sqlspec/adapters/psycopg/driver.py +196 -283
- sqlspec/adapters/sqlite/driver.py +154 -142
- sqlspec/base.py +56 -85
- sqlspec/extensions/litestar/__init__.py +3 -12
- sqlspec/extensions/litestar/config.py +22 -7
- sqlspec/extensions/litestar/handlers.py +142 -85
- sqlspec/extensions/litestar/plugin.py +9 -8
- sqlspec/extensions/litestar/providers.py +521 -0
- sqlspec/filters.py +215 -11
- sqlspec/mixins.py +161 -12
- sqlspec/statement.py +276 -271
- sqlspec/typing.py +18 -1
- sqlspec/utils/__init__.py +2 -2
- sqlspec/utils/singleton.py +35 -0
- sqlspec/utils/sync_tools.py +90 -151
- sqlspec/utils/text.py +68 -5
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/METADATA +8 -1
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/RECORD +31 -29
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,19 +1,24 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from contextlib import contextmanager
|
|
2
3
|
from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
|
|
3
4
|
|
|
4
5
|
from duckdb import DuckDBPyConnection
|
|
5
6
|
|
|
6
7
|
from sqlspec.base import SyncDriverAdapterProtocol
|
|
7
|
-
from sqlspec.
|
|
8
|
+
from sqlspec.filters import StatementFilter
|
|
9
|
+
from sqlspec.mixins import ResultConverter, SQLTranslatorMixin, SyncArrowBulkOperationsMixin
|
|
10
|
+
from sqlspec.statement import SQLStatement
|
|
8
11
|
from sqlspec.typing import ArrowTable, StatementParameterType
|
|
9
12
|
|
|
10
13
|
if TYPE_CHECKING:
|
|
11
|
-
from collections.abc import Generator, Sequence
|
|
14
|
+
from collections.abc import Generator, Mapping, Sequence
|
|
12
15
|
|
|
13
16
|
from sqlspec.typing import ArrowTable, ModelDTOT, StatementParameterType, T
|
|
14
17
|
|
|
15
18
|
__all__ = ("DuckDBConnection", "DuckDBDriver")
|
|
16
19
|
|
|
20
|
+
logger = logging.getLogger("sqlspec")
|
|
21
|
+
|
|
17
22
|
DuckDBConnection = DuckDBPyConnection
|
|
18
23
|
|
|
19
24
|
|
|
@@ -21,6 +26,7 @@ class DuckDBDriver(
|
|
|
21
26
|
SyncArrowBulkOperationsMixin["DuckDBConnection"],
|
|
22
27
|
SQLTranslatorMixin["DuckDBConnection"],
|
|
23
28
|
SyncDriverAdapterProtocol["DuckDBConnection"],
|
|
29
|
+
ResultConverter,
|
|
24
30
|
):
|
|
25
31
|
"""DuckDB Sync Driver Adapter."""
|
|
26
32
|
|
|
@@ -32,7 +38,6 @@ class DuckDBDriver(
|
|
|
32
38
|
self.connection = connection
|
|
33
39
|
self.use_cursor = use_cursor
|
|
34
40
|
|
|
35
|
-
# --- Helper Methods --- #
|
|
36
41
|
def _cursor(self, connection: "DuckDBConnection") -> "DuckDBConnection":
|
|
37
42
|
if self.use_cursor:
|
|
38
43
|
return connection.cursor()
|
|
@@ -49,14 +54,58 @@ class DuckDBDriver(
|
|
|
49
54
|
else:
|
|
50
55
|
yield connection
|
|
51
56
|
|
|
57
|
+
def _process_sql_params(
|
|
58
|
+
self,
|
|
59
|
+
sql: str,
|
|
60
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
61
|
+
*filters: "StatementFilter",
|
|
62
|
+
**kwargs: Any,
|
|
63
|
+
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
64
|
+
"""Process SQL and parameters for DuckDB using SQLStatement.
|
|
65
|
+
|
|
66
|
+
DuckDB supports both named (:name, $name) and positional (?) parameters.
|
|
67
|
+
This method processes the SQL with dialect-aware parsing and handles
|
|
68
|
+
parameters appropriately for DuckDB.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
sql: SQL statement.
|
|
72
|
+
parameters: Query parameters.
|
|
73
|
+
*filters: Statement filters to apply.
|
|
74
|
+
**kwargs: Additional keyword arguments.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Tuple of processed SQL and parameters.
|
|
78
|
+
"""
|
|
79
|
+
data_params_for_statement: Optional[Union[Mapping[str, Any], Sequence[Any]]] = None
|
|
80
|
+
combined_filters_list: list[StatementFilter] = list(filters)
|
|
81
|
+
|
|
82
|
+
if parameters is not None:
|
|
83
|
+
if isinstance(parameters, StatementFilter):
|
|
84
|
+
combined_filters_list.insert(0, parameters)
|
|
85
|
+
else:
|
|
86
|
+
data_params_for_statement = parameters
|
|
87
|
+
if data_params_for_statement is not None and not isinstance(data_params_for_statement, (list, tuple, dict)):
|
|
88
|
+
data_params_for_statement = (data_params_for_statement,)
|
|
89
|
+
statement = SQLStatement(sql, data_params_for_statement, kwargs=kwargs, dialect=self.dialect)
|
|
90
|
+
for filter_obj in combined_filters_list:
|
|
91
|
+
statement = statement.apply_filter(filter_obj)
|
|
92
|
+
|
|
93
|
+
processed_sql, processed_params, _ = statement.process()
|
|
94
|
+
if processed_params is None:
|
|
95
|
+
return processed_sql, None
|
|
96
|
+
if isinstance(processed_params, dict):
|
|
97
|
+
return processed_sql, processed_params
|
|
98
|
+
if isinstance(processed_params, (list, tuple)):
|
|
99
|
+
return processed_sql, tuple(processed_params)
|
|
100
|
+
return processed_sql, (processed_params,) # type: ignore[unreachable]
|
|
101
|
+
|
|
52
102
|
# --- Public API Methods --- #
|
|
53
103
|
@overload
|
|
54
104
|
def select(
|
|
55
105
|
self,
|
|
56
106
|
sql: str,
|
|
57
107
|
parameters: "Optional[StatementParameterType]" = None,
|
|
58
|
-
|
|
59
|
-
*,
|
|
108
|
+
*filters: "StatementFilter",
|
|
60
109
|
connection: "Optional[DuckDBConnection]" = None,
|
|
61
110
|
schema_type: None = None,
|
|
62
111
|
**kwargs: Any,
|
|
@@ -66,8 +115,7 @@ class DuckDBDriver(
|
|
|
66
115
|
self,
|
|
67
116
|
sql: str,
|
|
68
117
|
parameters: "Optional[StatementParameterType]" = None,
|
|
69
|
-
|
|
70
|
-
*,
|
|
118
|
+
*filters: "StatementFilter",
|
|
71
119
|
connection: "Optional[DuckDBConnection]" = None,
|
|
72
120
|
schema_type: "type[ModelDTOT]",
|
|
73
121
|
**kwargs: Any,
|
|
@@ -76,33 +124,32 @@ class DuckDBDriver(
|
|
|
76
124
|
self,
|
|
77
125
|
sql: str,
|
|
78
126
|
parameters: "Optional[StatementParameterType]" = None,
|
|
79
|
-
|
|
80
|
-
*,
|
|
127
|
+
*filters: "StatementFilter",
|
|
81
128
|
connection: "Optional[DuckDBConnection]" = None,
|
|
82
129
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
83
130
|
**kwargs: Any,
|
|
84
|
-
) -> "Sequence[Union[
|
|
131
|
+
) -> "Sequence[Union[dict[str, Any], ModelDTOT]]":
|
|
132
|
+
"""Fetch data from the database.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
List of row data as either model instances or dictionaries.
|
|
136
|
+
"""
|
|
85
137
|
connection = self._connection(connection)
|
|
86
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
138
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
87
139
|
with self._with_cursor(connection) as cursor:
|
|
88
|
-
cursor.execute(sql, parameters
|
|
89
|
-
results = cursor.fetchall()
|
|
140
|
+
cursor.execute(sql, [] if parameters is None else parameters)
|
|
141
|
+
results = cursor.fetchall()
|
|
90
142
|
if not results:
|
|
91
143
|
return []
|
|
92
|
-
|
|
93
|
-
column_names
|
|
94
|
-
|
|
95
|
-
if schema_type is not None:
|
|
96
|
-
return [cast("ModelDTOT", schema_type(**dict(zip(column_names, row)))) for row in results] # pyright: ignore[reportUnknownArgumentType]
|
|
97
|
-
return [dict(zip(column_names, row)) for row in results] # pyright: ignore[reportUnknownArgumentType]
|
|
144
|
+
column_names = [column[0] for column in cursor.description or []]
|
|
145
|
+
return self.to_schema([dict(zip(column_names, row)) for row in results], schema_type=schema_type)
|
|
98
146
|
|
|
99
147
|
@overload
|
|
100
148
|
def select_one(
|
|
101
149
|
self,
|
|
102
150
|
sql: str,
|
|
103
151
|
parameters: "Optional[StatementParameterType]" = None,
|
|
104
|
-
|
|
105
|
-
*,
|
|
152
|
+
*filters: "StatementFilter",
|
|
106
153
|
connection: "Optional[DuckDBConnection]" = None,
|
|
107
154
|
schema_type: None = None,
|
|
108
155
|
**kwargs: Any,
|
|
@@ -112,8 +159,7 @@ class DuckDBDriver(
|
|
|
112
159
|
self,
|
|
113
160
|
sql: str,
|
|
114
161
|
parameters: "Optional[StatementParameterType]" = None,
|
|
115
|
-
|
|
116
|
-
*,
|
|
162
|
+
*filters: "StatementFilter",
|
|
117
163
|
connection: "Optional[DuckDBConnection]" = None,
|
|
118
164
|
schema_type: "type[ModelDTOT]",
|
|
119
165
|
**kwargs: Any,
|
|
@@ -121,33 +167,32 @@ class DuckDBDriver(
|
|
|
121
167
|
def select_one(
|
|
122
168
|
self,
|
|
123
169
|
sql: str,
|
|
124
|
-
parameters: Optional[
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
connection: Optional["DuckDBConnection"] = None,
|
|
170
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
171
|
+
*filters: "StatementFilter",
|
|
172
|
+
connection: "Optional[DuckDBConnection]" = None,
|
|
128
173
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
129
174
|
**kwargs: Any,
|
|
130
|
-
) -> "Union[
|
|
175
|
+
) -> "Union[dict[str, Any], ModelDTOT]":
|
|
176
|
+
"""Fetch one row from the database.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
The first row of the query results.
|
|
180
|
+
"""
|
|
131
181
|
connection = self._connection(connection)
|
|
132
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
182
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
133
183
|
with self._with_cursor(connection) as cursor:
|
|
134
|
-
cursor.execute(sql, parameters
|
|
135
|
-
result = cursor.fetchone()
|
|
136
|
-
result = self.check_not_found(result)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if schema_type is not None:
|
|
140
|
-
return cast("ModelDTOT", schema_type(**dict(zip(column_names, result)))) # pyright: ignore[reportUnknownArgumentType]
|
|
141
|
-
# Always return dictionaries
|
|
142
|
-
return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
|
|
184
|
+
cursor.execute(sql, [] if parameters is None else parameters)
|
|
185
|
+
result = cursor.fetchone()
|
|
186
|
+
result = self.check_not_found(result)
|
|
187
|
+
column_names = [column[0] for column in cursor.description or []]
|
|
188
|
+
return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
|
|
143
189
|
|
|
144
190
|
@overload
|
|
145
191
|
def select_one_or_none(
|
|
146
192
|
self,
|
|
147
193
|
sql: str,
|
|
148
194
|
parameters: "Optional[StatementParameterType]" = None,
|
|
149
|
-
|
|
150
|
-
*,
|
|
195
|
+
*filters: "StatementFilter",
|
|
151
196
|
connection: "Optional[DuckDBConnection]" = None,
|
|
152
197
|
schema_type: None = None,
|
|
153
198
|
**kwargs: Any,
|
|
@@ -157,8 +202,7 @@ class DuckDBDriver(
|
|
|
157
202
|
self,
|
|
158
203
|
sql: str,
|
|
159
204
|
parameters: "Optional[StatementParameterType]" = None,
|
|
160
|
-
|
|
161
|
-
*,
|
|
205
|
+
*filters: "StatementFilter",
|
|
162
206
|
connection: "Optional[DuckDBConnection]" = None,
|
|
163
207
|
schema_type: "type[ModelDTOT]",
|
|
164
208
|
**kwargs: Any,
|
|
@@ -166,33 +210,33 @@ class DuckDBDriver(
|
|
|
166
210
|
def select_one_or_none(
|
|
167
211
|
self,
|
|
168
212
|
sql: str,
|
|
169
|
-
parameters: Optional[
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
connection: Optional["DuckDBConnection"] = None,
|
|
213
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
214
|
+
*filters: "StatementFilter",
|
|
215
|
+
connection: "Optional[DuckDBConnection]" = None,
|
|
173
216
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
174
217
|
**kwargs: Any,
|
|
175
|
-
) -> "Optional[Union[
|
|
218
|
+
) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
|
|
219
|
+
"""Fetch one row from the database.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
The first row of the query results, or None if no results.
|
|
223
|
+
"""
|
|
176
224
|
connection = self._connection(connection)
|
|
177
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
225
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
178
226
|
with self._with_cursor(connection) as cursor:
|
|
179
|
-
cursor.execute(sql, parameters
|
|
180
|
-
result = cursor.fetchone()
|
|
227
|
+
cursor.execute(sql, [] if parameters is None else parameters)
|
|
228
|
+
result = cursor.fetchone()
|
|
181
229
|
if result is None:
|
|
182
230
|
return None
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if schema_type is not None:
|
|
186
|
-
return cast("ModelDTOT", schema_type(**dict(zip(column_names, result)))) # pyright: ignore[reportUnknownArgumentType]
|
|
187
|
-
return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
|
|
231
|
+
column_names = [column[0] for column in cursor.description or []]
|
|
232
|
+
return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
|
|
188
233
|
|
|
189
234
|
@overload
|
|
190
235
|
def select_value(
|
|
191
236
|
self,
|
|
192
237
|
sql: str,
|
|
193
238
|
parameters: "Optional[StatementParameterType]" = None,
|
|
194
|
-
|
|
195
|
-
*,
|
|
239
|
+
*filters: "StatementFilter",
|
|
196
240
|
connection: "Optional[DuckDBConnection]" = None,
|
|
197
241
|
schema_type: None = None,
|
|
198
242
|
**kwargs: Any,
|
|
@@ -202,8 +246,7 @@ class DuckDBDriver(
|
|
|
202
246
|
self,
|
|
203
247
|
sql: str,
|
|
204
248
|
parameters: "Optional[StatementParameterType]" = None,
|
|
205
|
-
|
|
206
|
-
*,
|
|
249
|
+
*filters: "StatementFilter",
|
|
207
250
|
connection: "Optional[DuckDBConnection]" = None,
|
|
208
251
|
schema_type: "type[T]",
|
|
209
252
|
**kwargs: Any,
|
|
@@ -212,29 +255,33 @@ class DuckDBDriver(
|
|
|
212
255
|
self,
|
|
213
256
|
sql: str,
|
|
214
257
|
parameters: "Optional[StatementParameterType]" = None,
|
|
215
|
-
|
|
216
|
-
*,
|
|
258
|
+
*filters: "StatementFilter",
|
|
217
259
|
connection: "Optional[DuckDBConnection]" = None,
|
|
218
260
|
schema_type: "Optional[type[T]]" = None,
|
|
219
261
|
**kwargs: Any,
|
|
220
262
|
) -> "Union[T, Any]":
|
|
263
|
+
"""Fetch a single value from the database.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
The first value from the first row of results.
|
|
267
|
+
"""
|
|
221
268
|
connection = self._connection(connection)
|
|
222
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
269
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
223
270
|
with self._with_cursor(connection) as cursor:
|
|
224
|
-
cursor.execute(sql, parameters
|
|
225
|
-
result = cursor.fetchone()
|
|
226
|
-
result = self.check_not_found(result)
|
|
271
|
+
cursor.execute(sql, [] if parameters is None else parameters)
|
|
272
|
+
result = cursor.fetchone()
|
|
273
|
+
result = self.check_not_found(result)
|
|
274
|
+
result_value = result[0]
|
|
227
275
|
if schema_type is None:
|
|
228
|
-
return
|
|
229
|
-
return schema_type(
|
|
276
|
+
return result_value
|
|
277
|
+
return schema_type(result_value) # type: ignore[call-arg]
|
|
230
278
|
|
|
231
279
|
@overload
|
|
232
280
|
def select_value_or_none(
|
|
233
281
|
self,
|
|
234
282
|
sql: str,
|
|
235
283
|
parameters: "Optional[StatementParameterType]" = None,
|
|
236
|
-
|
|
237
|
-
*,
|
|
284
|
+
*filters: "StatementFilter",
|
|
238
285
|
connection: "Optional[DuckDBConnection]" = None,
|
|
239
286
|
schema_type: None = None,
|
|
240
287
|
**kwargs: Any,
|
|
@@ -244,8 +291,7 @@ class DuckDBDriver(
|
|
|
244
291
|
self,
|
|
245
292
|
sql: str,
|
|
246
293
|
parameters: "Optional[StatementParameterType]" = None,
|
|
247
|
-
|
|
248
|
-
*,
|
|
294
|
+
*filters: "StatementFilter",
|
|
249
295
|
connection: "Optional[DuckDBConnection]" = None,
|
|
250
296
|
schema_type: "type[T]",
|
|
251
297
|
**kwargs: Any,
|
|
@@ -254,45 +300,43 @@ class DuckDBDriver(
|
|
|
254
300
|
self,
|
|
255
301
|
sql: str,
|
|
256
302
|
parameters: "Optional[StatementParameterType]" = None,
|
|
257
|
-
|
|
258
|
-
*,
|
|
303
|
+
*filters: "StatementFilter",
|
|
259
304
|
connection: "Optional[DuckDBConnection]" = None,
|
|
260
305
|
schema_type: "Optional[type[T]]" = None,
|
|
261
306
|
**kwargs: Any,
|
|
262
307
|
) -> "Optional[Union[T, Any]]":
|
|
263
308
|
connection = self._connection(connection)
|
|
264
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
309
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
265
310
|
with self._with_cursor(connection) as cursor:
|
|
266
|
-
cursor.execute(sql, parameters
|
|
267
|
-
result = cursor.fetchone()
|
|
311
|
+
cursor.execute(sql, [] if parameters is None else parameters)
|
|
312
|
+
result = cursor.fetchone()
|
|
268
313
|
if result is None:
|
|
269
314
|
return None
|
|
270
315
|
if schema_type is None:
|
|
271
|
-
return result[0]
|
|
316
|
+
return result[0]
|
|
272
317
|
return schema_type(result[0]) # type: ignore[call-arg]
|
|
273
318
|
|
|
274
319
|
def insert_update_delete(
|
|
275
320
|
self,
|
|
276
321
|
sql: str,
|
|
277
|
-
parameters: Optional[
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
connection: Optional["DuckDBConnection"] = None,
|
|
322
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
323
|
+
*filters: "StatementFilter",
|
|
324
|
+
connection: "Optional[DuckDBConnection]" = None,
|
|
281
325
|
**kwargs: Any,
|
|
282
326
|
) -> int:
|
|
283
327
|
connection = self._connection(connection)
|
|
284
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
328
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
285
329
|
with self._with_cursor(connection) as cursor:
|
|
286
|
-
|
|
287
|
-
|
|
330
|
+
params = [] if parameters is None else parameters
|
|
331
|
+
cursor.execute(sql, params)
|
|
332
|
+
return getattr(cursor, "rowcount", -1)
|
|
288
333
|
|
|
289
334
|
@overload
|
|
290
335
|
def insert_update_delete_returning(
|
|
291
336
|
self,
|
|
292
337
|
sql: str,
|
|
293
338
|
parameters: "Optional[StatementParameterType]" = None,
|
|
294
|
-
|
|
295
|
-
*,
|
|
339
|
+
*filters: "StatementFilter",
|
|
296
340
|
connection: "Optional[DuckDBConnection]" = None,
|
|
297
341
|
schema_type: None = None,
|
|
298
342
|
**kwargs: Any,
|
|
@@ -302,8 +346,7 @@ class DuckDBDriver(
|
|
|
302
346
|
self,
|
|
303
347
|
sql: str,
|
|
304
348
|
parameters: "Optional[StatementParameterType]" = None,
|
|
305
|
-
|
|
306
|
-
*,
|
|
349
|
+
*filters: "StatementFilter",
|
|
307
350
|
connection: "Optional[DuckDBConnection]" = None,
|
|
308
351
|
schema_type: "type[ModelDTOT]",
|
|
309
352
|
**kwargs: Any,
|
|
@@ -312,38 +355,34 @@ class DuckDBDriver(
|
|
|
312
355
|
self,
|
|
313
356
|
sql: str,
|
|
314
357
|
parameters: "Optional[StatementParameterType]" = None,
|
|
315
|
-
|
|
316
|
-
*,
|
|
358
|
+
*filters: "StatementFilter",
|
|
317
359
|
connection: "Optional[DuckDBConnection]" = None,
|
|
318
360
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
319
361
|
**kwargs: Any,
|
|
320
362
|
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
321
363
|
connection = self._connection(connection)
|
|
322
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
364
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
323
365
|
with self._with_cursor(connection) as cursor:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
result =
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
# Always return dictionaries
|
|
331
|
-
return dict(zip(column_names, result[0])) # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
|
|
366
|
+
params = [] if parameters is None else parameters
|
|
367
|
+
cursor.execute(sql, params)
|
|
368
|
+
result = cursor.fetchall()
|
|
369
|
+
result = self.check_not_found(result)
|
|
370
|
+
column_names = [col[0] for col in cursor.description or []]
|
|
371
|
+
return self.to_schema(dict(zip(column_names, result[0])), schema_type=schema_type)
|
|
332
372
|
|
|
333
373
|
def execute_script(
|
|
334
374
|
self,
|
|
335
375
|
sql: str,
|
|
336
|
-
parameters: Optional[
|
|
337
|
-
|
|
338
|
-
*,
|
|
339
|
-
connection: Optional["DuckDBConnection"] = None,
|
|
376
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
377
|
+
connection: "Optional[DuckDBConnection]" = None,
|
|
340
378
|
**kwargs: Any,
|
|
341
379
|
) -> str:
|
|
342
380
|
connection = self._connection(connection)
|
|
343
381
|
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
344
382
|
with self._with_cursor(connection) as cursor:
|
|
345
|
-
|
|
346
|
-
|
|
383
|
+
params = [] if parameters is None else parameters
|
|
384
|
+
cursor.execute(sql, params)
|
|
385
|
+
return cast("str", getattr(cursor, "statusmessage", "DONE"))
|
|
347
386
|
|
|
348
387
|
# --- Arrow Bulk Operations ---
|
|
349
388
|
|
|
@@ -351,13 +390,36 @@ class DuckDBDriver(
|
|
|
351
390
|
self,
|
|
352
391
|
sql: str,
|
|
353
392
|
parameters: "Optional[StatementParameterType]" = None,
|
|
354
|
-
|
|
355
|
-
*,
|
|
393
|
+
*filters: "StatementFilter",
|
|
356
394
|
connection: "Optional[DuckDBConnection]" = None,
|
|
357
395
|
**kwargs: Any,
|
|
358
396
|
) -> "ArrowTable":
|
|
397
|
+
"""Execute a SQL query and return results as an Apache Arrow Table.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
sql: The SQL query string.
|
|
401
|
+
parameters: Parameters for the query.
|
|
402
|
+
*filters: Optional filters to apply to the SQL statement.
|
|
403
|
+
connection: Optional connection override.
|
|
404
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
An Apache Arrow Table containing the query results.
|
|
408
|
+
"""
|
|
359
409
|
connection = self._connection(connection)
|
|
360
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
410
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
361
411
|
with self._with_cursor(connection) as cursor:
|
|
362
|
-
|
|
363
|
-
|
|
412
|
+
params = [] if parameters is None else parameters
|
|
413
|
+
cursor.execute(sql, params)
|
|
414
|
+
return cast("ArrowTable", cursor.fetch_arrow_table())
|
|
415
|
+
|
|
416
|
+
def _connection(self, connection: "Optional[DuckDBConnection]" = None) -> "DuckDBConnection":
|
|
417
|
+
"""Get the connection to use for the operation.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
connection: Optional connection to use.
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
The connection to use.
|
|
424
|
+
"""
|
|
425
|
+
return connection or self.connection
|