]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Generalize the IS and IS NOT operators so that their right-hand side can be
authordrh <drh@noemail.net>
Wed, 23 Sep 2009 02:29:36 +0000 (02:29 +0000)
committerdrh <drh@noemail.net>
Wed, 23 Sep 2009 02:29:36 +0000 (02:29 +0000)
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

addopcodes.awk
manifest
manifest.uuid
src/expr.c
src/parse.y
src/sqliteInt.h
src/vdbe.c

index 66d6e19baa3fc4c4d603012d2332fe222963d532..c90e1dd7f4c0f42740fb74adf78f5310b429572a 100644 (file)
@@ -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
index e7db2c5897302e349b45e7bba1dbeb832c302280..212cfdf42eeb6e5885c6cbb32f0be38af1238321 100644 (file)
--- 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-----
index d94e23f4ce270b15f027a02e0f7ff2785c5cc644..82c45ac0f3248633bfc0d54b6d68a000511ddc9a 100644 (file)
@@ -1 +1 @@
-3fc938c961fd7810594224b91a2d6e1ac9e48084
\ No newline at end of file
+98853f6104076c50ea92175e17a3254bfbbd4619
\ No newline at end of file
index 50b24de413423884968b6ea7b9273e7c317e9c5b..998bf41a8018f9e7a69d82ed37467f4a72aa68ff 100644 (file)
@@ -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, &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_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, &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_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, &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_NOTNULL: {
       testcase( op==TK_ISNULL );
index f6682285961b06111b15da0244ef32de096b0b6c..8fe4ef02778b7c661e70f54ea1794d101350c3fe 100644 (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) 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
index 438ed583f61ec6cb818a8af1edbe7dbdec3a392a..17aed293b270ae72070028af838a495c9c2f5bd6 100644 (file)
@@ -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
index 71487d1a90794c91f3b3a390f6536f3dc0a72cae..b18ddc5b548cb843aac70c34e8993ab2ee2f3011 100644 (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
 ** 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; 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 );
+  }
+#endif /* SQLITE_DEBUG */
   for(i=0; i<n; i++){
     idx = aPermute ? aPermute[i] : i;
     REGISTER_TRACE(p1+idx, &p->aMem[p1+idx]);