sqlspec 0.17.1__py3-none-any.whl → 0.19.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 +1 -1
- sqlspec/_sql.py +54 -159
- sqlspec/adapters/adbc/config.py +24 -30
- sqlspec/adapters/adbc/driver.py +42 -61
- sqlspec/adapters/aiosqlite/config.py +5 -10
- sqlspec/adapters/aiosqlite/driver.py +9 -25
- sqlspec/adapters/aiosqlite/pool.py +43 -35
- sqlspec/adapters/asyncmy/config.py +10 -7
- sqlspec/adapters/asyncmy/driver.py +18 -39
- sqlspec/adapters/asyncpg/config.py +4 -0
- sqlspec/adapters/asyncpg/driver.py +32 -79
- sqlspec/adapters/bigquery/config.py +12 -65
- sqlspec/adapters/bigquery/driver.py +39 -133
- sqlspec/adapters/duckdb/config.py +11 -15
- sqlspec/adapters/duckdb/driver.py +61 -85
- sqlspec/adapters/duckdb/pool.py +2 -5
- sqlspec/adapters/oracledb/_types.py +8 -1
- sqlspec/adapters/oracledb/config.py +55 -38
- sqlspec/adapters/oracledb/driver.py +35 -92
- sqlspec/adapters/oracledb/migrations.py +257 -0
- sqlspec/adapters/psqlpy/config.py +13 -9
- sqlspec/adapters/psqlpy/driver.py +28 -103
- sqlspec/adapters/psycopg/config.py +9 -5
- sqlspec/adapters/psycopg/driver.py +107 -175
- sqlspec/adapters/sqlite/config.py +7 -5
- sqlspec/adapters/sqlite/driver.py +37 -73
- sqlspec/adapters/sqlite/pool.py +3 -12
- sqlspec/base.py +19 -22
- sqlspec/builder/__init__.py +1 -1
- sqlspec/builder/_base.py +34 -20
- sqlspec/builder/_ddl.py +407 -183
- sqlspec/builder/_insert.py +1 -1
- sqlspec/builder/mixins/_insert_operations.py +26 -6
- sqlspec/builder/mixins/_merge_operations.py +1 -1
- sqlspec/builder/mixins/_select_operations.py +1 -5
- sqlspec/cli.py +281 -33
- sqlspec/config.py +183 -14
- sqlspec/core/__init__.py +89 -14
- sqlspec/core/cache.py +57 -104
- sqlspec/core/compiler.py +57 -112
- sqlspec/core/filters.py +1 -21
- sqlspec/core/hashing.py +13 -47
- sqlspec/core/parameters.py +272 -261
- sqlspec/core/result.py +12 -27
- sqlspec/core/splitter.py +17 -21
- sqlspec/core/statement.py +150 -159
- sqlspec/driver/_async.py +2 -15
- sqlspec/driver/_common.py +16 -95
- sqlspec/driver/_sync.py +2 -15
- sqlspec/driver/mixins/_result_tools.py +8 -29
- sqlspec/driver/mixins/_sql_translator.py +6 -8
- sqlspec/exceptions.py +1 -2
- sqlspec/extensions/litestar/plugin.py +15 -8
- sqlspec/loader.py +43 -115
- sqlspec/migrations/__init__.py +1 -1
- sqlspec/migrations/base.py +34 -45
- sqlspec/migrations/commands.py +34 -15
- sqlspec/migrations/loaders.py +1 -1
- sqlspec/migrations/runner.py +104 -19
- sqlspec/migrations/tracker.py +49 -2
- sqlspec/protocols.py +3 -6
- sqlspec/storage/__init__.py +4 -4
- sqlspec/storage/backends/fsspec.py +5 -6
- sqlspec/storage/backends/obstore.py +7 -8
- sqlspec/storage/registry.py +3 -3
- sqlspec/utils/__init__.py +2 -2
- sqlspec/utils/logging.py +6 -10
- sqlspec/utils/sync_tools.py +27 -4
- sqlspec/utils/text.py +6 -1
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/METADATA +1 -1
- sqlspec-0.19.0.dist-info/RECORD +138 -0
- sqlspec/builder/_ddl_utils.py +0 -103
- sqlspec-0.17.1.dist-info/RECORD +0 -138
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Multi-connection pool for aiosqlite."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
@@ -36,7 +36,7 @@ class AiosqliteConnectTimeoutError(SQLSpecError):
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class AiosqlitePoolConnection:
|
|
39
|
-
"""Wrapper for database connections
|
|
39
|
+
"""Wrapper for database connections in the pool."""
|
|
40
40
|
|
|
41
41
|
__slots__ = ("_closed", "connection", "id", "idle_since")
|
|
42
42
|
|
|
@@ -120,18 +120,18 @@ class AiosqlitePoolConnection:
|
|
|
120
120
|
|
|
121
121
|
|
|
122
122
|
class AiosqliteConnectionPool:
|
|
123
|
-
"""Multi-connection pool for aiosqlite
|
|
123
|
+
"""Multi-connection pool for aiosqlite."""
|
|
124
124
|
|
|
125
125
|
__slots__ = (
|
|
126
|
-
"
|
|
126
|
+
"_closed_event_instance",
|
|
127
127
|
"_connect_timeout",
|
|
128
128
|
"_connection_parameters",
|
|
129
129
|
"_connection_registry",
|
|
130
130
|
"_idle_timeout",
|
|
131
|
-
"
|
|
131
|
+
"_lock_instance",
|
|
132
132
|
"_operation_timeout",
|
|
133
133
|
"_pool_size",
|
|
134
|
-
"
|
|
134
|
+
"_queue_instance",
|
|
135
135
|
"_tracked_threads",
|
|
136
136
|
"_wal_initialized",
|
|
137
137
|
)
|
|
@@ -141,7 +141,7 @@ class AiosqliteConnectionPool:
|
|
|
141
141
|
connection_parameters: "dict[str, Any]",
|
|
142
142
|
pool_size: int = 5,
|
|
143
143
|
connect_timeout: float = 30.0,
|
|
144
|
-
idle_timeout: float = 24 * 60 * 60,
|
|
144
|
+
idle_timeout: float = 24 * 60 * 60,
|
|
145
145
|
operation_timeout: float = 10.0,
|
|
146
146
|
) -> None:
|
|
147
147
|
"""Initialize connection pool.
|
|
@@ -159,13 +159,35 @@ class AiosqliteConnectionPool:
|
|
|
159
159
|
self._idle_timeout = idle_timeout
|
|
160
160
|
self._operation_timeout = operation_timeout
|
|
161
161
|
|
|
162
|
-
self._queue: asyncio.Queue[AiosqlitePoolConnection] = asyncio.Queue(maxsize=pool_size)
|
|
163
162
|
self._connection_registry: dict[str, AiosqlitePoolConnection] = {}
|
|
164
|
-
self._lock = asyncio.Lock()
|
|
165
|
-
self._closed_event = asyncio.Event()
|
|
166
163
|
self._tracked_threads: set[Union[threading.Thread, AiosqliteConnection]] = set()
|
|
167
164
|
self._wal_initialized = False
|
|
168
165
|
|
|
166
|
+
self._queue_instance: Optional[asyncio.Queue[AiosqlitePoolConnection]] = None
|
|
167
|
+
self._lock_instance: Optional[asyncio.Lock] = None
|
|
168
|
+
self._closed_event_instance: Optional[asyncio.Event] = None
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def _queue(self) -> "asyncio.Queue[AiosqlitePoolConnection]":
|
|
172
|
+
"""Lazy initialization of asyncio.Queue for Python 3.9 compatibility."""
|
|
173
|
+
if self._queue_instance is None:
|
|
174
|
+
self._queue_instance = asyncio.Queue(maxsize=self._pool_size)
|
|
175
|
+
return self._queue_instance
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def _lock(self) -> asyncio.Lock:
|
|
179
|
+
"""Lazy initialization of asyncio.Lock for Python 3.9 compatibility."""
|
|
180
|
+
if self._lock_instance is None:
|
|
181
|
+
self._lock_instance = asyncio.Lock()
|
|
182
|
+
return self._lock_instance
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def _closed_event(self) -> asyncio.Event:
|
|
186
|
+
"""Lazy initialization of asyncio.Event for Python 3.9 compatibility."""
|
|
187
|
+
if self._closed_event_instance is None:
|
|
188
|
+
self._closed_event_instance = asyncio.Event()
|
|
189
|
+
return self._closed_event_instance
|
|
190
|
+
|
|
169
191
|
@property
|
|
170
192
|
def is_closed(self) -> bool:
|
|
171
193
|
"""Check if pool is closed.
|
|
@@ -173,7 +195,7 @@ class AiosqliteConnectionPool:
|
|
|
173
195
|
Returns:
|
|
174
196
|
True if pool is closed
|
|
175
197
|
"""
|
|
176
|
-
return self._closed_event.is_set()
|
|
198
|
+
return self._closed_event_instance is not None and self._closed_event.is_set()
|
|
177
199
|
|
|
178
200
|
def size(self) -> int:
|
|
179
201
|
"""Get total number of connections in pool.
|
|
@@ -189,6 +211,8 @@ class AiosqliteConnectionPool:
|
|
|
189
211
|
Returns:
|
|
190
212
|
Number of connections currently in use
|
|
191
213
|
"""
|
|
214
|
+
if self._queue_instance is None:
|
|
215
|
+
return len(self._connection_registry)
|
|
192
216
|
return len(self._connection_registry) - self._queue.qsize()
|
|
193
217
|
|
|
194
218
|
def _track_aiosqlite_thread(self, connection: "AiosqliteConnection") -> None:
|
|
@@ -200,7 +224,7 @@ class AiosqliteConnectionPool:
|
|
|
200
224
|
self._tracked_threads.add(connection)
|
|
201
225
|
|
|
202
226
|
async def _create_connection(self) -> AiosqlitePoolConnection:
|
|
203
|
-
"""Create a new connection
|
|
227
|
+
"""Create a new connection.
|
|
204
228
|
|
|
205
229
|
Returns:
|
|
206
230
|
New pool connection instance
|
|
@@ -209,7 +233,6 @@ class AiosqliteConnectionPool:
|
|
|
209
233
|
connection.daemon = True
|
|
210
234
|
connection = await connection
|
|
211
235
|
|
|
212
|
-
# Detect database type for appropriate optimization
|
|
213
236
|
database_path = str(self._connection_parameters.get("database", ""))
|
|
214
237
|
is_shared_cache = "cache=shared" in database_path
|
|
215
238
|
is_memory_db = ":memory:" in database_path or "mode=memory" in database_path
|
|
@@ -234,10 +257,9 @@ class AiosqliteConnectionPool:
|
|
|
234
257
|
|
|
235
258
|
if is_shared_cache:
|
|
236
259
|
self._wal_initialized = True
|
|
237
|
-
logger.debug("Database optimized for shared cache (memory: %s)", is_memory_db)
|
|
238
260
|
|
|
239
261
|
except Exception as e:
|
|
240
|
-
logger.warning("Failed to
|
|
262
|
+
logger.warning("Failed to configure connection: %s", e)
|
|
241
263
|
await connection.execute("PRAGMA foreign_keys = ON")
|
|
242
264
|
await connection.execute("PRAGMA busy_timeout = 30000")
|
|
243
265
|
await connection.commit()
|
|
@@ -249,7 +271,7 @@ class AiosqliteConnectionPool:
|
|
|
249
271
|
async with self._lock:
|
|
250
272
|
self._connection_registry[pool_connection.id] = pool_connection
|
|
251
273
|
|
|
252
|
-
logger.debug("Created
|
|
274
|
+
logger.debug("Created aiosqlite connection: %s", pool_connection.id)
|
|
253
275
|
return pool_connection
|
|
254
276
|
|
|
255
277
|
async def _claim_if_healthy(self, connection: AiosqlitePoolConnection) -> bool:
|
|
@@ -259,7 +281,7 @@ class AiosqliteConnectionPool:
|
|
|
259
281
|
connection: Connection to check and claim
|
|
260
282
|
|
|
261
283
|
Returns:
|
|
262
|
-
True if connection was
|
|
284
|
+
True if connection was claimed
|
|
263
285
|
"""
|
|
264
286
|
if connection.idle_time > self._idle_timeout:
|
|
265
287
|
logger.debug("Connection %s exceeded idle timeout, retiring", connection.id)
|
|
@@ -368,9 +390,6 @@ class AiosqliteConnectionPool:
|
|
|
368
390
|
async def _wait_for_threads_to_terminate(self, timeout: float = 1.0) -> None:
|
|
369
391
|
"""Wait for all tracked aiosqlite connection threads to terminate.
|
|
370
392
|
|
|
371
|
-
Since we use daemon threads, this is just a best-effort cleanup.
|
|
372
|
-
The threads will terminate when the process exits regardless.
|
|
373
|
-
|
|
374
393
|
Args:
|
|
375
394
|
timeout: Maximum time to wait for thread termination in seconds
|
|
376
395
|
"""
|
|
@@ -396,13 +415,9 @@ class AiosqliteConnectionPool:
|
|
|
396
415
|
elapsed = time.time() - start_time
|
|
397
416
|
|
|
398
417
|
if remaining_threads > 0:
|
|
399
|
-
logger.debug(
|
|
400
|
-
"%d aiosqlite threads still running after %.2fs (daemon threads will terminate on exit)",
|
|
401
|
-
remaining_threads,
|
|
402
|
-
elapsed,
|
|
403
|
-
)
|
|
418
|
+
logger.debug("%d aiosqlite threads still running after %.2fs", remaining_threads, elapsed)
|
|
404
419
|
else:
|
|
405
|
-
logger.debug("All aiosqlite connection threads terminated
|
|
420
|
+
logger.debug("All aiosqlite connection threads terminated in %.2fs", elapsed)
|
|
406
421
|
|
|
407
422
|
async def acquire(self) -> AiosqlitePoolConnection:
|
|
408
423
|
"""Acquire a connection from the pool.
|
|
@@ -461,32 +476,25 @@ class AiosqliteConnectionPool:
|
|
|
461
476
|
await self.release(connection)
|
|
462
477
|
|
|
463
478
|
async def close(self) -> None:
|
|
464
|
-
"""Close the connection pool
|
|
465
|
-
|
|
466
|
-
Ensures all connections are properly closed and background threads are terminated.
|
|
467
|
-
"""
|
|
479
|
+
"""Close the connection pool."""
|
|
468
480
|
if self.is_closed:
|
|
469
481
|
return
|
|
470
482
|
self._closed_event.set()
|
|
471
483
|
|
|
472
|
-
# Clear the queue
|
|
473
484
|
while not self._queue.empty():
|
|
474
485
|
self._queue.get_nowait()
|
|
475
486
|
|
|
476
|
-
# Get all connections and clear registry
|
|
477
487
|
async with self._lock:
|
|
478
488
|
connections = list(self._connection_registry.values())
|
|
479
489
|
self._connection_registry.clear()
|
|
480
490
|
|
|
481
|
-
# Close all connections
|
|
482
491
|
if connections:
|
|
483
492
|
close_tasks = [asyncio.wait_for(conn.close(), timeout=self._operation_timeout) for conn in connections]
|
|
484
493
|
results = await asyncio.gather(*close_tasks, return_exceptions=True)
|
|
485
494
|
|
|
486
|
-
# Log any close errors
|
|
487
495
|
for i, result in enumerate(results):
|
|
488
496
|
if isinstance(result, Exception):
|
|
489
497
|
logger.warning("Error closing connection %s: %s", connections[i].id, result)
|
|
490
498
|
|
|
491
499
|
await self._wait_for_threads_to_terminate(timeout=1.0)
|
|
492
|
-
logger.debug("Aiosqlite connection pool closed
|
|
500
|
+
logger.debug("Aiosqlite connection pool closed")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Asyncmy database configuration
|
|
1
|
+
"""Asyncmy database configuration."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from collections.abc import AsyncGenerator
|
|
@@ -58,7 +58,7 @@ class AsyncmyPoolParams(AsyncmyConnectionParams, total=False):
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver]): # pyright: ignore
|
|
61
|
-
"""Configuration for Asyncmy database connections
|
|
61
|
+
"""Configuration for Asyncmy database connections."""
|
|
62
62
|
|
|
63
63
|
driver_type: ClassVar[type[AsyncmyDriver]] = AsyncmyDriver
|
|
64
64
|
connection_type: "ClassVar[type[AsyncmyConnection]]" = AsyncmyConnection # pyright: ignore
|
|
@@ -70,6 +70,7 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver
|
|
|
70
70
|
pool_instance: "Optional[Pool]" = None,
|
|
71
71
|
migration_config: Optional[dict[str, Any]] = None,
|
|
72
72
|
statement_config: "Optional[StatementConfig]" = None,
|
|
73
|
+
driver_features: "Optional[dict[str, Any]]" = None,
|
|
73
74
|
) -> None:
|
|
74
75
|
"""Initialize Asyncmy configuration.
|
|
75
76
|
|
|
@@ -78,6 +79,7 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver
|
|
|
78
79
|
pool_instance: Existing pool instance to use
|
|
79
80
|
migration_config: Migration configuration
|
|
80
81
|
statement_config: Statement configuration override
|
|
82
|
+
driver_features: Driver feature configuration
|
|
81
83
|
"""
|
|
82
84
|
processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
|
|
83
85
|
if "extra" in processed_pool_config:
|
|
@@ -97,7 +99,7 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver
|
|
|
97
99
|
pool_instance=pool_instance,
|
|
98
100
|
migration_config=migration_config,
|
|
99
101
|
statement_config=statement_config,
|
|
100
|
-
driver_features={},
|
|
102
|
+
driver_features=driver_features or {},
|
|
101
103
|
)
|
|
102
104
|
|
|
103
105
|
async def _create_pool(self) -> "Pool": # pyright: ignore
|
|
@@ -107,7 +109,11 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver
|
|
|
107
109
|
async def _close_pool(self) -> None:
|
|
108
110
|
"""Close the actual async connection pool."""
|
|
109
111
|
if self.pool_instance:
|
|
110
|
-
|
|
112
|
+
self.pool_instance.close()
|
|
113
|
+
|
|
114
|
+
async def close_pool(self) -> None:
|
|
115
|
+
"""Close the connection pool."""
|
|
116
|
+
await self._close_pool()
|
|
111
117
|
|
|
112
118
|
async def create_connection(self) -> AsyncmyConnection: # pyright: ignore
|
|
113
119
|
"""Create a single async connection (not from pool).
|
|
@@ -166,9 +172,6 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver
|
|
|
166
172
|
def get_signature_namespace(self) -> "dict[str, type[Any]]":
|
|
167
173
|
"""Get the signature namespace for Asyncmy types.
|
|
168
174
|
|
|
169
|
-
This provides all Asyncmy-specific types that Litestar needs to recognize
|
|
170
|
-
to avoid serialization attempts.
|
|
171
|
-
|
|
172
175
|
Returns:
|
|
173
176
|
Dictionary mapping type names to types.
|
|
174
177
|
"""
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
"""AsyncMy MySQL driver implementation
|
|
1
|
+
"""AsyncMy MySQL driver implementation.
|
|
2
2
|
|
|
3
|
-
Provides
|
|
4
|
-
|
|
5
|
-
- MySQL-specific type coercion and data handling
|
|
6
|
-
- Error categorization for MySQL/MariaDB
|
|
7
|
-
- Transaction management
|
|
3
|
+
Provides MySQL/MariaDB connectivity with parameter style conversion,
|
|
4
|
+
type coercion, error handling, and transaction management.
|
|
8
5
|
"""
|
|
9
6
|
|
|
10
7
|
import logging
|
|
@@ -34,7 +31,6 @@ logger = logging.getLogger(__name__)
|
|
|
34
31
|
__all__ = ("AsyncmyCursor", "AsyncmyDriver", "AsyncmyExceptionHandler", "asyncmy_statement_config")
|
|
35
32
|
|
|
36
33
|
|
|
37
|
-
# Enhanced AsyncMy statement configuration using core modules with performance optimizations
|
|
38
34
|
asyncmy_statement_config = StatementConfig(
|
|
39
35
|
dialect="mysql",
|
|
40
36
|
parameter_config=ParameterStyleConfig(
|
|
@@ -42,12 +38,7 @@ asyncmy_statement_config = StatementConfig(
|
|
|
42
38
|
supported_parameter_styles={ParameterStyle.QMARK, ParameterStyle.POSITIONAL_PYFORMAT},
|
|
43
39
|
default_execution_parameter_style=ParameterStyle.POSITIONAL_PYFORMAT,
|
|
44
40
|
supported_execution_parameter_styles={ParameterStyle.POSITIONAL_PYFORMAT},
|
|
45
|
-
type_coercion_map={
|
|
46
|
-
dict: to_json,
|
|
47
|
-
list: to_json,
|
|
48
|
-
tuple: lambda v: to_json(list(v)),
|
|
49
|
-
bool: int, # MySQL represents booleans as integers
|
|
50
|
-
},
|
|
41
|
+
type_coercion_map={dict: to_json, list: to_json, tuple: lambda v: to_json(list(v)), bool: int},
|
|
51
42
|
has_native_list_expansion=False,
|
|
52
43
|
needs_static_script_compilation=True,
|
|
53
44
|
preserve_parameter_format=True,
|
|
@@ -125,13 +116,10 @@ class AsyncmyExceptionHandler:
|
|
|
125
116
|
|
|
126
117
|
|
|
127
118
|
class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
128
|
-
"""AsyncMy MySQL/MariaDB driver
|
|
119
|
+
"""AsyncMy MySQL/MariaDB driver.
|
|
129
120
|
|
|
130
|
-
Provides MySQL/MariaDB connectivity with
|
|
131
|
-
|
|
132
|
-
- MySQL-specific type coercion (bool -> int, dict/list -> JSON)
|
|
133
|
-
- Error categorization for MySQL/MariaDB
|
|
134
|
-
- Transaction management
|
|
121
|
+
Provides MySQL/MariaDB connectivity with parameter style conversion,
|
|
122
|
+
type coercion, error handling, and transaction management.
|
|
135
123
|
"""
|
|
136
124
|
|
|
137
125
|
__slots__ = ()
|
|
@@ -145,18 +133,17 @@ class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
|
145
133
|
) -> None:
|
|
146
134
|
if statement_config is None:
|
|
147
135
|
cache_config = get_cache_config()
|
|
148
|
-
|
|
136
|
+
statement_config = asyncmy_statement_config.replace(
|
|
149
137
|
enable_caching=cache_config.compiled_cache_enabled,
|
|
150
138
|
enable_parsing=True,
|
|
151
139
|
enable_validation=True,
|
|
152
140
|
dialect="mysql",
|
|
153
141
|
)
|
|
154
|
-
statement_config = enhanced_config
|
|
155
142
|
|
|
156
143
|
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
157
144
|
|
|
158
145
|
def with_cursor(self, connection: "AsyncmyConnection") -> "AsyncmyCursor":
|
|
159
|
-
"""Create
|
|
146
|
+
"""Create context manager for AsyncMy cursor."""
|
|
160
147
|
return AsyncmyCursor(connection)
|
|
161
148
|
|
|
162
149
|
def handle_database_exceptions(self) -> "AbstractAsyncContextManager[None]":
|
|
@@ -173,13 +160,12 @@ class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
|
173
160
|
Returns:
|
|
174
161
|
None - always proceeds with standard execution for AsyncMy
|
|
175
162
|
"""
|
|
176
|
-
_ = (cursor, statement)
|
|
163
|
+
_ = (cursor, statement)
|
|
177
164
|
return None
|
|
178
165
|
|
|
179
166
|
async def _execute_script(self, cursor: Any, statement: "SQL") -> "ExecutionResult":
|
|
180
|
-
"""Execute SQL script
|
|
167
|
+
"""Execute SQL script with statement splitting and parameter handling.
|
|
181
168
|
|
|
182
|
-
Uses core module optimization for statement parsing and parameter processing.
|
|
183
169
|
Parameters are embedded as static values for script execution compatibility.
|
|
184
170
|
"""
|
|
185
171
|
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
@@ -197,38 +183,34 @@ class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
|
197
183
|
)
|
|
198
184
|
|
|
199
185
|
async def _execute_many(self, cursor: Any, statement: "SQL") -> "ExecutionResult":
|
|
200
|
-
"""Execute SQL with multiple parameter sets using
|
|
186
|
+
"""Execute SQL with multiple parameter sets using AsyncMy batch processing.
|
|
201
187
|
|
|
202
|
-
|
|
188
|
+
Handles MySQL type conversion and parameter processing.
|
|
203
189
|
"""
|
|
204
190
|
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
205
191
|
|
|
206
|
-
# Enhanced parameter validation for executemany
|
|
207
192
|
if not prepared_parameters:
|
|
208
193
|
msg = "execute_many requires parameters"
|
|
209
194
|
raise ValueError(msg)
|
|
210
195
|
|
|
211
196
|
await cursor.executemany(sql, prepared_parameters)
|
|
212
197
|
|
|
213
|
-
# Calculate affected rows based on parameter count for AsyncMy
|
|
214
198
|
affected_rows = len(prepared_parameters) if prepared_parameters else 0
|
|
215
199
|
|
|
216
200
|
return self.create_execution_result(cursor, rowcount_override=affected_rows, is_many_result=True)
|
|
217
201
|
|
|
218
202
|
async def _execute_statement(self, cursor: Any, statement: "SQL") -> "ExecutionResult":
|
|
219
|
-
"""Execute single SQL statement with
|
|
203
|
+
"""Execute single SQL statement with AsyncMy MySQL data handling.
|
|
220
204
|
|
|
221
|
-
|
|
205
|
+
Handles parameter processing and MySQL result processing.
|
|
222
206
|
"""
|
|
223
207
|
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
224
208
|
await cursor.execute(sql, prepared_parameters or None)
|
|
225
209
|
|
|
226
|
-
# Enhanced SELECT result processing for MySQL
|
|
227
210
|
if statement.returns_rows():
|
|
228
211
|
fetched_data = await cursor.fetchall()
|
|
229
212
|
column_names = [desc[0] for desc in cursor.description or []]
|
|
230
213
|
|
|
231
|
-
# AsyncMy may return tuples or dicts - ensure consistent dict format
|
|
232
214
|
if fetched_data and not isinstance(fetched_data[0], dict):
|
|
233
215
|
data = [dict(zip(column_names, row)) for row in fetched_data]
|
|
234
216
|
else:
|
|
@@ -238,19 +220,16 @@ class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
|
238
220
|
cursor, selected_data=data, column_names=column_names, data_row_count=len(data), is_select_result=True
|
|
239
221
|
)
|
|
240
222
|
|
|
241
|
-
# Enhanced non-SELECT result processing for MySQL
|
|
242
223
|
affected_rows = cursor.rowcount if cursor.rowcount is not None else -1
|
|
243
224
|
last_id = getattr(cursor, "lastrowid", None) if cursor.rowcount and cursor.rowcount > 0 else None
|
|
244
225
|
return self.create_execution_result(cursor, rowcount_override=affected_rows, last_inserted_id=last_id)
|
|
245
226
|
|
|
246
|
-
# MySQL transaction management with enhanced async error handling
|
|
247
227
|
async def begin(self) -> None:
|
|
248
|
-
"""Begin a database transaction
|
|
228
|
+
"""Begin a database transaction.
|
|
249
229
|
|
|
250
230
|
Explicitly starts a MySQL transaction to ensure proper transaction boundaries.
|
|
251
231
|
"""
|
|
252
232
|
try:
|
|
253
|
-
# Execute explicit BEGIN to start transaction
|
|
254
233
|
async with AsyncmyCursor(self.connection) as cursor:
|
|
255
234
|
await cursor.execute("BEGIN")
|
|
256
235
|
except asyncmy.errors.MySQLError as e:
|
|
@@ -258,7 +237,7 @@ class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
|
258
237
|
raise SQLSpecError(msg) from e
|
|
259
238
|
|
|
260
239
|
async def rollback(self) -> None:
|
|
261
|
-
"""Rollback the current transaction
|
|
240
|
+
"""Rollback the current transaction."""
|
|
262
241
|
try:
|
|
263
242
|
await self.connection.rollback()
|
|
264
243
|
except asyncmy.errors.MySQLError as e:
|
|
@@ -266,7 +245,7 @@ class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
|
266
245
|
raise SQLSpecError(msg) from e
|
|
267
246
|
|
|
268
247
|
async def commit(self) -> None:
|
|
269
|
-
"""Commit the current transaction
|
|
248
|
+
"""Commit the current transaction."""
|
|
270
249
|
try:
|
|
271
250
|
await self.connection.commit()
|
|
272
251
|
except asyncmy.errors.MySQLError as e:
|
|
@@ -144,6 +144,10 @@ class AsyncpgConfig(AsyncDatabaseConfig[AsyncpgConnection, "Pool[Record]", Async
|
|
|
144
144
|
if self.pool_instance:
|
|
145
145
|
await self.pool_instance.close()
|
|
146
146
|
|
|
147
|
+
async def close_pool(self) -> None:
|
|
148
|
+
"""Close the connection pool."""
|
|
149
|
+
await self._close_pool()
|
|
150
|
+
|
|
147
151
|
async def create_connection(self) -> "AsyncpgConnection":
|
|
148
152
|
"""Create a single async connection from the pool.
|
|
149
153
|
|