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.

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