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.

Files changed (199) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +256 -24
  3. sqlspec/_typing.py +71 -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 +69 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +340 -0
  9. sqlspec/adapters/adbc/driver.py +266 -58
  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 +153 -0
  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 +88 -15
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
  18. sqlspec/adapters/aiosqlite/driver.py +143 -40
  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 +2 -2
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +493 -0
  26. sqlspec/adapters/asyncmy/config.py +68 -23
  27. sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
  28. sqlspec/adapters/asyncmy/driver.py +313 -58
  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 +59 -35
  37. sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
  38. sqlspec/adapters/asyncpg/driver.py +170 -25
  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 +27 -10
  45. sqlspec/adapters/bigquery/data_dictionary.py +149 -0
  46. sqlspec/adapters/bigquery/driver.py +368 -142
  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 +125 -0
  50. sqlspec/adapters/duckdb/_types.py +1 -1
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +553 -0
  53. sqlspec/adapters/duckdb/config.py +80 -20
  54. sqlspec/adapters/duckdb/data_dictionary.py +163 -0
  55. sqlspec/adapters/duckdb/driver.py +167 -45
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +4 -4
  59. sqlspec/adapters/duckdb/type_converter.py +133 -0
  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 +122 -32
  65. sqlspec/adapters/oracledb/data_dictionary.py +509 -0
  66. sqlspec/adapters/oracledb/driver.py +353 -91
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +767 -0
  69. sqlspec/adapters/oracledb/migrations.py +348 -73
  70. sqlspec/adapters/oracledb/type_converter.py +207 -0
  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 +46 -17
  76. sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
  77. sqlspec/adapters/psqlpy/driver.py +123 -209
  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 +102 -0
  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 +69 -35
  86. sqlspec/adapters/psycopg/data_dictionary.py +331 -0
  87. sqlspec/adapters/psycopg/driver.py +238 -81
  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 +87 -15
  96. sqlspec/adapters/sqlite/data_dictionary.py +149 -0
  97. sqlspec/adapters/sqlite/driver.py +137 -54
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +18 -9
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +162 -89
  104. sqlspec/builder/_column.py +62 -29
  105. sqlspec/builder/_ddl.py +180 -121
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +53 -94
  109. sqlspec/builder/_insert.py +32 -131
  110. sqlspec/builder/_join.py +375 -0
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +111 -17
  113. sqlspec/builder/_select.py +1457 -24
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +307 -194
  116. sqlspec/config.py +252 -67
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +17 -17
  119. sqlspec/core/compiler.py +62 -9
  120. sqlspec/core/filters.py +37 -37
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +83 -48
  123. sqlspec/core/result.py +102 -46
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +36 -30
  126. sqlspec/core/type_conversion.py +235 -0
  127. sqlspec/driver/__init__.py +7 -6
  128. sqlspec/driver/_async.py +188 -151
  129. sqlspec/driver/_common.py +285 -80
  130. sqlspec/driver/_sync.py +188 -152
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +75 -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/__init__.py +4 -3
  153. sqlspec/migrations/base.py +302 -39
  154. sqlspec/migrations/commands.py +611 -144
  155. sqlspec/migrations/context.py +142 -0
  156. sqlspec/migrations/fix.py +199 -0
  157. sqlspec/migrations/loaders.py +68 -23
  158. sqlspec/migrations/runner.py +543 -107
  159. sqlspec/migrations/tracker.py +237 -21
  160. sqlspec/migrations/utils.py +51 -3
  161. sqlspec/migrations/validation.py +177 -0
  162. sqlspec/protocols.py +66 -36
  163. sqlspec/storage/_utils.py +98 -0
  164. sqlspec/storage/backends/fsspec.py +134 -106
  165. sqlspec/storage/backends/local.py +78 -51
  166. sqlspec/storage/backends/obstore.py +278 -162
  167. sqlspec/storage/registry.py +75 -39
  168. sqlspec/typing.py +16 -84
  169. sqlspec/utils/config_resolver.py +153 -0
  170. sqlspec/utils/correlation.py +4 -5
  171. sqlspec/utils/data_transformation.py +3 -2
  172. sqlspec/utils/deprecation.py +9 -8
  173. sqlspec/utils/fixtures.py +4 -4
  174. sqlspec/utils/logging.py +46 -6
  175. sqlspec/utils/module_loader.py +2 -2
  176. sqlspec/utils/schema.py +288 -0
  177. sqlspec/utils/serializers.py +50 -2
  178. sqlspec/utils/sync_tools.py +21 -17
  179. sqlspec/utils/text.py +1 -2
  180. sqlspec/utils/type_guards.py +111 -20
  181. sqlspec/utils/version.py +433 -0
  182. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
  183. sqlspec-0.27.0.dist-info/RECORD +207 -0
  184. sqlspec/builder/mixins/__init__.py +0 -55
  185. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -254
  186. sqlspec/builder/mixins/_delete_operations.py +0 -50
  187. sqlspec/builder/mixins/_insert_operations.py +0 -282
  188. sqlspec/builder/mixins/_join_operations.py +0 -389
  189. sqlspec/builder/mixins/_merge_operations.py +0 -592
  190. sqlspec/builder/mixins/_order_limit_operations.py +0 -152
  191. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  192. sqlspec/builder/mixins/_select_operations.py +0 -936
  193. sqlspec/builder/mixins/_update_operations.py +0 -218
  194. sqlspec/builder/mixins/_where_clause.py +0 -1304
  195. sqlspec-0.25.0.dist-info/RECORD +0 -139
  196. sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
  197. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
  198. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
  199. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
