sqlspec 0.26.0__py3-none-any.whl → 0.28.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 (212) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +55 -25
  3. sqlspec/_typing.py +155 -52
  4. sqlspec/adapters/adbc/_types.py +1 -1
  5. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  6. sqlspec/adapters/adbc/adk/store.py +880 -0
  7. sqlspec/adapters/adbc/config.py +62 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +74 -2
  9. sqlspec/adapters/adbc/driver.py +226 -58
  10. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  11. sqlspec/adapters/adbc/litestar/store.py +504 -0
  12. sqlspec/adapters/adbc/type_converter.py +44 -50
  13. sqlspec/adapters/aiosqlite/_types.py +1 -1
  14. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  15. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  16. sqlspec/adapters/aiosqlite/config.py +86 -16
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
  18. sqlspec/adapters/aiosqlite/driver.py +127 -38
  19. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  21. sqlspec/adapters/aiosqlite/pool.py +7 -7
  22. sqlspec/adapters/asyncmy/__init__.py +7 -1
  23. sqlspec/adapters/asyncmy/_types.py +1 -1
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  26. sqlspec/adapters/asyncmy/config.py +59 -17
  27. sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
  28. sqlspec/adapters/asyncmy/driver.py +293 -62
  29. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  31. sqlspec/adapters/asyncpg/__init__.py +2 -1
  32. sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
  33. sqlspec/adapters/asyncpg/_types.py +11 -7
  34. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  35. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  36. sqlspec/adapters/asyncpg/config.py +57 -36
  37. sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
  38. sqlspec/adapters/asyncpg/driver.py +153 -23
  39. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  41. sqlspec/adapters/bigquery/_types.py +1 -1
  42. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  43. sqlspec/adapters/bigquery/adk/store.py +585 -0
  44. sqlspec/adapters/bigquery/config.py +36 -11
  45. sqlspec/adapters/bigquery/data_dictionary.py +42 -2
  46. sqlspec/adapters/bigquery/driver.py +489 -144
  47. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  48. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  49. sqlspec/adapters/bigquery/type_converter.py +55 -23
  50. sqlspec/adapters/duckdb/_types.py +2 -2
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +563 -0
  53. sqlspec/adapters/duckdb/config.py +79 -21
  54. sqlspec/adapters/duckdb/data_dictionary.py +41 -2
  55. sqlspec/adapters/duckdb/driver.py +225 -44
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +5 -5
  59. sqlspec/adapters/duckdb/type_converter.py +51 -21
  60. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  61. sqlspec/adapters/oracledb/_types.py +20 -2
  62. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  63. sqlspec/adapters/oracledb/adk/store.py +1628 -0
  64. sqlspec/adapters/oracledb/config.py +120 -36
  65. sqlspec/adapters/oracledb/data_dictionary.py +87 -20
  66. sqlspec/adapters/oracledb/driver.py +475 -86
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  69. sqlspec/adapters/oracledb/migrations.py +316 -25
  70. sqlspec/adapters/oracledb/type_converter.py +91 -16
  71. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  72. sqlspec/adapters/psqlpy/_types.py +2 -1
  73. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  74. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  75. sqlspec/adapters/psqlpy/config.py +45 -19
  76. sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
  77. sqlspec/adapters/psqlpy/driver.py +108 -41
  78. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  79. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  80. sqlspec/adapters/psqlpy/type_converter.py +40 -11
  81. sqlspec/adapters/psycopg/_type_handlers.py +80 -0
  82. sqlspec/adapters/psycopg/_types.py +2 -1
  83. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  84. sqlspec/adapters/psycopg/adk/store.py +962 -0
  85. sqlspec/adapters/psycopg/config.py +65 -37
  86. sqlspec/adapters/psycopg/data_dictionary.py +91 -3
  87. sqlspec/adapters/psycopg/driver.py +200 -78
  88. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  90. sqlspec/adapters/sqlite/__init__.py +2 -1
  91. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  92. sqlspec/adapters/sqlite/_types.py +1 -1
  93. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  94. sqlspec/adapters/sqlite/adk/store.py +582 -0
  95. sqlspec/adapters/sqlite/config.py +85 -16
  96. sqlspec/adapters/sqlite/data_dictionary.py +34 -2
  97. sqlspec/adapters/sqlite/driver.py +120 -52
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +5 -5
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +91 -58
  104. sqlspec/builder/_column.py +5 -5
  105. sqlspec/builder/_ddl.py +98 -89
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +41 -44
  109. sqlspec/builder/_insert.py +5 -82
  110. sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +9 -11
  113. sqlspec/builder/_select.py +1313 -25
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +76 -69
  116. sqlspec/config.py +331 -62
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +18 -18
  119. sqlspec/core/compiler.py +6 -8
  120. sqlspec/core/filters.py +55 -47
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +76 -45
  123. sqlspec/core/result.py +234 -47
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +32 -31
  126. sqlspec/core/type_conversion.py +3 -2
  127. sqlspec/driver/__init__.py +1 -3
  128. sqlspec/driver/_async.py +183 -160
  129. sqlspec/driver/_common.py +197 -109
  130. sqlspec/driver/_sync.py +189 -161
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +70 -7
  134. sqlspec/extensions/adk/__init__.py +53 -0
  135. sqlspec/extensions/adk/_types.py +51 -0
  136. sqlspec/extensions/adk/converters.py +172 -0
  137. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  138. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  139. sqlspec/extensions/adk/service.py +181 -0
  140. sqlspec/extensions/adk/store.py +536 -0
  141. sqlspec/extensions/aiosql/adapter.py +69 -61
  142. sqlspec/extensions/fastapi/__init__.py +21 -0
  143. sqlspec/extensions/fastapi/extension.py +331 -0
  144. sqlspec/extensions/fastapi/providers.py +543 -0
  145. sqlspec/extensions/flask/__init__.py +36 -0
  146. sqlspec/extensions/flask/_state.py +71 -0
  147. sqlspec/extensions/flask/_utils.py +40 -0
  148. sqlspec/extensions/flask/extension.py +389 -0
  149. sqlspec/extensions/litestar/__init__.py +21 -4
  150. sqlspec/extensions/litestar/cli.py +54 -10
  151. sqlspec/extensions/litestar/config.py +56 -266
  152. sqlspec/extensions/litestar/handlers.py +46 -17
  153. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  154. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  155. sqlspec/extensions/litestar/plugin.py +349 -224
  156. sqlspec/extensions/litestar/providers.py +25 -25
  157. sqlspec/extensions/litestar/store.py +265 -0
  158. sqlspec/extensions/starlette/__init__.py +10 -0
  159. sqlspec/extensions/starlette/_state.py +25 -0
  160. sqlspec/extensions/starlette/_utils.py +52 -0
  161. sqlspec/extensions/starlette/extension.py +254 -0
  162. sqlspec/extensions/starlette/middleware.py +154 -0
  163. sqlspec/loader.py +30 -49
  164. sqlspec/migrations/base.py +200 -76
  165. sqlspec/migrations/commands.py +591 -62
  166. sqlspec/migrations/context.py +6 -9
  167. sqlspec/migrations/fix.py +199 -0
  168. sqlspec/migrations/loaders.py +47 -19
  169. sqlspec/migrations/runner.py +241 -75
  170. sqlspec/migrations/tracker.py +237 -21
  171. sqlspec/migrations/utils.py +51 -3
  172. sqlspec/migrations/validation.py +177 -0
  173. sqlspec/protocols.py +106 -36
  174. sqlspec/storage/_utils.py +85 -0
  175. sqlspec/storage/backends/fsspec.py +133 -107
  176. sqlspec/storage/backends/local.py +78 -51
  177. sqlspec/storage/backends/obstore.py +276 -168
  178. sqlspec/storage/registry.py +75 -39
  179. sqlspec/typing.py +30 -84
  180. sqlspec/utils/__init__.py +25 -4
  181. sqlspec/utils/arrow_helpers.py +81 -0
  182. sqlspec/utils/config_resolver.py +6 -6
  183. sqlspec/utils/correlation.py +4 -5
  184. sqlspec/utils/data_transformation.py +3 -2
  185. sqlspec/utils/deprecation.py +9 -8
  186. sqlspec/utils/fixtures.py +4 -4
  187. sqlspec/utils/logging.py +46 -6
  188. sqlspec/utils/module_loader.py +205 -5
  189. sqlspec/utils/portal.py +311 -0
  190. sqlspec/utils/schema.py +288 -0
  191. sqlspec/utils/serializers.py +113 -4
  192. sqlspec/utils/sync_tools.py +36 -22
  193. sqlspec/utils/text.py +1 -2
  194. sqlspec/utils/type_guards.py +136 -20
  195. sqlspec/utils/version.py +433 -0
  196. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
  197. sqlspec-0.28.0.dist-info/RECORD +221 -0
  198. sqlspec/builder/mixins/__init__.py +0 -55
  199. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
  200. sqlspec/builder/mixins/_delete_operations.py +0 -50
  201. sqlspec/builder/mixins/_insert_operations.py +0 -282
  202. sqlspec/builder/mixins/_merge_operations.py +0 -698
  203. sqlspec/builder/mixins/_order_limit_operations.py +0 -145
  204. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  205. sqlspec/builder/mixins/_select_operations.py +0 -930
  206. sqlspec/builder/mixins/_update_operations.py +0 -199
  207. sqlspec/builder/mixins/_where_clause.py +0 -1298
  208. sqlspec-0.26.0.dist-info/RECORD +0 -157
  209. sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
  210. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
  211. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
  212. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,7 +3,7 @@
