/ 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 Unified Diffs Ignore Whitespace Patch

Changes to src/vdbe.c.

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
...
616
617
618
619
620
621
622



623
624
625
626
627
628
629
....
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
....
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
....
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
....
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
....
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
....
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
....
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
....
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
....
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
#define Deephemeralize(P) \
   if( ((P)->flags&MEM_Ephem)!=0 \
       && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}

/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
#define isSorter(x) ((x)->pSorter!=0)

/*
** The first argument passed to the IncrementExplainCounter() macro must
** be a non-NULL pointer to an object of type VdbeCursor. The second 
** argument must be either "nLoop" or "nVisit" (without the double-quotes).
*/
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
# define IncrementExplainCounter(pCsr, counter) \
    if( (pCsr)->pExplain ) (pCsr)->pExplain->counter++
#else
# define IncrementExplainCounter(pCsr, counter)
#endif

/*
** Allocate VdbeCursor number iCur.  Return a pointer to it.  Return NULL
** if we run out of memory.
*/
static VdbeCursor *allocateCursor(
  Vdbe *p,              /* The virtual machine */
  int iCur,             /* Index of the new VdbeCursor */
................................................................................
    assert( pc>=0 && pc<p->nOp );
    if( db->mallocFailed ) goto no_mem;
#ifdef VDBE_PROFILE
    start = sqlite3Hwtime();
#endif
    nVmStep++;
    pOp = &aOp[pc];




    /* Only allow tracing if SQLITE_DEBUG is defined.
    */
#ifdef SQLITE_DEBUG
    if( db->flags & SQLITE_VdbeTrace ){
      sqlite3VdbePrintOp(stdout, pc, pOp);
    }
................................................................................
  assert( pC->isOrdered );
  assert( pC->pCursor!=0 );
  oc = pOp->opcode;
  pC->nullRow = 0;
#ifdef SQLITE_DEBUG
  pC->seekOp = pOp->opcode;
#endif
  IncrementExplainCounter(pC, nLoop);
  if( pC->isTable ){
    /* The input value in P3 might be of any type: integer, real, string,
    ** blob, or NULL.  But it needs to be an integer before we can do
    ** the seek, so convert it. */
    pIn3 = &aMem[pOp->p3];
    if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
      applyNumericAffinity(pIn3, 0);
................................................................................
      res = sqlite3BtreeEof(pC->pCursor);
    }
  }
  assert( pOp->p2>0 );
  VdbeBranchTaken(res!=0,2);
  if( res ){
    pc = pOp->p2 - 1;
  }else{
    IncrementExplainCounter(pC, nVisit);
  }
  break;
}

/* Opcode: Seek P1 P2 * * *
** Synopsis:  intkey=r[P2]
**
................................................................................
  assert( pC->isTable );
  assert( pC->pseudoTableReg==0 );
  pCrsr = pC->pCursor;
  assert( pCrsr!=0 );
  res = 0;
  iKey = pIn3->u.i;
  rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
  IncrementExplainCounter(pC, nLoop);
  pC->movetoTarget = iKey;  /* Used by OP_Delete */
  pC->nullRow = 0;
  pC->cacheStatus = CACHE_STALE;
  pC->deferredMoveto = 0;
  VdbeBranchTaken(res!=0,2);
  if( res!=0 ){
    pc = pOp->p2 - 1;
  }else{
    IncrementExplainCounter(pC, nVisit);
  }
  pC->seekResult = res;
  break;
}

/* Opcode: Sequence P1 P2 * * *
** Synopsis: r[P2]=cursor[P1].ctr++
................................................................................
  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  assert( pC!=0 );
  pCrsr = pC->pCursor;
  res = 0;
  assert( pCrsr!=0 );
  rc = sqlite3BtreeLast(pCrsr, &res);
  IncrementExplainCounter(pC, nLoop);
  if( res==0 ) IncrementExplainCounter(pC, nVisit);
  pC->nullRow = (u8)res;
  pC->deferredMoveto = 0;
  pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_DEBUG
  pC->seekOp = OP_Last;
#endif
  if( pOp->p2>0 ){
................................................................................
  }else{
    pCrsr = pC->pCursor;
    assert( pCrsr );
    rc = sqlite3BtreeFirst(pCrsr, &res);
    pC->deferredMoveto = 0;
    pC->cacheStatus = CACHE_STALE;
  }
  IncrementExplainCounter(pC, nLoop);
  pC->nullRow = (u8)res;
  assert( pOp->p2>0 && pOp->p2<p->nOp );
  VdbeBranchTaken(res!=0,2);
  if( res ){
    pc = pOp->p2 - 1;
  }else{
    IncrementExplainCounter(pC, nVisit);
  }
  break;
}

/* Opcode: Next P1 P2 P3 P4 P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
................................................................................
       || pC->seekOp==OP_Last );

  rc = pOp->p4.xAdvance(pC->pCursor, &res);
next_tail:
  pC->cacheStatus = CACHE_STALE;
  VdbeBranchTaken(res==0,2);
  if( res==0 ){
    IncrementExplainCounter(pC, nVisit);
    pC->nullRow = 0;
    pc = pOp->p2 - 1;
    p->aCounter[pOp->p5]++;
#ifdef SQLITE_TEST
    sqlite3_search_count++;
#endif
  }else{
................................................................................
    sqlite3VtabImportErrmsg(p, pVtab);
    if( rc==SQLITE_OK ){
      res = pModule->xEof(pVtabCursor);
    }
    VdbeBranchTaken(res!=0,2);
    if( res ){
      pc = pOp->p2 - 1;
    }else{
      IncrementExplainCounter(pCur, nVisit);
    }
    IncrementExplainCounter(pCur, nLoop);
  }
  pCur->nullRow = 0;

  break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */

................................................................................
  if( rc==SQLITE_OK ){
    res = pModule->xEof(pCur->pVtabCursor);
  }
  VdbeBranchTaken(!res,2);
  if( !res ){
    /* If there is data, jump to P2 */
    pc = pOp->p2 - 1;
    IncrementExplainCounter(pCur, nVisit);
  }
  goto check_for_interrupt;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VRename P1 * * P4 *
................................................................................
    sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
  }
#endif /* SQLITE_DEBUG */
#endif /* SQLITE_OMIT_TRACE */
  break;
}

#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
case OP_Explain: {
  ExplainArg *pArg;
  VdbeCursor *pCur;
  if( pOp->p4type==P4_EXPLAIN && (pArg = pOp->p4.pExplain)->iCsr>=0 ){
    pArg = pOp->p4.pExplain;
    pCur = p->apCsr[pArg->iCsr];
    pCur->pExplain = pArg;
  }
  break;
}
#endif


/* Opcode: Noop * * * * *
**
** Do nothing.  This instruction is often useful as a jump
** destination.
*/
/*







<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>







 







<







 







<
<







 







<







<
<







 







<
<







 







<





<
<







 







<







 







<
<

<







 







<







 







<
<
<
<
<
<
<
<
<
<
<
<
<







163
164
165
166
167
168
169












170
171
172
173
174
175
176
...
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
....
3555
3556
3557
3558
3559
3560
3561

3562
3563
3564
3565
3566
3567
3568
....
3663
3664
3665
3666
3667
3668
3669


3670
3671
3672
3673
3674
3675
3676
....
3873
3874
3875
3876
3877
3878
3879

3880
3881
3882
3883
3884
3885
3886


3887
3888
3889
3890
3891
3892
3893
....
4448
4449
4450
4451
4452
4453
4454


4455
4456
4457
4458
4459
4460
4461
....
4517
4518
4519
4520
4521
4522
4523

4524
4525
4526
4527
4528


4529
4530
4531
4532
4533
4534
4535
....
4632
4633
4634
4635
4636
4637
4638

4639
4640
4641
4642
4643
4644
4645
....
6054
6055
6056
6057
6058
6059
6060


6061

6062
6063
6064
6065
6066
6067
6068
....
6147
6148
6149
6150
6151
6152
6153

6154
6155
6156
6157
6158
6159
6160
....
6346
6347
6348
6349
6350
6351
6352













6353
6354
6355
6356
6357
6358
6359
#define Deephemeralize(P) \
   if( ((P)->flags&MEM_Ephem)!=0 \
       && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}

/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
#define isSorter(x) ((x)->pSorter!=0)













/*
** Allocate VdbeCursor number iCur.  Return a pointer to it.  Return NULL
** if we run out of memory.
*/
static VdbeCursor *allocateCursor(
  Vdbe *p,              /* The virtual machine */
  int iCur,             /* Index of the new VdbeCursor */
................................................................................
    assert( pc>=0 && pc<p->nOp );
    if( db->mallocFailed ) goto no_mem;
#ifdef VDBE_PROFILE
    start = sqlite3Hwtime();
#endif
    nVmStep++;
    pOp = &aOp[pc];
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
    if( p->pFrame==0 ) p->anExec[pc]++;
#endif

    /* Only allow tracing if SQLITE_DEBUG is defined.
    */
#ifdef SQLITE_DEBUG
    if( db->flags & SQLITE_VdbeTrace ){
      sqlite3VdbePrintOp(stdout, pc, pOp);
    }
................................................................................
  assert( pC->isOrdered );
  assert( pC->pCursor!=0 );
  oc = pOp->opcode;
  pC->nullRow = 0;
#ifdef SQLITE_DEBUG
  pC->seekOp = pOp->opcode;
#endif

  if( pC->isTable ){
    /* The input value in P3 might be of any type: integer, real, string,
    ** blob, or NULL.  But it needs to be an integer before we can do
    ** the seek, so convert it. */
    pIn3 = &aMem[pOp->p3];
    if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
      applyNumericAffinity(pIn3, 0);
................................................................................
      res = sqlite3BtreeEof(pC->pCursor);
    }
  }
  assert( pOp->p2>0 );
  VdbeBranchTaken(res!=0,2);
  if( res ){
    pc = pOp->p2 - 1;


  }
  break;
}

/* Opcode: Seek P1 P2 * * *
** Synopsis:  intkey=r[P2]
**
................................................................................
  assert( pC->isTable );
  assert( pC->pseudoTableReg==0 );
  pCrsr = pC->pCursor;
  assert( pCrsr!=0 );
  res = 0;
  iKey = pIn3->u.i;
  rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);

  pC->movetoTarget = iKey;  /* Used by OP_Delete */
  pC->nullRow = 0;
  pC->cacheStatus = CACHE_STALE;
  pC->deferredMoveto = 0;
  VdbeBranchTaken(res!=0,2);
  if( res!=0 ){
    pc = pOp->p2 - 1;


  }
  pC->seekResult = res;
  break;
}

/* Opcode: Sequence P1 P2 * * *
** Synopsis: r[P2]=cursor[P1].ctr++
................................................................................
  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  assert( pC!=0 );
  pCrsr = pC->pCursor;
  res = 0;
  assert( pCrsr!=0 );
  rc = sqlite3BtreeLast(pCrsr, &res);


  pC->nullRow = (u8)res;
  pC->deferredMoveto = 0;
  pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_DEBUG
  pC->seekOp = OP_Last;
#endif
  if( pOp->p2>0 ){
................................................................................
  }else{
    pCrsr = pC->pCursor;
    assert( pCrsr );
    rc = sqlite3BtreeFirst(pCrsr, &res);
    pC->deferredMoveto = 0;
    pC->cacheStatus = CACHE_STALE;
  }

  pC->nullRow = (u8)res;
  assert( pOp->p2>0 && pOp->p2<p->nOp );
  VdbeBranchTaken(res!=0,2);
  if( res ){
    pc = pOp->p2 - 1;


  }
  break;
}

/* Opcode: Next P1 P2 P3 P4 P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
................................................................................
       || pC->seekOp==OP_Last );

  rc = pOp->p4.xAdvance(pC->pCursor, &res);
next_tail:
  pC->cacheStatus = CACHE_STALE;
  VdbeBranchTaken(res==0,2);
  if( res==0 ){

    pC->nullRow = 0;
    pc = pOp->p2 - 1;
    p->aCounter[pOp->p5]++;
#ifdef SQLITE_TEST
    sqlite3_search_count++;
#endif
  }else{
................................................................................
    sqlite3VtabImportErrmsg(p, pVtab);
    if( rc==SQLITE_OK ){
      res = pModule->xEof(pVtabCursor);
    }
    VdbeBranchTaken(res!=0,2);
    if( res ){
      pc = pOp->p2 - 1;


    }

  }
  pCur->nullRow = 0;

  break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */

................................................................................
  if( rc==SQLITE_OK ){
    res = pModule->xEof(pCur->pVtabCursor);
  }
  VdbeBranchTaken(!res,2);
  if( !res ){
    /* If there is data, jump to P2 */
    pc = pOp->p2 - 1;

  }
  goto check_for_interrupt;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VRename P1 * * P4 *
................................................................................
    sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
  }
#endif /* SQLITE_DEBUG */
#endif /* SQLITE_OMIT_TRACE */
  break;
}















/* Opcode: Noop * * * * *
**
** Do nothing.  This instruction is often useful as a jump
** destination.
*/
/*

Changes to src/vdbe.h.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
293
294
295
296
297
298
299
300




301



/*
** A single VDBE is an opaque structure named "Vdbe".  Only routines
** in the source file sqliteVdbe.c are allowed to see the insides
** of this structure.
*/
typedef struct Vdbe Vdbe;
typedef struct ExplainArg ExplainArg;

/*
** The names of the following types declared in vdbeInt.h are required
** for the VdbeOp definition.
*/
typedef struct Mem Mem;
typedef struct SubProgram SubProgram;
................................................................................
    FuncDef *pFunc;        /* Used when p4type is P4_FUNCDEF */
    CollSeq *pColl;        /* Used when p4type is P4_COLLSEQ */
    Mem *pMem;             /* Used when p4type is P4_MEM */
    VTable *pVtab;         /* Used when p4type is P4_VTAB */
    KeyInfo *pKeyInfo;     /* Used when p4type is P4_KEYINFO */
    int *ai;               /* Used when p4type is P4_INTARRAY */
    SubProgram *pProgram;  /* Used when p4type is P4_SUBPROGRAM */
    ExplainArg *pExplain;  /* Used when p4type is P4_EXPLAIN */
    int (*xAdvance)(BtCursor *, int *);
  } p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
  char *zComment;          /* Comment to improve readability */
#endif
#ifdef VDBE_PROFILE
  u32 cnt;                 /* Number of times this instruction was executed */
