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
@@ -16,7 +16,7 @@ PostgreSQL Features:
16
16
 
17
17
  import datetime
18
18
  import io
19
- from typing import TYPE_CHECKING, Any, Optional
19
+ from typing import TYPE_CHECKING, Any
20
20
 
21
21
  import psycopg
22
22
 
@@ -26,7 +26,19 @@ from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
26
26
  from sqlspec.core.result import SQLResult
27
27
  from sqlspec.core.statement import SQL, StatementConfig
28
28
  from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
29
- from sqlspec.exceptions import SQLParsingError, SQLSpecError
29
+ from sqlspec.exceptions import (
30
+ CheckViolationError,
31
+ DatabaseConnectionError,
32
+ DataError,
33
+ ForeignKeyViolationError,
34
+ IntegrityError,
35
+ NotNullViolationError,
36
+ OperationalError,
37
+ SQLParsingError,
38
+ SQLSpecError,
39
+ TransactionError,
40
+ UniqueViolationError,
41
+ )
30
42
  from sqlspec.utils.logging import get_logger
31
43
  from sqlspec.utils.serializers import to_json
32
44
 
@@ -127,7 +139,7 @@ class PsycopgSyncCursor:
127
139
 
128
140
  def __init__(self, connection: PsycopgSyncConnection) -> None:
129
141
  self.connection = connection
130
- self.cursor: Optional[Any] = None
142
+ self.cursor: Any | None = None
131
143
 
132
144
  def __enter__(self) -> Any:
133
145
  self.cursor = self.connection.cursor()
@@ -139,7 +151,11 @@ class PsycopgSyncCursor:
139
151
 
140
152
 
141
153
  class PsycopgSyncExceptionHandler:
142
- """Context manager for handling PostgreSQL psycopg database exceptions."""
154
+ """Context manager for handling PostgreSQL psycopg database exceptions.
155
+
156
+ Maps PostgreSQL SQLSTATE error codes to specific SQLSpec exceptions
157
+ for better error handling in application code.
158
+ """
143
159
 
144
160
  __slots__ = ()
145
161
 
@@ -149,39 +165,90 @@ class PsycopgSyncExceptionHandler:
149
165
  def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
150
166
  if exc_type is None:
151
167
  return
152
-
153
- if issubclass(exc_type, psycopg.IntegrityError):
154
- e = exc_val
155
- msg = f"PostgreSQL psycopg integrity constraint violation: {e}"
156
- raise SQLSpecError(msg) from e
157
- if issubclass(exc_type, psycopg.ProgrammingError):
158
- e = exc_val
159
- error_msg = str(e).lower()
160
- if "syntax" in error_msg or "parse" in error_msg:
161
- msg = f"PostgreSQL psycopg SQL syntax error: {e}"
162
- raise SQLParsingError(msg) from e
163
- msg = f"PostgreSQL psycopg programming error: {e}"
164
- raise SQLSpecError(msg) from e
165
- if issubclass(exc_type, psycopg.OperationalError):
166
- e = exc_val
167
- msg = f"PostgreSQL psycopg operational error: {e}"
168
- raise SQLSpecError(msg) from e
169
- if issubclass(exc_type, psycopg.DatabaseError):
170
- e = exc_val
171
- msg = f"PostgreSQL psycopg database error: {e}"
172
- raise SQLSpecError(msg) from e
173
168
  if issubclass(exc_type, psycopg.Error):
