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