sqlspec 0.13.1__py3-none-any.whl → 0.16.2__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 (185) hide show
  1. sqlspec/__init__.py +71 -8
  2. sqlspec/__main__.py +12 -0
  3. sqlspec/__metadata__.py +1 -3
  4. sqlspec/_serialization.py +1 -2
  5. sqlspec/_sql.py +930 -136
  6. sqlspec/_typing.py +278 -142
  7. sqlspec/adapters/adbc/__init__.py +4 -3
  8. sqlspec/adapters/adbc/_types.py +12 -0
  9. sqlspec/adapters/adbc/config.py +116 -285
  10. sqlspec/adapters/adbc/driver.py +462 -340
  11. sqlspec/adapters/aiosqlite/__init__.py +18 -3
  12. sqlspec/adapters/aiosqlite/_types.py +13 -0
  13. sqlspec/adapters/aiosqlite/config.py +202 -150
  14. sqlspec/adapters/aiosqlite/driver.py +226 -247
  15. sqlspec/adapters/asyncmy/__init__.py +18 -3
  16. sqlspec/adapters/asyncmy/_types.py +12 -0
  17. sqlspec/adapters/asyncmy/config.py +80 -199
  18. sqlspec/adapters/asyncmy/driver.py +257 -215
  19. sqlspec/adapters/asyncpg/__init__.py +19 -4
  20. sqlspec/adapters/asyncpg/_types.py +17 -0
  21. sqlspec/adapters/asyncpg/config.py +81 -214
  22. sqlspec/adapters/asyncpg/driver.py +284 -359
  23. sqlspec/adapters/bigquery/__init__.py +17 -3
  24. sqlspec/adapters/bigquery/_types.py +12 -0
  25. sqlspec/adapters/bigquery/config.py +191 -299
  26. sqlspec/adapters/bigquery/driver.py +474 -634
  27. sqlspec/adapters/duckdb/__init__.py +14 -3
  28. sqlspec/adapters/duckdb/_types.py +12 -0
  29. sqlspec/adapters/duckdb/config.py +414 -397
  30. sqlspec/adapters/duckdb/driver.py +342 -393
  31. sqlspec/adapters/oracledb/__init__.py +19 -5
  32. sqlspec/adapters/oracledb/_types.py +14 -0
  33. sqlspec/adapters/oracledb/config.py +123 -458
  34. sqlspec/adapters/oracledb/driver.py +505 -531
  35. sqlspec/adapters/psqlpy/__init__.py +13 -3
  36. sqlspec/adapters/psqlpy/_types.py +11 -0
  37. sqlspec/adapters/psqlpy/config.py +93 -307
  38. sqlspec/adapters/psqlpy/driver.py +504 -213
  39. sqlspec/adapters/psycopg/__init__.py +19 -5
  40. sqlspec/adapters/psycopg/_types.py +17 -0
  41. sqlspec/adapters/psycopg/config.py +143 -472
  42. sqlspec/adapters/psycopg/driver.py +704 -825
  43. sqlspec/adapters/sqlite/__init__.py +14 -3
  44. sqlspec/adapters/sqlite/_types.py +11 -0
  45. sqlspec/adapters/sqlite/config.py +208 -142
  46. sqlspec/adapters/sqlite/driver.py +263 -278
  47. sqlspec/base.py +105 -9
  48. sqlspec/{statement/builder → builder}/__init__.py +12 -14
  49. sqlspec/{statement/builder/base.py → builder/_base.py} +184 -86
  50. sqlspec/{statement/builder/column.py → builder/_column.py} +97 -60
  51. sqlspec/{statement/builder/ddl.py → builder/_ddl.py} +61 -131
  52. sqlspec/{statement/builder → builder}/_ddl_utils.py +4 -10
  53. sqlspec/{statement/builder/delete.py → builder/_delete.py} +10 -30
  54. sqlspec/builder/_insert.py +421 -0
  55. sqlspec/builder/_merge.py +71 -0
  56. sqlspec/{statement/builder → builder}/_parsing_utils.py +49 -26
  57. sqlspec/builder/_select.py +170 -0
  58. sqlspec/{statement/builder/update.py → builder/_update.py} +16 -20
  59. sqlspec/builder/mixins/__init__.py +55 -0
  60. sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
  61. sqlspec/{statement/builder/mixins/_delete_from.py → builder/mixins/_delete_operations.py} +8 -1
  62. sqlspec/builder/mixins/_insert_operations.py +244 -0
  63. sqlspec/{statement/builder/mixins/_join.py → builder/mixins/_join_operations.py} +45 -13
  64. sqlspec/{statement/builder/mixins/_merge_clauses.py → builder/mixins/_merge_operations.py} +188 -30
  65. sqlspec/builder/mixins/_order_limit_operations.py +135 -0
  66. sqlspec/builder/mixins/_pivot_operations.py +153 -0
  67. sqlspec/builder/mixins/_select_operations.py +604 -0
  68. sqlspec/builder/mixins/_update_operations.py +202 -0
  69. sqlspec/builder/mixins/_where_clause.py +644 -0
  70. sqlspec/cli.py +247 -0
  71. sqlspec/config.py +183 -138
  72. sqlspec/core/__init__.py +63 -0
  73. sqlspec/core/cache.py +871 -0
  74. sqlspec/core/compiler.py +417 -0
  75. sqlspec/core/filters.py +830 -0
  76. sqlspec/core/hashing.py +310 -0
  77. sqlspec/core/parameters.py +1237 -0
  78. sqlspec/core/result.py +677 -0
  79. sqlspec/{statement → core}/splitter.py +321 -191
  80. sqlspec/core/statement.py +676 -0
  81. sqlspec/driver/__init__.py +7 -10
  82. sqlspec/driver/_async.py +422 -163
  83. sqlspec/driver/_common.py +545 -287
  84. sqlspec/driver/_sync.py +426 -160
  85. sqlspec/driver/mixins/__init__.py +2 -13
  86. sqlspec/driver/mixins/_result_tools.py +193 -0
  87. sqlspec/driver/mixins/_sql_translator.py +65 -14
  88. sqlspec/exceptions.py +5 -252
  89. sqlspec/extensions/aiosql/adapter.py +93 -96
  90. sqlspec/extensions/litestar/__init__.py +2 -1
  91. sqlspec/extensions/litestar/cli.py +48 -0
  92. sqlspec/extensions/litestar/config.py +0 -1
  93. sqlspec/extensions/litestar/handlers.py +15 -26
  94. sqlspec/extensions/litestar/plugin.py +21 -16
  95. sqlspec/extensions/litestar/providers.py +17 -52
  96. sqlspec/loader.py +423 -104
  97. sqlspec/migrations/__init__.py +35 -0
  98. sqlspec/migrations/base.py +414 -0
  99. sqlspec/migrations/commands.py +443 -0
  100. sqlspec/migrations/loaders.py +402 -0
  101. sqlspec/migrations/runner.py +213 -0
  102. sqlspec/migrations/tracker.py +140 -0
  103. sqlspec/migrations/utils.py +129 -0
  104. sqlspec/protocols.py +51 -186
  105. sqlspec/storage/__init__.py +1 -1
  106. sqlspec/storage/backends/base.py +37 -40
  107. sqlspec/storage/backends/fsspec.py +136 -112
  108. sqlspec/storage/backends/obstore.py +138 -160
  109. sqlspec/storage/capabilities.py +5 -4
  110. sqlspec/storage/registry.py +57 -106
  111. sqlspec/typing.py +136 -115
  112. sqlspec/utils/__init__.py +2 -2
  113. sqlspec/utils/correlation.py +0 -3
  114. sqlspec/utils/deprecation.py +6 -6
  115. sqlspec/utils/fixtures.py +6 -6
  116. sqlspec/utils/logging.py +0 -2
  117. sqlspec/utils/module_loader.py +7 -12
  118. sqlspec/utils/singleton.py +0 -1
  119. sqlspec/utils/sync_tools.py +17 -38
  120. sqlspec/utils/text.py +12 -51
  121. sqlspec/utils/type_guards.py +482 -235
  122. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/METADATA +7 -2
  123. sqlspec-0.16.2.dist-info/RECORD +134 -0
  124. sqlspec-0.16.2.dist-info/entry_points.txt +2 -0
  125. sqlspec/driver/connection.py +0 -207
  126. sqlspec/driver/mixins/_csv_writer.py +0 -91
  127. sqlspec/driver/mixins/_pipeline.py +0 -512
  128. sqlspec/driver/mixins/_result_utils.py +0 -140
  129. sqlspec/driver/mixins/_storage.py +0 -926
  130. sqlspec/driver/mixins/_type_coercion.py +0 -130
  131. sqlspec/driver/parameters.py +0 -138
  132. sqlspec/service/__init__.py +0 -4
  133. sqlspec/service/_util.py +0 -147
  134. sqlspec/service/base.py +0 -1131
  135. sqlspec/service/pagination.py +0 -26
  136. sqlspec/statement/__init__.py +0 -21
  137. sqlspec/statement/builder/insert.py +0 -288
  138. sqlspec/statement/builder/merge.py +0 -95
  139. sqlspec/statement/builder/mixins/__init__.py +0 -65
  140. sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
  141. sqlspec/statement/builder/mixins/_case_builder.py +0 -91
  142. sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
  143. sqlspec/statement/builder/mixins/_from.py +0 -63
  144. sqlspec/statement/builder/mixins/_group_by.py +0 -118
  145. sqlspec/statement/builder/mixins/_having.py +0 -35
  146. sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
  147. sqlspec/statement/builder/mixins/_insert_into.py +0 -36
  148. sqlspec/statement/builder/mixins/_insert_values.py +0 -67
  149. sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
  150. sqlspec/statement/builder/mixins/_order_by.py +0 -46
  151. sqlspec/statement/builder/mixins/_pivot.py +0 -79
  152. sqlspec/statement/builder/mixins/_returning.py +0 -37
  153. sqlspec/statement/builder/mixins/_select_columns.py +0 -61
  154. sqlspec/statement/builder/mixins/_set_ops.py +0 -122
  155. sqlspec/statement/builder/mixins/_unpivot.py +0 -77
  156. sqlspec/statement/builder/mixins/_update_from.py +0 -55
  157. sqlspec/statement/builder/mixins/_update_set.py +0 -94
  158. sqlspec/statement/builder/mixins/_update_table.py +0 -29
  159. sqlspec/statement/builder/mixins/_where.py +0 -401
  160. sqlspec/statement/builder/mixins/_window_functions.py +0 -86
  161. sqlspec/statement/builder/select.py +0 -221
  162. sqlspec/statement/filters.py +0 -596
  163. sqlspec/statement/parameter_manager.py +0 -220
  164. sqlspec/statement/parameters.py +0 -867
  165. sqlspec/statement/pipelines/__init__.py +0 -210
  166. sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
  167. sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
  168. sqlspec/statement/pipelines/context.py +0 -115
  169. sqlspec/statement/pipelines/transformers/__init__.py +0 -7
  170. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
  171. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
  172. sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
  173. sqlspec/statement/pipelines/validators/__init__.py +0 -23
  174. sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
  175. sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
  176. sqlspec/statement/pipelines/validators/_performance.py +0 -718
  177. sqlspec/statement/pipelines/validators/_security.py +0 -967
  178. sqlspec/statement/result.py +0 -435
  179. sqlspec/statement/sql.py +0 -1704
  180. sqlspec/statement/sql_compiler.py +0 -140
  181. sqlspec/utils/cached_property.py +0 -25
  182. sqlspec-0.13.1.dist-info/RECORD +0 -150
  183. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/WHEEL +0 -0
  184. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/LICENSE +0 -0
  185. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/NOTICE +0 -0
@@ -3,276 +3,120 @@
3
3
  import contextlib
4
4
  import logging
5
5
  from contextlib import asynccontextmanager
6
- from typing import TYPE_CHECKING, Any, ClassVar, Optional, cast
6
+ from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypedDict, Union, cast
7
7
 
8
8
  from psycopg.rows import dict_row
9
9
  from psycopg_pool import AsyncConnectionPool, ConnectionPool
10
+ from typing_extensions import NotRequired
10
11
 
12
+ from sqlspec.adapters.psycopg._types import PsycopgAsyncConnection, PsycopgSyncConnection
11
13
  from sqlspec.adapters.psycopg.driver import (
12
- PsycopgAsyncConnection,
14
+ PsycopgAsyncCursor,
13
15
  PsycopgAsyncDriver,
14
- PsycopgSyncConnection,
16
+ PsycopgSyncCursor,
15
17
  PsycopgSyncDriver,
18
+ psycopg_statement_config,
16
19
  )
17
20
  from sqlspec.config import AsyncDatabaseConfig, SyncDatabaseConfig
18
- from sqlspec.statement.sql import SQLConfig
19
- from sqlspec.typing import DictRow, Empty
20
21
 
21
22
  if TYPE_CHECKING:
22
23
  from collections.abc import AsyncGenerator, Callable, Generator