174
- e = exc_val
175
- msg = f"PostgreSQL psycopg error: {e}"
176
- raise SQLSpecError(msg) from e
177
- if issubclass(exc_type, Exception):
178
- e = exc_val
179
- error_msg = str(e).lower()
180
- if "parse" in error_msg or "syntax" in error_msg:
181
- msg = f"SQL parsing failed: {e}"
182
- raise SQLParsingError(msg) from e
183
- msg = f"Unexpected database operation error: {e}"
184
- raise SQLSpecError(msg) from e
169
+ self._map_postgres_exception(exc_val)
170
+
171
+ def _map_postgres_exception(self, e: Any) -> None:
172
+ """Map PostgreSQL exception to SQLSpec exception.
173
+
174
+ Args:
175
+ e: psycopg.Error instance
176
+
177
+ Raises:
178
+ Specific SQLSpec exception based on SQLSTATE code
179
+ """
180
+ error_code = getattr(e, "sqlstate", None)
181
+
182
+ if not error_code:
183
+ self._raise_generic_error(e, None)
184
+ return
185
+
186
+ if error_code == "23505":
187
+ self._raise_unique_violation(e, error_code)
188
+ elif error_code == "23503":
189
+ self._raise_foreign_key_violation(e, error_code)
190
+ elif error_code == "23502":
191
+ self._raise_not_null_violation(e, error_code)
192
+ elif error_code == "23514":
193
+ self._raise_check_violation(e, error_code)
194
+ elif error_code.startswith("23"):
195
+ self._raise_integrity_error(e, error_code)
196
+ elif error_code.startswith("42"):
197
+ self._raise_parsing_error(e, error_code)
198
+ elif error_code.startswith("08"):
199
+ self._raise_connection_error(e, error_code)
200
+ elif error_code.startswith("40"):
201
+ self._raise_transaction_error(e, error_code)
202
+ elif error_code.startswith("22"):
203
+ self._raise_data_error(e, error_code)
204
+ elif error_code.startswith(("53", "54", "55", "57", "58")):
205
+ self._raise_operational_error(e, error_code)
206
+ else:
207
+ self._raise_generic_error(e, error_code)
208
+
209
+ def _raise_unique_violation(self, e: Any, code: str) -> None:
210
+ msg = f"PostgreSQL unique constraint violation [{code}]: {e}"
211
+ raise UniqueViolationError(msg) from e
212
+
213
+ def _raise_foreign_key_violation(self, e: Any, code: str) -> None:
214
+ msg = f"PostgreSQL foreign key constraint violation [{code}]: {e}"
215
+ raise ForeignKeyViolationError(msg) from e
216
+
217
+ def _raise_not_null_violation(self, e: Any, code: str) -> None:
218
+ msg = f"PostgreSQL not-null constraint violation [{code}]: {e}"
219
+ raise NotNullViolationError(msg) from e
220
+
221
+ def _raise_check_violation(self, e: Any, code: str) -> None:
222
+ msg = f"PostgreSQL check constraint violation [{code}]: {e}"
223
+ raise CheckViolationError(msg) from e
224
+
225
+ def _raise_integrity_error(self, e: Any, code: str) -> None:
226
+ msg = f"PostgreSQL integrity constraint violation [{code}]: {e}"
227
+ raise IntegrityError(msg) from e
228
+
229
+ def _raise_parsing_error(self, e: Any, code: str) -> None:
230
+ msg = f"PostgreSQL SQL syntax error [{code}]: {e}"
231
+ raise SQLParsingError(msg) from e
232
+
233
+ def _raise_connection_error(self, e: Any, code: str) -> None:
234
+ msg = f"PostgreSQL connection error [{code}]: {e}"
235
+ raise DatabaseConnectionError(msg) from e
236
+
237
+ def _raise_transaction_error(self, e: Any, code: str) -> None:
238
+ msg = f"PostgreSQL transaction error [{code}]: {e}"
239
+ raise TransactionError(msg) from e
240
+
241
+ def _raise_data_error(self, e: Any, code: str) -> None:
242
+ msg = f"PostgreSQL data error [{code}]: {e}"
243
+ raise DataError(msg) from e
244
+
245
+ def _raise_operational_error(self, e: Any, code: str) -> None:
246
+ msg = f"PostgreSQL operational error [{code}]: {e}"
247
+ raise OperationalError(msg) from e
248
+
249
+ def _raise_generic_error(self, e: Any, code: "str | None") -> None:
250
+ msg = f"PostgreSQL database error [{code}]: {e}" if code else f"PostgreSQL database error: {e}"
251
+ raise SQLSpecError(msg) from e
185
252
 
186
253
 
187
254
  class PsycopgSyncDriver(SyncDriverAdapterBase):
@@ -200,8 +267,8 @@ class PsycopgSyncDriver(SyncDriverAdapterBase):
200
267
  def __init__(
201
268
  self,
202
269
  connection: PsycopgSyncConnection,
203
- statement_config: "Optional[StatementConfig]" = None,
204
- driver_features: "Optional[dict[str, Any]]" = None,
270
+ statement_config: "StatementConfig | None" = None,
271
+ driver_features: "dict[str, Any] | None" = None,
205
272
  ) -> None:
206
273
  if statement_config is None:
207
274
  cache_config = get_cache_config()
@@ -214,7 +281,7 @@ class PsycopgSyncDriver(SyncDriverAdapterBase):
214
281
  statement_config = default_config
215
282
 
216
283
  super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
217
- self._data_dictionary: Optional[SyncDataDictionaryBase] = None
284
+ self._data_dictionary: SyncDataDictionaryBase | None = None
218
285
 
219
286
  def with_cursor(self, connection: PsycopgSyncConnection) -> PsycopgSyncCursor:
220
287
  """Create context manager for PostgreSQL cursor."""
@@ -263,7 +330,7 @@ class PsycopgSyncDriver(SyncDriverAdapterBase):
263
330
  except Exception as cleanup_error:
