sqlspec 0.26.0__py3-none-any.whl → 0.27.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 (197) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +55 -25
  3. sqlspec/_typing.py +62 -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 +870 -0
  7. sqlspec/adapters/adbc/config.py +62 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +52 -2
  9. sqlspec/adapters/adbc/driver.py +144 -45
  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 +527 -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 +493 -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 +450 -0
  36. sqlspec/adapters/asyncpg/config.py +57 -36
  37. sqlspec/adapters/asyncpg/data_dictionary.py +41 -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 +576 -0
  44. sqlspec/adapters/bigquery/config.py +25 -11
  45. sqlspec/adapters/bigquery/data_dictionary.py +42 -2
  46. sqlspec/adapters/bigquery/driver.py +352 -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 +553 -0
  53. sqlspec/adapters/duckdb/config.py +79 -21
  54. sqlspec/adapters/duckdb/data_dictionary.py +41 -2
  55. sqlspec/adapters/duckdb/driver.py +138 -43
  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 +1745 -0
  64. sqlspec/adapters/oracledb/config.py +120 -36
  65. sqlspec/adapters/oracledb/data_dictionary.py +87 -20
  66. sqlspec/adapters/oracledb/driver.py +292 -84
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +767 -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 +482 -0
  75. sqlspec/adapters/psqlpy/config.py +45 -19
  76. sqlspec/adapters/psqlpy/data_dictionary.py +41 -2
  77. sqlspec/adapters/psqlpy/driver.py +101 -31
  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 +944 -0
  85. sqlspec/adapters/psycopg/config.py +65 -37
  86. sqlspec/adapters/psycopg/data_dictionary.py +77 -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 +572 -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 +231 -60
  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 +37 -37
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +76 -45
  123. sqlspec/core/result.py +102 -46
  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 +95 -161
  129. sqlspec/driver/_common.py +133 -80
  130. sqlspec/driver/_sync.py +95 -162
  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 +73 -53
  142. sqlspec/extensions/litestar/__init__.py +21 -4
  143. sqlspec/extensions/litestar/cli.py +54 -10
  144. sqlspec/extensions/litestar/config.py +59 -266
  145. sqlspec/extensions/litestar/handlers.py +46 -17
  146. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  147. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  148. sqlspec/extensions/litestar/plugin.py +324 -223
  149. sqlspec/extensions/litestar/providers.py +25 -25
  150. sqlspec/extensions/litestar/store.py +265 -0
  151. sqlspec/loader.py +30 -49
  152. sqlspec/migrations/base.py +200 -76
  153. sqlspec/migrations/commands.py +591 -62
  154. sqlspec/migrations/context.py +6 -9
  155. sqlspec/migrations/fix.py +199 -0
  156. sqlspec/migrations/loaders.py +47 -19
  157. sqlspec/migrations/runner.py +241 -75
  158. sqlspec/migrations/tracker.py +237 -21
  159. sqlspec/migrations/utils.py +51 -3
  160. sqlspec/migrations/validation.py +177 -0
  161. sqlspec/protocols.py +66 -36
  162. sqlspec/storage/_utils.py +98 -0
  163. sqlspec/storage/backends/fsspec.py +134 -106
  164. sqlspec/storage/backends/local.py +78 -51
  165. sqlspec/storage/backends/obstore.py +278 -162
  166. sqlspec/storage/registry.py +75 -39
  167. sqlspec/typing.py +14 -84
  168. sqlspec/utils/config_resolver.py +6 -6
  169. sqlspec/utils/correlation.py +4 -5
  170. sqlspec/utils/data_transformation.py +3 -2
  171. sqlspec/utils/deprecation.py +9 -8
  172. sqlspec/utils/fixtures.py +4 -4
  173. sqlspec/utils/logging.py +46 -6
  174. sqlspec/utils/module_loader.py +2 -2
  175. sqlspec/utils/schema.py +288 -0
  176. sqlspec/utils/serializers.py +3 -3
  177. sqlspec/utils/sync_tools.py +21 -17
  178. sqlspec/utils/text.py +1 -2
  179. sqlspec/utils/type_guards.py +111 -20
  180. sqlspec/utils/version.py +433 -0
  181. {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
  182. sqlspec-0.27.0.dist-info/RECORD +207 -0
  183. sqlspec/builder/mixins/__init__.py +0 -55
  184. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
  185. sqlspec/builder/mixins/_delete_operations.py +0 -50
  186. sqlspec/builder/mixins/_insert_operations.py +0 -282
  187. sqlspec/builder/mixins/_merge_operations.py +0 -698
  188. sqlspec/builder/mixins/_order_limit_operations.py +0 -145
  189. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  190. sqlspec/builder/mixins/_select_operations.py +0 -930
  191. sqlspec/builder/mixins/_update_operations.py +0 -199
  192. sqlspec/builder/mixins/_where_clause.py +0 -1298
  193. sqlspec-0.26.0.dist-info/RECORD +0 -157
  194. sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
  195. {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
  196. {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
  197. {sqlspec-0.26.0.dist-info → sqlspec-0.27.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,43 @@ 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 information_schema.
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
+ psycopg_driver = cast("PsycopgSyncDriver", driver)
140
+
141
+ if schema:
142
+ sql = f"""
143
+ SELECT column_name, data_type, is_nullable, column_default
144
+ FROM information_schema.columns
145
+ WHERE table_name = '{table}' AND table_schema = '{schema}'
146
+ ORDER BY ordinal_position
147
+ """
148
+ else:
149
+ sql = f"""
150
+ SELECT column_name, data_type, is_nullable, column_default
151
+ FROM information_schema.columns
152
+ WHERE table_name = '{table}' AND table_schema = 'public'
153
+ ORDER BY ordinal_position
154
+ """
155
+
156
+ result = psycopg_driver.execute(sql)
157
+ return result.data or []
158
+
122
159
  def list_available_features(self) -> "list[str]":
123
160
  """List available PostgreSQL feature flags.
124
161
 
@@ -144,7 +181,7 @@ class PostgresSyncDataDictionary(SyncDataDictionaryBase):
144
181
  class PostgresAsyncDataDictionary(AsyncDataDictionaryBase):
145
182
  """PostgreSQL-specific async data dictionary."""
146
183
 
147
- async def get_version(self, driver: AsyncDriverAdapterBase) -> "Optional[VersionInfo]":
184
+ async def get_version(self, driver: AsyncDriverAdapterBase) -> "VersionInfo | None":
148
185
  """Get PostgreSQL database version information.
149
186
 
150
187
  Args:
@@ -235,6 +272,43 @@ class PostgresAsyncDataDictionary(AsyncDataDictionaryBase):
235
272
  }
236
273
  return type_map.get(type_category, "TEXT")
237
274
 
275
+ async def get_columns(
276
+ self, driver: AsyncDriverAdapterBase, table: str, schema: "str | None" = None
277
+ ) -> "list[dict[str, Any]]":
278
+ """Get column information for a table using information_schema.
279
+
280
+ Args:
281
+ driver: Psycopg async driver instance
282
+ table: Table name to query columns for
283
+ schema: Schema name (None for default 'public')
284
+
285
+ Returns:
286
+ List of column metadata dictionaries with keys:
287
+ - column_name: Name of the column
288
+ - data_type: PostgreSQL data type
289
+ - is_nullable: Whether column allows NULL (YES/NO)
290
+ - column_default: Default value if any
291
+ """
292
+ psycopg_driver = cast("PsycopgAsyncDriver", driver)
293
+
294
+ if schema:
295
+ sql = f"""
296
+ SELECT column_name, data_type, is_nullable, column_default
297
+ FROM information_schema.columns
298
+ WHERE table_name = '{table}' AND table_schema = '{schema}'
299
+ ORDER BY ordinal_position
300
+ """
301
+ else:
302
+ sql = f"""
303
+ SELECT column_name, data_type, is_nullable, column_default
304
+ FROM information_schema.columns
305
+ WHERE table_name = '{table}' AND table_schema = 'public'
306
+ ORDER BY ordinal_position
307
+ """
308
+
309
+ result = await psycopg_driver.execute(sql)
310
+ return result.data or []
311
+
238
312
  def list_available_features(self) -> "list[str]":
239
313
  """List available PostgreSQL feature flags.
240
314