Extending SQL to Causal Queries
April 16, 2026 · View on GitHub
The DO Operator
This project adds a DO() clause to DuckDB's SQL syntax:
SELECT AVG(disease_outcome) FROM observations DO(gene_expressed = 1)
DO(variable = value) is intended to express a causal intervention — "what is the average outcome when we set gene_expressed to 1?" It is implemented as a DuckDB 1.5 C++ extension using the ParserExtension API: when DuckDB's standard parser encounters the unknown DO(...) syntax, our extension intercepts the query, strips the clause, and stores the intervention for later execution.
Note: Parsing works. Execution (actually computing the causal estimate) is not yet implemented.
Background: Runtime-Extensible Parsers
This work is based on the CIDR 2025 paper Runtime-Extensible SQL Parsers Using PEG by Hannes Mühleisen and Mark Raasveldt (the creators of DuckDB). The paper argues that SQL parsers should be modifiable at runtime — without recompiling the database — so that domain-specific extensions can introduce new syntax.
DuckDB 1.5 ships this as the ParserExtension API. Extensions register a parse_function_t: a C++ callback that fires when the standard PostgreSQL-based parser fails. The extension receives the raw query string, parses what it understands, and returns a result that DuckDB plans and executes normally.
Our DO() clause is a direct application of this: the standard parser rejects DO(gene_expressed = 1), our callback intercepts it, strips and stores the intervention, and hands control back to DuckDB. No grammar files needed — just a C++ function registered at extension load time.
Toggling the Extension
On — use the extension's own DuckDB shell (the extension is built in):
./ext/build/release/duckdb
Off — use the system DuckDB (no extension loaded):
/usr/local/bin/duckdb
To load the extension on-demand in any DuckDB session:
LOAD './ext/build/release/extension/quack/quack.duckdb_extension';
Custom Queries
Store data in data/ (CSV or Parquet):
causal_sql/data/observations.csv
Write queries in queries/ as plain .sql files:
causal_sql/queries/my_query.sql
Run a query file:
./ext/build/release/duckdb -f queries/my_query.sql
Or run inline:
./ext/build/release/duckdb -c "SELECT AVG(disease_outcome) FROM read_csv_auto('data/observations.csv') DO(gene_expressed = 1);"