Skip to content

Add Exasol ODBC dialect#386

Open
mkcorneli wants to merge 1 commit intoposit-dev:mainfrom
mkcorneli:add-exasol-odbc-dialect
Open

Add Exasol ODBC dialect#386
mkcorneli wants to merge 1 commit intoposit-dev:mainfrom
mkcorneli:add-exasol-odbc-dialect

Conversation

@mkcorneli
Copy link
Copy Markdown

@mkcorneli mkcorneli commented Apr 27, 2026

Summary

Adds an ExasolDialect and an exasol branch in OdbcReader::detect_dialect(). Lets the existing generic OdbcReader work correctly against Exasol with no new reader implementation — same scope as the existing Snowflake dialect.

Closes #330. Related context on a possible Arrow/ADBC path: #331 (this PR uses the ODBC path; ADBC remains open).

Connection string:

odbc://Driver=Exasol;EXAHOST=host:8563;EXAUID=user;EXAPWD=pass

Why each override exists

The 11 method overrides in src/reader/exasol.rs each fix a default that is rejected or wrong on Exasol. Override SQL was verified against exasol/docker-db:2025.2.0-arm64dev.0 by a probe-test campaign maintained on a sibling branch (test module src/reader/exasol_probes.rs, runs only when EXASOL_TEST_URL is set; not included in this PR to keep the diff focused).

