1# 2011 March 07 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. 12# 13 14if {![info exists testdir]} { 15 set testdir [file join [file dirname [info script]] .. .. test] 16} 17source [file join [file dirname [info script]] session_common.tcl] 18source $testdir/tester.tcl 19 20set testprefix session1 21 22proc do_changeset_test {tn session res} { 23 set r [list] 24 foreach x $res {lappend r $x} 25 uplevel do_test $tn [list [subst -nocommands { 26 set x [list] 27 sqlite3session_foreach c [$session changeset] { lappend x [set c] } 28 set x 29 }]] [list $r] 30} 31 32proc do_changeset_invert_test {tn session res} { 33 set r [list] 34 foreach x $res {lappend r $x} 35 uplevel do_test $tn [list [subst -nocommands { 36 set x [list] 37 set changeset [sqlite3changeset_invert [$session changeset]] 38 sqlite3session_foreach c [set changeset] { lappend x [set c] } 39 set x 40 }]] [list $r] 41} 42 43do_execsql_test 1.0 { 44 CREATE TABLE t1(x PRIMARY KEY, y); 45 INSERT INTO t1 VALUES('abc', 'def'); 46} 47 48#------------------------------------------------------------------------- 49# Test creating, attaching tables to and deleting session objects. 50# 51do_test 1.1 { sqlite3session S db main } {S} 52do_test 1.2 { S delete } {} 53do_test 1.3 { sqlite3session S db main } {S} 54do_test 1.4 { S attach t1 } {} 55do_test 1.5 { S delete } {} 56do_test 1.6 { sqlite3session S db main } {S} 57do_test 1.7 { S attach t1 ; S attach t2 ; S attach t3 } {} 58do_test 1.8 { S attach t1 ; S attach t2 ; S attach t3 } {} 59do_test 1.9 { S delete } {} 60do_test 1.10 { 61 sqlite3session S db main 62 S attach t1 63 execsql { INSERT INTO t1 VALUES('ghi', 'jkl') } 64} {} 65do_test 1.11 { S delete } {} 66do_test 1.12 { 67 sqlite3session S db main 68 S attach t1 69 execsql { INSERT INTO t1 VALUES('mno', 'pqr') } 70 execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 } 71 execsql { DELETE FROM t1 WHERE rowid = 2 } 72} {} 73do_test 1.13 { 74 S changeset 75 S delete 76} {} 77 78#------------------------------------------------------------------------- 79# Simple changeset tests. Also test the sqlite3changeset_invert() 80# function. 81# 82do_test 2.1.1 { 83 execsql { DELETE FROM t1 } 84 sqlite3session S db main 85 S attach t1 86 execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') } 87 execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') } 88 execsql { INSERT INTO t1 VALUES(3, 'Thonburi') } 89} {} 90do_changeset_test 2.1.2 S { 91 {INSERT t1 {} {i 1 t Sukhothai}} 92 {INSERT t1 {} {i 2 t Ayutthaya}} 93 {INSERT t1 {} {i 3 t Thonburi}} 94} 95do_changeset_invert_test 2.1.3 S { 96 {DELETE t1 {i 1 t Sukhothai} {}} 97 {DELETE t1 {i 2 t Ayutthaya} {}} 98 {DELETE t1 {i 3 t Thonburi} {}} 99} 100do_test 2.1.4 { S delete } {} 101 102do_test 2.2.1 { 103 sqlite3session S db main 104 S attach t1 105 execsql { DELETE FROM t1 WHERE 1 } 106} {} 107do_changeset_test 2.2.2 S { 108 {DELETE t1 {i 1 t Sukhothai} {}} 109 {DELETE t1 {i 2 t Ayutthaya} {}} 110 {DELETE t1 {i 3 t Thonburi} {}} 111} 112do_changeset_invert_test 2.2.3 S { 113 {INSERT t1 {} {i 1 t Sukhothai}} 114 {INSERT t1 {} {i 2 t Ayutthaya}} 115 {INSERT t1 {} {i 3 t Thonburi}} 116} 117do_test 2.2.4 { S delete } {} 118 119do_test 2.3.1 { 120 execsql { DELETE FROM t1 } 121 sqlite3session S db main 122 execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') } 123 execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') } 124 execsql { INSERT INTO t1 VALUES(3, 'Thonburi') } 125 S attach t1 126 execsql { 127 UPDATE t1 SET x = 10 WHERE x = 1; 128 UPDATE t1 SET y = 'Surin' WHERE x = 2; 129 UPDATE t1 SET x = 20, y = 'Thapae' WHERE x = 3; 130 } 131} {} 132 133do_changeset_test 2.3.2 S { 134 {INSERT t1 {} {i 10 t Sukhothai}} 135 {DELETE t1 {i 1 t Sukhothai} {}} 136 {UPDATE t1 {i 2 t Ayutthaya} {{} {} t Surin}} 137 {DELETE t1 {i 3 t Thonburi} {}} 138 {INSERT t1 {} {i 20 t Thapae}} 139} 140 141do_changeset_invert_test 2.3.3 S { 142 {DELETE t1 {i 10 t Sukhothai} {}} 143 {INSERT t1 {} {i 1 t Sukhothai}} 144 {UPDATE t1 {{} {} t Surin} {i 2 t Ayutthaya}} 145 {INSERT t1 {} {i 3 t Thonburi}} 146 {DELETE t1 {i 20 t Thapae} {}} 147} 148do_test 2.3.4 { S delete } {} 149 150do_test 2.4.1 { 151 sqlite3session S db main 152 S attach t1 153 execsql { INSERT INTO t1 VALUES(100, 'Bangkok') } 154 execsql { DELETE FROM t1 WHERE x = 100 } 155} {} 156do_changeset_test 2.4.2 S {} 157do_changeset_invert_test 2.4.3 S {} 158do_test 2.4.4 { S delete } {} 159 160#------------------------------------------------------------------------- 161# Test the application of simple changesets. These tests also test that 162# the conflict callback is invoked correctly. For these tests, the 163# conflict callback always returns OMIT. 164# 165db close 166forcedelete test.db test.db2 167sqlite3 db test.db 168sqlite3 db2 test.db2 169 170proc xConflict {args} { 171 lappend ::xConflict $args 172 return "" 173} 174 175proc bgerror {args} { set ::background_error $args } 176 177proc do_conflict_test {tn args} { 178 set O(-tables) [list] 179 set O(-sql) [list] 180 set O(-conflicts) [list] 181 182 array set V $args 183 foreach key [array names V] { 184 if {![info exists O($key)]} {error "no such option: $key"} 185 } 186 array set O $args 187 188 sqlite3session S db main 189 foreach t $O(-tables) { S attach $t } 190 execsql $O(-sql) 191 set ::xConflict [list] 192 sqlite3changeset_apply db2 [S changeset] xConflict 193 194 set conflicts [list] 195 foreach c $O(-conflicts) { 196 lappend conflicts $c 197 } 198 199 after 1 {set go 1} 200 vwait go 201 202 uplevel do_test $tn [list { set ::xConflict }] [list $conflicts] 203 S delete 204} 205 206proc do_db2_test {testname sql {result {}}} { 207 uplevel do_test $testname [list "execsql {$sql} db2"] [list [list {*}$result]] 208} 209 210# Test INSERT changesets. 211# 212do_test 3.1.0 { 213 execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) } db2 214 execsql { 215 CREATE TABLE t1(a PRIMARY KEY, b); 216 INSERT INTO t1 VALUES(1, 'one'); 217 INSERT INTO t1 VALUES(2, 'two'); 218 } db 219} {} 220do_db2_test 3.1.1 "INSERT INTO t1 VALUES(6, 'VI')" 221do_conflict_test 3.1.2 -tables t1 -sql { 222 INSERT INTO t1 VALUES(3, 'three'); 223 INSERT INTO t1 VALUES(4, 'four'); 224 INSERT INTO t1 VALUES(5, 'five'); 225 INSERT INTO t1 VALUES(6, 'six'); 226 INSERT INTO t1 VALUES(7, 'seven'); 227 INSERT INTO t1 VALUES(8, NULL); 228} -conflicts { 229 {INSERT t1 CONSTRAINT {i 8 n {}}} 230 {INSERT t1 CONFLICT {i 6 t six} {i 6 t VI}} 231} 232 233do_db2_test 3.1.3 "SELECT * FROM t1" { 234 6 VI 3 three 4 four 5 five 7 seven 235} 236do_execsql_test 3.1.4 "SELECT * FROM t1" { 237 1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 {} 238} 239 240# Test DELETE changesets. 241# 242do_execsql_test 3.2.1 { 243 PRAGMA foreign_keys = on; 244 CREATE TABLE t2(a PRIMARY KEY, b); 245 CREATE TABLE t3(c, d REFERENCES t2); 246 INSERT INTO t2 VALUES(1, 'one'); 247 INSERT INTO t2 VALUES(2, 'two'); 248 INSERT INTO t2 VALUES(3, 'three'); 249 INSERT INTO t2 VALUES(4, 'four'); 250} 251do_db2_test 3.2.2 { 252 PRAGMA foreign_keys = on; 253 CREATE TABLE t2(a PRIMARY KEY, b); 254 CREATE TABLE t3(c, d REFERENCES t2); 255 INSERT INTO t2 VALUES(1, 'one'); 256 INSERT INTO t2 VALUES(2, 'two'); 257 INSERT INTO t2 VALUES(4, 'five'); 258 INSERT INTO t3 VALUES('i', 1); 259} 260do_conflict_test 3.2.3 -tables t2 -sql { 261 DELETE FROM t2 WHERE a = 1; 262 DELETE FROM t2 WHERE a = 2; 263 DELETE FROM t2 WHERE a = 3; 264 DELETE FROM t2 WHERE a = 4; 265} -conflicts { 266 {DELETE t2 CONSTRAINT {i 1 t one}} 267 {DELETE t2 NOTFOUND {i 3 t three}} 268 {DELETE t2 DATA {i 4 t four} {i 4 t five}} 269} 270do_execsql_test 3.2.4 "SELECT * FROM t2" {} 271do_db2_test 3.2.5 "SELECT * FROM t2" {1 one 4 five} 272 273# Test UPDATE changesets. 274# 275do_execsql_test 3.3.1 { 276 CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c)); 277 INSERT INTO t4 VALUES(1, 2, 3); 278 INSERT INTO t4 VALUES(4, 5, 6); 279 INSERT INTO t4 VALUES(7, 8, 9); 280 INSERT INTO t4 VALUES(10, 11, 12); 281} 282do_db2_test 3.3.2 { 283 CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c)); 284 INSERT INTO t4 VALUES(0, 2, 3); 285 INSERT INTO t4 VALUES(4, 5, 7); 286 INSERT INTO t4 VALUES(7, 8, 9); 287 INSERT INTO t4 VALUES(10, 11, 12); 288} 289do_conflict_test 3.3.3 -tables t4 -sql { 290 UPDATE t4 SET a = -1 WHERE b = 2; 291 UPDATE t4 SET a = -1 WHERE b = 5; 292 UPDATE t4 SET a = NULL WHERE c = 9; 293 UPDATE t4 SET a = 'x' WHERE b = 11; 294} -conflicts { 295 {UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}} 296 {UPDATE t4 DATA {i 1 i 2 i 3} {i -1 {} {} {} {}} {i 0 i 2 i 3}} 297 {UPDATE t4 NOTFOUND {i 4 i 5 i 6} {i -1 {} {} {} {}}} 298} 299do_db2_test 3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12} 300do_execsql_test 3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12} 301 302#------------------------------------------------------------------------- 303# This next block of tests verifies that values returned by the conflict 304# handler are intepreted correctly. 305# 306 307proc test_reset {} { 308 db close 309 db2 close 310 forcedelete test.db test.db2 311 sqlite3 db test.db 312 sqlite3 db2 test.db2 313} 314 315proc xConflict {args} { 316 lappend ::xConflict $args 317 return $::conflict_return 318} 319 320foreach {tn conflict_return after} { 321 1 OMIT {1 2 value1 4 5 7 10 x x} 322 2 REPLACE {1 2 value1 4 5 value2 10 8 9} 323} { 324 test_reset 325 326 do_test 4.$tn.1 { 327 foreach db {db db2} { 328 execsql { 329 CREATE TABLE t1(a, b, c, PRIMARY KEY(a)); 330 INSERT INTO t1 VALUES(1, 2, 3); 331 INSERT INTO t1 VALUES(4, 5, 6); 332 INSERT INTO t1 VALUES(7, 8, 9); 333 } $db 334 } 335 execsql { 336 REPLACE INTO t1 VALUES(4, 5, 7); 337 REPLACE INTO t1 VALUES(10, 'x', 'x'); 338 } db2 339 } {} 340 341 do_conflict_test 4.$tn.2 -tables t1 -sql { 342 UPDATE t1 SET c = 'value1' WHERE a = 1; -- no conflict 343 UPDATE t1 SET c = 'value2' WHERE a = 4; -- DATA conflict 344 UPDATE t1 SET a = 10 WHERE a = 7; -- CONFLICT conflict 345 } -conflicts { 346 {INSERT t1 CONFLICT {i 10 i 8 i 9} {i 10 t x t x}} 347 {UPDATE t1 DATA {i 4 {} {} i 6} {{} {} {} {} t value2} {i 4 i 5 i 7}} 348 } 349 350 do_db2_test 4.$tn.3 "SELECT * FROM t1 ORDER BY a" $after 351} 352 353foreach {tn conflict_return} { 354 1 OMIT 355 2 REPLACE 356} { 357 test_reset 358 359 do_test 5.$tn.1 { 360 # Create an identical schema in both databases. 361 set schema { 362 CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y)); 363 } 364 execsql $schema db 365 execsql $schema db2 366 367 # Add some rows to [db2]. These rows will cause conflicts later 368 # on when the changeset from [db] is applied to it. 369 execsql { 370 INSERT INTO "'foolish name'" VALUES('one', 'one', 'ii'); 371 INSERT INTO "'foolish name'" VALUES('one', 'two', 'i'); 372 INSERT INTO "'foolish name'" VALUES('two', 'two', 'ii'); 373 } db2 374 375 } {} 376 377 do_conflict_test 5.$tn.2 -tables {{'foolish name'}} -sql { 378 INSERT INTO "'foolish name'" VALUES('one', 'two', 2); 379 } -conflicts { 380 {INSERT {'foolish name'} CONFLICT {t one t two i 2} {t one t two t i}} 381 } 382 383 set res(REPLACE) {one one ii one two 2 two two ii} 384 set res(OMIT) {one one ii one two i two two ii} 385 do_db2_test 5.$tn.3 { 386 SELECT * FROM "'foolish name'" ORDER BY x, y 387 } $res($conflict_return) 388 389 390 do_test 5.$tn.1 { 391 set schema { 392 CREATE TABLE d1("z""z" PRIMARY KEY, y); 393 INSERT INTO d1 VALUES(1, 'one'); 394 INSERT INTO d1 VALUES(2, 'two'); 395 } 396 execsql $schema db 397 execsql $schema db2 398 399 execsql { 400 UPDATE d1 SET y = 'TWO' WHERE "z""z" = 2; 401 } db2 402 403 } {} 404 405 do_conflict_test 5.$tn.2 -tables d1 -sql { 406 DELETE FROM d1 WHERE "z""z" = 2; 407 } -conflicts { 408 {DELETE d1 DATA {i 2 t two} {i 2 t TWO}} 409 } 410 411 set res(REPLACE) {1 one} 412 set res(OMIT) {1 one 2 TWO} 413 do_db2_test 5.$tn.3 "SELECT * FROM d1" $res($conflict_return) 414} 415 416#------------------------------------------------------------------------- 417# Test that two tables can be monitored by a single session object. 418# 419test_reset 420set schema { 421 CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b); 422 CREATE TABLE t2(a, b PRIMARY KEY); 423} 424do_test 6.0 { 425 execsql $schema db 426 execsql $schema db2 427 execsql { 428 INSERT INTO t1 VALUES('a', 'b'); 429 INSERT INTO t2 VALUES('a', 'b'); 430 } db2 431} {} 432 433set conflict_return "" 434do_conflict_test 6.1 -tables {t1 t2} -sql { 435 INSERT INTO t1 VALUES('1', '2'); 436 INSERT INTO t1 VALUES('A', 'B'); 437 INSERT INTO t2 VALUES('A', 'B'); 438} -conflicts { 439 {INSERT t1 CONFLICT {t A t B} {t a t b}} 440} 441 442do_db2_test 6.2 "SELECT * FROM t1" {a b 1 2} 443do_db2_test 6.3 "SELECT * FROM t2" {a b A B} 444 445catch { db2 close } 446finish_test 447 448