sqlspec 0.25.0__py3-none-any.whl → 0.27.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.
- sqlspec/__init__.py +7 -15
- sqlspec/_serialization.py +256 -24
- sqlspec/_typing.py +71 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +870 -0
- sqlspec/adapters/adbc/config.py +69 -12
- sqlspec/adapters/adbc/data_dictionary.py +340 -0
- sqlspec/adapters/adbc/driver.py +266 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +153 -0
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +527 -0
- sqlspec/adapters/aiosqlite/config.py +88 -15
- sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
- sqlspec/adapters/aiosqlite/driver.py +143 -40
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +7 -7
- sqlspec/adapters/asyncmy/__init__.py +7 -1
- sqlspec/adapters/asyncmy/_types.py +2 -2
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +68 -23
- sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
- sqlspec/adapters/asyncmy/driver.py +313 -58
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +2 -1
- sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
- sqlspec/adapters/asyncpg/_types.py +11 -7
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +450 -0
- sqlspec/adapters/asyncpg/config.py +59 -35
- sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
- sqlspec/adapters/asyncpg/driver.py +170 -25
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/_types.py +1 -1
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +576 -0
- sqlspec/adapters/bigquery/config.py +27 -10
- sqlspec/adapters/bigquery/data_dictionary.py +149 -0
- sqlspec/adapters/bigquery/driver.py +368 -142
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +125 -0
- sqlspec/adapters/duckdb/_types.py +1 -1
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +80 -20
- sqlspec/adapters/duckdb/data_dictionary.py +163 -0
- sqlspec/adapters/duckdb/driver.py +167 -45
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +4 -4
- sqlspec/adapters/duckdb/type_converter.py +133 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +20 -2
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1745 -0
- sqlspec/adapters/oracledb/config.py +122 -32
- sqlspec/adapters/oracledb/data_dictionary.py +509 -0
- sqlspec/adapters/oracledb/driver.py +353 -91
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +348 -73
- sqlspec/adapters/oracledb/type_converter.py +207 -0
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +2 -1
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +482 -0
- sqlspec/adapters/psqlpy/config.py +46 -17
- sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
- sqlspec/adapters/psqlpy/driver.py +123 -209
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +102 -0
- sqlspec/adapters/psycopg/_type_handlers.py +80 -0
- sqlspec/adapters/psycopg/_types.py +2 -1
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +944 -0
- sqlspec/adapters/psycopg/config.py +69 -35
- sqlspec/adapters/psycopg/data_dictionary.py +331 -0
- sqlspec/adapters/psycopg/driver.py +238 -81
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/sqlite/__init__.py +2 -1
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +1 -1
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +572 -0
- sqlspec/adapters/sqlite/config.py +87 -15
- sqlspec/adapters/sqlite/data_dictionary.py +149 -0
- sqlspec/adapters/sqlite/driver.py +137 -54
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +18 -9
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +162 -89
- sqlspec/builder/_column.py +62 -29
- sqlspec/builder/_ddl.py +180 -121
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +53 -94
- sqlspec/builder/_insert.py +32 -131
- sqlspec/builder/_join.py +375 -0
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +111 -17
- sqlspec/builder/_select.py +1457 -24
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +307 -194
- sqlspec/config.py +252 -67
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +17 -17
- sqlspec/core/compiler.py +62 -9
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +83 -48
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +36 -30
- sqlspec/core/type_conversion.py +235 -0
- sqlspec/driver/__init__.py +7 -6
- sqlspec/driver/_async.py +188 -151
- sqlspec/driver/_common.py +285 -80
- sqlspec/driver/_sync.py +188 -152
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +75 -7
- sqlspec/extensions/adk/__init__.py +53 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +181 -0
- sqlspec/extensions/adk/store.py +536 -0
- sqlspec/extensions/aiosql/adapter.py +73 -53
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +59 -266
- sqlspec/extensions/litestar/handlers.py +46 -17
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +324 -223
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/__init__.py +4 -3
- sqlspec/migrations/base.py +302 -39
- sqlspec/migrations/commands.py +611 -144
- sqlspec/migrations/context.py +142 -0
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +68 -23
- sqlspec/migrations/runner.py +543 -107
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +66 -36
- sqlspec/storage/_utils.py +98 -0
- sqlspec/storage/backends/fsspec.py +134 -106
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +278 -162
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +16 -84
- sqlspec/utils/config_resolver.py +153 -0
- sqlspec/utils/correlation.py +4 -5
- sqlspec/utils/data_transformation.py +3 -2
- sqlspec/utils/deprecation.py +9 -8
- sqlspec/utils/fixtures.py +4 -4
- sqlspec/utils/logging.py +46 -6
- sqlspec/utils/module_loader.py +2 -2
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +50 -2
- sqlspec/utils/sync_tools.py +21 -17
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +111 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
- sqlspec-0.27.0.dist-info/RECORD +207 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -254
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_join_operations.py +0 -389
- sqlspec/builder/mixins/_merge_operations.py +0 -592
- sqlspec/builder/mixins/_order_limit_operations.py +0 -152
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -936
- sqlspec/builder/mixins/_update_operations.py +0 -218
- sqlspec/builder/mixins/_where_clause.py +0 -1304
- sqlspec-0.25.0.dist-info/RECORD +0 -139
- sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,23 +6,28 @@ from files using aiosql while using SQLSpec's features for execution and type ma
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
-
from collections.abc import
|
|
10
|
-
from contextlib import
|
|
11
|
-
from typing import
|
|
9
|
+
from collections.abc import Generator
|
|
10
|
+
from contextlib import contextmanager
|
|
11
|
+
from typing import Any, ClassVar, Generic, TypeVar
|
|
12
12
|
|
|
13
13
|
from sqlspec.core.result import SQLResult
|
|
14
14
|
from sqlspec.core.statement import SQL, StatementConfig
|
|
15
|
+
from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
|
|
15
16
|
from sqlspec.exceptions import MissingDependencyError
|
|
16
|
-
from sqlspec.typing import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
from sqlspec.typing import (
|
|
18
|
+
AIOSQL_INSTALLED,
|
|
19
|
+
AiosqlAsyncProtocol,
|
|
20
|
+
AiosqlParamType,
|
|
21
|
+
AiosqlSQLOperationType,
|
|
22
|
+
AiosqlSyncProtocol,
|
|
23
|
+
)
|
|
20
24
|
|
|
21
25
|
logger = logging.getLogger("sqlspec.extensions.aiosql")
|
|
22
26
|
|
|
23
27
|
__all__ = ("AiosqlAsyncAdapter", "AiosqlSyncAdapter")
|
|
24
28
|
|
|
25
29
|
T = TypeVar("T")
|
|
30
|
+
DriverT = TypeVar("DriverT", bound="SyncDriverAdapterBase | AsyncDriverAdapterBase")
|
|
26
31
|
|
|
27
32
|
|
|
28
33
|
class AsyncCursorLike:
|
|
@@ -34,7 +39,7 @@ class AsyncCursorLike:
|
|
|
34
39
|
return list(self.result.data)
|
|
35
40
|
return []
|
|
36
41
|
|
|
37
|
-
async def fetchone(self) ->
|
|
42
|
+
async def fetchone(self) -> Any | None:
|
|
38
43
|
rows = await self.fetchall()
|
|
39
44
|
return rows[0] if rows else None
|
|
40
45
|
|
|
@@ -48,7 +53,7 @@ class CursorLike:
|
|
|
48
53
|
return list(self.result.data)
|
|
49
54
|
return []
|
|
50
55
|
|
|
51
|
-
def fetchone(self) ->
|
|
56
|
+
def fetchone(self) -> Any | None:
|
|
52
57
|
rows = self.fetchall()
|
|
53
58
|
return rows[0] if rows else None
|
|
54
59
|
|
|
@@ -59,7 +64,7 @@ def _check_aiosql_available() -> None:
|
|
|
59
64
|
raise MissingDependencyError(msg, "aiosql")
|
|
60
65
|
|
|
61
66
|
|
|
62
|
-
def _normalize_dialect(dialect: "
|
|
67
|
+
def _normalize_dialect(dialect: "str | Any | None") -> str:
|
|
63
68
|
"""Normalize dialect name for SQLGlot compatibility.
|
|
64
69
|
|
|
65
70
|
Args:
|
|
@@ -71,12 +76,12 @@ def _normalize_dialect(dialect: "Union[str, Any, None]") -> str:
|
|
|
71
76
|
if dialect is None:
|
|
72
77
|
return "sql"
|
|
73
78
|
|
|
74
|
-
if
|
|
75
|
-
dialect_str = str(dialect.__name__).lower() # pyright: ignore
|
|
76
|
-
elif hasattr(dialect, "name"):
|
|
77
|
-
dialect_str = str(dialect.name).lower() # pyright: ignore
|
|
78
|
-
elif isinstance(dialect, str):
|
|
79
|
+
if isinstance(dialect, str):
|
|
79
80
|
dialect_str = dialect.lower()
|
|
81
|
+
elif hasattr(dialect, "__name__"):
|
|
82
|
+
dialect_str = str(dialect.__name__).lower()
|
|
83
|
+
elif hasattr(dialect, "name"):
|
|
84
|
+
dialect_str = str(dialect.name).lower()
|
|
80
85
|
else:
|
|
81
86
|
dialect_str = str(dialect).lower()
|
|
82
87
|
|
|
@@ -91,19 +96,19 @@ def _normalize_dialect(dialect: "Union[str, Any, None]") -> str:
|
|
|
91
96
|
return dialect_mapping.get(dialect_str, dialect_str)
|
|
92
97
|
|
|
93
98
|
|
|
94
|
-
class _AiosqlAdapterBase:
|
|
99
|
+
class _AiosqlAdapterBase(Generic[DriverT]):
|
|
95
100
|
"""Base adapter class providing common functionality for aiosql integration."""
|
|
96
101
|
|
|
97
|
-
def __init__(self, driver:
|
|
102
|
+
def __init__(self, driver: DriverT) -> None:
|
|
98
103
|
"""Initialize the base adapter.
|
|
99
104
|
|
|
100
105
|
Args:
|
|
101
106
|
driver: SQLSpec driver to use for execution.
|
|
102
107
|
"""
|
|
103
108
|
_check_aiosql_available()
|
|
104
|
-
self.driver = driver
|
|
109
|
+
self.driver: DriverT = driver
|
|
105
110
|
|
|
106
|
-
def process_sql(self, query_name: str, op_type: "
|
|
111
|
+
def process_sql(self, query_name: str, op_type: "AiosqlSQLOperationType", sql: str) -> str:
|
|
107
112
|
"""Process SQL string for aiosql compatibility.
|
|
108
113
|
|
|
109
114
|
Args:
|
|
@@ -116,7 +121,7 @@ class _AiosqlAdapterBase:
|
|
|
116
121
|
"""
|
|
117
122
|
return sql
|
|
118
123
|
|
|
119
|
-
def _create_sql_object(self, sql: str, parameters: "
|
|
124
|
+
def _create_sql_object(self, sql: str, parameters: "AiosqlParamType" = None) -> SQL:
|
|
120
125
|
"""Create SQL object with proper configuration.
|
|
121
126
|
|
|
122
127
|
Args:
|
|
@@ -134,7 +139,7 @@ class _AiosqlAdapterBase:
|
|
|
134
139
|
)
|
|
135
140
|
|
|
136
141
|
|
|
137
|
-
class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
142
|
+
class AiosqlSyncAdapter(_AiosqlAdapterBase[SyncDriverAdapterBase], AiosqlSyncProtocol):
|
|
138
143
|
"""Synchronous adapter that implements aiosql protocol using SQLSpec drivers.
|
|
139
144
|
|
|
140
145
|
This adapter bridges aiosql's synchronous driver protocol with SQLSpec's sync drivers,
|
|
@@ -152,7 +157,7 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
152
157
|
super().__init__(driver)
|
|
153
158
|
|
|
154
159
|
def select(
|
|
155
|
-
self, conn: Any, query_name: str, sql: str, parameters: "
|
|
160
|
+
self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType", record_class: Any | None = None
|
|
156
161
|
) -> Generator[Any, None, None]:
|
|
157
162
|
"""Execute a SELECT query and return results as generator.
|
|
158
163
|
|
|
@@ -182,8 +187,8 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
182
187
|
yield from result.data
|
|
183
188
|
|
|
184
189
|
def select_one(
|
|
185
|
-
self, conn: Any, query_name: str, sql: str, parameters: "
|
|
186
|
-
) ->
|
|
190
|
+
self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType", record_class: Any | None = None
|
|
191
|
+
) -> tuple[Any, ...] | None:
|
|
187
192
|
"""Execute a SELECT query and return first result.
|
|
188
193
|
|
|
189
194
|
Args:
|
|
@@ -206,13 +211,14 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
206
211
|
"Use schema_type in driver.execute or _sqlspec_schema_type in parameters."
|
|
207
212
|
)
|
|
208
213
|
|
|
209
|
-
result =
|
|
214
|
+
result = self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
210
215
|
|
|
211
216
|
if hasattr(result, "data") and result.data and isinstance(result, SQLResult):
|
|
212
|
-
|
|
217
|
+
row = result.data[0]
|
|
218
|
+
return tuple(row.values()) if isinstance(row, dict) else row
|
|
213
219
|
return None
|
|
214
220
|
|
|
215
|
-
def select_value(self, conn: Any, query_name: str, sql: str, parameters: "
|
|
221
|
+
def select_value(self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType") -> Any | None:
|
|
216
222
|
"""Execute a SELECT query and return first value of first row.
|
|
217
223
|
|
|
218
224
|
Args:
|
|
@@ -235,7 +241,9 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
235
241
|
return row
|
|
236
242
|
|
|
237
243
|
@contextmanager
|
|
238
|
-
def select_cursor(
|
|
244
|
+
def select_cursor(
|
|
245
|
+
self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType"
|
|
246
|
+
) -> Generator[Any, None, None]:
|
|
239
247
|
"""Execute a SELECT query and return cursor context manager.
|
|
240
248
|
|
|
241
249
|
Args:
|
|
@@ -251,7 +259,7 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
251
259
|
|
|
252
260
|
yield CursorLike(result)
|
|
253
261
|
|
|
254
|
-
def insert_update_delete(self, conn: Any, query_name: str, sql: str, parameters: "
|
|
262
|
+
def insert_update_delete(self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType") -> int:
|
|
255
263
|
"""Execute INSERT/UPDATE/DELETE and return affected rows.
|
|
256
264
|
|
|
257
265
|
Args:
|
|
@@ -263,11 +271,11 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
263
271
|
Returns:
|
|
264
272
|
Number of affected rows
|
|
265
273
|
"""
|
|
266
|
-
result =
|
|
274
|
+
result = self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
267
275
|
|
|
268
276
|
return result.rows_affected if hasattr(result, "rows_affected") else 0
|
|
269
277
|
|
|
270
|
-
def insert_update_delete_many(self, conn: Any, query_name: str, sql: str, parameters: "
|
|
278
|
+
def insert_update_delete_many(self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType") -> int:
|
|
271
279
|
"""Execute INSERT/UPDATE/DELETE with many parameter sets.
|
|
272
280
|
|
|
273
281
|
Args:
|
|
@@ -279,13 +287,11 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
279
287
|
Returns:
|
|
280
288
|
Number of affected rows
|
|
281
289
|
"""
|
|
282
|
-
result =
|
|
283
|
-
"SQLResult", self.driver.execute_many(self._create_sql_object(sql), parameters=parameters, connection=conn)
|
|
284
|
-
)
|
|
290
|
+
result = self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
285
291
|
|
|
286
292
|
return result.rows_affected if hasattr(result, "rows_affected") else 0
|
|
287
293
|
|
|
288
|
-
def insert_returning(self, conn: Any, query_name: str, sql: str, parameters: "
|
|
294
|
+
def insert_returning(self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType") -> Any | None:
|
|
289
295
|
"""Execute INSERT with RETURNING and return result.
|
|
290
296
|
|
|
291
297
|
Args:
|
|
@@ -300,7 +306,18 @@ class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
|
300
306
|
return self.select_one(conn, query_name, sql, parameters)
|
|
301
307
|
|
|
302
308
|
|
|
303
|
-
class
|
|
309
|
+
class _AsyncCursorContextManager(Generic[T]):
|
|
310
|
+
def __init__(self, cursor_like: T) -> None:
|
|
311
|
+
self._cursor_like = cursor_like
|
|
312
|
+
|
|
313
|
+
async def __aenter__(self) -> T:
|
|
314
|
+
return self._cursor_like
|
|
315
|
+
|
|
316
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class AiosqlAsyncAdapter(_AiosqlAdapterBase[AsyncDriverAdapterBase], AiosqlAsyncProtocol):
|
|
304
321
|
"""Asynchronous adapter that implements aiosql protocol using SQLSpec drivers.
|
|
305
322
|
|
|
306
323
|
This adapter bridges aiosql's async driver protocol with SQLSpec's async drivers,
|
|
@@ -318,7 +335,7 @@ class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
|
318
335
|
super().__init__(driver)
|
|
319
336
|
|
|
320
337
|
async def select(
|
|
321
|
-
self, conn: Any, query_name: str, sql: str, parameters: "
|
|
338
|
+
self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType", record_class: Any | None = None
|
|
322
339
|
) -> list[Any]:
|
|
323
340
|
"""Execute a SELECT query and return results as list.
|
|
324
341
|
|
|
@@ -342,15 +359,15 @@ class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
|
342
359
|
"Use schema_type in driver.execute or _sqlspec_schema_type in parameters."
|
|
343
360
|
)
|
|
344
361
|
|
|
345
|
-
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
362
|
+
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
346
363
|
|
|
347
364
|
if hasattr(result, "data") and result.data is not None and isinstance(result, SQLResult):
|
|
348
365
|
return list(result.data)
|
|
349
366
|
return []
|
|
350
367
|
|
|
351
368
|
async def select_one(
|
|
352
|
-
self, conn: Any, query_name: str, sql: str, parameters: "
|
|
353
|
-
) ->
|
|
369
|
+
self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType", record_class: Any | None = None
|
|
370
|
+
) -> Any | None:
|
|
354
371
|
"""Execute a SELECT query and return first result.
|
|
355
372
|
|
|
356
373
|
Args:
|
|
@@ -373,13 +390,14 @@ class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
|
373
390
|
"Use schema_type in driver.execute or _sqlspec_schema_type in parameters."
|
|
374
391
|
)
|
|
375
392
|
|
|
376
|
-
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
393
|
+
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
377
394
|
|
|
378
395
|
if hasattr(result, "data") and result.data and isinstance(result, SQLResult):
|
|
379
|
-
|
|
396
|
+
row = result.data[0]
|
|
397
|
+
return tuple(row.values()) if isinstance(row, dict) else row
|
|
380
398
|
return None
|
|
381
399
|
|
|
382
|
-
async def select_value(self, conn: Any, query_name: str, sql: str, parameters: "
|
|
400
|
+
async def select_value(self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType") -> Any | None:
|
|
383
401
|
"""Execute a SELECT query and return first value of first row.
|
|
384
402
|
|
|
385
403
|
Args:
|
|
@@ -401,8 +419,9 @@ class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
|
401
419
|
return row[0] if len(row) > 0 else None
|
|
402
420
|
return row
|
|
403
421
|
|
|
404
|
-
|
|
405
|
-
|
|
422
|
+
async def select_cursor(
|
|
423
|
+
self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType"
|
|
424
|
+
) -> "_AsyncCursorContextManager[Any]":
|
|
406
425
|
"""Execute a SELECT query and return cursor context manager.
|
|
407
426
|
|
|
408
427
|
Args:
|
|
@@ -414,11 +433,10 @@ class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
|
414
433
|
Yields:
|
|
415
434
|
Cursor-like object with results
|
|
416
435
|
"""
|
|
417
|
-
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
418
|
-
|
|
419
|
-
yield AsyncCursorLike(result)
|
|
436
|
+
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
437
|
+
return _AsyncCursorContextManager(AsyncCursorLike(result))
|
|
420
438
|
|
|
421
|
-
async def insert_update_delete(self, conn: Any, query_name: str, sql: str, parameters: "
|
|
439
|
+
async def insert_update_delete(self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType") -> None:
|
|
422
440
|
"""Execute INSERT/UPDATE/DELETE.
|
|
423
441
|
|
|
424
442
|
Args:
|
|
@@ -430,9 +448,11 @@ class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
|
430
448
|
Note:
|
|
431
449
|
Returns None per aiosql async protocol
|
|
432
450
|
"""
|
|
433
|
-
await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
451
|
+
await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
434
452
|
|
|
435
|
-
async def insert_update_delete_many(
|
|
453
|
+
async def insert_update_delete_many(
|
|
454
|
+
self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType"
|
|
455
|
+
) -> None:
|
|
436
456
|
"""Execute INSERT/UPDATE/DELETE with many parameter sets.
|
|
437
457
|
|
|
438
458
|
Args:
|
|
@@ -444,9 +464,9 @@ class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
|
444
464
|
Note:
|
|
445
465
|
Returns None per aiosql async protocol
|
|
446
466
|
"""
|
|
447
|
-
await self.driver.
|
|
467
|
+
await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
448
468
|
|
|
449
|
-
async def insert_returning(self, conn: Any, query_name: str, sql: str, parameters: "
|
|
469
|
+
async def insert_returning(self, conn: Any, query_name: str, sql: str, parameters: "AiosqlParamType") -> Any | None:
|
|
450
470
|
"""Execute INSERT with RETURNING and return result.
|
|
451
471
|
|
|
452
472
|
Args:
|
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
from sqlspec.extensions.litestar import handlers, providers
|
|
2
1
|
from sqlspec.extensions.litestar.cli import database_group
|
|
3
|
-
from sqlspec.extensions.litestar.config import
|
|
4
|
-
from sqlspec.extensions.litestar.plugin import
|
|
2
|
+
from sqlspec.extensions.litestar.config import LitestarConfig
|
|
3
|
+
from sqlspec.extensions.litestar.plugin import (
|
|
4
|
+
DEFAULT_COMMIT_MODE,
|
|
5
|
+
DEFAULT_CONNECTION_KEY,
|
|
6
|
+
DEFAULT_POOL_KEY,
|
|
7
|
+
DEFAULT_SESSION_KEY,
|
|
8
|
+
CommitMode,
|
|
9
|
+
SQLSpecPlugin,
|
|
10
|
+
)
|
|
11
|
+
from sqlspec.extensions.litestar.store import BaseSQLSpecStore
|
|
5
12
|
|
|
6
|
-
__all__ = (
|
|
13
|
+
__all__ = (
|
|
14
|
+
"DEFAULT_COMMIT_MODE",
|
|
15
|
+
"DEFAULT_CONNECTION_KEY",
|
|
16
|
+
"DEFAULT_POOL_KEY",
|
|
17
|
+
"DEFAULT_SESSION_KEY",
|
|
18
|
+
"BaseSQLSpecStore",
|
|
19
|
+
"CommitMode",
|
|
20
|
+
"LitestarConfig",
|
|
21
|
+
"SQLSpecPlugin",
|
|
22
|
+
"database_group",
|
|
23
|
+
)
|
|
@@ -3,22 +3,18 @@
|
|
|
3
3
|
from contextlib import suppress
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
+
import rich_click as click
|
|
6
7
|
from litestar.cli._utils import LitestarGroup
|
|
7
8
|
|
|
8
9
|
from sqlspec.cli import add_migration_commands
|
|
9
10
|
|
|
10
|
-
try:
|
|
11
|
-
import rich_click as click
|
|
12
|
-
except ImportError:
|
|
13
|
-
import click # type: ignore[no-redef]
|
|
14
|
-
|
|
15
11
|
if TYPE_CHECKING:
|
|
16
12
|
from litestar import Litestar
|
|
17
13
|
|
|
18
|
-
from sqlspec.extensions.litestar.plugin import
|
|
14
|
+
from sqlspec.extensions.litestar.plugin import SQLSpecPlugin
|
|
19
15
|
|
|
20
16
|
|
|
21
|
-
def get_database_migration_plugin(app: "Litestar") -> "
|
|
17
|
+
def get_database_migration_plugin(app: "Litestar") -> "SQLSpecPlugin":
|
|
22
18
|
"""Retrieve the SQLSpec plugin from the Litestar application's plugins.
|
|
23
19
|
|
|
24
20
|
Args:
|
|
@@ -31,18 +27,66 @@ def get_database_migration_plugin(app: "Litestar") -> "SQLSpec":
|
|
|
31
27
|
ImproperConfigurationError: If the SQLSpec plugin is not found
|
|
32
28
|
"""
|
|
33
29
|
from sqlspec.exceptions import ImproperConfigurationError
|
|
34
|
-
from sqlspec.extensions.litestar.plugin import
|
|
30
|
+
from sqlspec.extensions.litestar.plugin import SQLSpecPlugin
|
|
35
31
|
|
|
36
32
|
with suppress(KeyError):
|
|
37
|
-
return app.plugins.get(
|
|
33
|
+
return app.plugins.get(SQLSpecPlugin)
|
|
38
34
|
msg = "Failed to initialize database migrations. The required SQLSpec plugin is missing."
|
|
39
35
|
raise ImproperConfigurationError(msg)
|
|
40
36
|
|
|
41
37
|
|
|
42
|
-
@click.group(cls=LitestarGroup, name="db")
|
|
38
|
+
@click.group(cls=LitestarGroup, name="db", aliases=["database"])
|
|
43
39
|
def database_group(ctx: "click.Context") -> None:
|
|
44
40
|
"""Manage SQLSpec database components."""
|
|
45
41
|
ctx.obj = {"app": ctx.obj, "configs": get_database_migration_plugin(ctx.obj.app).config}
|
|
46
42
|
|
|
47
43
|
|
|
48
44
|
add_migration_commands(database_group)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def add_sessions_delete_expired_command() -> None:
|
|
48
|
+
"""Add delete-expired command to Litestar's sessions CLI group."""
|
|
49
|
+
try:
|
|
50
|
+
from litestar.cli._utils import console
|
|
51
|
+
from litestar.cli.commands.sessions import get_session_backend, sessions_group
|
|
52
|
+
except ImportError:
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
@sessions_group.command("delete-expired") # type: ignore[misc]
|
|
56
|
+
@click.option(
|
|
57
|
+
"--verbose", is_flag=True, default=False, help="Show detailed information about the cleanup operation"
|
|
58
|
+
)
|
|
59
|
+
def delete_expired_sessions_command(app: "Litestar", verbose: bool) -> None:
|
|
60
|
+
"""Delete expired sessions from the session store.
|
|
61
|
+
|
|
62
|
+
This command removes all sessions that have passed their expiration time.
|
|
63
|
+
It can be scheduled via cron or systemd timers for automatic maintenance.
|
|
64
|
+
|
|
65
|
+
Examples:
|
|
66
|
+
litestar sessions delete-expired
|
|
67
|
+
litestar sessions delete-expired --verbose
|
|
68
|
+
"""
|
|
69
|
+
import anyio
|
|
70
|
+
|
|
71
|
+
backend = get_session_backend(app)
|
|
72
|
+
store = backend.config.get_store_from_app(app)
|
|
73
|
+
|
|
74
|
+
if not hasattr(store, "delete_expired"):
|
|
75
|
+
console.print(f"[red]{type(store).__name__} does not support deleting expired sessions")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
async def _delete_expired() -> int:
|
|
79
|
+
return await store.delete_expired() # type: ignore[no-any-return]
|
|
80
|
+
|
|
81
|
+
count = anyio.run(_delete_expired)
|
|
82
|
+
|
|
83
|
+
if count > 0:
|
|
84
|
+
if verbose:
|
|
85
|
+
console.print(f"[green]Successfully deleted {count} expired session(s)")
|
|
86
|
+
else:
|
|
87
|
+
console.print(f"[green]Deleted {count} expired session(s)")
|
|
88
|
+
else:
|
|
89
|
+
console.print("[yellow]No expired sessions found")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
add_sessions_delete_expired_command()
|