/ Check-in [061b8006]
Login

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

Overview
Comment:Fix where.c handling of "IN (SELECT ...)" expressions when the SELECT returns more than one result column. Also error handling for other row value constructor cases.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rowvalue
Files: files | file ages | folders
SHA1:061b8006034f06a0311b4304c8b14d2c8b0153df
User & Date: dan 2016-07-26 18:06:08
References
2016-09-16
13:57 New ticket [0eab1ac7] O(N*N) behavior where it should be O(N). artifact: 62ca7f9b user: drh
Context
2016-07-26
18:15
Merge latest trunk changes into this branch. check-in: d4f3d52c user: dan tags: rowvalue
18:06
Fix where.c handling of "IN (SELECT ...)" expressions when the SELECT returns more than one result column. Also error handling for other row value constructor cases. check-in: 061b8006 user: dan tags: rowvalue
2016-07-23
20:24
Allow vector IN(SELECT ...) expressions to use an index if either all the indexed columns are declared NOT NULL or if there is no difference between the expression evaluating to 0 and NULL (as in a WHERE clause). check-in: e2fd6f49 user: dan tags: rowvalue
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/expr.c.

334
335
336
337
338
339
340













341
342
343
344
345
346
347
...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
....
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078













2079
2080
2081
2082
2083
2084
2085
....
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
....
3346
3347
3348
3349
3350
3351
3352

3353
3354



3355

3356
3357
3358
3359
3360
3361
3362
    assert( i==0 );
    return pVector;
  }else if( pVector->flags & EP_xIsSelect ){
    return pVector->x.pSelect->pEList->a[i].pExpr;
  }
  return pVector->x.pList->a[i].pExpr;
}














static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){
  Vdbe *v = pParse->pVdbe;
  Expr *pLeft = pExpr->pLeft;
  Expr *pRight = pExpr->pRight;
  int nLeft = sqlite3ExprVectorSize(pLeft);
  int nRight = sqlite3ExprVectorSize(pRight);
................................................................................
      case TK_GE:
        opCmp = OP_Cmp;
        opTest = OP_CmpTest;
        p3 = pExpr->op;
        break;
    }

    if( pLeft->flags & EP_xIsSelect ){
      assert( pLeft->op==TK_SELECT || pLeft->op==TK_REGISTER );
      regLeft = sqlite3ExprCodeTarget(pParse, pLeft, 1);
      assert( regLeft!=1 );
    }
    if( pRight->flags & EP_xIsSelect ){
      assert( pRight->op==TK_SELECT || pRight->op==TK_REGISTER );
      regRight = sqlite3ExprCodeTarget(pParse, pRight, 1);
      assert( regRight!=1 );
    }
    if( pParse->nErr ) return;

    for(i=0; i<nLeft; i++){
      int regFree1 = 0, regFree2 = 0;
      Expr *pL, *pR; 
      int r1, r2;

................................................................................

  zRet = sqlite3DbMallocZero(pParse->db, nVal+1);
  if( zRet ){
    int i;
    for(i=0; i<nVal; i++){
      Expr *pA;
      char a;
      if( nVal==1 ){
        pA = pLeft;
      }else{    
        pA = exprVectorField(pLeft, i);
      }
      a = sqlite3ExprAffinity(pA);
      zRet[i] = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[i].pExpr, a);
    }
    zRet[nVal] = '\0';
  }
  return zRet;
}














/*
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
** or IN operators.  Examples:
**
**     (SELECT a FROM b)          -- subquery
**     EXISTS (SELECT a FROM b)   -- EXISTS subquery
................................................................................
        ** table allocated and opened above.
        */
        Select *pSelect = pExpr->x.pSelect;
        ExprList *pEList = pSelect->pEList;

        assert( !isRowid );
        if( pEList->nExpr!=nVal ){
          sqlite3ErrorMsg(pParse, "SELECT has %d columns - expected %d",
              pEList->nExpr, nVal);
        }else{
          SelectDest dest;
          int i;
          sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
          dest.zAffSdst = exprINAffinity(pParse, pExpr);
          assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
          pSelect->iLimit = 0;
................................................................................
        sqlite3ReleaseTempRange(pParse, r1, nFarg);
      }
      break;
    }
#ifndef SQLITE_OMIT_SUBQUERY
    case TK_EXISTS:
    case TK_SELECT: {

      testcase( op==TK_EXISTS );
      testcase( op==TK_SELECT );



      inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);

      break;
    }
    case TK_IN: {
      int destIfFalse = sqlite3VdbeMakeLabel(v);
      int destIfNull = sqlite3VdbeMakeLabel(v);
      sqlite3VdbeAddOp2(v, OP_Null, 0, target);
      sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);







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







 







|
|
<
<
<
<
<
<
<
<







 







|











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







 







<
|







 







>


>
>
>
|
>







334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
...
398
399
400
401
402
403
404
405
406








407
408
409
410
411
412
413
....
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
....
2194
2195
2196
2197
2198
2199
2200

2201
2202
2203
2204
2205
2206
2207
2208
....
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
    assert( i==0 );
    return pVector;
  }else if( pVector->flags & EP_xIsSelect ){
    return pVector->x.pSelect->pEList->a[i].pExpr;
  }
  return pVector->x.pList->a[i].pExpr;
}

static int exprVectorSubselect(Parse *pParse, Expr *pExpr){
  int reg = 0;
  if( pExpr->flags & EP_xIsSelect ){
    assert( pExpr->op==TK_REGISTER || pExpr->op==TK_SELECT );
    if( pExpr->op==TK_REGISTER ){
      reg = pExpr->iTable;
    }else{
      reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
    }
  }
  return reg;
}

static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){
  Vdbe *v = pParse->pVdbe;
  Expr *pLeft = pExpr->pLeft;
  Expr *pRight = pExpr->pRight;
  int nLeft = sqlite3ExprVectorSize(pLeft);
  int nRight = sqlite3ExprVectorSize(pRight);
................................................................................
      case TK_GE:
        opCmp = OP_Cmp;
        opTest = OP_CmpTest;
        p3 = pExpr->op;
        break;
    }

    regLeft = exprVectorSubselect(pParse, pLeft);
    regRight = exprVectorSubselect(pParse, pRight);








    if( pParse->nErr ) return;

    for(i=0; i<nLeft; i++){
      int regFree1 = 0, regFree2 = 0;
      Expr *pL, *pR; 
      int r1, r2;

................................................................................

  zRet = sqlite3DbMallocZero(pParse->db, nVal+1);
  if( zRet ){
    int i;
    for(i=0; i<nVal; i++){
      Expr *pA;
      char a;
      if( nVal==1 && 0 ){
        pA = pLeft;
      }else{    
        pA = exprVectorField(pLeft, i);
      }
      a = sqlite3ExprAffinity(pA);
      zRet[i] = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[i].pExpr, a);
    }
    zRet[nVal] = '\0';
  }
  return zRet;
}

#ifndef SQLITE_OMIT_SUBQUERY
/*
** Load the Parse object passed as the first argument with an error 
** message of the form:
**
**   "sub-select returns N columns - expected M"
*/   
void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){
  const char *zFmt = "sub-select returns %d columns - expected %d";
  sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect);
}
#endif

/*
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
** or IN operators.  Examples:
**
**     (SELECT a FROM b)          -- subquery
**     EXISTS (SELECT a FROM b)   -- EXISTS subquery
................................................................................
        ** table allocated and opened above.
        */
        Select *pSelect = pExpr->x.pSelect;
        ExprList *pEList = pSelect->pEList;

        assert( !isRowid );
        if( pEList->nExpr!=nVal ){

          sqlite3SubselectError(pParse, pEList->nExpr, nVal);
        }else{
          SelectDest dest;
          int i;
          sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
          dest.zAffSdst = exprINAffinity(pParse, pExpr);
          assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
          pSelect->iLimit = 0;
................................................................................
        sqlite3ReleaseTempRange(pParse, r1, nFarg);
      }
      break;
    }
#ifndef SQLITE_OMIT_SUBQUERY
    case TK_EXISTS:
    case TK_SELECT: {
      int nCol;
      testcase( op==TK_EXISTS );
      testcase( op==TK_SELECT );
      if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
        sqlite3SubselectError(pParse, nCol, 1);
      }else{
        inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
      }
      break;
    }
    case TK_IN: {
      int destIfFalse = sqlite3VdbeMakeLabel(v);
      int destIfNull = sqlite3VdbeMakeLabel(v);
      sqlite3VdbeAddOp2(v, OP_Null, 0, target);
      sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);

Changes to src/resolve.c.

