2009-05-19 20 views
0

私はLAPP環境(linux apache postgresql php)で作業しています。私はトランザクション内で準備された文を使用する方法を知るためにtriynです(可能な場合)。準備されたステートメントをPHPからトランザクションを通して使用することはできますか?

例1、簡単なトランザクション:

私はコードが言葉その後、より良い説明を願って、私は右のトランザクションをundestood場合は、上記の例では

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; 

、データベース内でのみ有効になりますlast_activityの更新...あなたは?

/* 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"); 

をそして、それは正常に動作します:私はPHPでそのトランザクションを使用しようとすると

(両方PDOまたはPG_方法で)コードは、(例2)のようになります。私は準備された文と例2をenvolveしようとすると、私の問題が来る、とにかく

たぶん見て醜いが、が動作する(提案は常に歓迎されている)ように見える(私は例の準備の使用2ということを知っています文が

)実施例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 ...*/ 

まあ、例3は、単に仕事をいけない、準備されたステートメントが原因ロールバックでトランザクションた場合に有効になります。

したがって、準備された文をトランザクションで使用することはできませんか、間違った方法をとっていますか?

編集:いくつかの後

は、私は、この時点で到着しましたよ、PDOで試してみてください。

<?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(); 
?> 

上記のコードは、この出力で失敗します。だから、

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

、最初の実行が失敗したとき(id 293のもの)にトランザクションが自動的に中止されたように思えます... PDOの自動ロールバックなどがありますか?

私の目標は、最初のbig whileループを完了し、最後にフラグとしてbool varを使用して、トランザクションをロールバックするかコミットするかを決定することです。

+1

準備された文を実行することは、通常の文を実行するのと同じように、トランザクションの中で確実に動作するはずです。私はいつもそれらを使用しますが、PHPではなくPerlから使用します。おそらく、実際にサーバ上で実行されているものをトレースすると(set log_statement = 'all')、期待していないときにコミットが行われていると表示されますか? – araqnid

+0

私は、この問題がPDOの不適切な使用により多くなる可能性があると思います。 APIは、より深いフィードバックのためにライブラリを使用し、何が失敗しているのか、理由、場所、およびタイミングを簡単に確認できます。 –

+0

ちょっとeditet、ありがとう – Strae

答えて

0

を使用してPHP/Postgreの固体として、そのトランザクションは中止とマークされています。実際にロールバックされているわけではありません.を除き、何もほとんど実行できません。私は、PDOが自動的にロールバックを発行しないと仮定し、「ロールバック」メソッドを呼び出すのを待ちます。

あなたが望むと思うものを達成するために、セーブポイントを使用することができます。トランザクション全体をロールバックするのではなく、セーブポイントにロールバックしてトランザクションを続行するだけで済みます。私はpsqlのからこれを使用した例をあげる:

[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] =# 

は、したがって、この例では、トンの最初の列は主キーです。私はidに9の2つの行をtに挿入しようとしましたが、一意制約があります。現在のステートメントでは「現在のトランザクションは中止されました...」というエラーが発生するため、正しい値で挿入をやり直すことはできません。しかし、私は "savepointにロールバック"することができます。これは、私が "savepoint"( "xyzzy"はセーブポイントの名前です)をしたときの状態に戻ります。次に、正しいinsertコマンドを発行し、最後にトランザクションをコミットすることができます(両方の挿入をコミットします)。

あなたの場合、私はあなたのUPDATEステートメントの前にセーブポイントを作成する必要があると思います。エラーが発生した場合は、「savepointにロールバック」してフラグを設定します。セーブポイントの固有の名前を生成する必要があります。たとえば、カウンタを使用します。

私はこのすべてをやっている理由を理解していません。トランザクションをロールバックすることを知ったらすぐに処理を停止したいと思いますか?または、ループ内で何か他の処理が行われているかどうかを確認する必要がありますか?

+0

p_ordersの例では、p_ordersとp_itemsをすべてh_ordersとh_itemsテーブルに移動します。何かが失敗したら、すべて終了します。現実の世界では、すべてのp_orderにトランザクションを使用することができますが、これはトランザクションstmt環境の動作方法を手助けするための単なる例です。 – Strae

+0

DAMN - PDOはネストされたセーブポイントをサポートしません.- 私はちょうどループの始まりの前にセーブポイントを追加する必要があります。ありがとう、私はPHPのより良いdbハンドラを見回します(doctrine?) – Strae

1

また、あなたは最初の例の末尾にランダムにコミットしている

pdo_obj->beginTransaction() 
pdo_obj->commit() 
pdo_obj->prepare() 

を使用する必要があります。

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"); 

コミットしておらず、手作業で調整する必要がある場合は、別のトランザクションを使用してください。クエリが失敗してしまう可能性があるため、クエリの準備(リコールした場合)はトランザクションの一部です。実際にSQL文を手動で取得してクエリに変換することはできません。 PDOインタフェースには理由があるため抽象概念があります。:)

http://uk3.php.net/pdo < - 任意のステートメントはトランザクション中にサーバーエラーが発生した場合にPDO

のPostgreSQLで

幸運

+0

私はクエリを準備することはトランザクションの一部ではないと思います。私が手動で "BEGIN"、 "PREPARE test AS SELECT version()"、 "ABORT"、 "EXECUTE test"バージョン文字列しかし、クエリの準備に失敗した場合、トランザクションは中止とマークされているため、トランザクションに関することです。 – araqnid

+0

私はまだ取引で準備します。トランザクションに関連する論理コードブロック内の例外や無関係を簡単に取り込めるようにします。また、多くのクエリを持っているときに役立ち、読者としてその取引に近い「親」の中にそれらを準備します。準備されたステートメントが複数の場所で使用されている場合、これは異なります。 –

関連する問題