diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 135 | ||||
-rw-r--r-- | src/prepare.c | 2 | ||||
-rw-r--r-- | src/sqliteInt.h | 3 |
3 files changed, 133 insertions, 7 deletions
diff --git a/src/build.c b/src/build.c index 58950330c..091498fc5 100644 --- a/src/build.c +++ b/src/build.c @@ -384,6 +384,7 @@ static void freeIndex(sqlite3 *db, Index *p){ #endif sqlite3ExprDelete(db, p->pPartIdxWhere); sqlite3DbFree(db, p->zColAff); + if( p->isResized ) sqlite3DbFree(db, p->azColl); sqlite3DbFree(db, p); } @@ -946,7 +947,8 @@ void sqlite3StartTable( }else #endif { - sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2); + assert( sqlite3VdbeCurrentAddr(v) < 100 ); + pParse->addrCrTab = (u16)sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2); } sqlite3OpenMasterTable(pParse, iDb); sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1); @@ -1247,6 +1249,7 @@ void sqlite3AddPrimaryKey( pTab->keyConf = (u8)onError; assert( autoInc==0 || autoInc==1 ); pTab->tabFlags |= autoInc*TF_Autoincrement; + if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder; }else if( autoInc ){ #ifndef SQLITE_OMIT_AUTOINCREMENT sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " @@ -1507,6 +1510,31 @@ static char *createTableStmt(sqlite3 *db, Table *p){ } /* +** Resize an Index object to hold N columns total. Return SQLITE_OK +** on success and SQLITE_NOMEM on an OOM error. +*/ +static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){ + char *zExtra; + int nByte; + if( pIdx->nColumn>=N ) return SQLITE_OK; + assert( pIdx->isResized==0 ); + nByte = (sizeof(char*) + sizeof(i16) + 1)*N; + zExtra = sqlite3DbMallocZero(db, nByte); + if( zExtra==0 ) return SQLITE_NOMEM; + memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); + pIdx->azColl = (char**)zExtra; + zExtra += sizeof(char*)*N; + memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn); + pIdx->aiColumn = (i16*)zExtra; + zExtra += sizeof(i16)*N; + memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn); + pIdx->aSortOrder = (u8*)zExtra; + pIdx->nColumn = N; + pIdx->isResized = 1; + return SQLITE_OK; +} + +/* ** Estimate the total row width for a table. */ static void estimateTableWidth(Table *pTab){ @@ -1535,6 +1563,101 @@ static void estimateIndexWidth(Index *pIdx){ pIdx->szIdxRow = sqlite3LogEst(wIndex*4); } +/* Return true if value x is found any of the first nCol entries of aiCol[] +*/ +static int hasColumn(const i16 *aiCol, int nCol, int x){ + while( nCol-- > 0 ) if( x==*(aiCol++) ) return 1; + return 0; +} + +/* +** The table pTab has a WITHOUT ROWID clause at the end. Go through and +** make all the changes necessary to make this a WITHOUT ROWID table. +** +** (1) Convert the OP_CreateTable into an no-op. +** (2) Make sure all table columns are part of the PRIMARY KEY +** (3) Make sure all PRIMARY KEY columns are part of all UNIQUE +** indices +*/ +static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ + Index *pIdx; + Index *pPk; + int nPk; + int i, j; + sqlite3 *db = pParse->db; + + /* Convert the OP_CreateTable opcode that would normally create the + ** root-page for the table into a OP_Null opcode. This prevents the + ** allocation of the root-page (which would never been used, as all + ** content is stored in the primary-key index instead) and it causes + ** a NULL value in the sqlite_master.rootpage field of the schema. + */ + if( pParse->addrCrTab ){ + sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrCrTab)->opcode = OP_Null; + } + + /* Locate the PRIMARY KEY index. Or, if this table was originally + ** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index. + */ + if( pTab->iPKey>=0 ){ + ExprList *pList; + pList = sqlite3ExprListAppend(pParse, 0, 0); + if( pList==0 ) return; + pList->a[0].zName = sqlite3DbStrDup(pParse->db, + pTab->aCol[pTab->iPKey].zName); + pList->a[0].sortOrder = pParse->iPkSortOrder; + assert( pParse->pNewTable==pTab ); + pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0); + if( pPk==0 ) return; + pTab->iPKey = -1; + } + for(pPk=pTab->pIndex; pPk && pPk->autoIndex<2; pPk=pPk->pNext){} + assert( pPk!=0 ); + nPk = pPk->nKeyCol; + + /* Update the in-memory representation of all UNIQUE indices by converting + ** the final rowid column into one or more columns of the PRIMARY KEY. + */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int n; + if( pIdx->autoIndex==2 ) continue; + if( pIdx->nKeyCol==pTab->nCol ){ + pIdx->nColumn = pTab->nCol; + continue; + } + for(i=n=0; i<nPk; i++){ + if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++; + } + if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return; + for(i=0, j=pIdx->nKeyCol; i<nPk; i++){ + if( !hasColumn(pIdx->aiColumn, j, pPk->aiColumn[i]) ){ + assert( j<pPk->nColumn ); + pIdx->aiColumn[j] = pPk->aiColumn[i]; + pIdx->azColl[j] = pPk->azColl[i]; + j++; + } + } + assert( pIdx->nColumn==j ); + assert( pIdx->nKeyCol+n==j ); + } + + /* Add all table columns to the PRIMARY KEY index + */ + if( nPk<pTab->nCol ){ + if( resizeIndexObject(db, pPk, pTab->nCol) ) return; + for(i=0, j=nPk; i<pTab->nCol; i++){ + if( !hasColumn(pPk->aiColumn, j, i) ){ + assert( j<pPk->nColumn ); + pPk->aiColumn[j] = i; + pPk->azColl[j] = "BINARY"; + j++; + } + } + assert( pPk->nColumn==j ); + assert( pTab->nCol==j ); + } +} + /* ** This routine is called to report the final ")" that terminates ** a CREATE TABLE statement. @@ -1578,8 +1701,10 @@ void sqlite3EndTable( if( tabOpts & TF_WithoutRowid ){ if( (p->tabFlags & TF_HasPrimaryKey)==0 ){ sqlite3ErrorMsg(pParse, "no PRIMARY KEY for table %s", p->zName); + }else{ + p->tabFlags |= TF_WithoutRowid; + convertToWithoutRowidTable(pParse, p); } - p->tabFlags |= TF_WithoutRowid; } iDb = sqlite3SchemaToIndex(db, p->pSchema); @@ -2585,7 +2710,6 @@ Index *sqlite3CreateIndex( char *zName = 0; /* Name of the index */ int nName; /* Number of characters in zName */ int i, j; - Token nullId; /* Fake token for an empty ID list */ DbFixer sFix; /* For assigning database names to pTable */ int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */ sqlite3 *db = pParse->db; @@ -2742,11 +2866,10 @@ Index *sqlite3CreateIndex( ** So create a fake list to simulate this. */ if( pList==0 ){ - nullId.z = pTab->aCol[pTab->nCol-1].zName; - nullId.n = sqlite3Strlen30((char*)nullId.z); pList = sqlite3ExprListAppend(pParse, 0, 0); if( pList==0 ) goto exit_create_index; - sqlite3ExprListSetName(pParse, pList, &nullId, 0); + pList->a[0].zName = sqlite3DbStrDup(pParse->db, + pTab->aCol[pTab->nCol-1].zName); pList->a[0].sortOrder = (u8)sortOrder; } diff --git a/src/prepare.c b/src/prepare.c index cfc9c3485..542d0bf8b 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -66,7 +66,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ assert( iDb>=0 && iDb<db->nDb ); if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ if( argv[1]==0 ){ - corruptSchema(pData, argv[0], 0); + /* NULL root page for a WITHOUT ROWID table */ }else if( argv[2] && argv[2][0] ){ /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because db->init.busy is set to 1, no VDBE code is generated diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4565e92a8..8ff95e361 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1594,6 +1594,7 @@ struct Index { unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ + unsigned isResized:1; /* True if resizeIndexObject() has been called */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ @@ -2284,6 +2285,8 @@ struct Parse { int nVar; /* Number of '?' variables seen in the SQL so far */ int nzVar; /* Number of available slots in azVar[] */ + u8 addrCrTab; /* Address of OP_CreateTable opcode */ + u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ #ifndef SQLITE_OMIT_VIRTUALTABLE u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ |