]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add tests to check inter-process WAL locking.
authordan <dan@noemail.net>
Wed, 14 Apr 2010 18:06:50 +0000 (18:06 +0000)
committerdan <dan@noemail.net>
Wed, 14 Apr 2010 18:06:50 +0000 (18:06 +0000)
FossilOrigin-Name: 9435f3135849e0d38fde1669201db508561a6308

manifest
manifest.uuid
src/log.c
test/lock2.test
test/lock_common.tcl [new file with mode: 0644]
test/wal.test

index 611f989a4faa046fc2cb4fe2b21a034f0377eeb1..92200a9b4f302ac1ba95ebd51e74fec62282adc1 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improve\sthe\slogLockRegion()\sfunction\sin\slog.c.
-D 2010-04-14T15:49:40
+C Add\stests\sto\scheck\sinter-process\sWAL\slocking.
+D 2010-04-14T18:06:51
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -131,7 +131,7 @@ F src/journal.c b0ea6b70b532961118ab70301c00a33089f9315c
 F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
 F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
 F src/loadext.c 1c7a61ce1281041f437333f366a96aa0d29bb581
-F src/log.c e01488f4e515d17ecb21d6cebc5a37bec5b0e56c
+F src/log.c a3558ae5a48f808134db9b77a893141ba79402e7
 F src/log.h a2654af46ce7b5732f4d5a731abfdd180f0a06d9
 F src/main.c c0e7192bad5b90544508b241eb2487ac661de890
 F src/malloc.c a08f16d134f0bfab6b20c3cd142ebf3e58235a6a
@@ -473,12 +473,13 @@ F test/limit.test 2db7b3b34fb925b8e847d583d2eb67531d0ce67e
 F test/loadext.test 0393ce12d9616aa87597dd0ec88181de181f6db0
 F test/loadext2.test 0bcaeb4d81cd5b6e883fdfea3c1bdbe1f173cbca
 F test/lock.test 842e80b6be816c79525a20b098cca066989feed7
-F test/lock2.test 7bb642551df59b3de135291d62ee82409420181e
+F test/lock2.test ec208a5f394d92affaf599fde3f374361657d0ff
 F test/lock3.test f271375930711ae044080f4fe6d6eda930870d00
 F test/lock4.test f4f36271aa5ae1da449646bf43c7341f6b2b4c4e
 F test/lock5.test 6b1f78f09ad1522843dad571b76b321e6f439bf7
 F test/lock6.test 862aa71e97b288d6b3f92ba3313f51bd0b003776
 F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64
+F test/lock_common.tcl 58aa21f38c28223cc1107b5b2c9d7d61aa428e79
 F test/lookaside.test 1dd350dc6dff015c47c07fcc5a727a72fc5bae02
 F test/main.test 2be2352ac77ac5b238c6337a5469aeeef57677e6
 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
@@ -756,7 +757,7 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
 F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
-F test/wal.test a4be3c7a36e3db3d9d276dc7664c2ed5eece1e8a
+F test/wal.test 812dde0a689f69ce9b2d897ce4f08d752bd06749
 F test/walcrash.test 45cfbab30bb7cbe0b2e9d5cabe90dbcad10cb89b
 F test/walslow.test 38076d5fad49e3678027be0f8110e6a32d531dc2
 F test/walthread.test 58cd64b06f186251f09f64e4918fb74a7e52c963
@@ -803,7 +804,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P a9617eff39177250e2f118f25fdd4b3acb8b0478
-R 7008c4f8854b7fbb33a10340b4b449d8
+P 5e9dd3bd8e829376408925fb4cfcd5bb1eb1105f
+R b62050501d5438f40040e5ea09ab63d7
 U dan
