]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add extra tests to e_walckpt.test.
authordan <dan@noemail.net>
Tue, 9 Dec 2014 20:13:40 +0000 (20:13 +0000)
committerdan <dan@noemail.net>
Tue, 9 Dec 2014 20:13:40 +0000 (20:13 +0000)
FossilOrigin-Name: 84f9581019961efa31297f8be48427b17bcca857

manifest
manifest.uuid
src/test1.c
test/e_walckpt.test

index b0f6c74e0a4a63a925a707ac48213f1fb273315c..0baa94d673dffbf52218884e673c6292e19783e3 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Lower\sthe\sdefault\sSQLITE_SORTER_PMASZ\svalue\sback\sto\s10,\swhere\sit\shas\sbeen\sfor\nthe\spast\scouple\sof\sreleases.\s\sApplications\sthat\sneed\sa\slarger\svalue\scan\sset\none.
-D 2014-12-09T19:16:41.997
+C Add\sextra\stests\sto\se_walckpt.test.
+D 2014-12-09T20:13:40.856
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 6c4f961fa91d0b4fa121946a19f9e5eac2f2f809
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -238,7 +238,7 @@ F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 81712116e826b0089bb221b018929536b2b5406f
 F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc
 F src/tclsqlite.c 0a874655dd39a9875e39c5d3c464db662171d228
-F src/test1.c fa655a378b11bf811f0c15c56eb1d232118224cc
+F src/test1.c f60b9e973cf813fdabb2b67ccbb4a08e9f1d81be
 F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
 F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
@@ -478,7 +478,7 @@ F test/e_update.test 312cb8f5ccfe41515a6bb092f8ea562a9bd54d52
 F test/e_uri.test 5ae33760fb2039c61aa2d90886f1664664173585
 F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9
 F test/e_wal.test 0967f0b8f1dfda871dc7b9b5574198f1f4f7d69a
-F test/e_walckpt.test 18de8fca6b74f29bf7d24a2e267eec749b8fec50
+F test/e_walckpt.test 3116a98fa0dd9b2c9e493de7c59730adfe436746
 F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
 F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
 F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
@@ -1226,7 +1226,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 4f7549ff924b8ed8f90fc447cd4be11421453984
-R 07540d2951e499e9ba9f784727d9cc12
-U drh
-Z 7254b0c65f796e3de7bb7b37671c0413
+P 1ba8911c18f2fe34c20dc42f25a8f3c1c798fa7a
+R 3c53b0c88f36488767cc876fe1e6e0b0
+U dan
+Z bbcee16517facfe70298aa47dfb20c31
index f4de208a5d7f8be7a4d53e3ff7cee4f44343b64c..587f7e93dcfe0a80d5c71e5f76f51e025f571534 100644 (file)
@@ -1 +1 @@
-1ba8911c18f2fe34c20dc42f25a8f3c1c798fa7a
\ No newline at end of file
+84f9581019961efa31297f8be48427b17bcca857
\ No newline at end of file
index 9f97c7371fb1e02f3232bba1a324817ca5486d9f..009b88a5432e0a35a4af5dd963bf85ae1dba5dad 100644 (file)
@@ -5712,6 +5712,7 @@ static int test_wal_checkpoint_v2(
   rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt);
   if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
     const char *zErrCode = sqlite3ErrName(rc);
+    Tcl_ResetResult(interp);
     Tcl_AppendResult(interp, zErrCode, " - ", (char *)sqlite3_errmsg(db), 0);
     return TCL_ERROR;
   }
index f0edc46beeef0c845cd02a98164b3e5ceb143fb5..e6b6566bed0c60db1c72ee448090c405107ddead 100644 (file)
@@ -42,6 +42,38 @@ proc compare_db_hashes {} {
   set ret
 }
 
