From: drh Date: Wed, 23 Sep 2009 02:29:36 +0000 (+0000) Subject: Generalize the IS and IS NOT operators so that their right-hand side can be X-Git-Tag: fts3-refactor~168 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6a2fe09387572e67602e84517d9a68063040209e;p=thirdparty%2Fsqlite.git Generalize the IS and IS NOT operators so that their right-hand side can be an arbitrary expression and not simple the constant NULL. They work like = and <> except that NULL values compare equal to one another an unequal to everything else. FossilOrigin-Name: 98853f6104076c50ea92175e17a3254bfbbd4619 --- diff --git a/addopcodes.awk b/addopcodes.awk index 66d6e19baa..c90e1dd7f4 100644 --- a/addopcodes.awk +++ b/addopcodes.awk @@ -19,6 +19,7 @@ END { printf "#define TK_%-29s %4d\n", "TO_NUMERIC", ++max printf "#define TK_%-29s %4d\n", "TO_INT", ++max printf "#define TK_%-29s %4d\n", "TO_REAL", ++max + printf "#define TK_%-29s %4d\n", "ISNOT", ++max printf "#define TK_%-29s %4d\n", "END_OF_FILE", ++max printf "#define TK_%-29s %4d\n", "ILLEGAL", ++max printf "#define TK_%-29s %4d\n", "SPACE", ++max diff --git a/manifest b/manifest index e7db2c5897..212cfdf42e 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,8 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 -C Factor\sthe\sUMINUS\sand\sUPLUS\stokens\sout\sof\sthe\sparser\sso\sthat\sthe\sparser\ntables\scan\sgo\sback\sto\susing\s8-bit\svalues\sinstead\sof\s16-bit\svalues. -D 2009-09-22T20:08:35 +C Generalize\sthe\sIS\sand\sIS\sNOT\soperators\sso\sthat\stheir\sright-hand\sside\scan\sbe\nan\sarbitrary\sexpression\sand\snot\ssimple\sthe\sconstant\sNULL.\s\sThey\swork\slike\n=\sand\s<>\sexcept\sthat\sNULL\svalues\scompare\sequal\sto\sone\sanother\san\sunequal\sto\neverything\selse. +D 2009-09-23T02:29:37 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -10,7 +10,7 @@ F Makefile.vxworks 10010ddbf52e2503c7c49c7c0b7c7a096f8638a6 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 F VERSION 7260e7baf934051dee42458206e915b75570f41d F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 -F addopcodes.awk 08eb3bdfef10a131530e3ad7fa1a6902a52dad15 +F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248 F art/SQLite.eps 9b43cc99cfd2be687d386faea6862ea68d6a72b2 F art/SQLite.gif 1bbb94484963f1382e27e1c5e86dd0c1061eba2b @@ -117,7 +117,7 @@ F src/callback.c 10d237171472865f58fb07d515737238c9e06688 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/date.c 657ff12ca0f1195b531561afacbb38b772d16638 F src/delete.c 15499f5d10047d38e68ce991b3f88cbddb6e0931 -F src/expr.c 638b599adad562d41c3bf90f542f9419664aa7b8 +F src/expr.c 8a663240f374a5326ee157df3d27751f58b7676a F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/fkey.c fa1ad144926a8536e23cbbf64c0e51b8f2fdd4bf F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606 @@ -153,7 +153,7 @@ F src/os_unix.c 5369272992c14dd198c02ddfc2fd7a1516906c40 F src/os_win.c 49a360be4f42d5a63d00be9aa44449ed4d6717e0 F src/pager.c ebd0a8f2421e8f0ad5b78201440004bf3e1c96d8 F src/pager.h 11852d044c86cf5a9d6e34171fb0c4fcf1f6265f -F src/parse.y 749f39fa218418a19c46970336c2343214e2fe05 +F src/parse.y 563ecc9e633bbb465383667380aff1646ecd96f7 F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36 F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c @@ -167,7 +167,7 @@ F src/select.c 1d0a13137532321b4364f964e46f057d271691e3 F src/shell.c d0171721c7402b368e257ddfc09ed54d0c74070c F src/sqlite.h.in 5af8181f815831a8672c3834c60e6b4418448bcc F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h 6a949ccf79ae441ac37f0d3033204895aa2a9e6a +F src/sqliteInt.h 35f0d4203924272b5ae849c88b7b857eda3bb272 F src/sqliteLimit.h 504a3161886d2938cbd163054ad620b8356df758 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d @@ -209,7 +209,7 @@ F src/update.c 4fac66ecaea13c9c13e7d3de8da66aae3cce90e2 F src/utf.c 99cf927eabb104621ba889ac0dd075fc1657ad30 F src/util.c 59d4e9456bf1fe581f415a783fa0cee6115c8f35 F src/vacuum.c 869d08eaab64e2a4eaf4ef9ea34b851892b65a75 -F src/vdbe.c 03857c07b8d159eb86ff559138d2eb5317cbfe32 +F src/vdbe.c 4946e2ac1124b0cb0ee882ed7628d2d8b339b8ef F src/vdbe.h 7d5075e3fa4e5587a9be8d5e503857c825490cef F src/vdbeInt.h 546ed25cad488c053819e19d09751d71d3ce3601 F src/vdbeapi.c 524d79eb17bbcbe31c37c908b8e01edc5c684a90 @@ -756,14 +756,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 55b263fa2b2fed8c721154e3c48f4226be95065c -R eef40f23e9c60a6f8dbfd20ac239494a +P 3fc938c961fd7810594224b91a2d6e1ac9e48084 +R d99ab25244fa05bd56d10d71bb3bee44 U drh -Z a26ff37b38b185f4ba103e5a00635dcb +Z 4608482892179163853473e1fbf42a31 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) -iD8DBQFKuS7GoxKgR168RlERArmIAJ9Zh9EPcETkJ903DGxflMeqs+sSrQCfRsQh -N09u5KCVOpvUNYJvUIHJJug= -=Nr38 +iD8DBQFKuYgUoxKgR168RlERApeHAJ48L4oeCXw8ZV6ov3G5+bjNMfQIewCfX/Rc +qXj8iamFDJ6JXwF0VP/iAOI= +=lSDQ -----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index d94e23f4ce..82c45ac0f3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3fc938c961fd7810594224b91a2d6e1ac9e48084 \ No newline at end of file +98853f6104076c50ea92175e17a3254bfbbd4619 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 50b24de413..998bf41a80 100644 --- a/src/expr.c +++ b/src/expr.c @@ -152,7 +152,7 @@ static char comparisonAffinity(Expr *pExpr){ char aff; assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT || pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE || - pExpr->op==TK_NE ); + pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT ); assert( pExpr->pLeft ); aff = sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ @@ -2244,6 +2244,19 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ testcase( regFree2==0 ); break; } + case TK_IS: + case TK_ISNOT: { + testcase( op==TK_IS ); + testcase( op==TK_ISNOT ); + codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, + pExpr->pRight, &r2, ®Free2); + op = (op==TK_IS) ? TK_EQ : TK_NE; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; + } case TK_AND: case TK_OR: case TK_PLUS: @@ -3018,6 +3031,19 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ testcase( regFree2==0 ); break; } + case TK_IS: + case TK_ISNOT: { + testcase( op==TK_IS ); + testcase( op==TK_ISNOT ); + codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, + pExpr->pRight, &r2, ®Free2); + op = (op==TK_IS) ? TK_EQ : TK_NE; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, dest, SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; + } case TK_ISNULL: case TK_NOTNULL: { assert( TK_ISNULL==OP_IsNull ); @@ -3167,6 +3193,19 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ testcase( regFree2==0 ); break; } + case TK_IS: + case TK_ISNOT: { + testcase( op==TK_IS ); + testcase( op==TK_ISNOT ); + codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, + pExpr->pRight, &r2, ®Free2); + op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, dest, SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; + } case TK_ISNULL: case TK_NOTNULL: { testcase( op==TK_ISNULL ); diff --git a/src/parse.y b/src/parse.y index f668228596..8fe4ef0277 100644 --- a/src/parse.y +++ b/src/parse.y @@ -884,10 +884,26 @@ expr(A) ::= expr(X) likeop(OP) expr(Y) escape(E). [LIKE_KW] { } expr(A) ::= expr(X) ISNULL|NOTNULL(E). {spanUnaryPostfix(&A,pParse,@E,&X,&E);} -expr(A) ::= expr(X) IS NULL(E). {spanUnaryPostfix(&A,pParse,TK_ISNULL,&X,&E);} expr(A) ::= expr(X) NOT NULL(E). {spanUnaryPostfix(&A,pParse,TK_NOTNULL,&X,&E);} -expr(A) ::= expr(X) IS NOT NULL(E). - {spanUnaryPostfix(&A,pParse,TK_NOTNULL,&X,&E);} + +// expr1 IS expr2 +// expr1 IS NOT expr2 +// +// If expr2 is NULL then code as TK_ISNULL or TK_NOTNULL. If expr2 +// is any other expression, code as TK_IS or TK_ISNOT. +// +expr(A) ::= expr(X) IS expr(Y). { + spanBinaryExpr(&A,pParse,TK_IS,&X,&Y); + if( pParse->db->mallocFailed==0 && Y.pExpr->op==TK_NULL ){ + A.pExpr->op = TK_ISNULL; + } +} +expr(A) ::= expr(X) IS NOT expr(Y). { + spanBinaryExpr(&A,pParse,TK_ISNOT,&X,&Y); + if( pParse->db->mallocFailed==0 && Y.pExpr->op==TK_NULL ){ + A.pExpr->op = TK_NOTNULL; + } +} %include { /* Construct an expression node for a unary prefix operator diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 438ed583f6..17aed293b2 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1126,6 +1126,7 @@ struct CollSeq { */ #define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ #define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ +#define SQLITE_NULLEQ 0x80 /* NULL=NULL */ /* ** An object of this type is created for each virtual table present in diff --git a/src/vdbe.c b/src/vdbe.c index 71487d1a90..b18ddc5b54 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1680,12 +1680,24 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ ** This works just like the Lt opcode except that the jump is taken if ** the operands in registers P1 and P3 are not equal. See the Lt opcode for ** additional information. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is false. If either operand is NULL then the result is true. +** If neither operand is NULL the the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Eq P1 P2 P3 P4 P5 ** ** This works just like the Lt opcode except that the jump is taken if ** the operands in registers P1 and P3 are equal. ** See the Lt opcode for additional information. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is true. If either operand is NULL then the result is false. +** If neither operand is NULL the the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Le P1 P2 P3 P4 P5 ** @@ -1711,37 +1723,46 @@ 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 flags; - int res; - char affinity; - - flags = pIn1->flags|pIn3->flags; - - if( flags&MEM_Null ){ - /* If either operand is NULL then the result is always NULL. - ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. - */ - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &p->aMem[pOp->p2]; - MemSetTypeFlag(pOut, MEM_Null); - REGISTER_TRACE(pOp->p2, pOut); - }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; + int res; /* Result of the comparison of pIn1 against pIn3 */ + char affinity; /* Affinity to use for comparison */ + + if( (pIn1->flags | pIn3->flags)&MEM_Null ){ + /* One or both operands are NULL */ + if( pOp->p5 & SQLITE_NULLEQ ){ + /* If SQLITE_NULLEQ is set (which will only happen if the operator is + ** OP_Eq or OP_Ne) then take the jump or not depending on whether + ** or not both operands are null. + */ + assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); + res = (pIn1->flags & pIn3->flags & MEM_Null)==0; + }else{ + /* SQLITE_NULLEQ is clear and at least one operand is NULL, + ** then the result is always NULL. + ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. + */ + if( pOp->p5 & SQLITE_STOREP2 ){ + pOut = &p->aMem[pOp->p2]; + MemSetTypeFlag(pOut, MEM_Null); + REGISTER_TRACE(pOp->p2, pOut); + }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ + pc = pOp->p2-1; + } + break; + } + }else{ + /* Neither operand is NULL. Do a comparison. */ + affinity = pOp->p5 & SQLITE_AFF_MASK; + if( affinity ){ + applyAffinity(pIn1, affinity, encoding); + applyAffinity(pIn3, affinity, encoding); + if( db->mallocFailed ) goto no_mem; } - break; - } - affinity = pOp->p5 & SQLITE_AFF_MASK; - if( affinity ){ - applyAffinity(pIn1, affinity, encoding); - applyAffinity(pIn3, affinity, encoding); - if( db->mallocFailed ) goto no_mem; + assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); + ExpandBlob(pIn1); + ExpandBlob(pIn3); + res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } - - assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); - ExpandBlob(pIn1); - ExpandBlob(pIn3); - res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); switch( pOp->opcode ){ case OP_Eq: res = res==0; break; case OP_Ne: res = res!=0; break; @@ -1807,9 +1828,18 @@ case OP_Compare: { assert( n>0 ); assert( pKeyInfo!=0 ); p1 = pOp->p1; - assert( p1>0 && p1+n<=p->nMem+1 ); p2 = pOp->p2; - assert( p2>0 && p2+n<=p->nMem+1 ); +#if SQLITE_DEBUG + if( aPermute ){ + int k, mx = 0; + for(k=0; kmx ) mx = aPermute[k]; + assert( p1>0 && p1+mx<=p->nMem+1 ); + assert( p2>0 && p2+mx<=p->nMem+1 ); + }else{ + assert( p1>0 && p1+n<=p->nMem+1 ); + assert( p2>0 && p2+n<=p->nMem+1 ); + } +#endif /* SQLITE_DEBUG */ for(i=0; iaMem[p1+idx]);