Bases de Datos 7 min de lectura 1452 palabras

DuckDB 2.0: modo servidor, VARIANT, triggers y un modo para agentes

EN
DuckDB 2.0: modo servidor, VARIANT, triggers y un modo para agentes

El equipo de DuckDB ha publicado DuckDB 2.0.0 LTS “Cinnamon Teal”, con nombre de pato, como siempre: la cerceta canela, un pato de color pardo rojizo del oeste de América. Si la 1.0 fue la versión de la estabilidad, la 2.0 es, según sus autores, la mayor versión en funcionalidades hasta ahora, con casi 20.000 commits desde la 1.5.

Es también la primera versión grande desde que AWS compró DuckLabs, y viene cargada. He hecho algunas pruebas, así que cuento lo más interesante y algunas cosas que me he encontrado por el camino.

Tres versiones con soporte

Con esta versión conviven tres líneas de DuckDB:

VersiónSoporte hasta
2.0 (LTS)al menos octubre de 2027
1.5marzo de 2027, ampliado para dar tiempo a migrar
1.4 (LTS anterior)finales de noviembre de 2026

La CLI y el cliente de Python ya están disponibles. Otros clientes, como Go, R o Java, y algunas extensiones pueden tardar unos días más.

DuckDB como servidor

DuckDB ha sido siempre una base de datos embebida, dentro del proceso. En la 1.5.3, en mayo, apareció el protocolo Quack para usarla en modo cliente-servidor, y en la 2.0 llega pulido, con mejoras de rendimiento y estabilidad, y con una sentencia nueva, CONNECT, para conectarse a una base de datos remota y pasar a usarla en un solo paso.

En el servidor:

CALL quack_serve(token = 'my_token');

CREATE TABLE events (date DATE, name VARCHAR);
INSERT INTO events VALUES ('2027-04-07', 'DuckCon #8');

Y en el cliente:

ATTACH 'quack:localhost' AS qk (TOKEN 'my_token');
CONNECT qk;
SELECT * FROM events;

Desde la CLI es todavía más directo: duckdb -serve arranca un servidor y duckdb -connect se conecta a él. Por defecto el servidor sólo escucha en localhost, y para aceptar conexiones de otras máquinas recomiendan ponerlo detrás de un proxy inverso con TLS.

CONNECT no se limita a Quack: también sirve para PostgreSQL y MySQL, y en ese caso un nuevo optimizador envía las consultas al servidor remoto en lugar de traerse las tablas por la red.

Lo he probado desde Python con dos procesos y funciona: el servidor escucha en http://localhost:9494 y el cliente lee la tabla con ATTACH y el nombre cualificado (qk.events). Eso sí, en mi prueba, después de CONNECT el cliente de Python no permitía materializar resultados (materialize() is not available while CONNECT-ed), así que desde Python, de momento, me quedo con ATTACH.

E/S asíncrona

DuckDB 2.0 lee Parquet, CSV y su propio formato con E/S asíncrona. La lectura deja de depender del procesamiento de la consulta: se lanzan las peticiones en segundo plano y se mantienen varias en curso a la vez. Para lecturas remotas, como ficheros en s3://, esto significa mucho más paralelismo y consultas bastante más rápidas.

Es la mejora que más me interesa, porque cuando los datos están en almacenamiento remoto la latencia de cada petición suele pesar más que la CPU. Complementa bien lo que contaba en el artículo sobre formatos de archivo y rendimiento.

VARIANT, JSON con esteroides

El tipo VARIANT guarda datos semiestructurados, como JSON, pero sin ser texto: cada fila puede tener una forma distinta, y DuckDB detecta la estructura común y la “trocea” en columnas para que se comprima bien y se procese rápido. Apareció en la 1.5 y en la 2.0 está integrado de principio a fin, incluida la lectura y escritura en Parquet.

CREATE TABLE events (payload VARIANT);
INSERT INTO events VALUES
    ('{"user": {"id": 42, "tags": ["a", "b"]}}'::JSON::VARIANT),
    ('{"event": "click", "x": 10, "y": 20}'::JSON::VARIANT);

SELECT variant_type(payload), variant_keys(payload) FROM events;
variant_type(payload)variant_keys(payload)
OBJECT[user]
OBJECT[event, x, y]

Un modo para agentes

Esta me ha hecho gracia, y tiene mucho sentido. Cada vez más, quien usa la CLI de DuckDB no es una persona, sino un agente como Claude Code, Codex o Cursor. La CLI 2.0 detecta cuándo la ejecuta un agente y cambia de comportamiento:

  • Muestra tablas compactas en Markdown en lugar de cajas con relleno.
  • Dice claramente cuándo un resultado se ha truncado.
  • Para pronto las consultas descontroladas.
  • Devuelve los errores en JSON.
  • Avisa del coste estimado de las consultas largas antes de ejecutarlas, para que el agente decida si esperar.

En sus pruebas con TPC-H, el modo agente redujo un 59% la cantidad de salida que tenía que leer el modelo. Se puede forzar con -agent o desactivar con -no-agent.