23
24
 
24
- from psycopg import Connection
25
- from sqlglot.dialects.dialect import DialectType
25
+ from sqlspec.core.statement import StatementConfig
26
+
26
27
 
27
28
  logger = logging.getLogger("sqlspec.adapters.psycopg")
28
29
 
29
- CONNECTION_FIELDS = frozenset(
30
- {
31
- "conninfo",
32
- "host",
33
- "port",
34
- "user",
35
- "password",
36
- "dbname",
37
- "connect_timeout",
38
- "options",
39
- "application_name",
40
- "sslmode",
41
- "sslcert",
42
- "sslkey",
43
- "sslrootcert",
44
- "autocommit",
45
- }
46
- )
47
30
 
48
- POOL_FIELDS = CONNECTION_FIELDS.union(
49
- {
50
- "min_size",
51
- "max_size",
52
- "name",
53
- "timeout",
54
- "max_waiting",
55
- "max_lifetime",
56
- "max_idle",
57
- "reconnect_timeout",
58
- "num_workers",
59
- "configure",
60
- "kwargs",
61
- }
31
+ class PsycopgConnectionParams(TypedDict, total=False):
32
+ """Psycopg connection parameters."""
33
+
34
+ conninfo: NotRequired[str]
35
+ host: NotRequired[str]
36
+ port: NotRequired[int]
37
+ user: NotRequired[str]
38
+ password: NotRequired[str]
39
+ dbname: NotRequired[str]
40
+ connect_timeout: NotRequired[int]
41
+ options: NotRequired[str]
42
+ application_name: NotRequired[str]
43
+ sslmode: NotRequired[str]
44
+ sslcert: NotRequired[str]
45
+ sslkey: NotRequired[str]
46
+ sslrootcert: NotRequired[str]
47
+ autocommit: NotRequired[bool]
48
+ extra: NotRequired[dict[str, Any]]
49
+
50
+
51
+ class PsycopgPoolParams(PsycopgConnectionParams, total=False):
52
+ """Psycopg pool parameters."""
53
+
54
+ min_size: NotRequired[int]
55
+ max_size: NotRequired[int]
56
+ name: NotRequired[str]
57
+ timeout: NotRequired[float]
58
+ max_waiting: NotRequired[int]
59
+ max_lifetime: NotRequired[float]
60
+ max_idle: NotRequired[float]
61
+ reconnect_timeout: NotRequired[float]
62
+ num_workers: NotRequired[int]
63
+ configure: NotRequired["Callable[..., Any]"]
64
+ kwargs: NotRequired[dict[str, Any]]
65
+
66
+
67
+ __all__ = (
68
+ "PsycopgAsyncConfig",
69
+ "PsycopgAsyncCursor",
70
+ "PsycopgConnectionParams",
71
+ "PsycopgPoolParams",
72
+ "PsycopgSyncConfig",
73
+ "PsycopgSyncCursor",
62
74
  )
63
75
 
64
- __all__ = ("CONNECTION_FIELDS", "POOL_FIELDS", "PsycopgAsyncConfig", "PsycopgSyncConfig")
65
-
66
76
 
67
77
  class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool, PsycopgSyncDriver]):
68
78
  """Configuration for Psycopg synchronous database connections with direct field-based configuration."""
69
79
 
70
- __slots__ = (
71
- "_dialect",
72
- "application_name",
73
- "autocommit",
74
- "configure",
75
- "connect_timeout",
76
- "conninfo",
77
- "dbname",
78
- "default_row_type",
79
- "extras",
80
- "host",
81
- "kwargs",
82
- "max_idle",
83
- "max_lifetime",
84
- "max_size",
85
- "max_waiting",
86
- "min_size",
87
- "name",
88
- "num_workers",
89
- "options",
90
- "password",
91
- "pool_instance",
92
- "port",
93
- "reconnect_timeout",
94
- "sslcert",
95
- "sslkey",
96
- "sslmode",
97
- "sslrootcert",
98
- "statement_config",
99
- "timeout",
100
- "user",
101
- )
102
-
103
- is_async: ClassVar[bool] = False
104
- supports_connection_pooling: ClassVar[bool] = True
105
-
106
- # Driver class reference for dialect resolution
107
- driver_type: type[PsycopgSyncDriver] = PsycopgSyncDriver
108
- connection_type: type[PsycopgSyncConnection] = PsycopgSyncConnection
109
- # Parameter style support information
110
- supported_parameter_styles: ClassVar[tuple[str, ...]] = ("pyformat_positional", "pyformat_named")
111
- """Psycopg supports %s (positional) and %(name)s (named) parameter styles."""
112
-
113
- preferred_parameter_style: ClassVar[str] = "pyformat_positional"
114
- """Psycopg's native parameter style is %s (pyformat positional)."""
80
+ driver_type: "ClassVar[type[PsycopgSyncDriver]]" = PsycopgSyncDriver
81
+ connection_type: "ClassVar[type[PsycopgSyncConnection]]" = PsycopgSyncConnection
115
82
 
116
83
  def __init__(
117
84
  self,
118
- statement_config: "Optional[SQLConfig]" = None,
119
- default_row_type: "type[DictRow]" = DictRow,
120
- # Connection parameters
121
- conninfo: Optional[str] = None,
122
- host: Optional[str] = None,
123
- port: Optional[int] = None,
124
- user: Optional[str] = None,
125
- password: Optional[str] = None,
126
- dbname: Optional[str] = None,
127
- connect_timeout: Optional[float] = None,
128
- options: Optional[str] = None,
129
- application_name: Optional[str] = None,
130
- sslmode: Optional[str] = None,
131
- sslcert: Optional[str] = None,
132
- sslkey: Optional[str] = None,
133
- sslrootcert: Optional[str] = None,
134
- autocommit: Optional[bool] = None,
135
- # Pool parameters
136
- min_size: Optional[int] = None,
137
- max_size: Optional[int] = None,
138
- name: Optional[str] = None,
139
- timeout: Optional[float] = None,
140
- max_waiting: Optional[int] = None,
141
- max_lifetime: Optional[float] = None,
142
- max_idle: Optional[float] = None,
143
- reconnect_timeout: Optional[float] = None,
144
- num_workers: Optional[int] = None,
145
- configure: Optional["Callable[[Connection[Any]], None]"] = None,
146
- kwargs: Optional[dict[str, Any]] = None,
147
- # User-defined extras
148
- extras: Optional[dict[str, Any]] = None,
149
- **additional_kwargs: Any,
85
+ *,
86
+ pool_config: "Optional[Union[PsycopgPoolParams, dict[str, Any]]]" = None,
87
+ pool_instance: Optional["ConnectionPool"] = None,
88
+ statement_config: "Optional[StatementConfig]" = None,
89
+ migration_config: Optional[dict[str, Any]] = None,
150
90
  ) -> None:
