2023-12-18
Erweiterungen für DuckDB-Wasm
Carlo Piovesan
In diesem Blogbeitrag geht es um zwei spannende DuckDB-Features: den DuckDB-Wasm-Client und DuckDB-Erweiterungen. Ich zeige, wie diese bisher getrennten Features jetzt zusammenarbeiten. Sie sind für DuckDB-Wasm-Nutzer verfügbar und können unter shell.duckdb.org ausprobiert werden.
DuckDB-Erweiterungen
DuckDBs Philosophie ist ein schlankes Kernsystem, damit Robustheit und Portabilität stimmen. Ein konkurrierendes Ziel ist Flexibilität und ein breites Funktionsspektrum für fortgeschrittene Analysen. Dafür hat DuckDB einen Erweiterungsmechanismus: Erweiterungen werden zur Laufzeit installiert und geladen.
DuckDB-Erweiterungen lokal ausführen
Für DuckDB ein einfaches End-to-End-Beispiel über die Kommandozeile:
INSTALL tpch;LOAD tpch;CALL dbgen(sf = 0.1);PRAGMA tpch(7);Das Skript installiert zuerst die TPC-H-Erweiterung aus dem offiziellen Erweiterungs-Repository; sie implementiert den bekannten TPC-H-Benchmark. Dann wird die TPC-H-Erweiterung geladen und mit dbgen Daten erzeugt. Zum Schluss läuft TPC-H Query 7.
Das Beispiel zeigt einen Fall, in dem eine Erweiterung DuckDB um ein Feature ergänzt (den TPC-H-Datengenerator), das nicht im Basis-Executable steckt. Stattdessen wird es aus dem Erweiterungs-Repository heruntergeladen, geladen und lokal im DuckDB-Rahmen ausgeführt.
Derzeit hat DuckDB mehrere Erweiterungen. Sie ergänzen Dateisysteme, Dateiformate, Datenbank- und Netzwerkprotokolle. Außerdem implementieren sie neue Funktionen, etwa Full-Text-Suche.
DuckDB-Wasm
Unter der Federführung von André Kohn wurde DuckDB 2021 auf die WebAssembly-Plattform portiert. WebAssembly, auch Wasm, ist eine in den letzten Jahren entwickelte W3C-Standardsprache. Stellen Sie sich ein maschinenunabhängiges Binärformat vor, das Sie in der Sandbox eines Webbrowsers ausführen können.
Dank DuckDB-Wasm hat jede und jeder Zugriff auf eine DuckDB-Instanz – nur einen Browser-Tab entfernt. Die gesamte Berechnung läuft lokal im Browser, keine Daten verlassen das Gerät. DuckDB-Wasm ist eine Bibliothek für verschiedene Deployments (z. B. Notebooks, die ohne Server im Browser laufen). In diesem Beitrag nutzen wir die Web-Shell: SQL-Statements werden zeilenweise eingegeben, das Verhalten orientiert sich an der DuckDB-CLI-Shell.
DuckDB-Erweiterungen, in DuckDB-Wasm!
DuckDB-Wasm unterstützt jetzt DuckDB-Erweiterungen. Dazu kommen vier neue Kernpunkte. Erstens: Die DuckDB-Wasm-Bibliothek lässt sich mit dynamischer Erweiterungssupport kompilieren. Zweitens: DuckDB-Erweiterungen können zu einem einzelnen WebAssembly-Modul kompiliert werden. Drittens: Nutzer und Entwickler, die mit DuckDB-Wasm arbeiten, können den Satz geladener Erweiterungen selbst wählen. Viertens: Die Features der DuckDB-Wasm-Shell liegen jetzt deutlich näher an der nativen CLI-Funktionalität.
Die TPC-H-Erweiterung in DuckDB-Wasm nutzen
Zur Demonstration wieder das TPC-H-Datengenerierungsbeispiel. Um das Skript im Browser auszuführen, starten Sie eine Online-DuckDB-Shell mit diesen Befehlen. Das Skript erzeugt den TPC-H-Datensatz mit Scale Factor 0.1, das entspricht 100 MB unkomprimiertes CSV.
Wenn das Skript fertig ist, können Sie weiter Abfragen ausführen oder die Datei customer.parquet (1 MB) mit folgenden Befehlen herunterladen:
COPY customer TO 'customer.parquet';.files download customer.parquetZuerst wird customer.parquet ins Dateisystem von DuckDB-Wasm kopiert, dann über den Browser heruntergeladen.
Kurz: Ihre DuckDB-Instanz, die vollständig im Browser läuft, hat zuerst die TPC-H-Erweiterung installiert und geladen. Dann hat sie mit der Erweiterung Logik Daten erzeugt und in eine Parquet-Datei umgewandelt. Schließlich konnten Sie die Parquet-Datei als normale Datei auf Ihr lokales Dateisystem herunterladen.
Die Spatial-Erweiterung in DuckDB-Wasm nutzen
Um zu zeigen, was DuckDB-Wasm-Erweiterungen möglich machen, und die Grenzen auszutesten: Wie wäre es mit der Spatial-Erweiterung in DuckDB-Wasm? Diese Erweiterung implementiert geospatiale Typen und Funktionen für geospatiale Daten und passende Workloads.
Zum Installieren und Laden der Spatial-Erweiterung in DuckDB-Wasm:
INSTALL spatial;LOAD spatial;Mit der Spatial-Erweiterung nutzt die folgende Abfrage den New-York-Taxi-Datensatz und berechnet die Fläche der Taxi-Zonen je Borough:
CREATE TABLE nyc AS SELECT borough, st_union_agg(geom) AS full_geom, st_area(full_geom) AS area, st_centroid(full_geom) AS centroid, count(*) AS count FROM st_read('https://raw.githubusercontent.com/duckdb/duckdb-spatial/main/test/data/nyc_taxi/taxi_zones/taxi_zones.shp')GROUP BY borough;
SELECT borough, area, centroid::VARCHAR, countFROM nyc;Sowohl Ihr lokaler DuckDB-Client als auch die Online-DuckDB-Shell führen dieselbe Analyse aus.
Unter der Haube
Schauen wir uns an, wie das zusammenhängt. Die folgende Abbildung gibt einen Überblick über die Architektur von DuckDB-Wasm. Beide Komponenten in der Abbildung laufen im Webbrowser.

