/ Check-in [07e69f43]
Login

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

Overview
Comment:Fix the vector BETWEEN operator so that it only evaluates the left-most vector expression once. Add support for vector comparisons in the CASE operator.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rowvalue
Files: files | file ages | folders
SHA1:07e69f43a294d35b5145a2b0242ee42d50adab14
User & Date: drh 2016-08-22 14:30:05
Context
2016-08-23
17:30
Fix an uninitialized variable in CASE expression code generation. check-in: c8ffae05 user: drh tags: rowvalue
2016-08-22
14:30
Fix the vector BETWEEN operator so that it only evaluates the left-most vector expression once. Add support for vector comparisons in the CASE operator. check-in: 07e69f43 user: drh tags: rowvalue
00:48
Reinstate the mechanism in BETWEEN that avoids evaluating the first expression more than once, but fix the affinity extractor so that it works with this mechanism. The de-duplication of the first expression still does not work for vector expressions, though. check-in: 2f39987f user: drh tags: rowvalue
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/expr.c.

9
10
11
12
13
14
15





16
17
18
19
20
21
22
...
322
323
324
325
326
327
328


329
330
331
332
333
334
335
336
337
338
...
350
351
352
353
354
355
356
357


358
359
360
361
362
363
364
...
463
464
465
466
467
468
469
470
471
472



473
474
475
476
477
478
479
480
....
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637

2638
2639
2640
2641
2642
2643
2644
....
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
....
3224
3225
3226
3227
3228
3229
3230
3231


3232
3233
3234
3235
3236
3237
3238
3239
3240































3241
3242
3243
3244
3245
3246
3247
....
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
....
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
*/
#include "sqliteInt.h"






/*
** Return the 'affinity' of the expression pExpr if any.
**
** If pExpr is a column, a reference to a column via an 'AS' alias,
** or a sub-select with a column as the return value, then the 
** affinity of that column is returned. Otherwise, 0x00 is returned,
................................................................................
/*
** If the expression passed as the only argument is of type TK_VECTOR 
** return the number of expressions in the vector. Or, if the expression
** is a sub-select, return the number of columns in the sub-select. For
** any other type of expression, return 1.
*/
int sqlite3ExprVectorSize(Expr *pExpr){


  if( pExpr->op==TK_VECTOR ){
    return pExpr->x.pList->nExpr;
  }else if( pExpr->op==TK_SELECT ){
    return pExpr->x.pSelect->pEList->nExpr;
  }else{
    return 1;
  }
}

#ifndef SQLITE_OMIT_SUBQUERY
................................................................................
** just the expression for the i-th term of the result set, and may
** not be ready for evaluation because the table cursor has not yet
** been positioned.
*/
Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){
  assert( i<sqlite3ExprVectorSize(pVector) );
  if( sqlite3ExprIsVector(pVector) ){
    if( pVector->op==TK_SELECT ){


      return pVector->x.pSelect->pEList->a[i].pExpr;
    }else{
      return pVector->x.pList->a[i].pExpr;
    }
  }
  return pVector;
}
................................................................................
  Parse *pParse,                  /* Parse context */
  Expr *pVector,                  /* Vector to extract element from */
  int iField,                     /* Field to extract from pVector */
  int regSelect,                  /* First in array of registers */
  Expr **ppExpr,                  /* OUT: Expression element */
  int *pRegFree                   /* OUT: Temp register to free */
){
  assert( pVector->op==TK_VECTOR || pVector->op==TK_SELECT );
  assert( pParse->nErr || pParse->db->mallocFailed
          || (pVector->op==TK_VECTOR)==(regSelect==0) );



  if( pVector->op==TK_SELECT ){
    *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr;
     return regSelect+iField;
  }
  *ppExpr = pVector->x.pList->a[iField].pExpr;
  return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree);
}

