/ Check-in [9ea37422]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:If SQLITE_ENABLE_STMT_SCANSTATUS is defined, record the number of times each VDBE opcode is executed. Derive the values returned by sqlite3_stmt_scanstatus() from these records on demand.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | scanstatus
Files: files | file ages | folders
SHA1:9ea37422a8cc2fce51bb10508e5e90f40fd4b511
User & Date: dan 2014-11-01 20:38:06
Context
2014-11-01
21:00
Minor performance enhancements to SQLITE_ENABLE_STMT_SCANSTATUS code. check-in: f13d6ba8 user: dan tags: scanstatus
20:38
If SQLITE_ENABLE_STMT_SCANSTATUS is defined, record the number of times each VDBE opcode is executed. Derive the values returned by sqlite3_stmt_scanstatus() from these records on demand. check-in: 9ea37422 user: dan tags: scanstatus
18:08
Minor fixes and documentation improvements for sqlite3_stmt_scanstatus(). check-in: 8d8cc960 user: dan tags: scanstatus
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/vdbe.c.

   163    163   #define Deephemeralize(P) \
   164    164      if( ((P)->flags&MEM_Ephem)!=0 \
   165    165          && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}
   166    166   
   167    167   /* Return true if the cursor was opened using the OP_OpenSorter opcode. */
   168    168   #define isSorter(x) ((x)->pSorter!=0)
   169    169   
   170         -/*
   171         -** The first argument passed to the IncrementExplainCounter() macro must
   172         -** be a non-NULL pointer to an object of type VdbeCursor. The second 
   173         -** argument must be either "nLoop" or "nVisit" (without the double-quotes).
   174         -*/
   175         -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
   176         -# define IncrementExplainCounter(pCsr, counter) \
   177         -    if( (pCsr)->pExplain ) (pCsr)->pExplain->counter++
   178         -#else
   179         -# define IncrementExplainCounter(pCsr, counter)
   180         -#endif
   181         -
   182    170   /*
   183    171   ** Allocate VdbeCursor number iCur.  Return a pointer to it.  Return NULL
   184    172   ** if we run out of memory.
   185    173   */
   186    174   static VdbeCursor *allocateCursor(
   187    175     Vdbe *p,              /* The virtual machine */
   188    176     int iCur,             /* Index of the new VdbeCursor */
................................................................................
   616    604       assert( pc>=0 && pc<p->nOp );
   617    605       if( db->mallocFailed ) goto no_mem;
   618    606   #ifdef VDBE_PROFILE
   619    607       start = sqlite3Hwtime();
   620    608   #endif
   621    609       nVmStep++;
   622    610       pOp = &aOp[pc];
          611  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
          612  +    if( p->pFrame==0 ) p->anExec[pc]++;
          613  +#endif
   623    614   
   624    615       /* Only allow tracing if SQLITE_DEBUG is defined.
   625    616       */
   626    617   #ifdef SQLITE_DEBUG
   627    618       if( db->flags & SQLITE_VdbeTrace ){
   628    619         sqlite3VdbePrintOp(stdout, pc, pOp);
   629    620       }
................................................................................
  3564   3555     assert( pC->isOrdered );
  3565   3556     assert( pC->pCursor!=0 );
  3566   3557     oc = pOp->opcode;
  3567   3558     pC->nullRow = 0;
  3568   3559   #ifdef SQLITE_DEBUG
  3569   3560     pC->seekOp = pOp->opcode;
  3570   3561   #endif
  3571         -  IncrementExplainCounter(pC, nLoop);
  3572   3562     if( pC->isTable ){
  3573   3563       /* The input value in P3 might be of any type: integer, real, string,
  3574   3564       ** blob, or NULL.  But it needs to be an integer before we can do
  3575   3565       ** the seek, so convert it. */
  3576   3566       pIn3 = &aMem[pOp->p3];
  3577   3567       if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
  3578   3568         applyNumericAffinity(pIn3, 0);
................................................................................
  3673   3663         res = sqlite3BtreeEof(pC->pCursor);
  3674   3664       }
  3675   3665     }
  3676   3666     assert( pOp->p2>0 );
  3677   3667     VdbeBranchTaken(res!=0,2);
  3678   3668     if( res ){
  3679   3669       pc = pOp->p2 - 1;
  3680         -  }else{
  3681         -    IncrementExplainCounter(pC, nVisit);
  3682   3670     }
  3683   3671     break;
  3684   3672   }
  3685   3673   
  3686   3674   /* Opcode: Seek P1 P2 * * *
  3687   3675   ** Synopsis:  intkey=r[P2]
  3688   3676   **
................................................................................
  3885   3873     assert( pC->isTable );
  3886   3874     assert( pC->pseudoTableReg==0 );
  3887   3875     pCrsr = pC->pCursor;
  3888   3876     assert( pCrsr!=0 );
  3889   3877     res = 0;
  3890   3878     iKey = pIn3->u.i;
  3891   3879     rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
  3892         -  IncrementExplainCounter(pC, nLoop);
  3893   3880     pC->movetoTarget = iKey;  /* Used by OP_Delete */
  3894   3881     pC->nullRow = 0;
  3895   3882     pC->cacheStatus = CACHE_STALE;
  3896   3883     pC->deferredMoveto = 0;
  3897   3884     VdbeBranchTaken(res!=0,2);
  3898   3885     if( res!=0 ){
  3899   3886       pc = pOp->p2 - 1;
  3900         -  }else{
  3901         -    IncrementExplainCounter(pC, nVisit);
  3902   3887     }
  3903   3888     pC->seekResult = res;
  3904   3889     break;
  3905   3890   }
  3906   3891   
  3907   3892   /* Opcode: Sequence P1 P2 * * *
  3908   3893   ** Synopsis: r[P2]=cursor[P1].ctr++
................................................................................
  4463   4448     assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  4464   4449     pC = p->apCsr[pOp->p1];
  4465   4450     assert( pC!=0 );
  4466   4451     pCrsr = pC->pCursor;
  4467   4452     res = 0;
  4468   4453     assert( pCrsr!=0 );
  4469   4454     rc = sqlite3BtreeLast(pCrsr, &res);
  4470         -  IncrementExplainCounter(pC, nLoop);
  4471         -  if( res==0 ) IncrementExplainCounter(pC, nVisit);
  4472   4455     pC->nullRow = (u8)res;
  4473   4456     pC->deferredMoveto = 0;
  4474   4457     pC->cacheStatus = CACHE_STALE;
  4475   4458   #ifdef SQLITE_DEBUG
  4476   4459     pC->seekOp = OP_Last;
  4477   4460   #endif
  4478   4461     if( pOp->p2>0 ){
................................................................................
  4534   4517     }else{
  4535   4518       pCrsr = pC->pCursor;
  4536   4519       assert( pCrsr );
  4537   4520       rc = sqlite3BtreeFirst(pCrsr, &res);
  4538   4521       pC->deferredMoveto = 0;
  4539   4522       pC->cacheStatus = CACHE_STALE;
  4540   4523     }
  4541         -  IncrementExplainCounter(pC, nLoop);
  4542   4524     pC->nullRow = (u8)res;
  4543   4525     assert( pOp->p2>0 && pOp->p2<p->nOp );
  4544   4526     VdbeBranchTaken(res!=0,2);
  4545   4527     if( res ){
  4546   4528       pc = pOp->p2 - 1;
  4547         -  }else{
  4548         -    IncrementExplainCounter(pC, nVisit);
  4549   4529     }
  4550   4530     break;
  4551   4531   }
  4552   4532   
  4553   4533   /* Opcode: Next P1 P2 P3 P4 P5
  4554   4534   **
  4555   4535   ** Advance cursor P1 so that it points to the next key/data pair in its
................................................................................
  4652   4632          || pC->seekOp==OP_Last );
  4653   4633   
  4654   4634     rc = pOp->p4.xAdvance(pC->pCursor, &res);
  4655   4635   next_tail:
  4656   4636     pC->cacheStatus = CACHE_STALE;
  4657   4637     VdbeBranchTaken(res==0,2);
  4658   4638     if( res==0 ){
  4659         -    IncrementExplainCounter(pC, nVisit);
  4660   4639       pC->nullRow = 0;
  4661   4640       pc = pOp->p2 - 1;
  4662   4641       p->aCounter[pOp->p5]++;
  4663   4642   #ifdef SQLITE_TEST
  4664   4643       sqlite3_search_count++;
  4665   4644   #endif
  4666   4645     }else{
................................................................................
  6075   6054       sqlite3VtabImportErrmsg(p, pVtab);
  6076   6055       if( rc==SQLITE_OK ){
  6077   6056         res = pModule->xEof(pVtabCursor);
  6078   6057       }
  6079   6058       VdbeBranchTaken(res!=0,2);
  6080   6059       if( res ){
  6081   6060         pc = pOp->p2 - 1;
  6082         -    }else{
  6083         -      IncrementExplainCounter(pCur, nVisit);
  6084   6061       }
  6085         -    IncrementExplainCounter(pCur, nLoop);
  6086   6062     }
  6087   6063     pCur->nullRow = 0;
  6088   6064   
  6089   6065     break;
  6090   6066   }
  6091   6067   #endif /* SQLITE_OMIT_VIRTUALTABLE */
  6092   6068   
................................................................................
  6171   6147     if( rc==SQLITE_OK ){
  6172   6148       res = pModule->xEof(pCur->pVtabCursor);
  6173   6149     }
  6174   6150     VdbeBranchTaken(!res,2);
  6175   6151     if( !res ){
  6176   6152       /* If there is data, jump to P2 */
  6177   6153       pc = pOp->p2 - 1;
  6178         -    IncrementExplainCounter(pCur, nVisit);
  6179   6154     }
  6180   6155     goto check_for_interrupt;
  6181   6156   }
  6182   6157   #endif /* SQLITE_OMIT_VIRTUALTABLE */
  6183   6158   
  6184   6159   #ifndef SQLITE_OMIT_VIRTUALTABLE
  6185   6160   /* Opcode: VRename P1 * * P4 *
................................................................................
  6371   6346       sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
  6372   6347     }
  6373   6348   #endif /* SQLITE_DEBUG */
  6374   6349   #endif /* SQLITE_OMIT_TRACE */
  6375   6350     break;
  6376   6351   }
  6377   6352   
  6378         -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  6379         -case OP_Explain: {
  6380         -  ExplainArg *pArg;
  6381         -  VdbeCursor *pCur;
  6382         -  if( pOp->p4type==P4_EXPLAIN && (pArg = pOp->p4.pExplain)->iCsr>=0 ){
  6383         -    pArg = pOp->p4.pExplain;
  6384         -    pCur = p->apCsr[pArg->iCsr];
  6385         -    pCur->pExplain = pArg;
  6386         -  }
  6387         -  break;
  6388         -}
  6389         -#endif
  6390         -
  6391   6353   
  6392   6354   /* Opcode: Noop * * * * *
  6393   6355   **
  6394   6356   ** Do nothing.  This instruction is often useful as a jump
  6395   6357   ** destination.
  6396   6358   */
  6397   6359   /*

Changes to src/vdbe.h.

    21     21   
    22     22   /*
    23     23   ** A single VDBE is an opaque structure named "Vdbe".  Only routines
    24     24   ** in the source file sqliteVdbe.c are allowed to see the insides
    25     25   ** of this structure.
    26     26   */
    27     27   typedef struct Vdbe Vdbe;
    28         -typedef struct ExplainArg ExplainArg;
    29     28   
    30     29   /*
    31     30   ** The names of the following types declared in vdbeInt.h are required
    32     31   ** for the VdbeOp definition.
    33     32   */
    34     33   typedef struct Mem Mem;
    35     34   typedef struct SubProgram SubProgram;
