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.
- sqlspec/__init__.py +7 -15
- sqlspec/_serialization.py +55 -25
- sqlspec/_typing.py +155 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +880 -0
- sqlspec/adapters/adbc/config.py +62 -12
- sqlspec/adapters/adbc/data_dictionary.py +74 -2
- sqlspec/adapters/adbc/driver.py +226 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +44 -50
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +536 -0
- sqlspec/adapters/aiosqlite/config.py +86 -16
- sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
- sqlspec/adapters/aiosqlite/driver.py +127 -38
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +7 -7
- sqlspec/adapters/asyncmy/__init__.py +7 -1
- sqlspec/adapters/asyncmy/_types.py +1 -1
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +503 -0
- sqlspec/adapters/asyncmy/config.py +59 -17
- sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
- sqlspec/adapters/asyncmy/driver.py +293 -62
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +2 -1
- sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
- sqlspec/adapters/asyncpg/_types.py +11 -7
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +460 -0
- sqlspec/adapters/asyncpg/config.py +57 -36
- sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
- sqlspec/adapters/asyncpg/driver.py +153 -23
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/_types.py +1 -1
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +585 -0
- sqlspec/adapters/bigquery/config.py +36 -11
- sqlspec/adapters/bigquery/data_dictionary.py +42 -2
- sqlspec/adapters/bigquery/driver.py +489 -144
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +55 -23
- sqlspec/adapters/duckdb/_types.py +2 -2
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +563 -0
- sqlspec/adapters/duckdb/config.py +79 -21
- sqlspec/adapters/duckdb/data_dictionary.py +41 -2
- sqlspec/adapters/duckdb/driver.py +225 -44
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +5 -5
- sqlspec/adapters/duckdb/type_converter.py +51 -21
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +20 -2
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1628 -0
- sqlspec/adapters/oracledb/config.py +120 -36
- sqlspec/adapters/oracledb/data_dictionary.py +87 -20
- sqlspec/adapters/oracledb/driver.py +475 -86
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +765 -0
- sqlspec/adapters/oracledb/migrations.py +316 -25
- sqlspec/adapters/oracledb/type_converter.py +91 -16
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +2 -1
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +483 -0
- sqlspec/adapters/psqlpy/config.py +45 -19
- sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
- sqlspec/adapters/psqlpy/driver.py +108 -41
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +40 -11
- sqlspec/adapters/psycopg/_type_handlers.py +80 -0
- sqlspec/adapters/psycopg/_types.py +2 -1
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +962 -0
- sqlspec/adapters/psycopg/config.py +65 -37
- sqlspec/adapters/psycopg/data_dictionary.py +91 -3
- sqlspec/adapters/psycopg/driver.py +200 -78
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/sqlite/__init__.py +2 -1
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +1 -1
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +582 -0
- sqlspec/adapters/sqlite/config.py +85 -16
- sqlspec/adapters/sqlite/data_dictionary.py +34 -2
- sqlspec/adapters/sqlite/driver.py +120 -52
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +5 -5
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +91 -58
- sqlspec/builder/_column.py +5 -5
- sqlspec/builder/_ddl.py +98 -89
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +41 -44
- sqlspec/builder/_insert.py +5 -82
- sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +9 -11
- sqlspec/builder/_select.py +1313 -25
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +76 -69
- sqlspec/config.py +331 -62
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +18 -18
- sqlspec/core/compiler.py +6 -8
- sqlspec/core/filters.py +55 -47
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +76 -45
- sqlspec/core/result.py +234 -47
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +32 -31
- sqlspec/core/type_conversion.py +3 -2
- sqlspec/driver/__init__.py +1 -3
- sqlspec/driver/_async.py +183 -160
- sqlspec/driver/_common.py +197 -109
- sqlspec/driver/_sync.py +189 -161
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +70 -7
- sqlspec/extensions/adk/__init__.py +53 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +181 -0
- sqlspec/extensions/adk/store.py +536 -0
- sqlspec/extensions/aiosql/adapter.py +69 -61
- sqlspec/extensions/fastapi/__init__.py +21 -0
- sqlspec/extensions/fastapi/extension.py +331 -0
- sqlspec/extensions/fastapi/providers.py +543 -0
- sqlspec/extensions/flask/__init__.py +36 -0
- sqlspec/extensions/flask/_state.py +71 -0
- sqlspec/extensions/flask/_utils.py +40 -0
- sqlspec/extensions/flask/extension.py +389 -0
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +56 -266
- sqlspec/extensions/litestar/handlers.py +46 -17
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +349 -224
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/extensions/starlette/__init__.py +10 -0
- sqlspec/extensions/starlette/_state.py +25 -0
- sqlspec/extensions/starlette/_utils.py +52 -0
- sqlspec/extensions/starlette/extension.py +254 -0
- sqlspec/extensions/starlette/middleware.py +154 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/base.py +200 -76
- sqlspec/migrations/commands.py +591 -62
- sqlspec/migrations/context.py +6 -9
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +47 -19
- sqlspec/migrations/runner.py +241 -75
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +106 -36
- sqlspec/storage/_utils.py +85 -0
- sqlspec/storage/backends/fsspec.py +133 -107
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +276 -168
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +30 -84
- sqlspec/utils/__init__.py +25 -4
- sqlspec/utils/arrow_helpers.py +81 -0
- sqlspec/utils/config_resolver.py +6 -6
- sqlspec/utils/correlation.py +4 -5
- sqlspec/utils/data_transformation.py +3 -2
- sqlspec/utils/deprecation.py +9 -8
- sqlspec/utils/fixtures.py +4 -4
- sqlspec/utils/logging.py +46 -6
- sqlspec/utils/module_loader.py +205 -5
- sqlspec/utils/portal.py +311 -0
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +113 -4
- sqlspec/utils/sync_tools.py +36 -22
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +136 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
- sqlspec-0.28.0.dist-info/RECORD +221 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_merge_operations.py +0 -698
- sqlspec/builder/mixins/_order_limit_operations.py +0 -145
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -930
- sqlspec/builder/mixins/_update_operations.py +0 -199
- sqlspec/builder/mixins/_where_clause.py +0 -1298
- sqlspec-0.26.0.dist-info/RECORD +0 -157
- sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.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
|
|
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
|
|
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:
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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: "
|
|
204
|
-
driver_features: "
|
|
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:
|
|
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") -> "
|
|
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:
|
|
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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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: "
|
|
519
|
-
driver_features: "
|
|
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:
|
|
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") -> "
|
|
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:
|