From: drh Date: Sat, 28 Oct 2006 00:28:09 +0000 (+0000) Subject: Enhance the optimizer so that IS NULL can use an available index. (CVS 3494) X-Git-Tag: version-3.6.10~2674 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=50b39968993997742f3c1f5ddefc5ddc9710a871;p=thirdparty%2Fsqlite.git Enhance the optimizer so that IS NULL can use an available index. (CVS 3494) FossilOrigin-Name: 64762a9d582e4655d6bc5989d8e0ad773d659a7d --- diff --git a/manifest b/manifest index 07342d6320..632c881ee3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\s".dump"\scommand\sin\sthe\scommand-line\sshell\sso\sthat\sit\sshows\nTRIGGERs\sand\sVIEWs.\s\sTicket\s#2044.\s(CVS\s3493) -D 2006-10-27T14:21:54 +C Enhance\sthe\soptimizer\sso\sthat\sIS\sNULL\scan\suse\san\savailable\sindex.\s(CVS\s3494) +D 2006-10-28T00:28:09 F Makefile.in 4379c909d46b38b8c5db3533084601621d4f14b2 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -121,7 +121,7 @@ F src/update.c 951f95ef044cf6d28557c48dc35cb0711a0b9129 F src/utf.c 67ecb1032bc0b42c105e88d65ef9d9f626eb0e1f F src/util.c 91d4cb189476906639ae611927d939691d1365f6 F src/vacuum.c f6a7943f1f1002cb82ef2ea026cb1975a5b687cb -F src/vdbe.c 1d64351d52e06d55788ad342e1caf6d6a89901fe +F src/vdbe.c 3e31f2e49423955afb2ac905ceacf54c679b1f28 F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa F src/vdbeInt.h e3eaab262b67b84474625cfc38aec1125c32834b F src/vdbeapi.c f1858a5edc3a5e32d038514dd9e7e9091400a782 @@ -129,7 +129,7 @@ F src/vdbeaux.c 78c744f127df03ea63098ebb7dd7fe3eb303e284 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c 26623176bf1c616aa478da958fac49502491a921 F src/vtab.c aa30e940058ea56a1b7a9a7019ec21d307316fb4 -F src/where.c 65a19a70c94ba78eb201d84084e8e127fbc40331 +F src/where.c 1c33e196807bfe1bd021988525cf4728916a4a44 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/all.test 5df90d015ca63fcef2a4b62c24f7316b66c4bfd4 @@ -419,7 +419,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 4d336e9ef5f65b95959e7d01cd0357d46e9b1fa5 -R d3fdd3765b2a768156c9efe0b3361fb5 +P 58171a41f706dd2fab1da5d83d2176d0103643fb +R 3d00eb1d0264ea0aeb0bc4ee950a7a9d U drh -Z cae1c5a81ec8310a9861d0fd4c0ac748 +Z f47a4f5a11ed13f459b99613e5b7d9ba diff --git a/manifest.uuid b/manifest.uuid index e68d639339..486014ce91 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -58171a41f706dd2fab1da5d83d2176d0103643fb \ No newline at end of file +64762a9d582e4655d6bc5989d8e0ad773d659a7d \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index a02d2eddb1..801887b9a3 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.578 2006/10/27 14:06:58 drh Exp $ +** $Id: vdbe.c,v 1.579 2006/10/28 00:28:09 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -3851,38 +3851,6 @@ case OP_IdxGE: { /* no-push */ break; } -/* Opcode: IdxIsNull P1 P2 * -** -** The top of the stack contains an index entry such as might be generated -** by the MakeIdxRec opcode. This routine looks at the first P1 fields of -** that key. If any of the first P1 fields are NULL, then a jump is made -** to address P2. Otherwise we fall straight through. -** -** The index entry is always popped from the stack. -*/ -case OP_IdxIsNull: { /* no-push */ - int i = pOp->p1; - int k, n; - const char *z; - u32 serial_type; - - assert( pTos>=p->aStack ); - assert( pTos->flags & MEM_Blob ); - z = pTos->z; - n = pTos->n; - k = sqlite3GetVarint32((u8*)z, &serial_type); - for(; k0; i--){ - k += sqlite3GetVarint32((u8*)&z[k], &serial_type); - if( serial_type==0 ){ /* Serial type 0 is a NULL */ - pc = pOp->p2-1; - break; - } - } - Release(pTos); - pTos--; - break; -} - /* Opcode: Destroy P1 P2 * ** ** Delete an entire database table or index whose root page in the database diff --git a/src/where.c b/src/where.c index 24d7c63109..cea97c14d6 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.230 2006/10/27 14:06:59 drh Exp $ +** $Id: where.c,v 1.231 2006/10/28 00:28:09 drh Exp $ */ #include "sqliteInt.h" @@ -157,6 +157,7 @@ struct ExprMaskSet { #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_MATCH 64 +#define WO_ISNULL 128 /* ** Value for flags returned by bestIndex() @@ -354,7 +355,7 @@ static int allowedOp(int op){ assert( TK_LT>TK_EQ && TK_LTTK_EQ && TK_LE=TK_EQ && op<=TK_GE); + return op==TK_IN || (op>=TK_EQ && op<=TK_GE) || op==TK_ISNULL; } /* @@ -388,9 +389,12 @@ static int operatorMask(int op){ assert( allowedOp(op) ); if( op==TK_IN ){ c = WO_IN; + }else if( op==TK_ISNULL ){ + c = WO_ISNULL; }else{ c = WO_EQ<<(op-TK_EQ); } + assert( op!=TK_ISNULL || c==WO_ISNULL ); assert( op!=TK_IN || c==WO_IN ); assert( op!=TK_EQ || c==WO_EQ ); assert( op!=TK_LT || c==WO_LT ); @@ -422,7 +426,7 @@ static WhereTerm *findTerm( && pTerm->leftColumn==iColumn && (pTerm->eOperator & op)!=0 ){ - if( iCur>=0 && pIdx ){ + if( iCur>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ Expr *pX = pTerm->pExpr; CollSeq *pColl; char idxaff; @@ -590,13 +594,17 @@ static void exprAnalyze( Bitmask prereqAll; int nPattern; int isComplete; + int op; if( sqlite3MallocFailed() ) return; prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft); - if( pExpr->op==TK_IN ){ + op = pExpr->op; + if( op==TK_IN ){ assert( pExpr->pRight==0 ); pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->pList) | exprSelectTableUsage(pMaskSet, pExpr->pSelect); + }else if( op==TK_ISNULL ){ + pTerm->prereqRight = 0; }else{ pTerm->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight); } @@ -608,13 +616,13 @@ static void exprAnalyze( pTerm->leftCursor = -1; pTerm->iParent = -1; pTerm->eOperator = 0; - if( allowedOp(pExpr->op) && (pTerm->prereqRight & prereqLeft)==0 ){ + if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; if( pLeft->op==TK_COLUMN ){ pTerm->leftCursor = pLeft->iTable; pTerm->leftColumn = pLeft->iColumn; - pTerm->eOperator = operatorMask(pExpr->op); + pTerm->eOperator = operatorMask(op); } if( pRight && pRight->op==TK_COLUMN ){ WhereTerm *pNew; @@ -1341,7 +1349,7 @@ static double bestIndex( flags = 0; for(i=0; inColumn; i++){ int j = pProbe->aiColumn[i]; - pTerm = findTerm(pWC, iCur, j, notReady, WO_EQ|WO_IN, pProbe); + pTerm = findTerm(pWC, iCur, j, notReady, WO_EQ|WO_IN|WO_ISNULL, pProbe); if( pTerm==0 ) break; flags |= WHERE_COLUMN_EQ; if( pTerm->eOperator & WO_IN ){ @@ -1480,30 +1488,18 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ } /* -** Generate code that builds a probe for an index. Details: -** -** * Check the top nColumn entries on the stack. If any -** of those entries are NULL, jump immediately to brk, -** which is the loop exit, since no index entry will match -** if any part of the key is NULL. Pop (nColumn+nExtra) -** elements from the stack. -** -** * Construct a probe entry from the top nColumn entries in -** the stack with affinities appropriate for index pIdx. -** Only nColumn elements are popped from the stack in this case -** (by OP_MakeRecord). +** Generate code that builds a probe for an index. ** +** There should be nColumn values on the stack. The index +** to be probed is pIdx. Pop the values from the stack and +** replace them all with a single record that is the index +** problem. */ static void buildIndexProbe( Vdbe *v, /* Generate code into this VM */ int nColumn, /* The number of columns to check for NULL */ - int nExtra, /* Number of extra values beyond nColumn */ - int brk, /* Jump to here if no match is possible */ Index *pIdx /* Index that we will be searching */ ){ - sqlite3VdbeAddOp(v, OP_NotNull, -nColumn, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeAddOp(v, OP_Pop, nColumn+nExtra, 0); - sqlite3VdbeAddOp(v, OP_Goto, 0, brk); sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0); sqlite3IndexAffinityStr(v, pIdx); } @@ -1527,15 +1523,17 @@ static void codeEqualityTerm( WhereLevel *pLevel /* When level of the FROM clause we are working on */ ){ Expr *pX = pTerm->pExpr; - if( pX->op!=TK_IN ){ - assert( pX->op==TK_EQ ); + Vdbe *v = pParse->pVdbe; + if( pX->op==TK_EQ ){ sqlite3ExprCode(pParse, pX->pRight); + }else if( pX->op==TK_ISNULL ){ + sqlite3VdbeAddOp(v, OP_Null, 0, 0); #ifndef SQLITE_OMIT_SUBQUERY }else{ int iTab; int *aIn; - Vdbe *v = pParse->pVdbe; + assert( pX->op==TK_IN ); sqlite3CodeSubselect(pParse, pX); iTab = pX->iTable; sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0); @@ -1609,13 +1607,13 @@ static void codeAllEqualityTerms( */ for(j=0; jnColumn; j++){ int k = pIdx->aiColumn[j]; - pTerm = findTerm(pWC, iCur, k, notReady, WO_EQ|WO_IN, pIdx); + pTerm = findTerm(pWC, iCur, k, notReady, WO_EQ|WO_IN|WO_ISNULL, pIdx); if( pTerm==0 ) break; assert( (pTerm->flags & TERM_CODED)==0 ); codeEqualityTerm(pParse, pTerm, brk, pLevel); -#if 0 /* WORK IN PROGRESS */ - sqlite3VdbeAddOp(v, OP_IsNull, termsInMem ? -1 : -(j+1), brk); -#endif + if( (pTerm->eOperator & WO_ISNULL)==0 ){ + sqlite3VdbeAddOp(v, OP_IsNull, termsInMem ? -1 : -(j+1), brk); + } if( termsInMem ){ sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem+j+1, 1); } @@ -1993,7 +1991,9 @@ WhereInfo *sqlite3WhereBegin( sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum, (char*)pKey, P3_KEYINFO_HANDOFF); } - if( (pLevel->flags & WHERE_IDX_ONLY)!=0 ){ + if( (pLevel->flags & (WHERE_IDX_ONLY|WHERE_COLUMN_RANGE))!=0 ){ + /* Only call OP_SetNumColumns on the index if we might later use + ** OP_Column on the index. */ sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1); } sqlite3CodeVerifySchema(pParse, iDb); @@ -2166,7 +2166,6 @@ WhereInfo *sqlite3WhereBegin( int btmEq=0; /* True if btm limit uses ==. False if strictly > */ int topOp, btmOp; /* Operators for the top and bottom search bounds */ int testOp; - int nNotNull; /* Number of rows of index that must be non-NULL */ int topLimit = (pLevel->flags & WHERE_TOP_LIMIT)!=0; int btmLimit = (pLevel->flags & WHERE_BTM_LIMIT)!=0; @@ -2188,7 +2187,6 @@ WhereInfo *sqlite3WhereBegin( ** operator and the top bound is a < or <= operator. For a descending ** index the operators are reversed. */ - nNotNull = nEq + topLimit; if( pIdx->aSortOrder[nEq]==SQLITE_SO_ASC ){ topOp = WO_LT|WO_LE; btmOp = WO_GT|WO_GE; @@ -2213,6 +2211,7 @@ WhereInfo *sqlite3WhereBegin( pX = pTerm->pExpr; assert( (pTerm->flags & TERM_CODED)==0 ); sqlite3ExprCode(pParse, pX->pRight); + sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), brk); topEq = pTerm->eOperator & (WO_LE|WO_GE); disableTerm(pLevel, pTerm); testOp = OP_IdxGE; @@ -2223,7 +2222,7 @@ WhereInfo *sqlite3WhereBegin( if( testOp!=OP_Noop ){ int nCol = nEq + topLimit; pLevel->iMem = pParse->nMem++; - buildIndexProbe(v, nCol, nEq, brk, pIdx); + buildIndexProbe(v, nCol, pIdx); if( bRev ){ int op = topEq ? OP_MoveLe : OP_MoveLt; sqlite3VdbeAddOp(v, op, iIdxCur, brk); @@ -2251,6 +2250,7 @@ WhereInfo *sqlite3WhereBegin( pX = pTerm->pExpr; assert( (pTerm->flags & TERM_CODED)==0 ); sqlite3ExprCode(pParse, pX->pRight); + sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), brk); btmEq = pTerm->eOperator & (WO_LE|WO_GE); disableTerm(pLevel, pTerm); }else{ @@ -2258,7 +2258,7 @@ WhereInfo *sqlite3WhereBegin( } if( nEq>0 || btmLimit ){ int nCol = nEq + btmLimit; - buildIndexProbe(v, nCol, 0, brk, pIdx); + buildIndexProbe(v, nCol, pIdx); if( bRev ){ pLevel->iMem = pParse->nMem++; sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); @@ -2285,8 +2285,10 @@ WhereInfo *sqlite3WhereBegin( sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC); } } - sqlite3VdbeAddOp(v, OP_RowKey, iIdxCur, 0); - sqlite3VdbeAddOp(v, OP_IdxIsNull, nNotNull, cont); + if( topLimit | btmLimit ){ + sqlite3VdbeAddOp(v, OP_Column, iIdxCur, nEq); + sqlite3VdbeAddOp(v, OP_IsNull, 1, cont); + } if( !omitTable ){ sqlite3VdbeAddOp(v, OP_IdxRowid, iIdxCur, 0); sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); @@ -2312,7 +2314,7 @@ WhereInfo *sqlite3WhereBegin( /* Generate a single key that will be used to both start and terminate ** the search */ - buildIndexProbe(v, nEq, 0, brk, pIdx); + buildIndexProbe(v, nEq, pIdx); sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0); /* Generate code (1) to move to the first matching element of the table. @@ -2333,8 +2335,6 @@ WhereInfo *sqlite3WhereBegin( sqlite3VdbeOp3(v, OP_IdxGE, iIdxCur, brk, "+", P3_STATIC); pLevel->op = OP_Next; } - sqlite3VdbeAddOp(v, OP_RowKey, iIdxCur, 0); - sqlite3VdbeAddOp(v, OP_IdxIsNull, nEq, cont); if( !omitTable ){ sqlite3VdbeAddOp(v, OP_IdxRowid, iIdxCur, 0); sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);