Skip to content

feat(adbc): add Oracle, Db2, GizmoSQL, and Quack driver support - #839

Merged
cofin merged 7 commits into
mainfrom
feat/adbc-enterprise-drivers
Oct 14, 2026
Merged

cofin merged 7 commits into
mainfrom
feat/adbc-enterprise-drivers

Conversation

@cofin

@cofin cofin commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Summary

Expand SQLSpec's Apache Arrow Database Connectivity (sqlspec.adapters.adbc) adapter, extension stores (litestar, events, and adk), and documentation to support the pure-Go GizmoData ADBC drivers (adbc-driver-oracle, adbc-driver-db2, adbc-driver-gizmosql, adbc-driver-quack). Also restructure the ADBC adapter documentation (docs/reference/adapters/adbc.rst) and related adapter/extension pages for clarity and quick navigation.

Key Changes

ADBC Core Adapter (sqlspec/adapters/adbc/)

  • Driver Resolution & URI Routing (core.py, config.py):
    • Add driver aliases and URI schemes for Oracle (oracle, oracledb, oracle://), IBM Db2 (db2, ibmdb2, ibm_db, db2://, DATABASE=...;HOSTNAME=...;), native GizmoSQL (gizmosql, gizmo, gizmosql://, gizmo://), and DuckDB Quack (quack, quack://, quack+tls://), as well as internal dialect/driver-manager routing for mssql / tsql.
    • Prefer adbc_driver_gizmosql.dbapi.connect when adbc-driver-gizmosql is installed and fall back to adbc_driver_flightsql.dbapi.connect otherwise; support auth_type="external" (adbc.gizmosql.auth_type) for OAuth browser SSO.
    • Normalize discrete username/password into db_kwargs for adbc-driver-oracle and token into db_kwargs["adbc.quack.token"] for adbc-driver-quack.
  • Parameter Profiles & Dialect Detection (core.py):
    • Configure positional execution parameter profiles for enterprise ADBC drivers (ParameterStyle.POSITIONAL_COLON :1, :2 for oracle; ParameterStyle.QMARK ? for db2 and mssql/tsql) with preserve_parameter_format=False while supporting named input styles (NAMED_COLON, POSITIONAL_COLON, QMARK, and NAMED_AT).
    • Detect oracle, db2, mssql, and duckdb (including Quack) from adbc_get_info() vendor/driver metadata.
  • Exception Mapping (core.py):
    • Map Oracle ORA-xxxxx codes, IBM Db2 SQLSTATE/SQLxxxxN codes (including reason code 68 lock timeout vs. 911/913 deadlock), and SQL Server Msg / TDS vendor codes (including 547 check vs. foreign-key constraint disambiguation) to canonical SQLSpecError subclasses.
  • Savepoints, Uppercase Column Normalization & Data Dictionary (driver.py, data_dictionary.py):
    • Add dialect-aware savepoint SQL (SAVEPOINT <name> ON ROLLBACK RETAIN CURSORS / RELEASE SAVEPOINT <name> for db2; SAVEPOINT <name> with no-op release for oracle; SAVE TRANSACTION <name> / ROLLBACK TRANSACTION <name> with no-op release for mssql/tsql) and identifier validation via validate_savepoint_name.
    • Enable enable_lowercase_column_names=True by default for oracle and db2 dialects across row and Arrow execution paths while preserving quoted mixed-case identifiers.
    • Resolve version-aware Oracle JSON types (JSON on Oracle 21c+ and CLOB on 19c and earlier) in AdbcDataDictionary.

Extension Stores (sqlspec/adapters/adbc/{litestar,events,adk}/)

  • Litestar Session Store (litestar/store.py):
    • Add oracle, db2, and mssql/tsql dialect support to ADBCStore with dialect-specific column types (BLOB/VARBINARY(MAX)), UTC timestamp expressions, idempotent table/index creation and drop guards, and atomic MERGE / update-then-insert session persistence without untyped None Arrow binds.
  • Events Queue Store (events/store.py):
    • Extend AdbcEventQueueStore with oracle, db2, and mssql DDL, column types, and idempotent schema creation/drop wrappers.
  • Google ADK Session & Memory Stores (adk/store.py):
    • Extend AdbcADKStore and AdbcADKMemoryStore for oracle, db2, and mssql with _oracle_store_sql (:1, :2 placeholder rewriting), dialect-specific table/index guards (PL/SQL -955/-1408/-942 blocks for Oracle, SYSCAT.TABLES/SYSCAT.INDEXES probes for Db2, and OBJECT_ID/sys.indexes checks for SQL Server), OFFSET ? ROWS FETCH NEXT ? ROWS ONLY pagination, and POSSTR memory search on Db2.

Documentation (docs/)

  • ADBC Adapter Reference & Guides (docs/reference/adapters/adbc.rst, docs/usage/drivers_and_querying.rst, docs/reference/adapters/db2.rst, docs/extensions/adk/, docs/reference/extensions/events.rst, docs/usage/frameworks/litestar/session_stores.rst):
    • Reorganize docs/reference/adapters/adbc.rst into a clear quick-reference driver matrix (install, driver_name aliases, URI schemes, parameter styles, dialect), per-backend connection recipes, and concise operational sections for pip-installable ADBC drivers.
    • Update adapter and extension store guides to reflect ADBC support for Oracle, IBM Db2, GizmoSQL, and Quack.

Verification

uv run --no-sync pytest tests/unit/adapters/test_adapter_implementations.py tests/unit/adapters/test_adbc/ -q
uv run --no-sync python tools/scripts/mypyc_inventory.py --check
uv run --no-sync python .agents/skills/recursive-code-review/scripts/scan_codebase_invariants.py --check sqlspec/adapters/adbc/ tests/integration/adapters/_shared/_driver_type_system.py
uv run --no-sync ruff check sqlspec/adapters/adbc tests/unit/adapters/test_adbc tests/integration/adapters/_shared/_driver_type_system.py
uv run --no-sync mypy sqlspec/adapters/adbc tests/unit/adapters/test_adbc tests/integration/adapters/_shared/_driver_type_system.py
PYRIGHT_PYTHON_FORCE_VERSION=1.1.402 uv run --no-sync pyright sqlspec/adapters/adbc tests/unit/adapters/test_adbc tests/integration/adapters/_shared/_driver_type_system.py
make docs

@codecov-commenter

codecov-commenter commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.59173% with 223 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.56%. Comparing base (1184109) to head (4136de7).

Files with missing lines Patch % Lines
sqlspec/adapters/adbc/adk/store.py 78.81% 27 Missing and 16 partials ⚠️
sqlspec/adapters/adbc/core.py 90.62% 11 Missing and 16 partials ⚠️
sqlspec/adapters/adbc/litestar/store.py 50.90% 22 Missing and 5 partials ⚠️
sqlspec/driver/_query_cache.py 78.57% 18 Missing and 6 partials ⚠️
sqlspec/driver/_common.py 84.03% 10 Missing and 9 partials ⚠️
sqlspec/adapters/adbc/events/store.py 73.58% 4 Missing and 10 partials ⚠️
sqlspec/core/parameters/_processor.py 82.25% 8 Missing and 3 partials ⚠️
sqlspec/adapters/adbc/driver.py 72.97% 8 Missing and 2 partials ⚠️
sqlspec/driver/_async.py 83.87% 6 Missing and 4 partials ⚠️
sqlspec/driver/_sync.py 84.12% 6 Missing and 4 partials ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #839      +/-   ##
==========================================
- Coverage   81.40%   80.56%   -0.85%     
==========================================
  Files         527      527              
  Lines       85274    86216     +942     
  Branches    15789    16044     +255     
==========================================
+ Hits        69421    69456      +35     
- Misses      11731    12558     +827     
- Partials     4122     4202      +80     
Flag Coverage Δ
integration 58.76% <50.89%> (-2.33%) ⬇️
py3.11 79.84% <81.65%> (+0.05%) ⬆️
py3.12 79.84% <81.65%> (+0.05%) ⬆️
py3.13 79.84% <81.65%> (+0.05%) ⬆️
py3.14 72.66% <79.66%> (-8.13%) ⬇️
unit 73.26% <79.85%> (+0.23%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sqlspec/adapters/aiosqlite/driver.py 74.92% <100.00%> (-0.46%) ⬇️
sqlspec/adapters/asyncpg/data_dictionary.py 70.44% <100.00%> (ø)
...spec/adapters/cockroach_asyncpg/data_dictionary.py 47.51% <100.00%> (ø)
...spec/adapters/cockroach_psycopg/data_dictionary.py 41.80% <100.00%> (ø)
sqlspec/adapters/psqlpy/data_dictionary.py 70.98% <100.00%> (ø)
sqlspec/adapters/psycopg/data_dictionary.py 69.20% <100.00%> (ø)
sqlspec/adapters/sqlite/driver.py 81.02% <100.00%> (-0.31%) ⬇️
sqlspec/config.py 96.54% <100.00%> (+0.01%) ⬆️
sqlspec/core/compiler.py 91.39% <100.00%> (-0.06%) ⬇️
sqlspec/extensions/adk/memory/store.py 70.85% <100.00%> (+0.16%) ⬆️
... and 19 more

... and 41 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cofin cofin changed the title feat(adbc): add Oracle, Db2, SQL Server, GizmoSQL, and Quack driver support feat(adbc): add Oracle, Db2, GizmoSQL, and Quack driver support Oct 13, 2026
cofin and others added 4 commits October 13, 2026 17:21
…ate baselines (#838)

## Summary

Prepares the `v0.67.0` release, upgrades project dependencies in
`uv.lock`, updates Sphinx documentation configuration, optimizes
statement compilation, caching, and driver hot-path execution across
sync and async adapters, unifies DuckDB row materialization under the
native Python type contract, and hardens the adapter and subsystem
performance benchmark gates with refreshed baselines.

## Key Changes

### Core Compilation & Statement Caching (`sqlspec/core/`)
- **Zero-Copy Cached AST Sharing (`statement.py`, `compiler.py`)**:
- Track shared SQLGlot AST expressions on `ProcessedState`
(`expression_is_shared=True`) and expose
`ProcessedState.ensure_owned_expression()` and `SQL._peek_expression()`.
- Defer AST deep-copying to public `SQL.expression` and
`SQL.statement_expression` accessors and mutating compile transforms
(`_apply_parameter_casts`), eliminating per-call `.copy()` overhead on
compile-cache hits, parameter rebinds, and parse-cache lookups.
- **Single-Pass Parameter Processing & Precomputed Coercion
(`parameters/_processor.py`, `statement.py`, `utils/dispatch.py`)**:
- Precompute and memoize type-coercion fallback items
(`type_coercion_fallbacks`) on `ParameterProcessor` and cache
fallback-tuple lookups in `type_coercion_dispatcher`.
- Short-circuit sequence and mapping coercion when no container elements
require conversion (`_NO_MATCH` sentinel).
- Share a single rebind `ParameterProcessor` instance across cached
`SQL` rebinds and construct cached direct `SQL` instances with a single
`ProcessedState` allocation (`SQL._create_cached_direct`).
- **Lazy Fast-Path Result Statements (`result/_base.py`)**:
- Add `SQLResult.create_cached_select` so cached `SELECT` executions
defer `SQL` statement construction until `result.statement` is accessed.

### Driver Execution Pipeline (`sqlspec/driver/`, `sqlspec/config.py`)
- **Expanded Fast-Path Eligibility (`_common.py`, `_sync.py`,
`_async.py`)**:
- Add `_resolve_fast_params(parameters, kwargs)` so parameterless
(`execute(sql)`) and scalar-positional (`execute(sql, a, b)`) calls
route through `_cached_execution` alongside tuple, list, and dict
parameters.
- Guard `_cache_statement` against caching statements whose
`parsed_expression` is `None` or whose `operation_type` is `"COMMAND"`
or `"UNKNOWN"`.
- Precompute `CachedQuery.needs_style_remap` at cache-store time and
skip `StatementFilter` scans when raw parameters are already normalized.
- **Per-Config Shared Statement Caches (`_query_cache.py`, `_common.py`,
`config.py`)**:
- Introduce thread-safe (`threading.RLock`) `QueryCache` and
`StatementCacheHolder` owned by the database config and shared across
all sessions created with the config's default `StatementConfig`.
- **Fast-Path Observability & Inline Exception Isolation (`_common.py`,
`_sync.py`, `_async.py`)**:
- Keep the statement-cache fast path enabled when spans, statement
observers, or lifecycle hooks are active via
`_execute_with_observability_sync` and
`_execute_with_observability_async`.
- Check `sys.exc_info()[1] is None` inline before entering exception
handlers on fast paths, invoking generator-isolated
`_run_with_sync_exception_handler` / `_run_with_async_exception_handler`
only when an ambient exception is active.
- Add DB-API cursor fast-path `SELECT` execution
(`_execute_cache_hit_with_cursor`) for cursor-based adapters (`cursor is
not self.connection`).
- **Single-Pass `execute_many` Preparation (`_common.py`)**:
- Reuse batch parameters already coerced during `statement.compile()`
via `_many_parameters_already_prepared` and copy parameter rows in
`_check_declared_parameters` only when declared defaults are applied
(`_row_needs_declared_default`).

### Database Adapters (`sqlspec/adapters/`, `sqlspec/extensions/adk/`)
- **DuckDB Native Type Contract & Bulk Insert Caching (`duckdb/core.py`,
`duckdb/driver.py`)**:
- Unify slow-path (`dispatch_execute`) and cache-hit
(`_execute_cache_hit_with_cursor`) row materialization in
`_materialize_select_rows` so all `execute()`, `select()`, and `fetch*`
calls return DuckDB-native `fetchall()` Python types (`HUGEINT`,
`UHUGEINT`, `BIT`, `TIMETZ`, `TIMESTAMP_NS`, `INTERVAL`, `MAP`,
`BIGNUM`/`VARINT`, fixed-size arrays, and nested `UUID` always
materialize via `fetchall()`; compatible schemas use Arrow when observed
row counts reach `ARROW_MIN_ROWS = 64`).
- Cache parsed `(target_table, column_sql)` fragments in
`StatementCacheHolder.duckdb_bulk_sql` for Arrow bulk `execute_many`
inserts and replace `contextlib.suppress` in `resolve_rowcount`.
- **Aiosqlite Pool & Fast-Path Cursor Execution (`aiosqlite/pool.py`,
`aiosqlite/driver.py`, `sqlite/driver.py`)**:
- Add `_try_acquire_idle_connection()` to bypass `asyncio.wait_for` when
an idle worker connection is immediately available and skip
`end_transaction()` on release when `raw_conn.in_transaction` is
`False`.
- **PostgreSQL, CockroachDB & ADK Store Typing (`asyncpg`, `psqlpy`,
`psycopg`, `cockroach_asyncpg`, `cockroach_psycopg`,
`extensions/adk/`)**:
- Explicitly type `_POSTGRES_METADATA_DOMAINS` and
`_COCKROACH_METADATA_DOMAINS` as `tuple[str, ...]` and define concrete
`_drop_tables_sql` / `_drop_memory_table_sql` base methods on
`_ADKStoreCommon` and `_ADKMemoryStoreCommon`.

### Release, Benchmarks & Documentation (`Makefile`, `tools/`, `docs/`,
`pyproject.toml`, `uv.lock`)
- **Version, Lockfile & Docs (`pyproject.toml`, `uv.lock`,
`docs/changelog.rst`, `docs/conf.py`)**:
- Bump version to `0.67.0`, upgrade locked dependencies, update Sphinx
configuration for MyST v5.1+, and update `docs/changelog.rst`.
- **Benchmark Harness, Gate & Baselines (`Makefile`, `tools/scripts/`,
`tools/perf_baselines/`)**:
- Enforce workload metadata parity (`rows`, `pool_size`,
`mypyc_compiled`) and add `--mode ratio` (`abs-fallback`) and
`--print-run-args` to `bench_compare.py`.
- Update `make bench-gate-adapters` to derive run arguments from each
baseline JSON and evaluate all requested drivers before exiting.
- Add `scalar_param_select`, `no_param_select`, `complex_query`,
`session_per_request`, and `observability_select` scenarios across
`sqlite`, `aiosqlite`, `duckdb`, `asyncpg`, `psycopg_sync`,
`psycopg_async`, `psqlpy`, and `oracle`, plus 9 interleaved stub-driver
micro-benchmarks in `bench_subsystems.py`.
- Re-capture baselines in `tools/perf_baselines/` (`sqlite.json`,
`aiosqlite.json`, `duckdb.json`, `subsystems.json`, `oracle.json`,
`asyncpg.json`, `psycopg_sync.json`, `psycopg_async.json`,
`psqlpy.json`, `postgres.json`, `adbc.json`) and update
`tools/perf_baselines/README.md`.

## Verification

```bash
make lint
make bench-gate-adapters DRIVERS="sqlite aiosqlite duckdb"
uv run --no-sync pytest tests/unit -q
uv run --no-sync pytest tests/integration/adapters/sqlite tests/integration/adapters/duckdb -q
uv run --no-sync python tools/scripts/mypyc_inventory.py --check
uv run --no-sync python tools/scripts/mypyc_smoke.py
```
@cofin
cofin merged commit ae39a4d into main Oct 14, 2026
25 of 26 checks passed
@cofin
cofin deleted the feat/adbc-enterprise-drivers branch October 14, 2026 00:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants