thunderduck-sqlalchemy
A SQLAlchemy dialect and PEP 249 DBAPI for thunderduck. Query your lakehouse from anything that speaks SQLAlchemy — Superset, Airflow, Dagster, Redash, pandas, Jupyter, Streamlit.
pip install thunderduck-sqlalchemy
Works with SQLAlchemy 1.4 and 2.x, on Python 3.10+ — so it installs cleanly into Apache Superset (which pins SQLAlchemy 1.4) as well as modern Airflow, Dagster and pandas environments.
Connect
Create an API token in the thunderduck console (Settings → API Tokens; it
starts with tdk_ and is shown once), then:
from sqlalchemy import create_engine, text
from sqlalchemy.pool import NullPool
engine = create_engine(
"thunderduck://:tdk_your_token@api.thunderduck.io/",
poolclass=NullPool,
)
with engine.connect() as conn:
for row in conn.execute(text('SELECT * FROM "lego"."public"."lego_sets" LIMIT 10')):
print(row)
pandas works directly:
import pandas as pd
df = pd.read_sql('SELECT * FROM "thunder_duck_demo"."iris"', engine)
URL options
| Option | Default | Meaning |
|---|---|---|
password / ?token= |
— | Required. Your tdk_… API token. |
?ssl=false |
true |
Use plain HTTP. For in-cluster use against the console-api Service. |
?poll_ms= |
1000 |
How often to poll for completion. |
?timeout_ms= |
300000 |
How long to wait for a query. |
?arraysize= |
1000 |
Rows fetched per page. |
Table names
thunderduck names are catalog.schema.table (flat-file catalogs) or
catalog.table (Iceberg/Nessie catalogs). SQLAlchemy models two levels, so
the schema is the whole dotted prefix:
from sqlalchemy import MetaData, Table
md = MetaData()
sets = Table("lego_sets", md, schema="lego.public", autoload_with=engine) # 3-level
iris = Table("iris", md, schema="thunder_duck_demo", autoload_with=engine) # 2-level
Reflection (inspect(engine).get_schema_names() etc.) reads thunderduck's
catalog API rather than issuing SQL, so browsing schemas is fast and free.
Apache Superset
Installing the package is all that is needed — it registers a Superset DB engine spec automatically. Add a database of type Other with:
thunderduck://:tdk_your_token@api.thunderduck.io/
Superset's stop button genuinely cancels the running query.
How it works, and what follows from that
Every statement is submitted to thunderduck's REST API, which runs it as an isolated Kubernetes Job; results are then paged back over HTTP. That model has consequences worth knowing up front:
- Latency. Expect seconds, not milliseconds, before the first row. Built for analytics and BI, not for tight interactive loops.
- Read-only. No DML, no DDL, no transactions.
commit()androllback()are no-ops. - Use
NullPool. A connection holds no server-side state, so pooling buys nothing. The driver also never issues aSELECT 1health check, because that would start a whole Job. - Parameters are rendered client-side. thunderduck's API accepts SQL only,
so this driver renders bound parameters into the statement, escaping them
strictly and refusing types it does not recognise. Always pass untrusted
values as parameters — never build SQL by string concatenation. The
paramstyle is
pyformat, so — as with psycopg2 — a literal percent sign in hand-written SQL must be escaped as%%when you pass parameters. - Row ceiling. The server caps how many rows one page returns and how many a result set stores. Very large result sets are truncated; aggregate in SQL rather than pulling raw rows.
Also available
For JVM tools (DBeaver, DataGrip) there is a JDBC driver — see the
jdbc-driver/ directory of the thunderduck repository.
Development
uv sync --dev
uv run pytest
uv run ruff check . && uv run ruff format --check .
One gotcha: the committed uv.lock resolves SQLAlchemy 2.x, so a plain
uv run pytest only exercises one of the two supported majors. Apache
Superset pins sqlalchemy<2, so check that combination too before releasing:
uv venv --python 3.10 .sa14
uv pip install --python .sa14 -e . "sqlalchemy<2" pytest
.sa14/bin/python -c "import sqlalchemy; print('SQLALCHEMY', sqlalchemy.__version__)"
.sa14/bin/pytest -q
CI runs both legs on every push.
License
Apache-2.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file thunderduck_sqlalchemy-0.1.0.tar.gz.
File metadata
- Download URL: thunderduck_sqlalchemy-0.1.0.tar.gz
- Upload date:
- Size: 18.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d01df25cd6e226e7ac70662ee5609e4d3ac5daa44d465decb251ce39c72ddaa
|
|
| MD5 |
5b86ed689d2f423b26e72e20a04e3004
|
|
| BLAKE2b-256 |
0c200464942e674d7261ef05b4e25d27492d591c205864a7ec72a5655f8d335f
|
Provenance
The following attestation bundles were made for thunderduck_sqlalchemy-0.1.0.tar.gz:
Publisher:
publish-sqlalchemy.yml on 212data/thunderduck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
thunderduck_sqlalchemy-0.1.0.tar.gz -
Subject digest:
9d01df25cd6e226e7ac70662ee5609e4d3ac5daa44d465decb251ce39c72ddaa - Sigstore transparency entry: 2521476000
- Sigstore integration time:
-
Permalink:
212data/thunderduck@bfaaf34b17f1884339c6c76788fb917d1185fd71 -
Branch / Tag:
refs/tags/thunderduck-sqlalchemy-v0.1.0 - Owner: https://github.com/212data
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-sqlalchemy.yml@bfaaf34b17f1884339c6c76788fb917d1185fd71 -
Trigger Event:
push
-
Statement type:
File details
Details for the file thunderduck_sqlalchemy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: thunderduck_sqlalchemy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6eeff47b9d208e086b39f106fa7a70e756719bf95a4a95f9e4739bd40d94222f
|
|
| MD5 |
bddb14cfec63aec1fe9b74d9a3f5e71b
|
|
| BLAKE2b-256 |
c0df98120ec3b2d30721528efa956ad28876ce32fba258caa3eb1a20db864815
|
Provenance
The following attestation bundles were made for thunderduck_sqlalchemy-0.1.0-py3-none-any.whl:
Publisher:
publish-sqlalchemy.yml on 212data/thunderduck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
thunderduck_sqlalchemy-0.1.0-py3-none-any.whl -
Subject digest:
6eeff47b9d208e086b39f106fa7a70e756719bf95a4a95f9e4739bd40d94222f - Sigstore transparency entry: 2521476602
- Sigstore integration time:
-
Permalink:
212data/thunderduck@bfaaf34b17f1884339c6c76788fb917d1185fd71 -
Branch / Tag:
refs/tags/thunderduck-sqlalchemy-v0.1.0 - Owner: https://github.com/212data
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-sqlalchemy.yml@bfaaf34b17f1884339c6c76788fb917d1185fd71 -
Trigger Event:
push
-
Statement type: