1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-06 15:49:35 +03:00

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
This commit is contained in:
drh
2009-09-23 02:29:36 +00:00
parent 7ba5bc5bf2
commit 6a2fe09387
7 changed files with 134 additions and 47 deletions

View File

@@ -19,6 +19,7 @@ END {
printf "#define TK_%-29s %4d\n", "TO_NUMERIC", ++max 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_INT", ++max
printf "#define TK_%-29s %4d\n", "TO_REAL", ++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", "END_OF_FILE", ++max
printf "#define TK_%-29s %4d\n", "ILLEGAL", ++max printf "#define TK_%-29s %4d\n", "ILLEGAL", ++max
printf "#define TK_%-29s %4d\n", "SPACE", ++max printf "#define TK_%-29s %4d\n", "SPACE", ++max

View File

@@ -1,8 +1,8 @@
-----BEGIN PGP SIGNED MESSAGE----- -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1 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. 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-22T20:08:35 D 2009-09-23T02:29:37
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -10,7 +10,7 @@ F Makefile.vxworks 10010ddbf52e2503c7c49c7c0b7c7a096f8638a6
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION 7260e7baf934051dee42458206e915b75570f41d F VERSION 7260e7baf934051dee42458206e915b75570f41d
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 08eb3bdfef10a131530e3ad7fa1a6902a52dad15 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248 F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
F art/SQLite.eps 9b43cc99cfd2be687d386faea6862ea68d6a72b2 F art/SQLite.eps 9b43cc99cfd2be687d386faea6862ea68d6a72b2
F art/SQLite.gif 1bbb94484963f1382e27e1c5e86dd0c1061eba2b F art/SQLite.gif 1bbb94484963f1382e27e1c5e86dd0c1061eba2b
@@ -117,7 +117,7 @@ F src/callback.c 10d237171472865f58fb07d515737238c9e06688
F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
F src/date.c 657ff12ca0f1195b531561afacbb38b772d16638 F src/date.c 657ff12ca0f1195b531561afacbb38b772d16638
F src/delete.c 15499f5d10047d38e68ce991b3f88cbddb6e0931 F src/delete.c 15499f5d10047d38e68ce991b3f88cbddb6e0931
F src/expr.c 638b599adad562d41c3bf90f542f9419664aa7b8 F src/expr.c 8a663240f374a5326ee157df3d27751f58b7676a
F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
F src/fkey.c fa1ad144926a8536e23cbbf64c0e51b8f2fdd4bf F src/fkey.c fa1ad144926a8536e23cbbf64c0e51b8f2fdd4bf
F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606 F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606
@@ -153,7 +153,7 @@ F src/os_unix.c 5369272992c14dd198c02ddfc2fd7a1516906c40
F src/os_win.c 49a360be4f42d5a63d00be9aa44449ed4d6717e0 F src/os_win.c 49a360be4f42d5a63d00be9aa44449ed4d6717e0
F src/pager.c ebd0a8f2421e8f0ad5b78201440004bf3e1c96d8 F src/pager.c ebd0a8f2421e8f0ad5b78201440004bf3e1c96d8
F src/pager.h 11852d044c86cf5a9d6e34171fb0c4fcf1f6265f F src/pager.h 11852d044c86cf5a9d6e34171fb0c4fcf1f6265f
F src/parse.y 749f39fa218418a19c46970336c2343214e2fe05 F src/parse.y 563ecc9e633bbb465383667380aff1646ecd96f7
F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa
F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36 F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36
F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c
@@ -167,7 +167,7 @@ F src/select.c 1d0a13137532321b4364f964e46f057d271691e3
F src/shell.c d0171721c7402b368e257ddfc09ed54d0c74070c F src/shell.c d0171721c7402b368e257ddfc09ed54d0c74070c
F src/sqlite.h.in 5af8181f815831a8672c3834c60e6b4418448bcc F src/sqlite.h.in 5af8181f815831a8672c3834c60e6b4418448bcc
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
F src/sqliteInt.h 6a949ccf79ae441ac37f0d3033204895aa2a9e6a F src/sqliteInt.h 35f0d4203924272b5ae849c88b7b857eda3bb272
F src/sqliteLimit.h 504a3161886d2938cbd163054ad620b8356df758 F src/sqliteLimit.h 504a3161886d2938cbd163054ad620b8356df758
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
@@ -209,7 +209,7 @@ F src/update.c 4fac66ecaea13c9c13e7d3de8da66aae3cce90e2
F src/utf.c 99cf927eabb104621ba889ac0dd075fc1657ad30 F src/utf.c 99cf927eabb104621ba889ac0dd075fc1657ad30
F src/util.c 59d4e9456bf1fe581f415a783fa0cee6115c8f35 F src/util.c 59d4e9456bf1fe581f415a783fa0cee6115c8f35
F src/vacuum.c 869d08eaab64e2a4eaf4ef9ea34b851892b65a75 F src/vacuum.c 869d08eaab64e2a4eaf4ef9ea34b851892b65a75
F src/vdbe.c 03857c07b8d159eb86ff559138d2eb5317cbfe32 F src/vdbe.c 4946e2ac1124b0cb0ee882ed7628d2d8b339b8ef
F src/vdbe.h 7d5075e3fa4e5587a9be8d5e503857c825490cef F src/vdbe.h 7d5075e3fa4e5587a9be8d5e503857c825490cef
F src/vdbeInt.h 546ed25cad488c053819e19d09751d71d3ce3601 F src/vdbeInt.h 546ed25cad488c053819e19d09751d71d3ce3601
F src/vdbeapi.c 524d79eb17bbcbe31c37c908b8e01edc5c684a90 F src/vdbeapi.c 524d79eb17bbcbe31c37c908b8e01edc5c684a90
@@ -756,14 +756,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 55b263fa2b2fed8c721154e3c48f4226be95065c P 3fc938c961fd7810594224b91a2d6e1ac9e48084
R eef40f23e9c60a6f8dbfd20ac239494a R d99ab25244fa05bd56d10d71bb3bee44
U drh U drh
Z a26ff37b38b185f4ba103e5a00635dcb Z 4608482892179163853473e1fbf42a31
-----BEGIN PGP SIGNATURE----- -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux) Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFKuS7GoxKgR168RlERArmIAJ9Zh9EPcETkJ903DGxflMeqs+sSrQCfRsQh iD8DBQFKuYgUoxKgR168RlERApeHAJ48L4oeCXw8ZV6ov3G5+bjNMfQIewCfX/Rc
N09u5KCVOpvUNYJvUIHJJug= qXj8iamFDJ6JXwF0VP/iAOI=
=Nr38 =lSDQ
-----END PGP SIGNATURE----- -----END PGP SIGNATURE-----

