Skip to content

feat(adapters): add sybase ase and sap sql anywhere dialects, data dictionaries, and adapters - #870

Merged
cofin merged 36 commits into
mainfrom
research_sybase_adbc_driver
Oct 31, 2026
Merged

cofin merged 36 commits into
mainfrom
research_sybase_adbc_driver

Conversation

@cofin

@cofin cofin commented Oct 30, 2026

Copy link
Copy Markdown
Member

Summary

Adds first-class SQLGlot dialects, system catalog data dictionaries, database adapters, migration trackers, and framework/AI extension stores (Litestar, Events, and Google ADK) for both SAP Adaptive Server Enterprise (sybase) over pymssql (FreeTDS TDS 5.0) and SAP SQL Anywhere (sqlanywhere) over sqlanydb.

Key Changes

Custom SQLGlot Dialects & Query Builder (sqlspec.dialects.sybase, sqlspec.builder)

  • Added:
    • Sybase (sqlspec.dialects.sybase:Sybase) and SQLAnywhere (sqlspec.dialects.sybase:SQLAnywhere) custom SQLGlot dialects registered under [project.entry-points."sqlglot.dialects"] and exported from sqlspec.dialects.
    • Identity-cached per-instance generator dispatch overlays (sybase_dispatch, sqlanywhere_dispatch) in sqlspec/dialects/sybase/_generators.py so sqlglot[c] compiled generators are never subclassed directly and TSQLGenerator.TRANSFORMS is never mutated for "tsql"/"mssql" callers.
    • Copy-on-write AST cloning (clone_with_detached_pagination) in sybase_select_sql, sybase_set_operation_sql, sqlanywhere_select_sql, and sqlanywhere_set_operation_sql so expression.sql(dialect=..., copy=False) on shared or cached ASTs never strips LIMIT/OFFSET in place.
    • NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_SENSITIVE on Sybase and SQLAnywhere so QueryBuilder._optimize_expression() resolves single-table MappingSchema(..., normalize=False) on the first pass and populates QueryCache.
    • Sybase ASE ROWS LIMIT <limit> OFFSET <offset> and SELECT TOP <n> pagination rendering/parsing, TEXT/UNITEXT/IMAGE/BIGDATETIME/BIGTIME/VARCHAR(36) type mappings, IDENTITY without seed/increment arguments, GETUTCDATE(), CHAR_LENGTH(), CHARINDEX(), and LOWER(a) LIKE LOWER(b).
    • SAP SQL Anywhere trailing LIMIT <limit> [OFFSET <offset>] and Watcom-SQL SELECT TOP <n> START AT <m> parsing (converting 1-based START AT to 0-based OFFSET), LONG VARCHAR/LONG NVARCHAR/LONG BINARY/TIMESTAMP/TIMESTAMP WITH TIME ZONE/UNIQUEIDENTIFIER/BIT type mappings, CURRENT UTC TIMESTAMP, and native CREATE TABLE IF NOT EXISTS / DROP TABLE IF EXISTS.
  • Changed:
    • Updated _normalize_dialect in sqlspec/builder/_parsing_utils.py to normalize "sybase"/"ase"/"sybase_ase" to "sybase" and "sqlanywhere"/"sql_anywhere"/"sa" to "sqlanywhere".
    • Added "sybase" to MERGE_UNSUPPORTED_DIALECTS in sqlspec/builder/_merge.py.

System Catalog Data Dictionaries & Fixtures (sqlspec.data_dictionary, sqlspec.utils.fixtures)

  • Added:
    • SYBASE_CONFIG and 9 catalog SQL queries under sqlspec/data_dictionary/dialects/sybase/ introspecting dbo.sysobjects, dbo.syscolumns, dbo.systypes, dbo.sysindexes, dbo.sysreferences, dbo.sysusers, and dbo.syscomments.
    • SQLANYWHERE_CONFIG and 9 catalog SQL queries under sqlspec/data_dictionary/dialects/sqlanywhere/ introspecting SYS.SYSTAB, SYS.SYSTABCOL, SYS.SYSDOMAIN, SYS.SYSIDX, SYS.SYSIDXCOL, SYS.SYSFKEY, SYS.SYSUSER, and SYS.SYSVIEW.
  • Changed:
    • Extended sqlspec/utils/fixtures.py to resynchronize Sybase ASE identity columns via EXEC sp_chgattribute '<table>', 'identity_burn_max', 0, '<max>' and SQL Anywhere identity columns via CALL sa_reset_identity('<table>', '<schema>', <max>), exclude LOB types (text, unitext, image, long varchar, long nvarchar, long binary) from export ordering, and decode bigdatetime, smalldatetime, datetimetz, bigtime, money, smallmoney, and uniqueidentifier columns.

SAP ASE Adapter (sqlspec.adapters.sybase)

  • Added:
    • SybaseConfig, SybaseDriver, SybaseConnectionPool, SybaseCursor, SybaseExceptionHandler, SybaseSyncDataDictionary, and SybaseSyncMigrationTracker.
    • Thread-locked TDSVER="5.0" environment negotiation in SybaseConnectionPool.new_connection() so FreeTDS libsybdb transmits a native TDS 5.0 login packet without triggering pymssql._mssql's Cython tds_version validator, paired with Sybase-safe DEFAULT_SYBASE_CONN_PROPERTIES (SET QUOTED_IDENTIFIER ON; SET ANSINULL ON; SET TEXTSIZE 2147483647;) and autocommit=True (supports_transactional_ddl = False, avoiding ASE Error 2762).
    • ASE error code mapping (2601/2627, 546/547, 233/515, 548, 1205, 12205, 102/156/11021, 207/208, 4002/18456/20002, 20047/2762/1105, 247/220/168/3606, 10330/10331/10332) to canonical sqlspec.exceptions.
    • SybaseStore (sqlspec.adapters.sybase.litestar), SybaseSyncEventQueueStore (sqlspec.adapters.sybase.events), and SybaseADKStore / SybaseADKMemoryStore (sqlspec.adapters.sybase.adk).

SAP SQL Anywhere Adapter (sqlspec.adapters.sqlanywhere)

  • Added:
    • SqlanywhereConfig, SqlanywhereDriver, SqlanywhereConnectionPool, SqlanywhereCursor, SqlanywhereExceptionHandler, SqlanywhereSyncDataDictionary, and SqlanywhereSyncMigrationTracker over sqlanydb (ParameterStyle.QMARK).
    • Connection parameter normalization mapping friendly keys (user/username, password, database, server/engine, database_file, host + port) to sqlanydb.connect keywords (uid, pwd, dbn, eng, dbf, host="host:port" with default port 2638).
    • SQLSTATE and negative SQL Anywhere SQLCODE (-193/-196, -194/-198, -195, -209/-1091, -210/-306/-307/-684, -131, -141/-143, -100/-103/-832, -157/-158, -121) exception mapping.
    • SqlanywhereStore (sqlspec.adapters.sqlanywhere.litestar), SqlanywhereSyncEventQueueStore (sqlspec.adapters.sqlanywhere.events), and SqlanywhereADKStore / SqlanywhereADKMemoryStore (sqlspec.adapters.sqlanywhere.adk).

Packaging & MyPyC Compilation (pyproject.toml, tools/scripts/)

  • Changed:
    • Registered sybase = ["pymssql"] and sqlanywhere = ["sqlanydb>=1.0.14"] optional dependency extras and pytest markers in pyproject.toml.
    • Enrolled sqlspec/dialects/sybase/{_generators,_parsers,_transforms}.py, sqlspec/data_dictionary/dialects/{sybase,sqlanywhere}/config.py, and sqlspec/adapters/{sybase,sqlanywhere}/{core,pool}.py in the MyPyC compiled surface (pyproject.toml, tools/scripts/mypyc_inventory.py, tools/scripts/mypyc_smoke.py).

cofin added 22 commits October 30, 2026 23:39
…arding

- register integration database plugins dynamically inside pytest_configure
- update DuckDB sequence resynchronization to use CTE and schema-qualified range calculation
- add shared apply_wallet_ssl_context helper for OracleDB with config_dir fallback and unit tests
- rewrite Db2 ADBC non-query statement classification using public SQL statement properties
- clean up TypedDict docstrings for AlloyDB columnar scan and drop unsupported psqlpy pool param
- decouple ODBC DSN assertion from pinning SQLite fallback
- centralize module resolution in tests/fixtures/modules.py across test conftests
- separate collection hook tests into test_collection_groups.py and remove legacy test path guards
- rename MySQL ADK residual test suite to test_adk_store.py and narrow FK integrity check
- update Makefile integration and coverage targets to execute database shards sequentially
- adjust CI integration job timeout to 45 minutes with a 35-minute watchdog
- upgrade dependencies via make upgrade and point pytest-databases to test/clientless-all branch
@cofin
cofin marked this pull request as ready for review October 31, 2026 00:53
@cofin
cofin force-pushed the research_sybase_adbc_driver branch from a2f4074 to 3f9c0f7 Compare October 31, 2026 01:53
@cofin
cofin changed the base branch from main to pytest_databases_clientless_research October 31, 2026 01:54
…straint check