................................................................................
struct VdbeOpList {
  u8 opcode;          /* What operation to perform */
  signed char p1;     /* First operand */
  signed char p2;     /* Second parameter (often the jump destination) */
  signed char p3;     /* Third parameter */
};
typedef struct VdbeOpList VdbeOpList;

/*
** Structure used as the P4 parameter for OP_Explain opcodes.
*/
struct ExplainArg {
  int iCsr;                       /* Cursor number this applies to */
  i64 nLoop;                      /* Number of times loop has run */
  i64 nVisit;                     /* Total number of rows visited */
  i64 nEst;                       /* Estimated number of rows per scan */
  const char *zName;              /* Name of table/index being scanned */
  const char *zExplain;           /* EQP text for this loop */
};


/*
** Allowed values of VdbeOp.p4type
*/
#define P4_NOTUSED    0   /* The P4 parameter is not used */
#define P4_DYNAMIC  (-1)  /* Pointer to a string obtained from sqliteMalloc() */
#define P4_STATIC   (-2)  /* Pointer to a static string */
................................................................................
#define P4_MPRINTF  (-11) /* P4 is a string obtained from sqlite3_mprintf() */
#define P4_REAL     (-12) /* P4 is a 64-bit floating point value */
#define P4_INT64    (-13) /* P4 is a 64-bit signed integer */
#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() */
#define P4_EXPLAIN  (-20) /* P4 is a pointer to an instance of ExplainArg */

/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
#define P5_ConstraintUnique  2
#define P5_ConstraintCheck   3
#define P5_ConstraintFK      4

................................................................................
#else
# define VdbeCoverage(v)
# define VdbeCoverageIf(v,x)
# define VdbeCoverageAlwaysTaken(v)
# define VdbeCoverageNeverTaken(v)
# define VDBE_OFFSET_LINENO(x) 0
#endif





#endif









<







 







<







 







<
<
<
<
<
<
<
<
<
<
<
<
<







 







<







 








>
>
>
>

>
>
21
22
23
24
25
26
27

28
29
30
31
32
33
34
..
55
56
57
58
59
60
61

62
63
64
65
66
67
68
..
95
96
97
98
99
100
101













102
103
104
105
106
107
108
...
115
116
117
118
119
120
121

122
123
124
125
126
127
128
...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291

/*
** A single VDBE is an opaque structure named "Vdbe".  Only routines
** in the source file sqliteVdbe.c are allowed to see the insides
** of this structure.
*/
typedef struct Vdbe Vdbe;


/*
** The names of the following types declared in vdbeInt.h are required
** for the VdbeOp definition.
*/
typedef struct Mem Mem;
typedef struct SubProgram SubProgram;
................................................................................
    FuncDef *pFunc;        /* Used when p4type is P4_FUNCDEF */
    CollSeq *pColl;        /* Used when p4type is P4_COLLSEQ */
    Mem *pMem;             /* Used when p4type is P4_MEM */
    VTable *pVtab;         /* Used when p4type is P4_VTAB */
    KeyInfo *pKeyInfo;     /* Used when p4type is P4_KEYINFO */
    int *ai;               /* Used when p4type is P4_INTARRAY */
    SubProgram *pProgram;  /* Used when p4type is P4_SUBPROGRAM */

    int (*xAdvance)(BtCursor *, int *);
  } p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
  char *zComment;          /* Comment to improve readability */
#endif
#ifdef VDBE_PROFILE
  u32 cnt;                 /* Number of times this instruction was executed */
................................................................................
struct VdbeOpList {
  u8 opcode;          /* What operation to perform */
  signed char p1;     /* First operand */
  signed char p2;     /* Second parameter (often the jump destination) */
  signed char p3;     /* Third parameter */
};
typedef struct VdbeOpList VdbeOpList;














/*
** Allowed values of VdbeOp.p4type
*/
#define P4_NOTUSED    0   /* The P4 parameter is not used */
#define P4_DYNAMIC  (-1)  /* Pointer to a string obtained from sqliteMalloc() */
#define P4_STATIC   (-2)  /* Pointer to a static string */
................................................................................
#define P4_MPRINTF  (-11) /* P4 is a string obtained from sqlite3_mprintf() */
#define P4_REAL     (-12) /* P4 is a 64-bit floating point value */
#define P4_INT64    (-13) /* P4 is a 64-bit signed integer */
#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() */


/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
#define P5_ConstraintUnique  2
#define P5_ConstraintCheck   3
#define P5_ConstraintFK      4

................................................................................
#else
# define VdbeCoverage(v)
# define VdbeCoverageIf(v,x)
# define VdbeCoverageAlwaysTaken(v)
# define VdbeCoverageNeverTaken(v)
# define VDBE_OFFSET_LINENO(x) 0
#endif

#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
void sqlite3VdbeScanCounter(Vdbe*, int, int, int, i64, const char*);
#else
# define sqlite3VdbeScanCounter(a,b,c,d,e)
#endif

#endif

Changes to src/vdbeInt.h.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
...
288
289
290
291
292
293
294
295
296
297
298
299









300
301
302
303
304
305
306
...
366
367
368
369
370
371
372
373
374



375
376
377
378
379
380
381
  Bool isTable:1;       /* True if a table requiring integer keys */
  Bool isOrdered:1;     /* True if the underlying table is BTREE_UNORDERED */
  Pgno pgnoRoot;        /* Root page of the open btree cursor */
  sqlite3_vtab_cursor *pVtabCursor;  /* The cursor for a virtual table */
  i64 seqCount;         /* Sequence counter */
  i64 movetoTarget;     /* Argument to the deferred sqlite3BtreeMoveto() */
  VdbeSorter *pSorter;  /* Sorter object for OP_SorterOpen cursors */
  ExplainArg *pExplain; /* Object to store seek/visit counts (may be NULL) */

  /* Cached information about the header for the data record that the
  ** cursor is currently pointing to.  Only valid if cacheStatus matches
  ** Vdbe.cacheCtr.  Vdbe.cacheCtr will never take on the value of
  ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that
  ** the cache is out of date.
  **
................................................................................
  Vdbe *pVdbe;       /* Attach the explanation to this Vdbe */
  StrAccum str;      /* The string being accumulated */
  int nIndent;       /* Number of elements in aIndent */
  u16 aIndent[100];  /* Levels of indentation */
  char zBase[100];   /* Initial space */
};


/* A bitfield type for use inside of structures.  Always follow with :N where
** N is the number of bits.
*/
typedef unsigned bft;  /* Bit Field Type */










/*
** An instance of the virtual machine.  This structure contains the complete
** state of the virtual machine.
**
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
** is really a pointer to an instance of this structure.
................................................................................
  VdbeFrame *pDelFrame;   /* List of frame objects to free on VM reset */
  int nFrame;             /* Number of frames in pFrame list */
  u32 expmask;            /* Binding to these vars invalidates VM */
  SubProgram *pProgram;   /* Linked list of all sub-programs used by VM */
  int nOnceFlag;          /* Size of array aOnceFlag[] */
  u8 *aOnceFlag;          /* Flags for OP_Once */
  AuxData *pAuxData;      /* Linked list of auxdata allocations */
  ExplainArg **apExplain; /* Array of pointers to P4_EXPLAIN p4 values */
  int nExplain;           /* Number of entries in array apExplain */



};

