1# 2010 April 13 2# 3# The author disclaims copyright to this source code. In place of 4# a legal notice, here is a blessing: 5# 6# May you do good and not evil. 7# May you find forgiveness for yourself and forgive others. 8# May you share freely, never taking more than you give. 9# 10#*********************************************************************** 11# This file implements regression tests for SQLite library. The 12# focus of this file is testing the operation of the library in 13# "PRAGMA journal_mode=WAL" mode. 14# 15 16set testdir [file dirname $argv0] 17 18source $testdir/tester.tcl 19if {[run_thread_tests]==0} { finish_test ; return } 20 21do_test walthread-1.1 { 22 execsql { 23 PRAGMA journal_mode = WAL; 24 CREATE TABLE t1(x PRIMARY KEY); 25 INSERT INTO t1 VALUES(randomblob(100)); 26 INSERT INTO t1 VALUES(randomblob(100)); 27 INSERT INTO t1 SELECT md5sum(x) FROM t1; 28 } 29} {wal} 30do_test walthread-1.2 { 31 execsql { 32 SELECT (SELECT count(*) FROM t1), ( 33 SELECT md5sum(x) FROM t1 WHERE oid != (SELECT max(oid) FROM t1) 34 ) == ( 35 SELECT x FROM t1 WHERE oid = (SELECT max(oid) FROM t1) 36 ) 37 } 38} {3 1} 39do_test walthread-1.3 { 40 execsql { PRAGMA integrity_check } 41} {ok} 42do_test walthread-1.4 { 43 execsql { PRAGMA lock_status } 44} {main unlocked temp unknown} 45 46#-------------------------------------------------------------------------- 47# Start N threads. Each thread performs both read and write transactions. 48# Each read transaction consists of: 49# 50# 1) Reading the md5sum of all but the last table row, 51# 2) Running integrity check. 52# 3) Reading the value stored in the last table row, 53# 4) Check that the values read in steps 1 and 3 are the same, and that 54# the md5sum of all but the last table row has not changed. 55# 56# Each write transaction consists of: 57# 58# 1) Modifying the contents of t1 (inserting, updating, deleting rows). 59# 2) Appending a new row to the table containing the md5sum() of all 60# rows in the table. 61# 62# Each of the N threads runs N read transactions followed by a single write 63# transaction in a loop as fast as possible. 64# 65# Ther is also a single checkpointer thread. It runs the following loop: 66# 67# 1) Execute "CHECKPOINT main 32 -1 1" 68# 2) Sleep for 500 ms. 69# 70 71set thread_program { 72 proc rest {ms} { 73 set ::rest 0 74 after $ms {set ::rest 1} 75 vwait ::rest 76 } 77 78 proc dosql {DB sql} { 79 set res "" 80 set stmt [sqlite3_prepare_v2 $DB $sql -1 dummy_tail] 81 set rc [sqlite3_step $stmt] 82 if {$rc eq "SQLITE_ROW"} { 83 set res [sqlite3_column_text $stmt 0] 84 } 85 set rc [sqlite3_finalize $stmt] 86 87 if {$rc ne "SQLITE_OK"} { 88 error "$rc: [sqlite3_errmsg $DB]" 89 } 90 return $res 91 } 92 93 proc read_transaction {DB} { 94 dosql $DB BEGIN 95 96 set md5_1 [dosql $DB { 97 SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1) 98 }] 99 set check [dosql $DB { PRAGMA integrity_check }] 100 set md5_2 [dosql $DB { 101 SELECT x FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1) 102 }] 103 set md5_3 [dosql $DB { 104 SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1) 105 }] 106 107 dosql $DB COMMIT 108 109 if {$check ne "ok" 110 || $md5_1 ne $md5_2 111 || $md5_2 ne $md5_3 112 } { 113 error "Failed read transaction $check $md5_1 $md5_2 $md5_3" 114 } 115 } 116 117 proc write_transaction {DB} { 118 dosql $DB BEGIN 119 dosql $DB "INSERT INTO t1 VALUES(randomblob(100))" 120 dosql $DB "INSERT INTO t1 VALUES(randomblob(100))" 121 dosql $DB "INSERT INTO t1 SELECT md5sum(x) FROM t1" 122 dosql $DB COMMIT 123 } 124 125 proc checkpointer {DB} { 126 while { !$::finished } { 127 dosql $DB "PRAGMA checkpoint" 128 rest 1000 129 } 130 } 131 132 proc worker {DB N} { 133 set j 0 134 while { !$::finished } { 135 for {set i 0} {$i < $N} {incr i} { read_transaction $DB } 136 write_transaction $DB 137 rest 1 138 } 139 } 140 141 set ::finished 0 142 after [expr $seconds*1000] {set ::finished 1} 143 144 set ::DB [sqlthread open test.db] 145 dosql $::DB { PRAGMA journal_mode = WAL } 146 147 148 set rc [catch { 149 if {$role eq "worker"} { worker $DB $N } 150 if {$role eq "checkpointer"} { checkpointer $DB } 151 } msg] 152 153 sqlite3_close $::DB 154 155 if {$rc==0} { set msg OK } 156 set msg 157} 158 159set NTHREAD 6 160set SECONDS 30 161 162#set prg "set N $NTHREAD ; set seconds $SECONDS" 163set prg "set N 1 ; set seconds $SECONDS" 164 165array unset finished 166for {set i 0} {$i < $NTHREAD} {incr i} { 167 thread_spawn finished($i) {set role worker} $prg $thread_program 168} 169thread_spawn finished(C) {set role checkpointer} $prg $thread_program 170#set finished(C) 1 171 172puts "... test runs for approximately $SECONDS seconds ..." 173for {set i 0} {$i < $::NTHREAD} {incr i} { 174 if {![info exists finished($i)]} { 175 vwait finished($i) 176 } 177 do_test walthread-2.$i { 178 set ::finished($i) 179 } OK 180} 181do_test walthread-2.C { 182 if {![info exists finished(C)]} { vwait finished(C) } 183 set ::finished(C) 184} OK 185 186set logsize 0 187 188set rows [execsql { SELECT count(*) FROM t1 }] 189catch { set logsize [expr [file size test.db-wal] / 1024] } 190set dbsize [expr [file size test.db] / 1024] 191 192puts "rows=$rows db=${dbsize}K log=${logsize}K" 193 194finish_test 195 196 197