/ Check-in [059d0d05]
Login

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

Overview
Comment:Fix problems with vector == comparisons and NULL values.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rowvalue
Files: files | file ages | folders
SHA1:059d0d05354e6efab7892c97b339ffa0b5303587
User & Date: dan 2016-07-30 21:02:33
Context
2016-08-01
16:37
Fix a problem with IN(...) constraints where the LHS is a sub-select that is an aggregate query. check-in: 1f4dba87 user: dan tags: rowvalue
2016-07-30
21:02
Fix problems with vector == comparisons and NULL values. check-in: 059d0d05 user: dan tags: rowvalue
17:59
Merge latest trunk with this branch. check-in: 63ae02d0 user: dan tags: rowvalue
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/expr.c.

345
346
347
348
349
350
351








352
353
354
355
356
357
358
359
360

361




























362
363


364





365
366
367
368
369
370
371
...
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
    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);
  int addr = sqlite3VdbeMakeLabel(v);
................................................................................
  /* Check that both sides of the comparison are vectors, and that
  ** both are the same length.  */
  if( nLeft!=nRight ){
    sqlite3ErrorMsg(pParse, "invalid use of row value");
  }else{
    int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0;
    int opCmp;
    int opTest;
    int i;
    int p3 = 1;

    int regLeft = 0;
    int regRight = 0;


    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 
    );

    switch( pExpr->op ){
      case TK_EQ:
      case TK_IS:
        opTest = OP_IfNot;
        opCmp = OP_Eq;
        break;

      case TK_NE:
      case TK_ISNOT:
        opTest = OP_If;
        opCmp = OP_Ne;
        break;

      case TK_LT:
      case TK_LE:
      case TK_GT:
      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;

      if( i ) sqlite3ExprCachePush(pParse);

      if( regLeft ){
        pL = pLeft->x.pSelect->pEList->a[i].pExpr;
        r1 = regLeft+i;
      }else{
        pL = pLeft->x.pList->a[i].pExpr;
        r1 = sqlite3ExprCodeTemp(pParse, pL, &regFree1);
      }









      if( regRight ){
        pR = pRight->x.pSelect->pEList->a[i].pExpr;
        r2 = regRight+i;
      }else{
        pR = pRight->x.pList->a[i].pExpr;
        r2 = sqlite3ExprCodeTemp(pParse, pR, &regFree1);




      }













      codeCompare(pParse, pL, pR, opCmp, r1, r2, dest, SQLITE_STOREP2 | p5);
      sqlite3VdbeAddOp3(v, opTest, dest, addr, p3);





      sqlite3ReleaseTempReg(pParse, regFree1);
      sqlite3ReleaseTempReg(pParse, regFree2);
      if( i ) sqlite3ExprCachePop(pParse);
    }


  }

  sqlite3VdbeResolveLabel(v, addr);
}

#if SQLITE_MAX_EXPR_DEPTH>0
/*







>
>
>
>
>
>
>
>
|

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







 







<

|
>


>







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

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





<

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

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

>
>
>
>
|
<
>
>
>
>
>




>
>







345
346
347
348
349
350
351
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
...
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
486
487
488
489
490
491
492
493
494
495
496
497
    return pVector;
  }else if( pVector->flags & EP_xIsSelect ){
    return pVector->x.pSelect->pEList->a[i].pExpr;
  }
  return pVector->x.pList->a[i].pExpr;
}

/*
** If expression pExpr is of type TK_SELECT, generate code to evaluate
** it. Return the register in which the result is stored (or, if the 
** sub-select returns more than one column, the first in an array
** of registers in which the result is stored).
**
** If pExpr is not a TK_SELECT expression, return 0.
*/
static int exprCodeSubselect(Parse *pParse, Expr *pExpr){
  int reg = 0;


  if( pExpr->op==TK_SELECT ){


    reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
  }
  return reg;
}

