Skip to content

The engine runs a DuckDB 2.0 preview (T-467) - #335

Open
chasers wants to merge 4 commits into
mainfrom
duckdb-2-preview
Open

chasers wants to merge 4 commits into
mainfrom
duckdb-2-preview

Conversation

@chasers

@chasers chasers commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Tracker: T-467 (groundwork for T-395).

Pins the adbc driver to DuckDB 2.0.0-alpha38195, the v2.0-cyanoptera preview build that has httpfs, json, ducklake, aws and postgres published for every platform (the newer alpha41396 has none), and adapts the engine to what 2.0 changed:

  • Arrow null columns. A bare NULL select column now arrives as Arrow's null type with no type, data or length, which adbc 0.12.1 cannot decode. Engine.Result reads it as nils, as many as the batch's other columns hold.
  • The new parser's AST. Engine.Ast gains cast_type/1, constant/1 and arguments/1, which read both the 1.5 shape and 2.0's (unbound TYPE nodes for a cast's target, literal {kind, text} constants in later builds, named function arguments, limit_type: PERCENTAGE). The materialized gates, the pruner, SingleTable and the decomposer read through them.
  • EXPLAIN. Plans are box-drawn and mixed-case; EXPLAIN ANALYZE over the C API needs enable_profiling, which the runner now sets with the job's per-connection statements before the lockdown seals the configuration.
  • Lockdown allows DuckLake's own SETs. The preview's DuckLake extension runs three SETs on its metadata connection at every transaction, which lock_configuration refused, invalidating the transaction and failing every query on a job engine or partial worker (SQLite and Postgres metadata alike). Both lockdowns now set allowed_configs to exactly those three options before locking; none reaches outside the engine and the list locks with the rest.
  • One test tagged out. epoch_ms past the timestamp range is an INTERNAL error in alpha38195 that invalidates the database instead of yielding NULL under TRY; alpha41396 has the fix. The writer test for that case carries :duckdb_preview_internal_error and is excluded by default.

Catalogs this build creates are DuckLake format 1.1-dev1, which the DuckLake extension built for DuckDB 1.5.3 refuses to attach; a format 1.0 catalog attaches unchanged under the preview and a 1.5.3 node reads it back afterwards, verified with SQLite metadata. Three versions are involved: the DuckDB engine, the DuckLake extension built for it, and the catalog format stamped in the metadata database. docs/deployment.md carries the upgrade note.

The exact build is mirrored as the prerelease duckdb-v2.0.0-alpha38195 on this repo, since DuckDB's preview URL redirects to an expiring blob and moves nightly.

CI: every job green, including the integration and kind cluster suites. Locally: mix precommit (1847 tests), mix ci and mix dialyzer green, and the previously failing integration files verified against a local Postgres 17.11.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NBmgYoYciJzbcxNYA9Z4d6

Chase Granberry and others added 4 commits September 11, 2026 22:57
…irror of the build, with the AST readers taking the new parser's shapes (T-467)

DuckDB 2.0.0 is scheduled for 2026-10-21. The v2.0-cyanoptera preview path at
artifacts.duckdb.org redirects to an expiring Actions blob and advances with
every nightly, and only some builds have extensions published for them, so the
driver is pinned to alpha38195 — the build with httpfs, json, ducklake, aws and
postgres for every platform — mirrored as the prerelease
duckdb-v2.0.0-alpha38195 on this repository.

What the preview changed and what took it:

- A bare NULL select column arrives over Arrow as the null type, with no type,
  data or length, which adbc 0.12.1 cannot decode; Engine.Result reads it as
  nils, as many as the batch's other columns hold.
- The PEG parser's json_serialize_sql: a CAST's target is an unbound TYPE node
  (cast_type.type_info.expr, or type_expr in later builds) carrying the name as
  written; a CONSTANT is a literal of kind and text in later builds; a
  FUNCTION's children are arguments; LIMIT n% is a LIMIT_MODIFIER with
  limit_type PERCENTAGE. Engine.Ast gains cast_type/1, constant/1 and
  arguments/1 reading both shapes, and the materialized gates, the pruner,
  SingleTable, and the decomposer read through them.
- EXPLAIN renders box-drawn, mixed-case plans, and EXPLAIN ANALYZE over the
  C API answers "profiling is disabled" unless enable_profiling is set; the
  runner sets it with the job's other per-connection statements, ahead of the
  lockdown that seals the configuration.
- epoch_ms of a value past the timestamp range is an INTERNAL error in this
  build, which invalidates the database instead of yielding NULL under TRY; the
  writer test for that case is tagged out (:duckdb_preview_internal_error)
  until the pin moves past alpha41396, where it is fixed.

Catalogs this build creates are DuckLake 1.1-dev1, which the 1.5.3 extension
refuses; a 1.0 catalog attaches unchanged and 1.5.3 reads it back, so an
existing deployment keeps its rollback. The deployment doc carries the note.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBmgYoYciJzbcxNYA9Z4d6
…talog format apart from the engine version

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBmgYoYciJzbcxNYA9Z4d6
… connection, so job engines and partial workers read the lake on DuckDB 2.0 (T-467)

The integration and kind-cluster jobs failed on every query with
"TransactionContext Error: Failed to query snapshot ... Current transaction
is aborted (please ROLLBACK)", on SQLite and Postgres metadata alike. The
DuckLake extension built for the 2.0 preview runs three SETs on its metadata
connection at every transaction — pg_experimental_filter_pushdown off for
Postgres, sqlite_disable_multithreaded_scans on for SQLite, and
current_transaction_invalidation_policy right after BEGIN. A locked
configuration refuses them, and the refused SET after the BEGIN invalidates
the transaction, so the snapshot query that follows fails. Bisected to
`SET lock_configuration = true` alone, on the CLI as well as through ADBC;
1.5.3's extension predates the SETs.

DuckDB's allowed_configs lists options exempt from the lock, so both
lockdowns (Runner and PartialWorker) now set it to exactly those three
before locking, through Catalog.DuckLake.allowed_configs_statement/0. None
of the three reaches outside the engine, and the list locks with the rest.

The two DuckLake query-path tests that read EXPLAIN ANALYZE enable profiling
on their engine first, as the runner does for an analyze job.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBmgYoYciJzbcxNYA9Z4d6
… anywhere in a cast target is refused, an all-NULL batch is an error, the lockdowns share one ordered builder, and the overflow test is skipped by version

Findings from the review of the 2.0 preview branch, each fixed at its root:

- The decomposer accepted `LIMIT n%` — 2.0 folds it into LIMIT_MODIFIER with
  limit_type PERCENTAGE — and rendered it as `LIMIT n`; gate_modifiers refuses
  it as :percent_limit. A DECIMAL limit's serialized value is DuckDB's unscaled
  integer, so `LIMIT 1.5` read as 15; Ast.integer/1 accepts only the integer
  types, and SingleTable and the decomposer read limits through it.
- The zoned-type gate read only a cast's top-level type, so a TIMESTAMPTZ
  nested in a STRUCT or list target passed; it now reads every TYPE node in a
  cast target through Ast.type_name/1.
- A batch of nothing but untyped NULL columns carries no row count and read as
  empty; Result.from_adbc/2 answers {:error, :unsized_batch}, from_adbc/1
  raises, and a connection reports it as an error.
- Ast.cast_type/1 answered "UNBOUND" for an unbound cast without a TYPE node;
  the TYPE heads guard their name and the id clause excludes UNBOUND. The
  readers are exercised against the pinned parser in ast_test, so a pin that
  moves to a spelling they do not read fails there.
- The Runner and PartialWorker lockdown lists were near-clones whose ordering
  (allowed_configs before lock_configuration) held by position; both now call
  QueryService.Lockdown.statements/3, and Identifier.sql_list/1 replaces four
  copies of the list renderer. The runner's explain goes back to one query with
  a prefix.
- The writer's overflow test is skipped by pinned version rather than excluded
  by a static tag, so it runs again when the pin moves. The deployment note
  says what the fault does in production, and names the driver mirror as the
  trust root, with each asset's SHA-256 in the configuration doc.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBmgYoYciJzbcxNYA9Z4d6
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.

1 participant