Override Why default fails Replacement
string_type_name Bare VARCHAR rejected in DDL VARCHAR(2000000) (Exasol's practical max)
time_type_name TIME '01:02:03' raises Feature not supported: SQL-Type TIME VARCHAR(32), mirroring SqliteDialect precedent
sql_greatest / sql_least ANSI CASE fallback works but is verbose Native GREATEST / LEAST
sql_date_literal Default INTERVAL N DAY (unquoted) rejected by Exasol's parser ADD_DAYS(DATE '1970-01-01', N)
sql_datetime_literal MICROSECOND is not a valid Exasol interval subtype ADD_SECONDS(TIMESTAMP '...', secs) — see TIMESTAMP-precision note below
sql_time_literal Both TIME literal and NANOSECOND interval unsupported 'HH:MM:SS.uuuuuu' ISO-8601 string (matches time_type_name storage contract)
sql_list_catalogs / _schemas / _tables / _columns Exasol has no information_schema views SYS.EXA_SCHEMAS, SYS.EXA_ALL_TABLES, SYS.EXA_ALL_COLUMNS

Note on TIMESTAMP precision: Exasol's TIMESTAMP truncates to millisecond precision; sub-millisecond fractional input is silently zeroed by the database. This cannot be worked around at the dialect layer — documented at the top of src/reader/exasol.rs.

Catalog tier: Exasol treats schemas as the top tier. sql_list_catalogs surfaces every schema as a top-level catalog; sql_list_schemas ignores its catalog argument.

Out of scope

  • Native ExasolReader. Generic OdbcReader works fine; no need for a new reader implementation.
  • ADBC / exarrow-rs path. Tracked separately in Arrow support in future plans? #331.
  • Pre-existing OdbcReader Int32 buffer issue for DECIMAL with precision < 10. Affects GREATEST / LEAST result consumption when the operands are DECIMAL columns. The dialect's emitted SQL is correct; the issue is in the result-binding layer in src/reader/odbc.rs. Will be filed as a separate issue.

Test plan

  • cargo test --features odbc --lib — 11 new dialect tests + extended detection test pass; full lib suite regression-clean (1335 tests pass before + after).
  • cargo fmt --all -- --check — clean.
  • cargo clippy --features odbc --lib — no new warnings (one pre-existing warning in src/execute/scale.rs:254 is unrelated, introduced in No-polars in ggsql #350).
  • Live-Exasol smoke test against exasol/docker-db:2025.2.0-arm64dev.0 (via the sibling probe-test campaign, not part of this PR).

Introduce ExasolDialect implementing SqlDialect, plus an `exasol` branch
in OdbcReader's detect_dialect(). Lets the existing generic OdbcReader
work correctly against Exasol with no new reader implementation —
parallel in scope to the existing Snowflake dialect.

Connection string:
  odbc://Driver=Exasol;EXAHOST=host:8563;EXAUID=user;EXAPWD=pass

The 11 method overrides in src/reader/exasol.rs were each chosen to fix
a default that is rejected or wrong on Exasol. Override SQL was verified
against exasol/docker-db:2025.2.0-arm64dev.0 by a separate probe-test
campaign maintained on a sibling branch (run with EXASOL_TEST_URL set;
test module: src/reader/exasol_probes.rs, not included in this PR).

Behavior justifications:

- string_type_name -> VARCHAR(2000000)
  Bare VARCHAR is rejected in DDL on Exasol; an explicit length is
  required. 2_000_000 is Exasol's practical max — a smaller N risks
  truncating Polars/Arrow string columns at register() time.

- time_type_name -> VARCHAR(32)
  Exasol has no SQL TIME type. `TIME '01:02:03'` raises
  `Feature not supported: SQL-Type TIME`. VARCHAR-for-TIME mirrors the
  SqliteDialect precedent at src/reader/sqlite.rs:45-47; consumers do a
  Polars/Arrow-side reparse for Vega-Lite temporal axes.

- sql_greatest / sql_least
  Exasol has native GREATEST/LEAST; emit those instead of the ANSI CASE
  fallback for shorter, more readable SQL.

- sql_date_literal -> ADD_DAYS(DATE '1970-01-01', N)
  Default `INTERVAL N DAY` (unquoted) is rejected by Exasol's parser.
  ADD_DAYS sidesteps interval syntax entirely and pairs naturally with
  ADD_SECONDS below.

- sql_datetime_literal -> ADD_SECONDS(TIMESTAMP '...', secs)
  Default `INTERVAL N MICROSECOND` fails — MICROSECOND is not a valid
  Exasol interval subtype (Exasol supports YEAR/MONTH/DAY/HOUR/MINUTE/
  SECOND only). Note: Exasol's TIMESTAMP itself truncates to millisecond
  precision; sub-millisecond input is silently zeroed by the database.
  This is documented in the file's top doc-block.

- sql_time_literal -> 'HH:MM:SS.uuuuuu' string
  Default emits TIME literal + NANOSECOND interval, both unsupported.
  Time travels as VARCHAR(32) per time_type_name, so emitting an
  ISO-8601 string matches the storage contract.

- sql_list_catalogs / sql_list_schemas / sql_list_tables /
  sql_list_columns
  Exasol has no information_schema views; the ANSI defaults raise
  `object not found`. Substituted with SYS.EXA_SCHEMAS, SYS.EXA_ALL_TABLES,
  and SYS.EXA_ALL_COLUMNS. Catalog filters are ignored (Exasol has no
  catalog tier above schemas). Single quotes in schema/table arguments
  are escaped via doubling.

There is a separate, pre-existing OdbcReader Int32 buffer issue that
affects DECIMAL-returning function results (e.g. GREATEST/LEAST over
DECIMAL columns with precision < 10). The dialect's emitted SQL is
correct; the issue is in the result-binding layer and will be filed as
its own upstream issue.

Tests (all in src/reader/exasol.rs and src/reader/odbc.rs):

- 11 dialect tests, one per override, asserting exact SQL strings.
- Single-quote escaping verified for sql_list_tables / sql_list_columns.
- test_detect_dialect extended with `Driver=Exasol;...` recognition
  (existing Snowflake / PostgreSQL / generic-fallback assertions
  preserved).
- New test_detect_dialect_exasol_curly_form covers `Driver={Exasol};`
  and uppercase `DRIVER={EXASOL};` mixed-case forms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

Feature Request: Exasol database connection (co-developed)

1 participant