sqlspec 0.16.1__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.
- 51ff5a9eadfdefd49f98__mypyc.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +92 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +77 -0
- sqlspec/_sql.py +1780 -0
- sqlspec/_typing.py +680 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +361 -0
- sqlspec/adapters/adbc/driver.py +512 -0
- sqlspec/adapters/aiosqlite/__init__.py +19 -0
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +253 -0
- sqlspec/adapters/aiosqlite/driver.py +248 -0
- sqlspec/adapters/asyncmy/__init__.py +19 -0
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +180 -0
- sqlspec/adapters/asyncmy/driver.py +274 -0
- sqlspec/adapters/asyncpg/__init__.py +21 -0
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +229 -0
- sqlspec/adapters/asyncpg/driver.py +344 -0
- sqlspec/adapters/bigquery/__init__.py +18 -0
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +298 -0
- sqlspec/adapters/bigquery/driver.py +558 -0
- sqlspec/adapters/duckdb/__init__.py +22 -0
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +504 -0
- sqlspec/adapters/duckdb/driver.py +368 -0
- sqlspec/adapters/oracledb/__init__.py +32 -0
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +317 -0
- sqlspec/adapters/oracledb/driver.py +538 -0
- sqlspec/adapters/psqlpy/__init__.py +16 -0
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +214 -0
- sqlspec/adapters/psqlpy/driver.py +530 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +426 -0
- sqlspec/adapters/psycopg/driver.py +796 -0
- sqlspec/adapters/sqlite/__init__.py +15 -0
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +240 -0
- sqlspec/adapters/sqlite/driver.py +294 -0
- sqlspec/base.py +571 -0
- sqlspec/builder/__init__.py +62 -0
- sqlspec/builder/_base.py +473 -0
- sqlspec/builder/_column.py +320 -0
- sqlspec/builder/_ddl.py +1346 -0
- sqlspec/builder/_ddl_utils.py +103 -0
- sqlspec/builder/_delete.py +76 -0
- sqlspec/builder/_insert.py +256 -0
- sqlspec/builder/_merge.py +71 -0
- sqlspec/builder/_parsing_utils.py +140 -0
- sqlspec/builder/_select.py +170 -0
- sqlspec/builder/_update.py +188 -0
- sqlspec/builder/mixins/__init__.py +55 -0
- sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
- sqlspec/builder/mixins/_delete_operations.py +41 -0
- sqlspec/builder/mixins/_insert_operations.py +244 -0
- sqlspec/builder/mixins/_join_operations.py +122 -0
- sqlspec/builder/mixins/_merge_operations.py +476 -0
- sqlspec/builder/mixins/_order_limit_operations.py +135 -0
- sqlspec/builder/mixins/_pivot_operations.py +153 -0
- sqlspec/builder/mixins/_select_operations.py +603 -0
- sqlspec/builder/mixins/_update_operations.py +187 -0
- sqlspec/builder/mixins/_where_clause.py +621 -0
- sqlspec/cli.py +247 -0
- sqlspec/config.py +395 -0
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +871 -0
- sqlspec/core/compiler.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +417 -0
- sqlspec/core/filters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters.py +1237 -0
- sqlspec/core/result.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result.py +677 -0
- sqlspec/core/splitter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +819 -0
- sqlspec/core/statement.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +676 -0
- sqlspec/driver/__init__.py +19 -0
- sqlspec/driver/_async.py +502 -0
- sqlspec/driver/_common.py +631 -0
- sqlspec/driver/_sync.py +503 -0
- sqlspec/driver/mixins/__init__.py +6 -0
- sqlspec/driver/mixins/_result_tools.py +193 -0
- sqlspec/driver/mixins/_sql_translator.py +86 -0
- sqlspec/exceptions.py +193 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/aiosql/__init__.py +10 -0
- sqlspec/extensions/aiosql/adapter.py +461 -0
- sqlspec/extensions/litestar/__init__.py +6 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/cli.py +48 -0
- sqlspec/extensions/litestar/config.py +92 -0
- sqlspec/extensions/litestar/handlers.py +260 -0
- sqlspec/extensions/litestar/plugin.py +145 -0
- sqlspec/extensions/litestar/providers.py +454 -0
- sqlspec/loader.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +760 -0
- sqlspec/migrations/__init__.py +35 -0
- sqlspec/migrations/base.py +414 -0
- sqlspec/migrations/commands.py +443 -0
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +213 -0
- sqlspec/migrations/tracker.py +140 -0
- sqlspec/migrations/utils.py +129 -0
- sqlspec/protocols.py +407 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +23 -0
- sqlspec/storage/backends/__init__.py +0 -0
- sqlspec/storage/backends/base.py +163 -0
- sqlspec/storage/backends/fsspec.py +386 -0
- sqlspec/storage/backends/obstore.py +459 -0
- sqlspec/storage/capabilities.py +102 -0
- sqlspec/storage/registry.py +239 -0
- sqlspec/typing.py +299 -0
- sqlspec/utils/__init__.py +3 -0
- sqlspec/utils/correlation.py +150 -0
- sqlspec/utils/deprecation.py +106 -0
- sqlspec/utils/fixtures.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +58 -0
- sqlspec/utils/logging.py +127 -0
- sqlspec/utils/module_loader.py +89 -0
- sqlspec/utils/serializers.py +4 -0
- sqlspec/utils/singleton.py +32 -0
- sqlspec/utils/sync_tools.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +237 -0
- sqlspec/utils/text.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +96 -0
- sqlspec/utils/type_guards.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1139 -0
- sqlspec-0.16.1.dist-info/METADATA +365 -0
- sqlspec-0.16.1.dist-info/RECORD +148 -0
- sqlspec-0.16.1.dist-info/WHEEL +7 -0
- sqlspec-0.16.1.dist-info/entry_points.txt +2 -0
- sqlspec-0.16.1.dist-info/licenses/LICENSE +21 -0
- sqlspec-0.16.1.dist-info/licenses/NOTICE +29 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""Aiosqlite database configuration with optimized connection management."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Final, Optional, TypedDict
|
|
7
|
+
|
|
8
|
+
import aiosqlite
|
|
9
|
+
from typing_extensions import NotRequired
|
|
10
|
+
|
|
11
|
+
from sqlspec.adapters.aiosqlite.driver import AiosqliteCursor, AiosqliteDriver, aiosqlite_statement_config
|
|
12
|
+
from sqlspec.config import AsyncDatabaseConfig
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import AsyncGenerator
|
|
16
|
+
|
|
17
|
+
from sqlspec.adapters.aiosqlite._types import AiosqliteConnection
|
|
18
|
+
from sqlspec.core.statement import StatementConfig
|
|
19
|
+
|
|
20
|
+
__all__ = ("AiosqliteConfig", "AiosqliteConnectionParams", "AiosqliteConnectionPool")
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
# Core PRAGMAs for SQLite performance optimization
|
|
25
|
+
WAL_PRAGMA_SQL: Final[str] = "PRAGMA journal_mode = WAL"
|
|
26
|
+
FOREIGN_KEYS_SQL: Final[str] = "PRAGMA foreign_keys = ON"
|
|
27
|
+
SYNC_NORMAL_SQL: Final[str] = "PRAGMA synchronous = NORMAL"
|
|
28
|
+
BUSY_TIMEOUT_SQL: Final[str] = "PRAGMA busy_timeout = 5000" # 5 seconds
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AiosqliteConnectionPool:
|
|
32
|
+
"""Connection pool for Aiosqlite using a single shared connection approach.
|
|
33
|
+
|
|
34
|
+
Uses a single shared connection per database file since aiosqlite internally
|
|
35
|
+
handles queuing and serialization of operations.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
__slots__ = ("_closed", "_connection", "_connection_parameters", "_lock")
|
|
39
|
+
|
|
40
|
+
def __init__(self, connection_parameters: "dict[str, Any]") -> None:
|
|
41
|
+
"""Initialize connection manager.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
connection_parameters: SQLite connection parameters
|
|
45
|
+
"""
|
|
46
|
+
self._connection: Optional[AiosqliteConnection] = None
|
|
47
|
+
self._connection_parameters = connection_parameters
|
|
48
|
+
self._lock = asyncio.Lock()
|
|
49
|
+
self._closed = False
|
|
50
|
+
|
|
51
|
+
async def _ensure_connection(self) -> "AiosqliteConnection":
|
|
52
|
+
"""Ensure we have a valid connection, creating one if needed."""
|
|
53
|
+
async with self._lock:
|
|
54
|
+
if self._connection is None or self._closed:
|
|
55
|
+
self._connection = await aiosqlite.connect(**self._connection_parameters)
|
|
56
|
+
|
|
57
|
+
await self._connection.execute(WAL_PRAGMA_SQL)
|
|
58
|
+
await self._connection.execute(FOREIGN_KEYS_SQL)
|
|
59
|
+
await self._connection.execute(SYNC_NORMAL_SQL)
|
|
60
|
+
await self._connection.execute(BUSY_TIMEOUT_SQL)
|
|
61
|
+
await self._connection.commit()
|
|
62
|
+
|
|
63
|
+
self._closed = False
|
|
64
|
+
logger.debug("Created new aiosqlite connection")
|
|
65
|
+
|
|
66
|
+
return self._connection
|
|
67
|
+
|
|
68
|
+
@asynccontextmanager
|
|
69
|
+
async def get_connection(self) -> "AsyncGenerator[AiosqliteConnection, None]":
|
|
70
|
+
"""Get the shared connection.
|
|
71
|
+
|
|
72
|
+
Yields:
|
|
73
|
+
The shared Aiosqlite connection instance.
|
|
74
|
+
"""
|
|
75
|
+
connection = await self._ensure_connection()
|
|
76
|
+
yield connection
|
|
77
|
+
|
|
78
|
+
async def close(self) -> None:
|
|
79
|
+
"""Close the shared connection."""
|
|
80
|
+
async with self._lock:
|
|
81
|
+
if self._connection is not None and not self._closed:
|
|
82
|
+
await self._connection.close()
|
|
83
|
+
self._connection = None
|
|
84
|
+
self._closed = True
|
|
85
|
+
logger.debug("Closed aiosqlite connection")
|
|
86
|
+
|
|
87
|
+
def size(self) -> int:
|
|
88
|
+
"""Get connection count."""
|
|
89
|
+
return 0 if self._closed or self._connection is None else 1
|
|
90
|
+
|
|
91
|
+
def checked_out(self) -> int:
|
|
92
|
+
"""Get number of checked out connections."""
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
async def acquire(self) -> "AiosqliteConnection":
|
|
96
|
+
"""Get the shared connection directly.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
The shared connection instance.
|
|
100
|
+
"""
|
|
101
|
+
return await self._ensure_connection()
|
|
102
|
+
|
|
103
|
+
async def release(self, connection: "AiosqliteConnection") -> None:
|
|
104
|
+
"""No-op release for compatibility.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
connection: Connection to release (ignored)
|
|
108
|
+
"""
|
|
109
|
+
_ = connection
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class AiosqliteConnectionParams(TypedDict, total=False):
|
|
113
|
+
"""aiosqlite connection parameters."""
|
|
114
|
+
|
|
115
|
+
database: NotRequired[str]
|
|
116
|
+
timeout: NotRequired[float]
|
|
117
|
+
detect_types: NotRequired[int]
|
|
118
|
+
isolation_level: NotRequired[Optional[str]]
|
|
119
|
+
check_same_thread: NotRequired[bool]
|
|
120
|
+
cached_statements: NotRequired[int]
|
|
121
|
+
uri: NotRequired[bool]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class AiosqliteConfig(AsyncDatabaseConfig):
|
|
125
|
+
"""Database configuration for AioSQLite engine."""
|
|
126
|
+
|
|
127
|
+
driver_type: ClassVar[type[AiosqliteDriver]] = AiosqliteDriver
|
|
128
|
+
cursor_type: ClassVar[type[AiosqliteCursor]] = AiosqliteCursor
|
|
129
|
+
|
|
130
|
+
def __init__(
|
|
131
|
+
self,
|
|
132
|
+
*,
|
|
133
|
+
pool_instance: "Optional[AiosqliteConnectionPool]" = None,
|
|
134
|
+
pool_config: "Optional[dict[str, Any]]" = None,
|
|
135
|
+
migration_config: "Optional[dict[str, Any]]" = None,
|
|
136
|
+
statement_config: "Optional[StatementConfig]" = None,
|
|
137
|
+
**kwargs: Any,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Initialize AioSQLite configuration.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
pool_instance: Optional pre-configured connection pool instance.
|
|
143
|
+
pool_config: Optional pool configuration dict (AiosqliteConnectionParams).
|
|
144
|
+
migration_config: Optional migration configuration.
|
|
145
|
+
statement_config: Optional statement configuration.
|
|
146
|
+
**kwargs: Additional connection parameters.
|
|
147
|
+
"""
|
|
148
|
+
connection_params = {}
|
|
149
|
+
if pool_config:
|
|
150
|
+
connection_params.update(pool_config)
|
|
151
|
+
connection_params.update(kwargs)
|
|
152
|
+
|
|
153
|
+
super().__init__(
|
|
154
|
+
pool_config=connection_params,
|
|
155
|
+
pool_instance=pool_instance,
|
|
156
|
+
migration_config=migration_config or {},
|
|
157
|
+
statement_config=statement_config or aiosqlite_statement_config,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
self._connection_parameters = self._parse_connection_parameters(connection_params)
|
|
161
|
+
|
|
162
|
+
if pool_instance is None:
|
|
163
|
+
self.pool_instance: AiosqliteConnectionPool = AiosqliteConnectionPool(self._connection_parameters)
|
|
164
|
+
|
|
165
|
+
def _parse_connection_parameters(self, params: "dict[str, Any]") -> "dict[str, Any]":
|
|
166
|
+
"""Parse connection parameters for AioSQLite.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
params: Connection parameters dict.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Processed connection parameters dict.
|
|
173
|
+
"""
|
|
174
|
+
result = params.copy()
|
|
175
|
+
|
|
176
|
+
if "database" not in result:
|
|
177
|
+
# Default to in-memory database
|
|
178
|
+
result["database"] = ":memory:"
|
|
179
|
+
|
|
180
|
+
# Convert regular :memory: to shared memory for multi-connection access
|
|
181
|
+
if result.get("database") == ":memory:":
|
|
182
|
+
result["database"] = "file::memory:?cache=shared"
|
|
183
|
+
result["uri"] = True
|
|
184
|
+
|
|
185
|
+
for pool_param in ["pool_min_size", "pool_max_size", "pool_timeout", "pool_recycle_seconds"]:
|
|
186
|
+
result.pop(pool_param, None)
|
|
187
|
+
|
|
188
|
+
return result
|
|
189
|
+
|
|
190
|
+
@asynccontextmanager
|
|
191
|
+
async def provide_connection(self) -> "AsyncGenerator[AiosqliteConnection, None]":
|
|
192
|
+
"""Provide a database connection.
|
|
193
|
+
|
|
194
|
+
Yields:
|
|
195
|
+
AiosqliteConnection: Database connection instance.
|
|
196
|
+
"""
|
|
197
|
+
async with self.pool_instance.get_connection() as connection:
|
|
198
|
+
yield connection
|
|
199
|
+
|
|
200
|
+
@asynccontextmanager
|
|
201
|
+
async def provide_session(
|
|
202
|
+
self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
|
|
203
|
+
) -> "AsyncGenerator[AiosqliteDriver, None]":
|
|
204
|
+
"""Provide an async database session.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
*args: Additional positional arguments.
|
|
208
|
+
statement_config: Optional statement configuration override.
|
|
209
|
+
**kwargs: Additional keyword arguments.
|
|
210
|
+
|
|
211
|
+
Yields:
|
|
212
|
+
AiosqliteDriver: Database session instance.
|
|
213
|
+
"""
|
|
214
|
+
_ = args, kwargs
|
|
215
|
+
effective_statement_config = statement_config or self.statement_config
|
|
216
|
+
async with self.pool_instance.get_connection() as connection:
|
|
217
|
+
session = self.driver_type(connection, statement_config=effective_statement_config)
|
|
218
|
+
try:
|
|
219
|
+
yield session
|
|
220
|
+
finally:
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
async def close(self) -> None:
|
|
224
|
+
"""Close the connection manager."""
|
|
225
|
+
if self.pool_instance:
|
|
226
|
+
await self.pool_instance.close()
|
|
227
|
+
|
|
228
|
+
def _get_connection_config_dict(self) -> "dict[str, Any]":
|
|
229
|
+
"""Get connection configuration dictionary.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Connection parameters for creating connections.
|
|
233
|
+
"""
|
|
234
|
+
return self._connection_parameters.copy()
|
|
235
|
+
|
|
236
|
+
async def _create_pool(self) -> "AiosqliteConnectionPool":
|
|
237
|
+
"""Create the connection manager instance.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
AiosqliteConnectionPool: The connection manager instance.
|
|
241
|
+
"""
|
|
242
|
+
if self.pool_instance is None:
|
|
243
|
+
self.pool_instance = AiosqliteConnectionPool(self._connection_parameters)
|
|
244
|
+
return self.pool_instance
|
|
245
|
+
|
|
246
|
+
async def _close_pool(self) -> None:
|
|
247
|
+
"""Close the connection manager."""
|
|
248
|
+
if self.pool_instance:
|
|
249
|
+
await self.pool_instance.close()
|
|
250
|
+
|
|
251
|
+
async def close_pool(self) -> None:
|
|
252
|
+
"""Close the connection pool (delegates to _close_pool)."""
|
|
253
|
+
await self._close_pool()
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""AIOSQLite driver implementation for async SQLite operations.
|
|
2
|
+
|
|
3
|
+
Provides async SQLite database connectivity with:
|
|
4
|
+
- Async parameter processing with type coercion
|
|
5
|
+
- Thread-safe caching system
|
|
6
|
+
- Context management for resource handling
|
|
7
|
+
- SQLite-specific optimizations
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import contextlib
|
|
11
|
+
import datetime
|
|
12
|
+
from decimal import Decimal
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
14
|
+
|
|
15
|
+
import aiosqlite
|
|
16
|
+
|
|
17
|
+
from sqlspec.core.cache import get_cache_config
|
|
18
|
+
from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
|
|
19
|
+
from sqlspec.core.statement import StatementConfig
|
|
20
|
+
from sqlspec.driver import AsyncDriverAdapterBase
|
|
21
|
+
from sqlspec.exceptions import SQLParsingError, SQLSpecError
|
|
22
|
+
from sqlspec.utils.serializers import to_json
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from contextlib import AbstractAsyncContextManager
|
|
26
|
+
|
|
27
|
+
from sqlspec.adapters.aiosqlite._types import AiosqliteConnection
|
|
28
|
+
from sqlspec.core.result import SQLResult
|
|
29
|
+
from sqlspec.core.statement import SQL
|
|
30
|
+
from sqlspec.driver import ExecutionResult
|
|
31
|
+
|
|
32
|
+
__all__ = ("AiosqliteCursor", "AiosqliteDriver", "AiosqliteExceptionHandler", "aiosqlite_statement_config")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
aiosqlite_statement_config = StatementConfig(
|
|
36
|
+
dialect="sqlite",
|
|
37
|
+
parameter_config=ParameterStyleConfig(
|
|
38
|
+
default_parameter_style=ParameterStyle.QMARK,
|
|
39
|
+
supported_parameter_styles={ParameterStyle.QMARK},
|
|
40
|
+
default_execution_parameter_style=ParameterStyle.QMARK,
|
|
41
|
+
supported_execution_parameter_styles={ParameterStyle.QMARK},
|
|
42
|
+
type_coercion_map={
|
|
43
|
+
bool: int,
|
|
44
|
+
datetime.datetime: lambda v: v.isoformat(),
|
|
45
|
+
datetime.date: lambda v: v.isoformat(),
|
|
46
|
+
Decimal: str,
|
|
47
|
+
dict: to_json,
|
|
48
|
+
list: to_json,
|
|
49
|
+
tuple: lambda v: to_json(list(v)),
|
|
50
|
+
},
|
|
51
|
+
has_native_list_expansion=False,
|
|
52
|
+
needs_static_script_compilation=False,
|
|
53
|
+
preserve_parameter_format=True,
|
|
54
|
+
),
|
|
55
|
+
enable_parsing=True,
|
|
56
|
+
enable_validation=True,
|
|
57
|
+
enable_caching=True,
|
|
58
|
+
enable_parameter_type_wrapping=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AiosqliteCursor:
|
|
63
|
+
"""Async context manager for AIOSQLite cursor management."""
|
|
64
|
+
|
|
65
|
+
__slots__ = ("connection", "cursor")
|
|
66
|
+
|
|
67
|
+
def __init__(self, connection: "AiosqliteConnection") -> None:
|
|
68
|
+
self.connection = connection
|
|
69
|
+
self.cursor: Optional[aiosqlite.Cursor] = None
|
|
70
|
+
|
|
71
|
+
async def __aenter__(self) -> "aiosqlite.Cursor":
|
|
72
|
+
self.cursor = await self.connection.cursor()
|
|
73
|
+
return self.cursor
|
|
74
|
+
|
|
75
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
76
|
+
_ = (exc_type, exc_val, exc_tb)
|
|
77
|
+
if self.cursor is not None:
|
|
78
|
+
with contextlib.suppress(Exception):
|
|
79
|
+
await self.cursor.close()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AiosqliteExceptionHandler:
|
|
83
|
+
"""Custom async context manager for handling AIOSQLite database exceptions."""
|
|
84
|
+
|
|
85
|
+
__slots__ = ()
|
|
86
|
+
|
|
87
|
+
async def __aenter__(self) -> None:
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
91
|
+
if exc_type is None:
|
|
92
|
+
return
|
|
93
|
+
if issubclass(exc_type, aiosqlite.IntegrityError):
|
|
94
|
+
e = exc_val
|
|
95
|
+
msg = f"AIOSQLite integrity constraint violation: {e}"
|
|
96
|
+
raise SQLSpecError(msg) from e
|
|
97
|
+
if issubclass(exc_type, aiosqlite.OperationalError):
|
|
98
|
+
e = exc_val
|
|
99
|
+
error_msg = str(e).lower()
|
|
100
|
+
if "locked" in error_msg:
|
|
101
|
+
msg = f"AIOSQLite database locked: {e}. Consider enabling WAL mode or reducing concurrency."
|
|
102
|
+
raise SQLSpecError(msg) from e
|
|
103
|
+
if "syntax" in error_msg or "malformed" in error_msg:
|
|
104
|
+
msg = f"AIOSQLite SQL syntax error: {e}"
|
|
105
|
+
raise SQLParsingError(msg) from e
|
|
106
|
+
msg = f"AIOSQLite operational error: {e}"
|
|
107
|
+
raise SQLSpecError(msg) from e
|
|
108
|
+
if issubclass(exc_type, aiosqlite.DatabaseError):
|
|
109
|
+
e = exc_val
|
|
110
|
+
msg = f"AIOSQLite database error: {e}"
|
|
111
|
+
raise SQLSpecError(msg) from e
|
|
112
|
+
if issubclass(exc_type, aiosqlite.Error):
|
|
113
|
+
e = exc_val
|
|
114
|
+
msg = f"AIOSQLite error: {e}"
|
|
115
|
+
raise SQLSpecError(msg) from e
|
|
116
|
+
if issubclass(exc_type, Exception):
|
|
117
|
+
e = exc_val
|
|
118
|
+
error_msg = str(e).lower()
|
|
119
|
+
if "parse" in error_msg or "syntax" in error_msg:
|
|
120
|
+
msg = f"SQL parsing failed: {e}"
|
|
121
|
+
raise SQLParsingError(msg) from e
|
|
122
|
+
msg = f"Unexpected async database operation error: {e}"
|
|
123
|
+
raise SQLSpecError(msg) from e
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class AiosqliteDriver(AsyncDriverAdapterBase):
|
|
127
|
+
"""AIOSQLite driver for async SQLite database operations.
|
|
128
|
+
|
|
129
|
+
Provides async SQLite connectivity with:
|
|
130
|
+
- Statement processing and parameter handling
|
|
131
|
+
- Cursor management and resource cleanup
|
|
132
|
+
- Exception handling for SQLite operations
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
__slots__ = ()
|
|
136
|
+
dialect = "sqlite"
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self,
|
|
140
|
+
connection: "AiosqliteConnection",
|
|
141
|
+
statement_config: "Optional[StatementConfig]" = None,
|
|
142
|
+
driver_features: "Optional[dict[str, Any]]" = None,
|
|
143
|
+
) -> None:
|
|
144
|
+
if statement_config is None:
|
|
145
|
+
cache_config = get_cache_config()
|
|
146
|
+
enhanced_config = aiosqlite_statement_config.replace(
|
|
147
|
+
enable_caching=cache_config.compiled_cache_enabled,
|
|
148
|
+
enable_parsing=True,
|
|
149
|
+
enable_validation=True,
|
|
150
|
+
dialect="sqlite",
|
|
151
|
+
)
|
|
152
|
+
statement_config = enhanced_config
|
|
153
|
+
|
|
154
|
+
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
155
|
+
|
|
156
|
+
def with_cursor(self, connection: "AiosqliteConnection") -> "AiosqliteCursor":
|
|
157
|
+
"""Create async context manager for AIOSQLite cursor."""
|
|
158
|
+
return AiosqliteCursor(connection)
|
|
159
|
+
|
|
160
|
+
def handle_database_exceptions(self) -> "AbstractAsyncContextManager[None]":
|
|
161
|
+
"""Handle AIOSQLite-specific exceptions."""
|
|
162
|
+
return AiosqliteExceptionHandler()
|
|
163
|
+
|
|
164
|
+
async def _try_special_handling(self, cursor: "aiosqlite.Cursor", statement: "SQL") -> "Optional[SQLResult]":
|
|
165
|
+
"""Hook for AIOSQLite-specific special operations.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
cursor: AIOSQLite cursor object
|
|
169
|
+
statement: SQL statement to analyze
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
None - always proceeds with standard execution for AIOSQLite
|
|
173
|
+
"""
|
|
174
|
+
_ = (cursor, statement)
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
async def _execute_script(self, cursor: "aiosqlite.Cursor", statement: "SQL") -> "ExecutionResult":
|
|
178
|
+
"""Execute SQL script using statement splitting and parameter handling."""
|
|
179
|
+
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
180
|
+
statements = self.split_script_statements(sql, statement.statement_config, strip_trailing_semicolon=True)
|
|
181
|
+
|
|
182
|
+
successful_count = 0
|
|
183
|
+
last_cursor = cursor
|
|
184
|
+
|
|
185
|
+
for stmt in statements:
|
|
186
|
+
await cursor.execute(stmt, prepared_parameters or ())
|
|
187
|
+
successful_count += 1
|
|
188
|
+
|
|
189
|
+
return self.create_execution_result(
|
|
190
|
+
last_cursor, statement_count=len(statements), successful_statements=successful_count, is_script_result=True
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
async def _execute_many(self, cursor: "aiosqlite.Cursor", statement: "SQL") -> "ExecutionResult":
|
|
194
|
+
"""Execute SQL with multiple parameter sets using async batch processing."""
|
|
195
|
+
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
196
|
+
|
|
197
|
+
if not prepared_parameters:
|
|
198
|
+
msg = "execute_many requires parameters"
|
|
199
|
+
raise ValueError(msg)
|
|
200
|
+
|
|
201
|
+
await cursor.executemany(sql, prepared_parameters)
|
|
202
|
+
|
|
203
|
+
affected_rows = cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
204
|
+
|
|
205
|
+
return self.create_execution_result(cursor, rowcount_override=affected_rows, is_many_result=True)
|
|
206
|
+
|
|
207
|
+
async def _execute_statement(self, cursor: "aiosqlite.Cursor", statement: "SQL") -> "ExecutionResult":
|
|
208
|
+
"""Execute single SQL statement with async data handling."""
|
|
209
|
+
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
210
|
+
await cursor.execute(sql, prepared_parameters or ())
|
|
211
|
+
|
|
212
|
+
if statement.returns_rows():
|
|
213
|
+
fetched_data = await cursor.fetchall()
|
|
214
|
+
column_names = [col[0] for col in cursor.description or []]
|
|
215
|
+
|
|
216
|
+
data = [dict(zip(column_names, row)) for row in fetched_data]
|
|
217
|
+
|
|
218
|
+
return self.create_execution_result(
|
|
219
|
+
cursor, selected_data=data, column_names=column_names, data_row_count=len(data), is_select_result=True
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
affected_rows = cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
223
|
+
return self.create_execution_result(cursor, rowcount_override=affected_rows)
|
|
224
|
+
|
|
225
|
+
async def begin(self) -> None:
|
|
226
|
+
"""Begin a database transaction."""
|
|
227
|
+
try:
|
|
228
|
+
if not self.connection.in_transaction:
|
|
229
|
+
await self.connection.execute("BEGIN")
|
|
230
|
+
except aiosqlite.Error as e:
|
|
231
|
+
msg = f"Failed to begin transaction: {e}"
|
|
232
|
+
raise SQLSpecError(msg) from e
|
|
233
|
+
|
|
234
|
+
async def rollback(self) -> None:
|
|
235
|
+
"""Rollback the current transaction."""
|
|
236
|
+
try:
|
|
237
|
+
await self.connection.rollback()
|
|
238
|
+
except aiosqlite.Error as e:
|
|
239
|
+
msg = f"Failed to rollback transaction: {e}"
|
|
240
|
+
raise SQLSpecError(msg) from e
|
|
241
|
+
|
|
242
|
+
async def commit(self) -> None:
|
|
243
|
+
"""Commit the current transaction."""
|
|
244
|
+
try:
|
|
245
|
+
await self.connection.commit()
|
|
246
|
+
except aiosqlite.Error as e:
|
|
247
|
+
msg = f"Failed to commit transaction: {e}"
|
|
248
|
+
raise SQLSpecError(msg) from e
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from sqlspec.adapters.asyncmy._types import AsyncmyConnection
|
|
2
|
+
from sqlspec.adapters.asyncmy.config import AsyncmyConfig, AsyncmyConnectionParams, AsyncmyPoolParams
|
|
3
|
+
from sqlspec.adapters.asyncmy.driver import (
|
|
4
|
+
AsyncmyCursor,
|
|
5
|
+
AsyncmyDriver,
|
|
6
|
+
AsyncmyExceptionHandler,
|
|
7
|
+
asyncmy_statement_config,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
"AsyncmyConfig",
|
|
12
|
+
"AsyncmyConnection",
|
|
13
|
+
"AsyncmyConnectionParams",
|
|
14
|
+
"AsyncmyCursor",
|
|
15
|
+
"AsyncmyDriver",
|
|
16
|
+
"AsyncmyExceptionHandler",
|
|
17
|
+
"AsyncmyPoolParams",
|
|
18
|
+
"asyncmy_statement_config",
|
|
19
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from asyncmy import Connection
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from typing_extensions import TypeAlias
|
|
7
|
+
|
|
8
|
+
AsyncmyConnection: TypeAlias = Connection
|
|
9
|
+
else:
|
|
10
|
+
AsyncmyConnection = Connection
|
|
11
|
+
|
|
12
|
+
__all__ = ("AsyncmyConnection",)
|