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 |
Timelines: | family | ancestors | descendants | both | rowvalue |
Files: | files | file ages | folders |
SHA1: |
07e69f43a294d35b5145a2b0242ee42d |
User & Date: | drh 2016-08-22 14:30:05.854 |
Context
2016-08-23
| ||
17:30 | Fix an uninitialized variable in CASE expression code generation. (check-in: c8ffae05e1 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: 07e69f43a2 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: 2f39987f21 user: drh tags: rowvalue) | |
Changes
Changes to src/expr.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** 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, | > > > > > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ** 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, |
︙ | ︙ | |||
322 323 324 325 326 327 328 | /* ** 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){ | > > | | | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | /* ** 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 |
︙ | ︙ | |||
350 351 352 353 354 355 356 | ** 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) ){ | | > > | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | ** 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; } |
︙ | ︙ | |||
463 464 465 466 467 468 469 | 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 */ ){ | | | > > | > | | 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | 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); } |
︙ | ︙ | |||
2626 2627 2628 2629 2630 2631 2632 | 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 */ | | > | 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 | 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); |
︙ | ︙ | |||
2667 2668 2669 2670 2671 2672 2673 | /* 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); | < | | | < < < < < < | 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 | /* 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 ){ |
︙ | ︙ | |||
3224 3225 3226 3227 3228 3229 3230 | } return 0; } #endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ /* | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | } 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 |
︙ | ︙ | |||
3777 3778 3779 3780 3781 3782 3783 | 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 ); | | | 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 | 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, ®Free1)); 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 |
︙ | ︙ | |||
4095 4096 4097 4098 4099 4100 4101 | 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; | < | < | 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 | 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, ®Free1)); 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 |