aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.c135
-rw-r--r--src/prepare.c2
-rw-r--r--src/sqliteInt.h3
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() */