在运行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中的错误还是我做错了什么?
答案 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。