Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -54,11 +54,11 @@ bitvec.o build.o \ callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \ fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o \ fts3_write.o func.o global.o hash.o \ - icu.o insert.o kvmem.o legacy.o \ + icu.o insert.o kvlsm.o kvmem.o legacy.o \ main.o malloc.o math.o mem0.o mem1.o mem2.o mem3.o mem5.o \ mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \ opcodes.o os.o os_os2.o os_unix.o os_win.o \ parse.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o status.o storage.o \ @@ -91,10 +91,11 @@ $(TOP)/src/global.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ + $(TOP)/src/kvlsm.c \ $(TOP)/src/kvmem.c \ $(TOP)/src/legacy.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ $(TOP)/src/math.c \ @@ -211,10 +212,11 @@ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_rtree.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_storage.c \ + $(TOP)/src/test_storage2.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c @@ -458,14 +460,20 @@ # Rules to build the 'testfixture' application. # TESTFIXTURE_FLAGS = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE -testfixture$(EXE): $(TESTSRC2) libsqlite4.a $(TESTSRC) $(TOP)/src/tclsqlite.c +TESTFIXTURE_PREREQ = $(TESTSRC) $(TESTSRC2) +TESTFIXTURE_PREREQ += $(TOP)/src/tclsqlite.c +TESTFIXTURE_PREREQ += libsqlite4.a +TESTFIXTURE_PREREQ += $(LIBEXTRA) + +testfixture$(EXE): $(TESTFIXTURE_PREREQ) $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ - -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) libsqlite4.a + -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) libsqlite4.a \ + $(LIBEXTRA) amalgamation-testfixture$(EXE): sqlite4.c $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite4.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) Index: src/auth.c ================================================================== --- src/auth.c +++ src/auth.c @@ -168,13 +168,10 @@ if( NEVER(pTab==0) ) return; if( iCol>=0 ){ assert( iColnCol ); zCol = pTab->aCol[iCol].zName; - }else if( pTab->iPKey>=0 ){ - assert( pTab->iPKeynCol ); - zCol = pTab->aCol[pTab->iPKey].zName; }else{ zCol = "ROWID"; } assert( iDb>=0 && iDbnDb ); if( SQLITE_IGNORE==sqlite4AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -213,21 +213,30 @@ } /* ** Find an available table number for database iDb */ -static int firstAvailableTableNumber(sqlite4 *db, int iDb){ +static int firstAvailableTableNumber( + sqlite4 *db, /* Database handle */ + int iDb, /* Index of database in db->aDb[] */ + Table *pTab /* New table being constructed */ +){ HashElem *i; int maxTab = 1; - for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); i;i=sqliteHashNext(i)){ - Table *pTab = (Table*)sqliteHashData(i); - if( pTab->tnum > maxTab ) maxTab = pTab->tnum; - } + for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash); i;i=sqliteHashNext(i)){ Index *pIdx = (Index*)sqliteHashData(i); if( pIdx->tnum > maxTab ) maxTab = pIdx->tnum; } + + if( pTab ){ + Index *pIdx; + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->tnum > maxTab ) maxTab = pIdx->tnum; + } + } + return maxTab+1; } /* ** Run the parser and code generator recursively in order to generate @@ -834,11 +843,10 @@ pParse->rc = SQLITE_NOMEM; pParse->nErr++; goto begin_table_error; } pTable->zName = zName; - pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; pTable->nRef = 1; pTable->nRowEst = 1000000; assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; @@ -860,11 +868,11 @@ ** indices to be created and the table record must come before the ** indices. Hence, the record number for the table must be allocated ** now. */ if( !db->init.busy && (v = sqlite4GetVdbe(pParse))!=0 ){ - int reg1, reg2, reg3; + int reg1, reg3; sqlite4BeginWriteOperation(pParse, 0, iDb); #ifndef SQLITE_OMIT_VIRTUALTABLE if( isVirtual ){ sqlite4VdbeAddOp0(v, OP_VBegin); @@ -879,21 +887,22 @@ ** The root page number of the new table is left in reg pParse->regRoot. ** The rowid and root page number values are needed by the code that ** sqlite4EndTable will generate. */ reg1 = pParse->regRowid = ++pParse->nMem; - reg2 = pParse->regRoot = ++pParse->nMem; reg3 = ++pParse->nMem; +#if 0 #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) if( isView || isVirtual ){ sqlite4VdbeAddOp2(v, OP_Integer, 0, reg2); }else #endif { int tnum = firstAvailableTableNumber(db, iDb); sqlite4VdbeAddOp2(v, OP_Integer, tnum, reg2); } +#endif sqlite4OpenMasterTable(pParse, iDb); sqlite4VdbeAddOp2(v, OP_NewRowid, 0, reg1); sqlite4VdbeAddOp2(v, OP_Null, 0, reg3); sqlite4VdbeAddOp3(v, OP_Insert, 0, reg3, reg1); sqlite4VdbeChangeP5(v, OPFLAG_APPEND); @@ -1127,11 +1136,13 @@ int onError, /* What to do with a uniqueness conflict */ int autoInc, /* True if the AUTOINCREMENT keyword is present */ int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */ ){ Table *pTab = pParse->pNewTable; +#if 0 char *zType = 0; +#endif int iCol = -1, i; if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit; if( pTab->tabFlags & TF_HasPrimaryKey ){ sqlite4ErrorMsg(pParse, "table \"%s\" has more than one primary key", pTab->zName); @@ -1139,26 +1150,31 @@ } pTab->tabFlags |= TF_HasPrimaryKey; if( pList==0 ){ iCol = pTab->nCol - 1; pTab->aCol[iCol].isPrimKey = 1; + pTab->aCol[iCol].notNull = 1; }else{ for(i=0; inExpr; i++){ for(iCol=0; iColnCol; iCol++){ if( sqlite4StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){ break; } } if( iColnCol ){ pTab->aCol[iCol].isPrimKey = 1; + pTab->aCol[iCol].notNull = 1; } } if( pList->nExpr>1 ) iCol = -1; } + +#if 0 if( iCol>=0 && iColnCol ){ zType = pTab->aCol[iCol].zType; } + if( zType && sqlite4StrICmp(zType, "INTEGER")==0 && sortOrder==SQLITE_SO_ASC ){ pTab->iPKey = iCol; pTab->keyConf = (u8)onError; assert( autoInc==0 || autoInc==1 ); @@ -1166,16 +1182,18 @@ }else if( autoInc ){ #ifndef SQLITE_OMIT_AUTOINCREMENT sqlite4ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " "INTEGER PRIMARY KEY"); #endif - }else{ + }else +#endif + + { Index *p; - p = sqlite4CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); - if( p ){ - p->autoIndex = 2; - } + p = sqlite4CreateIndex( + pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0, 1 + ); pList = 0; } primary_key_exit: sqlite4ExprListDelete(pParse->db, pList); @@ -1417,10 +1435,109 @@ assert( k<=n ); } sqlite4_snprintf(n-k, &zStmt[k], "%s", zEnd); return zStmt; } + +static Index *newIndex( + Parse *pParse, /* Parse context for current statement */ + Table *pTab, /* Table index is created on */ + const char *zName, /* Name of index object to create */ + int nCol, /* Number of columns in index */ + int onError, /* One of OE_Abort, OE_Replace etc. */ + int nExtra, /* Bytes of extra space to allocate */ + char **pzExtra /* OUT: Pointer to extra space */ +){ + sqlite4 *db = pParse->db; /* Database handle */ + Index *pIndex; /* Return value */ + char *zExtra = 0; /* nExtra bytes of extra space allocated */ + int nName; /* Length of zName in bytes */ + + /* Allocate the index structure. */ + nName = sqlite4Strlen30(zName); + pIndex = sqlite4DbMallocZero(db, + ROUND8(sizeof(Index)) + /* Index structure */ + ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */ + sizeof(char *)*nCol + /* Index.azColl */ + sizeof(int)*nCol + /* Index.aiColumn */ + sizeof(u8)*nCol + /* Index.aSortOrder */ + nName + 1 + /* Index.zName */ + nExtra /* Collation sequence names */ + ); + assert( pIndex || db->mallocFailed ); + + if( pIndex ){ + zExtra = (char*)pIndex; + pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))]; + pIndex->azColl = (char**) + ((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1)); + assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) ); + assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) ); + pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]); + pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]); + pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]); + zExtra = (char *)(&pIndex->zName[nName+1]); + memcpy(pIndex->zName, zName, nName+1); + pIndex->pTable = pTab; + pIndex->nColumn = nCol; + pIndex->onError = (u8)onError; + pIndex->pSchema = pTab->pSchema; + + if( db->init.busy ){ + Hash *pIdxHash = &pIndex->pSchema->idxHash; + Index *p; + + p = sqlite4HashInsert(pIdxHash, pIndex->zName, nName, pIndex); + if( p ){ + assert( p==pIndex ); + db->mallocFailed = 1; + sqlite4DbFree(db, pIndex); + pIndex = 0; + } + } + } + + *pzExtra = zExtra; + return pIndex; +} + + +/* +** Allocate and populate an Index structure representing an implicit +** primary key. In implicit primary key behaves similarly to the built-in +** INTEGER PRIMARY KEY columns in SQLite 3. +*/ +static void addImplicitPrimaryKey( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table to add implicit PRIMARY KEY to */ + int iDb +){ + Index *pIndex; /* New index */ + char *zExtra; + + assert( !pTab->pIndex || pTab->pIndex->eIndexType!=SQLITE_INDEX_PRIMARYKEY ); + assert( sqlite4Strlen30("binary")==6 ); + pIndex = newIndex(pParse, pTab, pTab->zName, 1, OE_Abort, 1+6, &zExtra); + if( pIndex ){ + sqlite4 *db = pParse->db; + + pIndex->aiColumn[0] = -1; + pIndex->azColl[0] = zExtra; + memcpy(zExtra, "binary", 7); + pIndex->eIndexType = SQLITE_INDEX_PRIMARYKEY; + pIndex->pNext = pTab->pIndex; + pTab->pIndex = pIndex; + sqlite4DefaultRowEst(pIndex); + pTab->tabFlags |= TF_HasPrimaryKey; + + if( db->init.busy ){ + pIndex->tnum = db->init.newTnum; + }else{ + pIndex->tnum = firstAvailableTableNumber(db, iDb, pTab); + } + } +} /* ** This routine is called to report the final ")" that terminates ** a CREATE TABLE statement. ** @@ -1447,20 +1564,33 @@ Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; sqlite4 *db = pParse->db; int iDb; + int iPkRoot = 0; /* Root page of primary key index */ if( (pEnd==0 && pSelect==0) || db->mallocFailed ){ return; } p = pParse->pNewTable; if( p==0 ) return; assert( !db->init.busy || !pSelect ); - iDb = sqlite4SchemaToIndex(db, p->pSchema); + + if( !IsView(p) ){ + Index *pPk; /* PRIMARY KEY index of table p */ + if( 0==(p->tabFlags & TF_HasPrimaryKey) ){ + /* If no explicit PRIMARY KEY has been created, add an implicit + ** primary key here. An implicit primary key works the way "rowid" + ** did in SQLite 3. */ + addImplicitPrimaryKey(pParse, p, iDb); + } + pPk = sqlite4FindPrimaryKey(p, 0); + assert( pPk || pParse->nErr || db->mallocFailed ); + if( pPk ) iPkRoot = pPk->tnum; + } #ifndef SQLITE_OMIT_CHECK /* Resolve names in all CHECK constraint expressions. */ if( p->pCheck ){ @@ -1480,20 +1610,10 @@ return; } } #endif /* !defined(SQLITE_OMIT_CHECK) */ - /* If the db->init.busy is 1 it means we are reading the SQL off the - ** "sqlite_master" or "sqlite_temp_master" table on the disk. - ** So do not write to the disk again. Extract the root page number - ** for the table from the db->init.newTnum field. (The page number - ** should have been put there by the sqliteOpenCb routine.) - */ - if( db->init.busy ){ - p->tnum = db->init.newTnum; - } - /* If not initializing, then create a record for the new table ** in the SQLITE_MASTER table of the database. ** ** If this is a TEMPORARY table, write the entry into the auxiliary ** file instead of into the main database file. @@ -1541,12 +1661,11 @@ if( pSelect ){ SelectDest dest; Table *pSelTab; assert(pParse->nTab==1); - sqlite4VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); - sqlite4VdbeChangeP5(v, 1); + sqlite4VdbeAddOp3(v, OP_OpenWrite, 1, iPkRoot, iDb); pParse->nTab = 2; sqlite4SelectDestInit(&dest, SRT_Table, 1); sqlite4Select(pParse, pSelect, &dest); sqlite4VdbeAddOp1(v, OP_Close, 1); if( pParse->nErr==0 ){ @@ -1575,17 +1694,17 @@ ** SQLITE_MASTER table. We just need to update that slot with all ** the information we've collected. */ sqlite4NestedParse(pParse, "UPDATE %Q.%s " - "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " + "SET type='%s', name=%Q, tbl_name=%Q, rootpage=%d, sql=%Q " "WHERE rowid=#%d", db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zType, p->zName, p->zName, - pParse->regRoot, + iPkRoot, zStmt, pParse->regRowid ); sqlite4DbFree(db, zStmt); sqlite4ChangeCookie(pParse, iDb); @@ -1843,11 +1962,13 @@ ** erasing iTable (this can happen with an auto-vacuum database). */ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ Vdbe *v = sqlite4GetVdbe(pParse); sqlite4VdbeAddOp2(v, OP_Clear, iTable, iDb); +#if 0 sqlite4MayAbort(pParse); +#endif } /* ** Write VDBE code to erase table pTab and all associated indices on disk. ** Code to update the sqlite_master tables and internal schema definitions @@ -1855,11 +1976,10 @@ ** is also added (this can happen with an auto-vacuum database). */ static void destroyTable(Parse *pParse, Table *pTab){ Index *pIdx; int iDb = sqlite4SchemaToIndex(pParse->db, pTab->pSchema); - destroyRootPage(pParse, pTab->tnum, iDb); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ destroyRootPage(pParse, pIdx->tnum, iDb); } } @@ -2209,110 +2329,67 @@ /* ** Generate code that will erase and refill index *pIdx. This is ** used to initialize a newly created index or to recompute the ** content of an index in response to a REINDEX command. */ -static void sqlite4RefillIndex(Parse *pParse, Index *pIndex){ - Table *pTab = pIndex->pTable; /* The table that is indexed */ - int iTab = pParse->nTab++; /* Cursor used for pTab */ - int iIdx = pParse->nTab++; /* Cursor used for pIndex */ +static void sqlite4RefillIndex(Parse *pParse, Index *pIdx){ + Table *pTab = pIdx->pTable; /* The table that is indexed */ + int iTab = pParse->nTab++; /* Cursor used for PK of pTab */ + int iIdx = pParse->nTab++; /* Cursor used for pIdx */ int iSorter; /* Cursor opened by OpenSorter (if in use) */ int addr1; /* Address of top of loop */ int addr2; /* Address to jump to for next iteration */ int tnum; /* Root page of index */ Vdbe *v; /* Generate code into this virtual machine */ - KeyInfo *pKey; /* KeyInfo for index */ -#ifdef SQLITE_OMIT_MERGE_SORT - int regIdxKey; /* Registers containing the index key */ -#endif + int regKey; /* Registers containing the index key */ int regRecord; /* Register holding assemblied index record */ sqlite4 *db = pParse->db; /* The database connection */ - int iDb = sqlite4SchemaToIndex(db, pIndex->pSchema); + int iDb = sqlite4SchemaToIndex(db, pIdx->pSchema); + Index *pPk; #ifndef SQLITE_OMIT_AUTHORIZATION - if( sqlite4AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0, + if( sqlite4AuthCheck(pParse, SQLITE_REINDEX, pIdx->zName, 0, db->aDb[iDb].zName ) ){ return; } #endif - /* Require a write-lock on the table to perform this operation */ - sqlite4TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); - + pPk = sqlite4FindPrimaryKey(pTab, 0); v = sqlite4GetVdbe(pParse); if( v==0 ) return; - tnum = pIndex->tnum; - sqlite4VdbeAddOp2(v, OP_Clear, tnum, iDb); - pKey = sqlite4IndexKeyinfo(pParse, pIndex); - sqlite4VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, - (char *)pKey, P4_KEYINFO_HANDOFF); - -#ifndef SQLITE_OMIT_MERGE_SORT - /* Open the sorter cursor if we are to use one. */ - iSorter = pParse->nTab++; - sqlite4VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO); -#else - iSorter = iTab; -#endif - - /* Open the table. Loop through all rows of the table, inserting index - ** records into the sorter. */ - sqlite4OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); + + /* A write-lock on the table is required to perform this operation. Easiest + ** way to do this is to open a write-cursor on the PK - even though this + ** operation only requires read access. */ + sqlite4OpenPrimaryKey(pParse, iTab, iDb, pTab, OP_OpenWrite); + + /* Delete the current contents (if any) of the index. Then open a write + ** cursor on it. */ + sqlite4VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); + sqlite4OpenIndex(pParse, iIdx, iDb, pIdx, OP_OpenWrite); + + /* Loop through the contents of the PK index. At each row, insert the + ** corresponding entry into the auxiliary index. */ addr1 = sqlite4VdbeAddOp2(v, OP_Rewind, iTab, 0); regRecord = sqlite4GetTempRange(pParse,2); - -#ifndef SQLITE_OMIT_MERGE_SORT - sqlite4GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1, iIdx); - sqlite4VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); + regKey = sqlite4GetTempReg(pParse); + sqlite4EncodeIndexKey(pParse, pPk, iTab, pIdx, iIdx, regKey); + if( pIdx->onError!=OE_None ){ + const char *zErr = "indexed columns are not unique"; + int addrTest; + + addrTest = sqlite4VdbeAddOp4Int(v, OP_IsUnique, iIdx, 0, regKey, 0); + sqlite4HaltConstraint(pParse, OE_Abort, zErr, P4_STATIC); + sqlite4VdbeJumpHere(v, addrTest); + } + sqlite4VdbeAddOp3(v, OP_IdxInsert, iIdx, 0, regKey); sqlite4VdbeAddOp2(v, OP_Next, iTab, addr1+1); sqlite4VdbeJumpHere(v, addr1); - addr1 = sqlite4VdbeAddOp2(v, OP_SorterSort, iSorter, 0); - if( pIndex->onError!=OE_None ){ - int j2 = sqlite4VdbeCurrentAddr(v) + 3; - sqlite4VdbeAddOp2(v, OP_Goto, 0, j2); - addr2 = sqlite4VdbeCurrentAddr(v); - sqlite4VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord); - sqlite4HaltConstraint( - pParse, OE_Abort, "indexed columns are not unique", P4_STATIC - ); - }else{ - addr2 = sqlite4VdbeCurrentAddr(v); - } - sqlite4VdbeAddOp2(v, OP_SorterData, iSorter, regRecord); - sqlite4VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord); ***** busted - sqlite4VdbeChangeP5(v, OPFLAG_USESEEKRESULT | OPFLAG_APPENDBIAS); -#else - regIdxKey = sqlite4GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1, iIdx); - addr2 = addr1 + 1; - if( pIndex->onError!=OE_None ){ - const int regRowid = regIdxKey + pIndex->nColumn; - const int j2 = sqlite4VdbeCurrentAddr(v) + 2; - void * const pRegKey = SQLITE_INT_TO_PTR(regIdxKey); - - /* The registers accessed by the OP_IsUnique opcode were allocated - ** using sqlite4GetTempRange() inside of the sqlite4GenerateIndexKey() - ** call above. Just before that function was freed they were released - ** (made available to the compiler for reuse) using - ** sqlite4ReleaseTempRange(). So in some ways having the OP_IsUnique - ** opcode use the values stored within seems dangerous. However, since - ** we can be sure that no other temp registers have been allocated - ** since sqlite4ReleaseTempRange() was called, it is safe to do so. - */ - sqlite4VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32); - sqlite4HaltConstraint( - pParse, OE_Abort, "indexed columns are not unique", P4_STATIC); - } - sqlite4VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, regRecord+1); - sqlite4VdbeChangeP5(v, OPFLAG_USESEEKRESULT); -#endif - sqlite4ReleaseTempRange(pParse, regRecord, 2); - sqlite4VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); - sqlite4VdbeJumpHere(v, addr1); + sqlite4ReleaseTempReg(pParse, regKey); sqlite4VdbeAddOp1(v, OP_Close, iTab); sqlite4VdbeAddOp1(v, OP_Close, iIdx); - sqlite4VdbeAddOp1(v, OP_Close, iSorter); } /* ** Create a new index for an SQL table. pName1.pName2 is the name of the index ** and pTblList is the name of the table that is to be indexed. Both will @@ -2337,27 +2414,26 @@ ExprList *pList, /* A list of columns to be indexed */ int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ Token *pStart, /* The CREATE token that begins this statement */ Token *pEnd, /* The ")" that closes the CREATE INDEX statement */ int sortOrder, /* Sort order of primary key when pList==NULL */ - int ifNotExist /* Omit error if index already exists */ + int ifNotExist, /* Omit error if index already exists */ + int bPrimaryKey /* True to create the tables primary key */ ){ Index *pRet = 0; /* Pointer to return */ Table *pTab = 0; /* Table to be indexed */ Index *pIndex = 0; /* The index to be created */ 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. */ sqlite4 *db = pParse->db; Db *pDb; /* The specific table containing the indexed database */ int iDb; /* Index of the database that is being written */ Token *pName = 0; /* Unqualified name of the index to create */ struct ExprList_item *pListItem; /* For looping over pList */ - int nCol; int nExtra = 0; char *zExtra; assert( pStart==0 || pEnd!=0 ); /* pEnd must be non-NULL if pStart is */ assert( pParse->nErr==0 ); /* Never called with prior errors */ @@ -2375,10 +2451,11 @@ /* Use the two-part index name to determine the database ** to search for the table. 'Fix' the table name to this db ** before looking up the table. */ + assert( !bPrimaryKey ); assert( pName1 && pName2 ); iDb = sqlite4TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) goto exit_create_index; assert( pName && pName->z ); @@ -2415,23 +2492,31 @@ } pDb = &db->aDb[iDb]; assert( pTab!=0 ); assert( pParse->nErr==0 ); + + /* TODO: We will need to reinstate this block when sqlite_master is + ** modified to use an implicit primary key. */ +#if 0 if( sqlite4StrNICmp(pTab->zName, "sqlite_", 7)==0 && memcmp(&pTab->zName[7],"altertab_",9)!=0 ){ sqlite4ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); goto exit_create_index; } +#endif + #ifndef SQLITE_OMIT_VIEW if( pTab->pSelect ){ + assert( !bPrimaryKey ); sqlite4ErrorMsg(pParse, "views may not be indexed"); goto exit_create_index; } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ + assert( !bPrimaryKey ); sqlite4ErrorMsg(pParse, "virtual tables may not be indexed"); goto exit_create_index; } #endif @@ -2447,10 +2532,11 @@ ** If pName==0 it means that we are ** dealing with a primary key or UNIQUE constraint. We have to invent our ** own name. */ if( pName ){ + assert( !bPrimaryKey ); zName = sqlite4NameFromToken(db, pName); if( zName==0 ) goto exit_create_index; assert( pName->z!=0 ); if( SQLITE_OK!=sqlite4CheckObjectName(pParse, zName) ){ goto exit_create_index; @@ -2468,24 +2554,26 @@ assert( !db->init.busy ); sqlite4CodeVerifySchema(pParse, iDb); } goto exit_create_index; } - }else{ + }else if( !bPrimaryKey ){ int n; Index *pLoop; for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){} zName = sqlite4MPrintf(db, "sqlite_autoindex_%s_%d", pTab->zName, n); - if( zName==0 ){ - goto exit_create_index; - } + }else{ + zName = sqlite4MPrintf(db, "%s", pTab->zName); + } + if( zName==0 ){ + goto exit_create_index; } /* Check for authorization to create an index. */ #ifndef SQLITE_OMIT_AUTHORIZATION - { + if( bPrimaryKey==0 ){ const char *zDb = pDb->zName; if( sqlite4AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){ goto exit_create_index; } i = SQLITE_CREATE_INDEX; @@ -2494,13 +2582,16 @@ goto exit_create_index; } } #endif - /* If pList==0, it means this routine was called to make a primary - ** key out of the last column added to the table under construction. - ** So create a fake list to simulate this. + /* If pList==0, it means this routine was called as a result of a PRIMARY + ** KEY or UNIQUE constraint attached to the last column added to the table + ** under construction. So create a fake list to simulate this. + ** + ** TODO: This 'fake list' could be created by the caller to reduce the + ** number of parameters passed to this function. */ if( pList==0 ){ nullId.z = pTab->aCol[pTab->nCol-1].zName; nullId.n = sqlite4Strlen30((char*)nullId.z); pList = sqlite4ExprListAppend(pParse, 0, 0); @@ -2522,43 +2613,22 @@ nExtra += (1 + sqlite4Strlen30(pColl->zName)); } } } - /* - ** Allocate the index structure. - */ - nName = sqlite4Strlen30(zName); - nCol = pList->nExpr; - pIndex = sqlite4DbMallocZero(db, - ROUND8(sizeof(Index)) + /* Index structure */ - ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */ - sizeof(char *)*nCol + /* Index.azColl */ - sizeof(int)*nCol + /* Index.aiColumn */ - sizeof(u8)*nCol + /* Index.aSortOrder */ - nName + 1 + /* Index.zName */ - nExtra /* Collation sequence names */ - ); - if( db->mallocFailed ){ - goto exit_create_index; - } - zExtra = (char*)pIndex; - pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))]; - pIndex->azColl = (char**) - ((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1)); - assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) ); - assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) ); - pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]); - pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]); - pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]); - zExtra = (char *)(&pIndex->zName[nName+1]); - memcpy(pIndex->zName, zName, nName+1); - pIndex->pTable = pTab; - pIndex->nColumn = pList->nExpr; - pIndex->onError = (u8)onError; - pIndex->autoIndex = (u8)(pName==0); - pIndex->pSchema = db->aDb[iDb].pSchema; + /* Allocate the new Index structure. */ + pIndex = newIndex(pParse, pTab, zName, pList->nExpr, onError, nExtra,&zExtra); + if( !pIndex ) goto exit_create_index; + + assert( pIndex->eIndexType==SQLITE_INDEX_USER ); + if( pName==0 ){ + if( bPrimaryKey ){ + pIndex->eIndexType = SQLITE_INDEX_PRIMARYKEY; + }else{ + pIndex->eIndexType = SQLITE_INDEX_UNIQUE; + } + } /* Check to see if we should honor DESC requests on index columns */ if( pDb->pSchema->file_format>=4 ){ sortOrderMask = -1; /* Honor DESC */ @@ -2645,11 +2715,11 @@ */ Index *pIdx; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int k; assert( pIdx->onError!=OE_None ); - assert( pIdx->autoIndex ); + assert( pIdx->eIndexType!=SQLITE_INDEX_USER ); assert( pIndex->onError!=OE_None ); if( pIdx->nColumn!=pIndex->nColumn ) continue; for(k=0; knColumn; k++){ const char *z1; @@ -2683,21 +2753,12 @@ /* Link the new Index structure to its table and to the other ** in-memory database structures. */ if( db->init.busy ){ - Index *p; - p = sqlite4HashInsert(&pIndex->pSchema->idxHash, - pIndex->zName, sqlite4Strlen30(pIndex->zName), - pIndex); - if( p ){ - assert( p==pIndex ); /* Malloc must have failed */ - db->mallocFailed = 1; - goto exit_create_index; - } db->flags |= SQLITE_InternChanges; - if( pTblName!=0 ){ + if( pTblName!=0 || bPrimaryKey ){ pIndex->tnum = db->init.newTnum; } } /* If the db->init.busy is 0 then create the index on disk. This @@ -2713,60 +2774,62 @@ ** If pTblName==0 it means this index is generated as a primary key ** or UNIQUE constraint of a CREATE TABLE statement. Since the table ** has just been created, it contains no data and the index initialization ** step can be skipped. */ - else{ /* if( db->init.busy==0 ) */ - Vdbe *v; - char *zStmt; - - v = sqlite4GetVdbe(pParse); - if( v==0 ) goto exit_create_index; - - - /* Create the rootpage for the index - */ - sqlite4BeginWriteOperation(pParse, 1, iDb); - pIndex->tnum = firstAvailableTableNumber(db, iDb); - - /* Gather the complete text of the CREATE INDEX statement into - ** the zStmt variable - */ - if( pStart ){ - assert( pEnd!=0 ); - /* A named index with an explicit CREATE INDEX statement */ - zStmt = sqlite4MPrintf(db, "CREATE%s INDEX %.*s", - onError==OE_None ? "" : " UNIQUE", - (int)(pEnd->z - pName->z) + 1, - pName->z); - }else{ - /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ - /* zStmt = sqlite4MPrintf(""); */ - zStmt = 0; - } - - /* Add an entry in sqlite_master for this index - */ - sqlite4NestedParse(pParse, - "INSERT INTO %Q.%s VALUES('index',%Q,%Q,%d,%Q);", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), - pIndex->zName, - pTab->zName, - pIndex->tnum, - zStmt - ); - sqlite4DbFree(db, zStmt); - - /* Fill the index with data and reparse the schema. Code an OP_Expire - ** to invalidate all pre-compiled statements. - */ - if( pTblName ){ - sqlite4RefillIndex(pParse, pIndex); - sqlite4ChangeCookie(pParse, iDb); - sqlite4VdbeAddParseSchemaOp(v, iDb, - sqlite4MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); - sqlite4VdbeAddOp1(v, OP_Expire, 0); + else{ + pIndex->tnum = firstAvailableTableNumber(db, iDb, pTab); + if( bPrimaryKey==0 ){ + Vdbe *v; + char *zStmt; + + v = sqlite4GetVdbe(pParse); + if( v==0 ) goto exit_create_index; + + /* Create the rootpage for the index + */ + sqlite4BeginWriteOperation(pParse, 1, iDb); + pIndex->tnum = firstAvailableTableNumber(db, iDb, pTab); + + /* Gather the complete text of the CREATE INDEX statement into + ** the zStmt variable + */ + if( pStart ){ + assert( pEnd!=0 ); + /* A named index with an explicit CREATE INDEX statement */ + zStmt = sqlite4MPrintf(db, "CREATE%s INDEX %.*s", + onError==OE_None ? "" : " UNIQUE", + (int)(pEnd->z - pName->z) + 1, + pName->z); + }else{ + /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ + /* zStmt = sqlite4MPrintf(""); */ + zStmt = 0; + } + + /* Add an entry in sqlite_master for this index + */ + sqlite4NestedParse(pParse, + "INSERT INTO %Q.%s VALUES('index',%Q,%Q,%d,%Q);", + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), + pIndex->zName, + pTab->zName, + pIndex->tnum, + zStmt + ); + sqlite4DbFree(db, zStmt); + + /* Fill the index with data and reparse the schema. Code an OP_Expire + ** to invalidate all pre-compiled statements. + */ + if( pTblName ){ + sqlite4RefillIndex(pParse, pIndex); + sqlite4ChangeCookie(pParse, iDb); + sqlite4VdbeAddParseSchemaOp(v, iDb, + sqlite4MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); + sqlite4VdbeAddOp1(v, OP_Expire, 0); + } } } /* When adding an index to the list of indices for a table, make ** sure all indices labeled OE_Replace come after all those labeled @@ -2811,11 +2874,11 @@ ** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the ** number of rows in the table that match any particular value of the ** first column of the index. aiRowEst[2] is an estimate of the number ** of rows that match any particular combiniation of the first 2 columns ** of the index. And so forth. It must always be the case that -* +** ** aiRowEst[N]<=aiRowEst[N-1] ** aiRowEst[N]>=1 ** ** Apart from that, we have little to go on besides intuition as to ** how aiRowEst[] should be initialized. The numbers generated here @@ -2864,11 +2927,11 @@ sqlite4CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); } pParse->checkSchema = 1; goto exit_drop_index; } - if( pIndex->autoIndex ){ + if( pIndex->eIndexType!=SQLITE_INDEX_USER ){ sqlite4ErrorMsg(pParse, "index associated with UNIQUE " "or PRIMARY KEY constraint cannot be dropped", 0); goto exit_drop_index; } iDb = sqlite4SchemaToIndex(db, pIndex->pSchema); @@ -3653,30 +3716,55 @@ ** pointer. If an error occurs (out of memory or missing collation ** sequence), NULL is returned and the state of pParse updated to reflect ** the error. */ KeyInfo *sqlite4IndexKeyinfo(Parse *pParse, Index *pIdx){ + Index *pPk; /* Primary key index on same table */ int i; - int nCol = pIdx->nColumn; - int nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol; + int nCol; + int nBytes; sqlite4 *db = pParse->db; - KeyInfo *pKey = (KeyInfo *)sqlite4DbMallocZero(db, nBytes); + KeyInfo *pKey; + + if( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){ + pPk = 0; + }else{ + pPk = sqlite4FindPrimaryKey(pIdx->pTable, 0); + } + nCol = pIdx->nColumn + (pPk ? pPk->nColumn : 0); + nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol; + pKey = (KeyInfo *)sqlite4DbMallocZero(db, nBytes); if( pKey ){ pKey->db = pParse->db; pKey->aSortOrder = (u8 *)&(pKey->aColl[nCol]); assert( &pKey->aSortOrder[nCol]==&(((u8 *)pKey)[nBytes]) ); - for(i=0; inColumn; i++){ char *zColl = pIdx->azColl[i]; assert( zColl ); pKey->aColl[i] = sqlite4LocateCollSeq(pParse, zColl); pKey->aSortOrder[i] = pIdx->aSortOrder[i]; } + if( pPk ){ + for(i=0; inColumn; i++){ + char *zColl = pIdx->azColl[i]; + assert( zColl ); + pKey->aColl[i+pIdx->nColumn] = sqlite4LocateCollSeq(pParse, zColl); + pKey->aSortOrder[i+pIdx->nColumn] = pPk->aSortOrder[i]; + } + } + pKey->nField = (u16)nCol; + if( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){ + pKey->nData = pIdx->pTable->nCol; + }else{ + pKey->nPK = pPk->nColumn; + } } if( pParse->nErr ){ sqlite4DbFree(db, pKey); pKey = 0; } return pKey; } Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -24,11 +24,10 @@ ** ** The following fields are initialized appropriate in pSrc: ** ** pSrc->a[0].pTab Pointer to the Table object ** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one -** */ Table *sqlite4SrcListLookup(Parse *pParse, SrcList *pSrc){ struct SrcList_item *pItem = pSrc->a; Table *pTab; assert( pItem && pSrc->nSrc==1 ); @@ -222,139 +221,100 @@ void sqlite4DeleteFrom( Parse *pParse, /* The parser context */ SrcList *pTabList, /* The table from which we should delete things */ Expr *pWhere /* The WHERE clause. May be null */ ){ - Vdbe *v; /* The virtual database engine */ - Table *pTab; /* The table from which records will be deleted */ - const char *zDb; /* Name of database holding pTab */ - int end, addr = 0; /* A couple addresses of generated code */ - int i; /* Loop counter */ - WhereInfo *pWInfo; /* Information about the WHERE clause */ - Index *pIdx; /* For looping over indices of the table */ - int iCur; /* VDBE Cursor number for pTab */ - sqlite4 *db; /* Main database structure */ - AuthContext sContext; /* Authorization context */ - NameContext sNC; /* Name context to resolve expressions in */ - int iDb; /* Database number */ - int memCnt = -1; /* Memory cell used for change counting */ - int rcauth; /* Value returned by authorization callback */ - -#ifndef SQLITE_OMIT_TRIGGER - int isView; /* True if attempting to delete from a view */ - Trigger *pTrigger; /* List of table triggers, if required */ -#endif + sqlite4 *db = pParse->db; /* Main database structure */ + Vdbe *v; /* The virtual database engine */ + Table *pTab; /* Table to delete from */ + const char *zDb; /* Name of database holding pTab */ + AuthContext sContext; /* Authorization context */ + NameContext sNC; /* Name context to resolve WHERE expression */ + int iDb; /* Database number */ + int rcauth; /* Value returned by authorization callback */ + int iCur; /* Cursor number used by where.c */ + Trigger *pTrigger; /* List of triggers, or NULL */ memset(&sContext, 0, sizeof(sContext)); + memset(&sNC, 0, sizeof(sNC)); + db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto delete_from_cleanup; } assert( pTabList->nSrc==1 ); - /* Locate the table which we want to delete. This table has to be - ** put in an SrcList structure because some of the subroutines we - ** will be calling are designed to work with multiple tables and expect - ** an SrcList* parameter instead of just a Table* parameter. - */ + /* Locate the table which we want to delete. If it is a view, make sure + ** that the column names are initialized. */ pTab = sqlite4SrcListLookup(pParse, pTabList); if( pTab==0 ) goto delete_from_cleanup; + iDb = sqlite4SchemaToIndex(db, pTab->pSchema); + zDb = db->aDb[iDb].zName; + assert( iDbnDb ); - /* Figure out if we have any triggers and if the table being - ** deleted from is a view - */ -#ifndef SQLITE_OMIT_TRIGGER + /* Figure out if there are any triggers */ pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - isView = pTab->pSelect!=0; -#else -# define pTrigger 0 -# define isView 0 -#endif -#ifdef SQLITE_OMIT_VIEW -# undef isView -# define isView 0 -#endif - - /* If pTab is really a view, make sure it has been initialized. - */ - if( sqlite4ViewGetColumnNames(pParse, pTab) ){ - goto delete_from_cleanup; - } - - if( sqlite4IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ - goto delete_from_cleanup; - } - iDb = sqlite4SchemaToIndex(db, pTab->pSchema); - assert( iDbnDb ); - zDb = db->aDb[iDb].zName; + + /* If pTab is really a view, make sure it has been initialized. */ + if( sqlite4ViewGetColumnNames(pParse, pTab) ) goto delete_from_cleanup; + + /* Check the table is not read-only. A table is read-only if it is one + ** of the built-in system tables (e.g. sqlite_master, sqlite_stat) or + ** if it is a view and there are no INSTEAD OF triggers to handle the + ** delete. */ + if( sqlite4IsReadOnly(pParse, pTab, pTrigger!=0) ) goto delete_from_cleanup; + assert( !IsView(pTab) || pTrigger ); + assert( !IsView(pTab) || pTab->pIndex==0 ); + + /* Invoke the authorization callback */ rcauth = sqlite4AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb); assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE ); if( rcauth==SQLITE_DENY ){ goto delete_from_cleanup; } - assert(!isView || pTrigger); - - /* Assign cursor number to the table and all its indices. - */ - assert( pTabList->nSrc==1 ); - iCur = pTabList->a[0].iCursor = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - pParse->nTab++; - } - - /* Start the view context - */ - if( isView ){ - sqlite4AuthContextPush(pParse, &sContext, pTab->zName); - } - - /* Begin generating code. - */ + + /* Assign a cursor number to the table or view this statement is + ** deleting from. If pTab is actually a view, this will be used as the + ** ephemeral table cursor. + ** + ** Or, if this is a real table, it is the number of a read-only cursor + ** used by where.c to iterate through those records that match the WHERE + ** clause supplied by the user. This is a separate cursor from the array + ** of read-write cursors used to delete entries from each of the tables + ** indexes. */ + pTabList->a[0].iCursor = iCur = pParse->nTab++; + + /* Begin generating code */ v = sqlite4GetVdbe(pParse); - if( v==0 ){ - goto delete_from_cleanup; - } + if( v==0 ) goto delete_from_cleanup; if( pParse->nested==0 ) sqlite4VdbeCountChanges(v); sqlite4BeginWriteOperation(pParse, 1, iDb); /* If we are trying to delete from a view, realize that view into - ** a ephemeral table. - */ -#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) - if( isView ){ + ** a ephemeral table. */ + if( IsView(pTab) ){ + sqlite4AuthContextPush(pParse, &sContext, pTab->zName); sqlite4MaterializeView(pParse, pTab, pWhere, iCur); } -#endif - /* Resolve the column names in the WHERE clause. - */ - memset(&sNC, 0, sizeof(sNC)); + /* Resolve the column names in the WHERE clause. This has to come after + ** the call to sqlite4MaterializeView() above. */ sNC.pParse = pParse; sNC.pSrcList = pTabList; if( sqlite4ResolveExprNames(&sNC, pWhere) ){ goto delete_from_cleanup; } - /* Initialize the counter of the number of rows deleted, if - ** we are counting rows. - */ - if( db->flags & SQLITE_CountRows ){ - memCnt = ++pParse->nMem; - sqlite4VdbeAddOp2(v, OP_Integer, 0, memCnt); - } - #ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to erase the whole table. Prior to version 3.6.5, ** this optimization caused the row change count (the value returned by ** API function sqlite4_count_changes) to be set incorrectly. */ if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) - && 0==sqlite4FkRequired(pParse, pTab, 0, 0) + && 0==sqlite4FkRequired(pParse, pTab, 0) ){ - assert( !isView ); - sqlite4VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, - pTab->zName, P4_STATIC); + Index *pIdx; /* For looping over indices of the table */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); sqlite4VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); } }else @@ -361,42 +321,40 @@ #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ /* The usual case: There is a WHERE clause so we have to scan through ** the table and pick which records to delete. */ { - int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ - int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ - int regRowid; /* Actual register containing rowids */ + WhereInfo *pWInfo; /* Information about the WHERE clause */ + int baseCur = 0; + int regSet = ++pParse->nMem; /* Register for rowset of rows to delete */ + int regKey = ++pParse->nMem; /* Used for storing row keys */ + int addrTop; /* Instruction (KeySetRead) at top of loop */ - /* Collect rowids of every row to be deleted. - */ - sqlite4VdbeAddOp2(v, OP_Null, 0, iRowSet); + /* Query the table for all rows that match the WHERE clause. Store the + ** PRIMARY KEY for each matching row in the KeySet object in register + ** regSet. After the scan is complete, the VM will loop through the set + ** of keys in the KeySet and delete each row. Rows must be deleted after + ** the scan is complete because deleting an item can change the scan + ** order. */ + sqlite4VdbeAddOp2(v, OP_Null, 0, regSet); + VdbeComment((v, "initialize KeySet")); pWInfo = sqlite4WhereBegin( pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK ); if( pWInfo==0 ) goto delete_from_cleanup; - regRowid = sqlite4ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid); - sqlite4VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); - if( db->flags & SQLITE_CountRows ){ - sqlite4VdbeAddOp2(v, OP_AddImm, memCnt, 1); - } + sqlite4VdbeAddOp2(v, OP_RowKey, iCur, regKey); + sqlite4VdbeAddOp2(v, OP_KeySetAdd, regSet, regKey); sqlite4WhereEnd(pWInfo); - /* Delete every item whose key was written to the list during the - ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. */ - end = sqlite4VdbeMakeLabel(v); - - /* Unless this is a view, open cursors for the table we are - ** deleting from and all its indices. If this is a view, then the - ** only effect this statement has is to fire the INSTEAD OF - ** triggers. */ - if( !isView ){ - sqlite4OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); + /* Unless this is a view, open cursors for all indexes on the table + ** from which we are deleting. */ + if( !IsView(pTab) ){ + baseCur = pParse->nTab; + sqlite4OpenAllIndexes(pParse, pTab, baseCur, OP_OpenWrite); } - addr = sqlite4VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); + addrTop = sqlite4VdbeAddOp3(v, OP_KeySetRead, regSet, 0, regKey); /* Delete the row */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite4GetVTable(db, pTab); @@ -405,60 +363,29 @@ sqlite4VdbeChangeP5(v, OE_Abort); sqlite4MayAbort(pParse); }else #endif { - int count = (pParse->nested==0); /* True to count changes */ - sqlite4GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default); + sqlite4GenerateRowDelete( + pParse, pTab, baseCur, regKey, pParse->nested==0, pTrigger, OE_Default + ); } /* End of the delete loop */ - sqlite4VdbeAddOp2(v, OP_Goto, 0, addr); - sqlite4VdbeResolveLabel(v, end); - - /* Close the cursors open on the table and its indexes. */ - if( !isView && !IsVirtual(pTab) ){ - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqlite4VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum); - } - sqlite4VdbeAddOp1(v, OP_Close, iCur); - } - } - - /* Update the sqlite_sequence table by storing the content of the - ** maximum rowid counter values recorded while inserting into - ** autoincrement tables. - */ - if( pParse->nested==0 && pParse->pTriggerTab==0 ){ - sqlite4AutoincrementEnd(pParse); - } - - /* Return the number of rows that were deleted. If this routine is - ** generating code because of a call to sqlite4NestedParse(), do not - ** invoke the callback function. - */ - if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ - sqlite4VdbeAddOp2(v, OP_ResultRow, memCnt, 1); - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); + sqlite4VdbeAddOp2(v, OP_Goto, 0, addrTop); + sqlite4VdbeJumpHere(v, addrTop); + + /* Close all open cursors */ + sqlite4CloseAllIndexes(pParse, pTab, baseCur); } delete_from_cleanup: sqlite4AuthContextPop(&sContext); sqlite4SrcListDelete(db, pTabList); sqlite4ExprDelete(db, pWhere); return; } -/* Make sure "isView" and other macros defined above are undefined. Otherwise -** thely may interfere with compilation of other functions in this file -** (or in another file, if this file becomes part of the amalgamation). */ -#ifdef isView - #undef isView -#endif -#ifdef pTrigger - #undef pTrigger -#endif /* ** This routine generates VDBE code that causes a single row of a ** single table to be deleted. ** @@ -476,98 +403,152 @@ ** ** This routine generates code to remove both the table record and all ** index entries that point to that record. */ void sqlite4GenerateRowDelete( - Parse *pParse, /* Parsing context */ - Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ - int iRowid, /* Memory cell that contains the rowid to delete */ - int count, /* If non-zero, increment the row change counter */ - Trigger *pTrigger, /* List of triggers to (potentially) fire */ - int onconf /* Default ON CONFLICT policy for triggers */ + Parse *pParse, /* Parsing context */ + Table *pTab, /* Table containing the row to be deleted */ + int baseCur, /* Base cursor number */ + int regKey, /* Register containing PK of row to delete */ + int bCount, /* True to increment the row change counter */ + Trigger *pTrigger, /* List of triggers to (potentially) fire */ + int onconf /* Default ON CONFLICT policy for triggers */ ){ Vdbe *v = pParse->pVdbe; /* Vdbe */ - int iOld = 0; /* First register in OLD.* array */ + int regOld = 0; /* First register in OLD.* array */ int iLabel; /* Label resolved to end of generated code */ + int iPk; /* Offset of PK cursor in cursor array */ + int iPkCsr; /* Primary key cursor number */ + Index *pPk; /* Primary key index */ /* Vdbe is guaranteed to have been allocated by this stage. */ assert( v ); - /* Seek cursor iCur to the row to delete. If this row no longer exists + pPk = sqlite4FindPrimaryKey(pTab, &iPk); + iPkCsr = baseCur + iPk; + + /* Seek the PK cursor to the row to delete. If this row no longer exists ** (this can happen if a trigger program has already deleted it), do ** not attempt to delete it or fire any DELETE triggers. */ iLabel = sqlite4VdbeMakeLabel(v); - sqlite4VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); + sqlite4VdbeAddOp4Int(v, OP_NotFound, iPkCsr, iLabel, regKey, 0); /* If there are any triggers to fire, allocate a range of registers to ** use for the old.* references in the triggers. */ - if( sqlite4FkRequired(pParse, pTab, 0, 0) || pTrigger ){ + if( sqlite4FkRequired(pParse, pTab, 0) || pTrigger ){ u32 mask; /* Mask of OLD.* columns in use */ int iCol; /* Iterator used while populating OLD.* */ - /* TODO: Could use temporary registers here. Also could attempt to - ** avoid copying the contents of the rowid register. */ + /* Determine which table columns may be required by either foreign key + ** logic or triggers. This block sets stack variable mask to a 32-bit mask + ** where bit 0 corresponds to the left-most table column, bit 1 to the + ** second left-most, and so on. If an OLD.* column may be required, then + ** the corresponding bit is set. + ** + ** Or, if the table contains more than 32 columns and at least one of + ** the columns following the 32nd is required, set mask to 0xffffffff. */ mask = sqlite4TriggerColmask( pParse, pTrigger, 0, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onconf ); mask |= sqlite4FkOldmask(pParse, pTab); - iOld = pParse->nMem+1; - pParse->nMem += (1 + pTab->nCol); - /* Populate the OLD.* pseudo-table register array. These values will be - ** used by any BEFORE and AFTER triggers that exist. */ - sqlite4VdbeAddOp2(v, OP_Copy, iRowid, iOld); + /* Allocate an array of (nCol+1) registers, where nCol is the number + ** of columns in the table. + ** + ** If the table has an implicit PK, the first register in the array + ** contains the rowid. Otherwise, its contents are undefined. The + ** remaining registers contain the OLD.* column values, in order. */ + regOld = pParse->nMem+1; + pParse->nMem += (pTab->nCol+1); for(iCol=0; iColnCol; iCol++){ if( mask==0xffffffff || mask&(1<aiColumn[0]<0 ){ + sqlite4VdbeAddOp2(v, OP_Rowid, iPkCsr, regOld); + } /* Invoke BEFORE DELETE trigger programs. */ sqlite4CodeRowTrigger(pParse, pTrigger, - TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel + TK_DELETE, 0, TRIGGER_BEFORE, pTab, regOld, onconf, iLabel ); /* Seek the cursor to the row to be deleted again. It may be that ** the BEFORE triggers coded above have already removed the row ** being deleted. Do not attempt to delete the row a second time, and ** do not fire AFTER triggers. */ - sqlite4VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); + sqlite4VdbeAddOp4Int(v, OP_NotFound, iPkCsr, iLabel, regKey, 0); /* Do FK processing. This call checks that any FK constraints that ** refer to this table (i.e. constraints attached to other tables) ** are not violated by deleting this row. */ - sqlite4FkCheck(pParse, pTab, iOld, 0); + sqlite4FkCheck(pParse, pTab, regOld+1, 0); } /* Delete the index and table entries. Skip this step if pTab is really ** a view (in which case the only effect of the DELETE statement is to ** fire the INSTEAD OF triggers). */ - if( pTab->pSelect==0 ){ - sqlite4GenerateRowIndexDelete(pParse, pTab, iCur, 0); - sqlite4VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); - if( count ){ - sqlite4VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); - } + if( !IsView(pTab) ){ + sqlite4GenerateRowIndexDelete(pParse, pTab, baseCur, 0); } /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key - ** to the row just deleted. */ - sqlite4FkActions(pParse, pTab, 0, iOld); + ** to the row just deleted. This is a no-op if there are no configured + ** foreign keys that use this table as a parent table. */ + sqlite4FkActions(pParse, pTab, 0, regOld+1); /* Invoke AFTER DELETE trigger programs. */ sqlite4CodeRowTrigger(pParse, pTrigger, - TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel + TK_DELETE, 0, TRIGGER_AFTER, pTab, regOld, onconf, iLabel ); /* Jump here if the row had already been deleted before any BEFORE ** trigger programs were invoked. Or if a trigger program throws a ** RAISE(IGNORE) exception. */ sqlite4VdbeResolveLabel(v, iLabel); } + +void sqlite4EncodeIndexKey( + Parse *pParse, + Index *pPk, int iPkCsr, + Index *pIdx, int iIdxCsr, + int regOut +){ + Vdbe *v = pParse->pVdbe; /* VM to write code to */ + int nTmpReg; /* Number of temp registers required */ + int regTmp; /* First register in temp array */ + int i; /* Iterator variable */ + + /* Allocate temp registers */ + assert( pIdx!=pPk ); + nTmpReg = pIdx->nColumn + pPk->nColumn; + regTmp = sqlite4GetTempRange(pParse, nTmpReg); + + /* Assemble the values for the key in the array of temp registers */ + for(i=0; inColumn; i++){ + int regVal = regTmp + i; + sqlite4VdbeAddOp3(v, OP_Column, iPkCsr, pIdx->aiColumn[i], regVal); + } + for(i=0; inColumn; i++){ + int iCol = pPk->aiColumn[i]; + int regVal = pIdx->nColumn + regTmp + i; + if( iCol<0 ){ + sqlite4VdbeAddOp2(v, OP_Rowid, iPkCsr, regVal); + }else{ + sqlite4VdbeAddOp3(v, OP_Column, iPkCsr, pPk->aiColumn[i], regVal); + } + } + + /* Build the index key */ + sqlite4VdbeAddOp3(v, OP_MakeIdxKey, iIdxCsr, regTmp, regOut); + + /* Release temp registers */ + sqlite4ReleaseTempRange(pParse, regTmp, nTmpReg); +} /* ** This routine generates VDBE code that causes the deletion of all ** index entries associated with a single row of a single table. ** @@ -582,24 +563,39 @@ ** ** 3. The "iCur" cursor must be pointing to the row that is to be ** deleted. */ void sqlite4GenerateRowIndexDelete( - Parse *pParse, /* Parsing and code generating context */ - Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ - int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */ + Parse *pParse, /* Parsing and code generating context */ + Table *pTab, /* Table containing the row to be deleted */ + int baseCur, /* Cursor number for the table */ + int *aRegIdx /* Only delete if (aRegIdx && aRegIdx[i]>0) */ ){ + Vdbe *v = pParse->pVdbe; + Index *pPk; + int iPk; int i; + int regKey; Index *pIdx; - int r1; + + regKey = sqlite4GetTempReg(pParse); + pPk = sqlite4FindPrimaryKey(pTab, &iPk); - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue; - r1 = sqlite4GenerateIndexKey(pParse, pIdx, iCur, 0, 0, 0); - sqlite4VdbeAddOp3(pParse->pVdbe, OP_IdxDelete, iCur+i, r1,pIdx->nColumn+1); + for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + if( pIdx!=pPk && (aRegIdx==0 || aRegIdx[i]>0) ){ + int addrNotFound; + sqlite4EncodeIndexKey(pParse, pPk, baseCur+iPk, pIdx, baseCur+i, regKey); + addrNotFound = sqlite4VdbeAddOp4(v, + OP_NotFound, baseCur+i, 0, regKey, 0, P4_INT32 + ); + sqlite4VdbeAddOp1(v, OP_Delete, baseCur+i); + sqlite4VdbeJumpHere(v, addrNotFound); + } } + + sqlite4VdbeAddOp1(v, OP_Delete, baseCur+iPk); + sqlite4ReleaseTempReg(pParse, regKey); } /* ** Generate code that will assemble an index key and put it in register ** regOut. The key with be for index pIdx which is an index on pTab. @@ -628,11 +624,11 @@ nCol = pIdx->nColumn; regBase = sqlite4GetTempRange(pParse, nCol+1); sqlite4VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol); for(j=0; jaiColumn[j]; - if( idx==pTab->iPKey ){ + if( idx<0 ){ sqlite4VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j); }else{ sqlite4VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j); sqlite4ColumnDefault(v, pTab, idx, -1); } Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -1457,10 +1457,14 @@ sqlite4 *db = pParse->db; /* Database connection */ Table *pTab; /* Table . */ Expr *pExpr; /* Expression */ int iCol; /* Index of column */ int iDb; /* Database idx for pTab */ + Index *pIdx; + CollSeq *pReq; + char aff; + int affinity_ok; assert( p ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ @@ -1476,55 +1480,43 @@ /* This function is only called from two places. In both cases the vdbe ** has already been allocated. So assume sqlite4GetVdbe() is always ** successful here. */ assert(v); - if( iCol<0 ){ - int iAddr; - - iAddr = sqlite4CodeOnce(pParse); - - sqlite4OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); - eType = IN_INDEX_ROWID; - - sqlite4VdbeJumpHere(v, iAddr); - }else{ - Index *pIdx; /* Iterator variable */ - - /* The collation sequence used by the comparison. If an index is to - ** be used in place of a temp-table, it must be ordered according - ** to this collation sequence. */ - CollSeq *pReq = sqlite4BinaryCompareCollSeq(pParse, pX->pLeft, pExpr); - - /* Check that the affinity that will be used to perform the - ** comparison is the same as the affinity of the column. If - ** it is not, it is not possible to use any index. - */ - char aff = comparisonAffinity(pX); - int affinity_ok = (pTab->aCol[iCol].affinity==aff||aff==SQLITE_AFF_NONE); - - for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ - if( (pIdx->aiColumn[0]==iCol) - && sqlite4FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq - && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None)) + + /* The collation sequence used by the comparison. If an index is to + ** be used in place of a temp-table, it must be ordered according + ** to this collation sequence. */ + pReq = sqlite4BinaryCompareCollSeq(pParse, pX->pLeft, pExpr); + + /* Check that the affinity that will be used to perform the + ** comparison is the same as the affinity of the column. If + ** it is not, it is not possible to use any index. + */ + aff = comparisonAffinity(pX); + affinity_ok = (pTab->aCol[iCol].affinity==aff||aff==SQLITE_AFF_NONE); + + for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ + if( (pIdx->aiColumn[0]==iCol) + && sqlite4FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq + && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None)) ){ - int iAddr; - char *pKey; - - pKey = (char *)sqlite4IndexKeyinfo(pParse, pIdx); - iAddr = sqlite4CodeOnce(pParse); - - sqlite4VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, - pKey,P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIdx->zName)); - eType = IN_INDEX_INDEX; - - sqlite4VdbeJumpHere(v, iAddr); - if( prNotFound && !pTab->aCol[iCol].notNull ){ - *prNotFound = ++pParse->nMem; - sqlite4VdbeAddOp2(v, OP_Null, 0, *prNotFound); - } + int iAddr; + char *pKey; + + pKey = (char *)sqlite4IndexKeyinfo(pParse, pIdx); + iAddr = sqlite4CodeOnce(pParse); + + sqlite4VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, + pKey,P4_KEYINFO_HANDOFF); + VdbeComment((v, "%s", pIdx->zName)); + eType = IN_INDEX_INDEX; + + sqlite4VdbeJumpHere(v, iAddr); + if( prNotFound && !pTab->aCol[iCol].notNull ){ + *prNotFound = ++pParse->nMem; + sqlite4VdbeAddOp2(v, OP_Null, 0, *prNotFound); } } } } @@ -2148,11 +2140,11 @@ Table *pTab, /* The table containing the value */ int iTabCur, /* The cursor for this table */ int iCol, /* Index of the column to extract */ int regOut /* Extract the valud into this register */ ){ - if( iCol<0 || iCol==pTab->iPKey ){ + if( iCol<0 ){ sqlite4VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); }else{ int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; sqlite4VdbeAddOp3(v, op, iTabCur, iCol, regOut); } @@ -2706,11 +2698,11 @@ /* If the opcode is TK_TRIGGER, then the expression is a reference ** to a column in the new.* or old.* pseudo-tables available to ** trigger programs. In this case Expr.iTable is set to 1 for the ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn ** is set to the column of the pseudo-table to read, or to -1 to - ** read the rowid field. + ** read the rowid field (if applicable - see below). ** ** The expression is implemented using an OP_Param opcode. The p1 ** parameter is set to 0 for an old.rowid reference, or to (i+1) ** to reference another column of the old.* pseudo-table, where ** i is the index of the column. For a new.rowid reference, p1 is @@ -2725,17 +2717,21 @@ ** Then p1 is interpreted as follows: ** ** p1==0 -> old.rowid p1==3 -> new.rowid ** p1==1 -> old.a p1==4 -> new.a ** p1==2 -> old.b p1==5 -> new.b + ** + ** As of SQLite 4, the rowid references are only valid if the table is + ** declared without an explicit PRIMARY KEY (as it is in the example + ** above). If the table does have an explicit PRIMARY KEY, the contents + ** of the old.rowid and new.rowid registers are not defined. */ Table *pTab = pExpr->pTab; int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; assert( pExpr->iTable==0 || pExpr->iTable==1 ); assert( pExpr->iColumn>=-1 && pExpr->iColumnnCol ); - assert( pTab->iPKey<0 || pExpr->iColumn!=pTab->iPKey ); assert( p1>=0 && p1<(pTab->nCol*2+2) ); sqlite4VdbeAddOp2(v, OP_Param, p1, target); VdbeComment((v, "%s.%s -> $%d", (pExpr->iTable ? "new" : "old"), Index: src/fkey.c ================================================================== --- src/fkey.c +++ src/fkey.c @@ -140,15 +140,14 @@ /* ** A foreign key constraint requires that the key columns in the parent ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint. ** Given that pParent is the parent table for foreign key constraint pFKey, -** search the schema a unique index on the parent key columns. +** search the schema for a unique index on the parent key columns. ** -** If successful, zero is returned. If the parent key is an INTEGER PRIMARY -** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx -** is set to point to the unique index. +** If successful, zero is returned and *ppIdx is set to point to the +** unique index. ** ** If the parent key consists of a single column (the foreign key constraint ** is not a composite foreign key), output variable *paiCol is set to NULL. ** Otherwise, it is set to point to an allocated array of size N, where ** N is the number of columns in the parent key. The first element of the @@ -183,41 +182,26 @@ Table *pParent, /* Parent table of FK constraint pFKey */ FKey *pFKey, /* Foreign key to find index for */ Index **ppIdx, /* OUT: Unique index on parent table */ int **paiCol /* OUT: Map of index columns in pFKey */ ){ - Index *pIdx = 0; /* Value to return via *ppIdx */ - int *aiCol = 0; /* Value to return via *paiCol */ - int nCol = pFKey->nCol; /* Number of columns in parent key */ - char *zKey = pFKey->aCol[0].zCol; /* Name of left-most parent key column */ + Index *pIdx = 0; /* Value to return via *ppIdx */ + int *aiCol = 0; /* Value to return via *paiCol */ + int nCol = pFKey->nCol; /* Number of columns in parent key */ + int bImplicit; /* True if no explicit parent columns */ /* The caller is responsible for zeroing output parameters. */ assert( ppIdx && *ppIdx==0 ); assert( !paiCol || *paiCol==0 ); assert( pParse ); - /* If this is a non-composite (single column) foreign key, check if it - ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx - ** and *paiCol set to zero and return early. - ** - ** Otherwise, for a composite foreign key (more than one column), allocate + bImplicit = (pFKey->aCol[0].zCol==0); + + /* If this is a composite foreign key (more than one column), allocate ** space for the aiCol array (returned via output parameter *paiCol). - ** Non-composite foreign keys do not require the aiCol array. - */ - if( nCol==1 ){ - /* The FK maps to the IPK if any of the following are true: - ** - ** 1) There is an INTEGER PRIMARY KEY column and the FK is implicitly - ** mapped to the primary key of table pParent, or - ** 2) The FK is explicitly mapped to a column declared as INTEGER - ** PRIMARY KEY. - */ - if( pParent->iPKey>=0 ){ - if( !zKey ) return 0; - if( !sqlite4StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; - } - }else if( paiCol ){ + ** Non-composite foreign keys do not require the aiCol array. */ + if( paiCol && nCol>1 ){ assert( nCol>1 ); aiCol = (int *)sqlite4DbMallocRaw(pParse->db, nCol*sizeof(int)); if( !aiCol ) return 1; *paiCol = aiCol; } @@ -226,26 +210,23 @@ if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number ** of columns. If each indexed column corresponds to a foreign key ** column of pFKey, then this index is a winner. */ - if( zKey==0 ){ - /* If zKey is NULL, then this foreign key is implicitly mapped to - ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be - ** identified by the test (Index.autoIndex==2). */ - if( pIdx->autoIndex==2 ){ + if( bImplicit ){ + if( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){ if( aiCol ){ int i; for(i=0; iaCol[i].iFrom; } break; } }else{ - /* If zKey is non-NULL, then this foreign key was declared to - ** map to an explicit list of columns in table pParent. Check if this - ** index matches those columns. Also, check that the index uses - ** the default collation sequences for each column. */ + /* If this foreign key was declared to map to an explicit list of + ** columns in table pParent. Check if this index matches those + ** columns. Also, check that the index uses the default collation + ** sequences for each column. */ int i, j; for(i=0; iaiColumn[i]; /* Index of column in parent tbl */ char *zDfltColl; /* Def. collation for column */ char *zIdxCol; /* Name of indexed column */ @@ -316,108 +297,84 @@ int iDb, /* Index of database housing pTab */ Table *pTab, /* Parent table of FK pFKey */ Index *pIdx, /* Unique index on parent key columns in pTab */ FKey *pFKey, /* Foreign key constraint */ int *aiCol, /* Map from parent key columns to child table columns */ - int regData, /* Address of array containing child table row */ + int regContent, /* Address of array containing child table row */ int nIncr, /* Increment constraint counter by this */ int isIgnore /* If true, pretend pTab contains all NULL values */ ){ int i; /* Iterator variable */ Vdbe *v = sqlite4GetVdbe(pParse); /* Vdbe to add code to */ int iCur = pParse->nTab - 1; /* Cursor number to use */ int iOk = sqlite4VdbeMakeLabel(v); /* jump here if parent key found */ - /* If nIncr is less than zero, then check at runtime if there are any - ** outstanding constraints to resolve. If there are not, there is no need - ** to check if deleting this row resolves any outstanding violations. - ** - ** Check if any of the key columns in the child table row are NULL. If - ** any are, then the constraint is considered satisfied. No need to - ** search for a matching row in the parent table. */ + assert( pIdx ); + + /* If nIncr is less than zero (this is a DELETE), then check at runtime if + ** there are any outstanding constraints to resolve. If there are not, + ** there is no need to check if deleting this row resolves any outstanding + ** violations. */ if( nIncr<0 ){ sqlite4VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk); } + + /* Check if any of the key columns in the child table row are NULL. If + ** any are, then the constraint is considered satisfied. No need to + ** search for a matching row in the parent table. */ for(i=0; inCol; i++){ - int iReg = aiCol[i] + regData + 1; + int iReg = aiCol[i] + regContent; sqlite4VdbeAddOp2(v, OP_IsNull, iReg, iOk); } if( isIgnore==0 ){ - if( pIdx==0 ){ - /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY - ** column of the parent table (table pTab). */ - int iMustBeInt; /* Address of MustBeInt instruction */ - int regTemp = sqlite4GetTempReg(pParse); - - /* Invoke MustBeInt to coerce the child key value to an integer (i.e. - ** apply the affinity of the parent key). If this fails, then there - ** is no matching parent key. Before using MustBeInt, make a copy of - ** the value. Otherwise, the value inserted into the child key column - ** will have INTEGER affinity applied to it, which may not be correct. */ - sqlite4VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp); - iMustBeInt = sqlite4VdbeAddOp2(v, OP_MustBeInt, regTemp, 0); - - /* If the parent table is the same as the child table, and we are about - ** to increment the constraint-counter (i.e. this is an INSERT operation), - ** then check if the row being inserted matches itself. If so, do not - ** increment the constraint-counter. */ - if( pTab==pFKey->pFrom && nIncr==1 ){ - sqlite4VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); - } - - sqlite4OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); - sqlite4VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); - sqlite4VdbeAddOp2(v, OP_Goto, 0, iOk); - sqlite4VdbeJumpHere(v, sqlite4VdbeCurrentAddr(v)-2); - sqlite4VdbeJumpHere(v, iMustBeInt); - sqlite4ReleaseTempReg(pParse, regTemp); - }else{ - int nCol = pFKey->nCol; - int regTemp = sqlite4GetTempRange(pParse, nCol); - int regRec = sqlite4GetTempReg(pParse); - KeyInfo *pKey = sqlite4IndexKeyinfo(pParse, pIdx); - - sqlite4VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); - sqlite4VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); - for(i=0; ipFrom && nIncr==1 ){ - int iJump = sqlite4VdbeCurrentAddr(v) + nCol + 1; - for(i=0; iaiColumn[i]+1+regData; - assert( aiCol[i]!=pTab->iPKey ); - if( pIdx->aiColumn[i]==pTab->iPKey ){ - /* The parent key is a composite key that includes the IPK column */ - iParent = regData; - } - sqlite4VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); - sqlite4VdbeChangeP5(v, SQLITE_JUMPIFNULL); - } - sqlite4VdbeAddOp2(v, OP_Goto, 0, iOk); - } - - sqlite4VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); - sqlite4VdbeChangeP4(v, -1, sqlite4IndexAffinityStr(v,pIdx), P4_TRANSIENT); - sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); - - sqlite4ReleaseTempReg(pParse, regRec); - sqlite4ReleaseTempRange(pParse, regTemp, nCol); - } + int nCol = pFKey->nCol; + int regTemp = sqlite4GetTempRange(pParse, nCol); + int regRec = sqlite4GetTempReg(pParse); + + sqlite4OpenIndex(pParse, iCur, iDb, pIdx, OP_OpenRead); + + /* Assemble the child key values in a contiguous array of registers. + ** Then apply the affinity transformation for the parent index. */ + for(i=0; ipFrom && nIncr==1 ){ + int iJump = sqlite4VdbeCurrentAddr(v) + nCol + 1; + for(i=0; iaiColumn[i]+regContent; + sqlite4VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); + sqlite4VdbeChangeP5(v, SQLITE_JUMPIFNULL); + assert( iChild<=pParse->nMem && iParent<=pParse->nMem ); + } + sqlite4VdbeAddOp2(v, OP_Goto, 0, iOk); + } + + sqlite4VdbeAddOp4Int(v, OP_MakeIdxKey, iCur, regTemp, regRec, nCol); + sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); + +#if 0 + sqlite4VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); + sqlite4VdbeChangeP4(v, -1, sqlite4IndexAffinityStr(v,pIdx), P4_TRANSIENT); + sqlite4VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); +#endif + + sqlite4ReleaseTempReg(pParse, regRec); + sqlite4ReleaseTempRange(pParse, regTemp, nCol); } if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ /* Special case: If this is an INSERT statement that will insert exactly ** one row into the table, raise a constraint immediately instead of @@ -437,15 +394,15 @@ sqlite4VdbeResolveLabel(v, iOk); sqlite4VdbeAddOp1(v, OP_Close, iCur); } /* -** This function is called to generate code executed when a row is deleted -** from the parent table of foreign key constraint pFKey and, if pFKey is -** deferred, when a row is inserted into the same table. When generating -** code for an SQL UPDATE operation, this function may be called twice - -** once to "delete" the old row and once to "insert" the new row. +** This function is called to generate code executed when a row is inserted +** into or deleted from the parent table of foreign key constraint pFKey. +** When generating code for an SQL UPDATE operation, this function may be +** called twice - once to "delete" the old row and once to "insert" the +** new row. ** ** The code generated by this function scans through the rows in the child ** table that correspond to the parent table row being deleted or inserted. ** For each child row found, one of the following actions is taken: ** @@ -482,11 +439,11 @@ NameContext sNameContext; /* Context used to resolve WHERE clause */ WhereInfo *pWInfo; /* Context used by sqlite4WhereXXX() */ int iFkIfZero = 0; /* Address of OP_FkIfZero */ Vdbe *v = sqlite4GetVdbe(pParse); - assert( !pIdx || pIdx->pTable==pTab ); + assert( pIdx && pIdx->pTable==pTab ); if( nIncr<0 ){ iFkIfZero = sqlite4VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); } @@ -507,22 +464,16 @@ pLeft = sqlite4Expr(db, TK_REGISTER, 0); if( pLeft ){ /* Set the collation sequence and affinity of the LHS of each TK_EQ ** expression to the parent key column defaults. */ - if( pIdx ){ - Column *pCol; - iCol = pIdx->aiColumn[i]; - pCol = &pTab->aCol[iCol]; - if( pTab->iPKey==iCol ) iCol = -1; - pLeft->iTable = regData+iCol+1; - pLeft->affinity = pCol->affinity; - pLeft->pColl = sqlite4LocateCollSeq(pParse, pCol->zColl); - }else{ - pLeft->iTable = regData; - pLeft->affinity = SQLITE_AFF_INTEGER; - } + Column *pCol; + iCol = pIdx->aiColumn[i]; + pCol = &pTab->aCol[iCol]; + pLeft->iTable = regData+iCol; + pLeft->affinity = pCol->affinity; + pLeft->pColl = sqlite4LocateCollSeq(pParse, pCol->zColl); } iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iCol>=0 ); zCol = pFKey->pFrom->aCol[iCol].zName; pRight = sqlite4Expr(db, TK_ID, zCol); @@ -555,13 +506,12 @@ sNameContext.pSrcList = pSrc; sNameContext.pParse = pParse; sqlite4ResolveExprNames(&sNameContext, pWhere); /* Create VDBE to loop through the entries in pSrc that match the WHERE - ** clause. If the constraint is not deferred, throw an exception for - ** each row found. Otherwise, for deferred constraints, increment the - ** deferred constraint counter by nIncr for each row selected. */ + ** clause. For each row found, increment the relevant constraint counter + ** by nIncr. */ pWInfo = sqlite4WhereBegin(pParse, pSrc, pWhere, 0, 0, 0); if( nIncr>0 && pFKey->isDeferred==0 ){ sqlite4ParseToplevel(pParse)->mayAbort = 1; } sqlite4VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); @@ -760,26 +710,23 @@ aiCol = aiFree; }else{ iCol = pFKey->aCol[0].iFrom; aiCol = &iCol; } - for(i=0; inCol; i++){ - if( aiCol[i]==pTab->iPKey ){ - aiCol[i] = -1; - } #ifndef SQLITE_OMIT_AUTHORIZATION + for(i=0; inCol; i++){ /* Request permission to read the parent key columns. If the ** authorization callback returns SQLITE_IGNORE, behave as if any ** values read from the parent table are NULL. */ if( db->xAuth ){ int rcauth; - char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; + char *zCol = pTo->aCol[pIdx->aiColumn[i]].zName; rcauth = sqlite4AuthReadCol(pParse, pTo->zName, zCol, iDb); isIgnore = (rcauth==SQLITE_IGNORE); } -#endif } +#endif /* Take a shared-cache advisory read-lock on the parent table. Allocate ** a cursor to use to search the unique index on the parent key columns ** in the parent table. */ sqlite4TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName); @@ -851,11 +798,11 @@ #define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) /* ** This function is called before generating code to update or delete a -** row contained in table pTab. +** row contained in table pTab. */ u32 sqlite4FkOldmask( Parse *pParse, /* Parse context */ Table *pTab /* Table being modified */ ){ @@ -892,12 +839,11 @@ ** returns false. */ int sqlite4FkRequired( Parse *pParse, /* Parse context */ Table *pTab, /* Table being modified */ - int *aChange, /* Non-NULL for UPDATE operations */ - int chngRowid /* True for UPDATE that affects rowid */ + int *aChange /* Non-NULL for UPDATE operations */ ){ if( pParse->db->flags&SQLITE_ForeignKeys ){ if( !aChange ){ /* A DELETE operation. Foreign key processing is required if the ** table in question is either the child or parent table for any @@ -912,11 +858,10 @@ /* Check if any child key columns are being modified. */ for(p=pTab->pFKey; p; p=p->pNextFrom){ for(i=0; inCol; i++){ int iChildKey = p->aCol[i].iFrom; if( aChange[iChildKey]>=0 ) return 1; - if( iChildKey==pTab->iPKey && chngRowid ) return 1; } } /* Check if any parent key columns are being modified. */ for(p=sqlite4FkReferences(pTab); p; p=p->pNextTo){ @@ -925,11 +870,10 @@ int iKey; for(iKey=0; iKeynCol; iKey++){ Column *pCol = &pTab->aCol[iKey]; if( (zKey ? !sqlite4StrICmp(pCol->zName, zKey) : pCol->isPrimKey) ){ if( aChange[iKey]>=0 ) return 1; - if( iKey==pTab->iPKey && chngRowid ) return 1; } } } } } Index: src/global.c ================================================================== --- src/global.c +++ src/global.c @@ -168,10 +168,17 @@ 0, /* pInitMutex */ 0, /* nRefInitMutex */ 0, /* xLog */ 0, /* pLogArg */ 0, /* bLocaltimeFault */ + +#ifdef SQLITE_ENABLE_LSM + sqlite4KVStoreOpenLsm, /* xKVFile */ +#else + sqlite4KVStoreOpenMem, /* xKVFile */ +#endif + sqlite4KVStoreOpenMem, /* xKVTmp */ }; /* ** Hash table for global functions - functions common to all Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -22,63 +22,127 @@ int iCur, /* The cursor number of the table */ int iDb, /* The database index in sqlite4.aDb[] */ Table *pTab, /* The table to be opened */ int opcode /* OP_OpenRead or OP_OpenWrite */ ){ - Vdbe *v; - if( IsVirtual(pTab) ) return; + assert( 0 ); +} + +/* +** Open VDBE cursor iCur to access index pIdx. pIdx is guaranteed to be +** a part of database iDb. +*/ +void sqlite4OpenIndex( + Parse *p, /* Current parser context */ + int iCur, /* The cursor number of the cursor to open */ + int iDb, /* The database index in sqlite4.aDb[] */ + Index *pIdx, /* The index to be opened */ + int opcode /* OP_OpenRead or OP_OpenWrite */ +){ + KeyInfo *pKey; /* KeyInfo structure describing PK index */ + Vdbe *v; /* VM to write code into */ + + assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); + assert( pIdx->tnum>0 ); + v = sqlite4GetVdbe(p); + pKey = sqlite4IndexKeyinfo(p, pIdx); + testcase( pKey==0 ); + + sqlite4VdbeAddOp3(v, opcode, iCur, pIdx->tnum, iDb); + sqlite4VdbeChangeP4(v, -1, (const char *)pKey, P4_KEYINFO_HANDOFF); + VdbeComment((v, "%s", pIdx->zName)); +} + +/* +** Generate code that will open the primary key of a table for either +** reading (if opcode==OP_OpenRead) or writing (if opcode==OP_OpenWrite). +*/ +void sqlite4OpenPrimaryKey( + Parse *p, /* Current parser context */ + int iCur, /* The cursor number of the cursor to open */ + int iDb, /* The database index in sqlite4.aDb[] */ + Table *pTab, /* The table to be opened */ + int opcode /* OP_OpenRead or OP_OpenWrite */ +){ assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); - sqlite4TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); - sqlite4VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb); - sqlite4VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32); - VdbeComment((v, "%s", pTab->zName)); + if( IsVirtual(pTab)==0 ){ + Index *pIdx; /* PRIMARY KEY index for table pTab */ + + pIdx = sqlite4FindPrimaryKey(pTab, 0); + sqlite4TableLock(p, iDb, pIdx->tnum, (opcode==OP_OpenWrite), pTab->zName); + sqlite4OpenIndex(p, iCur, iDb, pIdx, opcode); + assert( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ); + } } /* ** Return a pointer to the column affinity string associated with index ** pIdx. A column affinity string has one character for each column in -** the table, according to the affinity of the column: +** the index key. If the index is the PRIMARY KEY of its table, the key +** consists of the index columns only. Otherwise, it consists of the +** indexed columns, followed by the columns that make up the tables PRIMARY +** KEY. For each column in the index key, the corresponding character of +** the affinity string is set according to the column affinity, as follows: ** ** Character Column affinity ** ------------------------------ ** 'a' TEXT ** 'b' NONE ** 'c' NUMERIC ** 'd' INTEGER ** 'e' REAL ** -** An extra 'd' is appended to the end of the string to cover the -** rowid that appears as the last column in every index. -** ** Memory for the buffer containing the column index affinity string ** is managed along with the rest of the Index structure. It will be ** released when sqlite4DeleteIndex() is called. */ const char *sqlite4IndexAffinityStr(Vdbe *v, Index *pIdx){ + /* The first time a column affinity string for a particular index is + ** required, it is allocated and populated here. It is then stored as + ** a member of the Index structure for subsequent use. The column + ** affinity string will eventually be deleted by sqliteDeleteIndex() + ** when the Index structure itself is cleaned up. */ if( !pIdx->zColAff ){ - /* The first time a column affinity string for a particular index is - ** required, it is allocated and populated here. It is then stored as - ** a member of the Index structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqliteDeleteIndex() when the Index structure itself is cleaned - ** up. - */ - int n; - Table *pTab = pIdx->pTable; sqlite4 *db = sqlite4VdbeDb(v); - pIdx->zColAff = (char *)sqlite4DbMallocRaw(0, pIdx->nColumn+2); - if( !pIdx->zColAff ){ + Table *pTab = pIdx->pTable; /* Table pIdx is attached to */ + int n; /* Iterator variable for zAff */ + Index *pPk; /* Primary key on same table as pIdx */ + Index *p; /* Iterator variable */ + char *zAff; /* Affinity string to populate and return */ + int nAff; /* Characters in zAff */ + + /* Determine how many characters are in the affinity string. There is + ** one character for each indexed column, and, if the index is not itself + ** the primary key, one character for each column in the primary key + ** of the table pIdx indexes. */ + nAff = pIdx->nColumn; + pPk = sqlite4FindPrimaryKey(pTab, 0); + if( pIdx!=pPk ){ + nAff += pPk->nColumn; + } + + /* Allocate space for the affinity string */ + zAff = pIdx->zColAff = (char *)sqlite4DbMallocRaw(0, nAff+1); + if( !zAff ){ db->mallocFailed = 1; return 0; } - for(n=0; nnColumn; n++){ - pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; + + /* Populate the affinity string. This loop runs either once or twice. + ** The first iteration populates zAff with affinities according to the + ** columns indexed by pIdx. If pIdx is not itself the table's primary + ** key, then the second iteration of the loop adds the primary key + ** columns to zAff. */ + for(n=0, p=pIdx; p; p=(p==pPk ? 0 : pPk)){ + int i; + for(i=0; inColumn; i++){ + int iCol = p->aiColumn[i]; + zAff[n++] = (iCol<0) ? SQLITE_AFF_INTEGER : pTab->aCol[iCol].affinity; + } } - pIdx->zColAff[n++] = SQLITE_AFF_INTEGER; - pIdx->zColAff[n] = 0; + zAff[n] = 0; } return pIdx->zColAff; } @@ -145,13 +209,10 @@ VdbeOp *pOp = sqlite4VdbeGetOp(v, i); assert( pOp!=0 ); if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ Index *pIndex; int tnum = pOp->p2; - if( tnum==pTab->tnum ){ - return 1; - } for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ if( tnum==pIndex->tnum ){ return 1; } } @@ -462,14 +523,20 @@ /* Register allocations */ int regFromSelect = 0;/* Base register for data coming from SELECT */ int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */ int regRowCount = 0; /* Memory cell used for the row counter */ int regIns; /* Block of regs holding rowid+data being inserted */ - int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ + + int iPk; /* Cursor offset of PK index cursor */ + Index *pPk; /* Primary key for table pTab */ + int bImplicitPK; /* True if table pTab has an implicit PK */ + int regContent; /* First register in column value array */ + int regRowid; /* If bImplicitPK, register holding IPK */ + #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ int tmask; /* Mask of trigger times */ @@ -479,12 +546,11 @@ memset(&dest, 0, sizeof(dest)); if( pParse->nErr || db->mallocFailed ){ goto insert_cleanup; } - /* Locate the table into which we will be inserting new information. - */ + /* Locate the table into which we will be inserting new information. */ assert( pTabList->nSrc==1 ); zTab = pTabList->a[0].zName; if( NEVER(zTab==0) ) goto insert_cleanup; pTab = sqlite4SrcListLookup(pParse, pTabList); if( pTab==0 ){ @@ -495,14 +561,21 @@ pDb = &db->aDb[iDb]; zDb = pDb->zName; if( sqlite4AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ goto insert_cleanup; } + + /* Set bImplicitPK to true for an implicit PRIMARY KEY, or false otherwise. + ** Also set pPk to point to the primary key, and iPk to the cursor offset + ** of the primary key cursor (i.e. so that the cursor opened on the primary + ** key index is VDBE cursor (baseCur+iPk). */ + pPk = sqlite4FindPrimaryKey(pTab, &iPk); + assert( (pPk==0)==IsView(pTab) ); + bImplicitPK = (pPk && pPk->aiColumn[0]==-1); /* Figure out if we have any triggers and if the table being - ** inserted into is a view - */ + ** inserted into is a view. */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = sqlite4TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); isView = pTab->pSelect!=0; #else # define pTrigger 0 @@ -515,26 +588,24 @@ #endif assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); /* If pTab is really a view, make sure it has been initialized. ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual - ** module table). - */ + ** module table). */ if( sqlite4ViewGetColumnNames(pParse, pTab) ){ goto insert_cleanup; } /* Ensure that: - * (a) the table is not read-only, - * (b) that if it is a view then ON INSERT triggers exist + ** (a) the table is not read-only (e.g. sqlite_master, sqlite_stat), and + ** (b) that if it is a view then ON INSERT triggers exist */ if( sqlite4IsReadOnly(pParse, pTab, tmask) ){ goto insert_cleanup; } - /* Allocate a VDBE - */ + /* Allocate a VDBE and begin a write transaction */ v = sqlite4GetVdbe(pParse); if( v==0 ) goto insert_cleanup; if( pParse->nested==0 ) sqlite4VdbeCountChanges(v); sqlite4BeginWriteOperation(pParse, pSelect || pTrigger, iDb); @@ -553,15 +624,10 @@ assert( pList==0 ); goto insert_end; } #endif /* SQLITE_OMIT_XFER_OPT */ - /* If this is an AUTOINCREMENT table, look up the sequence number in the - ** sqlite_sequence table and store it in memory cell regAutoinc. - */ - regAutoinc = autoIncBegin(pParse, iDb, pTab); - /* Figure out how many columns of data are supplied. If the data ** is coming from a SELECT statement, then generate a co-routine that ** produces a single row of the SELECT on each invocation. The ** co-routine is the common header to the 3rd and 4th templates. */ @@ -641,10 +707,11 @@ ** goto L ** M: ... */ int regRec; /* Register to hold packed record */ int regTempRowid; /* Register to hold temp table ROWID */ + int regTempKey; /* Register to hold key encoded rowid */ int addrTop; /* Label "L" */ int addrIf; /* Address of jump to M */ srcTab = pParse->nTab++; regRec = sqlite4GetTempReg(pParse); @@ -660,12 +727,12 @@ sqlite4ReleaseTempReg(pParse, regRec); sqlite4ReleaseTempReg(pParse, regTempRowid); } }else{ /* This is the case if the data for the INSERT is coming from a VALUES - ** clause - */ + ** (or DEFAULT VALUES) clause. Resolve all references in the VALUES(...) + ** expressions. */ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; srcTab = -1; assert( useTempTable==0 ); @@ -684,11 +751,11 @@ for(i=0; inCol; i++){ nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0); } } if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){ - sqlite4ErrorMsg(pParse, + sqlite4ErrorMsg(pParse, "table %S has %d columns but %d values were supplied", pTabList, 0, pTab->nCol-nHidden, nColumn); goto insert_cleanup; } if( pColumn!=0 && nColumn!=pColumn->nId ){ @@ -695,68 +762,48 @@ sqlite4ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); goto insert_cleanup; } /* If the INSERT statement included an IDLIST term, then make sure - ** all elements of the IDLIST really are columns of the table and - ** remember the column indices. - ** - ** If the table has an INTEGER PRIMARY KEY column and that column - ** is named in the IDLIST, then record in the keyColumn variable - ** the index into IDLIST of the primary key column. keyColumn is - ** the index of the primary key as it appears in IDLIST, not as - ** is appears in the original table. (The index of the primary - ** key in the original table is pTab->iPKey.) + ** all elements of the IDLIST really are columns of the table. Set + ** the pColumn->a[iCol].idx variables to indicate which column of the + ** table each IDLIST element corresponds to. */ if( pColumn ){ for(i=0; inId; i++){ pColumn->a[i].idx = -1; } for(i=0; inId; i++){ + char *zTest = pColumn->a[i].zName; for(j=0; jnCol; j++){ - if( sqlite4StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ + if( sqlite4StrICmp(zTest, pTab->aCol[j].zName)==0 ){ pColumn->a[i].idx = j; - if( j==pTab->iPKey ){ - keyColumn = i; - } break; } } - if( j>=pTab->nCol ){ - if( sqlite4IsRowid(pColumn->a[i].zName) ){ - keyColumn = i; - }else{ - sqlite4ErrorMsg(pParse, "table %S has no column named %s", + if( j==pTab->nCol ){ + sqlite4ErrorMsg(pParse, "table %S has no column named %s", pTabList, 0, pColumn->a[i].zName); - pParse->checkSchema = 1; - goto insert_cleanup; - } + pParse->checkSchema = 1; + goto insert_cleanup; } } } - /* If there is no IDLIST term but the table has an integer primary - ** key, the set the keyColumn variable to the primary key column index - ** in the original table definition. - */ - if( pColumn==0 && nColumn>0 ){ - keyColumn = pTab->iPKey; - } - - /* Initialize the count of rows to be inserted - */ - if( db->flags & SQLITE_CountRows ){ - regRowCount = ++pParse->nMem; - sqlite4VdbeAddOp2(v, OP_Integer, 0, regRowCount); - } - - /* If this is not a view, open the table and and all indices */ + /* If this is not a view, open a write cursor on each index. Allocate + ** a contiguous array of (nIdx+1) registers, where nIdx is the total + ** number of indexes (including the PRIMARY KEY index). + ** + ** Register aRegIdx[0]: The PRIMARY KEY index key + ** Register aRegIdx[1..nIdx-1]: Keys for other table indexes + ** Register aRegIdx[nIdx]: Data record for table row. + */ if( !isView ){ int nIdx; baseCur = pParse->nTab; - nIdx = sqlite4OpenTableAndIndices(pParse, pTab, baseCur, OP_OpenWrite); + nIdx = sqlite4OpenAllIndexes(pParse, pTab, baseCur, OP_OpenWrite); aRegIdx = sqlite4DbMallocRaw(db, sizeof(int)*(nIdx+1)); if( aRegIdx==0 ){ goto insert_cleanup; } for(i=0; inMem+1; - pParse->nMem += pTab->nCol + 1; + /* Allocate an array of registers in which to assemble the values for the + ** new row. If the table has an explicit primary key, we need one register + ** for each table column. If the table uses an implicit primary key, the + ** nCol+1 registers are required. */ + regRowid = ++pParse->nMem; + regContent = pParse->nMem+1; + pParse->nMem += pTab->nCol; + if( IsVirtual(pTab) ){ + /* TODO: Fix this */ + regContent++; regRowid++; pParse->nMem++; } - regData = regRowid+1; - - /* Run the BEFORE and INSTEAD OF triggers, if there are any - */ - endOfLoop = sqlite4VdbeMakeLabel(v); - if( tmask & TRIGGER_BEFORE ){ - int regCols = sqlite4GetTempRange(pParse, pTab->nCol+1); - - /* build the NEW.* reference row. Note that if there is an INTEGER - ** PRIMARY KEY into which a NULL is being inserted, that NULL will be - ** translated into a unique ID for the row. But on a BEFORE trigger, - ** we do not know what the unique ID will be (because the insert has - ** not happened yet) so we substitute a rowid of -1 - */ - if( keyColumn<0 ){ - sqlite4VdbeAddOp2(v, OP_Integer, -1, regCols); - }else{ - int j1; - if( useTempTable ){ - sqlite4VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols); - }else{ - assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite4ExprCode(pParse, pList->a[keyColumn].pExpr, regCols); - } - j1 = sqlite4VdbeAddOp1(v, OP_NotNull, regCols); - sqlite4VdbeAddOp2(v, OP_Integer, -1, regCols); - sqlite4VdbeJumpHere(v, j1); - sqlite4VdbeAddOp1(v, OP_MustBeInt, regCols); - } - - /* Cannot have triggers on a virtual table. If it were possible, - ** this block would have to account for hidden column. - */ - assert( !IsVirtual(pTab) ); - - /* Create the new column data - */ - for(i=0; inCol; i++){ - if( pColumn==0 ){ - j = i; - }else{ - for(j=0; jnId; j++){ - if( pColumn->a[j].idx==i ) break; - } - } - if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId) ){ - sqlite4ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1); - }else if( useTempTable ){ - sqlite4VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1); - }else{ - assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite4ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1); - } - } - - /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger, - ** do not attempt any conversions before assembling the record. - ** If this is a real table, attempt conversions as required by the - ** table column affinities. - */ - if( !isView ){ - sqlite4VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol); - sqlite4TableAffinityStr(v, pTab); - } - - /* Fire BEFORE or INSTEAD OF triggers */ - sqlite4CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, - pTab, regCols-pTab->nCol-1, onError, endOfLoop); - - sqlite4ReleaseTempRange(pParse, regCols, pTab->nCol+1); - } - - /* Push the record number for the new entry onto the stack. The - ** record number is a randomly generate integer created by NewRowid - ** except when the table has an INTEGER PRIMARY KEY column, in which - ** case the record number is the same as that column. - */ - if( !isView ){ - if( IsVirtual(pTab) ){ - /* The row that the VUpdate opcode will delete: none */ - sqlite4VdbeAddOp2(v, OP_Null, 0, regIns); - } - if( keyColumn>=0 ){ - if( useTempTable ){ - sqlite4VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid); - }else if( pSelect ){ - sqlite4VdbeAddOp2(v, OP_SCopy, regFromSelect+keyColumn, regRowid); - }else{ - VdbeOp *pOp; - sqlite4ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid); - pOp = sqlite4VdbeGetOp(v, -1); - if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){ - appendFlag = 1; - pOp->opcode = OP_NewRowid; - pOp->p1 = baseCur; - pOp->p2 = regRowid; - pOp->p3 = regAutoinc; - } - } - /* If the PRIMARY KEY expression is NULL, then use OP_NewRowid - ** to generate a unique primary key value. - */ - if( !appendFlag ){ - int j1; - if( !IsVirtual(pTab) ){ - j1 = sqlite4VdbeAddOp1(v, OP_NotNull, regRowid); - sqlite4VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); - sqlite4VdbeJumpHere(v, j1); - }else{ - j1 = sqlite4VdbeCurrentAddr(v); - sqlite4VdbeAddOp2(v, OP_IsNull, regRowid, j1+2); - } - sqlite4VdbeAddOp1(v, OP_MustBeInt, regRowid); - } - }else if( IsVirtual(pTab) ){ - sqlite4VdbeAddOp2(v, OP_Null, 0, regRowid); - }else{ - sqlite4VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); - appendFlag = 1; - } - autoIncStep(pParse, regAutoinc, regRowid); - - /* Push onto the stack, data for all columns of the new entry, beginning - ** with the first column. - */ - nHidden = 0; - for(i=0; inCol; i++){ - int iRegStore = regRowid+1+i; - if( i==pTab->iPKey ){ - /* The value of the INTEGER PRIMARY KEY column is always a NULL. - ** Whenever this column is read, the record number will be substituted - ** in its place. So will fill this column with a NULL to avoid - ** taking up data space with information that will never be used. */ - sqlite4VdbeAddOp2(v, OP_Null, 0, iRegStore); - continue; - } - if( pColumn==0 ){ - if( IsHiddenColumn(&pTab->aCol[i]) ){ - assert( IsVirtual(pTab) ); - j = -1; - nHidden++; - }else{ - j = i - nHidden; - } - }else{ - for(j=0; jnId; j++){ - if( pColumn->a[j].idx==i ) break; - } - } - if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){ - sqlite4ExprCode(pParse, pTab->aCol[i].pDflt, iRegStore); - }else if( useTempTable ){ - sqlite4VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore); - }else if( pSelect ){ - sqlite4VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore); - }else{ - sqlite4ExprCode(pParse, pList->a[j].pExpr, iRegStore); - } - } - - /* Generate code to check constraints and generate index keys and - ** do the insertion. - */ + + endOfLoop = sqlite4VdbeMakeLabel(v); + + for(i=0; inCol; i++){ + j = i; + if( pColumn ){ + for(j=0; jnId; j++){ + if( pColumn->a[j].idx==i ) break; + } + } + + if( nColumn==0 || (pColumn && j>=pColumn->nId) ){ + sqlite4ExprCode(pParse, pTab->aCol[i].pDflt, regContent+i); + }else if( useTempTable ){ + sqlite4VdbeAddOp3(v, OP_Column, srcTab, j, regContent+i); + }else if( pSelect ){ + sqlite4VdbeAddOp2(v, OP_SCopy, regFromSelect+j, regContent+i); + }else{ + assert( pSelect==0 ); /* Otherwise useTempTable is true */ + sqlite4ExprCodeAndCache(pParse, pList->a[j].pExpr, regContent+i); + } + } + + if( !isView ){ + sqlite4VdbeAddOp2(v, OP_Affinity, regContent, pTab->nCol); + sqlite4TableAffinityStr(v, pTab); + } + + /* Fire BEFORE or INSTEAD OF triggers */ + if( pTrigger ){ + sqlite4VdbeAddOp2(v, OP_Integer, -1, regRowid); + VdbeComment((v, "new.rowid value for BEFORE triggers")); + sqlite4CodeRowTrigger( + pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, + pTab, (regRowid - pTab->nCol - 1), onError, endOfLoop + ); + } + + if( bImplicitPK ){ + assert( !isView ); + sqlite4VdbeAddOp2(v, OP_NewRowid, baseCur+iPk, regRowid); + } + + if( !isView ){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite4GetVTable(db, pTab); sqlite4VtabMakeWritable(pParse, pTab); sqlite4VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); @@ -972,32 +905,28 @@ sqlite4VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite4MayAbort(pParse); }else #endif { + /* Generate code to check constraints and generate index keys and + ** do the insertion. */ int isReplace; /* Set to true if constraints may cause a replace */ - sqlite4GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx, - keyColumn>=0, 0, onError, endOfLoop, &isReplace + sqlite4GenerateConstraintChecks(pParse, pTab, baseCur, + regContent, aRegIdx, 0, 0, onError, endOfLoop, &isReplace ); - sqlite4FkCheck(pParse, pTab, 0, regIns); - sqlite4CompleteInsertion( - pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0 + sqlite4FkCheck(pParse, pTab, 0, regContent); + sqlite4CompleteInsertion(pParse, pTab, baseCur, + regContent, aRegIdx, 0, appendFlag, isReplace==0 ); } } - /* Update the count of rows that are inserted - */ - if( (db->flags & SQLITE_CountRows)!=0 ){ - sqlite4VdbeAddOp2(v, OP_AddImm, regRowCount, 1); - } - - if( pTrigger ){ - /* Code AFTER triggers */ - sqlite4CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, - pTab, regData-2-pTab->nCol, onError, endOfLoop); - } + /* Code AFTER triggers */ + sqlite4CodeRowTrigger( + pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, + pTab, regRowid - pTab->nCol - 1, onError, endOfLoop + ); /* The bottom of the main insertion loop, if the data source ** is a SELECT statement. */ sqlite4VdbeResolveLabel(v, endOfLoop); @@ -1010,12 +939,11 @@ sqlite4VdbeJumpHere(v, addrInsTop); } if( !IsVirtual(pTab) && !isView ){ /* Close all tables opened */ - sqlite4VdbeAddOp1(v, OP_Close, baseCur); - for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ + for(idx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ sqlite4VdbeAddOp1(v, OP_Close, idx+baseCur); } } insert_end: @@ -1025,21 +953,10 @@ */ if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite4AutoincrementEnd(pParse); } - /* - ** Return the number of rows inserted. If this routine is - ** generating code because of a call to sqlite4NestedParse(), do not - ** invoke the callback function. - */ - if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ - sqlite4VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); - } - insert_cleanup: sqlite4SrcListDelete(db, pTabList); sqlite4ExprListDelete(db, pList); sqlite4SelectDelete(db, pSelect); sqlite4IdListDelete(db, pColumn); @@ -1057,12 +974,186 @@ #endif #ifdef tmask #undef tmask #endif +/* +** Return the name of the iCol'th column in index pIdx. +*/ +const char *indexColumnName(Index *pIdx, int iCol){ + int iTbl = pIdx->aiColumn[iCol]; + assert( iTbl>=-1 && iTblpTable->nCol ); + if( iTbl<0 ){ + assert( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY && pIdx->nColumn==1 ); + return "rowid"; + } + return pIdx->pTable->aCol[iTbl].zName; +} + +static void generateNotNullChecks( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table to generate checks for */ + int regContent, /* Index of the range of input registers */ + int overrideError, /* Override default OE_* with this */ + int ignoreDest /* Jump to this lable if OE_Ignore */ +){ + Vdbe *v = pParse->pVdbe; + int i; + + for(i=0; inCol; i++){ + int onError = pTab->aCol[i].notNull; + if( onError ){ + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){ + onError = OE_Abort; + } + + switch( onError ){ + case OE_Abort: + sqlite4MayAbort(pParse); + case OE_Rollback: + case OE_Fail: { + char *zMsg = sqlite4MPrintf(pParse->db, "%s.%s may not be NULL", + pTab->zName, pTab->aCol[i].zName + ); + sqlite4VdbeAddOp4(v, OP_HaltIfNull, + SQLITE_CONSTRAINT, onError, regContent+i, zMsg, P4_DYNAMIC + ); + break; + } + + case OE_Ignore: + sqlite4VdbeAddOp2(v, OP_IsNull, regContent+i, ignoreDest); + break; + + default: { + int j1 = sqlite4VdbeAddOp1(v, OP_NotNull, regContent+i); + sqlite4ExprCode(pParse, pTab->aCol[i].pDflt, regContent+i); + sqlite4VdbeJumpHere(v, j1); + assert( onError==OE_Replace ); + break; + } + } + } + } +} + +#ifndef SQLITE_OMIT_CHECK +static void generateCheckChecks( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table to generate checks for */ + int regContent, /* Index of the range of input registers */ + int overrideError, /* Override default OE_* with this */ + int ignoreDest /* Jump to this lable if OE_Ignore */ +){ + Vdbe *v = pParse->pVdbe; + + if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){ + int onError; + int allOk = sqlite4VdbeMakeLabel(v); + pParse->ckBase = regContent; + sqlite4ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL); + onError = overrideError!=OE_Default ? overrideError : OE_Abort; + if( onError==OE_Ignore ){ + sqlite4VdbeAddOp2(v, OP_Goto, 0, ignoreDest); + }else{ + if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ + sqlite4HaltConstraint(pParse, onError, 0, 0); + } + sqlite4VdbeResolveLabel(v, allOk); + } +} +#else /* !defined(SQLITE_OMIT_CHECK) */ +# define generateCheckChecks(a,b,c,d,e) +#endif + +Index *sqlite4FindPrimaryKey( + Table *pTab, /* Table to locate primary key for */ + int *piPk /* OUT: Index of PRIMARY KEY */ +){ + Index *p; + int iPk = 0; + for(p=pTab->pIndex; p && p->eIndexType!=SQLITE_INDEX_PRIMARYKEY; p=p->pNext){ + iPk++; + } + if( piPk ) *piPk = iPk; + return p; +} + +/* +** Index pIdx is a UNIQUE index. This function returns a pointer to a buffer +** containing an error message to tell the user that the UNIQUE constraint +** has failed. +** +** The returned buffer should be freed by the caller using sqlite4DbFree(). +*/ +static char *notUniqueMessage( + Parse *pParse, /* Parse context */ + Index *pIdx /* Index to generate error message for */ +){ + const int nCol = pIdx->nColumn; /* Number of columns indexed by pIdx */ + StrAccum errMsg; /* Buffer to build error message within */ + int iCol; /* Used to iterate through indexed columns */ + + sqlite4StrAccumInit(&errMsg, 0, 0, 200); + errMsg.db = pParse->db; + sqlite4StrAccumAppend(&errMsg, (nCol>1 ? "columns " : "column "), -1); + for(iCol=0; iColnColumn; iCol++){ + const char *zCol = indexColumnName(pIdx, iCol); + sqlite4StrAccumAppend(&errMsg, (iCol==0 ? "" : ", "), -1); + sqlite4StrAccumAppend(&errMsg, zCol, -1); + } + sqlite4StrAccumAppend(&errMsg, (nCol>1 ? " are" : " is"), -1); + sqlite4StrAccumAppend(&errMsg, " not unique", -1); + return sqlite4StrAccumFinish(&errMsg); +} /* +** This function generates code used as part of both INSERT and UPDATE +** statements. The generated code performs two tasks: +** +** 1. Checks all NOT NULL, CHECK and UNIQUE database constraints, +** including the implicit NOT NULL and UNIQUE constraints imposed +** by the PRIMARY KEY definition. +** +** 2. Generates serialized index keys (using OP_MakeKey) for the caller +** to store in database indexes. This function does not encode the +** actual data record, just the index keys. +** +** Both INSERT and UPDATE use this function in concert with the +** sqlite4CompleteInsertion(). This function does as described above, and +** then CompleteInsertion() generates code to serialize the data record +** and do the actual inserts into the database. +** +** regContent: +** The first in an array of registers that contain the column values +** for the new row. Register regContent contains the value for the +** left-most table column, (regContent+1) contains the value for the next +** column, and so on. All entries in this array have had any required +** affinity transformations applied already. All zero-blobs have been +** expanded. +** +** If the table has an implicit primary key and aRegIdx[0] is not 0 (see +** below), register (regContent-1) is also valid. It contains the new +** implicit integer PRIMARY KEY value. +** +** aRegIdx: +** Array sized so that there is one entry for each index (including the +** PK index) attached to the database table. Entries are in the same order +** as the linked list of Index structures attached to the table. +** +** If an array entry is non-zero, it contains the register that the +** corresponding index key should be written to. If an entry is zero, then +** the corresponding index key is not required by the caller. In this case +** any UNIQUE constraint enforced by the index does not need to be checked. +** +** +** ** Generate code to do constraint checks prior to an INSERT or an UPDATE. ** ** The input is a range of consecutive registers as follows: ** ** 1. The rowid of the row after the update. @@ -1136,278 +1227,145 @@ ** cursors do not need to be open for indices where aRegIdx[i]==0. */ void sqlite4GenerateConstraintChecks( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ - int baseCur, /* Index of a read/write cursor pointing at pTab */ - int regRowid, /* Index of the range of input registers */ + int baseCur, /* First in array of cursors for pTab indexes */ + int regContent, /* Index of the range of input registers */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ - int rowidChng, /* True if the rowid might collide with existing entry */ + int regOldKey, /* For an update, the original encoded PK */ int isUpdate, /* True for UPDATE, False for INSERT */ int overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ ){ + u8 aPkRoot[10]; /* Root page number for pPk as a varint */ + int nPkRoot; /* Size of aPkRoot in bytes */ + Index *pPk; /* Primary key index for table pTab */ int i; /* loop counter */ Vdbe *v; /* VDBE under constrution */ int nCol; /* Number of columns */ int onError; /* Conflict resolution strategy */ - int j1; /* Addresss of jump instruction */ - int j2 = 0, j3; /* Addresses of jump instructions */ - int regData; /* Register containing first data column */ int iCur; /* Table cursor number */ Index *pIdx; /* Pointer to one of the indices */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ - int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; v = sqlite4GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ nCol = pTab->nCol; - regData = regRowid + 1; - - /* Test all NOT NULL constraints. - */ - for(i=0; iiPKey ){ - continue; - } - onError = pTab->aCol[i].notNull; - if( onError==OE_None ) continue; - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){ - onError = OE_Abort; - } - assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail - || onError==OE_Ignore || onError==OE_Replace ); - switch( onError ){ - case OE_Abort: - sqlite4MayAbort(pParse); - case OE_Rollback: - case OE_Fail: { - char *zMsg; - sqlite4VdbeAddOp3(v, OP_HaltIfNull, - SQLITE_CONSTRAINT, onError, regData+i); - zMsg = sqlite4MPrintf(pParse->db, "%s.%s may not be NULL", - pTab->zName, pTab->aCol[i].zName); - sqlite4VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); - break; - } - case OE_Ignore: { - sqlite4VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest); - break; - } - default: { - assert( onError==OE_Replace ); - j1 = sqlite4VdbeAddOp1(v, OP_NotNull, regData+i); - sqlite4ExprCode(pParse, pTab->aCol[i].pDflt, regData+i); - sqlite4VdbeJumpHere(v, j1); - break; - } - } - } - - /* Test all CHECK constraints - */ -#ifndef SQLITE_OMIT_CHECK - if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){ - int allOk = sqlite4VdbeMakeLabel(v); - pParse->ckBase = regData; - sqlite4ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL); - onError = overrideError!=OE_Default ? overrideError : OE_Abort; - if( onError==OE_Ignore ){ - sqlite4VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - }else{ - if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ - sqlite4HaltConstraint(pParse, onError, 0, 0); - } - sqlite4VdbeResolveLabel(v, allOk); - } -#endif /* !defined(SQLITE_OMIT_CHECK) */ - - /* If we have an INTEGER PRIMARY KEY, make sure the primary key - ** of the new record does not previously exist. Except, if this - ** is an UPDATE and the primary key is not changing, that is OK. - */ - if( rowidChng ){ - onError = pTab->keyConf; - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - - if( isUpdate ){ - j2 = sqlite4VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng); - } - j3 = sqlite4VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid); - switch( onError ){ - default: { - onError = OE_Abort; - /* Fall thru into the next case */ - } - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - sqlite4HaltConstraint( - pParse, onError, "PRIMARY KEY must be unique", P4_STATIC); - break; - } - case OE_Replace: { - /* If there are DELETE triggers on this table and the - ** recursive-triggers flag is set, call GenerateRowDelete() to - ** remove the conflicting row from the the table. This will fire - ** the triggers and remove both the table and index b-tree entries. - ** - ** Otherwise, if there are no triggers or the recursive-triggers - ** flag is not set, but the table has one or more indexes, call - ** GenerateRowIndexDelete(). This removes the index b-tree entries - ** only. The table b-tree entry will be replaced by the new entry - ** when it is inserted. - ** - ** If either GenerateRowDelete() or GenerateRowIndexDelete() is called, - ** also invoke MultiWrite() to indicate that this VDBE may require - ** statement rollback (if the statement is aborted after the delete - ** takes place). Earlier versions called sqlite4MultiWrite() regardless, - ** but being more selective here allows statements like: - ** - ** REPLACE INTO t(rowid) VALUES($newrowid) - ** - ** to run without a statement journal if there are no indexes on the - ** table. - */ - Trigger *pTrigger = 0; - if( pParse->db->flags&SQLITE_RecTriggers ){ - pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - } - if( pTrigger || sqlite4FkRequired(pParse, pTab, 0, 0) ){ - sqlite4MultiWrite(pParse); - sqlite4GenerateRowDelete( - pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace - ); - }else if( pTab->pIndex ){ - sqlite4MultiWrite(pParse); - sqlite4GenerateRowIndexDelete(pParse, pTab, baseCur, 0); - } - seenReplace = 1; - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite4VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - break; - } - } - sqlite4VdbeJumpHere(v, j3); - if( isUpdate ){ - sqlite4VdbeJumpHere(v, j2); - } - } + pPk = sqlite4FindPrimaryKey(pTab, 0); + nPkRoot = sqlite4PutVarint64(aPkRoot, pPk->tnum); + + assert( pPk->eIndexType==SQLITE_INDEX_PRIMARYKEY ); + + /* Test all NOT NULL constraints. */ + generateNotNullChecks(pParse, pTab, regContent, overrideError, ignoreDest); + + /* Test all CHECK constraints */ + generateCheckChecks(pParse, pTab, regContent, overrideError, ignoreDest); /* Test all UNIQUE constraints by creating entries for each UNIQUE ** index and making sure that duplicate entries do not already exist. ** Add the new records to the indices as we go. */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ - int regIdx; - int regR; + int nTmpReg; /* Number of temp registers required */ + int regTmp; /* First temp register allocated */ + int regPk; /* PK of conflicting row (for REPLACE) */ if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ - /* Create a key for accessing the index entry */ - regIdx = sqlite4GetTempRange(pParse, pIdx->nColumn+1); + /* Create an index key. Primary key indexes consists of just the primary + ** key values. Other indexes consists of the indexed columns followed by + ** the primary key values. */ + nTmpReg = 1 + pIdx->nColumn + (pIdx==pPk ? 0 : pPk->nColumn); + regTmp = sqlite4GetTempRange(pParse, nTmpReg); + regPk = regTmp + nTmpReg - 1; + for(i=0; inColumn; i++){ int idx = pIdx->aiColumn[i]; - if( idx==pTab->iPKey ){ - sqlite4VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); - }else{ - sqlite4VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i); - } - } - sqlite4VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); - sqlite4VdbeAddOp2(v, OP_MakeKey, baseCur+iCur+1, aRegIdx[iCur]+1); - sqlite4VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); - sqlite4VdbeChangeP4(v, -1, sqlite4IndexAffinityStr(v, pIdx), P4_TRANSIENT); - sqlite4ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); - - /* Find out what action to take in case there is an indexing conflict */ - onError = pIdx->onError; - if( onError==OE_None ){ - sqlite4ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); - continue; /* pIdx is not a UNIQUE index */ - } - if( overrideError!=OE_Default ){ - onError = overrideError; - }else if( onError==OE_Default ){ - onError = OE_Abort; - } - if( seenReplace ){ - if( onError==OE_Ignore ) onError = OE_Replace; - else if( onError==OE_Fail ) onError = OE_Abort; - } - - /* Check to see if the new index entry will be unique */ - regR = sqlite4GetTempReg(pParse); - sqlite4VdbeAddOp2(v, OP_SCopy, regOldRowid, regR); - j3 = sqlite4VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0, - regR, SQLITE_INT_TO_PTR(regIdx), - P4_INT32); - sqlite4ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); - - /* Generate code that executes if the new index entry is not unique */ - assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail - || onError==OE_Ignore || onError==OE_Replace ); - switch( onError ){ - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - int j; - StrAccum errMsg; - const char *zSep; - char *zErr; - - sqlite4StrAccumInit(&errMsg, 0, 0, 200); - errMsg.db = pParse->db; - zSep = pIdx->nColumn>1 ? "columns " : "column "; - for(j=0; jnColumn; j++){ - char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; - sqlite4StrAccumAppend(&errMsg, zSep, -1); - zSep = ", "; - sqlite4StrAccumAppend(&errMsg, zCol, -1); - } - sqlite4StrAccumAppend(&errMsg, - pIdx->nColumn>1 ? " are not unique" : " is not unique", -1); - zErr = sqlite4StrAccumFinish(&errMsg); - sqlite4HaltConstraint(pParse, onError, zErr, 0); - sqlite4DbFree(errMsg.db, zErr); - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite4VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - break; - } - default: { - Trigger *pTrigger = 0; - assert( onError==OE_Replace ); - sqlite4MultiWrite(pParse); - if( pParse->db->flags&SQLITE_RecTriggers ){ - pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - } - sqlite4GenerateRowDelete( - pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace - ); - seenReplace = 1; - break; - } - } - sqlite4VdbeJumpHere(v, j3); - sqlite4ReleaseTempReg(pParse, regR); + sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i); + } + if( pIdx!=pPk ){ + for(i=0; inColumn; i++){ + int idx = pPk->aiColumn[i]; + sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i+pIdx->nColumn); + } + } + sqlite4VdbeAddOp3(v, OP_MakeIdxKey, baseCur+iCur, regTmp, aRegIdx[iCur]); + VdbeComment((v, "key for %s", pIdx->zName)); + + /* If Index.onError==OE_None, then pIdx is not a UNIQUE or PRIMARY KEY + ** index. In this case there is no need to test the index for uniqueness + ** - all that is required is to populate the aRegIdx[iCur] register. Jump + ** to the next iteration of the loop if this is the case. */ + onError = pIdx->onError; + if( onError!=OE_None ){ + int iTest; /* Address of OP_IsUnique instruction */ + int iTest2 = 0; /* Address of OP_Eq instruction */ + int regOut = 0; /* PK of row to replace */ + + /* Figure out what to do if a UNIQUE constraint is encountered. + ** + ** TODO: If a previous constraint is a REPLACE, why change IGNORE to + ** REPLACE and FAIL to ABORT here? */ + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + if( seenReplace ){ + if( onError==OE_Ignore ) onError = OE_Replace; + else if( onError==OE_Fail ) onError = OE_Abort; + } + + if( onError==OE_Replace ){ + sqlite4VdbeAddOp3(v, OP_Blob, nPkRoot, regPk, 0); + sqlite4VdbeChangeP4(v, -1, aPkRoot, nPkRoot); + regOut = regPk; + } + if( regOldKey && pIdx==pPk ){ + iTest2 = sqlite4VdbeAddOp3(v, OP_Eq, regOldKey, 0, aRegIdx[iCur]); + } + iTest = sqlite4VdbeAddOp3(v, OP_IsUnique, baseCur+iCur, 0, aRegIdx[iCur]); + sqlite4VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(regOut), P4_INT32); + + switch( onError ){ + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + char *zErr = notUniqueMessage(pParse, pIdx); + sqlite4HaltConstraint(pParse, onError, zErr, 0); + sqlite4DbFree(pParse->db, zErr); + break; + } + + case OE_Ignore: { + assert( seenReplace==0 ); + sqlite4VdbeAddOp2(v, OP_Goto, 0, ignoreDest); + break; + } + default: { + Trigger *pTrigger; + assert( onError==OE_Replace ); + sqlite4MultiWrite(pParse); + pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0); + sqlite4GenerateRowDelete( + pParse, pTab, baseCur, regOut, 0, pTrigger, OE_Replace + ); + seenReplace = 1; + break; + } + } + + /* If the OP_IsUnique passes (no constraint violation) jump here */ + sqlite4VdbeJumpHere(v, iTest); + if( iTest2 ) sqlite4VdbeJumpHere(v, iTest2); + } + + sqlite4ReleaseTempRange(pParse, regTmp, nTmpReg); } if( pbMayReplace ){ *pbMayReplace = seenReplace; } @@ -1414,103 +1372,118 @@ } /* ** This routine generates code to finish the INSERT or UPDATE operation ** that was started by a prior call to sqlite4GenerateConstraintChecks. -** A consecutive range of registers starting at regRowid contains the -** rowid and the content to be inserted. -** ** The arguments to this routine should be the same as the first six ** arguments to sqlite4GenerateConstraintChecks. +** +** Argument regContent points to the first in a contiguous array of +** registers that contain the row content. This function uses OP_MakeRecord +** to encode them into a record before inserting them into the database. +** +** The array aRegIdx[] contains one entry for each index attached to +** the table, in the same order as the Table.pIndex linked list. If an +** aRegIdx[] entry is 0, this indicates that the entry in the corresponding +** index does not need to be modified. Otherwise, it is the number of +** a register containing the serialized key to insert into the index. +** aRegIdx[0] (the PRIMARY KEY index key) is never 0. */ void sqlite4CompleteInsertion( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int baseCur, /* Index of a read/write cursor pointing at pTab */ - int regRowid, /* Range of content */ + int regContent, /* First register of content */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ int isUpdate, /* True for UPDATE, False for INSERT */ int appendBias, /* True if this is likely to be an append */ int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */ ){ int i; Vdbe *v; - int nIdx; Index *pIdx; u8 pik_flags; - int regData; int regRec; v = sqlite4GetVdbe(pParse); + assert( aRegIdx[0] ); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} - for(i=nIdx-1; i>=0; i--){ - if( aRegIdx[i]==0 ) continue; - sqlite4VdbeAddOp3(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i], aRegIdx[i]+1); - if( useSeekResult ){ - sqlite4VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - } - } - regData = regRowid + 1; - regRec = sqlite4GetTempReg(pParse); - sqlite4VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); - sqlite4TableAffinityStr(v, pTab); - sqlite4ExprCacheAffinityChange(pParse, regData, pTab->nCol); + if( pParse->nested ){ pik_flags = 0; }else{ - pik_flags = OPFLAG_NCHANGE; - pik_flags |= (isUpdate?OPFLAG_ISUPDATE:OPFLAG_LASTROWID); - } - if( appendBias ){ - pik_flags |= OPFLAG_APPEND; - } - if( useSeekResult ){ - pik_flags |= OPFLAG_USESEEKRESULT; - } - sqlite4VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid); - if( !pParse->nested ){ - sqlite4VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); - } - sqlite4VdbeChangeP5(v, pik_flags); + pik_flags = OPFLAG_NCHANGE | (isUpdate?OPFLAG_ISUPDATE:0); + } + + /* Generate code to serialize array of registers into a database record. */ + regRec = sqlite4GetTempReg(pParse); + sqlite4VdbeAddOp3(v, OP_MakeRecord, regContent, pTab->nCol, regRec); + sqlite4TableAffinityStr(v, pTab); + sqlite4ExprCacheAffinityChange(pParse, regContent, pTab->nCol); + + /* Write the entry to each index. */ + for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + if( aRegIdx[i] ){ + int regData = 0; + int flags = 0; + if( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){ + regData = regRec; + flags = pik_flags; + } + sqlite4VdbeAddOp3(v, OP_IdxInsert, baseCur+i, regData, aRegIdx[i]); + sqlite4VdbeChangeP5(v, flags); + } + } } /* ** Generate code that will open cursors for a table and for all ** indices of that table. The "baseCur" parameter is the cursor number used ** for the table. Indices are opened on subsequent cursors. ** ** Return the number of indices on the table. */ -int sqlite4OpenTableAndIndices( +int sqlite4OpenAllIndexes( Parse *pParse, /* Parsing context */ Table *pTab, /* Table to be opened */ int baseCur, /* Cursor number assigned to the table */ int op /* OP_OpenRead or OP_OpenWrite */ ){ + int i = 0; + if( IsVirtual(pTab)==0 ){ + int iDb; + Index *pIdx; + + iDb = sqlite4SchemaToIndex(pParse->db, pTab->pSchema); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + sqlite4OpenIndex(pParse, baseCur+i, iDb, pIdx, op); + i++; + } + if( pParse->nTabnTab = baseCur+i; + } + } + return i; +} + +void sqlite4CloseAllIndexes( + Parse *pParse, + Table *pTab, + int baseCur +){ int i; - int iDb; Index *pIdx; Vdbe *v; - if( IsVirtual(pTab) ) return 0; - iDb = sqlite4SchemaToIndex(pParse->db, pTab->pSchema); + assert( pTab->pIndex==0 || IsVirtual(pTab)==0 ); + assert( pTab->pIndex==0 || IsView(pTab)==0 ); + v = sqlite4GetVdbe(pParse); - assert( v!=0 ); - sqlite4OpenTable(pParse, baseCur, iDb, pTab, op); - for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - KeyInfo *pKey = sqlite4IndexKeyinfo(pParse, pIdx); - assert( pIdx->pSchema==pTab->pSchema ); - sqlite4VdbeAddOp4(v, op, i+baseCur, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIdx->zName)); - } - if( pParse->nTabnTab = baseCur+i; - } - return i-1; + for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + sqlite4VdbeAddOp1(v, OP_Close, baseCur+i); + } } #ifdef SQLITE_TEST /* @@ -1738,13 +1711,10 @@ */ if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ return 0; } #endif - if( (pParse->db->flags & SQLITE_CountRows)!=0 ){ - return 0; /* xfer opt does not play well with PRAGMA count_changes */ - } /* If we get this far, it means that the xfer optimization is at ** least a possibility, though it might only work if the destination ** table (tab1) is initially empty. */ ADDED src/kvlsm.c Index: src/kvlsm.c ================================================================== --- /dev/null +++ src/kvlsm.c @@ -0,0 +1,416 @@ +/* +** 2012 January 20 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** An in-memory key/value storage subsystem that presents the interfadce +** defined by storage.h +*/ +#include "sqliteInt.h" + +#ifdef SQLITE_ENABLE_LSM +#include "lsm.h" + +typedef struct KVLsm KVLsm; +typedef struct KVLsmCsr KVLsmCsr; + +struct KVLsm { + KVStore base; /* Base class, must be first */ + lsm_db *pDb; /* LSM database handle */ + lsm_cursor *pCsr; /* LSM cursor holding read-trans open */ +}; + +struct KVLsmCsr { + KVCursor base; /* Base class. Must be first */ + lsm_cursor *pCsr; /* LSM cursor handle */ +}; + +#define MAX(x,y) (((x)>(y)) ? (x) : (y)) + +/* +** Begin a transaction or subtransaction. +** +** If iLevel==1 then begin an outermost read transaction. +** +** If iLevel==2 then begin an outermost write transaction. +** +** If iLevel>2 then begin a nested write transaction. +** +** iLevel may not be less than 1. After this routine returns successfully +** the transaction level will be equal to iLevel. The transaction level +** must be at least 1 to read and at least 2 to write. +*/ +static int kvlsmBegin(KVStore *pKVStore, int iLevel){ + int rc = SQLITE_OK; + KVLsm *p = (KVLsm *)pKVStore; + + assert( iLevel>0 ); + if( p->pCsr==0 ){ + rc = lsm_csr_open(p->pDb, &p->pCsr); + } + if( rc==SQLITE_OK && iLevel>=2 && iLevel>=pKVStore->iTransLevel ){ + rc = lsm_begin(p->pDb, iLevel-1); + } + + if( rc==SQLITE_OK ){ + pKVStore->iTransLevel = MAX(iLevel, pKVStore->iTransLevel); + }else if( pKVStore->iTransLevel==0 ){ + lsm_csr_close(p->pCsr); + p->pCsr = 0; + } + + return rc; +} + +/* +** Commit a transaction or subtransaction. +** +** Make permanent all changes back through the most recent xBegin +** with the iLevel+1. If iLevel==0 then make all changes permanent. +** The argument iLevel will always be less than the current transaction +** level when this routine is called. +** +** Commit is divided into two phases. A rollback is still possible after +** phase one completes. In this implementation, phase one is a no-op since +** phase two cannot fail. +** +** After this routine returns successfully, the transaction level will be +** equal to iLevel. +*/ +static int kvlsmCommitPhaseOne(KVStore *pKVStore, int iLevel){ + return SQLITE_OK; +} +static int kvlsmCommitPhaseTwo(KVStore *pKVStore, int iLevel){ + int rc = SQLITE_OK; + KVLsm *p = (KVLsm *)pKVStore; + + if( pKVStore->iTransLevel>iLevel ){ + if( pKVStore->iTransLevel>=2 ){ + rc = lsm_commit(p->pDb, MAX(1, iLevel)); + } + if( iLevel==0 ){ + lsm_csr_close(p->pCsr); + p->pCsr = 0; + } + if( rc==SQLITE_OK ){ + pKVStore->iTransLevel = iLevel; + } + } + return rc; +} + +/* +** Rollback a transaction or subtransaction. +** +** Revert all uncommitted changes back through the most recent xBegin or +** xCommit with the same iLevel. If iLevel==0 then back out all uncommited +** changes. +** +** After this routine returns successfully, the transaction level will be +** equal to iLevel. +*/ +static int kvlsmRollback(KVStore *pKVStore, int iLevel){ + int rc = SQLITE_OK; + KVLsm *p = (KVLsm *)pKVStore; + + if( pKVStore->iTransLevel>iLevel ){ + if( pKVStore->iTransLevel>=2 ){ + rc = lsm_rollback(p->pDb, MAX(1, iLevel)); + } + if( iLevel==0 ){ + lsm_csr_close(p->pCsr); + p->pCsr = 0; + } + if( rc==SQLITE_OK ){ + pKVStore->iTransLevel = iLevel; + } + } + return rc; +} + +/* +** Revert a transaction back to what it was when it started. +*/ +static int kvlsmRevert(KVStore *pKVStore, int iLevel){ + return SQLITE_OK; +} + +/* +** Implementation of the xReplace(X, aKey, nKey, aData, nData) method. +** +** Insert or replace the entry with the key aKey[0..nKey-1]. The data for +** the new entry is aData[0..nData-1]. Return SQLITE_OK on success or an +** error code if the insert fails. +** +** The inputs aKey[] and aData[] are only valid until this routine +** returns. If the storage engine needs to keep that information +** long-term, it will need to make its own copy of these values. +** +** A transaction will always be active when this routine is called. +*/ +static int kvlsmReplace( + KVStore *pKVStore, + const KVByteArray *aKey, KVSize nKey, + const KVByteArray *aData, KVSize nData +){ + KVLsm *p = (KVLsm *)pKVStore; + return lsm_write(p->pDb, (void *)aKey, nKey, (void *)aData, nData); +} + +/* +** Create a new cursor object. +*/ +static int kvlsmOpenCursor(KVStore *pKVStore, KVCursor **ppKVCursor){ + int rc = SQLITE_OK; + KVLsm *p = (KVLsm *)pKVStore; + KVLsmCsr *pCsr; + + pCsr = (KVLsmCsr *)sqlite4_malloc(sizeof(KVLsmCsr)); + if( pCsr==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pCsr, 0, sizeof(KVLsmCsr)); + rc = lsm_csr_open(p->pDb, &pCsr->pCsr); + + if( rc==SQLITE_OK ){ + pCsr->base.pStore = pKVStore; + pCsr->base.pStoreVfunc = pKVStore->pStoreVfunc; + }else{ + sqlite4_free(pCsr); + pCsr = 0; + } + } + + *ppKVCursor = (KVCursor*)pCsr; + return rc; +} + +/* +** Reset a cursor +*/ +static int kvlsmReset(KVCursor *pKVCursor){ + return SQLITE_OK; +} + +/* +** Destroy a cursor object +*/ +static int kvlsmCloseCursor(KVCursor *pKVCursor){ + KVLsmCsr *pCsr = (KVLsmCsr *)pKVCursor; + lsm_csr_close(pCsr->pCsr); + sqlite4_free(pCsr); + return SQLITE_OK; +} + +/* +** Move a cursor to the next non-deleted node. +*/ +static int kvlsmNextEntry(KVCursor *pKVCursor){ + int rc; + KVLsmCsr *pCsr = (KVLsmCsr *)pKVCursor; + rc = lsm_csr_next(pCsr->pCsr); + if( rc==SQLITE_OK && lsm_csr_valid(pCsr->pCsr)==0 ){ + rc = SQLITE_NOTFOUND; + } + return rc; +} + +/* +** Move a cursor to the previous non-deleted node. +*/ +static int kvlsmPrevEntry(KVCursor *pKVCursor){ + int rc; + KVLsmCsr *pCsr = (KVLsmCsr *)pKVCursor; + rc = lsm_csr_prev(pCsr->pCsr); + if( rc==SQLITE_OK && lsm_csr_valid(pCsr->pCsr)==0 ){ + rc = SQLITE_NOTFOUND; + } + return rc; +} + +/* +** Seek a cursor. +*/ +static int kvlsmSeek( + KVCursor *pKVCursor, + const KVByteArray *aKey, + KVSize nKey, + int dir +){ + int rc; + KVLsmCsr *pCsr = (KVLsmCsr *)pKVCursor; + + assert( dir==0 || dir==1 || dir==-1 ); + assert( LSM_SEEK_EQ==0 && LSM_SEEK_GE==1 && LSM_SEEK_LE==-1 ); + + rc = lsm_csr_seek(pCsr->pCsr, (void *)aKey, nKey, dir); + if( rc==SQLITE_OK ){ + if( lsm_csr_valid(pCsr->pCsr)==0 ){ + rc = SQLITE_NOTFOUND; + }else{ + void *pDbKey; + int nDbKey; + + rc = lsm_csr_key(pCsr->pCsr, &pDbKey, &nDbKey); + if( rc==SQLITE_OK && (nDbKey!=nKey || memcmp(pDbKey, aKey, nKey)) ){ + rc = SQLITE_INEXACT; + } + } + } + + return rc; +} + +/* +** Delete the entry that the cursor is pointing to. +** +** Though the entry is "deleted", it still continues to exist as a +** phantom. Subsequent xNext or xPrev calls will work, as will +** calls to xKey and xData, thought the result from xKey and xData +** are undefined. +*/ +static int kvlsmDelete(KVCursor *pKVCursor){ + int rc; + void *pKey; + int nKey; + KVLsmCsr *pCsr = (KVLsmCsr *)pKVCursor; + + assert( lsm_csr_valid(pCsr->pCsr) ); + rc = lsm_csr_key(pCsr->pCsr, &pKey, &nKey); + if( rc==SQLITE_OK ){ + rc = lsm_delete(((KVLsm *)(pKVCursor->pStore))->pDb, pKey, nKey); + } + + return SQLITE_OK; +} + +/* +** Return the key of the node the cursor is pointing to. +*/ +static int kvlsmKey( + KVCursor *pKVCursor, /* The cursor whose key is desired */ + const KVByteArray **paKey, /* Make this point to the key */ + KVSize *pN /* Make this point to the size of the key */ +){ + KVLsmCsr *pCsr = (KVLsmCsr *)pKVCursor; + + assert( lsm_csr_valid(pCsr->pCsr) ); + return lsm_csr_key(pCsr->pCsr, (void **)paKey, (int *)pN); +} + +/* +** Return the data of the node the cursor is pointing to. +*/ +static int kvlsmData( + KVCursor *pKVCursor, /* The cursor from which to take the data */ + KVSize ofst, /* Offset into the data to begin reading */ + KVSize n, /* Number of bytes requested */ + const KVByteArray **paData, /* Pointer to the data written here */ + KVSize *pNData /* Number of bytes delivered */ +){ + KVLsmCsr *pCsr = (KVLsmCsr *)pKVCursor; + int rc; + void *pData; + int nData; + + rc = lsm_csr_value(pCsr->pCsr, &pData, &nData); + if( rc==SQLITE_OK ){ + if( n<0 ){ + *paData = pData; + *pNData = nData; + }else{ + int nOut = n; + if( (ofst+n)> nData ) nOut = nData - ofst; + if( nOut<0 ) nOut = 0; + + *paData = &((u8 *)pData)[n]; + *pNData = nOut; + } + } + + return rc; +} + +/* +** Destructor for the entire in-memory storage tree. +*/ +static int kvlsmClose(KVStore *pKVStore){ + KVLsm *p = (KVLsm *)pKVStore; + + /* If there is an active transaction, roll it back. The important + ** part is that the read-transaction cursor is closed. Otherwise, the + ** call to lsm_close() below will fail. */ + kvlsmRollback(pKVStore, 0); + assert( p->pCsr==0 ); + + lsm_close(p->pDb); + sqlite4_free(p); + return SQLITE_OK; +} + +/* +** Create a new in-memory storage engine and return a pointer to it. +*/ +int sqlite4KVStoreOpenLsm( + KVStore **ppKVStore, + const char *zName, + unsigned openFlags +){ + + /* Virtual methods for an LSM data store */ + static const KVStoreMethods kvlsmMethods = { + kvlsmReplace, + kvlsmOpenCursor, + kvlsmSeek, + kvlsmNextEntry, + kvlsmPrevEntry, + kvlsmDelete, + kvlsmKey, + kvlsmData, + kvlsmReset, + kvlsmCloseCursor, + kvlsmBegin, + kvlsmCommitPhaseOne, + kvlsmCommitPhaseTwo, + kvlsmRollback, + kvlsmRevert, + kvlsmClose + }; + + KVLsm *pNew; + int rc = SQLITE_OK; + + pNew = (KVLsm *)sqlite4_malloc(sizeof(KVLsm)); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pNew, 0, sizeof(KVLsm)); + pNew->base.pStoreVfunc = &kvlsmMethods; + + rc = lsm_new(&pNew->pDb); + if( rc==SQLITE_OK ){ + rc = lsm_open(pNew->pDb, zName); + } + + if( rc!=SQLITE_OK ){ + lsm_close(pNew->pDb); + sqlite4_free(pNew); + pNew = 0; + } + } + + *ppKVStore = (KVStore*)pNew; + return rc; +} + +#endif /* SQLITE_ENABLE_LSM */ + + + Index: src/kvmem.c ================================================================== --- src/kvmem.c +++ src/kvmem.c @@ -148,10 +148,11 @@ ** of p that points to p. Or if p is the root node, return pp. */ static KVMemNode **kvmemFromPtr(KVMemNode *p, KVMemNode **pp){ KVMemNode *pUp = p->pUp; if( pUp==0 ) return pp; + assert( pUp->pAfter==p || pUp->pBefore==p ); if( pUp->pAfter==p ) return &pUp->pAfter; return &pUp->pBefore; } /* @@ -187,11 +188,11 @@ const KVByteArray *aK1, KVSize nK1, const KVByteArray *aK2, KVSize nK2 ){ int c; c = memcmp(aK1, aK2, nK1nRef = 1; pChng = kvmemNewChng(p, pNode); if( pChng==0 ){ sqlite4_free(pNode); pNode = 0; - }else{ - pChng->pData = 0; } + assert( pChng==0 || pChng->pData==0 ); } return pNode; } + +#ifdef SQLITE_DEBUG +/* +** Return the number of times that node pNode occurs in the sub-tree +** headed by node pSub. This is used to assert() that no node structure +** is linked into the tree more than once. +*/ +static int countNodeOccurences(KVMemNode *pSub, KVMemNode *pNode){ + int iRet = (pSub==pNode); + if( pSub ){ + iRet += countNodeOccurences(pSub->pBefore, pNode); + iRet += countNodeOccurences(pSub->pAfter, pNode); + } + return iRet; +} + +/* +** Check that all the pUp pointers in the sub-tree headed by pSub are +** correct. Fail an assert if this is not the case. +*/ +static void assertUpPointers(KVMemNode *pSub){ + if( pSub ){ + assert( pSub->pBefore==0 || pSub->pBefore->pUp==pSub ); + assert( pSub->pAfter==0 || pSub->pAfter->pUp==pSub ); + assertUpPointers(pSub->pBefore); + assertUpPointers(pSub->pAfter); + } +} + +#else +#define assertUpPointers(x) +#endif /* Remove node pOld from the tree. pOld must be an element of the tree. */ static void kvmemRemoveNode(KVMem *p, KVMemNode *pOld){ - KVMemNode **ppParent; - KVMemNode *pBalance; - + KVMemNode **ppParent; /* Location of pointer to pOld */ + KVMemNode *pBalance; /* Node to run kvmemBalance() on */ kvmemDataUnref(pOld->pData); pOld->pData = 0; ppParent = kvmemFromPtr(pOld, &p->pRoot); if( pOld->pBefore==0 && pOld->pAfter==0 ){ *ppParent = 0; pBalance = pOld->pUp; }else if( pOld->pBefore && pOld->pAfter ){ - KVMemNode *pX, *pY; + KVMemNode *pX; /* Smallest node that is larger than pOld */ + KVMemNode *pY; /* Left-hand child of pOld */ pX = kvmemFirst(pOld->pAfter); - *kvmemFromPtr(pX, 0) = 0; - pBalance = pX->pUp; - pX->pAfter = pOld->pAfter; - if( pX->pAfter ){ - pX->pAfter->pUp = pX; + assert( pX->pBefore==0 ); + if( pX==pOld->pAfter ){ + pBalance = pX; }else{ - assert( pBalance==pOld ); - pBalance = pX; + *kvmemFromPtr(pX, 0) = pX->pAfter; + if( pX->pAfter ) pX->pAfter->pUp = pX->pUp; + pBalance = pX->pUp; + pX->pAfter = pOld->pAfter; + if( pX->pAfter ){ + pX->pAfter->pUp = pX; + }else{ + assert( pBalance==pOld ); + pBalance = pX; + } } pX->pBefore = pY = pOld->pBefore; if( pY ) pY->pUp = pX; pX->pUp = pOld->pUp; *ppParent = pX; @@ -376,11 +414,13 @@ pBalance->pUp = pOld->pUp; }else if( pOld->pAfter==0 ){ *ppParent = pBalance = pOld->pBefore; pBalance->pUp = pOld->pUp; } + assertUpPointers(p->pRoot); p->pRoot = kvmemBalance(pBalance); + assertUpPointers(p->pRoot); kvmemNodeUnref(pOld); } /* ** End of low-level access routines @@ -438,26 +478,46 @@ static int kvmemCommitPhaseTwo(KVStore *pKVStore, int iLevel){ KVMem *p = (KVMem*)pKVStore; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); assert( iLevel>=0 ); assert( iLevelbase.iTransLevel ); + assertUpPointers(p->pRoot); while( p->base.iTransLevel>iLevel && p->base.iTransLevel>1 ){ KVMemChng *pChng, *pNext; - for(pChng=p->apLog[p->base.iTransLevel-2]; pChng; pChng=pNext){ - KVMemNode *pNode = pChng->pNode; - if( pNode->pData ){ - pNode->mxTrans = pChng->oldTrans; - }else{ - kvmemRemoveNode(p, pNode); - } - kvmemDataUnref(pChng->pData); - pNext = pChng->pNext; - sqlite4_free(pChng); - } + + if( iLevel<2 ){ + for(pChng=p->apLog[p->base.iTransLevel-2]; pChng; pChng=pNext){ + KVMemNode *pNode = pChng->pNode; + if( pNode->pData ){ + pNode->mxTrans = pChng->oldTrans; + }else{ + kvmemRemoveNode(p, pNode); + } + kvmemDataUnref(pChng->pData); + pNext = pChng->pNext; + sqlite4_free(pChng); + } + }else{ + KVMemChng **pp; + int iFrom = p->base.iTransLevel-2; + int iTo = p->base.iTransLevel-3; + assert( iTo>=0 ); + + for(pp=&p->apLog[iFrom]; *pp; pp=&((*pp)->pNext)){ + assert( (*pp)->pNode->mxTrans==p->base.iTransLevel + || (*pp)->pNode->mxTrans==(p->base.iTransLevel-1) + ); + (*pp)->pNode->mxTrans = p->base.iTransLevel - 1; + } + *pp = p->apLog[iTo]; + p->apLog[iTo] = p->apLog[iFrom]; + } + p->apLog[p->base.iTransLevel-2] = 0; p->base.iTransLevel--; } + assertUpPointers(p->pRoot); p->base.iTransLevel = iLevel; return SQLITE_OK; } /* @@ -472,16 +532,15 @@ */ static int kvmemRollback(KVStore *pKVStore, int iLevel){ KVMem *p = (KVMem*)pKVStore; assert( p->iMagicKVMemBase==SQLITE_KVMEMBASE_MAGIC ); assert( iLevel>=0 ); - assert( iLevelbase.iTransLevel ); while( p->base.iTransLevel>iLevel && p->base.iTransLevel>1 ){ KVMemChng *pChng, *pNext; for(pChng=p->apLog[p->base.iTransLevel-2]; pChng; pChng=pNext){ KVMemNode *pNode = pChng->pNode; - if( pChng->pData ){ + if( pChng->pData || pChng->oldTrans>0 ){ kvmemDataUnref(pNode->pData); pNode->pData = pChng->pData; pNode->mxTrans = pChng->oldTrans; }else{ kvmemRemoveNode(p, pNode); @@ -629,10 +688,52 @@ memset(pCur, 0, sizeof(*pCur)); sqlite4_free(pCur); } return SQLITE_OK; } + +/* +** Move a cursor to the next non-deleted node. +*/ +static int kvmemNextEntry(KVCursor *pKVCursor){ + KVMemCursor *pCur; + KVMemNode *pNode; + + pCur = (KVMemCursor*)pKVCursor; + assert( pCur->iMagicKVMemCur==SQLITE_KVMEMCUR_MAGIC ); + pNode = pCur->pNode; + kvmemReset(pKVCursor); + do{ + pNode = kvmemNext(pNode); + }while( pNode && pNode->pData==0 ); + if( pNode ){ + pCur->pNode = kvmemNodeRef(pNode); + pCur->pData = kvmemDataRef(pNode->pData); + } + return pNode ? SQLITE_OK : SQLITE_NOTFOUND; +} + +/* +** Move a cursor to the previous non-deleted node. +*/ +static int kvmemPrevEntry(KVCursor *pKVCursor){ + KVMemCursor *pCur; + KVMemNode *pNode; + + pCur = (KVMemCursor*)pKVCursor; + assert( pCur->iMagicKVMemCur==SQLITE_KVMEMCUR_MAGIC ); + pNode = pCur->pNode; + kvmemReset(pKVCursor); + do{ + pNode = kvmemPrev(pNode); + }while( pNode && pNode->pData==0 ); + if( pNode ){ + pCur->pNode = kvmemNodeRef(pNode); + pCur->pData = kvmemDataRef(pNode->pData); + } + return pNode ? SQLITE_OK : SQLITE_NOTFOUND; +} /* ** Seek a cursor. */ static int kvmemSeek( @@ -675,14 +776,32 @@ kvmemNodeUnref(pCur->pNode); kvmemDataUnref(pCur->pData); if( pBest ){ pCur->pNode = kvmemNodeRef(pBest); pCur->pData = kvmemDataRef(pBest->pData); + + /* The cursor currently points to a deleted node. If parameter 'direction' + ** was zero (exact matches only), then the search has failed - return + ** SQLITE_NOTFOUND. Otherwise, advance to the next (if direction is +ve) + ** or the previous (if direction is -ve) undeleted node in the tree. */ + if( pCur->pData==0 ){ + if( direction==0 ){ + rc = SQLITE_NOTFOUND; + }else{ + if( (direction>0 ? kvmemNextEntry : kvmemPrevEntry)(pCur) ){ + rc = SQLITE_NOTFOUND; + }else{ + rc = SQLITE_INEXACT; + } + } + } + }else{ pCur->pNode = 0; pCur->pData = 0; } + assert( rc!=SQLITE_DONE ); return rc; } /* ** Delete the entry that the cursor is pointing to. @@ -707,59 +826,18 @@ if( pNode==0 ) return SQLITE_OK; if( pNode->pData==0 ) return SQLITE_OK; if( pNode->mxTransbase.iTransLevel ){ pChng = kvmemNewChng(p, pNode); if( pChng==0 ) return SQLITE_NOMEM; + assert( pNode->pData==0 ); }else{ kvmemDataUnref(pNode->pData); pNode->pData = 0; } return SQLITE_OK; } -/* -** Move a cursor to the next non-deleted node. -*/ -static int kvmemNextEntry(KVCursor *pKVCursor){ - KVMemCursor *pCur; - KVMemNode *pNode; - - pCur = (KVMemCursor*)pKVCursor; - assert( pCur->iMagicKVMemCur==SQLITE_KVMEMCUR_MAGIC ); - pNode = pCur->pNode; - kvmemReset(pKVCursor); - do{ - pNode = kvmemNext(pNode); - }while( pNode && pNode->pData==0 ); - if( pNode ){ - pCur->pNode = kvmemNodeRef(pNode); - pCur->pData = kvmemDataRef(pNode->pData); - } - return pNode ? SQLITE_OK : SQLITE_NOTFOUND; -} - -/* -** Move a cursor to the previous non-deleted node. -*/ -static int kvmemPrevEntry(KVCursor *pKVCursor){ - KVMemCursor *pCur; - KVMemNode *pNode; - - pCur = (KVMemCursor*)pKVCursor; - assert( pCur->iMagicKVMemCur==SQLITE_KVMEMCUR_MAGIC ); - pNode = pCur->pNode; - kvmemReset(pKVCursor); - do{ - pNode = kvmemPrev(pNode); - }while( pNode && pNode->pData==0 ); - if( pNode ){ - pCur->pNode = kvmemNodeRef(pNode); - pCur->pData = kvmemDataRef(pNode->pData); - } - return pNode ? SQLITE_OK : SQLITE_NOTFOUND; -} - /* ** Return the key of the node the cursor is pointing to. */ static int kvmemKey( KVCursor *pKVCursor, /* The cursor whose key is desired */ @@ -848,15 +926,20 @@ }; /* ** Create a new in-memory storage engine and return a pointer to it. */ -int sqlite4KVStoreOpenMem(KVStore **ppKVStore, unsigned openFlags){ +int sqlite4KVStoreOpenMem( + KVStore **ppKVStore, + const char *zName, + unsigned openFlags +){ KVMem *pNew = sqlite4_malloc( sizeof(*pNew) ); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); pNew->base.pStoreVfunc = &kvmemMethods; pNew->iMagicKVMemBase = SQLITE_KVMEMBASE_MAGIC; pNew->openFlags = openFlags; *ppKVStore = (KVStore*)pNew; return SQLITE_OK; } + Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -276,10 +276,22 @@ ** the SQLite library is in use. */ if( sqlite4GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT; va_start(ap, op); switch( op ){ + case SQLITE_CONFIG_SET_KVFACTORY: { + sqlite4GlobalConfig.xKVFile = *va_arg(ap, + int (*)(KVStore **, const char *, unsigned int) + ); + break; + } + + case SQLITE_CONFIG_GET_KVFACTORY: { + *va_arg(ap, int (**)(KVStore **, const char *, unsigned int)) = + sqlite4GlobalConfig.xKVFile; + break; + } /* Mutex configuration options are only available in a threadsafe ** compile. */ #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -287,19 +287,19 @@ // In addition to the type name, we also care about the primary key and // UNIQUE constraints. // ccons ::= NULL onconf. -ccons ::= NOT NULL onconf(R). {sqlite4AddNotNull(pParse, R);} +ccons ::= NOT NULL onconf(R). {sqlite4AddNotNull(pParse, R);} ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I). - {sqlite4AddPrimaryKey(pParse,0,R,I,Z);} -ccons ::= UNIQUE onconf(R). {sqlite4CreateIndex(pParse,0,0,0,0,R,0,0,0,0);} -ccons ::= CHECK LP expr(X) RP. {sqlite4AddCheckConstraint(pParse,X.pExpr);} + {sqlite4AddPrimaryKey(pParse,0,R,I,Z);} +ccons ::= UNIQUE onconf(R). {sqlite4CreateIndex(pParse,0,0,0,0,R,0,0,0,0,0);} +ccons ::= CHECK LP expr(X) RP. {sqlite4AddCheckConstraint(pParse,X.pExpr);} ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R). - {sqlite4CreateForeignKey(pParse,0,&T,TA,R);} -ccons ::= defer_subclause(D). {sqlite4DeferForeignKey(pParse,D);} -ccons ::= COLLATE ids(C). {sqlite4AddCollateType(pParse, &C);} + {sqlite4CreateForeignKey(pParse,0,&T,TA,R);} +ccons ::= defer_subclause(D). {sqlite4DeferForeignKey(pParse,D);} +ccons ::= COLLATE ids(C). {sqlite4AddCollateType(pParse, &C);} // The optional AUTOINCREMENT keyword %type autoinc {int} autoinc(X) ::= . {X = 0;} autoinc(X) ::= AUTOINCR. {X = 1;} @@ -339,15 +339,15 @@ conslist ::= conslist COMMA tcons. conslist ::= conslist tcons. conslist ::= tcons. tcons ::= CONSTRAINT nm. tcons ::= PRIMARY KEY LP idxlist(X) autoinc(I) RP onconf(R). - {sqlite4AddPrimaryKey(pParse,X,R,I,0);} + {sqlite4AddPrimaryKey(pParse,X,R,I,0);} tcons ::= UNIQUE LP idxlist(X) RP onconf(R). - {sqlite4CreateIndex(pParse,0,0,0,X,R,0,0,0,0);} + {sqlite4CreateIndex(pParse,0,0,0,X,R,0,0,0,0,0);} tcons ::= CHECK LP expr(E) RP onconf. - {sqlite4AddCheckConstraint(pParse,E.pExpr);} + {sqlite4AddCheckConstraint(pParse,E.pExpr);} tcons ::= FOREIGN KEY LP idxlist(FA) RP REFERENCES nm(T) idxlist_opt(TA) refargs(R) defer_subclause_opt(D). { sqlite4CreateForeignKey(pParse, FA, &T, TA, R); sqlite4DeferForeignKey(pParse, D); } @@ -1089,11 +1089,11 @@ // cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D) ON nm(Y) LP idxlist(Z) RP(E). { sqlite4CreateIndex(pParse, &X, &D, sqlite4SrcListAppend(pParse->db,0,&Y,0), Z, U, - &S, &E, SQLITE_SO_ASC, NE); + &S, &E, SQLITE_SO_ASC, NE, 0); } %type uniqueflag {int} uniqueflag(A) ::= UNIQUE. {A = OE_Abort;} uniqueflag(A) ::= . {A = OE_None;} Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -534,10 +534,170 @@ */ if( sqlite4StrICmp(zLeft, "kvdump")==0 ){ sqlite4KVStoreDump(db->aDb[0].pKV); }else #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ + /* + ** PRAGMA integrity_check + ** + ** Check that for each table, the content of any auxilliary indexes are + ** consistent with the primary key index. + */ + if( sqlite4StrICmp(zLeft, "integrity_check")==0 ){ + const int baseCsr = 1; /* Base cursor for OpenAllIndexes() call */ + + const int regErrcnt = 1; /* Register containing error count */ + const int regErrstr = 2; /* Register containing error string */ + const int regTmp = 3; /* Register for tmp use */ + const int regRowcnt1 = 4; /* Register containing row count (from PK) */ + const int regRowcnt2 = 5; /* Register containing error count */ + const int regResult = 6; /* Register containing result string */ + const int regKey = 7; /* Register containing encoded key */ + const int regArray = 8; /* First in array of registers */ + + int i; + int nMaxArray = 1; + int addrNot = 0; + Vdbe *v; + + if( sqlite4ReadSchema(pParse) ) goto pragma_out; + + for(i=0; inDb; i++){ + if( OMIT_TEMPDB && i==1 ) continue; + sqlite4CodeVerifySchema(pParse, i); + } + + v = sqlite4GetVdbe(pParse); + sqlite4VdbeAddOp2(v, OP_Integer, 0, regErrcnt); + sqlite4VdbeAddOp4(v, OP_String8, 0, regErrstr, 0, "", 0); + + for(i=0; inDb; i++){ + Hash *pTbls; + HashElem *x; + + if( OMIT_TEMPDB && i==1 ) continue; + + pTbls = &db->aDb[i].pSchema->tblHash; + for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ + Index *pIdx; + Table *pTab = (Table *)sqliteHashData(x); + int addrRewind; + int nIdx = 0; + int iPkCsr; + Index *pPk; + int iCsr; + + /* Do nothing for views */ + if( IsView(pTab) ) continue; + + /* Open all indexes for table pTab. */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){ + pPk = pIdx; + iPkCsr = nIdx+baseCsr; + } + nIdx++; + } + sqlite4OpenAllIndexes(pParse, pTab, baseCsr, OP_OpenRead); + + sqlite4VdbeAddOp2(v, OP_Integer, 0, regRowcnt1); + addrRewind = sqlite4VdbeAddOp1(v, OP_Rewind, iPkCsr); + + /* Increment the row-count register */ + sqlite4VdbeAddOp2(v, OP_AddImm, regRowcnt1, 1); + + for(iCsr=baseCsr, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCsr++){ + assert( (pIdx->eIndexType==SQLITE_INDEX_PRIMARYKEY)==(iCsr==iPkCsr) ); + if( iCsr!=iPkCsr ){ + char *zErr; + int iCol; + int jmp; + for(iCol=0; iColnColumn; iCol++){ + int r = regArray + iCol; + sqlite4VdbeAddOp3(v, OP_Column, iPkCsr, pIdx->aiColumn[iCol], r); + assert( pIdx->aiColumn[iCol]>=0 ); + } + for(iCol=0; iColnColumn; iCol++){ + int reg = regArray + pIdx->nColumn + iCol; + int iTblCol = pPk->aiColumn[iCol]; + if( iTblCol<0 ){ + sqlite4VdbeAddOp2(v, OP_Rowid, iPkCsr, reg); + }else{ + sqlite4VdbeAddOp3(v, OP_Column, iPkCsr, iTblCol, reg); + } + } + + if( (pPk->nColumn+pIdx->nColumn)>nMaxArray ){ + nMaxArray = pPk->nColumn + pIdx->nColumn; + } + + sqlite4VdbeAddOp3(v, OP_MakeIdxKey, iCsr, regArray, regKey); + jmp = sqlite4VdbeAddOp4(v, OP_Found, iCsr, 0, regKey, 0, P4_INT32); + sqlite4VdbeAddOp2(v, OP_AddImm, regErrcnt, 1); + zErr = sqlite4MPrintf( + db, "entry missing from index %s: ", pIdx->zName + ); + sqlite4VdbeAddOp4(v, OP_String8, 0, regTmp, 0, zErr, 0); + sqlite4VdbeAddOp3(v, OP_Concat, regTmp, regErrstr, regErrstr); + sqlite4VdbeAddOp3(v, OP_Function, 0, regKey, regTmp); + sqlite4VdbeChangeP4(v, -1, + (char *)sqlite4FindFunction(db, "hex", 3, 1, SQLITE_UTF8, 0), + P4_FUNCDEF + ); + sqlite4VdbeChangeP5(v, 1); + sqlite4VdbeAddOp3(v, OP_Concat, regTmp, regErrstr, regErrstr); + sqlite4VdbeAddOp4(v, OP_String8, 0, regTmp, 0, "\n", 0); + sqlite4VdbeAddOp3(v, OP_Concat, regTmp, regErrstr, regErrstr); + sqlite4VdbeJumpHere(v, jmp); + sqlite4DbFree(db, zErr); + } + } + sqlite4VdbeAddOp2(v, OP_Next, iPkCsr, addrRewind+1); + sqlite4VdbeJumpHere(v, addrRewind); + + for(iCsr=baseCsr, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCsr++){ + if( iCsr!=iPkCsr ){ + char *zErr; + int addrEq; + int addrRewind2; + sqlite4VdbeAddOp2(v, OP_Integer, 0, regRowcnt2); + addrRewind2 = sqlite4VdbeAddOp1(v, OP_Rewind, iCsr); + sqlite4VdbeAddOp2(v, OP_AddImm, regRowcnt2, 1); + sqlite4VdbeAddOp2(v, OP_Next, iCsr, addrRewind2+1); + sqlite4VdbeJumpHere(v, addrRewind2); + zErr = sqlite4MPrintf( + db, "wrong # number of entries in index %s\n", pIdx->zName + ); + addrEq = sqlite4VdbeAddOp3(v, OP_Eq, regRowcnt1, 0, regRowcnt2); + sqlite4VdbeAddOp2(v, OP_AddImm, regErrcnt, 1); + sqlite4VdbeAddOp4(v, OP_String8, 0, regTmp, 0, zErr, 0); + sqlite4VdbeAddOp3(v, OP_Concat, regTmp, regErrstr, regErrstr); + + sqlite4VdbeJumpHere(v, addrEq); + sqlite4DbFree(db, zErr); + } + } + + for(iCsr=baseCsr; iCsr<(baseCsr+nIdx); iCsr++){ + sqlite4VdbeAddOp1(v, OP_Close, iCsr); + } + } + } + + sqlite4VdbeAddOp4(v, OP_String8, 0, regResult, 0, "ok", 0); + addrNot = sqlite4VdbeAddOp1(v, OP_IfNot, regErrcnt); + sqlite4VdbeAddOp4(v, OP_String8, 0, regArray, 0, " errors:\n", 0); + sqlite4VdbeAddOp3(v, OP_Concat, regArray, regErrcnt, regResult); + sqlite4VdbeAddOp3(v, OP_Concat, regErrstr, regResult, regResult); + sqlite4VdbeJumpHere(v, addrNot); + + pParse->nMem = (regArray + nMaxArray); + sqlite4VdbeSetNumCols(v, 1); + sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC); + sqlite4VdbeAddOp2(v, OP_ResultRow, regResult, 1); + + }else /* ** PRAGMA shrink_memory ** Index: src/resolve.c ================================================================== --- src/resolve.c +++ src/resolve.c @@ -218,12 +218,11 @@ cnt++; pExpr->iTable = pItem->iCursor; pExpr->pTab = pTab; pMatch = pItem; pSchema = pTab->pSchema; - /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ - pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; + pExpr->iColumn = (i16)j; break; } } } } @@ -249,13 +248,10 @@ pSchema = pTab->pSchema; cntTab++; for(iCol=0; iColnCol; iCol++){ Column *pCol = &pTab->aCol[iCol]; if( sqlite4StrICmp(pCol->zName, zCol)==0 ){ - if( iCol==pTab->iPKey ){ - iCol = -1; - } break; } } if( iCol>=pTab->nCol && sqlite4IsRowid(zCol) ){ iCol = -1; /* IMP: R-44911-55124 */ @@ -415,18 +411,14 @@ Expr *p = sqlite4ExprAlloc(db, TK_COLUMN, 0, 0); if( p ){ struct SrcList_item *pItem = &pSrc->a[iSrc]; p->pTab = pItem->pTab; p->iTable = pItem->iCursor; - if( p->pTab->iPKey==iCol ){ - p->iColumn = -1; - }else{ - p->iColumn = (ynVar)iCol; - testcase( iCol==BMS ); - testcase( iCol==BMS-1 ); - pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); - } + p->iColumn = (ynVar)iCol; + testcase( iCol==BMS ); + testcase( iCol==BMS-1 ); + pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); ExprSetProperty(p, EP_Resolved); } return p; } Index: src/rowset.c ================================================================== --- src/rowset.c +++ src/rowset.c @@ -418,5 +418,76 @@ return 1; } } return 0; } + +typedef struct KeySetEntry KeySetEntry; + +struct KeySetEntry { + char *z; + int n; + KeySetEntry *pNext; +}; + +struct KeySet { + sqlite4 *db; /* Database handle for sqlite4DbMalloc() */ + KeySetEntry *pFirst; + KeySetEntry *pLast; +}; + +KeySet *sqlite4KeySetInit(sqlite4 *db){ + KeySet *pRet; + pRet = (KeySet *)sqlite4DbMallocZero(db, sizeof(KeySet)); + if( pRet ){ + pRet->db = db; + } + return pRet; +} + +void sqlite4KeySetInsert(KeySet *pKeySet, const char *z, int n){ + KeySetEntry *pNew; + int nByte = n + sizeof(KeySetEntry); + + pNew = (KeySetEntry *)sqlite4DbMallocZero(pKeySet->db, nByte); + if( pNew ){ + pNew->z = (char *)&pNew[1]; + pNew->n =n; + memcpy(pNew->z, z, n); + if( pKeySet->pFirst ){ + pKeySet->pLast = pKeySet->pLast->pNext = pNew; + }else{ + pKeySet->pLast = pKeySet->pFirst = pNew; + } + } +} + +/* +** Read the blob of data stored in the current key-set entry. +*/ +const char *sqlite4KeySetRead(KeySet *pKeySet, int *pn){ + const char *pRet; + if( pKeySet->pFirst ){ + *pn = pKeySet->pFirst->n; + pRet = pKeySet->pFirst->z; + }else{ + pRet = 0; + *pn = 0; + } + return pRet; +} + +int sqlite4KeySetNext(KeySet *pKeySet){ + KeySetEntry *pFirst = pKeySet->pFirst->pNext; + sqlite4DbFree(pKeySet->db, pKeySet->pFirst); + pKeySet->pFirst = pFirst; + return (pFirst!=0); +} + +void sqlite4KeySetFree(KeySet *pKeySet){ + while( pKeySet->pFirst ){ + sqlite4KeySetNext(pKeySet); + } + sqlite4DbFree(pKeySet->db, pKeySet); +} + + Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -412,36 +412,47 @@ /* ** Insert code into "v" that will push the record on the top of the ** stack into the sorter. */ static void pushOntoSorter( - Parse *pParse, /* Parser context */ - ExprList *pOrderBy, /* The ORDER BY clause */ - Select *pSelect, /* The whole SELECT statement */ - int regData /* Register holding data to be sorted */ + Parse *pParse, /* Parser context */ + ExprList *pOrderBy, /* The ORDER BY clause */ + Select *pSelect, /* The whole SELECT statement */ + int regData /* Register holding data to be sorted */ ){ Vdbe *v = pParse->pVdbe; int nExpr = pOrderBy->nExpr; - int regBase = sqlite4GetTempRange(pParse, nExpr+2); - int regRecord = sqlite4GetTempReg(pParse); + int regBase = sqlite4GetTempRange(pParse, nExpr+1); int regKey = sqlite4GetTempReg(pParse); int op; + + /* Assemble the sort-key values in a contiguous array of registers + ** starting at regBase. The sort-key consists of the result of each + ** expression in the ORDER BY clause followed by a unique sequence + ** number. The sequence number allows more than one row with the same + ** sort-key. */ sqlite4ExprCacheClear(pParse); sqlite4ExprCodeExprList(pParse, pOrderBy, regBase, 0); sqlite4VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr); - sqlite4ExprCodeMove(pParse, regData, regBase+nExpr+1, 1); - sqlite4VdbeAddOp2(v, OP_MakeKey, pOrderBy->iECursor, regKey); - sqlite4VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord); + + /* Encode the sort-key. */ + sqlite4VdbeAddOp3(v, OP_MakeIdxKey, pOrderBy->iECursor, regBase, regKey); + + /* Insert an entry into the sorter. The key inserted is the encoded key + ** created by the OP_MakeIdxKey coded above. The value is the record + ** currently stored in register regData. */ if( pSelect->selFlags & SF_UseSorter ){ op = OP_SorterInsert; }else{ op = OP_IdxInsert; } - sqlite4VdbeAddOp3(v, op, pOrderBy->iECursor, regRecord, regKey); - sqlite4ReleaseTempReg(pParse, regRecord); + sqlite4VdbeAddOp3(v, op, pOrderBy->iECursor, regData, regKey); + + /* Release the temporary registers */ sqlite4ReleaseTempReg(pParse, regKey); - sqlite4ReleaseTempRange(pParse, regBase, nExpr+2); + sqlite4ReleaseTempRange(pParse, regBase, nExpr+1); + if( pSelect->iLimit ){ int addr1, addr2; int iLimit; if( pSelect->iOffset ){ iLimit = pSelect->iOffset+1; @@ -764,32 +775,40 @@ ** Space to hold the KeyInfo structure is obtain from malloc. The calling ** function is responsible for seeing that this structure is eventually ** freed. Add the KeyInfo structure to the P4 field of an opcode using ** P4_KEYINFO_HANDOFF is the usual way of dealing with this. */ -static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){ - sqlite4 *db = pParse->db; - int nExpr; - KeyInfo *pInfo; - struct ExprList_item *pItem; - int i; - - nExpr = pList->nExpr; - pInfo = sqlite4DbMallocZero(db, sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) ); +static KeyInfo *keyInfoFromExprList( + Parse *pParse, + ExprList *pList, + int bOrderBy +){ + sqlite4 *db = pParse->db; /* Database handle */ + int nField; /* Number of fields in keys */ + KeyInfo *pInfo; /* Object to return */ + int nByte; /* Bytes of space to allocate */ + + assert( bOrderBy==0 || bOrderBy==1 ); + + nField = pList->nExpr + bOrderBy; + nByte = sizeof(KeyInfo) + nField * sizeof(CollSeq *) + nField; + pInfo = (KeyInfo *)sqlite4DbMallocZero(db, nByte); + if( pInfo ){ - pInfo->aSortOrder = (u8*)&pInfo->aColl[nExpr]; - pInfo->nField = (u16)nExpr; + int i; /* Used to iterate through pList */ + + pInfo->aSortOrder = (u8*)&pInfo->aColl[nField]; + pInfo->nField = (u16)nField; pInfo->enc = ENC(db); pInfo->db = db; - for(i=0, pItem=pList->a; inExpr; i++){ CollSeq *pColl; - pColl = sqlite4ExprCollSeq(pParse, pItem->pExpr); - if( !pColl ){ - pColl = db->pDfltColl; - } + pColl = sqlite4ExprCollSeq(pParse, pList->a[i].pExpr); + if( !pColl ) pColl = db->pDfltColl; pInfo->aColl[i] = pColl; - pInfo->aSortOrder[i] = pItem->sortOrder; + pInfo->aSortOrder[i] = pList->a[i].sortOrder; } } return pInfo; } @@ -880,43 +899,39 @@ # define explainComposite(v,w,x,y,z) #endif /* ** If the inner loop was generated using a non-null pOrderBy argument, -** then the results were placed in a sorter. After the loop is terminated -** we need to run the sorter and output the results. The following -** routine generates the code needed to do that. +** then the results were placed in a sorter. After the loop is terminated +** we need to loop through the contents of the sorter and output the +** results. The following routine generates the code needed to do that. */ static void generateSortTail( - Parse *pParse, /* Parsing context */ - Select *p, /* The SELECT statement */ - Vdbe *v, /* Generate code into this VDBE */ - int nColumn, /* Number of columns of data */ - SelectDest *pDest /* Write the sorted results here */ + Parse *pParse, /* Parsing context */ + Select *p, /* The SELECT statement */ + Vdbe *v, /* Generate code into this VDBE */ + int nColumn, /* Number of columns of data */ + SelectDest *pDest /* Write the sorted results here */ ){ int addrBreak = sqlite4VdbeMakeLabel(v); /* Jump here to exit loop */ int addrContinue = sqlite4VdbeMakeLabel(v); /* Jump here for next cycle */ int addr; - int iTab; - int pseudoTab = 0; + int iTab; /* Sorter object cursor */ ExprList *pOrderBy = p->pOrderBy; int eDest = pDest->eDest; int iParm = pDest->iParm; int regRow; - int regRowid; + int regRowid = 0; iTab = pOrderBy->iECursor; regRow = sqlite4GetTempReg(pParse); - if( eDest==SRT_Output || eDest==SRT_Coroutine ){ - pseudoTab = pParse->nTab++; - sqlite4VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn); - regRowid = 0; - }else{ + if( eDest!=SRT_Output && eDest!=SRT_Coroutine ){ regRowid = sqlite4GetTempReg(pParse); } + if( p->selFlags & SF_UseSorter ){ int regSortOut = ++pParse->nMem; int ptab2 = pParse->nTab++; sqlite4VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2); addr = 1 + sqlite4VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); @@ -925,11 +940,11 @@ sqlite4VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow); sqlite4VdbeChangeP5(v, OPFLAG_CLEARCACHE); }else{ addr = 1 + sqlite4VdbeAddOp2(v, OP_Sort, iTab, addrBreak); codeOffset(v, p, addrContinue); - sqlite4VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow); + /* sqlite4VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow); */ } switch( eDest ){ case SRT_Table: case SRT_EphemTab: { testcase( eDest==SRT_Table ); @@ -960,17 +975,17 @@ default: { int i; assert( eDest==SRT_Output || eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); testcase( eDest==SRT_Coroutine ); + + /* Read the data out of the sorter and into the array of nColumn + ** contiguous registers starting at pDest->iMem. */ for(i=0; iiMem+i ); - sqlite4VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i); - if( i==0 ){ - sqlite4VdbeChangeP5(v, OPFLAG_CLEARCACHE); - } + sqlite4VdbeAddOp3(v, OP_Column, iTab, i, pDest->iMem+i); } + if( eDest==SRT_Output ){ sqlite4VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn); sqlite4ExprCacheAffinityChange(pParse, pDest->iMem, nColumn); }else{ sqlite4VdbeAddOp1(v, OP_Yield, pDest->iParm); @@ -988,13 +1003,10 @@ sqlite4VdbeAddOp2(v, OP_SorterNext, iTab, addr); }else{ sqlite4VdbeAddOp2(v, OP_Next, iTab, addr); } sqlite4VdbeResolveLabel(v, addrBreak); - if( eDest==SRT_Output || eDest==SRT_Coroutine ){ - sqlite4VdbeAddOp2(v, OP_Close, pseudoTab, 0); - } } /* ** Return a pointer to a string containing the 'declaration type' of the ** expression pExpr. The string may be treated as static by the caller. @@ -1090,11 +1102,10 @@ zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol); } }else if( ALWAYS(pTab->pSchema) ){ /* A real table */ assert( !pS ); - if( iCol<0 ) iCol = pTab->iPKey; assert( iCol==-1 || (iCol>=0 && iColnCol) ); if( iCol<0 ){ zType = "INTEGER"; zOriginCol = "rowid"; }else{ @@ -1214,11 +1225,10 @@ for(j=0; ALWAYS(jnSrc); j++){ if( pTabList->a[j].iCursor==p->iTable ) break; } assert( jnSrc ); pTab = pTabList->a[j].pTab; - if( iCol<0 ) iCol = pTab->iPKey; assert( iCol==-1 || (iCol>=0 && iColnCol) ); if( iCol<0 ){ zCol = "rowid"; }else{ zCol = pTab->aCol[iCol].zName; @@ -1281,11 +1291,10 @@ } if( pColExpr->op==TK_COLUMN && ALWAYS(pColExpr->pTab!=0) ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; pTab = pColExpr->pTab; - if( iCol<0 ) iCol = pTab->iPKey; zName = sqlite4MPrintf(db, "%s", iCol>=0 ? pTab->aCol[iCol].zName : "rowid"); }else if( pColExpr->op==TK_ID ){ assert( !ExprHasProperty(pColExpr, EP_IntValue) ); zName = sqlite4MPrintf(db, "%s", pColExpr->u.zToken); @@ -1393,11 +1402,10 @@ pTab->nRef = 1; pTab->zName = 0; pTab->nRowEst = 1000000; selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect); - pTab->iPKey = -1; if( db->mallocFailed ){ sqlite4DeleteTable(db, pTab); return 0; } return pTab; @@ -3237,11 +3245,10 @@ if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; pTab->zName = sqlite4MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); - pTab->iPKey = -1; pTab->nRowEst = 1000000; pTab->tabFlags |= TF_Ephemeral; #endif }else{ /* An ordinary table or view name in the FROM clause */ @@ -3552,11 +3559,11 @@ if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ sqlite4ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " "argument"); pFunc->iDistinct = -1; }else{ - KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList); + KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0); sqlite4VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); } } } @@ -3946,11 +3953,13 @@ ** we figure out that the sorting index is not needed. The addrSortIndex ** variable is used to facilitate that change. */ if( pOrderBy ){ KeyInfo *pKeyInfo; - pKeyInfo = keyInfoFromExprList(pParse, pOrderBy); + pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 1); + if( pKeyInfo ) pKeyInfo->nData = pEList->nExpr; + pOrderBy->iECursor = pParse->nTab++; p->addrOpenEphm[2] = addrSortIndex = sqlite4VdbeAddOp4(v, OP_OpenEphemeral, pOrderBy->iECursor, pOrderBy->nExpr+2, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); @@ -3977,11 +3986,11 @@ /* Open a virtual index to use for the distinct set. */ if( p->selFlags & SF_Distinct ){ KeyInfo *pKeyInfo; distinct = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, p->pEList); + pKeyInfo = keyInfoFromExprList(pParse, p->pEList, 0); addrDistinctIndex = sqlite4VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); }else{ distinct = addrDistinctIndex = -1; } @@ -4131,11 +4140,11 @@ ** implement it. Allocate that sorting index now. If it turns out ** that we do not need it after all, the OP_SorterOpen instruction ** will be converted into a Noop. */ sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy); + pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0); addrSortingIdx = sqlite4VdbeAddOp4(v, OP_SorterOpen, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); /* Initialize memory locations used by GROUP BY aggregate processing Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -1531,10 +1531,13 @@ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ #define SQLITE_CONFIG_PCACHE2 18 /* sqlite4_pcache_methods2* */ #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite4_pcache_methods2* */ +#define SQLITE_CONFIG_SET_KVFACTORY 20 /* int(*)(KVStore**,const char*,u32) */ +#define SQLITE_CONFIG_GET_KVFACTORY 21 /* int(**)(KVStore**,const char*,u32) */ + /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite4_db_config()] interface. Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -20,10 +20,11 @@ #define SQLITE_OMIT_VACUUM 1 #define SQLITE_OMIT_AUTOVACUUM 1 #define SQLITE_OMIT_SHARED_CACHE 1 /*#define SQLITE_OMIT_PAGER_PRAGMAS 1*/ #define SQLITE_OMIT_PROGRESS_CALLBACK 1 + #define SQLITE_OMIT_MERGE_SORT 1 /* ** These #defines should enable >2GB file support on POSIX if the ** underlying operating system supports it. If the OS lacks @@ -652,10 +653,11 @@ typedef struct IdList IdList; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; +typedef struct KeySet KeySet; typedef struct Lookaside Lookaside; typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; typedef struct Parse Parse; @@ -675,10 +677,11 @@ typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; typedef struct WhereLevel WhereLevel; + /* ** Defer sourcing vdbe.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. @@ -1291,20 +1294,17 @@ ** sub-query that appears instead of a real table name in the FROM clause ** of a SELECT statement. */ struct Table { char *zName; /* Name of the table or view */ - int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ - int tnum; /* Root BTree node for this table (see note above) */ tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ - u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK Expr *pCheck; /* The AND of all CHECK constraints */ #endif @@ -1343,10 +1343,17 @@ # define IsHiddenColumn(X) ((X)->isHidden) #else # define IsVirtual(X) 0 # define IsHiddenColumn(X) 0 #endif + +/* Test to see if a table is actually a view. */ +#ifndef SQLITE_OMIT_VIEW +# define IsView(X) ((X)->pSelect!=0) +#else +# define IsView(X) 0 +#endif /* ** Each foreign key constraint is an instance of the following structure. ** ** A foreign key is associated with two tables. The "from" table is @@ -1430,10 +1437,11 @@ struct KeyInfo { sqlite4 *db; /* The database connection */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nField; /* Total number of entries in aColl[] */ u16 nPK; /* Number of primary key entries at the end of aColl[] */ + u16 nData; /* Number of columns of data in KV entry value */ u8 *aSortOrder; /* Sort order for each column. May be NULL */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; /* @@ -1497,11 +1505,11 @@ int *aiColumn; /* Which columns are used by this index. 1st is 0 */ tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ Table *pTable; /* The SQL table being indexed */ int tnum; /* Page containing root of this index in database file */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 eIndexType; /* SQLITE_INDEX_USER, UNIQUE or PRIMARYKEY */ u8 bUnordered; /* Use this index for == or IN queries only */ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ @@ -1511,10 +1519,15 @@ tRowcnt avgEq; /* Average nEq value for key values not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ #endif }; +/* Index.eIndexType must be set to one of the following. */ +#define SQLITE_INDEX_USER 0 /* Index created by CREATE INDEX statement */ +#define SQLITE_INDEX_UNIQUE 1 /* Index created by UNIQUE constraint */ +#define SQLITE_INDEX_PRIMARYKEY 2 /* Index is the tables PRIMARY KEY */ + /* ** Each sample stored in the sqlite_stat3 table is represented in memory ** using a structure of this type. See documentation at the top of the ** analyze.c source file for additional information. */ @@ -2246,11 +2259,13 @@ #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ #endif int regRowid; /* Register holding rowid of CREATE TABLE entry */ +#if 0 int regRoot; /* Register holding root page number for new objects */ +#endif AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ int nMaxArg; /* Max args passed to user function by sub-program */ /* Information used while coding trigger programs. */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ @@ -2487,10 +2502,12 @@ sqlite4_mutex *pInitMutex; /* Mutex used by sqlite4_initialize() */ int nRefInitMutex; /* Number of users of pInitMutex */ void (*xLog)(void*,int,const char*); /* Function for logging */ void *pLogArg; /* First argument to xLog() */ int bLocaltimeFault; /* True to fail localtime() calls */ + int (*xKVFile)(KVStore **, const char *, unsigned int); + int (*xKVTmp)(KVStore **, const char *, unsigned int); }; /* ** Context pointer passed down through the tree-walk. */ @@ -2751,10 +2768,16 @@ RowSet *sqlite4RowSetInit(sqlite4*, void*, unsigned int); void sqlite4RowSetClear(RowSet*); void sqlite4RowSetInsert(RowSet*, i64); int sqlite4RowSetTest(RowSet*, u8 iBatch, i64); int sqlite4RowSetNext(RowSet*, i64*); + +KeySet *sqlite4KeySetInit(sqlite4*); +void sqlite4KeySetInsert(KeySet *, const char *, int); +const char *sqlite4KeySetRead(KeySet *, int *); +int sqlite4KeySetNext(KeySet *); +void sqlite4KeySetFree(KeySet *); void sqlite4CreateView(Parse*,Token*,Token*,Token*,Select*,int,int); #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) int sqlite4ViewGetColumnNames(Parse*,Table*); @@ -2785,11 +2808,11 @@ void sqlite4SrcListShiftJoinType(SrcList*); void sqlite4SrcListAssignCursors(Parse*, SrcList*); void sqlite4IdListDelete(sqlite4*, IdList*); void sqlite4SrcListDelete(sqlite4*, SrcList*); Index *sqlite4CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, - Token*, int, int); + Token*, int, int, int); void sqlite4DropIndex(Parse*, SrcList*, int); int sqlite4Select(Parse*, Select*, SelectDest*); Select *sqlite4SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,int,Expr*,Expr*); void sqlite4SelectDelete(sqlite4*, Select*); @@ -2854,10 +2877,11 @@ int sqlite4ExprNeedsNoAffinityChange(const Expr*, char); int sqlite4IsRowid(const char*); void sqlite4GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int); void sqlite4GenerateRowIndexDelete(Parse*, Table*, int, int*); int sqlite4GenerateIndexKey(Parse*, Index*, int, int, int, int); +void sqlite4EncodeIndexKey(Parse *, Index *, int, Index *, int, int); void sqlite4GenerateConstraintChecks(Parse*,Table*,int,int, int*,int,int,int,int,int*); void sqlite4CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); int sqlite4OpenTableAndIndices(Parse*, Table*, int, int); void sqlite4BeginWriteOperation(Parse*, int, int); @@ -2878,10 +2902,12 @@ int sqlite4SafetyCheckSickOrOk(sqlite4*); void sqlite4ChangeCookie(Parse*, int); #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) void sqlite4MaterializeView(Parse*, Table*, Expr*, int); +#else +# define sqlite4MaterializeView(w,x,y,z) #endif #ifndef SQLITE_OMIT_TRIGGER void sqlite4BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*, Expr*,int, int); @@ -3082,10 +3108,16 @@ char *sqlite4StrAccumFinish(StrAccum*); void sqlite4StrAccumReset(StrAccum*); void sqlite4SelectDestInit(SelectDest*,int,int); Expr *sqlite4CreateColumnExpr(sqlite4 *, SrcList *, int, int); +void sqlite4OpenPrimaryKey(Parse*, int iCur, int iDb, Table*, int); +void sqlite4OpenIndex(Parse*, int iCur, int iDb, Index*, int); +int sqlite4OpenAllIndexes(Parse *, Table *, int, int); +void sqlite4CloseAllIndexes(Parse *, Table *, int); +Index *sqlite4FindPrimaryKey(Table *, int *); + /* ** The interface to the LEMON-generated parser */ void *sqlite4ParserAlloc(void*(*)(size_t)); void sqlite4ParserFree(void*, void(*)(void*)); @@ -3164,19 +3196,19 @@ */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) void sqlite4FkCheck(Parse*, Table*, int, int); void sqlite4FkDropTable(Parse*, SrcList *, Table*); void sqlite4FkActions(Parse*, Table*, ExprList*, int); - int sqlite4FkRequired(Parse*, Table*, int*, int); + int sqlite4FkRequired(Parse*, Table*, int*); u32 sqlite4FkOldmask(Parse*, Table*); FKey *sqlite4FkReferences(Table *); #else #define sqlite4FkActions(a,b,c,d) #define sqlite4FkCheck(a,b,c,d) #define sqlite4FkDropTable(a,b,c) #define sqlite4FkOldmask(a,b) 0 - #define sqlite4FkRequired(a,b,c,d) 0 + #define sqlite4FkRequired(a,b,c) 0 #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite4FkDelete(sqlite4 *, Table*); #else #define sqlite4FkDelete(a,b) Index: src/storage.c ================================================================== --- src/storage.c +++ src/storage.c @@ -86,11 +86,19 @@ unsigned flags /* Option flags */ ){ KVStore *pNew = 0; int rc; - rc = sqlite4KVStoreOpenMem(&pNew, flags); + if( zUri && zUri[0] + && sqlite4GlobalConfig.xKVFile + && memcmp(":memory:", zUri, 8) + ){ + rc = sqlite4GlobalConfig.xKVFile(&pNew, zUri, flags); + }else{ + rc = sqlite4GlobalConfig.xKVTmp(&pNew, zUri, flags); + } + *ppKVStore = pNew; if( pNew ){ sqlite4_randomness(sizeof(pNew->kvId), &pNew->kvId); sqlite4_snprintf(sizeof(pNew->zKVName), pNew->zKVName, "%s", zName); @@ -303,10 +311,18 @@ /* ** Key for the meta-data */ static const KVByteArray metadataKey[] = { 0x00, 0x00 }; + +static void writeMetaArray(KVByteArray *aMeta, int iElem, u32 iVal){ + int i = sizeof(u32) * iElem; + aMeta[i+0] = (iVal>>24)&0xff; + aMeta[i+1] = (iVal>>16)&0xff; + aMeta[i+2] = (iVal>>8) &0xff; + aMeta[i+3] = (iVal>>0) &0xff; +} /* ** Read nMeta unsigned 32-bit integers of metadata beginning at iStart. */ int sqlite4KVStoreGetMeta(KVStore *p, int iStart, int nMeta, unsigned int *a){ @@ -351,49 +367,48 @@ int nMeta, /* number of 32-bit integers to be written */ unsigned int *a /* The integers to write */ ){ KVCursor *pCur; int rc; - int i, j; - KVSize nData; - const KVByteArray *aData; - KVByteArray *aNew; - KVSize nNew; + rc = sqlite4KVStoreOpenCursor(p, &pCur); if( rc==SQLITE_OK ){ + const KVByteArray *aData; /* Original database meta-array value */ + KVSize nData; /* Size of aData[] in bytes */ + KVByteArray *aNew; /* New database meta-array value */ + KVSize nNew; /* Size of aNew[] in bytes */ + + /* Read the current meta-array value from the database */ rc = sqlite4KVCursorSeek(pCur, metadataKey, sizeof(metadataKey), 0); if( rc==SQLITE_OK ){ rc = sqlite4KVCursorData(pCur, 0, -1, &aData, &nData); }else if( rc==SQLITE_NOTFOUND ){ nData = 0; aData = 0; rc = SQLITE_OK; } + + /* Encode and write the new meta-array value to the database */ if( rc==SQLITE_OK ){ - nNew = iStart+nMeta; + nNew = sizeof(a[0]) * (iStart+nMeta); if( nNew>24)&0xff; - aNew[j+1] = (a[i]>>16)&0xff; - aNew[j+2] = (a[i]>>8)&0xff; - aNew[j+3] = a[i] & 0xff; - i++; - j += 4; + for(i=iStart; ipReal->pStoreVfunc->xBegin(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapCommitPhaseOne(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xCommitPhaseOne(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapCommitPhaseTwo(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xCommitPhaseTwo(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapRollback(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xRollback(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapRevert(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xRevert(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapReplace( + KVStore *pKVStore, + const KVByteArray *aKey, KVSize nKey, + const KVByteArray *aData, KVSize nData +){ + KVWrap *p = (KVWrap *)pKVStore; + return p->pReal->pStoreVfunc->xReplace(p->pReal, aKey, nKey, aData, nData); +} + +/* +** Create a new cursor object. +*/ +static int kvwrapOpenCursor(KVStore *pKVStore, KVCursor **ppKVCursor){ + int rc = SQLITE_OK; + KVWrap *p = (KVWrap *)pKVStore; + KVWrapCsr *pCsr; + + pCsr = (KVWrapCsr *)sqlite4_malloc(sizeof(KVWrapCsr)); + if( pCsr==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pCsr, 0, sizeof(KVWrapCsr)); + rc = p->pReal->pStoreVfunc->xOpenCursor(p->pReal, &pCsr->pReal); + if( rc!=SQLITE_OK ){ + sqlite4_free(pCsr); + pCsr = 0; + }else{ + pCsr->base.pStore = pKVStore; + pCsr->base.pStoreVfunc = pKVStore->pStoreVfunc; + } + } + + *ppKVCursor = (KVCursor*)pCsr; + return rc; +} + +/* +** Reset a cursor +*/ +static int kvwrapReset(KVCursor *pKVCursor){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xReset(pCsr->pReal); +} + +/* +** Destroy a cursor object +*/ +static int kvwrapCloseCursor(KVCursor *pKVCursor){ + int rc; + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + rc = p->pReal->pStoreVfunc->xCloseCursor(pCsr->pReal); + sqlite4_free(pCsr); + return rc; +} + +/* +** Move a cursor to the next non-deleted node. +*/ +static int kvwrapNextEntry(KVCursor *pKVCursor){ + int rc; + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + rc = p->pReal->pStoreVfunc->xNext(pCsr->pReal); + if( rc==SQLITE_OK ) kvwg.nStep++; + return rc; +} + +/* +** Move a cursor to the previous non-deleted node. +*/ +static int kvwrapPrevEntry(KVCursor *pKVCursor){ + int rc; + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + rc = p->pReal->pStoreVfunc->xPrev(pCsr->pReal); + if( rc==SQLITE_OK ) kvwg.nStep++; + return rc; +} + +/* +** Seek a cursor. +*/ +static int kvwrapSeek( + KVCursor *pKVCursor, + const KVByteArray *aKey, + KVSize nKey, + int dir +){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + + /* If aKey[0]==0, this is a seek to retrieve meta-data. Don't count this. */ + if( aKey[0] ) kvwg.nSeek++; + + return p->pReal->pStoreVfunc->xSeek(pCsr->pReal, aKey, nKey, dir); +} + +/* +** Delete the entry that the cursor is pointing to. +** +** Though the entry is "deleted", it still continues to exist as a +** phantom. Subsequent xNext or xPrev calls will work, as will +** calls to xKey and xData, thought the result from xKey and xData +** are undefined. +*/ +static int kvwrapDelete(KVCursor *pKVCursor){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xDelete(pCsr->pReal); +} + +/* +** Return the key of the node the cursor is pointing to. +*/ +static int kvwrapKey( + KVCursor *pKVCursor, /* The cursor whose key is desired */ + const KVByteArray **paKey, /* Make this point to the key */ + KVSize *pN /* Make this point to the size of the key */ +){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xKey(pCsr->pReal, paKey, pN); +} + +/* +** Return the data of the node the cursor is pointing to. +*/ +static int kvwrapData( + KVCursor *pKVCursor, /* The cursor from which to take the data */ + KVSize ofst, /* Offset into the data to begin reading */ + KVSize n, /* Number of bytes requested */ + const KVByteArray **paData, /* Pointer to the data written here */ + KVSize *pNData /* Number of bytes delivered */ +){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xData(pCsr->pReal, ofst, n, paData, pNData); +} + +/* +** Destructor for the entire in-memory storage tree. +*/ +static int kvwrapClose(KVStore *pKVStore){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xClose(p->pReal); + sqlite4_free(p); + return rc; +} + +static int newFileStorage( + KVStore **ppKVStore, + const char *zName, + unsigned openFlags +){ + + /* Virtual methods for an LSM data store */ + static const KVStoreMethods kvwrapMethods = { + kvwrapReplace, + kvwrapOpenCursor, + kvwrapSeek, + kvwrapNextEntry, + kvwrapPrevEntry, + kvwrapDelete, + kvwrapKey, + kvwrapData, + kvwrapReset, + kvwrapCloseCursor, + kvwrapBegin, + kvwrapCommitPhaseOne, + kvwrapCommitPhaseTwo, + kvwrapRollback, + kvwrapRevert, + kvwrapClose + }; + + KVWrap *pNew; + int rc = SQLITE_OK; + + pNew = (KVWrap *)sqlite4_malloc(sizeof(KVWrap)); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pNew, 0, sizeof(KVWrap)); + pNew->base.pStoreVfunc = &kvwrapMethods; + rc = kvwg.xFactory(&pNew->pReal, zName, openFlags); + if( rc!=SQLITE_OK ){ + sqlite4_free(pNew); + pNew = 0; + } + } + + *ppKVStore = (KVStore*)pNew; + return rc; +} + +static int kvwrap_install_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + if( kvwg.xFactory==0 ){ + sqlite4_config(SQLITE_CONFIG_GET_KVFACTORY, &kvwg.xFactory); + sqlite4_config(SQLITE_CONFIG_SET_KVFACTORY, newFileStorage); + } + return TCL_OK; +} + +static int kvwrap_seek_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, Tcl_NewIntObj(kvwg.nSeek)); + return TCL_OK; +} + +static int kvwrap_step_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, Tcl_NewIntObj(kvwg.nStep)); + return TCL_OK; +} + +static int kvwrap_reset_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + kvwg.nStep = 0; + kvwg.nSeek = 0; + + Tcl_ResetResult(interp); + return TCL_OK; +} + + +/* +** TCLCMD: kvwrap SUB-COMMAND +*/ +static int kvwrap_command( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + struct Subcmd { + const char *zCmd; + int (*xCmd)(Tcl_Interp *, int, Tcl_Obj **); + } aSub[] = { + { "install", kvwrap_install_cmd }, + { "step", kvwrap_step_cmd }, + { "seek", kvwrap_seek_cmd }, + { "reset", kvwrap_reset_cmd }, + }; + int iSub; + int rc; + + rc = Tcl_GetIndexFromObjStruct( + interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub + ); + if( rc==TCL_OK ){ + rc = aSub[iSub].xCmd(interp, objc, (Tcl_Obj **)objv); + } + + return rc; +} + +/* +** Register the TCL commands defined above with the TCL interpreter. +** +** This routine should be the only externally visible symbol in this +** source code file. +*/ +int Sqliteteststorage2_Init(Tcl_Interp *interp){ + Tcl_CreateObjCommand(interp, "kvwrap", kvwrap_command, 0, 0); + return TCL_OK; +} + Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -82,15 +82,15 @@ /* ** Process an UPDATE statement. ** ** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL; ** \_______/ \________/ \______/ \________________/ -* onError pTabList pChanges pWhere +* onError pSrc pChanges pWhere */ void sqlite4Update( Parse *pParse, /* The parser context */ - SrcList *pTabList, /* The table in which we should change things */ + SrcList *pSrc, /* The table in which we should change things */ ExprList *pChanges, /* Things to be changed */ Expr *pWhere, /* The WHERE clause. May be null */ int onError /* How to handle constraint errors */ ){ int i, j; /* Loop counters */ @@ -104,13 +104,10 @@ sqlite4 *db; /* The database structure */ int *aRegIdx = 0; /* One register assigned to each index to be updated */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ - int chngRowid; /* True if the record number is being changed */ - Expr *pRowidExpr = 0; /* Expression defining the new record number */ - int openAll = 0; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ int okOnePass; /* True for one-pass algorithm without the FIFO */ int hasFK; /* True if foreign key processing is required */ @@ -121,29 +118,46 @@ int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ #endif int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ /* Register Allocations */ - int regRowCount = 0; /* A count of rows changed */ - int regOldRowid; /* The old rowid */ + int regOldKey; /* Register containing the original PK */ + int regNewRowid; /* The new rowid */ int regNew; /* Content of the NEW.* table in triggers */ - int regOld = 0; /* Content of OLD.* table in triggers */ - int regRowSet = 0; /* Rowset of rows to be updated */ + + int regOld = 0; /* Content of OLD.* table in triggers */ + int regKeySet = 0; /* Register containing KeySet object */ + Index *pPk = 0; /* The primary key index of this table */ + int iPk = 0; /* Offset of primary key in aRegIdx[] */ + int bChngPk = 0; /* True if any PK columns are updated */ + int bOpenAll = 0; /* True if all indexes were opened */ + int bImplicitPk = 0; /* True if pTab has an implicit PK */ + int regOldTr = 0; /* Content of OLD.* table including IPK */ + int regNewTr = 0; /* Content of NEW.* table including IPK */ memset(&sContext, 0, sizeof(sContext)); db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto update_cleanup; } - assert( pTabList->nSrc==1 ); + assert( pSrc->nSrc==1 ); - /* Locate the table which we want to update. + /* Locate and analyze the table to be updated. This block sets: + ** + ** pTab + ** iDb + ** pPk + ** bImplicitPk */ - pTab = sqlite4SrcListLookup(pParse, pTabList); + pTab = sqlite4SrcListLookup(pParse, pSrc); if( pTab==0 ) goto update_cleanup; iDb = sqlite4SchemaToIndex(pParse->db, pTab->pSchema); + if( IsView(pTab)==0 ){ + pPk = sqlite4FindPrimaryKey(pTab, &iPk); + bImplicitPk = (pPk->aiColumn[0]<0); + } /* Figure out if we have any triggers and if the table being ** updated is a view. */ #ifndef SQLITE_OMIT_TRIGGER @@ -158,66 +172,68 @@ #ifdef SQLITE_OMIT_VIEW # undef isView # define isView 0 #endif - if( sqlite4ViewGetColumnNames(pParse, pTab) ){ - goto update_cleanup; - } - if( sqlite4IsReadOnly(pParse, pTab, tmask) ){ - goto update_cleanup; - } + if( sqlite4ViewGetColumnNames(pParse, pTab) ) goto update_cleanup; + if( sqlite4IsReadOnly(pParse, pTab, tmask) ) goto update_cleanup; + aXRef = sqlite4DbMallocRaw(db, sizeof(int) * pTab->nCol ); if( aXRef==0 ) goto update_cleanup; for(i=0; inCol; i++) aXRef[i] = -1; /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and - ** allocate enough space, just in case. - */ - pTabList->a[0].iCursor = iCur = pParse->nTab++; + ** allocate enough space, just in case. */ + iCur = pParse->nTab; + pSrc->a[0].iCursor = iCur+iPk; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ pParse->nTab++; } + if( IsView(pTab) ) pParse->nTab++; /* Initialize the name-context */ memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; - sNC.pSrcList = pTabList; - - /* Resolve the column names in all the expressions of the - ** of the UPDATE statement. Also find the column index - ** for each column to be updated in the pChanges array. For each - ** column to be updated, make sure we have authorization to change - ** that column. - */ - chngRowid = 0; + sNC.pSrcList = pSrc; + + /* Resolve the column names in all the expressions of the of the UPDATE + ** statement. Also find the column index for each column to be updated in + ** the pChanges array. For each column to be updated, make sure we have + ** authorization to change that column. + ** + ** Also, if any columns that are part of the tables primary key are + ** to be modified, set the bChngPk variable to true. This is significant + ** because if the primary key changes, *all* index entries need to be + ** replaced (not just those that index modified columns). */ for(i=0; inExpr; i++){ + int iPkCol; /* To iterate through PK columns */ + + /* Resolve any names in the expression for this assignment */ if( sqlite4ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ goto update_cleanup; } - for(j=0; jnCol; j++){ - if( sqlite4StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){ - if( j==pTab->iPKey ){ - chngRowid = 1; - pRowidExpr = pChanges->a[i].pExpr; - } - aXRef[j] = i; - break; - } - } - if( j>=pTab->nCol ){ - if( sqlite4IsRowid(pChanges->a[i].zName) ){ - chngRowid = 1; - pRowidExpr = pChanges->a[i].pExpr; - }else{ - sqlite4ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName); - pParse->checkSchema = 1; - goto update_cleanup; - } - } + + /* Resolve the column name on the left of the assignment */ + for(j=0; jnCol; j++){ + if( sqlite4StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ) break; + } + if( j==pTab->nCol ){ + sqlite4ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName); + pParse->checkSchema = 1; + goto update_cleanup; + } + aXRef[j] = i; + + /* Check if this column is part of the primary key. If so, set bChngPk. */ + if( !IsView(pTab) ){ + for(iPkCol=0; iPkColnColumn; iPkCol++){ + if( pPk->aiColumn[iPkCol]==j ) bChngPk = 1; + } + } + #ifndef SQLITE_OMIT_AUTHORIZATION { int rc; rc = sqlite4AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, pTab->aCol[j].zName, db->aDb[iDb].zName); @@ -228,68 +244,75 @@ } } #endif } - hasFK = sqlite4FkRequired(pParse, pTab, aXRef, chngRowid); - - /* Allocate memory for the array aRegIdx[]. There is one entry in the - ** array for each index associated with table being updated. Fill in - ** the value with a register number for indices that are to be used - ** and with zero for unused indices. - */ - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} - if( nIdx>0 ){ - aRegIdx = sqlite4DbMallocRaw(db, sizeof(Index*) * nIdx ); - if( aRegIdx==0 ) goto update_cleanup; - } - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - int reg; - if( hasFK || chngRowid ){ - reg = ++pParse->nMem; /* Register for index key */ - pParse->nMem++; /* Extra register for index data */ - }else{ - reg = 0; - for(i=0; inColumn; i++){ - if( aXRef[pIdx->aiColumn[i]]>=0 ){ - reg = ++pParse->nMem; - break; - } - } - } - aRegIdx[j] = reg; - } - /* Begin generating code. */ v = sqlite4GetVdbe(pParse); if( v==0 ) goto update_cleanup; if( pParse->nested==0 ) sqlite4VdbeCountChanges(v); sqlite4BeginWriteOperation(pParse, 1, iDb); #ifndef SQLITE_OMIT_VIRTUALTABLE + /* TODO: This is currently broken */ /* Virtual tables must be handled separately */ if( IsVirtual(pTab) ){ - updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, - pWhere, onError); + updateVirtualTable(pParse, pSrc, pTab, pChanges, 0, aXRef, pWhere, onError); pWhere = 0; - pTabList = 0; + pSrc = 0; goto update_cleanup; } #endif - /* Allocate required registers. */ - regRowSet = ++pParse->nMem; - regOldRowid = regNewRowid = ++pParse->nMem; + hasFK = sqlite4FkRequired(pParse, pTab, aXRef); + + /* Allocate memory for the array aRegIdx[]. There is one entry in the + ** array for each index associated with table being updated. Fill in + ** the value with a register number for indices that are to be used + ** and with zero for unused indices. */ + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} + aRegIdx = sqlite4DbMallocZero(db, sizeof(Index*) * nIdx ); + if( aRegIdx==0 ) goto update_cleanup; + + /* Allocate registers for and populate the aRegIdx array. */ + for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ + if( pIdx==pPk || hasFK || bChngPk ){ + aRegIdx[j] = ++pParse->nMem; + }else{ + for(i=0; inColumn; i++){ + if( aXRef[pIdx->aiColumn[i]]>=0 ){ + aRegIdx[j] = ++pParse->nMem; + break; + } + } + } + } + + /* Allocate other required registers. Specifically: + ** + ** regKeySet: 1 register + ** regOldKey: 1 register + ** regOldTr: nCol+1 registers + ** regNewTr: nCol+1 registers + ** + ** The regOldTr allocation is only required if there are either triggers + ** or foreign keys to be processed. + ** + ** The regOldTr and regNewTr register arrays include space for the + ** implicit primary key value if the table in question does not have an + ** explicit PRIMARY KEY. + */ + regKeySet = ++pParse->nMem; + regOldKey = ++pParse->nMem; if( pTrigger || hasFK ){ - regOld = pParse->nMem + 1; - pParse->nMem += pTab->nCol; - } - if( chngRowid || pTrigger || hasFK ){ - regNewRowid = ++pParse->nMem; - } - regNew = pParse->nMem + 1; - pParse->nMem += pTab->nCol; + regOldTr = pParse->nMem + 1; + regOld = regOldTr+1; + pParse->nMem += (pTab->nCol + 1); + } + regNewTr = pParse->nMem + 1; + regNew = regNewTr+1; + pParse->nMem += (pTab->nCol+1); /* Start the view context. */ if( isView ){ sqlite4AuthContextPush(pParse, &sContext, pTab->zName); } @@ -308,108 +331,98 @@ */ if( sqlite4ResolveExprNames(&sNC, pWhere) ){ goto update_cleanup; } - /* Begin the database scan - */ - sqlite4VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); - pWInfo = sqlite4WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED - ); - if( pWInfo==0 ) goto update_cleanup; - okOnePass = pWInfo->okOnePass; - - /* Remember the rowid of every item to be updated. - */ - sqlite4VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid); - if( !okOnePass ){ - sqlite4VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); - } - - /* End the database scan loop. - */ - sqlite4WhereEnd(pWInfo); - - /* Initialize the count of updated rows - */ - if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ - regRowCount = ++pParse->nMem; - sqlite4VdbeAddOp2(v, OP_Integer, 0, regRowCount); - } - - if( !isView ){ - /* - ** Open every index that needs updating. Note that if any - ** index could potentially invoke a REPLACE conflict resolution - ** action, then we need to open all indices because we might need - ** to be deleting some records. - */ - if( !okOnePass ) sqlite4OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); - if( onError==OE_Replace ){ - openAll = 1; - }else{ - openAll = 0; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->onError==OE_Replace ){ - openAll = 1; - break; - } - } - } - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ - KeyInfo *pKey = sqlite4IndexKeyinfo(pParse, pIdx); - sqlite4VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - assert( pParse->nTab>iCur+i+1 ); - } - } - } - - /* Top of the update loop */ - if( okOnePass ){ - int a1 = sqlite4VdbeAddOp1(v, OP_NotNull, regOldRowid); - addr = sqlite4VdbeAddOp0(v, OP_Goto); - sqlite4VdbeJumpHere(v, a1); - }else{ - addr = sqlite4VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); + /* This block codes a loop that iterates through all rows of the table + ** identified by the UPDATE statements WHERE clause. The primary key + ** of each row visited by the loop is added to the KeySet object stored + ** in register regKeySet. + ** + ** There is one exception to the above: If static analysis of the WHERE + ** clause indicates that the loop will visit at most one row, then the + ** KeySet object is bypassed and the primary key of the single row (if + ** any) left in register regOldKey. This is called the "one-pass" + ** approach. Set okOnePass to true if it can be used in this case. */ + sqlite4VdbeAddOp3(v, OP_Null, 0, regKeySet, regOldKey); + pWInfo = sqlite4WhereBegin(pParse, pSrc, pWhere, 0, 0, WHERE_ONEPASS_DESIRED); + if( pWInfo==0 ) goto update_cleanup; + okOnePass = pWInfo->okOnePass; + sqlite4VdbeAddOp2(v, OP_RowKey, iCur+iPk, regOldKey); + if( !okOnePass ){ + sqlite4VdbeAddOp2(v, OP_KeySetAdd, regKeySet, regOldKey); + } + sqlite4WhereEnd(pWInfo); + + /* Open every index that needs updating. If any index could potentially + ** invoke a REPLACE conflict resolution action, then we need to open all + ** indices because we might need to be deleting some records. */ + if( !isView ){ + /* Set bOpenAll to true if this UPDATE might strike a REPLACE */ + bOpenAll = (onError==OE_Replace); + for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + if( aRegIdx[i] && pIdx->onError==OE_Replace ) bOpenAll = 1; + } + + /* If bOpenAll is true, open all indexes. Otherwise, just open those + ** indexes for which the corresponding aRegIdx[] entry is non-zero + ** (those that index columns that will be modified by this UPDATE + ** statement). Also, if the one-pass approach is being used, do not + ** open the primary key index here - it is already open. */ + for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + if( (bOpenAll || aRegIdx[i]) && (okOnePass==0 || pIdx!=pPk) ){ + sqlite4OpenIndex(pParse, iCur+i, iDb, pIdx, OP_OpenWrite); + } + } + } + + /* The next instruction coded is the top of the update loop (executed once + ** for each row to be updated). + ** + ** If okOnePass is true, then regOldKey either contains the encoded PK of + ** the row to update, or it is NULL (indicating that this statement will + ** update zero rows). If this is the case, jump to the end of the loop + ** without doing anything. Otherwise - if okOnePass is true and regOldKey + ** contains something other than NULL - proceed. + ** + ** Or, if okOnePass is false, then the KeySet object stored in register + ** regKeySet contains the set of encoded PKs for the rows that will + ** be updated by this statement. Read the next one into register regOldKey. + ** Or, if the KeySet is already empty, jump to the end of the loop. + */ + if( okOnePass ){ + int a1 = sqlite4VdbeAddOp1(v, OP_NotNull, regOldKey); + addr = sqlite4VdbeAddOp0(v, OP_Goto); + sqlite4VdbeJumpHere(v, a1); + }else{ + addr = sqlite4VdbeAddOp3(v, OP_KeySetRead, regKeySet, 0, regOldKey); } /* Make cursor iCur point to the record that is being updated. If ** this record does not exist for some reason (deleted by a trigger, - ** for example, then jump to the next iteration of the RowSet loop. */ - sqlite4VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - - /* If the record number will change, set register regNewRowid to - ** contain the new value. If the record number is not being modified, - ** then regNewRowid is the same register as regOldRowid, which is - ** already populated. */ - assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid ); - if( chngRowid ){ - sqlite4ExprCode(pParse, pRowidExpr, regNewRowid); - sqlite4VdbeAddOp1(v, OP_MustBeInt, regNewRowid); - } + ** for example, then jump to the next iteration of the KeySet loop. + ** TODO: If okOnePass is true, does iCur already point to this record? */ + sqlite4VdbeAddOp4(v, OP_NotFound, iCur+iPk, addr, regOldKey, 0, P4_INT32); /* If there are triggers on this table, populate an array of registers ** with the required old.* column data. */ if( hasFK || pTrigger ){ u32 oldmask = (hasFK ? sqlite4FkOldmask(pParse, pTab) : 0); oldmask |= sqlite4TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); + + if( bImplicitPk ){ + sqlite4VdbeAddOp2(v, OP_Rowid, iCur+iPk, regOldTr); + } for(i=0; inCol; i++){ if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<nCol-1); for(i=0; inCol; i++){ - if( i==pTab->iPKey ){ - /*sqlite4VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ - }else{ - j = aXRef[i]; - if( j>=0 ){ - sqlite4ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); - }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<=0 ){ + sqlite4ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); + }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<nCol); sqlite4TableAffinityStr(v, pTab); sqlite4CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); + TRIGGER_BEFORE, pTab, regOldTr, onError, addr); /* The row-trigger may have deleted the row being updated. In this ** case, jump to the next row. No updates or AFTER triggers are ** required. This behaviour - what happens when the row being updated ** is deleted or renamed by a BEFORE trigger - is left undefined in the ** documentation. */ - sqlite4VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); + sqlite4VdbeAddOp4Int(v, OP_NotFound, iCur+iPk, addr, regOldKey, 0); /* If it did not delete it, the row-trigger may still have modified ** some of the columns of the row being updated. Load the values for ** all columns not modified by the update statement into their ** registers in case this has happened. */ for(i=0; inCol; i++){ - if( aXRef[i]<0 && i!=pTab->iPKey ){ - sqlite4VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); + if( aXRef[i]<0 ){ + sqlite4VdbeAddOp3(v, OP_Column, iCur+iPk, i, regNew+i); sqlite4ColumnDefault(v, pTab, i, regNew+i); } } } if( !isView ){ int j1; /* Address of jump instruction */ /* Do constraint checks. */ - sqlite4GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, - aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0); + assert( bChngPk==0 || bImplicitPk==0 ); + if( bChngPk==0 ) aRegIdx[iPk] = 0; + sqlite4GenerateConstraintChecks( + pParse, pTab, iCur, regNew, aRegIdx, regOldKey, 1, onError, addr, 0 + ); + if( bChngPk==0 ) aRegIdx[iPk] = regOldKey; /* Do FK constraint checks. */ if( hasFK ){ - sqlite4FkCheck(pParse, pTab, regOldRowid, 0); + sqlite4FkCheck(pParse, pTab, regOld, 0); } /* Delete the index entries associated with the current record. */ - j1 = sqlite4VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); + j1 = sqlite4VdbeAddOp4(v, OP_NotFound, iCur+iPk, 0, regOldKey, 0, P4_INT32); sqlite4GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); - /* If changing the record number, delete the old record. */ - if( hasFK || chngRowid ){ + /* Delete the old record */ + if( hasFK || bChngPk ){ sqlite4VdbeAddOp2(v, OP_Delete, iCur, 0); } sqlite4VdbeJumpHere(v, j1); if( hasFK ){ - sqlite4FkCheck(pParse, pTab, 0, regNewRowid); + sqlite4FkCheck(pParse, pTab, 0, regNew); } /* Insert the new index entries and the new record. */ - sqlite4CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0); + sqlite4CompleteInsertion(pParse, pTab, iCur, regNew, aRegIdx, 1, 0, 0); /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just updated. */ if( hasFK ){ - sqlite4FkActions(pParse, pTab, pChanges, regOldRowid); + sqlite4FkActions(pParse, pTab, pChanges, regOldKey); } } - /* Increment the row counter - */ - if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){ - sqlite4VdbeAddOp2(v, OP_AddImm, regRowCount, 1); - } - sqlite4CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_AFTER, pTab, regOldRowid, onError, addr); + TRIGGER_AFTER, pTab, regOldTr, onError, addr); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ sqlite4VdbeAddOp2(v, OP_Goto, 0, addr); sqlite4VdbeJumpHere(v, addr); - /* Close all tables */ + /* Close all cursors */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ - sqlite4VdbeAddOp2(v, OP_Close, iCur+i+1, 0); + if( bOpenAll || aRegIdx[i] ){ + sqlite4VdbeAddOp2(v, OP_Close, iCur+i, 0); } } - sqlite4VdbeAddOp2(v, OP_Close, iCur, 0); /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite4AutoincrementEnd(pParse); } - /* - ** Return the number of rows that were changed. If this routine is - ** generating code because of a call to sqlite4NestedParse(), do not - ** invoke the callback function. - */ - if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){ - sqlite4VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); - sqlite4VdbeSetNumCols(v, 1); - sqlite4VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); - } - update_cleanup: sqlite4AuthContextPop(&sContext); sqlite4DbFree(db, aRegIdx); sqlite4DbFree(db, aXRef); - sqlite4SrcListDelete(db, pTabList); + sqlite4SrcListDelete(db, pSrc); sqlite4ExprListDelete(db, pChanges); sqlite4ExprDelete(db, pWhere); return; } /* Make sure "isView" and other macros defined above are undefined. Otherwise Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2124,21 +2124,27 @@ }else{ aData = 0; MemSetTypeFlag(pDest, MEM_Null); } if( rc==SQLITE_OK && aData ){ - rc = sqlite4VdbeCreateDecoder(db, aData, nData, pC->nField, &pCodec); + /* TODO: Fix this somehow... */ + int nField = pC->nField; + if( pC->pKeyInfo && pC->pKeyInfo->nData ) nField = pC->pKeyInfo->nData; + + rc = sqlite4VdbeCreateDecoder(db, aData, nData, nField, &pCodec); if( rc==0 ){ pDefault = (pOp->p4type==P4_MEM) ? pOp->p4.pMem : 0; rc = sqlite4VdbeDecodeValue(pCodec, pOp->p2, pDefault, pDest); + assert( rc==SQLITE_OK ); sqlite4VdbeDestroyDecoder(pCodec); } }else{ sqlite4VdbeMemSetNull(pDest); } UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); + assert( rc<100 ); break; } /* Opcode: Affinity P1 P2 * P4 * ** @@ -2149,21 +2155,75 @@ ** memory cell in the range. */ case OP_Affinity: { const char *zAffinity; /* The affinity to be applied */ char cAff; /* A single character of affinity */ + Mem *pEnd; zAffinity = pOp->p4.z; assert( zAffinity!=0 ); - assert( zAffinity[pOp->p2]==0 ); - pIn1 = &aMem[pOp->p1]; - while( (cAff = *(zAffinity++))!=0 ){ - assert( pIn1 <= &p->aMem[p->nMem] ); + assert( sqlite4Strlen30(zAffinity)>=pOp->p2 ); + + pEnd = &aMem[pOp->p2+pOp->p1]; + for(pIn1=&aMem[pOp->p1]; pIn1apCsr[pOp->p1]; + pKeyInfo = pC->pKeyInfo; + pData0 = &aMem[pOp->p2]; + pOut = &aMem[pOp->p3]; + aRec = 0; + + memAboutToChange(p, pOut); + + nField = pKeyInfo->nField; + if( pOp->p4type==P4_INT32 && pOp->p4.i ){ + nField = pOp->p4.i; + assert( nField<=pKeyInfo->nField ); + } + rc = sqlite4VdbeEncodeKey( + db, pData0, nField, pC->iRoot, pKeyInfo, &aRec, &nRec, 0 + ); + + if( rc ){ + sqlite4DbFree(db, aRec); + }else{ + rc = sqlite4VdbeMemSetStr(pOut, aRec, nRec, 0, SQLITE_DYNAMIC); + REGISTER_TRACE(pOp->p3, pOut); + UPDATE_MAX_BLOBSIZE(pOut); + } + break; } /* Opcode: MakeKey P1 P2 * * * ** @@ -2171,21 +2231,25 @@ ** opcode performs the subsequent MakeRecord and also generates ** a key for the cursor P1 and stores that key in register P2. */ /* Opcode: MakeRecord P1 P2 P3 P4 * ** -** Convert registers P1..P1+P2-1 into a data record and store the result -** in register P3. The OP_Column opcode can be used to decode the record. -** -** P4 may be a string that is P2 characters long. The nth character of the -** string indicates the column affinity that should be used for the nth -** field of the index key. -** -** The mapping from character to affinity is given by the SQLITE_AFF_ -** macros defined in sqliteInt.h. -** -** If P4 is NULL then all index fields have the affinity NONE. +** This opcode uses the array of P2 registers starting at P1 as inputs. +** +** P4 may be a string that is P2 characters long, or it may be NULL. The nth +** character of the string indicates the column affinity that should be used +** for the nth field of the index key. The mapping from character to affinity +** is given by the SQLITE_AFF_ macros defined in sqliteInt.h. If P4 is NULL +** then all index fields have the affinity NONE. +** +** This opcode expands any zero-blobs within the input array. Then if +** P4 is not NULL it applies the affinities that it specifies to the input +** array elements. Finally, if P3 is not 0, it encodes the input array +** into a data record and stores the result in register P3. The OP_Column +** opcode can be used to decode the record. +** +** Specifying P3==0 is only useful if the previous opcode is an OP_MakeKey. */ case OP_MakeKey: case OP_MakeRecord: { Mem *pData0; /* First field to be combined into the record */ Mem *pLast; /* Last field of the record */ @@ -2219,15 +2283,10 @@ assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 ); pData0 = &aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; - /* Identify the output register */ - assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); - pOut = &aMem[pOp->p3]; - memAboutToChange(p, pOut); - /* Loop through the input elements. Apply affinity to each one and ** expand all zero-blobs. */ for(pMem=pData0; pMem<=pLast; pMem++){ assert( memIsValid(pMem) ); @@ -2240,23 +2299,27 @@ } /* Compute the key (if this is a MakeKey opcode) */ if( pC ){ aRec = 0; - rc = sqlite4VdbeEncodeKey(db, pData0, nField, pC->iRoot, pC->pKeyInfo, - &aRec, &nRec, 0); + rc = sqlite4VdbeEncodeKey(db, + pData0, pC->pKeyInfo->nField, pC->iRoot, pC->pKeyInfo, &aRec, &nRec, 0 + ); if( rc ){ sqlite4DbFree(db, aRec); }else{ rc = sqlite4VdbeMemSetStr(pKeyOut, aRec, nRec, 0, SQLITE_DYNAMIC); REGISTER_TRACE(keyReg, pKeyOut); UPDATE_MAX_BLOBSIZE(pKeyOut); } } - /* Compute the value */ - if( rc==SQLITE_OK ){ + /* If P3 is not 0, compute the data rescord */ + if( rc==SQLITE_OK && pOp->p3 ){ + assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); + pOut = &aMem[pOp->p3]; + memAboutToChange(p, pOut); aRec = 0; rc = sqlite4VdbeEncodeData(db, pData0, nField, &aRec, &nRec); if( rc ){ sqlite4DbFree(db, aRec); }else{ @@ -2306,10 +2369,49 @@ ** there are active writing VMs or active VMs that use shared cache. ** ** This instruction causes the VM to halt. */ case OP_AutoCommit: { + int bAutoCommit; /* New value for auto-commit flag */ + int bRollback; /* True to do transaction rollback */ + + bAutoCommit = pOp->p1; + bRollback = pOp->p2; + assert( bAutoCommit==1 || bRollback==0 ); + + if( bAutoCommit==db->autoCommit ){ + /* This branch is taken if the user is trying to BEGIN a transaction + ** when one is already open, or trying to commit or rollback a transaction + ** when none is open. Return a suitable error message. */ + const char *zErr; + if( bAutoCommit==0 ){ + zErr = "cannot start a transaction within a transaction"; + }else if( bRollback ){ + zErr = "cannot rollback - no transaction is active"; + }else{ + zErr = "cannot commit - no transaction is active"; + } + sqlite4SetString(&p->zErrMsg, db, zErr); + rc = SQLITE_ERROR; + } + + else if( bAutoCommit==0 ){ + db->autoCommit = 0; + }else{ + if( bRollback ){ + sqlite4RollbackAll(db); + }else if( rc = sqlite4VdbeCheckFk(p, 1) ){ + rc = SQLITE_ERROR; + goto vdbe_return; + } + + db->autoCommit = 1; + sqlite4VdbeHalt(p); + rc = (p->rc ? SQLITE_DONE : SQLITE_ERROR); + goto vdbe_return; + } + break; } /* Opcode: Transaction P1 P2 * * * ** @@ -2346,14 +2448,15 @@ if( pKV->iTransLevel==0 ){ rc = sqlite4KVStoreBegin(pKV, 1); } }else{ /* A write transaction is needed */ - needStmt = pKV->iTransLevel>0 && (p->needSavepoint || db->activeVdbeCnt>1); + needStmt = db->autoCommit==0 && (p->needSavepoint || db->activeVdbeCnt>1); if( pKV->iTransLevel<2 ){ rc = sqlite4KVStoreBegin(pKV, 2); - }else if( p->needSavepoint ){ + } + if( needStmt ){ rc = sqlite4KVStoreBegin(pKV, pKV->iTransLevel+1); if( rc==SQLITE_OK ){ p->stmtTransMask |= ((yDbMask)1)<p1; } } @@ -2603,12 +2706,17 @@ assert( pOp->p1>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; - rc = sqlite4KVStoreOpen(db, "ephm", ":memory:", &pCx->pTmpKV, - SQLITE_KVOPEN_TEMPORARY | SQLITE_KVOPEN_NO_TRANSACTIONS); + + rc = sqlite4KVStoreOpen(db, "ephm", 0, &pCx->pTmpKV, + SQLITE_KVOPEN_TEMPORARY | SQLITE_KVOPEN_NO_TRANSACTIONS + ); + if( rc==SQLITE_OK ) rc = sqlite4KVStoreOpenCursor(pCx->pTmpKV, &pCx->pKVCur); + if( rc==SQLITE_OK ) rc = sqlite4KVStoreBegin(pCx->pTmpKV, 2); + pCx->pKeyInfo = pOp->p4.pKeyInfo; if( pCx->pKeyInfo ) pCx->pKeyInfo->enc = ENC(p->db); pCx->isIndex = !pCx->isTable; break; } @@ -2672,19 +2780,69 @@ assert( pOp->p1>=0 && pOp->p1nCursor ); sqlite4VdbeFreeCursor(p, p->apCsr[pOp->p1]); p->apCsr[pOp->p1] = 0; break; } + +/* Opcode: SeekPk P1 * P3 * * +** +** P1 must be a cursor open on a PRIMARY KEY index. P3 is a cursor open +** on an auxiliary index on the same table. P3 must be pointing to a valid +** index entry. +** +** This opcode seeks cursor P1 so that it points to the PK index entry +** that corresponds to the same table row as the current entry that +** cursor P3 points to. The entry must exist. If it does not, this opcode +** throws an SQLITE_CORRUPT exception. +*/ +case OP_SeekPk: { + VdbeCursor *pPk; /* Cursor P1 */ + VdbeCursor *pIdx; /* Cursor P3 */ + KVByteArray *aKey; /* Key data from cursor pIdx */ + KVSize nKey; /* Size of aKey[] in bytes */ + int nShort; /* Size of aKey[] without PK fields */ + KVByteArray *aPkKey; + KVSize nPkKey; + int nVarint; + + pPk = p->apCsr[pOp->p1]; + pIdx = p->apCsr[pOp->p3]; + assert( pIdx->pKeyInfo->nPK>0 ); + assert( pPk->pKeyInfo->nPK==0 ); + + rc = sqlite4KVCursorKey(pIdx->pKVCur, &aKey, &nKey); + if( rc==SQLITE_OK ){ + nShort = sqlite4VdbeShortKey(aKey, nKey, + pIdx->pKeyInfo->nField - pIdx->pKeyInfo->nPK + ); + + nPkKey = sqlite4VarintLen(pPk->iRoot) + nKey - nShort; + aPkKey = sqlite4DbMallocRaw(db, nPkKey); + + if( aPkKey ){ + putVarint32(aPkKey, pPk->iRoot); + memcpy(&aPkKey[nPkKey - (nKey-nShort)], &aKey[nShort], nKey-nShort); + rc = sqlite4KVCursorSeek(pPk->pKVCur, aPkKey, nPkKey, 0); + if( rc==SQLITE_NOTFOUND ){ + rc = SQLITE_CORRUPT_BKPT; + } + pPk->nullRow = 0; + sqlite4DbFree(db, aPkKey); + } + } + + break; +} /* Opcode: SeekGe P1 P2 P3 P4 * ** ** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), ** use the value in register P3 as the key. If cursor P1 refers ** to an SQL index, then P3 is the first in an array of P4 registers ** that are used as an unpacked index key. ** -** Reposition cursor P1 so that it points to the smallest entry that +** Reposition cursor P1 so that it points to the smallest entry that ** is greater than or equal to the key value. If there are no records ** greater than or equal to the key and P2 is not zero, then jump to P2. ** ** See also: Found, NotFound, Distinct, SeekLt, SeekGt, SeekLe */ @@ -2729,66 +2887,65 @@ */ case OP_SeekLt: /* jump, in3 */ case OP_SeekLe: /* jump, in3 */ case OP_SeekGe: /* jump, in3 */ case OP_SeekGt: { /* jump, in3 */ - int oc; - VdbeCursor *pC; - int nField; - KVByteArray *aProbe; - KVSize nProbe; - const KVByteArray *aKey; - KVSize nKey; - int c; - int n; - sqlite4_uint64 iRoot; + int op; /* Copy of pOp->opcode (the op-code) */ + VdbeCursor *pC; /* Cursor P1 */ + int nField; /* Number of values to encode into key */ + KVByteArray *aProbe; /* Buffer containing encoded key */ + KVSize nProbe; /* Size of aProbe[] in bytes */ + int dir; /* KV search dir (+ve or -ve) */ + const KVByteArray *aKey; /* Pointer to final cursor key */ + KVSize nKey; /* Size of aKey[] in bytes */ + + pC = p->apCsr[pOp->p1]; + pC->nullRow = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p2!=0 ); - pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); assert( pC->isOrdered ); - oc = pOp->opcode; - pC->nullRow = 0; - if( pC->isTable ){ - nField = 1; - }else{ - nField = pOp->p4.i; - } - rc = sqlite4VdbeEncodeKey(db, pIn3, nField, pC->iRoot, pC->pKeyInfo, - &aProbe, &nProbe, 0); - if( rc ){ - sqlite4DbFree(db, aProbe); - break; - } - rc = sqlite4KVCursorSeek(pC->pKVCur, aProbe, nProbe, - oc<=OP_SeekLe ? -1 : 1); - sqlite4DbFree(db, aProbe); - if( rc==SQLITE_OK ){ - if( oc==OP_SeekLt ){ - rc = sqlite4KVCursorPrev(pC->pKVCur); - }else if( oc==OP_SeekGt ){ - rc = sqlite4KVCursorNext(pC->pKVCur); - } - }else if( rc==SQLITE_INEXACT ){ - rc = SQLITE_OK; - } - if( rc==SQLITE_OK ){ + + /* Encode a database key consisting of the contents of the P4 registers + ** starting at register P3. Have the vdbecodec module allocate an extra + ** free byte at the end of the database key (see below). */ + op = pOp->opcode; + nField = pOp->p4.i; + pIn3 = &aMem[pOp->p3]; + rc = sqlite4VdbeEncodeKey( + db, pIn3, nField, pC->iRoot, pC->pKeyInfo, &aProbe, &nProbe, 1 + ); + + /* Opcode search-dir increment-key + ** -------------------------------------- + ** SeekLt -1 no + ** SeekLe -1 yes + ** SeekGe +1 no + ** SeekGt +1 yes + */ + dir = +1; + if( op==OP_SeekLe || op==OP_SeekLt ) dir = -1; + if( op==OP_SeekLe || op==OP_SeekGt ) aProbe[nProbe++] = 0xFF; + if( rc==SQLITE_OK ){ + rc = sqlite4KVCursorSeek(pC->pKVCur, aProbe, nProbe, dir); + } + if( rc==SQLITE_OK || rc==SQLITE_INEXACT ){ rc = sqlite4KVCursorKey(pC->pKVCur, &aKey, &nKey); - if( rc==SQLITE_OK ){ - iRoot = 0; - n = sqlite4GetVarint64(aKey, nKey, &iRoot); - if( iRoot!=pC->iRoot ) rc = SQLITE_DONE; - c = aKey[n]; - if( c<0x05 || c>0xfa ) rc = SQLITE_DONE; + if( rc==SQLITE_OK && memcmp(aKey, aProbe, sqlite4VarintLen(pC->iRoot)) ){ + rc = SQLITE_NOTFOUND; } } - if( rc==SQLITE_DONE ){ + + /* Free the key allocated above. If no error has occurred but the cursor + ** does not currently point to a valid entry, jump to instruction P2. */ + sqlite4DbFree(db, aProbe); + if( rc==SQLITE_NOTFOUND ){ rc = SQLITE_OK; pc = pOp->p2 - 1; } break; } @@ -2896,14 +3053,16 @@ } if( rc==SQLITE_OK ){ rc = sqlite4KVCursorSeek(pC->pKVCur, pProbe, nProbe, +1); if( rc==SQLITE_INEXACT || rc==SQLITE_OK ){ rc = sqlite4KVCursorKey(pC->pKVCur, &pKey, &nKey); - if( rc==SQLITE_OK && nKey>=nProbe && memcmp(pKey, pProbe, nKey)==0 ){ + if( rc==SQLITE_OK && nKey>=nProbe && memcmp(pKey, pProbe, nProbe)==0 ){ alreadyExists = 1; pC->nullRow = 0; } + }else if( rc==SQLITE_NOTFOUND ){ + rc = SQLITE_OK; } } sqlite4DbFree(db, pFree); if( pOp->opcode==OP_Found ){ if( alreadyExists ) pc = pOp->p2 - 1; @@ -2913,112 +3072,77 @@ break; } /* Opcode: IsUnique P1 P2 P3 P4 * ** -** Cursor P1 is open on an index. -** -** The P3 register contains an integer record number. Call this record -** number R. Register P4 is the first in a set of N contiguous registers -** that make up an unpacked index key that can be used with cursor P1. -** The value of N can be inferred from the KeyInfo.nField of the cursor. -** N includes the rowid value appended to the end of the index record. -** This rowid value may or may not be the same as R. -** -** If any of the N registers beginning with register P4 contains a NULL -** value, jump immediately to P2. -** -** Otherwise, this instruction checks if cursor P1 contains an entry -** where the first (N-1) fields match but the rowid value at the end -** of the index entry is not R. If there is no such entry (meaning that -** a row about to be inserted with rowid R is unique) then control jumps -** to instruction P2. Otherwise, the rowid of the conflicting index -** entry is copied to register P3 and control falls through to the next -** instruction. -** -** See also: NotFound, NotExists, Found -*/ -case OP_IsUnique: { /* jump, in3 */ -#if 0 - u16 ii; - VdbeCursor *pCx; - KVCursor *pKVCur; - u16 nField; - Mem *aMx; - KVByteArray *pProbe; - KVSize nProbe; - KVSize nShort; - KVSize nData; - int isUnique; - i64 R; /* Rowid stored in register P3 */ - - pIn3 = &aMem[pOp->p3]; - aMx = &aMem[pOp->p4.i]; - /* Assert that the values of parameters P1 and P4 are in range. */ - assert( pOp->p4type==P4_INT32 ); - assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - - /* Find the index cursor. */ - pCx = p->apCsr[pOp->p1]; - pCx->seekResult = 0; - pCx->cacheStatus = CACHE_STALE; - pKVCur = pCx->pKVCur; - nField = pCx->pKeyInfo->nField; - - /* If any of the values are NULL, take the jump. */ - nField = pCx->pKeyInfo->nField; - for(ii=0; iip2 - 1; - pCrsr = 0; - break; - } - } - assert( (aMx[nField].flags & MEM_Null)==0 ); - - isUnique = 1; - if( pCrsr!=0 ){ - /* Extract the value of R from register P3. */ - sqlite4VdbeMemIntegerify(pIn3); - R = pIn3->u.i; - - /* Generate the probe key */ - rc = sqlite4VdbeEncodeKey(db, pIn3, nField, pCx->iRoot, - pCx->pKeyInfo, &pProbe, &nProbe, &nShort); - if( rc==SQLITE_OK ){ - rc = sqlite4KVCursorSeek(pKVCur, pProbe, nProbe, +1); - if( rc==SQLITE_OK ){ - /* Full key already exists in the index. Not unique. */ - isUnique = 0; - }else if( rc==SQLITE_INEXACT ){ - int c = sqlite4KVCursorCompare(pKVCur, pProbe, nShort); - if( c>0 ){ - rc = sqlite4KVCursorPrev(pKVCur); - if( rc==SQLITE_OK ){ - c = sqlite4KVCursorCompare(pKVCur, pProbe, nShort); - } - } - if( c ) isUnique = 0; - } - sqlite4DbFree(db, pProbe); - if( isUnique ){ - pc = pOp->p2 - 1; - }else{ - /* Collision. Copy the conflicting rowid into register P3. */ - rc = sqlite4KVCursorData(pKVCur, 0, -1, &aData, &nData); - if( rc==SQLITE_OK ){ - rc = sqlite4VdbeCreateDecoder(db, aData, nData, nField, &pCodec); - if( rc==SQLITE_OK ){ - rc = sqlite4VdbeDecodeValue(pCodec, nField-1, 0, pIn3); - sqlite4VdbeDestroyDecoder(pCodec); - } - } - } - } - } -#endif +** Cursor P1 is open on an index that enforces a UNIQUE constraint. +** Register P3 contains an encoded key suitable to be inserted into the +** index. If the key can be inserted into the index without violating +** a UNIQUE constraint, jump to instruction P2. Otherwise, fall through +** to the next instruction. +** +** If P4 is a non-zero integer and the jump is not taken, then it is +** a register that currently contains a blob. At the start of the blob +** is a varint that contains the index number for the PRIMARY KEY index +** of the table. The contents of P4 are overwritten with an index key +** composed of the varint from the start of the initial blob content +** and the PRIMARY KEY values from the index entry causing the UNIQUE +** constraint to fail. +*/ +case OP_IsUnique: { /* jump, in3 */ + VdbeCursor *pC; + Mem *pProbe; + Mem *pOut; + int iOut; + int nShort; + int dir; + u64 dummy; + + KVByteArray *aKey; /* Key read from cursor */ + KVSize nKey; /* Size of aKey in bytes */ + + assert( pOp->p4type==P4_INT32 ); + + pProbe = &aMem[pOp->p3]; + pC = p->apCsr[pOp->p1]; + pOut = (pOp->p4.i==0 ? 0 : &aMem[pOp->p4.i]); + assert( pOut==0 || (pOut->flags & MEM_Blob) ); + + nShort = sqlite4VdbeShortKey(pProbe->z, pProbe->n, + pC->pKeyInfo->nField - pC->pKeyInfo->nPK + ); + assert( nShort<=pProbe->n ); + assert( (nShort==pProbe->n)==(pC->pKeyInfo->nPK==0) ); + + dir = (pC->pKeyInfo->nPK==0 ? 0 : 1); + rc = sqlite4KVCursorSeek(pC->pKVCur, pProbe->z, nShort, dir); + + if( rc==SQLITE_OK && pOut ){ + sqlite4VdbeMemCopy(pOut, pProbe); + }else if( rc==SQLITE_NOTFOUND ){ + rc = SQLITE_OK; + pc = pOp->p2-1; + }else if( rc==SQLITE_INEXACT ){ + assert( nShortn ); + rc = sqlite4KVCursorKey(pC->pKVCur, &aKey, &nKey); + if( rc==SQLITE_OK ){ + if( nKeyz, aKey, nShort) + || (nKey==pProbe->n && 0==memcmp(pProbe->z, aKey, nKey)) + ){ + pc = pOp->p2-1; + }else if( pOut ){ + iOut = sqlite4GetVarint64(pOut->z, pOut->n, &dummy); + rc = sqlite4VdbeMemGrow(pOut, iOut+(pProbe->n - nShort), 1); + if( rc==SQLITE_OK ){ + memcpy(&pOut->z[iOut], &aKey[nShort], (pProbe->n - nShort)); + pOut->n = iOut + (pProbe->n - nShort); + } + } + } + } + break; } /* Opcode: Sequence P1 P2 * * * ** @@ -3033,11 +3157,11 @@ pOut->u.i = p->apCsr[pOp->p1]->seqCount++; break; } -/* Opcode: NewRowid P1 P2 P3 * * +/* Opcode: NewRowid P1 P2 * * * ** ** Get a new integer record number (a.k.a "rowid") used as the key to a table. ** The record number is not previously used as a key in the database ** table that cursor P1 points to. The new record number is written ** to register P2. @@ -3071,11 +3195,10 @@ ** The second algorithm is to select a rowid at random and see if ** it already exists in the table. If it does not exist, we have ** succeeded. If the random rowid does exist, we select a new one ** and try again, up to 100 times. */ - assert( pC->isTable ); rc = sqlite4VdbeSeekEnd(pC, -1); if( rc==SQLITE_NOTFOUND ){ v = 0; rc = SQLITE_OK; @@ -3157,11 +3280,10 @@ pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( memIsValid(pData) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->isTable ); REGISTER_TRACE(pOp->p2, pData); if( pOp->opcode==OP_Insert ){ pKey = &aMem[pOp->p3]; assert( pKey->flags & MEM_Int ); @@ -3295,11 +3417,11 @@ assert( pC!=0 ); #ifndef SQLITE_OMIT_MERGE_SORT assert( pC->isSorter ); rc = sqlite4VdbeSorterRowkey(pC, pOut); #else - pOp->opcode = OP_RowKey; + pOp->opcode = OP_RowData; pc--; #endif break; } @@ -3335,11 +3457,10 @@ /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC->isSorter==0 ); - assert( pC->isIndex || pOp->opcode==OP_RowData ); assert( pC!=0 ); assert( pC->nullRow==0 ); assert( pC->pseudoTableReg==0 ); assert( !pC->isSorter ); assert( pC->pKVCur!=0 ); @@ -3351,11 +3472,11 @@ rc = sqlite4KVCursorData(pCrsr, 0, -1, &pData, &nData); } if( rc==SQLITE_OK && nData>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } - sqlite4VdbeMemSetStr(pOut, (const char*)pData, nData, 0, SQLITE_DYNAMIC); + sqlite4VdbeMemSetStr(pOut, (const char*)pData, nData, 0, SQLITE_TRANSIENT); pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -3437,10 +3558,12 @@ assert( pC!=0 ); rc = sqlite4VdbeSeekEnd(pC, -1); if( rc==SQLITE_NOTFOUND ){ rc = SQLITE_OK; if( pOp->p2 ) pc = pOp->p2 - 1; + }else{ + pC->nullRow = 0; } break; } @@ -3576,25 +3699,15 @@ } pC->rowidIsValid = 0; break; } -/* Opcode: IdxInsert P1 P2 * * P5 -** -** Register P2 holds an SQL index key made using the -** MakeRecord instructions. This opcode writes that key -** into the index P1. Data for the entry is nil. -** -** P3 is a flag that provides a hint to the b-tree layer that this -** insert is likely to be an append. -** -** This instruction only works for indices. The equivalent instruction -** for tables is OP_Insert. +/* Opcode: SorterInsert P1 P2 P3 */ case OP_SorterInsert: { /* in2 */ +#ifndef SQLITE_OMIT_MERGE_SORT VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) ); pIn2 = &aMem[pOp->p2]; @@ -3603,55 +3716,52 @@ rc = ExpandBlob(pIn2); if( rc==SQLITE_OK ){ rc = sqlite4VdbeSorterWrite(db, pC, pIn2); } break; +#endif + + /* If OMIT_MERGE_SORT is defined, fall through to IdxInsert. */ } - /* Opcode: IdxInsert P1 P2 P3 * P5 ** -** Register P2 holds the data and register P3 holds the key for an -** index record. Write this record into the index specified by the +** Register P3 holds the key and register P2 holds the data for an +** index entry. Write this record into the index specified by the ** cursor P1. -** -** P5 is a flag that provides a hint to the storage layer that this -** insert is likely to be an append. -** -** This instruction only works for indices. The equivalent instruction -** for tables is OP_Insert. */ -case OP_IdxInsert: { /* in2 */ +case OP_IdxInsert: { VdbeCursor *pC; Mem *pKey; Mem *pData; - assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; - assert( pC ); - assert( pC->pKVCur ); - assert( pC->pKVCur->pStore ); pKey = &aMem[pOp->p3]; + pData = pOp->p2 ? &aMem[pOp->p2] : 0; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pC && pC->pKVCur && pC->pKVCur->pStore ); assert( pKey->flags & MEM_Blob ); - pData = &aMem[pOp->p2]; - assert( pData->flags & MEM_Blob ); + assert( pData==0 || (pData->flags & MEM_Blob) ); + + if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; + rc = sqlite4KVStoreReplace( pC->pKVCur->pStore, pKey->z, pKey->n, - pData->z, pData->n + (pData ? pData->z : 0), (pData ? pData->n : 0) ); + break; } -/* Opcode: IdxDelete P1 P2 P3 * * +/* Opcode: IdxDelete P1 * P3 * * ** -** The content of P3 registers starting at register P2 form -** an unpacked index key. This opcode removes that entry from the -** index opened by cursor P1. +** P1 is a cursor open on a database index. P3 contains a key suitable for +** the index. Delete P3 from P1. */ case OP_IdxDelete: { - assert( 0 ); break; } /* Opcode: IdxRowid P1 P2 * * * ** @@ -3664,39 +3774,52 @@ case OP_IdxRowid: { /* out2-prerelease */ assert( 0 ); break; } -/* Opcode: IdxGE P1 P2 P3 P4 P5 -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the ROWID. Compare this key value against the index -** that P1 is currently pointing to, ignoring the ROWID on the P1 index. -** -** If the P1 index entry is greater than or equal to the key value -** then jump to P2. Otherwise fall through to the next instruction. -** -** If P5 is non-zero then the key value is increased by an epsilon -** prior to the comparison. This make the opcode work like IdxGT except -** that if the key from register P3 is a prefix of the key in the cursor, -** the result is false whereas it would be true with IdxGT. -*/ -/* Opcode: IdxLT P1 P2 P3 P4 P5 -** -** The P4 register values beginning with P3 form an unpacked index -** key that omits the ROWID. Compare this key value against the index -** that P1 is currently pointing to, ignoring the ROWID on the P1 index. -** -** If the P1 index entry is less than the key value then jump to P2. -** Otherwise fall through to the next instruction. -** -** If P5 is non-zero then the key value is increased by an epsilon prior -** to the comparison. This makes the opcode work like IdxLE. +/* Opcode: IdxGE P1 P2 P3 +** +** P1 is an open cursor. P3 contains a database key formatted by MakeKey. +** This opcode compares the current key that index P1 points to with +** the key in register P3. +** +** If the index key is greater than or equal to the key in register P3, +** then jump to instruction P2. Otherwise, fall through to the next VM +** instruction. The comparison is done using memcmp(), except that if P3 +** is a prefix of the P1 key they are considered equal. */ case OP_IdxLT: /* jump */ -case OP_IdxGE: { /* jump */ - assert( 0 ); +case OP_IdxLE: /* jump */ +case OP_IdxGE: /* jump */ +case OP_IdxGT: { /* jump */ + VdbeCursor *pC; /* Cursor P1 */ + KVByteArray *aKey; /* Key from cursor P1 */ + KVSize nKey; /* Size of aKey[] in bytes */ + Mem *pCmp; /* Memory cell to compare index key with */ + int nCmp; /* Bytes of data to compare using memcmp() */ + int res; /* Result of memcmp() call */ + int bJump; /* True to take the jump */ + + pCmp = &aMem[pOp->p3]; + assert( pCmp->flags & MEM_Blob ); + pC = p->apCsr[pOp->p1]; + rc = sqlite4KVCursorKey(pC->pKVCur, &aKey, &nKey); + + if( rc==SQLITE_OK ){ + nCmp = pCmp->n; + if( nCmp>nKey ) nCmp = nKey; + + res = memcmp(aKey, pCmp->z, nCmp); + switch( pOp->opcode ){ + case OP_IdxLT: bJump = (res < 0); break; + case OP_IdxLE: bJump = (res <= 0); break; + case OP_IdxGE: bJump = (res >= 0); break; + case OP_IdxGT: bJump = (res > 0); break; + } + + if( bJump ) pc = pOp->p2 - 1; + } break; } /* Opcode: Clear P1 P2 P3 ** @@ -3860,10 +3983,53 @@ */ case OP_IntegrityCk: { break; } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +/* Opcode: KeySetAdd P1 P2 * * * +** +** Read the blob value from register P2 and store it in KeySet object P1. +*/ +case OP_KeySetAdd: { /* in1, in2 */ + pIn1 = &aMem[pOp->p1]; + if( (pIn1->flags & MEM_KeySet)==0 ){ + sqlite4VdbeMemSetKeySet(pIn1); + if( (pIn1->flags & MEM_KeySet)==0 ) goto no_mem; + } + pIn2 = &aMem[pOp->p2]; + assert( pIn2->flags & MEM_Blob ); + sqlite4KeySetInsert(pIn1->u.pKeySet, pIn2->z, pIn2->n); + break; +} + +/* Opcode: KeySetRead P1 P2 P3 * * +** +** Remove a value from MemSet object P1 and store it in register P3. +** Or, if MemSet P1 is already empty, leave P3 unchanged and jump to +** instruction P2. +*/ +case OP_KeySetRead: { /* in1 */ + const char *aKey; + int nKey; + + CHECK_FOR_INTERRUPT; + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p3]; + if( (pIn1->flags & MEM_KeySet) + && (aKey = sqlite4KeySetRead(pIn1->u.pKeySet, &nKey)) + ){ + rc = sqlite4VdbeMemSetStr(pOut, aKey, nKey, 0, SQLITE_TRANSIENT); + sqlite4KeySetNext(pIn1->u.pKeySet); + }else{ + /* The KeySet is empty */ + sqlite4VdbeMemSetNull(pIn1); + pc = pOp->p2 - 1; + } + + break; +} /* Opcode: RowSetAdd P1 P2 * * * ** ** Insert the integer value held by register P2 into a boolean index ** held in register P1. @@ -4099,10 +4265,11 @@ case OP_Param: { /* out2-prerelease */ VdbeFrame *pFrame; Mem *pIn; pFrame = p->pFrame; pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; + assert( memIsValid(pIn) ); sqlite4VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); break; } #endif /* #ifndef SQLITE_OMIT_TRIGGER */ Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -138,10 +138,11 @@ union { i64 i; /* Integer value used when MEM_Int is set in flags */ int nZero; /* Used when bit MEM_Zero is set in flags */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ + KeySet *pKeySet; /* Used only when flags==MEM_KeySet */ VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ } u; int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ @@ -173,10 +174,12 @@ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ #define MEM_Invalid 0x0080 /* Value is undefined */ #define MEM_TypeMask 0x00ff /* Mask of type bits */ + +#define MEM_KeySet 0x0020 /* Value is a KeySet object */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the ** string is \000 or \u0000 terminated @@ -381,14 +384,15 @@ int nIn, /* Number of entries in aIn[] */ int iTabno, /* The table this key applies to */ KeyInfo *pKeyInfo, /* Collating sequence information */ u8 **pzOut, /* Write the resulting key here */ int *pnOut, /* Number of bytes in the key */ - int *pnShort /* Number of bytes omitting primary key */ + int bIncr /* Make the key "incrementable" */ ); int sqlite4VdbeEncodeIntKey(u8 *aBuf,sqlite4_int64 v); int sqlite4VdbeDecodeIntKey(const KVByteArray*, KVSize, sqlite4_int64*); +int sqlite4VdbeShortKey(u8 *, int, int); int sqlite4MemCompare(const Mem*, const Mem*, const CollSeq*); int sqlite4VdbeExec(Vdbe*); int sqlite4VdbeList(Vdbe*); int sqlite4VdbeHalt(Vdbe*); int sqlite4VdbeChangeEncoding(Mem *, int); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -1013,11 +1013,11 @@ ** sqlite4MemRelease() were called from here. With -O2, this jumps ** to 6.6 percent. The test case is inserting 1000 rows into a table ** with no indexes using a single prepared INSERT statement, bind() ** and reset(). Inserts are grouped into a transaction. */ - if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){ + if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_KeySet) ){ sqlite4VdbeMemRelease(p); }else if( p->zMalloc ){ sqlite4DbFree(db, p->zMalloc); p->zMalloc = 0; } @@ -1803,58 +1803,82 @@ checkActiveVdbeCnt(db); if( p->pc<0 ){ /* No commit or rollback needed if the program never started */ }else{ - /* Check for immediate foreign key violations. */ - if( p->rc==SQLITE_OK ){ - sqlite4VdbeCheckFk(p, 0); - } - - /* If the auto-commit flag is set and this is the only active writer - ** VM, then we do either a commit or rollback of the current transaction. - */ - if( !sqlite4VtabInSync(db) - && db->autoCommit - && db->writeVdbeCnt==(p->readOnly==0) - ){ - rc = sqlite4VdbeCheckFk(p, 1); - if( rc!=SQLITE_OK ){ - rc = SQLITE_CONSTRAINT; - }else{ - /* The auto-commit flag is true, the vdbe program was successful - ** or hit an 'OR FAIL' constraint and there are no deferred foreign - ** key constraints to hold up the transaction. This means a commit - ** is required. */ - rc = vdbeCommit(db, p); - } - if( rc==SQLITE_BUSY && p->readOnly ){ - /* will return the error */ - }else if( rc!=SQLITE_OK ){ - p->rc = rc; - sqlite4RollbackAll(db); - }else{ - db->nDeferredCons = 0; - sqlite4CommitInternalChanges(db); - } - }else{ - /* Not in auto-commit mode. If the statement failed, rollback - ** the effects of just this one statement */ - if( p->rc!=SQLITE_OK && p->stmtTransMask!=0 ){ - int i; - for(i=0; inDb; i++){ - if( p->stmtTransMask & ((yDbMask)1)<aDb[i].pKV; - rc = sqlite4KVStoreRollback(pKV, pKV->iTransLevel-1); - if( rc ){ - sqlite4RollbackAll(db); - break; - } - } - } - } - } + /* eAction: + ** + ** 0 - Do commit, either statement or transaction. + ** 1 - Do rollback, either statement or transaction. + ** 2 - Do transaction rollback. + */ + int eAction = (p->rc!=SQLITE_OK); + + if( p->rc==SQLITE_CONSTRAINT ){ + if( p->errorAction==OE_Rollback ){ + eAction = 2; + }else if( p->errorAction==OE_Fail ){ + eAction = 0; + } + } + + if( eAction==0 && sqlite4VdbeCheckFk(p, 0) ){ + eAction = 1; + } + + if( eAction==2 || ( + sqlite4VtabInSync(db)==0 + && db->writeVdbeCnt==(p->readOnly==0) + && db->autoCommit + )){ + if( eAction==0 && sqlite4VdbeCheckFk(p, 1) ){ + eAction = 1; + } + + if( eAction==0 ){ + int rc = vdbeCommit(db, p); + if( rc!=SQLITE_OK ){ + p->rc = rc; + eAction = 1; + }else{ + assert( db->nDeferredCons<=0 ); + sqlite4CommitInternalChanges(db); + } + } + + if( eAction ){ + sqlite4RollbackAll(db); + db->autoCommit = 1; + } + + db->nDeferredCons = 0; + }else if( p->stmtTransMask ){ + /* Auto-commit mode is turned off and no "OR ROLLBACK" constraint was + ** encountered. So either commit (if eAction==0) or rollback (if + ** eAction==1) any statement transactions opened by this VM. */ + + int i; /* Used to iterate thru attached dbs */ + int (*xFunc)(KVStore *,int); /* Commit or rollback function */ + + assert( eAction==0 || eAction==1 ); + xFunc = (eAction ? sqlite4KVStoreRollback : sqlite4KVStoreCommit); + for(i=0; inDb; i++){ + if( p->stmtTransMask & ((yDbMask)1)<aDb[i].pKV; + rc = xFunc(pKV, pKV->iTransLevel-1); + if( rc ){ + sqlite4RollbackAll(db); + break; + } + } + } + } + + if( p->changeCntOn ){ + sqlite4VdbeSetChanges(db, (eAction ? 0 : p->nChange)); + } + p->nChange = 0; } /* We have successfully halted and closed the VM. Record this fact. */ if( p->pc>=0 ){ db->activeVdbeCnt--; @@ -1861,16 +1885,16 @@ if( !p->readOnly ){ db->writeVdbeCnt--; } assert( db->activeVdbeCnt>=db->writeVdbeCnt ); } + p->magic = VDBE_MAGIC_HALT; checkActiveVdbeCnt(db); if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } - return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); } /* Index: src/vdbecodec.c ================================================================== --- src/vdbecodec.c +++ src/vdbecodec.c @@ -106,16 +106,19 @@ }else{ size = type - 9; } if( ia)[ofst]; - for(i=4; ia[ofst+i-3]; + for(iByte=1; iBytea[ofst+iByte]; } sqlite4VdbeMemSetInt64(pOut, v); }else if( type<=21 ){ sqlite4_uint64 x; int e; @@ -526,11 +529,11 @@ if( e<=10 ) p->aOut[i] = 0x17+e; } }else if( flags & MEM_Str ){ if( enlargeEncoderAllocation(p, pMem->n*4 + 2) ) return SQLITE_NOMEM; - p->aOut[p->nOut++] = 0x0e; /* Text */ + p->aOut[p->nOut++] = 0x24; /* Text */ if( pColl==0 || pColl->xMkKey==0 ){ memcpy(p->aOut+p->nOut, pMem->z, pMem->n); p->nOut += pMem->n; }else{ n = pColl->xMkKey(pColl->pUser, pMem->z, pMem->n, p->aOut+p->nOut, @@ -550,11 +553,11 @@ n = pMem->n; a = (u8*)pMem->z; s = 1; t = 0; if( enlargeEncoderAllocation(p, (n*8+6)/7 + 2) ) return SQLITE_NOMEM; - p->aOut[p->nOut++] = 0x0f; /* Blob */ + p->aOut[p->nOut++] = 0x25; /* Blob */ for(i=0; iaOut[p->nOut++] = 0x80 | t | (x>>s); if( s<7 ){ t = x<<(7-s); @@ -573,59 +576,105 @@ } return SQLITE_OK; } /* -** Generate a record key from one or more data values +** Variables aKey/nKey contain an encoded index key. This function returns +** the length (in bytes) of the key with all but the first nField fields +** removed. +*/ +int sqlite4VdbeShortKey( + u8 *aKey, /* Buffer containing encoded key */ + int nKey, /* Size of buffer aKey[] in bytes */ + int nField /* Number of fields */ +){ + u8 *p = aKey; + u8 *pEnd = &aKey[nKey]; + u64 dummy; + int i; + + p = aKey; + p += sqlite4GetVarint64(p, pEnd-p, &dummy); + + for(i=0; inField ); + x.db = db; x.aOut = 0; x.nOut = 0; x.nAlloc = 0; *paOut = 0; *pnOut = 0; + if( enlargeEncoderAllocation(&x, (nIn+1)*10) ) return SQLITE_NOMEM; x.nOut = sqlite4PutVarint64(x.aOut, iTabno); - if( pKeyInfo ){ - nField = pKeyInfo->nField; - iShort = nField - pKeyInfo->nPK; - aColl = pKeyInfo->aColl; - so = pKeyInfo->aSortOrder; - }else{ - nField = 1; - iShort = 0; - xColl = &defaultColl; - aColl = &xColl; - so = 0; - } - for(i=0; inField - pKeyInfo->nPK; + aColl = pKeyInfo->aColl; + so = pKeyInfo->aSortOrder; + for(i=0; i=0x17 && x<=0x21 ){ isNeg = 0; e = x-0x17; + }else if( x==0x15 ){ + *pVal = 0; + return 1; }else{ return 0; } m = 0; i = 1; Index: src/vdbecursor.c ================================================================== --- src/vdbecursor.c +++ src/vdbecursor.c @@ -39,21 +39,22 @@ int rc; KVByteArray aProbe[16]; assert( iEnd==(+1) || iEnd==(-1) ); nProbe = sqlite4PutVarint64(aProbe, pC->iRoot); - aProbe[nProbe++] = 16 - iEnd*12; - rc = sqlite4KVCursorSeek(pCur, aProbe, nProbe, iEnd); + aProbe[nProbe] = 0xFF; + + rc = sqlite4KVCursorSeek(pCur, aProbe, nProbe+(iEnd==-1), iEnd); if( rc==SQLITE_OK ){ - return SQLITE_CORRUPT; - } - if( rc==SQLITE_INEXACT ){ + rc = SQLITE_CORRUPT_BKPT; + }else if( rc==SQLITE_INEXACT ){ rc = sqlite4KVCursorKey(pCur, &aKey, &nKey); - if( rc==SQLITE_OK && (nKeyflags&MEM_RowSet)==0 ); + assert( (pMem->flags&MEM_KeySet)==0 ); assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE || desiredEnc==SQLITE_UTF16BE ); if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){ return SQLITE_OK; } @@ -73,11 +73,11 @@ ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) + (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) + ((pMem->flags&MEM_Ephem) ? 1 : 0) + ((pMem->flags&MEM_Static) ? 1 : 0) ); - assert( (pMem->flags&MEM_RowSet)==0 ); + assert( (pMem->flags&MEM_KeySet)==0 ); if( n<32 ) n = 32; if( sqlite4DbMallocSize(pMem->db, pMem->zMalloc)z==pMem->zMalloc ){ pMem->z = pMem->zMalloc = sqlite4DbReallocOrFree(pMem->db, pMem->z, n); @@ -115,11 +115,11 @@ ** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. */ int sqlite4VdbeMemMakeWriteable(Mem *pMem){ int f; assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); - assert( (pMem->flags&MEM_RowSet)==0 ); + assert( (pMem->flags&MEM_KeySet)==0 ); ExpandBlob(pMem); f = pMem->flags; if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ if( sqlite4VdbeMemGrow(pMem, pMem->n + 2, 1) ){ return SQLITE_NOMEM; @@ -173,11 +173,11 @@ assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( !(fg&MEM_Zero) ); assert( !(fg&(MEM_Str|MEM_Blob)) ); assert( fg&(MEM_Int|MEM_Real) ); - assert( (pMem->flags&MEM_RowSet)==0 ); + assert( (pMem->flags&MEM_KeySet)==0 ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); if( sqlite4VdbeMemGrow(pMem, nByte, 0) ){ return SQLITE_NOMEM; @@ -240,16 +240,16 @@ if( p->flags&MEM_Agg ){ sqlite4VdbeMemFinalize(p, p->u.pDef); assert( (p->flags & MEM_Agg)==0 ); sqlite4VdbeMemRelease(p); }else if( p->flags&MEM_Dyn && p->xDel ){ - assert( (p->flags&MEM_RowSet)==0 ); + assert( (p->flags&MEM_KeySet)==0 ); assert( p->xDel!=SQLITE_DYNAMIC ); p->xDel((void *)p->z); p->xDel = 0; - }else if( p->flags&MEM_RowSet ){ - sqlite4RowSetClear(p->u.pRowSet); + }else if( p->flags&MEM_KeySet ){ + sqlite4KeySetFree(p->u.pKeySet); }else if( p->flags&MEM_Frame ){ sqlite4VdbeMemSetNull(p); } } @@ -450,12 +450,12 @@ if( pMem->flags & MEM_Frame ){ VdbeFrame *pFrame = pMem->u.pFrame; pFrame->pParent = pFrame->v->pDelFrame; pFrame->v->pDelFrame = pFrame; } - if( pMem->flags & MEM_RowSet ){ - sqlite4RowSetClear(pMem->u.pRowSet); + if( pMem->flags & MEM_KeySet ){ + sqlite4KeySetFree(pMem->u.pKeySet); } MemSetTypeFlag(pMem, MEM_Null); pMem->type = SQLITE_NULL; } @@ -519,10 +519,20 @@ sqlite4DbMallocSize(db, pMem->zMalloc)); assert( pMem->u.pRowSet!=0 ); pMem->flags = MEM_RowSet; } } + +void sqlite4VdbeMemSetKeySet(Mem *pMem){ + sqlite4 *db = pMem->db; + assert( db!=0 ); + assert( (pMem->flags & MEM_KeySet)==0 ); + sqlite4VdbeMemRelease(pMem); + + pMem->u.pKeySet = sqlite4KeySetInit(db); + pMem->flags = MEM_KeySet; +} /* ** Return true if the Mem object contains a TEXT or BLOB that is ** too large - whose size exceeds SQLITE_MAX_LENGTH. */ Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -242,12 +242,10 @@ ** is set to WO_IN|WO_EQ. The WhereLevel.wsFlags field can then be used as ** the "op" parameter to findTerm when we are resolving equality constraints. ** ISNULL constraints will then not be used on the right table of a left ** join. Tickets #2177 and #2189. */ -#define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ -#define WHERE_ROWID_RANGE 0x00002000 /* rowidEXPR */ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* xEXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ @@ -591,12 +589,14 @@ int iColumn, /* Column number of LHS */ Bitmask notReady, /* RHS must not overlap with this mask */ u32 op, /* Mask of WO_xx values describing operator */ Index *pIdx /* Must be compatible with this index, if not NULL */ ){ + sqlite4 *db = pWC->pParse->db; /* Database handle */ WhereTerm *pTerm; int k; + assert( iCur>=0 ); op &= WO_ALL; for(; pWC; pWC=pWC->pOuter){ for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ if( pTerm->leftCursor==iCur @@ -603,31 +603,37 @@ && (pTerm->prereqRight & notReady)==0 && pTerm->u.leftColumn==iColumn && (pTerm->eOperator & op)!=0 ){ if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ + Table *pTab = pIdx->pTable; + const char *zColl; /* Collation sequence used by index */ + CollSeq *pColl; /* Collation sequence used by expression */ Expr *pX = pTerm->pExpr; - CollSeq *pColl; - char idxaff; int j; Parse *pParse = pWC->pParse; - idxaff = pIdx->pTable->aCol[iColumn].affinity; - if( !sqlite4IndexAffinityOk(pX, idxaff) ) continue; + if( !sqlite4IndexAffinityOk(pX, pTab->aCol[iColumn].affinity) ){ + continue; + } - /* Figure out the collation sequence required from an index for - ** it to be useful for optimising expression pX. Store this - ** value in variable pColl. - */ + /* Figure out the collation sequence used by expression pX. Store + ** this in pColl. Also the collation sequence used by the index. + ** Store this one in zColl. */ assert(pX->pLeft); pColl = sqlite4BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - assert(pColl || pParse->nErr); - - for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ - if( NEVER(j>=pIdx->nColumn) ) return 0; + assert( pParse->nErr || (pColl && pColl->enc==pIdx->pSchema->enc) ); + for(j=0; pIdx->aiColumn[j]!=iColumn && jnColumn; j++); + if( j>=pIdx->nColumn ){ + zColl = pTab->aCol[iColumn].zColl; + }else{ + zColl = pIdx->azColl[j]; } - if( pColl && sqlite4StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; + + /* If the collation sequence used by the index is not the same as + ** that used by the expression, then this term is not a match. */ + if( pColl!=sqlite4FindCollSeq(db, ENC(db), zColl, 0) ) continue; } return pTerm; } } } @@ -638,11 +644,14 @@ static void exprAnalyze(SrcList*, WhereClause*, int); /* ** Call exprAnalyze on all terms in a WHERE clause. ** -** +** Note that exprAnalyze() might add new virtual terms onto the end of +** the WHERE clause. We do not want to analyze these virtual terms, so +** start analyzing at the end and work forward so that the added virtual +** terms are never processed. */ static void exprAnalyzeAll( SrcList *pTabList, /* the FROM clause */ WhereClause *pWC /* the WHERE clause to be analyzed */ ){ @@ -1577,10 +1586,35 @@ } return 0; } + +/* +** Return the table column number of the iIdxCol'th field in the index +** keys used by index pIdx, including any appended PRIMARY KEY fields. +** If there is no iIdxCol'th field in index pIdx, return -2. +** +** Example: +** +** CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)); +** CREATE INDEX i1 ON t1(c); +** +** Index i1 in the example above consists of three fields - the indexed +** field "c" followed by the two primary key fields. The automatic PRIMARY +** KEY index consists of two fields only. +*/ +static int idxColumnNumber(Index *pIdx, Index *pPk, int iIdxCol){ + int iRet = -2; + if( iIdxColnColumn ){ + iRet = pIdx->aiColumn[iIdxCol]; + }else if( iIdxCol<(pIdx->nColumn + pPk->nColumn) ){ + iRet = pPk->aiColumn[iIdxCol - pIdx->nColumn]; + } + return iRet; +} + /* ** This routine decides if pIdx can be used to satisfy the ORDER BY ** clause. If it can, it returns 1. If pIdx cannot satisfy the ** ORDER BY clause, this routine returns 0. ** @@ -1606,126 +1640,137 @@ ExprList *pOrderBy, /* The ORDER BY clause */ int nEqCol, /* Number of index columns with == constraints */ int wsFlags, /* Index usages flags */ int *pbRev /* Set to 1 if ORDER BY is DESC */ ){ - int i, j; /* Loop counters */ + sqlite4 *db = pParse->db; /* Database handle */ int sortOrder = 0; /* XOR of index and ORDER BY sort direction */ int nTerm; /* Number of ORDER BY terms */ - struct ExprList_item *pTerm; /* A term of the ORDER BY clause */ - sqlite4 *db = pParse->db; + int iTerm; /* Used to iterate through nTerm terms */ + int iNext = nEqCol; /* Index of next unmatched column in index */ + int nIdxCol; /* Number of columns in index, incl. PK */ + Index *pPk; + Table *pTab; if( !pOrderBy ) return 0; if( wsFlags & WHERE_COLUMN_IN ) return 0; if( pIdx->bUnordered ) return 0; + pTab = pIdx->pTable; + pPk = sqlite4FindPrimaryKey(pTab, 0); nTerm = pOrderBy->nExpr; + nIdxCol = pIdx->nColumn + (pIdx==pPk ? 0 : pPk->nColumn); + assert( nTerm>0 ); - - /* Argument pIdx must either point to a 'real' named index structure, - ** or an index structure allocated on the stack by bestKVIndex() to - ** represent the rowid index that is part of every table. */ - assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); - - /* Match terms of the ORDER BY clause against columns of - ** the index. - ** - ** Note that indices have pIdx->nColumn regular columns plus - ** one additional column containing the rowid. The rowid column - ** of the index is also allowed to match against the ORDER BY - ** clause. - */ - for(i=j=0, pTerm=pOrderBy->a; jnColumn; i++){ + assert( pIdx && pIdx->zName ); + + for(iTerm=0; iTermpExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){ - /* Can not use an index sort on anything that is not a column in the - ** left-most table of the FROM clause */ - break; - } - pColl = sqlite4ExprCollSeq(pParse, pExpr); - if( !pColl ){ - pColl = db->pDfltColl; - } - if( pIdx->zName && inColumn ){ - iColumn = pIdx->aiColumn[i]; - if( iColumn==pIdx->pTable->iPKey ){ - iColumn = -1; - } - iSortOrder = pIdx->aSortOrder[i]; - zColl = pIdx->azColl[i]; - }else{ - iColumn = -1; - iSortOrder = 0; - zColl = pColl->zName; - } - if( pExpr->iColumn!=iColumn || sqlite4StrICmp(pColl->zName, zColl) ){ - /* Term j of the ORDER BY clause does not match column i of the index */ - if( inColumn ){ - /* Index column i is the rowid. All other terms match. */ - break; - }else{ - /* If an index column fails to match and is not constrained by == - ** then the index cannot satisfy the ORDER BY constraint. - */ - return 0; - } - } - assert( pIdx->aSortOrder!=0 || iColumn==-1 ); - assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 ); - assert( iSortOrder==0 || iSortOrder==1 ); - termSortOrder = iSortOrder ^ pTerm->sortOrder; - if( i>nEqCol ){ - if( termSortOrder!=sortOrder ){ - /* Indices can only be used if all ORDER BY terms past the - ** equality constraints are all either DESC or ASC. */ - return 0; - } - }else{ - sortOrder = termSortOrder; - } - j++; - pTerm++; + /* Can not use an index sort on anything that is not a column in the + ** left-most table of the FROM clause. Break out of the loop if this + ** expression is anything other than that. */ + pTerm = &pOrderBy->a[iTerm]; + pExpr = pTerm->pExpr; + if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ) break; + iColumn = pExpr->iColumn; + + /* Check that column iColumn is a part of the index. If it is not, then + ** this index may not be used as a sorting index. This block also checks + ** that column iColumn is either the iNext'th column of the index, or + ** else one of the nEqCol columns that the index guarantees will be + ** constant. */ + for(iIdxCol=0; iIdxCol=nEqCol && iIdxCol!=iNext) ) break; + + /* Check that the collation sequence used by the expression is the same + ** as the collation sequence used by the index. If not, this is not a + ** sorting index. */ + pColl = sqlite4ExprCollSeq(pParse, pExpr); + if( !pColl ) pColl = db->pDfltColl; + if( iIdxColnColumn ){ + zColl = pIdx->azColl[iIdxCol]; + }else{ + zColl = pTab->aCol[iColumn].zColl; + } + if( pColl!=sqlite4FindCollSeq(db, ENC(db), zColl, 0) ) break; + + if( iIdxCol==iNext ){ + u8 reqSortOrder; + u8 idxSortOrder = SQLITE_SO_ASC; + if( iIdxColnColumn ) idxSortOrder = pIdx->aSortOrder[iIdxCol]; + assert( idxSortOrder==SQLITE_SO_ASC || idxSortOrder==SQLITE_SO_DESC ); + + reqSortOrder = (idxSortOrder ^ pTerm->sortOrder); + if( iNext==nEqCol ){ + sortOrder = reqSortOrder; + }else if( sortOrder!=reqSortOrder ){ + break; + } + iNext++; + } + +#if 0 if( iColumn<0 && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){ /* If the indexed column is the primary key and everything matches ** so far and none of the ORDER BY terms to the right reference other ** tables in the join, then we are assured that the index can be used ** to sort because the primary key is unique and so none of the other ** columns will make any difference */ j = nTerm; } +#endif } *pbRev = sortOrder!=0; - if( j>=nTerm ){ - /* All terms of the ORDER BY clause are covered by this index so - ** this index can be used for sorting. */ - return 1; - } - if( pIdx->onError!=OE_None && i==pIdx->nColumn - && (wsFlags & WHERE_COLUMN_NULL)==0 - && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){ - /* All terms of this index match some prefix of the ORDER BY clause - ** and the index is UNIQUE and no terms on the tail of the ORDER BY - ** clause reference other tables in a join. If this is all true then - ** the order by clause is superfluous. Not that if the matching - ** condition is IS NULL then the result is not necessarily unique - ** even on a UNIQUE index, so disallow those cases. */ - return 1; - } + + if( iTerm>=nTerm ){ + /* All terms of the ORDER BY clause are covered by this index. The + ** index can therefore be used for sorting. */ + return 1; + } + + if( pIdx->onError!=OE_None + && iNext>=pIdx->nColumn + && (wsFlags & WHERE_COLUMN_NULL)==0 + && !referencesOtherTables(pOrderBy, pMaskSet, iTerm, base) + ){ + + if( iNext==nIdxCol ){ + /* All columns indexed by this UNIQUE index, and all PK columns are + ** are matched by a prefix of the ORDER BY clause. And since the PK + ** columns are guaranteed to be unique and NOT NULL, there is no way + ** for the trailing ORDER BY terms to affect the sort order. Therefore, + ** we have a sorting index. */ + return 1; + }else{ + int i; + for(i=nEqCol; inColumn; i++){ + int iCol = pIdx->aiColumn[i]; + if( iCol>=0 && pTab->aCol[iCol].notNull==0 ) break; + } + + /* All columns indexed by this UNIQUE index are matched by a prefix + ** of the ORDER BY clause. And there is reason to believe that none + ** of the expressions in the ORDER BY prefix will evalulate to NULL. + ** The index may be used for sorting in this case too since it is + ** guaranteed that none of the trailing, unmatched ORDER BY terms + ** affect the sort order. */ + return (i>=pIdx->nColumn); + } + } + return 0; } /* ** Prepare a crude estimate of the logarithm of the input value. @@ -2840,11 +2885,10 @@ } return rc; } #endif /* defined(SQLITE_ENABLE_STAT3) */ - /* ** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. ** @@ -2881,17 +2925,14 @@ ExprList *pDistinct, /* The select-list if query is DISTINCT */ WhereCost *pCost /* Lowest cost query plan */ ){ int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ Index *pProbe; /* An index we are evaluating */ - Index *pIdx; /* Copy of pProbe, or zero for IPK index */ + Index *pFirst; /* First index to evaluate */ + Index *pPk; /* Primary Key index */ int eqTermMask; /* Current mask of valid equality operators */ int idxEqTermMask; /* Index mask of valid equality operators */ - Index sPk; /* A fake index object for the primary key */ - tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ - int aiColumnPk = -1; /* The aColumn[] value for the sPk index */ - int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */ /* Initialize the cost to a worst-case value */ memset(pCost, 0, sizeof(*pCost)); pCost->rCost = SQLITE_BIG_DBL; @@ -2904,46 +2945,39 @@ idxEqTermMask = WO_EQ|WO_IN; }else{ idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL; } + /* Normally, this function considers all indexes attached to the table + ** being queried. Except, if an INDEXED BY clause is specified then only + ** the named index is considered. And if a NOT INDEXED clause was present + ** only the PRIMARY KEY index may be considered. + */ + assert( pSrc->notIndexed==0 && "TODO: Re-enable this" ); + assert( pSrc->pIndex==0 && "TODO: Re-enable this" ); +#if 0 if( pSrc->pIndex ){ /* An INDEXED BY clause specifies a particular index to use */ - pIdx = pProbe = pSrc->pIndex; + assert(!"TODO: Fix this"); + pFirst = pSrc->pIndex; wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); eqTermMask = idxEqTermMask; }else{ - /* There is no INDEXED BY clause. Create a fake Index object in local - ** variable sPk to represent the rowid primary key index. Make this - ** fake index the first in a chain of Index objects with all of the real - ** indices to follow */ - Index *pFirst; /* First of real indices on the table */ - memset(&sPk, 0, sizeof(Index)); - sPk.nColumn = 1; - sPk.aiColumn = &aiColumnPk; - sPk.aiRowEst = aiRowEstPk; - sPk.onError = OE_Replace; - sPk.pTable = pSrc->pTab; - aiRowEstPk[0] = pSrc->pTab->nRowEst; - aiRowEstPk[1] = 1; - pFirst = pSrc->pTab->pIndex; - if( pSrc->notIndexed==0 ){ - /* The real indices of the table are only considered if the - ** NOT INDEXED qualifier is omitted from the FROM clause */ - sPk.pNext = pFirst; - } - pProbe = &sPk; wsFlagMask = ~( WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE ); eqTermMask = WO_EQ|WO_IN; - pIdx = 0; + pFirst = pSrc->pTab->pIndex; } +#else + eqTermMask = idxEqTermMask; + pFirst = pSrc->pTab->pIndex; +#endif + pPk = sqlite4FindPrimaryKey(pSrc->pTab, 0); - /* Loop over all indices looking for the best one to use - */ - for(; pProbe; pIdx=pProbe=pProbe->pNext){ + /* Loop over all indices looking for the best one to use */ + for(pProbe=pFirst; pProbe; pProbe=pProbe->pNext){ const tRowcnt * const aiRowEst = pProbe->aiRowEst; double cost; /* Cost of using pProbe */ double nRow; /* Estimated number of rows in result set */ double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */ int rev; /* True to scan in reverse order */ @@ -3017,22 +3051,30 @@ int nInMul = 1; /* Number of distinct equalities to lookup */ double rangeDiv = (double)1; /* Estimated reduction in search space */ int nBound = 0; /* Number of range constraints seen */ int bSort = !!pOrderBy; /* True if external sort required */ int bDist = !!pDistinct; /* True if index cannot help with DISTINCT */ - int bLookup = 0; /* True if not a covering index */ + int bLookup = 0; /* True if not the PK index */ WhereTerm *pTerm; /* A single term of the WHERE clause */ #ifdef SQLITE_ENABLE_STAT3 WhereTerm *pFirstTerm = 0; /* First term matching the index */ #endif + int nCol = pProbe->nColumn; /* Total columns in index record */ + + /* Unless pProbe is the primary key index, then the encoded PK column + ** values are at the end of each record. Set variable nCol to the total + ** number of columns encoded into each index record, including the PK + ** columns. */ + if( pProbe!=pPk ) nCol += pPk->nColumn; /* Determine the values of nEq and nInMul */ - for(nEq=0; nEqnColumn; nEq++){ - int j = pProbe->aiColumn[nEq]; - pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); + for(nEq=0; nEqpWC!=pWC ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ @@ -3059,21 +3101,21 @@ ** ** Otherwise, if the search may find more than one row, test to see if ** there is a range constraint on indexed column (nEq+1) that can be ** optimized using the index. */ - if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ + if( nEq>=pProbe->nColumn && pProbe->onError!=OE_None ){ testcase( wsFlags & WHERE_COLUMN_IN ); testcase( wsFlags & WHERE_COLUMN_NULL ); if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ wsFlags |= WHERE_UNIQUE; } }else if( pProbe->bUnordered==0 ){ - int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]); - if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ - WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); - WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx); + int j = idxColumnNumber(pProbe, pPk, nEq); + if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pProbe) ){ + WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pProbe); + WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pProbe); whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv); if( pTop ){ nBound = 1; wsFlags |= WHERE_TOP_LIMIT; used |= pTop->prereqRight; @@ -3083,11 +3125,11 @@ nBound++; wsFlags |= WHERE_BTM_LIMIT; used |= pBtm->prereqRight; testcase( pBtm->pWC!=pWC ); } - wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); + wsFlags |= WHERE_COLUMN_RANGE; } } /* If there is an ORDER BY clause and the index being considered will ** naturally scan rows in the required order, set the appropriate flags @@ -3095,32 +3137,37 @@ ** will scan rows in a different order, set the bSort variable. */ if( isSortingIndex( pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, nEq, wsFlags, &rev) ){ bSort = 0; - wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; + wsFlags |= WHERE_COLUMN_RANGE|WHERE_ORDERBY; wsFlags |= (rev ? WHERE_REVERSE : 0); } /* If there is a DISTINCT qualifier and this index will scan rows in ** order of the DISTINCT expressions, clear bDist and set the appropriate ** flags in wsFlags. */ if( isDistinctIndex(pParse, pWC, pProbe, iCur, pDistinct, nEq) ){ bDist = 0; - wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT; + wsFlags |= WHERE_COLUMN_RANGE|WHERE_DISTINCT; } - /* If currently calculating the cost of using an index (not the IPK + /* If currently calculating the cost of using an index (not the PK ** index), determine if all required column data may be obtained without ** using the main table (i.e. if the index is a covering ** index for this query). If it is, set the WHERE_IDX_ONLY flag in - ** wsFlags. Otherwise, set the bLookup variable to true. */ - if( pIdx && wsFlags ){ + ** wsFlags. Otherwise, set the bLookup variable to true. + ** + ** TODO: Not clear if this optimization can be applied in SQLite 4. Fix + ** this block once that is figured out. + */ +#if 0 + if( wsFlags ){ Bitmask m = pSrc->colUsed; int j; - for(j=0; jnColumn; j++){ - int x = pIdx->aiColumn[j]; + for(j=0; jnColumn; j++){ + int x = pProbe->aiColumn[j]; if( xeIndexType!=SQLITE_INDEX_PRIMARYKEY); /* ** Estimate the number of rows of output. For an "x IN (SELECT...)" ** constraint, do not let the estimate exceed half the rows in the table. */ @@ -3191,29 +3240,21 @@ */ cost = aiRowEst[0]*4; }else{ log10N = estLog(aiRowEst[0]); cost = nRow; - if( pIdx ){ - if( bLookup ){ - /* For an index lookup followed by a table lookup: - ** nInMul index searches to find the start of each index range - ** + nRow steps through the index - ** + nRow table searches to lookup the table entry using the rowid - */ - cost += (nInMul + nRow)*log10N; - }else{ - /* For a covering index: - ** nInMul index searches to find the initial entry - ** + nRow steps through the index - */ - cost += nInMul*log10N; - } - }else{ - /* For a rowid primary key lookup: - ** nInMult table searches to find the initial entry for each range - ** + nRow steps through the table + if( bLookup ){ + /* For an index lookup followed by a table lookup: + ** nInMul index searches to find the start of each index range + ** + nRow steps through the index + ** + nRow table searches to lookup the table entry using the PK + */ + cost += (nInMul + nRow)*log10N; + }else{ + /* For a covering index: + ** nInMul index searches to find the initial entry + ** + nRow steps through the index */ cost += nInMul*log10N; } } @@ -3292,35 +3333,34 @@ WHERETRACE(( "%s(%s): nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%x\n" " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n", - pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), + pSrc->pTab->zName, pProbe->zName, nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags, notReady, log10N, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. */ - if( (!pIdx || wsFlags) + if( (pProbe==pFirst || wsFlags) && (costrCost || (cost<=pCost->rCost && nRowplan.nRow)) ){ pCost->rCost = cost; pCost->used = used; pCost->plan.nRow = nRow; - pCost->plan.wsFlags = (wsFlags&wsFlagMask); + pCost->plan.wsFlags = wsFlags; pCost->plan.nEq = nEq; - pCost->plan.u.pIdx = pIdx; + pCost->plan.u.pIdx = pProbe; } - /* If there was an INDEXED BY clause, then only that one index is + /* If there was an INDEXED BY or NOT INDEXED clause, only one index is ** considered. */ - if( pSrc->pIndex ) break; + if( pSrc->pIndex || pSrc->notIndexed ) break; /* Reset masks for the next index in the loop */ - wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); eqTermMask = idxEqTermMask; } /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag ** is set, then reverse the order that the index will be scanned @@ -3330,11 +3370,10 @@ if( !pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){ pCost->plan.wsFlags |= WHERE_REVERSE; } assert( pOrderBy || (pCost->plan.wsFlags&WHERE_ORDERBY)==0 ); - assert( pCost->plan.u.pIdx==0 || (pCost->plan.wsFlags&WHERE_ROWID_EQ)==0 ); assert( pSrc->pIndex==0 || pCost->plan.u.pIdx==0 || pCost->plan.u.pIdx==pSrc->pIndex ); @@ -3756,22 +3795,10 @@ ((flags & WHERE_TEMP_INDEX)?"":" "), ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName), zWhere ); sqlite4DbFree(db, zWhere); - }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ - zMsg = sqlite4MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg); - - if( flags&WHERE_ROWID_EQ ){ - zMsg = sqlite4MAppendf(db, zMsg, "%s (rowid=?)", zMsg); - }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ - zMsg = sqlite4MAppendf(db, zMsg, "%s (rowid>? AND rowid?)", zMsg); - }else if( flags&WHERE_TOP_LIMIT ){ - zMsg = sqlite4MAppendf(db, zMsg, "%s (rowidplan.u.pVtabIdx; zMsg = sqlite4MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg, @@ -3790,11 +3817,10 @@ } #else # define explainOneScan(u,v,w,x,y,z) #endif /* SQLITE_OMIT_EXPLAIN */ - /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ static Bitmask codeOneLoopStart( @@ -3805,11 +3831,10 @@ Expr *pWhere /* Complete WHERE clause */ ){ int j, k; /* Loop counters */ int iCur; /* The VDBE cursor for the table */ int addrNxt; /* Where to jump to continue with the next IN case */ - int omitTable; /* True if we use the index only */ int bRev; /* True if we need to scan in reverse order */ WhereLevel *pLevel; /* The where level to be coded */ WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ Parse *pParse; /* Parsing context */ @@ -3825,12 +3850,10 @@ pWC = pWInfo->pWC; pLevel = &pWInfo->a[iLevel]; pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; iCur = pTabItem->iCursor; bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; - omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0 - && (wctrlFlags & WHERE_FORCE_TABLE)==0; /* Create labels for the "break" and "continue" instructions ** for the current loop. Jump to addrBrk to break out of a loop. ** Jump to cont to go immediately to the next iteration of the ** loop. @@ -3895,108 +3918,11 @@ sqlite4ReleaseTempRange(pParse, iReg, nConstraint+2); sqlite4ExprCachePop(pParse, 1); }else #endif /* SQLITE_OMIT_VIRTUALTABLE */ - if( pLevel->plan.wsFlags & WHERE_ROWID_EQ ){ - /* Case 1: We can directly reference a single row using an - ** equality comparison against the ROWID field. Or - ** we reference multiple rows using a "rowid IN (...)" - ** construct. - */ - iReleaseReg = sqlite4GetTempReg(pParse); - pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); - assert( pTerm!=0 ); - assert( pTerm->pExpr!=0 ); - assert( pTerm->leftCursor==iCur ); - assert( omitTable==0 ); - testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); - addrNxt = pLevel->addrNxt; - sqlite4VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); - sqlite4VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg); - sqlite4ExprCacheStore(pParse, iCur, -1, iRowidReg); - VdbeComment((v, "pk")); - pLevel->op = OP_Noop; - }else if( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ){ - /* Case 2: We have an inequality comparison against the ROWID field. - */ - int testOp = OP_Noop; - int start; - int memEndValue = 0; - WhereTerm *pStart, *pEnd; - - assert( omitTable==0 ); - pStart = findTerm(pWC, iCur, -1, notReady, WO_GT|WO_GE, 0); - pEnd = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0); - if( bRev ){ - pTerm = pStart; - pStart = pEnd; - pEnd = pTerm; - } - if( pStart ){ - Expr *pX; /* The expression that defines the start bound */ - int r1, rTemp; /* Registers for holding the start boundary */ - - /* The following constant maps TK_xx codes into corresponding - ** seek opcodes. It depends on a particular ordering of TK_xx - */ - const u8 aMoveOp[] = { - /* TK_GT */ OP_SeekGt, - /* TK_LE */ OP_SeekLe, - /* TK_LT */ OP_SeekLt, - /* TK_GE */ OP_SeekGe - }; - assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */ - assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */ - assert( TK_GE==TK_GT+3 ); /* ... is correcct. */ - - testcase( pStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - pX = pStart->pExpr; - assert( pX!=0 ); - assert( pStart->leftCursor==iCur ); - r1 = sqlite4ExprCodeTemp(pParse, pX->pRight, &rTemp); - sqlite4VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1); - VdbeComment((v, "pk")); - sqlite4ExprCacheAffinityChange(pParse, r1, 1); - sqlite4ReleaseTempReg(pParse, rTemp); - disableTerm(pLevel, pStart); - }else{ - sqlite4VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk); - } - if( pEnd ){ - Expr *pX; - pX = pEnd->pExpr; - assert( pX!=0 ); - assert( pEnd->leftCursor==iCur ); - testcase( pEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - memEndValue = ++pParse->nMem; - sqlite4ExprCode(pParse, pX->pRight, memEndValue); - if( pX->op==TK_LT || pX->op==TK_GT ){ - testOp = bRev ? OP_Le : OP_Ge; - }else{ - testOp = bRev ? OP_Lt : OP_Gt; - } - disableTerm(pLevel, pEnd); - } - start = sqlite4VdbeCurrentAddr(v); - pLevel->op = bRev ? OP_Prev : OP_Next; - pLevel->p1 = iCur; - pLevel->p2 = start; - if( pStart==0 && pEnd==0 ){ - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; - }else{ - assert( pLevel->p5==0 ); - } - if( testOp!=OP_Noop ){ - iRowidReg = iReleaseReg = sqlite4GetTempReg(pParse); - sqlite4VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg); - sqlite4ExprCacheStore(pParse, iCur, -1, iRowidReg); - sqlite4VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg); - sqlite4VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); - } - }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){ + if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){ /* Case 3: A scan using an index. ** ** The WHERE clause may contain zero or more equality ** terms ("==" or "IN" operators) that refer to the N ** left-most columns of the index. It may also contain @@ -4036,13 +3962,16 @@ OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */ OP_SeekLe /* 7: (start_constraints && startEq && bRev) */ }; static const u8 aEndOp[] = { OP_Noop, /* 0: (!end_constraints) */ - OP_IdxGE, /* 1: (end_constraints && !bRev) */ - OP_IdxLT /* 2: (end_constraints && bRev) */ + OP_IdxGE, /* 1: (end_constraints && !endEq && !bRev) */ + OP_IdxLE, /* 2: (end_constraints && !endEq && bRev) */ + OP_IdxGT, /* 3: (end_constraints && endEq && !bRev) */ + OP_IdxLT /* 4: (end_constraints && endEq && bRev) */ }; + int nEq = pLevel->plan.nEq; /* Number of == or IN terms */ int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ int regBase; /* Base register holding constraint values */ int r1; /* Temp register */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ @@ -4055,14 +3984,18 @@ int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ char *zStartAff; /* Affinity for start of range constraint */ char *zEndAff; /* Affinity for end of range constraint */ + int regEndKey; /* Register for end-key */ + int iIneq; /* The table column subject to inequality */ + Index *pPk; /* Primary key index on same table as pIdx */ pIdx = pLevel->plan.u.pIdx; + pPk = sqlite4FindPrimaryKey(pIdx->pTable, 0); iIdxCur = pLevel->iIdxCur; - k = (nEq==pIdx->nColumn ? -1 : pIdx->aiColumn[nEq]); + iIneq = idxColumnNumber(pIdx, pPk, nEq); /* If this loop satisfies a sort order (pOrderBy) request that ** was passed to this function to implement a "SELECT min(x) ..." ** query, then the caller will only allow the loop to run for ** a single iteration. This means that the first row returned @@ -4079,35 +4012,36 @@ isMinQuery = 1; nExtraReg = 1; } /* Find any inequality constraint terms for the start and end - ** of the range. - */ + ** of the range. */ if( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ){ - pRangeEnd = findTerm(pWC, iCur, k, notReady, (WO_LT|WO_LE), pIdx); + pRangeEnd = findTerm(pWC, iCur, iIneq, notReady, (WO_LT|WO_LE), pIdx); nExtraReg = 1; } if( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ){ - pRangeStart = findTerm(pWC, iCur, k, notReady, (WO_GT|WO_GE), pIdx); + pRangeStart = findTerm(pWC, iCur, iIneq, notReady, (WO_GT|WO_GE), pIdx); nExtraReg = 1; } /* Generate code to evaluate all constraint terms using == or IN ** and store the values of those terms in an array of registers - ** starting at regBase. + ** starting at regBase. Ensure that nExtraReg registers are allocated + ** immediately following the array. */ regBase = codeAllEqualityTerms( pParse, pLevel, pWC, notReady, nExtraReg, &zStartAff ); + assert( (regBase+nEq+nExtraReg-1)<=pParse->nMem ); + zEndAff = sqlite4DbStrDup(pParse->db, zStartAff); addrNxt = pLevel->addrNxt; /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the - ** start and end terms (pRangeStart and pRangeEnd). - */ + ** start and end terms (pRangeStart and pRangeEnd). */ if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) || (bRev && pIdx->nColumn==nEq) ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); } @@ -4156,75 +4090,83 @@ testcase( op==OP_SeekGe ); testcase( op==OP_SeekLe ); testcase( op==OP_SeekLt ); sqlite4VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); - /* Load the value for the inequality constraint at the end of the - ** range (if any). - */ - nConstraint = nEq; - if( pRangeEnd ){ - Expr *pRight = pRangeEnd->pExpr->pRight; - sqlite4ExprCacheRemove(pParse, regBase+nEq, 1); - sqlite4ExprCode(pParse, pRight, regBase+nEq); - if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ - sqlite4ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); - } - if( zEndAff ){ - if( sqlite4CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ - /* Since the comparison is to be performed with no conversions - ** applied to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_NONE. */ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - if( sqlite4ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - } - codeApplyAffinity(pParse, regBase, nEq+1, zEndAff); - nConstraint++; - testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - } + /* Set variable op to the instruction required to determine if the + ** cursor is passed the end of the range. If the range is unbounded, + ** then set op to OP_Noop. Nothing to do in this case. */ + assert( (endEq==0 || endEq==1) ); + op = aEndOp[(pRangeEnd || nEq) * (1 + (endEq+endEq) + bRev)]; + testcase( op==OP_Noop ); + testcase( op==OP_IdxGE ); + testcase( op==OP_IdxLT ); + testcase( op==OP_IdxLE ); + testcase( op==OP_IdxGT ); + + if( op!=OP_Noop ){ + /* If there is an inequality at the end of this range, compute its + ** value here. */ + nConstraint = nEq; + if( pRangeEnd ){ + Expr *pRight = pRangeEnd->pExpr->pRight; + sqlite4ExprCacheRemove(pParse, regBase+nEq, 1); + sqlite4ExprCode(pParse, pRight, regBase+nEq); + if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ + sqlite4ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + } + if( zEndAff ){ + if( sqlite4CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ + /* Since the comparison is to be performed with no conversions + ** applied to the operands, set the affinity to apply to pRight to + ** SQLITE_AFF_NONE. */ + zEndAff[nEq] = SQLITE_AFF_NONE; + } + if( sqlite4ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){ + zEndAff[nEq] = SQLITE_AFF_NONE; + } + } + codeApplyAffinity(pParse, regBase, nEq+1, zEndAff); + nConstraint++; + testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ + } + + /* Now compute an end-key using OP_MakeIdxKey */ + regEndKey = ++pParse->nMem; + sqlite4VdbeAddOp3(v, OP_MakeIdxKey, iIdxCur, regBase, regEndKey); + sqlite4VdbeChangeP4(v, -1, (char *)nConstraint, P4_INT32); + } + sqlite4DbFree(pParse->db, zStartAff); sqlite4DbFree(pParse->db, zEndAff); /* Top of the loop body */ pLevel->p2 = sqlite4VdbeCurrentAddr(v); - /* Check if the index cursor is past the end of the range. */ - op = aEndOp[(pRangeEnd || nEq) * (1 + bRev)]; - testcase( op==OP_Noop ); - testcase( op==OP_IdxGE ); - testcase( op==OP_IdxLT ); if( op!=OP_Noop ){ - sqlite4VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); - sqlite4VdbeChangeP5(v, endEq!=bRev ?1:0); + sqlite4VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regEndKey, nConstraint); + } + + /* Seek the PK cursor, if required */ + disableTerm(pLevel, pRangeStart); + disableTerm(pLevel, pRangeEnd); + if( pIdx->eIndexType!=SQLITE_INDEX_PRIMARYKEY ){ + sqlite4VdbeAddOp3(v, OP_SeekPk, iCur, 0, iIdxCur); } /* If there are inequality constraints, check that the value - ** of the table column that the inequality contrains is not NULL. - ** If it is, jump to the next iteration of the loop. - */ + ** of the table column that the inequality constrains is not NULL. + ** If it is, jump to the next iteration of the loop. */ r1 = sqlite4GetTempReg(pParse); testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){ - sqlite4VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); + sqlite4VdbeAddOp3(v, OP_Column, iCur, pIdx->aiColumn[nEq], r1); sqlite4VdbeAddOp2(v, OP_IsNull, r1, addrCont); } sqlite4ReleaseTempReg(pParse, r1); - /* Seek the table cursor, if required */ - disableTerm(pLevel, pRangeStart); - disableTerm(pLevel, pRangeEnd); - if( !omitTable ){ - iRowidReg = iReleaseReg = sqlite4GetTempReg(pParse); - sqlite4VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); - sqlite4ExprCacheStore(pParse, iCur, -1, iRowidReg); - sqlite4VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ - } - /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ pLevel->op = OP_Noop; @@ -4232,10 +4174,11 @@ pLevel->op = OP_Prev; }else{ pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; + }else #ifndef SQLITE_OMIT_OR_OPTIMIZATION if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ /* Case 4: Two or more separately indexed terms connected by OR @@ -4401,11 +4344,10 @@ ** scan of the entire table. */ static const u8 aStep[] = { OP_Next, OP_Prev }; static const u8 aStart[] = { OP_Rewind, OP_Last }; assert( bRev==0 || bRev==1 ); - assert( omitTable==0 ); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite4VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } @@ -4719,15 +4661,11 @@ toTheLeft |= m; } } #endif - /* Analyze all of the subexpressions. Note that exprAnalyze() might - ** add new virtual terms onto the end of the WHERE clause. We do not - ** want to analyze these virtual terms, so start analyzing at the end - ** and work forward so that the added virtual terms are never processed. - */ + /* Analyze all of the subexpressions. */ exprAnalyzeAll(pTabList, pWC); if( db->mallocFailed ){ goto whereBeginError; } @@ -4997,21 +4935,23 @@ }else #endif if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; - sqlite4OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); + sqlite4OpenPrimaryKey(pParse, pTabItem->iCursor, iDb, pTab, op); testcase( pTab->nCol==BMS-1 ); testcase( pTab->nCol==BMS ); +#if 0 if( !pWInfo->okOnePass && pTab->nColcolUsed; int n = 0; for(; b; b=b>>1, n++){} sqlite4VdbeChangeP4(v, sqlite4VdbeCurrentAddr(v)-1, SQLITE_INT_TO_PTR(n), P4_INT32); assert( n<=pTab->nCol ); } +#endif }else{ sqlite4TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } #ifndef SQLITE_OMIT_AUTOMATIC_INDEX if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ @@ -5018,17 +4958,21 @@ constructAutomaticIndex(pParse, pWC, pTabItem, notReady, pLevel); }else #endif if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ Index *pIx = pLevel->plan.u.pIdx; - KeyInfo *pKey = sqlite4IndexKeyinfo(pParse, pIx); - int iIdxCur = pLevel->iIdxCur; - assert( pIx->pSchema==pTab->pSchema ); - assert( iIdxCur>=0 ); - sqlite4VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIx->zName)); + if( pIx->eIndexType==SQLITE_INDEX_PRIMARYKEY ){ + pLevel->iIdxCur = pTabItem->iCursor; + }else{ + KeyInfo *pKey = sqlite4IndexKeyinfo(pParse, pIx); + int iIdxCur = pLevel->iIdxCur; + assert( pIx->pSchema==pTab->pSchema ); + assert( iIdxCur>=0 ); + sqlite4VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb, + (char*)pKey, P4_KEYINFO_HANDOFF); + VdbeComment((v, "%s", pIx->zName)); + } } sqlite4CodeVerifySchema(pParse, iDb); notReady &= ~getMask(pWC->pMaskSet, pTabItem->iCursor); } pWInfo->iTop = sqlite4VdbeCurrentAddr(v); @@ -5069,16 +5013,11 @@ memcpy(&sqlite4_query_plan[nQPlan], z, n); nQPlan += n; } sqlite4_query_plan[nQPlan++] = ' '; } - testcase( pLevel->plan.wsFlags & WHERE_ROWID_EQ ); - testcase( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ); - if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ - memcpy(&sqlite4_query_plan[nQPlan], "* ", 2); - nQPlan += 2; - }else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ + if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ n = sqlite4Strlen30(pLevel->plan.u.pIdx->zName); if( n+nQPlan < sizeof(sqlite4_query_plan)-2 ){ memcpy(&sqlite4_query_plan[nQPlan], pLevel->plan.u.pIdx->zName, n); nQPlan += n; sqlite4_query_plan[nQPlan++] = ' '; @@ -5182,11 +5121,13 @@ int ws = pLevel->plan.wsFlags; if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite4VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } if( (ws & WHERE_INDEXED)!=0 && (ws & WHERE_TEMP_INDEX)==0 ){ - sqlite4VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); + if( pLevel->iIdxCur!=pTabItem->iCursor ){ + sqlite4VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); + } } } /* If this scan uses an index, make code substitutions to read data ** from the index in preference to the table. Sometimes, this means @@ -5199,10 +5140,11 @@ ** sqlite4WhereEnd will have created code that references the table ** directly. This loop scans all that code looking for opcodes ** that reference the table and converts them into opcodes that ** reference the index. */ +#if 0 if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 && !db->mallocFailed){ int k, j, last; VdbeOp *pOp; Index *pIdx = pLevel->plan.u.pIdx; @@ -5225,13 +5167,14 @@ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; } } } +#endif } /* Final cleanup */ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); return; } Index: test/conflict.test ================================================================== --- test/conflict.test +++ test/conflict.test @@ -51,16 +51,18 @@ 6 {INSERT OR ABORT} 1 {} 1 0 7 {INSERT OR ROLLBACK} 1 {} {} 0 } { do_test conflict-1.$i { set ::sqlite_opentemp_count 0 - set r0 [catch {execsql [subst { + execsql { DELETE FROM t1; DELETE FROM t2; INSERT INTO t1 VALUES(1,2,3); BEGIN; INSERT INTO t2 VALUES(1); + } + set r0 [catch {execsql [subst { $cmd INTO t1 VALUES(1,2,4); }]} r1] catch {execsql {COMMIT}} if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]} set r2 [execsql {SELECT x FROM t2}] @@ -97,16 +99,18 @@ 5 {INSERT OR FAIL} 1 {} 1 6 {INSERT OR ABORT} 1 {} 1 7 {INSERT OR ROLLBACK} 1 {} {} } { do_test conflict-2.$i { - set r0 [catch {execsql [subst { + execsql { DELETE FROM t1; DELETE FROM t2; INSERT INTO t1 VALUES(1,2,3); BEGIN; INSERT INTO t2 VALUES(1); + } + set r0 [catch {execsql [subst { $cmd INTO t1 VALUES(1,2,4); }]} r1] catch {execsql {COMMIT}} if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]} set r2 [execsql {SELECT x FROM t2}] @@ -189,19 +193,19 @@ 10 ABORT {INSERT OR REPLACE} 0 4 1 11 ROLLBACK {INSERT OR IGNORE } 0 3 1 } { do_test conflict-4.$i { if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"} - set r0 [catch {execsql [subst { + execsql [subst { DROP TABLE t1; CREATE TABLE t1(a,b,c,UNIQUE(a,b) $conf1); DELETE FROM t2; INSERT INTO t1 VALUES(1,2,3); BEGIN; INSERT INTO t2 VALUES(1); - $cmd INTO t1 VALUES(1,2,4); - }]} r1] + }] + set r0 [catch {execsql "$cmd INTO t1 VALUES(1,2,4)"} r1] catch {execsql {COMMIT}} if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]} set r2 [execsql {SELECT x FROM t2}] list $r0 $r1 $r2 } [list $t0 $t1 $t2] @@ -310,13 +314,15 @@ if {[info exists TEMP_STORE] && $TEMP_STORE==3} { set t3 0 } else { set t3 [expr {$t3+$t4}] } + + # Update for SQLite 4: No temporary files ever. + set t3 0 + do_test conflict-6.$i { - db close - sqlite4 db test.db if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"} execsql {pragma temp_store=file} set ::sqlite_opentemp_count 0 set r0 [catch {execsql [subst { DROP TABLE t1; @@ -332,10 +338,11 @@ if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]} set r2 [execsql {SELECT x FROM t3}] list $r0 $r1 $r2 $::sqlite_opentemp_count } [list $t0 $t1 $t2 $t3] } + # Test to make sure a lot of IGNOREs don't cause a stack overflow # do_test conflict-7.1 { execsql { @@ -351,39 +358,37 @@ SELECT count(*), min(a), max(b) FROM t1; } } {50 1 51} do_test conflict-7.2 { execsql { - PRAGMA count_changes=on; UPDATE OR IGNORE t1 SET a=1000; } -} {1} +} {} do_test conflict-7.2.1 { db changes } {1} do_test conflict-7.3 { execsql { - SELECT b FROM t1 WHERE a=1000; + SELECT b FROM t1 WHERE +a=1000; } } {2} do_test conflict-7.4 { execsql { SELECT count(*) FROM t1; } } {50} do_test conflict-7.5 { execsql { - PRAGMA count_changes=on; UPDATE OR REPLACE t1 SET a=1001; } -} {50} +} {} do_test conflict-7.5.1 { db changes } {50} do_test conflict-7.6 { execsql { - SELECT b FROM t1 WHERE a=1001; + SELECT b FROM t1 WHERE +a=1001; } } {51} do_test conflict-7.7 { execsql { SELECT count(*) FROM t1; @@ -405,60 +410,59 @@ INSERT INTO t1 VALUES(1,2); } execsql { INSERT OR IGNORE INTO t1 VALUES(2,3); } -} {1} +} {} do_test conflict-8.1.1 { db changes } {1} do_test conflict-8.2 { execsql { INSERT OR IGNORE INTO t1 VALUES(2,4); } -} {0} +} {} do_test conflict-8.2.1 { db changes } {0} do_test conflict-8.3 { execsql { INSERT OR REPLACE INTO t1 VALUES(2,4); } -} {1} +} {} do_test conflict-8.3.1 { db changes } {1} do_test conflict-8.4 { execsql { INSERT OR IGNORE INTO t1 SELECT * FROM t1; } -} {0} +} {} do_test conflict-8.4.1 { db changes } {0} do_test conflict-8.5 { execsql { INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1; } -} {2} +} {} do_test conflict-8.5.1 { db changes } {2} do_test conflict-8.6 { execsql { INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1; } -} {3} +} {} do_test conflict-8.6.1 { db changes } {3} integrity_check conflict-8.99 do_test conflict-9.1 { execsql { - PRAGMA count_changes=0; CREATE TABLE t2( a INTEGER UNIQUE ON CONFLICT IGNORE, b INTEGER UNIQUE ON CONFLICT FAIL, c INTEGER UNIQUE ON CONFLICT REPLACE, d INTEGER UNIQUE ON CONFLICT ABORT, @@ -482,11 +486,11 @@ SELECT * FROM t2; } } {0 {1 1 1 1 1 2 2 2 2 2}} do_test conflict-9.4 { catchsql { - UPDATE t2 SET a=a+1 WHERE a=1; + UPDATE t2 SET a=a+1 WHERE +a=1; SELECT * FROM t2; } } {0 {1 1 1 1 1 2 2 2 2 2}} do_test conflict-9.5 { catchsql { @@ -494,11 +498,11 @@ SELECT * FROM t2; } } {1 {column b is not unique}} do_test conflict-9.6 { catchsql { - UPDATE t2 SET b=b+1 WHERE b=1; + UPDATE t2 SET b=b+1 WHERE +b=1; SELECT * FROM t2; } } {1 {column b is not unique}} do_test conflict-9.7 { catchsql { @@ -514,11 +518,11 @@ } {2} do_test conflict-9.9 { catchsql { BEGIN; UPDATE t3 SET x=x+1; - UPDATE t2 SET b=b+1 WHERE b=1; + UPDATE t2 SET b=b+1 WHERE +b=1; SELECT * FROM t2; } } {1 {column b is not unique}} do_test conflict-9.10 { execsql {COMMIT} @@ -530,11 +534,11 @@ SELECT * FROM t2; } } {1 {column d is not unique}} do_test conflict-9.12 { catchsql { - UPDATE t2 SET d=d+1 WHERE d=1; + UPDATE t2 SET d=d+1 WHERE +d=1; SELECT * FROM t2; } } {1 {column d is not unique}} do_test conflict-9.13 { catchsql { @@ -550,11 +554,11 @@ } {4} do_test conflict-9.15 { catchsql { BEGIN; UPDATE t3 SET x=x+1; - UPDATE t2 SET d=d+1 WHERE d=1; + UPDATE t2 SET d=d+1 WHERE +d=1; SELECT * FROM t2; } } {1 {column d is not unique}} do_test conflict-9.16 { execsql {COMMIT} @@ -566,11 +570,11 @@ SELECT * FROM t2; } } {1 {column e is not unique}} do_test conflict-9.18 { catchsql { - UPDATE t2 SET e=e+1 WHERE e=1; + UPDATE t2 SET e=e+1 WHERE +e=1; SELECT * FROM t2; } } {1 {column e is not unique}} do_test conflict-9.19 { catchsql { @@ -586,11 +590,11 @@ } {5} do_test conflict-9.21 { catchsql { BEGIN; UPDATE t3 SET x=x+1; - UPDATE t2 SET e=e+1 WHERE e=1; + UPDATE t2 SET e=e+1 WHERE +e=1; SELECT * FROM t2; } } {1 {column e is not unique}} do_test conflict-9.22 { catch {execsql {COMMIT}} @@ -602,11 +606,11 @@ SELECT * FROM t2; } } {0 {2 2 2 2 2 3 3 1 3 3}} do_test conflict-9.24 { catchsql { - UPDATE t2 SET c=c-1 WHERE c=2; + UPDATE t2 SET c=c-1 WHERE +c=2; SELECT * FROM t2; } } {0 {2 2 1 2 2}} do_test conflict-9.25 { catchsql { @@ -650,15 +654,14 @@ execsql { -- Create a database object (pages 2, 3 of the file) BEGIN; CREATE TABLE abc(a UNIQUE, b, c); INSERT INTO abc VALUES(1, 2, 3); - INSERT INTO abc VALUES(4, 5, 6); - INSERT INTO abc VALUES(7, 8, 9); + INSert into abc VALUES(4, 5, 6); + insERT INTO abc VALUES(7, 8, 9); COMMIT; } - # Set a small cache size so that changes will spill into # the database file. execsql { PRAGMA cache_size = 10; @@ -680,11 +683,11 @@ INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; - DELETE FROM abc WHERE a = 4; + DELETE FROM abc WHERE +a = 4; } # Execute a statement that does a statement rollback due to # a constraint failure. # @@ -719,20 +722,21 @@ INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; - DELETE FROM abc WHERE a = 4; + DELETE FROM abc WHERE +a = 4; } catchsql { INSERT INTO abc SELECT 10, 20, 30 FROM def; } execsql { ROLLBACK; SELECT * FROM abc; } } {1 2 3 4 5 6 7 8 9} + # Repeat test conflict-11.1 but this time commit. # do_test conflict-11.5 { execsql { BEGIN; @@ -745,11 +749,11 @@ INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; INSERT INTO def SELECT * FROM def; - DELETE FROM abc WHERE a = 4; + DELETE FROM abc WHERE +a = 4; } catchsql { INSERT INTO abc SELECT 10, 20, 30 FROM def; } execsql { @@ -778,18 +782,17 @@ } {1 one 2 two} do_test conflict-12.3 { catchsql { UPDATE t5 SET a=a+1 WHERE a=1; } -} {1 {PRIMARY KEY must be unique}} +} {1 {column a is not unique}} do_test conflict-12.4 { execsql { UPDATE OR REPLACE t5 SET a=a+1 WHERE a=1; SELECT * FROM t5; } } {2 one} - # Ticket [c38baa3d969eab7946dc50ba9d9b4f0057a19437] # REPLACE works like ABORT on a CHECK constraint. # do_test conflict-13.1 { @@ -807,8 +810,7 @@ REPLACE INTO t13 VALUES(3); COMMIT; SELECT * FROM t13; } } {1 3} - finish_test Index: test/fkey2.test ================================================================== --- test/fkey2.test +++ test/fkey2.test @@ -24,12 +24,10 @@ #------------------------------------------------------------------------- # Test structure: # # fkey2-1.*: Simple tests to check that immediate and deferred foreign key # constraints work when not inside a transaction. -# -# fkey2-2.*: Tests to verify that deferred foreign keys work inside # explicit transactions (i.e that processing really is deferred). # # fkey2-3.*: Tests that a statement transaction is rolled back if an # immediate foreign key constraint is violated. # @@ -133,56 +131,61 @@ 4.11 "DELETE FROM t7 WHERE b=1" {1 {foreign key constraint failed}} 4.12 "UPDATE t7 SET b = 2" {1 {foreign key constraint failed}} 4.13 "UPDATE t7 SET b = 1" {0 {}} 4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {foreign key constraint failed}} 4.15 "UPDATE t7 SET b = 5" {1 {foreign key constraint failed}} - 4.16 "UPDATE t7 SET rowid = 5" {1 {foreign key constraint failed}} 4.17 "UPDATE t7 SET a = 10" {0 {}} 5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}} 5.2 "INSERT INTO t10 VALUES(1, 3)" {1 {foreign key mismatch}} } +# Run the simple tests with all FK constraints immediate. +# do_test fkey2-1.1.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.1.$tn { catchsql $zSql } $res } drop_all_tables +# Run the simple tests with all FK constraints deferred. +# do_test fkey2-1.2.0 { execsql [string map {/D/ {DEFERRABLE INITIALLY DEFERRED}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.2.$tn { catchsql $zSql } $res } drop_all_tables +# Run the simple tests with all FK constraints immediate. Put a +# BEGIN/COMMIT block around each write to the database. +# do_test fkey2-1.3.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] - execsql { PRAGMA count_changes = 1 } } {} foreach {tn zSql res} $FkeySimpleTests { - if {$res == "0 {}"} { set res {0 1} } + execsql BEGIN do_test fkey2-1.3.$tn { catchsql $zSql } $res + execsql COMMIT } -execsql { PRAGMA count_changes = 0 } drop_all_tables +# Run the simple tests with all FK constraints deferred. Put a +# BEGIN/COMMIT block around each write to the database. +# do_test fkey2-1.4.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] - execsql { PRAGMA count_changes = 1 } } {} foreach {tn zSql res} $FkeySimpleTests { - if {$res == "0 {}"} { set res {0 1} } - execsql BEGIN - do_test fkey2-1.4.$tn { catchsql $zSql } $res - execsql COMMIT + do_test fkey2-1.4.$tn { catchsql "BEGIN; $zSql; COMMIT;" } $res + catchsql commit } -execsql { PRAGMA count_changes = 0 } drop_all_tables + # Special test: When the parent key is an IPK, make sure the affinity of # the IPK is not applied to the child key value before it is inserted # into the child table. do_test fkey2-1.5.1 { @@ -214,10 +217,11 @@ catchsql { DELETE FROM i } } {1 {foreign key constraint failed}} # Use a collation sequence on the parent key. drop_all_tables +puts "src4: the following requires collation support" do_test fkey2-1.7.1 { execsql { CREATE TABLE i(i TEXT COLLATE nocase PRIMARY KEY); CREATE TABLE j(j TEXT COLLATE binary REFERENCES i(i)); INSERT INTO i VALUES('SQLite'); @@ -276,10 +280,11 @@ } fkey2-2-test 1 0 "INSERT INTO node VALUES(1, 0)" FKV fkey2-2-test 2 0 "BEGIN" fkey2-2-test 3 1 "INSERT INTO node VALUES(1, 0)" + fkey2-2-test 4 0 "UPDATE node SET parent = NULL" fkey2-2-test 5 0 "COMMIT" fkey2-2-test 6 0 "SELECT * FROM node" {1 {}} fkey2-2-test 7 0 "BEGIN" Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -122,15 +122,22 @@ # #------------------------------------------------------------------------- # Define the generic test suites: # +# src4 # veryquick # quick # full # lappend ::testsuitelist xxx + +test_suite "src4" -prefix "" -description { +} -files { + simple.test fkey1.test conflict.test trigger2.test select1.test + where.test +} test_suite "veryquick" -prefix "" -description { "Very" quick test suite. Runs in less than 5 minutes on a workstation. This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. Index: test/select1.test ================================================================== --- test/select1.test +++ test/select1.test @@ -240,12 +240,12 @@ } } {1 {misuse of aliased aggregate m}} do_test select1-2.23 { execsql { CREATE TABLE tkt2526(a,b,c PRIMARY KEY); - INSERT INTO tkt2526 VALUES('x','y',NULL); - INSERT INTO tkt2526 VALUES('x','z',NULL); + INSERT INTO tkt2526 VALUES('x','y',1); + INSERT INTO tkt2526 VALUES('x','z',2); } catchsql { SELECT count(a) AS cn FROM tkt2526 GROUP BY a HAVING cn=10; SELECT * FROM sqlite_master; } } {} + do_test select1-14.2 { execsql { SELECT 10 IN (SELECT rowid FROM sqlite_master); } } {0} ADDED test/simple.test Index: test/simple.test ================================================================== --- /dev/null +++ test/simple.test @@ -0,0 +1,790 @@ +# 2012 April 02 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# The tests in this file were used while developing the SQLite 4 code. +# +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix simple + +#set sqlite_where_trace 1 + +do_execsql_test 1.0 { + PRAGMA table_info = sqlite_master +} { + 0 type text 0 {} 0 + 1 name text 0 {} 0 + 2 tbl_name text 0 {} 0 + 3 rootpage integer 0 {} 0 + 4 sql text 0 {} 0 +} + +do_execsql_test 1.1 { SELECT * FROM sqlite_master } {} + +#explain { CREATE TABLE t1(a, b) } +#execsql { PRAGMA kv_trace = 1 } +#execsql { PRAGMA vdbe_trace = 1 } + +do_execsql_test 1.2 { + CREATE TABLE t1(a, b); + PRAGMA table_info = t1; +} { + 0 a {} 0 {} 0 + 1 b {} 0 {} 0 +} + +do_execsql_test 1.3 { + CREATE TABLE t2(x, y); + PRAGMA table_info = t2; +} { + 0 x {} 0 {} 0 + 1 y {} 0 {} 0 +} + +do_execsql_test 1.4 { + CREATE TABLE t3(k PRIMARY KEY, v); + PRAGMA table_info = t3; +} { + 0 k {} 1 {} 1 + 1 v {} 0 {} 0 +} + +do_execsql_test 1.5 { + SELECT name, rootpage FROM sqlite_master +} {t1 2 t2 3 t3 4} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 2.1 { + CREATE TABLE t1(k PRIMARY KEY, v); + CREATE TABLE t2(x, y); +} {} + +do_execsql_test 2.2.1 { INSERT INTO t1 VALUES('a', 'AAA') } +do_execsql_test 2.2.2 { SELECT * FROM t1 } {a AAA} +do_execsql_test 2.2.3 { INSERT INTO t1 VALUES('b', 'BBB') } +do_execsql_test 2.2.4 { SELECT * FROM t1 } {a AAA b BBB} + +do_execsql_test 2.3.1 { INSERT INTO t2 VALUES('123', '456') } +do_execsql_test 2.3.2 { SELECT * FROM t2 } {123 456} +do_execsql_test 2.3.3 { INSERT INTO t2 VALUES('789', '0ab') } +do_execsql_test 2.3.4 { SELECT * FROM t2 } {123 456 789 0ab} + +do_catchsql_test 2.2.5 { + INSERT INTO t1 VALUES('a', 'CCC') +} {1 {column k is not unique}} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 3.1 { CREATE TABLE t1(k PRIMARY KEY, v UNIQUE) } + +do_execsql_test 3.2 { + SELECT * FROM sqlite_master +} { + table t1 t1 2 {CREATE TABLE t1(k PRIMARY KEY, v UNIQUE)} + index sqlite_autoindex_t1_2 t1 3 {} +} + +#explain { INSERT INTO t1 VALUES('one', '111') } +#execsql { PRAGMA vdbe_trace = 1 } +#execsql { PRAGMA kv_trace = 1 } +# +do_execsql_test 3.3 { INSERT INTO t1 VALUES('one', '111') } {} + + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 4.1 { CREATE TABLE t1(k PRIMARY KEY, v) } +do_execsql_test 4.2 { CREATE INDEX i1 ON t1(v) } + +do_execsql_test 4.3 { + SELECT * FROM sqlite_master +} { + table t1 t1 2 {CREATE TABLE t1(k PRIMARY KEY, v)} + index i1 t1 3 {CREATE INDEX i1 ON t1(v)} +} +do_execsql_test 4.4 { INSERT INTO t1 VALUES('one', '111') } {} +do_execsql_test 4.5 { SELECT * FROM t1 } {one 111} + +do_execsql_test 4.6 { PRAGMA integrity_check } {ok} + + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 5.1 { CREATE TABLE t1(k, v UNIQUE) } +do_execsql_test 5.2 { CREATE INDEX i1 ON t1(v) } + +do_execsql_test 5.3 { + SELECT * FROM sqlite_master +} { + table t1 t1 3 {CREATE TABLE t1(k, v UNIQUE)} + index sqlite_autoindex_t1_1 t1 2 {} + index i1 t1 4 {CREATE INDEX i1 ON t1(v)} +} + +do_execsql_test 5.3 { INSERT INTO t1 VALUES('one', '111') } {} +do_execsql_test 5.4 { SELECT * FROM t1 } {one 111} +do_execsql_test 5.5 { PRAGMA integrity_check } {ok} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 6.1 { + CREATE TABLE t1(k PRIMARY KEY, v); + CREATE INDEX i1 ON t1(v); + INSERT INTO t1 VALUES('one', 1); + INSERT INTO t1 VALUES('two', 2); + INSERT INTO t1 VALUES('three', 3); + INSERT INTO t1 VALUES('four', 4); + INSERT INTO t1 VALUES('five', 5); +} + +do_execsql_test 6.2 { + SELECT * FROM t1 +} {five 5 four 4 one 1 three 3 two 2} + +do_execsql_test 6.3 { + CREATE TABLE t2(x PRIMARY KEY, y); + INSERT INTO t2 SELECT v, k FROM t1; + SELECT * FROM t2 +} {1 one 2 two 3 three 4 four 5 five} +do_execsql_test 6.4 { PRAGMA integrity_check } {ok} + +do_execsql_test 6.5 { + CREATE TABLE t3(a, b); + INSERT INTO t3 SELECT k, v FROM t1; + SELECT * FROM t3 +} {five 5 four 4 one 1 three 3 two 2} + +do_execsql_test 6.6 { + INSERT INTO t3 SELECT a, b FROM t3; + SELECT * FROM t3; +} {five 5 four 4 one 1 three 3 two 2 five 5 four 4 one 1 three 3 two 2} + +do_execsql_test 6.7 { PRAGMA integrity_check } {ok} +do_execsql_test 6.8 { CREATE INDEX i2 ON t3(a) } +do_execsql_test 6.9 { PRAGMA integrity_check } {ok} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 7.1 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a); +} +do_execsql_test 7.2.1 { INSERT INTO t1 VALUES('xyz', '123') } +do_execsql_test 7.2.2 { INSERT INTO t1 VALUES('xyz', '123') } +do_execsql_test 7.2.3 { INSERT INTO t1 VALUES('xyz', '123') } + +do_execsql_test 7.3 { + SELECT * FROM t1; +} {xyz 123 xyz 123 xyz 123} + +do_execsql_test 7.4 { PRAGMA integrity_check } {ok} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 8.1 { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES('a', 'b'); +} + +do_execsql_test 8.2 { DELETE FROM t1 WHERE b = 'b' } +do_execsql_test 8.3 { SELECT * FROM t1 } {} + +do_execsql_test 8.4 { + INSERT INTO t1 VALUES('a', 'A'); + INSERT INTO t1 VALUES('b', 'B'); + INSERT INTO t1 VALUES('c', 'A'); + INSERT INTO t1 VALUES('d', 'B'); + INSERT INTO t1 VALUES('e', 'A'); + INSERT INTO t1 VALUES('f', 'B'); +} +do_execsql_test 8.5 { DELETE FROM t1 WHERE b = 'B' } +do_execsql_test 8.6 { SELECT * FROM t1 } {a A c A e A} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 9.1 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(b); +} + +do_execsql_test 9.2 { + INSERT INTO t1 VALUES('a', 'A'); + INSERT INTO t1 VALUES('b', 'B'); + INSERT INTO t1 VALUES('c', 'A'); + INSERT INTO t1 VALUES('d', 'B'); + INSERT INTO t1 VALUES('e', 'A'); + INSERT INTO t1 VALUES('f', 'B'); +} +do_execsql_test 9.3 { DELETE FROM t1 WHERE +b = 'B' } +do_execsql_test 9.4 { SELECT * FROM t1 } {a A c A e A} +do_execsql_test 9.5 { PRAGMA integrity_check } {ok} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 10.1 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(b); +} +do_execsql_test 10.2 { + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); +} + +do_execsql_test 10.3 { UPDATE t1 SET b = 10 WHERE a=3 } +do_execsql_test 10.4 { SELECT * FROM t1 } {1 2 3 10} +do_execsql_test 10.5 { PRAGMA integrity_check } {ok} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 11.1 { + CREATE TABLE t1(a, b, c, UNIQUE(a)); + INSERT INTO t1 VALUES(1,2,3); +} +do_catchsql_test 11.2 { + INSERT INTO t1 VALUES(1,2,4) +} {1 {column a is not unique}} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 12.1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(3, 'three'); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); +} +do_execsql_test 12.2 { SELECT * FROM t1 ORDER BY a } {1 one 2 two 3 three} +do_execsql_test 12.3 { SELECT * FROM t1 ORDER BY b } {1 one 3 three 2 two} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 13.1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(3, 'three'); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); +} +do_execsql_test 13.2 { SELECT a FROM t1 } {3 1 2} +do_execsql_test 13.3 { CREATE TABLE t2(x, y) } +do_execsql_test 13.4 { SELECT a FROM t1 } {3 1 2} +do_execsql_test 13.5 { DROP TABLE t2 } +do_execsql_test 13.6 { SELECT a FROM t1 } {3 1 2} +do_execsql_test 13.7 { CREATE TABLE t2 AS SELECT * FROM t1 } +do_execsql_test 13.8 { SELECT a FROM t2 } {3 1 2} +do_execsql_test 13.9 { DROP TABLE t1 } +do_execsql_test 13.10 { SELECT a FROM t2 } {3 1 2} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 14.1 { + CREATE TABLE t1(a,b,c NOT NULL DEFAULT 5); + CREATE TABLE t2(a,b,c); + CREATE TABLE t3(x); + + INSERT INTO t2 VALUES(1,2,1); + INSERT INTO t2 VALUES(2,3,2); + INSERT INTO t2 VALUES(3,4,1); + INSERT INTO t2 VALUES(4,5,4); + + INSERT INTO t3 VALUES(1); +} + +do_execsql_test 14.2 { DROP TABLE t1 } +do_execsql_test 14.3 { SELECT * FROM t3 } 1 + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 15.1.1 { CREATE TABLE t1(x PRIMARY KEY) } +do_execsql_test 15.1.2 { + BEGIN; + INSERT INTO t1 VALUES('rollback is not implemented yet'); +} +do_execsql_test 15.1.3 { ROLLBACK } +do_execsql_test 15.1.4 { SELECT * FROM t1 } {} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 16.1.1 { + PRAGMA foreign_keys = ON; + CREATE TABLE p1(x PRIMARY KEY); + CREATE TABLE c1(y REFERENCES p1); + + INSERT INTO p1 VALUES(2); + INSERT INTO p1 VALUES(4); + INSERT INTO p1 VALUES(6); +} + +do_execsql_test 16.1.2 { INSERT INTO c1 VALUES(2) } +do_catchsql_test 16.1.3 { + INSERT INTO c1 VALUES(3) +} {1 {foreign key constraint failed}} +do_execsql_test 16.1.4 { SELECT * FROM c1 } {2} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 17.1 { + + PRAGMA foreign_keys = ON; + CREATE TABLE t1(x PRIMARY KEY); + CREATE TABLE t2(a PRIMARY KEY, b); + + INSERT INTO t1 VALUES('X'); + + INSERT INTO t2 VALUES(1, 'A'); + INSERT INTO t2 VALUES(2, 'B'); + INSERT INTO t2 VALUES(3, 'C'); + INSERT INTO t2 VALUES(4, 'D'); + INSERT INTO t2 VALUES(5, 'A'); +} + +do_catchsql_test 17.2 { + INSERT INTO t1 SELECT b FROM t2; +} {1 {column x is not unique}} + +do_execsql_test 17.3 { SELECT * FROM t1 } {X} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 18.1 { + CREATE TABLE t1(a, b, c, UNIQUE(a,b) ON CONFLICT IGNORE); + CREATE TABLE t2(x); + + INSERT INTO t1 VALUES(1,2,3); + BEGIN; + INSERT INTO t2 VALUES(1); + INSERT INTO t1 VALUES(1,2,4); + COMMIT; +} + +do_execsql_test 18.2 { SELECT * FROM t1 } {1 2 3} +do_execsql_test 18.3 { SELECT * FROM t2 } {1} + +#------------------------------------------------------------------------- +reset_db + +do_test 19.1 { + catchsql { + CREATE TABLE t4(x); + CREATE UNIQUE INDEX t4x ON t4(x); + BEGIN; + INSERT INTO t4 VALUES(1); + INSERT OR ROLLBACK INTO t4 VALUES(1); + } + execsql { SELECT * FROM t4 } +} {} + +# Check the above closed the transaction. +do_execsql_test 19.2 { BEGIN } +do_execsql_test 19.3 { COMMIT } + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 20.1 { + CREATE TABLE def(d, e, f); + BEGIN; + INSERT INTO def VALUES('a', 'b', 'c'); + + INSERT INTO def SELECT * FROM def; + INSERT INTO def SELECT * FROM def; + INSERT INTO def SELECT * FROM def; + INSERT INTO def SELECT * FROM def; + INSERT INTO def SELECT * FROM def; + + SELECT count(*) FROM def; +} {32} + +do_execsql_test 20.2 { ROLLBACK } +do_execsql_test 20.3 { SELECT count(*) FROM def } 0 + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 21.1 { + PRAGMA foreign_keys = on; + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(c REFERENCES t1(a), d); +} + +do_execsql_test 21.2 { + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t2 VALUES(1, 3); + INSERT INTO t2 VALUES(NULL, 4); +} + +do_catchsql_test 21.3 { + UPDATE t2 SET c=2 WHERE d=4; +} {1 {foreign key constraint failed}} + + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 22.1 { + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES('abc'); +} + +do_execsql_test 22.2 { UPDATE t1 SET x = 'abc' } +do_execsql_test 22.3 { SELECT * FROM t1 } {abc} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 23.1 { + PRAGMA foreign_keys = on; + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(c REFERENCES t1(a), d); +} +proc out {} { + set t1 [execsql {SELECT a FROM t1}] + set t2 [execsql {SELECT c FROM t2}] + puts "t1: $t1 t2: $t2" +} + +do_test 23.2 { + catchsql "BEGIN; INSERT INTO t2 VALUES(1, 3)" ; execsql COMMIT +} {} +do_test 23.3 { + catchsql "BEGIN; INSERT INTO t1 VALUES(1, 2)" ; execsql COMMIT +} {} +do_test 23.4 { + catchsql "BEGIN; INSERT INTO t2 VALUES(1, 3)" ; execsql COMMIT +} {} +do_test 23.5 { + catchsql "BEGIN; INSERT INTO t2 VALUES(2, 4)" ; execsql COMMIT +} {} +do_test 23.6 { + catchsql "BEGIN; INSERT INTO t2 VALUES(NULL, 4)" ; execsql COMMIT +} {} +do_test 23.7 { + catchsql "BEGIN; UPDATE t2 SET c=2 WHERE d=4" ; execsql COMMIT +} {} +do_test 23.8 { + catchsql "BEGIN; UPDATE t2 SET c=1 WHERE d=4" ; execsql COMMIT +} {} +do_test 23.9 { + catchsql "BEGIN; UPDATE t2 SET c=1 WHERE d=4" ; execsql COMMIT +} {} +do_test 23.10 { + catchsql "BEGIN; UPDATE t2 SET c=NULL WHERE d=4" ; execsql COMMIT +} {} +do_test 23.11 { + execsql BEGIN + catchsql "DELETE FROM t1 WHERE a=1" + execsql COMMIT +} {} + +do_catchsql_test 23.3 { + BEGIN; + UPDATE t1 SET a = 2; + COMMIT; +} {1 {foreign key constraint failed}} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 24.1 { + CREATE TABLE p(x INTEGER); + INSERT INTO p VALUES(35.0); + SELECT typeof(x) FROM p; +} {integer} + +do_execsql_test 24.2 { + CREATE TABLE p2(x INTEGER PRIMARY KEY); + INSERT INTO p2 VALUES(35.0); + SELECT typeof(x) FROM p2; +} {integer} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 25.1 { + PRAGMA foreign_keys = on; + CREATE TABLE p(x INT PRIMARY KEY); + CREATE TABLE c(y REFERENCES p); + INSERT INTO p VALUES(35); + INSERT INTO c VALUES(35.0); +} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 26.1 { + PRAGMA foreign_keys = on; + CREATE TABLE p(x INT PRIMARY KEY); + CREATE TABLE c(y REFERENCES p); + INSERT INTO p VALUES(35.0); + INSERT INTO c VALUES(35.0); +} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 27.1 { + CREATE TABLE t1(a, b); + CREATE TABLE log(x); + CREATE TRIGGER BEFORE UPDATE ON t1 BEGIN + INSERT INTO log VALUES(old.b || ' -> ' || new.b); + END; + INSERT INTO t1 VALUES(1, 'abc'); + UPDATE t1 SET b = 'xyz'; +} +do_execsql_test 27.2 { SELECT * FROM log } {{abc -> xyz}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 28.1 { + CREATE TABLE t1(a, b); + CREATE TABLE log(x); + CREATE TRIGGER BEFORE UPDATE ON t1 BEGIN + INSERT INTO log VALUES('rowid=' || old.rowid); + END; + INSERT INTO t1 VALUES(1, 'abc'); +} + +do_execsql_test 28.2 { SELECT rowid FROM t1 } 1 +do_execsql_test 28.3 { UPDATE t1 SET b = 'xyz'; } +do_execsql_test 28.4 { SELECT * FROM log } {{rowid=1}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 29.1 { + CREATE TABLE t1(a, b); + CREATE TABLE log(x,y,z); + CREATE TRIGGER tr BEFORE INSERT ON t1 BEGIN + INSERT INTO log VALUES(new.rowid, new.a, new.b); + END; +} +do_execsql_test 29.2 { INSERT INTO t1 VALUES('one', 'abc') } +do_execsql_test 29.3 { SELECT * FROM log } {-1 one abc} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 30.1 { + CREATE TABLE t1(a, b); + CREATE TABLE log(x,y,z); + CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN + INSERT INTO log VALUES(new.rowid, new.a, new.b); + END; +} +do_execsql_test 30.2 { INSERT INTO t1 VALUES('one', 'abc') } +do_execsql_test 30.3 { SELECT * FROM log } {1 one abc} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 31.1 { + CREATE TABLE tbl(a PRIMARY KEY, b, c); + CREATE TRIGGER tr AFTER INSERT ON tbl BEGIN + UPDATE tbl SET b = ''; + END; + INSERT INTO tbl VALUES(1, 2, 3); +} + +do_execsql_test 31.2 { SELECT * FROM tbl } {1 {} 3} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 32.1 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES(1, 2, 3); +} +do_execsql_test 32.2 { SELECT a, b, c FROM t1 } {1 2 3} +do_execsql_test 32.3 { + DROP TABLE t1; + CREATE TABLE t1(c, b, a); + INSERT INTO t1 VALUES(1, 2, 3); +} +do_execsql_test 32.4 { SELECT a, b, c FROM t1 } {3 2 1} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 33.1 { CREATE TABLE t1(a, b, c) } +do_execsql_test 33.2 { CREATE TABLE t2(a, b, c) } +do_execsql_test 33.3 { CREATE TABLE t3(a, b, c) } +do_execsql_test 33.4 { CREATE TABLE t4(a, b, c) } + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 34.1 { CREATE TABLE t1(x PRIMARY KEY) } + +do_execsql_test 34.2 { INSERT INTO t1 VALUES('123') } +do_test 34.3 { db changes } 1 + +do_execsql_test 34.4 { UPDATE t1 SET x = '456' } +do_test 34.5 { db changes } 1 + +do_execsql_test 34.6 { UPDATE t1 SET x = '456' WHERE x = '123' } +do_test 34.7 { db changes } 0 + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 35.1 { + CREATE TABLE tbl (a primary key, b, c); + INSERT INTO tbl VALUES(1, 2, 3); + INSERT INTO tbl VALUES(2, 2, 3); + CREATE TRIGGER ai_tbl AFTER INSERT ON tbl BEGIN + INSERT OR IGNORE INTO tbl values (new.a, 0, 0); + END; +} + +do_execsql_test 35.2 { INSERT OR REPLACE INTO tbl values (2, 2, 3) } +do_execsql_test 35.3 { SELECT * from tbl } {1 2 3 2 0 0} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 36.1 { + CREATE TABLE tbl (a primary key, b, c); + CREATE TRIGGER au_tbl AFTER UPDATE ON tbl BEGIN + UPDATE OR IGNORE tbl SET a = new.a, c = 10; + END; + + BEGIN; + INSERT INTO tbl VALUES(1, 3, 10); + INSERT INTO tbl VALUES(2, 3, 4); +} +do_catchsql_test 36.2 { + UPDATE OR ROLLBACK tbl SET a = 4 WHERE a = 1; +} {1 {column a is not unique}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 37.1 { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES('x', 'xxx'); + INSERT INTO t1 VALUES('y', 'yyy'); +} +do_execsql_test 37.2 { + BEGIN; + DELETE FROM t1 WHERE a='y'; + INSERT INTO t1 VALUES('y', 'yyy'); + DELETE FROM t1 WHERE a='y'; + INSERT INTO t1 VALUES('y', 'yyy'); + ROLLBACK; +} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 38.1 { + CREATE TABLE t1(a, b); + CREATE TABLE log(a, b); + + -- INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + + CREATE VIEW v1 AS SELECT a, b FROM t1; + CREATE TRIGGER tr1 INSTEAD OF DELETE ON v1 BEGIN + INSERT INTO log VALUES(old.b, old.a); + END; +} +do_execsql_test 38.2 { + DELETE FROM v1 WHERE a = 3; + SELECT * FROM log; +} {4 3} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 39.1 { + CREATE TABLE t1(a PRIMARY KEY, b); +} +do_catchsql_test 39.2 { + INSERT INTO t1 VALUES(NULL, 'xyz'); +} {1 {t1.a may not be NULL}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 40.1 { + CREATE TABLE abc(a, b, c, PRIMARY KEY(a, b)); + INSERT INTO abc VALUES(1, 1, 1); + SELECT * FROM abc; +} {1 1 1} +do_execsql_test 40.2 { SELECT max(a) FROM abc } {1} +do_execsql_test 40.3 { + SELECT a+(select max(a) FROM abc), + b+(select max(a) FROM abc), + c+(select max(a) FROM abc) + FROM abc +} {2 2 2} +do_execsql_test 40.4 { + INSERT INTO abc SELECT + a+(select max(a) FROM abc), + b+(select max(a) FROM abc), + c+(select max(a) FROM abc) + FROM abc; +} +do_execsql_test 40.5 { SELECT * FROM abc } {1 1 1 2 2 2} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 41.1 { + CREATE TABLE x(a, b); + INSERT INTO x VALUES(1, 'one'); + INSERT INTO x VALUES(2, 'two'); + INSERT INTO x VALUES(1, 'three'); +} + +do_execsql_test 41.2 { + SELECT * FROM x ORDER BY a; +} {1 one 1 three 2 two} + +#------------------------------------------------------------------------- +reset_db + +proc populate_t1 {} { + db eval { + INSERT INTO t1(a, b) VALUES(4, 'four'); + INSERT INTO t1(a, b) VALUES(9, 'nine'); + INSERT INTO t1(a, b) VALUES(5, 'five'); + INSERT INTO t1(a, b) VALUES(1, 'one'); + INSERT INTO t1(a, b) VALUES(7, 'seven'); + INSERT INTO t1(a, b) VALUES(8, 'eight'); + INSERT INTO t1(a, b) VALUES(2, 'two'); + INSERT INTO t1(a, b) VALUES(3, 'three'); + INSERT INTO t1(a, b) VALUES(6, 'six'); + INSERT INTO t1(a, b) VALUES(10, 'ten'); + } +} + +foreach {t schema} { + 1 "CREATE TABLE t1(a, b)" + 2 "CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a);" + 3 "CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b);" + 4 "CREATE TABLE t1(a PRIMARY KEY, b)" +} { + + do_test 42.$t.0 { + reset_db + execsql $schema + populate_t1 + } {} + + foreach {u sql res} { + 1 "SELECT * FROM t1 WHERE a = 7" {7 seven} + 2 "SELECT * FROM t1 WHERE b = 'seven'" {7 seven} + } { + do_execsql_test 42.$t.$u $sql $res + } +} + +finish_test ADDED test/src4.test Index: test/src4.test ================================================================== --- /dev/null +++ test/src4.test @@ -0,0 +1,19 @@ +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file runs all the tests run by quick.test except for those related +# to malloc or IO error simulation. With these tests omitted, the overall +# run time is reduced by about 75%. +# +# $Id: veryquick.test,v 1.9 2008/07/12 14:52:21 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/permutations.test + +run_test_suite src4 + +finish_test + Index: test/storage1.test ================================================================== --- test/storage1.test +++ test/storage1.test @@ -67,11 +67,11 @@ lappend res [storage_data $c1] lappend res [storage_prev $c1] storage_close_cursor $c1 storage_close $x set res -} {SQLITE_INEXACT 014557 EF01 SQLITE_OK 012345 ABCD SQLITE_DONE} +} {SQLITE_INEXACT 014557 EF01 SQLITE_OK 012345 ABCD SQLITE_NOTFOUND} do_test storage1-1.6 { set x [storage_open :memory:] storage_begin $x 2 storage_replace $x 012345 abcd storage_replace $x 014567 ef01 Index: test/tester.tcl ================================================================== --- test/tester.tcl +++ test/tester.tcl @@ -80,11 +80,11 @@ # 64KB into the database file instead of the one 1GB in. This means # the code that handles that special case can be tested without creating # very large database files. # set tcl_precision 15 -sqlite4_test_control_pending_byte 0x0010000 +#sqlite4_test_control_pending_byte 0x0010000 # If the pager codec is available, create a wrapper for the [sqlite4] # command that appends "-key {xyzzy}" to the command line. i.e. this: # @@ -354,12 +354,13 @@ # Install the malloc layer used to inject OOM errors. And the 'automatic' # extensions. This only needs to be done once for the process. # sqlite4_shutdown install_malloc_faultsim 1 + kvwrap install sqlite4_initialize - autoinstall_test_functions + #autoinstall_test_functions # If the --binarylog option was specified, create the logging VFS. This # call installs the new VFS as the default for all SQLite connections. # if {$cmdlinearg(binarylog)} { @@ -692,15 +693,15 @@ catch {db close} catch {db2 close} catch {db3 close} - vfs_unlink_test + #vfs_unlink_test sqlite4 db {} # sqlite4_clear_tsd_memdebug db close - sqlite4_reset_auto_extension + #sqlite4_reset_auto_extension sqlite4_soft_heap_limit 0 set nTest [incr_ntest] set nErr [set_test_counter errors] Index: test/trigger2.test ================================================================== --- test/trigger2.test +++ test/trigger2.test @@ -120,26 +120,22 @@ (SELECT coalesce(sum(a),0) FROM tbl), (SELECT coalesce(sum(b),0) FROM tbl), new.a, new.b); END; } - - do_test trigger2-1.$ii.1 { - set r {} - foreach v [execsql { - UPDATE tbl SET a = a * 10, b = b * 10; - SELECT * FROM rlog ORDER BY idx; - SELECT * FROM clog ORDER BY idx; - }] { - lappend r [expr {int($v)}] - } - set r - } [list 1 1 2 4 6 10 20 \ - 2 1 2 13 24 10 20 \ - 3 3 4 13 24 30 40 \ - 4 3 4 40 60 30 40 \ - 1 1 2 13 24 10 20 ] + + do_execsql_test trigger2-1.$ii.1 { + UPDATE tbl SET a = a * 10, b = b * 10; + SELECT * FROM rlog ORDER BY idx; + SELECT * FROM clog ORDER BY idx; + } { + 1 1 2 4 6 10 20 + 2 1 2 13 24 10 20 + 3 3 4 13 24 30 40 + 4 3 4 40 60 30 40 + 1 1 2 13 24 10 20 + } execsql { DELETE FROM rlog; DELETE FROM tbl; INSERT INTO tbl VALUES (100, 100); @@ -160,23 +156,19 @@ (SELECT coalesce(sum(a),0) FROM tbl), (SELECT coalesce(sum(b),0) FROM tbl), 0, 0); END; } - do_test trigger2-1.$ii.2 { - set r {} - foreach v [execsql { + do_execsql_test trigger2-1.$ii.2 { DELETE FROM tbl; SELECT * FROM rlog; - }] { - lappend r [expr {int($v)}] - } - set r - } [list 1 100 100 400 300 0 0 \ - 2 100 100 300 200 0 0 \ - 3 300 200 300 200 0 0 \ - 4 300 200 0 0 0 0 ] + } { + 1 100 100 400 300 0 0 + 2 100 100 300 200 0 0 + 3 300 200 300 200 0 0 + 4 300 200 0 0 0 0 + } execsql { DELETE FROM rlog; CREATE TRIGGER insert_before_row BEFORE INSERT ON tbl FOR EACH ROW BEGIN @@ -193,26 +185,26 @@ 0, 0, (SELECT coalesce(sum(a),0) FROM tbl), (SELECT coalesce(sum(b),0) FROM tbl), new.a, new.b); END; - } - do_test trigger2-1.$ii.3 { - execsql { - - CREATE TABLE other_tbl(a, b); - INSERT INTO other_tbl VALUES(1, 2); - INSERT INTO other_tbl VALUES(3, 4); - -- INSERT INTO tbl SELECT * FROM other_tbl; - INSERT INTO tbl VALUES(5, 6); - DROP TABLE other_tbl; - - SELECT * FROM rlog; - } - } [list 1 0 0 0 0 5 6 \ - 2 0 0 5 6 5 6 ] - + + CREATE TABLE other_tbl(a, b); + INSERT INTO other_tbl VALUES(1, 2); + INSERT INTO other_tbl VALUES(3, 4); + } + + do_execsql_test trigger2-1.$ii.3 { + -- INSERT INTO tbl SELECT * FROM other_tbl; + INSERT INTO tbl VALUES(5, 6); + SELECT * FROM rlog; + } { + 1 0 0 0 0 5 6 + 2 0 0 5 6 5 6 + } + + execsql { DROP TABLE other_tbl } integrity_check trigger2-1.$ii.4 } catchsql { DROP TABLE rlog; DROP TABLE clog; @@ -327,10 +319,11 @@ execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6]\ ON tbl BEGIN $tr_program_fixed END;" do_test trigger2-2.$ii-after "execsql {$statement $query}" $after_data execsql "DROP TRIGGER the_trigger;" + integrity_check trigger2-2.$ii-integrity } } catchsql { @@ -511,16 +504,18 @@ do_test trigger2-6.1e { execsql { SELECT * from tbl; } } {1 2 3 2 2 3} + do_test trigger2-6.1f { execsql { INSERT OR REPLACE INTO tbl values (2, 2, 3); SELECT * from tbl; } } {1 2 3 2 0 0} + do_test trigger2-6.1g { catchsql { INSERT OR ROLLBACK INTO tbl values (3, 2, 3); } } {1 {column a is not unique}} @@ -603,57 +598,64 @@ CREATE TABLE cd(c, d); INSERT INTO ab VALUES (1, 2); INSERT INTO ab VALUES (0, 0); INSERT INTO cd VALUES (3, 4); - CREATE TABLE tlog(ii INTEGER PRIMARY KEY, - olda, oldb, oldc, oldd, newa, newb, newc, newd); + CREATE TABLE tlog(olda, oldb, oldc, oldd, newa, newb, newc, newd); CREATE VIEW abcd AS SELECT a, b, c, d FROM ab, cd; CREATE TRIGGER before_update INSTEAD OF UPDATE ON abcd BEGIN - INSERT INTO tlog VALUES(NULL, + INSERT INTO tlog VALUES( old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d); END; CREATE TRIGGER after_update INSTEAD OF UPDATE ON abcd BEGIN - INSERT INTO tlog VALUES(NULL, + INSERT INTO tlog VALUES( old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d); END; CREATE TRIGGER before_delete INSTEAD OF DELETE ON abcd BEGIN - INSERT INTO tlog VALUES(NULL, + INSERT INTO tlog VALUES( old.a, old.b, old.c, old.d, 0, 0, 0, 0); END; CREATE TRIGGER after_delete INSTEAD OF DELETE ON abcd BEGIN - INSERT INTO tlog VALUES(NULL, + INSERT INTO tlog VALUES( old.a, old.b, old.c, old.d, 0, 0, 0, 0); END; CREATE TRIGGER before_insert INSTEAD OF INSERT ON abcd BEGIN - INSERT INTO tlog VALUES(NULL, + INSERT INTO tlog VALUES( 0, 0, 0, 0, new.a, new.b, new.c, new.d); END; CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN - INSERT INTO tlog VALUES(NULL, + INSERT INTO tlog VALUES( 0, 0, 0, 0, new.a, new.b, new.c, new.d); END; } } {}; -do_test trigger2-7.2 { - execsql { - UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1; - DELETE FROM abcd WHERE a = 1; - INSERT INTO abcd VALUES(10, 20, 30, 40); - SELECT * FROM tlog; - } -} [ list 1 1 2 3 4 100 25 3 4 \ - 2 1 2 3 4 100 25 3 4 \ - 3 1 2 3 4 0 0 0 0 \ - 4 1 2 3 4 0 0 0 0 \ - 5 0 0 0 0 10 20 30 40 \ - 6 0 0 0 0 10 20 30 40 ] +do_execsql_test trigger2-7.2.1 { UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1 } +do_execsql_test trigger2.7.2.2 { SELECT * FROM tlog } { + 1 2 3 4 100 25 3 4 + 1 2 3 4 100 25 3 4 +} +do_execsql_test trigger2-7.2.3 { DELETE FROM abcd WHERE a = 1 } +do_execsql_test trigger2.7.2.4 { SELECT * FROM tlog } { + 1 2 3 4 100 25 3 4 + 1 2 3 4 100 25 3 4 + 1 2 3 4 0 0 0 0 + 1 2 3 4 0 0 0 0 +} +do_execsql_test trigger2-7.2.5 { INSERT INTO abcd VALUES(10, 20, 30, 40) } +do_execsql_test trigger2.7.2.6 { SELECT * FROM tlog } { + 1 2 3 4 100 25 3 4 + 1 2 3 4 100 25 3 4 + 1 2 3 4 0 0 0 0 + 1 2 3 4 0 0 0 0 + 0 0 0 0 10 20 30 40 + 0 0 0 0 10 20 30 40 +} do_test trigger2-7.3 { execsql { DELETE FROM tlog; INSERT INTO abcd VALUES(10, 20, 30, 40); @@ -660,16 +662,16 @@ UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1; DELETE FROM abcd WHERE a = 1; SELECT * FROM tlog; } } [ list \ - 1 0 0 0 0 10 20 30 40 \ - 2 0 0 0 0 10 20 30 40 \ - 3 1 2 3 4 100 25 3 4 \ - 4 1 2 3 4 100 25 3 4 \ - 5 1 2 3 4 0 0 0 0 \ - 6 1 2 3 4 0 0 0 0 \ + 0 0 0 0 10 20 30 40 \ + 0 0 0 0 10 20 30 40 \ + 1 2 3 4 100 25 3 4 \ + 1 2 3 4 100 25 3 4 \ + 1 2 3 4 0 0 0 0 \ + 1 2 3 4 0 0 0 0 \ ] do_test trigger2-7.4 { execsql { DELETE FROM tlog; DELETE FROM abcd WHERE a = 1; @@ -676,16 +678,16 @@ INSERT INTO abcd VALUES(10, 20, 30, 40); UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1; SELECT * FROM tlog; } } [ list \ - 1 1 2 3 4 0 0 0 0 \ - 2 1 2 3 4 0 0 0 0 \ - 3 0 0 0 0 10 20 30 40 \ - 4 0 0 0 0 10 20 30 40 \ - 5 1 2 3 4 100 25 3 4 \ - 6 1 2 3 4 100 25 3 4 \ + 1 2 3 4 0 0 0 0 \ + 1 2 3 4 0 0 0 0 \ + 0 0 0 0 10 20 30 40 \ + 0 0 0 0 10 20 30 40 \ + 1 2 3 4 100 25 3 4 \ + 1 2 3 4 100 25 3 4 \ ] do_test trigger2-8.1 { execsql { CREATE TABLE t1(a,b,c); Index: test/where.test ================================================================== --- test/where.test +++ test/where.test @@ -51,12 +51,14 @@ } {} # Do an SQL statement. Append the search count to the end of the result. # proc count sql { - set ::sqlite_search_count 0 - return [concat [execsql $sql] $::sqlite_search_count] + kvwrap reset + set res [execsql $sql] + #puts "sql={$sql} seek=[kvwrap seek] step=[kvwrap step]" + return [concat $res [expr [kvwrap step] + [kvwrap seek]]] } # Verify that queries use an index. We are using the special variable # "sqlite_search_count" which tallys the number of executions of MoveTo # and Next operators in the VDBE. By verifing that the search count is @@ -132,11 +134,11 @@ set sqlite_query_plan } {t1 i1xy} do_test where-1.8.3 { count {SELECT x, y FROM t1 WHERE y=144 AND x=3} set sqlite_query_plan -} {{} i1xy} +} {t1 i1xy} do_test where-1.9 { count {SELECT x, y FROM t1 WHERE y=144 AND w>10 AND x=3} } {3 144 3} do_test where-1.10 { count {SELECT x, y FROM t1 WHERE x=3 AND w>=10 AND y=121} @@ -197,69 +199,69 @@ # count {SELECT w FROM t1 WHERE x=3 AND y BETWEEN 121 AND 196} # } {10 11 12 13 9} do_test where-1.27 { count {SELECT w FROM t1 WHERE x=3 AND y+1==122} -} {10 10} +} {10 17} do_test where-1.28 { count {SELECT w FROM t1 WHERE x+1=4 AND y+1==122} -} {10 99} +} {10 101} do_test where-1.29 { count {SELECT w FROM t1 WHERE y==121} -} {10 99} +} {10 101} do_test where-1.30 { count {SELECT w FROM t1 WHERE w>97} -} {98 99 100 3} +} {98 99 100 7} do_test where-1.31 { count {SELECT w FROM t1 WHERE w>=97} -} {97 98 99 100 4} +} {97 98 99 100 9} do_test where-1.33 { count {SELECT w FROM t1 WHERE w==97} -} {97 2} +} {97 3} do_test where-1.33.1 { count {SELECT w FROM t1 WHERE w<=97 AND w==97} -} {97 2} +} {97 3} do_test where-1.33.2 { count {SELECT w FROM t1 WHERE w<98 AND w==97} -} {97 2} +} {97 3} do_test where-1.33.3 { count {SELECT w FROM t1 WHERE w>=97 AND w==97} -} {97 2} +} {97 3} do_test where-1.33.4 { count {SELECT w FROM t1 WHERE w>96 AND w==97} -} {97 2} +} {97 3} do_test where-1.33.5 { count {SELECT w FROM t1 WHERE w==97 AND w==97} -} {97 2} +} {97 3} do_test where-1.34 { count {SELECT w FROM t1 WHERE w+1==98} -} {97 99} +} {97 101} do_test where-1.35 { count {SELECT w FROM t1 WHERE w<3} -} {1 2 2} +} {1 2 5} do_test where-1.36 { count {SELECT w FROM t1 WHERE w<=3} -} {1 2 3 3} +} {1 2 3 7} do_test where-1.37 { count {SELECT w FROM t1 WHERE w+1<=4 ORDER BY w} -} {1 2 3 99} +} {1 2 3 201} do_test where-1.38 { count {SELECT (w) FROM t1 WHERE (w)>(97)} -} {98 99 100 3} +} {98 99 100 7} do_test where-1.39 { count {SELECT (w) FROM t1 WHERE (w)>=(97)} -} {97 98 99 100 4} +} {97 98 99 100 9} do_test where-1.40 { count {SELECT (w) FROM t1 WHERE (w)==(97)} -} {97 2} +} {97 3} do_test where-1.41 { count {SELECT (w) FROM t1 WHERE ((w)+(1))==(98)} -} {97 99} +} {97 101} # Do the same kind of thing except use a join as the data source. # do_test where-2.1 { @@ -310,23 +312,23 @@ do_test where-3.1 { count { SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=11 } -} {11 90 11 8} +} {11 90 11 9} do_test where-3.2 { count { SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=12 } -} {12 89 12 8} +} {12 89 12 9} do_test where-3.3 { count { SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C WHERE A.w=15 AND B.p=C.w AND B.r=10202-A.y } -} {15 86 86 8} +} {15 86 86 9} # Test to see that the special case of a constant WHERE clause is # handled. # do_test where-4.1 { @@ -336,11 +338,11 @@ } {0} do_test where-4.2 { count { SELECT * FROM t1 WHERE 1 LIMIT 1 } -} {1 0 4 0} +} {1 0 4 1} do_test where-4.3 { execsql { SELECT 99 WHERE 0 } } {} @@ -371,89 +373,89 @@ ifcapable subquery { do_test where-5.1 { count { SELECT * FROM t1 WHERE rowid IN (1,2,3,1234) order by 1; } - } {1 0 4 2 1 9 3 1 16 4} + } {1 0 4 2 1 9 3 1 16 7} do_test where-5.2 { count { SELECT * FROM t1 WHERE rowid+0 IN (1,2,3,1234) order by 1; } - } {1 0 4 2 1 9 3 1 16 102} + } {1 0 4 2 1 9 3 1 16 201} do_test where-5.3 { count { SELECT * FROM t1 WHERE w IN (-1,1,2,3) order by 1; } - } {1 0 4 2 1 9 3 1 16 14} + } {1 0 4 2 1 9 3 1 16 10} do_test where-5.4 { count { SELECT * FROM t1 WHERE w+0 IN (-1,1,2,3) order by 1; } - } {1 0 4 2 1 9 3 1 16 102} + } {1 0 4 2 1 9 3 1 16 201} do_test where-5.5 { count { SELECT * FROM t1 WHERE rowid IN (select rowid from t1 where rowid IN (-1,2,4)) ORDER BY 1; } - } {2 1 9 4 2 25 3} + } {2 1 9 4 2 25 9} do_test where-5.6 { count { SELECT * FROM t1 WHERE rowid+0 IN (select rowid from t1 where rowid IN (-1,2,4)) ORDER BY 1; } - } {2 1 9 4 2 25 103} + } {2 1 9 4 2 25 206} do_test where-5.7 { count { SELECT * FROM t1 WHERE w IN (select rowid from t1 where rowid IN (-1,2,4)) ORDER BY 1; } - } {2 1 9 4 2 25 9} + } {2 1 9 4 2 25 11} do_test where-5.8 { count { SELECT * FROM t1 WHERE w+0 IN (select rowid from t1 where rowid IN (-1,2,4)) ORDER BY 1; } - } {2 1 9 4 2 25 103} + } {2 1 9 4 2 25 206} do_test where-5.9 { count { SELECT * FROM t1 WHERE x IN (1,7) ORDER BY 1; } - } {2 1 9 3 1 16 7} + } {2 1 9 3 1 16 6} do_test where-5.10 { count { SELECT * FROM t1 WHERE x+0 IN (1,7) ORDER BY 1; } - } {2 1 9 3 1 16 199} + } {2 1 9 3 1 16 201} do_test where-5.11 { count { SELECT * FROM t1 WHERE y IN (6400,8100) ORDER BY 1; } - } {79 6 6400 89 6 8100 199} + } {79 6 6400 89 6 8100 201} do_test where-5.12 { count { SELECT * FROM t1 WHERE x=6 AND y IN (6400,8100) ORDER BY 1; } - } {79 6 6400 89 6 8100 7} + } {79 6 6400 89 6 8100 6} do_test where-5.13 { count { SELECT * FROM t1 WHERE x IN (1,7) AND y NOT IN (6400,8100) ORDER BY 1; } - } {2 1 9 3 1 16 7} + } {2 1 9 3 1 16 6} do_test where-5.14 { count { SELECT * FROM t1 WHERE x IN (1,7) AND y IN (9,10) ORDER BY 1; } - } {2 1 9 8} + } {2 1 9 6} do_test where-5.15 { count { SELECT * FROM t1 WHERE x IN (1,7) AND y IN (9,16) ORDER BY 1; } - } {2 1 9 3 1 16 11} + } {2 1 9 3 1 16 8} } # This procedure executes the SQL. Then it checks to see if the OP_Sort # opcode was executed. If an OP_Sort did occur, then "sort" is appended # to the result. If no OP_Sort happened, then "nosort" is appended. @@ -493,10 +495,11 @@ do_test where-6.4 { cksort { SELECT * FROM t3 WHERE a<10 ORDER BY a LIMIT 3 } } {1 100 4 2 99 9 3 98 16 nosort} + do_test where-6.5 { cksort { SELECT * FROM t3 WHERE a>0 AND a<10 ORDER BY a LIMIT 3 } } {1 100 4 2 99 9 3 98 16 nosort} @@ -503,15 +506,19 @@ do_test where-6.6 { cksort { SELECT * FROM t3 WHERE a>0 ORDER BY a LIMIT 3 } } {1 100 4 2 99 9 3 98 16 nosort} + do_test where-6.7 { + # UPDATE: src4 does a sort here. It picks a different index because it + # does not support the covering index optimization. cksort { SELECT * FROM t3 WHERE b>0 ORDER BY a LIMIT 3 } -} {1 100 4 2 99 9 3 98 16 nosort} +} {1 100 4 2 99 9 3 98 16 sort} + ifcapable subquery { do_test where-6.8 { cksort { SELECT * FROM t3 WHERE a IN (3,5,7,1,9,4,2) ORDER BY a LIMIT 3 } @@ -556,14 +563,15 @@ cksort { SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY c DESC LIMIT 3 } } {1 100 4 nosort} do_test where-6.9.7 { + # UPDATE: src4 uses t3acb here. So does not require an external sort. cksort { SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY c,a LIMIT 3 } -} {1 100 4 sort} +} {1 100 4 nosort} do_test where-6.9.8 { cksort { SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a DESC, c ASC LIMIT 3 } } {1 100 4 nosort} @@ -620,15 +628,17 @@ do_test where-6.20 { cksort { SELECT y FROM t1 ORDER BY rowid LIMIT 3; } } {4 9 16 nosort} + do_test where-6.21 { cksort { SELECT y FROM t1 ORDER BY rowid, y LIMIT 3; } } {4 9 16 nosort} + do_test where-6.22 { cksort { SELECT y FROM t1 ORDER BY rowid, y DESC LIMIT 3; } } {4 9 16 nosort} @@ -1106,16 +1116,16 @@ } {1/1 1/4 4/1 4/4 nosort} do_test where-14.5 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b } -} {4/1 4/4 1/1 1/4 nosort} +} {4/1 4/4 1/1 1/4 sort} do_test where-14.6 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a||x.b DESC } -} {4/1 4/4 1/1 1/4 nosort} +} {4/1 4/4 1/1 1/4 sort} do_test where-14.7 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b } } {4/1 4/4 1/1 1/4 sort} @@ -1122,15 +1132,17 @@ do_test where-14.7.1 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a, y.a||y.b } } {4/1 4/4 1/1 1/4 sort} + do_test where-14.7.2 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, x.a, x.a||x.b } } {4/1 4/4 1/1 1/4 nosort} + do_test where-14.8 { cksort { SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.b, y.a||y.b DESC } } {4/4 4/1 1/4 1/1 sort}