DuckDB 2.0 Is on the Calendar. PyPI Is Still 1.5.6

DuckDB's calendar lists 2.0.0 for Oct 21, tentative. Szárnyas's Vera TPC-H used 1.6.0.dev379. DataFusion still turns NaN Parquet into a constant. A date is not a wheel.

DuckDB’s release calendar still shows 2.0.0 on 2026-10-21 and 2.0.1 on 2026-11-18. The same page says the dates are tentative and maintainers may push them to keep quality. That is a planning table. It is not pip install duckdb==2.0.0.

This morning PyPI’s JSON for the duckdb project still answered 1.5.6, uploaded 2026-09-28. GitHub’s releases list stopped at v1.5.6. Gábor Szárnyas on October 16 was already explicit: at the time of writing, v2.0 was not released, so the Vera TPC-H run used a preview build named 1.6.0.dev379. Three facts. Mix them and you will bump a production lockfile because a calendar cell turned orange.

We already filed DuckDB reading Parquet while pandas stayed in the tools list and Polars 2.0 collecting on streaming. This week the Python data stack’s drama is a version that is allowed to slip.

Tentative is the only word that matters

The calendar’s upcoming table is two rows. 21 October: 2.0.0. 18 November: 2.0.1. Semantic versioning note above: larger features in minor versions, patches mostly bugfixes. 2.0.0 is not a minor. Treat it like a storage-format and client-ecosystem event until the announcement post exists. It does not exist in the sources in hand.

LTS, from 1.4.0 onward, is every other version, community support currently a year. 1.5.0 Variegata landed 2026-03-09 with EOL 2027-03-01. 1.5.6 is a 28 September bugfix on that line. 1.4.5 LTS is the parallel long line. If your job is a warehouse notebook other people rerun, 1.4.x LTS or a pinned 1.5.6 is the boring answer on a day when 2.0.0 is a maybe.

September 2’s alpha note is older than a week. Use it only as background: Amsterdam branched v2.0-cyanoptera, feature freeze, projected second half of October, 2.0.0-dev on the install page. Freeze in September is not a wheel on the 21st. Clients and extensions wait on duckdb/duckdb. If your community extension did not set ref_next, you are on whatever SHA they freeze at release. That sentence is why you do not pip install -U duckdb in a DAG at 09:00.

A calendar with no publication date of its own is a living page. Recheck it. Recheck PyPI. Recheck the GitHub tag. If all three disagree, believe the tag.

1.5.6’s own announcement, 28 September, already pointed at Windows work landing in v2.0 and at regressions still being chased. That post is older than this week’s window. The useful residue is: even the maintainers did not tell you to take a Windows 6× number as general. Szárnyas’s Vera numbers are a different machine. Neither is your pin.

1.6.0.dev379 is a TPC-H pin, not yours

Szárnyas’s October 16 post is a hardware story that happens to name the preview. NVIDIA Vera: 88 Olympus cores, spatial multithreading, up to 1.2 TB/s memory bandwidth, arm64, so DuckDB’s regular build ran without a port. Collaboration with NVIDIA. Aimed at agentic workloads in their pitch. Your laptop is not 88 cores.

TPC-H scale factor 1,000, full benchmark, not only the read queries. Both boxes: Ubuntu 26.04, 768 GB RAM, one NVMe SSD 3+ TB, ext4. x86 baseline Intel Xeon 6 versus Vera. Harness duckdb-tpch. SET threads = 88. Warmup discarded. Three complete runs, median QphH@Size:

Runx86 QphH@SizeVera QphH@Size
12,120,588.823,073,292.99
22,022,795.013,131,306.27
32,109,782.193,052,102.87
Median2,109,782.193,073,292.99

That is vendor-adjacent hardware, vendor-adjacent binary. Do not put 3.07 million QphH on a MacBook. Do not put it on 1.5.6. The build string is 1.6.0.dev379. If your CI installs that from a nightly extra, you are reproducing a blog, not a release.

Vera running unmodified arm64 DuckDB is the portability note. Spark N1X Windows ARM is a different mess; this is Linux arm64 on a server CPU. If you were waiting for 2.0 to “get ARM,” the regular binary already ran. 2.0 is not the ARM story here. 2.0 is the version they would not print because it had not shipped.

Dataset generation used tpchgen-cli, a parallel Rust rewrite of dbgen, plus the old generator for update and query streams. That is how you rebuild their disk. It is not how you validate a duckdb wheel on Python 3.13. Different jobs. numactl showed up in their setup as CPU and memory binding. If you rerun TPC-H on a two-socket box without that bind, you are not rerunning their median. You are rerunning NUMA.

DataFusion still eats NaNs. DuckDB does not, on this file

Apache DataFusion issue 26490, opened October 19 by hanke580: a plain SELECT of a Parquet float column returns the wrong values. Parquet writers leave NaN out of min/max statistics; the spec requires that. A column chunk with one finite value plus some NaNs therefore has min == max == x and null_count == 0 even though the column is not constant. DataFusion treats it as constant and substitutes the statistic. NaN rows become x. isnan counts 0. No error.

