---
name: dbio-firebird-database
description: "Firebird/InterBase knowledge for Perl driver development (DBD module, SQL dialect, types, generators, transactions, pagination)"
user-invocable: false
allowed-tools: Read, Grep, Glob
model: sonnet
---

Firebird (and its ancestor InterBase) for Perl DB driver development. Where this
conflicts with generic Firebird docs, trust the driver code.

## DBD Module & DSN

`DBD::Firebird` (closely modeled on the older `DBD::InterBase`). Connection:
```perl
DBI->connect("dbi:Firebird:db=/path/to/db.fdb;host=localhost;ib_dialect=3", $user, $pass)
```
The driver forces SQL **dialect 3** by appending `ib_dialect=3` to the DSN
(`_set_sql_dialect`); `DBD::InterBase` otherwise defaults to dialect 1.

Critical flags: `ib_softcommit => 1` (via `on_connect_call => 'use_softcommit'`)
or `disable_sth_caching => 1` — without one of these you get `no statement
executing` errors. `ib_time_all => 'ISO'` (via `datetime_setup`) for date/time.

## Dialect / Version

Dialect 1 = InterBase compat (no `"`-quoting, no `BIGINT`). Dialect 3 = full
type system + identifier quoting — always used. Server version via
`rdb$get_context('SYSTEM', 'ENGINE_VERSION')`. `last_insert_id` needs Firebird
2+; `auto_nextval` works on earlier versions.

## Type System

Firebird types (driver mapping in `DBIO::Firebird::Type`):

| Category | Types |
|----------|-------|
| Integer | SMALLINT(2), INTEGER(4), BIGINT(8) — dialect 3 |
| Fixed | DECIMAL(p,s), NUMERIC(p,s) |
| Float | FLOAT, DOUBLE PRECISION |
| String | CHAR(n), VARCHAR(n) (max ~32KB) |
| Large | BLOB (binary), BLOB SUB_TYPE TEXT (text/CLOB) |
| Date | DATE (date only), TIME, TIMESTAMP (to 4 decimal places of seconds) |

No native BOOLEAN before FB3; driver deploys `boolean` as `SMALLINT`. No native
`TEXT`/`SERIAL` — mapped to `BLOB SUB_TYPE TEXT` / `BIGINT`. DDL emits
`VARCHAR(255)` for bare `varchar`, `DECIMAL(18,6)` for bare numeric.

## Identity / Generators / Sequences

Firebird has no auto-increment column; it uses **generators** (aka sequences).
- Fetch next value: `SELECT GEN_ID(name, 1) FROM rdb$database` — driver
  `_sequence_fetch('nextval', $seq)`.
- Modern syntax: `NEXT VALUE FOR seqname`. FB3+ also has
  `GENERATED BY DEFAULT AS IDENTITY` identity columns.
- Auto-increment is wired with a BEFORE INSERT trigger that calls `GEN_ID` /
  `NEXT VALUE FOR`. The driver discovers the generator backing an
  `auto_nextval` column by parsing `rdb$trigger_source` (`_dbh_get_autoinc_seq`).

## Transactions & Savepoints

Multi-generational (MVCC) — readers never block writers. `INSERT/UPDATE/DELETE`
support `RETURNING` (driver sets `_use_insert_returning(1)`). Savepoints:
`SAVEPOINT n` / `RELEASE SAVEPOINT n` / `ROLLBACK TO SAVEPOINT n`
(`_exec_svp_begin`/`release`/`rollback`).

## Pagination

Firebird has **no `LIMIT`/`OFFSET`**. The driver emits `FIRST n SKIP m`
immediately after `SELECT` (`apply_limit` → `_FirstSkip`):
```sql
SELECT FIRST 10 SKIP 20 * FROM t
```
(SQL-standard `ROWS m TO n` / `OFFSET ... FETCH` also exist in newer Firebird,
but the driver uses `FIRST/SKIP`.)

## Identifier Quoting & Case

Quote char is `"` (`sql_quote_char('"')`). **Unquoted identifiers are folded to
UPPERCASE** and stored uppercase in the catalog. Quoted identifiers are
case-sensitive and preserved verbatim. The driver upcases names when quoting is
off and matches case-sensitively when quoting is on (see `_dbh_get_autoinc_seq`,
introspection forces `FetchHashKeyName = 'NAME_lc'` because result columns come
back uppercased). `sqlt_type` is `Firebird`.

## Unicode / Charset

Set per-column/database CHARACTER SET; `UTF8` is the Unicode charset
(4-byte). No connection-level `SET NAMES` — charset is fixed at connect
(`charset=UTF8` in DSN) or per database. Datetime format is ISO
(`%Y-%m-%d %H:%M:%S.%4N`) via `DBIO::Firebird::DateTime::Format`.

## Introspection Catalog

No schemas — relation names are bare/unqualified. Read via `RDB$` system tables
(`DBIO::Firebird::Introspect`, keys forced lowercase):

| Object | Tables |
|--------|--------|
| Tables/views | `RDB$RELATIONS` (`rdb$relation_type`, `rdb$view_source`) |
| Columns | `RDB$RELATION_FIELDS` + `RDB$FIELDS` (`rdb$field_type`, precision/scale, `rdb$null_flag`, default) |
| Indexes | `RDB$INDICES` + `RDB$INDEX_SEGMENTS` (PK/UNIQUE-backed excluded) |
| Constraints | `RDB$RELATION_CONSTRAINTS` (`rdb$constraint_type`) |
| Foreign keys | `RDB$REF_CONSTRAINTS` (`rdb$update_rule`, `rdb$delete_rule`, `rdb$match_option`) |

## FK Behavior

Supports `MATCH FULL`/`SIMPLE` and `ON UPDATE`/`ON DELETE` actions. Composite
FK columns grouped by constraint name during introspection.

## Testing Env Vars

`DBIO_TEST_FIREBIRD_DSN`, `DBIO_TEST_FIREBIRD_USER`, `DBIO_TEST_FIREBIRD_PASS`.
Variants: `DBIO_TEST_FIREBIRD_INTERBASE_DSN` (DBD::InterBase),
`DBIO_TEST_FIREBIRD_ODBC_DSN` (ODBC driver). Docker: `firebirdsql/firebird`.
