From: dan Date: Sat, 30 Jul 2016 21:02:33 +0000 (+0000) Subject: Fix problems with vector == comparisons and NULL values. X-Git-Tag: version-3.15.0~110^2~95 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5c288b929a80fb532c00e74853f097b267eff860;p=thirdparty%2Fsqlite.git Fix problems with vector == comparisons and NULL values. FossilOrigin-Name: 059d0d05354e6efab7892c97b339ffa0b5303587 --- diff --git a/manifest b/manifest index fdb83a0054..6cdc700a0e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\swith\sthis\sbranch. -D 2016-07-30T17:59:39.678 +C Fix\sproblems\swith\svector\s==\scomparisons\sand\sNULL\svalues. +D 2016-07-30T21:02:33.694 F Makefile.in 6c20d44f72d4564f11652b26291a214c8367e5db F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 3340e479e5221f06c3d61726f8f7efff885e4233 @@ -337,7 +337,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39 F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f -F src/expr.c 4e79d7a355a59d5d0635f0a718a07abf21fe69bc +F src/expr.c f33dcbaf364c5c54a2f1aab7cf1de9fbd0c88f3d F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 F src/func.c 61a4114cf7004f10c542cfabbab9f2bcb9033045 @@ -449,7 +449,7 @@ F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/vacuum.c 9dd2f5d276bc6094d8f1d85ecd41b30c1a002a43 -F src/vdbe.c 44d75e3585d93bf56c72de5e2ec4c5a16cd40370 +F src/vdbe.c 9f15129214a55044f918a983e1560fd87b0130a8 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b @@ -1018,7 +1018,7 @@ F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test 8656a46fac138f2a964aebbc37fc256d0a956b9c +F test/rowvalue.test f4c06c37d8e3212156435495da453e12bdeef7e5 F test/rowvalue2.test 8d5dfe75b8f4d1868a2f91f0356f20d36cba64ff F test/rowvalue3.test 5127afb4414bf62546161497c04840c46e371770 F test/rowvalue4.test 4480898d62d6813e3e38d9d38c02b9a0be5f94be @@ -1512,7 +1512,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e9d9c6d46b46160fad6aa6e3441a65a09157515f 9fae75c08b7d3b3e13734193ad8398ef6971cbac -R 9891078e76bc1592d3c3f0175b7e1e59 +P 63ae02d084a332250ff6fd8d8c80e53bf5422a68 +R 27dbfd24654b70fef0137ceb387adc85 U dan -Z b42ff5e1f9b23663ffe713255f8923fa +Z 07f670f92b12ec2bcc84d4a9aa33539d diff --git a/manifest.uuid b/manifest.uuid index b26f57e106..f6c7658784 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -63ae02d084a332250ff6fd8d8c80e53bf5422a68 \ No newline at end of file +059d0d05354e6efab7892c97b339ffa0b5303587 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 5f5e1d058c..5acce2f0c7 100644 --- a/src/expr.c +++ b/src/expr.c @@ -349,19 +349,59 @@ static Expr *exprVectorField(Expr *pVector, int i){ return pVector->x.pList->a[i].pExpr; } -static int exprVectorSubselect(Parse *pParse, Expr *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->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); - } + 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; @@ -377,11 +417,12 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ }else{ int p5 = (pExpr->op==TK_IS || pExpr->op==TK_ISNOT) ? SQLITE_NULLEQ : 0; int opCmp; - int opTest; int i; - int p3 = 1; + 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 @@ -389,61 +430,64 @@ static void codeVectorCompare(Parse *pParse, Expr *pExpr, int dest){ || 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; + if( pExpr->op==TK_EQ || pExpr->op==TK_NE ){ + regTmp = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_Integer, (pExpr->op==TK_EQ), dest); } - regLeft = exprVectorSubselect(pParse, pLeft); - regRight = exprVectorSubselect(pParse, pRight); - if( pParse->nErr ) return; + regLeft = exprCodeSubselect(pParse, pLeft); + regRight = exprCodeSubselect(pParse, pRight); for(i=0; ix.pSelect->pEList->a[i].pExpr; - r1 = regLeft+i; - }else{ - pL = pLeft->x.pList->a[i].pExpr; - r1 = sqlite3ExprCodeTemp(pParse, pL, ®Free1); - } + r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1); + r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2); + + 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; - 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, ®Free1); + 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; } - 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); } + + sqlite3ReleaseTempReg(pParse, regTmp); } sqlite3VdbeResolveLabel(v, addr); diff --git a/src/vdbe.c b/src/vdbe.c index e2e1afb504..054f1997f3 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1961,6 +1961,15 @@ case OP_Cast: { /* in1 */ ** 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 */ @@ -1974,6 +1983,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ 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; @@ -3875,16 +3885,21 @@ seek_not_found: break; } -/* Opcode: CmpTest P1 P2 P3 * * +/* Opcode: CmpTest P1 P2 P3 P4 * ** ** 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: +** 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. ** -** P3 modification +** 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)) @@ -3893,18 +3908,34 @@ seek_not_found: */ 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; + + 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{ - bJump = 1; + 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; diff --git a/test/rowvalue.test b/test/rowvalue.test index 3989c39513..efa870009f 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -28,6 +28,9 @@ foreach {tn v1 v2 eq ne is isnot} { 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]