3
3
  import contextlib
4
4
  import logging
5
5
  from contextlib import asynccontextmanager
6
- from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypedDict, Union, cast
6
+ from typing import TYPE_CHECKING, Any, ClassVar, TypedDict, cast
7
7
 
8
8
  from psycopg.rows import dict_row
9
9
  from psycopg_pool import AsyncConnectionPool, ConnectionPool
@@ -18,6 +18,7 @@ from sqlspec.adapters.psycopg.driver import (
18
18
  psycopg_statement_config,
19
19
  )
20
20
  from sqlspec.config import AsyncDatabaseConfig, SyncDatabaseConfig
21
+ from sqlspec.typing import PGVECTOR_INSTALLED
21
22
 
22
23
  if TYPE_CHECKING:
23
24
  from collections.abc import AsyncGenerator, Callable, Generator
@@ -28,7 +29,7 @@ if TYPE_CHECKING:
28
29
  logger = logging.getLogger("sqlspec.adapters.psycopg")
29
30
 
30
31
 
31
- class PsycopgConnectionParams(TypedDict, total=False):
32
+ class PsycopgConnectionParams(TypedDict):
32
33
  """Psycopg connection parameters."""
33
34
 
34
35
  conninfo: NotRequired[str]
@@ -48,7 +49,7 @@ class PsycopgConnectionParams(TypedDict, total=False):
48
49
  extra: NotRequired[dict[str, Any]]
