0

私はLAPP環境(linux apache postgresql php)で作業しており、トランザクション内で準備されたステートメントを使用する方法を見つけようとしています(可能な場合)。

コードが言葉よりもよく説明してくれることを願っています:

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

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 の更新になります... ええ?

そのトランザクションを php で (PDO または pg_ メソッドの両方で) 使用しようとすると、コードは次のようになります (例 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");

そして、それはうまくいきます。見にくいかもしれませんが、機能しているようです(提案はいつでも歓迎します) 。

とにかく、私の問題は、例2を準備済みステートメントで包囲しようとすると発生します(例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) "エラー: 重複キーが一意の制約 "id_h_orders" に違反しています" }

array(3) { [0]=> string(5) "25P02" [1]=> int(7) [2]=> string(87) "エラー: 現在のトランザクションは中止されました。トランザクション ブロックが終了するまでコマンドは無視されました" }

致命的なエラー: 23 行目の /srv/www/test-db/test-db-pgsql-08.php の非オブジェクトに対するメンバー関数 fetch() の呼び出し

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

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

4

2 に答える 2

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 を使用した PHP/Postgre の実例

幸運を

于 2009-05-19T11:31:11.117 に答える
0

PostgreSQL では、トランザクション中にいずれかのステートメントでサーバー エラーが発生した場合、そのトランザクションは中止としてマークされます。これは、実際にはまだロールバックされているという意味ではありません。ロールバック以外のことはほとんどできません。PDO は自動的にロールバックを発行しないと思います。「ロールバック」メソッドを呼び出すのを待ちます。

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

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

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

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

なぜあなたがこのようなことをしているのか、完全には理解できません。トランザクションをロールバックすることがわかったらすぐに処理を停止したいですか? それとも、ループ内で他の処理が行われているのでしょうか?

于 2009-05-20T11:31:48.067 に答える