2016-06-21 57 views
6

Il PDO inTransaction() restituisce false mentre è ancora in una transazione se viene generata un'eccezione del database. Questo è probabilmente specifico per l'utilizzo di PostgreSQL. per esempio.PDO inTransaction() che restituisce false dopo l'eccezione del database

try { 
    $pdo->beginTransaction(); 
    $pdo->exec('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'); 
    // ... 
    // Cause any PDO exception 
    // ... 
    $pdo->commit(); 
} catch (\Exception $e) { 
    if ($pdo->inTransaction()) { 
     // Never gets here 
     $pdo->rollback(); 
    } 
    throw $e; 
} 

L'operazione è sicuramente non è finita, perché se comincio un altro ricevo un'eccezione che c'è già una transazione in corso. Non ho testato ogni tipo di eccezione, ma sta sicuramente accadendo per SQLSTATE[40001]: Serialization failure e violazioni delle chiavi primarie. Questo comportamento è previsto o è un bug in PHP?

Sembra che l'unico modo per conoscere il rollback sia mantenere una variabile separata per sapere che sono in una transazione, rendendo inutile inTransaction(). Ho notato che alcuni framework open source (come Doctrine) e applicazioni (come Drupal) mantengono la propria variabile per lo stato della transazione. Perché non possiamo affidarci al driver o al database per dirci se è in corso una transazione?

PHP 5.5.32 e PostgreSQL 9.4. Ho trovato un vecchio anno correlato bug report che è stato chiuso in una versione precedente di PHP.

+0

Vedere la risposta a http://stackoverflow.com/questions/22743357/autorollback-in-postgres-using-pdo ..... dagli sguardi di esso il "problema" era PostgreSQL non PHP e che quello che stai vedendo è normale. – Dave

risposta

4

per rispondere alle vostre domande:

È questo il comportamento previsto o è un bug in PHP?

No questo non è il comportamento previsto e deve essere un bug nella estensione PDO PgSQL.

Perché non possiamo contare sul driver o il database di dirci se una transazione è in corso?

Poiché i driver e i database sono creati dagli utenti. E gli esseri umani possono commettere errori quando creano un'applicazione così complessa come un database o un driver. Per me il tuo problema sembra che non sia un caso limite e potrebbe anche causare seri problemi di integrità in qualsiasi db. Si potrebbe anche voler considerare di aprire un ticker sul tracker bug php.

analisi del codice di patch

Tuttavia ho guardato dentro un po 'di più. Il confronto tra il codice della patch di 2 anni fa (source) e quello corrente del codice (source) mostra che nulla è cambiato da quando hanno corretto l'errore. Almeno non in quelle funzioni direttamente interessate, e potrebbe anche essere un bug leggermente diverso allora. Quindi la mia ipotesi è che ci sia qualcos'altro che, naturalmente, non ti aiuterà a risolvere il tuo problema.

Un lavoro possibile in tutto il proble corrente m:

si potrebbe verificare se la connessione è una transazione in sospeso. Per questo è necessario creare una seconda connessione quando si colpisce l'eccezione. Prima di tutto determinare il tuo attuale ID di connessione.

SELECT pg_backend_pid();

Nel mio caso è restituito il numero 19339. È necessario salvare questo numero prima di attivare la query che causerà l'eccezione. Ora nel blocco catch, è necessario guardare all'interno della tabella pg_catalog.pg_stat_activity.

cercare il vecchio collegamento e vedere se è stato è

active, idle in transaction o idle in transaction (aborted)

con:

SELECT state FROM pg_catalog.pg_stat_activity WHERE pid=19339;

Se restituisce idle non v'è alcuna transazione corrente per questo vecchia connessione. Se è uno dei tre superiori, c'è una transazione ancora attiva. Il manuale da PostgreSQL dice:

active: The backend is executing a query. 
idle: The backend is waiting for a new client command. 
idle in transaction: The backend is in a transaction, but is not currently executing a query. 
idle in transaction (aborted): This state is similar to idle in transaction, except one of the statements in the transaction caused an error. 
fastpath function call: The backend is executing a fast-path function. 
disabled: This state is reported if track_activities is disabled in this backend. 

L'ultimo stato indica il lato negativo di tutto questo. Funzionerà solo se il flag di configurazione track_activities è impostato su true.