Wenn Sie DuckDB-Wasm im Browser laden, werden zwei Komponenten aufgebaut: (1) Eine Wrapper-Bibliothek im Main-Thread, die als Brücke zwischen Nutzern oder Code, der DuckDB-Wasm verwendet, und der Hintergrundkomponente dient. (2) Eine DuckDB-Engine zum Ausführen von Abfragen. Diese Komponente lebt in einem Web Worker und kommuniziert mit der Main-Thread-Komponente über Nachrichten. Sie hat eine JavaScript-Schicht für Nachrichten und die ursprüngliche DuckDB-C++-Logik, kompiliert in eine einzelne WebAssembly-Datei.
Was passiert, wenn Erweiterungen dazukommen?

Erweiterungen für DuckDB-Wasm bestehen aus einem einzelnen WebAssembly-Modul. Es kodiert Logik und Daten der Erweiterung, die Liste importierter und exportierter Funktionen und einen eigenen Abschnitt mit Metadaten zur Verifikation der Erweiterung.
Damit das Laden funktioniert, blockiert die DuckDB-Engine-Komponente, holt und prüft externen WebAssembly-Code, verknüpft ihn, verdrahtet Import und Export – danach hängt das System zusammen und arbeitet weiter, als wäre es eine einzige Codebasis.
Der zentrale Codeblock, der das möglich macht:
EM_ASM( { const xhr = new XMLHttpRequest(); xhr.open("GET", UTF8ToString($0), false); xhr.responseType = "arraybuffer"; xhr.send(null); var uInt8Array = xhr.response; // Check signatures / version compatibility left as an exercise WebAssembly.validate(uInt8Array); // Here we add the uInt8Array to Emscripten's filesystem, // for it to be found by dlopen FS.writeFile(UTF8ToString($1), new Uint8Array(uInt8Array)); }, filename.c_str(), basename.c_str());
auto lib_hdl = dlopen(basename.c_str(), RTLD_NOW | RTLD_LOCAL);if (!lib_hdl) { throw IOException( "Extension \"%s\" could not be loaded: %s", filename, GetDLError() );}Hier stützen wir uns auf zwei starke Features von Emscripten, der Compiler-Toolchain, mit der wir DuckDB nach WebAssembly kompilieren.
Erstens erlaubt EM_ASM, JavaScript direkt in C++ einzubetten. Zur Laufzeit kehrt die WebAssembly-Komponente an dieser Stelle nach JavaScript zurück, führt einen blockierenden XMLHttpRequest auf eine URL wie https://extensions.duckdb.org/…/tpch.duckdb_extension.wasm aus
und prüft, dass das gerade geholte Paket tatsächlich ein gültiges WebAssembly-Modul ist.
Zweitens nutzen wir Emscriptens dlopen-Implementierung, mit der kompatible WebAssembly-Module verknüpft werden und als eine zusammensetzbare Codebasis wirken.
So wird dynamisches Laden von Erweiterungen umgesetzt, ausgelöst über das SQL-Statement LOAD.
Leitfaden für Entwickler
Wir sehen zwei Hauptgruppen, die Erweiterungen mit DuckDB-Wasm nutzen.
- Entwickler, die mit DuckDB-Wasm arbeiten: Wenn Sie eine Website oder eine Bibliothek bauen, die DuckDB-Wasm umhüllt, bedeutet die neue Erweiterungssupport ein breiteres Funktionsspektrum für Ihre Nutzer.
- Entwickler, die an DuckDB-Erweiterungen arbeiten: Wenn Sie eine DuckDB-Erweiterung geschrieben haben oder das vorhaben, erwägen Sie den Port nach DuckDB-Wasm. Das DuckDB-Erweiterungs-Template-Repository enthält die Konfiguration für die Kompilierung nach DuckDB-Wasm.
Einschränkungen
DuckDB-Wasm-Erweiterungen haben einige inhärente Grenzen. Zum Beispiel ist die Kommunikation mit nativen Executables auf Ihrer Maschine nicht möglich – das brauchen manche Erweiterungen, etwa die postgres-Scanner-Erweiterung.
Außerdem wird die Kompilierung nach Wasm für manche Bibliotheken, auf die Sie sich stützen, derzeit nicht unterstützt, oder Fähigkeiten sind nicht eins zu eins mit lokalen Executables, weil der Browser zusätzliche Anforderungen stellt, insbesondere rund um unsichere HTTP-Anfragen.
Fazit
In diesem Beitrag haben wir erklärt, wie DuckDB-Wasm Erweiterungen unterstützt, und das an mehreren Erweiterungen gezeigt: TPC-H, Parquet und spatial.
Dank der Portabilität von DuckDB funktionieren die Skripte aus diesem Beitrag auch auf dem Smartphone:

Für Neuigkeiten folgen Sie diesem Blog und treten Sie dem Wasm-Kanal in unserem Discord bei. Wenn Sie ein Beispiel haben, was mit Erweiterungen in DuckDB möglich ist, lassen Sie es uns wissen!