- update test_mysql_adk_owner_id_fk_constraint_isolation_and_cascade to catch driver IntegrityError exceptions
- fix frozenset set difference typing in postgres and cockroach data dictionaries
@cofin
cofin force-pushed the pytest_databases_clientless_research branch from 49c05ab to 7945fc6 Compare October 31, 2026 02:14
@cofin
cofin force-pushed the research_sybase_adbc_driver branch from 3f9c0f7 to 74fc6be Compare October 31, 2026 02:19
@codecov-commenter

codecov-commenter commented Oct 31, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 54.29279% with 1661 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.57%. Comparing base (0e80691) to head (113c941).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
sqlspec/adapters/sybase/adk/store.py 32.49% 289 Missing and 6 partials ⚠️
sqlspec/adapters/sqlanywhere/adk/store.py 29.67% 279 Missing and 3 partials ⚠️
sqlspec/adapters/sqlanywhere/driver.py 44.00% 107 Missing and 5 partials ⚠️
sqlspec/adapters/sybase/driver.py 52.31% 97 Missing and 6 partials ⚠️
sqlspec/adapters/sybase/litestar/store.py 31.08% 101 Missing and 1 partial ⚠️
sqlspec/adapters/sqlanywhere/litestar/store.py 36.15% 83 Missing ⚠️
sqlspec/adapters/sybase/core.py 57.65% 60 Missing and 23 partials ⚠️
sqlspec/adapters/sybase/pool.py 53.89% 70 Missing and 7 partials ⚠️
sqlspec/adapters/sqlanywhere/core.py 65.44% 47 Missing and 19 partials ⚠️
sqlspec/adapters/sqlanywhere/pool.py 64.74% 47 Missing and 8 partials ⚠️
... and 17 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #870      +/-   ##
==========================================
- Coverage   80.67%   80.57%   -0.11%     
==========================================
  Files         527      563      +36     
  Lines       95524    99315    +3791     
  Branches    18704    19155     +451     
==========================================
+ Hits        77066    80023    +2957     
- Misses      13582    14294     +712     
- Partials     4876     4998     +122     
Flag Coverage Δ
cloud-adbc-storage 31.22% <1.78%> (?)
integration 56.30% <2.09%> (+0.45%) ⬆️
mssql-db2 32.65% <1.76%> (?)
mysql-mariadb-embedded 38.41% <1.98%> (?)
oracle 31.78% <1.78%> (?)
postgres-cockroach 40.67% <2.06%> (?)
py3.11 79.15% <54.29%> (-0.87%) ⬇️
py3.12 79.15% <54.29%> (-0.87%) ⬇️
py3.13 79.15% <54.29%> (-0.87%) ⬇️
py3.14 79.96% <53.46%> (+5.96%) ⬆️
unit 73.73% <54.29%> (-0.80%) ⬇️

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

Files with missing lines Coverage Δ
sqlspec/adapters/sqlanywhere/__init__.py 100.00% <100.00%> (ø)
sqlspec/adapters/sqlanywhere/adk/__init__.py 100.00% <100.00%> (ø)
sqlspec/adapters/sqlanywhere/events/__init__.py 100.00% <100.00%> (ø)
sqlspec/adapters/sqlanywhere/litestar/__init__.py 100.00% <100.00%> (ø)
sqlspec/adapters/sybase/__init__.py 100.00% <100.00%> (ø)
sqlspec/adapters/sybase/adk/__init__.py 100.00% <100.00%> (ø)
sqlspec/adapters/sybase/events/__init__.py 100.00% <100.00%> (ø)
sqlspec/adapters/sybase/litestar/__init__.py 100.00% <100.00%> (ø)
sqlspec/builder/_merge.py 82.36% <100.00%> (ø)
sqlspec/data_dictionary/_registry.py 100.00% <ø> (ø)
... and 33 more

... and 55 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 added this pull request to stack #873 October 31, 2026 04:44
@cofin
cofin removed this pull request from stack #873 October 31, 2026 04:44
Base automatically changed from pytest_databases_clientless_research to main October 31, 2026 04:45
Signed-off-by: Cody Fincher <204685+cofin@users.noreply.github.com>
@cofin
cofin merged commit e7f6a78 into main Oct 31, 2026
4 checks passed
@cofin
cofin deleted the research_sybase_adbc_driver branch October 31, 2026 04:46
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