Skip to content

Feature: metadata lock release API for auto-commit mode (fbird_commit_ret holds locks, blocking DDL after introspection) #540

Description

@satwareAG-ironMike

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:

  1. Metadata locks from SELECT cursors remain held
  2. DDL operations that need exclusive metadata locks deadlock
  3. 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:

  1. 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).

  2. New parameter on fbird_commit_ret(): fbird_commit_ret($trans, FBIRD_RELEASE_LOCKS) — commits and retains but releases metadata locks.

  3. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions