2009-05-19 47 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

好运