aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c6
-rw-r--r--src/build.c11
-rw-r--r--src/main.c25
-rw-r--r--src/shell.c8
-rw-r--r--src/sqlite.h.in2
-rw-r--r--src/sqliteInt.h1
-rw-r--r--src/test1.c17
7 files changed, 48 insertions, 22 deletions
diff --git a/src/btree.c b/src/btree.c
index f9f76c2eb..eb5151351 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -175,6 +175,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 f02989bff..7e3ce1b76 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; i<nPk; i++){
- pTab->aCol[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; i<nPk; i++){
+ pTab->aCol[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 0d6f1be24..11585e7dc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3599,15 +3599,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 1a191e0fd..3130f4c6d 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 722b9235a..f256cea45 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -6260,7 +6260,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 379456d5f..0bc6f679d 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 b9503cf39..a87fcd859 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;
}
}