................................................................................
    56     55       FuncDef *pFunc;        /* Used when p4type is P4_FUNCDEF */
    57     56       CollSeq *pColl;        /* Used when p4type is P4_COLLSEQ */
    58     57       Mem *pMem;             /* Used when p4type is P4_MEM */
    59     58       VTable *pVtab;         /* Used when p4type is P4_VTAB */
    60     59       KeyInfo *pKeyInfo;     /* Used when p4type is P4_KEYINFO */
    61     60       int *ai;               /* Used when p4type is P4_INTARRAY */
    62     61       SubProgram *pProgram;  /* Used when p4type is P4_SUBPROGRAM */
    63         -    ExplainArg *pExplain;  /* Used when p4type is P4_EXPLAIN */
    64     62       int (*xAdvance)(BtCursor *, int *);
    65     63     } p4;
    66     64   #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
    67     65     char *zComment;          /* Comment to improve readability */
    68     66   #endif
    69     67   #ifdef VDBE_PROFILE
    70     68     u32 cnt;                 /* Number of times this instruction was executed */
................................................................................
    97     95   struct VdbeOpList {
    98     96     u8 opcode;          /* What operation to perform */
    99     97     signed char p1;     /* First operand */
   100     98     signed char p2;     /* Second parameter (often the jump destination) */
   101     99     signed char p3;     /* Third parameter */
   102    100   };
   103    101   typedef struct VdbeOpList VdbeOpList;
   104         -
   105         -/*
   106         -** Structure used as the P4 parameter for OP_Explain opcodes.
   107         -*/
   108         -struct ExplainArg {
   109         -  int iCsr;                       /* Cursor number this applies to */
   110         -  i64 nLoop;                      /* Number of times loop has run */
   111         -  i64 nVisit;                     /* Total number of rows visited */
   112         -  i64 nEst;                       /* Estimated number of rows per scan */
   113         -  const char *zName;              /* Name of table/index being scanned */
   114         -  const char *zExplain;           /* EQP text for this loop */
   115         -};
   116         -
   117    102   
   118    103   /*
   119    104   ** Allowed values of VdbeOp.p4type
   120    105   */
   121    106   #define P4_NOTUSED    0   /* The P4 parameter is not used */
   122    107   #define P4_DYNAMIC  (-1)  /* Pointer to a string obtained from sqliteMalloc() */
   123    108   #define P4_STATIC   (-2)  /* Pointer to a static string */
................................................................................
   130    115   #define P4_MPRINTF  (-11) /* P4 is a string obtained from sqlite3_mprintf() */
   131    116   #define P4_REAL     (-12) /* P4 is a 64-bit floating point value */
   132    117   #define P4_INT64    (-13) /* P4 is a 64-bit signed integer */
   133    118   #define P4_INT32    (-14) /* P4 is a 32-bit signed integer */
   134    119   #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
   135    120   #define P4_SUBPROGRAM  (-18) /* P4 is a pointer to a SubProgram structure */
   136    121   #define P4_ADVANCE  (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
   137         -#define P4_EXPLAIN  (-20) /* P4 is a pointer to an instance of ExplainArg */
   138    122   
   139    123   /* Error message codes for OP_Halt */
   140    124   #define P5_ConstraintNotNull 1
   141    125   #define P5_ConstraintUnique  2
   142    126   #define P5_ConstraintCheck   3
   143    127   #define P5_ConstraintFK      4
   144    128   
................................................................................
   293    277   #else
   294    278   # define VdbeCoverage(v)
   295    279   # define VdbeCoverageIf(v,x)
   296    280   # define VdbeCoverageAlwaysTaken(v)
   297    281   # define VdbeCoverageNeverTaken(v)
   298    282   # define VDBE_OFFSET_LINENO(x) 0
   299    283   #endif
          284  +
          285  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
          286  +void sqlite3VdbeScanCounter(Vdbe*, int, int, int, i64, const char*);
          287  +#else
          288  +# define sqlite3VdbeScanCounter(a,b,c,d,e)
          289  +#endif
   300    290   
   301    291   #endif

Changes to src/vdbeInt.h.

    79     79     Bool isTable:1;       /* True if a table requiring integer keys */
    80     80     Bool isOrdered:1;     /* True if the underlying table is BTREE_UNORDERED */
    81     81     Pgno pgnoRoot;        /* Root page of the open btree cursor */
    82     82     sqlite3_vtab_cursor *pVtabCursor;  /* The cursor for a virtual table */
    83     83     i64 seqCount;         /* Sequence counter */
    84     84     i64 movetoTarget;     /* Argument to the deferred sqlite3BtreeMoveto() */
    85     85     VdbeSorter *pSorter;  /* Sorter object for OP_SorterOpen cursors */
    86         -  ExplainArg *pExplain; /* Object to store seek/visit counts (may be NULL) */
    87     86   
    88     87     /* Cached information about the header for the data record that the
    89     88     ** cursor is currently pointing to.  Only valid if cacheStatus matches
    90     89     ** Vdbe.cacheCtr.  Vdbe.cacheCtr will never take on the value of
    91     90     ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that
    92     91     ** the cache is out of date.
    93     92     **
................................................................................
   288    287     Vdbe *pVdbe;       /* Attach the explanation to this Vdbe */
   289    288     StrAccum str;      /* The string being accumulated */
   290    289     int nIndent;       /* Number of elements in aIndent */
   291    290     u16 aIndent[100];  /* Levels of indentation */
   292    291     char zBase[100];   /* Initial space */
   293    292   };
   294    293   
   295         -
   296    294   /* A bitfield type for use inside of structures.  Always follow with :N where
   297    295   ** N is the number of bits.
   298    296   */
   299    297   typedef unsigned bft;  /* Bit Field Type */
          298  +
          299  +typedef struct ScanCounter ScanCounter;
          300  +struct ScanCounter {
          301  +  int addrExplain;                /* OP_Explain for loop */
          302  +  int addrLoop;                   /* Address of "loops" counter */
          303  +  int addrVisit;                  /* Address of "rows visited" counter */
          304  +  i64 nEst;                       /* Estimated rows per loop */
          305  +  char *zName;                    /* Name of table or index */
          306  +};
   300    307   
   301    308   /*
   302    309   ** An instance of the virtual machine.  This structure contains the complete
   303    310   ** state of the virtual machine.
   304    311   **
   305    312   ** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
   306    313   ** is really a pointer to an instance of this structure.
................................................................................
   366    373     VdbeFrame *pDelFrame;   /* List of frame objects to free on VM reset */
   367    374     int nFrame;             /* Number of frames in pFrame list */
   368    375     u32 expmask;            /* Binding to these vars invalidates VM */
   369    376     SubProgram *pProgram;   /* Linked list of all sub-programs used by VM */
   370    377     int nOnceFlag;          /* Size of array aOnceFlag[] */
   371    378     u8 *aOnceFlag;          /* Flags for OP_Once */
   372    379     AuxData *pAuxData;      /* Linked list of auxdata allocations */
   373         -  ExplainArg **apExplain; /* Array of pointers to P4_EXPLAIN p4 values */
   374         -  int nExplain;           /* Number of entries in array apExplain */
          380  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
          381  +  i64 *anExec;            /* Number of times each op has been executed */
          382  +  int nScan;              /* Entries in aScan[] */
          383  +  ScanCounter *aScan;     /* Scan definitions for sqlite3_stmt_scanstatus() */
          384  +#endif
   375    385   };
   376    386   
   377    387   /*
   378    388   ** The following are allowed values for Vdbe.magic
   379    389   */
   380    390   #define VDBE_MAGIC_INIT     0x26bceaa5    /* Building a VDBE program */
   381    391   #define VDBE_MAGIC_RUN      0xbdf20da3    /* VDBE is ready to execute */

