Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -986,11 +986,10 @@ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns indexed by pIdx */ - KeyInfo *pKey; /* KeyInfo structure for pIdx */ int *aGotoChng; /* Array of jump instruction addresses */ int addrRewind; /* Address of "OP_Rewind iIdxCur" */ int addrGotoChng0; /* Address of "Goto addr_chng_0" */ int addrNextRow; /* Address of "next_row:" */ const char *zIdxName; /* Name of the index */ @@ -999,11 +998,10 @@ if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0; VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName)); nCol = pIdx->nKeyCol; aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*(nCol+1)); if( aGotoChng==0 ) continue; - pKey = sqlite3IndexKeyinfo(pParse, pIdx); /* Populate the register containing the index name. */ if( pIdx->autoIndex==2 && !HasRowid(pTab) ){ zIdxName = pTab->zName; }else{ @@ -1050,11 +1048,11 @@ pParse->nMem = MAX(pParse->nMem, regPrev+nCol); /* Open a read-only cursor on the index being analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); /* Invoke the stat_init() function. The arguments are: ** ** (1) the number of columns in the index including the rowid, Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -380,10 +380,11 @@ */ static void freeIndex(sqlite3 *db, Index *p){ #ifndef SQLITE_OMIT_ANALYZE sqlite3DeleteIndexSamples(db, p); #endif + if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo); sqlite3ExprDelete(db, p->pPartIdxWhere); sqlite3DbFree(db, p->zColAff); if( p->isResized ) sqlite3DbFree(db, p->azColl); sqlite3DbFree(db, p); } @@ -2653,15 +2654,16 @@ if( memRootPage>=0 ){ tnum = memRootPage; }else{ tnum = pIndex->tnum; } - pKey = sqlite3IndexKeyinfo(pParse, pIndex); + pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); /* Open the sorter cursor if we are to use one. */ iSorter = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO); + sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*) + sqlite3KeyInfoRef(pKey), P4_KEYINFO); /* Open the table. Loop through all rows of the table, inserting index ** records into the sorter. */ sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); @@ -2672,11 +2674,11 @@ sqlite3VdbeResolveLabel(v, iPartIdxLabel); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); sqlite3VdbeJumpHere(v, addr1); if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, - (char *)pKey, P4_KEYINFO_HANDOFF); + (char *)pKey, P4_KEYINFO); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); assert( pKey!=0 || db->mallocFailed || pParse->nErr ); if( pIndex->onError!=OE_None && pKey!=0 ){ @@ -4140,40 +4142,44 @@ sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); } #endif /* -** Return a dynamicly allocated KeyInfo structure that can be used -** with OP_OpenRead or OP_OpenWrite to access database index pIdx. +** Return a KeyInfo structure that is appropriate for the given Index. +** +** The KeyInfo structure for an index is cached in the Index object. +** So there might be multiple references to the returned pointer. The +** caller should not try to modify the KeyInfo object. ** -** If successful, a pointer to the new structure is returned. In this case -** the caller is responsible for calling sqlite3DbFree(db, ) on the returned -** 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. +** The caller should invoke sqlite3KeyInfoUnref() on the returned object +** when it has finished using it. */ -KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){ +KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ int i; int nCol = pIdx->nColumn; int nKey = pIdx->nKeyCol; KeyInfo *pKey; - if( pIdx->uniqNotNull ){ - pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); - }else{ - pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); - } - if( pKey ){ - for(i=0; iazColl[i]; - if( zColl==0 ) zColl = "BINARY"; - pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl); - pKey->aSortOrder[i] = pIdx->aSortOrder[i]; - } - } - - if( pParse->nErr ){ - sqlite3DbFree(pParse->db, pKey); - pKey = 0; - } - return pKey; + if( pParse->nErr ) return 0; + if( pIdx->pKeyInfo==0 ){ + if( pIdx->uniqNotNull ){ + pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); + }else{ + pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); + } + if( pKey ){ + assert( sqlite3KeyInfoIsWriteable(pKey) ); + for(i=0; iazColl[i]; + if( zColl==0 ) zColl = "BINARY"; + pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl); + pKey->aSortOrder[i] = pIdx->aSortOrder[i]; + } + if( pParse->nErr ){ + sqlite3KeyInfoUnref(pKey); + }else{ + pIdx->pKeyInfo = pKey; + } + } + } + return sqlite3KeyInfoRef(pIdx->pKeyInfo); } Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -380,13 +380,12 @@ iPk = pParse->nMem+1; pParse->nMem += nPk; iKey = ++pParse->nMem; iEph = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iEph, nPk, 0, - (char*)sqlite3IndexKeyinfo(pParse, pPk), - P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 0, 0); if( pWInfo==0 ) goto delete_from_cleanup; for(i=0; iaiColumn[i],iPk+i); } Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -1579,18 +1579,13 @@ for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ if( (pIdx->aiColumn[0]==iCol) && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq && (!mustBeUnique || (pIdx->nKeyCol==1 && pIdx->onError!=OE_None)) ){ - int iAddr; - char *pKey; - - pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); - iAddr = sqlite3CodeOnce(pParse); - - sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, - pKey,P4_KEYINFO_HANDOFF); + int iAddr = sqlite3CodeOnce(pParse); + sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; sqlite3VdbeJumpHere(v, iAddr); @@ -1744,17 +1739,18 @@ dest.affSdst = (u8)affinity; assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); pExpr->x.pSelect->iLimit = 0; testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ - sqlite3DbFree(pParse->db, pKeyInfo); + sqlite3KeyInfoUnref(pKeyInfo); return 0; } pEList = pExpr->x.pSelect->pEList; assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ assert( pEList!=0 ); assert( pEList->nExpr>0 ); + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pEList->a[0].pExpr); }else if( ALWAYS(pExpr->x.pList!=0) ){ /* Case 2: expr IN (exprlist) ** @@ -1770,10 +1766,11 @@ if( !affinity ){ affinity = SQLITE_AFF_NONE; } if( pKeyInfo ){ + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); } /* Loop through each expression in . */ r1 = sqlite3GetTempReg(pParse); @@ -1811,11 +1808,11 @@ } sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r2); } if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO_HANDOFF); + sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); } break; } case TK_EXISTS: Index: src/fkey.c ================================================================== --- src/fkey.c +++ src/fkey.c @@ -377,14 +377,13 @@ sqlite3ReleaseTempReg(pParse, regTemp); }else{ int nCol = pFKey->nCol; int regTemp = sqlite3GetTempRange(pParse, nCol); int regRec = sqlite3GetTempReg(pParse); - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); for(i=0; itnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); + sqlite3TableLock(pParse, iDb, pTab->tnum, + (opcode==OP_OpenWrite)?1:0, pTab->zName); if( HasRowid(pTab) ){ sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nCol); VdbeComment((v, "%s", pTab->zName)); }else{ Index *pPk = sqlite3PrimaryKeyIndex(pTab); assert( pPk!=0 ); assert( pPk->tnum=pTab->tnum ); - sqlite3VdbeAddOp4(v, opcode, iCur, pPk->tnum, iDb, - (char*)sqlite3IndexKeyinfo(p, pPk), P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); VdbeComment((v, "%s", pTab->zName)); } } /* @@ -1704,16 +1705,15 @@ }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); } *piIdxCur = iBase; for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); if( pIdx->autoIndex==2 && !HasRowid(pTab) ) *piDataCur = iIdxCur; - sqlite3VdbeAddOp4(v, op, iIdxCur, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); } if( iBase>pParse->nTab ) pParse->nTab = iBase; return i; } @@ -1826,11 +1826,10 @@ int iSrc, iDest; /* Cursors from source and destination */ int addr1, addr2; /* Loop addresses */ int emptyDestTest = 0; /* Address of test for empty pDest */ int emptySrcTest = 0; /* Address of test for empty pSrc */ Vdbe *v; /* The VDBE we are building */ - KeyInfo *pKey; /* Key information for an index */ int regAutoinc; /* Memory register used by AUTOINC */ int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ int regData, regRowid; /* Registers holding data and rowid */ if( pSelect==0 ){ @@ -2026,17 +2025,15 @@ for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } assert( pSrcIdx ); - pKey = sqlite3IndexKeyinfo(pParse, pSrcIdx); - sqlite3VdbeAddOp4(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc); + sqlite3VdbeSetP4KeyInfo(pParse, pSrcIdx); VdbeComment((v, "%s", pSrcIdx->zName)); - pKey = sqlite3IndexKeyinfo(pParse, pDestIdx); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest); + sqlite3VdbeSetP4KeyInfo(pParse, pDestIdx); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData); sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -1678,13 +1678,12 @@ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); if( x==0 ){ if( pIdx==0 ){ sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); }else{ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); } }else{ k = 0; break; } Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -803,28 +803,60 @@ } /* ** Allocate a KeyInfo object sufficient for an index of N key columns and ** X extra columns. -** -** Actually, always allocate one extra column for the rowid at the end -** of the index. So the KeyInfo returned will have space sufficient for -** N+1 columns. */ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ - KeyInfo *p = sqlite3DbMallocZero(db, + KeyInfo *p = sqlite3DbMallocZero(0, sizeof(KeyInfo) + (N+X)*(sizeof(CollSeq*)+1)); if( p ){ p->aSortOrder = (u8*)&p->aColl[N+X]; p->nField = (u16)N; p->nXField = (u16)X; p->enc = ENC(db); p->db = db; + p->nRef = 1; + }else{ + db->mallocFailed = 1; + } + return p; +} + +/* +** Deallocate a KeyInfo object +*/ +void sqlite3KeyInfoUnref(KeyInfo *p){ + if( p ){ + assert( p->nRef>0 ); + assert( p->db==0 || p->db->pnBytesFreed==0 ); + p->nRef--; + if( p->nRef==0 ) sqlite3DbFree(p->db, p); + } +} + +/* +** Make a new pointer to a KeyInfo object +*/ +KeyInfo *sqlite3KeyInfoRef(KeyInfo *p){ + if( p ){ + assert( p->nRef>0 ); + p->nRef++; } return p; } +#ifdef SQLITE_DEBUG +/* +** Return TRUE if a KeyInfo object can be change. The KeyInfo object +** can only be changed if this is just a single reference to the object. +** +** This routine is used only inside of assert() statements. +*/ +int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; } +#endif /* SQLITE_DEBUG */ + /* ** Given an expression list, generate a KeyInfo structure that records ** the collating sequence for each expression in that expression list. ** ** If the ExprList is an ORDER BY or GROUP BY clause then the resulting @@ -833,12 +865,11 @@ ** then the KeyInfo structure is appropriate for initializing a virtual ** index to implement a DISTINCT test. ** ** 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. +** freed. */ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){ int nExpr; KeyInfo *pInfo; struct ExprList_item *pItem; @@ -846,10 +877,11 @@ int i; nExpr = pList->nExpr; pInfo = sqlite3KeyInfoAlloc(db, nExpr, 1); if( pInfo ){ + assert( sqlite3KeyInfoIsWriteable(pInfo) ); for(i=0, pItem=pList->a; ipExpr); if( !pColl ) pColl = db->pDfltColl; pInfo->aColl[i] = pColl; @@ -2010,15 +2042,16 @@ ** always safely abort as soon as the first unused slot is found */ assert( pLoop->addrOpenEphm[1]<0 ); break; } sqlite3VdbeChangeP2(v, addr, nCol); - sqlite3VdbeChangeP4(v, addr, (char*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo), + P4_KEYINFO); pLoop->addrOpenEphm[i] = -1; } } - sqlite3DbFree(db, pKeyInfo); + sqlite3KeyInfoUnref(pKeyInfo); } multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; @@ -2053,11 +2086,10 @@ SelectDest *pIn, /* Coroutine supplying data */ SelectDest *pDest, /* Where to send the data */ int regReturn, /* The return address register */ int regPrev, /* Previous result register. No uniqueness if 0 */ KeyInfo *pKeyInfo, /* For comparing with previous entry */ - int p4type, /* The p4 type for pKeyInfo */ int iBreak /* Jump here if we hit the LIMIT */ ){ Vdbe *v = pParse->pVdbe; int iContinue; int addr; @@ -2069,11 +2101,11 @@ */ if( regPrev ){ int j1, j2; j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst, - (char*)pKeyInfo, p4type); + (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); sqlite3VdbeJumpHere(v, j1); sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1); sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev); } @@ -2380,10 +2412,11 @@ pColl = multiSelectCollSeq(pParse, p, aPermute[i]); if( pColl==0 ) pColl = db->pDfltColl; pOrderBy->a[i].pExpr = sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName); } + assert( sqlite3KeyInfoIsWriteable(pKeyMerge) ); pKeyMerge->aColl[i] = pColl; pKeyMerge->aSortOrder[i] = pOrderBy->a[i].sortOrder; } } }else{ @@ -2407,10 +2440,11 @@ regPrev = pParse->nMem+1; pParse->nMem += nExpr+1; sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev); pKeyDup = sqlite3KeyInfoAlloc(db, nExpr, 1); if( pKeyDup ){ + assert( sqlite3KeyInfoIsWriteable(pKeyDup) ); for(i=0; iaColl[i] = multiSelectCollSeq(pParse, p, i); pKeyDup->aSortOrder[i] = 0; } } @@ -2488,21 +2522,22 @@ ** select as the next output row of the compound select. */ VdbeNoopComment((v, "Output routine for A")); addrOutA = generateOutputSubroutine(pParse, p, &destA, pDest, regOutA, - regPrev, pKeyDup, P4_KEYINFO_HANDOFF, labelEnd); + regPrev, pKeyDup, labelEnd); /* Generate a subroutine that outputs the current row of the B ** select as the next output row of the compound select. */ if( op==TK_ALL || op==TK_UNION ){ VdbeNoopComment((v, "Output routine for B")); addrOutB = generateOutputSubroutine(pParse, p, &destB, pDest, regOutB, - regPrev, pKeyDup, P4_KEYINFO_STATIC, labelEnd); + regPrev, pKeyDup, labelEnd); } + sqlite3KeyInfoUnref(pKeyDup); /* Generate a subroutine to run when the results from select A ** are exhausted and only data in select B remains. */ VdbeNoopComment((v, "eof-A subroutine")); @@ -2577,11 +2612,11 @@ /* Implement the main merge loop */ sqlite3VdbeResolveLabel(v, labelCmpr); sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, - (char*)pKeyMerge, P4_KEYINFO_HANDOFF); + (char*)pKeyMerge, P4_KEYINFO); sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); /* Jump to the this point in order to terminate the query. */ @@ -3803,11 +3838,11 @@ "argument"); pFunc->iDistinct = -1; }else{ KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, - (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + (char*)pKeyInfo, P4_KEYINFO); } } } } @@ -4258,11 +4293,11 @@ pKeyInfo = keyInfoFromExprList(pParse, pOrderBy); pOrderBy->iECursor = pParse->nTab++; p->addrOpenEphm[2] = addrSortIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pOrderBy->iECursor, pOrderBy->nExpr+2, 0, - (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + (char*)pKeyInfo, P4_KEYINFO); }else{ addrSortIndex = -1; } /* If the output is destined for a temporary table, open that table. @@ -4286,11 +4321,11 @@ if( p->selFlags & SF_Distinct ){ sDistinct.tabTnct = pParse->nTab++; sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, sDistinct.tabTnct, 0, 0, (char*)keyInfoFromExprList(pParse, p->pEList), - P4_KEYINFO_HANDOFF); + P4_KEYINFO); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED; }else{ sDistinct.eTnctType = WHERE_DISTINCT_NOOP; } @@ -4410,11 +4445,11 @@ */ sAggInfo.sortingIdx = pParse->nTab++; pKeyInfo = keyInfoFromExprList(pParse, pGroupBy); addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, - 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + 0, (char*)pKeyInfo, P4_KEYINFO); /* Initialize memory locations used by GROUP BY aggregate processing */ iUseFlag = ++pParse->nMem; iAbortFlag = ++pParse->nMem; @@ -4524,11 +4559,11 @@ sAggInfo.directMode = 1; sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); } } sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, - (char*)pKeyInfo, P4_KEYINFO); + (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); j1 = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); /* Generate code that runs whenever the GROUP BY changes. ** Changes in the GROUP BY are detected by the previous code @@ -4650,17 +4685,17 @@ pBest = pIdx; } } if( pBest ){ iRoot = pBest->tnum; - pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest); + pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); } /* Open a read-only cursor, execute the OP_Count, close the cursor. */ sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1); if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF); + sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); } sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); }else Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1530,14 +1530,15 @@ ** Note that aSortOrder[] and aColl[] have nField+1 slots. There ** are nField slots for the columns of an index then one extra slot ** for the rowid at the end. */ struct KeyInfo { - sqlite3 *db; /* The database connection */ + u32 nRef; /* Number of references to this KeyInfo object */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nField; /* Number of key columns in the index */ u16 nXField; /* Number of columns beyond the key columns */ + sqlite3 *db; /* The database connection */ u8 *aSortOrder; /* Sort order for each column. */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; /* @@ -1602,10 +1603,11 @@ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ Expr *pPartIdxWhere; /* WHERE clause for partial indices */ + KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */ int tnum; /* DB Page containing root of this index */ LogEst szIdxRow; /* Estimated average row size in bytes */ u16 nKeyCol; /* Number of columns forming the key */ u16 nColumn; /* Number of columns stored in the index */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ @@ -3157,11 +3159,16 @@ void sqlite3MinimumFileFormat(Parse*, int, int); void sqlite3SchemaClear(void *); Schema *sqlite3SchemaGet(sqlite3 *, Btree *); int sqlite3SchemaToIndex(sqlite3 *db, Schema *); KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int); -KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); +void sqlite3KeyInfoUnref(KeyInfo*); +KeyInfo *sqlite3KeyInfoRef(KeyInfo*); +KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*); +#ifdef SQLITE_DEBUG +int sqlite3KeyInfoIsWriteable(KeyInfo*); +#endif int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*), FuncDestructor *pDestructor ); Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -358,13 +358,12 @@ nPk = pPk->nKeyCol; iPk = pParse->nMem+1; pParse->nMem += nPk; regKey = ++pParse->nMem; iEph = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iEph, nPk, 0, - (char*)sqlite3IndexKeyinfo(pParse, pPk), - P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 0, 0); if( pWInfo==0 ) goto update_cleanup; for(i=0; iaiColumn[i], iPk+i); @@ -406,13 +405,12 @@ } } for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ assert( aRegIdx ); if( openAll || aRegIdx[i]>0 ){ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdxCur+i, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, OP_OpenWrite, iIdxCur+i, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); assert( pParse->nTab>iIdxCur+i ); VdbeComment((v, "%s", pIdx->zName)); } } } Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -115,20 +115,10 @@ #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ #define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ -/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure -** is made. That copy is freed when the Vdbe is finalized. But if the -** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still -** gets freed when the Vdbe is finalized so it still should be obtained -** from a single sqliteMalloc(). But no copy is made and the calling -** function should *not* try to free the KeyInfo. -*/ -#define P4_KEYINFO_HANDOFF (-16) -#define P4_KEYINFO_STATIC (-17) - /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 #define P5_ConstraintUnique 2 #define P5_ConstraintCheck 3 #define P5_ConstraintFK 4 @@ -184,10 +174,11 @@ void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); void sqlite3VdbeChangeP5(Vdbe*, u8 P5); void sqlite3VdbeJumpHere(Vdbe*, int addr); void sqlite3VdbeChangeToNoop(Vdbe*, int addr); void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); +void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); void sqlite3VdbeRunOnlyOnce(Vdbe*); void sqlite3VdbeDelete(Vdbe*); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -635,15 +635,17 @@ assert( db ); switch( p4type ){ case P4_REAL: case P4_INT64: case P4_DYNAMIC: - case P4_KEYINFO: - case P4_INTARRAY: - case P4_KEYINFO_HANDOFF: { + case P4_INTARRAY: { sqlite3DbFree(db, p4); break; + } + case P4_KEYINFO: { + if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4); + break; } case P4_MPRINTF: { if( db->pnBytesFreed==0 ) sqlite3_free(p4); break; } @@ -719,18 +721,10 @@ ** ** If n>=0 then the P4 operand is dynamic, meaning that a copy of ** the string is made into memory obtained from sqlite3_malloc(). ** A value of n==0 means copy bytes of zP4 up to and including the ** first null byte. If n>0 then copy n+1 bytes of zP4. -** -** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure. -** A copy is made of the KeyInfo structure into memory obtained from -** sqlite3_malloc, to be freed when the Vdbe is finalized. -** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure -** stored in memory that the caller has obtained from sqlite3_malloc. The -** caller should not free the allocation, it will be freed when the Vdbe is -** finalized. ** ** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points ** to a string or structure that is guaranteed to exist for the lifetime of ** the Vdbe. In these cases we can just copy the pointer. ** @@ -741,11 +735,11 @@ sqlite3 *db; assert( p!=0 ); db = p->db; assert( p->magic==VDBE_MAGIC_INIT ); if( p->aOp==0 || db->mallocFailed ){ - if ( n!=P4_KEYINFO && n!=P4_VTAB ) { + if( n!=P4_VTAB ){ freeP4(db, n, (void*)*(char**)&zP4); } return; } assert( p->nOp>0 ); @@ -764,25 +758,10 @@ pOp->p4type = P4_INT32; }else if( zP4==0 ){ pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; }else if( n==P4_KEYINFO ){ - KeyInfo *pOrig, *pNew; - - pOrig = (KeyInfo*)zP4; - pNew = sqlite3KeyInfoAlloc(db, pOrig->nField, pOrig->nXField); - pOp->p4.pKeyInfo = pNew; - if( pNew ){ - int n = pOrig->nField+pOrig->nXField; - memcpy(pNew->aColl, pOrig->aColl, n*sizeof(pNew->aColl[0])); - memcpy(pNew->aSortOrder, pOrig->aSortOrder, n); - pOp->p4type = P4_KEYINFO; - }else{ - p->db->mallocFailed = 1; - pOp->p4type = P4_NOTUSED; - } - }else if( n==P4_KEYINFO_HANDOFF ){ pOp->p4.p = (void*)zP4; pOp->p4type = P4_KEYINFO; }else if( n==P4_VTAB ){ pOp->p4.p = (void*)zP4; pOp->p4type = P4_VTAB; @@ -795,10 +774,22 @@ if( n==0 ) n = sqlite3Strlen30(zP4); pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n); pOp->p4type = P4_DYNAMIC; } } + +/* +** Set the P4 on the most recently added opcode to the KeyInfo for the +** index given. +*/ +void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ + Vdbe *v = pParse->pVdbe; + assert( v!=0 ); + assert( pIdx!=0 ); + sqlite3VdbeChangeP4(v, -1, (char*)sqlite3KeyInfoOfIndex(pParse, pIdx), + P4_KEYINFO); +} #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS /* ** Change the comment on the most recently coded instruction. Or ** insert a No-op and add the comment to that new instruction. This @@ -956,11 +947,10 @@ */ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ char *zP4 = zTemp; assert( nTemp>=20 ); switch( pOp->p4type ){ - case P4_KEYINFO_STATIC: case P4_KEYINFO: { int i, j; KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; assert( pKeyInfo->aSortOrder!=0 ); sqlite3_snprintf(nTemp, zTemp, "k(%d", pKeyInfo->nField); Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -1034,14 +1034,14 @@ int nCol = pIdx->nColumn; /* Number of index columns including rowid */ nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord); pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); if( pRec ){ - pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx); + pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx); if( pRec->pKeyInfo ){ assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol ); - pRec->pKeyInfo->enc = ENC(db); + assert( pRec->pKeyInfo->enc==ENC(db) ); pRec->flags = UNPACKED_PREFIX_MATCH; pRec->aMem = (Mem *)&pRec[1]; for(i=0; iaMem[i].flags = MEM_Null; pRec->aMem[i].type = SQLITE_NULL; @@ -1366,11 +1366,11 @@ Mem *aMem = pRec->aMem; sqlite3 *db = aMem[0].db; for(i=0; ipKeyInfo); + sqlite3KeyInfoUnref(pRec->pKeyInfo); sqlite3DbFree(db, pRec); } } #endif /* ifdef SQLITE_ENABLE_STAT4 */ Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -2013,11 +2013,10 @@ WhereTerm *pWCEnd; /* End of pWC->a[] */ Index *pIdx; /* Object describing the transient index */ Vdbe *v; /* Prepared statement under construction */ int addrInit; /* Address of the initialization bypass jump */ Table *pTable; /* The table being indexed */ - KeyInfo *pKeyinfo; /* Key information for the index */ int addrTop; /* Top of the index fill loop */ int regRecord; /* Register holding an index record */ int n; /* Column counter */ int i; /* Loop counter */ int mxBitCol; /* Maximum column in pSrc->colUsed */ @@ -2130,15 +2129,14 @@ assert( n==nKeyCol ); pIdx->aiColumn[n] = -1; pIdx->azColl[n] = "BINARY"; /* Create the automatic index */ - pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx); assert( pLevel->iIdxCur>=0 ); pLevel->iIdxCur = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1, 0, - (char*)pKeyinfo, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); /* Fill the automatic index with content */ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); regRecord = sqlite3GetTempReg(pParse); @@ -3994,10 +3992,11 @@ sqlite3_free(p->u.vtab.idxStr); p->u.vtab.needFree = 0; p->u.vtab.idxStr = 0; }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ sqlite3DbFree(db, p->u.btree.pIndex->zColAff); + sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo); sqlite3DbFree(db, p->u.btree.pIndex); p->u.btree.pIndex = 0; } } } @@ -6036,17 +6035,16 @@ }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } if( pLoop->wsFlags & WHERE_INDEXED ){ Index *pIx = pLoop->u.btree.pIndex; - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx); /* FIXME: As an optimization use pTabItem->iCursor if WHERE_IDX_ONLY */ int iIndexCur = pLevel->iIdxCur = iIdxCur ? iIdxCur : pParse->nTab++; assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); - sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIx); VdbeComment((v, "%s", pIx->zName)); } sqlite3CodeVerifySchema(pParse, iDb); notReady &= ~getMask(&pWInfo->sMaskSet, pTabItem->iCursor); }