use DBI;
use POSIX;
+use DBD::SQLite;
use Commods;
sub db_connect_core ($) {
my ($fn)= @_;
- my $h= DBI->connect("dbi:SQLite:$fn",'','',
- { AutoCommit=>0,
- RaiseError=>1, ShowErrorStatement=>1,
- unicode=>1 })
+ my $opts = { AutoCommit=>0,
+ RaiseError=>1, ShowErrorStatement=>1,
+ sqlite_unicode=>1 };
+
+ # DBI now wants to start a transaction whenever we even say
+ # SELECT. But this doesn't work if the DB is readonly. We can
+ # work around this by setting autocommit, in which case there is
+ # no need for a transaction for read-only db commands. Autocommit
+ # is (obviously) safe with readonly operations. But callers in
+ # yarrg do not specify to us whether they intend to write. So we
+ # decide, by looking at the file mode. And as belt-and-braces we
+ # set sqlite's own readonly flag as well.
+ # http://stackoverflow.com/questions/30082008/attempt-to-write-a-readonly-database-but-im-not
+ # http://stackoverflow.com/questions/35208727/can-sqlite-db-files-be-made-read-only
+ # http://cpansearch.perl.org/src/ISHIGAKI/DBD-SQLite-1.39/Changes
+ # (see entry for 1.38_01)
+ # http://stackoverflow.com/questions/17793672/perl-dbi-treats-setting-sqlite-db-cache-size-as-a-write-operation-when-subclassi
+ # https://rt.cpan.org/Public/Bug/Display.html?id=56444#
+ my $readonly =
+ (access $fn, POSIX::W_OK) ? 0 :
+ ($! == EACCES) ? 1 :
+ ($! == ENOENT) ? 0 :
+ die "$fn access(,W_OK) $!";
+ if ($readonly) {
+ $opts->{sqlite_open_flags} = DBD::SQLite::OPEN_READONLY;
+ $opts->{AutoCommit}=1;
+ }
+
+ my $h= DBI->connect("dbi:SQLite:$fn",'','',$opts)
or die "$fn $DBI::errstr ?";
return $h;
# default timeout is 30s which is plenty