]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Pull in recent trunk changes. Fix the ORDER BY optimizer so that it is better
authordrh <drh@noemail.net>
Mon, 3 Jun 2013 20:46:35 +0000 (20:46 +0000)
committerdrh <drh@noemail.net>
Mon, 3 Jun 2013 20:46:35 +0000 (20:46 +0000)
able to deal with COLLATE clauses.  Clean up ambiguities in the descidx1.test
script.

FossilOrigin-Name: 6bc71dfcf0ef757c5c2b426dd8fddc1e5ae0f598

1  2 
manifest
manifest.uuid
src/where.c
test/descidx1.test

diff --cc manifest
index 0a8380851591d01e1f4dd789ffb8515e3aaeae68,4c2a451fd1aaf36d75aa0d623528ec43af6a33c1..dba927beb09e3b3d4c73c8fd5ac1830ed4001773
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Do\snot\suse\san\sindex\sfullscan\sfor\san\sUPDATE\sor\sDELETE\sor\sif\sdisabled\sby\nsqlite3_test_control()\sor\ssqlite3_config().
- D 2013-06-03T19:17:40.684
 -C Fix\sa\stypo\sin\sa\scollating\sfunction\sinside\sthe\se_reindex.test\sscript.
 -D 2013-06-03T20:39:15.752
++C Pull\sin\srecent\strunk\schanges.\s\sFix\sthe\sORDER\sBY\soptimizer\sso\sthat\sit\sis\sbetter\nable\sto\sdeal\swith\sCOLLATE\sclauses.\s\sClean\sup\sambiguities\sin\sthe\sdescidx1.test\nscript.
++D 2013-06-03T20:46:35.011
  F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
  F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
  F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@@ -224,8 -224,8 +224,8 @@@ F src/sqliteInt.h 3ddccdf8ef912119da269
  F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
  F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
  F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
- F src/tclsqlite.c 2ecec9937e69bc17560ad886da35195daa7261b8
+ F src/tclsqlite.c b8835978e853a89bf58de88acc943a5ca94d752e
 -F src/test1.c 6d2a340eea1d866bf7059894491652a69a7ee802
 +F src/test1.c 5f9837aee1a0708ab2dbf67ce0b9c18e658cd36b
  F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35
  F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
  F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
@@@ -289,7 -289,7 +289,7 @@@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb
  F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d
  F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
  F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73
- F src/where.c 6226d9912f494a92b51b06c58b5c0eed9b7eb84e
 -F src/where.c 5c4cbc1e5205d8d534c483fa4495c81513b45dea
++F src/where.c 0082ef59948fc8c28b35226f44e1718cb1b0300a
  F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
  F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
  F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
@@@ -414,7 -414,7 +414,7 @@@ F test/default.test 6faf23ccb3001149243
  F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
  F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
  F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
--F test/descidx1.test 533dcbda614b0463b0ea029527fd27e5a9ab2d66
++F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
  F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
  F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
  F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
@@@ -1093,7 -1093,7 +1093,7 @@@ F tool/vdbe-compress.tcl f12c884766bd14
  F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
  F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
  F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
- P dcbbcb2e6c85780276cb3a392549f70e21e94408
- R 5e83ed1a81fa16225d238fe289659759
 -P 3bd5ad095b23102dd3379cb62997cbf23cc67b7a
 -R 0bab77d0f95310ae1c21cfea10915144
++P fabb21854e662b1d8e5631e79f828d5322ceb595 4d74fccf02134a998a84097b021ba9d501e34ff0
++R b746dc544342dfc07463de0423275bdc
  U drh
- Z 8c74536a41fa8c42d526bd323c460f6c
 -Z 1023ee14390bd42e471d5323a67fa234
