sqlspec 0.8.0__py3-none-any.whl → 0.9.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.

Files changed (45) hide show
  1. sqlspec/_typing.py +39 -6
  2. sqlspec/adapters/adbc/__init__.py +2 -2
  3. sqlspec/adapters/adbc/config.py +34 -11
  4. sqlspec/adapters/adbc/driver.py +302 -111
  5. sqlspec/adapters/aiosqlite/__init__.py +2 -2
  6. sqlspec/adapters/aiosqlite/config.py +2 -2
  7. sqlspec/adapters/aiosqlite/driver.py +164 -42
  8. sqlspec/adapters/asyncmy/__init__.py +3 -3
  9. sqlspec/adapters/asyncmy/config.py +11 -12
  10. sqlspec/adapters/asyncmy/driver.py +161 -37
  11. sqlspec/adapters/asyncpg/__init__.py +5 -5
  12. sqlspec/adapters/asyncpg/config.py +17 -19
  13. sqlspec/adapters/asyncpg/driver.py +386 -96
  14. sqlspec/adapters/duckdb/__init__.py +2 -2
  15. sqlspec/adapters/duckdb/config.py +2 -2
  16. sqlspec/adapters/duckdb/driver.py +190 -60
  17. sqlspec/adapters/oracledb/__init__.py +8 -8
  18. sqlspec/adapters/oracledb/config/__init__.py +6 -6
  19. sqlspec/adapters/oracledb/config/_asyncio.py +9 -10
  20. sqlspec/adapters/oracledb/config/_sync.py +8 -9
  21. sqlspec/adapters/oracledb/driver.py +384 -45
  22. sqlspec/adapters/psqlpy/__init__.py +0 -0
  23. sqlspec/adapters/psqlpy/config.py +250 -0
  24. sqlspec/adapters/psqlpy/driver.py +481 -0
  25. sqlspec/adapters/psycopg/__init__.py +10 -5
  26. sqlspec/adapters/psycopg/config/__init__.py +6 -6
  27. sqlspec/adapters/psycopg/config/_async.py +12 -12
  28. sqlspec/adapters/psycopg/config/_sync.py +13 -13
  29. sqlspec/adapters/psycopg/driver.py +432 -222
  30. sqlspec/adapters/sqlite/__init__.py +2 -2
  31. sqlspec/adapters/sqlite/config.py +2 -2
  32. sqlspec/adapters/sqlite/driver.py +176 -72
  33. sqlspec/base.py +687 -161
  34. sqlspec/exceptions.py +30 -0
  35. sqlspec/extensions/litestar/config.py +6 -0
  36. sqlspec/extensions/litestar/handlers.py +25 -0
  37. sqlspec/extensions/litestar/plugin.py +8 -1
  38. sqlspec/statement.py +373 -0
  39. sqlspec/typing.py +10 -1
  40. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/METADATA +144 -2
  41. sqlspec-0.9.1.dist-info/RECORD +61 -0
  42. sqlspec-0.8.0.dist-info/RECORD +0 -57
  43. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/WHEEL +0 -0
  44. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/LICENSE +0 -0
  45. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/NOTICE +0 -0
@@ -1,7 +1,7 @@
1
- from sqlspec.adapters.sqlite.config import Sqlite
1
+ from sqlspec.adapters.sqlite.config import SqliteConfig
2
2
  from sqlspec.adapters.sqlite.driver import SqliteDriver
3
3
 
4
4
  __all__ = (
5
- "Sqlite",
5
+ "SqliteConfig",
6
6
  "SqliteDriver",
7
7
  )
@@ -12,11 +12,11 @@ if TYPE_CHECKING:
12
12
  from collections.abc import Generator
13
13
 
14
14
 
15
- __all__ = ("Sqlite",)
15
+ __all__ = ("SqliteConfig",)
16
16
 
17
17
 
18
18
  @dataclass
