Skip to content

adbc-poolhouse

adbc-poolhouse creates a SQLAlchemy QueuePool from a typed warehouse config. One config in, one pool out, with no boilerplate around driver detection or connection string assembly.

Installation

pip install adbc-poolhouse

Or with uv:

uv add adbc-poolhouse

ADBC drivers

adbc-poolhouse manages the pool, not the driver. You also need an ADBC driver for your target warehouse. Install the matching extra with adbc-poolhouse:

Warehouse Install command
PyPI drivers
Apache Arrow Flight SQL pip install adbc-poolhouse[flightsql]
BigQuery pip install adbc-poolhouse[bigquery]
DuckDB pip install adbc-poolhouse[duckdb]
PostgreSQL pip install adbc-poolhouse[postgresql]
Quack pip install --pre adbc-poolhouse[quack]
Snowflake pip install adbc-poolhouse[snowflake]
SQLite pip install adbc-poolhouse[sqlite]
Foundry-distributed drivers
ClickHouse Foundry-distributed — see Foundry installation
Databricks Foundry-distributed — see Foundry installation
MSSQL / Azure SQL / Fabric Foundry-distributed — see Foundry installation
MySQL Foundry-distributed — see Foundry installation
Redshift Foundry-distributed — see Foundry installation
Trino Foundry-distributed — see Foundry installation

First pool in five minutes

All supported warehouses have a typed config class.

PyPI-installed: BigQueryConfig, DuckDBConfig, FlightSQLConfig, PostgreSQLConfig, QuackConfig, SnowflakeConfig, SQLiteConfig.

Foundry-distributed: ClickHouseConfig, DatabricksConfig, MSSQLConfig, MySQLConfig, RedshiftConfig, TrinoConfig.

The example below uses DuckDB, which needs no credentials or running server.

from adbc_poolhouse import DuckDBConfig, create_pool, close_pool

# File-backed database (connections share the same file)
config = DuckDBConfig(database="/tmp/warehouse.db")
pool = create_pool(config)

with pool.connect() as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT 42 AS answer")
    row = cursor.fetchone()
    print(row)  # (42,)

close_pool(pool)

pool.connect() checks out a connection from the pool and returns it when the with block exits. close_pool(pool) drains the pool and closes the underlying ADBC source connection.

Async

For asyncio or trio code, create_async_pool, managed_async_pool, and close_async_pool mirror the sync entry points and run each blocking ADBC call on a worker thread. Install the [async] extra (pip install adbc-poolhouse[async]) and see the async pool guide.

The async API is experimental. Its surface may change between minor releases, so pin the version you build against. See the async pool guide for the full caveat.

import anyio
from adbc_poolhouse import DuckDBConfig, create_async_pool, close_async_pool


async def main():
    pool = create_async_pool(DuckDBConfig(database="/tmp/warehouse.db"))
    try:
        async with await pool.connect() as conn:
            cur = conn.cursor()  # synchronous, no await
            await cur.execute("SELECT 42 AS answer")
            table = await cur.fetch_arrow_table()
            print(table.column("answer")[0].as_py())  # 42
    finally:
        await close_async_pool(pool)


anyio.run(main)

What's next

  • Pool lifecycle — how to dispose correctly, pytest fixture patterns, and common mistakes
  • Async pool — the asyncio/trio wrapper, honest concurrency limits, and the one connection per task rule
  • Consumer patterns — wiring a pool into FastAPI and reading credentials from a dbt profiles file
  • Configuration reference — environment variable prefixes, pool tuning, and secret handling
  • Snowflake guide — supported auth methods and private key variants
  • Warehouse guides — per-warehouse install commands, auth examples, and env var prefixes
  • Custom backends — raw driver arguments and writing a reusable config class for an unsupported warehouse

See also