sqlspec 0.11.1__py3-none-any.whl → 0.12.1__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 +725 -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.1.dist-info}/METADATA +97 -26
  137. sqlspec-0.12.1.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.1.dist-info}/WHEEL +0 -0
  154. {sqlspec-0.11.1.dist-info → sqlspec-0.12.1.dist-info}/licenses/LICENSE +0 -0
  155. {sqlspec-0.11.1.dist-info → sqlspec-0.12.1.dist-info}/licenses/NOTICE +0 -0
@@ -1,9 +1,6 @@
1
- from sqlspec.adapters.psqlpy.config import PsqlpyConfig, PsqlpyPoolConfig
1
+ """Psqlpy adapter for SQLSpec."""
2
+
3
+ from sqlspec.adapters.psqlpy.config import CONNECTION_FIELDS, POOL_FIELDS, PsqlpyConfig
2
4
  from sqlspec.adapters.psqlpy.driver import PsqlpyConnection, PsqlpyDriver
3
5
 
4
- __all__ = (
5
- "PsqlpyConfig",
6
- "PsqlpyConnection",
7
- "PsqlpyDriver",
8
- "PsqlpyPoolConfig",
9
- )
6
+ __all__ = ("CONNECTION_FIELDS", "POOL_FIELDS", "PsqlpyConfig", "PsqlpyConnection", "PsqlpyDriver")
@@ -1,250 +1,419 @@
1
- """Configuration for the psqlpy PostgreSQL adapter."""
1
+ """Psqlpy database configuration with direct field-based configuration."""
2
2
 
3
+ import logging
4
+ from collections.abc import AsyncGenerator
3
5
  from contextlib import asynccontextmanager
4
- from dataclasses import dataclass, field
5
- from typing import TYPE_CHECKING, Any, Optional, Union
6
+ from dataclasses import replace
7
+ from typing import TYPE_CHECKING, Any, ClassVar, Optional
6
8
 
7
- from psqlpy import Connection, ConnectionPool
9
+ from psqlpy import ConnectionPool
8
10
 
9
11
  from sqlspec.adapters.psqlpy.driver import PsqlpyConnection, PsqlpyDriver
10
- from sqlspec.base import AsyncDatabaseConfig, GenericPoolConfig
11
- from sqlspec.exceptions import ImproperConfigurationError
12
- from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
12
+ from sqlspec.config import AsyncDatabaseConfig
13
+ from sqlspec.statement.sql import SQLConfig
14
+ from sqlspec.typing import DictRow, Empty
13
15
 
14
16
  if TYPE_CHECKING:
