sqlspec 0.7.1__py3-none-any.whl → 0.8.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.

Files changed (49) hide show
  1. sqlspec/__init__.py +15 -0
  2. sqlspec/_serialization.py +16 -2
  3. sqlspec/_typing.py +1 -1
  4. sqlspec/adapters/adbc/__init__.py +7 -0
  5. sqlspec/adapters/adbc/config.py +160 -17
  6. sqlspec/adapters/adbc/driver.py +333 -0
  7. sqlspec/adapters/aiosqlite/__init__.py +6 -2
  8. sqlspec/adapters/aiosqlite/config.py +25 -7
  9. sqlspec/adapters/aiosqlite/driver.py +275 -0
  10. sqlspec/adapters/asyncmy/__init__.py +7 -2
  11. sqlspec/adapters/asyncmy/config.py +75 -14
  12. sqlspec/adapters/asyncmy/driver.py +255 -0
  13. sqlspec/adapters/asyncpg/__init__.py +9 -0
  14. sqlspec/adapters/asyncpg/config.py +99 -20
  15. sqlspec/adapters/asyncpg/driver.py +288 -0
  16. sqlspec/adapters/duckdb/__init__.py +6 -2
  17. sqlspec/adapters/duckdb/config.py +195 -13
  18. sqlspec/adapters/duckdb/driver.py +225 -0
  19. sqlspec/adapters/oracledb/__init__.py +11 -8
  20. sqlspec/adapters/oracledb/config/__init__.py +6 -6
  21. sqlspec/adapters/oracledb/config/_asyncio.py +98 -13
  22. sqlspec/adapters/oracledb/config/_common.py +1 -1
  23. sqlspec/adapters/oracledb/config/_sync.py +99 -14
  24. sqlspec/adapters/oracledb/driver.py +498 -0
  25. sqlspec/adapters/psycopg/__init__.py +11 -0
  26. sqlspec/adapters/psycopg/config/__init__.py +6 -6
  27. sqlspec/adapters/psycopg/config/_async.py +105 -13
  28. sqlspec/adapters/psycopg/config/_common.py +2 -2
  29. sqlspec/adapters/psycopg/config/_sync.py +105 -13
  30. sqlspec/adapters/psycopg/driver.py +616 -0
  31. sqlspec/adapters/sqlite/__init__.py +7 -0
  32. sqlspec/adapters/sqlite/config.py +25 -7
  33. sqlspec/adapters/sqlite/driver.py +303 -0
  34. sqlspec/base.py +416 -36
  35. sqlspec/extensions/litestar/__init__.py +19 -0
  36. sqlspec/extensions/litestar/_utils.py +56 -0
  37. sqlspec/extensions/litestar/config.py +81 -0
  38. sqlspec/extensions/litestar/handlers.py +188 -0
  39. sqlspec/extensions/litestar/plugin.py +100 -11
  40. sqlspec/typing.py +72 -17
  41. sqlspec/utils/__init__.py +3 -0
  42. sqlspec/utils/fixtures.py +4 -5
  43. sqlspec/utils/sync_tools.py +335 -0
  44. {sqlspec-0.7.1.dist-info → sqlspec-0.8.0.dist-info}/METADATA +1 -1
  45. sqlspec-0.8.0.dist-info/RECORD +57 -0
  46. sqlspec-0.7.1.dist-info/RECORD +0 -46
  47. {sqlspec-0.7.1.dist-info → sqlspec-0.8.0.dist-info}/WHEEL +0 -0
  48. {sqlspec-0.7.1.dist-info → sqlspec-0.8.0.dist-info}/licenses/LICENSE +0 -0
  49. {sqlspec-0.7.1.dist-info → sqlspec-0.8.0.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,303 @@
1
+ from contextlib import contextmanager
2
+ from sqlite3 import Connection, Cursor
3
+ from typing import TYPE_CHECKING, Any, Optional, Union, cast
4
+
5
+ from sqlspec.base import SyncDriverAdapterProtocol, T
6
+
7
+ if TYPE_CHECKING:
8
+ from collections.abc import Generator
9
+
10
+ from sqlspec.typing import ModelDTOT, StatementParameterType
11
+
12
+ __all__ = ("SqliteDriver",)
13
+
14
+
15
+ class SqliteDriver(SyncDriverAdapterProtocol["Connection"]):
16
+ """SQLite Sync Driver Adapter."""
17
+
18
+ connection: "Connection"
19
+
20
+ def __init__(self, connection: "Connection") -> None:
21
+ self.connection = connection
22
+
23
+ @staticmethod
24
+ def _cursor(connection: "Connection", *args: Any, **kwargs: Any) -> Cursor:
25
+ return connection.cursor(*args, **kwargs) # type: ignore[no-any-return]
26
+
27
+ @contextmanager
28
+ def _with_cursor(self, connection: "Connection") -> "Generator[Cursor, None, None]":
29
+ cursor = self._cursor(connection)
30
+ try:
31
+ yield cursor
32
+ finally:
33
+ cursor.close()
34
+
35
+ def select(
36
+ self,
37
+ sql: str,
38
+ parameters: Optional["StatementParameterType"] = None,
39
+ /,
40
+ connection: Optional["Connection"] = None,
41
+ schema_type: "Optional[type[ModelDTOT]]" = None,
42
+ ) -> "list[Union[ModelDTOT, dict[str, Any]]]":
43
+ """Fetch data from the database.
44
+
45
+ Returns:
46
+ List of row data as either model instances or dictionaries.
47
+ """
48
+ connection = self._connection(connection)
49
+ sql, parameters = self._process_sql_params(sql, parameters)
50
+ with self._with_cursor(connection) as cursor:
51
+ if not parameters:
52
+ cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
53
+ else:
54
+ cursor.execute(sql, parameters)
55
+ results = cursor.fetchall()
56
+ if not results:
57
+ return []
58
+ column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
59
+ if schema_type is not None:
60
+ return [cast("ModelDTOT", schema_type(**dict(zip(column_names, row)))) for row in results] # pyright: ignore[reportUnknownArgumentType]
61
+ return [dict(zip(column_names, row)) for row in results] # pyright: ignore[reportUnknownArgumentType]
62
+
63
+ def select_one(
64
+ self,
65
+ sql: str,
66
+ parameters: Optional["StatementParameterType"] = None,
67
+ /,
68
+ connection: Optional["Connection"] = None,
69
+ schema_type: "Optional[type[ModelDTOT]]" = None,
70
+ ) -> "Union[ModelDTOT, dict[str, Any]]":
71
+ """Fetch one row from the database.
72
+
73
+ Returns:
74
+ The first row of the query results.
75
+ """
76
+ connection = self._connection(connection)
77
+ sql, parameters = self._process_sql_params(sql, parameters)
78
+ with self._with_cursor(connection) as cursor:
79
+ if not parameters:
80
+ cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
81
+ else:
82
+ cursor.execute(sql, parameters)
83
+ result = cursor.fetchone()
84
+ result = self.check_not_found(result)
85
+ column_names = [c[0] for c in cursor.description or []]
86
+ if schema_type is None:
87
+ return dict(zip(column_names, result))
88
+ return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
89
+
90
+ def select_one_or_none(
91
+ self,
92
+ sql: str,
93
+ parameters: Optional["StatementParameterType"] = None,
94
+ /,
95
+ connection: Optional["Connection"] = None,
96
+ schema_type: "Optional[type[ModelDTOT]]" = None,
97
+ ) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
98
+ """Fetch one row from the database.
99
+
100
+ Returns:
101
+ The first row of the query results.
102
+ """
103
+ connection = self._connection(connection)
104
+ sql, parameters = self._process_sql_params(sql, parameters)
105
+ with self._with_cursor(connection) as cursor:
106
+ if not parameters:
107
+ cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
108
+ else:
109
+ cursor.execute(sql, parameters)
110
+ result = cursor.fetchone()
111
+ if result is None:
112
+ return None
113
+ column_names = [c[0] for c in cursor.description or []]
114
+ if schema_type is None:
115
+ return dict(zip(column_names, result))
116
+ return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
117
+
118
+ def select_value(
119
+ self,
120
+ sql: str,
121
+ parameters: "Optional[StatementParameterType]" = None,
122
+ /,
123
+ connection: "Optional[Connection]" = None,
124
+ schema_type: "Optional[type[T]]" = None,
125
+ ) -> "Union[T, Any]":
126
+ """Fetch a single value from the database.
127
+
128
+ Returns:
129
+ The first value from the first row of results, or None if no results.
130
+ """
131
+ connection = self._connection(connection)
132
+ sql, parameters = self._process_sql_params(sql, parameters)
133
+ with self._with_cursor(connection) as cursor:
134
+ if not parameters:
135
+ cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
136
+ else:
137
+ cursor.execute(sql, parameters)
138
+ result = cursor.fetchone()
139
+ result = self.check_not_found(result)
140
+ if schema_type is None:
141
+ return result[0]
142
+ return schema_type(result[0]) # type: ignore[call-arg]
143
+
144
+ def select_value_or_none(
145
+ self,
146
+ sql: str,
147
+ parameters: "Optional[StatementParameterType]" = None,
148
+ /,
149
+ connection: "Optional[Connection]" = None,
150
+ schema_type: "Optional[type[T]]" = None,
151
+ ) -> "Optional[Union[T, Any]]":
152
+ """Fetch a single value from the database.
153
+
154
+ Returns:
155
+ The first value from the first row of results, or None if no results.
156
+ """
157
+ connection = self._connection(connection)
158
+ sql, parameters = self._process_sql_params(sql, parameters)
159
+ with self._with_cursor(connection) as cursor:
160
+ if not parameters:
161
+ cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
162
+ else:
163
+ cursor.execute(sql, parameters)
164
+ result = cursor.fetchone()
165
+ if result is None:
166
+ return None
167
+ if schema_type is None:
168
+ return result[0]
169
+ return schema_type(result[0]) # type: ignore[call-arg]
170
+
171
+ def insert_update_delete(
172
+ self,
173
+ sql: str,
174
+ parameters: Optional["StatementParameterType"] = None,
175
+ /,
176
+ connection: Optional["Connection"] = None,
177
+ ) -> int:
178
+ """Insert, update, or delete data from the database.
179
+
180
+ Returns:
181
+ Row count affected by the operation.
182
+ """
183
+ connection = self._connection(connection)
184
+ sql, parameters = self._process_sql_params(sql, parameters)
185
+
186
+ 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)
191
+ return cursor.rowcount if hasattr(cursor, "rowcount") else -1
192
+
193
+ def insert_update_delete_returning(
194
+ self,
195
+ sql: str,
196
+ parameters: Optional["StatementParameterType"] = None,
197
+ /,
198
+ connection: Optional["Connection"] = None,
199
+ schema_type: "Optional[type[ModelDTOT]]" = None,
200
+ ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
201
+ """Insert, update, or delete data from the database and return result.
202
+
203
+ Returns:
204
+ The first row of results.
205
+ """
206
+ connection = self._connection(connection)
207
+ sql, parameters = self._process_sql_params(sql, parameters)
208
+
209
+ with self._with_cursor(connection) as cursor:
210
+ if not parameters:
211
+ cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
212
+ else:
213
+ cursor.execute(sql, parameters)
214
+ result = cursor.fetchall()
215
+ if len(result) == 0:
216
+ 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
+
227
+ Converts named parameters (:name) to positional parameters (?) for SQLite.
228
+
229
+ Args:
230
+ sql: The SQL query string.
231
+ parameters: The parameters for the query (dict, tuple, list, or None).
232
+
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
240
+
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)
248
+
249
+ return processed_sql, tuple(processed_params)
250
+
251
+ def execute_script(
252
+ self,
253
+ sql: str,
254
+ parameters: Optional["StatementParameterType"] = None,
255
+ /,
256
+ connection: Optional["Connection"] = None,
257
+ ) -> str:
258
+ """Execute a script.
259
+
260
+ Returns:
261
+ Status message for the operation.
262
+ """
263
+ connection = self._connection(connection)
264
+
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
+
292
+ with self._with_cursor(connection) as cursor:
293
+ if not parameters:
294
+ cursor.execute(sql) # pyright: ignore[reportUnknownMemberType]
295
+ else:
296
+ 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]))