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.

Files changed (155) hide show
  1. sqlspec/__init__.py +16 -3
  2. sqlspec/_serialization.py +3 -10
  3. sqlspec/_sql.py +1147 -0
  4. sqlspec/_typing.py +343 -41
  5. sqlspec/adapters/adbc/__init__.py +2 -6
  6. sqlspec/adapters/adbc/config.py +474 -149
  7. sqlspec/adapters/adbc/driver.py +330 -621
  8. sqlspec/adapters/aiosqlite/__init__.py +2 -6
  9. sqlspec/adapters/aiosqlite/config.py +143 -57
  10. sqlspec/adapters/aiosqlite/driver.py +269 -431
  11. sqlspec/adapters/asyncmy/__init__.py +3 -8
  12. sqlspec/adapters/asyncmy/config.py +247 -202
  13. sqlspec/adapters/asyncmy/driver.py +218 -436
  14. sqlspec/adapters/asyncpg/__init__.py +4 -7
  15. sqlspec/adapters/asyncpg/config.py +329 -176
  16. sqlspec/adapters/asyncpg/driver.py +417 -487
  17. sqlspec/adapters/bigquery/__init__.py +2 -2
  18. sqlspec/adapters/bigquery/config.py +407 -0
  19. sqlspec/adapters/bigquery/driver.py +600 -553
  20. sqlspec/adapters/duckdb/__init__.py +4 -1
  21. sqlspec/adapters/duckdb/config.py +432 -321
  22. sqlspec/adapters/duckdb/driver.py +392 -406
  23. sqlspec/adapters/oracledb/__init__.py +3 -8
  24. sqlspec/adapters/oracledb/config.py +625 -0
  25. sqlspec/adapters/oracledb/driver.py +548 -921
  26. sqlspec/adapters/psqlpy/__init__.py +4 -7
  27. sqlspec/adapters/psqlpy/config.py +372 -203
  28. sqlspec/adapters/psqlpy/driver.py +197 -533
  29. sqlspec/adapters/psycopg/__init__.py +3 -8
  30. sqlspec/adapters/psycopg/config.py +741 -0
  31. sqlspec/adapters/psycopg/driver.py +734 -694
  32. sqlspec/adapters/sqlite/__init__.py +2 -6
  33. sqlspec/adapters/sqlite/config.py +146 -81
  34. sqlspec/adapters/sqlite/driver.py +242 -405
  35. sqlspec/base.py +220 -784
  36. sqlspec/config.py +354 -0
  37. sqlspec/driver/__init__.py +22 -0
  38. sqlspec/driver/_async.py +252 -0
  39. sqlspec/driver/_common.py +338 -0
  40. sqlspec/driver/_sync.py +261 -0
  41. sqlspec/driver/mixins/__init__.py +17 -0
  42. sqlspec/driver/mixins/_pipeline.py +523 -0
  43. sqlspec/driver/mixins/_result_utils.py +122 -0
  44. sqlspec/driver/mixins/_sql_translator.py +35 -0
  45. sqlspec/driver/mixins/_storage.py +993 -0
  46. sqlspec/driver/mixins/_type_coercion.py +131 -0
  47. sqlspec/exceptions.py +299 -7
  48. sqlspec/extensions/aiosql/__init__.py +10 -0
  49. sqlspec/extensions/aiosql/adapter.py +474 -0
  50. sqlspec/extensions/litestar/__init__.py +1 -6
  51. sqlspec/extensions/litestar/_utils.py +1 -5
  52. sqlspec/extensions/litestar/config.py +5 -6
  53. sqlspec/extensions/litestar/handlers.py +13 -12
  54. sqlspec/extensions/litestar/plugin.py +22 -24
  55. sqlspec/extensions/litestar/providers.py +37 -55
  56. sqlspec/loader.py +528 -0
  57. sqlspec/service/__init__.py +3 -0
  58. sqlspec/service/base.py +24 -0
  59. sqlspec/service/pagination.py +26 -0
  60. sqlspec/statement/__init__.py +21 -0
  61. sqlspec/statement/builder/__init__.py +54 -0
  62. sqlspec/statement/builder/_ddl_utils.py +119 -0
  63. sqlspec/statement/builder/_parsing_utils.py +135 -0
  64. sqlspec/statement/builder/base.py +328 -0
  65. sqlspec/statement/builder/ddl.py +1379 -0
  66. sqlspec/statement/builder/delete.py +80 -0
  67. sqlspec/statement/builder/insert.py +274 -0
  68. sqlspec/statement/builder/merge.py +95 -0
  69. sqlspec/statement/builder/mixins/__init__.py +65 -0
  70. sqlspec/statement/builder/mixins/_aggregate_functions.py +151 -0
  71. sqlspec/statement/builder/mixins/_case_builder.py +91 -0
  72. sqlspec/statement/builder/mixins/_common_table_expr.py +91 -0
  73. sqlspec/statement/builder/mixins/_delete_from.py +34 -0
  74. sqlspec/statement/builder/mixins/_from.py +61 -0
  75. sqlspec/statement/builder/mixins/_group_by.py +119 -0
  76. sqlspec/statement/builder/mixins/_having.py +35 -0
  77. sqlspec/statement/builder/mixins/_insert_from_select.py +48 -0
  78. sqlspec/statement/builder/mixins/_insert_into.py +36 -0
  79. sqlspec/statement/builder/mixins/_insert_values.py +69 -0
  80. sqlspec/statement/builder/mixins/_join.py +110 -0
  81. sqlspec/statement/builder/mixins/_limit_offset.py +53 -0
  82. sqlspec/statement/builder/mixins/_merge_clauses.py +405 -0
  83. sqlspec/statement/builder/mixins/_order_by.py +46 -0
  84. sqlspec/statement/builder/mixins/_pivot.py +82 -0
  85. sqlspec/statement/builder/mixins/_returning.py +37 -0
  86. sqlspec/statement/builder/mixins/_select_columns.py +60 -0
  87. sqlspec/statement/builder/mixins/_set_ops.py +122 -0
  88. sqlspec/statement/builder/mixins/_unpivot.py +80 -0
  89. sqlspec/statement/builder/mixins/_update_from.py +54 -0
  90. sqlspec/statement/builder/mixins/_update_set.py +91 -0
  91. sqlspec/statement/builder/mixins/_update_table.py +29 -0
  92. sqlspec/statement/builder/mixins/_where.py +374 -0
  93. sqlspec/statement/builder/mixins/_window_functions.py +86 -0
  94. sqlspec/statement/builder/protocols.py +20 -0
  95. sqlspec/statement/builder/select.py +206 -0
  96. sqlspec/statement/builder/update.py +178 -0
  97. sqlspec/statement/filters.py +571 -0
  98. sqlspec/statement/parameters.py +736 -0
  99. sqlspec/statement/pipelines/__init__.py +67 -0
  100. sqlspec/statement/pipelines/analyzers/__init__.py +9 -0
  101. sqlspec/statement/pipelines/analyzers/_analyzer.py +649 -0
  102. sqlspec/statement/pipelines/base.py +315 -0
  103. sqlspec/statement/pipelines/context.py +119 -0
  104. sqlspec/statement/pipelines/result_types.py +41 -0
  105. sqlspec/statement/pipelines/transformers/__init__.py +8 -0
  106. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +256 -0
  107. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +623 -0
  108. sqlspec/statement/pipelines/transformers/_remove_comments.py +66 -0
  109. sqlspec/statement/pipelines/transformers/_remove_hints.py +81 -0
  110. sqlspec/statement/pipelines/validators/__init__.py +23 -0
  111. sqlspec/statement/pipelines/validators/_dml_safety.py +275 -0
  112. sqlspec/statement/pipelines/validators/_parameter_style.py +297 -0
  113. sqlspec/statement/pipelines/validators/_performance.py +703 -0
  114. sqlspec/statement/pipelines/validators/_security.py +990 -0
  115. sqlspec/statement/pipelines/validators/base.py +67 -0
  116. sqlspec/statement/result.py +527 -0
  117. sqlspec/statement/splitter.py +701 -0
  118. sqlspec/statement/sql.py +1198 -0
  119. sqlspec/storage/__init__.py +15 -0
  120. sqlspec/storage/backends/__init__.py +0 -0
  121. sqlspec/storage/backends/base.py +166 -0
  122. sqlspec/storage/backends/fsspec.py +315 -0
  123. sqlspec/storage/backends/obstore.py +464 -0
  124. sqlspec/storage/protocol.py +170 -0
  125. sqlspec/storage/registry.py +315 -0
  126. sqlspec/typing.py +157 -36
  127. sqlspec/utils/correlation.py +155 -0
  128. sqlspec/utils/deprecation.py +3 -6
  129. sqlspec/utils/fixtures.py +6 -11
  130. sqlspec/utils/logging.py +135 -0
  131. sqlspec/utils/module_loader.py +45 -43
  132. sqlspec/utils/serializers.py +4 -0
  133. sqlspec/utils/singleton.py +6 -8
  134. sqlspec/utils/sync_tools.py +15 -27
  135. sqlspec/utils/text.py +58 -26
  136. {sqlspec-0.11.1.dist-info → sqlspec-0.12.0.dist-info}/METADATA +97 -26
  137. sqlspec-0.12.0.dist-info/RECORD +145 -0
  138. sqlspec/adapters/bigquery/config/__init__.py +0 -3
  139. sqlspec/adapters/bigquery/config/_common.py +0 -40
  140. sqlspec/adapters/bigquery/config/_sync.py +0 -87
  141. sqlspec/adapters/oracledb/config/__init__.py +0 -9
  142. sqlspec/adapters/oracledb/config/_asyncio.py +0 -186
  143. sqlspec/adapters/oracledb/config/_common.py +0 -131
  144. sqlspec/adapters/oracledb/config/_sync.py +0 -186
  145. sqlspec/adapters/psycopg/config/__init__.py +0 -19
  146. sqlspec/adapters/psycopg/config/_async.py +0 -169
  147. sqlspec/adapters/psycopg/config/_common.py +0 -56
  148. sqlspec/adapters/psycopg/config/_sync.py +0 -168
  149. sqlspec/filters.py +0 -331
  150. sqlspec/mixins.py +0 -305
  151. sqlspec/statement.py +0 -378
  152. sqlspec-0.11.1.dist-info/RECORD +0 -69
  153. {sqlspec-0.11.1.dist-info → sqlspec-0.12.0.dist-info}/WHEEL +0 -0
  154. {sqlspec-0.11.1.dist-info → sqlspec-0.12.0.dist-info}/licenses/LICENSE +0 -0
  155. {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 AsyncpgConfig, AsyncpgPoolConfig
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
- __all__ = (
5
- "AsyncpgConfig",
6
- "AsyncpgConnection",
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 dataclass, field
3
- from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
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 asyncpg.pool import PoolConnectionProxy
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.base import AsyncDatabaseConfig, GenericPoolConfig
12
- from sqlspec.exceptions import ImproperConfigurationError
13
- from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
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 # pyright: ignore[reportAttributeAccessIssue]
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
- __all__ = (
24
- "AsyncpgConfig",
25
- "AsyncpgPoolConfig",
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
- T = TypeVar("T")
30
-
31
-
32
- @dataclass
33
- class AsyncpgPoolConfig(GenericPoolConfig):
34
- """Configuration for Asyncpg's :class:`Pool <asyncpg.pool.Pool>`.
35
-
36
- For details see: https://magicstack.github.io/asyncpg/current/api/index.html#connection-pools
37
- """
38
-
39
- dsn: str
40
- """Connection arguments specified using as a single string in the following format: ``postgres://user:pass@host:port/database?option=value``
41
- """
42
- connect_kwargs: "Optional[Union[dict[Any, Any], EmptyType]]" = Empty
43
- """A dictionary of arguments which will be passed directly to the ``connect()`` method as keyword arguments.
44
- """
45
- connection_class: "Optional[Union[type[Connection], EmptyType]]" = Empty # pyright: ignore[reportMissingTypeArgument]
46
- """The class to use for connections. Must be a subclass of Connection
47
- """
48
- record_class: "Union[type[Record], EmptyType]" = Empty
49
- """If specified, the class to use for records returned by queries on the connections in this pool. Must be a subclass of Record."""
50
-
51
- min_size: "Union[int, EmptyType]" = Empty
52
- """The number of connections to keep open inside the connection pool."""
53
- max_size: "Union[int, EmptyType]" = Empty
54
- """The number of connections to allow in connection pool "overflow", that is connections that can be opened above
55
- and beyond the pool_size setting, which defaults to 10."""
56
-
57
- max_queries: "Union[int, EmptyType]" = Empty
58
- """Number of queries after a connection is closed and replaced with a new connection.
59
- """
60
- max_inactive_connection_lifetime: "Union[float, EmptyType]" = Empty
61
- """Number of seconds after which inactive connections in the pool will be closed. Pass 0 to disable this mechanism."""
62
-
63
- setup: "Union[Coroutine[None, type[Connection], Any], EmptyType]" = Empty # pyright: ignore[reportMissingTypeArgument]
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
- @property
97
- def connection_config_dict(self) -> "dict[str, Any]":
98
- """Return the connection configuration as a dict.
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
- Returns:
101
- A string keyed dict of config kwargs for the asyncpg.connect function.
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
- Raises:
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
- if self.pool_config:
107
- connect_dict: dict[str, Any] = {}
108
-
109
- # Add dsn if available
110
- if hasattr(self.pool_config, "dsn"):
111
- connect_dict["dsn"] = self.pool_config.dsn
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
- # Add any connect_kwargs if available
114
- if (
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
- return connect_dict
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) -> "dict[str, Any]":
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
- Getter that returns the pool instance used by the plugin.
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
- if self.pool_instance is not None:
157
- return self.pool_instance
158
-
159
- if self.pool_config is None:
160
- msg = "One of 'pool_config' or 'pool_instance' must be provided."
161
- raise ImproperConfigurationError(msg)
162
-
163
- pool_config = self.pool_config_dict
164
- self.pool_instance = await asyncpg_create_pool(**pool_config)
165
- if self.pool_instance is None: # pyright: ignore[reportUnnecessaryComparison]
166
- msg = "Could not configure the 'pool_instance'. Please check your configuration." # type: ignore[unreachable]
167
- raise ImproperConfigurationError(msg)
168
- return self.pool_instance
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 provide_pool(self, *args: "Any", **kwargs: "Any") -> "Awaitable[Pool]": # pyright: ignore[reportMissingTypeArgument,reportUnknownParameterType]
171
- """Create a pool instance.
278
+ async def create_connection(self) -> AsyncpgConnection:
279
+ """Create a single async connection (not from pool).
172
280
 
173
281
  Returns:
174
- A Pool instance.
282
+ An AsyncPG connection instance.
175
283
  """
176
- return self.create_pool() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
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
- async def create_connection(self) -> "AsyncpgConnection":
179
- """Create and return a new asyncpg connection from the pool.
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
- Returns:
182
- A Connection instance.
292
+ Args:
293
+ *args: Additional arguments.
294
+ **kwargs: Additional keyword arguments.
183
295
 
184
- Raises:
185
- ImproperConfigurationError: If the connection could not be created.
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
- pool = await self.provide_pool()
189
- return await pool.acquire()
190
- except Exception as e:
191
- msg = f"Could not configure the asyncpg connection. Error: {e!s}"
192
- raise ImproperConfigurationError(msg) from e
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 provide_connection(self, *args: "Any", **kwargs: "Any") -> "AsyncGenerator[AsyncpgConnection, None]": # pyright: ignore[reportMissingTypeArgument,reportUnknownParameterType]
196
- """Create a connection instance.
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
- A connection instance.
318
+ An AsyncpgDriver instance.
200
319
  """
201
- db_pool = await self.provide_pool(*args, **kwargs) # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
202
- async with db_pool.acquire() as connection: # pyright: ignore[reportUnknownVariableType]
203
- yield connection
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
- async def close_pool(self) -> None:
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
- @asynccontextmanager
212
- async def provide_session(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[AsyncpgDriver, None]":
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
- Yields:
216
- A Aiosqlite driver instance.
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
- async with self.provide_connection(*args, **kwargs) as connection:
221
- yield self.driver_type(connection)
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