................................................................................
  Parse *pParse,        /* Parsing and code generating context */
  Expr *pExpr,          /* The IN expression */
  int destIfFalse,      /* Jump here if LHS is not contained in the RHS */
  int destIfNull        /* Jump here if the results are unknown due to NULLs */
){
  int rRhsHasNull = 0;  /* Register that is true if RHS contains NULL values */
  int eType;            /* Type of the RHS */
  int r1;               /* Temporary use register */
  Vdbe *v;              /* Statement under construction */
  int *aiMap = 0;       /* Map from vector field to index column */
  char *zAff = 0;       /* Affinity string for comparisons */
  int nVector;          /* Size of vectors for this IN(...) op */

  Expr *pLeft = pExpr->pLeft;
  int i;

  if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
  zAff = exprINAffinity(pParse, pExpr);
  if( zAff==0 ) return;
  nVector = sqlite3ExprVectorSize(pExpr->pLeft);
................................................................................

  /* Code the LHS, the <expr> from "<expr> IN (...)". If the LHS is a 
  ** vector, then it is stored in an array of nVector registers starting 
  ** at r1.
  */
  r1 = sqlite3GetTempRange(pParse, nVector);
  sqlite3ExprCachePush(pParse);
  if( nVector>1 && (pLeft->flags & EP_xIsSelect) ){
    int regSelect = sqlite3CodeSubselect(pParse, pLeft, 0, 0);
    for(i=0; i<nVector; i++){
      sqlite3VdbeAddOp3(v, OP_Copy, regSelect+i, r1+aiMap[i], 0);
    }
  }else{
    for(i=0; i<nVector; i++){
      Expr *pLhs = sqlite3VectorFieldSubexpr(pLeft, i);
      sqlite3ExprCode(pParse, pLhs, r1+aiMap[i]);
    }
  }

  /* If sqlite3FindInIndex() did not find or create an index that is
  ** suitable for evaluating the IN operator, then evaluate using a
  ** sequence of comparisons.
  */
  if( eType==IN_INDEX_NOOP ){
................................................................................
  }
  return 0;
}
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */


/*
** Convert an expression node to a TK_REGISTER


*/
static void exprToRegister(Expr *p, int iReg){
  p->op2 = p->op;
  p->op = TK_REGISTER;
  p->iTable = iReg;
  ExprClearProperty(p, EP_Skip);
}

static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int);
































/*
** Generate code into the current Vdbe to evaluate the given
** expression.  Attempt to store the results in register "target".
** Return the register where results are stored.
**
** With this routine, there is no guarantee that results will
................................................................................
      pEList = pExpr->x.pList;
      aListelem = pEList->a;
      nExpr = pEList->nExpr;
      endLabel = sqlite3VdbeMakeLabel(v);
      if( (pX = pExpr->pLeft)!=0 ){
        tempX = *pX;
        testcase( pX->op==TK_COLUMN );
        exprToRegister(&tempX, sqlite3ExprCodeTemp(pParse, pX, &regFree1));
        testcase( regFree1==0 );
        opCompare.op = TK_EQ;
        opCompare.pLeft = &tempX;
        pTest = &opCompare;
        /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
        ** The value in regFree1 might get SCopy-ed into the file result.
        ** So make sure that the regFree1 register is not reused for other
................................................................................
  exprAnd.pRight = &compRight;
  compLeft.op = TK_GE;
  compLeft.pLeft = &exprX;
  compLeft.pRight = pExpr->x.pList->a[0].pExpr;
  compRight.op = TK_LE;
  compRight.pLeft = &exprX;
  compRight.pRight = pExpr->x.pList->a[1].pExpr;
  if( sqlite3ExprIsVector(&exprX)==0 ){
    exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, &regFree1));
  }
  if( xJump ){
    xJump(pParse, &exprAnd, dest, jumpIfNull);
  }else{
    exprX.flags |= EP_FromJoin;
    sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
  }
  sqlite3ReleaseTempReg(pParse, regFree1);







>
>
>
>
>







 







>
>
|

|







 







|
>
>







 







|
|
|
>
>
>
|







 







|




>







 







|
<
|
|
<
<
<
<
<
<







 







|
>
>








<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







<
|
<







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
...
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
....
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
....
2680
2681
2682
2683
2684
2685
2686
2687

2688
2689






2690
2691
2692
2693
2694
2695
2696
....
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247

3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
....
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
....
4133
4134
4135
4136
4137
4138
4139

4140

4141
4142
4143
4144
4145
4146
4147
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
*/
#include "sqliteInt.h"

/* Forward declarations */
static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int);
static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree);


/*
** Return the 'affinity' of the expression pExpr if any.
**
** If pExpr is a column, a reference to a column via an 'AS' alias,
** or a sub-select with a column as the return value, then the 
** affinity of that column is returned. Otherwise, 0x00 is returned,
................................................................................
/*
** If the expression passed as the only argument is of type TK_VECTOR 
** return the number of expressions in the vector. Or, if the expression
** is a sub-select, return the number of columns in the sub-select. For
** any other type of expression, return 1.
*/
int sqlite3ExprVectorSize(Expr *pExpr){
  u8 op = pExpr->op;
  if( op==TK_REGISTER ) op = pExpr->op2;
  if( op==TK_VECTOR ){
    return pExpr->x.pList->nExpr;
  }else if( op==TK_SELECT ){
    return pExpr->x.pSelect->pEList->nExpr;
  }else{
    return 1;
  }
}

