AutoCommit设置为false时,perl DBI SQLite提交或分离失败

时间:2019-03-08 13:49:55

标签: sqlite perl

在运行Strawberry Perl 5.24的Windows 10计算机上,以下代码失败:

use DBI;

unlink glob("*.db3");
my $source = DBI->connect("dbi:SQLite:dbname=first.db3",q(),q(),{AutoCommit => 0, RaiseError =>1});

# populate source DB
$source->do("CREATE TABLE test(x integer)");
$source->do("INSERT INTO test(x) values (1)");
$source->commit();
$source->disconnect();

# copy source to dest
my $dest = DBI->connect("dbi:SQLite:dbname=second.db3",q(),q(),{AutoCommit => 0, RaiseError =>1});

$dest->do("CREATE TABLE test(x integer)");
$dest->do("ATTACH DATABASE 'first.db3' AS chunk_db");
$dest->do("INSERT INTO test(x) SELECT x FROM chunk_db.test");

# this statement will fail when AutoCommit => 0
$dest->commit;
$dest->do("DETACH DATABASE chunk_db");
$dest->disconnect();

要使其工作,我必须在创建AutoCommit => 1对象时与$dest连接。否则,我将收到以下错误消息:

   DBD::SQLite::db do failed: database chunk_db is locked at test.pl line 21.

这是DBI中的错误还是我做错了什么?

1 个答案:

答案 0 :(得分:1)

根据documentation,当AutoCommit模式关闭时:

  

SQLite的默认事务行为被推迟,这意味着直到第一次读或写操作才获取锁,因此,另一个线程或进程可能会创建一个单独的事务并在BEGIN之后写入数据库当前线程上的已执行,并最终导致“死锁”。为避免这种情况,如果您通过调用begin_work或关闭AutoCommit(自1.38_01起)开始事务,则DBD :: SQLite在内部会发出BEGIN IMMEDIATE。

     

如果由于某些原因您确实需要关闭此功能,请将sqlite_use_immediate_transaction数据库的handle属性设置为false,将使用默认的延迟事务。

(这似乎是不受欢迎的行为,也许是因为我很累,但是我看不到如何获得死锁;尝试锁定已经被另一个连接锁定的数据库时出现错误)

但是无论如何:

$ sqlite3 second.db3
sqlite> attach database 'first.db3' as chunk_db;
sqlite> begin immediate;
sqlite> detach database chunk_db;
Error: database chunk_db is locked

看起来很熟悉...

AutoCommit处于关闭状态时的默认行为表示您始终正在与获得的RESERVED lock进行交易。如您所见,这有一些不寻常的副作用。

所以,按我的喜好排序:

  • 打开AutoCommit模式并手动开始交易(使用$dbh->begin_work)。
  • 由于始终要关闭数据库连接,因此跳过DETACH
  • 在连接到数据库文件时,禁用AutoCommit并将sqlite_use_immediate_transaction选项设置为0。