-Z d0e2fbdd9e82a46dc4aa039b5fc8e3ce
+Z 3f45f30ce9e36f0f15e4d7966accef62
index 8fdc50e3bd7c3f0418fd847c2d102302d82c45e6..e7339388faa2a9c54e3954ebce52ff6cc28aab20 100644 (file)
@@ -1 +1 @@
-5e9dd3bd8e829376408925fb4cfcd5bb1eb1105f
\ No newline at end of file
+9435f3135849e0d38fde1669201db508561a6308
\ No newline at end of file
index 9b25216df40894720ef33db0fc13e451a9d14f1f..f1481bf92adcb6ade32bb14c71f2fa02ebea118a 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1100,7 +1100,7 @@ static int logLockRegion(Log *pLog, u32 mRegion, int op){
       /* 1000 */ {1, 1},    /* 1001 */ {0, 0}, 
       /* 1010 */ {0, 0},    /* 1011 */ {0, 0},
       /* 1100 */ {1, 2},    /* 1101 */ {0, 0}, 
-      /* 1110 */ {1, 3},    /* 1111 */ {0, 0}
+      /* 1110 */ {0, 0},    /* 1111 */ {0, 0}
     };
     int rc;                       /* Return code of fcntl() */
     struct flock f;               /* Locking operation */
index a2b75ca31122957bd39f46c653ca1a3038977dba..63319535d1f58cb1a2d000d228c47bccadb30f84 100644 (file)
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
+source $testdir/lock_common.tcl
 
-# Launch another testfixture process to be controlled by this one. A
-# channel name is returned that may be passed as the first argument to proc
-# 'testfixture' to execute a command. The child testfixture process is shut
-# down by closing the channel.
-proc launch_testfixture {} {
-  set prg [info nameofexec]
-  if {$prg eq ""} {
-    set prg [file join . testfixture]
-  }
-  set chan [open "|$prg tf_main.tcl" r+]
-  fconfigure $chan -buffering line
-  return $chan
-}
-
-# Execute a command in a child testfixture process, connected by two-way
-# channel $chan. Return the result of the command, or an error message.
-proc testfixture {chan cmd} {
-  puts $chan $cmd
-  puts $chan OVER
-  set r ""
-  while { 1 } {
-    set line [gets $chan]
-    if { $line == "OVER" } { 
-      return $r
-    }
-    if {[eof $chan]} {
-      return "ERROR: Child process hung up"
-    }
-    append r $line
-  }
-}
-
-# Write the main loop for the child testfixture processes into file
-# tf_main.tcl. The parent (this script) interacts with the child processes
-# via a two way pipe. The parent writes a script to the stdin of the child
-# process, followed by the word "OVER" on a line of its own. The child
-# process evaluates the script and writes the results to stdout, followed
-# by an "OVER" of its own.
-set f [open tf_main.tcl w]
-puts $f {
-  set l [open log w]
-  set script ""
-  while {![eof stdin]} {
-    flush stdout
-    set line [gets stdin]
-    puts $l "READ $line"
-    if { $line == "OVER" } {
-      catch {eval $script} result
-      puts $result
-      puts $l "WRITE $result"
-      puts OVER
-      puts $l "WRITE OVER"
-      flush stdout
-      set script ""
-    } else {
-      append script $line
-      append script " ; "
-    }
-  }
-  close $l
-}
-close $f
 
 # Simple locking test case:
 #
