Skip to content

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

1 watching

Forks

Latest commit

 

History

138 Commits

Folders and files

Repository files navigation

duckdb-firebird

Query Firebird directly from DuckDB.

The first Firebird extension published in the DuckDB Community Extensions registry.

Read-only scan, native ATTACH, metadata/diagnostics, and pushdown explain for Firebird 3/4/5 -- DuckDB's own SQL (CREATE TABLE AS, COPY ... TO PARQUET) handles materialization and export; this extension is the read path, not an ETL/CDC/scheduler layer.

license MIT release v1.3.1 linux matrix community extension merged total DuckDB Community downloads for firebird

Usage guide | Function manual | Roadmap (PT) | Contributing | Code of conduct | Security

duckdb-firebird is a DuckDB extension for read-only analytics on Firebird databases. It lets analysts keep Firebird as the transactional source of truth and use DuckDB as the local OLAP engine for ad hoc analysis, materialized tables, Parquet exports, lakehouse-style caches, and BI serving through tools such as GizmoSQL.

Principles

  • Read-only by design - optimized for analytics without mutating the OLTP source.
  • Push work to Firebird when safe - projection, filters, paging, and PK-range partitioning reduce wire traffic.
  • Keep DuckDB in charge of analytics - joins, aggregations, Parquet/S3, and local materializations stay in DuckDB's vectorized engine.
  • Legacy friendly - CHARACTER SET NONE databases default to none_encoding='win1252', with strict, iso8859_1, and blob available.
  • Public-repo ready - security policy, contribution docs, CI, Dependabot, and branch protection are in place.

Features

  • firebird_scan(conn, table) - scan a Firebird table from DuckDB SQL.
  • Native ATTACH - query Firebird tables as fb.main.TABLE.
  • Metadata discovery - SHOW TABLES, DESCRIBE, and information_schema.tables / information_schema.columns.
  • Projection pushdown - only requested columns are fetched.
  • Predicate pushdown - comparisons, IS NULL, BETWEEN, IN, NOT IN, NOT bool, AND / OR, and safe LIKE 'prefix%' cases. = / IN push down except on CHARACTER SET NONE text columns — those stay in DuckDB (see none_encoding), optionally guarded via SET firebird_unpushed_mode = 'warn' | 'error'; the none_pushdown=true opt-in pushes = / IN over NONE CHAR/VARCHAR columns by re-encoding each literal to the storage bytes (lossless byte-wise comparison).
  • Streaming batches - rows flow into DuckDB DataChunks instead of a whole-table in-memory copy.
  • Prepared statements + bind variables - string/date/numeric filters avoid unsafe SQL interpolation.
  • Paging - row_limit=N and row_offset=M map to Firebird ROWS.
  • Parallel PK-range scans - opt in with partitions=N.
  • Firebird 3/4/5 type mapping - including INT128, DECIMAL(38), TIMESTAMP WITH TIME ZONE, text BLOBs, binary BLOBs, booleans, dates, and timestamps. Int64-backed NUMERIC(18,s) defaults to DECIMAL(18,s); the numeric_widen_int64=true opt-in (firebird_scan parameter or ATTACH option) projects those columns as DECIMAL(38,s) so values beyond DuckDB's 18-digit domain (e.g. the scaled-INT64_MIN sentinel) survive losslessly. firebird_type_audit flags every affected column with the int64_numeric_widenable finding.
  • Diagnostics and metadata bridge - inspect constraints, indexes, generators, domains, dependencies, comments, health, type-fidelity findings (firebird_type_audit), index profile, table profile alerts, and firebird_explain_pushdown planning reports.
  • Plays well with plain DuckDB SQL - once scanned/attached, ordinary CREATE TABLE AS, COPY ... TO PARQUET, S3/MinIO writes, and Arrow Flight SQL via GizmoSQL are DuckDB's own features, not something this extension implements or manages.

Quick Start

INSTALL firebird FROM community;
LOAD firebird;

-- Live scan
SELECT *
FROM firebird_scan(
    'firebird://SYSDBA:masterkey@db.host:3050/srv:/data/prod.fdb?charset=UTF8',
    'EMPLOYEE'
)
LIMIT 10;

-- Projection + predicate pushdown
SELECT EMP_NO, FIRST_NAME
FROM firebird_scan('firebird://SYSDBA:masterkey@db.host:3050/srv:/data/prod.fdb', 'EMPLOYEE')
WHERE DEPT_NO = '600'
  AND HIRE_DATE >= DATE '2020-01-01';

-- Native catalog
ATTACH 'firebird://SYSDBA:masterkey@db.host:3050/srv:/data/prod.fdb' AS fb
    (TYPE firebird);

SELECT *
FROM fb.main.EMPLOYEE
WHERE DEPT_NO = '600';