151
91
  """Initialize Psycopg synchronous configuration.
152
92
 
153
93
  Args:
94
+ pool_config: Pool configuration parameters (TypedDict or dict)
95
+ pool_instance: Existing pool instance to use
154
96
  statement_config: Default SQL statement configuration
155
- default_row_type: Default row type for results
156
- conninfo: Connection string in libpq format
157
- host: Database server host
158
- port: Database server port
159
- user: Database user
160
- password: Database password
161
- dbname: Database name
162
- connect_timeout: Connection timeout in seconds
163
- options: Command-line options to send to the server
164
- application_name: Application name for logging and statistics
165
- sslmode: SSL mode (disable, prefer, require, etc.)
166
- sslcert: SSL client certificate file
167
- sslkey: SSL client private key file
168
- sslrootcert: SSL root certificate file
169
- autocommit: Enable autocommit mode
170
- min_size: Minimum number of connections in the pool
171
- max_size: Maximum number of connections in the pool
172
- name: Name of the connection pool
173
- timeout: Timeout for acquiring connections
174
- max_waiting: Maximum number of waiting clients
175
- max_lifetime: Maximum connection lifetime
176
- max_idle: Maximum idle time for connections
177
- reconnect_timeout: Time between reconnection attempts
178
- num_workers: Number of background workers
179
- configure: Callback to configure new connections
180
- kwargs: Additional connection parameters
181
- extras: Additional connection parameters not explicitly defined
182
- **additional_kwargs: Additional parameters (stored in extras)
183
- """
184
- # Store connection parameters as instance attributes
185
- self.conninfo = conninfo
186
- self.host = host
187
- self.port = port
188
- self.user = user
189
- self.password = password
190
- self.dbname = dbname
191
- self.connect_timeout = connect_timeout
192
- self.options = options
193
- self.application_name = application_name
194
- self.sslmode = sslmode
195
- self.sslcert = sslcert
196
- self.sslkey = sslkey
197
- self.sslrootcert = sslrootcert
198
- self.autocommit = autocommit
199
-
200
- # Store pool parameters as instance attributes
201
- self.min_size = min_size
202
- self.max_size = max_size
203
- self.name = name
204
- self.timeout = timeout
205
- self.max_waiting = max_waiting
206
- self.max_lifetime = max_lifetime
207
- self.max_idle = max_idle
208
- self.reconnect_timeout = reconnect_timeout
209
- self.num_workers = num_workers
210
- self.configure = configure
211
- self.kwargs = kwargs or {}
212
-
213
- self.extras = extras or {}
214
- self.extras.update(additional_kwargs)
215
-
216
- # Store other config
217
- self.statement_config = statement_config or SQLConfig()
218
- self.default_row_type = default_row_type
219
- self._dialect: DialectType = None
220
-
221
- super().__init__()
222
-
223
- @property
224
- def connection_config_dict(self) -> dict[str, Any]:
225
- """Return the connection configuration as a dict for psycopg operations.
226
-
227
- Returns only connection-specific parameters.
228
- """
229
- # Gather non-None parameters from connection fields only
230
- config = {
231
- field: getattr(self, field)
232
- for field in CONNECTION_FIELDS
233
- if getattr(self, field, None) is not None and getattr(self, field) is not Empty
234
- }
235
-
236
- # Merge extras and kwargs
237
- config.update(self.extras)
238
- if self.kwargs:
239
- config.update(self.kwargs)
240
-
241
- config["row_factory"] = dict_row
97
+ migration_config: Migration configuration
242
98
 
243
- return config
244
-
245
- @property
246
- def pool_config_dict(self) -> dict[str, Any]:
247
- """Return the pool configuration as a dict for psycopg pool operations.
248
-
249
- Returns all configuration parameters including connection and pool-specific parameters.
250
99
  """
251
- # Gather non-None parameters from all fields (connection + pool)
252
- config = {
253
- field: getattr(self, field)
254
- for field in POOL_FIELDS
255
- if getattr(self, field, None) is not None and getattr(self, field) is not Empty
256
- }
257
-
258
- # Merge extras and kwargs
259
- config.update(self.extras)
260
- if self.kwargs:
261
- config.update(self.kwargs)
262
-
263
- config["row_factory"] = dict_row
264
-
265
- return config
100
+ processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
101
+ if "extra" in processed_pool_config:
102
+ extras = processed_pool_config.pop("extra")
103
+ processed_pool_config.update(extras)
104
+
105
+ super().__init__(
106
+ pool_config=processed_pool_config,
107
+ pool_instance=pool_instance,
108
+ migration_config=migration_config,
109
+ statement_config=statement_config or psycopg_statement_config,
110
+ )
266
111
 
267
112
  def _create_pool(self) -> "ConnectionPool":
268
113
  """Create the actual connection pool."""
269
114
  logger.info("Creating Psycopg connection pool", extra={"adapter": "psycopg"})
270
115
 
271
116
  try:
272
- all_config = self.pool_config_dict.copy()
117
+ all_config = dict(self.pool_config)
273
118
 