19
- class Sqlite(NoPoolSyncConfig["Connection", "SqliteDriver"]):
19
+ class SqliteConfig(NoPoolSyncConfig["Connection", "SqliteDriver"]):
20
20
  """Configuration for SQLite database connections.
21
21
 
22
22
  This class provides configuration options for SQLite database connections, wrapping all parameters
@@ -1,11 +1,11 @@
1
1
  from contextlib import contextmanager
2
2
  from sqlite3 import Connection, Cursor
3
- from typing import TYPE_CHECKING, Any, Optional, Union, cast
3
+ from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
4
4
 
5
5
  from sqlspec.base import SyncDriverAdapterProtocol, T
6
6
 
7
7
  if TYPE_CHECKING:
8
- from collections.abc import Generator
8
+ from collections.abc import Generator, Sequence
9
9
 
10
10
  from sqlspec.typing import ModelDTOT, StatementParameterType
11
11
 
@@ -16,6 +16,7 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
16
16
  """SQLite Sync Driver Adapter."""
17
17
 
18
18
  connection: "Connection"
19
+ dialect: str = "sqlite"
19
20
 
20
21
  def __init__(self, connection: "Connection") -> None:
21
22
  self.connection = connection
@@ -32,21 +33,46 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
32
33
  finally:
33
34
  cursor.close()
34
35
 
36
+ # --- Public API Methods --- #
37
+ @overload
38
+ def select(
39
+ self,
40
+ sql: str,
41
+ parameters: "Optional[StatementParameterType]" = None,
42
+ /,
43
+ *,
44
+ connection: "Optional[Connection]" = None,
45
+ schema_type: None = None,
46
+ **kwargs: Any,
47
+ ) -> "Sequence[dict[str, Any]]": ...
48
+ @overload
49
+ def select(
50
+ self,
51
+ sql: str,
52
+ parameters: "Optional[StatementParameterType]" = None,
53
+ /,
54
+ *,
55
+ connection: "Optional[Connection]" = None,
56
+ schema_type: "type[ModelDTOT]",
57
+ **kwargs: Any,
58
+ ) -> "Sequence[ModelDTOT]": ...
35
59
  def select(
36
60
  self,
37
61
  sql: str,
38
62
  parameters: Optional["StatementParameterType"] = None,
39
63
  /,
64
+ *,
40
65
  connection: Optional["Connection"] = None,
41
66
  schema_type: "Optional[type[ModelDTOT]]" = None,
42
- ) -> "list[Union[ModelDTOT, dict[str, Any]]]":
67
+ **kwargs: Any,
68
+ ) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
43
69
  """Fetch data from the database.
44
70
 
45
71
  Returns:
46
72
  List of row data as either model instances or dictionaries.
47
73
  """
48
74
  connection = self._connection(connection)
49
- sql, parameters = self._process_sql_params(sql, parameters)
75
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
50
76
  with self._with_cursor(connection) as cursor:
51
77
  if not parameters:
52
78
  cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
@@ -60,13 +86,37 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
60
86
  return [cast("ModelDTOT", schema_type(**dict(zip(column_names, row)))) for row in results] # pyright: ignore[reportUnknownArgumentType]
61
87
  return [dict(zip(column_names, row)) for row in results] # pyright: ignore[reportUnknownArgumentType]
62
88
 
89
+ @overload
90
+ def select_one(
91
+ self,
92
+ sql: str,
93
+ parameters: "Optional[StatementParameterType]" = None,
94
+ /,
95
+ *,
96
+ connection: "Optional[Connection]" = None,
97
+ schema_type: None = None,
98
+ **kwargs: Any,
99
+ ) -> "dict[str, Any]": ...
100
+ @overload
101
+ def select_one(
102
+ self,
103
+ sql: str,
104
+ parameters: "Optional[StatementParameterType]" = None,
105
+ /,
106
+ *,
107
+ connection: "Optional[Connection]" = None,
108
+ schema_type: "type[ModelDTOT]",
109
+ **kwargs: Any,
110
+ ) -> "ModelDTOT": ...
63
111
  def select_one(
64
112
  self,
65
113
  sql: str,
66
114
  parameters: Optional["StatementParameterType"] = None,
67
115
  /,
116
+ *,
68
117
  connection: Optional["Connection"] = None,
69
118
  schema_type: "Optional[type[ModelDTOT]]" = None,
119
+ **kwargs: Any,
70
120
  ) -> "Union[ModelDTOT, dict[str, Any]]":
71
121
  """Fetch one row from the database.
