Query Snowflake, PostgreSQL, MySQL, Trino, Microsoft SQL Server, Apache DataFusion, Arrow Flight SQL, and any other system with an ADBC driver directly from DuckDB.
The extension registers as
adbc_scannerinternally; its functions and theATTACH ... (TYPE adbc)storage type are exposed under theadbcname.
Full documentation, including installation, driver setup, the ATTACH storage layer, the function reference, secrets, connection profiles, and cookbook examples, is available at:
https://query.farm/products/extensions/adbc_scanner
INSTALL adbc_scanner FROM community;
LOAD adbc_scanner;The adbc_* functions take the alias of an ATTACH … (TYPE adbc) database
instead of a connection handle. adbc_connect, adbc_disconnect,
adbc_commit, adbc_rollback and adbc_set_autocommit are removed: connect
with ATTACH, disconnect with DETACH, and use DuckDB's BEGIN / COMMIT /
ROLLBACK.
ATTACH 'shared.sqlite' AS db (TYPE adbc, driver 'sqlite');
CALL adbc_execute('db', 'CREATE TABLE IF NOT EXISTS messages (id INTEGER, body TEXT)');
CALL adbc_execute('db', 'INSERT INTO messages VALUES (1, ''hello'')');
SELECT * FROM adbc_scan_table('db', 'messages');
SELECT * FROM adbc_scan('db',
'SELECT id, body FROM messages WHERE id = ?', params := row(1),
columns := {'id': 'BIGINT', 'body': 'VARCHAR'});
BEGIN;
CALL adbc_execute('db', 'INSERT INTO messages VALUES (2, ''pending'')');
INSERT INTO db.messages VALUES (3, 'also pending');
ROLLBACK; -- discards both writes
DETACH db;Inside a BEGIN … COMMIT transaction, adbc_execute and adbc_insert join
the attachment's transaction (they commit or roll back with writes made through
db.…), and reads see its uncommitted writes; otherwise they autocommit on the
attachment's own connection, which reads also use, so session state such as
temporary tables carries across calls. Two adbc_* reads of one attachment
cannot run at the same time (for example a self-join); attach the database
twice for that. Writes to a READ_ONLY attachment are rejected. Secrets and connection profiles work through ATTACH options.
adbc_execute is a CALL-only command: EXPLAIN and PREPARE do not execute
it, EXPLAIN ANALYZE does. It returns one rows_affected value, or SQL NULL
if the driver does not supply a count.
Bulk ingestion with adbc_insert uses a bounded producer queue. Stream binding
and execution run together on its consumer thread, so drivers that read during
BindStream (including Grainlift) can ingest without blocking producer startup.
Query binding uses ADBC schema metadata only. Drivers such as SQLite that cannot
describe arbitrary queries without executing them require columns := {...}.
adbc_scan_table uses table metadata. Returned column counts and types are checked
against the bound schema before Arrow data is read.
See the migration guide for the full mapping from handles to attached databases, typed options, and driver limitations.
ADBC secrets may omit SCOPE when they specify a non-empty URI; the URI then
becomes the default lookup scope. Explicit scopes remain unchanged and can
match a different or broader prefix. Named secret references work either way.
CREATE SECRET example (
TYPE adbc,
DRIVER 'postgresql',
URI 'postgresql://host/database'
);This default requires an extension build containing the change. A secret with neither a URI nor an explicit scope is rejected.
For instructions on building the extension from source and running its tests, see docs/BUILDING.md.