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