diff --git a/test/lock_common.tcl b/test/lock_common.tcl
new file mode 100644 (file)
index 0000000..31c04e8
--- /dev/null
@@ -0,0 +1,77 @@
+# 2010 April 14
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file contains code used by several different test scripts. The
+# code in this file allows testfixture to control another process (or
+# processes) to test locking.
+#
+
+# Launch another testfixture process to be controlled by this one. A
+# channel name is returned that may be passed as the first argument to proc
+# 'testfixture' to execute a command. The child testfixture process is shut
+# down by closing the channel.
+proc launch_testfixture {} {
+  set prg [info nameofexec]
+  if {$prg eq ""} {
+    set prg [file join . testfixture]
+  }
+  set chan [open "|$prg tf_main.tcl" r+]
+  fconfigure $chan -buffering line
+  return $chan
+}
+
+# Execute a command in a child testfixture process, connected by two-way
+# channel $chan. Return the result of the command, or an error message.
+proc testfixture {chan cmd} {
+  puts $chan $cmd
+  puts $chan OVER
+  set r ""
+  while { 1 } {
+    set line [gets $chan]
+    if { $line == "OVER" } { 
+      return $r
+    }
+    if {[eof $chan]} {
+      return "ERROR: Child process hung up"
+    }
+    append r $line
+  }
+}
+
+# Write the main loop for the child testfixture processes into file
+# tf_main.tcl. The parent (this script) interacts with the child processes
+# via a two way pipe. The parent writes a script to the stdin of the child
+# process, followed by the word "OVER" on a line of its own. The child
+# process evaluates the script and writes the results to stdout, followed
+# by an "OVER" of its own.
+set f [open tf_main.tcl w]
+puts $f {
+  set l [open log w]
+  set script ""
+  while {![eof stdin]} {
+    flush stdout
+    set line [gets stdin]
+    puts $l "READ $line"
+    if { $line == "OVER" } {
+      catch {eval $script} result
+      puts $result
+      puts $l "WRITE $result"
+      puts OVER
+      puts $l "WRITE OVER"
+      flush stdout
+      set script ""
+    } else {
+      append script $line
+      append script " ; "
+    }
+  }
+  close $l
+}
+close $f
index cafda909a4bd408358e5275279f0a9163f7d1d08..99a19fe40048388528f56c980682f5459904e5d3 100644 (file)
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
-
-proc range {args} {
-  set ret [list]
-  foreach {start end} $args {
-    for {set i $start} {$i <= $end} {incr i} {
-      lappend ret $i
-    }
-  }
-  set ret
-}
+source $testdir/lock_common.tcl
 
 proc reopen_db {} {
   catch { db close }
   file delete -force test.db test.db-wal
   sqlite3_wal db test.db
-  #register_logtest
-}
-proc register_logtest {{db db}} {
-  register_logsummary_module $db
-  execsql { CREATE VIRTUAL TABLE temp.logsummary USING logsummary } $db
-  execsql { CREATE VIRTUAL TABLE temp.logcontent USING logcontent } $db
-  execsql { CREATE VIRTUAL TABLE temp.loglock USING loglock } $db
 }
 
 proc sqlite3_wal {args} {
@@ -309,100 +293,186 @@ unset handle
 
 #-------------------------------------------------------------------------
 # The following block of tests - wal-10.* - test that the WAL locking 
-# scheme works for clients in a single process.
+# scheme works in simple cases. This block of tests is run twice. Once
+# using multiple connections in the address space of the current process,
+# and once with all connections except one running in external processes.
 #
-reopen_db
-sqlite3_wal db2 test.db
-sqlite3_wal db3 test.db
+foreach code [list {
+  set ::code2_chan [launch_testfixture]
+  set ::code3_chan [launch_testfixture]
+  proc code2 {tcl} { testfixture $::code2_chan $tcl }
+  proc code3 {tcl} { testfixture $::code3_chan $tcl }
+  set tn 1
+} {
+  proc code2 {tcl} { uplevel #0 $tcl }
+  proc code3 {tcl} { uplevel #0 $tcl }
+  set tn 2
+}] {
+
+  eval $code
+  reopen_db
 
-do_test wal-10.1 {
-  execsql {
-    CREATE TABLE t1(a, b);
-    INSERT INTO t1 VALUES(1, 2);
-    BEGIN;
-      INSERT INTO t1 VALUES(3, 4);
-  }
-  execsql "SELECT * FROM t1" db2
-} {1 2}
-do_test wal-10.2 {
-  execsql { COMMIT }
-  execsql "SELECT * FROM t1" db2
-} {1 2 3 4}
-do_test wal-10.3 {
-  execsql { 
-    BEGIN;
+  # Open connections [db2] and [db3]. Depending on which iteration this
+  # is, the connections may be created in this interpreter, or in 
+  # interpreters running in other OS processes. As such, the [db2] and [db3]
+  # commands should only be accessed within [code2] and [code3] blocks,
+  # respectively.
+  #
+  code2 { sqlite3 db2 test.db ; db2 eval { PRAGMA journal_mode = WAL } }
+  code3 { sqlite3 db3 test.db ; db3 eval { PRAGMA journal_mode = WAL } }
+
+  # Shorthand commands. Execute SQL using database connection [db2] or 
+  # [db3]. Return the results.
+  #
+  proc sql2 {sql} { code2 [list db2 eval $sql] }
+  proc sql3 {sql} { code3 [list db3 eval $sql] }
+
+  # Initialize the database schema and contents.
+  #
+  do_test wal-10.$tn.1 {
+    execsql {
+      CREATE TABLE t1(a, b);
+      INSERT INTO t1 VALUES(1, 2);
       SELECT * FROM t1;
-  } db2
-} {1 2 3 4}
-do_test wal-10.4 {
-  catchsql { PRAGMA checkpoint } 
-} {1 {database is locked}}
-do_test wal-10.5 {
-  execsql { INSERT INTO t1 VALUES(5, 6) }
-  execsql { SELECT * FROM t1 } db2
-} {1 2 3 4}
-
-# Connection [db2] is holding a lock on a snapshot, preventing [db] from
-# checkpointing the database. Add a busy-handler to [db]. If [db2] completes
-# its transaction from within the busy-handler, [db] is able to complete
-# the checkpoint operation.
-#
-proc busyhandler x {
-  if {$x==4} {
-    execsql { COMMIT } db2
+    }
+  } {1 2}
+
+  # Open a transaction and write to the database using [db]. Check that [db2]
+  # is still able to read the snapshot before the transaction was opened.
+  #
+  do_test wal-10.$tn.2 {
+    execsql { BEGIN; INSERT INTO t1 VALUES(3, 4); }
+    sql2 {SELECT * FROM t1}
+  } {1 2}
+
+  # Have [db] commit the transaction. Check that [db2] is now seeing the 
+  # new, updated snapshot.
+  #
+  do_test wal-10.$tn.3 {
+    execsql { COMMIT }
+    sql2 {SELECT * FROM t1}
+  } {1 2 3 4}
+
+  # Have [db2] open a read transaction. Then write to the db via [db]. Check
+  # that [db2] is still seeing the original snapshot. Then read with [db3].
+  # [db3] should see the newly committed data.
+  #
+  do_test wal-10.$tn.4 {
+    sql2 { BEGIN ; SELECT * FROM t1}
+  } {1 2 3 4}
+  do_test wal-10.$tn.5 {
+    execsql { INSERT INTO t1 VALUES(5, 6); }
+    sql2 {SELECT * FROM t1}
+  } {1 2 3 4}
+  do_test wal-10.$tn.6 {
+    sql3 {SELECT * FROM t1}
+  } {1 2 3 4 5 6}
+  do_test wal-10.$tn.7 {
+    sql2 COMMIT
+  } {}
+
+  # Have [db2] open a write transaction. Then attempt to write to the 
+  # database via [db]. This should fail (writer lock cannot be obtained).
+  #
+  # Then open a read-transaction with [db]. Commit the [db2] transaction
+  # to disk. Verify that [db] still cannot write to the database (because
+  # it is reading an old snapshot).
+  #
+  # Close the current [db] transaction. Open a new one. [db] can now write
+  # to the database (as it is not locked and [db] is reading the latest
+  # snapshot).
+  #
+  do_test wal-10.$tn.7 {
+    sql2 { BEGIN; INSERT INTO t1 VALUES(7, 8) ; }
+    catchsql { INSERT INTO t1 VALUES(9, 10) }
+  } {1 {database is locked}}
+  do_test wal-10.$tn.8 {
+    execsql { BEGIN ; SELECT * FROM t1 }
+  } {1 2 3 4 5 6}
+  do_test wal-10.$tn.9 {
+    sql2 COMMIT
+    catchsql { INSERT INTO t1 VALUES(9, 10) }
+  } {1 {database is locked}}
+  do_test wal-10.$tn.10 {
+    execsql { COMMIT; BEGIN; INSERT INTO t1 VALUES(9, 10); COMMIT; }
+    execsql { SELECT * FROM t1 }
+  } {1 2 3 4 5 6 7 8 9 10}
+
+  # Open a read transaction with [db2]. Check that this prevents [db] from
+  # checkpointing the database. But not from writing to it.
+  #
+  do_test wal-10.$tn.11 {
+    sql2 { BEGIN; SELECT * FROM t1 }
+  } {1 2 3 4 5 6 7 8 9 10}
+  do_test wal-10.$tn.12 {
+    catchsql { PRAGMA checkpoint } 
+  } {1 {database is locked}}
+  do_test wal-10.$tn.13 {
+    execsql { INSERT INTO t1 VALUES(11, 12) }
+    sql2 {SELECT * FROM t1}
+  } {1 2 3 4 5 6 7 8 9 10}
+
+  # Connection [db2] is holding a lock on a snapshot, preventing [db] from
+  # checkpointing the database. Add a busy-handler to [db]. If [db2] completes
+  # its transaction from within the busy-handler, [db] is able to complete
+  # the checkpoint operation.
+  #
+  proc busyhandler x {
+    if {$x==4} { sql2 COMMIT }
+    if {$x<5} { return 0 }
+    return 1
   }
-  if {$x<5} {return 0}
-  return 1
-}
-db busy busyhandler
-do_test wal-10.6 {
-  execsql { PRAGMA checkpoint } 
-} {}
+  db busy busyhandler
+  do_test wal-10.$tn.14 {
+    execsql { PRAGMA checkpoint } 
+  } {}
+
+  # Similar to the test above. Except this time, a new read transaction is
+  # started (db3) while the checkpointer is waiting for an old one (db2) to 
+  # finish. The checkpointer can finish, but any subsequent write operations 
+  # must wait until after db3 has closed the read transaction, as db3 is a
+  # "region D" writer.
+  #
+  db busy {}
+  do_test wal-10.$tn.15 {
+    sql2 { BEGIN; SELECT * FROM t1; }
+  } {1 2 3 4 5 6 7 8 9 10 11 12}
+  do_test wal-10.$tn.16 {
+    catchsql { PRAGMA checkpoint } 
+  } {1 {database is locked}}
+  proc busyhandler x {
+    if {$x==3} { sql3 { BEGIN; SELECT * FROM t1 } }
+    if {$x==4} { sql2 COMMIT }
+    if {$x<5}  { return 0 }
+    return 1
+  }
+  db busy busyhandler
+  do_test wal-10.$tn.9 {
+    execsql { PRAGMA checkpoint } 
+  } {}
+  do_test wal-10.$tn.10 {
+    sql3 { SELECT * FROM t1 }
+  } {1 2 3 4 5 6 7 8 9 10 11 12}
+  do_test wal-10.$tn.11 {
+    catchsql { INSERT INTO t1 VALUES(13, 14) }
+  } {1 {database is locked}}
+  do_test wal-10.$tn.12 {
+    execsql { SELECT * FROM t1 }
+  } {1 2 3 4 5 6 7 8 9 10 11 12}
+  do_test wal-10.$tn.13 {
+    sql3 COMMIT
+  } {}
+  do_test wal-10.$tn.14 {
+    execsql { INSERT INTO t1 VALUES(13, 14) }
+    execsql { SELECT * FROM t1 }
+  } {1 2 3 4 5 6 7 8 9 10 11 12 13 14}
 
-# Similar to the test above. Except this time, a new read transaction is
-# started (db3) while the checkpointer is waiting for an old one to finish.
-# The checkpointer can finish, but any subsequent write operations must
-# wait until after db3 has closed the read transaction.
-#
-db busy {}
-do_test wal-10.7 {
-  execsql { 
-    BEGIN;
-      SELECT * FROM t1;
-  } db2
-} {1 2 3 4 5 6}
-do_test wal-10.8 {
-  execsql { INSERT INTO t1 VALUES(7, 8) }
-  catchsql { PRAGMA checkpoint } 
-} {1 {database is locked}}
-proc busyhandler x {
-  if {$x==3} { execsql { BEGIN; SELECT * FROM t1 } db3 }
-  if {$x==4} { execsql { COMMIT } db2 }
-  if {$x<5}  { return 0 }
-  return 1
+  catch { db close }
+  catch { code2 { db2 close } }
+  catch { code3 { db3 close } }
+  catch { close $::code2_chan }
+  catch { close $::code3_chan }
 }
-db busy busyhandler
-do_test wal-10.9 {
-  execsql { PRAGMA checkpoint } 
-} {}
-do_test wal-10.10 {
-  execsql { SELECT * FROM t1 } db3
-} {1 2 3 4 5 6 7 8}
-do_test wal-10.11 {
-  catchsql { INSERT INTO t1 VALUES(9, 10) }
-} {1 {database is locked}}
-do_test wal-10.12 {
-  execsql { SELECT * FROM t1 }
-} {1 2 3 4 5 6 7 8}
-do_test wal-10.13 {
-  execsql { COMMIT } db3
-} {}
-do_test wal-10.14 {
-  execsql { INSERT INTO t1 VALUES(9, 10) }
-  execsql { SELECT * FROM t1 }
-} {1 2 3 4 5 6 7 8 9 10}
 
-foreach handle {db db2 db3} { catch { $handle close } }
-unset handle
 finish_test