/*
** The following are allowed values for Vdbe.magic
*/
#define VDBE_MAGIC_INIT     0x26bceaa5    /* Building a VDBE program */
#define VDBE_MAGIC_RUN      0xbdf20da3    /* VDBE is ready to execute */







<







 







<




>
>
>
>
>
>
>
>
>







 







|
|
>
>
>







79
80
81
82
83
84
85

86
87
88
89
90
91
92
...
287
288
289
290
291
292
293

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
...
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
  Bool isTable:1;       /* True if a table requiring integer keys */
  Bool isOrdered:1;     /* True if the underlying table is BTREE_UNORDERED */
  Pgno pgnoRoot;        /* Root page of the open btree cursor */
  sqlite3_vtab_cursor *pVtabCursor;  /* The cursor for a virtual table */
  i64 seqCount;         /* Sequence counter */
  i64 movetoTarget;     /* Argument to the deferred sqlite3BtreeMoveto() */
  VdbeSorter *pSorter;  /* Sorter object for OP_SorterOpen cursors */


  /* Cached information about the header for the data record that the
  ** cursor is currently pointing to.  Only valid if cacheStatus matches
  ** Vdbe.cacheCtr.  Vdbe.cacheCtr will never take on the value of
  ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that
  ** the cache is out of date.
  **
................................................................................
  Vdbe *pVdbe;       /* Attach the explanation to this Vdbe */
  StrAccum str;      /* The string being accumulated */
  int nIndent;       /* Number of elements in aIndent */
  u16 aIndent[100];  /* Levels of indentation */
  char zBase[100];   /* Initial space */
};


/* A bitfield type for use inside of structures.  Always follow with :N where
** N is the number of bits.
*/
typedef unsigned bft;  /* Bit Field Type */

typedef struct ScanCounter ScanCounter;
struct ScanCounter {
  int addrExplain;                /* OP_Explain for loop */
  int addrLoop;                   /* Address of "loops" counter */
  int addrVisit;                  /* Address of "rows visited" counter */
  i64 nEst;                       /* Estimated rows per loop */
  char *zName;                    /* Name of table or index */
};

/*
** An instance of the virtual machine.  This structure contains the complete
** state of the virtual machine.
**
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
** is really a pointer to an instance of this structure.
................................................................................
  VdbeFrame *pDelFrame;   /* List of frame objects to free on VM reset */
  int nFrame;             /* Number of frames in pFrame list */
  u32 expmask;            /* Binding to these vars invalidates VM */
  SubProgram *pProgram;   /* Linked list of all sub-programs used by VM */
  int nOnceFlag;          /* Size of array aOnceFlag[] */
  u8 *aOnceFlag;          /* Flags for OP_Once */
  AuxData *pAuxData;      /* Linked list of auxdata allocations */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  i64 *anExec;            /* Number of times each op has been executed */
  int nScan;              /* Entries in aScan[] */
  ScanCounter *aScan;     /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif
};

/*
** The following are allowed values for Vdbe.magic
*/
#define VDBE_MAGIC_INIT     0x26bceaa5    /* Building a VDBE program */
#define VDBE_MAGIC_RUN      0xbdf20da3    /* VDBE is ready to execute */

Changes to src/vdbeapi.c.

1472
1473
1474
1475
1476
1477
1478

1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499






1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512

1513
1514
  }
#endif
  v = pVdbe->aCounter[op];
  if( resetFlag ) pVdbe->aCounter[op] = 0;
  return (int)v;
}


/*
** Return status data for a single loop within query pStmt.
*/
int sqlite3_stmt_scanstatus(
  sqlite3_stmt *pStmt,
  int idx,                        /* Index of loop to report on */
  sqlite3_int64 *pnLoop,          /* OUT: Number of times loop was run */
  sqlite3_int64 *pnVisit,         /* OUT: Number of rows visited (all loops) */
  sqlite3_int64 *pnEst,           /* OUT: Number of rows estimated (per loop) */
  const char **pzName,            /* OUT: Object name (table or index) */
  const char **pzExplain          /* OUT: EQP string */
){
  Vdbe *p = (Vdbe*)pStmt;
  ExplainArg *pExplain;
  if( idx<0 || idx>=p->nExplain ) return 1;
  pExplain = p->apExplain[idx];
  if( pnLoop ) *pnLoop = pExplain->nLoop;
  if( pnVisit ) *pnVisit = pExplain->nVisit;
  if( pnEst ) *pnEst = pExplain->nEst;
  if( *pzName ) *pzName = pExplain->zName;
  if( *pzExplain ) *pzExplain = pExplain->zExplain;






  return 0;
}

/*
** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
*/
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
  Vdbe *p = (Vdbe*)pStmt;
  int i;
  for(i=0; i<p->nExplain; i++){
    p->apExplain[i]->nLoop = 0;
    p->apExplain[i]->nVisit = 0;
  }

}








>













|
|
|
|
|
|
|
|
>
>
>
>
>
>








|
<
<
<
|
>
|
<
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515



1516
1517
1518

  }
#endif
  v = pVdbe->aCounter[op];
  if( resetFlag ) pVdbe->aCounter[op] = 0;
  return (int)v;
}

#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
** Return status data for a single loop within query pStmt.
*/
int sqlite3_stmt_scanstatus(
  sqlite3_stmt *pStmt,
  int idx,                        /* Index of loop to report on */
  sqlite3_int64 *pnLoop,          /* OUT: Number of times loop was run */
  sqlite3_int64 *pnVisit,         /* OUT: Number of rows visited (all loops) */
  sqlite3_int64 *pnEst,           /* OUT: Number of rows estimated (per loop) */
  const char **pzName,            /* OUT: Object name (table or index) */
  const char **pzExplain          /* OUT: EQP string */
){
  Vdbe *p = (Vdbe*)pStmt;
  ScanCounter *pScan;
  if( idx<0 || idx>=p->nScan ) return 1;
  pScan = &p->aScan[idx];
  if( pnLoop ) *pnLoop = p->anExec[pScan->addrLoop];
  if( pnVisit ) *pnVisit = p->anExec[pScan->addrVisit];
  if( pnEst ) *pnEst = pScan->nEst;
  if( *pzName ) *pzName = pScan->zName;
  if( *pzExplain ){
    if( pScan->addrExplain ){
      *pzExplain = p->aOp[ pScan->addrExplain ].p4.z;
    }else{
      *pzExplain = 0;
    }
  }
  return 0;
}

/*
** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
*/
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
  Vdbe *p = (Vdbe*)pStmt;
  memset(p->anExec, 0, p->nOp * sizeof(i64));



}
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */


Changes to src/vdbeaux.c.

592
593
594
595
596
597
598




























599
600
601
602
603
604
605
...
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
...
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
....
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
....
1709
1710
1711
1712
1713
1714
1715




1716
1717
1718
1719
1720
1721
1722
....
2693
2694
2695
2696
2697
2698
2699

2700





2701
2702
2703
2704
2705
2706
2707
      }
#endif
    }
    p->nOp += nOp;
  }
  return addr;
}





























