sqlspec 0.11.1__py3-none-any.whl → 0.12.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 +16 -3
- sqlspec/_serialization.py +3 -10
- sqlspec/_sql.py +1147 -0
- sqlspec/_typing.py +343 -41
- sqlspec/adapters/adbc/__init__.py +2 -6
- sqlspec/adapters/adbc/config.py +474 -149
- sqlspec/adapters/adbc/driver.py +330 -621
- sqlspec/adapters/aiosqlite/__init__.py +2 -6
- sqlspec/adapters/aiosqlite/config.py +143 -57
- sqlspec/adapters/aiosqlite/driver.py +269 -431
- sqlspec/adapters/asyncmy/__init__.py +3 -8
- sqlspec/adapters/asyncmy/config.py +247 -202
- sqlspec/adapters/asyncmy/driver.py +218 -436
- sqlspec/adapters/asyncpg/__init__.py +4 -7
- sqlspec/adapters/asyncpg/config.py +329 -176
- sqlspec/adapters/asyncpg/driver.py +417 -487
- sqlspec/adapters/bigquery/__init__.py +2 -2
- sqlspec/adapters/bigquery/config.py +407 -0
- sqlspec/adapters/bigquery/driver.py +600 -553
- sqlspec/adapters/duckdb/__init__.py +4 -1
- sqlspec/adapters/duckdb/config.py +432 -321
- sqlspec/adapters/duckdb/driver.py +392 -406
- sqlspec/adapters/oracledb/__init__.py +3 -8
- sqlspec/adapters/oracledb/config.py +625 -0
- sqlspec/adapters/oracledb/driver.py +548 -921
- sqlspec/adapters/psqlpy/__init__.py +4 -7
- sqlspec/adapters/psqlpy/config.py +372 -203
- sqlspec/adapters/psqlpy/driver.py +197 -533
- sqlspec/adapters/psycopg/__init__.py +3 -8
- sqlspec/adapters/psycopg/config.py +741 -0
- sqlspec/adapters/psycopg/driver.py +734 -694
- sqlspec/adapters/sqlite/__init__.py +2 -6
- sqlspec/adapters/sqlite/config.py +146 -81
- sqlspec/adapters/sqlite/driver.py +242 -405
- sqlspec/base.py +220 -784
- sqlspec/config.py +354 -0
- sqlspec/driver/__init__.py +22 -0
- sqlspec/driver/_async.py +252 -0
- sqlspec/driver/_common.py +338 -0
- sqlspec/driver/_sync.py +261 -0
- sqlspec/driver/mixins/__init__.py +17 -0
- sqlspec/driver/mixins/_pipeline.py +523 -0
- sqlspec/driver/mixins/_result_utils.py +122 -0
- sqlspec/driver/mixins/_sql_translator.py +35 -0
- sqlspec/driver/mixins/_storage.py +993 -0
- sqlspec/driver/mixins/_type_coercion.py +131 -0
- sqlspec/exceptions.py +299 -7
- sqlspec/extensions/aiosql/__init__.py +10 -0
- sqlspec/extensions/aiosql/adapter.py +474 -0
- sqlspec/extensions/litestar/__init__.py +1 -6
- sqlspec/extensions/litestar/_utils.py +1 -5
- sqlspec/extensions/litestar/config.py +5 -6
- sqlspec/extensions/litestar/handlers.py +13 -12
- sqlspec/extensions/litestar/plugin.py +22 -24
- sqlspec/extensions/litestar/providers.py +37 -55
- sqlspec/loader.py +528 -0
- sqlspec/service/__init__.py +3 -0
- sqlspec/service/base.py +24 -0
- sqlspec/service/pagination.py +26 -0
- sqlspec/statement/__init__.py +21 -0
- sqlspec/statement/builder/__init__.py +54 -0
- sqlspec/statement/builder/_ddl_utils.py +119 -0
- sqlspec/statement/builder/_parsing_utils.py +135 -0
- sqlspec/statement/builder/base.py +328 -0
- sqlspec/statement/builder/ddl.py +1379 -0
- sqlspec/statement/builder/delete.py +80 -0
- sqlspec/statement/builder/insert.py +274 -0
- sqlspec/statement/builder/merge.py +95 -0
- sqlspec/statement/builder/mixins/__init__.py +65 -0
- sqlspec/statement/builder/mixins/_aggregate_functions.py +151 -0
- sqlspec/statement/builder/mixins/_case_builder.py +91 -0
- sqlspec/statement/builder/mixins/_common_table_expr.py +91 -0
- sqlspec/statement/builder/mixins/_delete_from.py +34 -0
- sqlspec/statement/builder/mixins/_from.py +61 -0
- sqlspec/statement/builder/mixins/_group_by.py +119 -0
- sqlspec/statement/builder/mixins/_having.py +35 -0
- sqlspec/statement/builder/mixins/_insert_from_select.py +48 -0
- sqlspec/statement/builder/mixins/_insert_into.py +36 -0
- sqlspec/statement/builder/mixins/_insert_values.py +69 -0
- sqlspec/statement/builder/mixins/_join.py +110 -0
- sqlspec/statement/builder/mixins/_limit_offset.py +53 -0
- sqlspec/statement/builder/mixins/_merge_clauses.py +405 -0
- sqlspec/statement/builder/mixins/_order_by.py +46 -0
- sqlspec/statement/builder/mixins/_pivot.py +82 -0
- sqlspec/statement/builder/mixins/_returning.py +37 -0
- sqlspec/statement/builder/mixins/_select_columns.py +60 -0
- sqlspec/statement/builder/mixins/_set_ops.py +122 -0
- sqlspec/statement/builder/mixins/_unpivot.py +80 -0
- sqlspec/statement/builder/mixins/_update_from.py +54 -0
- sqlspec/statement/builder/mixins/_update_set.py +91 -0
- sqlspec/statement/builder/mixins/_update_table.py +29 -0
- sqlspec/statement/builder/mixins/_where.py +374 -0
- sqlspec/statement/builder/mixins/_window_functions.py +86 -0
- sqlspec/statement/builder/protocols.py +20 -0
- sqlspec/statement/builder/select.py +206 -0
- sqlspec/statement/builder/update.py +178 -0
- sqlspec/statement/filters.py +571 -0
- sqlspec/statement/parameters.py +736 -0
- sqlspec/statement/pipelines/__init__.py +67 -0
- sqlspec/statement/pipelines/analyzers/__init__.py +9 -0
- sqlspec/statement/pipelines/analyzers/_analyzer.py +649 -0
- sqlspec/statement/pipelines/base.py +315 -0
- sqlspec/statement/pipelines/context.py +119 -0
- sqlspec/statement/pipelines/result_types.py +41 -0
- sqlspec/statement/pipelines/transformers/__init__.py +8 -0
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +256 -0
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +623 -0
- sqlspec/statement/pipelines/transformers/_remove_comments.py +66 -0
- sqlspec/statement/pipelines/transformers/_remove_hints.py +81 -0
- sqlspec/statement/pipelines/validators/__init__.py +23 -0
- sqlspec/statement/pipelines/validators/_dml_safety.py +275 -0
- sqlspec/statement/pipelines/validators/_parameter_style.py +297 -0
- sqlspec/statement/pipelines/validators/_performance.py +703 -0
- sqlspec/statement/pipelines/validators/_security.py +990 -0
- sqlspec/statement/pipelines/validators/base.py +67 -0
- sqlspec/statement/result.py +527 -0
- sqlspec/statement/splitter.py +701 -0
- sqlspec/statement/sql.py +1198 -0
- sqlspec/storage/__init__.py +15 -0
- sqlspec/storage/backends/__init__.py +0 -0
- sqlspec/storage/backends/base.py +166 -0
- sqlspec/storage/backends/fsspec.py +315 -0
- sqlspec/storage/backends/obstore.py +464 -0
- sqlspec/storage/protocol.py +170 -0
- sqlspec/storage/registry.py +315 -0
- sqlspec/typing.py +157 -36
- sqlspec/utils/correlation.py +155 -0
- sqlspec/utils/deprecation.py +3 -6
- sqlspec/utils/fixtures.py +6 -11
- sqlspec/utils/logging.py +135 -0
- sqlspec/utils/module_loader.py +45 -43
- sqlspec/utils/serializers.py +4 -0
- sqlspec/utils/singleton.py +6 -8
- sqlspec/utils/sync_tools.py +15 -27
- sqlspec/utils/text.py +58 -26
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.0.dist-info}/METADATA +97 -26
- sqlspec-0.12.0.dist-info/RECORD +145 -0
- sqlspec/adapters/bigquery/config/__init__.py +0 -3
- sqlspec/adapters/bigquery/config/_common.py +0 -40
- sqlspec/adapters/bigquery/config/_sync.py +0 -87
- sqlspec/adapters/oracledb/config/__init__.py +0 -9
- sqlspec/adapters/oracledb/config/_asyncio.py +0 -186
- sqlspec/adapters/oracledb/config/_common.py +0 -131
- sqlspec/adapters/oracledb/config/_sync.py +0 -186
- sqlspec/adapters/psycopg/config/__init__.py +0 -19
- sqlspec/adapters/psycopg/config/_async.py +0 -169
- sqlspec/adapters/psycopg/config/_common.py +0 -56
- sqlspec/adapters/psycopg/config/_sync.py +0 -168
- sqlspec/filters.py +0 -331
- sqlspec/mixins.py +0 -305
- sqlspec/statement.py +0 -378
- sqlspec-0.11.1.dist-info/RECORD +0 -69
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
from sqlspec.adapters.asyncpg.config import
|
|
1
|
+
from sqlspec.adapters.asyncpg.config import CONNECTION_FIELDS, POOL_FIELDS, AsyncpgConfig
|
|
2
2
|
from sqlspec.adapters.asyncpg.driver import AsyncpgConnection, AsyncpgDriver
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"AsyncpgDriver",
|
|
8
|
-
"AsyncpgPoolConfig",
|
|
9
|
-
)
|
|
4
|
+
# AsyncpgDriver already imported above
|
|
5
|
+
|
|
6
|
+
__all__ = ("CONNECTION_FIELDS", "POOL_FIELDS", "AsyncpgConfig", "AsyncpgConnection", "AsyncpgDriver")
|
|
@@ -1,221 +1,374 @@
|
|
|
1
|
+
"""AsyncPG database configuration with direct field-based configuration."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import AsyncGenerator, Awaitable, Callable
|
|
1
5
|
from contextlib import asynccontextmanager
|
|
2
|
-
from dataclasses import
|
|
3
|
-
from typing import TYPE_CHECKING, Any,
|
|
6
|
+
from dataclasses import replace
|
|
7
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypedDict
|
|
4
8
|
|
|
5
9
|
from asyncpg import Record
|
|
6
10
|
from asyncpg import create_pool as asyncpg_create_pool
|
|
7
|
-
from
|
|
11
|
+
from typing_extensions import NotRequired, Unpack
|
|
8
12
|
|
|
9
|
-
from sqlspec._serialization import decode_json, encode_json
|
|
10
13
|
from sqlspec.adapters.asyncpg.driver import AsyncpgConnection, AsyncpgDriver
|
|
11
|
-
from sqlspec.
|
|
12
|
-
from sqlspec.
|
|
13
|
-
from sqlspec.typing import
|
|
14
|
+
from sqlspec.config import AsyncDatabaseConfig
|
|
15
|
+
from sqlspec.statement.sql import SQLConfig
|
|
16
|
+
from sqlspec.typing import DictRow, Empty
|
|
17
|
+
from sqlspec.utils.serializers import from_json, to_json
|
|
14
18
|
|
|
15
19
|
if TYPE_CHECKING:
|
|
16
|
-
from asyncio import AbstractEventLoop
|
|
17
|
-
from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine
|
|
20
|
+
from asyncio.events import AbstractEventLoop
|
|
18
21
|
|
|
19
|
-
from asyncpg.connection import Connection
|
|
20
22
|
from asyncpg.pool import Pool
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
from sqlglot.dialects.dialect import DialectType
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = ("CONNECTION_FIELDS", "POOL_FIELDS", "AsyncpgConfig")
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("sqlspec")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AsyncpgConnectionParams(TypedDict, total=False):
|
|
32
|
+
"""TypedDict for AsyncPG connection parameters."""
|
|
33
|
+
|
|
34
|
+
dsn: NotRequired[str]
|
|
35
|
+
host: NotRequired[str]
|
|
36
|
+
port: NotRequired[int]
|
|
37
|
+
user: NotRequired[str]
|
|
38
|
+
password: NotRequired[str]
|
|
39
|
+
database: NotRequired[str]
|
|
40
|
+
ssl: NotRequired[Any] # Can be bool, SSLContext, or specific string
|
|
41
|
+
passfile: NotRequired[str]
|
|
42
|
+
direct_tls: NotRequired[bool]
|
|
43
|
+
connect_timeout: NotRequired[float]
|
|
44
|
+
command_timeout: NotRequired[float]
|
|
45
|
+
statement_cache_size: NotRequired[int]
|
|
46
|
+
max_cached_statement_lifetime: NotRequired[int]
|
|
47
|
+
max_cacheable_statement_size: NotRequired[int]
|
|
48
|
+
server_settings: NotRequired[dict[str, str]]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AsyncpgPoolParams(AsyncpgConnectionParams, total=False):
|
|
52
|
+
"""TypedDict for AsyncPG pool parameters, inheriting connection parameters."""
|
|
53
|
+
|
|
54
|
+
min_size: NotRequired[int]
|
|
55
|
+
max_size: NotRequired[int]
|
|
56
|
+
max_queries: NotRequired[int]
|
|
57
|
+
max_inactive_connection_lifetime: NotRequired[float]
|
|
58
|
+
setup: NotRequired["Callable[[AsyncpgConnection], Awaitable[None]]"]
|
|
59
|
+
init: NotRequired["Callable[[AsyncpgConnection], Awaitable[None]]"]
|
|
60
|
+
loop: NotRequired["AbstractEventLoop"]
|
|
61
|
+
connection_class: NotRequired[type["AsyncpgConnection"]]
|
|
62
|
+
record_class: NotRequired[type[Record]]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class DriverParameters(AsyncpgPoolParams, total=False):
|
|
66
|
+
"""TypedDict for additional parameters that can be passed to AsyncPG."""
|
|
67
|
+
|
|
68
|
+
statement_config: NotRequired[SQLConfig]
|
|
69
|
+
default_row_type: NotRequired[type[DictRow]]
|
|
70
|
+
json_serializer: NotRequired[Callable[[Any], str]]
|
|
71
|
+
json_deserializer: NotRequired[Callable[[str], Any]]
|
|
72
|
+
pool_instance: NotRequired["Pool[Record]"]
|
|
73
|
+
extras: NotRequired[dict[str, Any]]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
CONNECTION_FIELDS = {
|
|
77
|
+
"dsn",
|
|
78
|
+
"host",
|
|
79
|
+
"port",
|
|
80
|
+
"user",
|
|
81
|
+
"password",
|
|
82
|
+
"database",
|
|
83
|
+
"ssl",
|
|
84
|
+
"passfile",
|
|
85
|
+
"direct_tls",
|
|
86
|
+
"connect_timeout",
|
|
87
|
+
"command_timeout",
|
|
88
|
+
"statement_cache_size",
|
|
89
|
+
"max_cached_statement_lifetime",
|
|
90
|
+
"max_cacheable_statement_size",
|
|
91
|
+
"server_settings",
|
|
92
|
+
}
|
|
93
|
+
POOL_FIELDS = CONNECTION_FIELDS.union(
|
|
94
|
+
{
|
|
95
|
+
"min_size",
|
|
96
|
+
"max_size",
|
|
97
|
+
"max_queries",
|
|
98
|
+
"max_inactive_connection_lifetime",
|
|
99
|
+
"setup",
|
|
100
|
+
"init",
|
|
101
|
+
"loop",
|
|
102
|
+
"connection_class",
|
|
103
|
+
"record_class",
|
|
104
|
+
}
|
|
26
105
|
)
|
|
27
106
|
|
|
28
107
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"""A coroutine to prepare a connection right before it is returned from Pool.acquire(). An example use case would be to automatically set up notifications listeners for all connections of a pool."""
|
|
65
|
-
init: "Union[Coroutine[None, type[Connection], Any], EmptyType]" = Empty # pyright: ignore[reportMissingTypeArgument]
|
|
66
|
-
"""A coroutine to prepare a connection right before it is returned from Pool.acquire(). An example use case would be to automatically set up notifications listeners for all connections of a pool."""
|
|
67
|
-
|
|
68
|
-
loop: "Union[AbstractEventLoop, EmptyType]" = Empty
|
|
69
|
-
"""An asyncio event loop instance. If None, the default event loop will be used."""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@dataclass
|
|
73
|
-
class AsyncpgConfig(AsyncDatabaseConfig["AsyncpgConnection", "Pool", "AsyncpgDriver"]): # pyright: ignore[reportMissingTypeArgument]
|
|
74
|
-
"""Asyncpg Configuration."""
|
|
75
|
-
|
|
76
|
-
pool_config: "Optional[AsyncpgPoolConfig]" = field(default=None)
|
|
77
|
-
"""Asyncpg Pool configuration"""
|
|
78
|
-
json_deserializer: "Callable[[str], Any]" = field(hash=False, default=decode_json)
|
|
79
|
-
"""For dialects that support the :class:`JSON <sqlalchemy.types.JSON>` datatype, this is a Python callable that will
|
|
80
|
-
convert a JSON string to a Python object. By default, this is set to SQLSpec's
|
|
81
|
-
:attr:`decode_json() <sqlspec._serialization.decode_json>` function."""
|
|
82
|
-
json_serializer: "Callable[[Any], str]" = field(hash=False, default=encode_json)
|
|
83
|
-
"""For dialects that support the JSON datatype, this is a Python callable that will render a given object as JSON.
|
|
84
|
-
By default, SQLSpec's :attr:`encode_json() <sqlspec._serialization.encode_json>` is used."""
|
|
85
|
-
connection_type: "type[AsyncpgConnection]" = field(
|
|
86
|
-
hash=False,
|
|
87
|
-
init=False,
|
|
88
|
-
default_factory=lambda: PoolConnectionProxy, # type: ignore[assignment]
|
|
108
|
+
class AsyncpgConfig(AsyncDatabaseConfig[AsyncpgConnection, "Pool[Record]", AsyncpgDriver]):
|
|
109
|
+
"""Configuration for AsyncPG database connections using TypedDict."""
|
|
110
|
+
|
|
111
|
+
__slots__ = (
|
|
112
|
+
"_dialect",
|
|
113
|
+
"command_timeout",
|
|
114
|
+
"connect_timeout",
|
|
115
|
+
"connection_class",
|
|
116
|
+
"database",
|
|
117
|
+
"default_row_type",
|
|
118
|
+
"direct_tls",
|
|
119
|
+
"dsn",
|
|
120
|
+
"extras",
|
|
121
|
+
"host",
|
|
122
|
+
"init",
|
|
123
|
+
"json_deserializer",
|
|
124
|
+
"json_serializer",
|
|
125
|
+
"loop",
|
|
126
|
+
"max_cacheable_statement_size",
|
|
127
|
+
"max_cached_statement_lifetime",
|
|
128
|
+
"max_inactive_connection_lifetime",
|
|
129
|
+
"max_queries",
|
|
130
|
+
"max_size",
|
|
131
|
+
"min_size",
|
|
132
|
+
"passfile",
|
|
133
|
+
"password",
|
|
134
|
+
"pool_instance",
|
|
135
|
+
"port",
|
|
136
|
+
"record_class",
|
|
137
|
+
"server_settings",
|
|
138
|
+
"setup",
|
|
139
|
+
"ssl",
|
|
140
|
+
"statement_cache_size",
|
|
141
|
+
"statement_config",
|
|
142
|
+
"user",
|
|
89
143
|
)
|
|
90
|
-
"""Type of the connection object"""
|
|
91
|
-
driver_type: "type[AsyncpgDriver]" = field(hash=False, init=False, default_factory=lambda: AsyncpgDriver) # type: ignore[type-abstract,unused-ignore]
|
|
92
|
-
"""Type of the driver object"""
|
|
93
|
-
pool_instance: "Optional[Pool[Any]]" = field(hash=False, default=None)
|
|
94
|
-
"""The connection pool instance. If set, this will be used instead of creating a new pool."""
|
|
95
144
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
145
|
+
driver_type: type[AsyncpgDriver] = AsyncpgDriver
|
|
146
|
+
connection_type: type[AsyncpgConnection] = type(AsyncpgConnection) # type: ignore[assignment]
|
|
147
|
+
supported_parameter_styles: ClassVar[tuple[str, ...]] = ("numeric",)
|
|
148
|
+
preferred_parameter_style: ClassVar[str] = "numeric"
|
|
149
|
+
|
|
150
|
+
def __init__(self, **kwargs: "Unpack[DriverParameters]") -> None:
|
|
151
|
+
"""Initialize AsyncPG configuration."""
|
|
152
|
+
# Known fields that are part of the config
|
|
153
|
+
known_fields = {
|
|
154
|
+
"dsn",
|
|
155
|
+
"host",
|
|
156
|
+
"port",
|
|
157
|
+
"user",
|
|
158
|
+
"password",
|
|
159
|
+
"database",
|
|
160
|
+
"ssl",
|
|
161
|
+
"passfile",
|
|
162
|
+
"direct_tls",
|
|
163
|
+
"connect_timeout",
|
|
164
|
+
"command_timeout",
|
|
165
|
+
"statement_cache_size",
|
|
166
|
+
"max_cached_statement_lifetime",
|
|
167
|
+
"max_cacheable_statement_size",
|
|
168
|
+
"server_settings",
|
|
169
|
+
"min_size",
|
|
170
|
+
"max_size",
|
|
171
|
+
"max_queries",
|
|
172
|
+
"max_inactive_connection_lifetime",
|
|
173
|
+
"setup",
|
|
174
|
+
"init",
|
|
175
|
+
"loop",
|
|
176
|
+
"connection_class",
|
|
177
|
+
"record_class",
|
|
178
|
+
"extras",
|
|
179
|
+
"statement_config",
|
|
180
|
+
"default_row_type",
|
|
181
|
+
"json_serializer",
|
|
182
|
+
"json_deserializer",
|
|
183
|
+
"pool_instance",
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
self.dsn = kwargs.get("dsn")
|
|
187
|
+
self.host = kwargs.get("host")
|
|
188
|
+
self.port = kwargs.get("port")
|
|
189
|
+
self.user = kwargs.get("user")
|
|
190
|
+
self.password = kwargs.get("password")
|
|
191
|
+
self.database = kwargs.get("database")
|
|
192
|
+
self.ssl = kwargs.get("ssl")
|
|
193
|
+
self.passfile = kwargs.get("passfile")
|
|
194
|
+
self.direct_tls = kwargs.get("direct_tls")
|
|
195
|
+
self.connect_timeout = kwargs.get("connect_timeout")
|
|
196
|
+
self.command_timeout = kwargs.get("command_timeout")
|
|
197
|
+
self.statement_cache_size = kwargs.get("statement_cache_size")
|
|
198
|
+
self.max_cached_statement_lifetime = kwargs.get("max_cached_statement_lifetime")
|
|
199
|
+
self.max_cacheable_statement_size = kwargs.get("max_cacheable_statement_size")
|
|
200
|
+
self.server_settings = kwargs.get("server_settings")
|
|
201
|
+
self.min_size = kwargs.get("min_size")
|
|
202
|
+
self.max_size = kwargs.get("max_size")
|
|
203
|
+
self.max_queries = kwargs.get("max_queries")
|
|
204
|
+
self.max_inactive_connection_lifetime = kwargs.get("max_inactive_connection_lifetime")
|
|
205
|
+
self.setup = kwargs.get("setup")
|
|
206
|
+
self.init = kwargs.get("init")
|
|
207
|
+
self.loop = kwargs.get("loop")
|
|
208
|
+
self.connection_class = kwargs.get("connection_class")
|
|
209
|
+
self.record_class = kwargs.get("record_class")
|
|
210
|
+
|
|
211
|
+
# Collect unknown parameters into extras
|
|
212
|
+
provided_extras = kwargs.get("extras", {})
|
|
213
|
+
unknown_params = {k: v for k, v in kwargs.items() if k not in known_fields}
|
|
214
|
+
self.extras = {**provided_extras, **unknown_params}
|
|
215
|
+
|
|
216
|
+
self.statement_config = (
|
|
217
|
+
SQLConfig() if kwargs.get("statement_config") is None else kwargs.get("statement_config")
|
|
218
|
+
)
|
|
219
|
+
self.default_row_type = kwargs.get("default_row_type", dict[str, Any])
|
|
220
|
+
self.json_serializer = kwargs.get("json_serializer", to_json)
|
|
221
|
+
self.json_deserializer = kwargs.get("json_deserializer", from_json)
|
|
222
|
+
pool_instance_from_kwargs = kwargs.get("pool_instance")
|
|
223
|
+
self._dialect: DialectType = None
|
|
224
|
+
|
|
225
|
+
super().__init__()
|
|
226
|
+
|
|
227
|
+
# Set pool_instance after super().__init__() to ensure it's not overridden
|
|
228
|
+
if pool_instance_from_kwargs is not None:
|
|
229
|
+
self.pool_instance = pool_instance_from_kwargs
|
|
99
230
|
|
|
100
|
-
|
|
101
|
-
|
|
231
|
+
@property
|
|
232
|
+
def connection_config_dict(self) -> dict[str, Any]:
|
|
233
|
+
"""Return the connection configuration as a dict for asyncpg.connect().
|
|
102
234
|
|
|
103
|
-
|
|
104
|
-
ImproperConfigurationError: If the connection configuration is not provided.
|
|
235
|
+
This method filters out pool-specific parameters that are not valid for asyncpg.connect().
|
|
105
236
|
"""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if
|
|
111
|
-
|
|
237
|
+
# Gather non-None connection parameters
|
|
238
|
+
config = {
|
|
239
|
+
field: getattr(self, field)
|
|
240
|
+
for field in CONNECTION_FIELDS
|
|
241
|
+
if getattr(self, field, None) is not None and getattr(self, field) is not Empty
|
|
242
|
+
}
|
|
112
243
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
hasattr(self.pool_config, "connect_kwargs")
|
|
116
|
-
and self.pool_config.connect_kwargs is not Empty
|
|
117
|
-
and isinstance(self.pool_config.connect_kwargs, dict)
|
|
118
|
-
):
|
|
119
|
-
connect_dict.update(dict(self.pool_config.connect_kwargs.items()))
|
|
244
|
+
# Add connection-specific extras (not pool-specific ones)
|
|
245
|
+
config.update(self.extras)
|
|
120
246
|
|
|
121
|
-
|
|
122
|
-
msg = "You must provide a 'pool_config' for this adapter."
|
|
123
|
-
raise ImproperConfigurationError(msg)
|
|
247
|
+
return config
|
|
124
248
|
|
|
125
249
|
@property
|
|
126
|
-
def pool_config_dict(self) ->
|
|
127
|
-
"""Return the pool configuration as a dict.
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
A string keyed dict of config kwargs for the Asyncpg :func:`create_pool <asyncpg.pool.create_pool>`
|
|
131
|
-
function.
|
|
132
|
-
|
|
133
|
-
Raises:
|
|
134
|
-
ImproperConfigurationError: If no pool_config is provided but a pool_instance is set.
|
|
135
|
-
"""
|
|
136
|
-
if self.pool_config:
|
|
137
|
-
return dataclass_to_dict(
|
|
138
|
-
self.pool_config,
|
|
139
|
-
exclude_empty=True,
|
|
140
|
-
exclude={"pool_instance", "driver_type", "connection_type"},
|
|
141
|
-
convert_nested=False,
|
|
142
|
-
)
|
|
143
|
-
msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
|
|
144
|
-
raise ImproperConfigurationError(msg)
|
|
145
|
-
|
|
146
|
-
async def create_pool(self) -> "Pool": # pyright: ignore[reportMissingTypeArgument,reportUnknownParameterType]
|
|
147
|
-
"""Return a pool. If none exists yet, create one.
|
|
250
|
+
def pool_config_dict(self) -> dict[str, Any]:
|
|
251
|
+
"""Return the full pool configuration as a dict for asyncpg.create_pool().
|
|
148
252
|
|
|
149
253
|
Returns:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
Raises:
|
|
153
|
-
ImproperConfigurationError: If neither pool_config nor pool_instance are provided,
|
|
154
|
-
or if the pool could not be configured.
|
|
254
|
+
A dictionary containing all pool configuration parameters.
|
|
155
255
|
"""
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
self.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
256
|
+
# All AsyncPG parameter names (connection + pool)
|
|
257
|
+
config = {
|
|
258
|
+
field: getattr(self, field)
|
|
259
|
+
for field in POOL_FIELDS
|
|
260
|
+
if getattr(self, field, None) is not None and getattr(self, field) is not Empty
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
# Merge extras parameters
|
|
264
|
+
config.update(self.extras)
|
|
265
|
+
|
|
266
|
+
return config
|
|
267
|
+
|
|
268
|
+
async def _create_pool(self) -> "Pool[Record]":
|
|
269
|
+
"""Create the actual async connection pool."""
|
|
270
|
+
pool_args = self.pool_config_dict
|
|
271
|
+
return await asyncpg_create_pool(**pool_args)
|
|
272
|
+
|
|
273
|
+
async def _close_pool(self) -> None:
|
|
274
|
+
"""Close the actual async connection pool."""
|
|
275
|
+
if self.pool_instance:
|
|
276
|
+
await self.pool_instance.close()
|
|
169
277
|
|
|
170
|
-
def
|
|
171
|
-
"""Create a pool
|
|
278
|
+
async def create_connection(self) -> AsyncpgConnection:
|
|
279
|
+
"""Create a single async connection (not from pool).
|
|
172
280
|
|
|
173
281
|
Returns:
|
|
174
|
-
|
|
282
|
+
An AsyncPG connection instance.
|
|
175
283
|
"""
|
|
176
|
-
|
|
284
|
+
if self.pool_instance is None:
|
|
285
|
+
self.pool_instance = await self._create_pool()
|
|
286
|
+
return await self.pool_instance.acquire()
|
|
177
287
|
|
|
178
|
-
|
|
179
|
-
|
|
288
|
+
@asynccontextmanager
|
|
289
|
+
async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[AsyncpgConnection, None]:
|
|
290
|
+
"""Provide an async connection context manager.
|
|
180
291
|
|
|
181
|
-
|
|
182
|
-
|
|
292
|
+
Args:
|
|
293
|
+
*args: Additional arguments.
|
|
294
|
+
**kwargs: Additional keyword arguments.
|
|
183
295
|
|
|
184
|
-
|
|
185
|
-
|
|
296
|
+
Yields:
|
|
297
|
+
An AsyncPG connection instance.
|
|
186
298
|
"""
|
|
299
|
+
if self.pool_instance is None:
|
|
300
|
+
self.pool_instance = await self._create_pool()
|
|
301
|
+
connection = None
|
|
187
302
|
try:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
303
|
+
connection = await self.pool_instance.acquire()
|
|
304
|
+
yield connection
|
|
305
|
+
finally:
|
|
306
|
+
if connection is not None:
|
|
307
|
+
await self.pool_instance.release(connection)
|
|
193
308
|
|
|
194
309
|
@asynccontextmanager
|
|
195
|
-
async def
|
|
196
|
-
"""
|
|
310
|
+
async def provide_session(self, *args: Any, **kwargs: Any) -> AsyncGenerator[AsyncpgDriver, None]:
|
|
311
|
+
"""Provide an async driver session context manager.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
*args: Additional arguments.
|
|
315
|
+
**kwargs: Additional keyword arguments.
|
|
197
316
|
|
|
198
317
|
Yields:
|
|
199
|
-
|
|
318
|
+
An AsyncpgDriver instance.
|
|
200
319
|
"""
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
320
|
+
async with self.provide_connection(*args, **kwargs) as connection:
|
|
321
|
+
# Create statement config with parameter style info if not already set
|
|
322
|
+
statement_config = self.statement_config
|
|
323
|
+
if statement_config is not None and statement_config.allowed_parameter_styles is None:
|
|
324
|
+
statement_config = replace(
|
|
325
|
+
statement_config,
|
|
326
|
+
allowed_parameter_styles=self.supported_parameter_styles,
|
|
327
|
+
target_parameter_style=self.preferred_parameter_style,
|
|
328
|
+
)
|
|
204
329
|
|
|
205
|
-
|
|
206
|
-
"""Close the pool."""
|
|
207
|
-
if self.pool_instance is not None:
|
|
208
|
-
await self.pool_instance.close()
|
|
209
|
-
self.pool_instance = None
|
|
330
|
+
yield self.driver_type(connection=connection, config=statement_config)
|
|
210
331
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
"""Create and provide a database session.
|
|
332
|
+
async def provide_pool(self, *args: Any, **kwargs: Any) -> "Pool[Record]":
|
|
333
|
+
"""Provide async pool instance.
|
|
214
334
|
|
|
215
|
-
|
|
216
|
-
|
|
335
|
+
Returns:
|
|
336
|
+
The async connection pool.
|
|
337
|
+
"""
|
|
338
|
+
if not self.pool_instance:
|
|
339
|
+
self.pool_instance = await self.create_pool()
|
|
340
|
+
return self.pool_instance
|
|
341
|
+
|
|
342
|
+
def get_signature_namespace(self) -> "dict[str, type[Any]]":
|
|
343
|
+
"""Get the signature namespace for AsyncPG types.
|
|
217
344
|
|
|
345
|
+
This provides all AsyncPG-specific types that Litestar needs to recognize
|
|
346
|
+
to avoid serialization attempts.
|
|
218
347
|
|
|
348
|
+
Returns:
|
|
349
|
+
Dictionary mapping type names to types.
|
|
219
350
|
"""
|
|
220
|
-
|
|
221
|
-
|
|
351
|
+
# Get base types from parent
|
|
352
|
+
namespace = super().get_signature_namespace()
|
|
353
|
+
|
|
354
|
+
# Add AsyncPG-specific types
|
|
355
|
+
try:
|
|
356
|
+
from asyncpg import Connection, Record
|
|
357
|
+
from asyncpg.connection import ConnectionMeta
|
|
358
|
+
from asyncpg.pool import Pool, PoolConnectionProxy, PoolConnectionProxyMeta
|
|
359
|
+
|
|
360
|
+
namespace.update(
|
|
361
|
+
{
|
|
362
|
+
"Connection": Connection,
|
|
363
|
+
"Pool": Pool,
|
|
364
|
+
"PoolConnectionProxy": PoolConnectionProxy,
|
|
365
|
+
"PoolConnectionProxyMeta": PoolConnectionProxyMeta,
|
|
366
|
+
"ConnectionMeta": ConnectionMeta,
|
|
367
|
+
"Record": Record,
|
|
368
|
+
"AsyncpgConnection": type(AsyncpgConnection), # The Union type alias
|
|
369
|
+
}
|
|
370
|
+
)
|
|
371
|
+
except ImportError:
|
|
372
|
+
logger.warning("Failed to import AsyncPG types for signature namespace")
|
|
373
|
+
|
|
374
|
+
return namespace
|