duckdb-adbc
The adbc extension lets DuckDB (v1.4.5+ or v1.5.4+) query any database that has an ADBC (Arrow Database Connectivity) driver — Snowflake, Databricks, BigQuery, PostgreSQL, MySQL, and more — directly from SQL, using zero-copy Arrow data transfer instead of legacy row-based APIs like ODBC/JDBC.
Repo: https://github.com/columnar-tech/duckdb-adbc-client
Prerequisites
1. Install the extension
From within a DuckDB session (or a .sql file / -c invocation):
INSTALL adbc FROM community;
LOAD adbc;
If DuckDB reports it can't find the extension, confirm the DuckDB version is 1.4.5+ (or 1.5.4+) — the extension requires a recent build.
2. Install dbc
ADBC drivers themselves are installed and managed with dbc, a small CLI from Columnar. Check whether it's already available with dbc --help; if not, install it with whichever of these fits the environment:
# shell (macOS/Linux)
curl -LsSf https://dbc.columnar.tech/install.sh | sh
# Homebrew
brew install columnar-tech/tap/dbc
# uv
uv tool install dbc
# pipx
pipx install dbc
# PowerShell (Windows)
powershell -ExecutionPolicy ByPass -c irm https://dbc.columnar.tech/install.ps1 | iex
# winget (Windows)
winget install dbc
3. Install an ADBC driver for the target database
dbc install sqlite
dbc install postgresql
dbc install snowflake
# ...etc.
If a query fails with a driver-not-found error, install the appropriate driver with dbc install <system> before retrying.
Connecting to a database
The extension connects via ADBC connection profiles — TOML files describing a driver and its options — referenced with a profile:// URI.
Create a profile
# mydb.toml
profile_version = 1
driver = "sqlite"
[Options]
uri = "./games.sqlite"
For remote/authenticated systems, add the relevant options, e.g.:
profile_version = 1
driver = "postgresql"
[Options]
uri = "postgresql://user:pwd@localhost:5432/app"
Save the profile to a discoverable location
# Linux
mv mydb.toml ~/.config/adbc/profiles/
# macOS
mv mydb.toml ~/Library/Application Support/ADBC/Profiles/
# Windows
move "mydb.toml" "%LOCALAPPDATA%\ADBC\Profiles\"
Once saved, reference the profile as profile://mydb (filename minus .toml) from SQL, or point directly at a path.
Prefer profiles over embedding credentials in ad-hoc SQL — they keep secrets out of query text and can be reused across sessions.
Common workflows
Ad-hoc read via read_adbc
Best for one-off queries, or when pushing a projection/predicate down to the remote system (the extension does not automatically push down projections/predicates for attached tables — use read_adbc directly for that).
INSTALL adbc FROM community;
LOAD adbc;
SELECT * FROM read_adbc('profile://mydb', 'SELECT * FROM games');
Persistent connection via ATTACH
Best when the remote database should behave like a local DuckDB catalog — supports catalog lookups, SELECT, INSERT, COPY, and CREATE TABLE AS (CTAS).
ATTACH 'profile://mydb' AS mydb (TYPE adbc);
USE mydb.main;
SHOW ALL TABLES;
SELECT * FROM games;
INSERT INTO games (SELECT 6, 'Battleship', 'Clifford Von Wickler', 1931, 7, 2, 2, 12.99);
-- Move data into or out of local DuckDB tables
CREATE TABLE memory.inventors AS (SELECT id, inventor FROM games);
CREATE TABLE game_inventors(id, inventor) AS (SELECT * FROM memory.inventors);
For systems with non-default schema/table delimiters (e.g. SQL Server's [schema].[table]):
ATTACH 'profile://mydb' AS mydb (TYPE adbc, DELIMITER '[]');
Arbitrary DDL/DML via adbc_execute
For statements that don't fit the SELECT/INSERT/COPY/CTAS surface (e.g. DROP TABLE, CREATE INDEX):
CALL adbc_execute('profile://mydb', 'DROP TABLE games');
Refreshing cached metadata
DuckDB caches schema/table metadata from attached ADBC databases locally. After a remote schema change, clear it:
CALL adbc_clear_cache();
Mixing ADBC reads and writes in one statement
Disabled by default to avoid concurrency bugs. To allow it:
SET adbc_mix_reads_writes = true;
To materialize input rows before INSERT/CTAS (also helps avoid concurrency issues when mixing reads/writes):
SET adbc_materialize_insert_rows = true;
Tuning connection pooling and insert batching
-- Max pooled connections per attached database (default 50)
SET adbc_connection_pool_size = 100;
-- Chunks buffered in memory before an ADBC bulk insert (default 1000, i.e. ~2M rows @ 2048 rows/chunk)
SET adbc_insert_buffer_size = 10000;
Limitations
- Autocommit only. Every statement takes effect immediately; there is no manual transaction control.
- No automatic pushdown. Predicate/projection pushdown isn't performed for attached (
ATTACH) tables — useread_adbcwith a scoped SQL query when pushdown matters. - No DuckDB-to-DuckDB. The extension doesn't currently support connecting to another DuckDB (or Quack) instance via ADBC.
- No intra-process concurrency. Concurrent ADBC operations within a single process aren't supported.
- Mixed reads/writes need an opt-in. See
adbc_mix_reads_writesabove.
Troubleshooting
- Extension won't load /
INSTALL adbc FROM communityfails. Confirm the DuckDB version is 1.4.5+ or 1.5.4+. - Driver not found / failed to load. Install it with
dbc install <system>; verify thedrivervalue in the profile matches the namedbcuses. - Profile not found (
profile://namefails). Confirm the.tomlfile is saved in the correct per-OS ADBC profile directory, or reference it by absolute path instead of name. - Authentication errors. Double-check the
uriand any credential fields under[Options]in the profile. For databases needing certificates, tokens, or extra parameters, consult the driver's own documentation viadbc. - Unexpected "Not implemented" error on INSERT. Likely a mixed read/write statement — set
adbc_mix_reads_writes = true(and consideradbc_materialize_insert_rows = true). - Stale results after an external schema change. Run
CALL adbc_clear_cache();.