2009-05-19 57 views
0

我在一個LAPP環境(linux apache postgresql php)上工作,而我只是三元組來找出如何在事務中使用準備好的語句(如果可能的話)。無法使用準備好的語句通過交易,從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; 

在上面的例子,如果我undestood權交易,在數據庫中的唯一的影響將是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) "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自動回滾還是其他?

我的目標是完成第一個大的while循環,最後使用bool var作爲標誌,決定是否回滾或提交事務。

+1

執行準備好的語句肯定應該像執行正則語句一樣在事務內部工作。我一直使用它們,但是來自Perl,而不是PHP。也許跟蹤服務器上實際正在執行的內容(set log_statement ='all')是否會在您不期望提交時完成提交? – araqnid 2009-05-19 23:15:14

+0

我認爲這個問題可能更多的是PDO使用不當。 API使用庫來獲得更深入的反饋,並使查看失敗的原因,原因,地點和時間更簡單。 – 2009-05-20 00:59:27

+0

只是editet,謝謝 – Strae 2009-05-20 07:04:34

回答

0

在PostgreSQL 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] =# 

所以在這個例子中,t的第一列是主鍵。我試圖插入兩行到ID爲9的t,並獲得唯一性約束。我不能只用正確的值重新插入,因爲現在任何語句都會得到「當前事務已中止...」錯誤。但是我可以做「回退到保存點」,這使我回到當我做「保存點」時的狀態(「xyzzy」是保存點的名稱)。然後我可以發出正確的插入命令並最終提交事務(它提交了兩個插入)。

因此,對於您的情況,我懷疑您需要做的是在您的UPDATE語句之前創建一個保存點:如果發生錯誤,請執行「回退到保存點」並設置標誌。例如,您需要爲保存點生成唯一的名稱:使用計數器。

我不完全確定我明白你爲什麼要這麼做。當你知道你要回滾交易時,你一定要立即停止處理?或者在循環中還有其他一些處理要發生嗎?

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

好運