<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Engineering on DuckDB Lab — Independent DuckDB Technical Blog</title><link>https://duckdblab.org/en/categories/engineering/</link><description>Recent content in Engineering on DuckDB Lab — Independent DuckDB Technical Blog</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Wed, 21 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://duckdblab.org/en/categories/engineering/index.xml" rel="self" type="application/rss+xml"/><item><title>DuckDB 2.0 Deep Dive: From Embedded Engine to a Servable Database</title><link>https://duckdblab.org/en/post/duckdb-2-0-deep-dive/</link><pubDate>Wed, 21 Oct 2026 00:00:00 +0000</pubDate><guid>https://duckdblab.org/en/post/duckdb-2-0-deep-dive/</guid><description>&lt;h2 id="why-20-is-not-just-another-feature-batch"&gt;Why 2.0 Is Not Just Another Feature Batch
&lt;/h2&gt;&lt;p&gt;The defining word of DuckDB 1.0 was &lt;strong&gt;stability and backward compatibility&lt;/strong&gt;, and 1.5 laid the groundwork with the Quack protocol, VARIANT, and agent observability. DuckDB 2.0 LTS (codename &amp;ldquo;Cinnamon Teal&amp;rdquo;, after the &lt;em&gt;Spatula cyanoptera&lt;/em&gt;, a reddish-brown duck of the western Americas) ships close to 20,000 commits since 1.5, and the team frames it as a &lt;strong&gt;feature release&lt;/strong&gt;, not a patch.&lt;/p&gt;
&lt;p&gt;A useful test for whether a release write-up is &amp;ldquo;deep&amp;rdquo; is not &amp;ldquo;how many features are listed&amp;rdquo;, but whether you can say &lt;strong&gt;which layer of the database each feature changes&lt;/strong&gt;. The real shift in 2.0 is that DuckDB moves from an &lt;strong&gt;in-process analytical engine&lt;/strong&gt; to a &lt;strong&gt;database that can run in embedded, server, and remote forms at the same time&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Serving&lt;/strong&gt;: the Quack protocol + &lt;code&gt;CONNECT&lt;/code&gt; let any DuckDB process serve its databases, and it can also reach into PostgreSQL/MySQL with remote pushdown&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous I/O&lt;/strong&gt;: the read layer decouples from the query layer, sharply improving throughput on object storage such as &lt;code&gt;s3://&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VARIANT as a first-class type&lt;/strong&gt;: semi-structured data is &amp;ldquo;shredded&amp;rdquo; for columnar execution, so JSON performance now rivals native columns&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A pluggable parser&lt;/strong&gt;: a PEG parser becomes the default, and extensions can define new SQL syntax&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Triggers&lt;/strong&gt;: &lt;code&gt;CREATE TRIGGER&lt;/code&gt; finally fills the last gap for audit tables and incremental materialized-view maintenance&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent mode&lt;/strong&gt;: when the CLI detects it is being driven by a coding agent, it switches to compact output and cut the text an LLM has to read by 59% on TPC-H&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below, each feature is broken down, with &lt;strong&gt;runnable code&lt;/strong&gt; (all verified on the DuckDB 2.0.0 CLI).&lt;/p&gt;
&lt;h2 id="1-serving-the-quack-protocol-and-connect"&gt;1. Serving: the Quack Protocol and CONNECT
&lt;/h2&gt;&lt;p&gt;From day one, DuckDB was an in-process database. Since 1.5.3 introduced the Quack protocol, 2.0 polishes it into a &lt;strong&gt;first-class capability&lt;/strong&gt; — any DuckDB process can serve its databases with &lt;code&gt;quack_serve&lt;/code&gt;, and another process attaches with &lt;code&gt;ATTACH ... 'quack:localhost'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Server side (serve + write):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CALL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quack_serve&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2027-04-07&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;DuckCon #8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Client side (attach + switch + query):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ATTACH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;quack:localhost&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TOKEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CONNECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;date&lt;/th&gt;
 &lt;th&gt;name&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;2027-04-07&lt;/td&gt;
 &lt;td&gt;DuckCon #8&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Security note: it listens on localhost by default. For cross-machine access, keep the server on localhost and put it behind a TLS-terminating reverse proxy; &lt;code&gt;allow_other_hostname&lt;/code&gt; is for testing only.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;The value of &lt;code&gt;CONNECT&lt;/code&gt; is not limited to Quack — it points your session at &lt;strong&gt;any remote database that speaks the protocol, including PostgreSQL and MySQL&lt;/strong&gt;. For those targets, the new &lt;strong&gt;remote pushdown optimizer&lt;/strong&gt; ships the query to the remote server instead of pulling whole tables over the wire. That one step widens DuckDB from a &amp;ldquo;local analytical engine&amp;rdquo; into something that can do &lt;strong&gt;federated queries across data sources&lt;/strong&gt;, which matters for multi-tenant SaaS and hybrid-data setups (further reading: &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-v2-server-mode-guide/" &gt;DuckDB v2.0 Server Mode&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The CLI also has shortcuts: &lt;code&gt;duckdb -serve&lt;/code&gt; to start a server, &lt;code&gt;duckdb -connect&lt;/code&gt; to attach, saving you from hand-writing &lt;code&gt;quack_serve&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="2-performance-asynchronous-io"&gt;2. Performance: Asynchronous I/O
