在大型PDO sqlite事务中捕获错误


Error trapping within a large PDO sqlite transaction

我正在将文本文件解析为使用php处理文件数据的空sqlite数据库。我使用以下命令建立数据库连接:

try {
  $db = new PDO('sqlite:temp.db');
  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e) {
  echo 'Connection failed: ' . $e->getMessage();
  die($error);
}

由于用户文件数据很大,没有排序,并且有多个内部外键依赖项,因此我在外键上使用deferable INITIALLY DEFERRED将数据作为单个事务插入。只要我的原始输入数据是正确的,这就可以很好地工作。当发生用户输入错误时,我将获得整个事务的单一通用异常(当然,随后会回滚)。用户错误通常(但不完全)表现为未定义的外键引用。

有没有人建议一种获取更多信息的方法,例如,在事务中导致异常的单个插入语句,或者丢失的特定外键,这将有助于跟踪用户输入错误?

由于用户输入数据没有顺序,并且该数据的结构很复杂,因此在提交之前验证它将非常复杂,如果可能的话,我希望避免手动编写该方面的代码。

// $queries is a query by query array of your insert data
// we'll assume any sub-arrays are initialized appropriately on demand
$primary_keys = array();
$foreign_keys = array();
foreach ($queries as $idx => $data) {
    // build your query appropriately
    // if this is an insert to a table with a primary key
    // that will be referenced by a foreign key:
        $primary_keys[$table_inserting_to][$idx] = $data['primary_key']; // the primary key value we're inserting
    // elseif this is an insert to a table with a foreign key
    // that references a primary key that could fail:
        $foreign_keys[$table_inserting_to][$idx] = $data['foreign_key']; // the foreign key value we're inserting
    // run your query
}
// commit your transaction
// whoops, we failed
// build the associations we need to check
$links = array('table_with_foreign_key' => 'table_with_primary_key', 'table_with_foreign_key_2' => 'table_with_primary_key_2');
$errors = array();
foreach ($links as $foreign => $primary) {
    $failed_keys = array_diff($foreign_keys[$foreign], $primary_keys[$primary]);
    foreach ($failed_keys as $key) {
        $errors[] = "Foreign key check $foreign to $primary, $key could not be found on query idx ".array_search($key, $foreign_keys[$foreign]);
    }
}

…如果您需要考虑数据库中已经存在的主键,您总是可以查询它们以初始化$primary_keys数组。

这个答案是基于您正在使用准备好的语句的假设

如果不是这样,你应该做必要的调整。

。收集数组

中的参数
$params = array(':name' => 'foo', ':cat' => 'bar', ':val' => 'baz', [...]);

B。在调用execute($params)之前,执行以下操作,在文件中注册您正在使用的参数:

file_put_contents('file.log', implode(';', $params) . "'r'n"); //or "'n" on linux, "'r" on mac

C。call execute($params)

D。如果查询成功,则GOTO A,否则,死亡。

使用这个简单的算法,您将有一个日志文件(名为file.log),其中包含已成功插入的所有参数以及错误的参数(在最后一行)。

可能的改进:

这个算法效率不高,因为你打开和关闭文件多次。如果您关心效率,请遵循以下说明:

X。定义$log = array();

。收集数组

中的参数
$params = array(':name' => 'foo', ':cat' => 'bar', ':val' => 'baz', [...]);

B。在调用execute($params)之前,执行以下操作来存储您正在使用的参数:

$log[] = implode(';', $params) . "'r'n"; //or "'n" on linux, "'r" on mac

C。call execute($params)

D。如果查询成功,则GOTO A,否则,file_put_contents('file.log', implode("'r'n", $log)); .

<<p> 最后考虑/strong>

将上述建议与错误引发的PDO异常结合起来。它的堆栈对于调试总是非常有用的。

要使用pdo启用例外,请在连接后使用此命令:

$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);