sqlspec 0.10.0__py3-none-any.whl → 0.11.0__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.

@@ -1,24 +1,31 @@
1
+ import logging
1
2
  import sqlite3
2
3
  from contextlib import contextmanager
3
4
  from sqlite3 import Cursor
4
- from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
5
+ from typing import TYPE_CHECKING, Any, Optional, Union, overload
5
6
 
6
7
  from sqlspec.base import SyncDriverAdapterProtocol
7
- from sqlspec.mixins import SQLTranslatorMixin
8
+ from sqlspec.mixins import ResultConverter, SQLTranslatorMixin
9
+ from sqlspec.statement import SQLStatement
10
+ from sqlspec.typing import is_dict
8
11
 
9
12
  if TYPE_CHECKING:
10
13
  from collections.abc import Generator, Sequence
11
14
 
15
+ from sqlspec.filters import StatementFilter
12
16
  from sqlspec.typing import ModelDTOT, StatementParameterType, T
13
17
 
14
18
  __all__ = ("SqliteConnection", "SqliteDriver")
15
19
 
20
+ logger = logging.getLogger("sqlspec")
21
+
16
22
  SqliteConnection = sqlite3.Connection
17
23
 
18
24
 
19
25
  class SqliteDriver(
20
26
  SQLTranslatorMixin["SqliteConnection"],
21
27
  SyncDriverAdapterProtocol["SqliteConnection"],
28
+ ResultConverter,
22
29
  ):
23
30
  """SQLite Sync Driver Adapter."""
24
31
 