Changes to src/vdbeapi.c.

  1472   1472     }
  1473   1473   #endif
  1474   1474     v = pVdbe->aCounter[op];
  1475   1475     if( resetFlag ) pVdbe->aCounter[op] = 0;
  1476   1476     return (int)v;
  1477   1477   }
  1478   1478   
         1479  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  1479   1480   /*
  1480   1481   ** Return status data for a single loop within query pStmt.
  1481   1482   */
  1482   1483   int sqlite3_stmt_scanstatus(
  1483   1484     sqlite3_stmt *pStmt,
  1484   1485     int idx,                        /* Index of loop to report on */
  1485   1486     sqlite3_int64 *pnLoop,          /* OUT: Number of times loop was run */
  1486   1487     sqlite3_int64 *pnVisit,         /* OUT: Number of rows visited (all loops) */
  1487   1488     sqlite3_int64 *pnEst,           /* OUT: Number of rows estimated (per loop) */
  1488   1489     const char **pzName,            /* OUT: Object name (table or index) */
  1489   1490     const char **pzExplain          /* OUT: EQP string */
  1490   1491   ){
  1491   1492     Vdbe *p = (Vdbe*)pStmt;
  1492         -  ExplainArg *pExplain;
  1493         -  if( idx<0 || idx>=p->nExplain ) return 1;
  1494         -  pExplain = p->apExplain[idx];
  1495         -  if( pnLoop ) *pnLoop = pExplain->nLoop;
  1496         -  if( pnVisit ) *pnVisit = pExplain->nVisit;
  1497         -  if( pnEst ) *pnEst = pExplain->nEst;
  1498         -  if( *pzName ) *pzName = pExplain->zName;
  1499         -  if( *pzExplain ) *pzExplain = pExplain->zExplain;
         1493  +  ScanCounter *pScan;
         1494  +  if( idx<0 || idx>=p->nScan ) return 1;
         1495  +  pScan = &p->aScan[idx];
         1496  +  if( pnLoop ) *pnLoop = p->anExec[pScan->addrLoop];
         1497  +  if( pnVisit ) *pnVisit = p->anExec[pScan->addrVisit];
         1498  +  if( pnEst ) *pnEst = pScan->nEst;
         1499  +  if( *pzName ) *pzName = pScan->zName;
         1500  +  if( *pzExplain ){
         1501  +    if( pScan->addrExplain ){
         1502  +      *pzExplain = p->aOp[ pScan->addrExplain ].p4.z;
         1503  +    }else{
         1504  +      *pzExplain = 0;
         1505  +    }
         1506  +  }
  1500   1507     return 0;
  1501   1508   }
  1502   1509   
  1503   1510   /*
  1504   1511   ** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
  1505   1512   */
  1506   1513   void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
  1507   1514     Vdbe *p = (Vdbe*)pStmt;
  1508         -  int i;
  1509         -  for(i=0; i<p->nExplain; i++){
  1510         -    p->apExplain[i]->nLoop = 0;
  1511         -    p->apExplain[i]->nVisit = 0;
  1512         -  }
         1515  +  memset(p->anExec, 0, p->nOp * sizeof(i64));
  1513   1516   }
         1517  +#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
  1514   1518   

Changes to src/vdbeaux.c.

   592    592         }
   593    593   #endif
   594    594       }
   595    595       p->nOp += nOp;
   596    596     }
   597    597     return addr;
   598    598   }
          599  +
          600  +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
          601  +/*
          602  +** Add an entry to the array of counters managed by sqlite3_stmt_scanstatus().
          603  +*/
          604  +void sqlite3VdbeScanCounter(
          605  +  Vdbe *p,                        /* VM to add scanstatus() to */
          606  +  int addrExplain,                /* Address of OP_Explain (or 0) */
          607  +  int addrLoop,                   /* Address of loop counter */ 
          608  +  int addrVisit,                  /* Address of rows visited counter */
          609  +  i64 nEst,                       /* Estimated number of rows */
          610  +  const char *zName               /* Name of table or index being scanned */
          611  +){
          612  +  int nByte = (p->nScan+1) * sizeof(ScanCounter);
          613  +  ScanCounter *aNew;
          614  +  aNew = (ScanCounter*)sqlite3DbRealloc(p->db, p->aScan, nByte);
          615  +  if( aNew ){
          616  +    ScanCounter *pNew = &aNew[p->nScan++];
          617  +    pNew->addrExplain = addrExplain;
          618  +    pNew->addrLoop = addrLoop;
          619  +    pNew->addrVisit = addrVisit;
          620  +    pNew->nEst = nEst;
          621  +    pNew->zName = sqlite3DbStrDup(p->db, zName);
          622  +    p->aScan = aNew;
          623  +  }
          624  +}
          625  +#endif
          626  +
   599    627   
   600    628   /*
   601    629   ** Change the value of the P1 operand for a specific instruction.
   602    630   ** This routine is useful when a large program is loaded from a
   603    631   ** static array using sqlite3VdbeAddOpList but we want to make a
   604    632   ** few minor changes to the program.
   605    633   */
................................................................................
   668    696   /*
   669    697   ** Delete a P4 value if necessary.
   670    698   */
   671    699   static void freeP4(sqlite3 *db, int p4type, void *p4){
   672    700     if( p4 ){
   673    701       assert( db );
   674    702       switch( p4type ){
   675         -      case P4_EXPLAIN:
   676    703         case P4_REAL:
   677    704         case P4_INT64:
   678    705         case P4_DYNAMIC:
   679    706         case P4_INTARRAY: {
   680    707           sqlite3DbFree(db, p4);
   681    708           break;
   682    709         }
................................................................................
   817    844       pOp->p4.p = (void*)zP4;
   818    845       pOp->p4type = P4_KEYINFO;
   819    846     }else if( n==P4_VTAB ){
   820    847       pOp->p4.p = (void*)zP4;
   821    848       pOp->p4type = P4_VTAB;
   822    849       sqlite3VtabLock((VTable *)zP4);
   823    850       assert( ((VTable *)zP4)->db==p->db );
   824         -  }else if( n==P4_EXPLAIN ){
   825         -    pOp->p4.p = (void*)zP4;
   826         -    pOp->p4type = P4_EXPLAIN;
   827         -    p->apExplain = (ExplainArg**)sqlite3DbReallocOrFree(
   828         -        p->db, p->apExplain, sizeof(ExplainArg*) * p->nExplain + 1
   829         -    );
   830         -    if( p->apExplain ) p->apExplain[p->nExplain++] = (ExplainArg*)zP4;
   831    851     }else if( n<0 ){
   832    852       pOp->p4.p = (void*)zP4;
   833    853       pOp->p4type = (signed char)n;
   834    854     }else{
   835    855       if( n==0 ) n = sqlite3Strlen30(zP4);
   836    856       pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
   837    857       pOp->p4type = P4_DYNAMIC;
................................................................................
  1107   1127         sqlite3_snprintf(nTemp, zTemp, "program");
  1108   1128         break;
  1109   1129       }
  1110   1130       case P4_ADVANCE: {
  1111   1131         zTemp[0] = 0;
  1112   1132         break;
  1113   1133       }
  1114         -    case P4_EXPLAIN: {
  1115         -      zP4 = (char*)pOp->p4.pExplain->zExplain;
  1116         -      break;
  1117         -    }
  1118   1134       default: {
  1119   1135         zP4 = pOp->p4.z;
  1120   1136         if( zP4==0 ){
  1121   1137           zP4 = zTemp;
  1122   1138           zTemp[0] = 0;
  1123   1139         }
  1124   1140       }
................................................................................
  1709   1725       p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
  1710   1726       if( nByte ){
  1711   1727         p->pFree = sqlite3DbMallocZero(db, nByte);
  1712   1728       }
  1713   1729       zCsr = p->pFree;
  1714   1730       zEnd = &zCsr[nByte];
  1715   1731     }while( nByte && !db->mallocFailed );
         1732  +
         1733  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
         1734  +    p->anExec = (i64*)sqlite3DbMallocZero(db, p->nOp*sizeof(i64));
         1735  +#endif
  1716   1736   
  1717   1737     p->nCursor = nCursor;
  1718   1738     p->nOnceFlag = nOnce;
  1719   1739     if( p->aVar ){
  1720   1740       p->nVar = (ynVar)nVar;
  1721   1741       for(n=0; n<nVar; n++){
  1722   1742         p->aVar[n].flags = MEM_Null;
................................................................................
  2693   2713       sqlite3DbFree(db, pSub);
  2694   2714     }
  2695   2715     for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
  2696   2716     vdbeFreeOpArray(db, p->aOp, p->nOp);
  2697   2717     sqlite3DbFree(db, p->aColName);
  2698   2718     sqlite3DbFree(db, p->zSql);
  2699   2719     sqlite3DbFree(db, p->pFree);
  2700         -  sqlite3DbFree(db, p->apExplain);
         2720  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
         2721  +  sqlite3DbFree(db, p->anExec);
         2722  +  for(i=0; i<p->nScan; i++){
         2723  +    sqlite3DbFree(db, p->aScan[i].zName);
         2724  +  }
         2725  +  sqlite3DbFree(db, p->aScan);
         2726  +#endif
  2701   2727   }
  2702   2728   
  2703   2729   /*
  2704   2730   ** Delete an entire VDBE.
  2705   2731   */
  2706   2732   void sqlite3VdbeDelete(Vdbe *p){
  2707   2733     sqlite3 *db;

Changes to src/where.c.

  2735   2735         }
  2736   2736       }
  2737   2737     }
  2738   2738     *pzAff = zAff;
  2739   2739     return regBase;
  2740   2740   }
  2741   2741   
  2742         -#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_STMT_SCANSTATUS)
         2742  +#ifndef SQLITE_OMIT_EXPLAIN
  2743   2743   /*
  2744   2744   ** This routine is a helper for explainIndexRange() below
  2745   2745   **
  2746   2746   ** pStr holds the text of an expression that we are building up one term
  2747   2747   ** at a time.  This routine adds a new term to the end of the expression.
  2748   2748   ** Terms are separated by AND so add the "AND" text for second and subsequent
  2749   2749   ** terms only.
................................................................................
  2808   2808   
  2809   2809   /*
  2810   2810   ** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
  2811   2811   ** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single
  2812   2812   ** record is added to the output to describe the table scan strategy in 
  2813   2813   ** pLevel.
  2814   2814   */
  2815         -static void explainOneScan(
         2815  +static int explainOneScan(
  2816   2816     Parse *pParse,                  /* Parse context */
  2817   2817     SrcList *pTabList,              /* Table list this loop refers to */
  2818         -  WhereInfo *pWInfo,              /* WHERE clause this loop belongs to */
         2818  +  WhereLevel *pLevel,             /* Scan to write OP_Explain opcode for */
  2819   2819     int iLevel,                     /* Value for "level" column of output */
         2820  +  int iFrom,                      /* Value for "from" column of output */
  2820   2821     u16 wctrlFlags                  /* Flags passed to sqlite3WhereBegin() */
  2821   2822   ){
  2822         -#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
         2823  +  int ret = 0;
         2824  +#ifndef SQLITE_DEBUG
  2823   2825     if( pParse->explain==2 )
  2824   2826   #endif
  2825   2827     {
  2826         -    WhereLevel *pLevel = &pWInfo->a[iLevel];
  2827   2828       struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
  2828   2829       Vdbe *v = pParse->pVdbe;      /* VM being constructed */
  2829   2830       sqlite3 *db = pParse->db;     /* Database handle */
  2830         -#ifdef SQLITE_OMIT_EXPLAIN
  2831         -    int iId = 0;                  /* Select id (left-most output column) */
  2832         -#else
  2833         -    int iId = pParse->iSelectId;   /* Select id (left-most output column) */
  2834         -#endif
         2831  +    int iId = pParse->iSelectId;  /* Select id (left-most output column) */
  2835   2832       int isSearch;                 /* True for a SEARCH. False for SCAN. */
  2836   2833       WhereLoop *pLoop;             /* The controlling WhereLoop object */
  2837   2834       u32 flags;                    /* Flags that describe this loop */
         2835  +    char *zMsg;                   /* Text to add to EQP output */
  2838   2836       StrAccum str;                 /* EQP output string */
  2839   2837       char zBuf[100];               /* Initial space for EQP output string */
  2840         -    const char *zObj;
  2841         -    ExplainArg *pExplain;
  2842         -    i64 nEstRow;                  /* Estimated rows per scan of pLevel */
  2843   2838   
  2844   2839       pLoop = pLevel->pWLoop;
  2845   2840       flags = pLoop->wsFlags;
  2846         -    if( (flags&WHERE_MULTI_OR) ) return;
         2841  +    if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0;
         2842  +
         2843  +    isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
         2844  +            || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
         2845  +            || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
  2847   2846   
  2848   2847       sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
  2849   2848       str.db = db;
  2850         -
  2851         -    /* Reserve space at the start of the buffer managed by the StrAccum
  2852         -    ** object for *pExplain.  */
  2853         -    assert( sizeof(*pExplain)<=sizeof(zBuf) );
  2854         -    str.nChar = sizeof(*pExplain);
  2855         -
  2856         -    /* Append the object (table or index) name to the buffer. */
  2857         -    if( pItem->pSelect ){
  2858         -      zObj = "";
  2859         -    }else if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
  2860         -      zObj = pLoop->u.btree.pIndex->zName;
  2861         -    }else{
  2862         -      zObj = pItem->zName;
  2863         -    }
  2864         -    sqlite3StrAccumAppend(&str, zObj, sqlite3Strlen30(zObj)+1);
  2865         -
  2866         -    /* Append the EQP text to the buffer */
  2867         -    isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
  2868         -            || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
  2869         -            || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
  2870   2849       sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
  2871   2850       if( pItem->pSelect ){
  2872         -#ifdef SQLITE_OMIT_EXPLAIN
  2873         -      sqlite3XPrintf(&str, 0, " SUBQUERY 0");
  2874         -#else
  2875   2851         sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
  2876         -#endif
  2877   2852       }else{
  2878   2853         sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
  2879   2854       }
  2880   2855   
  2881   2856       if( pItem->zAlias ){
  2882   2857         sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias);
  2883   2858       }
................................................................................
  2923   2898       }
  2924   2899   #ifndef SQLITE_OMIT_VIRTUALTABLE
  2925   2900       else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
  2926   2901         sqlite3XPrintf(&str, 0, " VIRTUAL TABLE INDEX %d:%s",
  2927   2902                     pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
  2928   2903       }
  2929   2904   #endif
  2930         -    nEstRow = pLoop->nOut>=10 ? sqlite3LogEstToInt(pLoop->nOut) : 1;
  2931   2905   #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
  2932         -    sqlite3XPrintf(&str, 0, " (~%llu rows)", nEstRow);
  2933         -#endif
  2934         -    pExplain = (ExplainArg*)sqlite3StrAccumFinish(&str);
  2935         -    assert( pExplain || db->mallocFailed );
  2936         -    if( pExplain ){
  2937         -      memset(pExplain, 0, sizeof(*pExplain));
  2938         -      if( pLoop->wsFlags & WHERE_INDEXED ){
  2939         -        pExplain->iCsr = pLevel->iIdxCur;
  2940         -      }else if( pItem->pSelect==0 ){
  2941         -        pExplain->iCsr = pLevel->iTabCur;
  2942         -      }else{
  2943         -        pExplain->iCsr = -1;
  2944         -      }
  2945         -      pExplain->nEst = nEstRow;
  2946         -      pExplain->zName = (const char*)&pExplain[1];
  2947         -      pExplain->zExplain = &pExplain->zName[sqlite3Strlen30(pExplain->zName)+1];
  2948         -      pWInfo->iExplain = sqlite3VdbeAddOp4(v, OP_Explain, 
  2949         -          iId, iLevel, pLevel->iFrom, (char*)pExplain,P4_EXPLAIN
  2950         -      );
  2951         -    }
  2952         -  }
         2906  +    if( pLoop->nOut>=10 ){
         2907  +      sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
         2908  +    }else{
         2909  +      sqlite3StrAccumAppend(&str, " (~1 row)", 9);
         2910  +    }
         2911  +#endif
         2912  +    zMsg = sqlite3StrAccumFinish(&str);
         2913  +    ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
         2914  +  }
         2915  +  return ret;
         2916  +}
         2917  +#else
         2918  +# define explainOneScan(u,v,w,x,y,z) 0
         2919  +#endif /* SQLITE_OMIT_EXPLAIN */
         2920  +
         2921  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
         2922  +static void addScanStatus(
         2923  +  Vdbe *v, 
         2924  +  SrcList *pSrclist,
         2925  +  WhereLevel *pLvl,
         2926  +  int addrExplain
         2927  +){
         2928  +  const char *zObj = 0;
         2929  +  i64 nEst = 1;
         2930  +  WhereLoop *pLoop = pLvl->pWLoop;
         2931  +  if( (pLoop->wsFlags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
         2932  +    zObj = pLoop->u.btree.pIndex->zName;
         2933  +  }else{
         2934  +    zObj = pSrclist->a[pLvl->iFrom].zName;
         2935  +  }
         2936  +  if( pLoop->nOut>=10 ){
         2937  +    nEst = sqlite3LogEstToInt(pLoop->nOut);
         2938  +  }
         2939  +  sqlite3VdbeScanCounter(
         2940  +      v, addrExplain, pLvl->addrBody, pLvl->addrVisit, nEst, zObj
         2941  +  );
  2953   2942   }
  2954   2943   #else
  2955         -# define explainOneScan(v,w,x,y,z)
  2956         -#endif /* !SQLITE_OMIT_EXPLAIN || SQLITE_ENABLE_STMT_SCANSTATUS */
         2944  +# define addScanStatus(a, b, c, d)
         2945  +#endif
         2946  +
  2957   2947   
  2958   2948   
  2959   2949   /*
  2960   2950   ** Generate code for the start of the iLevel-th loop in the WHERE clause
  2961   2951   ** implementation described by pWInfo.
  2962   2952   */
  2963   2953   static Bitmask codeOneLoopStart(
................................................................................
  3617   3607           /* Loop through table entries that match term pOrTerm. */
  3618   3608           WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
  3619   3609           pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
  3620   3610                                         wctrlFlags, iCovCur);
  3621   3611           assert( pSubWInfo || pParse->nErr || db->mallocFailed );
  3622   3612           if( pSubWInfo ){
  3623   3613             WhereLoop *pSubLoop;
  3624         -
  3625         -          /* If an OP_Explain was added for this sub-loop, fix the P2 and
  3626         -          ** P3 parameters to it so that they are relative to the current
  3627         -          ** context.  */
  3628         -          if( pSubWInfo->iExplain!=0 ){
  3629         -            sqlite3VdbeChangeP2(v, pSubWInfo->iExplain, iLevel);
  3630         -            sqlite3VdbeChangeP3(v, pSubWInfo->iExplain, pLevel->iFrom);
  3631         -          }
         3614  +          int addrExplain = explainOneScan(
         3615  +              pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
         3616  +          );
         3617  +          addScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
  3632   3618   
  3633   3619             /* This is the sub-WHERE clause body.  First skip over
  3634   3620             ** duplicate rows from prior sub-WHERE clauses, and record the
  3635   3621             ** rowid (or PRIMARY KEY) for the current row so that the same
  3636   3622             ** row will be skipped in subsequent sub-WHERE clauses.
  3637   3623             */
  3638   3624             if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
................................................................................
  3755   3741         pLevel->p1 = iCur;
  3756   3742         pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
  3757   3743         VdbeCoverageIf(v, bRev==0);
  3758   3744         VdbeCoverageIf(v, bRev!=0);
  3759   3745         pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
  3760   3746       }
  3761   3747     }
         3748  +
         3749  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
         3750  +  pLevel->addrVisit = sqlite3VdbeCurrentAddr(v);
         3751  +#endif
  3762   3752   
  3763   3753     /* Insert code to test every subexpression that can be completely
  3764   3754     ** computed using the current set of tables.
  3765   3755     */
  3766   3756     for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
  3767   3757       Expr *pE;
  3768   3758       testcase( pTerm->wtFlags & TERM_VIRTUAL );
................................................................................
  6457   6447   
  6458   6448     /* Generate the code to do the search.  Each iteration of the for
  6459   6449     ** loop below generates code for a single nested loop of the VM
  6460   6450     ** program.
  6461   6451     */
  6462   6452     notReady = ~(Bitmask)0;
  6463   6453     for(ii=0; ii<nTabList; ii++){
         6454  +    int addrExplain;
         6455  +    int wsFlags;
  6464   6456       pLevel = &pWInfo->a[ii];
         6457  +    wsFlags = pLevel->pWLoop->wsFlags;
  6465   6458   #ifndef SQLITE_OMIT_AUTOMATIC_INDEX
  6466   6459       if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
  6467   6460         constructAutomaticIndex(pParse, &pWInfo->sWC,
  6468   6461                   &pTabList->a[pLevel->iFrom], notReady, pLevel);
  6469   6462         if( db->mallocFailed ) goto whereBeginError;
  6470   6463       }
  6471   6464   #endif
  6472         -    explainOneScan(pParse, pTabList, pWInfo, ii, wctrlFlags);
         6465  +    addrExplain = explainOneScan(
         6466  +        pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
         6467  +    );
  6473   6468       pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
  6474   6469       notReady = codeOneLoopStart(pWInfo, ii, notReady);
  6475   6470       pWInfo->iContinue = pLevel->addrCont;
         6471  +    if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){
         6472  +      addScanStatus(v, pTabList, pLevel, addrExplain);
         6473  +    }
  6476   6474     }
  6477   6475   
  6478   6476     /* Done. */
  6479   6477     VdbeModuleComment((v, "Begin WHERE-core"));
  6480   6478     return pWInfo;
  6481   6479   
  6482   6480     /* Jump here if malloc fails */