#ifndef SQLITE_OMIT_SUBQUERY
................................................................................
** just the expression for the i-th term of the result set, and may
** not be ready for evaluation because the table cursor has not yet
** been positioned.
*/
Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){
  assert( i<sqlite3ExprVectorSize(pVector) );
  if( sqlite3ExprIsVector(pVector) ){
    if( pVector->op==TK_SELECT
     || (pVector->op==TK_REGISTER && pVector->op2==TK_SELECT)
    ){
      return pVector->x.pSelect->pEList->a[i].pExpr;
    }else{
      return pVector->x.pList->a[i].pExpr;
    }
  }
  return pVector;
}
................................................................................
  Parse *pParse,                  /* Parse context */
  Expr *pVector,                  /* Vector to extract element from */
  int iField,                     /* Field to extract from pVector */
  int regSelect,                  /* First in array of registers */
  Expr **ppExpr,                  /* OUT: Expression element */
  int *pRegFree                   /* OUT: Temp register to free */
){
  u8 op = pVector->op;
  assert( op==TK_VECTOR || op==TK_SELECT || op==TK_REGISTER );
  if( op==TK_REGISTER ){
    *ppExpr = sqlite3VectorFieldSubexpr(pVector, iField);
    return pVector->iTable+iField;
  }
  if( op==TK_SELECT ){
    *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr;
     return regSelect+iField;
  }
  *ppExpr = pVector->x.pList->a[iField].pExpr;
  return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree);
}

................................................................................
  Parse *pParse,        /* Parsing and code generating context */
  Expr *pExpr,          /* The IN expression */
  int destIfFalse,      /* Jump here if LHS is not contained in the RHS */
  int destIfNull        /* Jump here if the results are unknown due to NULLs */
){
  int rRhsHasNull = 0;  /* Register that is true if RHS contains NULL values */
  int eType;            /* Type of the RHS */
  int r1, r2;           /* Temporary use registers */
  Vdbe *v;              /* Statement under construction */
  int *aiMap = 0;       /* Map from vector field to index column */
  char *zAff = 0;       /* Affinity string for comparisons */
  int nVector;          /* Size of vectors for this IN(...) op */
  int iDummy;           /* Dummy parameter to exprCodeVector() */
  Expr *pLeft = pExpr->pLeft;
  int i;

  if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
  zAff = exprINAffinity(pParse, pExpr);
  if( zAff==0 ) return;
  nVector = sqlite3ExprVectorSize(pExpr->pLeft);
................................................................................

  /* Code the LHS, the <expr> from "<expr> IN (...)". If the LHS is a 
  ** vector, then it is stored in an array of nVector registers starting 
  ** at r1.
  */
  r1 = sqlite3GetTempRange(pParse, nVector);
  sqlite3ExprCachePush(pParse);
  r2 = exprCodeVector(pParse, pLeft, &iDummy);

  for(i=0; i<nVector; i++){
    sqlite3VdbeAddOp3(v, OP_Copy, r2+i, r1+aiMap[i], 0);






  }

  /* If sqlite3FindInIndex() did not find or create an index that is
  ** suitable for evaluating the IN operator, then evaluate using a
  ** sequence of comparisons.
  */
  if( eType==IN_INDEX_NOOP ){
................................................................................
  }
  return 0;
}
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */


/*
** Convert a scalar expression node to a TK_REGISTER referencing
** register iReg.  The caller must ensure that iReg already contains
** the correct value for the expression.
*/
static void exprToRegister(Expr *p, int iReg){
  p->op2 = p->op;
  p->op = TK_REGISTER;
  p->iTable = iReg;
  ExprClearProperty(p, EP_Skip);
}


/*
** Evaluate an expression (either a vector or a scalar expression) and store
** the result in continguous temporary registers.  Return the index of
** the first register used to store the result.
**
** If the returned result register is a temporary scalar, then also write
** that register number into *piFreeable.  If the returned result register
** is not a temporary or if the expression is a vector set *piFreeable
** to 0.
*/
static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
  int iResult;
  int nResult = sqlite3ExprVectorSize(p);
  if( nResult==1 ){
    iResult = sqlite3ExprCodeTemp(pParse, p, piFreeable);
  }else{
    *piFreeable = 0;
    if( p->op==TK_SELECT ){
      iResult = sqlite3CodeSubselect(pParse, p, 0, 0);
    }else{
      int i;
      iResult = pParse->nMem+1;
      pParse->nMem += nResult;
      for(i=0; i<nResult; i++){
        sqlite3ExprCode(pParse, p->x.pList->a[i].pExpr, i+iResult);
      }
    }
  }
  return iResult;
}


