diff --git a/manifest b/manifest index a2a6f9c0c1..0d5c8c8888 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\srecent\strunk\schanges,\sand\sespecially\sthe\sSQLITE_TESTCTRL_INITMODE\nenhancement. -D 2015-01-30T16:36:17.286 +C Merge\sthe\sSQLITE_TESTCTRL_IMPOSTER\schanges\sfrom\strunk. +D 2015-01-30T21:00:10.049 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5407a688f4d77a05c18a8142be8ae5a2829dd610 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -188,10 +188,10 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 F src/backup.c 7ddee9c7d505e07e959a575b18498f17c71e53ea F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5 -F src/btree.c 73cfa1752138438088a20f17f7bc501ba3b999b4 +F src/btree.c ddca0ae681c49813a4bfd88d5f922fac71e0ffaa F src/btree.h 94277c1d30c0b75705974bcc8b0c05e79c03d474 F src/btreeInt.h a3d0ae1d511365e1a2b76ad10960dbe55c286f34 -F src/build.c f5cfd7b32216f695b995bbc7c1a395f6d451d11f +F src/build.c eefaa4f1d86bc3c08023a61fdd1e695b47796975 F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463 F src/ctime.c 98f89724adc891a1a4c655bee04e33e716e05887 @@ -210,7 +210,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770 F src/loadext.c 86bd4e2fccd520b748cba52492ab60c4a770f660 -F src/main.c cf9e43a0900f35d708eca1f04db133d3860be996 +F src/main.c d6c5fd51a719fcdcf1cc9ef08349dbd4454cf2f3 F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987 @@ -245,16 +245,16 @@ F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c f6c46d3434439ab2084618d603e6d6dbeb0d6ada F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e F src/select.c 1f2087523007c42900ffcbdeaef06a23ad9329fc -F src/shell.c ed7cf7c29fb1a23d47179affc89cb447868fc976 -F src/sqlite.h.in 944152036fea0e4a0b3329a7d19fb3186aaa9232 +F src/shell.c 22b4406b0b59efd14b3b351a5809dda517df6d30 +F src/sqlite.h.in 78e493f94202d8083dd270e257786a6311d1fb3b F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d -F src/sqliteInt.h 25b73f7edb325b827522f7d9c3273f7c7d6d4cd6 +F src/sqliteInt.h 66180aa8f81155a7f391bbf759ee5a3b61d2f89f F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 81712116e826b0089bb221b018929536b2b5406f F src/table.c e7a09215315a978057fb42c640f890160dbcc45e F src/tclsqlite.c b321464aba1fff1ed9317ebc82a1a94887f97af8 -F src/test1.c 32ab487d570f8864c4040be6d5eca8d1f392dbbf +F src/test1.c 313567541c980e45220d6faed393b6ad454f8ecd F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622 F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e @@ -645,6 +645,7 @@ F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98 F test/hexlit.test f9ecde8145bfc2341573473256c74ae37a200497 F test/hook.test 162d7cef7a2d2b04839fe14402934e6a1b79442f F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 +F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8 w test/initmode.test F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75 F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0 @@ -670,7 +671,6 @@ F test/index7.test 917cf1e1c7439bb155abbeabec511b28945e157b F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 -F test/initmode.test 38bbaefeb47e034a162856e664a0da3b95e6999b F test/insert.test 38742b5e9601c8f8d76e9b7555f7270288c2d371 F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435 F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30 @@ -1254,7 +1254,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 6f8cda26e93f09eadf0b084131a1d4002d94d959 98e029134dc1300d3ecb48b41b5107ec69ba85db -R d1091e1b241c9e756b3cdf5ab40acb3b +P 36436dde74ce2536a9a430b4458216499ad113bf 42d5601739c90434e5adfda8fa99ef7b903877db +R 8701f18a4ed2282b10e5999d74aa1b13 U drh -Z f1982de9d1fa245a5e8f00d7b4ec9b1f +Z 76979458fdcef8860d5505bc7f40ac6e diff --git a/manifest.uuid b/manifest.uuid index 87dae2d2b9..41edd762be 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -36436dde74ce2536a9a430b4458216499ad113bf \ No newline at end of file +3ed6eb2fab5d95709ef392170339e6dd5ba13971 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 550629d616..4f752066ac 100644 --- a/src/btree.c +++ b/src/btree.c @@ -176,6 +176,12 @@ static int hasSharedCacheTableLock( for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ Index *pIdx = (Index *)sqliteHashData(p); if( pIdx->tnum==(int)iRoot ){ + if( iTab ){ + /* Two or more indexes share the same root page. There must + ** be imposter tables. So just return true. The assert is not + ** useful in that case. */ + return 1; + } iTab = pIdx->pTable->tnum; } } diff --git a/src/build.c b/src/build.c index f02989bffe..7e3ce1b76a 100644 --- a/src/build.c +++ b/src/build.c @@ -1731,11 +1731,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ assert( pPk!=0 ); nPk = pPk->nKeyCol; - /* Make sure every column of the PRIMARY KEY is NOT NULL */ - for(i=0; iaCol[pPk->aiColumn[i]].notNull = 1; + /* Make sure every column of the PRIMARY KEY is NOT NULL. (Except, + ** do not enforce this for imposter tables.) */ + if( !db->init.imposterTable ){ + for(i=0; iaCol[pPk->aiColumn[i]].notNull = 1; + } + pPk->uniqNotNull = 1; } - pPk->uniqNotNull = 1; /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; diff --git a/src/main.c b/src/main.c index cc80a4dfa8..6ccc6d68f6 100644 --- a/src/main.c +++ b/src/main.c @@ -3626,15 +3626,30 @@ int sqlite3_test_control(int op, ...){ break; } - /* sqlite3_test_control(SQLITE_TESTCTRL_INITMODE, db, busy, iDb, newTnum); + /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum); ** - ** Set the db->init.busy, db->init.iDb, and db->init.tnum fields. + ** This test control is used to create imposter tables. "db" is a pointer + ** to the database connection. dbName is the database name (ex: "main" or + ** "temp") which will receive the imposter. "onOff" turns imposter mode on + ** or off. "tnum" is the root page of the b-tree to which the imposter + ** table should connect. + ** + ** Enable imposter mode only when the schema has already been parsed. Then + ** run a single CREATE TABLE statement to construct the imposter table in the + ** parsed schema. Then turn imposter mode back off again. + ** + ** If onOff==0 and tnum>0 then reset the schema for all databases, causing + ** the schema to be reparsed the next time it is needed. This has the + ** effect of erasing all imposter tables. */ - case SQLITE_TESTCTRL_INITMODE: { + case SQLITE_TESTCTRL_IMPOSTER: { sqlite3 *db = va_arg(ap, sqlite3*); - db->init.busy = va_arg(ap,int); - db->init.iDb = va_arg(ap,int); + db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); + db->init.busy = db->init.imposterTable = va_arg(ap,int); db->init.newTnum = va_arg(ap,int); + if( db->init.busy==0 && db->init.newTnum>0 ){ + sqlite3ResetAllSchemasOfConnection(db); + } break; } } diff --git a/src/shell.c b/src/shell.c index 1a191e0fd0..3130f4c6de 100644 --- a/src/shell.c +++ b/src/shell.c @@ -3536,7 +3536,7 @@ static int do_meta_command(char *zLine, ShellState *p){ { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, - { "initmode", SQLITE_TESTCTRL_INITMODE }, + { "imposter", SQLITE_TESTCTRL_IMPOSTER }, }; int testctrl = -1; int rc = 0; @@ -3629,14 +3629,14 @@ static int do_meta_command(char *zLine, ShellState *p){ break; #endif - case SQLITE_TESTCTRL_INITMODE: + case SQLITE_TESTCTRL_IMPOSTER: if( nArg==5 ){ rc = sqlite3_test_control(testctrl, p->db, - integerValue(azArg[2]), + azArg[2], integerValue(azArg[3]), integerValue(azArg[4])); }else{ - fprintf(stderr,"Usage: .testctrl initmode fBusy iDb newTnum\n"); + fprintf(stderr,"Usage: .testctrl initmode dbName onoff tnum\n"); rc = 1; } break; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 407c1ea7ef..6e02a98688 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -6265,7 +6265,7 @@ int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 -#define SQLITE_TESTCTRL_INITMODE 25 +#define SQLITE_TESTCTRL_IMPOSTER 25 #define SQLITE_TESTCTRL_LAST 25 /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a904fc62c7..9de37160bf 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1087,6 +1087,7 @@ struct sqlite3 { u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ + u8 imposterTable; /* Building an imposter table */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ diff --git a/src/test1.c b/src/test1.c index 13f5b12942..ae6e7bc0ea 100644 --- a/src/test1.c +++ b/src/test1.c @@ -5915,7 +5915,7 @@ static int test_test_control( } aVerb[] = { { "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT }, { "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP }, - { "SQLITE_TESTCTRL_INITMODE", SQLITE_TESTCTRL_INITMODE }, + { "SQLITE_TESTCTRL_IMPOSTER", SQLITE_TESTCTRL_IMPOSTER }, }; int iVerb; int iFlag; @@ -5957,18 +5957,19 @@ static int test_test_control( break; } - case SQLITE_TESTCTRL_INITMODE: { - int fBusy, iDb, newTnum; + case SQLITE_TESTCTRL_IMPOSTER: { + int onOff, tnum; + const char *zDbName; sqlite3 *db; if( objc!=6 ){ - Tcl_WrongNumArgs(interp, 2, objv, "DB fBusy iDb newTnum"); + Tcl_WrongNumArgs(interp, 2, objv, "DB dbName onOff tnum"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR; - if( Tcl_GetIntFromObj(interp, objv[3], &fBusy) ) return TCL_ERROR; - if( Tcl_GetIntFromObj(interp, objv[4], &iDb) ) return TCL_ERROR; - if( Tcl_GetIntFromObj(interp, objv[5], &newTnum) ) return TCL_ERROR; - sqlite3_test_control(SQLITE_TESTCTRL_INITMODE, db, fBusy, iDb, newTnum); + zDbName = Tcl_GetString(objv[3]); + if( Tcl_GetIntFromObj(interp, objv[4], &onOff) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[5], &tnum) ) return TCL_ERROR; + sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zDbName, onOff, tnum); break; } } diff --git a/test/initmode.test b/test/imposter1.test similarity index 60% rename from test/initmode.test rename to test/imposter1.test index ce3f675857..196767be15 100644 --- a/test/initmode.test +++ b/test/imposter1.test @@ -12,17 +12,17 @@ # This file implements tests for SQLite library. # # The focus of this file is adding extra entries in the symbol table -# using sqlite3_test_control(SQLITE_TESTCTRL_INITMODE) and verifying that +# using sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER) and verifying that # SQLite handles those as expected. # set testdir [file dirname $argv0] source $testdir/tester.tcl -set testprefix initmode +set testprefix imposter # Create a bunch of data to sort against # -do_test initmode-1.0 { +do_test imposter-1.0 { execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d NOT NULL); CREATE INDEX t1b ON t1(b); @@ -31,15 +31,21 @@ do_test initmode-1.0 { INSERT INTO t1(a,b,c,d) SELECT i,1000+i,2000+i,3000+i FROM c; } set t1_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1'}] - set t1a_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1a'}] set t1b_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1b'}] + set t1c_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1c'}] - # Create a shadow table that uses the same b-tree as t1 but which does + # Create an imposter table that uses the same b-tree as t1 but which does # not have the indexes # - sqlite3_test_control SQLITE_TESTCTRL_INITMODE db 1 0 $t1_root + sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $t1_root db eval {CREATE TABLE xt1(a,b,c,d)} - sqlite3_test_control SQLITE_TESTCTRL_INITMODE db 0 0 0 + + # And create an imposter table for the t1c index. + sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $t1c_root + db eval {CREATE TABLE xt1c(c,rowid,PRIMARY KEY(c,rowid))WITHOUT ROWID;} + + # Go out of imposter mode for now. + sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0 # Create triggers to record changes to xt1. # @@ -64,10 +70,10 @@ do_test initmode-1.0 { # column corresponds to t1.rowid and t1.a, but the xt1.a column is always # NULL # -do_execsql_test initmode-1.1 { +do_execsql_test imposter-1.1 { SELECT rowid FROM xt1 WHERE a IS NOT NULL; } {} -do_execsql_test initmode-1.2 { +do_execsql_test imposter-1.2 { SELECT a,b,c,d FROM t1 EXCEPT SELECT rowid,b,c,d FROM xt1; SELECT rowid,b,c,d FROM xt1 EXCEPT SELECT a,b,c,d FROM t1; } {} @@ -78,7 +84,7 @@ do_execsql_test initmode-1.2 { # the NOT NULL constraint on t1.d, resulting in a logically inconsistent # database. # -do_execsql_test initmode-1.3 { +do_execsql_test imposter-1.3 { DELETE FROM xt1 WHERE rowid=5; INSERT INTO xt1(rowid,a,b,c,d) VALUES(99,'hello',1099,2022,NULL); SELECT * FROM chnglog ORDER BY rowid; @@ -87,14 +93,49 @@ do_execsql_test initmode-1.3 { {INSERT t1: rowid=99, a='hello', b=1099, c=2022, d=NULL} \ ] -do_execsql_test initmode-1.4a { +do_execsql_test imposter-1.4a { PRAGMA integrity_check; } {/NULL value in t1.d/} -do_execsql_test initmode-1.4b { +do_execsql_test imposter-1.4b { PRAGMA integrity_check; } {/row # missing from index t1b/} -do_execsql_test initmode-1.4c { +do_execsql_test imposter-1.4c { PRAGMA integrity_check; } {/row # missing from index t1c/} +# Cleanup the corruption. +# Then demonstrate that the xt1c imposter table can insert non-unique +# and NULL values into the UNIQUE index. +# +do_execsql_test imposter-2.0 { + DELETE FROM t1; + WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10) + INSERT INTO t1(a,b,c,d) SELECT i,i,i,i FROM c; + UPDATE xt1c SET c=NULL WHERE rowid=5; + PRAGMA integrity_check; +} {/row # missing from index t1c/} + +do_execsql_test imposter-2.1 { + DELETE FROM t1; + WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10) + INSERT INTO t1(a,b,c,d) SELECT i,i,i,i FROM c; + UPDATE xt1c SET c=99 WHERE rowid IN (5,7,9); + SELECT c FROM t1 ORDER BY c; +} {1 2 3 4 6 8 10 99 99 99} +do_execsql_test imposter-2.2 { + UPDATE xt1 SET c=99 WHERE rowid IN (5,7,9); + PRAGMA integrity_check; +} {/non-unique entry in index t1c/} + +# Erase the imposter tables +# +do_test imposter-3.1 { + sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 1 + db eval { + DELETE FROM t1 WHERE rowid IN (5,7,9); + PRAGMA integrity_check; + } +} {ok} + + finish_test