&lt;/h2&gt;&lt;p&gt;DuckDB 2.0 adds &lt;strong&gt;asynchronous I/O&lt;/strong&gt; for reading Parquet and CSV files as well as its native storage format: the I/O layer no longer shares the query layer&amp;rsquo;s thread pool, so reads run in the background with several in flight at once. On local disks it is incremental; on high-latency object storage such as &lt;code&gt;s3://&lt;/code&gt; it is a &lt;strong&gt;large gain&lt;/strong&gt; — higher network-throughput utilization and shorter query runtimes.&lt;/p&gt;
&lt;p&gt;The design is covered in depth in a dedicated engineering blog post (with before/after throughput curves); the key thing to remember is: &lt;strong&gt;when you read large remote files, 2.0&amp;rsquo;s async I/O is free performance you should be picking up.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="3-variant-becomes-first-class"&gt;3. VARIANT Becomes First-Class
&lt;/h2&gt;&lt;p&gt;VARIANT landed in 1.5, but 2.0 &lt;strong&gt;fully integrates&lt;/strong&gt; it: it stores schema-less, semi-structured data (typically JSON) while keeping columnar execution — think of it as &amp;ldquo;JSON on steroids&amp;rdquo;. Each row can hold a different shape, but it is not a text format.&lt;/p&gt;
&lt;p&gt;The core mechanism is &lt;strong&gt;shredding&lt;/strong&gt;: 2.0 detects the common structure in your data and breaks it apart into columnar storage, so it compresses well and executes fast. The pipeline runs end to end — from shredded execution out of storage, to shredded VARIANT read/write in Parquet, backed by a family of &lt;code&gt;variant_*&lt;/code&gt; functions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;VARIANT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;user&amp;#34;: {&amp;#34;id&amp;#34;: 42, &amp;#34;tags&amp;#34;: [&amp;#34;a&amp;#34;,&amp;#34;b&amp;#34;]}}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;VARIANT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;event&amp;#34;:&amp;#34;click&amp;#34;,&amp;#34;x&amp;#34;:10,&amp;#34;y&amp;#34;:20}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;VARIANT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variant_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variant_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;t&lt;/th&gt;
 &lt;th&gt;k&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;OBJECT&lt;/td&gt;
 &lt;td&gt;[user]&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OBJECT&lt;/td&gt;
 &lt;td&gt;[event, x, y]&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For anyone doing event streams, telemetry, or semi-structured log analysis, this means: &lt;strong&gt;you no longer have to give up columnar performance for JSON flexibility.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="4-agent-mode-designed-for-coding-agents"&gt;4. Agent Mode: Designed for Coding Agents
