sqlspec 0.25.0__py3-none-any.whl → 0.27.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 +256 -24
- sqlspec/_typing.py +71 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +870 -0
- sqlspec/adapters/adbc/config.py +69 -12
- sqlspec/adapters/adbc/data_dictionary.py +340 -0
- sqlspec/adapters/adbc/driver.py +266 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +153 -0
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +527 -0
- sqlspec/adapters/aiosqlite/config.py +88 -15
- sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
- sqlspec/adapters/aiosqlite/driver.py +143 -40
- 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 +2 -2
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +68 -23
- sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
- sqlspec/adapters/asyncmy/driver.py +313 -58
- 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 +450 -0
- sqlspec/adapters/asyncpg/config.py +59 -35
- sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
- sqlspec/adapters/asyncpg/driver.py +170 -25
- 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 +576 -0
- sqlspec/adapters/bigquery/config.py +27 -10
- sqlspec/adapters/bigquery/data_dictionary.py +149 -0
- sqlspec/adapters/bigquery/driver.py +368 -142
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +125 -0
- sqlspec/adapters/duckdb/_types.py +1 -1
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +80 -20
- sqlspec/adapters/duckdb/data_dictionary.py +163 -0
- sqlspec/adapters/duckdb/driver.py +167 -45
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +4 -4
- sqlspec/adapters/duckdb/type_converter.py +133 -0
- 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 +1745 -0
- sqlspec/adapters/oracledb/config.py +122 -32
- sqlspec/adapters/oracledb/data_dictionary.py +509 -0
- sqlspec/adapters/oracledb/driver.py +353 -91
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +348 -73
- sqlspec/adapters/oracledb/type_converter.py +207 -0
- 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 +482 -0
- sqlspec/adapters/psqlpy/config.py +46 -17
- sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
- sqlspec/adapters/psqlpy/driver.py +123 -209
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +102 -0
- 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 +944 -0
- sqlspec/adapters/psycopg/config.py +69 -35
- sqlspec/adapters/psycopg/data_dictionary.py +331 -0
- sqlspec/adapters/psycopg/driver.py +238 -81
- 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 +572 -0
- sqlspec/adapters/sqlite/config.py +87 -15
- sqlspec/adapters/sqlite/data_dictionary.py +149 -0
- sqlspec/adapters/sqlite/driver.py +137 -54
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +18 -9
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +162 -89
- sqlspec/builder/_column.py +62 -29
- sqlspec/builder/_ddl.py +180 -121
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +53 -94
- sqlspec/builder/_insert.py +32 -131
- sqlspec/builder/_join.py +375 -0
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +111 -17
- sqlspec/builder/_select.py +1457 -24
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +307 -194
- sqlspec/config.py +252 -67
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +17 -17
- sqlspec/core/compiler.py +62 -9
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +83 -48
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +36 -30
- sqlspec/core/type_conversion.py +235 -0
- sqlspec/driver/__init__.py +7 -6
- sqlspec/driver/_async.py +188 -151
- sqlspec/driver/_common.py +285 -80
- sqlspec/driver/_sync.py +188 -152
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +75 -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 +73 -53
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +59 -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 +324 -223
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/__init__.py +4 -3
- sqlspec/migrations/base.py +302 -39
- sqlspec/migrations/commands.py +611 -144
- sqlspec/migrations/context.py +142 -0
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +68 -23
- sqlspec/migrations/runner.py +543 -107
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +66 -36
- sqlspec/storage/_utils.py +98 -0
- sqlspec/storage/backends/fsspec.py +134 -106
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +278 -162
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +16 -84
- sqlspec/utils/config_resolver.py +153 -0
- 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 +2 -2
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +50 -2
- sqlspec/utils/sync_tools.py +21 -17
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +111 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
- sqlspec-0.27.0.dist-info/RECORD +207 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -254
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_join_operations.py +0 -389
- sqlspec/builder/mixins/_merge_operations.py +0 -592
- sqlspec/builder/mixins/_order_limit_operations.py +0 -152
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -936
- sqlspec/builder/mixins/_update_operations.py +0 -218
- sqlspec/builder/mixins/_where_clause.py +0 -1304
- sqlspec-0.25.0.dist-info/RECORD +0 -139
- sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from contextlib import asynccontextmanager
|
|
5
|
-
from typing import TYPE_CHECKING, Any, ClassVar,
|
|
5
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypedDict
|
|
6
6
|
|
|
7
7
|
from typing_extensions import NotRequired
|
|
8
8
|
|
|
@@ -15,30 +15,31 @@ from sqlspec.adapters.aiosqlite.pool import (
|
|
|
15
15
|
AiosqlitePoolConnection,
|
|
16
16
|
)
|
|
17
17
|
from sqlspec.config import AsyncDatabaseConfig
|
|
18
|
+
from sqlspec.utils.serializers import from_json, to_json
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
20
|
-
from collections.abc import AsyncGenerator
|
|
21
|
+
from collections.abc import AsyncGenerator, Callable
|
|
21
22
|
|
|
22
23
|
from sqlspec.core.statement import StatementConfig
|
|
23
24
|
|
|
24
|
-
__all__ = ("AiosqliteConfig", "AiosqliteConnectionParams", "AiosqlitePoolParams")
|
|
25
|
+
__all__ = ("AiosqliteConfig", "AiosqliteConnectionParams", "AiosqliteDriverFeatures", "AiosqlitePoolParams")
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
class AiosqliteConnectionParams(TypedDict
|
|
30
|
+
class AiosqliteConnectionParams(TypedDict):
|
|
30
31
|
"""TypedDict for aiosqlite connection parameters."""
|
|
31
32
|
|
|
32
33
|
database: NotRequired[str]
|
|
33
34
|
timeout: NotRequired[float]
|
|
34
35
|
detect_types: NotRequired[int]
|
|
35
|
-
isolation_level: NotRequired[
|
|
36
|
+
isolation_level: NotRequired[str | None]
|
|
36
37
|
check_same_thread: NotRequired[bool]
|
|
37
38
|
cached_statements: NotRequired[int]
|
|
38
39
|
uri: NotRequired[bool]
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
class AiosqlitePoolParams(AiosqliteConnectionParams
|
|
42
|
+
class AiosqlitePoolParams(AiosqliteConnectionParams):
|
|
42
43
|
"""TypedDict for aiosqlite pool parameters, inheriting connection parameters."""
|
|
43
44
|
|
|
44
45
|
pool_size: NotRequired[int]
|
|
@@ -48,20 +49,42 @@ class AiosqlitePoolParams(AiosqliteConnectionParams, total=False):
|
|
|
48
49
|
extra: NotRequired[dict[str, Any]]
|
|
49
50
|
|
|
50
51
|
|
|
52
|
+
class AiosqliteDriverFeatures(TypedDict):
|
|
53
|
+
"""Aiosqlite driver feature configuration.
|
|
54
|
+
|
|
55
|
+
Controls optional type handling and serialization features for SQLite connections.
|
|
56
|
+
|
|
57
|
+
enable_custom_adapters: Enable custom type adapters for JSON/UUID/datetime conversion.
|
|
58
|
+
Defaults to True for enhanced Python type support.
|
|
59
|
+
Set to False only if you need pure SQLite behavior without type conversions.
|
|
60
|
+
json_serializer: Custom JSON serializer function.
|
|
61
|
+
Defaults to sqlspec.utils.serializers.to_json.
|
|
62
|
+
json_deserializer: Custom JSON deserializer function.
|
|
63
|
+
Defaults to sqlspec.utils.serializers.from_json.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
enable_custom_adapters: NotRequired[bool]
|
|
67
|
+
json_serializer: "NotRequired[Callable[[Any], str]]"
|
|
68
|
+
json_deserializer: "NotRequired[Callable[[str], Any]]"
|
|
69
|
+
|
|
70
|
+
|
|
51
71
|
class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnectionPool, AiosqliteDriver]):
|
|
52
72
|
"""Database configuration for AioSQLite engine."""
|
|
53
73
|
|
|
54
74
|
driver_type: "ClassVar[type[AiosqliteDriver]]" = AiosqliteDriver
|
|
55
75
|
connection_type: "ClassVar[type[AiosqliteConnection]]" = AiosqliteConnection
|
|
76
|
+
supports_transactional_ddl: "ClassVar[bool]" = True
|
|
56
77
|
|
|
57
78
|
def __init__(
|
|
58
79
|
self,
|
|
59
80
|
*,
|
|
60
|
-
pool_config: "
|
|
61
|
-
pool_instance: "
|
|
62
|
-
migration_config: "
|
|
63
|
-
statement_config: "
|
|
64
|
-
driver_features: "
|
|
81
|
+
pool_config: "AiosqlitePoolParams | dict[str, Any] | None" = None,
|
|
82
|
+
pool_instance: "AiosqliteConnectionPool | None" = None,
|
|
83
|
+
migration_config: "dict[str, Any] | None" = None,
|
|
84
|
+
statement_config: "StatementConfig | None" = None,
|
|
85
|
+
driver_features: "AiosqliteDriverFeatures | dict[str, Any] | None" = None,
|
|
86
|
+
bind_key: "str | None" = None,
|
|
87
|
+
extension_config: "dict[str, dict[str, Any]] | None" = None,
|
|
65
88
|
) -> None:
|
|
66
89
|
"""Initialize AioSQLite configuration.
|
|
67
90
|
|
|
@@ -71,19 +94,43 @@ class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnec
|
|
|
71
94
|
migration_config: Optional migration configuration.
|
|
72
95
|
statement_config: Optional statement configuration.
|
|
73
96
|
driver_features: Optional driver feature configuration.
|
|
97
|
+
bind_key: Optional unique identifier for this configuration.
|
|
98
|
+
extension_config: Extension-specific configuration (e.g., Litestar plugin settings)
|
|
74
99
|
"""
|
|
75
100
|
config_dict = dict(pool_config) if pool_config else {}
|
|
76
101
|
|
|
77
102
|
if "database" not in config_dict or config_dict["database"] == ":memory:":
|
|
78
103
|
config_dict["database"] = "file::memory:?cache=shared"
|
|
79
104
|
config_dict["uri"] = True
|
|
105
|
+
elif "database" in config_dict:
|
|
106
|
+
database_path = str(config_dict["database"])
|
|
107
|
+
if database_path.startswith("file:") and not config_dict.get("uri"):
|
|
108
|
+
logger.debug(
|
|
109
|
+
"Database URI detected (%s) but uri=True not set. "
|
|
110
|
+
"Auto-enabling URI mode to prevent physical file creation.",
|
|
111
|
+
database_path,
|
|
112
|
+
)
|
|
113
|
+
config_dict["uri"] = True
|
|
114
|
+
|
|
115
|
+
processed_driver_features: dict[str, Any] = dict(driver_features) if driver_features else {}
|
|
116
|
+
|
|
117
|
+
if "enable_custom_adapters" not in processed_driver_features:
|
|
118
|
+
processed_driver_features["enable_custom_adapters"] = True
|
|
119
|
+
|
|
120
|
+
if "json_serializer" not in processed_driver_features:
|
|
121
|
+
processed_driver_features["json_serializer"] = to_json
|
|
122
|
+
|
|
123
|
+
if "json_deserializer" not in processed_driver_features:
|
|
124
|
+
processed_driver_features["json_deserializer"] = from_json
|
|
80
125
|
|
|
81
126
|
super().__init__(
|
|
82
127
|
pool_config=config_dict,
|
|
83
128
|
pool_instance=pool_instance,
|
|
84
129
|
migration_config=migration_config,
|
|
85
130
|
statement_config=statement_config or aiosqlite_statement_config,
|
|
86
|
-
driver_features=
|
|
131
|
+
driver_features=processed_driver_features,
|
|
132
|
+
bind_key=bind_key,
|
|
133
|
+
extension_config=extension_config,
|
|
87
134
|
)
|
|
88
135
|
|
|
89
136
|
def _get_pool_config_dict(self) -> "dict[str, Any]":
|
|
@@ -135,7 +182,7 @@ class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnec
|
|
|
135
182
|
|
|
136
183
|
@asynccontextmanager
|
|
137
184
|
async def provide_session(
|
|
138
|
-
self, *_args: Any, statement_config: "
|
|
185
|
+
self, *_args: Any, statement_config: "StatementConfig | None" = None, **_kwargs: Any
|
|
139
186
|
) -> "AsyncGenerator[AiosqliteDriver, None]":
|
|
140
187
|
"""Provide an async driver session context manager.
|
|
141
188
|
|
|
@@ -148,7 +195,11 @@ class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnec
|
|
|
148
195
|
An AiosqliteDriver instance.
|
|
149
196
|
"""
|
|
150
197
|
async with self.provide_connection(*_args, **_kwargs) as connection:
|
|
151
|
-
yield self.driver_type(
|
|
198
|
+
yield self.driver_type(
|
|
199
|
+
connection=connection,
|
|
200
|
+
statement_config=statement_config or self.statement_config,
|
|
201
|
+
driver_features=self.driver_features,
|
|
202
|
+
)
|
|
152
203
|
|
|
153
204
|
async def _create_pool(self) -> AiosqliteConnectionPool:
|
|
154
205
|
"""Create the connection pool instance.
|
|
@@ -162,7 +213,7 @@ class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnec
|
|
|
162
213
|
idle_timeout = config.pop("idle_timeout", 24 * 60 * 60)
|
|
163
214
|
operation_timeout = config.pop("operation_timeout", 10.0)
|
|
164
215
|
|
|
165
|
-
|
|
216
|
+
pool = AiosqliteConnectionPool(
|
|
166
217
|
connection_parameters=self._get_connection_config_dict(),
|
|
167
218
|
pool_size=pool_size,
|
|
168
219
|
connect_timeout=connect_timeout,
|
|
@@ -170,6 +221,28 @@ class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnec
|
|
|
170
221
|
operation_timeout=operation_timeout,
|
|
171
222
|
)
|
|
172
223
|
|
|
224
|
+
if self.driver_features.get("enable_custom_adapters", False):
|
|
225
|
+
self._register_type_adapters()
|
|
226
|
+
|
|
227
|
+
return pool
|
|
228
|
+
|
|
229
|
+
def _register_type_adapters(self) -> None:
|
|
230
|
+
"""Register custom type adapters and converters for SQLite.
|
|
231
|
+
|
|
232
|
+
Called once during pool creation if enable_custom_adapters is True.
|
|
233
|
+
Registers JSON serialization handlers if configured.
|
|
234
|
+
|
|
235
|
+
Note: aiosqlite uses the same sqlite3 module type registration as the
|
|
236
|
+
sync adapter, so this shares the implementation.
|
|
237
|
+
"""
|
|
238
|
+
if self.driver_features.get("enable_custom_adapters", False):
|
|
239
|
+
from sqlspec.adapters.sqlite._type_handlers import register_type_handlers
|
|
240
|
+
|
|
241
|
+
register_type_handlers(
|
|
242
|
+
json_serializer=self.driver_features.get("json_serializer"),
|
|
243
|
+
json_deserializer=self.driver_features.get("json_deserializer"),
|
|
244
|
+
)
|
|
245
|
+
|
|
173
246
|
async def close_pool(self) -> None:
|
|
174
247
|
"""Close the connection pool."""
|
|
175
248
|
if self.pool_instance and not self.pool_instance.is_closed:
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""SQLite-specific data dictionary for metadata queries via aiosqlite."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
|
+
|
|
6
|
+
from sqlspec.driver import AsyncDataDictionaryBase, AsyncDriverAdapterBase, VersionInfo
|
|
7
|
+
from sqlspec.utils.logging import get_logger
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
|
|
12
|
+
from sqlspec.adapters.aiosqlite.driver import AiosqliteDriver
|
|
13
|
+
|
|
14
|
+
logger = get_logger("adapters.aiosqlite.data_dictionary")
|
|
15
|
+
|
|
16
|
+
# Compiled regex patterns
|
|
17
|
+
SQLITE_VERSION_PATTERN = re.compile(r"(\d+)\.(\d+)\.(\d+)")
|
|
18
|
+
|
|
19
|
+
__all__ = ("AiosqliteAsyncDataDictionary",)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AiosqliteAsyncDataDictionary(AsyncDataDictionaryBase):
|
|
23
|
+
"""SQLite-specific async data dictionary via aiosqlite."""
|
|
24
|
+
|
|
25
|
+
async def get_version(self, driver: AsyncDriverAdapterBase) -> "VersionInfo | None":
|
|
26
|
+
"""Get SQLite database version information.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
driver: Async database driver instance
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
SQLite version information or None if detection fails
|
|
33
|
+
"""
|
|
34
|
+
version_str = await cast("AiosqliteDriver", driver).select_value("SELECT sqlite_version()")
|
|
35
|
+
if not version_str:
|
|
36
|
+
logger.warning("No SQLite version information found")
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
# Parse version like "3.45.0"
|
|
40
|
+
version_match = SQLITE_VERSION_PATTERN.match(str(version_str))
|
|
41
|
+
if not version_match:
|
|
42
|
+
logger.warning("Could not parse SQLite version: %s", version_str)
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
major, minor, patch = map(int, version_match.groups())
|
|
46
|
+
version_info = VersionInfo(major, minor, patch)
|
|
47
|
+
logger.debug("Detected SQLite version: %s", version_info)
|
|
48
|
+
return version_info
|
|
49
|
+
|
|
50
|
+
async def get_feature_flag(self, driver: AsyncDriverAdapterBase, feature: str) -> bool:
|
|
51
|
+
"""Check if SQLite database supports a specific feature.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
driver: AIOSQLite driver instance
|
|
55
|
+
feature: Feature name to check
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
True if feature is supported, False otherwise
|
|
59
|
+
"""
|
|
60
|
+
version_info = await self.get_version(driver)
|
|
61
|
+
if not version_info:
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
feature_checks: dict[str, Callable[..., bool]] = {
|
|
65
|
+
"supports_json": lambda v: v >= VersionInfo(3, 38, 0),
|
|
66
|
+
"supports_returning": lambda v: v >= VersionInfo(3, 35, 0),
|
|
67
|
+
"supports_upsert": lambda v: v >= VersionInfo(3, 24, 0),
|
|
68
|
+
"supports_window_functions": lambda v: v >= VersionInfo(3, 25, 0),
|
|
69
|
+
"supports_cte": lambda v: v >= VersionInfo(3, 8, 3),
|
|
70
|
+
"supports_transactions": lambda _: True,
|
|
71
|
+
"supports_prepared_statements": lambda _: True,
|
|
72
|
+
"supports_schemas": lambda _: False, # SQLite has ATTACH but not schemas
|
|
73
|
+
"supports_arrays": lambda _: False,
|
|
74
|
+
"supports_uuid": lambda _: False,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if feature in feature_checks:
|
|
78
|
+
return bool(feature_checks[feature](version_info))
|
|
79
|
+
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
async def get_optimal_type(self, driver: AsyncDriverAdapterBase, type_category: str) -> str:
|
|
83
|
+
"""Get optimal SQLite type for a category.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
driver: AIOSQLite driver instance
|
|
87
|
+
type_category: Type category
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
SQLite-specific type name
|
|
91
|
+
"""
|
|
92
|
+
version_info = await self.get_version(driver)
|
|
93
|
+
|
|
94
|
+
if type_category == "json":
|
|
95
|
+
if version_info and version_info >= VersionInfo(3, 38, 0):
|
|
96
|
+
return "JSON"
|
|
97
|
+
return "TEXT"
|
|
98
|
+
|
|
99
|
+
type_map = {"uuid": "TEXT", "boolean": "INTEGER", "timestamp": "TIMESTAMP", "text": "TEXT", "blob": "BLOB"}
|
|
100
|
+
return type_map.get(type_category, "TEXT")
|
|
101
|
+
|
|
102
|
+
async def get_columns(
|
|
103
|
+
self, driver: AsyncDriverAdapterBase, table: str, schema: "str | None" = None
|
|
104
|
+
) -> "list[dict[str, Any]]":
|
|
105
|
+
"""Get column information for a table using SQLite PRAGMA.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
driver: AioSQLite driver instance
|
|
109
|
+
table: Table name to query columns for
|
|
110
|
+
schema: Schema name (unused in SQLite)
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
List of column metadata dictionaries with keys:
|
|
114
|
+
- column_name: Name of the column
|
|
115
|
+
- data_type: SQLite data type
|
|
116
|
+
- nullable: Whether column allows NULL
|
|
117
|
+
- default_value: Default value if any
|
|
118
|
+
"""
|
|
119
|
+
aiosqlite_driver = cast("AiosqliteDriver", driver)
|
|
120
|
+
result = await aiosqlite_driver.execute(f"PRAGMA table_info({table})")
|
|
121
|
+
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
"column_name": row["name"] if isinstance(row, dict) else row[1],
|
|
125
|
+
"data_type": row["type"] if isinstance(row, dict) else row[2],
|
|
126
|
+
"nullable": not (row["notnull"] if isinstance(row, dict) else row[3]),
|
|
127
|
+
"default_value": row["dflt_value"] if isinstance(row, dict) else row[4],
|
|
128
|
+
}
|
|
129
|
+
for row in result.data or []
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
def list_available_features(self) -> "list[str]":
|
|
133
|
+
"""List available SQLite feature flags.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List of supported feature names
|
|
137
|
+
"""
|
|
138
|
+
return [
|
|
139
|
+
"supports_json",
|
|
140
|
+
"supports_returning",
|
|
141
|
+
"supports_upsert",
|
|
142
|
+
"supports_window_functions",
|
|
143
|
+
"supports_cte",
|
|
144
|
+
"supports_transactions",
|
|
145
|
+
"supports_prepared_statements",
|
|
146
|
+
"supports_schemas",
|
|
147
|
+
"supports_arrays",
|
|
148
|
+
"supports_uuid",
|
|
149
|
+
]
|
|
@@ -4,7 +4,7 @@ import asyncio
|
|
|
4
4
|
import contextlib
|
|
5
5
|
import datetime
|
|
6
6
|
from decimal import Decimal
|
|
7
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
9
|
import aiosqlite
|
|
10
10
|
|
|
@@ -12,7 +12,18 @@ from sqlspec.core.cache import get_cache_config
|
|
|
12
12
|
from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
|
|
13
13
|
from sqlspec.core.statement import StatementConfig
|
|
14
14
|
from sqlspec.driver import AsyncDriverAdapterBase
|
|
15
|
-
from sqlspec.exceptions import
|
|
15
|
+
from sqlspec.exceptions import (
|
|
16
|
+
CheckViolationError,
|
|
17
|
+
DatabaseConnectionError,
|
|
18
|
+
DataError,
|
|
19
|
+
ForeignKeyViolationError,
|
|
20
|
+
IntegrityError,
|
|
21
|
+
NotNullViolationError,
|
|
22
|
+
OperationalError,
|
|
23
|
+
SQLParsingError,
|
|
24
|
+
SQLSpecError,
|
|
25
|
+
UniqueViolationError,
|
|
26
|
+
)
|
|
16
27
|
from sqlspec.utils.serializers import to_json
|
|
17
28
|
|
|
18
29
|
if TYPE_CHECKING:
|
|
@@ -22,9 +33,19 @@ if TYPE_CHECKING:
|
|
|
22
33
|
from sqlspec.core.result import SQLResult
|
|
23
34
|
from sqlspec.core.statement import SQL
|
|
24
35
|
from sqlspec.driver import ExecutionResult
|
|
36
|
+
from sqlspec.driver._async import AsyncDataDictionaryBase
|
|
25
37
|
|
|
26
38
|
__all__ = ("AiosqliteCursor", "AiosqliteDriver", "AiosqliteExceptionHandler", "aiosqlite_statement_config")
|
|
27
39
|
|
|
40
|
+
SQLITE_CONSTRAINT_UNIQUE_CODE = 2067
|
|
41
|
+
SQLITE_CONSTRAINT_FOREIGNKEY_CODE = 787
|
|
42
|
+
SQLITE_CONSTRAINT_NOTNULL_CODE = 1811
|
|
43
|
+
SQLITE_CONSTRAINT_CHECK_CODE = 531
|
|
44
|
+
SQLITE_CONSTRAINT_CODE = 19
|
|
45
|
+
SQLITE_CANTOPEN_CODE = 14
|
|
46
|
+
SQLITE_IOERR_CODE = 10
|
|
47
|
+
SQLITE_MISMATCH_CODE = 20
|
|
48
|
+
|
|
28
49
|
|
|
29
50
|
aiosqlite_statement_config = StatementConfig(
|
|
30
51
|
dialect="sqlite",
|
|
@@ -60,21 +81,24 @@ class AiosqliteCursor:
|
|
|
60
81
|
|
|
61
82
|
def __init__(self, connection: "AiosqliteConnection") -> None:
|
|
62
83
|
self.connection = connection
|
|
63
|
-
self.cursor:
|
|
84
|
+
self.cursor: aiosqlite.Cursor | None = None
|
|
64
85
|
|
|
65
86
|
async def __aenter__(self) -> "aiosqlite.Cursor":
|
|
66
87
|
self.cursor = await self.connection.cursor()
|
|
67
88
|
return self.cursor
|
|
68
89
|
|
|
69
|
-
async def __aexit__(self,
|
|
70
|
-
_ = (exc_type, exc_val, exc_tb)
|
|
90
|
+
async def __aexit__(self, *_: Any) -> None:
|
|
71
91
|
if self.cursor is not None:
|
|
72
92
|
with contextlib.suppress(Exception):
|
|
73
93
|
await self.cursor.close()
|
|
74
94
|
|
|
75
95
|
|
|
76
96
|
class AiosqliteExceptionHandler:
|
|
77
|
-
"""Async context manager for
|
|
97
|
+
"""Async context manager for handling aiosqlite database exceptions.
|
|
98
|
+
|
|
99
|
+
Maps SQLite extended result codes to specific SQLSpec exceptions
|
|
100
|
+
for better error handling in application code.
|
|
101
|
+
"""
|
|
78
102
|
|
|
79
103
|
__slots__ = ()
|
|
80
104
|
|
|
@@ -84,56 +108,122 @@ class AiosqliteExceptionHandler:
|
|
|
84
108
|
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
85
109
|
if exc_type is None:
|
|
86
110
|
return
|
|
87
|
-
if issubclass(exc_type, aiosqlite.IntegrityError):
|
|
88
|
-
e = exc_val
|
|
89
|
-
msg = f"AIOSQLite integrity constraint violation: {e}"
|
|
90
|
-
raise SQLSpecError(msg) from e
|
|
91
|
-
if issubclass(exc_type, aiosqlite.OperationalError):
|
|
92
|
-
e = exc_val
|
|
93
|
-
error_msg = str(e).lower()
|
|
94
|
-
if "locked" in error_msg:
|
|
95
|
-
msg = f"AIOSQLite database locked: {e}. Consider enabling WAL mode or reducing concurrency."
|
|
96
|
-
raise SQLSpecError(msg) from e
|
|
97
|
-
if "syntax" in error_msg or "malformed" in error_msg:
|
|
98
|
-
msg = f"AIOSQLite SQL syntax error: {e}"
|
|
99
|
-
raise SQLParsingError(msg) from e
|
|
100
|
-
msg = f"AIOSQLite operational error: {e}"
|
|
101
|
-
raise SQLSpecError(msg) from e
|
|
102
|
-
if issubclass(exc_type, aiosqlite.DatabaseError):
|
|
103
|
-
e = exc_val
|
|
104
|
-
msg = f"AIOSQLite database error: {e}"
|
|
105
|
-
raise SQLSpecError(msg) from e
|
|
106
111
|
if issubclass(exc_type, aiosqlite.Error):
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
self._map_sqlite_exception(exc_val)
|
|
113
|
+
|
|
114
|
+
def _map_sqlite_exception(self, e: Any) -> None:
|
|
115
|
+
"""Map SQLite exception to SQLSpec exception.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
e: aiosqlite.Error instance
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
Specific SQLSpec exception based on error code
|
|
122
|
+
"""
|
|
123
|
+
error_code = getattr(e, "sqlite_errorcode", None)
|
|
124
|
+
error_name = getattr(e, "sqlite_errorname", None)
|
|
125
|
+
error_msg = str(e).lower()
|
|
126
|
+
|
|
127
|
+
if "locked" in error_msg:
|
|
128
|
+
msg = f"AIOSQLite database locked: {e}. Consider enabling WAL mode or reducing concurrency."
|
|
117
129
|
raise SQLSpecError(msg) from e
|
|
118
130
|
|
|
131
|
+
if not error_code:
|
|
132
|
+
if "unique constraint" in error_msg:
|
|
133
|
+
self._raise_unique_violation(e, 0)
|
|
134
|
+
elif "foreign key constraint" in error_msg:
|
|
135
|
+
self._raise_foreign_key_violation(e, 0)
|
|
136
|
+
elif "not null constraint" in error_msg:
|
|
137
|
+
self._raise_not_null_violation(e, 0)
|
|
138
|
+
elif "check constraint" in error_msg:
|
|
139
|
+
self._raise_check_violation(e, 0)
|
|
140
|
+
elif "syntax" in error_msg:
|
|
141
|
+
self._raise_parsing_error(e, None)
|
|
142
|
+
else:
|
|
143
|
+
self._raise_generic_error(e)
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
if error_code == SQLITE_CONSTRAINT_UNIQUE_CODE or error_name == "SQLITE_CONSTRAINT_UNIQUE":
|
|
147
|
+
self._raise_unique_violation(e, error_code)
|
|
148
|
+
elif error_code == SQLITE_CONSTRAINT_FOREIGNKEY_CODE or error_name == "SQLITE_CONSTRAINT_FOREIGNKEY":
|
|
149
|
+
self._raise_foreign_key_violation(e, error_code)
|
|
150
|
+
elif error_code == SQLITE_CONSTRAINT_NOTNULL_CODE or error_name == "SQLITE_CONSTRAINT_NOTNULL":
|
|
151
|
+
self._raise_not_null_violation(e, error_code)
|
|
152
|
+
elif error_code == SQLITE_CONSTRAINT_CHECK_CODE or error_name == "SQLITE_CONSTRAINT_CHECK":
|
|
153
|
+
self._raise_check_violation(e, error_code)
|
|
154
|
+
elif error_code == SQLITE_CONSTRAINT_CODE or error_name == "SQLITE_CONSTRAINT":
|
|
155
|
+
self._raise_integrity_error(e, error_code)
|
|
156
|
+
elif error_code == SQLITE_CANTOPEN_CODE or error_name == "SQLITE_CANTOPEN":
|
|
157
|
+
self._raise_connection_error(e, error_code)
|
|
158
|
+
elif error_code == SQLITE_IOERR_CODE or error_name == "SQLITE_IOERR":
|
|
159
|
+
self._raise_operational_error(e, error_code)
|
|
160
|
+
elif error_code == SQLITE_MISMATCH_CODE or error_name == "SQLITE_MISMATCH":
|
|
161
|
+
self._raise_data_error(e, error_code)
|
|
162
|
+
elif error_code == 1 or "syntax" in error_msg:
|
|
163
|
+
self._raise_parsing_error(e, error_code)
|
|
164
|
+
else:
|
|
165
|
+
self._raise_generic_error(e)
|
|
166
|
+
|
|
167
|
+
def _raise_unique_violation(self, e: Any, code: int) -> None:
|
|
168
|
+
msg = f"SQLite unique constraint violation [code {code}]: {e}"
|
|
169
|
+
raise UniqueViolationError(msg) from e
|
|
170
|
+
|
|
171
|
+
def _raise_foreign_key_violation(self, e: Any, code: int) -> None:
|
|
172
|
+
msg = f"SQLite foreign key constraint violation [code {code}]: {e}"
|
|
173
|
+
raise ForeignKeyViolationError(msg) from e
|
|
174
|
+
|
|
175
|
+
def _raise_not_null_violation(self, e: Any, code: int) -> None:
|
|
176
|
+
msg = f"SQLite not-null constraint violation [code {code}]: {e}"
|
|
177
|
+
raise NotNullViolationError(msg) from e
|
|
178
|
+
|
|
179
|
+
def _raise_check_violation(self, e: Any, code: int) -> None:
|
|
180
|
+
msg = f"SQLite check constraint violation [code {code}]: {e}"
|
|
181
|
+
raise CheckViolationError(msg) from e
|
|
182
|
+
|
|
183
|
+
def _raise_integrity_error(self, e: Any, code: int) -> None:
|
|
184
|
+
msg = f"SQLite integrity constraint violation [code {code}]: {e}"
|
|
185
|
+
raise IntegrityError(msg) from e
|
|
186
|
+
|
|
187
|
+
def _raise_parsing_error(self, e: Any, code: "int | None") -> None:
|
|
188
|
+
code_str = f"[code {code}]" if code else ""
|
|
189
|
+
msg = f"SQLite SQL syntax error {code_str}: {e}"
|
|
190
|
+
raise SQLParsingError(msg) from e
|
|
191
|
+
|
|
192
|
+
def _raise_connection_error(self, e: Any, code: int) -> None:
|
|
193
|
+
msg = f"SQLite connection error [code {code}]: {e}"
|
|
194
|
+
raise DatabaseConnectionError(msg) from e
|
|
195
|
+
|
|
196
|
+
def _raise_operational_error(self, e: Any, code: int) -> None:
|
|
197
|
+
msg = f"SQLite operational error [code {code}]: {e}"
|
|
198
|
+
raise OperationalError(msg) from e
|
|
199
|
+
|
|
200
|
+
def _raise_data_error(self, e: Any, code: int) -> None:
|
|
201
|
+
msg = f"SQLite data error [code {code}]: {e}"
|
|
202
|
+
raise DataError(msg) from e
|
|
203
|
+
|
|
204
|
+
def _raise_generic_error(self, e: Any) -> None:
|
|
205
|
+
msg = f"SQLite database error: {e}"
|
|
206
|
+
raise SQLSpecError(msg) from e
|
|
207
|
+
|
|
119
208
|
|
|
120
209
|
class AiosqliteDriver(AsyncDriverAdapterBase):
|
|
121
210
|
"""AIOSQLite driver for async SQLite database operations."""
|
|
122
211
|
|
|
123
|
-
__slots__ = ()
|
|
212
|
+
__slots__ = ("_data_dictionary",)
|
|
124
213
|
dialect = "sqlite"
|
|
125
214
|
|
|
126
215
|
def __init__(
|
|
127
216
|
self,
|
|
128
217
|
connection: "AiosqliteConnection",
|
|
129
|
-
statement_config: "
|
|
130
|
-
driver_features: "
|
|
218
|
+
statement_config: "StatementConfig | None" = None,
|
|
219
|
+
driver_features: "dict[str, Any] | None" = None,
|
|
131
220
|
) -> None:
|
|
132
221
|
if statement_config is None:
|
|
133
222
|
cache_config = get_cache_config()
|
|
134
223
|
statement_config = aiosqlite_statement_config.replace(enable_caching=cache_config.compiled_cache_enabled)
|
|
135
224
|
|
|
136
225
|
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
226
|
+
self._data_dictionary: AsyncDataDictionaryBase | None = None
|
|
137
227
|
|
|
138
228
|
def with_cursor(self, connection: "AiosqliteConnection") -> "AiosqliteCursor":
|
|
139
229
|
"""Create async context manager for AIOSQLite cursor."""
|
|
@@ -143,7 +233,7 @@ class AiosqliteDriver(AsyncDriverAdapterBase):
|
|
|
143
233
|
"""Handle AIOSQLite-specific exceptions."""
|
|
144
234
|
return AiosqliteExceptionHandler()
|
|
145
235
|
|
|
146
|
-
async def _try_special_handling(self, cursor: "aiosqlite.Cursor", statement: "SQL") -> "
|
|
236
|
+
async def _try_special_handling(self, cursor: "aiosqlite.Cursor", statement: "SQL") -> "SQLResult | None":
|
|
147
237
|
"""Hook for AIOSQLite-specific special operations.
|
|
148
238
|
|
|
149
239
|
Args:
|
|
@@ -195,7 +285,7 @@ class AiosqliteDriver(AsyncDriverAdapterBase):
|
|
|
195
285
|
fetched_data = await cursor.fetchall()
|
|
196
286
|
column_names = [col[0] for col in cursor.description or []]
|
|
197
287
|
|
|
198
|
-
data = [dict(zip(column_names, row)) for row in fetched_data]
|
|
288
|
+
data = [dict(zip(column_names, row, strict=False)) for row in fetched_data]
|
|
199
289
|
|
|
200
290
|
return self.create_execution_result(
|
|
201
291
|
cursor, selected_data=data, column_names=column_names, data_row_count=len(data), is_select_result=True
|
|
@@ -241,3 +331,16 @@ class AiosqliteDriver(AsyncDriverAdapterBase):
|
|
|
241
331
|
except aiosqlite.Error as e:
|
|
242
332
|
msg = f"Failed to commit transaction: {e}"
|
|
243
333
|
raise SQLSpecError(msg) from e
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def data_dictionary(self) -> "AsyncDataDictionaryBase":
|
|
337
|
+
"""Get the data dictionary for this driver.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Data dictionary instance for metadata queries
|
|
341
|
+
"""
|
|
342
|
+
if self._data_dictionary is None:
|
|
343
|
+
from sqlspec.adapters.aiosqlite.data_dictionary import AiosqliteAsyncDataDictionary
|
|
344
|
+
|
|
345
|
+
self._data_dictionary = AiosqliteAsyncDataDictionary()
|
|
346
|
+
return self._data_dictionary
|