sqlspec 0.16.0__cp310-cp310-win_amd64.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.cp310-win_amd64.pyd +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 +1347 -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 +440 -0
- sqlspec/builder/_column.py +324 -0
- sqlspec/builder/_ddl.py +1383 -0
- sqlspec/builder/_ddl_utils.py +104 -0
- sqlspec/builder/_delete.py +77 -0
- sqlspec/builder/_insert.py +241 -0
- sqlspec/builder/_merge.py +56 -0
- sqlspec/builder/_parsing_utils.py +140 -0
- sqlspec/builder/_select.py +174 -0
- sqlspec/builder/_update.py +186 -0
- sqlspec/builder/mixins/__init__.py +55 -0
- sqlspec/builder/mixins/_cte_and_set_ops.py +195 -0
- sqlspec/builder/mixins/_delete_operations.py +36 -0
- sqlspec/builder/mixins/_insert_operations.py +152 -0
- sqlspec/builder/mixins/_join_operations.py +115 -0
- sqlspec/builder/mixins/_merge_operations.py +416 -0
- sqlspec/builder/mixins/_order_limit_operations.py +123 -0
- sqlspec/builder/mixins/_pivot_operations.py +144 -0
- sqlspec/builder/mixins/_select_operations.py +599 -0
- sqlspec/builder/mixins/_update_operations.py +164 -0
- sqlspec/builder/mixins/_where_clause.py +609 -0
- sqlspec/cli.py +247 -0
- sqlspec/config.py +395 -0
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.cp310-win_amd64.pyd +0 -0
- sqlspec/core/cache.py +873 -0
- sqlspec/core/compiler.cp310-win_amd64.pyd +0 -0
- sqlspec/core/compiler.py +396 -0
- sqlspec/core/filters.cp310-win_amd64.pyd +0 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.cp310-win_amd64.pyd +0 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.cp310-win_amd64.pyd +0 -0
- sqlspec/core/parameters.py +1209 -0
- sqlspec/core/result.cp310-win_amd64.pyd +0 -0
- sqlspec/core/result.py +664 -0
- sqlspec/core/splitter.cp310-win_amd64.pyd +0 -0
- sqlspec/core/splitter.py +819 -0
- sqlspec/core/statement.cp310-win_amd64.pyd +0 -0
- sqlspec/core/statement.py +666 -0
- sqlspec/driver/__init__.py +19 -0
- sqlspec/driver/_async.py +472 -0
- sqlspec/driver/_common.py +612 -0
- sqlspec/driver/_sync.py +473 -0
- sqlspec/driver/mixins/__init__.py +6 -0
- sqlspec/driver/mixins/_result_tools.py +164 -0
- sqlspec/driver/mixins/_sql_translator.py +36 -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.cp310-win_amd64.pyd +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 +400 -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.cp310-win_amd64.pyd +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.cp310-win_amd64.pyd +0 -0
- sqlspec/utils/sync_tools.py +237 -0
- sqlspec/utils/text.cp310-win_amd64.pyd +0 -0
- sqlspec/utils/text.py +96 -0
- sqlspec/utils/type_guards.cp310-win_amd64.pyd +0 -0
- sqlspec/utils/type_guards.py +1135 -0
- sqlspec-0.16.0.dist-info/METADATA +365 -0
- sqlspec-0.16.0.dist-info/RECORD +148 -0
- sqlspec-0.16.0.dist-info/WHEEL +4 -0
- sqlspec-0.16.0.dist-info/entry_points.txt +2 -0
- sqlspec-0.16.0.dist-info/licenses/LICENSE +21 -0
- sqlspec-0.16.0.dist-info/licenses/NOTICE +29 -0
sqlspec/exceptions.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from typing import Any, Optional, Union
|
|
4
|
+
|
|
5
|
+
__all__ = (
|
|
6
|
+
"FileNotFoundInStorageError",
|
|
7
|
+
"ImproperConfigurationError",
|
|
8
|
+
"IntegrityError",
|
|
9
|
+
"MissingDependencyError",
|
|
10
|
+
"MultipleResultsFoundError",
|
|
11
|
+
"NotFoundError",
|
|
12
|
+
"RepositoryError",
|
|
13
|
+
"SQLBuilderError",
|
|
14
|
+
"SQLConversionError",
|
|
15
|
+
"SQLFileNotFoundError",
|
|
16
|
+
"SQLFileParseError",
|
|
17
|
+
"SQLParsingError",
|
|
18
|
+
"SQLSpecError",
|
|
19
|
+
"SerializationError",
|
|
20
|
+
"StorageOperationFailedError",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SQLSpecError(Exception):
|
|
25
|
+
"""Base exception class for SQLSpec exceptions."""
|
|
26
|
+
|
|
27
|
+
detail: str
|
|
28
|
+
|
|
29
|
+
def __init__(self, *args: Any, detail: str = "") -> None:
|
|
30
|
+
"""Initialize SQLSpecError.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
*args: args are converted to :class:`str` before passing to :class:`Exception`
|
|
34
|
+
detail: detail of the exception.
|
|
35
|
+
"""
|
|
36
|
+
str_args = [str(arg) for arg in args if arg]
|
|
37
|
+
if not detail:
|
|
38
|
+
if str_args:
|
|
39
|
+
detail, *str_args = str_args
|
|
40
|
+
elif hasattr(self, "detail"):
|
|
41
|
+
detail = self.detail
|
|
42
|
+
self.detail = detail
|
|
43
|
+
super().__init__(*str_args)
|
|
44
|
+
|
|
45
|
+
def __repr__(self) -> str:
|
|
46
|
+
if self.detail:
|
|
47
|
+
return f"{self.__class__.__name__} - {self.detail}"
|
|
48
|
+
return self.__class__.__name__
|
|
49
|
+
|
|
50
|
+
def __str__(self) -> str:
|
|
51
|
+
return " ".join((*self.args, self.detail)).strip()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MissingDependencyError(SQLSpecError, ImportError):
|
|
55
|
+
"""Raised when a required dependency is not installed."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, package: str, install_package: Optional[str] = None) -> None:
|
|
58
|
+
super().__init__(
|
|
59
|
+
f"Package {package!r} is not installed but required. You can install it by running "
|
|
60
|
+
f"'pip install sqlspec[{install_package or package}]' to install sqlspec with the required extra "
|
|
61
|
+
f"or 'pip install {install_package or package}' to install the package separately"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BackendNotRegisteredError(SQLSpecError):
|
|
66
|
+
"""Raised when a requested storage backend key is not registered."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, backend_key: str) -> None:
|
|
69
|
+
super().__init__(f"Storage backend '{backend_key}' is not registered. Please register it before use.")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class SQLParsingError(SQLSpecError):
|
|
73
|
+
"""Issues parsing SQL statements."""
|
|
74
|
+
|
|
75
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
76
|
+
if message is None:
|
|
77
|
+
message = "Issues parsing SQL statement."
|
|
78
|
+
super().__init__(message)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class SQLBuilderError(SQLSpecError):
|
|
82
|
+
"""Issues Building or Generating SQL statements."""
|
|
83
|
+
|
|
84
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
85
|
+
if message is None:
|
|
86
|
+
message = "Issues building SQL statement."
|
|
87
|
+
super().__init__(message)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class SQLConversionError(SQLSpecError):
|
|
91
|
+
"""Issues converting SQL statements."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
94
|
+
if message is None:
|
|
95
|
+
message = "Issues converting SQL statement."
|
|
96
|
+
super().__init__(message)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ImproperConfigurationError(SQLSpecError):
|
|
100
|
+
"""Raised when configuration is invalid or incomplete."""
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class SerializationError(SQLSpecError):
|
|
104
|
+
"""Encoding or decoding of an object failed."""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class RepositoryError(SQLSpecError):
|
|
108
|
+
"""Base repository exception type."""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class IntegrityError(RepositoryError):
|
|
112
|
+
"""Data integrity error."""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class NotFoundError(RepositoryError):
|
|
116
|
+
"""An identity does not exist."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class MultipleResultsFoundError(RepositoryError):
|
|
120
|
+
"""A single database result was required but more than one were found."""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class StorageOperationFailedError(SQLSpecError):
|
|
124
|
+
"""Raised when a storage backend operation fails (e.g., network, permission, API error)."""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class FileNotFoundInStorageError(StorageOperationFailedError):
|
|
128
|
+
"""Raised when a file or object is not found in the storage backend."""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class SQLFileNotFoundError(SQLSpecError):
|
|
132
|
+
"""Raised when a SQL file cannot be found."""
|
|
133
|
+
|
|
134
|
+
def __init__(self, name: str, path: "Optional[str]" = None) -> None:
|
|
135
|
+
"""Initialize the error.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
name: Name of the SQL file.
|
|
139
|
+
path: Optional path where the file was expected.
|
|
140
|
+
"""
|
|
141
|
+
message = f"SQL file '{name}' not found at path: {path}" if path else f"SQL file '{name}' not found"
|
|
142
|
+
super().__init__(message)
|
|
143
|
+
self.name = name
|
|
144
|
+
self.path = path
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SQLFileParseError(SQLSpecError):
|
|
148
|
+
"""Raised when a SQL file cannot be parsed."""
|
|
149
|
+
|
|
150
|
+
def __init__(self, name: str, path: str, original_error: "Exception") -> None:
|
|
151
|
+
"""Initialize the error.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
name: Name of the SQL file.
|
|
155
|
+
path: Path to the SQL file.
|
|
156
|
+
original_error: The underlying parsing error.
|
|
157
|
+
"""
|
|
158
|
+
message = f"Failed to parse SQL file '{name}' at {path}: {original_error}"
|
|
159
|
+
super().__init__(message)
|
|
160
|
+
self.name = name
|
|
161
|
+
self.path = path
|
|
162
|
+
self.original_error = original_error
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@contextmanager
|
|
166
|
+
def wrap_exceptions(
|
|
167
|
+
wrap_exceptions: bool = True, suppress: "Optional[Union[type[Exception], tuple[type[Exception], ...]]]" = None
|
|
168
|
+
) -> Generator[None, None, None]:
|
|
169
|
+
"""Context manager for exception handling with optional suppression.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
wrap_exceptions: If True, wrap exceptions in RepositoryError. If False, let them pass through.
|
|
173
|
+
suppress: Exception type(s) to suppress completely (like contextlib.suppress).
|
|
174
|
+
If provided, these exceptions are caught and ignored.
|
|
175
|
+
"""
|
|
176
|
+
try:
|
|
177
|
+
yield
|
|
178
|
+
|
|
179
|
+
except Exception as exc:
|
|
180
|
+
if suppress is not None and (
|
|
181
|
+
(isinstance(suppress, type) and isinstance(exc, suppress))
|
|
182
|
+
or (isinstance(suppress, tuple) and isinstance(exc, suppress))
|
|
183
|
+
):
|
|
184
|
+
return # Suppress this exception
|
|
185
|
+
|
|
186
|
+
# If it's already a SQLSpec exception, don't wrap it
|
|
187
|
+
if isinstance(exc, SQLSpecError):
|
|
188
|
+
raise
|
|
189
|
+
|
|
190
|
+
if wrap_exceptions is False:
|
|
191
|
+
raise
|
|
192
|
+
msg = "An error occurred during the operation."
|
|
193
|
+
raise RepositoryError(detail=msg) from exc
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""SQLSpec aiosql integration for loading SQL files.
|
|
2
|
+
|
|
3
|
+
This module provides a simple way to load aiosql-style SQL files and use them
|
|
4
|
+
with SQLSpec drivers. It focuses on just the file parsing functionality,
|
|
5
|
+
returning SQL objects that work with existing SQLSpec execution.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from sqlspec.extensions.aiosql.adapter import AiosqlAsyncAdapter, AiosqlSyncAdapter
|
|
9
|
+
|
|
10
|
+
__all__ = ("AiosqlAsyncAdapter", "AiosqlSyncAdapter")
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"""AioSQL adapter implementation for SQLSpec.
|
|
2
|
+
|
|
3
|
+
This module provides adapter classes that implement the aiosql adapter protocols
|
|
4
|
+
while using SQLSpec drivers under the hood. This enables users to load SQL queries
|
|
5
|
+
from files using aiosql while leveraging all of SQLSpec's advanced features.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from collections.abc import AsyncGenerator, Generator
|
|
10
|
+
from contextlib import asynccontextmanager, contextmanager
|
|
11
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeVar, Union, cast
|
|
12
|
+
|
|
13
|
+
from sqlspec.core.result import SQLResult
|
|
14
|
+
from sqlspec.core.statement import SQL, StatementConfig
|
|
15
|
+
from sqlspec.exceptions import MissingDependencyError
|
|
16
|
+
from sqlspec.typing import AIOSQL_INSTALLED
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger("sqlspec.extensions.aiosql")
|
|
22
|
+
|
|
23
|
+
__all__ = ("AiosqlAsyncAdapter", "AiosqlSyncAdapter")
|
|
24
|
+
|
|
25
|
+
T = TypeVar("T")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AsyncCursorLike:
|
|
29
|
+
def __init__(self, result: Any) -> None:
|
|
30
|
+
self.result = result
|
|
31
|
+
|
|
32
|
+
async def fetchall(self) -> list[Any]:
|
|
33
|
+
if isinstance(self.result, SQLResult) and self.result.data is not None:
|
|
34
|
+
return list(self.result.data)
|
|
35
|
+
return []
|
|
36
|
+
|
|
37
|
+
async def fetchone(self) -> Optional[Any]:
|
|
38
|
+
rows = await self.fetchall()
|
|
39
|
+
return rows[0] if rows else None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class CursorLike:
|
|
43
|
+
def __init__(self, result: Any) -> None:
|
|
44
|
+
self.result = result
|
|
45
|
+
|
|
46
|
+
def fetchall(self) -> list[Any]:
|
|
47
|
+
if isinstance(self.result, SQLResult) and self.result.data is not None:
|
|
48
|
+
return list(self.result.data)
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
def fetchone(self) -> Optional[Any]:
|
|
52
|
+
rows = self.fetchall()
|
|
53
|
+
return rows[0] if rows else None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _check_aiosql_available() -> None:
|
|
57
|
+
if not AIOSQL_INSTALLED:
|
|
58
|
+
msg = "aiosql"
|
|
59
|
+
raise MissingDependencyError(msg, "aiosql")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _normalize_dialect(dialect: "Union[str, Any, None]") -> str:
|
|
63
|
+
"""Normalize dialect name for SQLGlot compatibility.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
dialect: Original dialect name (can be str, Dialect, type[Dialect], or None)
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Converted dialect name compatible with SQLGlot
|
|
70
|
+
"""
|
|
71
|
+
if dialect is None:
|
|
72
|
+
return "sql"
|
|
73
|
+
|
|
74
|
+
if hasattr(dialect, "__name__"):
|
|
75
|
+
dialect_str = str(dialect.__name__).lower() # pyright: ignore
|
|
76
|
+
elif hasattr(dialect, "name"):
|
|
77
|
+
dialect_str = str(dialect.name).lower() # pyright: ignore
|
|
78
|
+
elif isinstance(dialect, str):
|
|
79
|
+
dialect_str = dialect.lower()
|
|
80
|
+
else:
|
|
81
|
+
dialect_str = str(dialect).lower()
|
|
82
|
+
|
|
83
|
+
dialect_mapping = {
|
|
84
|
+
"postgresql": "postgres",
|
|
85
|
+
"psycopg": "postgres",
|
|
86
|
+
"asyncpg": "postgres",
|
|
87
|
+
"psqlpy": "postgres",
|
|
88
|
+
"sqlite3": "sqlite",
|
|
89
|
+
"aiosqlite": "sqlite",
|
|
90
|
+
}
|
|
91
|
+
return dialect_mapping.get(dialect_str, dialect_str)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class _AiosqlAdapterBase:
|
|
95
|
+
"""Base adapter class providing common functionality for aiosql integration."""
|
|
96
|
+
|
|
97
|
+
def __init__(self, driver: "Union[SyncDriverAdapterBase, AsyncDriverAdapterBase]") -> None:
|
|
98
|
+
"""Initialize the base adapter.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
driver: SQLSpec driver to use for execution.
|
|
102
|
+
"""
|
|
103
|
+
_check_aiosql_available()
|
|
104
|
+
self.driver = driver
|
|
105
|
+
|
|
106
|
+
def process_sql(self, query_name: str, op_type: "Any", sql: str) -> str:
|
|
107
|
+
"""Process SQL string for aiosql compatibility.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
query_name: Name of the query
|
|
111
|
+
op_type: Operation type
|
|
112
|
+
sql: SQL string to process
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Processed SQL string
|
|
116
|
+
"""
|
|
117
|
+
return sql
|
|
118
|
+
|
|
119
|
+
def _create_sql_object(self, sql: str, parameters: "Any" = None) -> SQL:
|
|
120
|
+
"""Create SQL object with proper configuration.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
sql: SQL string
|
|
124
|
+
parameters: Query parameters
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Configured SQL object
|
|
128
|
+
"""
|
|
129
|
+
return SQL(
|
|
130
|
+
sql,
|
|
131
|
+
parameters,
|
|
132
|
+
config=StatementConfig(enable_validation=False),
|
|
133
|
+
dialect=_normalize_dialect(getattr(self.driver, "dialect", "sqlite")),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class AiosqlSyncAdapter(_AiosqlAdapterBase):
|
|
138
|
+
"""Synchronous adapter that implements aiosql protocol using SQLSpec drivers.
|
|
139
|
+
|
|
140
|
+
This adapter bridges aiosql's synchronous driver protocol with SQLSpec's sync drivers,
|
|
141
|
+
enabling queries loaded by aiosql to be executed with SQLSpec drivers.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
is_aio_driver: ClassVar[bool] = False
|
|
145
|
+
|
|
146
|
+
def __init__(self, driver: "SyncDriverAdapterBase") -> None:
|
|
147
|
+
"""Initialize the sync adapter.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
driver: SQLSpec sync driver to use for execution
|
|
151
|
+
"""
|
|
152
|
+
super().__init__(driver)
|
|
153
|
+
|
|
154
|
+
def select(
|
|
155
|
+
self, conn: Any, query_name: str, sql: str, parameters: "Any", record_class: Optional[Any] = None
|
|
156
|
+
) -> Generator[Any, None, None]:
|
|
157
|
+
"""Execute a SELECT query and return results as generator.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
conn: Database connection (passed through to SQLSpec driver)
|
|
161
|
+
query_name: Name of the query
|
|
162
|
+
sql: SQL string
|
|
163
|
+
parameters: Query parameters
|
|
164
|
+
record_class: Deprecated - use schema_type in driver.execute instead
|
|
165
|
+
|
|
166
|
+
Yields:
|
|
167
|
+
Query result rows
|
|
168
|
+
|
|
169
|
+
Note:
|
|
170
|
+
The record_class parameter is ignored for compatibility. Use schema_type
|
|
171
|
+
in driver.execute or _sqlspec_schema_type in parameters for type mapping.
|
|
172
|
+
"""
|
|
173
|
+
if record_class is not None:
|
|
174
|
+
logger.warning(
|
|
175
|
+
"record_class parameter is deprecated and ignored. "
|
|
176
|
+
"Use schema_type in driver.execute or _sqlspec_schema_type in parameters."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
result = self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
180
|
+
|
|
181
|
+
if isinstance(result, SQLResult) and result.data is not None:
|
|
182
|
+
yield from result.data
|
|
183
|
+
|
|
184
|
+
def select_one(
|
|
185
|
+
self, conn: Any, query_name: str, sql: str, parameters: "Any", record_class: Optional[Any] = None
|
|
186
|
+
) -> Optional[dict[str, Any]]:
|
|
187
|
+
"""Execute a SELECT query and return first result.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
conn: Database connection
|
|
191
|
+
query_name: Name of the query
|
|
192
|
+
sql: SQL string
|
|
193
|
+
parameters: Query parameters
|
|
194
|
+
record_class: Deprecated - use schema_type in driver.execute instead
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
First result row or None
|
|
198
|
+
|
|
199
|
+
Note:
|
|
200
|
+
The record_class parameter is ignored for compatibility. Use schema_type
|
|
201
|
+
in driver.execute or _sqlspec_schema_type in parameters for type mapping.
|
|
202
|
+
"""
|
|
203
|
+
if record_class is not None:
|
|
204
|
+
logger.warning(
|
|
205
|
+
"record_class parameter is deprecated and ignored. "
|
|
206
|
+
"Use schema_type in driver.execute or _sqlspec_schema_type in parameters."
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
result = cast("SQLResult", self.driver.execute(self._create_sql_object(sql, parameters), connection=conn))
|
|
210
|
+
|
|
211
|
+
if hasattr(result, "data") and result.data and isinstance(result, SQLResult):
|
|
212
|
+
return cast("Optional[dict[str, Any]]", result.data[0])
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
def select_value(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> Optional[Any]:
|
|
216
|
+
"""Execute a SELECT query and return first value of first row.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
conn: Database connection
|
|
220
|
+
query_name: Name of the query
|
|
221
|
+
sql: SQL string
|
|
222
|
+
parameters: Query parameters
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
First value of first row or None
|
|
226
|
+
"""
|
|
227
|
+
row = self.select_one(conn, query_name, sql, parameters)
|
|
228
|
+
if row is None:
|
|
229
|
+
return None
|
|
230
|
+
|
|
231
|
+
if isinstance(row, dict):
|
|
232
|
+
return next(iter(row.values())) if row else None
|
|
233
|
+
if hasattr(row, "__getitem__"):
|
|
234
|
+
return row[0] if len(row) > 0 else None
|
|
235
|
+
return row
|
|
236
|
+
|
|
237
|
+
@contextmanager
|
|
238
|
+
def select_cursor(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> Generator[Any, None, None]:
|
|
239
|
+
"""Execute a SELECT query and return cursor context manager.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
conn: Database connection
|
|
243
|
+
query_name: Name of the query
|
|
244
|
+
sql: SQL string
|
|
245
|
+
parameters: Query parameters
|
|
246
|
+
|
|
247
|
+
Yields:
|
|
248
|
+
Cursor-like object with results
|
|
249
|
+
"""
|
|
250
|
+
result = self.driver.execute(self._create_sql_object(sql, parameters), connection=conn)
|
|
251
|
+
|
|
252
|
+
yield CursorLike(result)
|
|
253
|
+
|
|
254
|
+
def insert_update_delete(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> int:
|
|
255
|
+
"""Execute INSERT/UPDATE/DELETE and return affected rows.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
conn: Database connection
|
|
259
|
+
query_name: Name of the query
|
|
260
|
+
sql: SQL string
|
|
261
|
+
parameters: Query parameters
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Number of affected rows
|
|
265
|
+
"""
|
|
266
|
+
result = cast("SQLResult", self.driver.execute(self._create_sql_object(sql, parameters), connection=conn))
|
|
267
|
+
|
|
268
|
+
return result.rows_affected if hasattr(result, "rows_affected") else 0
|
|
269
|
+
|
|
270
|
+
def insert_update_delete_many(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> int:
|
|
271
|
+
"""Execute INSERT/UPDATE/DELETE with many parameter sets.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
conn: Database connection
|
|
275
|
+
query_name: Name of the query
|
|
276
|
+
sql: SQL string
|
|
277
|
+
parameters: Sequence of parameter sets
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Number of affected rows
|
|
281
|
+
"""
|
|
282
|
+
result = cast(
|
|
283
|
+
"SQLResult", self.driver.execute_many(self._create_sql_object(sql), parameters=parameters, connection=conn)
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
return result.rows_affected if hasattr(result, "rows_affected") else 0
|
|
287
|
+
|
|
288
|
+
def insert_returning(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> Optional[Any]:
|
|
289
|
+
"""Execute INSERT with RETURNING and return result.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
conn: Database connection
|
|
293
|
+
query_name: Name of the query
|
|
294
|
+
sql: SQL string
|
|
295
|
+
parameters: Query parameters
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
Returned value or None
|
|
299
|
+
"""
|
|
300
|
+
return self.select_one(conn, query_name, sql, parameters)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class AiosqlAsyncAdapter(_AiosqlAdapterBase):
|
|
304
|
+
"""Asynchronous adapter that implements aiosql protocol using SQLSpec drivers.
|
|
305
|
+
|
|
306
|
+
This adapter bridges aiosql's async driver protocol with SQLSpec's async drivers,
|
|
307
|
+
enabling queries loaded by aiosql to be executed with SQLSpec async drivers.
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
is_aio_driver: ClassVar[bool] = True
|
|
311
|
+
|
|
312
|
+
def __init__(self, driver: "AsyncDriverAdapterBase") -> None:
|
|
313
|
+
"""Initialize the async adapter.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
driver: SQLSpec async driver to use for execution
|
|
317
|
+
"""
|
|
318
|
+
super().__init__(driver)
|
|
319
|
+
|
|
320
|
+
async def select(
|
|
321
|
+
self, conn: Any, query_name: str, sql: str, parameters: "Any", record_class: Optional[Any] = None
|
|
322
|
+
) -> list[Any]:
|
|
323
|
+
"""Execute a SELECT query and return results as list.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
conn: Database connection
|
|
327
|
+
query_name: Name of the query
|
|
328
|
+
sql: SQL string
|
|
329
|
+
parameters: Query parameters
|
|
330
|
+
record_class: Deprecated - use schema_type in driver.execute instead
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
List of query result rows
|
|
334
|
+
|
|
335
|
+
Note:
|
|
336
|
+
The record_class parameter is ignored for compatibility. Use schema_type
|
|
337
|
+
in driver.execute or _sqlspec_schema_type in parameters for type mapping.
|
|
338
|
+
"""
|
|
339
|
+
if record_class is not None:
|
|
340
|
+
logger.warning(
|
|
341
|
+
"record_class parameter is deprecated and ignored. "
|
|
342
|
+
"Use schema_type in driver.execute or _sqlspec_schema_type in parameters."
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn) # type: ignore[misc]
|
|
346
|
+
|
|
347
|
+
if hasattr(result, "data") and result.data is not None and isinstance(result, SQLResult):
|
|
348
|
+
return list(result.data)
|
|
349
|
+
return []
|
|
350
|
+
|
|
351
|
+
async def select_one(
|
|
352
|
+
self, conn: Any, query_name: str, sql: str, parameters: "Any", record_class: Optional[Any] = None
|
|
353
|
+
) -> Optional[Any]:
|
|
354
|
+
"""Execute a SELECT query and return first result.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
conn: Database connection
|
|
358
|
+
query_name: Name of the query
|
|
359
|
+
sql: SQL string
|
|
360
|
+
parameters: Query parameters
|
|
361
|
+
record_class: Deprecated - use schema_type in driver.execute instead
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
First result row or None
|
|
365
|
+
|
|
366
|
+
Note:
|
|
367
|
+
The record_class parameter is ignored for compatibility. Use schema_type
|
|
368
|
+
in driver.execute or _sqlspec_schema_type in parameters for type mapping.
|
|
369
|
+
"""
|
|
370
|
+
if record_class is not None:
|
|
371
|
+
logger.warning(
|
|
372
|
+
"record_class parameter is deprecated and ignored. "
|
|
373
|
+
"Use schema_type in driver.execute or _sqlspec_schema_type in parameters."
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn) # type: ignore[misc]
|
|
377
|
+
|
|
378
|
+
if hasattr(result, "data") and result.data and isinstance(result, SQLResult):
|
|
379
|
+
return result.data[0]
|
|
380
|
+
return None
|
|
381
|
+
|
|
382
|
+
async def select_value(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> Optional[Any]:
|
|
383
|
+
"""Execute a SELECT query and return first value of first row.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
conn: Database connection
|
|
387
|
+
query_name: Name of the query
|
|
388
|
+
sql: SQL string
|
|
389
|
+
parameters: Query parameters
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
First value of first row or None
|
|
393
|
+
"""
|
|
394
|
+
row = await self.select_one(conn, query_name, sql, parameters)
|
|
395
|
+
if row is None:
|
|
396
|
+
return None
|
|
397
|
+
|
|
398
|
+
if isinstance(row, dict):
|
|
399
|
+
return next(iter(row.values())) if row else None
|
|
400
|
+
if hasattr(row, "__getitem__"):
|
|
401
|
+
return row[0] if len(row) > 0 else None
|
|
402
|
+
return row
|
|
403
|
+
|
|
404
|
+
@asynccontextmanager
|
|
405
|
+
async def select_cursor(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> AsyncGenerator[Any, None]:
|
|
406
|
+
"""Execute a SELECT query and return cursor context manager.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
conn: Database connection
|
|
410
|
+
query_name: Name of the query
|
|
411
|
+
sql: SQL string
|
|
412
|
+
parameters: Query parameters
|
|
413
|
+
|
|
414
|
+
Yields:
|
|
415
|
+
Cursor-like object with results
|
|
416
|
+
"""
|
|
417
|
+
result = await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn) # type: ignore[misc]
|
|
418
|
+
|
|
419
|
+
yield AsyncCursorLike(result)
|
|
420
|
+
|
|
421
|
+
async def insert_update_delete(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> None:
|
|
422
|
+
"""Execute INSERT/UPDATE/DELETE.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
conn: Database connection
|
|
426
|
+
query_name: Name of the query
|
|
427
|
+
sql: SQL string
|
|
428
|
+
parameters: Query parameters
|
|
429
|
+
|
|
430
|
+
Note:
|
|
431
|
+
Returns None per aiosql async protocol
|
|
432
|
+
"""
|
|
433
|
+
await self.driver.execute(self._create_sql_object(sql, parameters), connection=conn) # type: ignore[misc]
|
|
434
|
+
|
|
435
|
+
async def insert_update_delete_many(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> None:
|
|
436
|
+
"""Execute INSERT/UPDATE/DELETE with many parameter sets.
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
conn: Database connection
|
|
440
|
+
query_name: Name of the query
|
|
441
|
+
sql: SQL string
|
|
442
|
+
parameters: Sequence of parameter sets
|
|
443
|
+
|
|
444
|
+
Note:
|
|
445
|
+
Returns None per aiosql async protocol
|
|
446
|
+
"""
|
|
447
|
+
await self.driver.execute_many(self._create_sql_object(sql), parameters=parameters, connection=conn) # type: ignore[misc]
|
|
448
|
+
|
|
449
|
+
async def insert_returning(self, conn: Any, query_name: str, sql: str, parameters: "Any") -> Optional[Any]:
|
|
450
|
+
"""Execute INSERT with RETURNING and return result.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
conn: Database connection
|
|
454
|
+
query_name: Name of the query
|
|
455
|
+
sql: SQL string
|
|
456
|
+
parameters: Query parameters
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
Returned value or None
|
|
460
|
+
"""
|
|
461
|
+
return await self.select_one(conn, query_name, sql, parameters)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from sqlspec.extensions.litestar import handlers, providers
|
|
2
|
+
from sqlspec.extensions.litestar.cli import database_group
|
|
3
|
+
from sqlspec.extensions.litestar.config import DatabaseConfig
|
|
4
|
+
from sqlspec.extensions.litestar.plugin import SQLSpec
|
|
5
|
+
|
|
6
|
+
__all__ = ("DatabaseConfig", "SQLSpec", "database_group", "handlers", "providers")
|