ADBC Scanner
Connect DuckDB to any database with an Arrow Database Connectivity (ADBC) driver — SQLite, PostgreSQL, Snowflake, Flight SQL, and more — using Arrow's columnar wire format end to end. Read, write, and inspect remote schemas without round-tripping through CSV/Parquet.
Install
-- Install the extension
INSTALL adbc_scanner FROM community;
-- Load it into your session
LOAD adbc_scanner;
-- Connect to SQLite via its ADBC driver
SET VARIABLE conn = (SELECT adbc_connect({'driver':'sqlite', 'uri':':memory:'}));
-- Run a query — result is a regular DuckDB table
SELECT * FROM adbc_scan(getvariable('conn')::BIGINT, 'SELECT * FROM my_table');
-- DDL / DML
SELECT adbc_execute(getvariable('conn')::BIGINT, 'CREATE TABLE users(id INT, name TEXT)');
-- Done
SELECT adbc_disconnect(getvariable('conn')::BIGINT); Technical Overview
Why Use ADBC Scanner?
Talk to any ADBC-capable database from DuckDB using Apache Arrow's columnar wire format end-to-end — no row conversions, no driver-specific glue.
🏹 What is ADBC?
Arrow Database Connectivity is a database-client API standard from the Apache Arrow project that moves data as Arrow columnar batches all the way from the database engine to your query. Where ODBC and JDBC convert rows on each side of the wire, ADBC keeps the columnar layout end-to-end.
- • Standard API, many drivers: Drivers exist for SQLite, PostgreSQL, Snowflake, BigQuery, Flight SQL, DuckDB itself, and a growing list of others — anywhere data already lives in or near Arrow.
- • No row-format intermediate: Result batches arrive as Arrow record batches and feed straight into DuckDB's vectorized executor without transposition or per-row allocation.
- • Pluggable driver model: Drivers are loaded as shared libraries (or via lightweight
manifest.tomlfiles) at runtime — no recompiling DuckDB to add support for a new database.
🔌 Two ways to use it
The extension exposes the same underlying ADBC machinery through two surfaces — pick by what you're trying to do. Both can be loaded simultaneously and target the same remote database.
- • Catalog mode —
ATTACH ... (TYPE adbc, driver '…'): Mounts the remote database as a DuckDB catalog. Schemas and tables show up under the alias you giveATTACH; projection and filter pushdown happen automatically; the driver streams Arrow batches into DuckDB's executor. Read-only — DDL and DML aren't supported through the catalog interface. - • Function mode —
adbc_connect+ scan / execute / insert: Returns aBIGINTconnection handle you stash in a SQL variable. Pass it toadbc_scan/adbc_scan_tablefor arbitrary SELECTs,adbc_executefor DDL/DML,adbc_insertfor Arrow-native bulk inserts, andadbc_commit/adbc_rollback(afteradbc_set_autocommit) for explicit transactions. Close withadbc_disconnect. - • Schema introspection:
adbc_tables,adbc_columns, andadbc_schemaexpose the remote catalog without running data queries. Useful for dashboards and schema-driven ETL that need to adapt as the upstream changes — and works whether you're in catalog mode, function mode, or both.
📦 Driver Setup
ADBC drivers ship as shared libraries (libadbc_driver_*.dylib / .so / .dll) plus an optional manifest.toml that names the symbol to load. The extension resolves them in two ways depending on how you reference them in adbc_connect.
- • By name (manifest-resolved):
'driver': 'sqlite'looks up a.tomlmanifest in the standard ADBC search paths. Most install methods —pip install adbc-driver-postgresql, Homebrew, system packages — drop manifests in the right place automatically. - • By explicit path:
'driver': '/path/to/libadbc_driver_xxx.dylib'skips manifest resolution — useful for vendored drivers or unusual install locations. - • Custom search paths: Pass
'search_paths': '/opt/adbc/drivers'inside the connect options to add manifest directories without changing OS-level config.
🆚 ADBC Scanner vs Airport
Both extensions stream Arrow over the wire, but they speak different protocols and target different worlds. Use this section to pick the right tool — or load both, since they coexist cleanly.
- • Use ADBC Scanner: When the system on the other end is a database with an ADBC driver — SQLite, PostgreSQL, Snowflake, BigQuery, MySQL, etc. ADBC is a client API standard.
- • Use Airport: When the system on the other end speaks Arrow Flight directly — typically custom services or Flight-native systems like Dremio. Flight is an RPC protocol, not a client API. See the
airportextension. - • Both can coexist: Nothing stops you from loading both: ADBC Scanner for traditional databases,
airportfor Flight-native services, all queryable from the same DuckDB session.
🎯 Common Use Cases
Federate live OLTP databases
Run cross-database analytics by pulling tables from PostgreSQL, MySQL, or SQLite into DuckDB on demand — no ETL machinery, no scheduled syncs. Use adbc_scan_table for whole tables or adbc_scan for filtered queries.
Bulk-load from cloud warehouses
Scan Snowflake or BigQuery query results directly into DuckDB for local analysis, joining against Parquet, CSV, or other DuckDB-native sources.
Schema-driven dashboards and ETL
Use adbc_tables / adbc_columns / adbc_schema to introspect remote catalogs and generate dashboards or pipelines that adapt to schema changes without code edits.
Arrow-native bulk export
Push DuckDB query results into a remote ADBC database with adbc_insert — columnar end-to-end, no row-format detour.
Deep Dive
Technical Details
Attaching as a catalog (recommended)
Most users want the catalog interface. One ATTACH and the remote database appears as an ordinary DuckDB catalog — schemas, tables, projection and filter pushdown, all without writing any adbc_* function calls:
-- libpq reads credentials from the URI itself, not separate options.
ATTACH 'postgresql://reader:secret@localhost/analytics' AS pg (
TYPE adbc,
driver 'postgresql'
);
SELECT user_id, COUNT(*)
FROM pg.public.activity
WHERE ts >= CURRENT_DATE - 7
GROUP BY user_id;
The full parameter list and more examples live in the Catalogs section. Catalog mode is read-only — for DDL, DML, transactions, or bulk Arrow-native inserts, drop down to function mode below.
Function mode (when you need to write)
When you need to mutate the remote, run server-side SQL, or do Arrow-native bulk inserts, adbc_connect returns a BIGINT handle that points at a driver-managed connection. Stash it in a SQL variable so you can reuse it across statements:
SET VARIABLE conn = (SELECT adbc_connect({'driver': 'postgresql', 'uri': :pg_uri}));
The handle is opaque — don’t compute on it, just pass it back into the other adbc_* functions. Connections are cleaned up when the DuckDB process exits, but call adbc_disconnect explicitly when you’re done — it releases the driver’s connection-pool slot immediately rather than waiting for process teardown.
Performance
The extension passes Arrow record batches straight from the driver into DuckDB’s vectorized executor with no transposition. Compared to a row-format client (ODBC, JDBC, libpq), three things change:
- No serialize/deserialize per row. Each batch is a contiguous block of columns the driver hands DuckDB by pointer; DuckDB reads it in place.
- Streaming. Results start producing rows as the first batch arrives —
LIMITshort-circuits the upstream query if the driver supports it, just like reading a Parquet file. Applies to both catalog mode (via the table-scan path) andadbc_scan. - Push-down depends on the driver. In catalog mode, DuckDB pushes projections and filter predicates into the driver automatically; the driver and the remote database decide what to do with them. In function mode, push-down only goes as far as the SQL string you hand to
adbc_scan. UseEXPLAINon the remote database (viaadbc_execute) when shape matters.
For adbc_insert, the same applies in reverse: DuckDB hands the driver Arrow batches it produced from your SELECT, so a SELECT * FROM big_table doesn’t materialize as rows on the way out.
Securing credentials
Connection options are plain SQL values, so anything you pass inline is visible in query logs and the catalog. The recommended pattern is parameterizing through SQL variables seeded from the environment, so the literal credential never appears in any cached SQL plan:
-- Catalog mode — libpq pulls user/pass from the URI directly. The URI
-- itself is a string literal (no `:bind_var` interpolation in ATTACH),
-- so build it elsewhere if you need to compose it from secrets.
ATTACH 'postgresql://reader:secret@localhost/analytics' AS pg (
TYPE adbc,
driver 'postgresql'
);
-- Function mode
SET VARIABLE conn = (SELECT adbc_connect({
'driver': 'postgresql',
'uri': 'postgresql://localhost:5432/analytics',
'username': 'reader',
'password': :pg_password
}));
getenv runs at parse time, so the literal password never appears in the cached SQL plan.
Drivers loaded by name resolve through the ADBC manifest search path; drivers loaded by absolute path skip that. In either case, the driver runs in DuckDB’s address space with the user’s privileges. Only load drivers you trust. In a multi-tenant or hosted DuckDB environment, treat both ATTACH ... (TYPE adbc, ...) and adbc_connect the same way you’d treat LOAD — restrict them to roles that already have arbitrary-code-execution authority. See the ADBC driver list for vetted upstream drivers.
Compared to alternatives
- DuckDB’s
postgres_scanner/mysql_scanner/sqlite_scanner— purpose-built per-database extensions, often faster for the database they target. Reach for ADBC Scanner when no purpose-built extension exists, when you need a single client surface across many backends, or when you specifically want Arrow-end-to-end (e.g., for Snowflake or BigQuery). airport— the same Arrow-end-to-end principle but over Arrow Flight instead of ADBC. Use it when the upstream is Flight-native; use this when the upstream is a database with an ADBC driver.- Python
pyarrow.flight/adbc_driver_*+ DataFrames — much heavier setup; you give up DuckDB’s vectorized execution against the streamed data and re-introduce row-format conversion at the DataFrame boundary.
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Description | |
|---|---|---|
| ADBC Catalog | ||
| adbc | Attach an [ADBC](https://arrow | |
| Connection Open, close, and inspect ADBC connections. Every other function takes a BIGINT connection handle returned by | ||
| adbc_clear_cache() | Drop any cached driver / connection state | |
| adbc_connect() | Open a connection to a remote database via an ADBC driver | |
| adbc_disconnect() | Close a connection opened with adbc_connect and free its resources | |
| adbc_info() | Return driver and server metadata for an open connection — vendor name, version strings, supported features | |
| Database Connection | ||
| adbc | Stored credentials and connection details for ADBC drivers | |
| Mutation Write — DDL/DML through | ||
| adbc_execute() | Execute a non-SELECT statement (DDL or DML) | |
| adbc_insert() | Bulk-insert into a remote table — passes Arrow record batches directly through the driver's bulk-load path | |
| Query Read data from the remote database — full SQL queries ( | ||
| adbc_scan() | Execute a SELECT and return its rows as a DuckDB table | |
| adbc_scan_table() | Stream an entire remote table by name, without writing SQL — equivalent to adbc_scan(conn, 'SELECT * FROM <table>') but routed through ADBC's bulk-read API where supported | |
| Schema Inspect the remote catalog — tables, columns, table types, full Arrow schemas — through the driver's metadata API. | ||
| adbc_columns() | Describe the columns of a table — name, type, nullability, default — pulled from the driver's catalog API | |
| adbc_schema() | Return the full Arrow schema for a query result without executing it | |
| adbc_table_types() | List the table-type categories the driver/server distinguishes (e | |
| adbc_tables() | List tables visible to the connection | |
| Transactions Manual transaction control. Toggle autocommit, then commit or roll back explicitly. | ||
| adbc_commit() | Commit the current transaction on the connection | |
| adbc_rollback() | Roll back the current transaction | |
| adbc_set_autocommit() | Toggle autocommit mode | |
API Reference
Function Documentation
Detailed documentation for each function including signatures, parameters, and examples.
adbc_clear_cache
Signature
Parameters
| Parameter | Type | Mode | Description |
|---|
Returns
Description
Drop any cached driver / connection state. Useful after dynamic driver loading or when libraries are swapped.
Examples
SELECT adbc_clear_cache(); Output
| adbc_clear_cache() |
|---|
| false |
adbc_columns
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named | |
catalog | VARCHAR | Named | |
schema | VARCHAR | Named | |
table_name | VARCHAR | Named | |
column_name | VARCHAR | Named |
Returns
Description
Describe the columns of a table — name, type, nullability, default — pulled from the driver's catalog API.
Examples
SELECT * FROM adbc_columns(conn); SELECT * FROM adbc_columns(conn, table_name := 'users'); SELECT * FROM adbc_columns(conn, table_name := 'users', column_name := 'id'); Related Functions
adbc_commit
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Positional |
Returns
Description
Commit the current transaction on the connection. Pair with adbc_set_autocommit(false) for explicit transaction control.
Examples
SELECT adbc_commit(connection_handle); Related Functions
adbc_connect
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
options | ANY | Positional |
Returns
Description
Open a connection to a remote database via an ADBC driver. Pass a STRUCT/MAP with at least driver and uri. Returns a BIGINT connection handle that every subsequent function call uses.
Examples
SELECT adbc_connect({'driver': 'sqlite', 'uri': ':memory:'}); Output
| adbc_connect(main.struct_pack(driver := 'sqlite', uri := ':memory:')) |
|---|
| 105553167043096 |
SELECT adbc_connect({'driver': '/path/to/driver.so', 'uri': 'connection_string'}); Related Functions
adbc_disconnect
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Positional |
Returns
Description
Close a connection opened with adbc_connect and free its resources.
Examples
SELECT adbc_disconnect(connection_handle); adbc_execute
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Positional | |
query | VARCHAR | Positional |
Returns
Description
Execute a non-SELECT statement (DDL or DML). Use for CREATE / INSERT / UPDATE / DELETE / DROP / ALTER.
Examples
SELECT adbc_execute(conn, 'CREATE TABLE test (id INTEGER)'); SELECT adbc_execute(conn, 'INSERT INTO test VALUES (1)'); SELECT adbc_execute(conn, 'DELETE FROM test WHERE id = 1'); Related Functions
adbc_info
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named |
Returns
Description
Return driver and server metadata for an open connection — vendor name, version strings, supported features.
Examples
SELECT * FROM adbc_info(connection_handle); adbc_insert
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named | |
table_name | VARCHAR | Named | |
data | TABLE | Named | |
mode | VARCHAR | Named |
Returns
Description
Bulk-insert into a remote table — passes Arrow record batches directly through the driver's bulk-load path. Faster than row-by-row INSERT.
Examples
SELECT * FROM adbc_insert(conn, 'target_table', (SELECT * FROM source_table)); SELECT * FROM adbc_insert(conn, 'target', (SELECT * FROM source), mode := 'create'); SELECT * FROM adbc_insert(conn, 'target', (SELECT * FROM source), mode := 'append'); Related Functions
adbc_rollback
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Positional |
Returns
Description
Roll back the current transaction.
Examples
SELECT adbc_rollback(connection_handle); Related Functions
adbc_scan
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named | |
query | VARCHAR | Named | |
params | ANY | Named | |
batch_size | BIGINT | Named |
Returns
Description
Execute a SELECT and return its rows as a DuckDB table. The headline read function — pass a SQL string and a connection handle.
Examples
SELECT * FROM adbc_scan(conn, 'SELECT * FROM users'); SELECT * FROM adbc_scan(conn, 'SELECT * FROM users WHERE id = ?', params := row(42)); SELECT * FROM adbc_scan(conn, 'SELECT * FROM large_table', batch_size := 65536); Related Functions
adbc_scan_table
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named | |
table_name | VARCHAR | Named | |
catalog | VARCHAR | Named | |
schema | VARCHAR | Named | |
batch_size | BIGINT | Named |
Returns
Description
Stream an entire remote table by name, without writing SQL — equivalent to adbc_scan(conn, 'SELECT * FROM <table>') but routed through ADBC's bulk-read API where supported.
Examples
SELECT * FROM adbc_scan_table(conn, 'users'); SELECT * FROM adbc_scan_table(conn, 'users', schema := 'public'); SELECT * FROM adbc_scan_table(conn, 'large_table', batch_size := 65536); Related Functions
adbc_schema
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named | |
table_name | VARCHAR | Named | |
catalog | VARCHAR | Named | |
schema | VARCHAR | Named |
Returns
Description
Return the full Arrow schema for a query result without executing it. Useful for prepared-statement-style introspection.
Examples
SELECT * FROM adbc_schema(conn, 'users'); SELECT * FROM adbc_schema(conn, 'users', catalog := 'main'); Related Functions
adbc_set_autocommit
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Positional | |
enabled | BOOLEAN | Positional |
Returns
Description
Toggle autocommit mode. Pass false to start an explicit transaction; pair with adbc_commit / adbc_rollback.
Examples
SELECT adbc_set_autocommit(connection_handle, false); SELECT adbc_set_autocommit(connection_handle, true); Related Functions
adbc_table_types
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named |
Returns
Description
List the table-type categories the driver/server distinguishes (e.g. TABLE / VIEW / SYSTEM TABLE).
Examples
SELECT * FROM adbc_table_types(conn); Related Functions
adbc_tables
Signature
Parameters (Named)
| Parameter | Type | Mode | Description |
|---|---|---|---|
connection_handle | BIGINT | Named | |
catalog | VARCHAR | Named | |
schema | VARCHAR | Named | |
table_name | VARCHAR | Named |
Returns
Description
List tables visible to the connection. Returns name, schema, and table type — driver-dependent filters available.
Examples
SELECT * FROM adbc_tables(conn); SELECT * FROM adbc_tables(conn, catalog := 'main'); SELECT * FROM adbc_tables(conn, table_name := 'users'); Related Functions
Database Storage
Storage Extensions
Storage extensions that enable secure database attachments with encryption, secret management, and credential rotation.
adbc
Description
Attach an [ADBC](https://arrow.apache.org/adbc/)-driven database as a DuckDB catalog. Once attached, DuckDB sees the remote schemas and tables as local objects — projection and filter pushdown happen automatically, and the executor pulls Arrow batches straight from the driver. Use this when you want a database-style interface; for ad-hoc reads / writes / arbitrary SQL, the [`adbc_scan`](#adbc_scan), [`adbc_execute`](#adbc_execute), and [`adbc_insert`](#adbc_insert) functions stay available alongside.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
driver | VARCHAR | Required | Driver identifier — a registered name (e.g. `'sqlite'`, `'postgresql'`), an absolute path to a `libadbc_driver_*` shared library, or a manifest name resolved via the ADBC search paths. |
entrypoint | VARCHAR | Optional | Custom driver entrypoint function name. Only needed for drivers that don't follow the standard `AdbcDriverInit` symbol convention. |
search_paths | VARCHAR | Optional | Extra colon-separated directories to look in for ADBC manifest (`*.toml`) files, in addition to the platform defaults. |
use_manifests | VARCHAR | Optional | Default: true Set to `'false'` to skip manifest resolution entirely (load the driver only by absolute path). |
batch_size | INTEGER | Optional | Hint for the number of rows per Arrow batch when scanning. Larger values reduce per-batch overhead at the cost of memory. |
Examples
Attach a PostgreSQL database via the postgresql ADBC driver
-- libpq pulls credentials from the URI; ATTACH parameters don't interpolate bind variables.
ATTACH 'postgresql://reader:secret@localhost/mydb' AS pg (
TYPE adbc,
driver 'postgresql'
);
-- Query as if it were local — projection + filter pushdown happen automatically
SELECT user_id, COUNT(*) AS events
FROM pg.public.activity
WHERE ts >= CURRENT_DATE - 7
GROUP BY user_id; Attach a SQLite database file via the sqlite ADBC driver
ATTACH '/data/app.db' AS local_app (
TYPE adbc,
driver 'sqlite'
);
SHOW TABLES FROM local_app.main; Attach using an explicit driver-library path (skips manifest resolution)
ATTACH 'snowflake://account.snowflakecomputing.com' AS sf (
TYPE adbc,
driver '/opt/adbc/lib/libadbc_driver_snowflake.dylib',
username :sf_user,
password :sf_password,
use_manifests 'false'
); Security
Secrets
DuckDB secrets for securely storing credentials and encryption keys used by the adbc scanner extension.
adbc
Description
Stored credentials and connection details for ADBC drivers. Reference a secret by name in `adbc_connect`, or rely on `SCOPE`-based URI lookup so the right secret is selected automatically when its scope prefix matches the connection URI.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| driver | VARCHAR | Required | ADBC driver name (e.g. `'sqlite'`, `'postgresql'`) or path to a shared library. |
| uri | VARCHAR | Optional | Connection URI passed to the driver. Driver-specific format. |
| username | VARCHAR | Optional | Database username. |
| password | VARCHAR | Optional | Database password. Automatically redacted in logs. |
| database | VARCHAR | Optional | Database name, when not encoded in the URI. |
| entrypoint | VARCHAR | Optional | Custom driver entrypoint function name (rarely needed). |
Examples
PostgreSQL with credentials and a SCOPE for auto-lookup
CREATE SECRET my_postgres (
TYPE adbc,
SCOPE 'postgresql://prod-server:5432',
driver 'postgresql',
uri 'postgresql://prod-server:5432/mydb',
username 'app_user',
password 'secret_password'
); Local SQLite
CREATE SECRET my_sqlite (
TYPE adbc,
SCOPE 'sqlite://data',
driver 'sqlite',
uri '/var/data/app.db'
); Use the secret explicitly by name
SET VARIABLE conn = (SELECT adbc_connect({'secret': 'my_postgres'})); Auto-lookup — DuckDB picks the secret whose SCOPE matches the URI
SET VARIABLE conn = (SELECT adbc_connect({
'uri': 'postgresql://prod-server:5432/mydb'
})); Persist the secret to ~/.duckdb/secrets/
CREATE PERSISTENT SECRET my_postgres (
TYPE adbc,
SCOPE 'postgresql://prod-server:5432',
driver 'postgresql',
uri 'postgresql://prod-server:5432/mydb',
username 'app_user',
password 'secret_password'
); Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
There are two ways to use the extension. Pick by what you’re doing:
- Catalog mode (
ATTACH ... TYPE adbc) — read-only, but the remote database appears as a normal DuckDB catalog with automatic projection and filter pushdown. Best for plain SELECT-style analytics. - Function mode (
adbc_connect+adbc_scan/adbc_execute/adbc_insert) — full read/write access including DDL, DML, and bulk Arrow-native inserts. Best when you need to mutate the remote, run server-side SQL, or hold a long-lived connection.
Both can coexist in the same session.
Attach a remote database (catalog mode)
-- PostgreSQL via the postgresql ADBC driver. libpq reads its credentials
-- from the URI (postgresql://user:pass@host/db), so embed them there
-- rather than passing username/password as separate ATTACH parameters.
ATTACH 'postgresql://reader:secret@localhost/analytics' AS pg (
TYPE adbc,
driver 'postgresql'
);
-- Query as if it were a local DuckDB catalog
SELECT user_id, COUNT(*) AS events
FROM pg.public.activity
WHERE ts >= CURRENT_DATE - 7
GROUP BY user_id;
-- SQLite file via the sqlite driver
ATTACH '/data/app.db' AS local_app (TYPE adbc, driver 'sqlite');
SHOW TABLES FROM local_app.main;
Catalog mode is read-only — see Catalogs for the full parameter list. Reach for function mode below when you need to write.
Open a connection (function mode)
-- By driver name (driver manifest must be installed)
SET VARIABLE conn = (SELECT adbc_connect({
'driver': 'sqlite',
'uri': ':memory:'
}));
-- Or by explicit driver path
SET VARIABLE conn = (SELECT adbc_connect({
'driver': '/path/to/libadbc_driver_sqlite.dylib',
'uri': '/data/app.db'
}));
-- PostgreSQL with credentials
SET VARIABLE conn = (SELECT adbc_connect({
'driver': 'postgresql',
'uri': 'postgresql://localhost:5432/analytics',
'username': 'reader',
'password': :pg_password
}));
Stash the connection handle in a SQL variable and reuse it across statements.
Query
-- Run any SELECT
SELECT * FROM adbc_scan(getvariable('conn')::BIGINT,
'SELECT * FROM orders WHERE created_at > now() - INTERVAL 1 DAY');
-- Or stream a whole table by name
SELECT * FROM adbc_scan_table(getvariable('conn')::BIGINT, 'orders');
Result is a normal DuckDB table — JOIN against local tables, persist with CTAS, re-export.
Mutation
-- DDL
SELECT adbc_execute(getvariable('conn')::BIGINT,
'CREATE TABLE users(id INT, name TEXT)');
-- DML
SELECT adbc_execute(getvariable('conn')::BIGINT,
'INSERT INTO users VALUES (1, ''Alice'')');
-- Bulk insert from a DuckDB query — Arrow-native, no row-format conversion.
-- adbc_insert is a TABLE function, so it goes in the FROM clause.
SELECT * FROM adbc_insert(
getvariable('conn')::BIGINT,
'users',
(SELECT id, name FROM staging_users)
);
Transactions
SELECT adbc_set_autocommit(getvariable('conn')::BIGINT, FALSE);
SELECT adbc_execute(getvariable('conn')::BIGINT, 'INSERT INTO orders VALUES (...)');
SELECT adbc_execute(getvariable('conn')::BIGINT, 'UPDATE inventory SET qty = qty - 1 WHERE sku = ''X''');
SELECT adbc_commit(getvariable('conn')::BIGINT);
-- or adbc_rollback on error
Inspect the remote schema
-- All tables
SELECT * FROM adbc_tables(getvariable('conn')::BIGINT);
-- Columns of a specific table (filters are named parameters)
SELECT * FROM adbc_columns(getvariable('conn')::BIGINT, table_name := 'orders');
-- Full Arrow schema of a query without running it
SELECT * FROM adbc_schema(getvariable('conn')::BIGINT, 'SELECT * FROM orders');
Useful for building dynamic dashboards or schema-driven ETL pipelines.
Disconnect
SELECT adbc_disconnect(getvariable('conn')::BIGINT);
Connections are also cleaned up when the DuckDB process exits, but explicit disconnect is good hygiene.
Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
Supported platform architectures
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | aarch64 | 9.57 MB |
| Linux | x86_64 | 10.84 MB |
| Linux (musl) | x86_64 | 11.30 MB |
| macOS | Apple Silicon | 7.41 MB |
| macOS | Intel | 8.42 MB |
| Windows | x86_64 | 7.74 MB |
Gzipped download size from the DuckDB community-extensions registry.
Talk to Any ADBC-Speaking Database
Install ADBC Scanner to query, mutate, and inspect external databases through their ADBC drivers — Arrow-native, zero-serialization, broad ecosystem support.