17cf7df7dSdan# 2011 Mar 16 27cf7df7dSdan# 37cf7df7dSdan# The author disclaims copyright to this source code. In place of 47cf7df7dSdan# a legal notice, here is a blessing: 57cf7df7dSdan# 67cf7df7dSdan# May you do good and not evil. 77cf7df7dSdan# May you find forgiveness for yourself and forgive others. 87cf7df7dSdan# May you share freely, never taking more than you give. 97cf7df7dSdan# 107cf7df7dSdan#*********************************************************************** 117cf7df7dSdan# 127cf7df7dSdan# The focus of this file is testing the session module. 137cf7df7dSdan# 147cf7df7dSdan 157cf7df7dSdanif {![info exists testdir]} { 167cf7df7dSdan set testdir [file join [file dirname [info script]] .. .. test] 177cf7df7dSdan} 187cf7df7dSdansource [file join [file dirname [info script]] session_common.tcl] 197cf7df7dSdansource $testdir/tester.tcl 209b1c62d4Sdrhifcapable !session {finish_test; return} 217cf7df7dSdan 227cf7df7dSdanset testprefix session2 237cf7df7dSdan 247cf7df7dSdanproc test_reset {} { 257cf7df7dSdan catch { db close } 267cf7df7dSdan catch { db2 close } 277cf7df7dSdan forcedelete test.db test.db2 287cf7df7dSdan sqlite3 db test.db 297cf7df7dSdan sqlite3 db2 test.db2 307cf7df7dSdan} 317cf7df7dSdan 327cf7df7dSdan########################################################################## 337cf7df7dSdan# End of proc definitions. Start of tests. 347cf7df7dSdan########################################################################## 357cf7df7dSdan 367cf7df7dSdantest_reset 377cf7df7dSdando_execsql_test 1.0 { 38*e0d2096aSdan CREATE TABLE t1(a INT PRIMARY KEY, b); 397cf7df7dSdan INSERT INTO t1 VALUES('i', 'one'); 407cf7df7dSdan} 417cf7df7dSdando_iterator_test 1.1 t1 { 427cf7df7dSdan DELETE FROM t1 WHERE a = 'i'; 437cf7df7dSdan INSERT INTO t1 VALUES('ii', 'two'); 447cf7df7dSdan} { 45244593c8Sdan {DELETE t1 0 X. {t i t one} {}} 46244593c8Sdan {INSERT t1 0 X. {} {t ii t two}} 477cf7df7dSdan} 48e5754eecSdan 4912ca0b56Sdando_iterator_test 1.2 t1 { 504e895da1Sdan INSERT INTO t1 VALUES(1.5, 99.9) 514e895da1Sdan} { 52244593c8Sdan {INSERT t1 0 X. {} {f 1.5 f 99.9}} 534e895da1Sdan} 547cf7df7dSdan 55e5754eecSdando_iterator_test 1.3 t1 { 56e5754eecSdan UPDATE t1 SET b = 100.1 WHERE a = 1.5; 57e5754eecSdan UPDATE t1 SET b = 99.9 WHERE a = 1.5; 58e5754eecSdan} { } 59e5754eecSdan 60e5754eecSdando_iterator_test 1.4 t1 { 61e5754eecSdan UPDATE t1 SET b = 100.1 WHERE a = 1.5; 62e5754eecSdan} { 63e5754eecSdan {UPDATE t1 0 X. {f 1.5 f 99.9} {{} {} f 100.1}} 64e5754eecSdan} 65e5754eecSdan 6612ca0b56Sdan 6727453faeSdan# Execute each of the following blocks of SQL on database [db1]. Collect 6827453faeSdan# changes using a session object. Apply the resulting changeset to 6927453faeSdan# database [db2]. Then check that the contents of the two databases are 7027453faeSdan# identical. 7127453faeSdan# 7227453faeSdan 7327453faeSdanset set_of_tests { 7427453faeSdan 1 { INSERT INTO %T1% VALUES(1, 2) } 7527453faeSdan 7627453faeSdan 2 { 7727453faeSdan INSERT INTO %T2% VALUES(1, NULL); 7827453faeSdan INSERT INTO %T2% VALUES(2, NULL); 7927453faeSdan INSERT INTO %T2% VALUES(3, NULL); 8027453faeSdan DELETE FROM %T2% WHERE a = 2; 8127453faeSdan INSERT INTO %T2% VALUES(4, NULL); 8227453faeSdan UPDATE %T2% SET b=0 WHERE b=1; 8327453faeSdan } 8427453faeSdan 8527453faeSdan 3 { INSERT INTO %T3% SELECT *, NULL FROM %T2% } 8627453faeSdan 8727453faeSdan 4 { 8827453faeSdan INSERT INTO %T3% SELECT a||a, b||b, NULL FROM %T3%; 8927453faeSdan DELETE FROM %T3% WHERE rowid%2; 9027453faeSdan } 9127453faeSdan 9227453faeSdan 5 { UPDATE %T3% SET c = a||b } 9327453faeSdan 9427453faeSdan 6 { UPDATE %T1% SET a = 32 } 9527453faeSdan 9627453faeSdan 7 { 9727453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 9827453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 9927453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10027453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10127453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10227453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10327453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10427453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10527453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10627453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10727453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 10827453faeSdan DELETE FROM %T1% WHERE (rowid%3)==0; 10927453faeSdan } 11027453faeSdan 11127453faeSdan 8 { 11227453faeSdan BEGIN; 11327453faeSdan INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%; 11427453faeSdan ROLLBACK; 11527453faeSdan } 11627453faeSdan 9 { 11727453faeSdan BEGIN; 11827453faeSdan UPDATE %T1% SET b = 'xxx'; 11927453faeSdan ROLLBACK; 12027453faeSdan } 12127453faeSdan 10 { 12227453faeSdan BEGIN; 12327453faeSdan DELETE FROM %T1% WHERE 1; 12427453faeSdan ROLLBACK; 12527453faeSdan } 12627453faeSdan 11 { 12727453faeSdan INSERT INTO %T1% VALUES(randomblob(21000), randomblob(0)); 12827453faeSdan INSERT INTO %T1% VALUES(1.5, 1.5); 12927453faeSdan INSERT INTO %T1% VALUES(4.56, -99.999999999999999999999); 13027453faeSdan } 13127453faeSdan 12 { 13227453faeSdan INSERT INTO %T2% VALUES(NULL, NULL); 13327453faeSdan } 13412ca0b56Sdan 13512ca0b56Sdan 13 { 13612ca0b56Sdan DELETE FROM %T1% WHERE 1; 13712ca0b56Sdan 13812ca0b56Sdan -- Insert many rows with real primary keys. Enough to force the session 13912ca0b56Sdan -- objects hash table to resize. 14012ca0b56Sdan INSERT INTO %T1% VALUES(0.1, 0.1); 14112ca0b56Sdan INSERT INTO %T1% SELECT a+0.1, b+0.1 FROM %T1%; 14212ca0b56Sdan INSERT INTO %T1% SELECT a+0.2, b+0.2 FROM %T1%; 14312ca0b56Sdan INSERT INTO %T1% SELECT a+0.4, b+0.4 FROM %T1%; 14412ca0b56Sdan INSERT INTO %T1% SELECT a+0.8, b+0.8 FROM %T1%; 14512ca0b56Sdan INSERT INTO %T1% SELECT a+1.6, b+1.6 FROM %T1%; 14612ca0b56Sdan INSERT INTO %T1% SELECT a+3.2, b+3.2 FROM %T1%; 14712ca0b56Sdan INSERT INTO %T1% SELECT a+6.4, b+6.4 FROM %T1%; 14812ca0b56Sdan INSERT INTO %T1% SELECT a+12.8, b+12.8 FROM %T1%; 14912ca0b56Sdan INSERT INTO %T1% SELECT a+25.6, b+25.6 FROM %T1%; 15012ca0b56Sdan INSERT INTO %T1% SELECT a+51.2, b+51.2 FROM %T1%; 15112ca0b56Sdan INSERT INTO %T1% SELECT a+102.4, b+102.4 FROM %T1%; 15212ca0b56Sdan INSERT INTO %T1% SELECT a+204.8, b+204.8 FROM %T1%; 15312ca0b56Sdan } 15412ca0b56Sdan 15512ca0b56Sdan 14 { 15612ca0b56Sdan DELETE FROM %T1% WHERE 1; 15712ca0b56Sdan } 15812ca0b56Sdan 15912ca0b56Sdan 15 { 16012ca0b56Sdan INSERT INTO %T1% VALUES(1, 1); 16112ca0b56Sdan INSERT INTO %T1% SELECT a+2, b+2 FROM %T1%; 16212ca0b56Sdan INSERT INTO %T1% SELECT a+4, b+4 FROM %T1%; 16312ca0b56Sdan INSERT INTO %T1% SELECT a+8, b+8 FROM %T1%; 16412ca0b56Sdan INSERT INTO %T1% SELECT a+256, b+256 FROM %T1%; 16512ca0b56Sdan } 166db04571cSdan 167db04571cSdan 16 { 168db04571cSdan INSERT INTO %T4% VALUES('abc', 'def'); 169db04571cSdan INSERT INTO %T4% VALUES('def', 'abc'); 170db04571cSdan } 171db04571cSdan 17 { UPDATE %T4% SET b = 1 } 1723cc89d95Sdan 173db04571cSdan 18 { DELETE FROM %T4% WHERE 1 } 1743cc89d95Sdan 1753cc89d95Sdan 19 { 1763cc89d95Sdan INSERT INTO t1 VALUES('', ''); 1773cc89d95Sdan INSERT INTO t1 VALUES(X'', X''); 1783cc89d95Sdan } 1793cc89d95Sdan 20 { 1803cc89d95Sdan DELETE FROM t1; 1813cc89d95Sdan INSERT INTO t1 VALUES('', NULL); 1823cc89d95Sdan } 18327453faeSdan} 18427453faeSdan 1857cf7df7dSdantest_reset 1867cf7df7dSdando_common_sql { 187*e0d2096aSdan CREATE TABLE t1(a int PRIMARY KEY, b); 1887cf7df7dSdan CREATE TABLE t2(a, b INTEGER PRIMARY KEY); 1897cf7df7dSdan CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b)); 190db04571cSdan CREATE TABLE t4(a, b, PRIMARY KEY(b, a)); 1917cf7df7dSdan} 1927cf7df7dSdan 193db04571cSdanforeach {tn sql} [string map {%T1% t1 %T2% t2 %T3% t3 %T4% t4} $set_of_tests] { 1947cf7df7dSdan do_then_apply_sql $sql 19512ca0b56Sdan do_test 2.$tn { compare_db db db2 } {} 1967cf7df7dSdan} 1977cf7df7dSdan 198d7fb7d24Sdan# The following block of tests is similar to the last, except that the 199d7fb7d24Sdan# session object is recording changes made to an attached database. The 200d7fb7d24Sdan# main database contains a table of the same name as the table being 201d7fb7d24Sdan# modified within the attached db. 202d7fb7d24Sdan# 203d7fb7d24Sdantest_reset 204d7fb7d24Sdanforcedelete test.db3 205d7fb7d24Sdansqlite3 db3 test.db3 20612ca0b56Sdando_test 3.0 { 207d7fb7d24Sdan execsql { 208d7fb7d24Sdan ATTACH 'test.db3' AS 'aux'; 209*e0d2096aSdan CREATE TABLE t1(a int, b PRIMARY KEY); 210d7fb7d24Sdan CREATE TABLE t2(x, y, z); 211d7fb7d24Sdan CREATE TABLE t3(a); 212d7fb7d24Sdan 213*e0d2096aSdan CREATE TABLE aux.t1(a int PRIMARY KEY, b); 214d7fb7d24Sdan CREATE TABLE aux.t2(a, b INTEGER PRIMARY KEY); 215d7fb7d24Sdan CREATE TABLE aux.t3(a, b, c, PRIMARY KEY(a, b)); 216db04571cSdan CREATE TABLE aux.t4(a, b, PRIMARY KEY(b, a)); 217d7fb7d24Sdan } 218d7fb7d24Sdan execsql { 219*e0d2096aSdan CREATE TABLE t1(a int PRIMARY KEY, b); 220d7fb7d24Sdan CREATE TABLE t2(a, b INTEGER PRIMARY KEY); 221d7fb7d24Sdan CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b)); 222db04571cSdan CREATE TABLE t4(a, b, PRIMARY KEY(b, a)); 223d7fb7d24Sdan } db2 224d7fb7d24Sdan} {} 225d7fb7d24Sdan 2264e895da1Sdanproc xTrace {args} { puts $args } 2274e895da1Sdan 22827453faeSdanforeach {tn sql} [ 229db04571cSdan string map {%T1% aux.t1 %T2% aux.t2 %T3% aux.t3 %T4% aux.t4} $set_of_tests 23027453faeSdan] { 231d7fb7d24Sdan do_then_apply_sql $sql aux 232db04571cSdan do_test 3.$tn { compare_db db2 db3 } {} 233d7fb7d24Sdan} 234d7fb7d24Sdancatch {db3 close} 235d7fb7d24Sdan 23627453faeSdan 23727453faeSdan#------------------------------------------------------------------------- 23827453faeSdan# The following tests verify that NULL values in primary key columns are 23927453faeSdan# handled correctly by the session module. 24027453faeSdan# 24127453faeSdantest_reset 24212ca0b56Sdando_execsql_test 4.0 { 24327453faeSdan CREATE TABLE t1(a PRIMARY KEY); 24427453faeSdan CREATE TABLE t2(a, b, c, PRIMARY KEY(c, b)); 24527453faeSdan CREATE TABLE t3(a, b INTEGER PRIMARY KEY); 24627453faeSdan} 24727453faeSdan 24827453faeSdanforeach {tn sql changeset} { 24927453faeSdan 1 { 25027453faeSdan INSERT INTO t1 VALUES(123); 25127453faeSdan INSERT INTO t1 VALUES(NULL); 25227453faeSdan INSERT INTO t1 VALUES(456); 25327453faeSdan } { 254244593c8Sdan {INSERT t1 0 X {} {i 456}} 255244593c8Sdan {INSERT t1 0 X {} {i 123}} 25627453faeSdan } 25727453faeSdan 25827453faeSdan 2 { 25927453faeSdan UPDATE t1 SET a = NULL; 26027453faeSdan } { 261244593c8Sdan {DELETE t1 0 X {i 456} {}} 262244593c8Sdan {DELETE t1 0 X {i 123} {}} 26327453faeSdan } 26427453faeSdan 26527453faeSdan 3 { DELETE FROM t1 } { } 26627453faeSdan 26727453faeSdan 4 { 26827453faeSdan INSERT INTO t3 VALUES(NULL, NULL) 26927453faeSdan } { 270244593c8Sdan {INSERT t3 0 .X {} {n {} i 1}} 27127453faeSdan } 27227453faeSdan 27327453faeSdan 5 { INSERT INTO t2 VALUES(1, 2, NULL) } { } 27427453faeSdan 6 { INSERT INTO t2 VALUES(1, NULL, 3) } { } 27527453faeSdan 7 { INSERT INTO t2 VALUES(1, NULL, NULL) } { } 276244593c8Sdan 8 { INSERT INTO t2 VALUES(1, 2, 3) } { {INSERT t2 0 .XX {} {i 1 i 2 i 3}} } 277244593c8Sdan 9 { DELETE FROM t2 WHERE 1 } { {DELETE t2 0 .XX {i 1 i 2 i 3} {}} } 27827453faeSdan 27927453faeSdan} { 28012ca0b56Sdan do_iterator_test 4.$tn {t1 t2 t3} $sql $changeset 28127453faeSdan} 28227453faeSdan 28327453faeSdan 284ff4d0f41Sdan#------------------------------------------------------------------------- 285ff4d0f41Sdan# Test that if NULL is passed to sqlite3session_attach(), all database 286ff4d0f41Sdan# tables are attached to the session object. 287ff4d0f41Sdan# 288ff4d0f41Sdantest_reset 289ff4d0f41Sdando_execsql_test 5.0 { 290ff4d0f41Sdan CREATE TABLE t1(a PRIMARY KEY); 291ff4d0f41Sdan CREATE TABLE t2(x, y PRIMARY KEY); 292ff4d0f41Sdan} 293ff4d0f41Sdan 294ff4d0f41Sdanforeach {tn sql changeset} { 295244593c8Sdan 1 { INSERT INTO t1 VALUES(35) } { {INSERT t1 0 X {} {i 35}} } 296244593c8Sdan 2 { INSERT INTO t2 VALUES(36, 37) } { {INSERT t2 0 .X {} {i 36 i 37}} } 297ff4d0f41Sdan 3 { 298ff4d0f41Sdan DELETE FROM t1 WHERE 1; 299ff4d0f41Sdan UPDATE t2 SET x = 34; 300ff4d0f41Sdan } { 301244593c8Sdan {DELETE t1 0 X {i 35} {}} 3026c39e6a8Sdan {UPDATE t2 0 .X {i 36 i 37} {i 34 {} {}}} 303ff4d0f41Sdan } 304ff4d0f41Sdan} { 305ff4d0f41Sdan do_iterator_test 5.$tn * $sql $changeset 306ff4d0f41Sdan} 307ff4d0f41Sdan 308b4480e94Sdan#------------------------------------------------------------------------- 309b4480e94Sdan# The next block of tests verify that the "indirect" flag is set 310b4480e94Sdan# correctly within changesets. The indirect flag is set for a change 311b4480e94Sdan# if either of the following are true: 312b4480e94Sdan# 313b4480e94Sdan# * The sqlite3session_indirect() API has been used to set the session 314b4480e94Sdan# indirect flag to true, or 315b4480e94Sdan# * The change was made by a trigger. 316b4480e94Sdan# 317b4480e94Sdan# If the same row is updated more than once during a session, then the 318b4480e94Sdan# change is considered indirect only if all changes meet the criteria 319b4480e94Sdan# above. 320b4480e94Sdan# 321b4480e94Sdantest_reset 322b4480e94Sdandb function indirect [list S indirect] 3237cf7df7dSdan 324b4480e94Sdando_execsql_test 6.0 { 325b4480e94Sdan CREATE TABLE t1(a PRIMARY KEY, b, c); 326b4480e94Sdan 327b4480e94Sdan CREATE TABLE t2(x PRIMARY KEY, y); 328b4480e94Sdan CREATE TRIGGER AFTER INSERT ON t2 WHEN new.x%2 BEGIN 329b4480e94Sdan INSERT INTO t2 VALUES(new.x+1, NULL); 330b4480e94Sdan END; 331b4480e94Sdan} 332b4480e94Sdan 333b4480e94Sdando_iterator_test 6.1.1 * { 334b4480e94Sdan INSERT INTO t1 VALUES(1, 'one', 'i'); 335b4480e94Sdan SELECT indirect(1); 336b4480e94Sdan INSERT INTO t1 VALUES(2, 'two', 'ii'); 337b4480e94Sdan SELECT indirect(0); 338b4480e94Sdan INSERT INTO t1 VALUES(3, 'three', 'iii'); 339b4480e94Sdan} { 340244593c8Sdan {INSERT t1 0 X.. {} {i 1 t one t i}} 341244593c8Sdan {INSERT t1 1 X.. {} {i 2 t two t ii}} 342244593c8Sdan {INSERT t1 0 X.. {} {i 3 t three t iii}} 343b4480e94Sdan} 344b4480e94Sdan 345b4480e94Sdando_iterator_test 6.1.2 * { 346b4480e94Sdan SELECT indirect(1); 347b4480e94Sdan UPDATE t1 SET c = 'I' WHERE a = 1; 348b4480e94Sdan SELECT indirect(0); 349b4480e94Sdan} { 350244593c8Sdan {UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}} 351b4480e94Sdan} 352b4480e94Sdando_iterator_test 6.1.3 * { 353b4480e94Sdan SELECT indirect(1); 354b4480e94Sdan UPDATE t1 SET c = '.' WHERE a = 1; 355b4480e94Sdan SELECT indirect(0); 356b4480e94Sdan UPDATE t1 SET c = 'o' WHERE a = 1; 357b4480e94Sdan} { 358244593c8Sdan {UPDATE t1 0 X.. {i 1 {} {} t I} {{} {} {} {} t o}} 359b4480e94Sdan} 360b4480e94Sdando_iterator_test 6.1.4 * { 361b4480e94Sdan SELECT indirect(0); 362b4480e94Sdan UPDATE t1 SET c = 'x' WHERE a = 1; 363b4480e94Sdan SELECT indirect(1); 364b4480e94Sdan UPDATE t1 SET c = 'i' WHERE a = 1; 365b4480e94Sdan} { 366244593c8Sdan {UPDATE t1 0 X.. {i 1 {} {} t o} {{} {} {} {} t i}} 367b4480e94Sdan} 368b4480e94Sdando_iterator_test 6.1.4 * { 369b4480e94Sdan SELECT indirect(1); 370b4480e94Sdan UPDATE t1 SET c = 'y' WHERE a = 1; 371b4480e94Sdan SELECT indirect(1); 372b4480e94Sdan UPDATE t1 SET c = 'I' WHERE a = 1; 373b4480e94Sdan} { 374244593c8Sdan {UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}} 375b4480e94Sdan} 376b4480e94Sdan 377b4480e94Sdando_iterator_test 6.1.5 * { 378b4480e94Sdan INSERT INTO t2 VALUES(1, 'x'); 379b4480e94Sdan} { 380244593c8Sdan {INSERT t2 0 X. {} {i 1 t x}} 381244593c8Sdan {INSERT t2 1 X. {} {i 2 n {}}} 382b4480e94Sdan} 383b4480e94Sdan 384b4480e94Sdando_iterator_test 6.1.6 * { 385b4480e94Sdan SELECT indirect(1); 386b4480e94Sdan INSERT INTO t2 VALUES(3, 'x'); 387b4480e94Sdan SELECT indirect(0); 388b4480e94Sdan UPDATE t2 SET y = 'y' WHERE x>2; 389b4480e94Sdan} { 390244593c8Sdan {INSERT t2 0 X. {} {i 3 t y}} 391244593c8Sdan {INSERT t2 0 X. {} {i 4 t y}} 392b4480e94Sdan} 393b4480e94Sdan 394b4480e94Sdando_iterator_test 6.1.7 * { 395b4480e94Sdan SELECT indirect(1); 396b4480e94Sdan DELETE FROM t2 WHERE x = 4; 397b4480e94Sdan SELECT indirect(0); 398b4480e94Sdan INSERT INTO t2 VALUES(4, 'new'); 399b4480e94Sdan} { 400244593c8Sdan {UPDATE t2 0 X. {i 4 t y} {{} {} t new}} 401b4480e94Sdan} 402b4480e94Sdan 40380fe2d93Sdando_iterator_test 6.1.8 * { 40480fe2d93Sdan CREATE TABLE t3(a, b PRIMARY KEY); 40580fe2d93Sdan CREATE TABLE t4(a, b PRIMARY KEY); 40680fe2d93Sdan CREATE TRIGGER t4t AFTER UPDATE ON t4 BEGIN 40780fe2d93Sdan UPDATE t3 SET a = new.a WHERE b = new.b; 40880fe2d93Sdan END; 40980fe2d93Sdan 41080fe2d93Sdan SELECT indirect(1); 41180fe2d93Sdan INSERT INTO t3 VALUES('one', 1); 41280fe2d93Sdan INSERT INTO t4 VALUES('one', 1); 41380fe2d93Sdan SELECT indirect(0); 41480fe2d93Sdan UPDATE t4 SET a = 'two' WHERE b = 1; 41580fe2d93Sdan} { 41680fe2d93Sdan {INSERT t3 1 .X {} {t two i 1}} 4176c39e6a8Sdan {INSERT t4 0 .X {} {t two i 1}} 41880fe2d93Sdan} 41980fe2d93Sdan 420f51e5f6cSdansqlite3session S db main 421f51e5f6cSdando_execsql_test 6.2.1 { 422f51e5f6cSdan SELECT indirect(0); 423f51e5f6cSdan SELECT indirect(-1); 424f51e5f6cSdan SELECT indirect(45); 425f51e5f6cSdan SELECT indirect(-100); 426f51e5f6cSdan} {0 0 1 1} 427f51e5f6cSdanS delete 428f51e5f6cSdan 429f51e5f6cSdan#------------------------------------------------------------------------- 430f51e5f6cSdan# Test that if a conflict-handler that has been passed either NOTFOUND or 431f51e5f6cSdan# CONSTRAINT returns REPLACE - the sqlite3changeset_apply() call returns 432f51e5f6cSdan# MISUSE and rolls back any changes made so far. 433f51e5f6cSdan# 434f51e5f6cSdan# 7.1.*: NOTFOUND conflict-callback. 435f51e5f6cSdan# 7.2.*: CONSTRAINT conflict-callback. 436f51e5f6cSdan# 437f51e5f6cSdanproc xConflict {args} {return REPLACE} 438f51e5f6cSdantest_reset 439f51e5f6cSdan 440f51e5f6cSdando_execsql_test 7.1.1 { 441f51e5f6cSdan CREATE TABLE t1(a PRIMARY KEY, b); 442f51e5f6cSdan INSERT INTO t1 VALUES(1, 'one'); 443f51e5f6cSdan INSERT INTO t1 VALUES(2, 'two'); 444f51e5f6cSdan} 445f51e5f6cSdando_test 7.1.2 { 446f51e5f6cSdan execsql { 447f51e5f6cSdan CREATE TABLE t1(a PRIMARY KEY, b NOT NULL); 448f51e5f6cSdan INSERT INTO t1 VALUES(1, 'one'); 449f51e5f6cSdan } db2 450f51e5f6cSdan} {} 451f51e5f6cSdando_test 7.1.3 { 452f51e5f6cSdan set changeset [changeset_from_sql { 453f51e5f6cSdan UPDATE t1 SET b = 'five' WHERE a = 1; 454f51e5f6cSdan UPDATE t1 SET b = 'six' WHERE a = 2; 455f51e5f6cSdan }] 456f51e5f6cSdan set x [list] 457f51e5f6cSdan sqlite3session_foreach c $changeset { lappend x $c } 458f51e5f6cSdan set x 459f51e5f6cSdan} [list \ 460f51e5f6cSdan {UPDATE t1 0 X. {i 1 t one} {{} {} t five}} \ 461f51e5f6cSdan {UPDATE t1 0 X. {i 2 t two} {{} {} t six}} \ 462f51e5f6cSdan] 463f51e5f6cSdando_test 7.1.4 { 464f51e5f6cSdan list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg 465f51e5f6cSdan} {1 SQLITE_MISUSE} 466f51e5f6cSdando_test 7.1.5 { execsql { SELECT * FROM t1 } db2 } {1 one} 467f51e5f6cSdan 468f51e5f6cSdando_test 7.2.1 { 469f51e5f6cSdan set changeset [changeset_from_sql { UPDATE t1 SET b = NULL WHERE a = 1 }] 470f51e5f6cSdan 471f51e5f6cSdan set x [list] 472f51e5f6cSdan sqlite3session_foreach c $changeset { lappend x $c } 473f51e5f6cSdan set x 474f51e5f6cSdan} [list \ 475f51e5f6cSdan {UPDATE t1 0 X. {i 1 t five} {{} {} n {}}} \ 476f51e5f6cSdan] 477f51e5f6cSdando_test 7.2.2 { 478f51e5f6cSdan list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg 479f51e5f6cSdan} {1 SQLITE_MISUSE} 480f51e5f6cSdando_test 7.2.3 { execsql { SELECT * FROM t1 } db2 } {1 one} 481f51e5f6cSdan 482f51e5f6cSdan#------------------------------------------------------------------------- 483f51e5f6cSdan# Test that if a conflict-handler returns ABORT, application of the 484f51e5f6cSdan# changeset is rolled back and the sqlite3changeset_apply() method returns 485f51e5f6cSdan# SQLITE_ABORT. 486f51e5f6cSdan# 487f51e5f6cSdan# Also test that the same thing happens if a conflict handler returns an 488f51e5f6cSdan# unrecognized integer value. Except, in this case SQLITE_MISUSE is returned 489f51e5f6cSdan# instead of SQLITE_ABORT. 490f51e5f6cSdan# 491f51e5f6cSdanforeach {tn conflict_return apply_return} { 492f51e5f6cSdan 1 ABORT SQLITE_ABORT 493f51e5f6cSdan 2 567 SQLITE_MISUSE 494f51e5f6cSdan} { 495f51e5f6cSdan test_reset 496f51e5f6cSdan proc xConflict {args} [list return $conflict_return] 497f51e5f6cSdan 498f51e5f6cSdan do_test 8.$tn.0 { 499f51e5f6cSdan do_common_sql { 500f51e5f6cSdan CREATE TABLE t1(x, y, PRIMARY KEY(x, y)); 501f51e5f6cSdan INSERT INTO t1 VALUES('x', 'y'); 502f51e5f6cSdan } 503f51e5f6cSdan execsql { INSERT INTO t1 VALUES('w', 'w') } 504f51e5f6cSdan 505f51e5f6cSdan set changeset [changeset_from_sql { DELETE FROM t1 WHERE 1 }] 506f51e5f6cSdan 507f51e5f6cSdan set x [list] 508f51e5f6cSdan sqlite3session_foreach c $changeset { lappend x $c } 509f51e5f6cSdan set x 510f51e5f6cSdan } [list \ 511f51e5f6cSdan {DELETE t1 0 XX {t w t w} {}} \ 512f51e5f6cSdan {DELETE t1 0 XX {t x t y} {}} \ 513f51e5f6cSdan ] 514f51e5f6cSdan 515f51e5f6cSdan do_test 8.$tn.1 { 516f51e5f6cSdan list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg 517f51e5f6cSdan } [list 1 $apply_return] 518f51e5f6cSdan 519f51e5f6cSdan do_test 8.$tn.2 { 520f51e5f6cSdan execsql {SELECT * FROM t1} db2 521f51e5f6cSdan } {x y} 522f51e5f6cSdan} 523f51e5f6cSdan 524f51e5f6cSdan 525f51e5f6cSdan#------------------------------------------------------------------------- 526f51e5f6cSdan# Try to cause an infinite loop as follows: 527f51e5f6cSdan# 528f51e5f6cSdan# 1. Have a changeset insert a row that causes a CONFLICT callback, 529f51e5f6cSdan# 2. Have the conflict handler return REPLACE, 530f51e5f6cSdan# 3. After the session module deletes the conflicting row, have a trigger 531f51e5f6cSdan# re-insert it. 532f51e5f6cSdan# 4. Goto step 1... 533f51e5f6cSdan# 534f51e5f6cSdan# This doesn't work, as the second invocation of the conflict handler is a 535f51e5f6cSdan# CONSTRAINT, not a CONFLICT. There is at most one CONFLICT callback for 536f51e5f6cSdan# each change in the changeset. 537f51e5f6cSdan# 538f51e5f6cSdantest_reset 539f51e5f6cSdanproc xConflict {type args} { 540f51e5f6cSdan if {$type == "CONFLICT"} { return REPLACE } 541f51e5f6cSdan return OMIT 542f51e5f6cSdan} 543f51e5f6cSdando_test 9.1 { 544f51e5f6cSdan execsql { 545f51e5f6cSdan CREATE TABLE t1(a PRIMARY KEY, b); 546f51e5f6cSdan } 547f51e5f6cSdan execsql { 548f51e5f6cSdan CREATE TABLE t1(a PRIMARY KEY, b); 549f51e5f6cSdan INSERT INTO t1 VALUES('x', 2); 550f51e5f6cSdan CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN 551f51e5f6cSdan INSERT INTO t1 VALUES(old.a, old.b); 552f51e5f6cSdan END; 553f51e5f6cSdan } db2 554f51e5f6cSdan} {} 555f51e5f6cSdando_test 9.2 { 556f51e5f6cSdan set changeset [changeset_from_sql { INSERT INTO t1 VALUES('x', 1) }] 557f51e5f6cSdan sqlite3changeset_apply db2 $changeset xConflict 558f51e5f6cSdan} {} 559f51e5f6cSdando_test 9.3 { 560f51e5f6cSdan execsql { SELECT * FROM t1 } db2 561f51e5f6cSdan} {x 2} 562f51e5f6cSdan 563e5754eecSdan#------------------------------------------------------------------------- 564e5754eecSdan# 565e5754eecSdantest_reset 566e5754eecSdandb function enable [list S enable] 567f51e5f6cSdan 568e5754eecSdando_common_sql { 569e5754eecSdan CREATE TABLE t1(a PRIMARY KEY, b); 570e5754eecSdan INSERT INTO t1 VALUES('x', 'X'); 571e5754eecSdan} 572e5754eecSdan 573e5754eecSdando_iterator_test 10.1 t1 { 574e5754eecSdan INSERT INTO t1 VALUES('y', 'Y'); 575e5754eecSdan SELECT enable(0); 576e5754eecSdan INSERT INTO t1 VALUES('z', 'Z'); 577e5754eecSdan SELECT enable(1); 578e5754eecSdan} { 579e5754eecSdan {INSERT t1 0 X. {} {t y t Y}} 580e5754eecSdan} 581e5754eecSdan 582e5754eecSdansqlite3session S db main 583e5754eecSdando_execsql_test 10.2 { 584e5754eecSdan SELECT enable(0); 585e5754eecSdan SELECT enable(-1); 586e5754eecSdan SELECT enable(1); 587e5754eecSdan SELECT enable(-1); 588e5754eecSdan} {0 0 1 1} 589e5754eecSdanS delete 590f51e5f6cSdan 591*e0d2096aSdan#------------------------------------------------------------------------- 592*e0d2096aSdantest_reset 593*e0d2096aSdando_common_sql { 594*e0d2096aSdan CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d, e, f); 595*e0d2096aSdan WITH s(i) AS ( 596*e0d2096aSdan SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<32 597*e0d2096aSdan ) 598*e0d2096aSdan INSERT INTO t1 SELECT NULL, 0, 0, 0, 0, 0 FROM s 599*e0d2096aSdan} 600*e0d2096aSdan 601*e0d2096aSdando_then_apply_sql { 602*e0d2096aSdan UPDATE t1 SET f=f+1 WHERE a=1; 603*e0d2096aSdan UPDATE t1 SET e=e+1 WHERE a=2; 604*e0d2096aSdan UPDATE t1 SET e=e+1, f=f+1 WHERE a=3; 605*e0d2096aSdan UPDATE t1 SET d=d+1 WHERE a=4; 606*e0d2096aSdan UPDATE t1 SET d=d+1, f=f+1 WHERE a=5; 607*e0d2096aSdan UPDATE t1 SET d=d+1, e=e+1 WHERE a=6; 608*e0d2096aSdan UPDATE t1 SET d=d+1, e=e+1, f=f+1 WHERE a=7; 609*e0d2096aSdan UPDATE t1 SET c=c+1 WHERE a=8; 610*e0d2096aSdan UPDATE t1 SET c=c+1, f=f+1 WHERE a=9; 611*e0d2096aSdan UPDATE t1 SET c=c+1, e=e+1 WHERE a=10; 612*e0d2096aSdan UPDATE t1 SET c=c+1, e=e+1, f=f+1 WHERE a=11; 613*e0d2096aSdan UPDATE t1 SET c=c+1, d=d+1 WHERE a=12; 614*e0d2096aSdan UPDATE t1 SET c=c+1, d=d+1, f=f+1 WHERE a=13; 615*e0d2096aSdan UPDATE t1 SET c=c+1, d=d+1, e=e+1 WHERE a=14; 616*e0d2096aSdan UPDATE t1 SET c=c+1, d=d+1, e=e+1, f=f+1 WHERE a=15; 617*e0d2096aSdan UPDATE t1 SET d=d+1 WHERE a=16; 618*e0d2096aSdan UPDATE t1 SET d=d+1, f=f+1 WHERE a=17; 619*e0d2096aSdan UPDATE t1 SET d=d+1, e=e+1 WHERE a=18; 620*e0d2096aSdan UPDATE t1 SET d=d+1, e=e+1, f=f+1 WHERE a=19; 621*e0d2096aSdan UPDATE t1 SET d=d+1, d=d+1 WHERE a=20; 622*e0d2096aSdan UPDATE t1 SET d=d+1, d=d+1, f=f+1 WHERE a=21; 623*e0d2096aSdan UPDATE t1 SET d=d+1, d=d+1, e=e+1 WHERE a=22; 624*e0d2096aSdan UPDATE t1 SET d=d+1, d=d+1, e=e+1, f=f+1 WHERE a=23; 625*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1 WHERE a=24; 626*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1, f=f+1 WHERE a=25; 627*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1, e=e+1 WHERE a=26; 628*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1, e=e+1, f=f+1 WHERE a=27; 629*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1, d=d+1 WHERE a=28; 630*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1, d=d+1, f=f+1 WHERE a=29; 631*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1, d=d+1, e=e+1 WHERE a=30; 632*e0d2096aSdan UPDATE t1 SET d=d+1, c=c+1, d=d+1, e=e+1, f=f+1 WHERE a=31; 633*e0d2096aSdan} 634*e0d2096aSdan 635*e0d2096aSdando_test 11.0 { 636*e0d2096aSdan compare_db db db2 637*e0d2096aSdan} {} 638*e0d2096aSdan 639b4480e94Sdanfinish_test 640