chiark / gitweb /
Normalise commodity name case (from uploads)
[ypp-sc-tools.db-test.git] / yarrg / CommodsDatabase.pm
index 3cb543dc43b3866971cff2cb28e39de7f6061631..1f977d40b7cca3d40e8dd030b3a253165793512f 100644 (file)
@@ -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(<<END);
 
  # Every buy/sell must refer to an entry in commods, islands, and stalls:
- SELECT * FROM $bs NATURAL LEFT JOIN commods WHERE commodname IS NULL;
- SELECT * FROM $bs NATURAL LEFT JOIN islands WHERE islandname IS NULL;
- SELECT * FROM $bs LEFT JOIN STALLS USING (stallid) WHERE stallname IS NULL;
+ SELECT * FROM $bs LEFT JOIN commods USING (commodid) WHERE commodname IS NULL;
+ SELECT * FROM $bs LEFT JOIN islands USING (islandid) WHERE islandname IS NULL;
+ SELECT * FROM $bs LEFT JOIN stalls USING (stallid, islandid)
+                                                      WHERE stallname IS NULL;
 
  # Every buy/sell must be part of an upload:
- SELECT * FROM $bs NATURAL LEFT JOIN uploads WHERE timestamp IS NULL;
+ SELECT * FROM $bs LEFT JOIN uploads USING (islandid) WHERE timestamp IS NULL;
 
  # The islandid in stalls must be the same as the islandid in buy/sell:
  SELECT * FROM $bs JOIN stalls USING (stallid)
@@ -205,8 +232,10 @@ END
     nooutput(<<END);
 
  # Every stall and upload must refer to an island:
- SELECT * FROM stalls NATURAL LEFT JOIN islands WHERE islandname IS NULL;
- SELECT * FROM uploads NATURAL LEFT JOIN islands WHERE islandname IS NULL;
+ SELECT * FROM stalls LEFT JOIN islands USING (islandid)
+                                        WHERE islandname IS NULL;
+ SELECT * FROM uploads LEFT JOIN islands USING (islandid)
+                                         WHERE islandname IS NULL;
 
 END
     if ($full) {
@@ -229,9 +258,9 @@ END
        WHERE dist IS NULL;
 
  # Every commod must refers to a commodclass and vice versa:
- SELECT * FROM commods NATURAL LEFT JOIN commodclasses
+ SELECT * FROM commods LEFT JOIN commodclasses USING (commodclassid)
        WHERE commodclass IS NULL;
- SELECT * FROM commodclasses NATURAL LEFT JOIN commods
+ SELECT * FROM commodclasses LEFT JOIN commods USING (commodclassid)
        WHERE commodname IS NULL;
 
  # Ordvals which are not commodclass ordvals are unique:
@@ -244,17 +273,17 @@ END
  # For every class, posinclass is dense from 1 to maxposinclass,
  # apart from the commods for which it is zero.
  SELECT commodclass,commodclassid,posinclass,count(*)
-       FROM commods NATURAL JOIN commodclasses
+       FROM commods JOIN commodclasses USING (commodclassid)
        WHERE posinclass > 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