++Z 5182eac11ae0820ee1af6bccc6719c26
diff --cc manifest.uuid
index c94f0e13710f78572897c4649122535bad900d85,e3db3db77490ca78f77eb30402345c715622c343..1e5db89b771205191301a7edd950317653246b33
@@@ -1,1 -1,1 +1,1 @@@
- fabb21854e662b1d8e5631e79f828d5322ceb595
 -4d74fccf02134a998a84097b021ba9d501e34ff0
++6bc71dfcf0ef757c5c2b426dd8fddc1e5ae0f598
diff --cc src/where.c
index ee604f07132e22e5937c944c8c31845ac57631d5,e614f4a6d864e70ec4328d0bdb254e3ac9f0d287..57dc7e8aef3bd5916d425c2b5e1447a0eb364586
@@@ -3784,591 -4938,21 +3784,591 @@@ static void whereLoopDelete(sqlite3 *db
  */
  static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
    if( ALWAYS(pWInfo) ){
 -    int i;
 -    for(i=0; i<pWInfo->nLevel; i++){
 -      sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo;
 -      if( pInfo ){
 -        /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */
 -        if( pInfo->needToFreeIdxStr ){
 -          sqlite3_free(pInfo->idxStr);
 +    whereClauseClear(pWInfo->pWC);
 +    while( pWInfo->pLoops ){
 +      WhereLoop *p = pWInfo->pLoops;
 +      pWInfo->pLoops = p->pNextLoop;
 +      whereLoopDelete(db, p);
 +    }
 +    sqlite3DbFree(db, pWInfo);
 +  }
 +}
 +
 +/*
 +** Insert or replace a WhereLoop entry using the template supplied.
 +**
 +** An existing WhereLoop entry might be overwritten if the new template
 +** is better and has fewer dependencies.  Or the template will be ignored
 +** and no insert will occur if an existing WhereLoop is faster and has
 +** fewer dependencies than the template.  Otherwise a new WhereLoop is
 +** added based on the template.
 +**
 +** If pBuilder->pBest is not NULL then we only care about the very
 +** best template and that template should be stored in pBuilder->pBest.
 +** If pBuilder->pBest is NULL then a list of the best templates are stored
 +** in pBuilder->pWInfo->pLoops.
 +**
 +** When accumulating multiple loops (when pBuilder->pBest is NULL) we
 +** still might overwrite similar loops with the new template if the
 +** template is better.  Loops may be overwritten if the following 
 +** conditions are met:
 +**
 +**    (1)  They have the same iTab.
 +**    (2)  They have the same iSortIdx.
 +**    (3)  The template has same or fewer dependencies than the current loop
 +**    (4)  The template has the same or lower cost than the current loop
 +**    (5)  The template uses more terms of the same index but has no additional
 +**         dependencies          
 +*/
 +static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
 +  WhereLoop **ppPrev, *p, *pNext = 0, *pToFree = 0;
 +  WhereTerm **paTerm = 0;
 +  sqlite3 *db = pBuilder->db;
 +  WhereInfo *pWInfo = pBuilder->pWInfo;
 +
 +  /* If pBuilder->pBest is defined, then only keep track of the single
 +  ** best WhereLoop.  pBuilder->pBest->maskSelf==0 indicates that no
 +  ** prior WhereLoops have been evaluated and that the current pTemplate
 +  ** is therefore the first and hence the best and should be retained.
 +  */
 +  if( (p = pBuilder->pBest)!=0 ){
 +    if( p->maskSelf!=0 ){
 +      if( p->rRun+p->rSetup < pTemplate->rRun+pTemplate->rSetup ){
 +        goto whereLoopInsert_noop;
 +      }
 +      if( p->rRun+p->rSetup == pTemplate->rRun+pTemplate->rSetup
 +       && p->prereq <= pTemplate->prereq ){
 +        goto whereLoopInsert_noop;
 +      }
 +    }
 +    *p = *pTemplate;
 +    p->aTerm = 0;
 +    p->u.vtab.needFree = 0;
 +#if WHERETRACE_ENABLED
 +    if( sqlite3WhereTrace & 0x8 ){
 +      sqlite3DebugPrintf("ins-best: ");
 +      whereLoopPrint(pTemplate, pBuilder->pTabList);
 +    }
 +#endif
 +    return SQLITE_OK;
 +  }
 +
 +  /* Search for an existing WhereLoop to overwrite, or which takes
 +  ** priority over pTemplate.
 +  */
 +  for(ppPrev=&pWInfo->pLoops, p=*ppPrev; p; ppPrev=&p->pNextLoop, p=*ppPrev){
 +    if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ) continue;
 +    if( p->nTerm<pTemplate->nTerm
 +     && (p->wsFlags & WHERE_INDEXED)!=0
 +     && (pTemplate->wsFlags & WHERE_INDEXED)!=0
 +     && p->u.btree.pIndex==pTemplate->u.btree.pIndex
 +     && p->prereq==pTemplate->prereq
 +    ){
 +      /* Overwrite an existing WhereLoop with an similar one that uses
 +      ** more terms of the index */
 +      pNext = p->pNextLoop;
 +      whereLoopClear(db, p);
 +      break;
 +    }
 +    if( (p->prereq & pTemplate->prereq)==p->prereq
 +     && p->rSetup<=pTemplate->rSetup
 +     && p->rRun<=pTemplate->rRun
 +    ){
 +      /* Already holding an equal or better WhereLoop.
 +      ** Return without changing or adding anything */
 +      goto whereLoopInsert_noop;
 +    }
 +    if( (p->prereq & pTemplate->prereq)==pTemplate->prereq
 +     && p->rSetup>=pTemplate->rSetup
 +     && p->rRun>=pTemplate->rRun
 +    ){
 +      /* Overwrite an existing WhereLoop with a better one */
 +      pNext = p->pNextLoop;
 +      whereLoopClear(db, p);
 +      break;
 +    }
 +  }
 +
 +  /* If we reach this point it means that either p[] should be overwritten
 +  ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new
 +  ** WhereLoop and insert it.
 +  */
 +#if WHERETRACE_ENABLED
 +  if( sqlite3WhereTrace & 0x8 ){
 +    if( p!=0 ){
 +      sqlite3DebugPrintf("ins-del:  ");
 +      whereLoopPrint(p, pBuilder->pTabList);
 +    }
 +    sqlite3DebugPrintf("ins-new:  ");
 +    whereLoopPrint(pTemplate, pBuilder->pTabList);
 +  }
 +#endif
 +  if( p==0 ){
 +    p = pToFree = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
 +    if( p==0 ) return SQLITE_NOMEM;
 +  }
 +  if( pTemplate->nTerm ){
 +    paTerm = sqlite3DbMallocRaw(db, pTemplate->nTerm*sizeof(p->aTerm[0]));
 +    if( paTerm==0 ){
 +      sqlite3DbFree(db, pToFree);
 +      return SQLITE_NOMEM;
 +    }
 +  }
 +  *p = *pTemplate;
 +  p->pNextLoop = pNext;
 +  *ppPrev = p;
 +  p->aTerm = paTerm;
 +  if( p->nTerm ){
 +    memcpy(p->aTerm, pTemplate->aTerm, p->nTerm*sizeof(p->aTerm[0]));
 +  }
 +  if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
 +    Index *pIndex = p->u.btree.pIndex;
 +    if( pIndex && pIndex->tnum==0 ){
 +      p->u.btree.pIndex = 0;
 +    }
 +  }else{
 +    pTemplate->u.vtab.needFree = 0;
 +  }
 +  return SQLITE_OK;
 +
 +  /* Jump here if the insert is a no-op */
 +whereLoopInsert_noop:
 +#if WHERETRACE_ENABLED
 +  if( sqlite3WhereTrace & 0x8 ){
 +    sqlite3DebugPrintf("ins-noop: ");
 +    whereLoopPrint(pTemplate, pBuilder->pTabList);
 +  }
 +#endif
 +  return SQLITE_OK;  
 +}
 +
 +/*
 +** We have so far matched pBuilder->pNew->u.btree.nEq terms of the index pIndex.
 +** Try to match one more.
 +**
 +** If pProbe->tnum==0, that means pIndex is a fake index used for the
 +** INTEGER PRIMARY KEY.
 +*/
 +static int whereLoopAddBtreeIndex(
 +  WhereLoopBuilder *pBuilder,     /* The WhereLoop factory */
 +  struct SrcList_item *pSrc,      /* FROM clause term being analyzed */
 +  Index *pProbe,                  /* An index on pSrc */
 +  int nInMul                      /* Number of iterations due to IN */
 +){
 +  sqlite3 *db;                    /* Database connection malloc context */
 +  WhereLoop *pNew;                /* Template WhereLoop under construction */
 +  WhereTerm *pTerm;               /* A WhereTerm under consideration */
 +  int opMask;                     /* Valid operators for constraints */
 +  WhereScan scan;                 /* Iterator for WHERE terms */
 +  WhereLoop savedLoop;            /* Saved original content of pNew[] */
 +  int iCol;                       /* Index of the column in the table */
 +  int rc = SQLITE_OK;             /* Return code */
 +  tRowcnt iRowEst;                /* Estimated index selectivity */
 +  double rLogSize;                /* Logarithm of table size */
 +  WhereTerm *pTop, *pBtm;         /* Top and bottom range constraints */
 +
 +  db = pBuilder->db;
 +  pNew = pBuilder->pNew;
 +  if( db->mallocFailed ) return SQLITE_NOMEM;
 +
 +  assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
 +  assert( pNew->u.btree.nEq<=pProbe->nColumn );
 +  assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
 +  if( pNew->wsFlags & WHERE_BTM_LIMIT ){
 +    opMask = WO_LT|WO_LE;
 +  }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){
 +    opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE;
 +  }else{
 +    opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE;
 +  }
 +  if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
 +
 +  if( pNew->u.btree.nEq < pProbe->nColumn ){
 +    iCol = pProbe->aiColumn[pNew->u.btree.nEq];
 +    iRowEst = pProbe->aiRowEst[pNew->u.btree.nEq+1];
 +  }else{
 +    iCol = -1;
 +    iRowEst = 1;
 +  }
 +  pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
 +                        opMask, pProbe);
 +  savedLoop = *pNew;
 +  pNew->rSetup = (double)0;
 +  rLogSize = estLog(pProbe->aiRowEst[0]);
 +  for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
 +    int nIn = 1;
 +    if( pTerm->prereqRight & pNew->maskSelf ) continue;
 +    pNew->wsFlags = savedLoop.wsFlags;
 +    pNew->u.btree.nEq = savedLoop.u.btree.nEq;
 +    pNew->nTerm = savedLoop.nTerm;
 +    if( pNew->nTerm>=pBuilder->mxTerm ) break; /* Repeated column in index */
 +    pNew->aTerm[pNew->nTerm++] = pTerm;
 +    pNew->prereq = (savedLoop.prereq | pTerm->prereqRight) & ~pNew->maskSelf;
 +    if( pTerm->eOperator & WO_IN ){
 +      Expr *pExpr = pTerm->pExpr;
 +      pNew->wsFlags |= WHERE_COLUMN_IN;
 +      if( ExprHasProperty(pExpr, EP_xIsSelect) ){
 +        /* "x IN (SELECT ...)":  Assume the SELECT returns 25 rows */
 +        nIn = 25;
 +      }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
 +        /* "x IN (value, value, ...)" */
 +        nIn = pExpr->x.pList->nExpr;
 +      }
 +      pNew->u.btree.nEq++;
 +      pNew->nOut = (double)iRowEst * nInMul * nIn;
 +    }else if( pTerm->eOperator & (WO_EQ) ){
 +      pNew->wsFlags |= WHERE_COLUMN_EQ;
 +      if( iCol<0 
 +       || (pProbe->onError==OE_Abort && nInMul==1
 +           && pNew->u.btree.nEq==pProbe->nColumn-1)
 +      ){
 +        pNew->wsFlags |= WHERE_UNIQUE;
 +      }
 +      pNew->u.btree.nEq++;
 +      pNew->nOut = (double)iRowEst * nInMul;
 +    }else if( pTerm->eOperator & (WO_ISNULL) ){
 +      pNew->wsFlags |= WHERE_COLUMN_NULL;
 +      pNew->u.btree.nEq++;
 +      pNew->nOut = (double)iRowEst * nInMul;
 +    }else if( pTerm->eOperator & (WO_GT|WO_GE) ){
 +      pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
 +      pBtm = pTerm;
 +      pTop = 0;
 +    }else if( pTerm->eOperator & (WO_LT|WO_LE) ){
 +      pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
 +      pTop = pTerm;
 +      pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
 +                     pNew->aTerm[pNew->nTerm-2] : 0;
 +    }
 +    pNew->rRun = rLogSize*nIn;  /* Cost for nIn binary searches */
 +    if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
 +      /* Adjust nOut and rRun for STAT3 range values */
 +      double rDiv;
 +      whereRangeScanEst(pBuilder->pParse, pProbe, pNew->u.btree.nEq,
 +                        pBtm, pTop, &rDiv);
 +      pNew->nOut = savedLoop.nOut/rDiv;
 +    }
 +#ifdef SQLITE_ENABLE_STAT3
 +    if( pNew->u.btree.nEq==1 && pProbe->nSample ){
 +      if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){
 +        rc = whereEqualScanEst(pBuilder->pParse, pProbe, pTerm->pExpr->pRight,
 +                               &pNew->nOut);
 +      }else if( (pTerm->eOperator & WO_IN)
 +             &&  !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)  ){
 +        rc = whereInScanEst(pBuilder->pParse, pProbe, pTerm->pExpr->x.pList,
 +                             &pNew->nOut);
 +
 +      }
 +    }
 +#endif
 +    if( pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK) ){
 +      pNew->rRun += pNew->nOut;  /* Unit step cost to reach each row */
 +    }else{
 +      /* Each row involves a step of the index, then a binary search of
 +      ** the main table */
 +      pNew->rRun += pNew->nOut*(1 + rLogSize);
 +    }
 +    /* TBD: Adjust nOut for additional constraints */
 +    rc = whereLoopInsert(pBuilder, pNew);
 +    if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
 +     && pNew->u.btree.nEq<=pProbe->nColumn
 +     && pProbe->zName!=0
 +    ){
 +      whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul*nIn);
 +    }
 +  }
 +  *pNew = savedLoop;
 +  return rc;
 +}
 +
 +/*
 +** Return True if it is possible that pIndex might be useful in
 +** implementing the ORDER BY clause in pBuilder.
 +**
 +** Return False if pBuilder does not contain an ORDER BY clause or
 +** if there is no way for pIndex to be useful in implementing that
 +** ORDER BY clause.
 +*/
 +static int indexMightHelpWithOrderBy(
 +  WhereLoopBuilder *pBuilder,
 +  Index *pIndex,
 +  int iCursor
 +){
 +  ExprList *pOB;
 +  int iCol;
 +  int ii;
 +
 +  if( (pOB = pBuilder->pOrderBy)==0 ) return 0;
 +  iCol = pIndex->aiColumn[0];
 +  for(ii=0; ii<pOB->nExpr; ii++){
-     Expr *pExpr = pOB->a[ii].pExpr;
++    Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
 +    if( pExpr->op!=TK_COLUMN ) return 0;
 +    if( pExpr->iTable==iCursor ){
 +      if( pExpr->iColumn==iCol ) return 1;
 +      return 0;
 +    }
 +  }
 +  return 0;
 +}
 +
 +/*
 +** Add all WhereLoop objects a single table of the join were the table
 +** is idenfied by pBuilder->pNew->iTab.  That table is guaranteed to be
 +** a b-tree table, not a virtual table.
 +*/
 +static int whereLoopAddBtree(
 +  WhereLoopBuilder *pBuilder, /* WHERE clause information */
 +  Bitmask mExtra              /* Extra prerequesites for using this table */
 +){
 +  Index *pProbe;              /* An index we are evaluating */
 +  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 */
 +  struct SrcList_item *pSrc;  /* The FROM clause btree term to add */
 +  WhereLoop *pNew;            /* Template WhereLoop object */
 +  int rc = SQLITE_OK;         /* Return code */
 +  int iSortIdx = 1;           /* Index number */
 +  int b;                      /* A boolean value */
 +  double rSize;               /* number of rows in the table */
 +  double rLogSize;            /* Logarithm of the number of rows in the table */
 +  
 +  pNew = pBuilder->pNew;
 +  pSrc = pBuilder->pTabList->a + pNew->iTab;
 +  assert( !IsVirtual(pSrc->pTab) );
 +
 +  if( pSrc->pIndex ){
 +    /* An INDEXED BY clause specifies a particular index to use */
 +    pProbe = pSrc->pIndex;
 +  }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;
 +  }
 +  rSize = (double)pSrc->pTab->nRowEst;
 +  rLogSize = estLog(rSize);
 +
 +  /* Automatic indexes */
 +  if( !pBuilder->pBest
 +   && (pBuilder->pParse->db->flags & SQLITE_AutoIndex)!=0 
 +   && !pSrc->viaCoroutine
 +   && !pSrc->notIndexed
 +   && !pSrc->isCorrelated
 +  ){
 +    /* Generate auto-index WhereLoops */
 +    WhereClause *pWC = pBuilder->pWC;
 +    WhereTerm *pTerm;
 +    WhereTerm *pWCEnd = pWC->a + pWC->nTerm;
 +    for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){
 +      if( pTerm->prereqRight & pNew->maskSelf ) continue;
 +      if( termCanDriveIndex(pTerm, pSrc, 0) ){
 +        pNew->u.btree.nEq = 1;
 +        pNew->u.btree.pIndex = 0;
 +        pNew->nTerm = 1;
 +        pNew->aTerm[0] = pTerm;
 +        pNew->rSetup = 20*rLogSize*pSrc->pTab->nRowEst;
 +        pNew->nOut = (double)10;
 +        pNew->rRun = rLogSize + pNew->nOut;
 +        pNew->wsFlags = WHERE_TEMP_INDEX;
 +        pNew->prereq = mExtra | pTerm->prereqRight;
 +        rc = whereLoopInsert(pBuilder, pNew);
 +      }
 +    }
 +  }
 +
 +  /* Loop over all indices
 +  */
 +  for(; rc==SQLITE_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){
 +    pNew->u.btree.nEq = 0;
 +    pNew->nTerm = 0;
 +    pNew->iSortIdx = 0;
 +    pNew->rSetup = (double)0;
 +    pNew->prereq = mExtra;
 +    pNew->u.btree.pIndex = pProbe;
 +    b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor);
 +    if( pProbe->tnum<=0 ){
 +      /* Integer primary key index */
 +      pNew->wsFlags = WHERE_IPK;
 +
 +      /* Full table scan */
 +      pNew->iSortIdx = b ? iSortIdx : 0;
 +      pNew->nOut = rSize;
 +      pNew->rRun = (rSize + rLogSize)*(3+b); /* 4x penalty for a full-scan */
 +      rc = whereLoopInsert(pBuilder, pNew);
 +      if( rc ) break;
 +    }else{
 +      Bitmask m = pSrc->colUsed;
 +      int j;
 +      for(j=pProbe->nColumn-1; j>=0; j--){
 +        int x = pProbe->aiColumn[j];
 +        if( x<BMS-1 ){
 +          m &= ~(((Bitmask)1)<<x);
          }
 -        sqlite3DbFree(db, pInfo);
        }
 -      if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){
 -        Index *pIdx = pWInfo->a[i].plan.u.pIdx;
 -        if( pIdx ){
 -          sqlite3DbFree(db, pIdx->zColAff);
 -          sqlite3DbFree(db, pIdx);
 +      pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
 +
 +      /* Full scan via index */
 +      if( (m==0 || b)
 +       && pProbe->bUnordered==0
 +       && (pBuilder->pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
 +       && sqlite3GlobalConfig.bUseCis
 +       && OptimizationEnabled(pBuilder->pParse->db, SQLITE_CoverIdxScan)
 +      ){
 +        pNew->iSortIdx = b ? iSortIdx : 0;
 +        pNew->nOut = rSize;
 +        pNew->rRun = (m==0) ? (rSize + rLogSize)*(1+b) : (rSize*rLogSize);
 +        rc = whereLoopInsert(pBuilder, pNew);
 +        if( rc ) break;
 +      }
 +    }
 +    rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 1);
 +
 +    /* If there was an INDEXED BY clause, then only that one index is
 +    ** considered. */
 +    if( pSrc->pIndex ) break;
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Add all WhereLoop objects for a table of the join identified by
 +** pBuilder->pNew->iTab.  That table is guaranteed to be a virtual table.
 +*/
 +static int whereLoopAddVirtual(
 +  WhereLoopBuilder *pBuilder,  /* WHERE clause information */
 +  Bitmask mExtra               /* Extra prerequesites for using this table */
 +){
 +  Parse *pParse;               /* The parsing context */
 +  WhereClause *pWC;            /* The WHERE clause */
 +  struct SrcList_item *pSrc;   /* The FROM clause term to search */
 +  Table *pTab;
 +  sqlite3 *db;
 +  sqlite3_index_info *pIdxInfo;
 +  struct sqlite3_index_constraint *pIdxCons;
 +  struct sqlite3_index_constraint_usage *pUsage;
 +  WhereTerm *pTerm;
 +  int i, j;
 +  int iTerm, mxTerm;
 +  int seenIn = 0;              /* True if an IN operator is seen */
 +  int seenVar = 0;             /* True if a non-constant constraint is seen */
 +  int iPhase;                  /* 0: const w/o IN, 1: const, 2: no IN,  2: IN */
 +  WhereLoop *pNew;
 +  int rc = SQLITE_OK;
 +
 +  pParse = pBuilder->pParse;
 +  db = pParse->db;
 +  pWC = pBuilder->pWC;
 +  pNew = pBuilder->pNew;
 +  pSrc = &pBuilder->pTabList->a[pNew->iTab];
 +  pTab = pSrc->pTab;
 +  assert( IsVirtual(pTab) );
 +  pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy);
 +  if( pIdxInfo==0 ) return SQLITE_NOMEM;
 +  pNew->prereq = 0;
 +  pNew->rSetup = 0;
 +  pNew->wsFlags = WHERE_VIRTUALTABLE;
 +  pNew->nTerm = 0;
 +  pNew->u.vtab.needFree = 0;
 +  pUsage = pIdxInfo->aConstraintUsage;
 +
 +  for(iPhase=0; iPhase<=3; iPhase++){
 +    if( !seenIn && (iPhase&1)!=0 ){
 +      iPhase++;
 +      if( iPhase>3 ) break;
 +    }
 +    if( !seenVar && iPhase>1 ) break;
 +    pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
 +    for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
 +      j = pIdxCons->iTermOffset;
 +      pTerm = &pWC->a[j];
 +      switch( iPhase ){
 +        case 0:    /* Constants without IN operator */
 +          pIdxCons->usable = 0;
 +          if( (pTerm->eOperator & WO_IN)!=0 ){
 +            seenIn = 1;
 +          }else if( pTerm->prereqRight!=0 ){
 +            seenVar = 1;
 +          }else{
 +            pIdxCons->usable = 1;
 +          }
 +          break;
 +        case 1:    /* Constants with IN operators */
 +          assert( seenIn );
 +          pIdxCons->usable = (pTerm->prereqRight==0);
 +          break;
 +        case 2:    /* Variables without IN */
 +          assert( seenVar );
 +          pIdxCons->usable = (pTerm->eOperator & WO_IN)==0;
 +          break;
 +        default:   /* Variables with IN */
 +          assert( seenVar && seenIn );
 +          pIdxCons->usable = 1;
 +          break;
 +      }
 +    }
 +    memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
 +    if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
 +    pIdxInfo->idxStr = 0;
 +    pIdxInfo->idxNum = 0;
 +    pIdxInfo->needToFreeIdxStr = 0;
 +    pIdxInfo->orderByConsumed = 0;
 +    /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
 +    pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
 +    rc = vtabBestIndex(pParse, pTab, pIdxInfo);
 +    if( rc ) goto whereLoopAddVtab_exit;
 +    pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
 +    pNew->prereq = 0;
 +    mxTerm = -1;
 +    for(i=0; i<pBuilder->mxTerm; i++) pNew->aTerm[i] = 0;
 +    pNew->u.vtab.omitMask = 0;
 +    for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
 +      if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
 +        if( iTerm>=pBuilder->mxTerm ) break;
 +        j = pIdxCons->iTermOffset;
 +        if( iTerm>=pIdxInfo->nConstraint
 +         || j<0
 +         || j>=pWC->nTerm
 +         || pNew->aTerm[iTerm]!=0
 +        ){
 +          rc = SQLITE_ERROR;
 +          sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName);
 +          goto whereLoopAddVtab_exit;
 +        }
 +        pTerm = &pWC->a[j];
 +        pNew->prereq |= pTerm->prereqRight;
 +        pNew->aTerm[iTerm] = pTerm;
 +        if( iTerm>mxTerm ) mxTerm = iTerm;
 +        if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
 +        if( (pTerm->eOperator & WO_IN)!=0 ){
 +          if( pUsage[i].omit==0 ){
 +            /* Do not attempt to use an IN constraint if the virtual table
 +            ** says that the equivalent EQ constraint cannot be safely omitted.
 +            ** If we do attempt to use such a constraint, some rows might be
 +            ** repeated in the output. */
 +            break;
 +          }
 +          /* A virtual table that is constrained by an IN clause may not
 +          ** consume the ORDER BY clause because (1) the order of IN terms
 +          ** is not necessarily related to the order of output terms and
 +          ** (2) Multiple outputs from a single IN value will not merge
 +          ** together.  */
 +          pIdxInfo->orderByConsumed = 0;
          }
        }
      }
index c7fab340a518f4d7f492b4bda1b74245e69cb3a0,c7fab340a518f4d7f492b4bda1b74245e69cb3a0..a223664ff32e31c46f0076bd15bf532a0317a95f
@@@ -197,12 -197,12 +197,12 @@@ ifcapable bloblit 
    } {1.0 2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0}
    do_test descidx1-4.3 {
      execsql {
--      SELECT d FROM t2 WHERE a>=2;
++      SELECT d FROM t2 WHERE a>=2 ORDER BY a;
      }
    } {2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0}
    do_test descidx1-4.4 {
      execsql {
--      SELECT d FROM t2 WHERE a>2;
++      SELECT d FROM t2 WHERE a>2 ORDER BY a;
      }
    } {3.0 4.0 5.0 6.0}
    do_test descidx1-4.5 {