1# 2015 February 16 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# 12 13if {![info exists testdir]} { 14 set testdir [file join [file dirname [info script]] .. .. test] 15} 16source $testdir/tester.tcl 17source $testdir/lock_common.tcl 18set ::testprefix rbu12 19 20set setup_sql { 21 DROP TABLE IF EXISTS xx; 22 DROP TABLE IF EXISTS xy; 23 CREATE TABLE xx(a, b, c PRIMARY KEY); 24 INSERT INTO xx VALUES(1, 2, 3); 25 CREATE TABLE xy(a, b, c PRIMARY KEY); 26 27 ATTACH 'rbu.db' AS rbu; 28 DROP TABLE IF EXISTS data_xx; 29 CREATE TABLE rbu.data_xx(a, b, c, rbu_control); 30 INSERT INTO data_xx VALUES(4, 5, 6, 0); 31 INSERT INTO data_xx VALUES(7, 8, 9, 0); 32 CREATE TABLE rbu.data_xy(a, b, c, rbu_control); 33 INSERT INTO data_xy VALUES(10, 11, 12, 0); 34 DETACH rbu; 35} 36 37do_multiclient_test tn { 38 39 # Initialize a target (test.db) and rbu (rbu.db) database. 40 # 41 forcedelete rbu.db 42 sql1 $setup_sql 43 44 # Using connection 2, open a read transaction on the target database. 45 # RBU will still be able to generate "test.db-oal", but it will not be 46 # able to rename it to "test.db-wal". 47 # 48 do_test 1.$tn.1 { 49 sql2 { BEGIN; SELECT * FROM xx; } 50 } {1 2 3} 51 do_test 1.$tn.2 { 52 sqlite3rbu rbu test.db rbu.db 53 while 1 { 54 set res [rbu step] 55 if {$res!="SQLITE_OK"} break 56 } 57 set res 58 } {SQLITE_BUSY} 59 60 do_test 1.$tn.3 { sql2 { SELECT * FROM xx; } } {1 2 3} 61 do_test 1.$tn.4 { sql2 { SELECT * FROM xy; } } {} 62 do_test 1.$tn.5 { 63 list [file exists test.db-wal] [file exists test.db-oal] 64 } {0 1} 65 do_test 1.$tn.6 { sql2 COMMIT } {} 66 67 # The rbu object that hit the SQLITE_BUSY error above cannot be reused. 68 # It is stuck in a permanent SQLITE_BUSY state at this point. 69 # 70 do_test 1.$tn.7 { rbu step } {SQLITE_BUSY} 71 do_test 1.$tn.8 { 72 list [catch { rbu close } msg] $msg 73 } {1 SQLITE_BUSY} 74 75 do_test 1.$tn.9.1 { sql2 { BEGIN EXCLUSIVE } } {} 76 do_test 1.$tn.9.2 { 77 sqlite3rbu rbu test.db rbu.db 78 rbu step 79 } {SQLITE_BUSY} 80 do_test 1.$tn.9.3 { 81 list [catch { rbu close } msg] $msg 82 } {1 {SQLITE_BUSY - database is locked}} 83 do_test 1.$tn.9.4 { sql2 COMMIT } {} 84 85 sqlite3rbu rbu test.db rbu.db 86 do_test 1.$tn.10.1 { sql2 { BEGIN EXCLUSIVE } } {} 87 do_test 1.$tn.10.2 { 88 rbu step 89 } {SQLITE_BUSY} 90 do_test 1.$tn.10.3 { 91 list [catch { rbu close } msg] $msg 92 } {1 SQLITE_BUSY} 93 do_test 1.$tn.10.4 { sql2 COMMIT } {} 94 95 # A new rbu object can finish the work though. 96 # 97 do_test 1.$tn.11 { 98 sqlite3rbu rbu test.db rbu.db 99 rbu step 100 } {SQLITE_OK} 101 do_test 1.$tn.12 { 102 list [file exists test.db-wal] [file exists test.db-oal] 103 } {1 0} 104 do_test 1.$tn.13 { 105 while 1 { 106 set res [rbu step] 107 if {$res!="SQLITE_OK"} break 108 } 109 set res 110 } {SQLITE_DONE} 111 112 do_test 1.$tn.14 { 113 rbu close 114 } {SQLITE_DONE} 115} 116 117do_multiclient_test tn { 118 119 # Initialize a target (test.db) and rbu (rbu.db) database. 120 # 121 forcedelete rbu.db 122 sql1 $setup_sql 123 124 do_test 2.$tn.1 { 125 sqlite3rbu rbu test.db rbu.db 126 while {[file exists test.db-wal]==0} { 127 if {[rbu step]!="SQLITE_OK"} {error "problem here...."} 128 } 129 rbu close 130 } {SQLITE_OK} 131 132 133 do_test 2.$tn.2 { sql2 { BEGIN IMMEDIATE } } {} 134 135 do_test 2.$tn.3 { 136 sqlite3rbu rbu test.db rbu.db 137 rbu step 138 } {SQLITE_BUSY} 139 140 do_test 2.$tn.4 { list [catch { rbu close } msg] $msg } {1 SQLITE_BUSY} 141 142 do_test 2.$tn.5 { 143 sql2 { SELECT * FROM xx ; COMMIT } 144 } {1 2 3 4 5 6 7 8 9} 145 146 do_test 2.$tn.6 { 147 sqlite3rbu rbu test.db rbu.db 148 rbu step 149 rbu close 150 } {SQLITE_OK} 151 152 do_test 2.$tn.7 { sql2 { BEGIN EXCLUSIVE } } {} 153 154 do_test 2.$tn.8 { 155 sqlite3rbu rbu test.db rbu.db 156 rbu step 157 } {SQLITE_BUSY} 158 do_test 2.$tn.9 { list [catch { rbu close } msg] $msg } {1 SQLITE_BUSY} 159 do_test 2.$tn.10 { 160 sql2 { SELECT * FROM xx ; COMMIT } 161 } {1 2 3 4 5 6 7 8 9} 162 163 do_test 2.$tn.11 { 164 sqlite3rbu rbu test.db rbu.db 165 while {[rbu step]=="SQLITE_OK"} {} 166 rbu close 167 } {SQLITE_DONE} 168 169} 170 171#------------------------------------------------------------------------- 172# Test that "PRAGMA data_version" works when an RBU client writes the 173# database. 174# 175do_multiclient_test tn { 176 177 # Initialize a target (test.db) and rbu (rbu.db) database. 178 # 179 forcedelete rbu.db 180 sql1 $setup_sql 181 182 # Check the initial database contains table "xx" with a single row. 183 # Also save the current values of "PRAGMA data-version" for [db1] 184 # and [db2]. 185 # 186 do_test 2.$tn.1 { 187 list [sql1 { SELECT count(*) FROM xx }] [sql2 { SELECT count(*) FROM xx }] 188 } {1 1} 189 set V1 [sql1 {PRAGMA data_version}] 190 set V2 [sql2 {PRAGMA data_version}] 191 192 # Check the values of data-version have not magically changed. 193 # 194 do_test 2.$tn.2 { 195 list [sql1 {PRAGMA data_version}] [sql2 {PRAGMA data_version}] 196 } [list $V1 $V2] 197 198 # Start stepping the RBU. From the point of view of [db1] and [db2], the 199 # data-version values remain unchanged until the database contents are 200 # modified. At which point the values are incremented. 201 # 202 sqlite3rbu rbu test.db rbu.db 203 set x 0 204 while {[db one {SELECT count(*) FROM xx}]==1} { 205 do_test 2.$tn.3.[incr x] { 206 list [sql1 {PRAGMA data_version}] [sql2 {PRAGMA data_version}] 207 } [list $V1 $V2] 208 rbu step 209 } 210 do_test 2.$tn.5.1 { expr {$V1 < [sql1 {PRAGMA data_version}]} } 1 211 do_test 2.$tn.5.2 { expr {$V2 < [sql2 {PRAGMA data_version}]} } 1 212 213 # Check the db contents is as expected. 214 # 215 do_test 2.$tn.4 { 216 list [sql1 {SELECT count(*) FROM xx}] [sql2 {SELECT count(*) FROM xx}] 217 } {3 3} 218 219 set V1 [sql1 {PRAGMA data_version}] 220 set V2 [sql2 {PRAGMA data_version}] 221 222 # Finish applying the RBU (i.e. do the incremental checkpoint). Check that 223 # this does not cause the data-version values to change. 224 # 225 while {[rbu step]=="SQLITE_OK"} { } 226 rbu close 227 228 do_test 2.$tn.6 { 229 list [sql1 {PRAGMA data_version}] [sql2 {PRAGMA data_version}] 230 } [list $V1 $V2] 231 232} 233 234finish_test 235