2009-05-19 3 views
0

Ich arbeite an einer LAPP-Umgebung (Linux Apache Postgresql PHP), und ich bin nur Triyn, um herauszufinden, wie Sie die vorbereitete Anweisung innerhalb der Transaktion (wenn möglich) verwenden.Kann die vorbereitete Anweisung durch die Transaktion von PHP verwendet werden?

I-Code hoffentlich besser erklären, dann Worte:

Beispiel 1, einfache Transaktion:

BEGIN; 
INSERT INTO requests (user_id, description, date) VALUES ('4', 'This dont worth anything', NOW()); 
UPDATE users SET num_requests = (num_requests + 1) WHERE id = '4'; 
--something gone wrong, cancel the transaction 
ROLLBACK; 
UPDATE users SET last_activity = NOW() WHERE id = '4' 
COMMIT; 

In dem obigen Beispiel, wenn ich Recht, die Transaktion undestood, wird der einzige Effekt in der Datenbank sein das Update von last_activity ... ye?

Wenn ich versuche, diese Transaktion in PHP zu verwenden (beide mit gU oder pg_ Methoden) sollte der Code aussieht, dass (Beispiel 2): ​​

/* skip the connection */ 
pg_query($pgConnection, "BEGIN"); 
pg_query($pgConnection, "INSERT INTO requests (user_id, description, date) VALUES ('$id_user', 'This dont worth anything', NOW())"); 
pg_query($pgConnection, "UPDATE users SET num_requests = (num_requests + 1) WHERE id = '$id_user'"); 
//something gone wrong, cancel the transaction 
pg_query($pgConnection, "ROLLBACK"); 
pg_query($pgConnection, "UPDATE users SET last_activity = NOW() WHERE id = '$id_user'"); 
pg_query($pgConnection, "COMMIT"); 

Und das funktioniert gut. Vielleicht hässlich zu sehen, aber scheinen (Vorschlag immer willkommen ist) zu arbeiten

Wie auch immer, mein Problem kommen, wenn ich versuche, das Beispiel 2 mit den vorbereiteten Anweisungen zu envolve (ich weiß, dass in dem Beispiel 2 die Verwendung vorbereitet Aussagen nicht sehr nützlich ist)

Beispiel 3:

/* skip the connection */ 
pg_prepare($pgConnection, 'insert_try', "INSERT INTO requests (user_id, description, date) VALUES ('$1', '$2', $3)"); 
pg_query($pgConnection, "BEGIN"); 
pg_execute($pgConnection, 'insert_try', array($user_id, 'This dont worth anything', date("Y-m-d"))); 
/* and so on ...*/ 

Nun, das Beispiel 3 einfach nicht funktioniert, wird die vorbereitete Anweisung, wenn die Transaktion fällig Rollbacks wirksam sein.

So können die vorbereiteten Anweisungen nicht in der Transaktion verwendet werden, oder nehme ich den falschen Weg?

EDIT:

Nach einigen Versuchen mit PDO, ich an diesem Punkt angekommen bin:

<?php 
$dbh = new PDO('pgsql:host=127.0.0.1;dbname=test', 'myuser', 'xxxxxx'); 

$rollback = false; 

$dbh->beginTransaction(); 

//create the prepared statements 
$insert_order = $dbh->prepare('INSERT INTO h_orders (id, id_customer, date, code) VALUES (?, ?, ?, ?)'); 
$insert_items = $dbh->prepare('INSERT INTO h_items (id, id_order, descr, price) VALUES (?, ?, ?, ?)'); 
$delete_order = $dbh->prepare('DELETE FROM p_orders WHERE id = ?'); 

//move the orders from p_orders to h_orders (history) 
$qeOrders = $dbh->query("SELECT id, id_customer, date, code FROM p_orders LIMIT 1"); 
while($rayOrder = $qeOrders->fetch(PDO::FETCH_ASSOC)){ 
    //h_orders already contain a row with id 293 
    //lets make the query fail 
    $insert_order->execute(array('293', $rayOrder['id_customer'], $rayOrder['date'], $rayOrder['code'])) OR var_dump($dbh->errorInfo()); 
    //this is the real execute 
    //$insert_order->execute(array($rayOrder['id'], $rayOrder['id_customer'], $rayOrder['date'], $rayOrder['code'])) OR die(damnIt('insert_order')); 
    //for each order, i move the items too 
    $qeItems = $dbh->query("SELECT id, id_order, descr, price FROM p_items WHERE id_order = '" . $rayOrder['id'] . "'") OR var_dump($dbh->errorInfo()); 
    while($rayItem = $qeItems->fetch(PDO::FETCH_ASSOC)){ 
     $insert_items->execute(array($rayItem['id'], $rayItem['id_order'], $rayItem['descr'], $rayItem['price'])) OR var_dump($dbh->errorInfo()); 
    } 
    //if everything is ok, delete the order from p_orders 
    $delete_order->execute(array($rayOrder['id'])) OR var_dump($dbh->errorInfo()); 
} 
//in here i'll use a bool var to see if anythings gone wrong and i need to rollback, 
//or all good and commit 
$dbh->rollBack(); 
//$dbh->commit(); 
?> 

Der Code nicht oben mit diesem Ausgang:

array(3) { [0]=> string(5) "00000" [1]=> int(7) [2]=> string(62) "ERROR: duplicate key violates unique constraint "id_h_orders"" }

array(3) { [0]=> string(5) "25P02" [1]=> int(7) [2]=> string(87) "ERROR: current transaction is aborted, commands ignored until end of transaction block" }

Fatal error: Call to a member function fetch() on a non-object in /srv/www/test-db/test-db-pgsql-08.php on line 23

So, Wenn die erste Ausführung fehlschlägt (die mit ID 293), wird die Transaktion automatisch abgebrochen ... macht das PDO automatisches Rollback oder etwas anderes?

Mein Ziel ist es, die erste große while-Schleife zu beenden, und am Ende, mit einem bool var als Flag, entscheiden, ob Rollback oder Commit die Transaktion.

+1

Die Ausführung vorbereiteter Anweisungen sollte in Transaktionen genauso funktionieren wie die Ausführung regulärer Anweisungen. Ich benutze sie die ganze Zeit, aber von Perl, nicht PHP. Vielleicht wird die Verfolgung, was tatsächlich auf dem Server ausgeführt wird (set log_statement = 'all'), angezeigt, wenn Commits ausgeführt werden, wenn Sie sie nicht erwartet haben? – araqnid

+0

Ich denke, dass das Problem mehr auf falsche Verwendung von PDO zurückzuführen ist. Die API verwendet die Bibliothek für tiefere Rückmeldungen und macht es einfacher zu sehen, was fehlschlägt, warum, wo und wann. –

+0

Nur editet, danke – Strae

Antwort

0

Mit PostgreSQL, wenn jede Aussage während einer Transaktion einen Server-Fehler erzeugt, wird diese Transaktion als abgebrochen markiert . Das bedeutet nicht, dass es tatsächlich zurückgerollt ist - nur dass Sie kaum etwas tun können außer rollen Sie es zurück. Ich nehme an, dass PDO nicht automatisch einen Rollback ausgibt, es wartet darauf, dass Sie die "Rollback" -Methode aufrufen.

Um zu erreichen, was ich denke, Sie können einen Sicherungspunkt verwenden. Anstatt die gesamte Transaktion rückgängig zu machen, können Sie einfach zum Sicherungspunkt zurückkehren und die Transaktion fortsetzen. Ich werde ein Beispiel für die Verwendung dieses von psql geben:

[email protected]@[local] =# begin; 
BEGIN 
[email protected]@[local] *=# insert into t values(9,6,1,true); 
INSERT 0 1 
[email protected]@[local] *=# savepoint xyzzy; 
SAVEPOINT 
[email protected]@[local] *=# insert into t values(9,6,2,true); 
ERROR: duplicate key value violates unique constraint "t_pkey" 
[email protected]@[local] !=# insert into t values(10,6,2,true); 
ERROR: current transaction is aborted, commands ignored until end of transaction block 
[email protected]@[local] !=# rollback to savepoint xyzzy; 
ROLLBACK 
[email protected]@[local] *=# insert into t values(10,6,2,true); 
INSERT 0 1 
[email protected]@[local] *=# commit; 
COMMIT 
[email protected]@[local] =# 

So in diesem Beispiel die erste Spalte von t ist der Primärschlüssel. Ich habe versucht, zwei Zeilen in t mit einer ID von 9 einzufügen, und bekam eine Eindeutigkeitsbedingung. Ich kann die Einfügung nicht einfach mit den richtigen Werten wiederholen, da jetzt jede Anweisung den Fehler "aktuelle Transaktion wird abgebrochen ..." erhalten wird. Aber ich kann "rollback to savepoint" machen, was mich zurück zu dem Zustand bringt, in dem ich war, als ich "savepoint" gemacht habe ("xyzzy" ist der Name des Savepoints). Dann kann ich den korrekten Einfügebefehl ausgeben und schließlich die Transaktion festschreiben (die beide Einfügungen festschreibt).

Also in Ihrem Fall, ich vermute, was Sie tun müssen, ist ein Sicherungspunkt vor Ihrer UPDATE-Anweisung zu erstellen: Wenn es einen Fehler gibt, ein "Rollback zu Savepoint" und setzen Sie Ihre Flagge. Sie müssen eindeutige Namen für die Sicherungspunkte generieren, z. B. mit einem Zähler.

Ich bin mir nicht ganz sicher, ich verstehe, warum Sie das alles tun. Möchten Sie die Verarbeitung beenden, sobald Sie wissen, dass Sie die Transaktion rückgängig machen möchten? Oder gibt es eine andere Verarbeitung in der Schleife, die auch passieren muss?

+0

Was im Beispiel p_orders zu tun ist, ist es, alle p_orders und p_items in die Tabelle h_orders und h_items zu verschieben. Wenn etwas schief geht, beende alles. In der realen Welt werde ich eine Transaktion für jeden p_order verwenden, aber dies ist nur ein Beispiel, um mir zu helfen, wie das transaction-stmt environmanet funktioniert. – Strae

+0

DAMN - PDO unterstützt keinen verschachtelten Savepoint -.- Nun, ich habe es versucht Lösung über Shell, und es funktioniert, ich muss nur den Sicherungspunkt vor dem Beginn der Schleife hinzufügen .. danke, ich werde nach einem besseren DB-Handler für PHP (Lehre?) – Strae

1

Sie

am Ende Ihrer ersten Beispiel
pdo_obj->beginTransaction() 
pdo_obj->commit() 
pdo_obj->prepare() 

Auch Sie haben eine zufällige begehen verwenden sollten.

begin 
// do all your stuff 
// check for errors through interface 
commit OR not 

pg_query($pgConnection, "ROLLBACK"); // end of tx(1) 
// start new transaction after last rollback = tx(2) 
pg_query($pgConnection, "UPDATE users SET last_activity = NOW() WHERE id = '$id_user'"); 
// commit tx(2) or don't here 
// this isn't needed pg_query($pgConnection, "COMMIT"); 

Wenn Sie nicht festgelegt haben und manuell anpassen müssen, verwenden Sie eine andere Transaktion. Die Vorbereitung Ihrer Anfrage (wenn ich mich erinnere) ist Teil einer Transaktion, weil sie fehlschlagen kann. Sie können eine SQL-Anweisung nicht wirklich manuell erstellen und sie in Abfragen umwandeln. Die PDO-Schnittstelle hat aus einem Grund Abstraktionen.:)

http://uk3.php.net/pdo < - Solidee Beispiele für PHP/Postgre PDO mit

Glück

+0

Ich denke nicht, dass die Vorbereitung einer Abfrage Teil einer Transaktion ist - wenn ich manuell "BEGIN", "PREPARE Test AS SELECT Version()", "ABORT", "EXECUTE test" es zurückgibt die Versionszeichenfolge Wenn jedoch das Vorbereiten einer Abfrage fehlschlägt, wird Ihre Transaktion als abgebrochen markiert, sodass sie in diesem Zusammenhang mit der Transaktion in Zusammenhang stehen. – araqnid

+0

würde ich noch in einer Transaktion vorbereiten. Erleichtert das Codieren zum Abfangen von Ausnahmen und was nicht in einem logischen Codeblock, der sich auf die Transaktion bezieht. Es hilft auch, wenn Sie viele Abfragen haben, bereiten Sie sie fast "Eltern" zu dieser Transaktion als der Leser vor. Wenn die vorbereitete Anweisung an mehreren Stellen verwendet wird, ist dies anders. –