72
122
 
@@ -74,7 +124,7 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
74
124
  The first row of the query results.
75
125
  """
76
126
  connection = self._connection(connection)
77
- sql, parameters = self._process_sql_params(sql, parameters)
127
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
78
128
  with self._with_cursor(connection) as cursor:
79
129
  if not parameters:
80
130
  cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
@@ -87,13 +137,37 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
87
137
  return dict(zip(column_names, result))
88
138
  return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
89
139
 
140
+ @overload
141
+ def select_one_or_none(
142
+ self,
143
+ sql: str,
144
+ parameters: "Optional[StatementParameterType]" = None,
145
+ /,
146
+ *,
147
+ connection: "Optional[Connection]" = None,
148
+ schema_type: None = None,
149
+ **kwargs: Any,
150
+ ) -> "Optional[dict[str, Any]]": ...
151
+ @overload
152
+ def select_one_or_none(
153
+ self,
154
+ sql: str,
155
+ parameters: "Optional[StatementParameterType]" = None,
156
+ /,
157
+ *,
158
+ connection: "Optional[Connection]" = None,
159
+ schema_type: "type[ModelDTOT]",
160
+ **kwargs: Any,
161
+ ) -> "Optional[ModelDTOT]": ...
90
162
  def select_one_or_none(
91
163
  self,
92
164
  sql: str,
93
165
  parameters: Optional["StatementParameterType"] = None,
94
166
  /,
167
+ *,
95
168
  connection: Optional["Connection"] = None,
96
169
  schema_type: "Optional[type[ModelDTOT]]" = None,
170
+ **kwargs: Any,
97
171
  ) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
98
172
  """Fetch one row from the database.
99
173
 
@@ -101,7 +175,7 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
101
175
  The first row of the query results.
102
176
  """
103
177
  connection = self._connection(connection)
104
- sql, parameters = self._process_sql_params(sql, parameters)
178
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
105
179
  with self._with_cursor(connection) as cursor:
106
180
  if not parameters:
107
181
  cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
@@ -115,13 +189,37 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
115
189
  return dict(zip(column_names, result))
116
190
  return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
117
191
 
192
+ @overload
193
+ def select_value(
194
+ self,
195
+ sql: str,
196
+ parameters: "Optional[StatementParameterType]" = None,
197
+ /,
198
+ *,
199
+ connection: "Optional[Connection]" = None,
200
+ schema_type: None = None,
201
+ **kwargs: Any,
202
+ ) -> "Any": ...
203
+ @overload
118
204
  def select_value(
119
205
  self,
120
206
  sql: str,
121
207
  parameters: "Optional[StatementParameterType]" = None,
122
208
  /,
209
+ *,
210
+ connection: "Optional[Connection]" = None,
211
+ schema_type: "type[T]",
212
+ **kwargs: Any,
213
+ ) -> "T": ...
214
+ def select_value(
215
+ self,
216
+ sql: str,
217
+ parameters: "Optional[StatementParameterType]" = None,
218
+ /,
219
+ *,
123
220
  connection: "Optional[Connection]" = None,
124
221
  schema_type: "Optional[type[T]]" = None,
222
+ **kwargs: Any,
125
223
  ) -> "Union[T, Any]":
126
224
  """Fetch a single value from the database.
127
225
 
@@ -129,7 +227,7 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
129
227
  The first value from the first row of results, or None if no results.
130
228
  """
131
229
  connection = self._connection(connection)
132
- sql, parameters = self._process_sql_params(sql, parameters)
230
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
133
231
  with self._with_cursor(connection) as cursor:
134
232
  if not parameters:
135
233
  cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
@@ -141,13 +239,37 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
141
239
  return result[0]
142
240
  return schema_type(result[0]) # type: ignore[call-arg]
143
241
 
242
+ @overload
243
+ def select_value_or_none(
244
+ self,
245
+ sql: str,
246
+ parameters: "Optional[StatementParameterType]" = None,
247
+ /,
248
+ *,
249
+ connection: "Optional[Connection]" = None,
250
+ schema_type: None = None,
251
+ **kwargs: Any,
252
+ ) -> "Optional[Any]": ...
253
+ @overload
144
254
  def select_value_or_none(
145
255
  self,
146
256
  sql: str,
147
257
  parameters: "Optional[StatementParameterType]" = None,
148
258
  /,
259
+ *,
260
+ connection: "Optional[Connection]" = None,
261
+ schema_type: "type[T]",
262
+ **kwargs: Any,
263
+ ) -> "Optional[T]": ...
264
+ def select_value_or_none(
265
+ self,
266
+ sql: str,
267
+ parameters: "Optional[StatementParameterType]" = None,
268
+ /,
269
+ *,
149
270
  connection: "Optional[Connection]" = None,
150
271
  schema_type: "Optional[type[T]]" = None,
272
+ **kwargs: Any,
151
273
  ) -> "Optional[Union[T, Any]]":
152
274
  """Fetch a single value from the database.
153
275
 
@@ -155,7 +277,7 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
155
277
  The first value from the first row of results, or None if no results.
156
278
  """
157
279
  connection = self._connection(connection)
158
- sql, parameters = self._process_sql_params(sql, parameters)
280
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
159
281
  with self._with_cursor(connection) as cursor:
160
282
  if not parameters:
161
283
  cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
@@ -173,7 +295,9 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
173
295
  sql: str,
174
296
  parameters: Optional["StatementParameterType"] = None,
175
297
  /,
298
+ *,
176
299
  connection: Optional["Connection"] = None,
300
+ **kwargs: Any,
177
301
  ) -> int:
178
302
  """Insert, update, or delete data from the database.
179
303
 
@@ -181,7 +305,7 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
181
305
  Row count affected by the operation.
182
306
  """
183
307
  connection = self._connection(connection)
184
- sql, parameters = self._process_sql_params(sql, parameters)
308
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
185
309
 
186
310
  with self._with_cursor(connection) as cursor:
187
311
  if not parameters:
@@ -190,13 +314,37 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
190
314
  cursor.execute(sql, parameters)
191
315
  return cursor.rowcount if hasattr(cursor, "rowcount") else -1
192
316
 
317
+ @overload
318
+ def insert_update_delete_returning(
319
+ self,
320
+ sql: str,
321
+ parameters: "Optional[StatementParameterType]" = None,
322
+ /,
323
+ *,
324
+ connection: "Optional[Connection]" = None,
325
+ schema_type: None = None,
326
+ **kwargs: Any,
327
+ ) -> "dict[str, Any]": ...
328
+ @overload
329
+ def insert_update_delete_returning(
330
+ self,
331
+ sql: str,
332
+ parameters: "Optional[StatementParameterType]" = None,
333
+ /,
334
+ *,
335
+ connection: "Optional[Connection]" = None,
336
+ schema_type: "type[ModelDTOT]",
337
+ **kwargs: Any,
338
+ ) -> "ModelDTOT": ...
193
339
  def insert_update_delete_returning(
194
340
  self,
195
341
  sql: str,
196
342
  parameters: Optional["StatementParameterType"] = None,
197
343
  /,
344
+ *,
198
345
  connection: Optional["Connection"] = None,
199
346
  schema_type: "Optional[type[ModelDTOT]]" = None,
347
+ **kwargs: Any,
200
348
  ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
201
349
  """Insert, update, or delete data from the database and return result.
202
350
 
@@ -204,7 +352,7 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
204
352
  The first row of results.
205
353
  """
206
354
  connection = self._connection(connection)
207
- sql, parameters = self._process_sql_params(sql, parameters)
355
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
208
356
 
209
357
  with self._with_cursor(connection) as cursor:
210
358
  if not parameters:
@@ -214,46 +362,32 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
214
362
  result = cursor.fetchall()
215
363
  if len(result) == 0:
216
364
  return None
217
- column_names = [c[0] for c in cursor.description or []]
218
- if schema_type is not None:
219
- return cast("ModelDTOT", schema_type(**dict(zip(column_names, result[0]))))
220
- return dict(zip(column_names, result[0]))
221
-
222
- def _process_sql_params(
223
- self, sql: str, parameters: "Optional[StatementParameterType]" = None
224
- ) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
225
- """Process SQL query and parameters for DB-API execution.
226
365
 
