Revision history for DBIO-Firebird

0.900004  2026-11-08
    * Storage
        - The text of an error names the type, the column, the constraint
          and the table, never a value: the key of a PRIMARY KEY, UNIQUE
          or FOREIGN KEY violation (Problematic key value is ("A" = ***,
          "B" = ***)) and the string of a conversion error are masked,
          also for short bound values and a value holding quotes, commas
          or line breaks; the refusals for a boolean or numeric column
          name the column only. $dbh->errstr stays raw.
        - Three transports: DBD::Firebird (DBIO::Firebird::Storage),
          DBD::InterBase (Storage::InterBase) and DBD::ODBC
          (DBIO::Firebird::Storage::ODBC, ADR 0010); the ODBC storage is
          declared as storage_type and untested against a Firebird ODBC
          driver.
        - SQL dialect 3 is forced on connect, also on a connection an
          AccessBroker opens. Another ib_dialect in the DSN or as an
          attribute is refused, naming the place, never the DSN or a
          password.
        - Dates and timestamps are read in the ISO formats by default
          (ib_time_all 'ISO' on every native connection, not on
          Storage::ODBC).
        - Text is UTF8 on every native connection: ib_charset=UTF8 and
          ib_enable_utf8 => 1 are added, also to every AccessBroker answer,
          unless the connect info names its own. Text comes back as
          characters and unflagged strings are flagged when bound.
        - ib_ attributes from an AccessBroker connect over dbi:Firebird:
          (not on Storage::ODBC). A HostBound view (Replicated) is bound
          into the DSN as db=host[/port]:path, host= / hostname= with
          port=, or a local database name; any other form is refused
          naming the form.
        - Reading and writing keeps working after DDL and after a rolled
          back transaction with statement caching on; a statement handle the
          DBD destroyed is prepared anew (ADR 0011).
        - A commit or DDL statement the server refuses dies with the
          server's error (a DBIO::Exception::Server) on the same
          connection, and is not repeated after a reconnect.
        - A BOOLEAN value is checked on both the single and the bulk path:
          1, 0 and undef pass, 'true' and 'false' (any case) are
          translated, '' is FALSE, anything else dies naming the column.
          Not on the ODBC storage.
        - A text or binary BLOB column can be searched by value; a value
          too long to compare on Firebird is refused before the DBD,
          naming table and column. The placeholders are found by core's
          scanner, so a ? in a literal, quoted name or comment is left
          alone.
        - A value that is no number is refused for a numeric column
          instead of being stored as 0. Not on Storage::ODBC.
        - Boolean, numeric and UTF8 adjustments of bound values run
          through core's _adjust_bind_values and leave the caller's bind
          list alone.
        - $rs->rand orders by RAND(), $rs->explain returns the plan
          without running the statement (refused over ODBC), and
          $rs->results_exist works. $rs->except, except_all, intersect
          and intersect_all are refused: Firebird has only UNION
          (ADR 0039).
        - transactional_ddl is declared 0: a whole deploy batch is not
          bracketed (see "Transactional DDL -- not claimed" in
          DBIO::Firebird::Deploy).

    * DDL
        - install_ddl writes declared sizes (varchar, char, numeric,
          decimal) and Firebird's type names: TIMESTAMP, BIGINT, SMALLINT,
          DOUBLE PRECISION, BOOLEAN, and BLOB SUB_TYPE TEXT for an unsized
          string and mediumtext/longtext.
        - Every identifier is double-quoted, as the storage quotes it, so
          the installed table is the table the ORM queries. A column
          without is_nullable is NOT NULL.
        - boolean is a BOOLEAN column (Firebird 3 is the minimum) with
          DEFAULT TRUE / FALSE from 0, 1, 'true' or 'false'.
          is_auto_increment is GENERATED BY DEFAULT AS IDENTITY; an
          identity with a default or on a non-integer type dies.
        - Foreign keys and unique constraints are written after all CREATE
          TABLE statements, so table order and cycles do not matter;
          on_delete and on_update come from the belongs_to or from the
          has_many's cascade_delete and cascade_copy; is_deferrable is
          refused.
        - The canonical index declaration (idx, indices; core ADR 0061)
          creates plain or unique indexes and refuses expression,
          partial and descending ones. An index the primary key, a unique
          constraint or a foreign key already provides is not built twice.
        - A string default with an apostrophe is quoted correctly.
        - Refused with a DBIO::Exception naming table and column: bit,
          enum, time zone types, binary and varbinary below Firebird 4,
          NUMERIC/DECIMAL above 18 digits (DBD::Firebird cannot bind or
          read an INT128), and a table or view name with a dot.

    * Diff
        - Foreign keys, unique constraints and primary keys are part of
          the diff (DBIO::Firebird::Diff::Constraint): new tables get them
          after every CREATE TABLE, existing tables get DROP CONSTRAINT /
          ADD CONSTRAINT, compared by what they say, not by name. The
          order lets an index or key on a dropped column go first, and
          DROP TABLE runs child before parent.
        - Views are created, replaced and dropped from rdb$view_source; a
          changed definition is dropped before the table changes and created
          again after. A name that is a table on one side and a view on the
          other is refused.
        - A column change the server always refuses (narrowing CHAR,
          character to non-character, no conversion, fewer digits, a type
          change of a key column) is refused before the first statement.
          Adding or removing IDENTITY on an existing column is refused.
        - A name that differs from the schema's only in letter case is
          refused, naming every pair, instead of DROP + CREATE (see "NAMES
          THAT DIFFER ONLY IN CASE" in DBIO::Firebird::Diff).
        - A default the declaration no longer has is dropped; the column
          size is carried on ADD and ALTER ... TYPE.

    * Deploy
        - DBIO::Firebird::Deploy inherits apply and upgrade from
          DBIO::Deploy::Base (ADR 0013): a diff that drops something or
          changes a column lossily runs with allow_destructive => 1 and
          stops before its first statement without it; dry_run returns the
          diff and executes nothing. Widening pairs (smallint to integer,
          date to timestamp, char to varchar, a larger size) are not lossy.
        - apply also refuses, naming table and column, a NOT NULL column
          without a default added to a table that holds rows and a column
          that becomes NOT NULL over a NULL.
        - diff, upgrade and install work on a schema connected through an
          AccessBroker (a coderef or dbh_maker answer is refused). The
          temporary database is created on the server also when the DSN
          spells host and path as attributes; its errors do not quote the
          password.

    * Introspect
        - The model reads columns, indexes, unique constraints, foreign keys
          and primary key names from the rdb$ tables (see "THE INTROSPECTED
          MODEL" in DBIO::Firebird::Introspect); field types follow the
          server, including NUMERIC/DECIMAL, boolean, int128, decfloat and
          time zone types.
        - A foreign key names its constraint under constraint_name
          (core ADR 0021); is_auto_increment is read from rdb$identity_type.

    * Dependencies
        - Requires DBIO 0.900004 and DBD::Firebird;
          DateTime::Format::Strptime is recommended.

    * Tests and documentation
        - DBIO::Firebird::Test::Conformance answers core's DDL, Diff and
          Bind case tables (ADR 0012), including conformance_upgrade and
          the deploy cases.
        - README states where the driver stands for async use.

0.900001  2026-07-12
    * Diff
        - Fix DROP INDEX / DROP TABLE collision in full-schema sync (karr #14,
          from dbio core #77 / dbio-postgresql #32). When diff reconciled a
          live schema containing a table absent from the target (removed from
          the app's DBIO classes, or an unmanaged leftover), it emitted both
          DROP TABLE <t> and a later standalone DROP INDEX for one of that
          table's indexes; since Firebird's DROP TABLE already removes the
          table's own indexes, the standalone DROP INDEX failed once the table
          was gone and aborted apply()/upgrade(). DBIO::Firebird::Diff::Index
          now receives the tables sections (via the aux-section wiring in
          DBIO::Firebird::Diff) and suppresses a standalone DROP INDEX for any
          index whose owning table is itself being dropped in the same pass.
          Indexes of surviving tables are still dropped as before. The diff
          signature stays backward compatible: absent table sections yield an
          empty dropped set (old two-argument behaviour).

    * Dependencies
        - DBD::Firebird is now a hard "requires" instead of "recommends":
          the driver is useless without it, and t/00-load.t failed (rather
          than skipping) when it was absent
          (codeberg #1, reported by Slaven Rezić / eserte)
        - Requires core DBIO >= 0.900001 (family-wide version alignment)

    * Project
        - Added SECURITY.md (CPAN Security Group guidelines v1.5.0)

    * Tests
        - Add t/45-diff-drop-table-index-cascade.t and extend t/40-diff.t:
          full-diff regression for the DROP INDEX / DROP TABLE collision
          (karr #14) plus Diff::Index unit cases for the new tables-aware drop
          suppression, including an over-suppression guard (a surviving table's
          index is still dropped standalone)
        - t/50-introspect-contract.t: make the generated-code table()
          assertions quote-agnostic (['"]). DBIO::Generate renders all
          literals through DBIO::Generate::Util::pl_str -> B::perlstring,
          which always emits double quotes, so the old single-quoted
          regexes failed 2/40 offline; matches Core's quote-agnostic
          precedent in ../dbio/t/generate/*

0.900000  2026-06-23
    First release. Firebird and InterBase database driver for DBIO.

    * Storage
        - DBD::Firebird storage (driver "Firebird") and DBD::InterBase
          storage variant (driver "InterBase"), auto-detected from the DSN
        - shared Firebird/InterBase storage base with generator-based
          autoincrement, savepoint support, and server-version detection
        - ODBC connection variant supported via the InterBase storage path
        - softcommit and datetime connect-calls, SQL dialect 3 forcing
        - quoted identifiers and INSERT ... RETURNING

    * SQLMaker
        - Firebird FIRST/SKIP limit dialect via apply_limit

    * Schema component and shortcut
        - DBIO::Firebird schema component pinning the storage type
        - "use DBIO -fb" shortcut (DBIO::Shortcut::fb)

    * Types and DateTime
        - Firebird type-system mapping for introspection and DDL
        - DateTime formatter for Firebird / InterBase

    * Introspection
        - native introspection via RDB$ system tables: tables, columns,
          indexes, foreign keys, and unique constraints

    * DDL and deploy
        - native DDL generation from DBIO Result classes, deduping sources
          that share a physical table, skipping subquery/view sources,
          dereferencing literal table names, and emitting CREATE VIEW for
          non-virtual views
        - desired-state deploy and upgrade via test-deploy-and-compare

    * Diff
        - native model diff for tables, columns, and indexes