/*
** Change the value of the P1 operand for a specific instruction.
** This routine is useful when a large program is loaded from a
** static array using sqlite3VdbeAddOpList but we want to make a
** few minor changes to the program.
*/
................................................................................
/*
** Delete a P4 value if necessary.
*/
static void freeP4(sqlite3 *db, int p4type, void *p4){
  if( p4 ){
    assert( db );
    switch( p4type ){
      case P4_EXPLAIN:
      case P4_REAL:
      case P4_INT64:
      case P4_DYNAMIC:
      case P4_INTARRAY: {
        sqlite3DbFree(db, p4);
        break;
      }
................................................................................
    pOp->p4.p = (void*)zP4;
    pOp->p4type = P4_KEYINFO;
  }else if( n==P4_VTAB ){
    pOp->p4.p = (void*)zP4;
    pOp->p4type = P4_VTAB;
    sqlite3VtabLock((VTable *)zP4);
    assert( ((VTable *)zP4)->db==p->db );
  }else if( n==P4_EXPLAIN ){
    pOp->p4.p = (void*)zP4;
    pOp->p4type = P4_EXPLAIN;
    p->apExplain = (ExplainArg**)sqlite3DbReallocOrFree(
        p->db, p->apExplain, sizeof(ExplainArg*) * p->nExplain + 1
    );
    if( p->apExplain ) p->apExplain[p->nExplain++] = (ExplainArg*)zP4;
  }else if( n<0 ){
    pOp->p4.p = (void*)zP4;
    pOp->p4type = (signed char)n;
  }else{
    if( n==0 ) n = sqlite3Strlen30(zP4);
    pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
    pOp->p4type = P4_DYNAMIC;
................................................................................
      sqlite3_snprintf(nTemp, zTemp, "program");
      break;
    }
    case P4_ADVANCE: {
      zTemp[0] = 0;
      break;
    }
    case P4_EXPLAIN: {
      zP4 = (char*)pOp->p4.pExplain->zExplain;
      break;
    }
    default: {
      zP4 = pOp->p4.z;
      if( zP4==0 ){
        zP4 = zTemp;
        zTemp[0] = 0;
      }
    }
................................................................................
    p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
    if( nByte ){
      p->pFree = sqlite3DbMallocZero(db, nByte);
    }
    zCsr = p->pFree;
    zEnd = &zCsr[nByte];
  }while( nByte && !db->mallocFailed );





  p->nCursor = nCursor;
  p->nOnceFlag = nOnce;
  if( p->aVar ){
    p->nVar = (ynVar)nVar;
    for(n=0; n<nVar; n++){
      p->aVar[n].flags = MEM_Null;
................................................................................
    sqlite3DbFree(db, pSub);
  }
  for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
  vdbeFreeOpArray(db, p->aOp, p->nOp);
  sqlite3DbFree(db, p->aColName);
  sqlite3DbFree(db, p->zSql);
  sqlite3DbFree(db, p->pFree);

  sqlite3DbFree(db, p->apExplain);





}

/*
** Delete an entire VDBE.
*/
void sqlite3VdbeDelete(Vdbe *p){
  sqlite3 *db;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<







 







<
<
<
<
<
<
<







 







<
<
<
<







 







>
>
>
>







 







>
|
>
>
>
>
>







592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
...
696
697
698
699
700
701
702

703
704
705
706
707
708
709
...
844
845
846
847
848
849
850







851
852
853
854
855
856
857
....
1127
1128
1129
1130
1131
1132
1133




1134
1135
1136
1137
1138
1139
1140
....
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
....
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
      }
#endif
    }
    p->nOp += nOp;
  }
  return addr;
}

#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
/*
** Add an entry to the array of counters managed by sqlite3_stmt_scanstatus().
*/
void sqlite3VdbeScanCounter(
  Vdbe *p,                        /* VM to add scanstatus() to */
  int addrExplain,                /* Address of OP_Explain (or 0) */
  int addrLoop,                   /* Address of loop counter */ 
  int addrVisit,                  /* Address of rows visited counter */
  i64 nEst,                       /* Estimated number of rows */
  const char *zName               /* Name of table or index being scanned */
){
  int nByte = (p->nScan+1) * sizeof(ScanCounter);
  ScanCounter *aNew;
  aNew = (ScanCounter*)sqlite3DbRealloc(p->db, p->aScan, nByte);
  if( aNew ){
    ScanCounter *pNew = &aNew[p->nScan++];
    pNew->addrExplain = addrExplain;
    pNew->addrLoop = addrLoop;
    pNew->addrVisit = addrVisit;
    pNew->nEst = nEst;
    pNew->zName = sqlite3DbStrDup(p->db, zName);
    p->aScan = aNew;
  }
}
#endif


/*
** Change the value of the P1 operand for a specific instruction.
** This routine is useful when a large program is loaded from a
** static array using sqlite3VdbeAddOpList but we want to make a
** few minor changes to the program.
*/
................................................................................
/*
** Delete a P4 value if necessary.
*/
static void freeP4(sqlite3 *db, int p4type, void *p4){
  if( p4 ){
    assert( db );
    switch( p4type ){

      case P4_REAL:
      case P4_INT64:
      case P4_DYNAMIC:
      case P4_INTARRAY: {
        sqlite3DbFree(db, p4);
        break;
      }
................................................................................
    pOp->p4.p = (void*)zP4;
    pOp->p4type = P4_KEYINFO;
  }else if( n==P4_VTAB ){
    pOp->p4.p = (void*)zP4;
    pOp->p4type = P4_VTAB;
    sqlite3VtabLock((VTable *)zP4);
    assert( ((VTable *)zP4)->db==p->db );







  }else if( n<0 ){
    pOp->p4.p = (void*)zP4;
    pOp->p4type = (signed char)n;
  }else{
    if( n==0 ) n = sqlite3Strlen30(zP4);
    pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
    pOp->p4type = P4_DYNAMIC;
................................................................................
      sqlite3_snprintf(nTemp, zTemp, "program");
      break;
    }
    case P4_ADVANCE: {
      zTemp[0] = 0;
      break;
    }




    default: {
      zP4 = pOp->p4.z;
      if( zP4==0 ){
        zP4 = zTemp;
        zTemp[0] = 0;
      }
    }
................................................................................
    p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
    if( nByte ){
      p->pFree = sqlite3DbMallocZero(db, nByte);
    }
    zCsr = p->pFree;
    zEnd = &zCsr[nByte];
  }while( nByte && !db->mallocFailed );

#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
    p->anExec = (i64*)sqlite3DbMallocZero(db, p->nOp*sizeof(i64));
#endif

  p->nCursor = nCursor;
  p->nOnceFlag = nOnce;
  if( p->aVar ){
    p->nVar = (ynVar)nVar;
    for(n=0; n<nVar; n++){
      p->aVar[n].flags = MEM_Null;
................................................................................
    sqlite3DbFree(db, pSub);
  }
  for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
  vdbeFreeOpArray(db, p->aOp, p->nOp);
  sqlite3DbFree(db, p->aColName);
  sqlite3DbFree(db, p->zSql);
  sqlite3DbFree(db, p->pFree);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  sqlite3DbFree(db, p->anExec);
  for(i=0; i<p->nScan; i++){
    sqlite3DbFree(db, p->aScan[i].zName);
  }
  sqlite3DbFree(db, p->aScan);
#endif
}

/*
** Delete an entire VDBE.
*/
void sqlite3VdbeDelete(Vdbe *p){
  sqlite3 *db;

Changes to src/where.c.

2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
....
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819

2820
2821
2822


2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837

2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869



2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
....
2923
2924
2925
2926
2927
2928
2929
2930
2931

2932



2933
2934
2935
2936
2937
2938
2939

2940
2941

2942
2943

2944
2945
2946
2947
2948
2949


2950







2951


2952



2953
2954
2955
2956



2957
2958
2959
2960
2961
2962
2963
....
3617
3618
3619
3620
3621
3622
3623
3624

3625
3626

3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
....
3755
3756
3757
3758
3759
3760
3761




3762
3763
3764
3765
3766
3767
3768
....
6457
6458
6459
6460
6461
6462
6463


6464

6465
6466
6467
6468
6469
6470
6471
6472


6473
6474
6475



6476
6477
6478
6479
6480
6481
6482
      }
    }
  }
  *pzAff = zAff;
  return regBase;
}