&lt;/h2&gt;&lt;p&gt;This is the most &amp;ldquo;era-defining&amp;rdquo; feature. When the CLI detects it is being driven by a coding agent such as Claude Code, Codex, or Cursor, it switches to &lt;strong&gt;agent mode&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;compact Markdown tables instead of padded boxes&lt;/li&gt;
&lt;li&gt;explicit notes when a result was truncated&lt;/li&gt;
&lt;li&gt;early termination of runaway queries&lt;/li&gt;
&lt;li&gt;errors reported as JSON&lt;/li&gt;
&lt;li&gt;long-running queries report their estimated cost up front, so the agent can decide whether to wait&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The team measured on TPC-H that &lt;strong&gt;agent mode cut the amount of DuckDB output the model had to read by 59%&lt;/strong&gt;. You can test it manually with &lt;code&gt;-agent&lt;/code&gt;, or tell an agent to turn it off with &lt;code&gt;-no-agent&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;duckdb -agent :memory: &lt;span class="s2"&gt;&amp;#34;SELECT 42 AS x;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The measured output is the compact form:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;| x:INTEGER |
|---|
| 42 |
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is consistent with DuckDB&amp;rsquo;s direction as a data interface for AI (further reading: &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-ai-agent-brain/" &gt;Building an AI Agent Brain with DuckDB&lt;/a&gt;, &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-mcp-server-guide/" &gt;DuckDB MCP Server Guide&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id="5-extensibility-a-brand-new-peg-parser"&gt;5. Extensibility: A Brand-New PEG Parser
&lt;/h2&gt;&lt;p&gt;DuckDB has always used a parser derived from PostgreSQL. In 2.0, the team&amp;rsquo;s own modern &lt;strong&gt;PEG parser&lt;/strong&gt; (an opt-in feature in 1.5) becomes the &lt;strong&gt;default&lt;/strong&gt;. Extensions can now &lt;strong&gt;hook into the grammar itself&lt;/strong&gt; — the team expects extensions that expose entirely new SQL syntax. Errors now carry precise source locations.&lt;/p&gt;
&lt;p&gt;Why the switch? The old parser was generated by YACC/Bison into an LALR(1) grammar, so as DuckSQL grew, even small additions risked colliding with existing rules and raising shift/reduce or reduce/reduce conflicts. A PEG grammar tries alternatives in written order, so &lt;strong&gt;adding syntax no longer risks breaking unrelated rules&lt;/strong&gt; (Python 3.9 moved to PEG for the same reason). The new parser is compatible with the old one; if something regresses, &lt;code&gt;SET enable_legacy_parser = true;&lt;/code&gt; switches it back (verified present and functional on 2.0).&lt;/p&gt;
&lt;h3 id="write-your-own-extensions-and-host-them-yourself"&gt;Write Your Own Extensions and Host Them Yourself
&lt;/h3&gt;&lt;p&gt;The revamped C API v2 is considerably more powerful, exposing components such as file systems and table functions to extensions. You can also register and host &lt;strong&gt;trusted extension repositories&lt;/strong&gt; — an organization can sign its own extensions and install/load them just like built-ins:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;allow_extension_repositories&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;allowed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EXTENSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;REPOSITORY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_repo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PREFIX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://extensions.example.org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;INSTALL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LOAD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_repo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At &lt;code&gt;CREATE&lt;/code&gt; time DuckDB fetches the repository&amp;rsquo;s public keys and pins them into the repository definition, printing each key&amp;rsquo;s SHA-256 fingerprint so you can compare against one published out of band. This feature is still evolving and its syntax may change.&lt;/p&gt;
&lt;h2 id="6-core-extension-updates"&gt;6. Core Extension Updates
&lt;/h2&gt;&lt;p&gt;2.0 ships a batch of updates and performance work across the core extensions; the production-relevant highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DuckLake 1.1&lt;/strong&gt;: new specification plus reference implementation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iceberg&lt;/strong&gt;: moving toward feature-complete, with new REST-catalog ATTACH options (derive a table location from its namespace, or read metadata from the catalog&amp;rsquo;s metadata-location file)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delta&lt;/strong&gt;: &lt;code&gt;CREATE TABLE&lt;/code&gt; (including partitioning and Delta table properties), time travel, and improved VARIANT support&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="7-triggers-audit-tables-and-incremental-materialized-views"&gt;7. Triggers: Audit Tables and Incremental Materialized Views
&lt;/h2&gt;&lt;p&gt;2.0 delivers &lt;code&gt;CREATE TRIGGER&lt;/code&gt;, with BEFORE/AFTER, FOR EACH ROW / FOR EACH STATEMENT, transition tables via &lt;code&gt;REFERENCING OLD/NEW TABLE&lt;/code&gt;, multiple triggers per event, RETURNING on triggered tables, and DROP TRIGGER. The classic use case is &lt;strong&gt;audit tables&lt;/strong&gt; — recording what changed in response to a table modification:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRIGGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trg_audit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AFTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;REFERENCING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EACH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;STATEMENT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;audit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Better still, triggers can &lt;strong&gt;incrementally maintain materialized views&lt;/strong&gt;. The following pair (one INSERT, one DELETE) maintains an aggregate of average age per nationality:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nationality&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age_statistics&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nationality&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;average_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DOUBLE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRIGGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;people_insert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AFTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;REFERENCING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EACH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;STATEMENT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;MERGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age_statistics&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USING&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nationality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;GROUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nationality&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nationality&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nationality&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MATCHED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;average_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MATCHED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nationality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum_age&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;num_people&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Measured result: after inserting 6 people, the aggregate table auto-generates:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;nationality&lt;/th&gt;
 &lt;th&gt;sum_age&lt;/th&gt;
 &lt;th&gt;num_people&lt;/th&gt;
 &lt;th&gt;average_age&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;HU&lt;/td&gt;
 &lt;td&gt;45&lt;/td&gt;
 &lt;td&gt;1&lt;/td&gt;
 &lt;td&gt;45.0&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NL&lt;/td&gt;
 &lt;td&gt;87&lt;/td&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;29.0&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;US&lt;/td&gt;
 &lt;td&gt;92&lt;/td&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;46.0&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The team keeps stressing that triggers bring DuckDB &lt;strong&gt;one step closer to materialized views&lt;/strong&gt;, a long-standing roadmap item (a few combinations such as BEFORE &amp;hellip; FOR EACH ROW are not yet supported; see the official limitations).&lt;/p&gt;
&lt;h2 id="8-sql-dialect-additions"&gt;8. SQL Dialect Additions
&lt;/h2&gt;&lt;p&gt;Practical additions to the dialect in 2.0:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;$&lt;/code&gt; variable shorthand&lt;/strong&gt;: write &lt;code&gt;$x&lt;/code&gt; in most expression positions instead of &lt;code&gt;getvariable('x')&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data-modifying CTEs&lt;/strong&gt;: &lt;code&gt;INSERT&lt;/code&gt;/&lt;code&gt;UPDATE&lt;/code&gt;/&lt;code&gt;DELETE&lt;/code&gt;/&lt;code&gt;COPY&lt;/code&gt; as pipeline steps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NEAREST joins&lt;/strong&gt;: turn top-k similarity search into a join clause, which helps vector/embedding workloads&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nested schemas&lt;/strong&gt;: &lt;code&gt;CREATE SCHEMA finance.reports;&lt;/code&gt; — schemas within schemas&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON mutation functions&lt;/strong&gt;: &lt;code&gt;json_set&lt;/code&gt; / &lt;code&gt;json_insert&lt;/code&gt; / &lt;code&gt;json_replace&lt;/code&gt; / &lt;code&gt;json_remove&lt;/code&gt; return modified copies of JSON documents (see &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-json-patch-functions/" &gt;JSON Patch Functions in Practice&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nested schema and JSON mutation, measured on the 2.0 CLI:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SCHEMA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;finance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SCHEMA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;finance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SCHEMAS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- memory | finance / main / reports, three rows
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;a&amp;#34;:1,&amp;#34;b&amp;#34;:2}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;$.c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;a&amp;#34;:1,&amp;#34;b&amp;#34;:2}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;$.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- {&amp;#34;a&amp;#34;:1,&amp;#34;b&amp;#34;:2,&amp;#34;c&amp;#34;:3} {&amp;#34;a&amp;#34;:1}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also: 2.0 supports the SQL-standard &lt;code&gt;FETCH FIRST&lt;/code&gt;, and allows &lt;code&gt;UNNEST&lt;/code&gt; in the &lt;code&gt;GROUP BY&lt;/code&gt; clause; where multiple rows match, &lt;code&gt;MERGE&lt;/code&gt; and &lt;code&gt;UPDATE ... FROM&lt;/code&gt; now have well-defined semantics.&lt;/p&gt;
&lt;h2 id="9-migrating-to-20-breaking-changes-and-caveats"&gt;9. Migrating to 2.0: Breaking Changes and Caveats
&lt;/h2&gt;&lt;p&gt;Upgrading is straightforward for most users, but &lt;strong&gt;multi-process, production deployments&lt;/strong&gt; should watch three things:&lt;/p&gt;
&lt;h3 id="storage-format-the-most-likely-footgun"&gt;Storage format (the most likely footgun)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Versions before 2.0 cannot read database files created by 2.0&lt;/strong&gt;; 2.0 itself can still read and write older files, and those remain readable by older versions. Test 2.0.0 on a shadow system before switching production; for mission-critical workloads, wait for the first November patch release.&lt;/p&gt;
&lt;h3 id="compiler-requirements"&gt;Compiler requirements
&lt;/h3&gt;&lt;p&gt;After a long stint on C++11, 2.0 now &lt;strong&gt;requires a C++17 compiler&lt;/strong&gt;; release builds switch to Clang by default (on Linux/macOS both DuckDB and its core extensions build with Clang; on Windows the CLI uses Clang while most core extensions still use MSVC).&lt;/p&gt;
&lt;h3 id="timezones-calendars-collations-icu-removed"&gt;Timezones, calendars, collations: ICU removed
&lt;/h3&gt;&lt;p&gt;2.0 &lt;strong&gt;drops the ICU library entirely&lt;/strong&gt; — the &lt;code&gt;icu&lt;/code&gt; extension now implements timezones, calendars, and collations itself, with timezone data built straight from the IANA database and compressed to roughly 45 kB. Everything behaves as before, and the native implementation is faster.&lt;/p&gt;
&lt;h3 id="deprecations"&gt;Deprecations
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Old single-arrow lambda syntax&lt;/strong&gt; (&lt;code&gt;x -&amp;gt; x + 1&lt;/code&gt;) is now disabled by default; use the Python-style form:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list_transform&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- [2, 3, 4]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- The old syntax errors on 2.0:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- Binder Error: Deprecated lambda arrow (-&amp;gt;) detected ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- Re-enable temporarily with SET lambda_syntax=&amp;#39;ENABLE_SINGLE_ARROW&amp;#39;; removed in v2.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Amalgamation builds&lt;/strong&gt; (bundling the whole source into one header + one .c) are deprecated — they have grown too large to be practically useful&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;macOS x86_64&lt;/strong&gt; is being phased out: it is still built and supported for now, but community extensions no longer build for it, and v2.1 will stop supporting it&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-three-lts-lines"&gt;The Three LTS Lines
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Version&lt;/th&gt;
 &lt;th&gt;Supported until&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;1.4&lt;/td&gt;
 &lt;td&gt;late Nov 2026&lt;/td&gt;
 &lt;td&gt;previous LTS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;1.5&lt;/td&gt;
 &lt;td&gt;Mar 2027&lt;/td&gt;
 &lt;td&gt;extended, effectively an LTS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2.0&lt;/td&gt;
 &lt;td&gt;Oct 2027&lt;/td&gt;
 &lt;td&gt;new LTS, Cinnamon Teal&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="summary-and-next-steps"&gt;Summary and Next Steps
&lt;/h2&gt;&lt;p&gt;The core of 2.0 is not &amp;ldquo;X more functions&amp;rdquo;; it is an &lt;strong&gt;expansion of the database&amp;rsquo;s form&lt;/strong&gt;: embedded, server, and remote runtimes coexist; VARIANT + triggers + async I/O let an &amp;ldquo;analytical&amp;rdquo; database genuinely do &amp;ldquo;transactional&amp;rdquo; work; the PEG parser + C API v2 open up customization for extension authors.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To land 2.0 serving in production: &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-v2-server-mode-guide/" &gt;Server Mode Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;To wire up AI workflows: &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-ai-agent-brain/" &gt;AI Agent Brain&lt;/a&gt;, &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-mcp-server-guide/" &gt;MCP Server Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;To get started: &lt;a class="link" href="https://duckdblab.org/en/post/duckdb-complete-guide/" &gt;Complete Guide&lt;/a&gt;, &lt;a class="link" href="https://duckdblab.org/en/cheatsheet/" &gt;Cheatsheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>