49
50
 
50
51
 
51
- class PsycopgPoolParams(PsycopgConnectionParams, total=False):
52
+ class PsycopgPoolParams(PsycopgConnectionParams):
52
53
  """Psycopg pool parameters."""
53
54
 
54
55
  min_size: NotRequired[int]
@@ -64,10 +65,25 @@ class PsycopgPoolParams(PsycopgConnectionParams, total=False):
64
65
  kwargs: NotRequired[dict[str, Any]]
65
66
 
66
67
 
68
+ class PsycopgDriverFeatures(TypedDict):
69
+ """Psycopg driver feature flags.
70
+
71
+ enable_pgvector: Enable automatic pgvector extension support for vector similarity search.
72
+ Requires pgvector-python package (pip install pgvector) and PostgreSQL with pgvector extension.
73
+ Defaults to True when pgvector-python is installed.
74
+ Provides automatic conversion between Python objects and PostgreSQL vector types.
75
+ Enables vector similarity operations and index support.
76
+ Set to False to disable pgvector support even when package is available.
77
+ """
78
+
79
+ enable_pgvector: NotRequired[bool]
80
+
81
+
67
82
  __all__ = (
68
83
  "PsycopgAsyncConfig",
69
84
  "PsycopgAsyncCursor",
70
85
  "PsycopgConnectionParams",
86
+ "PsycopgDriverFeatures",
71
87
  "PsycopgPoolParams",
72
88
  "PsycopgSyncConfig",
73
89
  "PsycopgSyncCursor",
@@ -79,16 +95,18 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
79
95
 
80
96
  driver_type: "ClassVar[type[PsycopgSyncDriver]]" = PsycopgSyncDriver
81
97
  connection_type: "ClassVar[type[PsycopgSyncConnection]]" = PsycopgSyncConnection
98
+ supports_transactional_ddl: "ClassVar[bool]" = True
82
99
 
83
100
  def __init__(
84
101
  self,
85
102
  *,
86
- pool_config: "Optional[Union[PsycopgPoolParams, dict[str, Any]]]" = None,
87
- pool_instance: Optional["ConnectionPool"] = None,
88
- migration_config: Optional[dict[str, Any]] = None,
89
- statement_config: "Optional[StatementConfig]" = None,
90
- driver_features: "Optional[dict[str, Any]]" = None,
91
- bind_key: "Optional[str]" = None,
103
+ pool_config: "PsycopgPoolParams | dict[str, Any] | None" = None,
104
+ pool_instance: "ConnectionPool | None" = None,
105
+ migration_config: dict[str, Any] | None = None,
106
+ statement_config: "StatementConfig | None" = None,
107
+ driver_features: "dict[str, Any] | None" = None,
108
+ bind_key: "str | None" = None,
109
+ extension_config: "dict[str, dict[str, Any]] | None" = None,
92
110
  ) -> None:
93
111
  """Initialize Psycopg synchronous configuration.
94
112
 
@@ -99,19 +117,26 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
99
117
  statement_config: Default SQL statement configuration
100
118
  driver_features: Optional driver feature configuration
101
119
  bind_key: Optional unique identifier for this configuration
120
+ extension_config: Extension-specific configuration (e.g., Litestar plugin settings)
102
121
  """
103
122
  processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
104
123
  if "extra" in processed_pool_config:
105
124
  extras = processed_pool_config.pop("extra")
106
125
  processed_pool_config.update(extras)
107
126
 
127
+ if driver_features is None:
128
+ driver_features = {}
129
+ if "enable_pgvector" not in driver_features:
130
+ driver_features["enable_pgvector"] = PGVECTOR_INSTALLED
131
+
108
132
  super().__init__(
109
133
  pool_config=processed_pool_config,
110
134
  pool_instance=pool_instance,
111
135
  migration_config=migration_config,
112
136
  statement_config=statement_config or psycopg_statement_config,
113
- driver_features=driver_features or {},
137
+ driver_features=driver_features,
114
138
  bind_key=bind_key,
139
+ extension_config=extension_config,
115
140
  )
116
141
 
117
142
  def _create_pool(self) -> "ConnectionPool":
@@ -140,15 +165,10 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
140
165
  if autocommit_setting is not None:
141
166
  conn.autocommit = autocommit_setting
142
167
 
143
- try:
144
- import pgvector.psycopg
168
+ if self.driver_features.get("enable_pgvector", False):
169
+ from sqlspec.adapters.psycopg._type_handlers import register_pgvector_sync
145
170
 
146
- pgvector.psycopg.register_vector(conn)
147
- logger.debug("pgvector registered successfully for psycopg sync connection")
148
- except ImportError:
149
- pass
150
- except Exception as e:
151
- logger.debug("Failed to register pgvector for psycopg sync: %s", e)
171
+ register_pgvector_sync(conn)
152
172
 
153
173
  pool_parameters["configure"] = all_config.pop("configure", configure_connection)
154
174
 
@@ -219,7 +239,7 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
219
239
 
220
240
  @contextlib.contextmanager
221
241
  def provide_session(
222
- self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
242
+ self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
223
243
  ) -> "Generator[PsycopgSyncDriver, None, None]":
224
244
  """Provide a driver session context manager.
225
245
 
@@ -233,7 +253,9 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool
233
253
  """
234
254
  with self.provide_connection(*args, **kwargs) as conn:
235
255
  final_statement_config = statement_config or self.statement_config
236
- yield self.driver_type(connection=conn, statement_config=final_statement_config)
256
+ yield self.driver_type(
257
+ connection=conn, statement_config=final_statement_config, driver_features=self.driver_features
258
+ )
237
259
 
238
260
  def provide_pool(self, *args: Any, **kwargs: Any) -> "ConnectionPool":
239
261
  """Provide pool instance.
@@ -264,16 +286,18 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
264
286
 
265
287
  driver_type: ClassVar[type[PsycopgAsyncDriver]] = PsycopgAsyncDriver
266
288
  connection_type: "ClassVar[type[PsycopgAsyncConnection]]" = PsycopgAsyncConnection
289
+ supports_transactional_ddl: "ClassVar[bool]" = True
267
290
 
268
291
  def __init__(
269
292
  self,
270
293
  *,
271
- pool_config: "Optional[Union[PsycopgPoolParams, dict[str, Any]]]" = None,
272
- pool_instance: "Optional[AsyncConnectionPool]" = None,
273
- migration_config: "Optional[dict[str, Any]]" = None,
274
- statement_config: "Optional[StatementConfig]" = None,
275
- driver_features: "Optional[dict[str, Any]]" = None,
276
- bind_key: "Optional[str]" = None,
294
+ pool_config: "PsycopgPoolParams | dict[str, Any] | None" = None,
295
+ pool_instance: "AsyncConnectionPool | None" = None,
296
+ migration_config: "dict[str, Any] | None" = None,
297
+ statement_config: "StatementConfig | None" = None,
298
+ driver_features: "dict[str, Any] | None" = None,
299
+ bind_key: "str | None" = None,
300
+ extension_config: "dict[str, dict[str, Any]] | None" = None,
277
301
  ) -> None:
278
302
  """Initialize Psycopg asynchronous configuration.
279
303
 
@@ -284,19 +308,26 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
284
308
  statement_config: Default SQL statement configuration
285
309
  driver_features: Optional driver feature configuration
286
310
  bind_key: Optional unique identifier for this configuration
311
+ extension_config: Extension-specific configuration (e.g., Litestar plugin settings)
287
312
  """
288
313
  processed_pool_config: dict[str, Any] = dict(pool_config) if pool_config else {}
289
314
  if "extra" in processed_pool_config:
290
315
  extras = processed_pool_config.pop("extra")
291
316
  processed_pool_config.update(extras)
292
317
 
318
+ if driver_features is None:
319
+ driver_features = {}
320
+ if "enable_pgvector" not in driver_features:
321
+ driver_features["enable_pgvector"] = PGVECTOR_INSTALLED
322
+
293
323
  super().__init__(
294
324
  pool_config=processed_pool_config,
295
325
  pool_instance=pool_instance,
296
326
  migration_config=migration_config,
297
327
  statement_config=statement_config or psycopg_statement_config,
298
- driver_features=driver_features or {},
328
+ driver_features=driver_features,
299
329
  bind_key=bind_key,
330
+ extension_config=extension_config,
300
331
  )
301
332
 
302
333
  async def _create_pool(self) -> "AsyncConnectionPool":
@@ -323,15 +354,10 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
323
354
  if autocommit_setting is not None:
324
355
  await conn.set_autocommit(autocommit_setting)
325
356
 
326
- try:
327
- from pgvector.psycopg import register_vector_async
357
+ if self.driver_features.get("enable_pgvector", False):
358
+ from sqlspec.adapters.psycopg._type_handlers import register_pgvector_async
328
359
 
329
- await register_vector_async(conn)
330
- logger.debug("pgvector registered successfully for psycopg async connection")
331
- except ImportError:
332
- pass
333
- except Exception as e:
334
- logger.debug("Failed to register pgvector for psycopg async: %s", e)
360
+ await register_pgvector_async(conn)
335
361
 
336
362
  pool_parameters["configure"] = all_config.pop("configure", configure_connection)
337
363
 
@@ -394,7 +420,7 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
394
420
 
395
421
  @asynccontextmanager
396
422
  async def provide_session(
397
- self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
423
+ self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
398
424
  ) -> "AsyncGenerator[PsycopgAsyncDriver, None]":
399
425
  """Provide an async driver session context manager.
400
426
 
@@ -408,7 +434,9 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec
408
434
  """
409
435
  async with self.provide_connection(*args, **kwargs) as conn:
410
436
  final_statement_config = statement_config or psycopg_statement_config
411
- yield self.driver_type(connection=conn, statement_config=final_statement_config)
437
+ yield self.driver_type(
438
+ connection=conn, statement_config=final_statement_config, driver_features=self.driver_features
439
+ )
412
440
 
413
441
  async def provide_pool(self, *args: Any, **kwargs: Any) -> "AsyncConnectionPool":
414
442
  """Provide async pool instance.
@@ -1,7 +1,7 @@
1
1
  """PostgreSQL-specific data dictionary for metadata queries via psycopg."""
2
2
 
3
3
  import re
4
- from typing import TYPE_CHECKING, Optional, cast
4
+ from typing import TYPE_CHECKING, Any, cast
5
5
 
6
6
  from sqlspec.driver import (
7
7
  AsyncDataDictionaryBase,
@@ -28,7 +28,7 @@ __all__ = ("PostgresAsyncDataDictionary", "PostgresSyncDataDictionary")
28
28
  class PostgresSyncDataDictionary(SyncDataDictionaryBase):
29
29
  """PostgreSQL-specific sync data dictionary."""
30
30
 
31
- def get_version(self, driver: SyncDriverAdapterBase) -> "Optional[VersionInfo]":
31
+ def get_version(self, driver: SyncDriverAdapterBase) -> "VersionInfo | None":
32
32
  """Get PostgreSQL database version information.
33
33
 
34
34
  Args:
@@ -119,6 +119,50 @@ class PostgresSyncDataDictionary(SyncDataDictionaryBase):
119
119
  }
120
120
  return type_map.get(type_category, "TEXT")
121
121
 
122
+ def get_columns(
123
+ self, driver: SyncDriverAdapterBase, table: str, schema: "str | None" = None
124
+ ) -> "list[dict[str, Any]]":
125
+ """Get column information for a table using pg_catalog.
126
+
127
+ Args:
128
+ driver: Psycopg sync driver instance
129
+ table: Table name to query columns for
130
+ schema: Schema name (None for default 'public')
131
+
132
+ Returns:
133
+ List of column metadata dictionaries with keys:
134
+ - column_name: Name of the column
135
+ - data_type: PostgreSQL data type
136
+ - is_nullable: Whether column allows NULL (YES/NO)
137
+ - column_default: Default value if any
138
+
139
+ Notes:
140
+ Uses pg_catalog instead of information_schema to avoid potential
141
+ issues with PostgreSQL 'name' type in some drivers.
142
+ """
143
+ psycopg_driver = cast("PsycopgSyncDriver", driver)
144
+
145
+ schema_name = schema or "public"
146
+ sql = """
147
+ SELECT
148
+ a.attname::text AS column_name,
149
+ pg_catalog.format_type(a.atttypid, a.atttypmod) AS data_type,
150
+ CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS is_nullable,
151
+ pg_catalog.pg_get_expr(d.adbin, d.adrelid)::text AS column_default
152
+ FROM pg_catalog.pg_attribute a
153
+ JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
154
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
155
+ LEFT JOIN pg_catalog.pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
156
+ WHERE c.relname = %s
157
+ AND n.nspname = %s
158
+ AND a.attnum > 0
159
+ AND NOT a.attisdropped
160
+ ORDER BY a.attnum
161
+ """
162
+
163
+ result = psycopg_driver.execute(sql, (table, schema_name))
164
+ return result.data or []
165
+
122
166
  def list_available_features(self) -> "list[str]":
123
167
  """List available PostgreSQL feature flags.
124
168
 
@@ -144,7 +188,7 @@ class PostgresSyncDataDictionary(SyncDataDictionaryBase):
144
188
  class PostgresAsyncDataDictionary(AsyncDataDictionaryBase):
145
189
  """PostgreSQL-specific async data dictionary."""
146
190
 
147
- async def get_version(self, driver: AsyncDriverAdapterBase) -> "Optional[VersionInfo]":
191
+ async def get_version(self, driver: AsyncDriverAdapterBase) -> "VersionInfo | None":
148
192
  """Get PostgreSQL database version information.
149
193
 
150
194
  Args:
@@ -235,6 +279,50 @@ class PostgresAsyncDataDictionary(AsyncDataDictionaryBase):
235
279
  }
236
280
  return type_map.get(type_category, "TEXT")
237
281
 
282
+ async def get_columns(
283
+ self, driver: AsyncDriverAdapterBase, table: str, schema: "str | None" = None
284
+ ) -> "list[dict[str, Any]]":
285
+ """Get column information for a table using pg_catalog.
286
+
287
+ Args:
288
+ driver: Psycopg async driver instance
289
+ table: Table name to query columns for
290
+ schema: Schema name (None for default 'public')
291
+
292
+ Returns:
293
+ List of column metadata dictionaries with keys:
294
+ - column_name: Name of the column
295
+ - data_type: PostgreSQL data type
296
+ - is_nullable: Whether column allows NULL (YES/NO)
297
+ - column_default: Default value if any
298
+
299
+ Notes:
300
+ Uses pg_catalog instead of information_schema to avoid potential
301
+ issues with PostgreSQL 'name' type in some drivers.
302
+ """
303
+ psycopg_driver = cast("PsycopgAsyncDriver", driver)
304
+
305
+ schema_name = schema or "public"
306
+ sql = """
307
+ SELECT
308
+ a.attname::text AS column_name,
309
+ pg_catalog.format_type(a.atttypid, a.atttypmod) AS data_type,
310
+ CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS is_nullable,
311
+ pg_catalog.pg_get_expr(d.adbin, d.adrelid)::text AS column_default
312
+ FROM pg_catalog.pg_attribute a
313
+ JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
314
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
315
+ LEFT JOIN pg_catalog.pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
316
+ WHERE c.relname = %s
317
+ AND n.nspname = %s
318
+ AND a.attnum > 0
319
+ AND NOT a.attisdropped
320
+ ORDER BY a.attnum
321
+ """
322
+
323
+ result = await psycopg_driver.execute(sql, (table, schema_name))
324
+ return result.data or []
325
+
238
326
  def list_available_features(self) -> "list[str]":
239
327
  """List available PostgreSQL feature flags.
240
328