sqlspec 0.14.1__py3-none-any.whl → 0.16.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 +50 -25
- sqlspec/__main__.py +1 -1
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +1 -2
- sqlspec/_sql.py +480 -121
- sqlspec/_typing.py +278 -142
- sqlspec/adapters/adbc/__init__.py +4 -3
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +115 -260
- sqlspec/adapters/adbc/driver.py +462 -367
- sqlspec/adapters/aiosqlite/__init__.py +18 -3
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +199 -129
- sqlspec/adapters/aiosqlite/driver.py +230 -269
- sqlspec/adapters/asyncmy/__init__.py +18 -3
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +80 -168
- sqlspec/adapters/asyncmy/driver.py +260 -225
- sqlspec/adapters/asyncpg/__init__.py +19 -4
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +82 -181
- sqlspec/adapters/asyncpg/driver.py +285 -383
- sqlspec/adapters/bigquery/__init__.py +17 -3
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +191 -258
- sqlspec/adapters/bigquery/driver.py +474 -646
- sqlspec/adapters/duckdb/__init__.py +14 -3
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +415 -351
- sqlspec/adapters/duckdb/driver.py +343 -413
- sqlspec/adapters/oracledb/__init__.py +19 -5
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +123 -379
- sqlspec/adapters/oracledb/driver.py +507 -560
- sqlspec/adapters/psqlpy/__init__.py +13 -3
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +93 -254
- sqlspec/adapters/psqlpy/driver.py +505 -234
- sqlspec/adapters/psycopg/__init__.py +19 -5
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +143 -403
- sqlspec/adapters/psycopg/driver.py +706 -872
- sqlspec/adapters/sqlite/__init__.py +14 -3
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +202 -118
- sqlspec/adapters/sqlite/driver.py +264 -303
- sqlspec/base.py +105 -9
- sqlspec/{statement/builder → builder}/__init__.py +12 -14
- sqlspec/{statement/builder → builder}/_base.py +120 -55
- sqlspec/{statement/builder → builder}/_column.py +17 -6
- sqlspec/{statement/builder → builder}/_ddl.py +46 -79
- sqlspec/{statement/builder → builder}/_ddl_utils.py +5 -10
- sqlspec/{statement/builder → builder}/_delete.py +6 -25
- sqlspec/{statement/builder → builder}/_insert.py +18 -65
- sqlspec/builder/_merge.py +56 -0
- sqlspec/{statement/builder → builder}/_parsing_utils.py +8 -11
- sqlspec/{statement/builder → builder}/_select.py +11 -56
- sqlspec/{statement/builder → builder}/_update.py +12 -18
- sqlspec/{statement/builder → builder}/mixins/__init__.py +10 -14
- sqlspec/{statement/builder → builder}/mixins/_cte_and_set_ops.py +48 -59
- sqlspec/{statement/builder → builder}/mixins/_insert_operations.py +34 -18
- sqlspec/{statement/builder → builder}/mixins/_join_operations.py +1 -3
- sqlspec/{statement/builder → builder}/mixins/_merge_operations.py +19 -9
- sqlspec/{statement/builder → builder}/mixins/_order_limit_operations.py +3 -3
- sqlspec/{statement/builder → builder}/mixins/_pivot_operations.py +4 -8
- sqlspec/{statement/builder → builder}/mixins/_select_operations.py +25 -38
- sqlspec/{statement/builder → builder}/mixins/_update_operations.py +15 -16
- sqlspec/{statement/builder → builder}/mixins/_where_clause.py +210 -137
- sqlspec/cli.py +4 -5
- sqlspec/config.py +180 -133
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.py +873 -0
- sqlspec/core/compiler.py +396 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.py +1209 -0
- sqlspec/core/result.py +664 -0
- sqlspec/{statement → core}/splitter.py +321 -191
- sqlspec/core/statement.py +666 -0
- sqlspec/driver/__init__.py +7 -10
- sqlspec/driver/_async.py +387 -176
- sqlspec/driver/_common.py +527 -289
- sqlspec/driver/_sync.py +390 -172
- sqlspec/driver/mixins/__init__.py +2 -19
- sqlspec/driver/mixins/_result_tools.py +164 -0
- sqlspec/driver/mixins/_sql_translator.py +6 -3
- sqlspec/exceptions.py +5 -252
- sqlspec/extensions/aiosql/adapter.py +93 -96
- sqlspec/extensions/litestar/cli.py +1 -1
- sqlspec/extensions/litestar/config.py +0 -1
- sqlspec/extensions/litestar/handlers.py +15 -26
- sqlspec/extensions/litestar/plugin.py +18 -16
- sqlspec/extensions/litestar/providers.py +17 -52
- sqlspec/loader.py +424 -105
- sqlspec/migrations/__init__.py +12 -0
- sqlspec/migrations/base.py +92 -68
- sqlspec/migrations/commands.py +24 -106
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +49 -51
- sqlspec/migrations/tracker.py +31 -44
- sqlspec/migrations/utils.py +64 -24
- sqlspec/protocols.py +7 -183
- sqlspec/storage/__init__.py +1 -1
- sqlspec/storage/backends/base.py +37 -40
- sqlspec/storage/backends/fsspec.py +136 -112
- sqlspec/storage/backends/obstore.py +138 -160
- sqlspec/storage/capabilities.py +5 -4
- sqlspec/storage/registry.py +57 -106
- sqlspec/typing.py +136 -115
- sqlspec/utils/__init__.py +2 -3
- sqlspec/utils/correlation.py +0 -3
- sqlspec/utils/deprecation.py +6 -6
- sqlspec/utils/fixtures.py +6 -6
- sqlspec/utils/logging.py +0 -2
- sqlspec/utils/module_loader.py +7 -12
- sqlspec/utils/singleton.py +0 -1
- sqlspec/utils/sync_tools.py +17 -38
- sqlspec/utils/text.py +12 -51
- sqlspec/utils/type_guards.py +443 -232
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/METADATA +7 -2
- sqlspec-0.16.0.dist-info/RECORD +134 -0
- sqlspec/adapters/adbc/transformers.py +0 -108
- sqlspec/driver/connection.py +0 -207
- sqlspec/driver/mixins/_cache.py +0 -114
- sqlspec/driver/mixins/_csv_writer.py +0 -91
- sqlspec/driver/mixins/_pipeline.py +0 -508
- sqlspec/driver/mixins/_query_tools.py +0 -796
- sqlspec/driver/mixins/_result_utils.py +0 -138
- sqlspec/driver/mixins/_storage.py +0 -912
- sqlspec/driver/mixins/_type_coercion.py +0 -128
- sqlspec/driver/parameters.py +0 -138
- sqlspec/statement/__init__.py +0 -21
- sqlspec/statement/builder/_merge.py +0 -95
- sqlspec/statement/cache.py +0 -50
- sqlspec/statement/filters.py +0 -625
- sqlspec/statement/parameters.py +0 -956
- sqlspec/statement/pipelines/__init__.py +0 -210
- sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
- sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
- sqlspec/statement/pipelines/context.py +0 -109
- sqlspec/statement/pipelines/transformers/__init__.py +0 -7
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
- sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
- sqlspec/statement/pipelines/validators/__init__.py +0 -23
- sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
- sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
- sqlspec/statement/pipelines/validators/_performance.py +0 -714
- sqlspec/statement/pipelines/validators/_security.py +0 -967
- sqlspec/statement/result.py +0 -435
- sqlspec/statement/sql.py +0 -1774
- sqlspec/utils/cached_property.py +0 -25
- sqlspec/utils/statement_hashing.py +0 -203
- sqlspec-0.14.1.dist-info/RECORD +0 -145
- /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,287 +1,248 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
6
13
|
from typing import TYPE_CHECKING, Any, Optional
|
|
7
14
|
|
|
8
15
|
import aiosqlite
|
|
9
16
|
|
|
10
|
-
from sqlspec.
|
|
11
|
-
from sqlspec.
|
|
12
|
-
from sqlspec.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
AsyncStorageMixin,
|
|
16
|
-
SQLTranslatorMixin,
|
|
17
|
-
ToSchemaMixin,
|
|
18
|
-
TypeCoercionMixin,
|
|
19
|
-
)
|
|
20
|
-
from sqlspec.driver.parameters import convert_parameter_sequence
|
|
21
|
-
from sqlspec.statement.parameters import ParameterStyle, ParameterValidator
|
|
22
|
-
from sqlspec.statement.result import SQLResult
|
|
23
|
-
from sqlspec.statement.sql import SQL, SQLConfig
|
|
24
|
-
from sqlspec.typing import DictRow, RowT
|
|
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
|
|
25
22
|
from sqlspec.utils.serializers import to_json
|
|
26
23
|
|
|
27
24
|
if TYPE_CHECKING:
|
|
28
|
-
from
|
|
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
|
+
)
|
|
29
60
|
|
|
30
|
-
__all__ = ("AiosqliteConnection", "AiosqliteDriver")
|
|
31
61
|
|
|
32
|
-
|
|
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"
|
|
33
137
|
|
|
34
|
-
|
|
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
|
|
35
153
|
|
|
154
|
+
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
36
155
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
SQLTranslatorMixin,
|
|
41
|
-
TypeCoercionMixin,
|
|
42
|
-
AsyncStorageMixin,
|
|
43
|
-
AsyncPipelinedExecutionMixin,
|
|
44
|
-
ToSchemaMixin,
|
|
45
|
-
):
|
|
46
|
-
"""Aiosqlite SQLite Driver Adapter. Modern protocol implementation."""
|
|
156
|
+
def with_cursor(self, connection: "AiosqliteConnection") -> "AiosqliteCursor":
|
|
157
|
+
"""Create async context manager for AIOSQLite cursor."""
|
|
158
|
+
return AiosqliteCursor(connection)
|
|
47
159
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
160
|
+
def handle_database_exceptions(self) -> "AbstractAsyncContextManager[None]":
|
|
161
|
+
"""Handle AIOSQLite-specific exceptions."""
|
|
162
|
+
return AiosqliteExceptionHandler()
|
|
51
163
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
detected_styles = set()
|
|
109
|
-
sql_str = statement.to_sql(placeholder_style=None) # Get raw SQL
|
|
110
|
-
validator = self.config.parameter_validator if self.config else ParameterValidator()
|
|
111
|
-
param_infos = validator.extract_parameters(sql_str)
|
|
112
|
-
if param_infos:
|
|
113
|
-
detected_styles = {p.style for p in param_infos}
|
|
114
|
-
|
|
115
|
-
target_style = self.default_parameter_style
|
|
116
|
-
|
|
117
|
-
unsupported_styles = detected_styles - set(self.supported_parameter_styles)
|
|
118
|
-
if unsupported_styles:
|
|
119
|
-
target_style = self.default_parameter_style
|
|
120
|
-
elif detected_styles:
|
|
121
|
-
# Prefer the first supported style found
|
|
122
|
-
for style in detected_styles:
|
|
123
|
-
if style in self.supported_parameter_styles:
|
|
124
|
-
target_style = style
|
|
125
|
-
break
|
|
126
|
-
|
|
127
|
-
if statement.is_many:
|
|
128
|
-
sql, params = self._get_compiled_sql(statement, target_style)
|
|
129
|
-
|
|
130
|
-
params = self._process_parameters(params)
|
|
131
|
-
|
|
132
|
-
return await self._execute_many(sql, params, connection=connection, **kwargs)
|
|
133
|
-
|
|
134
|
-
sql, params = self._get_compiled_sql(statement, target_style)
|
|
135
|
-
|
|
136
|
-
params = self._process_parameters(params)
|
|
137
|
-
|
|
138
|
-
return await self._execute(sql, params, statement, connection=connection, **kwargs)
|
|
139
|
-
|
|
140
|
-
async def _execute(
|
|
141
|
-
self, sql: str, parameters: Any, statement: SQL, connection: Optional[AiosqliteConnection] = None, **kwargs: Any
|
|
142
|
-
) -> SQLResult[RowT]:
|
|
143
|
-
conn = self._connection(connection)
|
|
144
|
-
|
|
145
|
-
async with managed_transaction_async(conn, auto_commit=True) as txn_conn:
|
|
146
|
-
converted_params = convert_parameter_sequence(parameters)
|
|
147
|
-
|
|
148
|
-
# Extract the actual parameters from the converted list
|
|
149
|
-
actual_params = converted_params[0] if converted_params and len(converted_params) == 1 else converted_params
|
|
150
|
-
|
|
151
|
-
# AIOSQLite expects tuple or dict - handle parameter conversion
|
|
152
|
-
if ":param_" in sql or (isinstance(actual_params, dict)):
|
|
153
|
-
# SQL has named placeholders, ensure params are dict
|
|
154
|
-
converted_params = self._convert_parameters_to_driver_format(
|
|
155
|
-
sql, actual_params, target_style=ParameterStyle.NAMED_COLON
|
|
156
|
-
)
|
|
157
|
-
else:
|
|
158
|
-
# SQL has positional placeholders, ensure params are list/tuple
|
|
159
|
-
converted_params = self._convert_parameters_to_driver_format(
|
|
160
|
-
sql, actual_params, target_style=ParameterStyle.QMARK
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
async with self._get_cursor(txn_conn) as cursor:
|
|
164
|
-
# Aiosqlite handles both dict and tuple parameters
|
|
165
|
-
await cursor.execute(sql, converted_params or ())
|
|
166
|
-
if self.returns_rows(statement.expression):
|
|
167
|
-
fetched_data = await cursor.fetchall()
|
|
168
|
-
column_names = [desc[0] for desc in cursor.description or []]
|
|
169
|
-
data_list: list[Any] = list(fetched_data) if fetched_data else []
|
|
170
|
-
return SQLResult(
|
|
171
|
-
statement=statement,
|
|
172
|
-
data=data_list,
|
|
173
|
-
column_names=column_names,
|
|
174
|
-
rows_affected=len(data_list),
|
|
175
|
-
operation_type="SELECT",
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
return SQLResult(
|
|
179
|
-
statement=statement,
|
|
180
|
-
data=[],
|
|
181
|
-
rows_affected=cursor.rowcount,
|
|
182
|
-
operation_type=self._determine_operation_type(statement),
|
|
183
|
-
metadata={"status_message": "OK"},
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
async def _execute_many(
|
|
187
|
-
self, sql: str, param_list: Any, connection: Optional[AiosqliteConnection] = None, **kwargs: Any
|
|
188
|
-
) -> SQLResult[RowT]:
|
|
189
|
-
# Use provided connection or driver's default connection
|
|
190
|
-
conn = connection if connection is not None else self._connection(None)
|
|
191
|
-
|
|
192
|
-
async with managed_transaction_async(conn, auto_commit=True) as txn_conn:
|
|
193
|
-
# Normalize parameter list using consolidated utility
|
|
194
|
-
converted_param_list = convert_parameter_sequence(param_list)
|
|
195
|
-
|
|
196
|
-
params_list: list[tuple[Any, ...]] = []
|
|
197
|
-
if converted_param_list and isinstance(converted_param_list, Sequence):
|
|
198
|
-
for param_set in converted_param_list:
|
|
199
|
-
if isinstance(param_set, (list, tuple)):
|
|
200
|
-
params_list.append(tuple(param_set))
|
|
201
|
-
elif param_set is None:
|
|
202
|
-
params_list.append(())
|
|
203
|
-
|
|
204
|
-
async with self._get_cursor(txn_conn) as cursor:
|
|
205
|
-
await cursor.executemany(sql, params_list)
|
|
206
|
-
return SQLResult(
|
|
207
|
-
statement=SQL(sql, _dialect=self.dialect),
|
|
208
|
-
data=[],
|
|
209
|
-
rows_affected=cursor.rowcount,
|
|
210
|
-
operation_type="EXECUTE",
|
|
211
|
-
metadata={"status_message": "OK"},
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
async def _execute_script(
|
|
215
|
-
self, script: str, connection: Optional[AiosqliteConnection] = None, **kwargs: Any
|
|
216
|
-
) -> SQLResult[RowT]:
|
|
217
|
-
# Use provided connection or driver's default connection
|
|
218
|
-
conn = connection if connection is not None else self._connection(None)
|
|
219
|
-
|
|
220
|
-
async with managed_transaction_async(conn, auto_commit=True) as txn_conn:
|
|
221
|
-
# Split script into individual statements for validation
|
|
222
|
-
statements = self._split_script_statements(script)
|
|
223
|
-
suppress_warnings = kwargs.get("_suppress_warnings", False)
|
|
224
|
-
|
|
225
|
-
executed_count = 0
|
|
226
|
-
total_rows = 0
|
|
227
|
-
|
|
228
|
-
# Execute each statement individually for better control and validation
|
|
229
|
-
async with self._get_cursor(txn_conn) as cursor:
|
|
230
|
-
for statement in statements:
|
|
231
|
-
if statement.strip():
|
|
232
|
-
# Validate each statement unless warnings suppressed
|
|
233
|
-
if not suppress_warnings:
|
|
234
|
-
# Run validation through pipeline
|
|
235
|
-
temp_sql = SQL(statement, config=self.config)
|
|
236
|
-
temp_sql._ensure_processed()
|
|
237
|
-
# Validation errors are logged as warnings by default
|
|
238
|
-
|
|
239
|
-
await cursor.execute(statement)
|
|
240
|
-
executed_count += 1
|
|
241
|
-
total_rows += cursor.rowcount or 0
|
|
242
|
-
|
|
243
|
-
return SQLResult(
|
|
244
|
-
statement=SQL(script, _dialect=self.dialect).as_script(),
|
|
245
|
-
data=[],
|
|
246
|
-
rows_affected=total_rows,
|
|
247
|
-
operation_type="SCRIPT",
|
|
248
|
-
metadata={"status_message": "SCRIPT EXECUTED"},
|
|
249
|
-
total_statements=executed_count,
|
|
250
|
-
successful_statements=executed_count,
|
|
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
|
|
251
220
|
)
|
|
252
221
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
|
258
241
|
|
|
259
|
-
|
|
242
|
+
async def commit(self) -> None:
|
|
243
|
+
"""Commit the current transaction."""
|
|
260
244
|
try:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
# Use async storage backend to read the file
|
|
266
|
-
file_path_str = str(file_path)
|
|
267
|
-
backend = self._get_storage_backend(file_path_str)
|
|
268
|
-
content = await backend.read_text_async(file_path_str, encoding="utf-8")
|
|
269
|
-
# Parse CSV content
|
|
270
|
-
import io
|
|
271
|
-
|
|
272
|
-
csv_file = io.StringIO(content)
|
|
273
|
-
reader = csv.reader(csv_file, **options)
|
|
274
|
-
header = next(reader) # Skip header
|
|
275
|
-
placeholders = ", ".join("?" for _ in header)
|
|
276
|
-
sql = f"INSERT INTO {table_name} VALUES ({placeholders})"
|
|
277
|
-
data_iter = list(reader)
|
|
278
|
-
await cursor.executemany(sql, data_iter)
|
|
279
|
-
rowcount = cursor.rowcount
|
|
280
|
-
await conn.commit()
|
|
281
|
-
return rowcount
|
|
282
|
-
finally:
|
|
283
|
-
await conn.close()
|
|
284
|
-
|
|
285
|
-
def _connection(self, connection: Optional[AiosqliteConnection] = None) -> AiosqliteConnection:
|
|
286
|
-
"""Get the connection to use for the operation."""
|
|
287
|
-
return connection or self.connection
|
|
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
|
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
from sqlspec.adapters.asyncmy.
|
|
2
|
-
from sqlspec.adapters.asyncmy.
|
|
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
|
+
)
|
|
3
9
|
|
|
4
|
-
__all__ = (
|
|
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",)
|