sqlspec 0.16.1__cp311-cp311-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-311-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-311-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +871 -0
- sqlspec/core/compiler.cpython-311-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +417 -0
- sqlspec/core/filters.cpython-311-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.cpython-311-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.cpython-311-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters.py +1237 -0
- sqlspec/core/result.cpython-311-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result.py +677 -0
- sqlspec/core/splitter.cpython-311-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +819 -0
- sqlspec/core/statement.cpython-311-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-311-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-311-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-311-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +237 -0
- sqlspec/utils/text.cpython-311-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +96 -0
- sqlspec/utils/type_guards.cpython-311-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,180 @@
|
|
|
1
|
+
"""Asyncmy database configuration with direct field-based configuration."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import AsyncGenerator
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypedDict, Union
|
|
7
|
+
|
|
8
|
+
import asyncmy
|
|
9
|
+
from asyncmy.cursors import Cursor, DictCursor
|
|
10
|
+
from asyncmy.pool import Pool as AsyncmyPool
|
|
11
|
+
from typing_extensions import NotRequired
|
|
12
|
+
|
|
13
|
+
from sqlspec.adapters.asyncmy._types import AsyncmyConnection
|
|
14
|
+
from sqlspec.adapters.asyncmy.driver import AsyncmyCursor, AsyncmyDriver, asyncmy_statement_config
|
|
15
|
+
from sqlspec.config import AsyncDatabaseConfig
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from asyncmy.cursors import Cursor, DictCursor
|
|
19
|
+
from asyncmy.pool import Pool
|
|
20
|
+
|
|
21
|
+
from sqlspec.core.statement import StatementConfig
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__all__ = ("AsyncmyConfig", "AsyncmyConnectionParams", "AsyncmyPoolParams")
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AsyncmyConnectionParams(TypedDict, total=False):
|
|
30
|
+
"""Asyncmy connection parameters."""
|
|
31
|
+
|
|
32
|
+
host: NotRequired[str]
|
|
33
|
+
user: NotRequired[str]
|
|
34
|
+
password: NotRequired[str]
|
|
35
|
+
database: NotRequired[str]
|
|
36
|
+
port: NotRequired[int]
|
|
37
|
+
unix_socket: NotRequired[str]
|
|
38
|
+
charset: NotRequired[str]
|
|
39
|
+
connect_timeout: NotRequired[int]
|
|
40
|
+
read_default_file: NotRequired[str]
|
|
41
|
+
read_default_group: NotRequired[str]
|
|
42
|
+
autocommit: NotRequired[bool]
|
|
43
|
+
local_infile: NotRequired[bool]
|
|
44
|
+
ssl: NotRequired[Any]
|
|
45
|
+
sql_mode: NotRequired[str]
|
|
46
|
+
init_command: NotRequired[str]
|
|
47
|
+
cursor_class: NotRequired[Union[type["Cursor"], type["DictCursor"]]]
|
|
48
|
+
extra: NotRequired[dict[str, Any]]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AsyncmyPoolParams(AsyncmyConnectionParams, total=False):
|
|
52
|
+
"""Asyncmy pool parameters."""
|
|
53
|
+
|
|
54
|
+
minsize: NotRequired[int]
|
|
55
|
+
maxsize: NotRequired[int]
|
|
56
|
+
echo: NotRequired[bool]
|
|
57
|
+
pool_recycle: NotRequired[int]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver]): # pyright: ignore
|
|
61
|
+
"""Configuration for Asyncmy database connections with direct field-based configuration."""
|
|
62
|
+
|
|
63
|
+
driver_type: ClassVar[type[AsyncmyDriver]] = AsyncmyDriver
|
|
64
|
+
connection_type: "ClassVar[type[AsyncmyConnection]]" = AsyncmyConnection # pyright: ignore
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
*,
|
|
69
|
+
pool_config: "Optional[Union[AsyncmyPoolParams, dict[str, Any]]]" = None,
|
|
70
|
+
pool_instance: "Optional[Pool]" = None,
|
|
71
|
+
migration_config: Optional[dict[str, Any]] = None,
|
|
72
|
+
statement_config: "Optional[StatementConfig]" = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Initialize Asyncmy configuration.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
pool_config: Pool configuration parameters
|
|
78
|
+
pool_instance: Existing pool instance to use
|
|
79
|
+
migration_config: Migration configuration
|
|
80
|
+
statement_config: Statement configuration override
|
|
81
|
+
"""
|
|
82
|
+
processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
|
|
83
|
+
if "extra" in processed_pool_config:
|
|
84
|
+
extras = processed_pool_config.pop("extra")
|
|
85
|
+
processed_pool_config.update(extras)
|
|
86
|
+
|
|
87
|
+
if "host" not in processed_pool_config:
|
|
88
|
+
processed_pool_config["host"] = "localhost"
|
|
89
|
+
if "port" not in processed_pool_config:
|
|
90
|
+
processed_pool_config["port"] = 3306
|
|
91
|
+
|
|
92
|
+
if statement_config is None:
|
|
93
|
+
statement_config = asyncmy_statement_config
|
|
94
|
+
|
|
95
|
+
super().__init__(
|
|
96
|
+
pool_config=processed_pool_config,
|
|
97
|
+
pool_instance=pool_instance,
|
|
98
|
+
migration_config=migration_config,
|
|
99
|
+
statement_config=statement_config,
|
|
100
|
+
driver_features={},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
async def _create_pool(self) -> "Pool": # pyright: ignore
|
|
104
|
+
"""Create the actual async connection pool."""
|
|
105
|
+
return await asyncmy.create_pool(**dict(self.pool_config))
|
|
106
|
+
|
|
107
|
+
async def _close_pool(self) -> None:
|
|
108
|
+
"""Close the actual async connection pool."""
|
|
109
|
+
if self.pool_instance:
|
|
110
|
+
await self.pool_instance.close()
|
|
111
|
+
|
|
112
|
+
async def create_connection(self) -> AsyncmyConnection: # pyright: ignore
|
|
113
|
+
"""Create a single async connection (not from pool).
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
An Asyncmy connection instance.
|
|
117
|
+
"""
|
|
118
|
+
if self.pool_instance is None:
|
|
119
|
+
self.pool_instance = await self.create_pool()
|
|
120
|
+
return await self.pool_instance.acquire() # pyright: ignore
|
|
121
|
+
|
|
122
|
+
@asynccontextmanager
|
|
123
|
+
async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[AsyncmyConnection, None]: # pyright: ignore
|
|
124
|
+
"""Provide an async connection context manager.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
*args: Additional arguments.
|
|
128
|
+
**kwargs: Additional keyword arguments.
|
|
129
|
+
|
|
130
|
+
Yields:
|
|
131
|
+
An Asyncmy connection instance.
|
|
132
|
+
"""
|
|
133
|
+
if self.pool_instance is None:
|
|
134
|
+
self.pool_instance = await self.create_pool()
|
|
135
|
+
async with self.pool_instance.acquire() as connection: # pyright: ignore
|
|
136
|
+
yield connection
|
|
137
|
+
|
|
138
|
+
@asynccontextmanager
|
|
139
|
+
async def provide_session(
|
|
140
|
+
self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
|
|
141
|
+
) -> AsyncGenerator[AsyncmyDriver, None]:
|
|
142
|
+
"""Provide an async driver session context manager.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
*args: Additional arguments.
|
|
146
|
+
statement_config: Optional statement configuration override.
|
|
147
|
+
**kwargs: Additional keyword arguments.
|
|
148
|
+
|
|
149
|
+
Yields:
|
|
150
|
+
An AsyncmyDriver instance.
|
|
151
|
+
"""
|
|
152
|
+
async with self.provide_connection(*args, **kwargs) as connection:
|
|
153
|
+
final_statement_config = statement_config or asyncmy_statement_config
|
|
154
|
+
yield self.driver_type(connection=connection, statement_config=final_statement_config)
|
|
155
|
+
|
|
156
|
+
async def provide_pool(self, *args: Any, **kwargs: Any) -> "Pool": # pyright: ignore
|
|
157
|
+
"""Provide async pool instance.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
The async connection pool.
|
|
161
|
+
"""
|
|
162
|
+
if not self.pool_instance:
|
|
163
|
+
self.pool_instance = await self.create_pool()
|
|
164
|
+
return self.pool_instance
|
|
165
|
+
|
|
166
|
+
def get_signature_namespace(self) -> "dict[str, type[Any]]":
|
|
167
|
+
"""Get the signature namespace for Asyncmy types.
|
|
168
|
+
|
|
169
|
+
This provides all Asyncmy-specific types that Litestar needs to recognize
|
|
170
|
+
to avoid serialization attempts.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Dictionary mapping type names to types.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
namespace = super().get_signature_namespace()
|
|
177
|
+
namespace.update(
|
|
178
|
+
{"AsyncmyConnection": AsyncmyConnection, "AsyncmyPool": AsyncmyPool, "AsyncmyCursor": AsyncmyCursor}
|
|
179
|
+
)
|
|
180
|
+
return namespace
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""AsyncMy MySQL driver implementation for async MySQL operations.
|
|
2
|
+
|
|
3
|
+
Provides async MySQL/MariaDB connectivity with:
|
|
4
|
+
- Parameter style conversion (QMARK to POSITIONAL_PYFORMAT)
|
|
5
|
+
- MySQL-specific type coercion and data handling
|
|
6
|
+
- Error categorization for MySQL/MariaDB
|
|
7
|
+
- Transaction management
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
12
|
+
|
|
13
|
+
import asyncmy
|
|
14
|
+
import asyncmy.errors
|
|
15
|
+
from asyncmy.cursors import Cursor, DictCursor
|
|
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.asyncmy._types import AsyncmyConnection
|
|
28
|
+
from sqlspec.core.result import SQLResult
|
|
29
|
+
from sqlspec.core.statement import SQL
|
|
30
|
+
from sqlspec.driver import ExecutionResult
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
__all__ = ("AsyncmyCursor", "AsyncmyDriver", "AsyncmyExceptionHandler", "asyncmy_statement_config")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Enhanced AsyncMy statement configuration using core modules with performance optimizations
|
|
38
|
+
asyncmy_statement_config = StatementConfig(
|
|
39
|
+
dialect="mysql",
|
|
40
|
+
parameter_config=ParameterStyleConfig(
|
|
41
|
+
default_parameter_style=ParameterStyle.QMARK,
|
|
42
|
+
supported_parameter_styles={ParameterStyle.QMARK, ParameterStyle.POSITIONAL_PYFORMAT},
|
|
43
|
+
default_execution_parameter_style=ParameterStyle.POSITIONAL_PYFORMAT,
|
|
44
|
+
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
|
+
},
|
|
51
|
+
has_native_list_expansion=False,
|
|
52
|
+
needs_static_script_compilation=True,
|
|
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 AsyncmyCursor:
|
|
63
|
+
"""Async context manager for AsyncMy cursor management."""
|
|
64
|
+
|
|
65
|
+
__slots__ = ("connection", "cursor")
|
|
66
|
+
|
|
67
|
+
def __init__(self, connection: "AsyncmyConnection") -> None:
|
|
68
|
+
self.connection = connection
|
|
69
|
+
self.cursor: Optional[Union[Cursor, DictCursor]] = None
|
|
70
|
+
|
|
71
|
+
async def __aenter__(self) -> Union[Cursor, DictCursor]:
|
|
72
|
+
self.cursor = 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
|
+
await self.cursor.close()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AsyncmyExceptionHandler:
|
|
82
|
+
"""Custom async context manager for handling AsyncMy database exceptions."""
|
|
83
|
+
|
|
84
|
+
__slots__ = ()
|
|
85
|
+
|
|
86
|
+
async def __aenter__(self) -> None:
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
90
|
+
if exc_type is None:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
if issubclass(exc_type, asyncmy.errors.IntegrityError):
|
|
94
|
+
e = exc_val
|
|
95
|
+
msg = f"AsyncMy MySQL integrity constraint violation: {e}"
|
|
96
|
+
raise SQLSpecError(msg) from e
|
|
97
|
+
if issubclass(exc_type, asyncmy.errors.ProgrammingError):
|
|
98
|
+
e = exc_val
|
|
99
|
+
error_msg = str(e).lower()
|
|
100
|
+
if "syntax" in error_msg or "parse" in error_msg:
|
|
101
|
+
msg = f"AsyncMy MySQL SQL syntax error: {e}"
|
|
102
|
+
raise SQLParsingError(msg) from e
|
|
103
|
+
msg = f"AsyncMy MySQL programming error: {e}"
|
|
104
|
+
raise SQLSpecError(msg) from e
|
|
105
|
+
if issubclass(exc_type, asyncmy.errors.OperationalError):
|
|
106
|
+
e = exc_val
|
|
107
|
+
msg = f"AsyncMy MySQL operational error: {e}"
|
|
108
|
+
raise SQLSpecError(msg) from e
|
|
109
|
+
if issubclass(exc_type, asyncmy.errors.DatabaseError):
|
|
110
|
+
e = exc_val
|
|
111
|
+
msg = f"AsyncMy MySQL database error: {e}"
|
|
112
|
+
raise SQLSpecError(msg) from e
|
|
113
|
+
if issubclass(exc_type, asyncmy.errors.Error):
|
|
114
|
+
e = exc_val
|
|
115
|
+
msg = f"AsyncMy MySQL error: {e}"
|
|
116
|
+
raise SQLSpecError(msg) from e
|
|
117
|
+
if issubclass(exc_type, Exception):
|
|
118
|
+
e = exc_val
|
|
119
|
+
error_msg = str(e).lower()
|
|
120
|
+
if "parse" in error_msg or "syntax" in error_msg:
|
|
121
|
+
msg = f"SQL parsing failed: {e}"
|
|
122
|
+
raise SQLParsingError(msg) from e
|
|
123
|
+
msg = f"Unexpected async database operation error: {e}"
|
|
124
|
+
raise SQLSpecError(msg) from e
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class AsyncmyDriver(AsyncDriverAdapterBase):
|
|
128
|
+
"""AsyncMy MySQL/MariaDB driver for async database operations.
|
|
129
|
+
|
|
130
|
+
Provides MySQL/MariaDB connectivity with:
|
|
131
|
+
- Parameter style conversion (QMARK to POSITIONAL_PYFORMAT)
|
|
132
|
+
- MySQL-specific type coercion (bool -> int, dict/list -> JSON)
|
|
133
|
+
- Error categorization for MySQL/MariaDB
|
|
134
|
+
- Transaction management
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
__slots__ = ()
|
|
138
|
+
dialect = "mysql"
|
|
139
|
+
|
|
140
|
+
def __init__(
|
|
141
|
+
self,
|
|
142
|
+
connection: "AsyncmyConnection",
|
|
143
|
+
statement_config: "Optional[StatementConfig]" = None,
|
|
144
|
+
driver_features: "Optional[dict[str, Any]]" = None,
|
|
145
|
+
) -> None:
|
|
146
|
+
if statement_config is None:
|
|
147
|
+
cache_config = get_cache_config()
|
|
148
|
+
enhanced_config = asyncmy_statement_config.replace(
|
|
149
|
+
enable_caching=cache_config.compiled_cache_enabled,
|
|
150
|
+
enable_parsing=True,
|
|
151
|
+
enable_validation=True,
|
|
152
|
+
dialect="mysql",
|
|
153
|
+
)
|
|
154
|
+
statement_config = enhanced_config
|
|
155
|
+
|
|
156
|
+
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
157
|
+
|
|
158
|
+
def with_cursor(self, connection: "AsyncmyConnection") -> "AsyncmyCursor":
|
|
159
|
+
"""Create async context manager for AsyncMy cursor."""
|
|
160
|
+
return AsyncmyCursor(connection)
|
|
161
|
+
|
|
162
|
+
def handle_database_exceptions(self) -> "AbstractAsyncContextManager[None]":
|
|
163
|
+
"""Handle database-specific exceptions and wrap them appropriately."""
|
|
164
|
+
return AsyncmyExceptionHandler()
|
|
165
|
+
|
|
166
|
+
async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "Optional[SQLResult]":
|
|
167
|
+
"""Hook for AsyncMy-specific special operations.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
cursor: AsyncMy cursor object
|
|
171
|
+
statement: SQL statement to analyze
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
None - always proceeds with standard execution for AsyncMy
|
|
175
|
+
"""
|
|
176
|
+
_ = (cursor, statement) # Mark as intentionally unused
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
async def _execute_script(self, cursor: Any, statement: "SQL") -> "ExecutionResult":
|
|
180
|
+
"""Execute SQL script using enhanced statement splitting and parameter handling.
|
|
181
|
+
|
|
182
|
+
Uses core module optimization for statement parsing and parameter processing.
|
|
183
|
+
Parameters are embedded as static values for script execution compatibility.
|
|
184
|
+
"""
|
|
185
|
+
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
186
|
+
statements = self.split_script_statements(sql, statement.statement_config, strip_trailing_semicolon=True)
|
|
187
|
+
|
|
188
|
+
successful_count = 0
|
|
189
|
+
last_cursor = cursor
|
|
190
|
+
|
|
191
|
+
for stmt in statements:
|
|
192
|
+
await cursor.execute(stmt, prepared_parameters or None)
|
|
193
|
+
successful_count += 1
|
|
194
|
+
|
|
195
|
+
return self.create_execution_result(
|
|
196
|
+
last_cursor, statement_count=len(statements), successful_statements=successful_count, is_script_result=True
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
async def _execute_many(self, cursor: Any, statement: "SQL") -> "ExecutionResult":
|
|
200
|
+
"""Execute SQL with multiple parameter sets using optimized AsyncMy batch processing.
|
|
201
|
+
|
|
202
|
+
Leverages core parameter processing for enhanced MySQL type handling and parameter conversion.
|
|
203
|
+
"""
|
|
204
|
+
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
205
|
+
|
|
206
|
+
# Enhanced parameter validation for executemany
|
|
207
|
+
if not prepared_parameters:
|
|
208
|
+
msg = "execute_many requires parameters"
|
|
209
|
+
raise ValueError(msg)
|
|
210
|
+
|
|
211
|
+
await cursor.executemany(sql, prepared_parameters)
|
|
212
|
+
|
|
213
|
+
# Calculate affected rows based on parameter count for AsyncMy
|
|
214
|
+
affected_rows = len(prepared_parameters) if prepared_parameters else 0
|
|
215
|
+
|
|
216
|
+
return self.create_execution_result(cursor, rowcount_override=affected_rows, is_many_result=True)
|
|
217
|
+
|
|
218
|
+
async def _execute_statement(self, cursor: Any, statement: "SQL") -> "ExecutionResult":
|
|
219
|
+
"""Execute single SQL statement with enhanced AsyncMy MySQL data handling and performance optimization.
|
|
220
|
+
|
|
221
|
+
Uses core processing for optimal parameter handling and MySQL result processing.
|
|
222
|
+
"""
|
|
223
|
+
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
224
|
+
await cursor.execute(sql, prepared_parameters or None)
|
|
225
|
+
|
|
226
|
+
# Enhanced SELECT result processing for MySQL
|
|
227
|
+
if statement.returns_rows():
|
|
228
|
+
fetched_data = await cursor.fetchall()
|
|
229
|
+
column_names = [desc[0] for desc in cursor.description or []]
|
|
230
|
+
|
|
231
|
+
# AsyncMy may return tuples or dicts - ensure consistent dict format
|
|
232
|
+
if fetched_data and not isinstance(fetched_data[0], dict):
|
|
233
|
+
data = [dict(zip(column_names, row)) for row in fetched_data]
|
|
234
|
+
else:
|
|
235
|
+
data = fetched_data
|
|
236
|
+
|
|
237
|
+
return self.create_execution_result(
|
|
238
|
+
cursor, selected_data=data, column_names=column_names, data_row_count=len(data), is_select_result=True
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Enhanced non-SELECT result processing for MySQL
|
|
242
|
+
affected_rows = cursor.rowcount if cursor.rowcount is not None else -1
|
|
243
|
+
last_id = getattr(cursor, "lastrowid", None) if cursor.rowcount and cursor.rowcount > 0 else None
|
|
244
|
+
return self.create_execution_result(cursor, rowcount_override=affected_rows, last_inserted_id=last_id)
|
|
245
|
+
|
|
246
|
+
# MySQL transaction management with enhanced async error handling
|
|
247
|
+
async def begin(self) -> None:
|
|
248
|
+
"""Begin a database transaction with enhanced async error handling.
|
|
249
|
+
|
|
250
|
+
Explicitly starts a MySQL transaction to ensure proper transaction boundaries.
|
|
251
|
+
"""
|
|
252
|
+
try:
|
|
253
|
+
# Execute explicit BEGIN to start transaction
|
|
254
|
+
async with AsyncmyCursor(self.connection) as cursor:
|
|
255
|
+
await cursor.execute("BEGIN")
|
|
256
|
+
except asyncmy.errors.MySQLError as e:
|
|
257
|
+
msg = f"Failed to begin MySQL transaction: {e}"
|
|
258
|
+
raise SQLSpecError(msg) from e
|
|
259
|
+
|
|
260
|
+
async def rollback(self) -> None:
|
|
261
|
+
"""Rollback the current transaction with enhanced async error handling."""
|
|
262
|
+
try:
|
|
263
|
+
await self.connection.rollback()
|
|
264
|
+
except asyncmy.errors.MySQLError as e:
|
|
265
|
+
msg = f"Failed to rollback MySQL transaction: {e}"
|
|
266
|
+
raise SQLSpecError(msg) from e
|
|
267
|
+
|
|
268
|
+
async def commit(self) -> None:
|
|
269
|
+
"""Commit the current transaction with enhanced async error handling."""
|
|
270
|
+
try:
|
|
271
|
+
await self.connection.commit()
|
|
272
|
+
except asyncmy.errors.MySQLError as e:
|
|
273
|
+
msg = f"Failed to commit MySQL transaction: {e}"
|
|
274
|
+
raise SQLSpecError(msg) from e
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""AsyncPG adapter for SQLSpec."""
|
|
2
|
+
|
|
3
|
+
from sqlspec.adapters.asyncpg._types import AsyncpgConnection
|
|
4
|
+
from sqlspec.adapters.asyncpg.config import AsyncpgConfig, AsyncpgConnectionConfig, AsyncpgPoolConfig
|
|
5
|
+
from sqlspec.adapters.asyncpg.driver import (
|
|
6
|
+
AsyncpgCursor,
|
|
7
|
+
AsyncpgDriver,
|
|
8
|
+
AsyncpgExceptionHandler,
|
|
9
|
+
asyncpg_statement_config,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = (
|
|
13
|
+
"AsyncpgConfig",
|
|
14
|
+
"AsyncpgConnection",
|
|
15
|
+
"AsyncpgConnectionConfig",
|
|
16
|
+
"AsyncpgCursor",
|
|
17
|
+
"AsyncpgDriver",
|
|
18
|
+
"AsyncpgExceptionHandler",
|
|
19
|
+
"AsyncpgPoolConfig",
|
|
20
|
+
"asyncpg_statement_config",
|
|
21
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Union
|
|
2
|
+
|
|
3
|
+
from asyncpg import Connection
|
|
4
|
+
from asyncpg.pool import PoolConnectionProxy
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from asyncpg import Record
|
|
8
|
+
from typing_extensions import TypeAlias
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
AsyncpgConnection: TypeAlias = Union[Connection[Record], PoolConnectionProxy[Record]]
|
|
13
|
+
else:
|
|
14
|
+
AsyncpgConnection = Union[Connection, PoolConnectionProxy]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = ("AsyncpgConnection",)
|