274
- # Separate pool-specific parameters that ConnectionPool accepts directly
275
- pool_params = {
119
+ pool_parameters = {
276
120
  "min_size": all_config.pop("min_size", 4),
277
121
  "max_size": all_config.pop("max_size", None),
278
122
  "name": all_config.pop("name", None),
@@ -284,29 +128,34 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
284
128
  "num_workers": all_config.pop("num_workers", 3),
285
129
  }
286
130
 
287
- # Capture autocommit setting before configuring the pool
288
131
  autocommit_setting = all_config.get("autocommit")
289
132
 
290
133
  def configure_connection(conn: "PsycopgSyncConnection") -> None:
291
134
  conn.row_factory = dict_row
292
- # Apply autocommit setting if specified
293
135
  if autocommit_setting is not None:
294
136
  conn.autocommit = autocommit_setting
295
137
 
296
- pool_params["configure"] = all_config.pop("configure", configure_connection)
138
+ try:
139
+ import pgvector.psycopg
140
+
141
+ pgvector.psycopg.register_vector(conn)
142
+ logger.debug("pgvector registered successfully for psycopg sync connection")
143
+ except ImportError:
144
+ pass
145
+ except Exception as e:
146
+ logger.debug("Failed to register pgvector for psycopg sync: %s", e)
147
+
148
+ pool_parameters["configure"] = all_config.pop("configure", configure_connection)
297
149
 
298
- pool_params = {k: v for k, v in pool_params.items() if v is not None}
150
+ pool_parameters = {k: v for k, v in pool_parameters.items() if v is not None}
299
151
 
300
152
  conninfo = all_config.pop("conninfo", None)
301
153
  if conninfo:
302
- # If conninfo is provided, use it directly
303
- # Don't pass kwargs when using conninfo string
304
- pool = ConnectionPool(conninfo, open=True, **pool_params)
154
+ pool = ConnectionPool(conninfo, open=True, **pool_parameters)
305
155
  else:
306
- # row_factory is already popped out earlier
307
- all_config.pop("row_factory", None)
308
- all_config.pop("kwargs", None)
309
- pool = ConnectionPool("", kwargs=all_config, open=True, **pool_params)
156
+ kwargs = all_config.pop("kwargs", {})
157
+ all_config.update(kwargs)
158
+ pool = ConnectionPool("", kwargs=all_config, open=True, **pool_parameters)
310
159
 
311
160
  logger.info("Psycopg connection pool created successfully", extra={"adapter": "psycopg"})
312
161
  except Exception as e:
@@ -322,7 +171,6 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
322
171
  logger.info("Closing Psycopg connection pool", extra={"adapter": "psycopg"})
323
172
 
324
173
  try:
325
- # This avoids the "cannot join current thread" error during garbage collection
326
174
  if hasattr(self.pool_instance, "_closed"):
327
175
  self.pool_instance._closed = True
328
176
 
@@ -366,29 +214,22 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
366
214
  conn.close()
367
215
 
368
216
  @contextlib.contextmanager
369
- def provide_session(self, *args: Any, **kwargs: Any) -> "Generator[PsycopgSyncDriver, None, None]":
217
+ def provide_session(
218
+ self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
219
+ ) -> "Generator[PsycopgSyncDriver, None, None]":
370
220
  """Provide a driver session context manager.
371
221
 
372
222
  Args:
373
223
  *args: Additional arguments.
224
+ statement_config: Optional statement configuration override.
374
225
  **kwargs: Additional keyword arguments.
375
226
 
376
227
  Yields:
377
228
  A PsycopgSyncDriver instance.
378
229
  """
379
230
  with self.provide_connection(*args, **kwargs) as conn:
380
- statement_config = self.statement_config
381
- # Inject parameter style info if not already set
382
- if statement_config.allowed_parameter_styles is None:
383
- from dataclasses import replace
384
-
385
- statement_config = replace(
386
- statement_config,
387
- allowed_parameter_styles=self.supported_parameter_styles,
388
- target_parameter_style=self.preferred_parameter_style,
389
- )
390
- driver = self.driver_type(connection=conn, config=statement_config)
391
- yield driver
231
+ final_statement_config = statement_config or self.statement_config
232
+ yield self.driver_type(connection=conn, statement_config=final_statement_config)
392
233
 
393
234
  def provide_pool(self, *args: Any, **kwargs: Any) -> "ConnectionPool":
394
235
  """Provide pool instance.
@@ -410,218 +251,50 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
410
251
  Dictionary mapping type names to types.
411
252
  """
412
253
  namespace = super().get_signature_namespace()
413
- namespace.update({"PsycopgSyncConnection": PsycopgSyncConnection})
254
+ namespace.update({"PsycopgSyncConnection": PsycopgSyncConnection, "PsycopgSyncCursor": PsycopgSyncCursor})
414
255
  return namespace
415
256
 
416
257
 
417
258
  class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnectionPool, PsycopgAsyncDriver]):
418
259
  """Configuration for Psycopg asynchronous database connections with direct field-based configuration."""
419
260
 
420
- __slots__ = (
421
- "_dialect",
422
- "application_name",
423
- "autocommit",
424
- "configure",
425
- "connect_timeout",
426
- "conninfo",
427
- "dbname",
428
- "default_row_type",
429
- "extras",
430
- "host",
431
- "kwargs",
432
- "max_idle",
433
- "max_lifetime",
434
- "max_size",
435
- "max_waiting",
436
- "min_size",
437
- "name",
438
- "num_workers",
439
- "options",
440
- "password",
441
- "pool_instance",
442
- "port",
443
- "reconnect_timeout",
444
- "sslcert",
445
- "sslkey",
446
- "sslmode",
447
- "sslrootcert",
448
- "statement_config",
449
- "timeout",
450
- "user",
451
- )
452
-
453
- is_async: ClassVar[bool] = True
454
- supports_connection_pooling: ClassVar[bool] = True
455
-
456
- # Driver class reference for dialect resolution
457
- driver_type: type[PsycopgAsyncDriver] = PsycopgAsyncDriver
458
- connection_type: type[PsycopgAsyncConnection] = PsycopgAsyncConnection
459
-
460
- # Parameter style support information
461
- supported_parameter_styles: ClassVar[tuple[str, ...]] = ("pyformat_positional", "pyformat_named")
462
- """Psycopg supports %s (pyformat_positional) and %(name)s (pyformat_named) parameter styles."""
463
-
464
- preferred_parameter_style: ClassVar[str] = "pyformat_positional"
465
- """Psycopg's preferred parameter style is %s (pyformat_positional)."""
261
+ driver_type: ClassVar[type[PsycopgAsyncDriver]] = PsycopgAsyncDriver
262
+ connection_type: "ClassVar[type[PsycopgAsyncConnection]]" = PsycopgAsyncConnection
466
263
 