/*
** Argument pVector points to a vector expression - either a TK_VECTOR
** or TK_SELECT that returns more than one column. This function generates
** code to evaluate expression iElem of the vector. The number of the
** register containing the result is returned. 
**
** Before returning, output parameter (*ppExpr) is set to point to the
** Expr object corresponding to element iElem of the vector.
**
** If pVector is a TK_SELECT expression, then argument regSelect is 
** passed the first in an array of registers that contain the results
** of the sub-select. 
**
** If output parameter (*pRegFree) is set to a non-zero value by this
** function, it is the value of a temporary register that should be
** freed by the caller.
*/
static int exprVectorRegister(
  Parse *pParse,                  /* Parse context */
  Expr *pVector,                  /* Vector to extract element from */
  int iElem,                      /* Element to extract from pVector */
  int regSelect,                  /* First in array of registers */
  Expr **ppExpr,                  /* OUT: Expression element */
  int *pRegFree                   /* OUT: Temp register to free */
){
  if( regSelect ){
    *ppExpr = pVector->x.pSelect->pEList->a[iElem].pExpr;
     return regSelect+iElem;
  }
  *ppExpr = pVector->x.pList->a[iElem].pExpr;
  return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree);
}

/*
** Expression pExpr is a comparison between two vector values. Compute
** the result of the comparison and write it to register dest.
*/
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);
  int addr = sqlite3VdbeMakeLabel(v);
................................................................................
  /* Check that both sides of the comparison are vectors, and that
  ** both are the same length.  */
  if( nLeft!=nRight ){
    sqlite3ErrorMsg(pParse, "invalid use of row value");
  }else{
    int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0;
    int opCmp;

    int i;
    int p3 = 0;
    int p4 = 0;
    int regLeft = 0;
    int regRight = 0;
    int regTmp = 0;

    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 
    );

    if( pExpr->op==TK_EQ || pExpr->op==TK_NE ){
      regTmp = sqlite3GetTempReg(pParse);
      sqlite3VdbeAddOp2(v, OP_Integer, (pExpr->op==TK_EQ), dest);



    }
















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


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

      if( i ) sqlite3ExprCachePush(pParse);
      r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, &regFree1);
      r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, &regFree2);






      switch( pExpr->op ){
        case TK_IS:
          codeCompare(
              pParse, pL, pR, OP_Eq, r1, r2, dest, SQLITE_STOREP2|SQLITE_NULLEQ
          );
          sqlite3VdbeAddOp3(v, OP_IfNot, dest, addr, 1);
          VdbeCoverage(v);
          break;




        case TK_ISNOT:
          codeCompare(
              pParse, pL, pR, OP_Ne, r1, r2, dest, SQLITE_STOREP2|SQLITE_NULLEQ
          );
          sqlite3VdbeAddOp3(v, OP_If, dest, addr, 1);
          VdbeCoverage(v);
          break;

        case TK_EQ:
        case TK_NE:
          codeCompare(pParse, pL, pR, OP_Cmp, r1, r2, regTmp,SQLITE_STOREP2|p5);
          sqlite3VdbeAddOp4Int(
              v, OP_CmpTest, regTmp, addr, dest, pExpr->op==TK_NE
          );
          VdbeCoverage(v);
          break;

        case TK_LT:
        case TK_LE:
        case TK_GT:
        case TK_GE:
          codeCompare(pParse, pL, pR, OP_Cmp, r1, r2, dest, SQLITE_STOREP2|p5);

          sqlite3VdbeAddOp4Int(v, OP_CmpTest, dest, addr, 0, pExpr->op);
          VdbeCoverage(v);
          break;
      }

      sqlite3ReleaseTempReg(pParse, regFree1);
      sqlite3ReleaseTempReg(pParse, regFree2);
      if( i ) sqlite3ExprCachePop(pParse);
    }

    sqlite3ReleaseTempReg(pParse, regTmp);
  }

  sqlite3VdbeResolveLabel(v, addr);
}

#if SQLITE_MAX_EXPR_DEPTH>0
/*

Changes to src/vdbe.c.

1957
1958
1959
1960
1961
1962
1963









1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976

1977
1978
1979
1980
1981
1982
1983
....
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882



3883

3884
3885

3886
3887
3888
3889
3890
3891
3892
3893
3894
3895

3896



3897








3898
3899
3900
3901
3902
3903


3904

3905
3906
3907

3908
3909
3910
3911
3912
3913
3914
*/
/* Opcode: Ge P1 P2 P3 P4 P5
** Synopsis: if r[P1]>=r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is greater than or equal to the content of
** register P1.  See the Lt opcode for additional information.









*/
case OP_Cmp:              /* in1, in3 */
case OP_Eq:               /* same as TK_EQ, jump, in1, in3 */
case OP_Ne:               /* same as TK_NE, jump, in1, in3 */
case OP_Lt:               /* same as TK_LT, jump, in1, in3 */
case OP_Le:               /* same as TK_LE, jump, in1, in3 */
case OP_Gt:               /* same as TK_GT, jump, in1, in3 */
case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
  int res;            /* Result of the comparison of pIn1 against pIn3 */
  char affinity;      /* Affinity to use for comparison */
  u16 flags1;         /* Copy of initial value of pIn1->flags */
  u16 flags3;         /* Copy of initial value of pIn3->flags */


  pIn1 = &aMem[pOp->p1];
  pIn3 = &aMem[pOp->p3];
  flags1 = pIn1->flags;
  flags3 = pIn3->flags;
  if( (flags1 | flags3)&MEM_Null ){
    /* One or both operands are NULL */
    if( pOp->p5 & SQLITE_NULLEQ ){
................................................................................
  }else if( eqOnly ){
    assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT );
    pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */
  }
  break;
}

/* Opcode: CmpTest P1 P2 P3 * *
**
** P2 is a jump destination. Register P1 is guaranteed to contain either
** an integer value or a NULL. The jump is taken if P1 contains any value
** other than 0 (i.e. NULL does cause a jump).



** 

** If P1 is not NULL, its value is modified to integer value 0 or 1 
** according to the value of the P3 operand:

**
**   P3            modification
**   --------------------------
**   OP_Lt         (P1 = (P1 < 0))
**   OP_Le         (P1 = (P1 <= 0))
**   OP_Gt         (P1 = (P1 > 0))
**   OP_Ge         (P1 = (P1 >= 0))
*/
case OP_CmpTest: {                /* in1, jump */
  int bJump;

  



  pIn1 = &aMem[pOp->p1];








  if( (pIn1->flags & MEM_Int) ){
    bJump = (pIn1->u.i!=0);
    switch( pOp->p3 ){
      case OP_Lt: pIn1->u.i = (pIn1->u.i < 0); break;
      case OP_Le: pIn1->u.i = (pIn1->u.i <= 0); break;
      case OP_Gt: pIn1->u.i = (pIn1->u.i > 0); break;


      default: assert( pOp->p3==OP_Ge ); pIn1->u.i = (pIn1->u.i >= 0); break;

    }
  }else{
    bJump = 1;

  }

  if( bJump ) goto jump_to_p2;
  break;
}

/* Opcode: Found P1 P2 P3 P4 *







>
>
>
>
>
>
>
>
>













>







 







|


|
|
>
>
>
|
>
|
|
>

|








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







1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
....
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
*/
/* Opcode: Ge P1 P2 P3 P4 P5
** Synopsis: if r[P1]>=r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is greater than or equal to the content of
** register P1.  See the Lt opcode for additional information.
**
** Opcode: Cmp P1 P2 P3 P4 P5
** Synopsis: P2 = cmp(P1, P3)
**
** The SQLITE_STOREP2 flag must be set for this opcode. It compares the
** values in registers P1 and P3 and stores the result of the comparison
** in register P2. The results is NULL if either of the two operands are
** NULL. Otherwise, it is an integer value less than zero, zero or greater 
** than zero if P3 is less than, equal to or greater than P1, respectively.
*/
case OP_Cmp:              /* in1, in3 */
case OP_Eq:               /* same as TK_EQ, jump, in1, in3 */
case OP_Ne:               /* same as TK_NE, jump, in1, in3 */
case OP_Lt:               /* same as TK_LT, jump, in1, in3 */
case OP_Le:               /* same as TK_LE, jump, in1, in3 */
case OP_Gt:               /* same as TK_GT, jump, in1, in3 */
case OP_Ge: {             /* same as TK_GE, jump, in1, in3 */
  int res;            /* Result of the comparison of pIn1 against pIn3 */
  char affinity;      /* Affinity to use for comparison */
  u16 flags1;         /* Copy of initial value of pIn1->flags */
  u16 flags3;         /* Copy of initial value of pIn3->flags */

  assert( pOp->opcode!=OP_Cmp || (pOp->p5 & SQLITE_STOREP2) );
  pIn1 = &aMem[pOp->p1];
  pIn3 = &aMem[pOp->p3];
  flags1 = pIn1->flags;
  flags3 = pIn3->flags;
  if( (flags1 | flags3)&MEM_Null ){
    /* One or both operands are NULL */
    if( pOp->p5 & SQLITE_NULLEQ ){
................................................................................
  }else if( eqOnly ){
    assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT );
    pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */
  }
  break;
}

