X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=ypp-sc-tools.db-test.git;a=blobdiff_plain;f=yarrg%2FCommodsDatabase.pm;h=1f977d40b7cca3d40e8dd030b3a253165793512f;hp=3cb543dc43b3866971cff2cb28e39de7f6061631;hb=HEAD;hpb=8de9e86e749db455deea697684b67be7874c5efa diff --git a/yarrg/CommodsDatabase.pm b/yarrg/CommodsDatabase.pm index 3cb543d..1f977d4 100644 --- a/yarrg/CommodsDatabase.pm +++ b/yarrg/CommodsDatabase.pm @@ -35,6 +35,7 @@ use warnings; use DBI; use POSIX; +use DBD::SQLite; use Commods; @@ -64,10 +65,35 @@ sub dbr_connect ($$) { 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 @@ -188,12 +214,13 @@ sub db_check_referential_integrity ($) { nooutput(< 0 GROUP BY commodclassid,posinclass HAVING count(*) > 1; SELECT commodclass,commodclassid,count(*) - FROM commods NATURAL JOIN commodclasses + FROM commods JOIN commodclasses USING (commodclassid) WHERE posinclass > 0 GROUP BY commodclassid HAVING count(*) != maxposinclass; SELECT * - FROM commods NATURAL JOIN commodclasses + FROM commods JOIN commodclasses USING (commodclassid) WHERE posinclass < 0 OR posinclass > maxposinclass; END