@@ -40,6 +47,48 @@ class SqliteDriver(
40
47
  finally:
41
48
  cursor.close()
42
49
 
50
+ def _process_sql_params(
51
+ self,
52
+ sql: str,
53
+ parameters: "Optional[StatementParameterType]" = None,
54
+ /,
55
+ *filters: "StatementFilter",
56
+ **kwargs: Any,
57
+ ) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
58
+ """Process SQL and parameters for SQLite using SQLStatement.
59
+
60
+ SQLite supports both named (:name) and positional (?) parameters.
61
+ This method processes the SQL with dialect-aware parsing and handles
62
+ parameters appropriately for SQLite.
63
+
64
+ Args:
65
+ sql: The SQL to process.
66
+ parameters: The parameters to process.
67
+ *filters: Statement filters to apply.
68
+ **kwargs: Additional keyword arguments.
69
+
70
+ Returns:
71
+ A tuple of (processed SQL, processed parameters).
72
+ """
73
+ # Create a SQLStatement with SQLite dialect
74
+ statement = SQLStatement(sql, parameters, kwargs=kwargs, dialect=self.dialect)
75
+
76
+ for filter_obj in filters:
77
+ statement = statement.apply_filter(filter_obj)
78
+
79
+ processed_sql, processed_params, _ = statement.process()
80
+
81
+ if processed_params is None:
82
+ return processed_sql, None
83
+
84
+ if is_dict(processed_params):
85
+ return processed_sql, processed_params
86
+
87
+ if isinstance(processed_params, (list, tuple)):
88
+ return processed_sql, tuple(processed_params)
89
+
90
+ return processed_sql, (processed_params,)
91
+
43
92
  # --- Public API Methods --- #
44
93
  @overload
45
94
  def select(
@@ -47,7 +96,7 @@ class SqliteDriver(
47
96
  sql: str,
48
97
  parameters: "Optional[StatementParameterType]" = None,
49
98
  /,
50
- *,
99
+ *filters: "StatementFilter",
51
100
  connection: "Optional[SqliteConnection]" = None,
52
101
  schema_type: None = None,
53
102
  **kwargs: Any,
@@ -58,7 +107,7 @@ class SqliteDriver(
58
107
  sql: str,
59
108
  parameters: "Optional[StatementParameterType]" = None,
60
109
  /,
61
- *,
110
+ *filters: "StatementFilter",
62
111
  connection: "Optional[SqliteConnection]" = None,
63
112
  schema_type: "type[ModelDTOT]",
64
113
  **kwargs: Any,
@@ -66,32 +115,31 @@ class SqliteDriver(
66
115
  def select(
67
116
  self,
68
117
  sql: str,
69
- parameters: Optional["StatementParameterType"] = None,
118
+ parameters: "Optional[StatementParameterType]" = None,
70
119
  /,
71
- *,
72
- connection: Optional["SqliteConnection"] = None,
120
+ *filters: "StatementFilter",
121
+ connection: "Optional[SqliteConnection]" = None,
73
122
  schema_type: "Optional[type[ModelDTOT]]" = None,
74
123
  **kwargs: Any,
75
- ) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
124
+ ) -> "Sequence[Union[dict[str, Any], ModelDTOT]]":
76
125
  """Fetch data from the database.
77
126
 
78
127
  Returns:
79
128
  List of row data as either model instances or dictionaries.
80
129
  """
81
130
  connection = self._connection(connection)
82
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
131
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
132
+
83
133
  with self._with_cursor(connection) as cursor:
84
- if not parameters:
85
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
86
- else:
87
- cursor.execute(sql, parameters)
134
+ cursor.execute(sql, parameters or [])
88
135
  results = cursor.fetchall()
89
136
  if not results:
90
137
  return []
91
- column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
92
- if schema_type is not None:
93
- return [cast("ModelDTOT", schema_type(**dict(zip(column_names, row)))) for row in results] # pyright: ignore[reportUnknownArgumentType]
94
- return [dict(zip(column_names, row)) for row in results] # pyright: ignore[reportUnknownArgumentType]
138
+
139
+ # Get column names
140
+ column_names = [column[0] for column in cursor.description]
141
+
142
+ return self.to_schema([dict(zip(column_names, row)) for row in results], schema_type=schema_type)
95
143
 
96
144
  @overload
97
145
  def select_one(
@@ -99,7 +147,7 @@ class SqliteDriver(
99
147
  sql: str,
100
148
  parameters: "Optional[StatementParameterType]" = None,
101
149
  /,
102
- *,
150
+ *filters: "StatementFilter",
103
151
  connection: "Optional[SqliteConnection]" = None,
104
152
  schema_type: None = None,
105
153
  **kwargs: Any,
@@ -110,7 +158,7 @@ class SqliteDriver(
110
158
  sql: str,
111
159
  parameters: "Optional[StatementParameterType]" = None,
112
160
  /,
113
- *,
161
+ *filters: "StatementFilter",
114
162
  connection: "Optional[SqliteConnection]" = None,
115
163
  schema_type: "type[ModelDTOT]",
116
164
  **kwargs: Any,
@@ -118,31 +166,31 @@ class SqliteDriver(
118
166
  def select_one(
119
167
  self,
120
168
  sql: str,
121
- parameters: Optional["StatementParameterType"] = None,
169
+ parameters: "Optional[StatementParameterType]" = None,
122
170
  /,
123
- *,
124
- connection: Optional["SqliteConnection"] = None,
171
+ *filters: "StatementFilter",
172
+ connection: "Optional[SqliteConnection]" = None,
125
173
  schema_type: "Optional[type[ModelDTOT]]" = None,
126
174
  **kwargs: Any,
127
- ) -> "Union[ModelDTOT, dict[str, Any]]":
175
+ ) -> "Union[dict[str, Any], ModelDTOT]":
128
176
  """Fetch one row from the database.
129
177
 
130
178
  Returns:
131
179
  The first row of the query results.
132
180
  """
133
181
  connection = self._connection(connection)
134
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
135
- with self._with_cursor(connection) as cursor:
136
- if not parameters:
137
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
138
- else:
139
- cursor.execute(sql, parameters)
140
- result = cursor.fetchone()
141
- result = self.check_not_found(result)
142
- column_names = [c[0] for c in cursor.description or []]
143
- if schema_type is None:
144
- return dict(zip(column_names, result))
145
- return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
182
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
183
+
184
+ # Execute the query
185
+ cursor = connection.cursor()
186
+ cursor.execute(sql, parameters or [])
187
+ result = cursor.fetchone()
188
+ result = self.check_not_found(result)
189
+
190
+ # Get column names
191
+ column_names = [column[0] for column in cursor.description]
192
+
193
+ return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
146
194
 
147
195
  @overload
148
196
  def select_one_or_none(
@@ -150,7 +198,7 @@ class SqliteDriver(
150
198
  sql: str,
151
199
  parameters: "Optional[StatementParameterType]" = None,
152
200
  /,
153
- *,
201
+ *filters: "StatementFilter",
154
202
  connection: "Optional[SqliteConnection]" = None,
155
203
  schema_type: None = None,
156
204
  **kwargs: Any,
@@ -161,7 +209,7 @@ class SqliteDriver(
161
209
  sql: str,
162
210
  parameters: "Optional[StatementParameterType]" = None,
163
211
  /,
164
- *,
212
+ *filters: "StatementFilter",
165
213
  connection: "Optional[SqliteConnection]" = None,
166
214
  schema_type: "type[ModelDTOT]",
167
215
  **kwargs: Any,
@@ -169,32 +217,31 @@ class SqliteDriver(
169
217
  def select_one_or_none(
170
218
  self,
171
219
  sql: str,
172
- parameters: Optional["StatementParameterType"] = None,
220
+ parameters: "Optional[StatementParameterType]" = None,
173
221
  /,
174
- *,
175
- connection: Optional["SqliteConnection"] = None,
222
+ *filters: "StatementFilter",
223
+ connection: "Optional[SqliteConnection]" = None,
176
224
  schema_type: "Optional[type[ModelDTOT]]" = None,
177
225
  **kwargs: Any,
178
- ) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
226
+ ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
179
227
  """Fetch one row from the database.
180
228
 
181
229
  Returns:
182
- The first row of the query results.
230
+ The first row of the query results, or None if no results.
183
231
  """
184
232
  connection = self._connection(connection)
185
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
233
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
234
+
186
235
  with self._with_cursor(connection) as cursor:
187
- if not parameters:
188
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
189
- else:
190
- cursor.execute(sql, parameters)
236
+ cursor.execute(sql, parameters or [])
191
237
  result = cursor.fetchone()
192
238
  if result is None:
193
239
  return None
194
- column_names = [c[0] for c in cursor.description or []]
195
- if schema_type is None:
196
- return dict(zip(column_names, result))
197
- return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
240
+
241
+ # Get column names
242
+ column_names = [column[0] for column in cursor.description]
243
+
244
+ return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
198
245
 
199
246
  @overload
200
247
  def select_value(
@@ -202,7 +249,7 @@ class SqliteDriver(
202
249
  sql: str,
203
250
  parameters: "Optional[StatementParameterType]" = None,
204
251
  /,
205
- *,
252
+ *filters: "StatementFilter",
206
253
  connection: "Optional[SqliteConnection]" = None,
207
254
  schema_type: None = None,
208
255
  **kwargs: Any,
@@ -213,7 +260,7 @@ class SqliteDriver(
213
260
  sql: str,
214
261
  parameters: "Optional[StatementParameterType]" = None,
215
262
  /,
216
- *,
263
+ *filters: "StatementFilter",
217
264
  connection: "Optional[SqliteConnection]" = None,
218
265
  schema_type: "type[T]",
219
266
  **kwargs: Any,
@@ -223,7 +270,7 @@ class SqliteDriver(
223
270
  sql: str,
224
271
  parameters: "Optional[StatementParameterType]" = None,
225
272
  /,
226
- *,
273
+ *filters: "StatementFilter",
227
274
  connection: "Optional[SqliteConnection]" = None,
228
275
  schema_type: "Optional[type[T]]" = None,
229
276
  **kwargs: Any,
@@ -231,20 +278,21 @@ class SqliteDriver(
231
278
  """Fetch a single value from the database.
232
279
 
233
280
  Returns:
234
- The first value from the first row of results, or None if no results.
281
+ The first value from the first row of results.
235
282
  """
236
283
  connection = self._connection(connection)
237
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
284
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
285
+
238
286
  with self._with_cursor(connection) as cursor:
239
- if not parameters:
240
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
241
- else:
242
- cursor.execute(sql, parameters)
287
+ cursor.execute(sql, parameters or [])
243
288
  result = cursor.fetchone()
244
289
  result = self.check_not_found(result)
290
+
291
+ # Return first value from the row
292
+ result_value = result[0]
245
293
  if schema_type is None:
246
- return result[0]
247
- return schema_type(result[0]) # type: ignore[call-arg]
294
+ return result_value
295
+ return schema_type(result_value) # type: ignore[call-arg]
248
296
 
249
297
  @overload
250
298
  def select_value_or_none(
@@ -252,7 +300,7 @@ class SqliteDriver(
252
300
  sql: str,
253
301
  parameters: "Optional[StatementParameterType]" = None,
254
302
  /,
255
- *,
303
+ *filters: "StatementFilter",
256
304
  connection: "Optional[SqliteConnection]" = None,
257
305
  schema_type: None = None,
258
306
  **kwargs: Any,
@@ -263,7 +311,7 @@ class SqliteDriver(
263
311
  sql: str,
264
312
  parameters: "Optional[StatementParameterType]" = None,
265
313
  /,
266
- *,
314
+ *filters: "StatementFilter",
267
315
  connection: "Optional[SqliteConnection]" = None,
268
316
  schema_type: "type[T]",
269
317
  **kwargs: Any,
@@ -273,7 +321,7 @@ class SqliteDriver(
273
321
  sql: str,
274
322
  parameters: "Optional[StatementParameterType]" = None,
275
323
  /,
276
- *,
324
+ *filters: "StatementFilter",
277
325
  connection: "Optional[SqliteConnection]" = None,
278
326
  schema_type: "Optional[type[T]]" = None,
279
327
  **kwargs: Any,
@@ -284,26 +332,27 @@ class SqliteDriver(
284
332
  The first value from the first row of results, or None if no results.
285
333
  """
286
334
  connection = self._connection(connection)
287
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
335
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
336
+
288
337
  with self._with_cursor(connection) as cursor:
289
- if not parameters:
290
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
291
- else:
292
- cursor.execute(sql, parameters)
338
+ cursor.execute(sql, parameters or [])
293
339
  result = cursor.fetchone()
294
340
  if result is None:
295
341
  return None
342
+
343
+ # Return first value from the row
344
+ result_value = result[0]
296
345
  if schema_type is None:
297
- return result[0]
298
- return schema_type(result[0]) # type: ignore[call-arg]
346
+ return result_value
347
+ return schema_type(result_value) # type: ignore[call-arg]
299
348
 
300
349
  def insert_update_delete(
301
350
  self,
302
351
  sql: str,
303
- parameters: Optional["StatementParameterType"] = None,
352
+ parameters: "Optional[StatementParameterType]" = None,
304
353
  /,
305
- *,
306
- connection: Optional["SqliteConnection"] = None,
354
+ *filters: "StatementFilter",
355
+ connection: "Optional[SqliteConnection]" = None,
307
356
  **kwargs: Any,
308
357
  ) -> int:
309
358
  """Insert, update, or delete data from the database.
@@ -312,14 +361,11 @@ class SqliteDriver(
312
361
  Row count affected by the operation.
313
362
  """
314
363
  connection = self._connection(connection)
315
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
364
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
316
365
 
317
366
  with self._with_cursor(connection) as cursor:
318
- if not parameters:
319
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
320
- else:
321
- cursor.execute(sql, parameters)
322
- return cursor.rowcount if hasattr(cursor, "rowcount") else -1
367
+ cursor.execute(sql, parameters or [])
368
+ return cursor.rowcount
323
369
 
324
370
  @overload
325
371
  def insert_update_delete_returning(
@@ -327,7 +373,7 @@ class SqliteDriver(
327
373
  sql: str,
328
374
  parameters: "Optional[StatementParameterType]" = None,
329
375
  /,
330
- *,
376
+ *filters: "StatementFilter",
331
377
  connection: "Optional[SqliteConnection]" = None,
332
378
  schema_type: None = None,
333
379
  **kwargs: Any,
@@ -338,7 +384,7 @@ class SqliteDriver(
338
384
  sql: str,
339
385
  parameters: "Optional[StatementParameterType]" = None,
340
386
  /,
341
- *,
387
+ *filters: "StatementFilter",
342
388
  connection: "Optional[SqliteConnection]" = None,
343
389
  schema_type: "type[ModelDTOT]",
344
390
  **kwargs: Any,
@@ -346,54 +392,34 @@ class SqliteDriver(
346
392
  def insert_update_delete_returning(
347
393
  self,
348
394
  sql: str,
349
- parameters: Optional["StatementParameterType"] = None,
395
+ parameters: "Optional[StatementParameterType]" = None,
350
396
  /,
351
- *,
352
- connection: Optional["SqliteConnection"] = None,
397
+ *filters: "StatementFilter",
398
+ connection: "Optional[SqliteConnection]" = None,
353
399
  schema_type: "Optional[type[ModelDTOT]]" = None,
354
400
  **kwargs: Any,
355
- ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
401
+ ) -> "Union[dict[str, Any], ModelDTOT]":
356
402
  """Insert, update, or delete data from the database and return result.
357
403
 
358
404
  Returns:
359
405
  The first row of results.
360
406
  """
361
407
  connection = self._connection(connection)
362
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
408
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
363
409
 
364
410
  with self._with_cursor(connection) as cursor:
365
- if not parameters:
366
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
367
- else:
368
- cursor.execute(sql, parameters)
369
- result = cursor.fetchall()
370
- if len(result) == 0:
371
- return None
372
-
373
- # Get column names from cursor description
374
- column_names = [c[0] for c in cursor.description or []]
375
-
376
- # Get the first row's values - ensure we're getting the actual values
377
- row_values = result[0]
378
-
379
- # Debug print to see what we're getting
380
-
381
- # Create dictionary mapping column names to values
382
- result_dict = {}
383
- for i, col_name in enumerate(column_names):
384
- result_dict[col_name] = row_values[i]
385
-
386
- if schema_type is not None:
387
- return cast("ModelDTOT", schema_type(**result_dict))
388
- return result_dict
411
+ cursor.execute(sql, parameters or [])
412
+ result = cursor.fetchone()
413
+ result = self.check_not_found(result)
414
+ column_names = [column[0] for column in cursor.description]
415
+ return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
389
416
 
390
417
  def execute_script(
391
418
  self,
392
419
  sql: str,
393
- parameters: Optional["StatementParameterType"] = None,
420
+ parameters: "Optional[StatementParameterType]" = None,
394
421
  /,
395
- *,
396
- connection: Optional["SqliteConnection"] = None,
422
+ connection: "Optional[SqliteConnection]" = None,
397
423
  **kwargs: Any,
398
424
  ) -> str:
399
425
  """Execute a script.
@@ -404,11 +430,17 @@ class SqliteDriver(
404
430
  connection = self._connection(connection)
405
431
  sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
406
432
 
407
- # The _process_sql_params handles parameter formatting for the dialect.
408
433
  with self._with_cursor(connection) as cursor:
409
- if not parameters:
410
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
411
- else:
412
- cursor.execute(sql, parameters)
434
+ cursor.executescript(sql)
435
+ return "DONE"
413
436
 
414
- return cast("str", cursor.statusmessage) if hasattr(cursor, "statusmessage") else "DONE" # pyright: ignore[reportUnknownMemberType,reportAttributeAccessIssue]
437
+ def _connection(self, connection: "Optional[SqliteConnection]" = None) -> "SqliteConnection":
438
+ """Get the connection to use for the operation.
439
+
440
+ Args:
441
+ connection: Optional connection to use.
442
+
443
+ Returns:
444
+ The connection to use.
445
+ """
446
+ return connection or self.connection