They reproduced on DataFusion main 290ad15 (October 18) with datafusion-cli 55.1.0, and on 91cd7ff (October 15). pyarrow 26.0.0 wrote files. Expected: SELECT * returns NaN, -8.6e-27, matching pyarrow, DuckDB, and Polars on the same file. Turning datafusion.execution.collect_statistics off restores the NaNs. Files without statistics also read correctly. [NaN, 2.0, 3.0] (min ≠ max) and [NaN, NaN] (no min/max) are fine. The failure is specifically min==max hiding NaN.

The CLI transcript in the issue is the part to steal into a test. SELECT * FROM 'nan.parquet' prints -8.6e-27 twice. count() FILTER (WHERE isnan(c0)) prints 0. nan_f32.parquet prints 1.5 for the NaN row. nan_nostats.parquet prints NaN then -8.6e-27. Same bytes, statistics on or off, different table. If your notebook “helpfully” enables collect_statistics, you inherited the lie.

Both column orders were affected. Files with TYPE_DEFINED_ORDER and no nan_count still fail. DataFusion/arrow-rs 60 writes IEEE_754_TOTAL_ORDER with nan_count and still leaves NaN out of min/max. The writer and the reader disagree with the file they just made. That is not a DuckDB 2.0 talking point. That is a Parquet stats talking point.

DataFusion’s own COPY of (NaN, 5.0) writes min = max = 5.0, null_count = 0, nan_count = 1 under IEEE_754_TOTAL_ORDER, then reads the file back as 5.0, 5.0. It ignores a nan_count it wrote. Sensor and ML feature columns that are “one default plus NaNs” are the shape they flag. If you imputed missing sensor ticks as NaN and one valid float, DataFusion can report a constant column. Your model then trains on a lie.

This is why you do not swap engines because a calendar said 2.0. DuckDB is in the “reads it correctly” column on this bug. DataFusion is in the “silent constant” column. Polars 2.0 from October 6 is also in the correct column for this file. If your pipeline is DataFusion SQL over Parquet floats, 26490 is the ticket, not DuckDB’s version cell.

Related older issues they cite: pruning with NaN (#15812) and a draft PR (#24804). Open as of the issue body. Do not claim it is fixed. Do not claim DuckDB 2.0 is what saves you. 1.5.6 already reads the file in their account.

pandas is not in the issue’s “reads it correctly” sentence. pyarrow, DuckDB, Polars are. If your stack is pandas via pyarrow parquet, you may still be fine on read. Do not assume. Round-trip a NaN-plus-constant column in the engine you actually run. We already said pandas does not need to go extinct. It also does not get a free pass on a bug it was not named in.

We keep DuckLake as SQL in the catalog in a different drawer. Iceberg JSON versus SQL catalogs do not decide NaN. Statistics substitution does. DuckLake’s own calendar, for the spec, listed 2026-10-21 as spec 1.1 / extension 2.0 in a search snippet this week. That is a different 2.0. Do not upgrade the lakehouse extension because the engine calendar is orange. Check the compatibility matrix against the DuckDB version you actually pinned.

What to pin today

If 2.0.0 is a GitHub tag and a PyPI file by the time you read this, read the announcement, then try it on a throwaway database file. Storage-format changes are how people learn the hard way. The September freeze post did not, in the extract we have, promise that 1.5.6 can open a file 2.0.0 just rewrote. Do not find out on the warehouse volume.

If the tag is still missing, stay on 1.5.6 or 1.4.5 LTS. Write the pin. Write the reason: calendar was tentative, Vera used a dev build, DataFusion taught you this week that Parquet stats lie.

Szárnyas discarded the warmup run. Do the same with a new major. First open of a .duckdb file after a major bump is not the run you publish. Copy the file. Open the copy. Run the query you actually ship. If the copy grows a new header, keep the original on 1.5.6 until you have a rollback.

If you must taste Cyanoptera, use the install page’s 2.0.0-dev on a laptop with a copy of the data. File bugs with a repro, as the September note asked. That is community work. It is not a migration. Amsterdam asked for eyeballs in September. Eyeballs on a laptop are cheaper than eyeballs on a Tuesday DAG.

Python: pip index versions duckdb before you merge. Nightlies named 2.0.0.dev261019... showed up on PyPI in this run as dev wheels. Dev is not 2.0.0. A DAG that tracks latest will ingest a dev wheel if you let it. Don’t. ==1.5.6 is a sentence. >=1.5 is how the nightlies get in.

Vera’s 3.07 million QphH is a median of three runs on 768 GB and 88 threads. Quote it as a hardware blog. Quote 1.6.0.dev379 next to it. Quote 1.5.6 next to your lockfile. 88 threads is SET threads = 88. Your notebook default is not 88. Do not compare a SF1000 server median to a 10 GB parquet on a laptop and call it 2.0.

A calendar date is a promise the maintainers already reserved the right to break. PyPI is the artifact. DataFusion issue 26490 is the reminder that the engine you did not upgrade can still be the one that lies about NaN. DuckDB, today, is the one that did not. That is enough news for a Tuesday without a 2.0.0 wheel.

Spread The Article

Share this guide

Send this article to your network or keep a copy of the direct link.

X Facebook LinkedIn Reddit Telegram

Discussion

Leave a comment

No comments yet

Be the first to start the conversation.