View File

@@ -1 +1 @@
3fc938c961fd7810594224b91a2d6e1ac9e48084 98853f6104076c50ea92175e17a3254bfbbd4619

View File

@@ -152,7 +152,7 @@ static char comparisonAffinity(Expr *pExpr){
char aff; char aff;
assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT || 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_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 ); assert( pExpr->pLeft );
aff = sqlite3ExprAffinity(pExpr->pLeft); aff = sqlite3ExprAffinity(pExpr->pLeft);
if( pExpr->pRight ){ if( pExpr->pRight ){
@@ -2244,6 +2244,19 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
testcase( regFree2==0 ); testcase( regFree2==0 );
break; break;
} }
case TK_IS:
case TK_ISNOT: {
testcase( op==TK_IS );
testcase( op==TK_ISNOT );
codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
pExpr->pRight, &r2, &regFree2);
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_AND:
case TK_OR: case TK_OR:
case TK_PLUS: case TK_PLUS:
@@ -3018,6 +3031,19 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
testcase( regFree2==0 ); testcase( regFree2==0 );
break; break;
} }
case TK_IS:
case TK_ISNOT: {
testcase( op==TK_IS );
testcase( op==TK_ISNOT );
codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
pExpr->pRight, &r2, &regFree2);
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_ISNULL:
case TK_NOTNULL: { case TK_NOTNULL: {
assert( TK_ISNULL==OP_IsNull ); assert( TK_ISNULL==OP_IsNull );
@@ -3167,6 +3193,19 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
testcase( regFree2==0 ); testcase( regFree2==0 );
break; break;
} }
case TK_IS:
case TK_ISNOT: {
testcase( op==TK_IS );
testcase( op==TK_ISNOT );
codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
pExpr->pRight, &r2, &regFree2);
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_ISNULL:
case TK_NOTNULL: { case TK_NOTNULL: {
testcase( op==TK_ISNULL ); testcase( op==TK_ISNULL );

View File

@@ -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) 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) 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 { %include {
/* Construct an expression node for a unary prefix operator /* Construct an expression node for a unary prefix operator

View File

@@ -1126,6 +1126,7 @@ struct CollSeq {
*/ */
#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ #define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */
#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ #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 ** An object of this type is created for each virtual table present in

View File

@@ -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 ** 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 ** the operands in registers P1 and P3 are not equal. See the Lt opcode for
** additional information. ** 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 /* Opcode: Eq P1 P2 P3 P4 P5
** **
** This works just like the Lt opcode except that the jump is taken if ** This works just like the Lt opcode except that the jump is taken if
** the operands in registers P1 and P3 are equal. ** the operands in registers P1 and P3 are equal.
** See the Lt opcode for additional information. ** 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 /* Opcode: Le P1 P2 P3 P4 P5
** **
@@ -1711,14 +1723,21 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */
case OP_Le: /* same as TK_LE, 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_Gt: /* same as TK_GT, jump, in1, in3 */
case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
int flags; int res; /* Result of the comparison of pIn1 against pIn3 */
int res; char affinity; /* Affinity to use for comparison */
char affinity;
flags = pIn1->flags|pIn3->flags; if( (pIn1->flags | pIn3->flags)&MEM_Null ){
/* One or both operands are NULL */
if( flags&MEM_Null ){ if( pOp->p5 & SQLITE_NULLEQ ){
/* If either operand is NULL then the result is always NULL. /* 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. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set.
*/ */
if( pOp->p5 & SQLITE_STOREP2 ){ if( pOp->p5 & SQLITE_STOREP2 ){
@@ -1730,7 +1749,8 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
} }
break; break;
} }
}else{
/* Neither operand is NULL. Do a comparison. */
affinity = pOp->p5 & SQLITE_AFF_MASK; affinity = pOp->p5 & SQLITE_AFF_MASK;
if( affinity ){ if( affinity ){
applyAffinity(pIn1, affinity, encoding); applyAffinity(pIn1, affinity, encoding);
@@ -1742,6 +1762,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
ExpandBlob(pIn1); ExpandBlob(pIn1);
ExpandBlob(pIn3); ExpandBlob(pIn3);
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
switch( pOp->opcode ){ switch( pOp->opcode ){
case OP_Eq: res = res==0; break; case OP_Eq: res = res==0; break;
case OP_Ne: res = res!=0; break; case OP_Ne: res = res!=0; break;
@@ -1807,9 +1828,18 @@ case OP_Compare: {
assert( n>0 ); assert( n>0 );
assert( pKeyInfo!=0 ); assert( pKeyInfo!=0 );
p1 = pOp->p1; p1 = pOp->p1;
assert( p1>0 && p1+n<=p->nMem+1 );
p2 = pOp->p2; p2 = pOp->p2;
#if SQLITE_DEBUG
if( aPermute ){
int k, mx = 0;
for(k=0; k<n; k++) if( aPermute[k]>mx ) 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 ); assert( p2>0 && p2+n<=p->nMem+1 );
}
#endif /* SQLITE_DEBUG */
for(i=0; i<n; i++){ for(i=0; i<n; i++){
idx = aPermute ? aPermute[i] : i; idx = aPermute ? aPermute[i] : i;
REGISTER_TRACE(p1+idx, &p->aMem[p1+idx]); REGISTER_TRACE(p1+idx, &p->aMem[p1+idx]);