467
264
  def __init__(
468
265
  self,
469
- statement_config: "Optional[SQLConfig]" = None,
470
- default_row_type: "type[DictRow]" = DictRow,
471
- # Connection parameters
472
- conninfo: Optional[str] = None,
473
- host: Optional[str] = None,
474
- port: Optional[int] = None,
475
- user: Optional[str] = None,
476
- password: Optional[str] = None,
477
- dbname: Optional[str] = None,
478
- connect_timeout: Optional[float] = None,
479
- options: Optional[str] = None,
480
- application_name: Optional[str] = None,
481
- sslmode: Optional[str] = None,
482
- sslcert: Optional[str] = None,
483
- sslkey: Optional[str] = None,
484
- sslrootcert: Optional[str] = None,
485
- autocommit: Optional[bool] = None,
486
- # Pool parameters
487
- min_size: Optional[int] = None,
488
- max_size: Optional[int] = None,
489
- name: Optional[str] = None,
490
- timeout: Optional[float] = None,
491
- max_waiting: Optional[int] = None,
492
- max_lifetime: Optional[float] = None,
493
- max_idle: Optional[float] = None,
494
- reconnect_timeout: Optional[float] = None,
495
- num_workers: Optional[int] = None,
496
- configure: Optional["Callable[[Connection[Any]], None]"] = None,
497
- kwargs: Optional[dict[str, Any]] = None,
498
- # User-defined extras
499
- extras: Optional[dict[str, Any]] = None,
500
- **additional_kwargs: Any,
266
+ *,
267
+ pool_config: "Optional[Union[PsycopgPoolParams, dict[str, Any]]]" = None,
268
+ pool_instance: "Optional[AsyncConnectionPool]" = None,
269
+ migration_config: "Optional[dict[str, Any]]" = None,
270
+ statement_config: "Optional[StatementConfig]" = None,
501
271
  ) -> None:
502
272
  """Initialize Psycopg asynchronous configuration.
503
273
 
504
274
  Args:
275
+ pool_config: Pool configuration parameters (TypedDict or dict)
276
+ pool_instance: Existing pool instance to use
505
277
  statement_config: Default SQL statement configuration
506
- default_row_type: Default row type for results
507
- conninfo: Connection string in libpq format
508
- host: Database server host
509
- port: Database server port
510
- user: Database user
511
- password: Database password
512
- dbname: Database name
513
- connect_timeout: Connection timeout in seconds
514
- options: Command-line options to send to the server
515
- application_name: Application name for logging and statistics
516
- sslmode: SSL mode (disable, prefer, require, etc.)
517
- sslcert: SSL client certificate file
518
- sslkey: SSL client private key file
519
- sslrootcert: SSL root certificate file
520
- autocommit: Enable autocommit mode
521
- min_size: Minimum number of connections in the pool
522
- max_size: Maximum number of connections in the pool
523
- name: Name of the connection pool
524
- timeout: Timeout for acquiring connections
525
- max_waiting: Maximum number of waiting clients
526
- max_lifetime: Maximum connection lifetime
527
- max_idle: Maximum idle time for connections
528
- reconnect_timeout: Time between reconnection attempts
529
- num_workers: Number of background workers
530
- configure: Callback to configure new connections
531
- kwargs: Additional connection parameters
532
- extras: Additional connection parameters not explicitly defined
533
- **additional_kwargs: Additional parameters (stored in extras)
278
+ migration_config: Migration configuration
534
279
  """
535
- # Store connection parameters as instance attributes
536
- self.conninfo = conninfo
537
- self.host = host
538
- self.port = port
539
- self.user = user
540
- self.password = password
541
- self.dbname = dbname
542
- self.connect_timeout = connect_timeout
543
- self.options = options
544
- self.application_name = application_name
545
- self.sslmode = sslmode
546
- self.sslcert = sslcert
547
- self.sslkey = sslkey
548
- self.sslrootcert = sslrootcert
549
- self.autocommit = autocommit
550
-
551
- # Store pool parameters as instance attributes
552
- self.min_size = min_size
553
- self.max_size = max_size
554
- self.name = name
555
- self.timeout = timeout
556
- self.max_waiting = max_waiting
557
- self.max_lifetime = max_lifetime
558
- self.max_idle = max_idle
559
- self.reconnect_timeout = reconnect_timeout
560
- self.num_workers = num_workers
561
- self.configure = configure
562
- self.kwargs = kwargs or {}
563
-
564
- self.extras = extras or {}
565
- self.extras.update(additional_kwargs)
566
-
567
- # Store other config
568
- self.statement_config = statement_config or SQLConfig()
569
- self.default_row_type = default_row_type
570
- self._dialect: DialectType = None
571
-
572
- super().__init__()
573
-
574
- @property
575
- def connection_config_dict(self) -> dict[str, Any]:
576
- """Return the connection configuration as a dict for psycopg operations.
577
-
578
- Returns only connection-specific parameters.
579
- """
580
- # Gather non-None parameters from connection fields only
581
- config = {
582
- field: getattr(self, field)
583
- for field in CONNECTION_FIELDS
584
- if getattr(self, field, None) is not None and getattr(self, field) is not Empty
585
- }
586
-
587
- # Merge extras and kwargs
588
- config.update(self.extras)
589
- if self.kwargs:
590
- config.update(self.kwargs)
591
-
592
- config["row_factory"] = dict_row
593
-
594
- return config
595
-
596
- @property
597
- def pool_config_dict(self) -> dict[str, Any]:
598
- """Return the pool configuration as a dict for psycopg pool operations.
599
-
600
- Returns all configuration parameters including connection and pool-specific parameters.
601
- """
602
- # Gather non-None parameters from all fields (connection + pool)
603
- config = {
604
- field: getattr(self, field)
605
- for field in POOL_FIELDS
606
- if getattr(self, field, None) is not None and getattr(self, field) is not Empty
607
- }
608
-
609
- # Merge extras and kwargs
610
- config.update(self.extras)
611
- if self.kwargs:
612
- config.update(self.kwargs)
613
-
614
- config["row_factory"] = dict_row
615
-
616
- return config
280
+ processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
281
+ if "extra" in processed_pool_config:
282
+ extras = processed_pool_config.pop("extra")
283
+ processed_pool_config.update(extras)
284
+
285
+ super().__init__(
286
+ pool_config=processed_pool_config,
287
+ pool_instance=pool_instance,
288
+ migration_config=migration_config,
289
+ statement_config=statement_config or psycopg_statement_config,
290
+ )
617
291
 
