xref: /sqlite-3.40.0/test/imposter1.test (revision 1ffede8c)
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