Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge the SQLITE_TESTCTRL_IMPOSTER changes from trunk. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | ota-update |
Files: | files | file ages | folders |
SHA1: |
3ed6eb2fab5d95709ef392170339e6dd |
User & Date: | drh 2015-01-30 21:00:10.049 |
Context
2015-01-31
| ||
02:34 | Merge in support for the index_xinfo pragma. (check-in: f9b6dc7702 user: drh tags: ota-update) | |
2015-01-30
| ||
21:00 | Merge the SQLITE_TESTCTRL_IMPOSTER changes from trunk. (check-in: 3ed6eb2fab user: drh tags: ota-update) | |
20:59 | Change SQLITE_TESTCTRL_INITMODE to SQLITE_TESTCTRL_IMPOSTER. Revise the order of parameters. Give it the ability to reset the schema parse table so that imposter tables can be erased. (check-in: 42d5601739 user: drh tags: trunk) | |
16:36 | Merge all recent trunk changes, and especially the SQLITE_TESTCTRL_INITMODE enhancement. (check-in: 36436dde74 user: drh tags: ota-update) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
172 173 174 175 176 177 178 179 180 181 182 183 184 185 | ** written. For index b-trees, it is the root page of the associated ** table. */ if( isIndex ){ HashElem *p; for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ Index *pIdx = (Index *)sqliteHashData(p); if( pIdx->tnum==(int)iRoot ){ iTab = pIdx->pTable->tnum; } } }else{ iTab = iRoot; } | > > > > > > | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | ** written. For index b-trees, it is the root page of the associated ** table. */ if( isIndex ){ HashElem *p; 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; } } }else{ iTab = iRoot; } |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
1727 1728 1729 1730 1731 1732 1733 | } pPk->nKeyCol = j; } pPk->isCovering = 1; assert( pPk!=0 ); nPk = pPk->nKeyCol; | | > > | | | | > | 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 | } pPk->nKeyCol = j; } pPk->isCovering = 1; assert( pPk!=0 ); nPk = pPk->nKeyCol; /* 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; i<nPk; i++){ pTab->aCol[pPk->aiColumn[i]].notNull = 1; } pPk->uniqNotNull = 1; } /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; /* Update the in-memory representation of all UNIQUE indices by converting ** the final rowid column into one or more columns of the PRIMARY KEY. */ |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
3622 3623 3624 3625 3626 3627 3628 | ** not. */ case SQLITE_TESTCTRL_ISINIT: { if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR; break; } | | | > > > > > > > > > > > > | | | > > > | 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 | ** not. */ case SQLITE_TESTCTRL_ISINIT: { if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR; break; } /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum); ** ** 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_IMPOSTER: { sqlite3 *db = va_arg(ap, sqlite3*); 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; } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } |
︙ | ︙ |
Changes to src/shell.c.
︙ | ︙ | |||
3532 3533 3534 3535 3536 3537 3538 | { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, | | | 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 | { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, { "imposter", SQLITE_TESTCTRL_IMPOSTER }, }; int testctrl = -1; int rc = 0; int i, n; open_db(p, 0); /* convert testctrl text option to value. allow any unique prefix |
︙ | ︙ | |||
3625 3626 3627 3628 3629 3630 3631 | } else { fprintf(stderr,"Error: testctrl %s takes a single char * option\n", azArg[1]); } break; #endif | | | | | 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 | } else { fprintf(stderr,"Error: testctrl %s takes a single char * option\n", azArg[1]); } break; #endif case SQLITE_TESTCTRL_IMPOSTER: if( nArg==5 ){ rc = sqlite3_test_control(testctrl, p->db, azArg[2], integerValue(azArg[3]), integerValue(azArg[4])); }else{ fprintf(stderr,"Usage: .testctrl initmode dbName onoff tnum\n"); rc = 1; } break; case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
6261 6262 6263 6264 6265 6266 6267 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 | | | 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 | #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 #define SQLITE_TESTCTRL_IMPOSTER 25 #define SQLITE_TESTCTRL_LAST 25 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 | int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct sqlite3InitInfo { /* Information used during initialization */ int newTnum; /* Rootpage of table being initialized */ u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ int nVdbeWrite; /* Number of active VDBEs that read and write */ int nVdbeExec; /* Number of nested calls to VdbeExec() */ int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ | > | 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 | int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct sqlite3InitInfo { /* Information used during initialization */ int newTnum; /* Rootpage of table being initialized */ 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 */ int nVdbeWrite; /* Number of active VDBEs that read and write */ int nVdbeExec; /* Number of nested calls to VdbeExec() */ int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
5911 5912 5913 5914 5915 5916 5917 | ){ struct Verb { const char *zName; int i; } aVerb[] = { { "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT }, { "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP }, | | | 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 | ){ struct Verb { const char *zName; int i; } aVerb[] = { { "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT }, { "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP }, { "SQLITE_TESTCTRL_IMPOSTER", SQLITE_TESTCTRL_IMPOSTER }, }; int iVerb; int iFlag; int rc; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "VERB ARGS..."); |
︙ | ︙ | |||
5953 5954 5955 5956 5957 5958 5959 | } if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[3], &val) ) return TCL_ERROR; sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, val); break; } | | | > | | | | | | 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 | } if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[3], &val) ) return TCL_ERROR; sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, val); break; } case SQLITE_TESTCTRL_IMPOSTER: { int onOff, tnum; const char *zDbName; sqlite3 *db; if( objc!=6 ){ Tcl_WrongNumArgs(interp, 2, objv, "DB dbName onOff tnum"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR; 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; } } Tcl_ResetResult(interp); return TCL_OK; } |
︙ | ︙ |
Name change from test/initmode.test to test/imposter1.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 | # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests for SQLite library. # # The focus of this file is adding extra entries in the symbol table | | | | | | | | > > | > > > > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # May you share freely, never taking more than you give. # #*********************************************************************** # # 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_IMPOSTER) and verifying that # SQLite handles those as expected. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix imposter # Create a bunch of data to sort against # do_test imposter-1.0 { execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d NOT NULL); CREATE INDEX t1b ON t1(b); CREATE UNIQUE INDEX t1c ON t1(c); WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30) 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 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 an imposter table that uses the same b-tree as t1 but which does # not have the indexes # sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $t1_root db eval {CREATE TABLE xt1(a,b,c,d)} # 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. # db eval { CREATE TEMP TABLE chnglog(desc TEXT); CREATE TEMP TRIGGER xt1_del AFTER DELETE ON xt1 BEGIN INSERT INTO chnglog VALUES( |
︙ | ︙ | |||
60 61 62 63 64 65 66 | } } {} # The xt1 table has separate xt1.rowid and xt1.a columns. The xt1.rowid # column corresponds to t1.rowid and t1.a, but the xt1.a column is always # NULL # | | | | | | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | } } {} # The xt1 table has separate xt1.rowid and xt1.a columns. The xt1.rowid # column corresponds to t1.rowid and t1.a, but the xt1.a column is always # NULL # do_execsql_test imposter-1.1 { SELECT rowid FROM xt1 WHERE a IS NOT NULL; } {} 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; } {} # Make changes via the xt1 shadow table. This will not update the # indexes on t1 nor check the uniqueness constraint on t1.c nor check # the NOT NULL constraint on t1.d, resulting in a logically inconsistent # database. # 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; } [list \ {DELETE t1: rowid=5, a=NULL, b=1005, c=2005, d=3005} \ {INSERT t1: rowid=99, a='hello', b=1099, c=2022, d=NULL} \ ] do_execsql_test imposter-1.4a { PRAGMA integrity_check; } {/NULL value in t1.d/} do_execsql_test imposter-1.4b { PRAGMA integrity_check; } {/row # missing from index t1b/} 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 |