Skip to content

Add Exasol ODBC dialect - #386

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

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

Conversation

@mkcorneli

@mkcornelimkcorneli commented Apr 27, 2026

Copy link
Copy Markdown

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).

OverrideWhy default failsReplacement
string_type_nameBare VARCHAR rejected in DDLVARCHAR(2000000) (Exasol's practical max)
time_type_nameTIME '01:02:03' raises Feature not supported: SQL-Type TIMEVARCHAR(32), mirroring SqliteDialect precedent
sql_greatest / sql_leastANSI CASE fallback works but is verboseNative GREATEST / LEAST
sql_date_literalDefault INTERVAL N DAY (unquoted) rejected by Exasol's parserADD_DAYS(DATE '1970-01-01', N)
sql_datetime_literalMICROSECOND is not a valid Exasol interval subtypeADD_SECONDS(TIMESTAMP '...', secs) — see TIMESTAMP-precision note below
sql_time_literalBoth TIME literal and NANOSECOND interval unsupported'HH:MM:SS.uuuuuu' ISO-8601 string (matches time_type_name storage contract)
sql_list_catalogs / _schemas / _tables / _columnsExasol has no information_schema viewsSYS.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>
@thomasp85

Copy link
Copy Markdown
Collaborator

Thanks for this. Sorry this PR has stayed unresponded for so long. We are currently working on a bunch of non-reader features that we want to get in before we handle the various reader PRs that have been accumulating

Sign up for freeto 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)

2 participants

@mkcorneli@thomasp85