+#-------------------------------------------------------------------------
+# All calls to the [sqlite3_wal_checkpoint_v2] command made within this
+# file use this wrapper. It's sole purpose is to throw an error if the
+# following requirement is violated:
+#
+# EVIDENCE-OF: R-60567-47780 Unless it returns SQLITE_MISUSE, the
+# sqlite3_wal_checkpoint_v2() interface sets the error information that
+# is queried by sqlite3_errcode() and sqlite3_errmsg().
+#
+proc wal_checkpoint_v2 {db args} {
+  set rc [catch {
+    uplevel sqlite3_wal_checkpoint_v2 $db $args
+  } msg]
+
+  set errcode "SQLITE_OK"
+  if {$rc} {
+    set errcode [lindex [split $msg " "] 0]
+  } elseif { [lindex $msg 0] } {
+    set errcode "SQLITE_BUSY"
+  }
+
+  if {$errcode != "SQLITE_MISUSE" && [sqlite3_errcode $db] != $errcode} {
+    error "sqlite3_errcode mismatch! (1) $errcode!=[sqlite3_errcode $db]"
+  }
+
+  if {$rc==0} {
+    return $msg
+  } else {
+    error $msg
+  }
+}
+
 
 # The following tests are run 3 times, each using a different method of 
 # invoking a checkpoint:
@@ -63,7 +95,7 @@ proc compare_db_hashes {} {
 foreach {tn script} {
   1 {
     proc checkpoint {db mode args} {
-      eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args
+      eval wal_checkpoint_v2 [list $db] [list $mode] $args
     }
   }
 
@@ -90,7 +122,7 @@ foreach {tn script} {
           error "$rc - [sqlite3_errmsg $db]"
         }
       } else {
-        eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args
+        eval wal_checkpoint_v2 [list $db] [list $mode] $args
       }
     }
   }