Analyst Workflow

Use live scans when you need fresh operational data:

SELECT o.CUSTOMER_ID, SUM(o.TOTAL_VALUE) AS revenue
FROM fb.main.ORDERS o
WHERE o.ORDER_DATE >= DATE '2026-01-01'
GROUP BY 1;

Materialize when the query is reused or joins large historical tables:

CREATE OR REPLACE TABLE mart_orders_2026 AS
SELECT *
FROM fb.main.ORDERS
WHERE ORDER_DATE >= DATE '2026-01-01';

Export to Parquet for a local lake cache:

COPY (
    SELECT *
    FROM fb.main.ORDERS
    WHERE ORDER_DATE >= DATE '2026-01-01'
)
TO 'lake/erp/orders_2026.parquet'
(FORMAT PARQUET);

For a longer, analyst-oriented walkthrough, including materialized tables, incremental refresh, Parquet partitioning, MinIO, and Power BI/GizmoSQL usage, see docs/en/usage_guide.md. For a public how-to of each SQL function and option, see docs/en/function_manual.md (PT: docs/pt/function_manual.md).

Connection Strings

firebird://USER:PASS@HOST:PORT/DB_PATH?charset=UTF8&dialect=3&role=ROLE
user=SYSDBA;password=masterkey;database=server:/data/db.fdb;charset=UTF8
C:/data/local.fdb
/var/lib/firebird/data/local.fdb

Security note: connection strings may end up in shell history, notebooks, DuckDB logs, or BI configuration. Use least-privilege Firebird users and avoid committing real credentials.

Long fetches getting killed by NAT/firewall idle timeouts (-504 cursor lost)? SET firebird_dummy_packet_interval = 60; arms a wire-level keepalive (seconds) on every connection; see the function manual's session options.

Named Parameters

SELECT *
FROM firebird_scan(
    '/data/prod.fdb',
    'EMPLOYEE',
    user='analytics',
    password='secret',
    charset='UTF8',
    role='READER',
    dialect=3,
    partitions=4,
    row_limit=100,
    row_offset=200
);

row_offset requires row_limit. Paging is a global Firebird slice and runs serially; explicit partitions > 1 with paging is rejected to avoid returning incorrect slices.

Charset Handling

DuckDB strings are UTF-8. Firebird databases declared with a real character set can usually use the default charset=UTF8; Firebird transliterates values on the wire.

CHARACTER SET NONE is different: Firebird returns raw bytes. The extension defaults to none_encoding='win1252', matching many legacy Brazilian and Western-European ERPs. Override it when needed:

none_encoding Behavior
win1252 Default. Decode NONE text bytes as Windows-1252 to UTF-8.
iso8859_1 / latin1 Decode NONE text bytes as ISO-8859-1 to UTF-8.
strict Accept only already-valid UTF-8; error on invalid bytes.
blob Surface NONE text columns as DuckDB BLOB raw bytes.
SELECT *
FROM firebird_scan('C:/legacy/erp.fdb', 'TABENTRADASAIDA',
                   none_encoding='win1252');

ATTACH 'C:/legacy/erp.fdb' AS erp
    (TYPE firebird, user 'SYSDBA', password 'masterkey',
     none_encoding 'win1252');

When none_encoding is not strict, text filter pushdown on NONE columns is disabled deliberately: DuckDB applies those filters after transcoding so query results remain correct. To push = / IN over NONE CHAR/VARCHAR columns anyway — re-encoding each UTF-8 literal to the stored bytes behind a charset introducer, lossless under Firebird's byte-wise NONE comparison — pass none_pushdown=true to firebird_scan or the ATTACH options.

Metadata

SHOW TABLES FROM fb;
DESCRIBE fb.main.EMPLOYEE;

SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'main';

SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'EMPLOYEE'
ORDER BY ordinal_position;

Firebird has a flat namespace, so the extension exposes a single DuckDB schema: main. Name lookup is case-insensitive unless quoted identifiers require exact DuckDB behavior.

Current Status

