A DuckDB storage/catalog extension that attaches a
Firebird database and lets you query its tables with native SQL, with
no native fbclient required. It speaks the Firebird wire
protocol in pure Rust (via rsfbclient),
compiled into a C++ extension that registers a real DuckDB catalog.
ATTACH 'firebird://user:password@host:3050//path/to/db.fdb' AS fb (TYPE firebird);
-- Firebird tables behave like native DuckDB tables:
SHOW ALL TABLES;
SELECT * FROM fb.EMPLOYEES WHERE ACTIVE;
SELECT d.NAME, count(*) FROM fb.PROJECTS p JOIN fb.DEPARTMENTS d ON p.DEPT_ID = d.ID GROUP BY d.NAME;Tables and column types are discovered from the Firebird system catalog and show
up in SHOW ALL TABLES / information_schema. The optimizer pushes column
projection, WHERE filters, and LIMIT down into the Firebird query.
Status: read-only.
SELECTand joins work;INSERT/UPDATE/DELETE/DDL are intentionally rejected.
Because it links DuckDB's internal C++ API, the binary is version-locked to DuckDB v1.5.3 and must be loaded by a matching client.
CI builds the extension for linux_amd64, linux_arm64, and osx_arm64 and
publishes a DuckDB custom extension repository to GitHub Pages (see
.github/workflows/catalog-extension.yml).
Install it straight from DuckDB v1.5.3 — no local build, no clone:
duckdb -unsigned # unsigned extensions must be allowed at startupSET custom_extension_repository = 'https://archmaxai.github.io/duckdb_firebird';
INSTALL firebird; -- downloads the right platform binary
LOAD firebird;
ATTACH 'firebird://user:password@host:3050//path/db.fdb' AS fb (TYPE firebird);From Python:
import duckdb
con = duckdb.connect(config={"allow_unsigned_extensions": "true"})
con.execute("SET custom_extension_repository = 'https://archmaxai.github.io/duckdb_firebird'")
con.execute("INSTALL firebird")
con.execute("LOAD firebird")The published binaries are unsigned, so
allow_unsigned_extensions(or the-unsignedCLI flag) is required. Tagged releases (catalog-vX.Y.Z) are titled with the DuckDB version they target and attach the rawfirebird.<duckdb_version>.<platform>.duckdb_extensionfiles as GitHub Release assets, which you can download andLOAD '<path>'directly.Enable publishing once in the repo: Settings → Pages → Build and deployment → Source: GitHub Actions. The repository URL to use is printed on the published Pages site's landing page.
The ATTACH target is a DSN, and each connection field is resolved in this
order:
- the matching component of the DSN, then
- the corresponding
FIREBIRD_*environment variable, then - a built-in default.
| Field | Env fallback | Default |
|---|---|---|
| host | FIREBIRD_HOST |
localhost |
| port | FIREBIRD_PORT |
3050 |
| user | FIREBIRD_USER |
SYSDBA |
| password | FIREBIRD_PASSWORD |
masterkey |
| database | FIREBIRD_DATABASE |
(required) |
| charset | FIREBIRD_CHARSET |
UTF-8 |
firebird://user:password@host:port/database?charset=UTF8
- IPv6 hosts use bracket notation:
firebird://user:password@[2001:db8::1]:3050/db.fdb - Windows paths are taken verbatim:
.../C:\firebird\data\example.fdb - Absolute Unix paths need a double slash (the first
/is the authority/path separator, as in JDBC Firebird):firebird://SYSDBA:masterkey@127.0.0.1:3050//var/lib/firebird/data/test.fdb
Firebird treats unquoted login names case-insensitively and stores the SRP
password verifier under the upper-cased name. The extension upper-cases the
username for you, so myuser and MYUSER both work.
| Firebird | DuckDB |
|---|---|
SMALLINT / INTEGER / BIGINT |
SMALLINT / INTEGER / BIGINT |
FLOAT |
FLOAT |
DOUBLE PRECISION |
DOUBLE |
NUMERIC / DECIMAL |
DOUBLE* |
CHAR / VARCHAR |
VARCHAR |
BLOB SUB_TYPE TEXT |
VARCHAR |
BLOB SUB_TYPE BINARY |
BLOB |
BOOLEAN |
BOOLEAN |
DATE / TIME / TIMESTAMP |
DATE / TIME / TIMESTAMP |
* NUMERIC/DECIMAL surface as DOUBLE, so very large/high-scale values may
lose precision; cast on the Firebird side (e.g. CAST(col AS VARCHAR)) if you
need exact decimals.
The extension lives in native/. See
native/README.md for build instructions, architecture, the
pushdown details (projection / filter / limit), and the network-performance
notes (batched fetch, bulk metadata, socket timeouts).
native/setup.sh # fetch the pinned DuckDB v1.5.3 source + ci-tools
cd native && GEN=ninja make release # -> build/release/extension/firebird/firebird.duckdb_extensionA throwaway Firebird 5 server with seed data is provided via Docker:
docker compose up -d # starts firebird on localhost:3050, seeds test.fdbSeed schema lives in test/initdb/01_schema.sql (EMPLOYEES, DEPARTMENTS,
PROJECTS with foreign keys, plus a type-coverage table). There are two test
suites, both covering joins, aggregates, subqueries, window functions, set ops,
filter/limit pushdown, type fidelity, and read-only enforcement:
# DuckDB sqllogictest harness (also run in CI). Needs the server above + a build.
cd native && make test_release
# Self-checking bash suite (manages the container for you).
native/test_queries.sh # FRESH=1 recreates the container to re-seedThe sqllogictest fixtures live in native/test/sql/ and run
through DuckDB's unittest binary, so they execute on every CI build (Linux) and
gate publishing.
This project is licensed under the MIT License.
| Component | License | Use |
|---|---|---|
| DuckDB | MIT | Host database; the extension links its C++ API (ABI-locked to v1.5.3). |
rsfbclient |
MIT | Pure-Rust Firebird wire protocol client (no native fbclient needed). A vendored, lightly patched copy of rsfbclient-rust lives in native/rust/vendor/ — see its PATCHES.md for the batched-fetch / socket-timeout changes. |
| extension-ci-tools | MIT | DuckDB's reusable extension build/CI makefiles. |
| DuckDB extension template | MIT | Project scaffolding the native/ layout is based on. |