Changes to src/whereInt.h.

    81     81           u8 eEndLoopOp;         /* IN Loop terminator. OP_Next or OP_Prev */
    82     82         } *aInLoop;           /* Information about each nested IN operator */
    83     83       } in;                 /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
    84     84       Index *pCovidx;       /* Possible covering index for WHERE_MULTI_OR */
    85     85     } u;
    86     86     struct WhereLoop *pWLoop;  /* The selected WhereLoop object */
    87     87     Bitmask notReady;          /* FROM entries not usable at this level */
           88  +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
           89  +  int addrVisit;        /* Address at which row is visited */
           90  +#endif
    88     91   };
    89     92   
    90     93   /*
    91     94   ** Each instance of this object represents an algorithm for evaluating one
    92     95   ** term of a join.  Every term of the FROM clause will have at least
    93     96   ** one corresponding WhereLoop object (unless INDEXED BY constraints
    94     97   ** prevent a query solution - which is an error) and many terms of the

Changes to test/scanstatus.test.

   111    111     zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j=?)}
   112    112   }
   113    113   
   114    114   do_execsql_test 2.4.1 {
   115    115     SELECT * FROM x1 WHERE j<'two'
   116    116   } {4 four 1 one 3 three}
   117    117   do_scanstatus_test 2.4.2 {
   118         -  nLoop 1 nVisit 4 nEst 262144 zName x1j 
          118  +  nLoop 1 nVisit 3 nEst 262144 zName x1j 
   119    119     zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j<?)}
   120    120   }
   121    121   
   122    122   do_execsql_test 2.5.1 {
   123    123     SELECT * FROM x1 WHERE j>='two'
   124    124   } {2 two}
   125    125   do_scanstatus_test 2.5.2 {
................................................................................
   148    148     zExplain {SEARCH TABLE x2 USING INDEX x2j (j>? AND j<?)}
   149    149   }
   150    150   
   151    151   do_execsql_test 2.8.1 {
   152    152     SELECT * FROM x2 WHERE i=1 AND j='two'
   153    153   }
   154    154   do_scanstatus_test 2.8.2 {
   155         -  nLoop 1 nVisit 1 nEst 8 zName x2ij 
          155  +  nLoop 1 nVisit 0 nEst 8 zName x2ij 
   156    156     zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
   157    157   }
   158    158   
   159    159   do_execsql_test 2.9.1 {
   160    160     SELECT * FROM x2 WHERE i=5 AND j='two'
   161    161   }
   162    162   do_scanstatus_test 2.9.2 {
................................................................................
   164    164     zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
   165    165   }
   166    166   
   167    167   do_execsql_test 2.10.1 {
   168    168     SELECT * FROM x2 WHERE i=3 AND j='three'
   169    169   } {3 three {3 three}}
   170    170   do_scanstatus_test 2.10.2 {
   171         -  nLoop 1 nVisit 2 nEst 8 zName x2ij 
          171  +  nLoop 1 nVisit 1 nEst 8 zName x2ij 
   172    172     zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
   173    173   }
   174    174   
   175    175   #-------------------------------------------------------------------------
   176    176   # Try with queries that use the OR optimization.
   177    177   #
   178    178   do_execsql_test 3.1 {
................................................................................
   183    183     WITH d(x) AS (SELECT 1 UNION ALL SELECT x+1 AS n FROM d WHERE n<=100)
   184    184     INSERT INTO a1 SELECT x, x, x, x FROM d;
   185    185   }
   186    186   
   187    187   do_execsql_test 3.2.1 {
   188    188     SELECT d FROM a1 WHERE (a=4 OR b=13)
   189    189   } {4 13}
   190         -
   191         -do_scanstatus_test 2.4 {
   192         -  nLoop 1 nVisit 2 nEst 10 zName a1a 
          190  +do_scanstatus_test 3.2.2 {
          191  +  nLoop 1 nVisit 1 nEst 10 zName a1a 
   193    192     zExplain {SEARCH TABLE a1 USING INDEX a1a (a=?)}
   194         -
   195         -  nLoop 1 nVisit 2 nEst 10 zName a1bc 
          193  +  nLoop 1 nVisit 1 nEst 10 zName a1bc 
   196    194     zExplain {SEARCH TABLE a1 USING INDEX a1bc (b=?)}
   197    195   }
   198    196   
          197  +do_execsql_test 3.2.1 {
          198  +  SELECT count(*) FROM a1 WHERE (a BETWEEN 4 AND 12) OR (b BETWEEN 40 AND 60)
          199  +} {30}
          200  +do_scanstatus_test 3.2.2 {
          201  +  nLoop 1 nVisit 9 nEst 16384 zName a1a 
          202  +  zExplain {SEARCH TABLE a1 USING INDEX a1a (a>? AND a<?)}
          203  +  nLoop 1 nVisit 21 nEst 16384 zName a1bc
          204  +  zExplain {SEARCH TABLE a1 USING INDEX a1bc (b>? AND b<?)}
          205  +}
          206  +
          207  +do_execsql_test 3.3.1 {
          208  +  SELECT count(*) FROM a1 AS x, a1 AS y 
          209  +  WHERE (x.a BETWEEN 4 AND 12) AND (y.b BETWEEN 1 AND 10)
          210  +} {90}
          211  +do_scanstatus_test 3.2.2 {
          212  +  nLoop 1 nVisit 10 nEst 16384 zName a1bc 
          213  +  zExplain {SEARCH TABLE a1 AS y USING COVERING INDEX a1bc (b>? AND b<?)}
          214  +  nLoop 10 nVisit 90 nEst 16384 zName a1a
          215  +  zExplain {SEARCH TABLE a1 AS x USING COVERING INDEX a1a (a>? AND a<?)}
          216  +}
          217  +
          218  +do_execsql_test 3.4.1 {
          219  +  SELECT count(*) FROM a1 WHERE a IN (1, 5, 10, 15);
          220  +} {4}
          221  +do_scanstatus_test 3.4.2 {
          222  +  nLoop 1 nVisit 4 nEst 40 zName a1a 
          223  +  zExplain {SEARCH TABLE a1 USING COVERING INDEX a1a (a=?)}
          224  +}
   199    225   
          226  +do_execsql_test 3.4.1 {
          227  +  SELECT count(*) FROM a1 WHERE rowid IN (1, 5, 10, 15);
          228  +} {4}
          229  +do_scanstatus_test 3.4.2 {
          230  +  nLoop 1 nVisit 4 nEst 4 zName a1
          231  +  zExplain {SEARCH TABLE a1 USING INTEGER PRIMARY KEY (rowid=?)}
          232  +}
   200    233   
   201    234   finish_test