Triggers

DuckDB 2.0 trae triggers: CREATE TRIGGER con BEFORE y AFTER, FOR EACH ROW y FOR EACH STATEMENT, y tablas de transición con REFERENCING OLD/NEW TABLE. El caso clásico es una tabla de auditoría:

CREATE TABLE target (id INTEGER, val INTEGER);
CREATE TABLE audit (id INTEGER, old_val INTEGER, new_val INTEGER);

CREATE TRIGGER trg_audit
    AFTER UPDATE ON target
    REFERENCING OLD TABLE AS o NEW TABLE AS n
    FOR EACH STATEMENT
    INSERT INTO audit
    SELECT n.id, o.val, n.val FROM o JOIN n ON o.id = n.id;

INSERT INTO target VALUES (1, 10), (2, 20);
UPDATE target SET val = val + 1 WHERE id = 1;

FROM audit;
idold_valnew_val
11011

El anuncio incluye un ejemplo más ambicioso: mantener una tabla de estadísticas actualizada de forma incremental con dos triggers y MERGE INTO, que es casi una vista materializada. Las vistas materializadas llevan tiempo en la lista de trabajo futuro de DuckDB, y los triggers las acercan. Todavía hay combinaciones no soportadas, como BEFORE ... FOR EACH ROW.

Más novedades

  • Un parser SQL nuevo. DuckDB usaba un parser derivado del de PostgreSQL, generado con YACC/Bison. Ahora el parser por defecto es uno propio basado en PEG, que permite a las extensiones añadir sintaxis nueva y da mensajes de error con la posición exacta. Debería ser compatible con el anterior, y si algo falla se puede volver al antiguo con SET enable_legacy_parser = true;.
  • Repositorios de extensiones propios, firmados con claves RSA, para que una organización distribuya sus extensiones como si fueran las oficiales. Todavía está en desarrollo y la sintaxis puede cambiar.
  • Extensiones: DuckLake 1.1, mejoras en Iceberg (catálogos REST) y en Delta, que ya puede crear tablas y viajar en el tiempo.
  • SQL: CTE que modifican datos, joins NEAREST para búsquedas de similitud con vectores, esquemas anidados, funciones para modificar JSON (json_set, json_insert…), FETCH FIRST y la abreviatura $variable en lugar de getvariable('x').
  • Zonas horarias sin ICU: la extensión icu ya no depende de la biblioteca ICU. Usa los datos de la base de datos IANA comprimidos en unos 45 kB, y además es más rápida.

Antes de actualizar

Hay algunos cambios que conviene tener en cuenta:

  • Formato de almacenamiento. DuckDB 2.0 lee y escribe los ficheros antiguos, y estos siguen siendo legibles por versiones anteriores. Pero un fichero creado con DuckDB 2.0 no lo pueden leer versiones anteriores. Si hay varios procesos con versiones distintas compartiendo ficheros, es el punto delicado. Recomiendan probar en un sistema paralelo y, para cargas críticas, esperar al primer parche, previsto para noviembre.
  • Lambdas. La sintaxis con flecha (x -> x + 1) queda desactivada por defecto, y la nueva es lambda x: x + 1. Lo he comprobado: con la flecha, la consulta falla con un error que explica el cambio. Se puede reactivar temporalmente con SET lambda_syntax = 'enable_single_arrow';, pero desaparecerá en la 2.1.
  • Python 3.11 o superior. Al probarlo con uv me encontré con que el paquete duckdb==2.0.0 exige Python 3.11 como mínimo. Con un Python más antiguo, el gestor de paquetes no encuentra una versión compatible.
  • $variable en Python. En el cliente de Python, $x dentro de una consulta se interpreta como un parámetro con nombre, no como la variable de DuckDB, así que hay que pasar el valor como parámetro.
  • Plataformas y compilación. Ahora hace falta un compilador de C++17, la amalgamation (todo el código en un fichero) queda obsoleta, y macOS x86_64 empieza a desaparecer: las extensiones de la comunidad ya no se compilan para esa plataforma y la 2.1 dejará de soportarla.

Lo que me llevo

Lo que más me llama la atención de esta versión es que DuckDB deja de ser sólo “SQLite para análisis”. Con Quack y CONNECT puede funcionar como servidor; con los triggers y las CTE que modifican datos se acerca a lo que se espera de una base de datos transaccional; y con el modo agente reconoce que buena parte de su uso ya no lo hacen personas escribiendo en la terminal.

En el día a día, con datos en almacenamiento remoto, la E/S asíncrona es probablemente lo que antes se note, y VARIANT puede simplificar mucho el tratamiento de JSON. Pero con el cambio de formato de almacenamiento y la versión mínima de Python, la recomendación del propio equipo me parece la sensata: probarlo en paralelo, y para producción, esperar al primer parche de noviembre.

Fuente: Announcing DuckDB 2.0.0 LTS “Cinnamon Teal”, el equipo de DuckDB.