227
- Converts named parameters (:name) to positional parameters (?) for SQLite.
366
+ # Get column names from cursor description
367
+ column_names = [c[0] for c in cursor.description or []]
228
368
 
229
- Args:
230
- sql: The SQL query string.
231
- parameters: The parameters for the query (dict, tuple, list, or None).
369
+ # Get the first row's values - ensure we're getting the actual values
370
+ row_values = result[0]
232
371
 
233
- Returns:
234
- A tuple containing the processed SQL string and the processed parameters.
235
- """
236
- if not isinstance(parameters, dict) or not parameters:
237
- # If parameters are not a dict, or empty dict, assume positional/no params
238
- # Let the underlying driver handle tuples/lists directly
239
- return sql, parameters
372
+ # Debug print to see what we're getting
240
373
 
241
- # Convert named parameters to positional parameters
242
- processed_sql = sql
243
- processed_params: list[Any] = []
244
- for key, value in parameters.items():
245
- # Replace :key with ? in the SQL
246
- processed_sql = processed_sql.replace(f":{key}", "?")
247
- processed_params.append(value)
374
+ # Create dictionary mapping column names to values
375
+ result_dict = {}
376
+ for i, col_name in enumerate(column_names):
377
+ result_dict[col_name] = row_values[i]
248
378
 
249
- return processed_sql, tuple(processed_params)
379
+ if schema_type is not None:
380
+ return cast("ModelDTOT", schema_type(**result_dict))
381
+ return result_dict
250
382
 
251
383
  def execute_script(
252
384
  self,
253
385
  sql: str,
254
386
  parameters: Optional["StatementParameterType"] = None,
255
387
  /,
388
+ *,
256
389
  connection: Optional["Connection"] = None,
390
+ **kwargs: Any,
257
391
  ) -> str:
258
392
  """Execute a script.
259
393
 
@@ -261,43 +395,13 @@ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
261
395
  Status message for the operation.
262
396
  """
263
397
  connection = self._connection(connection)
398
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
264
399
 
265
- # For DDL statements, don't pass parameters to execute
266
- # SQLite doesn't support parameters for DDL statements
267
- with self._with_cursor(connection) as cursor:
268
- if not parameters:
269
- cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
270
- else:
271
- sql, parameters = self._process_sql_params(sql, parameters)
272
- cursor.execute(sql, parameters) # type: ignore[arg-type]
273
-
274
- return cast("str", cursor.statusmessage) if hasattr(cursor, "statusmessage") else "DONE" # pyright: ignore[reportUnknownMemberType,reportAttributeAccessIssue]
275
-
276
- def execute_script_returning(
277
- self,
278
- sql: str,
279
- parameters: Optional["StatementParameterType"] = None,
280
- /,
281
- connection: Optional["Connection"] = None,
282
- schema_type: "Optional[type[ModelDTOT]]" = None,
283
- ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
284
- """Execute a script and return result.
285
-
286
- Returns:
287
- The first row of results.
288
- """
289
- connection = self._connection(connection)
290
- sql, parameters = self._process_sql_params(sql, parameters)
291
-
400
+ # The _process_sql_params handles parameter formatting for the dialect.
292
401
  with self._with_cursor(connection) as cursor:
293
402
  if not parameters:
294
403
  cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
295
404
  else:
296
405
  cursor.execute(sql, parameters)
297
- result = cursor.fetchall()
298
- if len(result) == 0:
299
- return None
300
- column_names = [c[0] for c in cursor.description or []]
301
- if schema_type is not None:
302
- return cast("ModelDTOT", schema_type(**dict(zip(column_names, result[0]))))
303
- return dict(zip(column_names, result[0]))
406
+
407
+ return cast("str", cursor.statusmessage) if hasattr(cursor, "statusmessage") else "DONE" # pyright: ignore[reportUnknownMemberType,reportAttributeAccessIssue]