sqlite - 当 AutoCommit 设置为 false 时,perl DBI SQLite 提交或分离失败
问题描述
以下代码在运行 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 中的错误还是我做错了什么?
解决方案
根据文档,AutoCommit
关闭模式时:
The default transaction behavior of SQLite is deferred, that means, locks are not acquired until the first read or write operation, and thus it is possible that another thread or process could create a separate transaction and write to the database after the BEGIN on the current thread has executed, and eventually cause a "deadlock". To avoid this, DBD::SQLite internally issues a BEGIN IMMEDIATE if you begin a transaction by calling begin_work or by turning off AutoCommit (since 1.38_01).
If you really need to turn off this feature for some reasons, set sqlite_use_immediate_transaction database handle attribute to false, and the default deferred transaction will be used.
(This seems like undesirable behavior, and maybe it's because I'm tired but I don't see how you can get deadlocks; just errors when trying to lock a database that's already locked by another connection to it)
But anyways:
$ 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
That looks familiar...
The default behavior when AutoCommit
is off means you're always in a transaction with an acquired RESERVED lock. This has some unusual side-effects as you're seeing.
So, solutions in my order of preference:
- Turn on
AutoCommit
mode and manually begin transactions (With$dbh->begin_work
). - Skip the
DETACH
since you're closing the database connection anyways. - Leave
AutoCommit
disabled and set thesqlite_use_immediate_transaction
option to 0 when connecting to the database file.
推荐阅读
- python - 如何在使用 python 写入 XML 文件时保留 XML 版本和注释?
- azure - 在 Azure Durable Functions Orchestator 中使用异步辅助函数是否安全?
- angular - 我想通过事件发射器为我的 html 输入设置值,并且在提交表单时这些值没有绑定。如何解决问题?
- c - 添加一段正常工作的代码时出现分段错误
- python-3.x - 模式识别和序列检测
- powershell - Foreach 循环错误地将文件添加到父文件夹
- kubernetes - 如何使用 shell 脚本在 Kubernetes 中手动对哪些组件进行故障排除
- string - 如何根据字符的相似程度定义两个字符串之间的距离?
- windows - 当我启用 VT-X(虚拟化技术)intel i5-3450 时 PC 崩溃
- r - 在 R 中按特定时间范围过滤