#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_STMT_SCANSTATUS)
/*
** This routine is a helper for explainIndexRange() below
**
** pStr holds the text of an expression that we are building up one term
** at a time.  This routine adds a new term to the end of the expression.
** Terms are separated by AND so add the "AND" text for second and subsequent
** terms only.
................................................................................

/*
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single
** record is added to the output to describe the table scan strategy in 
** pLevel.
*/
static void explainOneScan(
  Parse *pParse,                  /* Parse context */
  SrcList *pTabList,              /* Table list this loop refers to */
  WhereInfo *pWInfo,              /* WHERE clause this loop belongs to */
  int iLevel,                     /* Value for "level" column of output */

  u16 wctrlFlags                  /* Flags passed to sqlite3WhereBegin() */
){
#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)


  if( pParse->explain==2 )
#endif
  {
    WhereLevel *pLevel = &pWInfo->a[iLevel];
    struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
    Vdbe *v = pParse->pVdbe;      /* VM being constructed */
    sqlite3 *db = pParse->db;     /* Database handle */
#ifdef SQLITE_OMIT_EXPLAIN
    int iId = 0;                  /* Select id (left-most output column) */
#else
    int iId = pParse->iSelectId;   /* Select id (left-most output column) */
#endif
    int isSearch;                 /* True for a SEARCH. False for SCAN. */
    WhereLoop *pLoop;             /* The controlling WhereLoop object */
    u32 flags;                    /* Flags that describe this loop */

    StrAccum str;                 /* EQP output string */
    char zBuf[100];               /* Initial space for EQP output string */
    const char *zObj;
    ExplainArg *pExplain;
    i64 nEstRow;                  /* Estimated rows per scan of pLevel */

    pLoop = pLevel->pWLoop;
    flags = pLoop->wsFlags;
    if( (flags&WHERE_MULTI_OR) ) return;

    sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
    str.db = db;

    /* Reserve space at the start of the buffer managed by the StrAccum
    ** object for *pExplain.  */
    assert( sizeof(*pExplain)<=sizeof(zBuf) );
    str.nChar = sizeof(*pExplain);

    /* Append the object (table or index) name to the buffer. */
    if( pItem->pSelect ){
      zObj = "";
    }else if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
      zObj = pLoop->u.btree.pIndex->zName;
    }else{
      zObj = pItem->zName;
    }
    sqlite3StrAccumAppend(&str, zObj, sqlite3Strlen30(zObj)+1);

    /* Append the EQP text to the buffer */
    isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
            || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
            || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));



    sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
    if( pItem->pSelect ){
#ifdef SQLITE_OMIT_EXPLAIN
      sqlite3XPrintf(&str, 0, " SUBQUERY 0");
#else
      sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
#endif
    }else{
      sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
    }

    if( pItem->zAlias ){
      sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias);
    }
................................................................................
    }
#ifndef SQLITE_OMIT_VIRTUALTABLE
    else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
      sqlite3XPrintf(&str, 0, " VIRTUAL TABLE INDEX %d:%s",
                  pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
    }
#endif
    nEstRow = pLoop->nOut>=10 ? sqlite3LogEstToInt(pLoop->nOut) : 1;
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS

    sqlite3XPrintf(&str, 0, " (~%llu rows)", nEstRow);



#endif
    pExplain = (ExplainArg*)sqlite3StrAccumFinish(&str);
    assert( pExplain || db->mallocFailed );
    if( pExplain ){
      memset(pExplain, 0, sizeof(*pExplain));
      if( pLoop->wsFlags & WHERE_INDEXED ){
        pExplain->iCsr = pLevel->iIdxCur;

      }else if( pItem->pSelect==0 ){
        pExplain->iCsr = pLevel->iTabCur;

      }else{
        pExplain->iCsr = -1;

      }
      pExplain->nEst = nEstRow;
      pExplain->zName = (const char*)&pExplain[1];
      pExplain->zExplain = &pExplain->zName[sqlite3Strlen30(pExplain->zName)+1];
      pWInfo->iExplain = sqlite3VdbeAddOp4(v, OP_Explain, 
          iId, iLevel, pLevel->iFrom, (char*)pExplain,P4_EXPLAIN


      );







    }


  }



}
#else
# define explainOneScan(v,w,x,y,z)
#endif /* !SQLITE_OMIT_EXPLAIN || SQLITE_ENABLE_STMT_SCANSTATUS */





/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
*/
static Bitmask codeOneLoopStart(
................................................................................
        /* Loop through table entries that match term pOrTerm. */
        WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
        pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
                                      wctrlFlags, iCovCur);
        assert( pSubWInfo || pParse->nErr || db->mallocFailed );
        if( pSubWInfo ){
          WhereLoop *pSubLoop;


          /* If an OP_Explain was added for this sub-loop, fix the P2 and
          ** P3 parameters to it so that they are relative to the current

          ** context.  */
          if( pSubWInfo->iExplain!=0 ){
            sqlite3VdbeChangeP2(v, pSubWInfo->iExplain, iLevel);
            sqlite3VdbeChangeP3(v, pSubWInfo->iExplain, pLevel->iFrom);
          }

          /* This is the sub-WHERE clause body.  First skip over
          ** duplicate rows from prior sub-WHERE clauses, and record the
          ** rowid (or PRIMARY KEY) for the current row so that the same
          ** row will be skipped in subsequent sub-WHERE clauses.
          */
          if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
................................................................................
      pLevel->p1 = iCur;
      pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
      VdbeCoverageIf(v, bRev==0);
      VdbeCoverageIf(v, bRev!=0);
      pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
    }
  }





  /* Insert code to test every subexpression that can be completely
  ** computed using the current set of tables.
  */
  for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
    Expr *pE;
    testcase( pTerm->wtFlags & TERM_VIRTUAL );
................................................................................

  /* Generate the code to do the search.  Each iteration of the for
  ** loop below generates code for a single nested loop of the VM
  ** program.
  */
  notReady = ~(Bitmask)0;
  for(ii=0; ii<nTabList; ii++){


    pLevel = &pWInfo->a[ii];

#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
    if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
      constructAutomaticIndex(pParse, &pWInfo->sWC,
                &pTabList->a[pLevel->iFrom], notReady, pLevel);
      if( db->mallocFailed ) goto whereBeginError;
    }
#endif
    explainOneScan(pParse, pTabList, pWInfo, ii, wctrlFlags);


    pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
    notReady = codeOneLoopStart(pWInfo, ii, notReady);
    pWInfo->iContinue = pLevel->addrCont;



  }

  /* Done. */
  VdbeModuleComment((v, "Begin WHERE-core"));
  return pWInfo;

  /* Jump here if malloc fails */







