sqlspec 0.25.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.
- sqlspec/__init__.py +7 -15
- sqlspec/_serialization.py +256 -24
- sqlspec/_typing.py +71 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +870 -0
- sqlspec/adapters/adbc/config.py +69 -12
- sqlspec/adapters/adbc/data_dictionary.py +340 -0
- sqlspec/adapters/adbc/driver.py +266 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +153 -0
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +527 -0
- sqlspec/adapters/aiosqlite/config.py +88 -15
- sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
- sqlspec/adapters/aiosqlite/driver.py +143 -40
- 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 +2 -2
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +68 -23
- sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
- sqlspec/adapters/asyncmy/driver.py +313 -58
- 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 +450 -0
- sqlspec/adapters/asyncpg/config.py +59 -35
- sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
- sqlspec/adapters/asyncpg/driver.py +170 -25
- 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 +576 -0
- sqlspec/adapters/bigquery/config.py +27 -10
- sqlspec/adapters/bigquery/data_dictionary.py +149 -0
- sqlspec/adapters/bigquery/driver.py +368 -142
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +125 -0
- sqlspec/adapters/duckdb/_types.py +1 -1
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +80 -20
- sqlspec/adapters/duckdb/data_dictionary.py +163 -0
- sqlspec/adapters/duckdb/driver.py +167 -45
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +4 -4
- sqlspec/adapters/duckdb/type_converter.py +133 -0
- 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 +1745 -0
- sqlspec/adapters/oracledb/config.py +122 -32
- sqlspec/adapters/oracledb/data_dictionary.py +509 -0
- sqlspec/adapters/oracledb/driver.py +353 -91
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +348 -73
- sqlspec/adapters/oracledb/type_converter.py +207 -0
- 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 +482 -0
- sqlspec/adapters/psqlpy/config.py +46 -17
- sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
- sqlspec/adapters/psqlpy/driver.py +123 -209
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +102 -0
- 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 +944 -0
- sqlspec/adapters/psycopg/config.py +69 -35
- sqlspec/adapters/psycopg/data_dictionary.py +331 -0
- sqlspec/adapters/psycopg/driver.py +238 -81
- 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 +572 -0
- sqlspec/adapters/sqlite/config.py +87 -15
- sqlspec/adapters/sqlite/data_dictionary.py +149 -0
- sqlspec/adapters/sqlite/driver.py +137 -54
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +18 -9
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +162 -89
- sqlspec/builder/_column.py +62 -29
- sqlspec/builder/_ddl.py +180 -121
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +53 -94
- sqlspec/builder/_insert.py +32 -131
- sqlspec/builder/_join.py +375 -0
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +111 -17
- sqlspec/builder/_select.py +1457 -24
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +307 -194
- sqlspec/config.py +252 -67
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +17 -17
- sqlspec/core/compiler.py +62 -9
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +83 -48
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +36 -30
- sqlspec/core/type_conversion.py +235 -0
- sqlspec/driver/__init__.py +7 -6
- sqlspec/driver/_async.py +188 -151
- sqlspec/driver/_common.py +285 -80
- sqlspec/driver/_sync.py +188 -152
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +75 -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 +73 -53
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +59 -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 +324 -223
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/__init__.py +4 -3
- sqlspec/migrations/base.py +302 -39
- sqlspec/migrations/commands.py +611 -144
- sqlspec/migrations/context.py +142 -0
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +68 -23
- sqlspec/migrations/runner.py +543 -107
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +66 -36
- sqlspec/storage/_utils.py +98 -0
- sqlspec/storage/backends/fsspec.py +134 -106
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +278 -162
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +16 -84
- sqlspec/utils/config_resolver.py +153 -0
- 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 +2 -2
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +50 -2
- sqlspec/utils/sync_tools.py +21 -17
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +111 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
- sqlspec-0.27.0.dist-info/RECORD +207 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -254
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_join_operations.py +0 -389
- sqlspec/builder/mixins/_merge_operations.py +0 -592
- sqlspec/builder/mixins/_order_limit_operations.py +0 -152
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -936
- sqlspec/builder/mixins/_update_operations.py +0 -218
- sqlspec/builder/mixins/_where_clause.py +0 -1304
- sqlspec-0.25.0.dist-info/RECORD +0 -139
- sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -14,8 +14,9 @@ PostgreSQL Features:
|
|
|
14
14
|
- PostgreSQL-specific error handling
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
import datetime
|
|
17
18
|
import io
|
|
18
|
-
from typing import TYPE_CHECKING, Any
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
19
20
|
|
|
20
21
|
import psycopg
|
|
21
22
|
|
|
@@ -25,14 +26,28 @@ from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
|
|
|
25
26
|
from sqlspec.core.result import SQLResult
|
|
26
27
|
from sqlspec.core.statement import SQL, StatementConfig
|
|
27
28
|
from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
|
|
28
|
-
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
|
+
)
|
|
29
42
|
from sqlspec.utils.logging import get_logger
|
|
30
43
|
from sqlspec.utils.serializers import to_json
|
|
31
44
|
|
|
32
45
|
if TYPE_CHECKING:
|
|
33
46
|
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
34
47
|
|
|
48
|
+
from sqlspec.driver._async import AsyncDataDictionaryBase
|
|
35
49
|
from sqlspec.driver._common import ExecutionResult
|
|
50
|
+
from sqlspec.driver._sync import SyncDataDictionaryBase
|
|
36
51
|
|
|
37
52
|
logger = get_logger("adapters.psycopg")
|
|
38
53
|
|
|
@@ -94,7 +109,12 @@ psycopg_statement_config = StatementConfig(
|
|
|
94
109
|
ParameterStyle.NAMED_PYFORMAT,
|
|
95
110
|
ParameterStyle.NUMERIC,
|
|
96
111
|
},
|
|
97
|
-
type_coercion_map={
|
|
112
|
+
type_coercion_map={
|
|
113
|
+
dict: to_json,
|
|
114
|
+
datetime.datetime: lambda x: x,
|
|
115
|
+
datetime.date: lambda x: x,
|
|
116
|
+
datetime.time: lambda x: x,
|
|
117
|
+
},
|
|
98
118
|
has_native_list_expansion=True,
|
|
99
119
|
needs_static_script_compilation=False,
|
|
100
120
|
preserve_parameter_format=True,
|
|
@@ -119,20 +139,23 @@ class PsycopgSyncCursor:
|
|
|
119
139
|
|
|
120
140
|
def __init__(self, connection: PsycopgSyncConnection) -> None:
|
|
121
141
|
self.connection = connection
|
|
122
|
-
self.cursor:
|
|
142
|
+
self.cursor: Any | None = None
|
|
123
143
|
|
|
124
144
|
def __enter__(self) -> Any:
|
|
125
145
|
self.cursor = self.connection.cursor()
|
|
126
146
|
return self.cursor
|
|
127
147
|
|
|
128
|
-
def __exit__(self,
|
|
129
|
-
_ = (exc_type, exc_val, exc_tb)
|
|
148
|
+
def __exit__(self, *_: Any) -> None:
|
|
130
149
|
if self.cursor is not None:
|
|
131
150
|
self.cursor.close()
|
|
132
151
|
|
|
133
152
|
|
|
134
153
|
class PsycopgSyncExceptionHandler:
|
|
135
|
-
"""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
|
+
"""
|
|
136
159
|
|
|
137
160
|
__slots__ = ()
|
|
138
161
|
|
|
@@ -142,39 +165,90 @@ class PsycopgSyncExceptionHandler:
|
|
|
142
165
|
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
143
166
|
if exc_type is None:
|
|
144
167
|
return
|
|
145
|
-
|
|
146
|
-
if issubclass(exc_type, psycopg.IntegrityError):
|
|
147
|
-
e = exc_val
|
|
148
|
-
msg = f"PostgreSQL psycopg integrity constraint violation: {e}"
|
|
149
|
-
raise SQLSpecError(msg) from e
|
|
150
|
-
if issubclass(exc_type, psycopg.ProgrammingError):
|
|
151
|
-
e = exc_val
|
|
152
|
-
error_msg = str(e).lower()
|
|
153
|
-
if "syntax" in error_msg or "parse" in error_msg:
|
|
154
|
-
msg = f"PostgreSQL psycopg SQL syntax error: {e}"
|
|
155
|
-
raise SQLParsingError(msg) from e
|
|
156
|
-
msg = f"PostgreSQL psycopg programming error: {e}"
|
|
157
|
-
raise SQLSpecError(msg) from e
|
|
158
|
-
if issubclass(exc_type, psycopg.OperationalError):
|
|
159
|
-
e = exc_val
|
|
160
|
-
msg = f"PostgreSQL psycopg operational error: {e}"
|
|
161
|
-
raise SQLSpecError(msg) from e
|
|
162
|
-
if issubclass(exc_type, psycopg.DatabaseError):
|
|
163
|
-
e = exc_val
|
|
164
|
-
msg = f"PostgreSQL psycopg database error: {e}"
|
|
165
|
-
raise SQLSpecError(msg) from e
|
|
166
168
|
if issubclass(exc_type, psycopg.Error):
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
178
252
|
|
|
179
253
|
|
|
180
254
|
class PsycopgSyncDriver(SyncDriverAdapterBase):
|
|
@@ -187,14 +261,14 @@ class PsycopgSyncDriver(SyncDriverAdapterBase):
|
|
|
187
261
|
bulk data transfer, and PostgreSQL-specific error handling.
|
|
188
262
|
"""
|
|
189
263
|
|
|
190
|
-
__slots__ = ()
|
|
264
|
+
__slots__ = ("_data_dictionary",)
|
|
191
265
|
dialect = "postgres"
|
|
192
266
|
|
|
193
267
|
def __init__(
|
|
194
268
|
self,
|
|
195
269
|
connection: PsycopgSyncConnection,
|
|
196
|
-
statement_config: "
|
|
197
|
-
driver_features: "
|
|
270
|
+
statement_config: "StatementConfig | None" = None,
|
|
271
|
+
driver_features: "dict[str, Any] | None" = None,
|
|
198
272
|
) -> None:
|
|
199
273
|
if statement_config is None:
|
|
200
274
|
cache_config = get_cache_config()
|
|
@@ -207,6 +281,7 @@ class PsycopgSyncDriver(SyncDriverAdapterBase):
|
|
|
207
281
|
statement_config = default_config
|
|
208
282
|
|
|
209
283
|
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
284
|
+
self._data_dictionary: SyncDataDictionaryBase | None = None
|
|
210
285
|
|
|
211
286
|
def with_cursor(self, connection: PsycopgSyncConnection) -> PsycopgSyncCursor:
|
|
212
287
|
"""Create context manager for PostgreSQL cursor."""
|
|
@@ -255,7 +330,7 @@ class PsycopgSyncDriver(SyncDriverAdapterBase):
|
|
|
255
330
|
except Exception as cleanup_error:
|
|
256
331
|
logger.warning("Failed to cleanup transaction state: %s", cleanup_error)
|
|
257
332
|
|
|
258
|
-
def _try_special_handling(self, cursor: Any, statement: "SQL") -> "
|
|
333
|
+
def _try_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
|
|
259
334
|
"""Hook for PostgreSQL-specific special operations.
|
|
260
335
|
|
|
261
336
|
Args:
|
|
@@ -411,6 +486,19 @@ class PsycopgSyncDriver(SyncDriverAdapterBase):
|
|
|
411
486
|
affected_rows = cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
412
487
|
return self.create_execution_result(cursor, rowcount_override=affected_rows)
|
|
413
488
|
|
|
489
|
+
@property
|
|
490
|
+
def data_dictionary(self) -> "SyncDataDictionaryBase":
|
|
491
|
+
"""Get the data dictionary for this driver.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Data dictionary instance for metadata queries
|
|
495
|
+
"""
|
|
496
|
+
if self._data_dictionary is None:
|
|
497
|
+
from sqlspec.adapters.psycopg.data_dictionary import PostgresSyncDataDictionary
|
|
498
|
+
|
|
499
|
+
self._data_dictionary = PostgresSyncDataDictionary()
|
|
500
|
+
return self._data_dictionary
|
|
501
|
+
|
|
414
502
|
|
|
415
503
|
class PsycopgAsyncCursor:
|
|
416
504
|
"""Async context manager for PostgreSQL psycopg cursor management."""
|
|
@@ -419,7 +507,7 @@ class PsycopgAsyncCursor:
|
|
|
419
507
|
|
|
420
508
|
def __init__(self, connection: "PsycopgAsyncConnection") -> None:
|
|
421
509
|
self.connection = connection
|
|
422
|
-
self.cursor:
|
|
510
|
+
self.cursor: Any | None = None
|
|
423
511
|
|
|
424
512
|
async def __aenter__(self) -> Any:
|
|
425
513
|
self.cursor = self.connection.cursor()
|
|
@@ -432,7 +520,11 @@ class PsycopgAsyncCursor:
|
|
|
432
520
|
|
|
433
521
|
|
|
434
522
|
class PsycopgAsyncExceptionHandler:
|
|
435
|
-
"""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
|
+
"""
|
|
436
528
|
|
|
437
529
|
__slots__ = ()
|
|
438
530
|
|
|
@@ -442,39 +534,90 @@ class PsycopgAsyncExceptionHandler:
|
|
|
442
534
|
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
443
535
|
if exc_type is None:
|
|
444
536
|
return
|
|
445
|
-
|
|
446
|
-
if issubclass(exc_type, psycopg.IntegrityError):
|
|
447
|
-
e = exc_val
|
|
448
|
-
msg = f"PostgreSQL psycopg integrity constraint violation: {e}"
|
|
449
|
-
raise SQLSpecError(msg) from e
|
|
450
|
-
if issubclass(exc_type, psycopg.ProgrammingError):
|
|
451
|
-
e = exc_val
|
|
452
|
-
error_msg = str(e).lower()
|
|
453
|
-
if "syntax" in error_msg or "parse" in error_msg:
|
|
454
|
-
msg = f"PostgreSQL psycopg SQL syntax error: {e}"
|
|
455
|
-
raise SQLParsingError(msg) from e
|
|
456
|
-
msg = f"PostgreSQL psycopg programming error: {e}"
|
|
457
|
-
raise SQLSpecError(msg) from e
|
|
458
|
-
if issubclass(exc_type, psycopg.OperationalError):
|
|
459
|
-
e = exc_val
|
|
460
|
-
msg = f"PostgreSQL psycopg operational error: {e}"
|
|
461
|
-
raise SQLSpecError(msg) from e
|
|
462
|
-
if issubclass(exc_type, psycopg.DatabaseError):
|
|
463
|
-
e = exc_val
|
|
464
|
-
msg = f"PostgreSQL psycopg database error: {e}"
|
|
465
|
-
raise SQLSpecError(msg) from e
|
|
466
537
|
if issubclass(exc_type, psycopg.Error):
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
|
478
621
|
|
|
479
622
|
|
|
480
623
|
class PsycopgAsyncDriver(AsyncDriverAdapterBase):
|
|
@@ -488,14 +631,14 @@ class PsycopgAsyncDriver(AsyncDriverAdapterBase):
|
|
|
488
631
|
and async pub/sub support.
|
|
489
632
|
"""
|
|
490
633
|
|
|
491
|
-
__slots__ = ()
|
|
634
|
+
__slots__ = ("_data_dictionary",)
|
|
492
635
|
dialect = "postgres"
|
|
493
636
|
|
|
494
637
|
def __init__(
|
|
495
638
|
self,
|
|
496
639
|
connection: "PsycopgAsyncConnection",
|
|
497
|
-
statement_config: "
|
|
498
|
-
driver_features: "
|
|
640
|
+
statement_config: "StatementConfig | None" = None,
|
|
641
|
+
driver_features: "dict[str, Any] | None" = None,
|
|
499
642
|
) -> None:
|
|
500
643
|
if statement_config is None:
|
|
501
644
|
cache_config = get_cache_config()
|
|
@@ -508,6 +651,7 @@ class PsycopgAsyncDriver(AsyncDriverAdapterBase):
|
|
|
508
651
|
statement_config = default_config
|
|
509
652
|
|
|
510
653
|
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
654
|
+
self._data_dictionary: AsyncDataDictionaryBase | None = None
|
|
511
655
|
|
|
512
656
|
def with_cursor(self, connection: "PsycopgAsyncConnection") -> "PsycopgAsyncCursor":
|
|
513
657
|
"""Create async context manager for PostgreSQL cursor."""
|
|
@@ -556,7 +700,7 @@ class PsycopgAsyncDriver(AsyncDriverAdapterBase):
|
|
|
556
700
|
except Exception as cleanup_error:
|
|
557
701
|
logger.warning("Failed to cleanup transaction state: %s", cleanup_error)
|
|
558
702
|
|
|
559
|
-
async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "
|
|
703
|
+
async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
|
|
560
704
|
"""Hook for PostgreSQL-specific special operations.
|
|
561
705
|
|
|
562
706
|
Args:
|
|
@@ -714,3 +858,16 @@ class PsycopgAsyncDriver(AsyncDriverAdapterBase):
|
|
|
714
858
|
|
|
715
859
|
affected_rows = cursor.rowcount if cursor.rowcount and cursor.rowcount > 0 else 0
|
|
716
860
|
return self.create_execution_result(cursor, rowcount_override=affected_rows)
|
|
861
|
+
|
|
862
|
+
@property
|
|
863
|
+
def data_dictionary(self) -> "AsyncDataDictionaryBase":
|
|
864
|
+
"""Get the data dictionary for this driver.
|
|
865
|
+
|
|
866
|
+
Returns:
|
|
867
|
+
Data dictionary instance for metadata queries
|
|
868
|
+
"""
|
|
869
|
+
if self._data_dictionary is None:
|
|
870
|
+
from sqlspec.adapters.psycopg.data_dictionary import PostgresAsyncDataDictionary
|
|
871
|
+
|
|
872
|
+
self._data_dictionary = PostgresAsyncDataDictionary()
|
|
873
|
+
return self._data_dictionary
|