Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix problems with vector == comparisons and NULL values. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | rowvalue |
Files: | files | file ages | folders |
SHA1: |
059d0d05354e6efab7892c97b339ffa0 |
User & Date: | dan 2016-07-30 21:02:33.694 |
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: 1f4dba87da user: dan tags: rowvalue) | |
2016-07-30
| ||
21:02 | Fix problems with vector == comparisons and NULL values. (check-in: 059d0d0535 user: dan tags: rowvalue) | |
17:59 | Merge latest trunk with this branch. (check-in: 63ae02d084 user: dan tags: rowvalue) | |
Changes
Changes to src/expr.c.
︙ | ︙ | |||
345 346 347 348 349 350 351 | return pVector; }else if( pVector->flags & EP_xIsSelect ){ return pVector->x.pSelect->pEList->a[i].pExpr; } return pVector->x.pList->a[i].pExpr; } | > > > > > > > > | < < | < < | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > > > > > < | > > | < < < < < | < < < < < | < < < < < < < < | | < < | > | | | | | > > > > | > > > > > > > > > > > > > > | | < | < > > > | > > > < < > > | 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 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 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, ®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; 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 | */ /* 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 ){ | > > > > > > > > > > | 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 | */ /* 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 ){ |
︙ | ︙ | |||
3871 3872 3873 3874 3875 3876 3877 | }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; } | | | > > > | | > | | > | > | > > > | > > > > > > > > | | | | | | > > | > | | | > | 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 | }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] |
︙ | ︙ |