/ Check-in [ae127bcc]
Login

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

Overview
Comment:Do vector comparison size checking early - at name resolution time - to forestall future problems.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rowvalue
Files: files | file ages | folders
SHA1:ae127bcc0a5827786853f47b229021435d6098d7
User & Date: drh 2016-09-05 12:12:56
Context
2016-09-05
15:02
Simplifications to the SQLITE_KEEPNULL flag on VDBE comparison operators. check-in: 96269f01 user: drh tags: rowvalue
12:12
Do vector comparison size checking early - at name resolution time - to forestall future problems. check-in: ae127bcc user: drh tags: rowvalue
12:02
Catch vector size mismatch problems during name resolution to avoid later problems. Closed-Leaf check-in: 56562a03 user: drh tags: early-vector-size-check
09:44
Fix a crash that could occur under certain circumstances if the vectors on either side of a comparison operator were of a different size. check-in: 42670935 user: dan tags: rowvalue
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/expr.c.

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
....
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
  int dest,             /* Write results into this register */
  u8 op,                /* Comparison operator */
  u8 p5                 /* SQLITE_NULLEQ or zero */
){
  Vdbe *v = pParse->pVdbe;
  Expr *pLeft = pExpr->pLeft;
  Expr *pRight = pExpr->pRight;

  if( sqlite3ExprCheckComparison(pParse, pLeft, pRight)==0 ){
    int nLeft = sqlite3ExprVectorSize(pLeft);
    int i;
    int regLeft = 0;
    int regRight = 0;
    u8 opx = op;
    int addrDone = sqlite3VdbeMakeLabel(v);


    assert( pExpr->op==TK_EQ || pExpr->op==TK_NE 
         || pExpr->op==TK_IS || pExpr->op==TK_ISNOT 
         || pExpr->op==TK_LT || pExpr->op==TK_GT 
         || pExpr->op==TK_LE || pExpr->op==TK_GE 
    );
    assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ)
              || (pExpr->op==TK_ISNOT && op==TK_NE) );
    assert( p5==0 || pExpr->op!=op );
    assert( p5==SQLITE_NULLEQ || pExpr->op==op );

    p5 |= SQLITE_STOREP2;
    if( opx==TK_LE ) opx = TK_LT;
    if( opx==TK_GE ) opx = TK_GT;

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

    for(i=0; 1 /*Loop exits by "break"*/; i++){
      int regFree1 = 0, regFree2 = 0;
      Expr *pL, *pR; 
      int r1, r2;
      assert( i>=0 && i<nLeft );
      if( i>0 ) sqlite3ExprCachePush(pParse);
      r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, &regFree1);
      r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, &regFree2);
      codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5);
      testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
      testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
      testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
      testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
      testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
      testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
      sqlite3ReleaseTempReg(pParse, regFree1);
      sqlite3ReleaseTempReg(pParse, regFree2);
      if( i>0 ) sqlite3ExprCachePop(pParse);
      if( i==nLeft-1 ){
        break;
      }
      if( opx==TK_EQ ){
        sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v);
        p5 |= SQLITE_KEEPNULL;
      }else if( opx==TK_NE ){
        sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v);
        p5 |= SQLITE_KEEPNULL;
      }else{
        assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE );
        sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone);
        VdbeCoverageIf(v, op==TK_LT);
        VdbeCoverageIf(v, op==TK_GT);
        VdbeCoverageIf(v, op==TK_LE);
        VdbeCoverageIf(v, op==TK_GE);
        if( i==nLeft-2 ) opx = op;
      }
    }
    sqlite3VdbeResolveLabel(v, addrDone);
  }
}

#if SQLITE_MAX_EXPR_DEPTH>0
/*
** Check that argument nHeight is less than or equal to the maximum
** expression depth allowed. If it is not, leave an error message in
** pParse.
................................................................................
    }
    return 1;
  }
  return 0;
}
#endif

/*
** Expressions pLeft and pRight are the left and right sides of a comparison
** operator. If either pLeft or pRight is a vector and the other is not, or
** if they are both vectors but of a different size, leave an error message
** in the Parse object and return non-zero. Or, if there is no problem, 
** return 0.
*/
int sqlite3ExprCheckComparison(Parse *pParse, Expr *pLeft, Expr *pRight){
  int nLeft = sqlite3ExprVectorSize(pLeft);
  int nRight = sqlite3ExprVectorSize(pRight);
  if( nLeft!=nRight ){
    if( (pRight->flags & EP_xIsSelect) ){
      sqlite3SubselectError(pParse, nRight, nLeft);
    }else if( pLeft->flags & EP_xIsSelect ){
      sqlite3SubselectError(pParse, nLeft, nRight);
    }else{
      sqlite3ErrorMsg(pParse, "row value misused");
    }
    return 1;
  }
  return 0;
}

#ifndef SQLITE_OMIT_SUBQUERY
/*
** Generate code for an IN expression.
**
**      x IN (SELECT ...)
**      x IN (value, value, ...)
**







<
<
|
|
|
|
|
|

>
|
|
|
|
|
|
|
|
|

|
|
|

|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<







 







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







517
518
519
520
521
522
523


524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586

587
588
589
590
591
592
593
....
2645
2646
2647
2648
2649
2650
2651























2652
2653
2654
2655
2656
2657
2658
  int dest,             /* Write results into this register */
  u8 op,                /* Comparison operator */
  u8 p5                 /* SQLITE_NULLEQ or zero */
){
  Vdbe *v = pParse->pVdbe;
  Expr *pLeft = pExpr->pLeft;
  Expr *pRight = pExpr->pRight;


  int nLeft = sqlite3ExprVectorSize(pLeft);
  int i;
  int regLeft = 0;
  int regRight = 0;
  u8 opx = op;
  int addrDone = sqlite3VdbeMakeLabel(v);

  assert( nLeft==sqlite3ExprVectorSize(pRight) );
  assert( pExpr->op==TK_EQ || pExpr->op==TK_NE 
       || pExpr->op==TK_IS || pExpr->op==TK_ISNOT 
       || pExpr->op==TK_LT || pExpr->op==TK_GT 
       || pExpr->op==TK_LE || pExpr->op==TK_GE 
  );
  assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ)
            || (pExpr->op==TK_ISNOT && op==TK_NE) );
  assert( p5==0 || pExpr->op!=op );
  assert( p5==SQLITE_NULLEQ || pExpr->op==op );

  p5 |= SQLITE_STOREP2;
  if( opx==TK_LE ) opx = TK_LT;
  if( opx==TK_GE ) opx = TK_GT;

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

  for(i=0; 1 /*Loop exits by "break"*/; i++){
    int regFree1 = 0, regFree2 = 0;
    Expr *pL, *pR; 
    int r1, r2;
    assert( i>=0 && i<nLeft );
    if( i>0 ) sqlite3ExprCachePush(pParse);
    r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, &regFree1);
    r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, &regFree2);
    codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5);
    testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
    testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
    testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
    testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
    testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
    testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
    sqlite3ReleaseTempReg(pParse, regFree1);
    sqlite3ReleaseTempReg(pParse, regFree2);
    if( i>0 ) sqlite3ExprCachePop(pParse);
    if( i==nLeft-1 ){
      break;
    }
    if( opx==TK_EQ ){
      sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v);
      p5 |= SQLITE_KEEPNULL;
    }else if( opx==TK_NE ){
      sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v);
      p5 |= SQLITE_KEEPNULL;
    }else{
      assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE );
      sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone);
      VdbeCoverageIf(v, op==TK_LT);
      VdbeCoverageIf(v, op==TK_GT);
      VdbeCoverageIf(v, op==TK_LE);
      VdbeCoverageIf(v, op==TK_GE);
      if( i==nLeft-2 ) opx = op;
    }
  }
  sqlite3VdbeResolveLabel(v, addrDone);

}

#if SQLITE_MAX_EXPR_DEPTH>0
/*
** Check that argument nHeight is less than or equal to the maximum
** expression depth allowed. If it is not, leave an error message in
** pParse.
................................................................................
    }
    return 1;
  }
  return 0;
}
#endif
























#ifndef SQLITE_OMIT_SUBQUERY
/*
** Generate code for an IN expression.
**
**      x IN (SELECT ...)
**      x IN (value, value, ...)
**

Changes to src/resolve.c.

771
772
773
774
775
776
777



























778
779
780
781
782
783
784
        }
      }
      break;
    }
    case TK_VARIABLE: {
      notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
      break;



























    }
  }
  return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}

/*
** pEList is a list of expressions which are really the result set of the







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







771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
        }
      }
      break;
    }
    case TK_VARIABLE: {
      notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
      break;
    }
    case TK_EQ:
    case TK_NE:
    case TK_LT:
    case TK_LE:
    case TK_GT:
    case TK_GE:
    case TK_IS:
    case TK_ISNOT: {
      int nLeft, nRight;
      if( pParse->db->mallocFailed ) break;
      assert( pExpr->pRight!=0 );
      assert( pExpr->pLeft!=0 );
      nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
      nRight = sqlite3ExprVectorSize(pExpr->pRight);
      if( nLeft!=nRight ){
        testcase( pExpr->op==TK_EQ );
        testcase( pExpr->op==TK_NE );
        testcase( pExpr->op==TK_LT );
        testcase( pExpr->op==TK_LE );
        testcase( pExpr->op==TK_GT );
        testcase( pExpr->op==TK_GE );
        testcase( pExpr->op==TK_IS );
        testcase( pExpr->op==TK_ISNOT );
        sqlite3ErrorMsg(pParse, "row value misused");
      }
      break; 
    }
  }
  return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}

/*
** pEList is a list of expressions which are really the result set of the

Changes to src/sqliteInt.h.

4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);

#ifndef SQLITE_OMIT_SUBQUERY
int sqlite3ExprCheckIN(Parse*, Expr*);
#else
# define sqlite3ExprCheckIN(x,y) SQLITE_OK
#endif
int sqlite3ExprCheckComparison(Parse*, Expr*, Expr*);

#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
void sqlite3AnalyzeFunctions(void);
int sqlite3Stat4ProbeSetValue(
    Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
void sqlite3Stat4ProbeFree(UnpackedRecord*);







<







4005
4006
4007
4008
4009
4010
4011

4012
4013
4014
4015
4016
4017
4018
void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);

#ifndef SQLITE_OMIT_SUBQUERY
int sqlite3ExprCheckIN(Parse*, Expr*);
#else
# define sqlite3ExprCheckIN(x,y) SQLITE_OK
#endif


#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
void sqlite3AnalyzeFunctions(void);
int sqlite3Stat4ProbeSetValue(
    Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
void sqlite3Stat4ProbeFree(UnpackedRecord*);

Changes to src/whereexpr.c.

948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
....
1188
1189
1190
1191
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
  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( pRight && sqlite3ExprCheckComparison(pParse, pLeft, pRight) ){
      return;
    }

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

    if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
................................................................................
  if( pWC->op==TK_AND 
  && (pExpr->op==TK_EQ || pExpr->op==TK_IS)
  && sqlite3ExprIsVector(pExpr->pLeft)
  && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 
    || (pExpr->pRight->flags & EP_xIsSelect)==0
  )){
    int nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
    if( nLeft==sqlite3ExprVectorSize(pExpr->pRight) ){
      int i;
      for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){

        int idxNew;
        Expr *pNew;
        Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
        Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);

        pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
        idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
        exprAnalyze(pSrc, pWC, idxNew);
      }
      pTerm = &pWC->a[idxTerm];
      pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL;  /* Disable the original */
      pTerm->eOperator = 0;
    }
  }

  /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
  ** a virtual term for each vector component. The expression object
  ** used by each such virtual term is pExpr (the full vector IN(...) 
  ** expression). The WhereTerm.iField variable identifies the index within
  ** the vector on the LHS that the virtual term represents.  */







<
<
<
<







 







<
|
|
>
|
|
|
|

|
|
|
|
|
|
|
<







948
949
950
951
952
953
954




955
956
957
958
959
960
961
....
1184
1185
1186
1187
1188
1189
1190

1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205

1206
1207
1208
1209
1210
1211
1212
  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( pTerm->iField>0 ){
      assert( op==TK_IN );
      assert( pLeft->op==TK_VECTOR );
      pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
    }

    if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
................................................................................
  if( pWC->op==TK_AND 
  && (pExpr->op==TK_EQ || pExpr->op==TK_IS)
  && sqlite3ExprIsVector(pExpr->pLeft)
  && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 
    || (pExpr->pRight->flags & EP_xIsSelect)==0
  )){
    int nLeft = sqlite3ExprVectorSize(pExpr->pLeft);

    int i;
    assert( nLeft==sqlite3ExprVectorSize(pExpr->pRight) );
    for(i=0; i<nLeft; i++){
      int idxNew;
      Expr *pNew;
      Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
      Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);

      pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
      idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
      exprAnalyze(pSrc, pWC, idxNew);
    }
    pTerm = &pWC->a[idxTerm];
    pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL;  /* Disable the original */
    pTerm->eOperator = 0;

  }

  /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
  ** a virtual term for each vector component. The expression object
  ** used by each such virtual term is pExpr (the full vector IN(...) 
  ** expression). The WhereTerm.iField variable identifies the index within
  ** the vector on the LHS that the virtual term represents.  */

Changes to test/rowvalue.test.

224
225
226
227
228
229
230
231



























232
  CREATE TABLE t3(a TEXT,b TEXT,c TEXT,d TEXT,e TEXT,f TEXT);
  CREATE INDEX t3x ON t3(b,c,d,e,f);

  SELECT a FROM t3
    WHERE (c,d) IN (SELECT 'c','d' FROM dual)
    AND (a,b,e) IN (SELECT 'a','b','d' FROM dual);
}




























finish_test








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

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  CREATE TABLE t3(a TEXT,b TEXT,c TEXT,d TEXT,e TEXT,f TEXT);
  CREATE INDEX t3x ON t3(b,c,d,e,f);

  SELECT a FROM t3
    WHERE (c,d) IN (SELECT 'c','d' FROM dual)
    AND (a,b,e) IN (SELECT 'a','b','d' FROM dual);
}

do_catchsql_test 11.1 {
  CREATE TABLE t11(a);
  SELECT * FROM t11 WHERE (a,a)<=1;
} {1 {row value misused}}
do_catchsql_test 11.2 {
  SELECT * FROM t11 WHERE (a,a)<1;
} {1 {row value misused}}
do_catchsql_test 11.3 {
  SELECT * FROM t11 WHERE (a,a)>=1;
} {1 {row value misused}}
do_catchsql_test 11.4 {
  SELECT * FROM t11 WHERE (a,a)>1;
} {1 {row value misused}}
do_catchsql_test 11.5 {
  SELECT * FROM t11 WHERE (a,a)==1;
} {1 {row value misused}}
do_catchsql_test 11.6 {
  SELECT * FROM t11 WHERE (a,a)<>1;
} {1 {row value misused}}
do_catchsql_test 11.7 {
  SELECT * FROM t11 WHERE (a,a) IS 1;
} {1 {row value misused}}
do_catchsql_test 11.8 {
  SELECT * FROM t11 WHERE (a,a) IS NOT 1;
} {1 {row value misused}}


finish_test

Changes to test/rowvalue4.test.

304
305
306
307
308
309
310
311
312
313

  CREATE TABLE c3(d);
}
do_catchsql_test 8.2 {
  SELECT * FROM c2 CROSS JOIN c3 WHERE 
    ( (a, b) == (SELECT x, y FROM c1) AND c3.d = c ) OR
    ( c == (SELECT x, y FROM c1) AND c3.d = c )
} {1 {sub-select returns 2 columns - expected 1}}

finish_test







|


304
305
306
307
308
309
310
311
312
313

  CREATE TABLE c3(d);
}
do_catchsql_test 8.2 {
  SELECT * FROM c2 CROSS JOIN c3 WHERE 
    ( (a, b) == (SELECT x, y FROM c1) AND c3.d = c ) OR
    ( c == (SELECT x, y FROM c1) AND c3.d = c )
} {1 {row value misused}}

finish_test

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 {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 {







|







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 {row value misused}}

# 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 {