Problem
When using fbird_commit_ret() for auto-commit simulation (as doctrine-firebird-driver does), metadata locks from SELECT cursors are held by the retained transaction. This causes DDL operations (CREATE TABLE, DROP TABLE, ALTER TABLE) to hang indefinitely when they follow schema introspection queries.
Reproduction
$conn = fbird_connect("localhost/3050:/path/to/db.fdb", "SYSDBA", "masterkey", "UTF8");
// Start a transaction (simulating auto-commit)
$trans = fbird_trans_start(...);
// SELECT from RDB$RELATIONS (schema introspection)
$result = fbird_query($conn, "SELECT * FROM RDB\$RELATIONS WHERE RDB\$SYSTEM_FLAG = 0", $trans);
$rows = fbird_fetch_all($result);
fbird_free_result($result);
// Commit retain (keeps transaction open for statement reuse)
fbird_commit_ret($trans);
// DDL operation — HANGS because metadata locks from the SELECT are still held
fbird_query($conn, "CREATE TABLE test_table (id INTEGER)", $trans);
// ↑ hangs indefinitely
Root cause
Firebird holds metadata locks at the transaction level, not the cursor level. fbird_commit_ret() commits data changes but keeps the transaction open, which means:
- Metadata locks from SELECT cursors remain held
- DDL operations that need exclusive metadata locks deadlock
fbird_free_result() frees the cursor handle but does NOT release the metadata locks
Current workaround
The doctrine-firebird-driver uses gc_collect_cycles() and retry loops before DDL operations to force PHP to destroy Result objects, which triggers __destruct() → free(). But this is fragile and doesn't always work.
Using fbird_commit() (not retain) fixes the hang but invalidates all prepared statements, breaking statement reuse — a common DBAL pattern.
Desired solution
One of:
-
New function: fbird_release_metadata_locks($connection) — releases metadata locks without closing the transaction or invalidating prepared statements. This would correspond to a Firebird API feature if one exists, or a client-side workaround (e.g., starting a new transaction for DDL operations and switching back).
-
New parameter on fbird_commit_ret(): fbird_commit_ret($trans, FBIRD_RELEASE_LOCKS) — commits and retains but releases metadata locks.
-
Auto-detection in fbird_prepare_ex() / fbird_query(): when preparing a DDL statement, automatically commit and re-create the transaction if the current transaction holds metadata locks.
Impact
This affects any PHP application using php-firebird with DBAL's schema introspection + migration APIs. The doctrine-firebird-driver test suite has 10+ tests that exercise this pattern (SchemaTest, SchemaManagerFunctionalTestCase, etc.) and all require workarounds.
Environment
- php-firebird: v13.0.0
- PHP: 8.5.8
- Firebird: 3.0.14, 4.0.7, 5.0.4
- OS: Linux (Arch)
Problem
When using
fbird_commit_ret()for auto-commit simulation (asdoctrine-firebird-driverdoes), metadata locks from SELECT cursors are held by the retained transaction. This causes DDL operations (CREATE TABLE, DROP TABLE, ALTER TABLE) to hang indefinitely when they follow schema introspection queries.Reproduction
Root cause
Firebird holds metadata locks at the transaction level, not the cursor level.
fbird_commit_ret()commits data changes but keeps the transaction open, which means:fbird_free_result()frees the cursor handle but does NOT release the metadata locksCurrent workaround
The doctrine-firebird-driver uses
gc_collect_cycles()and retry loops before DDL operations to force PHP to destroy Result objects, which triggers__destruct()→free(). But this is fragile and doesn't always work.Using
fbird_commit()(not retain) fixes the hang but invalidates all prepared statements, breaking statement reuse — a common DBAL pattern.Desired solution
One of:
New function:
fbird_release_metadata_locks($connection)— releases metadata locks without closing the transaction or invalidating prepared statements. This would correspond to a Firebird API feature if one exists, or a client-side workaround (e.g., starting a new transaction for DDL operations and switching back).New parameter on
fbird_commit_ret():fbird_commit_ret($trans, FBIRD_RELEASE_LOCKS)— commits and retains but releases metadata locks.Auto-detection in
fbird_prepare_ex()/fbird_query(): when preparing a DDL statement, automatically commit and re-create the transaction if the current transaction holds metadata locks.Impact
This affects any PHP application using php-firebird with DBAL's schema introspection + migration APIs. The
doctrine-firebird-drivertest suite has 10+ tests that exercise this pattern (SchemaTest, SchemaManagerFunctionalTestCase, etc.) and all require workarounds.Environment