264
331
  logger.warning("Failed to cleanup transaction state: %s", cleanup_error)
265
332
 
266
- def _try_special_handling(self, cursor: Any, statement: "SQL") -> "Optional[SQLResult]":
333
+ def _try_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
267
334
  """Hook for PostgreSQL-specific special operations.
268
335
 
269
336
  Args:
@@ -440,7 +507,7 @@ class PsycopgAsyncCursor:
440
507
 
441
508
  def __init__(self, connection: "PsycopgAsyncConnection") -> None:
442
509
  self.connection = connection
443
- self.cursor: Optional[Any] = None
510
+ self.cursor: Any | None = None
444
511
 
445
512
  async def __aenter__(self) -> Any:
446
513
  self.cursor = self.connection.cursor()
@@ -453,7 +520,11 @@ class PsycopgAsyncCursor:
453
520
 
454
521
 
455
522
  class PsycopgAsyncExceptionHandler:
456
- """Async context manager for handling PostgreSQL psycopg database exceptions."""
523
+ """Async context manager for handling PostgreSQL psycopg database exceptions.
524
+
525
+ Maps PostgreSQL SQLSTATE error codes to specific SQLSpec exceptions
526
+ for better error handling in application code.
527
+ """
457
528
 
458
529
  __slots__ = ()
459
530
 
@@ -463,39 +534,90 @@ class PsycopgAsyncExceptionHandler:
463
534
  async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
464
535
  if exc_type is None:
465
536
  return
466
-
467
- if issubclass(exc_type, psycopg.IntegrityError):
468
- e = exc_val
469
- msg = f"PostgreSQL psycopg integrity constraint violation: {e}"
470
- raise SQLSpecError(msg) from e
471
- if issubclass(exc_type, psycopg.ProgrammingError):
472
- e = exc_val
473
- error_msg = str(e).lower()
474
- if "syntax" in error_msg or "parse" in error_msg:
475
- msg = f"PostgreSQL psycopg SQL syntax error: {e}"
476
- raise SQLParsingError(msg) from e
477
- msg = f"PostgreSQL psycopg programming error: {e}"
478
- raise SQLSpecError(msg) from e
479
- if issubclass(exc_type, psycopg.OperationalError):
480
- e = exc_val
481
- msg = f"PostgreSQL psycopg operational error: {e}"
482
- raise SQLSpecError(msg) from e
483
- if issubclass(exc_type, psycopg.DatabaseError):
484
- e = exc_val
485
- msg = f"PostgreSQL psycopg database error: {e}"
486
- raise SQLSpecError(msg) from e
487
537
  if issubclass(exc_type, psycopg.Error):
488
- e = exc_val
489
- msg = f"PostgreSQL psycopg error: {e}"
490
- raise SQLSpecError(msg) from e
491
- if issubclass(exc_type, Exception):
492
- e = exc_val
493
- error_msg = str(e).lower()
494
- if "parse" in error_msg or "syntax" in error_msg:
495
- msg = f"SQL parsing failed: {e}"
496
- raise SQLParsingError(msg) from e
497
- msg = f"Unexpected async database operation error: {e}"
498
- raise SQLSpecError(msg) from e
538
+ self._map_postgres_exception(exc_val)
539
+
540
+ def _map_postgres_exception(self, e: Any) -> None:
541
+ """Map PostgreSQL exception to SQLSpec exception.
542
+
543
+ Args:
544
+ e: psycopg.Error instance
545
+
546
+ Raises:
547
+ Specific SQLSpec exception based on SQLSTATE code
548
+ """
549
+ error_code = getattr(e, "sqlstate", None)
550
+
551
+ if not error_code:
552
+ self._raise_generic_error(e, None)
553
+ return
554
+
555
+ if error_code == "23505":
556
+ self._raise_unique_violation(e, error_code)
557
+ elif error_code == "23503":
558
+ self._raise_foreign_key_violation(e, error_code)
559
+ elif error_code == "23502":
560
+ self._raise_not_null_violation(e, error_code)
561
+ elif error_code == "23514":
562
+ self._raise_check_violation(e, error_code)
563
+ elif error_code.startswith("23"):
564
+ self._raise_integrity_error(e, error_code)
565
+ elif error_code.startswith("42"):
566
+ self._raise_parsing_error(e, error_code)
567
+ elif error_code.startswith("08"):
568
+ self._raise_connection_error(e, error_code)
569
+ elif error_code.startswith("40"):
570
+ self._raise_transaction_error(e, error_code)
571
+ elif error_code.startswith("22"):
572
+ self._raise_data_error(e, error_code)
573
+ elif error_code.startswith(("53", "54", "55", "57", "58")):
574
+ self._raise_operational_error(e, error_code)
575
+ else:
576
+ self._raise_generic_error(e, error_code)
577
+
578
+ def _raise_unique_violation(self, e: Any, code: str) -> None:
579
+ msg = f"PostgreSQL unique constraint violation [{code}]: {e}"
580
+ raise UniqueViolationError(msg) from e
581
+
582
+ def _raise_foreign_key_violation(self, e: Any, code: str) -> None:
583
+ msg = f"PostgreSQL foreign key constraint violation [{code}]: {e}"
584
+ raise ForeignKeyViolationError(msg) from e
585
+
586
+ def _raise_not_null_violation(self, e: Any, code: str) -> None:
587
+ msg = f"PostgreSQL not-null constraint violation [{code}]: {e}"
588
+ raise NotNullViolationError(msg) from e
589
+
590
+ def _raise_check_violation(self, e: Any, code: str) -> None:
591
+ msg = f"PostgreSQL check constraint violation [{code}]: {e}"
592
+ raise CheckViolationError(msg) from e
593
+
594
+ def _raise_integrity_error(self, e: Any, code: str) -> None:
595
+ msg = f"PostgreSQL integrity constraint violation [{code}]: {e}"
596
+ raise IntegrityError(msg) from e
597
+
598
+ def _raise_parsing_error(self, e: Any, code: str) -> None:
599
+ msg = f"PostgreSQL SQL syntax error [{code}]: {e}"
600
+ raise SQLParsingError(msg) from e
601
+
602
+ def _raise_connection_error(self, e: Any, code: str) -> None:
603
+ msg = f"PostgreSQL connection error [{code}]: {e}"
604
+ raise DatabaseConnectionError(msg) from e
605
+
606
+ def _raise_transaction_error(self, e: Any, code: str) -> None:
607
+ msg = f"PostgreSQL transaction error [{code}]: {e}"
608
+ raise TransactionError(msg) from e
609
+
610
+ def _raise_data_error(self, e: Any, code: str) -> None:
611
+ msg = f"PostgreSQL data error [{code}]: {e}"
612
+ raise DataError(msg) from e
613
+
614
+ def _raise_operational_error(self, e: Any, code: str) -> None:
615
+ msg = f"PostgreSQL operational error [{code}]: {e}"
616
+ raise OperationalError(msg) from e
617
+
618
+ def _raise_generic_error(self, e: Any, code: "str | None") -> None:
619
+ msg = f"PostgreSQL database error [{code}]: {e}" if code else f"PostgreSQL database error: {e}"
620
+ raise SQLSpecError(msg) from e
499
621
 
500
622
 
501
623
  class PsycopgAsyncDriver(AsyncDriverAdapterBase):
@@ -515,8 +637,8 @@ class PsycopgAsyncDriver(AsyncDriverAdapterBase):
515
637
  def __init__(
516
638
  self,
517
639
  connection: "PsycopgAsyncConnection",
518
- statement_config: "Optional[StatementConfig]" = None,
519
- driver_features: "Optional[dict[str, Any]]" = None,
640
+ statement_config: "StatementConfig | None" = None,
641
+ driver_features: "dict[str, Any] | None" = None,
520
642
  ) -> None:
521
643
  if statement_config is None:
522
644
  cache_config = get_cache_config()
@@ -529,7 +651,7 @@ class PsycopgAsyncDriver(AsyncDriverAdapterBase):
529
651
  statement_config = default_config
530
652
 
531
653
  super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
532
- self._data_dictionary: Optional[AsyncDataDictionaryBase] = None
654
+ self._data_dictionary: AsyncDataDictionaryBase | None = None
533
655
 
534
656
  def with_cursor(self, connection: "PsycopgAsyncConnection") -> "PsycopgAsyncCursor":
535
657
  """Create async context manager for PostgreSQL cursor."""
@@ -578,7 +700,7 @@ class PsycopgAsyncDriver(AsyncDriverAdapterBase):
578
700
  except Exception as cleanup_error:
579
701
  logger.warning("Failed to cleanup transaction state: %s", cleanup_error)
580
702
 
581
- async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "Optional[SQLResult]":
703
+ async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
582
704
  """Hook for PostgreSQL-specific special operations.
583
705
 
584
706
  Args:
@@ -0,0 +1,5 @@
1
+ """Litestar integration for Psycopg adapter."""
2
+
3
+ from sqlspec.adapters.psycopg.litestar.store import PsycopgAsyncStore, PsycopgSyncStore
4
+
5
+ __all__ = ("PsycopgAsyncStore", "PsycopgSyncStore")