Published community release: v1.2.1 (INSTALL firebird FROM community; descriptor merged as duckdb/community-extensions#3022 on 2026-10-12). Newer repository releases are distributed through this project's GitHub Releases; a community-catalog update is a separate, explicitly authorized publication step. The copy in community-extensions/description.yml mirrors the latest prepared descriptor. Descriptors pin an immutable commit SHA in repo.ref (catalog convention).

main is at v1.3.1 (see docs/en/release_notes_v1.3.1.md and CHANGELOG.md). This patch completes DuckDB v2.0 NOT IN pushdown and its residual-filter observability. Supported DuckDB versions are v1.5.6+ and v2.0+, with release assets built per DuckDB line (-duckdb2 suffix for the v2.0-built binaries). v1.3.0 introduced the dual-path v1.5.x/v2.x C++ API build. v1.2.1 added connection-string support for none_encoding / none_pushdown; v1.2.0 delivered the G-queue — none_pushdown (=/IN over CHARACTER SET NONE columns), the firebird_unpushed_mode guard, numeric_widen_int64 (NUMERIC(18,s) → DECIMAL(38,s)), firebird_dummy_packet_interval keepalive with fetch-failure context, and bytes_read_estimate telemetry.

v1.0.1 (see docs/en/release_notes_v1.0.1.md) delivered Metadata Bridge 2.0, pushdown explain planning, type audit, database/ table/index diagnostics, the Smart Scan Planning report, deterministic paging safeguards, lossless type/BLOB hardening, and a closed Production Stability + Runtime/ABI Compatibility gate (fresh DuckDB v1.5.2/v1.5.3/v1.5.4/v1.5.5 and Firebird 3/4/5 matrices, a read-only maturity battery run against a real ~90GB database, zero product errors). DuckDB v1.5.5 — the current DuckDB Community Extensions compatibility target — and v1.5.6 (released 2026-09-28) are validated as additional matrix versions (v1.5.6: full suite, 21/21 test files, 2026-09-29); the repository's own default build pin stays v1.5.3 for now. See docs/en/release_notes_v1.0.0.md and docs/pt/duckdb_1_5_compatibility_plan.md for full detail.

Area Status
firebird_scan table function Done
Native ATTACH ... (TYPE firebird) Done
firebird_tables discovery Done
firebird_attach_sql generated-view helper Done
Projection pushdown Done
Predicate pushdown Done
Manual paging with row_limit / row_offset Done
Prepared statement bind variables Done
PK-range parallel scan via partitions=N Done
CHARACTER SET NONE decoding Done
Metadata via SHOW, DESCRIBE, information_schema Done
Automatic DuckDB LIMIT pushdown Deferred upstream hook
LRU prepared statement cache Deferred benchmark item
Scanner-native Arrow RecordBatch output v1.x candidate

Firebird-native diagnostics

Area Status
firebird_profile_table('fb.main.T') factual table/view diagnostics Done
Structured profile alerts (alerts LIST(STRUCT(code,severity,message))) Done
Heavy-view shape detection (JOIN / aggregation / no-filter warnings) Done
Row estimate (estimated_rows / row_estimate_method, opt-in exact COUNT(*) via exact_row_count=true) Done (v1.1.0)
Structured required-filter alerts (filter_before_scan, materialize_before_scan) Done (v1.1.0)
firebird_explain_pushdown(sql) planning report Done
Pushdown explainability in firebird_last_query / firebird_query_log (limit_pushed, offset_pushed, not_pushed_reasons) Done
firebird_pool_stats('fb') connection-pool introspection Done
Pool active-lease count + sanitized last_error (active_connections, last_error) Done (v1.1.0)
firebird_health('fb') database/server health facts Done
firebird_index_profile('fb.main.T') index diagnostics Done
firebird_type_audit('fb') type/charset fidelity findings Done
DECFLOAT(16/34) lossless fallback (VARCHAR via server-side CAST) Done
Adaptive parallel scan recommendations (shared with scanner planning) Done
All 19 functions documented in-band in duckdb_functions() Done (v1.0.2)
none_pushdown: =/IN pushdown over CHARACTER SET NONE text Done (v1.2.0)
firebird_unpushed_mode residual-filter guard (silent/warn/error) Done (v1.2.0)
numeric_widen_int64: NUMERIC(18,s) as DECIMAL(38,s), opt-in Done (v1.2.0)
Keepalive DPB (firebird_dummy_packet_interval) + fetch-failure context Done (v1.2.0)
bytes_read_estimate telemetry + per-catalog lifetime total Done (v1.2.0)

firebird_pool_stats('fb') reports one attached catalog's pool state (config + idle queue + active leases + lifetime counters + last error) by explicit alias. It does not enumerate catalogs, reads only counters the pool already tracks, and never leases a connection.

DECFLOAT(16) / DECFLOAT(34) (Firebird 4+ IEEE Decimal64/Decimal128) now surface as lossless VARCHAR via a server-side CAST(... AS VARCHAR(64)), replacing the old behavior where the column was typed DOUBLE but always fetched NULL. No local decimal-float decoder, no DOUBLE default. The DECFLOAT test fixture is dedicated and not yet in the main CI fixture.

Adaptive parallel scan recommendations refine firebird_profile_table's recommended_partitions and warnings only — firebird_scan is unchanged, nothing is parallelized automatically, and no performance gain is promised. A numeric PK with a small range still recommends partitions=1, and a server-side parallelism caveat (Firebird 5 ParallelWorkers) is surfaced when partitions > 1 is suggested.

Arrow note: the scanner produces DuckDB Vector / DataChunk columns. When a Flight SQL client consumes query results through DuckDB or GizmoSQL, DuckDB converts the result stream to Arrow at the boundary. Direct scanner-native Arrow is intentionally deferred.

Runtime requirement: Firebird client library

Starting with v0.5.5, the extension loads the Firebird client library at runtime via dlopen / LoadLibrary. It is not embedded in the published binary, and the build no longer links libfbclient — including the community-extensions build that publishes INSTALL firebird FROM community;.

You need a Firebird client reachable on the platform's default dynamic-loader search path on the machine that runs the queries:

Platform Provide
Linux libfbclient.so.2 (or libfbclient.so) - e.g. apt-get install libfbclient2, dnf install libfbclient, or a Firebird server install
macOS libfbclient.dylib from a Firebird install
Windows fbclient.dll (or fbclient_ms.dll) from a Firebird install or the Firebird ODBC client distribution

If the library lives somewhere non-standard, set DUCKDB_FIREBIRD_CLIENT_LIBRARY to the absolute path before the first firebird_* call. The override is authoritative: when set non-empty and the path cannot be loaded, the extension raises a clear IO Error: DUCKDB_FIREBIRD_CLIENT_LIBRARY is set to '...' but that file could not be loaded. instead of silently falling back. On Windows the DLLs that fbclient.dll itself needs (for Firebird 3: msvcr100.dll, zlib1.dll, ICU) are looked up in that library's own folder first, so pointing the variable at a Firebird install or portable kit is enough.

When no client is reachable, the first firebird_* call fails with:

IO Error: Firebird client library not found. Install the Firebird
client (libfbclient on Linux/macOS, fbclient.dll on Windows) or set
DUCKDB_FIREBIRD_CLIENT_LIBRARY=/path/to/library. Tried: ...

Build

# Linux dependencies (firebird-dev is OPTIONAL - only needed if you
# want to build against a system-installed ibase.h; the repo ships
# vendored headers under third_party/firebird/include for the
# community CI path).
sudo apt-get install -y cmake ninja-build g++ pkg-config python3 ccache

# Build and package
scripts/build_linux_local.sh
scripts/package_dist_linux.sh

# Tests require a reachable Firebird test database AND a Firebird
# client library on the loader path.
./build/release/test/unittest test/sql/firebird_scan.test

Windows builds use the companion batch scripts:

scripts\build_windows_local.bat
scripts\package_dist.bat

Release assets are automated by .github/workflows/release-assets.yml. Pushing a v* tag builds and uploads Linux .tar.gz and Windows .zip archives to the GitHub Release. The workflow can also be run manually with a tag input.

See docs/en/guide_linux.md and docs/en/guide_windows.md.

Documentation

Docs are split by language under docs/en/ (English, primary) and docs/pt/ (Portuguese translations).

Repository Layout

src/
  firebird_client.cpp      libfbclient wrapper
  firebird_types.cpp       Firebird to DuckDB type mapping
  firebird_query.cpp       SQL builder and pushdown
  firebird_scanner.cpp     table functions
  firebird_storage.cpp     native ATTACH catalog
  include/
test/sql/                  sqllogictest coverage
scripts/                   fixtures and smoke scripts
docs/en/                   English guides, roadmap, reports (primary)
docs/pt/                   Portuguese translations
community-extensions/      DuckDB community descriptor copy

Governance

License

DuckDB Firebird is released under the MIT License.

Support the project

If DuckDB Firebird helps your work, you can support its continued development.

Support Fernando Lozer on Buy Me a Coffee

Community Catalog

The DuckDB community-extension submission was merged via duckdb/community-extensions#1980. The catalog currently serves v1.0.1; the v1.0.2 descriptor update is fully green and awaiting maintainer merge (duckdb/community-extensions#2849), and the v1.1.0 bump is prepared. Descriptors pin an immutable commit SHA in repo.ref (catalog convention), and the build keeps the Firebird client runtime-loaded — no libfbclient build dependency. Normal users should install from the official DuckDB community repository:

INSTALL firebird FROM community;
LOAD firebird;

Use the GitHub release artifact or a locally built .duckdb_extension only for development work on this repository, testing unreleased changes, or offline machines without access to the DuckDB community repository:

LOAD '/absolute/path/to/firebird.duckdb_extension';

Local unsigned builds may require starting DuckDB with -unsigned.

Author

Fernando Lozer — GitHub @flozer · LinkedIn /fernandolozer

Credits

Design and type-mapping decisions build on firebird_peregrine_falcon and the encoding work from fb-cdc-rust.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages