1# 2018 March 14 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 sessionrebase 22 23set ::lConflict [list] 24proc xConflict {args} { 25 set res [lindex $::lConflict 0] 26 set ::lConflict [lrange $::lConflict 1 end] 27 return $res 28} 29 30#------------------------------------------------------------------------- 31# The following test cases - 1.* - test that the rebase blobs output by 32# sqlite3_changeset_apply_v2 look correct in some simple cases. The blob 33# is itself a changeset, containing records determined as follows: 34# 35# * For each conflict resolved with REPLACE, the rebase blob contains 36# a DELETE record. All fields other than the PK fields are undefined. 37# 38# * For each conflict resolved with OMIT, the rebase blob contains an 39# INSERT record. For an INSERT or UPDATE operation, the indirect flag 40# is clear and all updated fields are defined. For a DELETE operation, 41# the indirect flag is set and all non-PK fields left undefined. 42# 43proc do_apply_v2_test {tn sql modsql conflict_handler res} { 44 45 execsql BEGIN 46 sqlite3session S db main 47 S attach * 48 execsql $sql 49 set changeset [S changeset] 50 S delete 51 execsql ROLLBACK 52 53 execsql BEGIN 54 execsql $modsql 55 set ::lConflict $conflict_handler 56 set blob [sqlite3changeset_apply_v2 db $changeset xConflict] 57 execsql ROLLBACK 58 59 uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]] 60} 61 62 63set ::lConflict [list] 64proc xConflict {args} { 65 set res [lindex $::lConflict 0] 66 set ::lConflict [lrange $::lConflict 1 end] 67 return $res 68} 69 70# Take a copy of database test.db in file test.db2. Execute $sql1 71# against test.db and $sql2 against test.db2. Capture a changeset 72# for each. Then send the test.db2 changeset to test.db and apply 73# it with the conflict handlers in $conflict_handler. Patch the 74# test.db changeset and then execute it against test.db2. Test that 75# the two databases come out the same. 76# 77proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} { 78 79 for {set i 1} {$i <= 2} {incr i} { 80 forcedelete test.db2 test.db2-journal test.db2-wal 81 forcecopy test.db test.db2 82 sqlite3 db2 test.db2 83 84 db eval BEGIN 85 86 sqlite3session S1 db main 87 S1 attach * 88 execsql $sql1 db 89 set c1 [S1 changeset] 90 S1 delete 91 92 if {$i==1} { 93 sqlite3session S2 db2 main 94 S2 attach * 95 execsql $sql2 db2 96 set c2 [S2 changeset] 97 S2 delete 98 } else { 99 set c2 [list] 100 foreach sql [split $sql2 ";"] { 101 if {[string is space $sql]} continue 102 sqlite3session S2 db2 main 103 S2 attach * 104 execsql $sql db2 105 lappend c2 [S2 changeset] 106 S2 delete 107 } 108 } 109 110 set ::lConflict $conflict_handler 111 set rebase [list] 112 if {$i==1} { 113 lappend rebase [sqlite3changeset_apply_v2 db $c2 xConflict] 114 } else { 115 foreach c $c2 { 116 lappend rebase [sqlite3changeset_apply_v2 db $c xConflict] 117 } 118 } 119 #if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint } 120 #puts [changeset_to_list [lindex $rebase 0]] ; breakpoint 121 #puts [llength $rebase] 122 123 sqlite3rebaser_create R 124 foreach r $rebase { 125 R configure $r 126 } 127 set c1r [R rebase $c1] 128 R delete 129 #if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] } 130 131 sqlite3changeset_apply_v2 db2 $c1r xConflictAbort 132 133 uplevel [list do_test $tn.$i.1 [list compare_db db db2] {}] 134 db2 close 135 136 if {$testsql!=""} { 137 uplevel [list do_execsql_test $tn.$i.2 $testsql $testres] 138 } 139 140 db eval ROLLBACK 141 } 142} 143 144do_execsql_test 1.0 { 145 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 146 INSERT INTO t1 VALUES(1, 'value A'); 147} 148 149do_apply_v2_test 1.1.1 { 150 UPDATE t1 SET b = 'value B' WHERE a=1; 151} { 152 UPDATE t1 SET b = 'value C' WHERE a=1; 153} { 154 OMIT 155} { 156 {INSERT t1 0 X. {} {i 1 t {value B}}} 157} 158 159do_apply_v2_test 1.1.2 { 160 UPDATE t1 SET b = 'value B' WHERE a=1; 161} { 162 UPDATE t1 SET b = 'value C' WHERE a=1; 163} { 164 REPLACE 165} { 166 {INSERT t1 1 X. {} {i 1 t {value B}}} 167} 168 169do_apply_v2_test 1.2.1 { 170 INSERT INTO t1 VALUES(2, 'first'); 171} { 172 INSERT INTO t1 VALUES(2, 'second'); 173} { 174 OMIT 175} { 176 {INSERT t1 0 X. {} {i 2 t first}} 177} 178do_apply_v2_test 1.2.2 { 179 INSERT INTO t1 VALUES(2, 'first'); 180} { 181 INSERT INTO t1 VALUES(2, 'second'); 182} { 183 REPLACE 184} { 185 {INSERT t1 1 X. {} {i 2 t first}} 186} 187 188do_apply_v2_test 1.3.1 { 189 DELETE FROM t1 WHERE a=1; 190} { 191 UPDATE t1 SET b='value D' WHERE a=1; 192} { 193 OMIT 194} { 195 {DELETE t1 0 X. {i 1 t {value A}} {}} 196} 197do_apply_v2_test 1.3.2 { 198 DELETE FROM t1 WHERE a=1; 199} { 200 UPDATE t1 SET b='value D' WHERE a=1; 201} { 202 REPLACE 203} { 204 {DELETE t1 1 X. {i 1 t {value A}} {}} 205} 206 207#------------------------------------------------------------------------- 208# Test cases 2.* - simple tests of rebasing actual changesets. 209# 210# 2.1.1 - 1u2u1r 211# 2.1.2 - 1u2u2r 212# 2.1.3 - 1d2d 213# 2.1.4 - 1d2u1r 214# 2.1.5 - 1d2u2r !! 215# 2.1.6 - 1u2d1r 216# 2.1.7 - 1u2d2r 217# 218# 2.1.8 - 1i2i2r 219# 2.1.9 - 1i2i1r 220# 221 222proc xConflictAbort {args} { 223 return "ABORT" 224} 225 226reset_db 227do_execsql_test 2.1.0 { 228 CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT); 229 INSERT INTO t1 VALUES(1, 'one'); 230 INSERT INTO t1 VALUES(2, 'two'); 231 INSERT INTO t1 VALUES(3, 'three'); 232} 233do_rebase_test 2.1.1 { 234 UPDATE t1 SET b = 'two.1' WHERE a=2 235} { 236 UPDATE t1 SET b = 'two.2' WHERE a=2; 237} { 238 OMIT 239} { SELECT * FROM t1 } {1 one 2 two.1 3 three} 240 241do_rebase_test 2.1.2 { 242 UPDATE t1 SET b = 'two.1' WHERE a=2 243} { 244 UPDATE t1 SET b = 'two.2' WHERE a=2; 245} { 246 REPLACE 247} { SELECT * FROM t1 } {1 one 2 two.2 3 three} 248 249do_rebase_test 2.1.3 { 250 DELETE FROM t1 WHERE a=3 251} { 252 DELETE FROM t1 WHERE a=3; 253} { 254 OMIT 255} { SELECT * FROM t1 } {1 one 2 two} 256 257do_rebase_test 2.1.4 { 258 DELETE FROM t1 WHERE a=1 259} { 260 UPDATE t1 SET b='one.2' WHERE a=1 261} { 262 OMIT 263} { SELECT * FROM t1 } {2 two 3 three} 264 265#do_rebase_test 2.1.5 { 266# DELETE FROM t1 WHERE a=1; 267#} { 268# UPDATE t1 SET b='one.2' WHERE a=1 269#} { 270# REPLACE 271#} { SELECT * FROM t1 } {2 two 3 three} 272 273do_rebase_test 2.1.6 { 274 UPDATE t1 SET b='three.1' WHERE a=3 275} { 276 DELETE FROM t1 WHERE a=3; 277} { 278 OMIT 279} { SELECT * FROM t1 } {1 one 2 two 3 three.1} 280 281do_rebase_test 2.1.7 { 282 UPDATE t1 SET b='three.1' WHERE a=3 283} { 284 DELETE FROM t1 WHERE a=3; 285} { 286 REPLACE 287} { SELECT * FROM t1 } {1 one 2 two} 288 289do_rebase_test 2.1.8 { 290 INSERT INTO t1 VALUES(4, 'four.1') 291} { 292 INSERT INTO t1 VALUES(4, 'four.2'); 293} { 294 REPLACE 295} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2} 296 297do_rebase_test 2.1.9 { 298 INSERT INTO t1 VALUES(4, 'four.1') 299} { 300 INSERT INTO t1 VALUES(4, 'four.2'); 301} { 302 OMIT 303} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.1} 304 305do_execsql_test 2.2.0 { 306 CREATE TABLE t2(x, y, z PRIMARY KEY); 307 INSERT INTO t2 VALUES('i', 'a', 'A'); 308 INSERT INTO t2 VALUES('ii', 'b', 'B'); 309 INSERT INTO t2 VALUES('iii', 'c', 'C'); 310 311 CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c); 312 INSERT INTO t3 VALUES(-1, 'z', 'Z'); 313 INSERT INTO t3 VALUES(-2, 'y', 'Y'); 314} 315 316do_rebase_test 2.2.1 { 317 UPDATE t2 SET x=1 WHERE z='A' 318} { 319 UPDATE t2 SET y='one' WHERE z='A'; 320} { 321} { SELECT * FROM t2 WHERE z='A' } { 1 one A } 322 323do_rebase_test 2.2.2 { 324 UPDATE t2 SET x=1, y='one' WHERE z='B' 325} { 326 UPDATE t2 SET y='two' WHERE z='B'; 327} { 328 REPLACE 329} { SELECT * FROM t2 WHERE z='B' } { 1 two B } 330 331do_rebase_test 2.2.3 { 332 UPDATE t2 SET x=1, y='one' WHERE z='B' 333} { 334 UPDATE t2 SET y='two' WHERE z='B'; 335} { 336 OMIT 337} { SELECT * FROM t2 WHERE z='B' } { 1 one B } 338 339#------------------------------------------------------------------------- 340reset_db 341do_execsql_test 3.0 { 342 CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c)); 343 CREATE TABLE abcdefghijkl(x PRIMARY KEY, y, z); 344 345 INSERT INTO t3 VALUES(1, 2, 3); 346 INSERT INTO t3 VALUES(4, 2, 5); 347 INSERT INTO t3 VALUES(7, 2, 9); 348 349 INSERT INTO abcdefghijkl VALUES('a', 'b', 'c'); 350 INSERT INTO abcdefghijkl VALUES('d', 'e', 'f'); 351 INSERT INTO abcdefghijkl VALUES('g', 'h', 'i'); 352} 353 354foreach {tn p} { 355 1 OMIT 2 REPLACE 356} { 357 do_rebase_test 3.1.$tn { 358 INSERT INTO t3 VALUES(1, 1, 1); 359 UPDATE abcdefghijkl SET y=2; 360 } { 361 INSERT INTO t3 VALUES(4, 1, 1); 362 DELETE FROM abcdefghijkl; 363 } [list $p $p $p $p $p $p $p $p] 364 365 do_rebase_test 3.2.$tn { 366 INSERT INTO abcdefghijkl SELECT * FROM t3; 367 UPDATE t3 SET b=b+1; 368 } { 369 INSERT INTO t3 VALUES(3, 3, 3); 370 INSERT INTO abcdefghijkl SELECT * FROM t3; 371 } [list $p $p $p $p $p $p $p $p] 372 373 do_rebase_test 3.3.$tn { 374 INSERT INTO abcdefghijkl VALUES(22, 23, 24); 375 } { 376 INSERT INTO abcdefghijkl VALUES(22, 25, 26); 377 UPDATE abcdefghijkl SET y=400 WHERE x=22; 378 } [list $p $p $p $p $p $p $p $p] 379 380 do_rebase_test 3.4.$tn { 381 INSERT INTO abcdefghijkl VALUES(22, 23, 24); 382 } { 383 INSERT INTO abcdefghijkl VALUES(22, 25, 26); 384 UPDATE abcdefghijkl SET y=400 WHERE x=22; 385 } [list REPLACE $p] 386} 387 388#------------------------------------------------------------------------- 389# Check that apply_v2() does not create a rebase buffer for a patchset. 390# And that it is not possible to rebase a patchset. 391# 392do_execsql_test 4.0 { 393 CREATE TABLE t5(o PRIMARY KEY, p, q); 394 INSERT INTO t5 VALUES(1, 2, 3); 395 INSERT INTO t5 VALUES(4, 5, 6); 396} 397foreach {tn cmd rebasable} { 398 1 patchset 0 399 2 changeset 1 400} { 401 proc xConflict {args} { return "OMIT" } 402 do_test 4.1.$tn { 403 execsql { 404 BEGIN; 405 DELETE FROM t5 WHERE o=4; 406 } 407 408 sqlite3session S db main 409 S attach * 410 execsql { 411 INSERT INTO t5 VALUES(4, 'five', 'six'); 412 } 413 set P [S $cmd] 414 S delete 415 416 execsql ROLLBACK; 417 418 set ::rebase [sqlite3changeset_apply_v2 db $P xConflict] 419 expr [llength $::rebase]>0 420 } $rebasable 421} 422 423foreach {tn cmd rebasable} { 424 1 patchset 0 425 2 changeset 1 426} { 427 do_test 4.2.$tn { 428 sqlite3session S db main 429 S attach * 430 execsql { 431 INSERT INTO t5 VALUES(5+$tn, 'five', 'six'); 432 } 433 set P [S $cmd] 434 S delete 435 436 sqlite3rebaser_create R 437 R configure $::rebase 438 expr [catch {R rebase $P}]==0 439 } $rebasable 440 441 catch { R delete } 442} 443finish_test 444 445