sqlspec 0.11.0__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 -644
- sqlspec/adapters/aiosqlite/__init__.py +2 -6
- sqlspec/adapters/aiosqlite/config.py +143 -57
- sqlspec/adapters/aiosqlite/driver.py +269 -462
- sqlspec/adapters/asyncmy/__init__.py +3 -8
- sqlspec/adapters/asyncmy/config.py +247 -202
- sqlspec/adapters/asyncmy/driver.py +217 -451
- sqlspec/adapters/asyncpg/__init__.py +4 -7
- sqlspec/adapters/asyncpg/config.py +329 -176
- sqlspec/adapters/asyncpg/driver.py +418 -498
- sqlspec/adapters/bigquery/__init__.py +2 -2
- sqlspec/adapters/bigquery/config.py +407 -0
- sqlspec/adapters/bigquery/driver.py +592 -634
- sqlspec/adapters/duckdb/__init__.py +4 -1
- sqlspec/adapters/duckdb/config.py +432 -321
- sqlspec/adapters/duckdb/driver.py +393 -436
- sqlspec/adapters/oracledb/__init__.py +3 -8
- sqlspec/adapters/oracledb/config.py +625 -0
- sqlspec/adapters/oracledb/driver.py +549 -942
- sqlspec/adapters/psqlpy/__init__.py +4 -7
- sqlspec/adapters/psqlpy/config.py +372 -203
- sqlspec/adapters/psqlpy/driver.py +197 -550
- sqlspec/adapters/psycopg/__init__.py +3 -8
- sqlspec/adapters/psycopg/config.py +741 -0
- sqlspec/adapters/psycopg/driver.py +732 -733
- sqlspec/adapters/sqlite/__init__.py +2 -6
- sqlspec/adapters/sqlite/config.py +146 -81
- sqlspec/adapters/sqlite/driver.py +243 -426
- sqlspec/base.py +220 -825
- 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.0.dist-info → sqlspec-0.12.0.dist-info}/METADATA +100 -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 -330
- sqlspec/mixins.py +0 -306
- sqlspec/statement.py +0 -378
- sqlspec-0.11.0.dist-info/RECORD +0 -69
- {sqlspec-0.11.0.dist-info → sqlspec-0.12.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.11.0.dist-info → sqlspec-0.12.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.11.0.dist-info → sqlspec-0.12.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
"""Psycopg database configuration with direct field-based configuration."""
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import logging
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
6
|
+
from dataclasses import replace
|
|
7
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, cast
|
|
8
|
+
|
|
9
|
+
from psycopg.rows import dict_row
|
|
10
|
+
from psycopg_pool import AsyncConnectionPool, ConnectionPool
|
|
11
|
+
|
|
12
|
+
from sqlspec.adapters.psycopg.driver import (
|
|
13
|
+
PsycopgAsyncConnection,
|
|
14
|
+
PsycopgAsyncDriver,
|
|
15
|
+
PsycopgSyncConnection,
|
|
16
|
+
PsycopgSyncDriver,
|
|
17
|
+
)
|
|
18
|
+
from sqlspec.config import AsyncDatabaseConfig, SyncDatabaseConfig
|
|
19
|
+
from sqlspec.statement.sql import SQLConfig
|
|
20
|
+
from sqlspec.typing import DictRow, Empty
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from collections.abc import AsyncGenerator, Callable, Generator
|
|
24
|
+
|
|
25
|
+
from psycopg import Connection
|
|
26
|
+
from sqlglot.dialects.dialect import DialectType
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("sqlspec.adapters.psycopg")
|
|
29
|
+
|
|
30
|
+
CONNECTION_FIELDS = frozenset(
|
|
31
|
+
{
|
|
32
|
+
"conninfo",
|
|
33
|
+
"host",
|
|
34
|
+
"port",
|
|
35
|
+
"user",
|
|
36
|
+
"password",
|
|
37
|
+
"dbname",
|
|
38
|
+
"connect_timeout",
|
|
39
|
+
"options",
|
|
40
|
+
"application_name",
|
|
41
|
+
"sslmode",
|
|
42
|
+
"sslcert",
|
|
43
|
+
"sslkey",
|
|
44
|
+
"sslrootcert",
|
|
45
|
+
"autocommit",
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
POOL_FIELDS = CONNECTION_FIELDS.union(
|
|
50
|
+
{
|
|
51
|
+
"min_size",
|
|
52
|
+
"max_size",
|
|
53
|
+
"name",
|
|
54
|
+
"timeout",
|
|
55
|
+
"max_waiting",
|
|
56
|
+
"max_lifetime",
|
|
57
|
+
"max_idle",
|
|
58
|
+
"reconnect_timeout",
|
|
59
|
+
"num_workers",
|
|
60
|
+
"configure",
|
|
61
|
+
"kwargs",
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
__all__ = ("CONNECTION_FIELDS", "POOL_FIELDS", "PsycopgAsyncConfig", "PsycopgSyncConfig")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool, PsycopgSyncDriver]):
|
|
69
|
+
"""Configuration for Psycopg synchronous database connections with direct field-based configuration."""
|
|
70
|
+
|
|
71
|
+
__slots__ = (
|
|
72
|
+
"_dialect",
|
|
73
|
+
"application_name",
|
|
74
|
+
"autocommit",
|
|
75
|
+
"configure",
|
|
76
|
+
"connect_timeout",
|
|
77
|
+
"conninfo",
|
|
78
|
+
"dbname",
|
|
79
|
+
"default_row_type",
|
|
80
|
+
"extras",
|
|
81
|
+
"host",
|
|
82
|
+
"kwargs",
|
|
83
|
+
"max_idle",
|
|
84
|
+
"max_lifetime",
|
|
85
|
+
"max_size",
|
|
86
|
+
"max_waiting",
|
|
87
|
+
"min_size",
|
|
88
|
+
"name",
|
|
89
|
+
"num_workers",
|
|
90
|
+
"options",
|
|
91
|
+
"password",
|
|
92
|
+
"pool_instance",
|
|
93
|
+
"port",
|
|
94
|
+
"reconnect_timeout",
|
|
95
|
+
"sslcert",
|
|
96
|
+
"sslkey",
|
|
97
|
+
"sslmode",
|
|
98
|
+
"sslrootcert",
|
|
99
|
+
"statement_config",
|
|
100
|
+
"timeout",
|
|
101
|
+
"user",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
is_async: ClassVar[bool] = False
|
|
105
|
+
supports_connection_pooling: ClassVar[bool] = True
|
|
106
|
+
|
|
107
|
+
# Driver class reference for dialect resolution
|
|
108
|
+
driver_type: type[PsycopgSyncDriver] = PsycopgSyncDriver
|
|
109
|
+
connection_type: type[PsycopgSyncConnection] = PsycopgSyncConnection
|
|
110
|
+
# Parameter style support information
|
|
111
|
+
supported_parameter_styles: ClassVar[tuple[str, ...]] = ("pyformat_positional", "pyformat_named")
|
|
112
|
+
"""Psycopg supports %s (positional) and %(name)s (named) parameter styles."""
|
|
113
|
+
|
|
114
|
+
preferred_parameter_style: ClassVar[str] = "pyformat_positional"
|
|
115
|
+
"""Psycopg's native parameter style is %s (pyformat positional)."""
|
|
116
|
+
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
statement_config: "Optional[SQLConfig]" = None,
|
|
120
|
+
default_row_type: "type[DictRow]" = DictRow,
|
|
121
|
+
# Connection parameters
|
|
122
|
+
conninfo: Optional[str] = None,
|
|
123
|
+
host: Optional[str] = None,
|
|
124
|
+
port: Optional[int] = None,
|
|
125
|
+
user: Optional[str] = None,
|
|
126
|
+
password: Optional[str] = None,
|
|
127
|
+
dbname: Optional[str] = None,
|
|
128
|
+
connect_timeout: Optional[float] = None,
|
|
129
|
+
options: Optional[str] = None,
|
|
130
|
+
application_name: Optional[str] = None,
|
|
131
|
+
sslmode: Optional[str] = None,
|
|
132
|
+
sslcert: Optional[str] = None,
|
|
133
|
+
sslkey: Optional[str] = None,
|
|
134
|
+
sslrootcert: Optional[str] = None,
|
|
135
|
+
autocommit: Optional[bool] = None,
|
|
136
|
+
# Pool parameters
|
|
137
|
+
min_size: Optional[int] = None,
|
|
138
|
+
max_size: Optional[int] = None,
|
|
139
|
+
name: Optional[str] = None,
|
|
140
|
+
timeout: Optional[float] = None,
|
|
141
|
+
max_waiting: Optional[int] = None,
|
|
142
|
+
max_lifetime: Optional[float] = None,
|
|
143
|
+
max_idle: Optional[float] = None,
|
|
144
|
+
reconnect_timeout: Optional[float] = None,
|
|
145
|
+
num_workers: Optional[int] = None,
|
|
146
|
+
configure: Optional["Callable[[Connection[Any]], None]"] = None,
|
|
147
|
+
kwargs: Optional[dict[str, Any]] = None,
|
|
148
|
+
# User-defined extras
|
|
149
|
+
extras: Optional[dict[str, Any]] = None,
|
|
150
|
+
**additional_kwargs: Any,
|
|
151
|
+
) -> None:
|
|
152
|
+
"""Initialize Psycopg synchronous configuration.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
statement_config: Default SQL statement configuration
|
|
156
|
+
default_row_type: Default row type for results
|
|
157
|
+
conninfo: Connection string in libpq format
|
|
158
|
+
host: Database server host
|
|
159
|
+
port: Database server port
|
|
160
|
+
user: Database user
|
|
161
|
+
password: Database password
|
|
162
|
+
dbname: Database name
|
|
163
|
+
connect_timeout: Connection timeout in seconds
|
|
164
|
+
options: Command-line options to send to the server
|
|
165
|
+
application_name: Application name for logging and statistics
|
|
166
|
+
sslmode: SSL mode (disable, prefer, require, etc.)
|
|
167
|
+
sslcert: SSL client certificate file
|
|
168
|
+
sslkey: SSL client private key file
|
|
169
|
+
sslrootcert: SSL root certificate file
|
|
170
|
+
autocommit: Enable autocommit mode
|
|
171
|
+
min_size: Minimum number of connections in the pool
|
|
172
|
+
max_size: Maximum number of connections in the pool
|
|
173
|
+
name: Name of the connection pool
|
|
174
|
+
timeout: Timeout for acquiring connections
|
|
175
|
+
max_waiting: Maximum number of waiting clients
|
|
176
|
+
max_lifetime: Maximum connection lifetime
|
|
177
|
+
max_idle: Maximum idle time for connections
|
|
178
|
+
reconnect_timeout: Time between reconnection attempts
|
|
179
|
+
num_workers: Number of background workers
|
|
180
|
+
configure: Callback to configure new connections
|
|
181
|
+
kwargs: Additional connection parameters
|
|
182
|
+
extras: Additional connection parameters not explicitly defined
|
|
183
|
+
**additional_kwargs: Additional parameters (stored in extras)
|
|
184
|
+
"""
|
|
185
|
+
# Store connection parameters as instance attributes
|
|
186
|
+
self.conninfo = conninfo
|
|
187
|
+
self.host = host
|
|
188
|
+
self.port = port
|
|
189
|
+
self.user = user
|
|
190
|
+
self.password = password
|
|
191
|
+
self.dbname = dbname
|
|
192
|
+
self.connect_timeout = connect_timeout
|
|
193
|
+
self.options = options
|
|
194
|
+
self.application_name = application_name
|
|
195
|
+
self.sslmode = sslmode
|
|
196
|
+
self.sslcert = sslcert
|
|
197
|
+
self.sslkey = sslkey
|
|
198
|
+
self.sslrootcert = sslrootcert
|
|
199
|
+
self.autocommit = autocommit
|
|
200
|
+
|
|
201
|
+
# Store pool parameters as instance attributes
|
|
202
|
+
self.min_size = min_size
|
|
203
|
+
self.max_size = max_size
|
|
204
|
+
self.name = name
|
|
205
|
+
self.timeout = timeout
|
|
206
|
+
self.max_waiting = max_waiting
|
|
207
|
+
self.max_lifetime = max_lifetime
|
|
208
|
+
self.max_idle = max_idle
|
|
209
|
+
self.reconnect_timeout = reconnect_timeout
|
|
210
|
+
self.num_workers = num_workers
|
|
211
|
+
self.configure = configure
|
|
212
|
+
self.kwargs = kwargs or {}
|
|
213
|
+
|
|
214
|
+
# Handle extras and additional kwargs
|
|
215
|
+
self.extras = extras or {}
|
|
216
|
+
self.extras.update(additional_kwargs)
|
|
217
|
+
|
|
218
|
+
# Store other config
|
|
219
|
+
self.statement_config = statement_config or SQLConfig()
|
|
220
|
+
self.default_row_type = default_row_type
|
|
221
|
+
self._dialect: DialectType = None
|
|
222
|
+
|
|
223
|
+
super().__init__()
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def connection_config_dict(self) -> dict[str, Any]:
|
|
227
|
+
"""Return the connection configuration as a dict for psycopg operations.
|
|
228
|
+
|
|
229
|
+
Returns only connection-specific parameters.
|
|
230
|
+
"""
|
|
231
|
+
# Gather non-None parameters from connection fields only
|
|
232
|
+
config = {
|
|
233
|
+
field: getattr(self, field)
|
|
234
|
+
for field in CONNECTION_FIELDS
|
|
235
|
+
if getattr(self, field, None) is not None and getattr(self, field) is not Empty
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# Merge extras and kwargs
|
|
239
|
+
config.update(self.extras)
|
|
240
|
+
if self.kwargs:
|
|
241
|
+
config.update(self.kwargs)
|
|
242
|
+
|
|
243
|
+
# Set DictRow as the row factory
|
|
244
|
+
config["row_factory"] = dict_row
|
|
245
|
+
|
|
246
|
+
return config
|
|
247
|
+
|
|
248
|
+
@property
|
|
249
|
+
def pool_config_dict(self) -> dict[str, Any]:
|
|
250
|
+
"""Return the pool configuration as a dict for psycopg pool operations.
|
|
251
|
+
|
|
252
|
+
Returns all configuration parameters including connection and pool-specific parameters.
|
|
253
|
+
"""
|
|
254
|
+
# Gather non-None parameters from all fields (connection + pool)
|
|
255
|
+
config = {
|
|
256
|
+
field: getattr(self, field)
|
|
257
|
+
for field in POOL_FIELDS
|
|
258
|
+
if getattr(self, field, None) is not None and getattr(self, field) is not Empty
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Merge extras and kwargs
|
|
262
|
+
config.update(self.extras)
|
|
263
|
+
if self.kwargs:
|
|
264
|
+
config.update(self.kwargs)
|
|
265
|
+
|
|
266
|
+
# Set DictRow as the row factory
|
|
267
|
+
config["row_factory"] = dict_row
|
|
268
|
+
|
|
269
|
+
return config
|
|
270
|
+
|
|
271
|
+
def _create_pool(self) -> "ConnectionPool":
|
|
272
|
+
"""Create the actual connection pool."""
|
|
273
|
+
logger.info("Creating Psycopg connection pool", extra={"adapter": "psycopg"})
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
# Get all config (creates a new dict)
|
|
277
|
+
all_config = self.pool_config_dict.copy()
|
|
278
|
+
|
|
279
|
+
# Separate pool-specific parameters that ConnectionPool accepts directly
|
|
280
|
+
pool_params = {
|
|
281
|
+
"min_size": all_config.pop("min_size", 4),
|
|
282
|
+
"max_size": all_config.pop("max_size", None),
|
|
283
|
+
"name": all_config.pop("name", None),
|
|
284
|
+
"timeout": all_config.pop("timeout", 30.0),
|
|
285
|
+
"max_waiting": all_config.pop("max_waiting", 0),
|
|
286
|
+
"max_lifetime": all_config.pop("max_lifetime", 3600.0),
|
|
287
|
+
"max_idle": all_config.pop("max_idle", 600.0),
|
|
288
|
+
"reconnect_timeout": all_config.pop("reconnect_timeout", 300.0),
|
|
289
|
+
"num_workers": all_config.pop("num_workers", 3),
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# Create a configure callback to set row_factory
|
|
293
|
+
def configure_connection(conn: "PsycopgSyncConnection") -> None:
|
|
294
|
+
# Set DictRow as the row factory
|
|
295
|
+
conn.row_factory = dict_row
|
|
296
|
+
|
|
297
|
+
pool_params["configure"] = all_config.pop("configure", configure_connection)
|
|
298
|
+
|
|
299
|
+
# Remove None values from pool_params
|
|
300
|
+
pool_params = {k: v for k, v in pool_params.items() if v is not None}
|
|
301
|
+
|
|
302
|
+
# Handle conninfo vs individual connection parameters
|
|
303
|
+
conninfo = all_config.pop("conninfo", None)
|
|
304
|
+
if conninfo:
|
|
305
|
+
# If conninfo is provided, use it directly
|
|
306
|
+
# Don't pass kwargs when using conninfo string
|
|
307
|
+
pool = ConnectionPool(conninfo, **pool_params)
|
|
308
|
+
else:
|
|
309
|
+
# Otherwise, pass connection parameters via kwargs
|
|
310
|
+
# Remove any non-connection parameters
|
|
311
|
+
# row_factory is already popped out earlier
|
|
312
|
+
all_config.pop("row_factory", None)
|
|
313
|
+
# Remove pool-specific settings that may have been left
|
|
314
|
+
all_config.pop("kwargs", None)
|
|
315
|
+
pool = ConnectionPool("", kwargs=all_config, **pool_params)
|
|
316
|
+
|
|
317
|
+
logger.info("Psycopg connection pool created successfully", extra={"adapter": "psycopg"})
|
|
318
|
+
except Exception as e:
|
|
319
|
+
logger.exception("Failed to create Psycopg connection pool", extra={"adapter": "psycopg", "error": str(e)})
|
|
320
|
+
raise
|
|
321
|
+
return pool
|
|
322
|
+
|
|
323
|
+
def _close_pool(self) -> None:
|
|
324
|
+
"""Close the actual connection pool."""
|
|
325
|
+
if not self.pool_instance:
|
|
326
|
+
return
|
|
327
|
+
|
|
328
|
+
logger.info("Closing Psycopg connection pool", extra={"adapter": "psycopg"})
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
self.pool_instance.close()
|
|
332
|
+
logger.info("Psycopg connection pool closed successfully", extra={"adapter": "psycopg"})
|
|
333
|
+
except Exception as e:
|
|
334
|
+
logger.exception("Failed to close Psycopg connection pool", extra={"adapter": "psycopg", "error": str(e)})
|
|
335
|
+
raise
|
|
336
|
+
|
|
337
|
+
def create_connection(self) -> "PsycopgSyncConnection":
|
|
338
|
+
"""Create a single connection (not from pool).
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
A psycopg Connection instance configured with DictRow.
|
|
342
|
+
"""
|
|
343
|
+
if self.pool_instance is None:
|
|
344
|
+
self.pool_instance = self.create_pool()
|
|
345
|
+
return cast("PsycopgSyncConnection", self.pool_instance.getconn()) # pyright: ignore
|
|
346
|
+
|
|
347
|
+
@contextlib.contextmanager
|
|
348
|
+
def provide_connection(self, *args: Any, **kwargs: Any) -> "Generator[PsycopgSyncConnection, None, None]":
|
|
349
|
+
"""Provide a connection context manager.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
*args: Additional arguments.
|
|
353
|
+
**kwargs: Additional keyword arguments.
|
|
354
|
+
|
|
355
|
+
Yields:
|
|
356
|
+
A psycopg Connection instance.
|
|
357
|
+
"""
|
|
358
|
+
if self.pool_instance:
|
|
359
|
+
with self.pool_instance.connection() as conn:
|
|
360
|
+
yield conn # type: ignore[misc]
|
|
361
|
+
else:
|
|
362
|
+
conn = self.create_connection() # type: ignore[assignment]
|
|
363
|
+
try:
|
|
364
|
+
yield conn # type: ignore[misc]
|
|
365
|
+
finally:
|
|
366
|
+
conn.close()
|
|
367
|
+
|
|
368
|
+
@contextlib.contextmanager
|
|
369
|
+
def provide_session(self, *args: Any, **kwargs: Any) -> "Generator[PsycopgSyncDriver, None, None]":
|
|
370
|
+
"""Provide a driver session context manager.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
*args: Additional arguments.
|
|
374
|
+
**kwargs: Additional keyword arguments.
|
|
375
|
+
|
|
376
|
+
Yields:
|
|
377
|
+
A PsycopgSyncDriver instance.
|
|
378
|
+
"""
|
|
379
|
+
with self.provide_connection(*args, **kwargs) as conn:
|
|
380
|
+
# Create statement config with parameter style info if not already set
|
|
381
|
+
statement_config = self.statement_config
|
|
382
|
+
if statement_config.allowed_parameter_styles is None:
|
|
383
|
+
statement_config = replace(
|
|
384
|
+
statement_config,
|
|
385
|
+
allowed_parameter_styles=self.supported_parameter_styles,
|
|
386
|
+
target_parameter_style=self.preferred_parameter_style,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
driver = self.driver_type(connection=conn, config=statement_config)
|
|
390
|
+
yield driver
|
|
391
|
+
|
|
392
|
+
def provide_pool(self, *args: Any, **kwargs: Any) -> "ConnectionPool":
|
|
393
|
+
"""Provide pool instance.
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
The connection pool.
|
|
397
|
+
"""
|
|
398
|
+
if not self.pool_instance:
|
|
399
|
+
self.pool_instance = self.create_pool()
|
|
400
|
+
return self.pool_instance
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnectionPool, PsycopgAsyncDriver]):
|
|
404
|
+
"""Configuration for Psycopg asynchronous database connections with direct field-based configuration."""
|
|
405
|
+
|
|
406
|
+
__slots__ = (
|
|
407
|
+
"_dialect",
|
|
408
|
+
"application_name",
|
|
409
|
+
"autocommit",
|
|
410
|
+
"configure",
|
|
411
|
+
"connect_timeout",
|
|
412
|
+
"conninfo",
|
|
413
|
+
"dbname",
|
|
414
|
+
"default_row_type",
|
|
415
|
+
"extras",
|
|
416
|
+
"host",
|
|
417
|
+
"kwargs",
|
|
418
|
+
"max_idle",
|
|
419
|
+
"max_lifetime",
|
|
420
|
+
"max_size",
|
|
421
|
+
"max_waiting",
|
|
422
|
+
"min_size",
|
|
423
|
+
"name",
|
|
424
|
+
"num_workers",
|
|
425
|
+
"options",
|
|
426
|
+
"password",
|
|
427
|
+
"pool_instance",
|
|
428
|
+
"port",
|
|
429
|
+
"reconnect_timeout",
|
|
430
|
+
"sslcert",
|
|
431
|
+
"sslkey",
|
|
432
|
+
"sslmode",
|
|
433
|
+
"sslrootcert",
|
|
434
|
+
"statement_config",
|
|
435
|
+
"timeout",
|
|
436
|
+
"user",
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
is_async: ClassVar[bool] = True
|
|
440
|
+
supports_connection_pooling: ClassVar[bool] = True
|
|
441
|
+
|
|
442
|
+
# Driver class reference for dialect resolution
|
|
443
|
+
driver_type: type[PsycopgAsyncDriver] = PsycopgAsyncDriver
|
|
444
|
+
connection_type: type[PsycopgAsyncConnection] = PsycopgAsyncConnection
|
|
445
|
+
|
|
446
|
+
# Parameter style support information
|
|
447
|
+
supported_parameter_styles: ClassVar[tuple[str, ...]] = ("pyformat_positional", "pyformat_named")
|
|
448
|
+
"""Psycopg supports %s (pyformat_positional) and %(name)s (pyformat_named) parameter styles."""
|
|
449
|
+
|
|
450
|
+
preferred_parameter_style: ClassVar[str] = "pyformat_positional"
|
|
451
|
+
"""Psycopg's preferred parameter style is %s (pyformat_positional)."""
|
|
452
|
+
|
|
453
|
+
def __init__(
|
|
454
|
+
self,
|
|
455
|
+
statement_config: "Optional[SQLConfig]" = None,
|
|
456
|
+
default_row_type: "type[DictRow]" = DictRow,
|
|
457
|
+
# Connection parameters
|
|
458
|
+
conninfo: Optional[str] = None,
|
|
459
|
+
host: Optional[str] = None,
|
|
460
|
+
port: Optional[int] = None,
|
|
461
|
+
user: Optional[str] = None,
|
|
462
|
+
password: Optional[str] = None,
|
|
463
|
+
dbname: Optional[str] = None,
|
|
464
|
+
connect_timeout: Optional[float] = None,
|
|
465
|
+
options: Optional[str] = None,
|
|
466
|
+
application_name: Optional[str] = None,
|
|
467
|
+
sslmode: Optional[str] = None,
|
|
468
|
+
sslcert: Optional[str] = None,
|
|
469
|
+
sslkey: Optional[str] = None,
|
|
470
|
+
sslrootcert: Optional[str] = None,
|
|
471
|
+
autocommit: Optional[bool] = None,
|
|
472
|
+
# Pool parameters
|
|
473
|
+
min_size: Optional[int] = None,
|
|
474
|
+
max_size: Optional[int] = None,
|
|
475
|
+
name: Optional[str] = None,
|
|
476
|
+
timeout: Optional[float] = None,
|
|
477
|
+
max_waiting: Optional[int] = None,
|
|
478
|
+
max_lifetime: Optional[float] = None,
|
|
479
|
+
max_idle: Optional[float] = None,
|
|
480
|
+
reconnect_timeout: Optional[float] = None,
|
|
481
|
+
num_workers: Optional[int] = None,
|
|
482
|
+
configure: Optional["Callable[[Connection[Any]], None]"] = None,
|
|
483
|
+
kwargs: Optional[dict[str, Any]] = None,
|
|
484
|
+
# User-defined extras
|
|
485
|
+
extras: Optional[dict[str, Any]] = None,
|
|
486
|
+
**additional_kwargs: Any,
|
|
487
|
+
) -> None:
|
|
488
|
+
"""Initialize Psycopg asynchronous configuration.
|
|
489
|
+
|
|
490
|
+
Args:
|
|
491
|
+
statement_config: Default SQL statement configuration
|
|
492
|
+
default_row_type: Default row type for results
|
|
493
|
+
conninfo: Connection string in libpq format
|
|
494
|
+
host: Database server host
|
|
495
|
+
port: Database server port
|
|
496
|
+
user: Database user
|
|
497
|
+
password: Database password
|
|
498
|
+
dbname: Database name
|
|
499
|
+
connect_timeout: Connection timeout in seconds
|
|
500
|
+
options: Command-line options to send to the server
|
|
501
|
+
application_name: Application name for logging and statistics
|
|
502
|
+
sslmode: SSL mode (disable, prefer, require, etc.)
|
|
503
|
+
sslcert: SSL client certificate file
|
|
504
|
+
sslkey: SSL client private key file
|
|
505
|
+
sslrootcert: SSL root certificate file
|
|
506
|
+
autocommit: Enable autocommit mode
|
|
507
|
+
min_size: Minimum number of connections in the pool
|
|
508
|
+
max_size: Maximum number of connections in the pool
|
|
509
|
+
name: Name of the connection pool
|
|
510
|
+
timeout: Timeout for acquiring connections
|
|
511
|
+
max_waiting: Maximum number of waiting clients
|
|
512
|
+
max_lifetime: Maximum connection lifetime
|
|
513
|
+
max_idle: Maximum idle time for connections
|
|
514
|
+
reconnect_timeout: Time between reconnection attempts
|
|
515
|
+
num_workers: Number of background workers
|
|
516
|
+
configure: Callback to configure new connections
|
|
517
|
+
kwargs: Additional connection parameters
|
|
518
|
+
extras: Additional connection parameters not explicitly defined
|
|
519
|
+
**additional_kwargs: Additional parameters (stored in extras)
|
|
520
|
+
"""
|
|
521
|
+
# Store connection parameters as instance attributes
|
|
522
|
+
self.conninfo = conninfo
|
|
523
|
+
self.host = host
|
|
524
|
+
self.port = port
|
|
525
|
+
self.user = user
|
|
526
|
+
self.password = password
|
|
527
|
+
self.dbname = dbname
|
|
528
|
+
self.connect_timeout = connect_timeout
|
|
529
|
+
self.options = options
|
|
530
|
+
self.application_name = application_name
|
|
531
|
+
self.sslmode = sslmode
|
|
532
|
+
self.sslcert = sslcert
|
|
533
|
+
self.sslkey = sslkey
|
|
534
|
+
self.sslrootcert = sslrootcert
|
|
535
|
+
self.autocommit = autocommit
|
|
536
|
+
|
|
537
|
+
# Store pool parameters as instance attributes
|
|
538
|
+
self.min_size = min_size
|
|
539
|
+
self.max_size = max_size
|
|
540
|
+
self.name = name
|
|
541
|
+
self.timeout = timeout
|
|
542
|
+
self.max_waiting = max_waiting
|
|
543
|
+
self.max_lifetime = max_lifetime
|
|
544
|
+
self.max_idle = max_idle
|
|
545
|
+
self.reconnect_timeout = reconnect_timeout
|
|
546
|
+
self.num_workers = num_workers
|
|
547
|
+
self.configure = configure
|
|
548
|
+
self.kwargs = kwargs or {}
|
|
549
|
+
|
|
550
|
+
# Handle extras and additional kwargs
|
|
551
|
+
self.extras = extras or {}
|
|
552
|
+
self.extras.update(additional_kwargs)
|
|
553
|
+
|
|
554
|
+
# Store other config
|
|
555
|
+
self.statement_config = statement_config or SQLConfig()
|
|
556
|
+
self.default_row_type = default_row_type
|
|
557
|
+
self._dialect: DialectType = None
|
|
558
|
+
|
|
559
|
+
super().__init__()
|
|
560
|
+
|
|
561
|
+
@property
|
|
562
|
+
def connection_config_dict(self) -> dict[str, Any]:
|
|
563
|
+
"""Return the connection configuration as a dict for psycopg operations.
|
|
564
|
+
|
|
565
|
+
Returns only connection-specific parameters.
|
|
566
|
+
"""
|
|
567
|
+
# Gather non-None parameters from connection fields only
|
|
568
|
+
config = {
|
|
569
|
+
field: getattr(self, field)
|
|
570
|
+
for field in CONNECTION_FIELDS
|
|
571
|
+
if getattr(self, field, None) is not None and getattr(self, field) is not Empty
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
# Merge extras and kwargs
|
|
575
|
+
config.update(self.extras)
|
|
576
|
+
if self.kwargs:
|
|
577
|
+
config.update(self.kwargs)
|
|
578
|
+
|
|
579
|
+
# Set DictRow as the row factory
|
|
580
|
+
config["row_factory"] = dict_row
|
|
581
|
+
|
|
582
|
+
return config
|
|
583
|
+
|
|
584
|
+
@property
|
|
585
|
+
def pool_config_dict(self) -> dict[str, Any]:
|
|
586
|
+
"""Return the pool configuration as a dict for psycopg pool operations.
|
|
587
|
+
|
|
588
|
+
Returns all configuration parameters including connection and pool-specific parameters.
|
|
589
|
+
"""
|
|
590
|
+
# Gather non-None parameters from all fields (connection + pool)
|
|
591
|
+
config = {
|
|
592
|
+
field: getattr(self, field)
|
|
593
|
+
for field in POOL_FIELDS
|
|
594
|
+
if getattr(self, field, None) is not None and getattr(self, field) is not Empty
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
# Merge extras and kwargs
|
|
598
|
+
config.update(self.extras)
|
|
599
|
+
if self.kwargs:
|
|
600
|
+
config.update(self.kwargs)
|
|
601
|
+
|
|
602
|
+
# Set DictRow as the row factory
|
|
603
|
+
config["row_factory"] = dict_row
|
|
604
|
+
|
|
605
|
+
return config
|
|
606
|
+
|
|
607
|
+
async def _create_pool(self) -> "AsyncConnectionPool":
|
|
608
|
+
"""Create the actual async connection pool."""
|
|
609
|
+
logger.info("Creating async Psycopg connection pool", extra={"adapter": "psycopg"})
|
|
610
|
+
|
|
611
|
+
try:
|
|
612
|
+
# Get all config (creates a new dict)
|
|
613
|
+
all_config = self.pool_config_dict.copy()
|
|
614
|
+
|
|
615
|
+
# Separate pool-specific parameters that AsyncConnectionPool accepts directly
|
|
616
|
+
pool_params = {
|
|
617
|
+
"min_size": all_config.pop("min_size", 4),
|
|
618
|
+
"max_size": all_config.pop("max_size", None),
|
|
619
|
+
"name": all_config.pop("name", None),
|
|
620
|
+
"timeout": all_config.pop("timeout", 30.0),
|
|
621
|
+
"max_waiting": all_config.pop("max_waiting", 0),
|
|
622
|
+
"max_lifetime": all_config.pop("max_lifetime", 3600.0),
|
|
623
|
+
"max_idle": all_config.pop("max_idle", 600.0),
|
|
624
|
+
"reconnect_timeout": all_config.pop("reconnect_timeout", 300.0),
|
|
625
|
+
"num_workers": all_config.pop("num_workers", 3),
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
# Create a configure callback to set row_factory
|
|
629
|
+
async def configure_connection(conn: "PsycopgAsyncConnection") -> None:
|
|
630
|
+
# Set DictRow as the row factory
|
|
631
|
+
conn.row_factory = dict_row
|
|
632
|
+
|
|
633
|
+
pool_params["configure"] = all_config.pop("configure", configure_connection)
|
|
634
|
+
|
|
635
|
+
# Remove None values from pool_params
|
|
636
|
+
pool_params = {k: v for k, v in pool_params.items() if v is not None}
|
|
637
|
+
|
|
638
|
+
# Handle conninfo vs individual connection parameters
|
|
639
|
+
conninfo = all_config.pop("conninfo", None)
|
|
640
|
+
if conninfo:
|
|
641
|
+
# If conninfo is provided, use it directly
|
|
642
|
+
# Don't pass kwargs when using conninfo string
|
|
643
|
+
pool = AsyncConnectionPool(conninfo, **pool_params)
|
|
644
|
+
else:
|
|
645
|
+
# Otherwise, pass connection parameters via kwargs
|
|
646
|
+
# Remove any non-connection parameters
|
|
647
|
+
# row_factory is already popped out earlier
|
|
648
|
+
all_config.pop("row_factory", None)
|
|
649
|
+
# Remove pool-specific settings that may have been left
|
|
650
|
+
all_config.pop("kwargs", None)
|
|
651
|
+
pool = AsyncConnectionPool("", kwargs=all_config, **pool_params)
|
|
652
|
+
|
|
653
|
+
await pool.open()
|
|
654
|
+
logger.info("Async Psycopg connection pool created successfully", extra={"adapter": "psycopg"})
|
|
655
|
+
except Exception as e:
|
|
656
|
+
logger.exception(
|
|
657
|
+
"Failed to create async Psycopg connection pool", extra={"adapter": "psycopg", "error": str(e)}
|
|
658
|
+
)
|
|
659
|
+
raise
|
|
660
|
+
return pool
|
|
661
|
+
|
|
662
|
+
async def _close_pool(self) -> None:
|
|
663
|
+
"""Close the actual async connection pool."""
|
|
664
|
+
if not self.pool_instance:
|
|
665
|
+
return
|
|
666
|
+
|
|
667
|
+
logger.info("Closing async Psycopg connection pool", extra={"adapter": "psycopg"})
|
|
668
|
+
|
|
669
|
+
try:
|
|
670
|
+
await self.pool_instance.close()
|
|
671
|
+
logger.info("Async Psycopg connection pool closed successfully", extra={"adapter": "psycopg"})
|
|
672
|
+
except Exception as e:
|
|
673
|
+
logger.exception(
|
|
674
|
+
"Failed to close async Psycopg connection pool", extra={"adapter": "psycopg", "error": str(e)}
|
|
675
|
+
)
|
|
676
|
+
raise
|
|
677
|
+
|
|
678
|
+
async def create_connection(self) -> "PsycopgAsyncConnection": # pyright: ignore
|
|
679
|
+
"""Create a single async connection (not from pool).
|
|
680
|
+
|
|
681
|
+
Returns:
|
|
682
|
+
A psycopg AsyncConnection instance configured with DictRow.
|
|
683
|
+
"""
|
|
684
|
+
if self.pool_instance is None:
|
|
685
|
+
self.pool_instance = await self.create_pool()
|
|
686
|
+
return cast("PsycopgAsyncConnection", await self.pool_instance.getconn()) # pyright: ignore
|
|
687
|
+
|
|
688
|
+
@asynccontextmanager
|
|
689
|
+
async def provide_connection(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[PsycopgAsyncConnection, None]": # pyright: ignore
|
|
690
|
+
"""Provide an async connection context manager.
|
|
691
|
+
|
|
692
|
+
Args:
|
|
693
|
+
*args: Additional arguments.
|
|
694
|
+
**kwargs: Additional keyword arguments.
|
|
695
|
+
|
|
696
|
+
Yields:
|
|
697
|
+
A psycopg AsyncConnection instance.
|
|
698
|
+
"""
|
|
699
|
+
if self.pool_instance:
|
|
700
|
+
async with self.pool_instance.connection() as conn:
|
|
701
|
+
yield conn # type: ignore[misc]
|
|
702
|
+
else:
|
|
703
|
+
conn = await self.create_connection() # type: ignore[assignment]
|
|
704
|
+
try:
|
|
705
|
+
yield conn # type: ignore[misc]
|
|
706
|
+
finally:
|
|
707
|
+
await conn.close()
|
|
708
|
+
|
|
709
|
+
@asynccontextmanager
|
|
710
|
+
async def provide_session(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[PsycopgAsyncDriver, None]":
|
|
711
|
+
"""Provide an async driver session context manager.
|
|
712
|
+
|
|
713
|
+
Args:
|
|
714
|
+
*args: Additional arguments.
|
|
715
|
+
**kwargs: Additional keyword arguments.
|
|
716
|
+
|
|
717
|
+
Yields:
|
|
718
|
+
A PsycopgAsyncDriver instance.
|
|
719
|
+
"""
|
|
720
|
+
async with self.provide_connection(*args, **kwargs) as conn:
|
|
721
|
+
# Create statement config with parameter style info if not already set
|
|
722
|
+
statement_config = self.statement_config
|
|
723
|
+
if statement_config.allowed_parameter_styles is None:
|
|
724
|
+
statement_config = replace(
|
|
725
|
+
statement_config,
|
|
726
|
+
allowed_parameter_styles=self.supported_parameter_styles,
|
|
727
|
+
target_parameter_style=self.preferred_parameter_style,
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
driver = self.driver_type(connection=conn, config=statement_config)
|
|
731
|
+
yield driver
|
|
732
|
+
|
|
733
|
+
async def provide_pool(self, *args: Any, **kwargs: Any) -> "AsyncConnectionPool":
|
|
734
|
+
"""Provide async pool instance.
|
|
735
|
+
|
|
736
|
+
Returns:
|
|
737
|
+
The async connection pool.
|
|
738
|
+
"""
|
|
739
|
+
if not self.pool_instance:
|
|
740
|
+
self.pool_instance = await self.create_pool()
|
|
741
|
+
return self.pool_instance
|