/*
** Generate code into the current Vdbe to evaluate the given
** expression.  Attempt to store the results in register "target".
** Return the register where results are stored.
**
** With this routine, there is no guarantee that results will
................................................................................
      pEList = pExpr->x.pList;
      aListelem = pEList->a;
      nExpr = pEList->nExpr;
      endLabel = sqlite3VdbeMakeLabel(v);
      if( (pX = pExpr->pLeft)!=0 ){
        tempX = *pX;
        testcase( pX->op==TK_COLUMN );
        exprToRegister(&tempX, exprCodeVector(pParse, &tempX, &regFree1));
        testcase( regFree1==0 );
        opCompare.op = TK_EQ;
        opCompare.pLeft = &tempX;
        pTest = &opCompare;
        /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
        ** The value in regFree1 might get SCopy-ed into the file result.
        ** So make sure that the regFree1 register is not reused for other
................................................................................
  exprAnd.pRight = &compRight;
  compLeft.op = TK_GE;
  compLeft.pLeft = &exprX;
  compLeft.pRight = pExpr->x.pList->a[0].pExpr;
  compRight.op = TK_LE;
  compRight.pLeft = &exprX;
  compRight.pRight = pExpr->x.pList->a[1].pExpr;

  exprToRegister(&exprX, exprCodeVector(pParse, &exprX, &regFree1));

  if( xJump ){
    xJump(pParse, &exprAnd, dest, jumpIfNull);
  }else{
    exprX.flags |= EP_FromJoin;
    sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
  }
  sqlite3ReleaseTempReg(pParse, regFree1);

Changes to test/e_expr.test.

843
844
845
846
847
848
849



850
851
852
853
854
855
856
proc x {} { incr ::xcount ; return [expr $::x] }
foreach {tn x expr res nEval} {
  1  10  "x() >= 5 AND x() <= 15"  1  2
  2  10  "x() BETWEEN 5 AND 15"    1  1

  3   5  "x() >= 5 AND x() <= 5"   1  2
  4   5  "x() BETWEEN 5 AND 5"     1  1



} {
  do_test e_expr-13.1.$tn {
    set ::xcount 0
    set a [execsql "SELECT $expr"]
    list $::xcount $a
  } [list $nEval $res]
}







>
>
>







843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
proc x {} { incr ::xcount ; return [expr $::x] }
foreach {tn x expr res nEval} {
  1  10  "x() >= 5 AND x() <= 15"  1  2
  2  10  "x() BETWEEN 5 AND 15"    1  1

  3   5  "x() >= 5 AND x() <= 5"   1  2
  4   5  "x() BETWEEN 5 AND 5"     1  1

  5   9  "(x(),8) >= (9,7) AND (x(),8)<=(9,10)"  1 2
  6   9  "(x(),8) BETWEEN (9,7) AND (9,10)"      1 1
} {
  do_test e_expr-13.1.$tn {
    set ::xcount 0
    set a [execsql "SELECT $expr"]
    list $::xcount $a
  } [list $nEval $res]
}

Added test/rowvalue8.test.























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 2016-08-22
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# Use of row values in CASE statements.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix rowvalue8

do_execsql_test 1.1 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d);
  INSERT INTO t1(a,b,c,d) VALUES
     (1,1,2,3),
     (2,2,3,4),
     (3,1,2,4),
     (4,2,3,5),
     (5,3,4,6),
     (6,4,5,9);
  SELECT a, CASE (b,c) WHEN (1,2) THEN 'aleph'
                       WHEN (2,3) THEN 'bet'
                       WHEN (3,4) THEN 'gimel'
                       ELSE '-' END,
         '|'
    FROM t1
   ORDER BY a;
} {1 aleph | 2 bet | 3 aleph | 4 bet | 5 gimel | 6 - |}
do_execsql_test 1.2 {
  SELECT a, CASE (b,c,d) WHEN (1,2,3) THEN 'aleph'
                         WHEN (2,3,4) THEN 'bet'
                         WHEN (3,4,6) THEN 'gimel'
                         ELSE '-' END,
         '|'
    FROM t1
   ORDER BY a;
} {1 aleph | 2 bet | 3 - | 4 - | 5 gimel | 6 - |}

do_execsql_test 2.1 {
  CREATE TABLE t2(x INTEGER PRIMARY KEY, y);
  INSERT INTO t2(x,y) VALUES(1,6),(2,5),(3,4),(4,3),(5,2),(6,1);
  SELECT x, CASE (SELECT b,c FROM t1 WHERE a=y)
            WHEN (1,2) THEN 'aleph'
            WHEN (2,3) THEN 'bet'
            WHEN (3,4) THEN 'gimel'
            ELSE '-' END,
         '|'
    FROM t2
   ORDER BY +x;
} {1 - | 2 gimel | 3 bet | 4 aleph | 5 bet | 6 aleph |}


finish_test