/* Opcode: CmpTest P1 P2 P3 P4 *
**
** P2 is a jump destination. Register P1 is guaranteed to contain either
** an integer value or a NULL. 
**
** If P3 is non-zero, it identifies an output register. In this case, if
** P1 is NULL, P3 is also set to NULL. Or, if P1 is any integer value 
** other than 0, P3 is set to the value of P4 and a jump to P2 is taken.
**
** If P3 is 0, the jump is taken if P1 contains any value other than 0 (i.e.
** NULL does cause a jump). Additionally, if P1 is not NULL, its value is
** modified to integer value 0 or 1 according to the value of the P4 integer
** operand:
**
**   P4            modification
**   --------------------------
**   OP_Lt         (P1 = (P1 < 0))
**   OP_Le         (P1 = (P1 <= 0))
**   OP_Gt         (P1 = (P1 > 0))
**   OP_Ge         (P1 = (P1 >= 0))
*/
case OP_CmpTest: {                /* in1, jump */
  int bJump;
  pIn1 = &aMem[pOp->p1];

  if( pOp->p3 ){
    bJump = 0;
    if( pIn1->flags & MEM_Null ){
      memAboutToChange(p, &aMem[pOp->p3]);
      MemSetTypeFlag(&aMem[pOp->p3], MEM_Null);
    }else if( pIn1->u.i!=0 ){
      memAboutToChange(p, &aMem[pOp->p3]);
      MemSetTypeFlag(&aMem[pOp->p3], MEM_Int);
      aMem[pOp->p3].u.i = pOp->p4.i;
      bJump = 1;
    }
  }else{
    if( (pIn1->flags & MEM_Int) ){
      bJump = (pIn1->u.i!=0);
      switch( pOp->p4.i ){
        case OP_Lt: pIn1->u.i = (pIn1->u.i < 0); break;
        case OP_Le: pIn1->u.i = (pIn1->u.i <= 0); break;
        case OP_Gt: pIn1->u.i = (pIn1->u.i > 0); break;
        default: 
          assert( pOp->p4.i==OP_Ge ); 
          pIn1->u.i = (pIn1->u.i >= 0); 
          break;
      }
    }else{
      bJump = 1;
    }
  }

  if( bJump ) goto jump_to_p2;
  break;
}

/* Opcode: Found P1 P2 P3 P4 *

Changes to test/rowvalue.test.

24
25
26
27
28
29
30



31
32
33
34
35
36
37

foreach {tn v1 v2 eq ne is isnot} {
  1 "1, 2, 3"    "1, 2, 3"                   1  0     1 0
  2 "1, 0, 3"    "1, 2, 3"                   0  1     0 1
  3 "1, 2, NULL" "1, 2, 3"                   {} {}    0 1
  4 "1, 2, NULL" "1, 2, NULL"                {} {}    1 0
  5 "NULL, NULL, NULL" "NULL, NULL, NULL"    {} {}    1 0



} {
  do_execsql_test 1.$tn.eq "SELECT ($v1) == ($v2)" [list $eq]
  do_execsql_test 1.$tn.ne "SELECT ($v1) != ($v2)" [list $ne]

  do_execsql_test 1.$tn.is    "SELECT ($v1) IS ($v2)"     [list $is]
  do_execsql_test 1.$tn.isnot "SELECT ($v1) IS NOT ($v2)" [list $isnot]








>
>
>







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

foreach {tn v1 v2 eq ne is isnot} {
  1 "1, 2, 3"    "1, 2, 3"                   1  0     1 0
  2 "1, 0, 3"    "1, 2, 3"                   0  1     0 1
  3 "1, 2, NULL" "1, 2, 3"                   {} {}    0 1
  4 "1, 2, NULL" "1, 2, NULL"                {} {}    1 0
  5 "NULL, NULL, NULL" "NULL, NULL, NULL"    {} {}    1 0

  6 "1, NULL, 1" "1, 1, 1"                   {} {}    0 1
  7 "1, NULL, 1" "1, 1, 2"                   0  1     0 1
} {
  do_execsql_test 1.$tn.eq "SELECT ($v1) == ($v2)" [list $eq]
  do_execsql_test 1.$tn.ne "SELECT ($v1) != ($v2)" [list $ne]

  do_execsql_test 1.$tn.is    "SELECT ($v1) IS ($v2)"     [list $is]
  do_execsql_test 1.$tn.isnot "SELECT ($v1) IS NOT ($v2)" [list $isnot]