15
- from collections.abc import AsyncGenerator, Awaitable
16
-
17
-
18
- __all__ = (
19
- "PsqlpyConfig",
20
- "PsqlpyPoolConfig",
17
+ from collections.abc import Callable
18
+
19
+ from sqlglot.dialects.dialect import DialectType
20
+
21
+ logger = logging.getLogger("sqlspec.adapters.psqlpy")
22
+
23
+ CONNECTION_FIELDS = frozenset(
24
+ {
25
+ "dsn",
26
+ "username",
27
+ "password",
28
+ "db_name",
29
+ "host",
30
+ "port",
31
+ "connect_timeout_sec",
32
+ "connect_timeout_nanosec",
33
+ "tcp_user_timeout_sec",
34
+ "tcp_user_timeout_nanosec",
35
+ "keepalives",
36
+ "keepalives_idle_sec",
37
+ "keepalives_idle_nanosec",
38
+ "keepalives_interval_sec",
39
+ "keepalives_interval_nanosec",
40
+ "keepalives_retries",
41
+ "ssl_mode",
42
+ "ca_file",
43
+ "target_session_attrs",
44
+ "options",
45
+ "application_name",
46
+ "client_encoding",
47
+ "gssencmode",
48
+ "sslnegotiation",
49
+ "sslcompression",
50
+ "sslcert",
51
+ "sslkey",
52
+ "sslpassword",
53
+ "sslrootcert",
54
+ "sslcrl",
55
+ "require_auth",
56
+ "channel_binding",
57
+ "krbsrvname",
58
+ "gsslib",
59
+ "gssdelegation",
60
+ "service",
61
+ "load_balance_hosts",
62
+ }
21
63
  )
22
64
 
65
+ POOL_FIELDS = CONNECTION_FIELDS.union({"hosts", "ports", "conn_recycling_method", "max_db_pool_size", "configure"})
23
66
 
24
- @dataclass
25
- class PsqlpyPoolConfig(GenericPoolConfig):
26
- """Configuration for psqlpy connection pool.
27
-
28
- Ref: https://psqlpy-python.github.io/components/connection_pool.html#all-available-connectionpool-parameters
29
- """
30
-
31
- dsn: Optional[Union[str, EmptyType]] = Empty
32
- """DSN of the PostgreSQL."""
33
- # Required connection parameters
34
- username: Optional[Union[str, EmptyType]] = Empty
35
- """Username of the user in the PostgreSQL."""
36
- password: Optional[Union[str, EmptyType]] = Empty
37
- """Password of the user in the PostgreSQL."""
38
- db_name: Optional[Union[str, EmptyType]] = Empty
39
- """Name of the database in PostgreSQL."""
40
-
41
- # Single or Multi-host parameters (mutually exclusive)
42
- host: Optional[Union[str, EmptyType]] = Empty
43
- """Host of the PostgreSQL (use for single host)."""
44
- port: Optional[Union[int, EmptyType]] = Empty
45
- """Port of the PostgreSQL (use for single host)."""
46
- hosts: Optional[Union[list[str], EmptyType]] = Empty
47
- """List of hosts of the PostgreSQL (use for multiple hosts)."""
48
- ports: Optional[Union[list[int], EmptyType]] = Empty
49
- """List of ports of the PostgreSQL (use for multiple hosts)."""
50
-
51
- # Pool size
52
- max_db_pool_size: int = 10
53
- """Maximum size of the connection pool. Defaults to 10."""
54
-
55
- # Optional timeouts
56
- connect_timeout_sec: Optional[Union[int, EmptyType]] = Empty
57
- """The time limit in seconds applied to each socket-level connection attempt."""
58
- connect_timeout_nanosec: Optional[Union[int, EmptyType]] = Empty
59
- """Nanoseconds for connection timeout, can be used only with `connect_timeout_sec`."""
60
- tcp_user_timeout_sec: Optional[Union[int, EmptyType]] = Empty
61
- """The time limit that transmitted data may remain unacknowledged before a connection is forcibly closed."""
62
- tcp_user_timeout_nanosec: Optional[Union[int, EmptyType]] = Empty
63
- """Nanoseconds for tcp_user_timeout, can be used only with `tcp_user_timeout_sec`."""
64
-
65
- # Optional keepalives
66
- keepalives: bool = True
67
- """Controls the use of TCP keepalive. Defaults to True (on)."""
68
- keepalives_idle_sec: Optional[Union[int, EmptyType]] = Empty
69
- """The number of seconds of inactivity after which a keepalive message is sent to the server."""
70
- keepalives_idle_nanosec: Optional[Union[int, EmptyType]] = Empty
71
- """Nanoseconds for keepalives_idle_sec."""
72
- keepalives_interval_sec: Optional[Union[int, EmptyType]] = Empty
73
- """The time interval between TCP keepalive probes."""
74
- keepalives_interval_nanosec: Optional[Union[int, EmptyType]] = Empty
75
- """Nanoseconds for keepalives_interval_sec."""
76
- keepalives_retries: Optional[Union[int, EmptyType]] = Empty
77
- """The maximum number of TCP keepalive probes that will be sent before dropping a connection."""
78
-
79
- # Other optional parameters
80
- load_balance_hosts: Optional[Union[str, EmptyType]] = Empty
81
- """Controls the order in which the client tries to connect to the available hosts and addresses ('disable' or 'random')."""
82
- conn_recycling_method: Optional[Union[str, EmptyType]] = Empty
83
- """How a connection is recycled."""
84
- ssl_mode: Optional[Union[str, EmptyType]] = Empty
85
- """SSL mode."""
86
- ca_file: Optional[Union[str, EmptyType]] = Empty
87
- """Path to ca_file for SSL."""
88
- target_session_attrs: Optional[Union[str, EmptyType]] = Empty
89
- """Specifies requirements of the session (e.g., 'read-write')."""
90
- options: Optional[Union[str, EmptyType]] = Empty
91
- """Command line options used to configure the server."""
92
- application_name: Optional[Union[str, EmptyType]] = Empty
93
- """Sets the application_name parameter on the server."""
94
-
95
-
96
- @dataclass
97
- class PsqlpyConfig(AsyncDatabaseConfig[PsqlpyConnection, ConnectionPool, PsqlpyDriver]):
98
- """Configuration for psqlpy database connections, managing a connection pool.
67
+ __all__ = ("CONNECTION_FIELDS", "POOL_FIELDS", "PsqlpyConfig")
99
68
 
100
- This configuration class wraps `PsqlpyPoolConfig` and manages the lifecycle
101
- of a `psqlpy.ConnectionPool`.
102
- """
103
69
 
104
- pool_config: Optional[PsqlpyPoolConfig] = field(default=None)
105
- """Psqlpy Pool configuration"""
106
- driver_type: type[PsqlpyDriver] = field(default=PsqlpyDriver, init=False, hash=False)
107
- """Type of the driver object"""
108
- connection_type: type[PsqlpyConnection] = field(default=PsqlpyConnection, init=False, hash=False)
109
- """Type of the connection object"""
110
- pool_instance: Optional[ConnectionPool] = field(default=None, hash=False)
111
- """The connection pool instance. If set, this will be used instead of creating a new pool."""
70
+ class PsqlpyConfig(AsyncDatabaseConfig[PsqlpyConnection, ConnectionPool, PsqlpyDriver]):
71
+ """Configuration for Psqlpy asynchronous database connections with direct field-based configuration."""
72
+
73
+ __slots__ = (
74
+ "_dialect",
75
+ "application_name",
76
+ "ca_file",
77
+ "channel_binding",
78
+ "client_encoding",
79
+ "configure",
80
+ "conn_recycling_method",
81
+ "connect_timeout_nanosec",
82
+ "connect_timeout_sec",
83
+ "db_name",
84
+ "default_row_type",
85
+ "dsn",
86
+ "extras",
87
+ "gssdelegation",
88
+ "gssencmode",
89
+ "gsslib",
90
+ "host",
91
+ "hosts",
92
+ "keepalives",
93
+ "keepalives_idle_nanosec",
94
+ "keepalives_idle_sec",
95
+ "keepalives_interval_nanosec",
96
+ "keepalives_interval_sec",
97
+ "keepalives_retries",
98
+ "krbsrvname",
99
+ "load_balance_hosts",
100
+ "max_db_pool_size",
101
+ "options",
102
+ "password",
103
+ "pool_instance",
104
+ "port",
105
+ "ports",
106
+ "require_auth",
107
+ "service",
108
+ "ssl_mode",
109
+ "sslcert",
110
+ "sslcompression",
111
+ "sslcrl",
112
+ "sslkey",
113
+ "sslnegotiation",
114
+ "sslpassword",
115
+ "sslrootcert",
116
+ "statement_config",
117
+ "target_session_attrs",
118
+ "tcp_user_timeout_nanosec",
119
+ "tcp_user_timeout_sec",
120
+ "username",
121
+ )
122
+
123
+ is_async: ClassVar[bool] = True
124
+ supports_connection_pooling: ClassVar[bool] = True
125
+
126
+ driver_type: type[PsqlpyDriver] = PsqlpyDriver
127
+ connection_type: type[PsqlpyConnection] = PsqlpyConnection
128
+ # Parameter style support information
129
+ supported_parameter_styles: ClassVar[tuple[str, ...]] = ("numeric",)
130
+ """Psqlpy only supports $1, $2, ... (numeric) parameter style."""
131
+
132
+ preferred_parameter_style: ClassVar[str] = "numeric"
133
+ """Psqlpy's native parameter style is $1, $2, ... (numeric)."""
134
+
135
+ def __init__(
136
+ self,
137
+ statement_config: Optional[SQLConfig] = None,
138
+ default_row_type: type[DictRow] = DictRow,
139
+ # Connection parameters
140
+ dsn: Optional[str] = None,
141
+ username: Optional[str] = None,
142
+ password: Optional[str] = None,
143
+ db_name: Optional[str] = None,
144
+ host: Optional[str] = None,
145
+ port: Optional[int] = None,
146
+ hosts: Optional[list[str]] = None,
147
+ ports: Optional[list[int]] = None,
148
+ connect_timeout_sec: Optional[int] = None,
149
+ connect_timeout_nanosec: Optional[int] = None,
150
+ tcp_user_timeout_sec: Optional[int] = None,
151
+ tcp_user_timeout_nanosec: Optional[int] = None,
152
+ keepalives: Optional[bool] = None,
153
+ keepalives_idle_sec: Optional[int] = None,
154
+ keepalives_idle_nanosec: Optional[int] = None,
155
+ keepalives_interval_sec: Optional[int] = None,
156
+ keepalives_interval_nanosec: Optional[int] = None,
157
+ keepalives_retries: Optional[int] = None,
158
+ ssl_mode: Optional[str] = None,
159
+ ca_file: Optional[str] = None,
160
+ target_session_attrs: Optional[str] = None,
161
+ options: Optional[str] = None,
162
+ application_name: Optional[str] = None,
163
+ client_encoding: Optional[str] = None,
164
+ gssencmode: Optional[str] = None,
165
+ sslnegotiation: Optional[str] = None,
166
+ sslcompression: Optional[bool] = None,
167
+ sslcert: Optional[str] = None,
168
+ sslkey: Optional[str] = None,
169
+ sslpassword: Optional[str] = None,
170
+ sslrootcert: Optional[str] = None,
171
+ sslcrl: Optional[str] = None,
172
+ require_auth: Optional[str] = None,
173
+ channel_binding: Optional[str] = None,
174
+ krbsrvname: Optional[str] = None,
175
+ gsslib: Optional[str] = None,
176
+ gssdelegation: Optional[bool] = None,
177
+ service: Optional[str] = None,
178
+ load_balance_hosts: Optional[str] = None,
179
+ # Pool parameters
180
+ conn_recycling_method: Optional[str] = None,
181
+ max_db_pool_size: Optional[int] = None,
182
+ configure: Optional["Callable[[ConnectionPool], None]"] = None,
183
+ pool_instance: Optional[ConnectionPool] = None,
184
+ **kwargs: Any,
185
+ ) -> None:
186
+ """Initialize Psqlpy asynchronous configuration.
187
+
188
+ Args:
189
+ statement_config: Default SQL statement configuration
190
+ default_row_type: Default row type for results
191
+ dsn: DSN of the PostgreSQL database
192
+ username: Username of the user in the PostgreSQL
193
+ password: Password of the user in the PostgreSQL
194
+ db_name: Name of the database in PostgreSQL
195
+ host: Host of the PostgreSQL (use for single host)
196
+ port: Port of the PostgreSQL (use for single host)
197
+ hosts: List of hosts of the PostgreSQL (use for multiple hosts)
198
+ ports: List of ports of the PostgreSQL (use for multiple hosts)
199
+ connect_timeout_sec: The time limit in seconds applied to each socket-level connection attempt
200
+ connect_timeout_nanosec: Nanoseconds for connection timeout, can be used only with connect_timeout_sec
201
+ tcp_user_timeout_sec: The time limit that transmitted data may remain unacknowledged before a connection is forcibly closed
202
+ tcp_user_timeout_nanosec: Nanoseconds for tcp_user_timeout, can be used only with tcp_user_timeout_sec
203
+ keepalives: Controls the use of TCP keepalive. Defaults to True (on)
204
+ keepalives_idle_sec: The number of seconds of inactivity after which a keepalive message is sent to the server
205
+ keepalives_idle_nanosec: Nanoseconds for keepalives_idle_sec
206
+ keepalives_interval_sec: The time interval between TCP keepalive probes
207
+ keepalives_interval_nanosec: Nanoseconds for keepalives_interval_sec
208
+ keepalives_retries: The maximum number of TCP keepalive probes that will be sent before dropping a connection
209
+ ssl_mode: SSL mode (disable, prefer, require, verify-ca, verify-full)
210
+ ca_file: Path to ca_file for SSL
211
+ target_session_attrs: Specifies requirements of the session (e.g., 'read-write', 'read-only', 'primary', 'standby')
212
+ options: Command line options used to configure the server
213
+ application_name: Sets the application_name parameter on the server
214
+ client_encoding: Sets the client_encoding parameter
215
+ gssencmode: GSS encryption mode (disable, prefer, require)
216
+ sslnegotiation: SSL negotiation mode (postgres, direct)
217
+ sslcompression: Whether to use SSL compression
218
+ sslcert: Client SSL certificate file
219
+ sslkey: Client SSL private key file
220
+ sslpassword: Password for the SSL private key
221
+ sslrootcert: SSL root certificate file
222
+ sslcrl: SSL certificate revocation list file
223
+ require_auth: Authentication method requirements
224
+ channel_binding: Channel binding preference (disable, prefer, require)
225
+ krbsrvname: Kerberos service name
226
+ gsslib: GSS library to use
227
+ gssdelegation: Forward GSS credentials to server
228
+ service: Service name for additional parameters
229
+ load_balance_hosts: Controls the order in which the client tries to connect to the available hosts and addresses ('disable' or 'random')
230
+ conn_recycling_method: How a connection is recycled
231
+ max_db_pool_size: Maximum size of the connection pool. Defaults to 10
232
+ configure: Callback to configure new connections
233
+ pool_instance: Existing connection pool instance to use
234
+ **kwargs: Additional parameters (stored in extras)
235
+ """
236
+ # Store connection parameters as instance attributes
237
+ self.dsn = dsn
238
+ self.username = username
239
+ self.password = password
240
+ self.db_name = db_name
241
+ self.host = host
242
+ self.port = port
243
+ self.hosts = hosts
244
+ self.ports = ports
245
+ self.connect_timeout_sec = connect_timeout_sec
246
+ self.connect_timeout_nanosec = connect_timeout_nanosec
247
+ self.tcp_user_timeout_sec = tcp_user_timeout_sec
248
+ self.tcp_user_timeout_nanosec = tcp_user_timeout_nanosec
249
+ self.keepalives = keepalives
250
+ self.keepalives_idle_sec = keepalives_idle_sec
251
+ self.keepalives_idle_nanosec = keepalives_idle_nanosec
252
+ self.keepalives_interval_sec = keepalives_interval_sec
253
+ self.keepalives_interval_nanosec = keepalives_interval_nanosec
254
+ self.keepalives_retries = keepalives_retries
255
+ self.ssl_mode = ssl_mode
256
+ self.ca_file = ca_file
257
+ self.target_session_attrs = target_session_attrs
258
+ self.options = options
259
+ self.application_name = application_name
260
+ self.client_encoding = client_encoding
261
+ self.gssencmode = gssencmode
262
+ self.sslnegotiation = sslnegotiation
263
+ self.sslcompression = sslcompression
264
+ self.sslcert = sslcert
265
+ self.sslkey = sslkey
266
+ self.sslpassword = sslpassword
267
+ self.sslrootcert = sslrootcert
268
+ self.sslcrl = sslcrl
269
+ self.require_auth = require_auth
270
+ self.channel_binding = channel_binding
271
+ self.krbsrvname = krbsrvname
272
+ self.gsslib = gsslib
273
+ self.gssdelegation = gssdelegation
274
+ self.service = service
275
+ self.load_balance_hosts = load_balance_hosts
276
+
277
+ # Store pool parameters as instance attributes
278
+ self.conn_recycling_method = conn_recycling_method
279
+ self.max_db_pool_size = max_db_pool_size
280
+ self.configure = configure
281
+
282
+ self.extras = kwargs or {}
283
+
284
+ # Store other config
285
+ self.statement_config = statement_config or SQLConfig()
286
+ self.default_row_type = default_row_type
287
+ self.pool_instance: Optional[ConnectionPool] = pool_instance
288
+ self._dialect: DialectType = None
289
+
290
+ super().__init__()
112
291
 
113
292
  @property
114
- def connection_config_dict(self) -> "dict[str, Any]":
115
- """Return the minimal connection configuration as a dict for standalone use.
116
-
117
- Returns:
118
- A string keyed dict of config kwargs for a psqlpy.Connection.
293
+ def connection_config_dict(self) -> dict[str, Any]:
294
+ """Return the connection configuration as a dict for psqlpy.Connection.
119
295
 
120
- Raises:
121
- ImproperConfigurationError: If essential connection parameters are missing.
296
+ This method filters out pool-specific parameters that are not valid for psqlpy.Connection.
122
297
  """
123
- if self.pool_config:
124
- # Exclude pool-specific keys and internal metadata
125
- pool_specific_keys = {
126
- "max_db_pool_size",
127
- "load_balance_hosts",
128
- "conn_recycling_method",
129
- "pool_instance",
130
- "connection_type",
131
- "driver_type",
132
- }
133
- return dataclass_to_dict(
134
- self.pool_config,
135
- exclude_empty=True,
136
- convert_nested=False,
137
- exclude_none=True,
138
- exclude=pool_specific_keys,
139
- )
140
- msg = "You must provide a 'pool_config' for this adapter."
141
- raise ImproperConfigurationError(msg)
298
+ # Gather non-None connection parameters
299
+ config = {
300
+ field: getattr(self, field)
301
+ for field in CONNECTION_FIELDS
302
+ if getattr(self, field, None) is not None and getattr(self, field) is not Empty
303
+ }
142
304
 
143
- @property
144
- def pool_config_dict(self) -> "dict[str, Any]":
145
- """Return the pool configuration as a dict.
305
+ # Add connection-specific extras (not pool-specific ones)
306
+ config.update(self.extras)
146
307
 
147
- Raises:
148
- ImproperConfigurationError: If no pool_config is provided but a pool_instance
308
+ return config
309
+
310
+ @property
311
+ def pool_config_dict(self) -> dict[str, Any]:
312
+ """Return the full pool configuration as a dict for psqlpy.ConnectionPool.
149
313
 
150
314
  Returns:
151
- A string keyed dict of config kwargs for creating a psqlpy pool.
315
+ A dictionary containing all pool configuration parameters.
152
316
  """
153
- if self.pool_config:
154
- # Extract the config from the pool_config
155
- return dataclass_to_dict(
156
- self.pool_config,
157
- exclude_empty=True,
158
- convert_nested=False,
159
- exclude_none=True,
160
- exclude={"pool_instance", "connection_type", "driver_type"},
161
- )
317
+ # Gather non-None parameters from all fields (connection + pool)
318
+ config = {
319
+ field: getattr(self, field)
320
+ for field in POOL_FIELDS
321
+ if getattr(self, field, None) is not None and getattr(self, field) is not Empty
322
+ }
162
323
 
163
- msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
164
- raise ImproperConfigurationError(msg)
324
+ # Merge extras parameters
325
+ config.update(self.extras)
165
326
 
166
- async def create_pool(self) -> "ConnectionPool":
167
- """Return a pool. If none exists yet, create one.
327
+ return config
168
328
 
169
- Ensures that the pool is initialized and returns the instance.
329
+ async def _create_pool(self) -> ConnectionPool:
330
+ """Create the actual async connection pool."""
331
+ logger.info("Creating psqlpy connection pool", extra={"adapter": "psqlpy"})
170
332
 
171
- Returns:
172
- The pool instance used by the plugin.
333
+ try:
334
+ config = self.pool_config_dict
335
+ pool = ConnectionPool(**config) # pyright: ignore
336
+ logger.info("Psqlpy connection pool created successfully", extra={"adapter": "psqlpy"})
337
+ except Exception as e:
338
+ logger.exception("Failed to create psqlpy connection pool", extra={"adapter": "psqlpy", "error": str(e)})
339
+ raise
340
+ return pool
173
341
 
174
- Raises:
175
- ImproperConfigurationError: If the pool could not be configured.
176
- """
177
- if self.pool_instance is not None:
178
- return self.pool_instance
342
+ async def _close_pool(self) -> None:
343
+ """Close the actual async connection pool."""
344
+ if not self.pool_instance:
345
+ return
179
346
 
180
- if self.pool_config is None:
181
- msg = "One of 'pool_config' or 'pool_instance' must be provided."
182
- raise ImproperConfigurationError(msg)
347
+ logger.info("Closing psqlpy connection pool", extra={"adapter": "psqlpy"})
183
348
 
184
- # pool_config is guaranteed to exist due to __post_init__
185
349
  try:
186
- # psqlpy ConnectionPool doesn't have an explicit async connect/startup method
187
- # It creates connections on demand.
188
- self.pool_instance = ConnectionPool(**self.pool_config_dict)
350
+ self.pool_instance.close()
351
+ logger.info("Psqlpy connection pool closed successfully", extra={"adapter": "psqlpy"})
189
352
  except Exception as e:
190
- msg = f"Could not configure the 'pool_instance'. Error: {e!s}. Please check your configuration."
191
- raise ImproperConfigurationError(msg) from e
353
+ logger.exception("Failed to close psqlpy connection pool", extra={"adapter": "psqlpy", "error": str(e)})
354
+ raise
192
355
 
193
- return self.pool_instance
194
-
195
- def provide_pool(self, *args: "Any", **kwargs: "Any") -> "Awaitable[ConnectionPool]":
196
- """Create or return the pool instance.
356
+ async def create_connection(self) -> PsqlpyConnection:
357
+ """Create a single async connection (not from pool).
197
358
 
198
359
  Returns:
199
- An awaitable resolving to the Pool instance.
360
+ A psqlpy Connection instance.
200
361
  """
362
+ # Ensure pool exists
363
+ if not self.pool_instance:
364
+ self.pool_instance = await self._create_pool()
201
365
 
202
- async def _create() -> "ConnectionPool":
203
- return await self.create_pool()
366
+ # Get connection from pool
367
+ return await self.pool_instance.connection()
204
368
 
205
- return _create()
369
+ @asynccontextmanager
370
+ async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[PsqlpyConnection, None]:
371
+ """Provide an async connection context manager.
206
372
 
207
- def create_connection(self) -> "Awaitable[PsqlpyConnection]":
208
- """Create and return a new, standalone psqlpy connection using the configured parameters.
373
+ Args:
374
+ *args: Additional arguments.
375
+ **kwargs: Additional keyword arguments.
209
376
 
210
- Returns:
211
- An awaitable that resolves to a new Connection instance.
377
+ Yields:
378
+ A psqlpy Connection instance.
212
379
  """
380
+ # Ensure pool exists
381
+ if not self.pool_instance:
382
+ self.pool_instance = await self._create_pool()
213
383
 
214
- async def _create() -> "Connection":
215
- try:
216
- async with self.provide_connection() as conn:
217
- return conn
218
- except Exception as e:
219
- msg = f"Could not configure the psqlpy connection. Error: {e!s}"
220
- raise ImproperConfigurationError(msg) from e
221
-
222
- return _create()
384
+ async with self.pool_instance.acquire() as conn:
385
+ yield conn
223
386
 
224
387
  @asynccontextmanager
225
- async def provide_connection(self, *args: "Any", **kwargs: "Any") -> "AsyncGenerator[PsqlpyConnection, None]":
226
- """Acquire a connection from the pool.
388
+ async def provide_session(self, *args: Any, **kwargs: Any) -> AsyncGenerator[PsqlpyDriver, None]:
389
+ """Provide an async driver session context manager.
390
+
391
+ Args:
392
+ *args: Additional arguments.
393
+ **kwargs: Additional keyword arguments.
227
394
 
228
395
  Yields:
229
- A connection instance managed by the pool.
396
+ A PsqlpyDriver instance.
230
397
  """
231
- db_pool = await self.provide_pool(*args, **kwargs)
232
- async with db_pool.acquire() as conn:
233
- yield conn
398
+ async with self.provide_connection(*args, **kwargs) as conn:
399
+ # Create statement config with parameter style info if not already set
400
+ statement_config = self.statement_config
401
+ if statement_config.allowed_parameter_styles is None:
402
+ statement_config = replace(
403
+ statement_config,
404
+ allowed_parameter_styles=self.supported_parameter_styles,
405
+ target_parameter_style=self.preferred_parameter_style,
406
+ )
407
+
408
+ driver = self.driver_type(connection=conn, config=statement_config)
409
+ yield driver
410
+
411
+ async def provide_pool(self, *args: Any, **kwargs: Any) -> ConnectionPool:
412
+ """Provide async pool instance.
234
413
 
235
- def close_pool(self) -> None:
236
- """Close the connection pool."""
237
- if self.pool_instance is not None:
238
- # psqlpy pool close is synchronous
239
- self.pool_instance.close()
240
- self.pool_instance = None
241
-
242
- @asynccontextmanager
243
- async def provide_session(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[PsqlpyDriver, None]":
244
- """Create and provide a database session using a pooled connection.
245
-
246
- Yields:
247
- A Psqlpy driver instance wrapping a pooled connection.
414
+ Returns:
415
+ The async connection pool.
248
416
  """
249
- async with self.provide_connection(*args, **kwargs) as connection:
250
- yield self.driver_type(connection)
417
+ if not self.pool_instance:
418
+ self.pool_instance = await self.create_pool()
419
+ return self.pool_instance