NAME

DBIO::Firebird::Storage::ODBC - Driver for the Firebird RDBMS via DBD::ODBC

VERSION

version 0.900004

DESCRIPTION

Firebird reached through DBD::ODBC rather than through DBD::Firebird or DBD::InterBase. The engine semantics come from DBIO::Firebird::Storage::Common and the ODBC transport helpers from DBIO::Storage::DBI::ODBC; this class holds only what is true of this transport and of no other (ADR 0010).

The nested-statement-handles-across-commits problem of the two native drivers is a fact of their DBDs (connect_call_use_softcommit, disable_sth_caching); this class has not been checked against it (see "CAVEATS").

Why this class is not a subclass of the InterBase driver

DBIO::Firebird::Storage::InterBase speaks the ib_* API of DBD::InterBase -- ib_dialect in the DSN, ib_softcommit, ib_time_all, and $sth->func('ib_plan') for a query plan. None of it exists on DBD::ODBC, so inheriting from that class would put four connection-time attribute writes and one private XSUB call into a handle that has never heard of any of them. The split is the one ADR 0001 already draws and this class extends sideways: what is a fact about the SQL both engines speak lives on Storage::Common and is inherited here (the FIRST/SKIP limit dialect, RAND(), GEN_ID sequences, RDB$DATABASE in the EXISTS query, the refusal of the four set operations Firebird has no operator for, INSERT ... RETURNING, the " quote char, sqlt_type); what hangs off a DBD's own API stays with that DBD.

The same cut decides what an DBIO::AccessBroker may hand this transport: an odbc_ attribute in its hash answer or beside it goes to DBI->connect, an ib_ one is refused, since DBD::ODBC would keep it without applying it ("ib_ attributes through an AccessBroker" in DBIO::Firebird::Storage::InterBase).

Query plans are not reachable over ODBC

Firebird has no EXPLAIN statement of any kind, and its plan is an attribute of the prepared statement, read with isc_dsql_sql_info(isc_info_sql_get_plan). The two native DBDs expose that call as $sth->func('ib_plan'); DBD::ODBC exposes nothing equivalent, and the ODBC API has no vendor-neutral spelling for it either. _explain_query therefore throws here rather than inheriting either half of a wrong answer: core's EXPLAIN <query> would be a Token unknown parse error from the server, and the ib_plan override of DBIO::Firebird::Storage::InterBase would be a call into a function this handle does not have. Connect through DBD::Firebird or DBD::InterBase when you need a plan.

Which storage class an ODBC DSN lands in

The one this schema declares. An ODBC DSN does not say which engine is behind it -- every engine reached through DBD::ODBC shares the DBD name ODBC -- so nothing picks this class from the DSN: it is reached by declaring it, on the schema class or on a clone of it before connecting:

package MyApp::DB;
use base 'DBIO::Schema';
__PACKAGE__->storage_type('+DBIO::Firebird::Storage::ODBC');

# or, for a schema class shared between transports
my $schema = MyApp::DB->clone(storage_type => '+DBIO::Firebird::Storage::ODBC')
  ->connect('dbi:ODBC:Driver=Firebird;Dbname=/path/app.fdb', $user, $pass);

The declared storage class stands (DBIO core ADR 0059): the storage stays in this class, and driver resolution adds nothing to the connect string.

The Firebird component and the -fb shortcut declare DBIO::Firebird::Storage, the DBD::Firebird class. An ODBC DSN under that declaration does not reach this class: the storage stays DBIO::Firebird::Storage, core warns about the driver mismatch, and that class appends ib_dialect=3 to the ODBC connect string. A schema that loads the component with load_components('Firebird') cannot declare this class on a clone either, since the component sets its storage class on every connection. Letting the Firebird driver adapt itself to an ODBC DSN (_adapt_to_access_path, ADR 0059) is open.

DBIO core ADR 0067: a schema always declares its driver; nothing detects it from the connected handle.

METHODS

connect_call_datetime_setup

Used as:

on_connect_call => 'datetime_setup'

A noop on this transport, so that the same connect_info works for all three Firebird DBDs. The native drivers need "connect_call_datetime_setup" in DBIO::Firebird::Storage::InterBase to put DBD::InterBase into ISO date and timestamp formats (ib_time_all); the ODBC driver already answers in the ODBC canonical form, which is the same %Y-%m-%d %H:%M:%S shape DBIO::Firebird::DateTime::Format parses.

Without this method the connect-call would die looking for it -- datetime_setup is resolved by name against the storage class.

The native storages put every connection into the ISO formats by default; this transport needs nothing analogous, because the ODBC driver already answers in the canonical form. That is the reading of the existing claim above, not something measured here: no Firebird ODBC driver was available to test against, so the transport is unchanged.

CAVEATS

  • Unverified. No Firebird server was reached over ODBC for any of this. The two savepoint workarounds and the OdbcFb array-operations one are upstream DBIx::Class findings carried over with their reasons; the datetime claim above is the ODBC canonical format rather than something observed. The ODBC leg of t/10-firebird.t, t/20-firebird-core.t and t/30-datetime-firebird.t (DBIO_TEST_FIREBIRD_ODBC_DSN) is where a server checks them.

  • Query plans are unavailable, see "Query plans are not reachable over ODBC".

SEE ALSO

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.