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
sqlspec/adapters/adbc/driver.py
CHANGED
|
@@ -7,18 +7,31 @@ database dialects, parameter style conversion, and transaction management.
|
|
|
7
7
|
import contextlib
|
|
8
8
|
import datetime
|
|
9
9
|
import decimal
|
|
10
|
-
from typing import TYPE_CHECKING, Any,
|
|
10
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
11
11
|
|
|
12
|
-
from adbc_driver_manager.dbapi import DatabaseError, IntegrityError, OperationalError, ProgrammingError
|
|
13
12
|
from sqlglot import exp
|
|
14
13
|
|
|
14
|
+
from sqlspec.adapters.adbc.data_dictionary import AdbcDataDictionary
|
|
15
|
+
from sqlspec.adapters.adbc.type_converter import ADBCTypeConverter
|
|
15
16
|
from sqlspec.core.cache import get_cache_config
|
|
16
17
|
from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig
|
|
17
18
|
from sqlspec.core.statement import SQL, StatementConfig
|
|
18
19
|
from sqlspec.driver import SyncDriverAdapterBase
|
|
19
|
-
from sqlspec.exceptions import
|
|
20
|
+
from sqlspec.exceptions import (
|
|
21
|
+
CheckViolationError,
|
|
22
|
+
DatabaseConnectionError,
|
|
23
|
+
DataError,
|
|
24
|
+
ForeignKeyViolationError,
|
|
25
|
+
IntegrityError,
|
|
26
|
+
MissingDependencyError,
|
|
27
|
+
NotNullViolationError,
|
|
28
|
+
SQLParsingError,
|
|
29
|
+
SQLSpecError,
|
|
30
|
+
TransactionError,
|
|
31
|
+
UniqueViolationError,
|
|
32
|
+
)
|
|
33
|
+
from sqlspec.typing import Empty
|
|
20
34
|
from sqlspec.utils.logging import get_logger
|
|
21
|
-
from sqlspec.utils.serializers import to_json
|
|
22
35
|
|
|
23
36
|
if TYPE_CHECKING:
|
|
24
37
|
from contextlib import AbstractContextManager
|
|
@@ -28,6 +41,7 @@ if TYPE_CHECKING:
|
|
|
28
41
|
from sqlspec.adapters.adbc._types import AdbcConnection
|
|
29
42
|
from sqlspec.core.result import SQLResult
|
|
30
43
|
from sqlspec.driver import ExecutionResult
|
|
44
|
+
from sqlspec.driver._sync import SyncDataDictionaryBase
|
|
31
45
|
|
|
32
46
|
__all__ = ("AdbcCursor", "AdbcDriver", "AdbcExceptionHandler", "get_adbc_statement_config")
|
|
33
47
|
|
|
@@ -293,7 +307,7 @@ def _convert_array_for_postgres_adbc(value: Any) -> Any:
|
|
|
293
307
|
|
|
294
308
|
|
|
295
309
|
def get_type_coercion_map(dialect: str) -> "dict[type, Any]":
|
|
296
|
-
"""Get type coercion map for Arrow type handling.
|
|
310
|
+
"""Get type coercion map for Arrow type handling with dialect-aware type conversion.
|
|
297
311
|
|
|
298
312
|
Args:
|
|
299
313
|
dialect: Database dialect name
|
|
@@ -301,7 +315,9 @@ def get_type_coercion_map(dialect: str) -> "dict[type, Any]":
|
|
|
301
315
|
Returns:
|
|
302
316
|
Mapping of Python types to conversion functions
|
|
303
317
|
"""
|
|
304
|
-
|
|
318
|
+
tc = ADBCTypeConverter(dialect)
|
|
319
|
+
|
|
320
|
+
return {
|
|
305
321
|
datetime.datetime: lambda x: x,
|
|
306
322
|
datetime.date: lambda x: x,
|
|
307
323
|
datetime.time: lambda x: x,
|
|
@@ -309,18 +325,13 @@ def get_type_coercion_map(dialect: str) -> "dict[type, Any]":
|
|
|
309
325
|
bool: lambda x: x,
|
|
310
326
|
int: lambda x: x,
|
|
311
327
|
float: lambda x: x,
|
|
312
|
-
str:
|
|
328
|
+
str: tc.convert_if_detected,
|
|
313
329
|
bytes: lambda x: x,
|
|
314
330
|
tuple: _convert_array_for_postgres_adbc,
|
|
315
331
|
list: _convert_array_for_postgres_adbc,
|
|
316
332
|
dict: lambda x: x,
|
|
317
333
|
}
|
|
318
334
|
|
|
319
|
-
if dialect in {"postgres", "postgresql"}:
|
|
320
|
-
type_map[dict] = lambda x: to_json(x) if x is not None else None
|
|
321
|
-
|
|
322
|
-
return type_map
|
|
323
|
-
|
|
324
335
|
|
|
325
336
|
class AdbcCursor:
|
|
326
337
|
"""Context manager for cursor management."""
|
|
@@ -329,21 +340,24 @@ class AdbcCursor:
|
|
|
329
340
|
|
|
330
341
|
def __init__(self, connection: "AdbcConnection") -> None:
|
|
331
342
|
self.connection = connection
|
|
332
|
-
self.cursor:
|
|
343
|
+
self.cursor: Cursor | None = None
|
|
333
344
|
|
|
334
345
|
def __enter__(self) -> "Cursor":
|
|
335
346
|
self.cursor = self.connection.cursor()
|
|
336
347
|
return self.cursor
|
|
337
348
|
|
|
338
|
-
def __exit__(self,
|
|
339
|
-
_ = (exc_type, exc_val, exc_tb)
|
|
349
|
+
def __exit__(self, *_: Any) -> None:
|
|
340
350
|
if self.cursor is not None:
|
|
341
351
|
with contextlib.suppress(Exception):
|
|
342
352
|
self.cursor.close() # type: ignore[no-untyped-call]
|
|
343
353
|
|
|
344
354
|
|
|
345
355
|
class AdbcExceptionHandler:
|
|
346
|
-
"""Context manager for handling database exceptions.
|
|
356
|
+
"""Context manager for handling ADBC database exceptions.
|
|
357
|
+
|
|
358
|
+
ADBC propagates underlying database errors. Exception mapping
|
|
359
|
+
depends on the specific ADBC driver being used.
|
|
360
|
+
"""
|
|
347
361
|
|
|
348
362
|
__slots__ = ()
|
|
349
363
|
|
|
@@ -351,40 +365,118 @@ class AdbcExceptionHandler:
|
|
|
351
365
|
return None
|
|
352
366
|
|
|
353
367
|
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
368
|
+
_ = exc_tb
|
|
354
369
|
if exc_type is None:
|
|
355
370
|
return
|
|
371
|
+
self._map_adbc_exception(exc_val)
|
|
356
372
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
373
|
+
def _map_adbc_exception(self, e: Any) -> None:
|
|
374
|
+
"""Map ADBC exception to SQLSpec exception.
|
|
375
|
+
|
|
376
|
+
ADBC drivers may expose SQLSTATE codes or driver-specific codes.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
e: ADBC exception instance
|
|
380
|
+
"""
|
|
381
|
+
sqlstate = getattr(e, "sqlstate", None)
|
|
382
|
+
|
|
383
|
+
if sqlstate:
|
|
384
|
+
self._map_sqlstate_exception(e, sqlstate)
|
|
385
|
+
else:
|
|
386
|
+
self._map_message_based_exception(e)
|
|
387
|
+
|
|
388
|
+
def _map_sqlstate_exception(self, e: Any, sqlstate: str) -> None:
|
|
389
|
+
"""Map SQLSTATE code to exception.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
e: Exception instance
|
|
393
|
+
sqlstate: SQLSTATE error code
|
|
394
|
+
"""
|
|
395
|
+
if sqlstate == "23505":
|
|
396
|
+
self._raise_unique_violation(e)
|
|
397
|
+
elif sqlstate == "23503":
|
|
398
|
+
self._raise_foreign_key_violation(e)
|
|
399
|
+
elif sqlstate == "23502":
|
|
400
|
+
self._raise_not_null_violation(e)
|
|
401
|
+
elif sqlstate == "23514":
|
|
402
|
+
self._raise_check_violation(e)
|
|
403
|
+
elif sqlstate.startswith("23"):
|
|
404
|
+
self._raise_integrity_error(e)
|
|
405
|
+
elif sqlstate.startswith("42"):
|
|
406
|
+
self._raise_parsing_error(e)
|
|
407
|
+
elif sqlstate.startswith("08"):
|
|
408
|
+
self._raise_connection_error(e)
|
|
409
|
+
elif sqlstate.startswith("40"):
|
|
410
|
+
self._raise_transaction_error(e)
|
|
411
|
+
elif sqlstate.startswith("22"):
|
|
412
|
+
self._raise_data_error(e)
|
|
413
|
+
else:
|
|
414
|
+
self._raise_generic_error(e)
|
|
415
|
+
|
|
416
|
+
def _map_message_based_exception(self, e: Any) -> None:
|
|
417
|
+
"""Map exception using message-based detection.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
e: Exception instance
|
|
421
|
+
"""
|
|
422
|
+
error_msg = str(e).lower()
|
|
423
|
+
|
|
424
|
+
if "unique" in error_msg or "duplicate" in error_msg:
|
|
425
|
+
self._raise_unique_violation(e)
|
|
426
|
+
elif "foreign key" in error_msg:
|
|
427
|
+
self._raise_foreign_key_violation(e)
|
|
428
|
+
elif "not null" in error_msg or "null value" in error_msg:
|
|
429
|
+
self._raise_not_null_violation(e)
|
|
430
|
+
elif "check constraint" in error_msg:
|
|
431
|
+
self._raise_check_violation(e)
|
|
432
|
+
elif "constraint" in error_msg:
|
|
433
|
+
self._raise_integrity_error(e)
|
|
434
|
+
elif "syntax" in error_msg:
|
|
435
|
+
self._raise_parsing_error(e)
|
|
436
|
+
elif "connection" in error_msg or "connect" in error_msg:
|
|
437
|
+
self._raise_connection_error(e)
|
|
438
|
+
else:
|
|
439
|
+
self._raise_generic_error(e)
|
|
440
|
+
|
|
441
|
+
def _raise_unique_violation(self, e: Any) -> None:
|
|
442
|
+
msg = f"ADBC unique constraint violation: {e}"
|
|
443
|
+
raise UniqueViolationError(msg) from e
|
|
444
|
+
|
|
445
|
+
def _raise_foreign_key_violation(self, e: Any) -> None:
|
|
446
|
+
msg = f"ADBC foreign key constraint violation: {e}"
|
|
447
|
+
raise ForeignKeyViolationError(msg) from e
|
|
448
|
+
|
|
449
|
+
def _raise_not_null_violation(self, e: Any) -> None:
|
|
450
|
+
msg = f"ADBC not-null constraint violation: {e}"
|
|
451
|
+
raise NotNullViolationError(msg) from e
|
|
452
|
+
|
|
453
|
+
def _raise_check_violation(self, e: Any) -> None:
|
|
454
|
+
msg = f"ADBC check constraint violation: {e}"
|
|
455
|
+
raise CheckViolationError(msg) from e
|
|
456
|
+
|
|
457
|
+
def _raise_integrity_error(self, e: Any) -> None:
|
|
458
|
+
msg = f"ADBC integrity constraint violation: {e}"
|
|
459
|
+
raise IntegrityError(msg) from e
|
|
460
|
+
|
|
461
|
+
def _raise_parsing_error(self, e: Any) -> None:
|
|
462
|
+
msg = f"ADBC SQL parsing error: {e}"
|
|
463
|
+
raise SQLParsingError(msg) from e
|
|
464
|
+
|
|
465
|
+
def _raise_connection_error(self, e: Any) -> None:
|
|
466
|
+
msg = f"ADBC connection error: {e}"
|
|
467
|
+
raise DatabaseConnectionError(msg) from e
|
|
468
|
+
|
|
469
|
+
def _raise_transaction_error(self, e: Any) -> None:
|
|
470
|
+
msg = f"ADBC transaction error: {e}"
|
|
471
|
+
raise TransactionError(msg) from e
|
|
472
|
+
|
|
473
|
+
def _raise_data_error(self, e: Any) -> None:
|
|
474
|
+
msg = f"ADBC data error: {e}"
|
|
475
|
+
raise DataError(msg) from e
|
|
476
|
+
|
|
477
|
+
def _raise_generic_error(self, e: Any) -> None:
|
|
478
|
+
msg = f"ADBC database error: {e}"
|
|
479
|
+
raise SQLSpecError(msg) from e
|
|
388
480
|
|
|
389
481
|
|
|
390
482
|
class AdbcDriver(SyncDriverAdapterBase):
|
|
@@ -394,13 +486,13 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
394
486
|
database dialects, parameter style conversion, and transaction management.
|
|
395
487
|
"""
|
|
396
488
|
|
|
397
|
-
__slots__ = ("_detected_dialect", "dialect")
|
|
489
|
+
__slots__ = ("_data_dictionary", "_detected_dialect", "dialect")
|
|
398
490
|
|
|
399
491
|
def __init__(
|
|
400
492
|
self,
|
|
401
493
|
connection: "AdbcConnection",
|
|
402
|
-
statement_config: "
|
|
403
|
-
driver_features: "
|
|
494
|
+
statement_config: "StatementConfig | None" = None,
|
|
495
|
+
driver_features: "dict[str, Any] | None" = None,
|
|
404
496
|
) -> None:
|
|
405
497
|
self._detected_dialect = self._get_dialect(connection)
|
|
406
498
|
|
|
@@ -413,6 +505,7 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
413
505
|
|
|
414
506
|
super().__init__(connection=connection, statement_config=statement_config, driver_features=driver_features)
|
|
415
507
|
self.dialect = statement_config.dialect
|
|
508
|
+
self._data_dictionary: SyncDataDictionaryBase | None = None
|
|
416
509
|
|
|
417
510
|
@staticmethod
|
|
418
511
|
def _ensure_pyarrow_installed() -> None:
|
|
@@ -475,6 +568,93 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
475
568
|
return None
|
|
476
569
|
return parameters
|
|
477
570
|
|
|
571
|
+
def prepare_driver_parameters(
|
|
572
|
+
self,
|
|
573
|
+
parameters: Any,
|
|
574
|
+
statement_config: "StatementConfig",
|
|
575
|
+
is_many: bool = False,
|
|
576
|
+
prepared_statement: Any | None = None,
|
|
577
|
+
) -> Any:
|
|
578
|
+
"""Prepare parameters with cast-aware type coercion for ADBC.
|
|
579
|
+
|
|
580
|
+
For PostgreSQL, applies cast-aware parameter processing using metadata from the compiled statement.
|
|
581
|
+
This allows proper handling of JSONB casts and other type conversions.
|
|
582
|
+
Respects driver_features['enable_cast_detection'] configuration.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
parameters: Parameters in any format
|
|
586
|
+
statement_config: Statement configuration
|
|
587
|
+
is_many: Whether this is for execute_many operation
|
|
588
|
+
prepared_statement: Prepared statement containing the original SQL statement
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
Parameters with cast-aware type coercion applied
|
|
592
|
+
"""
|
|
593
|
+
enable_cast_detection = self.driver_features.get("enable_cast_detection", True)
|
|
594
|
+
|
|
595
|
+
if enable_cast_detection and prepared_statement and self.dialect in {"postgres", "postgresql"} and not is_many:
|
|
596
|
+
parameter_casts = self._get_parameter_casts(prepared_statement)
|
|
597
|
+
postgres_compatible = self._handle_postgres_empty_parameters(parameters)
|
|
598
|
+
return self._prepare_parameters_with_casts(postgres_compatible, parameter_casts, statement_config)
|
|
599
|
+
|
|
600
|
+
return super().prepare_driver_parameters(parameters, statement_config, is_many, prepared_statement)
|
|
601
|
+
|
|
602
|
+
def _get_parameter_casts(self, statement: SQL) -> "dict[int, str]":
|
|
603
|
+
"""Get parameter cast metadata from compiled statement.
|
|
604
|
+
|
|
605
|
+
Args:
|
|
606
|
+
statement: SQL statement with compiled metadata
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
Dict mapping parameter positions to cast types
|
|
610
|
+
"""
|
|
611
|
+
|
|
612
|
+
processed_state = statement.get_processed_state()
|
|
613
|
+
if processed_state is not Empty:
|
|
614
|
+
return processed_state.parameter_casts or {}
|
|
615
|
+
return {}
|
|
616
|
+
|
|
617
|
+
def _prepare_parameters_with_casts(
|
|
618
|
+
self, parameters: Any, parameter_casts: "dict[int, str]", statement_config: "StatementConfig"
|
|
619
|
+
) -> Any:
|
|
620
|
+
"""Prepare parameters with cast-aware type coercion.
|
|
621
|
+
|
|
622
|
+
Uses type coercion map for non-dict types and dialect-aware dict handling.
|
|
623
|
+
Respects driver_features configuration for JSON serialization backend.
|
|
624
|
+
|
|
625
|
+
Args:
|
|
626
|
+
parameters: Parameter values (list, tuple, or scalar)
|
|
627
|
+
parameter_casts: Mapping of parameter positions to cast types
|
|
628
|
+
statement_config: Statement configuration for type coercion
|
|
629
|
+
|
|
630
|
+
Returns:
|
|
631
|
+
Parameters with cast-aware type coercion applied
|
|
632
|
+
"""
|
|
633
|
+
from sqlspec.utils.serializers import to_json
|
|
634
|
+
|
|
635
|
+
json_encoder = self.driver_features.get("json_serializer", to_json)
|
|
636
|
+
|
|
637
|
+
if isinstance(parameters, (list, tuple)):
|
|
638
|
+
result: list[Any] = []
|
|
639
|
+
for idx, param in enumerate(parameters, start=1): # pyright: ignore
|
|
640
|
+
cast_type = parameter_casts.get(idx, "").upper()
|
|
641
|
+
if cast_type in {"JSON", "JSONB", "TYPE.JSON", "TYPE.JSONB"}:
|
|
642
|
+
if isinstance(param, dict):
|
|
643
|
+
result.append(json_encoder(param))
|
|
644
|
+
else:
|
|
645
|
+
result.append(param)
|
|
646
|
+
elif isinstance(param, dict):
|
|
647
|
+
result.append(ADBCTypeConverter(self.dialect).convert_dict(param)) # type: ignore[arg-type]
|
|
648
|
+
else:
|
|
649
|
+
if statement_config.parameter_config.type_coercion_map:
|
|
650
|
+
for type_check, converter in statement_config.parameter_config.type_coercion_map.items():
|
|
651
|
+
if type_check is not dict and isinstance(param, type_check):
|
|
652
|
+
param = converter(param)
|
|
653
|
+
break
|
|
654
|
+
result.append(param)
|
|
655
|
+
return tuple(result) if isinstance(parameters, tuple) else result
|
|
656
|
+
return parameters
|
|
657
|
+
|
|
478
658
|
def with_cursor(self, connection: "AdbcConnection") -> "AdbcCursor":
|
|
479
659
|
"""Create context manager for cursor.
|
|
480
660
|
|
|
@@ -494,7 +674,7 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
494
674
|
"""
|
|
495
675
|
return AdbcExceptionHandler()
|
|
496
676
|
|
|
497
|
-
def _try_special_handling(self, cursor: "Cursor", statement: SQL) -> "
|
|
677
|
+
def _try_special_handling(self, cursor: "Cursor", statement: SQL) -> "SQLResult | None":
|
|
498
678
|
"""Handle special operations.
|
|
499
679
|
|
|
500
680
|
Args:
|
|
@@ -519,17 +699,26 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
519
699
|
"""
|
|
520
700
|
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
521
701
|
|
|
702
|
+
parameter_casts = self._get_parameter_casts(statement)
|
|
703
|
+
|
|
522
704
|
try:
|
|
523
705
|
if not prepared_parameters:
|
|
524
706
|
cursor._rowcount = 0 # pyright: ignore[reportPrivateUsage]
|
|
525
707
|
row_count = 0
|
|
526
|
-
elif isinstance(prepared_parameters, list) and prepared_parameters:
|
|
708
|
+
elif isinstance(prepared_parameters, (list, tuple)) and prepared_parameters:
|
|
527
709
|
processed_params = []
|
|
528
710
|
for param_set in prepared_parameters:
|
|
529
711
|
postgres_compatible = self._handle_postgres_empty_parameters(param_set)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
712
|
+
|
|
713
|
+
if self.dialect in {"postgres", "postgresql"}:
|
|
714
|
+
# For postgres, always use cast-aware parameter preparation
|
|
715
|
+
formatted_params = self._prepare_parameters_with_casts(
|
|
716
|
+
postgres_compatible, parameter_casts, self.statement_config
|
|
717
|
+
)
|
|
718
|
+
else:
|
|
719
|
+
formatted_params = self.prepare_driver_parameters(
|
|
720
|
+
postgres_compatible, self.statement_config, is_many=False
|
|
721
|
+
)
|
|
533
722
|
processed_params.append(formatted_params)
|
|
534
723
|
|
|
535
724
|
cursor.executemany(sql, processed_params)
|
|
@@ -540,7 +729,6 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
540
729
|
|
|
541
730
|
except Exception:
|
|
542
731
|
self._handle_postgres_rollback(cursor)
|
|
543
|
-
logger.exception("Executemany failed")
|
|
544
732
|
raise
|
|
545
733
|
|
|
546
734
|
return self.create_execution_result(cursor, rowcount_override=row_count, is_many_result=True)
|
|
@@ -557,9 +745,18 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
557
745
|
"""
|
|
558
746
|
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
559
747
|
|
|
748
|
+
parameter_casts = self._get_parameter_casts(statement)
|
|
749
|
+
|
|
560
750
|
try:
|
|
561
751
|
postgres_compatible_params = self._handle_postgres_empty_parameters(prepared_parameters)
|
|
562
|
-
|
|
752
|
+
|
|
753
|
+
if self.dialect in {"postgres", "postgresql"}:
|
|
754
|
+
formatted_params = self._prepare_parameters_with_casts(
|
|
755
|
+
postgres_compatible_params, parameter_casts, self.statement_config
|
|
756
|
+
)
|
|
757
|
+
cursor.execute(sql, parameters=formatted_params)
|
|
758
|
+
else:
|
|
759
|
+
cursor.execute(sql, parameters=postgres_compatible_params)
|
|
563
760
|
|
|
564
761
|
except Exception:
|
|
565
762
|
self._handle_postgres_rollback(cursor)
|
|
@@ -570,7 +767,7 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
570
767
|
column_names = [col[0] for col in cursor.description or []]
|
|
571
768
|
|
|
572
769
|
if fetched_data and isinstance(fetched_data[0], tuple):
|
|
573
|
-
dict_data: list[dict[Any, Any]] = [dict(zip(column_names, row)) for row in fetched_data]
|
|
770
|
+
dict_data: list[dict[Any, Any]] = [dict(zip(column_names, row, strict=False)) for row in fetched_data]
|
|
574
771
|
else:
|
|
575
772
|
dict_data = fetched_data # type: ignore[assignment]
|
|
576
773
|
|
|
@@ -655,3 +852,14 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
655
852
|
except Exception as e:
|
|
656
853
|
msg = f"Failed to commit transaction: {e}"
|
|
657
854
|
raise SQLSpecError(msg) from e
|
|
855
|
+
|
|
856
|
+
@property
|
|
857
|
+
def data_dictionary(self) -> "SyncDataDictionaryBase":
|
|
858
|
+
"""Get the data dictionary for this driver.
|
|
859
|
+
|
|
860
|
+
Returns:
|
|
861
|
+
Data dictionary instance for metadata queries
|
|
862
|
+
"""
|
|
863
|
+
if self._data_dictionary is None:
|
|
864
|
+
self._data_dictionary = AdbcDataDictionary()
|
|
865
|
+
return self._data_dictionary
|