NAME

DBIO::Firebird::DDL - Generate Firebird DDL from DBIO Result classes

VERSION

version 0.900004

METHODS

server_major_version

my $major = DBIO::Firebird::DDL->server_major_version($schema);

The major version of the server behind the schema's storage (3, 4, 5 ...), read from rdb$get_context('SYSTEM','ENGINE_VERSION'), or undef when it cannot be asked (the storage does not connect). undef means "no judgement": nothing is refused on a guess. The one seam through which "install_ddl" learns the version, asked only when a column needs it; a test sets the version by replacing this method (local *DBIO::Firebird::DDL::server_major_version).

install_ddl

my $ddl = DBIO::Firebird::DDL->install_ddl($schema);

Returns the full installation DDL as a single string.

Every identifier taken from a result class -- table, column, primary key column, the name of a unique index -- is emitted double-quoted, exactly as DBIO::Firebird::Storage::Common quotes it in the statements the ORM sends. Firebird folds a bare identifier to upper case, so table('artist') has to become CREATE TABLE "artist" for SELECT ... FROM "artist" to find it, and a reserved word such as year is only accepted as a column name when quoted.

Firebird 3 is the minimum, and the rules of the Diff are measured against Firebird 5.0.4 only; the neighbouring minimum is a decision, not a measurement. A default_value on a boolean column is 0, 1, 'true' or 'false' (any case) and is written as TRUE or FALSE: the server accepts DEFAULT '0' there and then fails every insert that takes the default (measured on Firebird 5.0.4). Any other value ('f', '', 'yes', 2) dies with a DBIO::Exception; a scalar ref holding another word is SQL as written.

A string default_value is written as a SQL literal with each embedded single quote doubled (it's becomes DEFAULT 'it''s'); a scalar ref (\'CURRENT_TIMESTAMP') is literal SQL and is written as given.

The one exception is a scalar-ref name, table(\'cd'): that is literal SQL, the ORM writes it into FROM as given, and so does this method -- the server folds it to CD on both sides. The body of a view (view_definition) is the user's SQL and is passed through untouched.

A column with is_auto_increment is written as GENERATED BY DEFAULT AS IDENTITY (Firebird 3, the driver's minimum; no generator, no trigger), before NOT NULL. Identity and default_value exclude each other, and only smallint, integer and bigint (and serial/bigserial, which map to BIGINT) and numeric / decimal with scale 0 (size => 9 or size => [9, 0]; measured on Firebird 5.0.4) can carry it: any other combination dies with a DBIO::Exception. The introspected model carries the column as is_auto_increment (from rdb$identity_type), so the target of the diff has it, and a table or column created by Deploy->upgrade gets the clause as well (DBIO::Firebird::Diff::Table, DBIO::Firebird::Diff::Column).

Foreign keys and unique constraints are written after every CREATE TABLE (so neither the order of the tables nor a cycle matters), unique constraints first, as

ALTER TABLE "child" ADD CONSTRAINT "child_code" UNIQUE ("code");
ALTER TABLE "child" ADD CONSTRAINT "child_pid_fk" FOREIGN KEY ("pid") REFERENCES "parent" ("id") ON DELETE CASCADE;

A unique constraint is each add_unique_constraint, under its declared name; the primary key's own columns are skipped (the server refuses a second constraint over them), and a name above 63 characters dies (Firebird 5.0.4 takes 63 characters, even two-byte ones, and refuses 64; Firebird 3's limit of 31 bytes is not measured). A key is each belongs_to without is_foreign_key_constraint => 0, named <table>_<columns>_<target>_fk (the target is in the name, so two keys over one column to different tables do not meet), or, above 31 bytes (the limit of Firebird 3, the minimum), its first characters up to 22 bytes, _ and 8 hex digits of the md5 of the whole name. on_delete and on_update are the relationship's own where it declares them (exists, so on_delete => undef or '' declares "no action" and switches the derivation off); where it does not, they are derived from the other side, as DBIO::Relationship::Base describes: a has_many over the same columns with cascade_delete makes ON DELETE CASCADE, one with cascade_copy ON UPDATE CASCADE. The ORM deletes the parent first (cascade_delete runs after the requested delete), so a key without the action would refuse to delete a parent that has children. Written: NO ACTION, CASCADE, SET NULL, SET DEFAULT; RESTRICT, an empty or an absent value write no clause (RESTRICT is a syntax error on Firebird and what it reports for a key without one); anything else dies, and so does is_deferrable => 1 (Firebird has no deferrable constraints). Relationships that declare one key write it once; one key declared with different actions dies. A key must point at columns that are a primary key or a unique constraint; otherwise the server says so ("could not find UNIQUE or PRIMARY KEY constraint ... with specified columns"). The diff creates these for a new table and writes DROP / ADD CONSTRAINT for a difference on an existing one: DBIO::Firebird::Diff::Constraint.

A table name with a dot (table => 'other.t') dies with a DBIO::Exception: Firebird has no schemas before version 6, so the dot would become part of a literal name. A literal name (table(\'x')) is SQL as written. A column of data_type bit or enum dies as well: Firebird has no type for a bit string of a fixed width (BOOLEAN is one bit, BINARY(n) counts bytes, so every mapping changes what the size means) and no enumerated type; the message says refused and names the table and the column. So do the time zone types (timestamptz, timestamp with time zone, time with time zone): the server has them from Firebird 4 on, but DBD::Firebird 1.39 cannot bind a value to such a column and reads one back as the text ** unknown ** (measured, t/88-timezone-types-dbd-live.t), so a column the ORM can neither write nor read is not built. binary and varbinary exist from Firebird 4 on; on a server whose "server_major_version" is below 4 they are refused as well, naming the table and the column (when the version cannot be asked, nothing is refused and the server judges). A numeric or decimal with more than 18 digits is an INT128 and refused on every server, with table, column and the reason: DBD::Firebird 1.39 can neither bind nor read it (t/97-numeric-int128-dbd-live.t). The same refusals reach Deploy->diff and upgrade, which build their target through this method; the Diff ops write the types of the introspected model and have no check of their own.

A view with a dot in its name (table => 'other.v') dies like a table does; a literal name is SQL as written.

string without a size is the text type (BLOB SUB_TYPE TEXT, as in the core canon), with a size VARCHAR(n); real is DOUBLE PRECISION.

A column with is_unique (or is_single_unique_key) that a unique constraint (add_unique_constraint) over exactly that column covers gets no CREATE UNIQUE INDEX of its own: the constraint has its index, so the table holds one, not two.

Indexes come from the canonical index declaration of the result source (idx, indices; "index_declarations_for" in DBIO::ResultSource), plain or unique, with the columns as given. An index over exactly the columns, in the same order, of the primary key or of a unique constraint written here, or (when it is not unique) of a foreign key written here, is not built a second time: the server has an index for each of those. A unique index over a foreign key's columns is built; the target model of the diff is deployed through this method. The firebird block must be empty (firebird => {}): an expression index (COMPUTED BY), a partial one or a descending one is not read back by DBIO::Firebird::Introspect, so the diff could not hold it, and it is refused with a DBIO::Exception, like a type other than unique, options, and an entry that carries only another driver's block.

AUTHOR

DBIO & DBIx::Class Authors

COPYRIGHT AND LICENSE

Copyright (C) 2026 DBIO Authors Portions Copyright (C) 2005-2025 DBIx::Class Authors Based on DBIx::Class, heavily modified.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.