@@ -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, Optional, cast
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 MissingDependencyError, SQLParsingError, SQLSpecError
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
- type_map = {
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: lambda x: x,
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: Optional[Cursor] = None
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, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
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
- try:
358
- if issubclass(exc_type, IntegrityError):
359
- e = exc_val
360
- msg = f"Integrity constraint violation: {e}"
361
- raise SQLSpecError(msg) from e
362
- if issubclass(exc_type, ProgrammingError):
363
- e = exc_val
364
- error_msg = str(e).lower()
365
- if "syntax" in error_msg or "parse" in error_msg:
366
- msg = f"SQL syntax error: {e}"
367
- raise SQLParsingError(msg) from e
368
- msg = f"Programming error: {e}"
369
- raise SQLSpecError(msg) from e
370
- if issubclass(exc_type, OperationalError):
371
- e = exc_val
372
- msg = f"Operational error: {e}"
373
- raise SQLSpecError(msg) from e
374
- if issubclass(exc_type, DatabaseError):
375
- e = exc_val
376
- msg = f"Database error: {e}"
377
- raise SQLSpecError(msg) from e
378
- except ImportError:
379
- pass
380
- if issubclass(exc_type, Exception):
381
- e = exc_val
382
- error_msg = str(e).lower()
383
- if "parse" in error_msg or "syntax" in error_msg:
384
- msg = f"SQL parsing failed: {e}"
385
- raise SQLParsingError(msg) from e
386
- msg = f"Unexpected database operation error: {e}"
387
- raise SQLSpecError(msg) from e
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: "Optional[StatementConfig]" = None,
403
- driver_features: "Optional[dict[str, Any]]" = None,
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) -> "Optional[SQLResult]":
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
- formatted_params = self.prepare_driver_parameters(
531
- postgres_compatible, self.statement_config, is_many=False
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
- cursor.execute(sql, parameters=postgres_compatible_params)
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
@@ -0,0 +1,5 @@
1
+ """Litestar integration for ADBC adapter."""
2
+
3
+ from sqlspec.adapters.adbc.litestar.store import ADBCStore
4
+
5
+ __all__ = ("ADBCStore",)