@@ -270,36 +302,46 @@ foreach {tn script} {
       }
 
       2 {
-        # Close first the reader, then later the writer.
+        # Close first the reader, then later the writer. Give up before
+        # closing the [db6] reader.
         if {$n==5}  { catch {db2 eval commit} }
         if {$n==10} { catch {db3 eval commit} }
+        if {$n==15} { return 1 }
         return 0
       }
 
       3 {
-        # Close first the writer, then later the reader.
+        # Close first the writer, then later the reader. And finally the 
+        # [db6] reader.
         if {$n==5}  { catch {db2 eval commit} }
         if {$n==10} { catch {db3 eval commit} }
+        if {$n==15} { catch {db6 eval commit} }
         return 0
       }
     }
   }
 
   foreach {mode busy_handler_mode} { 
-    passive 1
-    full    1
-    full    2
-    full    3
+    passive  1
+    full     1       full     2       full    3
+    restart  1       restart  2       restart  3
+    truncate 1       truncate 2       truncate 3
   } {
+    set tp "$tn.$mode.$busy_handler_mode"
 
     set ::sync_counter 0
 
+    # Set up a callback function for xSync and xWrite calls made during
+    # the checkpoint.
+    #
+    set ::checkpoint_ongoing 0
     proc tvfs_callback {method args} {
+      if {$::checkpoint_ongoing==0} return
+
       set tail [file tail [lindex $args 0]]
       if {$method == "xSync" && $tail == "test.db"} {
         incr ::sync_counter
       }
-
       if {$method == "xWrite" && $tail=="test.db"} {
         if {$::write_ok < 0} {
           set ::write_ok [expr ![catch {db5 eval { BEGIN IMMEDIATE }}]]
@@ -308,6 +350,14 @@ foreach {tn script} {
         if {$::read_ok < 0} {
           set ::read_ok [expr ![catch {db5 eval { SELECT * FROM t1 }}]]
         }
+
+        # If one has not already been opened, open a read-transaction using
+        # connection [db6]
+        catch { db6 eval { BEGIN ; SELECT * FROM sqlite_master } } msg
+      }
+      if {$method == "xShmLock" } {
+        set details [lindex $args 2]
+        if {$details == "0 1 lock exclusive"} { set ::seen_writer_lock 1 }
       }
     }
 
@@ -318,7 +368,7 @@ foreach {tn script} {
     #tvfs filter xSync
     tvfs script tvfs_callback
 
-    do_execsql_test $tn.4.$mode.0 {
+    do_execsql_test $tp.0 {
       CREATE TABLE t1(a, b);
       CREATE TABLE t2(a, b);
       PRAGMA journal_mode = wal;
@@ -328,7 +378,7 @@ foreach {tn script} {
     } {wal}
 
     # Open a reader on the current database snapshot.
-    do_test $tn.4.$mode.1 {
+    do_test $tp.1 {
       sqlite3 db2 test.db -vfs tvfs
       execsql {
         BEGIN;
@@ -338,7 +388,7 @@ foreach {tn script} {
 
     # Open a writer. Write a transaction. Then begin, but do not commit,
     # a second transaction.
-    do_test $tn.4.$mode.2 {
+    do_test $tp.2 {
       sqlite3 db3 test.db -vfs tvfs
       execsql {
         INSERT INTO t2 VALUES(7, 8);
@@ -349,6 +399,7 @@ foreach {tn script} {
     } {1 2 3 4 5 6 7 8 9 10}
 
     sqlite3 db5 test.db -vfs tvfs
+    sqlite3 db6 test.db -vfs tvfs
 
     # Register a busy-handler with connection [db].
     #
@@ -357,11 +408,15 @@ foreach {tn script} {
     set ::busy_handler_counter 0
     set ::read_ok -1
     set ::write_ok -1
+    set ::seen_writer_lock 0
     
-    do_test $tn.4.$mode.3 {
+    set ::checkpoint_ongoing 1
+    do_test $tp.3 {
       checkpoint db $mode main
       set {} {}
     } {}
+    set ::checkpoint_ongoing 0
+    set ::did_restart_blocking [expr {[catch {db6 eval commit}]}]
 
     if { $mode=="passive" } {
       # EVIDENCE-OF: R-16333-64433 Checkpoint as many frames as possible
@@ -381,15 +436,15 @@ foreach {tn script} {
       # EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
       # in the SQLITE_CHECKPOINT_PASSIVE mode.
       #
-      #   It's not. Test case "$tn.4.$mode.6".
+      #   It's not. Test case "$tp.6".
       #
-      do_test $tn.4.$mode.4 {
+      do_test $tp.4 {
         forcecopy test.db abc.db
         sqlite3 db4 abc.db
         db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
       } {1 2 3 4 5 6}
-      do_test $tn.4.$mode.5 { set ::sync_counter } 0
-      do_test $tn.4.$mode.6 { set ::busy_handler_counter } 0
+      do_test $tp.5 { set ::sync_counter } 0
+      do_test $tp.6 { set ::busy_handler_counter } 0
       db4 close
   
       db2 eval COMMIT
@@ -405,26 +460,53 @@ foreach {tn script} {
       #   Also, because the checkpoint finishes this time, the db is synced.
       #   Which is part of R-16333-64433 above.
       #
-      do_test $tn.4.$mode.7 {
+      set ::checkpoint_ongoing 1
+      do_test $tp.7 {
         checkpoint db $mode main
         forcecopy test.db abc.db
         sqlite3 db4 abc.db
         db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
       } {1 2 3 4 5 6 7 8 9 10}
-      do_test $tn.4.$mode.6 { set ::sync_counter } 1
-      do_test $tn.4.$mode.7 { set ::busy_handler_counter } 0
+      set ::checkpoint_ongoing 0
+      do_test $tp.7 { set ::sync_counter } 1
+      do_test $tp.8 { set ::busy_handler_counter } 0
       db4 close
     }
 
-    if { $mode=="full" } {
+    if { $mode=="full" || $mode=="restart" || $mode=="truncate" } {
+
+      # EVIDENCE-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
+      # TRUNCATE modes also obtain the exclusive "writer" lock on the 
+      # database file.
+      #
+      #   Or at least attempts to obtain.
+      #
+      do_test $tp.9 {
+        set ::seen_writer_lock
+      } {1}
+
       if {$busy_handler_mode==2 || $busy_handler_mode==3} {
         # EVIDENCE-OF: R-59171-47567 This mode blocks (it invokes the
         # busy-handler callback) until there is no database writer and all
         # readers are reading from the most recent database snapshot.
         #
-        #   Show that both the reader and writer have finished:
+        #   The test below shows that both the reader and writer have 
+        #   finished:
+        #
+        #   Also restated by the following two. That both busy_handler_mode
+        #   values 2 and 3 work show that both of the following are true - as
+        #   they release the reader and writer transactions in different
+        #   orders.
+        #
+        # EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
+        # immediately, and a busy-handler is configured, it is invoked and the
+        # writer lock retried until either the busy-handler returns 0 or the
+        # lock is successfully obtained.
         #
-        do_test $tn.4.$mode.7 {
+        # EVIDENCE-OF: R-48107-00250 The busy-handler is also invoked while
+        # waiting for database readers as described above.
+        #
+        do_test $tp.7 {
           list [catchsql COMMIT db2] [catchsql COMMIT db3]
         } [list                                             \
             {1 {cannot commit - no transaction is active}}  \
@@ -434,27 +516,79 @@ foreach {tn script} {
         # EVIDENCE-OF: R-29177-48281 It then checkpoints all frames in the log
         # file and syncs the database file.
         #
-        do_test $tn.4.$mode.8 {
+        do_test $tp.8 {
           forcecopy test.db abc.db
           sqlite3 db4 abc.db
           db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
         } {1 2 3 4 5 6 7 8 9 10}
-        do_test $tn.4.$mode.9 { set ::sync_counter } 1
+        do_test $tp.9 { set ::sync_counter } 1
         db4 close
 
         # EVIDENCE-OF: R-51867-44713 This mode blocks new database writers
         # while it is pending, but new database readers are allowed to continue
         # unimpeded.
-        do_test $tn.4.$mode.10 {
+        #
+        # EVIDENCE-OF: R-47276-58266 Like SQLITE_CHECKPOINT_FULL, this mode
+        # blocks new database writer attempts while it is pending, but does not
+        # impede readers.
+        #
+        #   The first of the above two refers to "full" mode. The second
+        #   to "restart".
+        #
+        do_test $tp.10.1 {
           list $::write_ok $::read_ok
         } {0 1}
 
+        # EVIDENCE-OF: R-12410-31217 This mode works the same way as
+        # SQLITE_CHECKPOINT_FULL with the addition that after checkpointing the
+        # log file it blocks (calls the busy-handler callback) until all
+        # readers are reading from the database file only.
+        #
+        #     The stuff above passed, so the first part of this requirement
+        #     is met. The second part is tested below. If the checkpoint mode
+        #     was "restart" or "truncate", then the busy-handler will have
+        #     been called to block on wal-file readers.
+        #
+        do_test $tp.11 {
+          set ::did_restart_blocking
+        } [expr {($mode=="restart"||$mode=="truncate")&&$busy_handler_mode==3}]
+
+        # EVIDENCE-OF: R-44699-57140 This mode works the same way as
+        # SQLITE_CHECKPOINT_RESTART with the addition that it also truncates
+        # the log file to zero bytes just prior to a successful return.
+        if {$mode=="truncate" && $busy_handler_mode==3} {
+          do_test $tp.12 {
+            file size test.db-wal
+          } 0
+        }
+      } elseif {$busy_handler_mode==1} {
+
+        # EVIDENCE-OF: R-34519-06271 SQLITE_BUSY is returned in this case.
+        if {$tn!=2} {
+          # ($tn==2) is the loop that uses "PRAGMA wal_checkpoint"
+          do_test $tp.13 { sqlite3_errcode db } {SQLITE_BUSY}
+        }
+
+        # EVIDENCE-OF: R-49155-63541 If the busy-handler returns 0 before the
+        # writer lock is obtained or while waiting for database readers, the
+        # checkpoint operation proceeds from that point in the same way as
+        # SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
+        # without blocking any further.
+        do_test $tp.14 {
+          forcecopy test.db abc.db
+            sqlite3 db4 abc.db
+            db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
+        } {1 2 3 4 5 6}
+        do_test $tp.15 { set ::sync_counter } 0
+        do_test $tp.16 { set ::busy_handler_counter } 1
+        db4 close
       }
     }
 
     db2 close
     db3 close
     db5 close
+    db6 close
   }
 
   db close
@@ -480,9 +614,139 @@ foreach {tn mode res} {
   8  1000000 {1 {SQLITE_MISUSE - not an error}}
 } {
   do_test 4.$tn {
-    list [catch "sqlite3_wal_checkpoint_v2 db $mode" msg] $msg
+    list [catch "wal_checkpoint_v2 db $mode" msg] $msg
   } $res
 }
+db close
+
+foreach tn {1 2 3} {
+  forcedelete test.db test.db2 test.db3
+  testvfs tvfs
+
+  sqlite3 db test.db -vfs tvfs
+  execsql {
+    ATTACH 'test.db2' AS aux2;
+    ATTACH 'test.db3' AS aux3;
+    PRAGMA main.journal_mode = WAL;
+    PRAGMA aux2.journal_mode = WAL;
+    PRAGMA aux3.journal_mode = WAL;
+
+    CREATE TABLE main.t1(x,y);
+    CREATE TABLE aux2.t2(x,y);
+    CREATE TABLE aux3.t3(x,y);
+
+    INSERT INTO t1 VALUES('a', 'b');
+    INSERT INTO t2 VALUES('a', 'b');
+    INSERT INTO t3 VALUES('a', 'b');
+  }
+  sqlite3 db2 test.db2 -vfs tvfs
+
+  switch -- $tn {
+    1 {
+      # EVIDENCE-OF: R-41299-52117 If no error (SQLITE_BUSY or otherwise) is
+      # encountered while processing the attached databases, SQLITE_OK is
+      # returned.
+      do_test 5.$tn.1 {
+        lindex [wal_checkpoint_v2 db truncate] 0
+      } {0}    ;# 0 -> SQLITE_OK
+      do_test 5.$tn.2 {
+        list [expr [file size test.db-wal]==0]  \
+             [expr [file size test.db2-wal]==0] \
+             [expr [file size test.db3-wal]==0]
+      } {1 1 1}
+    }
+
+    2 {
+      # EVIDENCE-OF: R-38578-34175 If an SQLITE_BUSY error is encountered when
+      # processing one or more of the attached WAL databases, the operation is
+      # still attempted on any remaining attached databases and SQLITE_BUSY is
+      # returned at the end.
+      db2 eval { BEGIN; INSERT INTO t2 VALUES('d', 'e'); }
+      do_test 5.$tn.1 {
+        lindex [wal_checkpoint_v2 db truncate] 0
+      } {1}    ;# 1 -> SQLITE_BUSY
+      do_test 5.$tn.2 {
+        list [expr [file size test.db-wal]==0]  \
+             [expr [file size test.db2-wal]==0] \
+             [expr [file size test.db3-wal]==0]
+      } {1 0 1}
+      db2 eval ROLLBACK
+    }
+
+    3 {
+      # EVIDENCE-OF: R-38049-07913 If any other error occurs while processing
+      # an attached database, processing is abandoned and the error code is
+      # returned to the caller immediately.
+      tvfs filter xWrite
+      tvfs script inject_ioerr
+      proc inject_ioerr {method file args} {
+        if {[file tail $file]=="test.db2"} {
+          return "SQLITE_IOERR"
+        }
+        return 0
+      }
+      do_test 5.$tn.1 {
+        list [catch { wal_checkpoint_v2 db truncate } msg] $msg
+      } {1 {SQLITE_IOERR - disk I/O error}}
+      do_test 5.$tn.2 {
+        list [expr [file size test.db-wal]==0]  \
+             [expr [file size test.db2-wal]==0] \
+             [expr [file size test.db3-wal]==0]
+      } {1 0 0}
+      tvfs script ""
+    }
+  }
+
+  db close
+  db2 close
+}
+
+reset_db
+sqlite3 db2 test.db
+
+do_test 6.1 {
+  execsql {
+    PRAGMA journal_mode = WAL;
+    CREATE TABLE t1(a, b);
+    INSERT INTO t1 VALUES(1, 2);
+  }
+  file size test.db-wal
+} [wal_file_size 3 1024]
+
+do_test 6.2 {
+  db2 eval { BEGIN; SELECT * FROM t1; }
+  db  eval { INSERT INTO t1 VALUES(3, 4) }
+  file size test.db-wal
+} [wal_file_size 4 1024]
+
+#   At this point the log file contains 4 frames. 3 of which it should
+#   be possible to checkpoint.
+#
+# EVIDENCE-OF: R-16642-42503 If pnLog is not NULL, then *pnLog is set to
+# the total number of frames in the log file or to -1 if the checkpoint
+# could not run because of an error or because the database is not in
+# WAL mode.
+#
+# EVIDENCE-OF: R-10514-25250 If pnCkpt is not NULL,then *pnCkpt is set
+# to the total number of checkpointed frames in the log file (including
+# any that were already checkpointed before the function was called) or
+# to -1 if the checkpoint could not run due to an error or because the
+# database is not in WAL mode.
+#
+do_test 6.4 {
+  lrange [wal_checkpoint_v2 db passive] 1 2
+} {4 3} 
+
+# EVIDENCE-OF: R-37257-17813 Note that upon successful completion of an
+# SQLITE_CHECKPOINT_TRUNCATE, the log file will have been truncated to
+# zero bytes and so both *pnLog and *pnCkpt will be set to zero.
+#
+do_test 6.5 {
+  db2 eval COMMIT
+  wal_checkpoint_v2 db truncate
+} {0 0 0}
+
 
 
 finish_test
+