1*1ffede8cSdrh# 2015-01-30 2*1ffede8cSdrh# 3*1ffede8cSdrh# The author disclaims copyright to this source code. In place of 4*1ffede8cSdrh# a legal notice, here is a blessing: 5*1ffede8cSdrh# 6*1ffede8cSdrh# May you do good and not evil. 7*1ffede8cSdrh# May you find forgiveness for yourself and forgive others. 8*1ffede8cSdrh# May you share freely, never taking more than you give. 9*1ffede8cSdrh# 10*1ffede8cSdrh#*********************************************************************** 11*1ffede8cSdrh# 12*1ffede8cSdrh# This file implements tests for SQLite library. 13*1ffede8cSdrh# 14*1ffede8cSdrh# The focus of this file is adding extra entries in the symbol table 15*1ffede8cSdrh# using sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER) and verifying that 16*1ffede8cSdrh# SQLite handles those as expected. 17*1ffede8cSdrh# 18*1ffede8cSdrh 19*1ffede8cSdrhset testdir [file dirname $argv0] 20*1ffede8cSdrhsource $testdir/tester.tcl 21*1ffede8cSdrhset testprefix imposter 22*1ffede8cSdrh 23*1ffede8cSdrh# Create a bunch of data to sort against 24*1ffede8cSdrh# 25*1ffede8cSdrhdo_test imposter-1.0 { 26*1ffede8cSdrh execsql { 27*1ffede8cSdrh CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d NOT NULL); 28*1ffede8cSdrh CREATE INDEX t1b ON t1(b); 29*1ffede8cSdrh CREATE UNIQUE INDEX t1c ON t1(c); 30*1ffede8cSdrh WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30) 31*1ffede8cSdrh INSERT INTO t1(a,b,c,d) SELECT i,1000+i,2000+i,3000+i FROM c; 32*1ffede8cSdrh } 33*1ffede8cSdrh set t1_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1'}] 34*1ffede8cSdrh set t1b_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1b'}] 35*1ffede8cSdrh set t1c_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1c'}] 36*1ffede8cSdrh 37*1ffede8cSdrh # Create an imposter table that uses the same b-tree as t1 but which does 38*1ffede8cSdrh # not have the indexes 39*1ffede8cSdrh # 40*1ffede8cSdrh sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $t1_root 41*1ffede8cSdrh db eval {CREATE TABLE xt1(a,b,c,d)} 42*1ffede8cSdrh 43*1ffede8cSdrh # And create an imposter table for the t1c index. 44*1ffede8cSdrh sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $t1c_root 45*1ffede8cSdrh db eval {CREATE TABLE xt1c(c,rowid,PRIMARY KEY(c,rowid))WITHOUT ROWID;} 46*1ffede8cSdrh 47*1ffede8cSdrh # Go out of imposter mode for now. 48*1ffede8cSdrh sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 49*1ffede8cSdrh 50*1ffede8cSdrh # Create triggers to record changes to xt1. 51*1ffede8cSdrh # 52*1ffede8cSdrh db eval { 53*1ffede8cSdrh CREATE TEMP TABLE chnglog(desc TEXT); 54*1ffede8cSdrh CREATE TEMP TRIGGER xt1_del AFTER DELETE ON xt1 BEGIN 55*1ffede8cSdrh INSERT INTO chnglog VALUES( 56*1ffede8cSdrh printf('DELETE t1: rowid=%d, a=%s, b=%s, c=%s, d=%s', 57*1ffede8cSdrh old.rowid, quote(old.a), quote(old.b), quote(old.c), 58*1ffede8cSdrh quote(old.d))); 59*1ffede8cSdrh END; 60*1ffede8cSdrh CREATE TEMP TRIGGER xt1_ins AFTER INSERT ON xt1 BEGIN 61*1ffede8cSdrh INSERT INTO chnglog VALUES( 62*1ffede8cSdrh printf('INSERT t1: rowid=%d, a=%s, b=%s, c=%s, d=%s', 63*1ffede8cSdrh new.rowid, quote(new.a), quote(new.b), quote(new.c), 64*1ffede8cSdrh quote(new.d))); 65*1ffede8cSdrh END; 66*1ffede8cSdrh } 67*1ffede8cSdrh} {} 68*1ffede8cSdrh 69*1ffede8cSdrh# The xt1 table has separate xt1.rowid and xt1.a columns. The xt1.rowid 70*1ffede8cSdrh# column corresponds to t1.rowid and t1.a, but the xt1.a column is always 71*1ffede8cSdrh# NULL 72*1ffede8cSdrh# 73*1ffede8cSdrhdo_execsql_test imposter-1.1 { 74*1ffede8cSdrh SELECT rowid FROM xt1 WHERE a IS NOT NULL; 75*1ffede8cSdrh} {} 76*1ffede8cSdrhdo_execsql_test imposter-1.2 { 77*1ffede8cSdrh SELECT a,b,c,d FROM t1 EXCEPT SELECT rowid,b,c,d FROM xt1; 78*1ffede8cSdrh SELECT rowid,b,c,d FROM xt1 EXCEPT SELECT a,b,c,d FROM t1; 79*1ffede8cSdrh} {} 80*1ffede8cSdrh 81*1ffede8cSdrh 82*1ffede8cSdrh# Make changes via the xt1 shadow table. This will not update the 83*1ffede8cSdrh# indexes on t1 nor check the uniqueness constraint on t1.c nor check 84*1ffede8cSdrh# the NOT NULL constraint on t1.d, resulting in a logically inconsistent 85*1ffede8cSdrh# database. 86*1ffede8cSdrh# 87*1ffede8cSdrhdo_execsql_test imposter-1.3 { 88*1ffede8cSdrh DELETE FROM xt1 WHERE rowid=5; 89*1ffede8cSdrh INSERT INTO xt1(rowid,a,b,c,d) VALUES(99,'hello',1099,2022,NULL); 90*1ffede8cSdrh SELECT * FROM chnglog ORDER BY rowid; 91*1ffede8cSdrh} [list \ 92*1ffede8cSdrh {DELETE t1: rowid=5, a=NULL, b=1005, c=2005, d=3005} \ 93*1ffede8cSdrh {INSERT t1: rowid=99, a='hello', b=1099, c=2022, d=NULL} \ 94*1ffede8cSdrh] 95*1ffede8cSdrh 96*1ffede8cSdrhdo_execsql_test imposter-1.4a { 97*1ffede8cSdrh PRAGMA integrity_check; 98*1ffede8cSdrh} {/NULL value in t1.d/} 99*1ffede8cSdrhdo_execsql_test imposter-1.4b { 100*1ffede8cSdrh PRAGMA integrity_check; 101*1ffede8cSdrh} {/row # missing from index t1b/} 102*1ffede8cSdrhdo_execsql_test imposter-1.4c { 103*1ffede8cSdrh PRAGMA integrity_check; 104*1ffede8cSdrh} {/row # missing from index t1c/} 105*1ffede8cSdrh 106*1ffede8cSdrh# Cleanup the corruption. 107*1ffede8cSdrh# Then demonstrate that the xt1c imposter table can insert non-unique 108*1ffede8cSdrh# and NULL values into the UNIQUE index. 109*1ffede8cSdrh# 110*1ffede8cSdrhdo_execsql_test imposter-2.0 { 111*1ffede8cSdrh DELETE FROM t1; 112*1ffede8cSdrh WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10) 113*1ffede8cSdrh INSERT INTO t1(a,b,c,d) SELECT i,i,i,i FROM c; 114*1ffede8cSdrh UPDATE xt1c SET c=NULL WHERE rowid=5; 115*1ffede8cSdrh PRAGMA integrity_check; 116*1ffede8cSdrh} {/row # missing from index t1c/} 117*1ffede8cSdrh 118*1ffede8cSdrhdo_execsql_test imposter-2.1 { 119*1ffede8cSdrh DELETE FROM t1; 120*1ffede8cSdrh WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10) 121*1ffede8cSdrh INSERT INTO t1(a,b,c,d) SELECT i,i,i,i FROM c; 122*1ffede8cSdrh UPDATE xt1c SET c=99 WHERE rowid IN (5,7,9); 123*1ffede8cSdrh SELECT c FROM t1 ORDER BY c; 124*1ffede8cSdrh} {1 2 3 4 6 8 10 99 99 99} 125*1ffede8cSdrhdo_execsql_test imposter-2.2 { 126*1ffede8cSdrh UPDATE xt1 SET c=99 WHERE rowid IN (5,7,9); 127*1ffede8cSdrh PRAGMA integrity_check; 128*1ffede8cSdrh} {/non-unique entry in index t1c/} 129*1ffede8cSdrh 130*1ffede8cSdrh# Erase the imposter tables 131*1ffede8cSdrh# 132*1ffede8cSdrhdo_test imposter-3.1 { 133*1ffede8cSdrh sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 1 134*1ffede8cSdrh db eval { 135*1ffede8cSdrh DELETE FROM t1 WHERE rowid IN (5,7,9); 136*1ffede8cSdrh PRAGMA integrity_check; 137*1ffede8cSdrh } 138*1ffede8cSdrh} {ok} 139*1ffede8cSdrh 140*1ffede8cSdrh 141*1ffede8cSdrhfinish_test 142