618
292
  async def _create_pool(self) -> "AsyncConnectionPool":
619
293
  """Create the actual async connection pool."""
620
294
 
621
- all_config = self.pool_config_dict.copy()
295
+ all_config = dict(self.pool_config)
622
296
 
623
- # Separate pool-specific parameters that AsyncConnectionPool accepts directly
624
- pool_params = {
297
+ pool_parameters = {
625
298
  "min_size": all_config.pop("min_size", 4),
626
299
  "max_size": all_config.pop("max_size", None),
627
300
  "name": all_config.pop("name", None),
@@ -638,24 +311,30 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
638
311
 
639
312
  async def configure_connection(conn: "PsycopgAsyncConnection") -> None:
640
313
  conn.row_factory = dict_row
641
- # Apply autocommit setting if specified (async version requires await)
642
314
  if autocommit_setting is not None:
643
315
  await conn.set_autocommit(autocommit_setting)
644
316
 
645
- pool_params["configure"] = all_config.pop("configure", configure_connection)
317
+ try:
318
+ from pgvector.psycopg import register_vector_async
319
+
320
+ await register_vector_async(conn)
321
+ logger.debug("pgvector registered successfully for psycopg async connection")
322
+ except ImportError:
323
+ pass
324
+ except Exception as e:
325
+ logger.debug("Failed to register pgvector for psycopg async: %s", e)
326
+
327
+ pool_parameters["configure"] = all_config.pop("configure", configure_connection)
646
328
 
647
- pool_params = {k: v for k, v in pool_params.items() if v is not None}
329
+ pool_parameters = {k: v for k, v in pool_parameters.items() if v is not None}
648
330
 
649
331
  conninfo = all_config.pop("conninfo", None)
650
332
  if conninfo:
651
- # If conninfo is provided, use it directly
652
- # Don't pass kwargs when using conninfo string
653
- pool = AsyncConnectionPool(conninfo, open=False, **pool_params)
333
+ pool = AsyncConnectionPool(conninfo, open=False, **pool_parameters)
654
334
  else:
655
- # row_factory is already popped out earlier
656
- all_config.pop("row_factory", None)
657
- all_config.pop("kwargs", None)
658
- pool = AsyncConnectionPool("", kwargs=all_config, open=False, **pool_params)
335
+ kwargs = all_config.pop("kwargs", {})
336
+ all_config.update(kwargs)
337
+ pool = AsyncConnectionPool("", kwargs=all_config, open=False, **pool_parameters)
659
338
 
660
339
  await pool.open()
661
340
 
@@ -667,7 +346,6 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
667
346
  return
668
347
 
669
348
  try:
670
- # This avoids the "cannot join current thread" error during garbage collection
671
349
  if hasattr(self.pool_instance, "_closed"):
672
350
  self.pool_instance._closed = True
673
351
 
@@ -707,29 +385,22 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
707
385
  await conn.close()
708
386
 
709
387
  @asynccontextmanager
710
- async def provide_session(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[PsycopgAsyncDriver, None]":
388
+ async def provide_session(
389
+ self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
390
+ ) -> "AsyncGenerator[PsycopgAsyncDriver, None]":
711
391
  """Provide an async driver session context manager.
712
392
 
713
393
  Args:
714
394
  *args: Additional arguments.
395
+ statement_config: Optional statement configuration override.
715
396
  **kwargs: Additional keyword arguments.
716
397
 
717
398
  Yields:
718
399
  A PsycopgAsyncDriver instance.
719
400
  """
720
401
  async with self.provide_connection(*args, **kwargs) as conn:
721
- statement_config = self.statement_config
722
- # Inject parameter style info if not already set
723
- if statement_config.allowed_parameter_styles is None:
724
- from dataclasses import replace
725
-
726
- statement_config = replace(
727
- statement_config,
728
- allowed_parameter_styles=self.supported_parameter_styles,
729
- target_parameter_style=self.preferred_parameter_style,
730
- )
731
- driver = self.driver_type(connection=conn, config=statement_config)
732
- yield driver
402
+ final_statement_config = statement_config or psycopg_statement_config
403
+ yield self.driver_type(connection=conn, statement_config=final_statement_config)
733
404
 
734
405
  async def provide_pool(self, *args: Any, **kwargs: Any) -> "AsyncConnectionPool":
735
406
  """Provide async pool instance.
@@ -751,5 +422,5 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
751
422
  Dictionary mapping type names to types.
752
423
  """
753
424
  namespace = super().get_signature_namespace()
754
- namespace.update({"PsycopgAsyncConnection": PsycopgAsyncConnection})
425
+ namespace.update({"PsycopgAsyncConnection": PsycopgAsyncConnection, "PsycopgAsyncCursor": PsycopgAsyncCursor})
755
426
  return namespace