|







 







|


|

>


<
>
>



<



<
|
<
<
<



>


<
<
<



|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



>
>
>


<
<
<

<







 







<

>
|
>
>
>

|
<
<
<
|
<
>
|
<
>
|
|
>
|
<
|
|
|
|
>
>
|
>
>
>
>
>
>
>
|
>
>

>
>
>


<
<
>
>
>







 







<
>
|
<
>
|
<
<
<
<







 







>
>
>
>







 







>
>

>







|
>
>



>
>
>







2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
....
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822

2823
2824
2825
2826
2827

2828
2829
2830

2831



2832
2833
2834
2835
2836
2837



2838
2839
2840
2841
2842



















2843
2844
2845
2846
2847
2848
2849
2850



2851

2852
2853
2854
2855
2856
2857
2858
....
2898
2899
2900
2901
2902
2903
2904

2905
2906
2907
2908
2909
2910
2911
2912



2913

2914
2915

2916
2917
2918
2919
2920

2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943


2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
....
3607
3608
3609
3610
3611
3612
3613

3614
3615

3616
3617




3618
3619
3620
3621
3622
3623
3624
....
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
....
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
      }
    }
  }
  *pzAff = zAff;
  return regBase;
}

#ifndef SQLITE_OMIT_EXPLAIN
/*
** This routine is a helper for explainIndexRange() below
**
** pStr holds the text of an expression that we are building up one term
** at a time.  This routine adds a new term to the end of the expression.
** Terms are separated by AND so add the "AND" text for second and subsequent
** terms only.
................................................................................

/*
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single
** record is added to the output to describe the table scan strategy in 
** pLevel.
*/
static int explainOneScan(
  Parse *pParse,                  /* Parse context */
  SrcList *pTabList,              /* Table list this loop refers to */
  WhereLevel *pLevel,             /* Scan to write OP_Explain opcode for */
  int iLevel,                     /* Value for "level" column of output */
  int iFrom,                      /* Value for "from" column of output */
  u16 wctrlFlags                  /* Flags passed to sqlite3WhereBegin() */
){

  int ret = 0;
#ifndef SQLITE_DEBUG
  if( pParse->explain==2 )
#endif
  {

    struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
    Vdbe *v = pParse->pVdbe;      /* VM being constructed */
    sqlite3 *db = pParse->db;     /* Database handle */

    int iId = pParse->iSelectId;  /* Select id (left-most output column) */



    int isSearch;                 /* True for a SEARCH. False for SCAN. */
    WhereLoop *pLoop;             /* The controlling WhereLoop object */
    u32 flags;                    /* Flags that describe this loop */
    char *zMsg;                   /* Text to add to EQP output */
    StrAccum str;                 /* EQP output string */
    char zBuf[100];               /* Initial space for EQP output string */




    pLoop = pLevel->pWLoop;
    flags = pLoop->wsFlags;
    if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0;




















    isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
            || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
            || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));

    sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
    str.db = db;
    sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
    if( pItem->pSelect ){



      sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);

    }else{
      sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
    }

    if( pItem->zAlias ){
      sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias);
    }
................................................................................
    }
#ifndef SQLITE_OMIT_VIRTUALTABLE
    else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
      sqlite3XPrintf(&str, 0, " VIRTUAL TABLE INDEX %d:%s",
                  pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
    }
#endif

#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
    if( pLoop->nOut>=10 ){
      sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
    }else{
      sqlite3StrAccumAppend(&str, " (~1 row)", 9);
    }
#endif
    zMsg = sqlite3StrAccumFinish(&str);



    ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);

  }
  return ret;

}
#else
# define explainOneScan(u,v,w,x,y,z) 0
#endif /* SQLITE_OMIT_EXPLAIN */


#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
static void addScanStatus(
  Vdbe *v, 
  SrcList *pSrclist,
  WhereLevel *pLvl,
  int addrExplain
){
  const char *zObj = 0;
  i64 nEst = 1;
  WhereLoop *pLoop = pLvl->pWLoop;
  if( (pLoop->wsFlags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
    zObj = pLoop->u.btree.pIndex->zName;
  }else{
    zObj = pSrclist->a[pLvl->iFrom].zName;
  }
  if( pLoop->nOut>=10 ){
    nEst = sqlite3LogEstToInt(pLoop->nOut);
  }
  sqlite3VdbeScanCounter(
      v, addrExplain, pLvl->addrBody, pLvl->addrVisit, nEst, zObj
  );
}
#else


# define addScanStatus(a, b, c, d)
#endif



/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
*/
static Bitmask codeOneLoopStart(
................................................................................
        /* Loop through table entries that match term pOrTerm. */
        WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
        pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
                                      wctrlFlags, iCovCur);
        assert( pSubWInfo || pParse->nErr || db->mallocFailed );
        if( pSubWInfo ){
          WhereLoop *pSubLoop;

          int addrExplain = explainOneScan(
              pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0

          );
          addScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);





          /* This is the sub-WHERE clause body.  First skip over
          ** duplicate rows from prior sub-WHERE clauses, and record the
          ** rowid (or PRIMARY KEY) for the current row so that the same
          ** row will be skipped in subsequent sub-WHERE clauses.
          */
          if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
................................................................................
      pLevel->p1 = iCur;
      pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
      VdbeCoverageIf(v, bRev==0);
      VdbeCoverageIf(v, bRev!=0);
      pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
    }
  }

#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  pLevel->addrVisit = sqlite3VdbeCurrentAddr(v);
#endif

  /* Insert code to test every subexpression that can be completely
  ** computed using the current set of tables.
  */
  for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
    Expr *pE;
    testcase( pTerm->wtFlags & TERM_VIRTUAL );
................................................................................

  /* Generate the code to do the search.  Each iteration of the for
  ** loop below generates code for a single nested loop of the VM
  ** program.
  */
  notReady = ~(Bitmask)0;
  for(ii=0; ii<nTabList; ii++){
    int addrExplain;
    int wsFlags;
    pLevel = &pWInfo->a[ii];
    wsFlags = pLevel->pWLoop->wsFlags;
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
    if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
      constructAutomaticIndex(pParse, &pWInfo->sWC,
                &pTabList->a[pLevel->iFrom], notReady, pLevel);
      if( db->mallocFailed ) goto whereBeginError;
    }
#endif
    addrExplain = explainOneScan(
        pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
    );
    pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
    notReady = codeOneLoopStart(pWInfo, ii, notReady);
    pWInfo->iContinue = pLevel->addrCont;
    if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){
      addScanStatus(v, pTabList, pLevel, addrExplain);
    }
  }

  /* Done. */
  VdbeModuleComment((v, "Begin WHERE-core"));
  return pWInfo;

  /* Jump here if malloc fails */

Changes to src/whereInt.h.

81
82
83
84
85
86
87



88
89
90
91
92
93
94
        u8 eEndLoopOp;         /* IN Loop terminator. OP_Next or OP_Prev */
      } *aInLoop;           /* Information about each nested IN operator */
    } in;                 /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
    Index *pCovidx;       /* Possible covering index for WHERE_MULTI_OR */
  } u;
  struct WhereLoop *pWLoop;  /* The selected WhereLoop object */
  Bitmask notReady;          /* FROM entries not usable at this level */



};

/*
** Each instance of this object represents an algorithm for evaluating one
** term of a join.  Every term of the FROM clause will have at least
** one corresponding WhereLoop object (unless INDEXED BY constraints
** prevent a query solution - which is an error) and many terms of the







>
>
>







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
        u8 eEndLoopOp;         /* IN Loop terminator. OP_Next or OP_Prev */
      } *aInLoop;           /* Information about each nested IN operator */
    } in;                 /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
    Index *pCovidx;       /* Possible covering index for WHERE_MULTI_OR */
  } u;
  struct WhereLoop *pWLoop;  /* The selected WhereLoop object */
  Bitmask notReady;          /* FROM entries not usable at this level */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  int addrVisit;        /* Address at which row is visited */
#endif
};

/*
** Each instance of this object represents an algorithm for evaluating one
** term of a join.  Every term of the FROM clause will have at least
** one corresponding WhereLoop object (unless INDEXED BY constraints
** prevent a query solution - which is an error) and many terms of the

Changes to test/scanstatus.test.

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
183
184
185
186
187
188
189





190




191
192
193


194
















195
196
197
198






199
200
201
  zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j=?)}
}

do_execsql_test 2.4.1 {
  SELECT * FROM x1 WHERE j<'two'
} {4 four 1 one 3 three}
do_scanstatus_test 2.4.2 {
  nLoop 1 nVisit 4 nEst 262144 zName x1j 
  zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j<?)}
}

do_execsql_test 2.5.1 {
  SELECT * FROM x1 WHERE j>='two'
} {2 two}
do_scanstatus_test 2.5.2 {
................................................................................
  zExplain {SEARCH TABLE x2 USING INDEX x2j (j>? AND j<?)}
}

do_execsql_test 2.8.1 {
  SELECT * FROM x2 WHERE i=1 AND j='two'
}
do_scanstatus_test 2.8.2 {
  nLoop 1 nVisit 1 nEst 8 zName x2ij 
  zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
}

do_execsql_test 2.9.1 {
  SELECT * FROM x2 WHERE i=5 AND j='two'
}
do_scanstatus_test 2.9.2 {
................................................................................
  zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
}

do_execsql_test 2.10.1 {
  SELECT * FROM x2 WHERE i=3 AND j='three'
} {3 three {3 three}}
do_scanstatus_test 2.10.2 {
  nLoop 1 nVisit 2 nEst 8 zName x2ij 
  zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
}

#-------------------------------------------------------------------------
# Try with queries that use the OR optimization.
#
do_execsql_test 3.1 {
................................................................................
  WITH d(x) AS (SELECT 1 UNION ALL SELECT x+1 AS n FROM d WHERE n<=100)
  INSERT INTO a1 SELECT x, x, x, x FROM d;
}

do_execsql_test 3.2.1 {
  SELECT d FROM a1 WHERE (a=4 OR b=13)
} {4 13}










do_scanstatus_test 2.4 {
  nLoop 1 nVisit 2 nEst 10 zName a1a 
  zExplain {SEARCH TABLE a1 USING INDEX a1a (a=?)}



















  nLoop 1 nVisit 2 nEst 10 zName a1bc 
  zExplain {SEARCH TABLE a1 USING INDEX a1bc (b=?)}
}









finish_test







|







 







|







 







|







 







>
>
>
>
>
|
>
>
>
>
|
|
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|


>
>
>
>
>
>
|


111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j=?)}
}

do_execsql_test 2.4.1 {
  SELECT * FROM x1 WHERE j<'two'
} {4 four 1 one 3 three}
do_scanstatus_test 2.4.2 {
  nLoop 1 nVisit 3 nEst 262144 zName x1j 
  zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j<?)}
}

do_execsql_test 2.5.1 {
  SELECT * FROM x1 WHERE j>='two'
} {2 two}
do_scanstatus_test 2.5.2 {
................................................................................
  zExplain {SEARCH TABLE x2 USING INDEX x2j (j>? AND j<?)}
}

do_execsql_test 2.8.1 {
  SELECT * FROM x2 WHERE i=1 AND j='two'
}
do_scanstatus_test 2.8.2 {
  nLoop 1 nVisit 0 nEst 8 zName x2ij 
  zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
}

do_execsql_test 2.9.1 {
  SELECT * FROM x2 WHERE i=5 AND j='two'
}
do_scanstatus_test 2.9.2 {
................................................................................
  zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
}

do_execsql_test 2.10.1 {
  SELECT * FROM x2 WHERE i=3 AND j='three'
} {3 three {3 three}}
do_scanstatus_test 2.10.2 {
  nLoop 1 nVisit 1 nEst 8 zName x2ij 
  zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
}

#-------------------------------------------------------------------------
# Try with queries that use the OR optimization.
#
do_execsql_test 3.1 {
................................................................................
  WITH d(x) AS (SELECT 1 UNION ALL SELECT x+1 AS n FROM d WHERE n<=100)
  INSERT INTO a1 SELECT x, x, x, x FROM d;
}

do_execsql_test 3.2.1 {
  SELECT d FROM a1 WHERE (a=4 OR b=13)
} {4 13}
do_scanstatus_test 3.2.2 {
  nLoop 1 nVisit 1 nEst 10 zName a1a 
  zExplain {SEARCH TABLE a1 USING INDEX a1a (a=?)}
  nLoop 1 nVisit 1 nEst 10 zName a1bc 
  zExplain {SEARCH TABLE a1 USING INDEX a1bc (b=?)}
}

do_execsql_test 3.2.1 {
  SELECT count(*) FROM a1 WHERE (a BETWEEN 4 AND 12) OR (b BETWEEN 40 AND 60)
} {30}
do_scanstatus_test 3.2.2 {
  nLoop 1 nVisit 9 nEst 16384 zName a1a 
  zExplain {SEARCH TABLE a1 USING INDEX a1a (a>? AND a<?)}
  nLoop 1 nVisit 21 nEst 16384 zName a1bc
  zExplain {SEARCH TABLE a1 USING INDEX a1bc (b>? AND b<?)}
}

do_execsql_test 3.3.1 {
  SELECT count(*) FROM a1 AS x, a1 AS y 
  WHERE (x.a BETWEEN 4 AND 12) AND (y.b BETWEEN 1 AND 10)
} {90}
do_scanstatus_test 3.2.2 {
  nLoop 1 nVisit 10 nEst 16384 zName a1bc 
  zExplain {SEARCH TABLE a1 AS y USING COVERING INDEX a1bc (b>? AND b<?)}
  nLoop 10 nVisit 90 nEst 16384 zName a1a
  zExplain {SEARCH TABLE a1 AS x USING COVERING INDEX a1a (a>? AND a<?)}
}

do_execsql_test 3.4.1 {
  SELECT count(*) FROM a1 WHERE a IN (1, 5, 10, 15);
} {4}
do_scanstatus_test 3.4.2 {
  nLoop 1 nVisit 4 nEst 40 zName a1a 
  zExplain {SEARCH TABLE a1 USING COVERING INDEX a1a (a=?)}
}

do_execsql_test 3.4.1 {
  SELECT count(*) FROM a1 WHERE rowid IN (1, 5, 10, 15);
} {4}
do_scanstatus_test 3.4.2 {
  nLoop 1 nVisit 4 nEst 4 zName a1
  zExplain {SEARCH TABLE a1 USING INTEGER PRIMARY KEY (rowid=?)}
}

finish_test