763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
        assert( pNC->nRef>=nRef );
        if( nRef!=pNC->nRef ){
          ExprSetProperty(pExpr, EP_VarSelect);
          pNC->ncFlags |= NC_VarSelect;
        }

        if( pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1 ){
          if( !ExprHasProperty(pExpr, EP_VectorOk) ){
            sqlite3ErrorMsg(pParse, "invalid use of row value");
          }else{
            ExprSetProperty(pExpr, EP_Vector);
          }
        }
        if( pExpr->op==TK_IN ){
          ExprSetProperty(pExpr->pLeft, EP_VectorOk);







|







763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
        assert( pNC->nRef>=nRef );
        if( nRef!=pNC->nRef ){
          ExprSetProperty(pExpr, EP_VarSelect);
          pNC->ncFlags |= NC_VarSelect;
        }

        if( pExpr->op==TK_SELECT && pExpr->x.pSelect->pEList->nExpr>1 ){
          if( !ExprHasProperty(pExpr, EP_VectorOk) && 0 ){
            sqlite3ErrorMsg(pParse, "invalid use of row value");
          }else{
            ExprSetProperty(pExpr, EP_Vector);
          }
        }
        if( pExpr->op==TK_IN ){
          ExprSetProperty(pExpr->pLeft, EP_VectorOk);

Changes to src/where.c.

4709
4710
4711
4712
4713
4714
4715

4716
4717
4718
4719

4720
4721
4722
4723
4724
4725
4726
    }
    if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
      struct InLoop *pIn;
      int j;
      sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
      for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
        sqlite3VdbeJumpHere(v, pIn->addrInTop+1);

        sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
        VdbeCoverage(v);
        VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
        VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);

        sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
      }
    }
    sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
    if( pLevel->addrSkip ){
      sqlite3VdbeGoto(v, pLevel->addrSkip);
      VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName));







>
|
|
|
|
>







4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
    }
    if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
      struct InLoop *pIn;
      int j;
      sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
      for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
        sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
        if( pIn->eEndLoopOp!=OP_Noop ){
          sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
          VdbeCoverage(v);
          VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
          VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
        }
        sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
      }
    }
    sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
    if( pLevel->addrSkip ){
      sqlite3VdbeGoto(v, pLevel->addrSkip);
      VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName));

Changes to src/whereInt.h.

244
245
246
247
248
249
250

251
252
253
254
255
256
257
** in prereqRight and prereqAll.  The default is 64 bits, hence SQLite
** is only able to process joins with 64 or fewer tables.
*/
struct WhereTerm {
  Expr *pExpr;            /* Pointer to the subexpression that is this term */
  int iParent;            /* Disable pWC->a[iParent] when this term disabled */
  int leftCursor;         /* Cursor number of X in "X <op> <expr>" */

  union {
    int leftColumn;         /* Column number of X in "X <op> <expr>" */
    WhereOrInfo *pOrInfo;   /* Extra information if (eOperator & WO_OR)!=0 */
    WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
  } u;
  LogEst truthProb;       /* Probability of truth for this expression */
  u16 eOperator;          /* A WO_xx value describing <op> */







>







244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
** in prereqRight and prereqAll.  The default is 64 bits, hence SQLite
** is only able to process joins with 64 or fewer tables.
*/
struct WhereTerm {
  Expr *pExpr;            /* Pointer to the subexpression that is this term */
  int iParent;            /* Disable pWC->a[iParent] when this term disabled */
  int leftCursor;         /* Cursor number of X in "X <op> <expr>" */
  int iField;             /* Field in (?,?,?) IN (SELECT...) vector */
  union {
    int leftColumn;         /* Column number of X in "X <op> <expr>" */
    WhereOrInfo *pOrInfo;   /* Extra information if (eOperator & WO_OR)!=0 */
    WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
  } u;
  LogEst truthProb;       /* Probability of truth for this expression */
  u16 eOperator;          /* A WO_xx value describing <op> */

Changes to src/wherecode.c.

352
353
354
355
356
357
358

359
360
361
362
363
364
365
366
367
368
369
370



371
372
373
374
375
376
377
378
379
380
381

















382




























383
384
385
386
387
388
389
390
391

392
393
394
395

396

397
398
399
400
401

402
403


404

405
406


407
408


409




410

411
412
413

414
415
416
417
418
419
420
  int bRev,           /* True for reverse-order IN operations */
  int iTarget         /* Attempt to leave results in this register */
){
  Expr *pX = pTerm->pExpr;
  Vdbe *v = pParse->pVdbe;
  int iReg;                  /* Register holding results */


  assert( iTarget>0 );
  if( pX->op==TK_EQ || pX->op==TK_IS ){
    iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
  }else if( pX->op==TK_ISNULL ){
    iReg = iTarget;
    sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
#ifndef SQLITE_OMIT_SUBQUERY
  }else{
    int eType;
    int iTab;
    struct InLoop *pIn;
    WhereLoop *pLoop = pLevel->pWLoop;




    if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
      && pLoop->u.btree.pIndex!=0
      && pLoop->u.btree.pIndex->aSortOrder[iEq]
    ){
      testcase( iEq==0 );
      testcase( bRev );
      bRev = !bRev;
    }
    assert( pX->op==TK_IN );
    iReg = iTarget;

















    eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);




























    if( eType==IN_INDEX_INDEX_DESC ){
      testcase( bRev );
      bRev = !bRev;
    }
    iTab = pX->iTable;
    sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
    VdbeCoverageIf(v, bRev);
    VdbeCoverageIf(v, !bRev);
    assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );

    pLoop->wsFlags |= WHERE_IN_ABLE;
    if( pLevel->u.in.nIn==0 ){
      pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
    }

    pLevel->u.in.nIn++;

    pLevel->u.in.aInLoop =
       sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
                              sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
    pIn = pLevel->u.in.aInLoop;
    if( pIn ){

      pIn += pLevel->u.in.nIn - 1;
      pIn->iCur = iTab;


      if( eType==IN_INDEX_ROWID ){

        pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
      }else{


        pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
      }


      pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;




      sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);

    }else{
      pLevel->u.in.nIn = 0;
    }

#endif
  }
  disableTerm(pLevel, pTerm);
  return iReg;
}

/*







>












>
>
>











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









>




>
|
>





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



>







352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455

456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  int bRev,           /* True for reverse-order IN operations */
  int iTarget         /* Attempt to leave results in this register */
){
  Expr *pX = pTerm->pExpr;
  Vdbe *v = pParse->pVdbe;
  int iReg;                  /* Register holding results */

  assert( pLevel->pWLoop->aLTerm[iEq]==pTerm );
  assert( iTarget>0 );
  if( pX->op==TK_EQ || pX->op==TK_IS ){
    iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
  }else if( pX->op==TK_ISNULL ){
    iReg = iTarget;
    sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
#ifndef SQLITE_OMIT_SUBQUERY
  }else{
    int eType;
    int iTab;
    struct InLoop *pIn;
    WhereLoop *pLoop = pLevel->pWLoop;
    int i;
    int nEq = 0;
    int *aiMap = 0;

    if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
      && pLoop->u.btree.pIndex!=0
      && pLoop->u.btree.pIndex->aSortOrder[iEq]
    ){
      testcase( iEq==0 );
      testcase( bRev );
      bRev = !bRev;
    }
    assert( pX->op==TK_IN );
    iReg = iTarget;

    for(i=0; i<iEq; i++){
      if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){
        disableTerm(pLevel, pTerm);
        return iTarget;
      }
    }
    for(i=iEq;i<pLoop->nLTerm; i++){
      if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
    }

    if( nEq>1 ){
      aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
      if( !aiMap ) return 0;
    }

    if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
      eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
    }else{
      sqlite3 *db = pParse->db;
      ExprList *pOrigRhs = pX->x.pSelect->pEList;
      ExprList *pOrigLhs = pX->pLeft->x.pList;
      ExprList *pRhs = 0;         /* New Select.pEList for RHS */
      ExprList *pLhs = 0;         /* New pX->pLeft vector */

      for(i=iEq;i<pLoop->nLTerm; i++){
        if( pLoop->aLTerm[i]->pExpr==pX ){
          int iField = pLoop->aLTerm[i]->iField - 1;
          Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
          Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);

          pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
          pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
        }
      }

      pX->x.pSelect->pEList = pRhs;
      pX->pLeft->x.pList = pLhs;

      eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
      pX->x.pSelect->pEList = pOrigRhs;
      pX->pLeft->x.pList = pOrigLhs;
      sqlite3ExprListDelete(pParse->db, pLhs);
      sqlite3ExprListDelete(pParse->db, pRhs);
    }

    if( eType==IN_INDEX_INDEX_DESC ){
      testcase( bRev );
      bRev = !bRev;
    }
    iTab = pX->iTable;
    sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
    VdbeCoverageIf(v, bRev);
    VdbeCoverageIf(v, !bRev);
    assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );

    pLoop->wsFlags |= WHERE_IN_ABLE;
    if( pLevel->u.in.nIn==0 ){
      pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
    }

    i = pLevel->u.in.nIn;
    pLevel->u.in.nIn += nEq;
    pLevel->u.in.aInLoop =
       sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
                              sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
    pIn = pLevel->u.in.aInLoop;
    if( pIn ){
      int iMap = 0;               /* Index in aiMap[] */
      pIn += i;

      for(i=iEq;i<pLoop->nLTerm; i++, pIn++){
        if( pLoop->aLTerm[i]->pExpr==pX ){
          if( eType==IN_INDEX_ROWID ){
            assert( nEq==1 && i==iEq );
            pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
          }else{
            int iCol = aiMap ? aiMap[iMap++] : 0;
            int iOut = iReg + i - iEq;
            pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut);
          }
          if( i==iEq ){
            pIn->iCur = iTab;
            pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
          }else{
            pIn->eEndLoopOp = OP_Noop;
          }
        }
        sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
      }
    }else{
      pLevel->u.in.nIn = 0;
    }
    sqlite3DbFree(pParse->db, aiMap);
#endif
  }
  disableTerm(pLevel, pTerm);
  return iReg;
}

/*

Changes to src/whereexpr.c.

956
957
958
959
960
961
962






963
964
965
966
967
968
969
....
1186
1187
1188
1189
1190
1191
1192













1193
1194
1195
1196
1197
1198
1199
  pTerm->iParent = -1;
  pTerm->eOperator = 0;
  if( allowedOp(op) ){
    int iCur, iColumn;
    Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
    Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
    u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;






    if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
      pTerm->leftCursor = iCur;
      pTerm->u.leftColumn = iColumn;
      pTerm->eOperator = operatorMask(op) & opMask;
    }
    if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
    if( pRight 
................................................................................
      int idxNew;
      Expr *pNew;
      Expr *pLeft = exprVectorExpr(pParse, pExpr->pLeft, i);
      Expr *pRight = exprVectorExpr(pParse, pExpr->pRight, i);

      pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
      idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);













      exprAnalyze(pSrc, pWC, idxNew);
      markTermAsChild(pWC, idxNew, idxTerm);
    }
  }

#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
  /* When sqlite_stat3 histogram data is available an operator of the







>
>
>
>
>
>







 







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







956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
....
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
  pTerm->iParent = -1;
  pTerm->eOperator = 0;
  if( allowedOp(op) ){
    int iCur, iColumn;
    Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
    Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
    u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;

    if( op==TK_IN && pTerm->iField>0 ){
      assert( pLeft->op==TK_VECTOR );
      pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
    }

    if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
      pTerm->leftCursor = iCur;
      pTerm->u.leftColumn = iColumn;
      pTerm->eOperator = operatorMask(op) & opMask;
    }
    if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
    if( pRight 
................................................................................
      int idxNew;
      Expr *pNew;
      Expr *pLeft = exprVectorExpr(pParse, pExpr->pLeft, i);
      Expr *pRight = exprVectorExpr(pParse, pExpr->pRight, i);

      pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
      idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
      exprAnalyze(pSrc, pWC, idxNew);
      markTermAsChild(pWC, idxNew, idxTerm);
    }
  }

  if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0
   && pExpr->pLeft->op==TK_VECTOR
  ){
    int i;
    for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
      int idxNew;
      idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
      pWC->a[idxNew].iField = i+1;
      exprAnalyze(pSrc, pWC, idxNew);
      markTermAsChild(pWC, idxNew, idxTerm);
    }
  }

#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
  /* When sqlite_stat3 histogram data is available an operator of the

Changes to test/e_expr.test.

1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828

# EVIDENCE-OF: R-46899-53765 A SELECT used as a scalar quantity must
# return a result set with a single column.
#
# The following block tests that errors are returned in a bunch of cases
# where a subquery returns more than one column.
#
set M {only a single result allowed for a SELECT that is part of an expression}
foreach {tn sql} {
  1     { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) }
  2     { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) }
  3     { SELECT (SELECT 1, 2) }
  4     { SELECT (SELECT NULL, NULL, NULL) }
  5     { SELECT (SELECT * FROM t2) }
  6     { SELECT (SELECT * FROM (SELECT 1, 2, 3)) }
} {
  do_catchsql_test e_expr-35.2.$tn $sql [list 1 $M]
}

# EVIDENCE-OF: R-35764-28041 The result of the expression is the value
# of the only column in the first row returned by the SELECT statement.
#
# EVIDENCE-OF: R-41898-06686 If the SELECT yields more than one result
# row, all rows after the first are ignored.







|








|







1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828

# EVIDENCE-OF: R-46899-53765 A SELECT used as a scalar quantity must
# return a result set with a single column.
#
# The following block tests that errors are returned in a bunch of cases
# where a subquery returns more than one column.
#
set M {/1 {sub-select returns [23] columns - expected 1}/}
foreach {tn sql} {
  1     { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) }
  2     { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) }
  3     { SELECT (SELECT 1, 2) }
  4     { SELECT (SELECT NULL, NULL, NULL) }
  5     { SELECT (SELECT * FROM t2) }
  6     { SELECT (SELECT * FROM (SELECT 1, 2, 3)) }
} {
  do_catchsql_test e_expr-35.2.$tn $sql $M
}

# EVIDENCE-OF: R-35764-28041 The result of the expression is the value
# of the only column in the first row returned by the SELECT statement.
#
# EVIDENCE-OF: R-41898-06686 If the SELECT yields more than one result
# row, all rows after the first are ignored.

Changes to test/in.test.

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
...
625
626
627
628
629
630
631

632
633
634
635
636
637
638
639
640
641
642
643
    SELECT b FROM t1 WHERE a NOT IN t4;
  }
} {64 256 world}
do_test in-9.4 {
  catchsql {
    SELECT b FROM t1 WHERE a NOT IN tb;
  }
} {1 {only a single result allowed for a SELECT that is part of an expression}}

# IN clauses in CHECK constraints.  Ticket #1645
#
do_test in-10.1 {
  execsql {
    CREATE TABLE t5(
      a INTEGER,
................................................................................
} {}
do_test in-12.2 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
    );
  }
} {1 {only a single result allowed for a SELECT that is part of an expression}}
do_test in-12.3 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION SELECT a, b FROM t2
    );
  }
} {1 {only a single result allowed for a SELECT that is part of an expression}}
do_test in-12.4 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 EXCEPT SELECT a, b FROM t2
    );
  }
} {1 {only a single result allowed for a SELECT that is part of an expression}}
do_test in-12.5 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2
    );
  }
} {1 {only a single result allowed for a SELECT that is part of an expression}}
do_test in-12.6 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a FROM t2
    );
  }
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
................................................................................
} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
do_test in-12.14 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
    );
  }
} {1 {only a single result allowed for a SELECT that is part of an expression}}
do_test in-12.15 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a FROM t2
    );
  }
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
................................................................................
do_test in-13.14 {
  execsql {
    CREATE INDEX i5 ON b(id);
    SELECT * FROM a WHERE id NOT IN (SELECT id FROM b);
  }
} {}


do_test in-13.15 {
  catchsql {
    SELECT 0 WHERE (SELECT 0,0) OR (0 IN (1,2));
  }
} {1 {only a single result allowed for a SELECT that is part of an expression}}


do_test in-13.X {
  db nullvalue ""
} {}

finish_test







|







 







|






|






|






|







 







|







 







>




|







310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
    SELECT b FROM t1 WHERE a NOT IN t4;
  }
} {64 256 world}
do_test in-9.4 {
  catchsql {
    SELECT b FROM t1 WHERE a NOT IN tb;
  }
} {1 {sub-select returns 2 columns - expected 1}}

# IN clauses in CHECK constraints.  Ticket #1645
#
do_test in-10.1 {
  execsql {
    CREATE TABLE t5(
      a INTEGER,
................................................................................
} {}
do_test in-12.2 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
    );
  }
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.3 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION SELECT a, b FROM t2
    );
  }
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.4 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 EXCEPT SELECT a, b FROM t2
    );
  }
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.5 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2
    );
  }
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.6 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a FROM t2
    );
  }
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
................................................................................
} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
do_test in-12.14 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a, b FROM t2
    );
  }
} {1 {sub-select returns 2 columns - expected 1}}
do_test in-12.15 {
  catchsql {
    SELECT * FROM t2 WHERE a IN (
      SELECT a, b FROM t3 UNION ALL SELECT a FROM t2
    );
  }
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
................................................................................
do_test in-13.14 {
  execsql {
    CREATE INDEX i5 ON b(id);
    SELECT * FROM a WHERE id NOT IN (SELECT id FROM b);
  }
} {}

breakpoint
do_test in-13.15 {
  catchsql {
    SELECT 0 WHERE (SELECT 0,0) OR (0 IN (1,2));
  }
} {1 {sub-select returns 2 columns - expected 1}}


do_test in-13.X {
  db nullvalue ""
} {}

finish_test

Changes to test/rowvalue3.test.

38
39
40
41
42
43
44





























45



46

47



48



49
50




51
52


  9  "SELECT (1, 2) IN (SELECT rowid, b FROM t1)"  1
  10 "SELECT 1 WHERE (1, 2) IN (SELECT rowid, b FROM t1)"  1
  11 "SELECT 1 WHERE (1, NULL) IN (SELECT rowid, b FROM t1)"  {}
} {
  do_execsql_test 1.$tn $sql $res
}






























#explain_i { SELECT (4, NULL) IN (SELECT a, b FROM t1) } 



#do_execsql_test 2 { SELECT (4, NULL) IN (SELECT a, b FROM t1) } {}















finish_test










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

>
>
>
|

>
>
>
>


>
>
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  9  "SELECT (1, 2) IN (SELECT rowid, b FROM t1)"  1
  10 "SELECT 1 WHERE (1, 2) IN (SELECT rowid, b FROM t1)"  1
  11 "SELECT 1 WHERE (1, NULL) IN (SELECT rowid, b FROM t1)"  {}
} {
  do_execsql_test 1.$tn $sql $res
}

#-------------------------------------------------------------------------

do_execsql_test 2.0 {
  CREATE TABLE z1(x, y, z);
  CREATE TABLE kk(a, b);

  INSERT INTO z1 VALUES('a', 'b', 'c');
  INSERT INTO z1 VALUES('d', 'e', 'f');
  INSERT INTO z1 VALUES('g', 'h', 'i');

  -- INSERT INTO kk VALUES('y', 'y');
  INSERT INTO kk VALUES('d', 'e');
  -- INSERT INTO kk VALUES('x', 'x');

}

foreach {tn idx} {
  1 { }
  2 {  CREATE INDEX z1idx ON z1(x, y) }
  3 {  CREATE UNIQUE INDEX z1idx ON z1(x, y) }
} {
  execsql "DROP INDEX IF EXISTS z1idx"
  execsql $idx

  do_execsql_test 2.$tn.1 {
    SELECT * FROM z1 WHERE x IN (SELECT a FROM kk)
  } {d e f}

  do_execsql_test 2.$tn.2 {
    SELECT * FROM z1 WHERE (x,y) IN (SELECT a, b FROM kk)
  } {d e f}

  do_execsql_test 2.$tn.3 {
    SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b FROM kk)
  } {d e f}
  
  do_execsql_test 2.$tn.4 {
    SELECT * FROM z1 WHERE (x, +y) IN (SELECT a, b||'x' FROM kk)
  } {}

  do_execsql_test 2.$tn.5 {
    SELECT * FROM z1 WHERE (+x, y) IN (SELECT a, b FROM kk)
  } {d e f}
}

explain_i {
  SELECT * FROM z1 WHERE (x, y) IN (SELECT a, b FROM kk)
} 

finish_test



Changes to test/select7.test.

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#
ifcapable {subquery && compound} {
  do_test select7-5.1 {
    catchsql {
      CREATE TABLE t2(a,b);
      SELECT 5 IN (SELECT a,b FROM t2);
    }
  } [list 1 \
     {only a single result allowed for a SELECT that is part of an expression}]
  do_test select7-5.2 {
    catchsql {
      SELECT 5 IN (SELECT * FROM t2);
    }
  } [list 1 \
     {only a single result allowed for a SELECT that is part of an expression}]
  do_test select7-5.3 {
    catchsql {
      SELECT 5 IN (SELECT a,b FROM t2 UNION SELECT b,a FROM t2);
    }
  } [list 1 \
     {only a single result allowed for a SELECT that is part of an expression}]
  do_test select7-5.4 {
    catchsql {
      SELECT 5 IN (SELECT * FROM t2 UNION SELECT * FROM t2);
    }
  } [list 1 \
     {only a single result allowed for a SELECT that is part of an expression}]
}

# Verify that an error occurs if you have too many terms on a
# compound select statement.
#
if {[clang_sanitize_address]==0} {
  ifcapable compound {







|
<




|
<




|
<




|
<







110
111
112
113
114
115
116
117

118
119
120
121
122

123
124
125
126
127

128
129
130
131
132

133
134
135
136
137
138
139
#
ifcapable {subquery && compound} {
  do_test select7-5.1 {
    catchsql {
      CREATE TABLE t2(a,b);
      SELECT 5 IN (SELECT a,b FROM t2);
    }
  } {1 {sub-select returns 2 columns - expected 1}}

  do_test select7-5.2 {
    catchsql {
      SELECT 5 IN (SELECT * FROM t2);
    }
  } {1 {sub-select returns 2 columns - expected 1}}

  do_test select7-5.3 {
    catchsql {
      SELECT 5 IN (SELECT a,b FROM t2 UNION SELECT b,a FROM t2);
    }
  } {1 {sub-select returns 2 columns - expected 1}}

  do_test select7-5.4 {
    catchsql {
      SELECT 5 IN (SELECT * FROM t2 UNION SELECT * FROM t2);
    }
  } {1 {sub-select returns 2 columns - expected 1}}

}

# Verify that an error occurs if you have too many terms on a
# compound select statement.
#
if {[clang_sanitize_address]==0} {
  ifcapable compound {

Changes to test/subselect.test.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
} {3 4}

# Try a select with more than one result column.
#
do_test subselect-1.2 {
  set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg]
  lappend v $msg
} {1 {only a single result allowed for a SELECT that is part of an expression}}

# A subselect without an aggregate.
#
do_test subselect-1.3a {
  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=2)}
} {2}
do_test subselect-1.3b {







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
} {3 4}

# Try a select with more than one result column.
#
do_test subselect-1.2 {
  set v [catch {execsql {SELECT * FROM t1 WHERE a = (SELECT * FROM t1)}} msg]
  lappend v $msg
} {1 {sub-select returns 2 columns - expected 1}}

# A subselect without an aggregate.
#
do_test subselect-1.3a {
  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=2)}
} {2}
do_test subselect-1.3b {

Changes to test/tester.tcl.

1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
....
1306
1307
1308
1309
1310
1311
1312






1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
....
1332
1333
1334
1335
1336
1337
1338






1339
1340
1341
1342
1343
1344
1345
  } else {
    set R ""
    set G ""
    set B ""
    set D ""
  }
  foreach opcode {
      Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
      NoConflict Next Prev VNext VPrev VFilter
      SorterSort SorterNext
  } {
    set color($opcode) $B
  }
  foreach opcode {ResultRow} {
    set color($opcode) $G
  }
  foreach opcode {IdxInsert Insert Delete IdxDelete} {
................................................................................
    set x($addr) 0
    set op($addr) $opcode

    if {$opcode == "Goto" && ($bSeenGoto==0 || ($p2 > $addr+10))} {
      set linebreak($p2) 1
      set bSeenGoto 1
    }







    if {$opcode=="Next"  || $opcode=="Prev" 
     || $opcode=="VNext" || $opcode=="VPrev"
     || $opcode=="SorterNext"
    } {
      for {set i $p2} {$i<$addr} {incr i} {
        incr x($i) 2
      }
    }

    if {$opcode == "Goto" && $p2<$addr && $op($p2)=="Yield"} {
................................................................................
  }

  $db eval "explain $sql" {} {
    if {[info exists linebreak($addr)]} {
      output2 ""
    }
    set I [string repeat " " $x($addr)]







    set col ""
    catch { set col $color($opcode) }

    output2 [format {%-4d  %s%s%-12.12s%s  %-6d  %-6d  %-6d  % -17s %s  %s} \
      $addr $I $col $opcode $D $p1 $p2 $p3 $p4 $p5 $comment
    ]







|

|







 







>
>
>
>
>
>



|







 







>
>
>
>
>
>







1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
....
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
....
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
  } else {
    set R ""
    set G ""
    set B ""
    set D ""
  }
  foreach opcode {
      Seek SeekGE SeekGT SeekLE SeekLT NotFound Last Rewind
      NoConflict Next Prev VNext VPrev VFilter
      SorterSort SorterNext NextIfOpen
  } {
    set color($opcode) $B
  }
  foreach opcode {ResultRow} {
    set color($opcode) $G
  }
  foreach opcode {IdxInsert Insert Delete IdxDelete} {
................................................................................
    set x($addr) 0
    set op($addr) $opcode

    if {$opcode == "Goto" && ($bSeenGoto==0 || ($p2 > $addr+10))} {
      set linebreak($p2) 1
      set bSeenGoto 1
    }

    if {$opcode=="Once"} {
      for {set i $addr} {$i<$p2} {incr i} {
        set star($i) $addr
      }
    }

    if {$opcode=="Next"  || $opcode=="Prev" 
     || $opcode=="VNext" || $opcode=="VPrev"
     || $opcode=="SorterNext" || $opcode=="NextIfOpen"
    } {
      for {set i $p2} {$i<$addr} {incr i} {
        incr x($i) 2
      }
    }

    if {$opcode == "Goto" && $p2<$addr && $op($p2)=="Yield"} {
................................................................................
  }

  $db eval "explain $sql" {} {
    if {[info exists linebreak($addr)]} {
      output2 ""
    }
    set I [string repeat " " $x($addr)]

    if {[info exists star($addr)]} {
      set ii [expr $x($star($addr))]
      append I "  "
      set I [string replace $I $ii $ii *]
    }

    set col ""
    catch { set col $color($opcode) }

    output2 [format {%-4d  %s%s%-12.12s%s  %-6d  %-6d  %-6d  % -17s %s  %s} \
      $addr $I $col $opcode $D $p1 $p2 $p3 $p4 $p5 $comment
    ]