sqlspec 0.26.0__py3-none-any.whl → 0.28.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 +7 -15
- sqlspec/_serialization.py +55 -25
- sqlspec/_typing.py +155 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +880 -0
- sqlspec/adapters/adbc/config.py +62 -12
- sqlspec/adapters/adbc/data_dictionary.py +74 -2
- sqlspec/adapters/adbc/driver.py +226 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +44 -50
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +536 -0
- sqlspec/adapters/aiosqlite/config.py +86 -16
- sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
- sqlspec/adapters/aiosqlite/driver.py +127 -38
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +7 -7
- sqlspec/adapters/asyncmy/__init__.py +7 -1
- sqlspec/adapters/asyncmy/_types.py +1 -1
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +503 -0
- sqlspec/adapters/asyncmy/config.py +59 -17
- sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
- sqlspec/adapters/asyncmy/driver.py +293 -62
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +2 -1
- sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
- sqlspec/adapters/asyncpg/_types.py +11 -7
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +460 -0
- sqlspec/adapters/asyncpg/config.py +57 -36
- sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
- sqlspec/adapters/asyncpg/driver.py +153 -23
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/_types.py +1 -1
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +585 -0
- sqlspec/adapters/bigquery/config.py +36 -11
- sqlspec/adapters/bigquery/data_dictionary.py +42 -2
- sqlspec/adapters/bigquery/driver.py +489 -144
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +55 -23
- sqlspec/adapters/duckdb/_types.py +2 -2
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +563 -0
- sqlspec/adapters/duckdb/config.py +79 -21
- sqlspec/adapters/duckdb/data_dictionary.py +41 -2
- sqlspec/adapters/duckdb/driver.py +225 -44
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +5 -5
- sqlspec/adapters/duckdb/type_converter.py +51 -21
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +20 -2
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1628 -0
- sqlspec/adapters/oracledb/config.py +120 -36
- sqlspec/adapters/oracledb/data_dictionary.py +87 -20
- sqlspec/adapters/oracledb/driver.py +475 -86
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +765 -0
- sqlspec/adapters/oracledb/migrations.py +316 -25
- sqlspec/adapters/oracledb/type_converter.py +91 -16
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +2 -1
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +483 -0
- sqlspec/adapters/psqlpy/config.py +45 -19
- sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
- sqlspec/adapters/psqlpy/driver.py +108 -41
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +40 -11
- sqlspec/adapters/psycopg/_type_handlers.py +80 -0
- sqlspec/adapters/psycopg/_types.py +2 -1
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +962 -0
- sqlspec/adapters/psycopg/config.py +65 -37
- sqlspec/adapters/psycopg/data_dictionary.py +91 -3
- sqlspec/adapters/psycopg/driver.py +200 -78
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/sqlite/__init__.py +2 -1
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +1 -1
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +582 -0
- sqlspec/adapters/sqlite/config.py +85 -16
- sqlspec/adapters/sqlite/data_dictionary.py +34 -2
- sqlspec/adapters/sqlite/driver.py +120 -52
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +5 -5
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +91 -58
- sqlspec/builder/_column.py +5 -5
- sqlspec/builder/_ddl.py +98 -89
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +41 -44
- sqlspec/builder/_insert.py +5 -82
- sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +9 -11
- sqlspec/builder/_select.py +1313 -25
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +76 -69
- sqlspec/config.py +331 -62
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +18 -18
- sqlspec/core/compiler.py +6 -8
- sqlspec/core/filters.py +55 -47
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +76 -45
- sqlspec/core/result.py +234 -47
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +32 -31
- sqlspec/core/type_conversion.py +3 -2
- sqlspec/driver/__init__.py +1 -3
- sqlspec/driver/_async.py +183 -160
- sqlspec/driver/_common.py +197 -109
- sqlspec/driver/_sync.py +189 -161
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +70 -7
- sqlspec/extensions/adk/__init__.py +53 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +181 -0
- sqlspec/extensions/adk/store.py +536 -0
- sqlspec/extensions/aiosql/adapter.py +69 -61
- sqlspec/extensions/fastapi/__init__.py +21 -0
- sqlspec/extensions/fastapi/extension.py +331 -0
- sqlspec/extensions/fastapi/providers.py +543 -0
- sqlspec/extensions/flask/__init__.py +36 -0
- sqlspec/extensions/flask/_state.py +71 -0
- sqlspec/extensions/flask/_utils.py +40 -0
- sqlspec/extensions/flask/extension.py +389 -0
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +56 -266
- sqlspec/extensions/litestar/handlers.py +46 -17
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +349 -224
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/extensions/starlette/__init__.py +10 -0
- sqlspec/extensions/starlette/_state.py +25 -0
- sqlspec/extensions/starlette/_utils.py +52 -0
- sqlspec/extensions/starlette/extension.py +254 -0
- sqlspec/extensions/starlette/middleware.py +154 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/base.py +200 -76
- sqlspec/migrations/commands.py +591 -62
- sqlspec/migrations/context.py +6 -9
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +47 -19
- sqlspec/migrations/runner.py +241 -75
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +106 -36
- sqlspec/storage/_utils.py +85 -0
- sqlspec/storage/backends/fsspec.py +133 -107
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +276 -168
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +30 -84
- sqlspec/utils/__init__.py +25 -4
- sqlspec/utils/arrow_helpers.py +81 -0
- sqlspec/utils/config_resolver.py +6 -6
- sqlspec/utils/correlation.py +4 -5
- sqlspec/utils/data_transformation.py +3 -2
- sqlspec/utils/deprecation.py +9 -8
- sqlspec/utils/fixtures.py +4 -4
- sqlspec/utils/logging.py +46 -6
- sqlspec/utils/module_loader.py +205 -5
- sqlspec/utils/portal.py +311 -0
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +113 -4
- sqlspec/utils/sync_tools.py +36 -22
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +136 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
- sqlspec-0.28.0.dist-info/RECORD +221 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_merge_operations.py +0 -698
- sqlspec/builder/mixins/_order_limit_operations.py +0 -145
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -930
- sqlspec/builder/mixins/_update_operations.py +0 -199
- sqlspec/builder/mixins/_where_clause.py +0 -1298
- sqlspec-0.26.0.dist-info/RECORD +0 -157
- sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,60 +1,221 @@
|
|
|
1
|
-
from
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Literal, NoReturn, cast, overload
|
|
2
3
|
|
|
3
4
|
from litestar.di import Provide
|
|
4
5
|
from litestar.plugins import CLIPlugin, InitPluginProtocol
|
|
5
6
|
|
|
6
|
-
from sqlspec.base import SQLSpec
|
|
7
|
-
from sqlspec.config import
|
|
7
|
+
from sqlspec.base import SQLSpec
|
|
8
|
+
from sqlspec.config import (
|
|
9
|
+
AsyncConfigT,
|
|
10
|
+
AsyncDatabaseConfig,
|
|
11
|
+
DatabaseConfigProtocol,
|
|
12
|
+
DriverT,
|
|
13
|
+
NoPoolAsyncConfig,
|
|
14
|
+
NoPoolSyncConfig,
|
|
15
|
+
SyncConfigT,
|
|
16
|
+
SyncDatabaseConfig,
|
|
17
|
+
)
|
|
8
18
|
from sqlspec.exceptions import ImproperConfigurationError
|
|
9
|
-
from sqlspec.extensions.litestar.
|
|
10
|
-
from sqlspec.
|
|
19
|
+
from sqlspec.extensions.litestar._utils import get_sqlspec_scope_state, set_sqlspec_scope_state
|
|
20
|
+
from sqlspec.extensions.litestar.handlers import (
|
|
21
|
+
autocommit_handler_maker,
|
|
22
|
+
connection_provider_maker,
|
|
23
|
+
lifespan_handler_maker,
|
|
24
|
+
manual_handler_maker,
|
|
25
|
+
pool_provider_maker,
|
|
26
|
+
session_provider_maker,
|
|
27
|
+
)
|
|
28
|
+
from sqlspec.typing import NUMPY_INSTALLED, ConnectionT, PoolT, SchemaT
|
|
11
29
|
from sqlspec.utils.logging import get_logger
|
|
30
|
+
from sqlspec.utils.serializers import numpy_array_dec_hook, numpy_array_enc_hook, numpy_array_predicate
|
|
12
31
|
|
|
13
32
|
if TYPE_CHECKING:
|
|
14
|
-
from
|
|
33
|
+
from collections.abc import AsyncGenerator, Callable
|
|
34
|
+
from contextlib import AbstractAsyncContextManager
|
|
35
|
+
|
|
36
|
+
from litestar import Litestar
|
|
15
37
|
from litestar.config.app import AppConfig
|
|
16
38
|
from litestar.datastructures.state import State
|
|
17
|
-
from litestar.types import Scope
|
|
39
|
+
from litestar.types import BeforeMessageSendHookHandler, Scope
|
|
40
|
+
from rich_click import Group
|
|
18
41
|
|
|
19
42
|
from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
|
|
20
43
|
from sqlspec.loader import SQLFileLoader
|
|
21
44
|
|
|
22
45
|
logger = get_logger("extensions.litestar")
|
|
23
46
|
|
|
47
|
+
CommitMode = Literal["manual", "autocommit", "autocommit_include_redirect"]
|
|
48
|
+
DEFAULT_COMMIT_MODE: CommitMode = "manual"
|
|
49
|
+
DEFAULT_CONNECTION_KEY = "db_connection"
|
|
50
|
+
DEFAULT_POOL_KEY = "db_pool"
|
|
51
|
+
DEFAULT_SESSION_KEY = "db_session"
|
|
52
|
+
|
|
53
|
+
__all__ = (
|
|
54
|
+
"DEFAULT_COMMIT_MODE",
|
|
55
|
+
"DEFAULT_CONNECTION_KEY",
|
|
56
|
+
"DEFAULT_POOL_KEY",
|
|
57
|
+
"DEFAULT_SESSION_KEY",
|
|
58
|
+
"CommitMode",
|
|
59
|
+
"SQLSpecPlugin",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class _PluginConfigState:
|
|
65
|
+
"""Internal state for each database configuration."""
|
|
66
|
+
|
|
67
|
+
config: "DatabaseConfigProtocol[Any, Any, Any]"
|
|
68
|
+
connection_key: str
|
|
69
|
+
pool_key: str
|
|
70
|
+
session_key: str
|
|
71
|
+
commit_mode: CommitMode
|
|
72
|
+
extra_commit_statuses: "set[int] | None"
|
|
73
|
+
extra_rollback_statuses: "set[int] | None"
|
|
74
|
+
enable_correlation_middleware: bool
|
|
75
|
+
connection_provider: "Callable[[State, Scope], AsyncGenerator[Any, None]]" = field(init=False)
|
|
76
|
+
pool_provider: "Callable[[State, Scope], Any]" = field(init=False)
|
|
77
|
+
session_provider: "Callable[..., AsyncGenerator[Any, None]]" = field(init=False)
|
|
78
|
+
before_send_handler: "BeforeMessageSendHookHandler" = field(init=False)
|
|
79
|
+
lifespan_handler: "Callable[[Litestar], AbstractAsyncContextManager[None]]" = field(init=False)
|
|
80
|
+
annotation: "type[DatabaseConfigProtocol[Any, Any, Any]]" = field(init=False)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SQLSpecPlugin(InitPluginProtocol, CLIPlugin):
|
|
84
|
+
"""Litestar plugin for SQLSpec database integration.
|
|
85
|
+
|
|
86
|
+
Automatically configures NumPy array serialization when NumPy is installed,
|
|
87
|
+
enabling seamless bidirectional conversion between NumPy arrays and JSON
|
|
88
|
+
for vector embedding workflows.
|
|
89
|
+
|
|
90
|
+
Session Table Migrations:
|
|
91
|
+
The Litestar extension includes migrations for creating session storage tables.
|
|
92
|
+
To include these migrations in your database migration workflow, add 'litestar'
|
|
93
|
+
to the include_extensions list in your migration configuration.
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
config = AsyncpgConfig(
|
|
97
|
+
pool_config={"dsn": "postgresql://localhost/db"},
|
|
98
|
+
extension_config={
|
|
99
|
+
"litestar": {
|
|
100
|
+
"session_table": "custom_sessions" # Optional custom table name
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
migration_config={
|
|
104
|
+
"script_location": "migrations",
|
|
105
|
+
"include_extensions": ["litestar"], # Simple string list only
|
|
106
|
+
}
|
|
107
|
+
)
|
|
24
108
|
|
|
25
|
-
|
|
26
|
-
|
|
109
|
+
The session table migration will automatically use the appropriate column types
|
|
110
|
+
for your database dialect (JSONB for PostgreSQL, JSON for MySQL, TEXT for SQLite).
|
|
27
111
|
|
|
28
|
-
|
|
112
|
+
Extension migrations use the ext_litestar_ prefix (e.g., ext_litestar_0001) to
|
|
113
|
+
prevent version conflicts with application migrations.
|
|
114
|
+
"""
|
|
29
115
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*,
|
|
34
|
-
loader: "Optional[SQLFileLoader]" = None,
|
|
35
|
-
) -> None:
|
|
116
|
+
__slots__ = ("_plugin_configs", "_sqlspec")
|
|
117
|
+
|
|
118
|
+
def __init__(self, sqlspec: SQLSpec, *, loader: "SQLFileLoader | None" = None) -> None:
|
|
36
119
|
"""Initialize SQLSpec plugin.
|
|
37
120
|
|
|
38
121
|
Args:
|
|
39
|
-
|
|
40
|
-
loader: Optional SQL file loader instance.
|
|
122
|
+
sqlspec: Pre-configured SQLSpec instance with registered database configs.
|
|
123
|
+
loader: Optional SQL file loader instance (SQLSpec may already have one).
|
|
41
124
|
"""
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
125
|
+
self._sqlspec = sqlspec
|
|
126
|
+
|
|
127
|
+
self._plugin_configs: list[_PluginConfigState] = []
|
|
128
|
+
for cfg in self._sqlspec.configs.values():
|
|
129
|
+
config_union = cast(
|
|
130
|
+
"SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]",
|
|
131
|
+
cfg,
|
|
132
|
+
)
|
|
133
|
+
settings = self._extract_litestar_settings(config_union)
|
|
134
|
+
state = self._create_config_state(config_union, settings)
|
|
135
|
+
self._plugin_configs.append(state)
|
|
136
|
+
|
|
137
|
+
def _extract_litestar_settings(
|
|
138
|
+
self,
|
|
139
|
+
config: "SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]",
|
|
140
|
+
) -> "dict[str, Any]":
|
|
141
|
+
"""Extract Litestar settings from config.extension_config."""
|
|
142
|
+
litestar_config = config.extension_config.get("litestar", {})
|
|
143
|
+
|
|
144
|
+
connection_key = litestar_config.get("connection_key", DEFAULT_CONNECTION_KEY)
|
|
145
|
+
pool_key = litestar_config.get("pool_key", DEFAULT_POOL_KEY)
|
|
146
|
+
session_key = litestar_config.get("session_key", DEFAULT_SESSION_KEY)
|
|
147
|
+
commit_mode = litestar_config.get("commit_mode", DEFAULT_COMMIT_MODE)
|
|
148
|
+
|
|
149
|
+
if not config.supports_connection_pooling and pool_key == DEFAULT_POOL_KEY:
|
|
150
|
+
pool_key = f"_{DEFAULT_POOL_KEY}_{id(config)}"
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
"connection_key": connection_key,
|
|
154
|
+
"pool_key": pool_key,
|
|
155
|
+
"session_key": session_key,
|
|
156
|
+
"commit_mode": commit_mode,
|
|
157
|
+
"extra_commit_statuses": litestar_config.get("extra_commit_statuses"),
|
|
158
|
+
"extra_rollback_statuses": litestar_config.get("extra_rollback_statuses"),
|
|
159
|
+
"enable_correlation_middleware": litestar_config.get("enable_correlation_middleware", True),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
def _create_config_state(
|
|
163
|
+
self,
|
|
164
|
+
config: "SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]",
|
|
165
|
+
settings: "dict[str, Any]",
|
|
166
|
+
) -> _PluginConfigState:
|
|
167
|
+
"""Create plugin state with handlers for the given configuration."""
|
|
168
|
+
state = _PluginConfigState(
|
|
169
|
+
config=config,
|
|
170
|
+
connection_key=settings["connection_key"],
|
|
171
|
+
pool_key=settings["pool_key"],
|
|
172
|
+
session_key=settings["session_key"],
|
|
173
|
+
commit_mode=settings["commit_mode"],
|
|
174
|
+
extra_commit_statuses=settings.get("extra_commit_statuses"),
|
|
175
|
+
extra_rollback_statuses=settings.get("extra_rollback_statuses"),
|
|
176
|
+
enable_correlation_middleware=settings["enable_correlation_middleware"],
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
self._setup_handlers(state)
|
|
180
|
+
return state
|
|
181
|
+
|
|
182
|
+
def _setup_handlers(self, state: _PluginConfigState) -> None:
|
|
183
|
+
"""Setup handlers for the plugin state."""
|
|
184
|
+
connection_key = state.connection_key
|
|
185
|
+
pool_key = state.pool_key
|
|
186
|
+
commit_mode = state.commit_mode
|
|
187
|
+
config = state.config
|
|
188
|
+
is_async = config.is_async
|
|
189
|
+
|
|
190
|
+
state.connection_provider = connection_provider_maker(config, pool_key, connection_key)
|
|
191
|
+
state.pool_provider = pool_provider_maker(config, pool_key)
|
|
192
|
+
state.session_provider = session_provider_maker(config, connection_key)
|
|
193
|
+
state.lifespan_handler = lifespan_handler_maker(config, pool_key)
|
|
194
|
+
|
|
195
|
+
if commit_mode == "manual":
|
|
196
|
+
state.before_send_handler = manual_handler_maker(connection_key, is_async)
|
|
47
197
|
else:
|
|
48
|
-
|
|
198
|
+
commit_on_redirect = commit_mode == "autocommit_include_redirect"
|
|
199
|
+
state.before_send_handler = autocommit_handler_maker(
|
|
200
|
+
connection_key, is_async, commit_on_redirect, state.extra_commit_statuses, state.extra_rollback_statuses
|
|
201
|
+
)
|
|
49
202
|
|
|
50
203
|
@property
|
|
51
|
-
def config(
|
|
52
|
-
|
|
204
|
+
def config(
|
|
205
|
+
self,
|
|
206
|
+
) -> "list[SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]]":
|
|
207
|
+
"""Return the plugin configurations.
|
|
53
208
|
|
|
54
209
|
Returns:
|
|
55
210
|
List of database configurations.
|
|
56
211
|
"""
|
|
57
|
-
return
|
|
212
|
+
return [
|
|
213
|
+
cast(
|
|
214
|
+
"SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]",
|
|
215
|
+
state.config,
|
|
216
|
+
)
|
|
217
|
+
for state in self._plugin_configs
|
|
218
|
+
]
|
|
58
219
|
|
|
59
220
|
def on_cli_init(self, cli: "Group") -> None:
|
|
60
221
|
"""Configure CLI commands for SQLSpec database operations.
|
|
@@ -69,63 +230,88 @@ class SQLSpec(SQLSpecBase, InitPluginProtocol, CLIPlugin):
|
|
|
69
230
|
def on_app_init(self, app_config: "AppConfig") -> "AppConfig":
|
|
70
231
|
"""Configure Litestar application with SQLSpec database integration.
|
|
71
232
|
|
|
233
|
+
Automatically registers NumPy array serialization when NumPy is installed.
|
|
234
|
+
|
|
72
235
|
Args:
|
|
73
236
|
app_config: The Litestar application configuration instance.
|
|
74
237
|
|
|
75
238
|
Returns:
|
|
76
239
|
The updated application configuration instance.
|
|
77
240
|
"""
|
|
78
|
-
|
|
79
241
|
self._validate_dependency_keys()
|
|
80
242
|
|
|
81
243
|
def store_sqlspec_in_state() -> None:
|
|
82
244
|
app_config.state.sqlspec = self
|
|
83
245
|
|
|
84
246
|
app_config.on_startup.append(store_sqlspec_in_state)
|
|
85
|
-
app_config.signature_types.extend(
|
|
86
|
-
[SQLSpec, ConnectionT, PoolT, DriverT, DatabaseConfig, DatabaseConfigProtocol, SyncConfigT, AsyncConfigT]
|
|
87
|
-
)
|
|
247
|
+
app_config.signature_types.extend([SQLSpec, DatabaseConfigProtocol, SyncConfigT, AsyncConfigT])
|
|
88
248
|
|
|
89
|
-
signature_namespace = {}
|
|
249
|
+
signature_namespace = {"ConnectionT": ConnectionT, "PoolT": PoolT, "DriverT": DriverT, "SchemaT": SchemaT}
|
|
90
250
|
|
|
91
|
-
for
|
|
92
|
-
|
|
93
|
-
app_config.signature_types.append(
|
|
94
|
-
app_config.signature_types.append(
|
|
95
|
-
app_config.signature_types.append(
|
|
251
|
+
for state in self._plugin_configs:
|
|
252
|
+
state.annotation = type(state.config)
|
|
253
|
+
app_config.signature_types.append(state.annotation)
|
|
254
|
+
app_config.signature_types.append(state.config.connection_type)
|
|
255
|
+
app_config.signature_types.append(state.config.driver_type)
|
|
96
256
|
|
|
97
|
-
signature_namespace.update(
|
|
257
|
+
signature_namespace.update(state.config.get_signature_namespace()) # type: ignore[arg-type]
|
|
98
258
|
|
|
99
|
-
app_config.before_send.append(
|
|
100
|
-
app_config.lifespan.append(
|
|
259
|
+
app_config.before_send.append(state.before_send_handler)
|
|
260
|
+
app_config.lifespan.append(state.lifespan_handler)
|
|
101
261
|
app_config.dependencies.update(
|
|
102
262
|
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
263
|
+
state.connection_key: Provide(state.connection_provider),
|
|
264
|
+
state.pool_key: Provide(state.pool_provider),
|
|
265
|
+
state.session_key: Provide(state.session_provider),
|
|
106
266
|
}
|
|
107
267
|
)
|
|
108
268
|
|
|
109
269
|
if signature_namespace:
|
|
110
270
|
app_config.signature_namespace.update(signature_namespace)
|
|
111
271
|
|
|
272
|
+
if NUMPY_INSTALLED:
|
|
273
|
+
import numpy as np
|
|
274
|
+
|
|
275
|
+
if app_config.type_encoders is None:
|
|
276
|
+
app_config.type_encoders = {np.ndarray: numpy_array_enc_hook}
|
|
277
|
+
else:
|
|
278
|
+
encoders_dict = dict(app_config.type_encoders)
|
|
279
|
+
encoders_dict[np.ndarray] = numpy_array_enc_hook
|
|
280
|
+
app_config.type_encoders = encoders_dict
|
|
281
|
+
|
|
282
|
+
if app_config.type_decoders is None:
|
|
283
|
+
app_config.type_decoders = [(numpy_array_predicate, numpy_array_dec_hook)] # type: ignore[list-item]
|
|
284
|
+
else:
|
|
285
|
+
decoders_list = list(app_config.type_decoders)
|
|
286
|
+
decoders_list.append((numpy_array_predicate, numpy_array_dec_hook)) # type: ignore[arg-type]
|
|
287
|
+
app_config.type_decoders = decoders_list
|
|
288
|
+
|
|
112
289
|
return app_config
|
|
113
290
|
|
|
114
|
-
def get_annotations(
|
|
291
|
+
def get_annotations(
|
|
292
|
+
self,
|
|
293
|
+
) -> "list[type[SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]]]":
|
|
115
294
|
"""Return the list of annotations.
|
|
116
295
|
|
|
117
296
|
Returns:
|
|
118
297
|
List of annotations.
|
|
119
298
|
"""
|
|
120
|
-
return [
|
|
299
|
+
return [
|
|
300
|
+
cast(
|
|
301
|
+
"type[SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]]",
|
|
302
|
+
state.annotation,
|
|
303
|
+
)
|
|
304
|
+
for state in self._plugin_configs
|
|
305
|
+
]
|
|
121
306
|
|
|
122
307
|
def get_annotation(
|
|
123
|
-
self,
|
|
124
|
-
|
|
308
|
+
self,
|
|
309
|
+
key: "str | SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any] | type[SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]]",
|
|
310
|
+
) -> "type[SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]]":
|
|
125
311
|
"""Return the annotation for the given configuration.
|
|
126
312
|
|
|
127
313
|
Args:
|
|
128
|
-
key: The configuration instance or key to lookup
|
|
314
|
+
key: The configuration instance or key to lookup.
|
|
129
315
|
|
|
130
316
|
Raises:
|
|
131
317
|
KeyError: If no configuration is found for the given key.
|
|
@@ -133,51 +319,38 @@ class SQLSpec(SQLSpecBase, InitPluginProtocol, CLIPlugin):
|
|
|
133
319
|
Returns:
|
|
134
320
|
The annotation for the configuration.
|
|
135
321
|
"""
|
|
136
|
-
for
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
raise AttributeError(msg)
|
|
145
|
-
return c.annotation
|
|
322
|
+
for state in self._plugin_configs:
|
|
323
|
+
if key in {state.config, state.annotation} or key in {state.connection_key, state.pool_key}:
|
|
324
|
+
return cast(
|
|
325
|
+
"type[SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]]",
|
|
326
|
+
state.annotation,
|
|
327
|
+
)
|
|
328
|
+
|
|
146
329
|
msg = f"No configuration found for {key}"
|
|
147
330
|
raise KeyError(msg)
|
|
148
331
|
|
|
149
332
|
@overload
|
|
150
|
-
def get_config(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def get_config(self, name: "type[AsyncConfigT]") -> "AsyncConfigT": ...
|
|
154
|
-
|
|
155
|
-
@overload
|
|
156
|
-
def get_config(self, name: str) -> "DatabaseConfig": ...
|
|
333
|
+
def get_config(
|
|
334
|
+
self, name: "type[SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any]]"
|
|
335
|
+
) -> "SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any]": ...
|
|
157
336
|
|
|
158
337
|
@overload
|
|
159
|
-
def get_config(
|
|
338
|
+
def get_config(
|
|
339
|
+
self, name: "type[AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]]"
|
|
340
|
+
) -> "AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]": ...
|
|
160
341
|
|
|
161
342
|
@overload
|
|
162
|
-
def get_config(self, name: "type[AsyncDatabaseConfig]") -> "AsyncDatabaseConfig": ...
|
|
163
|
-
|
|
164
343
|
def get_config(
|
|
165
|
-
self, name:
|
|
166
|
-
) -> "
|
|
167
|
-
"""Get a configuration instance by name, supporting both base behavior and Litestar extensions.
|
|
344
|
+
self, name: str
|
|
345
|
+
) -> "SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any] | AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]": ...
|
|
168
346
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
347
|
+
def get_config(
|
|
348
|
+
self, name: "type[DatabaseConfigProtocol[Any, Any, Any]] | str | Any"
|
|
349
|
+
) -> "DatabaseConfigProtocol[Any, Any, Any]":
|
|
350
|
+
"""Get a configuration instance by name.
|
|
172
351
|
|
|
173
352
|
Args:
|
|
174
|
-
name: The configuration identifier
|
|
175
|
-
- Type annotation (base class behavior)
|
|
176
|
-
- connection_key (e.g., "auth_db_connection")
|
|
177
|
-
- pool_key (e.g., "analytics_db_pool")
|
|
178
|
-
- session_key (e.g., "reporting_db_session")
|
|
179
|
-
- config instance
|
|
180
|
-
- annotation type
|
|
353
|
+
name: The configuration identifier.
|
|
181
354
|
|
|
182
355
|
Raises:
|
|
183
356
|
KeyError: If no configuration is found for the given name.
|
|
@@ -185,202 +358,154 @@ class SQLSpec(SQLSpecBase, InitPluginProtocol, CLIPlugin):
|
|
|
185
358
|
Returns:
|
|
186
359
|
The configuration instance for the specified name.
|
|
187
360
|
"""
|
|
188
|
-
# First try base class behavior for type-based lookup
|
|
189
|
-
# Only call super() if name matches the expected base class types
|
|
190
|
-
if not isinstance(name, str):
|
|
191
|
-
try:
|
|
192
|
-
return super().get_config(name) # type: ignore[no-any-return]
|
|
193
|
-
except (KeyError, AttributeError):
|
|
194
|
-
# Fall back to Litestar-specific lookup patterns
|
|
195
|
-
pass
|
|
196
|
-
|
|
197
|
-
# Litestar-specific lookups by string keys
|
|
198
361
|
if isinstance(name, str):
|
|
199
|
-
for
|
|
200
|
-
if name in {
|
|
201
|
-
return
|
|
362
|
+
for state in self._plugin_configs:
|
|
363
|
+
if name in {state.connection_key, state.pool_key, state.session_key}:
|
|
364
|
+
return cast("DatabaseConfigProtocol[Any, Any, Any]", state.config) # type: ignore[redundant-cast]
|
|
202
365
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if name == c.config or annotation_match:
|
|
207
|
-
return c.config # Return the underlying config for type-based lookups
|
|
366
|
+
for state in self._plugin_configs:
|
|
367
|
+
if name in {state.config, state.annotation}:
|
|
368
|
+
return cast("DatabaseConfigProtocol[Any, Any, Any]", state.config) # type: ignore[redundant-cast]
|
|
208
369
|
|
|
209
370
|
msg = f"No database configuration found for name '{name}'. Available keys: {self._get_available_keys()}"
|
|
210
371
|
raise KeyError(msg)
|
|
211
372
|
|
|
212
373
|
def provide_request_session(
|
|
213
|
-
self,
|
|
214
|
-
|
|
215
|
-
state: "State",
|
|
216
|
-
scope: "Scope",
|
|
217
|
-
) -> "Union[SyncDriverAdapterBase, AsyncDriverAdapterBase]":
|
|
374
|
+
self, key: "str | SyncConfigT | AsyncConfigT | type[SyncConfigT | AsyncConfigT]", state: "State", scope: "Scope"
|
|
375
|
+
) -> "SyncDriverAdapterBase | AsyncDriverAdapterBase":
|
|
218
376
|
"""Provide a database session for the specified configuration key from request scope.
|
|
219
377
|
|
|
220
|
-
This is a convenience method that combines get_config and get_request_session
|
|
221
|
-
into a single call, similar to Advanced Alchemy's provide_session pattern.
|
|
222
|
-
|
|
223
378
|
Args:
|
|
224
|
-
key: The configuration identifier (same as get_config)
|
|
225
|
-
state: The Litestar application State object
|
|
226
|
-
scope: The ASGI scope containing the request context
|
|
379
|
+
key: The configuration identifier (same as get_config).
|
|
380
|
+
state: The Litestar application State object.
|
|
381
|
+
scope: The ASGI scope containing the request context.
|
|
227
382
|
|
|
228
383
|
Returns:
|
|
229
|
-
A driver session instance for the specified database configuration
|
|
230
|
-
|
|
231
|
-
Example:
|
|
232
|
-
>>> sqlspec_plugin = connection.app.state.sqlspec
|
|
233
|
-
>>> # Direct session access by key
|
|
234
|
-
>>> auth_session = sqlspec_plugin.provide_request_session(
|
|
235
|
-
... "auth_db", state, scope
|
|
236
|
-
... )
|
|
237
|
-
>>> analytics_session = sqlspec_plugin.provide_request_session(
|
|
238
|
-
... "analytics_db", state, scope
|
|
239
|
-
... )
|
|
384
|
+
A driver session instance for the specified database configuration.
|
|
240
385
|
"""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
386
|
+
plugin_state = self._get_plugin_state(key)
|
|
387
|
+
session_scope_key = f"{plugin_state.session_key}_instance"
|
|
388
|
+
|
|
389
|
+
session = get_sqlspec_scope_state(scope, session_scope_key)
|
|
390
|
+
if session is not None:
|
|
391
|
+
return cast("SyncDriverAdapterBase | AsyncDriverAdapterBase", session)
|
|
392
|
+
|
|
393
|
+
connection = get_sqlspec_scope_state(scope, plugin_state.connection_key)
|
|
394
|
+
if connection is None:
|
|
395
|
+
self._raise_missing_connection(plugin_state.connection_key)
|
|
396
|
+
|
|
397
|
+
session = plugin_state.config.driver_type(
|
|
398
|
+
connection=connection,
|
|
399
|
+
statement_config=plugin_state.config.statement_config,
|
|
400
|
+
driver_features=plugin_state.config.driver_features,
|
|
401
|
+
)
|
|
402
|
+
set_sqlspec_scope_state(scope, session_scope_key, session)
|
|
403
|
+
|
|
404
|
+
return cast("SyncDriverAdapterBase | AsyncDriverAdapterBase", session)
|
|
244
405
|
|
|
245
406
|
def provide_sync_request_session(
|
|
246
|
-
self, key: "
|
|
407
|
+
self, key: "str | SyncConfigT | type[SyncConfigT]", state: "State", scope: "Scope"
|
|
247
408
|
) -> "SyncDriverAdapterBase":
|
|
248
409
|
"""Provide a sync database session for the specified configuration key from request scope.
|
|
249
410
|
|
|
250
|
-
This method provides better type hints for sync database sessions, ensuring the returned
|
|
251
|
-
session is properly typed as SyncDriverAdapterBase for better IDE support and type safety.
|
|
252
|
-
|
|
253
411
|
Args:
|
|
254
|
-
key: The sync configuration identifier
|
|
255
|
-
state: The Litestar application State object
|
|
256
|
-
scope: The ASGI scope containing the request context
|
|
412
|
+
key: The sync configuration identifier.
|
|
413
|
+
state: The Litestar application State object.
|
|
414
|
+
scope: The ASGI scope containing the request context.
|
|
257
415
|
|
|
258
416
|
Returns:
|
|
259
|
-
A sync driver session instance for the specified database configuration
|
|
260
|
-
|
|
261
|
-
Example:
|
|
262
|
-
>>> sqlspec_plugin = connection.app.state.sqlspec
|
|
263
|
-
>>> auth_session = sqlspec_plugin.provide_sync_request_session(
|
|
264
|
-
... "auth_db", state, scope
|
|
265
|
-
... )
|
|
266
|
-
>>> # auth_session is now correctly typed as SyncDriverAdapterBase
|
|
417
|
+
A sync driver session instance for the specified database configuration.
|
|
267
418
|
"""
|
|
268
|
-
|
|
269
|
-
db_config = self._get_database_config(key)
|
|
270
|
-
session = db_config.get_request_session(state, scope)
|
|
419
|
+
session = self.provide_request_session(key, state, scope)
|
|
271
420
|
return cast("SyncDriverAdapterBase", session)
|
|
272
421
|
|
|
273
422
|
def provide_async_request_session(
|
|
274
|
-
self, key: "
|
|
423
|
+
self, key: "str | AsyncConfigT | type[AsyncConfigT]", state: "State", scope: "Scope"
|
|
275
424
|
) -> "AsyncDriverAdapterBase":
|
|
276
425
|
"""Provide an async database session for the specified configuration key from request scope.
|
|
277
426
|
|
|
278
|
-
This method provides better type hints for async database sessions, ensuring the returned
|
|
279
|
-
session is properly typed as AsyncDriverAdapterBase for better IDE support and type safety.
|
|
280
|
-
|
|
281
427
|
Args:
|
|
282
|
-
key: The async configuration identifier
|
|
283
|
-
state: The Litestar application State object
|
|
284
|
-
scope: The ASGI scope containing the request context
|
|
428
|
+
key: The async configuration identifier.
|
|
429
|
+
state: The Litestar application State object.
|
|
430
|
+
scope: The ASGI scope containing the request context.
|
|
285
431
|
|
|
286
432
|
Returns:
|
|
287
|
-
An async driver session instance for the specified database configuration
|
|
288
|
-
|
|
289
|
-
Example:
|
|
290
|
-
>>> sqlspec_plugin = connection.app.state.sqlspec
|
|
291
|
-
>>> auth_session = sqlspec_plugin.provide_async_request_session(
|
|
292
|
-
... "auth_db", state, scope
|
|
293
|
-
... )
|
|
294
|
-
>>> # auth_session is now correctly typed as AsyncDriverAdapterBase
|
|
433
|
+
An async driver session instance for the specified database configuration.
|
|
295
434
|
"""
|
|
296
|
-
|
|
297
|
-
db_config = self._get_database_config(key)
|
|
298
|
-
session = db_config.get_request_session(state, scope)
|
|
435
|
+
session = self.provide_request_session(key, state, scope)
|
|
299
436
|
return cast("AsyncDriverAdapterBase", session)
|
|
300
437
|
|
|
301
438
|
def provide_request_connection(
|
|
302
|
-
self,
|
|
303
|
-
|
|
304
|
-
state: "State",
|
|
305
|
-
scope: "Scope",
|
|
306
|
-
) -> Any:
|
|
439
|
+
self, key: "str | SyncConfigT | AsyncConfigT | type[SyncConfigT | AsyncConfigT]", state: "State", scope: "Scope"
|
|
440
|
+
) -> "Any":
|
|
307
441
|
"""Provide a database connection for the specified configuration key from request scope.
|
|
308
442
|
|
|
309
|
-
This is a convenience method that combines get_config and get_request_connection
|
|
310
|
-
into a single call.
|
|
311
|
-
|
|
312
443
|
Args:
|
|
313
|
-
key: The configuration identifier (same as get_config)
|
|
314
|
-
state: The Litestar application State object
|
|
315
|
-
scope: The ASGI scope containing the request context
|
|
444
|
+
key: The configuration identifier (same as get_config).
|
|
445
|
+
state: The Litestar application State object.
|
|
446
|
+
scope: The ASGI scope containing the request context.
|
|
316
447
|
|
|
317
448
|
Returns:
|
|
318
|
-
A database connection instance for the specified database configuration
|
|
319
|
-
|
|
320
|
-
Example:
|
|
321
|
-
>>> sqlspec_plugin = connection.app.state.sqlspec
|
|
322
|
-
>>> # Direct connection access by key
|
|
323
|
-
>>> auth_conn = sqlspec_plugin.provide_request_connection(
|
|
324
|
-
... "auth_db", state, scope
|
|
325
|
-
... )
|
|
326
|
-
>>> analytics_conn = sqlspec_plugin.provide_request_connection(
|
|
327
|
-
... "analytics_db", state, scope
|
|
328
|
-
... )
|
|
449
|
+
A database connection instance for the specified database configuration.
|
|
329
450
|
"""
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
def _get_database_config(
|
|
335
|
-
self, key: "Union[str, SyncConfigT, AsyncConfigT, type[Union[SyncConfigT, AsyncConfigT]]]"
|
|
336
|
-
) -> DatabaseConfig:
|
|
337
|
-
"""Get a DatabaseConfig wrapper instance by name.
|
|
338
|
-
|
|
339
|
-
This is used internally by provide_request_session and provide_request_connection
|
|
340
|
-
to get the DatabaseConfig wrapper that has the request session methods.
|
|
451
|
+
plugin_state = self._get_plugin_state(key)
|
|
452
|
+
connection = get_sqlspec_scope_state(scope, plugin_state.connection_key)
|
|
453
|
+
if connection is None:
|
|
454
|
+
self._raise_missing_connection(plugin_state.connection_key)
|
|
341
455
|
|
|
342
|
-
|
|
343
|
-
key: The configuration identifier
|
|
344
|
-
|
|
345
|
-
Returns:
|
|
346
|
-
The DatabaseConfig wrapper instance
|
|
456
|
+
return connection
|
|
347
457
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
458
|
+
def _get_plugin_state(
|
|
459
|
+
self, key: "str | SyncConfigT | AsyncConfigT | type[SyncConfigT | AsyncConfigT]"
|
|
460
|
+
) -> _PluginConfigState:
|
|
461
|
+
"""Get plugin state for a configuration by key."""
|
|
352
462
|
if isinstance(key, str):
|
|
353
|
-
for
|
|
354
|
-
if key in {
|
|
355
|
-
return
|
|
463
|
+
for state in self._plugin_configs:
|
|
464
|
+
if key in {state.connection_key, state.pool_key, state.session_key}:
|
|
465
|
+
return state
|
|
356
466
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if key == c.config or annotation_match:
|
|
361
|
-
return c
|
|
467
|
+
for state in self._plugin_configs:
|
|
468
|
+
if key in {state.config, state.annotation}:
|
|
469
|
+
return state
|
|
362
470
|
|
|
363
|
-
|
|
364
|
-
|
|
471
|
+
self._raise_config_not_found(key)
|
|
472
|
+
return None
|
|
365
473
|
|
|
366
474
|
def _get_available_keys(self) -> "list[str]":
|
|
367
475
|
"""Get a list of all available configuration keys for error messages."""
|
|
368
476
|
keys = []
|
|
369
|
-
for
|
|
370
|
-
keys.extend([
|
|
477
|
+
for state in self._plugin_configs:
|
|
478
|
+
keys.extend([state.connection_key, state.pool_key, state.session_key])
|
|
371
479
|
return keys
|
|
372
480
|
|
|
373
481
|
def _validate_dependency_keys(self) -> None:
|
|
374
|
-
"""Validate that connection and pool keys are unique across configurations.
|
|
482
|
+
"""Validate that connection and pool keys are unique across configurations."""
|
|
483
|
+
connection_keys = [state.connection_key for state in self._plugin_configs]
|
|
484
|
+
pool_keys = [state.pool_key for state in self._plugin_configs]
|
|
375
485
|
|
|
376
|
-
Raises:
|
|
377
|
-
ImproperConfigurationError: If connection keys or pool keys are not unique.
|
|
378
|
-
"""
|
|
379
|
-
connection_keys = [c.connection_key for c in self.config]
|
|
380
|
-
pool_keys = [c.pool_key for c in self.config]
|
|
381
486
|
if len(set(connection_keys)) != len(connection_keys):
|
|
382
|
-
|
|
383
|
-
|
|
487
|
+
self._raise_duplicate_connection_keys()
|
|
488
|
+
|
|
384
489
|
if len(set(pool_keys)) != len(pool_keys):
|
|
385
|
-
|
|
386
|
-
|
|
490
|
+
self._raise_duplicate_pool_keys()
|
|
491
|
+
|
|
492
|
+
def _raise_missing_connection(self, connection_key: str) -> None:
|
|
493
|
+
"""Raise error when connection is not found in scope."""
|
|
494
|
+
msg = f"No database connection found in scope for key '{connection_key}'. "
|
|
495
|
+
msg += "Ensure the connection dependency is properly configured and available."
|
|
496
|
+
raise ImproperConfigurationError(detail=msg)
|
|
497
|
+
|
|
498
|
+
def _raise_config_not_found(self, key: Any) -> NoReturn:
|
|
499
|
+
"""Raise error when configuration is not found."""
|
|
500
|
+
msg = f"No database configuration found for name '{key}'. Available keys: {self._get_available_keys()}"
|
|
501
|
+
raise KeyError(msg)
|
|
502
|
+
|
|
503
|
+
def _raise_duplicate_connection_keys(self) -> None:
|
|
504
|
+
"""Raise error when connection keys are not unique."""
|
|
505
|
+
msg = "When using multiple database configuration, each configuration must have a unique `connection_key`."
|
|
506
|
+
raise ImproperConfigurationError(detail=msg)
|
|
507
|
+
|
|
508
|
+
def _raise_duplicate_pool_keys(self) -> None:
|
|
509
|
+
"""Raise error when pool keys are not unique."""
|
|
510
|
+
msg = "When using multiple database configuration, each configuration must have a unique `pool_key`."
|
|
511
|
+
raise ImproperConfigurationError(detail=msg)
|