Fix problems with vector == comparisons and NULL values.

FossilOrigin-Name: 059d0d05354e6efab7892c97b339ffa0b5303587
This commit is contained in:
dan 2016-07-30 21:02:33 +00:00
parent cb17ce9300
commit 5c288b929a
5 changed files with 154 additions and 76 deletions

View File

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

View File

@ -1 +1 @@
63ae02d084a332250ff6fd8d8c80e53bf5422a68
059d0d05354e6efab7892c97b339ffa0b5303587

View File

@ -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{
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
);
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_EQ:
case TK_IS:
opTest = OP_IfNot;
opCmp = OP_Eq;
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_NE:
case TK_ISNOT:
opTest = OP_If;
opCmp = OP_Ne;
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:
opCmp = OP_Cmp;
opTest = OP_CmpTest;
p3 = pExpr->op;
codeCompare(pParse, pL, pR, OP_Cmp, r1, r2, dest, SQLITE_STOREP2|p5);
sqlite3VdbeAddOp4Int(v, OP_CmpTest, dest, addr, 0, pExpr->op);
VdbeCoverage(v);
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);
}
sqlite3ReleaseTempReg(pParse, regTmp);
}
sqlite3VdbeResolveLabel(v, addr);

View File

@ -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).
** an integer value or a NULL.
**
** If P1 is not NULL, its value is modified to integer value 0 or 1
** according to the value of the P3 operand:
** 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,19 +3908,35 @@ seek_not_found:
*/
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->p3 ){
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->p3==OP_Ge ); 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;

View File

@ -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]