{"article":{"slug":"announcing-duckdb-2-0-0-lts-cinnamon-teal","title":"Announcing DuckDB 2.0.0 LTS “Cinnamon Teal”","subtitle":null,"summary":"The DuckDB team announces DuckDB 2.0.0 LTS, its biggest feature release so far: a polished Quack protocol for DuckDB as a server, asynchronous I/O, a first-class VARIANT type, agent mode, triggers, an extensible PEG-based SQL parser, plus breaking changes and migration notes.","content_type":"announcement","language":"en","canonical_url":"https://duckdb.org/2026/10/21/announcing-duckdb-200","author":{"name":"The DuckDB team","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"DuckDB","url":"https://duckdb.org/","listing_slug":null,"listing":null},"topics":[{"name":"Databases","slug":"databases","url":"https://listedarticles.com/topics/databases"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"},{"name":"Announcements","slug":"announcements","url":"https://listedarticles.com/topics/announcements"},{"name":"Data Engineering","slug":"data-engineering","url":"https://listedarticles.com/topics/data-engineering"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":2753,"reading_minutes":12,"published_at":"2026-10-21T00:00:00.000Z","added_at":"2026-10-21T17:16:26.339Z","updated_at":"2026-10-21T17:16:26.339Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/announcing-duckdb-2-0-0-lts-cinnamon-teal","markdown_url":"https://listedarticles.com/articles/announcing-duckdb-2-0-0-lts-cinnamon-teal.md","example":false,"citation":"The DuckDB team, DuckDB. \"Announcing DuckDB 2.0.0 LTS “Cinnamon Teal”.\" 21 Oct 2026. https://duckdb.org/2026/10/21/announcing-duckdb-200 (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://duckdb.org/2026/10/21/announcing-duckdb-200"},"body_markdown":"# Announcing DuckDB 2.0.0 LTS “Cinnamon Teal”\n\n\nThe DuckDB team\n\n![](https://duckdb.org/images/blog/thumbs/duckdb-release-2-0-0.svg)\n\n*TL;DR: The DuckDB team is happy to announce that today we're releasing DuckDB version 2.0.0 LTS, codenamed “Cinnamon Teal” (Spatula cyanoptera). This is our biggest feature release so far, and it ships a polished version of the Quack protocol, asynchronous I/O, a first-class `VARIANT` type, agent mode, triggers, an extensible SQL parser and much more, built from close to 20,000 commits since DuckDB 1.5.*\n\nWe are proud to release DuckDB 2.0.0, codenamed “Cinnamon Teal” after the [*Spatula cyanoptera*](https://en.wikipedia.org/wiki/Cinnamon_teal), a strikingly reddish-brown duck found in the western Americas.\n\nWhile [DuckDB 1.0](https://duckdb.org/2024/06/03/announcing-duckdb-100.html) was all about stability and backward compatibility, DuckDB 2.0 is above all a feature release.\nIn this blog post, we summarize the highlights and then walk through the breaking changes and how to migrate.\nWe showcased many of the new features in [“A Preview of DuckDB v2.0”](https://duckdb.org/2026/08/17/duckdb-20-highlights.html), which covers each one in more depth.\nFor the complete release notes, see the [release page on GitHub](https://github.com/duckdb/duckdb/releases/tag/v2.0.0).\n\n> Tip To install the new version, please visit the [installation page](https://duckdb.org/install/).\n> Some clients, such as the CLI and Python, are already available. Other client libraries (e.g., Go, R, Java) and some extensions can take a few days to release due to the extra changes and review rounds required.\n\nWith this release, there are currently three DuckDB lines available.\nDuckDB 2.0 is a long-term support (LTS) release with support until at least October 2027.\nThe previous LTS release, version 1.4, is still supported until late November 2026.\nTo ensure a smooth transition and allow sufficient time for testing, we extended the support period of DuckDB 1.5 until March 2027, effectively also turning it into an LTS release.\nFor details, see the [release calendar](https://duckdb.org/release_calendar.html#lts-releases).\n\nTo find your way around the documentation for all of the new features, we also published a [“What's New in the Docs for 2.0”](https://duckdb.org/docs/2.0/whats_new_in_the_docs.html) page that links each feature below to its documentation.\n\n## [DuckDB as a Server](#duckdb-as-a-server)\n\nDuckDB has been an in-process database since day one.\nIn [DuckDB 1.5.3](https://duckdb.org/2026/05/20/announcing-duckdb-153.html), released in May, we introduced the Quack protocol that lets you run DuckDB in a traditional client-server setup, along with the [`quack` extension](https://duckdb.org/docs/2.0/core_extensions/quack.html), which implements DuckDB's native protocol, so any DuckDB process can [serve its databases](https://duckdb.org/docs/2.0/quack/overview.html#server-side-usage).\n\n### [Quack and `CONNECT`](#quack-and-connect)\n\nDuckDB 2.0.0 ships a polished version of the Quack protocol with substantial performance and stability improvements. It also introduces the new [`CONNECT` statement](https://duckdb.org/docs/2.0/sql/statements/connect.html) that allows attaching and switching to a remote database in a single command for better ergonomics:\n\n#### [DuckDB server](#duckdb-server)\n\n```\nCALL quack_serve(\n    token = 'my_token'\n);\n\nCREATE TABLE events\n    (date DATE, name VARCHAR);\n\nINSERT INTO events VALUES\n    ('2027-04-07',\n     'DuckCon #8');\n```\n\nquack:\n\n#### [DuckDB client](#duckdb-client)\n\n```\nATTACH 'quack:localhost'\n    AS qk (TOKEN 'my_token');\n\nCONNECT qk;\nSELECT * FROM events;\n\n┌────────────┬────────────┐\n│    date    │    name    │\n│    date    │  varchar   │\n├────────────┼────────────┤\n│ 2027-04-07 │ DuckCon #8 │\n└────────────┴────────────┘\n```\n\nFor simplicity, this example runs the server and the client on the same machine. By default, the server only listens on `localhost`. To accept connections from other machines, keep the server on `localhost` and put it behind a [TLS-terminating reverse proxy](https://duckdb.org/docs/2.0/quack/setup/reverse_proxy.html). The [`allow_other_hostname` option](https://duckdb.org/docs/2.0/quack/overview.html#starting-a-server), which exposes the server directly, is only meant for testing.\n\nThe `CONNECT` statement is not limited to Quack: it points your session at [any remote database that supports it](https://duckdb.org/docs/2.0/sql/statements/connect.html#supported-databases), including PostgreSQL and MySQL. For these, the new remote pushdown optimizer ships queries directly to the remote server instead of pulling tables over the wire.\n\n### [Quack in the CLI](#quack-in-the-cli)\n\nThe CLI also has shortcuts for setting up a Quack client-server pair: the new [`-serve` and `-connect` arguments](https://duckdb.org/docs/2.0/clients/cli/arguments.html#serving-and-connecting-to-remote-databases) start a Quack server or connect to one directly from the command line:\n\n#### [CLI server](#cli-server)\n\n```\nduckdb -serve\n```\n\n```\nCREATE TABLE t AS\n      SELECT 42 AS x;\n```\n\nquack:\n\n#### [CLI client](#cli-client)\n\n```\nduckdb -connect\n```\n\n```\nFROM t;\n\n┌───────┐\n│   x   │\n│ int32 │\n├───────┤\n│    42 │\n└───────┘\n```\n\n## [Performance](#performance)\n\n### [Asynchronous I/O](#asynchronous-io)\n\nDuckDB 2.0 introduces [asynchronous I/O](https://duckdb.org/docs/2.0/data/io.html) for reading Parquet and CSV files. The I/O layer now scales independently from the query processing layer, issuing reads in the background and keeping multiple requests in flight. This means far more parallelism for remote reads and dramatically faster queries on object storage such as [`s3://`](https://duckdb.org/docs/2.0/core_extensions/httpfs/s3api.html).\n\nWe described the design in detail in a [dedicated blog post](https://duckdb.org/2026/07/31/asynchronous-io.html).\nIn that post, we showed the network throughput over the course of a query with and without asynchronous I/O:\n\n![Network throughput](https://duckdb.org/images/blog/v20-release/sync-vs-async-network-throughput-light.svg)\n![Network throughput](https://duckdb.org/images/blog/v20-release/sync-vs-async-network-throughput-dark.svg)\n*Network throughput. Higher utilization and shorter runtime are better.*\n\n### [The `VARIANT` Type Becomes a First-Class Citizen](#the-variant-type-becomes-a-first-class-citizen)\n\nThe [`VARIANT` type](https://duckdb.org/docs/2.0/sql/data_types/variant.html) stores semi-structured, schema-less data such as JSON while keeping the performance of columnar execution.\nSo you can think of it as JSON on steroids: a `VARIANT` column can store differently shaped data in every row, but unlike JSON it is not a text format.\n\n[DuckDB 1.5 already introduced `VARIANT`](https://duckdb.org/2026/03/09/announcing-duckdb-150.html#variant-type), but DuckDB 2.0 fully integrates it. DuckDB 2.0 detects the common structure in your data and “shreds” it, so it compresses well and executes fast. The pipeline works end to end, from shredded execution straight out of storage to shredded `VARIANT` reading and writing for Parquet, backed by a family of [`variant_*` functions](https://duckdb.org/docs/2.0/sql/functions/variant.html). For example:\n\n```\nCREATE TABLE events (payload VARIANT);\nINSERT INTO events\nVALUES\n    ('{\"user\": {\"id\": 42, \"tags\": [\"a\", \"b\"]}}'::JSON::VARIANT),\n    ('{\"event\": \"click\", \"x\": 10, \"y\": 20}'::JSON::VARIANT);\n\nSELECT variant_type(payload), variant_keys(payload)\nFROM events;\n```\n\n```\n┌───────────────────────┬───────────────────────┐\n│ variant_type(payload) │ variant_keys(payload) │\n│        varchar        │       varchar[]       │\n├───────────────────────┼───────────────────────┤\n│ OBJECT                │ [user]                │\n│ OBJECT                │ [event, x, y]         │\n└───────────────────────┴───────────────────────┘\n```\n\n### [Agent Mode in the CLI](#agent-mode-in-the-cli)\n\nCoding agents such as Claude Code, Codex and Cursor increasingly drive the DuckDB CLI on their users' behalf. The 2.0 CLI detects when it is being run by an agent and switches to [agent mode](https://duckdb.org/2026/10/09/agent-mode.html): it prints compact Markdown tables instead of padded boxes, says clearly when a result was truncated, stops runaway queries early and reports errors as JSON.\n\nLong-running SQL queries report their expected cost prior to running, so the agent can decide whether to wait. In our experiments on TPC-H, agent mode cut the amount of DuckDB output the model had to read by 59%. You can turn it *on* for manual testing using the [`-agent` flag](https://duckdb.org/docs/2.0/clients/cli/agent_mode.html#activating-agent-mode) or instruct your agent to turn it *off* using the `-no-agent` flag.\n\n## [Extensibility](#extensibility)\n\n### [A Brand New SQL Parser](#a-brand-new-sql-parser)\n\nDuckDB has always used a parser derived from PostgreSQL's. In 2.0, our own modern, extensible [PEG-based parser](https://duckdb.org/2026/08/20/duckdb-20-peg-parser.html), which debuted as an opt-in feature in [DuckDB 1.5](https://duckdb.org/2026/03/09/announcing-duckdb-150.html#peg-parser), becomes the default. Extensions can now hook into the grammar itself (an idea we first explored in our [research on runtime-extensible parsers](https://duckdb.org/2024/11/22/runtime-extensible-parsers.html)), so expect extensions that expose entirely new SQL syntax. Error messages now come with precise source locations. For details, see the [PEG parser documentation](https://duckdb.org/docs/2.0/sql/peg_parser.html). The new parser is designed to be compatible with the old one, so you should not notice the swap. If you do, please file an issue. In the meantime, you can switch back to the old parser using the [`legacy_parser` extension](https://duckdb.org/docs/2.0/core_extensions/legacy_parser.html) by running `SET enable_legacy_parser = true;`.\n\nThe old parser had served DuckDB since its first commit in 2018. Its grammar was generated with YACC/Bison, which produces an LALR(1) parser, so as DuckSQL grew, even small additions could collide with existing rules and raise `shift/reduce` or `reduce/reduce` conflicts. A PEG grammar avoids this by trying alternatives in the order they are written, so adding syntax no longer risks breaking unrelated rules. Python itself moved to a PEG-based parser in [version 3.9](https://peps.python.org/pep-0617/) for the same flexibility.\n\n### [Write Extensions Once and Host Them Yourself](#write-extensions-once-and-host-them-yourself)\n\nDuckDB 2.0 ships a revamped [C API v2](https://duckdb.org/docs/2.0/clients/c/overview.html#c-api-v2), which is considerably more powerful than its predecessor and exposes components such as file systems and table functions to extensions. Before marking it as stable, we would like to test it in the field, so please give it a try and [send us your feedback](https://github.com/duckdb/duckdb/discussions/27525)!\n\nYou can also register and host your own trusted extension repositories, so an organization can host and sign its own extensions and have them install and load just like the built-in ones:\n\n```\nSET allow_extension_repositories = 'allowed';\n\nCREATE EXTENSION REPOSITORY my_repo\n    WITH PREFIX 'https://extensions.example.org';\n\nINSTALL AND LOAD my_ext FROM my_repo;\n```\n\nA repository is a name, a URL prefix and one or more RSA public keys that are trusted to sign the extensions served from it. The prefix can point at anything DuckDB can read: a local path, `https`, `s3`, you name it. At `CREATE` time, DuckDB fetches the repository's public keys and pins them into the repository definition, printing each key's SHA-256 fingerprint so you can compare it against one published out of band.\n\nThis feature is still a work in progress and its syntax may change in future releases. For more details, see the [Extension Repositories page](https://duckdb.org/docs/2.0/extensions/extension_repositories.html).\n\n### [Core Extension Updates](#core-extension-updates)\n\nDuckDB's [core extensions](https://duckdb.org/docs/2.0/core_extensions/overview.html) are also shipping several new features, fixes and performance optimizations. These are too numerous to all list here, so we only highlight a few of them and will cover them in dedicated blog posts in the coming weeks:\n\n- We released [DuckLake 1.1](https://ducklake.select/2026/10/21/ducklake-11/), including a new specification and [reference implementation](https://duckdb.org/docs/2.0/core_extensions/ducklake.html).\n- The [Iceberg extension](https://duckdb.org/docs/2.0/core_extensions/iceberg/overview.html) strives to become a feature-complete implementation. To this end, it adds REST catalog [`ATTACH` options](https://duckdb.org/docs/2.0/core_extensions/iceberg/iceberg_options.html#attach-options) that derive a table's location from its namespace or load table metadata from the `metadata-location` file returned by the catalog. Further options control the URL prefix and the namespace separator.\n- The [Delta extension](https://duckdb.org/docs/2.0/core_extensions/delta.html) can create Delta tables with `CREATE TABLE` (including partitioning and Delta table properties), supports time travel and improves `VARIANT` support.\n\n## [SQL Features](#sql-features)\n\n### [Triggers](#triggers)\n\nDuckDB 2.0 delivers triggers: the [`CREATE TRIGGER` statement](https://duckdb.org/docs/2.0/sql/statements/create_trigger.html) with `BEFORE` and `AFTER` triggers, `FOR EACH ROW` and `FOR EACH STATEMENT`, transition tables via `REFERENCING OLD/NEW TABLE`, multiple triggers per event, `RETURNING` on triggered tables and `DROP TRIGGER`. The classic use case is audit tables, where a trigger records what changed in response to a table modification. Some combinations, such as `BEFORE ... FOR EACH ROW` and `UPDATE ... FOR EACH ROW` triggers, are not yet supported; see the [limitations](https://duckdb.org/docs/2.0/sql/statements/create_trigger.html#limitations).\n\n```\nCREATE TRIGGER trg_audit\n    AFTER UPDATE ON target\n    REFERENCING\n        OLD TABLE AS o\n        NEW TABLE AS n\n    FOR EACH STATEMENT\n        INSERT INTO audit\n        SELECT n.id, o.val, n.val\n        FROM o JOIN n ON o.id = n.id;\n```\n\nTriggers can also maintain materialized views incrementally! For example, to keep track of the average age of people per nationality in a table `age_statistics`, you can use two triggers – one for insert and another for delete operations – and store the sum and the count alongside the average.\n\nTo see more details, click here.\n\n```\nCREATE TABLE people (name VARCHAR, nationality VARCHAR, age INTEGER);\nCREATE TABLE age_statistics (\n    nationality VARCHAR PRIMARY KEY,\n    sum_age INTEGER,\n    num_people INTEGER,\n    average_age DOUBLE\n);\n```\n\nOn insertion, we aggregate the new rows per nationality and merge them into `age_statistics`, adding a row for each new nationality:\n\n```\nCREATE TRIGGER people_insert\n    AFTER INSERT ON people\n    REFERENCING NEW TABLE AS n\n    FOR EACH STATEMENT\n        MERGE INTO age_statistics s\n        USING (\n            SELECT nationality, sum(age) AS sum_age, count(*) AS num_people\n            FROM n\n            GROUP BY nationality\n        ) d\n        ON s.nationality = d.nationality\n        WHEN MATCHED THEN UPDATE SET\n            sum_age = s.sum_age + d.sum_age,\n            num_people = s.num_people + d.num_people,\n            average_age = (s.sum_age + d.sum_age) / (s.num_people + d.num_people)\n        WHEN NOT MATCHED THEN INSERT\n            VALUES (d.nationality, d.sum_age, d.num_people, d.sum_age / d.num_people);\n```\n\nOn deletion, we subtract the deleted rows and drop a nationality once its last person has left:\n\n```\nCREATE TRIGGER people_delete\n    AFTER DELETE ON people\n    REFERENCING OLD TABLE AS o\n    FOR EACH STATEMENT\n        MERGE INTO age_statistics s\n        USING (\n            SELECT nationality, sum(age) AS sum_age, count(*) AS num_people\n            FROM o\n            GROUP BY nationality\n        ) d\n        ON s.nationality = d.nationality\n        WHEN MATCHED AND s.num_people = d.num_people THEN DELETE\n        WHEN MATCHED THEN UPDATE SET\n            sum_age = s.sum_age - d.sum_age,\n            num_people = s.num_people - d.num_people,\n            average_age = (s.sum_age - d.sum_age) / (s.num_people - d.num_people);\n```\n\nLet's see people come and go:\n\n```\nINSERT INTO people VALUES\n    ('Alice', 'US', 34), ('Bram', 'NL', 27), ('Csilla', 'HU', 45),\n    ('Dave', 'US', 58), ('Eva', 'NL', 19), ('Femke', 'NL', 41);\nFROM age_statistics ORDER BY nationality;\n```\n\n```\n┌─────────────┬─────────┬────────────┬─────────────┐\n│ nationality │ sum_age │ num_people │ average_age │\n│   varchar   │  int32  │   int32    │   double    │\n├─────────────┼─────────┼────────────┼─────────────┤\n│ HU          │      45 │          1 │        45.0 │\n│ NL          │      87 │          3 │        29.0 │\n│ US          │      92 │          2 │        46.0 │\n└─────────────┴─────────┴────────────┴─────────────┘\n```\n\n```\nDELETE FROM people WHERE name = 'Alice';\nDELETE FROM people WHERE nationality = 'HU';\nFROM age_statistics ORDER BY nationality;\n```\n\n```\n┌─────────────┬─────────┬────────────┬─────────────┐\n│ nationality │ sum_age │ num_people │ average_age │\n│   varchar   │  int32  │   int32    │   double    │\n├─────────────┼─────────┼────────────┼─────────────┤\n│ NL          │      87 │          3 │        29.0 │\n│ US          │      58 │          1 │        58.0 │\n└─────────────┴─────────┴────────────┴─────────────┘\n```\n\nTriggers bring us one step closer to materialized views, a long-standing item in the [“future work” section of our roadmap](https://duckdb.org/roadmap.html#future-work).\n\n### [SQL Dialect Additions](#sql-dialect-additions)\n\nAs always, DuckDB's [friendly SQL dialect](https://duckdb.org/docs/2.0/sql/dialect/friendly_sql.html) keeps growing. A few favorites from this release cycle:\n\n- The [`$variable` shorthand](https://duckdb.org/docs/2.0/sql/statements/set_variable.html#the-name-shorthand) lets you write `$x` in most places where an expression is allowed, instead of `getvariable('x')`.\n- [Data-modifying CTEs](https://duckdb.org/docs/2.0/sql/query_syntax/with.html#data-modifying-ctes) let you use `INSERT`, `UPDATE`, `DELETE` and `COPY` as pipeline steps.\n- [`NEAREST` joins](https://duckdb.org/docs/2.0/sql/query_syntax/from.html#nearest-joins) turn top-k similarity search into a join clause, handy for vector and embedding workloads.\n- [Nested schemas](https://duckdb.org/docs/2.0/sql/statements/create_schema.html#nested-schemas) allow schemas within schemas, e.g., `CREATE SCHEMA finance.reports`.\n- [JSON mutation functions](https://duckdb.org/docs/2.0/data/json/json_functions.html#json-mutation-functions) `json_set`, `json_insert`, `json_replace` and `json_remove` return modified copies of JSON documents (see also our [blog post on JSON patches](https://duckdb.org/2026/08/18/reconciling-json.html)).\n\nThere is more: DuckDB 2.0.0 supports the SQL-standard [`FETCH FIRST`](https://duckdb.org/docs/2.0/sql/query_syntax/limit.html#fetch-first) and allows [`UNNEST` in the `GROUP BY` clause](https://duckdb.org/docs/2.0/sql/query_syntax/groupby.html#grouping-by-unnest). It also ensures well-defined [`MERGE`](https://duckdb.org/docs/2.0/sql/statements/merge_into.html) and [`UPDATE ... FROM`](https://duckdb.org/docs/2.0/sql/statements/update.html) semantics for cases when multiple rows are matched.\n\n## [Migrating to 2.0](#migrating-to-20)\n\nDuckDB 2.0 comes with a small set of breaking changes. For most users, upgrading is straightforward, but complex deployments running many DuckDB processes need more care. The storage format is the most likely source of problems: DuckDB versions before 2.0 cannot read database files created by DuckDB 2.0. DuckDB 2.0 can still read and write your existing database files, and these remain readable by older versions. We recommend testing 2.0.0 on a shadow system before switching production over. And, if your workload is mission-critical, you may want to wait for the first patch release, [coming in November](https://duckdb.org/release_calendar.html#upcoming-releases).\n\nFor the full details, see the [migration guide](https://duckdb.org/docs/2.0/operations_manual/migration.html).\n\n## [Internals and Deprecations](#internals-and-deprecations)\n\n### [Compiler Requirements](#compiler-requirements)\n\nFollowing DuckDB's long stint on C++11, DuckDB 2.0 now [requires a C++17 compiler](https://duckdb.org/docs/2.0/dev/building/overview.html#prerequisites).\n\nWe also switched to Clang as the default compiler for our release builds. On Linux and macOS, both DuckDB and its core extensions are built with Clang. On Windows, the CLI is built with Clang, while most core extensions are still built with MSVC. We may move more Windows builds to Clang in the future, but this requires further changes.\n\n### [Timezones, Calendars and Collations Without ICU](#timezones-calendars-and-collations-without-icu)\n\nTimezone-aware timestamps, calendars and collations have always been powered by the ICU library. In 2.0, the ICU library is gone entirely: the [`icu` extension](https://duckdb.org/docs/2.0/core_extensions/icu.html) now implements timezones, calendars and collations itself, with the timezone data built directly from the IANA database and compressed down to around 45 kB.\n\nEverything keeps working exactly as before, and the native implementation is also faster. If you find any issues, please [let us know](https://github.com/duckdb/duckdb/issues)!\n\n### [Deprecations](#deprecations)\n\n#### [Lambda Syntax](#lambda-syntax)\n\nThe old single-arrow lambda syntax (`x -> x + 1`) is now disabled by default. Use the Python-style syntax instead:\n\n```\nSELECT list_transform([1, 2, 3], lambda x: x + 1);\n```\n\nYou can temporarily re-enable the old syntax with `SET lambda_syntax = 'enable_single_arrow';`. Both the flag and the single-arrow syntax will be removed entirely in v2.1.\n\n#### [Amalgamation Builds](#amalgamation-builds)\n\nThe [amalgamation build](https://duckdb.org/docs/1.4/dev/building/overview), which bundles DuckDB's entire source code into a single header and a single source file, is now deprecated. The amalgamation has grown too large to be realistically useful in most cases.\n\n#### [Gradual Phase-Out of macOS x86\\_64](#gradual-phase-out-of-macos-x86_64)\n\nWe are gradually phasing out `osx_amd64` from the list of fully supported [platforms](https://duckdb.org/docs/2.0/operations_manual/installing_duckdb/requirements.html#supported-platforms).\nFor the time being, DuckDB and [core extensions](https://duckdb.org/docs/2.0/core_extensions/overview.html) are still built and supported, but [community extensions](https://duckdb.org/docs/2.0/extensions/community_extensions.html) are no longer built for this platform. DuckDB 2.1 will stop supporting `osx_amd64`.\n\n## [Summary](#summary)\n\nThese were a few highlights, but there are many more features and improvements in this release. There have been almost 20,000 commits by many contributors since DuckDB 1.5, shipping smaller features as well as performance and usability improvements. The full [release notes can be found on GitHub](https://github.com/duckdb/duckdb/releases/tag/v2.0.0).\n\nWe would like to thank our community for the detailed issue reports and feedback that have driven this release, and our special thanks go to our contributors!\n\n##### In this article\n\n- [DuckDB as a Server](#duckdb-as-a-server)\n  - [Quack and CONNECT](#quack-and-connect)\n  - [Quack in the CLI](#quack-in-the-cli)\n- [Performance](#performance)\n  - [Asynchronous I/O](#asynchronous-io)\n  - [The VARIANT Type Becomes a First-Class Citizen](#the-variant-type-becomes-a-first-class-citizen)\n  - [Agent Mode in the CLI](#agent-mode-in-the-cli)\n- [Extensibility](#extensibility)\n  - [A Brand New SQL Parser](#a-brand-new-sql-parser)\n  - [Write Extensions Once and Host Them Yourself](#write-extensions-once-and-host-them-yourself)\n  - [Core Extension Updates](#core-extension-updates)\n- [SQL Features](#sql-features)\n  - [Triggers](#triggers)\n  - [SQL Dialect Additions](#sql-dialect-additions)\n- [Migrating to 2.0](#migrating-to-20)\n- [Internals and Deprecations](#internals-and-deprecations)\n  - [Compiler Requirements](#compiler-requirements)\n  - [Timezones, Calendars and Collations Without ICU](#timezones-calendars-and-collations-without-icu)\n  - [Deprecations](#deprecations)\n- [Summary](#summary)","body_html":"<h1 id=\"announcing-duckdb-2-0-0-lts-cinnamon-teal\">Announcing DuckDB 2.0.0 LTS “Cinnamon Teal”</h1>\n<p>The DuckDB team</p>\n<figure><img src=\"https://duckdb.org/images/blog/thumbs/duckdb-release-2-0-0.svg\" alt=\"\" loading=\"lazy\" decoding=\"async\" referrerpolicy=\"no-referrer\" /></figure>\n<p><em>TL;DR: The DuckDB team is happy to announce that today we&#39;re releasing DuckDB version 2.0.0 LTS, codenamed “Cinnamon Teal” (Spatula cyanoptera). This is our biggest feature release so far, and it ships a polished version of the Quack protocol, asynchronous I/O, a first-class <code>VARIANT</code> type, agent mode, triggers, an extensible SQL parser and much more, built from close to 20,000 commits since DuckDB 1.5.</em></p>\n<p>We are proud to release DuckDB 2.0.0, codenamed “Cinnamon Teal” after the <a href=\"https://en.wikipedia.org/wiki/Cinnamon_teal\" rel=\"nofollow ugc noopener\"><em>Spatula cyanoptera</em></a>, a strikingly reddish-brown duck found in the western Americas.</p>\n<p>While <a href=\"https://duckdb.org/2024/06/03/announcing-duckdb-100.html\" rel=\"nofollow ugc noopener\">DuckDB 1.0</a> was all about stability and backward compatibility, DuckDB 2.0 is above all a feature release.\nIn this blog post, we summarize the highlights and then walk through the breaking changes and how to migrate.\nWe showcased many of the new features in <a href=\"https://duckdb.org/2026/08/17/duckdb-20-highlights.html\" rel=\"nofollow ugc noopener\">“A Preview of DuckDB v2.0”</a>, which covers each one in more depth.\nFor the complete release notes, see the <a href=\"https://github.com/duckdb/duckdb/releases/tag/v2.0.0\" rel=\"nofollow ugc noopener\">release page on GitHub</a>.</p>\n<blockquote><p>Tip To install the new version, please visit the <a href=\"https://duckdb.org/install/\" rel=\"nofollow ugc noopener\">installation page</a>.\nSome clients, such as the CLI and Python, are already available. Other client libraries (e.g., Go, R, Java) and some extensions can take a few days to release due to the extra changes and review rounds required.</p></blockquote>\n<p>With this release, there are currently three DuckDB lines available.\nDuckDB 2.0 is a long-term support (LTS) release with support until at least October 2027.\nThe previous LTS release, version 1.4, is still supported until late November 2026.\nTo ensure a smooth transition and allow sufficient time for testing, we extended the support period of DuckDB 1.5 until March 2027, effectively also turning it into an LTS release.\nFor details, see the <a href=\"https://duckdb.org/release_calendar.html#lts-releases\" rel=\"nofollow ugc noopener\">release calendar</a>.</p>\n<p>To find your way around the documentation for all of the new features, we also published a <a href=\"https://duckdb.org/docs/2.0/whats_new_in_the_docs.html\" rel=\"nofollow ugc noopener\">“What&#39;s New in the Docs for 2.0”</a> page that links each feature below to its documentation.</p>\n<h2 id=\"duckdb-as-a-server\"><a href=\"#duckdb-as-a-server\">DuckDB as a Server</a></h2>\n<p>DuckDB has been an in-process database since day one.\nIn <a href=\"https://duckdb.org/2026/05/20/announcing-duckdb-153.html\" rel=\"nofollow ugc noopener\">DuckDB 1.5.3</a>, released in May, we introduced the Quack protocol that lets you run DuckDB in a traditional client-server setup, along with the <a href=\"https://duckdb.org/docs/2.0/core_extensions/quack.html\" rel=\"nofollow ugc noopener\"><code>quack</code> extension</a>, which implements DuckDB&#39;s native protocol, so any DuckDB process can <a href=\"https://duckdb.org/docs/2.0/quack/overview.html#server-side-usage\" rel=\"nofollow ugc noopener\">serve its databases</a>.</p>\n<h3 id=\"quack-and-connect\"><a href=\"#quack-and-connect\">Quack and <code>CONNECT</code></a></h3>\n<p>DuckDB 2.0.0 ships a polished version of the Quack protocol with substantial performance and stability improvements. It also introduces the new <a href=\"https://duckdb.org/docs/2.0/sql/statements/connect.html\" rel=\"nofollow ugc noopener\"><code>CONNECT</code> statement</a> that allows attaching and switching to a remote database in a single command for better ergonomics:</p>\n<h4 id=\"duckdb-server\"><a href=\"#duckdb-server\">DuckDB server</a></h4>\n<pre><code>CALL quack_serve(\n    token = &#39;my_token&#39;\n);\n\nCREATE TABLE events\n    (date DATE, name VARCHAR);\n\nINSERT INTO events VALUES\n    (&#39;2027-04-07&#39;,\n     &#39;DuckCon #8&#39;);</code></pre>\n<p>quack:</p>\n<h4 id=\"duckdb-client\"><a href=\"#duckdb-client\">DuckDB client</a></h4>\n<pre><code>ATTACH &#39;quack:localhost&#39;\n    AS qk (TOKEN &#39;my_token&#39;);\n\nCONNECT qk;\nSELECT * FROM events;\n\n┌────────────┬────────────┐\n│    date    │    name    │\n│    date    │  varchar   │\n├────────────┼────────────┤\n│ 2027-04-07 │ DuckCon #8 │\n└────────────┴────────────┘</code></pre>\n<p>For simplicity, this example runs the server and the client on the same machine. By default, the server only listens on <code>localhost</code>. To accept connections from other machines, keep the server on <code>localhost</code> and put it behind a <a href=\"https://duckdb.org/docs/2.0/quack/setup/reverse_proxy.html\" rel=\"nofollow ugc noopener\">TLS-terminating reverse proxy</a>. The <a href=\"https://duckdb.org/docs/2.0/quack/overview.html#starting-a-server\" rel=\"nofollow ugc noopener\"><code>allow_other_hostname</code> option</a>, which exposes the server directly, is only meant for testing.</p>\n<p>The <code>CONNECT</code> statement is not limited to Quack: it points your session at <a href=\"https://duckdb.org/docs/2.0/sql/statements/connect.html#supported-databases\" rel=\"nofollow ugc noopener\">any remote database that supports it</a>, including PostgreSQL and MySQL. For these, the new remote pushdown optimizer ships queries directly to the remote server instead of pulling tables over the wire.</p>\n<h3 id=\"quack-in-the-cli\"><a href=\"#quack-in-the-cli\">Quack in the CLI</a></h3>\n<p>The CLI also has shortcuts for setting up a Quack client-server pair: the new <a href=\"https://duckdb.org/docs/2.0/clients/cli/arguments.html#serving-and-connecting-to-remote-databases\" rel=\"nofollow ugc noopener\"><code>-serve</code> and <code>-connect</code> arguments</a> start a Quack server or connect to one directly from the command line:</p>\n<h4 id=\"cli-server\"><a href=\"#cli-server\">CLI server</a></h4>\n<pre><code>duckdb -serve</code></pre>\n<pre><code>CREATE TABLE t AS\n      SELECT 42 AS x;</code></pre>\n<p>quack:</p>\n<h4 id=\"cli-client\"><a href=\"#cli-client\">CLI client</a></h4>\n<pre><code>duckdb -connect</code></pre>\n<pre><code>FROM t;\n\n┌───────┐\n│   x   │\n│ int32 │\n├───────┤\n│    42 │\n└───────┘</code></pre>\n<h2 id=\"performance\"><a href=\"#performance\">Performance</a></h2>\n<h3 id=\"asynchronous-i-o\"><a href=\"#asynchronous-io\">Asynchronous I/O</a></h3>\n<p>DuckDB 2.0 introduces <a href=\"https://duckdb.org/docs/2.0/data/io.html\" rel=\"nofollow ugc noopener\">asynchronous I/O</a> for reading Parquet and CSV files. The I/O layer now scales independently from the query processing layer, issuing reads in the background and keeping multiple requests in flight. This means far more parallelism for remote reads and dramatically faster queries on object storage such as <a href=\"https://duckdb.org/docs/2.0/core_extensions/httpfs/s3api.html\" rel=\"nofollow ugc noopener\"><code>s3://</code></a>.</p>\n<p>We described the design in detail in a <a href=\"https://duckdb.org/2026/07/31/asynchronous-io.html\" rel=\"nofollow ugc noopener\">dedicated blog post</a>.\nIn that post, we showed the network throughput over the course of a query with and without asynchronous I/O:</p>\n<figure><img src=\"https://duckdb.org/images/blog/v20-release/sync-vs-async-network-throughput-light.svg\" alt=\"Network throughput\" loading=\"lazy\" decoding=\"async\" referrerpolicy=\"no-referrer\" /></figure>\n<figure><img src=\"https://duckdb.org/images/blog/v20-release/sync-vs-async-network-throughput-dark.svg\" alt=\"Network throughput\" loading=\"lazy\" decoding=\"async\" referrerpolicy=\"no-referrer\" /></figure>\n<p><em>Network throughput. Higher utilization and shorter runtime are better.</em></p>\n<h3 id=\"the-variant-type-becomes-a-first-class-citizen\"><a href=\"#the-variant-type-becomes-a-first-class-citizen\">The <code>VARIANT</code> Type Becomes a First-Class Citizen</a></h3>\n<p>The <a href=\"https://duckdb.org/docs/2.0/sql/data_types/variant.html\" rel=\"nofollow ugc noopener\"><code>VARIANT</code> type</a> stores semi-structured, schema-less data such as JSON while keeping the performance of columnar execution.\nSo you can think of it as JSON on steroids: a <code>VARIANT</code> column can store differently shaped data in every row, but unlike JSON it is not a text format.</p>\n<p><a href=\"https://duckdb.org/2026/03/09/announcing-duckdb-150.html#variant-type\" rel=\"nofollow ugc noopener\">DuckDB 1.5 already introduced <code>VARIANT</code></a>, but DuckDB 2.0 fully integrates it. DuckDB 2.0 detects the common structure in your data and “shreds” it, so it compresses well and executes fast. The pipeline works end to end, from shredded execution straight out of storage to shredded <code>VARIANT</code> reading and writing for Parquet, backed by a family of <a href=\"https://duckdb.org/docs/2.0/sql/functions/variant.html\" rel=\"nofollow ugc noopener\"><code>variant_*</code> functions</a>. For example:</p>\n<pre><code>CREATE TABLE events (payload VARIANT);\nINSERT INTO events\nVALUES\n    (&#39;{&quot;user&quot;: {&quot;id&quot;: 42, &quot;tags&quot;: [&quot;a&quot;, &quot;b&quot;]}}&#39;::JSON::VARIANT),\n    (&#39;{&quot;event&quot;: &quot;click&quot;, &quot;x&quot;: 10, &quot;y&quot;: 20}&#39;::JSON::VARIANT);\n\nSELECT variant_type(payload), variant_keys(payload)\nFROM events;</code></pre>\n<pre><code>┌───────────────────────┬───────────────────────┐\n│ variant_type(payload) │ variant_keys(payload) │\n│        varchar        │       varchar[]       │\n├───────────────────────┼───────────────────────┤\n│ OBJECT                │ [user]                │\n│ OBJECT                │ [event, x, y]         │\n└───────────────────────┴───────────────────────┘</code></pre>\n<h3 id=\"agent-mode-in-the-cli\"><a href=\"#agent-mode-in-the-cli\">Agent Mode in the CLI</a></h3>\n<p>Coding agents such as Claude Code, Codex and Cursor increasingly drive the DuckDB CLI on their users&#39; behalf. The 2.0 CLI detects when it is being run by an agent and switches to <a href=\"https://duckdb.org/2026/10/09/agent-mode.html\" rel=\"nofollow ugc noopener\">agent mode</a>: it prints compact Markdown tables instead of padded boxes, says clearly when a result was truncated, stops runaway queries early and reports errors as JSON.</p>\n<p>Long-running SQL queries report their expected cost prior to running, so the agent can decide whether to wait. In our experiments on TPC-H, agent mode cut the amount of DuckDB output the model had to read by 59%. You can turn it <em>on</em> for manual testing using the <a href=\"https://duckdb.org/docs/2.0/clients/cli/agent_mode.html#activating-agent-mode\" rel=\"nofollow ugc noopener\"><code>-agent</code> flag</a> or instruct your agent to turn it <em>off</em> using the <code>-no-agent</code> flag.</p>\n<h2 id=\"extensibility\"><a href=\"#extensibility\">Extensibility</a></h2>\n<h3 id=\"a-brand-new-sql-parser\"><a href=\"#a-brand-new-sql-parser\">A Brand New SQL Parser</a></h3>\n<p>DuckDB has always used a parser derived from PostgreSQL&#39;s. In 2.0, our own modern, extensible <a href=\"https://duckdb.org/2026/08/20/duckdb-20-peg-parser.html\" rel=\"nofollow ugc noopener\">PEG-based parser</a>, which debuted as an opt-in feature in <a href=\"https://duckdb.org/2026/03/09/announcing-duckdb-150.html#peg-parser\" rel=\"nofollow ugc noopener\">DuckDB 1.5</a>, becomes the default. Extensions can now hook into the grammar itself (an idea we first explored in our <a href=\"https://duckdb.org/2024/11/22/runtime-extensible-parsers.html\" rel=\"nofollow ugc noopener\">research on runtime-extensible parsers</a>), so expect extensions that expose entirely new SQL syntax. Error messages now come with precise source locations. For details, see the <a href=\"https://duckdb.org/docs/2.0/sql/peg_parser.html\" rel=\"nofollow ugc noopener\">PEG parser documentation</a>. The new parser is designed to be compatible with the old one, so you should not notice the swap. If you do, please file an issue. In the meantime, you can switch back to the old parser using the <a href=\"https://duckdb.org/docs/2.0/core_extensions/legacy_parser.html\" rel=\"nofollow ugc noopener\"><code>legacy_parser</code> extension</a> by running <code>SET enable_legacy_parser = true;</code>.</p>\n<p>The old parser had served DuckDB since its first commit in 2018. Its grammar was generated with YACC/Bison, which produces an LALR(1) parser, so as DuckSQL grew, even small additions could collide with existing rules and raise <code>shift/reduce</code> or <code>reduce/reduce</code> conflicts. A PEG grammar avoids this by trying alternatives in the order they are written, so adding syntax no longer risks breaking unrelated rules. Python itself moved to a PEG-based parser in <a href=\"https://peps.python.org/pep-0617/\" rel=\"nofollow ugc noopener\">version 3.9</a> for the same flexibility.</p>\n<h3 id=\"write-extensions-once-and-host-them-yourself\"><a href=\"#write-extensions-once-and-host-them-yourself\">Write Extensions Once and Host Them Yourself</a></h3>\n<p>DuckDB 2.0 ships a revamped <a href=\"https://duckdb.org/docs/2.0/clients/c/overview.html#c-api-v2\" rel=\"nofollow ugc noopener\">C API v2</a>, which is considerably more powerful than its predecessor and exposes components such as file systems and table functions to extensions. Before marking it as stable, we would like to test it in the field, so please give it a try and <a href=\"https://github.com/duckdb/duckdb/discussions/27525\" rel=\"nofollow ugc noopener\">send us your feedback</a>!</p>\n<p>You can also register and host your own trusted extension repositories, so an organization can host and sign its own extensions and have them install and load just like the built-in ones:</p>\n<pre><code>SET allow_extension_repositories = &#39;allowed&#39;;\n\nCREATE EXTENSION REPOSITORY my_repo\n    WITH PREFIX &#39;https://extensions.example.org&#39;;\n\nINSTALL AND LOAD my_ext FROM my_repo;</code></pre>\n<p>A repository is a name, a URL prefix and one or more RSA public keys that are trusted to sign the extensions served from it. The prefix can point at anything DuckDB can read: a local path, <code>https</code>, <code>s3</code>, you name it. At <code>CREATE</code> time, DuckDB fetches the repository&#39;s public keys and pins them into the repository definition, printing each key&#39;s SHA-256 fingerprint so you can compare it against one published out of band.</p>\n<p>This feature is still a work in progress and its syntax may change in future releases. For more details, see the <a href=\"https://duckdb.org/docs/2.0/extensions/extension_repositories.html\" rel=\"nofollow ugc noopener\">Extension Repositories page</a>.</p>\n<h3 id=\"core-extension-updates\"><a href=\"#core-extension-updates\">Core Extension Updates</a></h3>\n<p>DuckDB&#39;s <a href=\"https://duckdb.org/docs/2.0/core_extensions/overview.html\" rel=\"nofollow ugc noopener\">core extensions</a> are also shipping several new features, fixes and performance optimizations. These are too numerous to all list here, so we only highlight a few of them and will cover them in dedicated blog posts in the coming weeks:</p>\n<ul><li>We released <a href=\"https://ducklake.select/2026/10/21/ducklake-11/\" rel=\"nofollow ugc noopener\">DuckLake 1.1</a>, including a new specification and <a href=\"https://duckdb.org/docs/2.0/core_extensions/ducklake.html\" rel=\"nofollow ugc noopener\">reference implementation</a>.</li><li>The <a href=\"https://duckdb.org/docs/2.0/core_extensions/iceberg/overview.html\" rel=\"nofollow ugc noopener\">Iceberg extension</a> strives to become a feature-complete implementation. To this end, it adds REST catalog <a href=\"https://duckdb.org/docs/2.0/core_extensions/iceberg/iceberg_options.html#attach-options\" rel=\"nofollow ugc noopener\"><code>ATTACH</code> options</a> that derive a table&#39;s location from its namespace or load table metadata from the <code>metadata-location</code> file returned by the catalog. Further options control the URL prefix and the namespace separator.</li><li>The <a href=\"https://duckdb.org/docs/2.0/core_extensions/delta.html\" rel=\"nofollow ugc noopener\">Delta extension</a> can create Delta tables with <code>CREATE TABLE</code> (including partitioning and Delta table properties), supports time travel and improves <code>VARIANT</code> support.</li></ul>\n<h2 id=\"sql-features\"><a href=\"#sql-features\">SQL Features</a></h2>\n<h3 id=\"triggers\"><a href=\"#triggers\">Triggers</a></h3>\n<p>DuckDB 2.0 delivers triggers: the <a href=\"https://duckdb.org/docs/2.0/sql/statements/create_trigger.html\" rel=\"nofollow ugc noopener\"><code>CREATE TRIGGER</code> statement</a> with <code>BEFORE</code> and <code>AFTER</code> triggers, <code>FOR EACH ROW</code> and <code>FOR EACH STATEMENT</code>, transition tables via <code>REFERENCING OLD/NEW TABLE</code>, multiple triggers per event, <code>RETURNING</code> on triggered tables and <code>DROP TRIGGER</code>. The classic use case is audit tables, where a trigger records what changed in response to a table modification. Some combinations, such as <code>BEFORE ... FOR EACH ROW</code> and <code>UPDATE ... FOR EACH ROW</code> triggers, are not yet supported; see the <a href=\"https://duckdb.org/docs/2.0/sql/statements/create_trigger.html#limitations\" rel=\"nofollow ugc noopener\">limitations</a>.</p>\n<pre><code>CREATE TRIGGER trg_audit\n    AFTER UPDATE ON target\n    REFERENCING\n        OLD TABLE AS o\n        NEW TABLE AS n\n    FOR EACH STATEMENT\n        INSERT INTO audit\n        SELECT n.id, o.val, n.val\n        FROM o JOIN n ON o.id = n.id;</code></pre>\n<p>Triggers can also maintain materialized views incrementally! For example, to keep track of the average age of people per nationality in a table <code>age_statistics</code>, you can use two triggers – one for insert and another for delete operations – and store the sum and the count alongside the average.</p>\n<p>To see more details, click here.</p>\n<pre><code>CREATE TABLE people (name VARCHAR, nationality VARCHAR, age INTEGER);\nCREATE TABLE age_statistics (\n    nationality VARCHAR PRIMARY KEY,\n    sum_age INTEGER,\n    num_people INTEGER,\n    average_age DOUBLE\n);</code></pre>\n<p>On insertion, we aggregate the new rows per nationality and merge them into <code>age_statistics</code>, adding a row for each new nationality:</p>\n<pre><code>CREATE TRIGGER people_insert\n    AFTER INSERT ON people\n    REFERENCING NEW TABLE AS n\n    FOR EACH STATEMENT\n        MERGE INTO age_statistics s\n        USING (\n            SELECT nationality, sum(age) AS sum_age, count(*) AS num_people\n            FROM n\n            GROUP BY nationality\n        ) d\n        ON s.nationality = d.nationality\n        WHEN MATCHED THEN UPDATE SET\n            sum_age = s.sum_age + d.sum_age,\n            num_people = s.num_people + d.num_people,\n            average_age = (s.sum_age + d.sum_age) / (s.num_people + d.num_people)\n        WHEN NOT MATCHED THEN INSERT\n            VALUES (d.nationality, d.sum_age, d.num_people, d.sum_age / d.num_people);</code></pre>\n<p>On deletion, we subtract the deleted rows and drop a nationality once its last person has left:</p>\n<pre><code>CREATE TRIGGER people_delete\n    AFTER DELETE ON people\n    REFERENCING OLD TABLE AS o\n    FOR EACH STATEMENT\n        MERGE INTO age_statistics s\n        USING (\n            SELECT nationality, sum(age) AS sum_age, count(*) AS num_people\n            FROM o\n            GROUP BY nationality\n        ) d\n        ON s.nationality = d.nationality\n        WHEN MATCHED AND s.num_people = d.num_people THEN DELETE\n        WHEN MATCHED THEN UPDATE SET\n            sum_age = s.sum_age - d.sum_age,\n            num_people = s.num_people - d.num_people,\n            average_age = (s.sum_age - d.sum_age) / (s.num_people - d.num_people);</code></pre>\n<p>Let&#39;s see people come and go:</p>\n<pre><code>INSERT INTO people VALUES\n    (&#39;Alice&#39;, &#39;US&#39;, 34), (&#39;Bram&#39;, &#39;NL&#39;, 27), (&#39;Csilla&#39;, &#39;HU&#39;, 45),\n    (&#39;Dave&#39;, &#39;US&#39;, 58), (&#39;Eva&#39;, &#39;NL&#39;, 19), (&#39;Femke&#39;, &#39;NL&#39;, 41);\nFROM age_statistics ORDER BY nationality;</code></pre>\n<pre><code>┌─────────────┬─────────┬────────────┬─────────────┐\n│ nationality │ sum_age │ num_people │ average_age │\n│   varchar   │  int32  │   int32    │   double    │\n├─────────────┼─────────┼────────────┼─────────────┤\n│ HU          │      45 │          1 │        45.0 │\n│ NL          │      87 │          3 │        29.0 │\n│ US          │      92 │          2 │        46.0 │\n└─────────────┴─────────┴────────────┴─────────────┘</code></pre>\n<pre><code>DELETE FROM people WHERE name = &#39;Alice&#39;;\nDELETE FROM people WHERE nationality = &#39;HU&#39;;\nFROM age_statistics ORDER BY nationality;</code></pre>\n<pre><code>┌─────────────┬─────────┬────────────┬─────────────┐\n│ nationality │ sum_age │ num_people │ average_age │\n│   varchar   │  int32  │   int32    │   double    │\n├─────────────┼─────────┼────────────┼─────────────┤\n│ NL          │      87 │          3 │        29.0 │\n│ US          │      58 │          1 │        58.0 │\n└─────────────┴─────────┴────────────┴─────────────┘</code></pre>\n<p>Triggers bring us one step closer to materialized views, a long-standing item in the <a href=\"https://duckdb.org/roadmap.html#future-work\" rel=\"nofollow ugc noopener\">“future work” section of our roadmap</a>.</p>\n<h3 id=\"sql-dialect-additions\"><a href=\"#sql-dialect-additions\">SQL Dialect Additions</a></h3>\n<p>As always, DuckDB&#39;s <a href=\"https://duckdb.org/docs/2.0/sql/dialect/friendly_sql.html\" rel=\"nofollow ugc noopener\">friendly SQL dialect</a> keeps growing. A few favorites from this release cycle:</p>\n<ul><li>The <a href=\"https://duckdb.org/docs/2.0/sql/statements/set_variable.html#the-name-shorthand\" rel=\"nofollow ugc noopener\"><code>$variable</code> shorthand</a> lets you write <code>$x</code> in most places where an expression is allowed, instead of <code>getvariable(&#39;x&#39;)</code>.</li><li><a href=\"https://duckdb.org/docs/2.0/sql/query_syntax/with.html#data-modifying-ctes\" rel=\"nofollow ugc noopener\">Data-modifying CTEs</a> let you use <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code> and <code>COPY</code> as pipeline steps.</li><li><a href=\"https://duckdb.org/docs/2.0/sql/query_syntax/from.html#nearest-joins\" rel=\"nofollow ugc noopener\"><code>NEAREST</code> joins</a> turn top-k similarity search into a join clause, handy for vector and embedding workloads.</li><li><a href=\"https://duckdb.org/docs/2.0/sql/statements/create_schema.html#nested-schemas\" rel=\"nofollow ugc noopener\">Nested schemas</a> allow schemas within schemas, e.g., <code>CREATE SCHEMA finance.reports</code>.</li><li><a href=\"https://duckdb.org/docs/2.0/data/json/json_functions.html#json-mutation-functions\" rel=\"nofollow ugc noopener\">JSON mutation functions</a> <code>json_set</code>, <code>json_insert</code>, <code>json_replace</code> and <code>json_remove</code> return modified copies of JSON documents (see also our <a href=\"https://duckdb.org/2026/08/18/reconciling-json.html\" rel=\"nofollow ugc noopener\">blog post on JSON patches</a>).</li></ul>\n<p>There is more: DuckDB 2.0.0 supports the SQL-standard <a href=\"https://duckdb.org/docs/2.0/sql/query_syntax/limit.html#fetch-first\" rel=\"nofollow ugc noopener\"><code>FETCH FIRST</code></a> and allows <a href=\"https://duckdb.org/docs/2.0/sql/query_syntax/groupby.html#grouping-by-unnest\" rel=\"nofollow ugc noopener\"><code>UNNEST</code> in the <code>GROUP BY</code> clause</a>. It also ensures well-defined <a href=\"https://duckdb.org/docs/2.0/sql/statements/merge_into.html\" rel=\"nofollow ugc noopener\"><code>MERGE</code></a> and <a href=\"https://duckdb.org/docs/2.0/sql/statements/update.html\" rel=\"nofollow ugc noopener\"><code>UPDATE ... FROM</code></a> semantics for cases when multiple rows are matched.</p>\n<h2 id=\"migrating-to-2-0\"><a href=\"#migrating-to-20\">Migrating to 2.0</a></h2>\n<p>DuckDB 2.0 comes with a small set of breaking changes. For most users, upgrading is straightforward, but complex deployments running many DuckDB processes need more care. The storage format is the most likely source of problems: DuckDB versions before 2.0 cannot read database files created by DuckDB 2.0. DuckDB 2.0 can still read and write your existing database files, and these remain readable by older versions. We recommend testing 2.0.0 on a shadow system before switching production over. And, if your workload is mission-critical, you may want to wait for the first patch release, <a href=\"https://duckdb.org/release_calendar.html#upcoming-releases\" rel=\"nofollow ugc noopener\">coming in November</a>.</p>\n<p>For the full details, see the <a href=\"https://duckdb.org/docs/2.0/operations_manual/migration.html\" rel=\"nofollow ugc noopener\">migration guide</a>.</p>\n<h2 id=\"internals-and-deprecations\"><a href=\"#internals-and-deprecations\">Internals and Deprecations</a></h2>\n<h3 id=\"compiler-requirements\"><a href=\"#compiler-requirements\">Compiler Requirements</a></h3>\n<p>Following DuckDB&#39;s long stint on C++11, DuckDB 2.0 now <a href=\"https://duckdb.org/docs/2.0/dev/building/overview.html#prerequisites\" rel=\"nofollow ugc noopener\">requires a C++17 compiler</a>.</p>\n<p>We also switched to Clang as the default compiler for our release builds. On Linux and macOS, both DuckDB and its core extensions are built with Clang. On Windows, the CLI is built with Clang, while most core extensions are still built with MSVC. We may move more Windows builds to Clang in the future, but this requires further changes.</p>\n<h3 id=\"timezones-calendars-and-collations-without-icu\"><a href=\"#timezones-calendars-and-collations-without-icu\">Timezones, Calendars and Collations Without ICU</a></h3>\n<p>Timezone-aware timestamps, calendars and collations have always been powered by the ICU library. In 2.0, the ICU library is gone entirely: the <a href=\"https://duckdb.org/docs/2.0/core_extensions/icu.html\" rel=\"nofollow ugc noopener\"><code>icu</code> extension</a> now implements timezones, calendars and collations itself, with the timezone data built directly from the IANA database and compressed down to around 45 kB.</p>\n<p>Everything keeps working exactly as before, and the native implementation is also faster. If you find any issues, please <a href=\"https://github.com/duckdb/duckdb/issues\" rel=\"nofollow ugc noopener\">let us know</a>!</p>\n<h3 id=\"deprecations\"><a href=\"#deprecations\">Deprecations</a></h3>\n<h4 id=\"lambda-syntax\"><a href=\"#lambda-syntax\">Lambda Syntax</a></h4>\n<p>The old single-arrow lambda syntax (<code>x -&gt; x + 1</code>) is now disabled by default. Use the Python-style syntax instead:</p>\n<pre><code>SELECT list_transform([1, 2, 3], lambda x: x + 1);</code></pre>\n<p>You can temporarily re-enable the old syntax with <code>SET lambda_syntax = &#39;enable_single_arrow&#39;;</code>. Both the flag and the single-arrow syntax will be removed entirely in v2.1.</p>\n<h4 id=\"amalgamation-builds\"><a href=\"#amalgamation-builds\">Amalgamation Builds</a></h4>\n<p>The <a href=\"https://duckdb.org/docs/1.4/dev/building/overview\" rel=\"nofollow ugc noopener\">amalgamation build</a>, which bundles DuckDB&#39;s entire source code into a single header and a single source file, is now deprecated. The amalgamation has grown too large to be realistically useful in most cases.</p>\n<h4 id=\"gradual-phase-out-of-macos-x86-64\"><a href=\"#gradual-phase-out-of-macos-x86_64\">Gradual Phase-Out of macOS x86_64</a></h4>\n<p>We are gradually phasing out <code>osx_amd64</code> from the list of fully supported <a href=\"https://duckdb.org/docs/2.0/operations_manual/installing_duckdb/requirements.html#supported-platforms\" rel=\"nofollow ugc noopener\">platforms</a>.\nFor the time being, DuckDB and <a href=\"https://duckdb.org/docs/2.0/core_extensions/overview.html\" rel=\"nofollow ugc noopener\">core extensions</a> are still built and supported, but <a href=\"https://duckdb.org/docs/2.0/extensions/community_extensions.html\" rel=\"nofollow ugc noopener\">community extensions</a> are no longer built for this platform. DuckDB 2.1 will stop supporting <code>osx_amd64</code>.</p>\n<h2 id=\"summary\"><a href=\"#summary\">Summary</a></h2>\n<p>These were a few highlights, but there are many more features and improvements in this release. There have been almost 20,000 commits by many contributors since DuckDB 1.5, shipping smaller features as well as performance and usability improvements. The full <a href=\"https://github.com/duckdb/duckdb/releases/tag/v2.0.0\" rel=\"nofollow ugc noopener\">release notes can be found on GitHub</a>.</p>\n<p>We would like to thank our community for the detailed issue reports and feedback that have driven this release, and our special thanks go to our contributors!</p>\n<h5 id=\"in-this-article\">In this article</h5>\n<ul><li><a href=\"#duckdb-as-a-server\">DuckDB as a Server</a><ul><li><a href=\"#quack-and-connect\">Quack and CONNECT</a></li><li><a href=\"#quack-in-the-cli\">Quack in the CLI</a></li></ul></li><li><a href=\"#performance\">Performance</a><ul><li><a href=\"#asynchronous-io\">Asynchronous I/O</a></li><li><a href=\"#the-variant-type-becomes-a-first-class-citizen\">The VARIANT Type Becomes a First-Class Citizen</a></li><li><a href=\"#agent-mode-in-the-cli\">Agent Mode in the CLI</a></li></ul></li><li><a href=\"#extensibility\">Extensibility</a><ul><li><a href=\"#a-brand-new-sql-parser\">A Brand New SQL Parser</a></li><li><a href=\"#write-extensions-once-and-host-them-yourself\">Write Extensions Once and Host Them Yourself</a></li><li><a href=\"#core-extension-updates\">Core Extension Updates</a></li></ul></li><li><a href=\"#sql-features\">SQL Features</a><ul><li><a href=\"#triggers\">Triggers</a></li><li><a href=\"#sql-dialect-additions\">SQL Dialect Additions</a></li></ul></li><li><a href=\"#migrating-to-20\">Migrating to 2.0</a></li><li><a href=\"#internals-and-deprecations\">Internals and Deprecations</a><ul><li><a href=\"#compiler-requirements\">Compiler Requirements</a></li><li><a href=\"#timezones-calendars-and-collations-without-icu\">Timezones, Calendars and Collations Without ICU</a></li><li><a href=\"#deprecations\">Deprecations</a></li></ul></li><li><a href=\"#summary\">Summary</a></li></ul>","headings":[{"level":1,"text":"Announcing DuckDB 2.0.0 LTS “Cinnamon Teal”","id":"announcing-duckdb-2-0-0-lts-cinnamon-teal"},{"level":2,"text":"DuckDB as a Server","id":"duckdb-as-a-server"},{"level":3,"text":"Quack and CONNECT","id":"quack-and-connect"},{"level":3,"text":"Quack in the CLI","id":"quack-in-the-cli"},{"level":2,"text":"Performance","id":"performance"},{"level":3,"text":"Asynchronous I/O","id":"asynchronous-i-o"},{"level":3,"text":"The VARIANT Type Becomes a First-Class Citizen","id":"the-variant-type-becomes-a-first-class-citizen"},{"level":3,"text":"Agent Mode in the CLI","id":"agent-mode-in-the-cli"},{"level":2,"text":"Extensibility","id":"extensibility"},{"level":3,"text":"A Brand New SQL Parser","id":"a-brand-new-sql-parser"},{"level":3,"text":"Write Extensions Once and Host Them Yourself","id":"write-extensions-once-and-host-them-yourself"},{"level":3,"text":"Core Extension Updates","id":"core-extension-updates"},{"level":2,"text":"SQL Features","id":"sql-features"},{"level":3,"text":"Triggers","id":"triggers"},{"level":3,"text":"SQL Dialect Additions","id":"sql-dialect-additions"},{"level":2,"text":"Migrating to 2.0","id":"migrating-to-2-0"},{"level":2,"text":"Internals and Deprecations","id":"internals-and-deprecations"},{"level":3,"text":"Compiler Requirements","id":"compiler-requirements"},{"level":3,"text":"Timezones, Calendars and Collations Without ICU","id":"timezones-calendars-and-collations-without-icu"},{"level":3,"text":"Deprecations","id":"deprecations"},{"level":2,"text":"Summary","id":"summary"}]}}