]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
NULL values are distinct. A comparison involving a NULL is always false.
authordrh <drh@noemail.net>
Sun, 26 May 2002 20:54:33 +0000 (20:54 +0000)
committerdrh <drh@noemail.net>
Sun, 26 May 2002 20:54:33 +0000 (20:54 +0000)
Operations on a NULL value yield a NULL result.  This change makes SQLite
operate more like the SQL spec, but it may break existing applications that
assumed the old behavior.  All the old tests pass but we still need to add
new tests to better verify the new behavior.  Fix for ticket #44. (CVS 589)

FossilOrigin-Name: 9051173742f1b0e15a809d12a0c9c98fd2c4614d

15 files changed:
manifest
manifest.uuid
src/expr.c
src/insert.c
src/select.c
src/sqliteInt.h
src/trigger.c
src/vdbe.c
src/vdbe.h
src/where.c
test/expr.test
test/select4.test
test/subselect.test
test/trigger2.test
test/unique.test

index 3a7b09999ac299f6fb4c70d5a94e880fd2148787..c8b0abd05b362d1f722cc468db90483fa98cb3db 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Additional\stesting\sof\sLEFT\sOUTER\sJOIN.\s(CVS\s588)
-D 2002-05-25T00:18:21
+C NULL\svalues\sare\sdistinct.\s\sA\scomparison\sinvolving\sa\sNULL\sis\salways\sfalse.\nOperations\son\sa\sNULL\svalue\syield\sa\sNULL\sresult.\s\sThis\schange\smakes\sSQLite\noperate\smore\slike\sthe\sSQL\sspec,\sbut\sit\smay\sbreak\sexisting\sapplications\sthat\nassumed\sthe\sold\sbehavior.\s\sAll\sthe\sold\stests\spass\sbut\swe\sstill\sneed\sto\sadd\nnew\stests\sto\sbetter\sverify\sthe\snew\sbehavior.\s\sFix\sfor\sticket\s#44.\s(CVS\s589)
+D 2002-05-26T20:54:33
 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
 F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -23,11 +23,11 @@ F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
 F src/build.c 36e42718a7a94f554ea39508993378482f5335c7
 F src/delete.c a2b098cbbf518e6b641847e26de85827793bc523
 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
-F src/expr.c 818a702ba93e444813b8935a7ab509f6e3352b49
+F src/expr.c 1a7a2f5f2b7dd37659783cdb6efaac74a092ba71
 F src/func.c a31dcba85bc2ecb9b752980289cf7e6cd0cafbce
 F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
-F src/insert.c bbbd803da8b125ec5a5f881f4d559887eb922c57
+F src/insert.c 24b4e146319bada6f82a1d5eae6b38b3065d132f
 F src/main.c 6e53c49a390fabd5fecce9e3b128c61c85208000
 F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
 F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc
@@ -37,11 +37,11 @@ F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
 F src/parse.y c681da701bf142967325b8791f22418e2d81552d
 F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c bbf00ee2b4412c7249baf0ba737ba6a93fe82e78
+F src/select.c 9b404b6eeb3428896fdbddc6e1ace68ee931ec17
 F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
-F src/sqliteInt.h 9d565908e2ca53c54d1a0ae445cba039ee018aba
+F src/sqliteInt.h 9de24fb527b3e472be19600e7c22534b981c8e61
 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
 F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
 F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
@@ -49,12 +49,12 @@ F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730
 F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
 F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
 F src/tokenize.c facec7dc0b4a13e17ad67702f548dac2f7c6a732
-F src/trigger.c 75dd64808c56ff1b20ee6c6620f5d61487712d74
+F src/trigger.c f9adb404ea355a8be2c9cd9740794a898cf1096c
 F src/update.c f68375173bf5338cae3e97012708e10f206aedd9
 F src/util.c 707c30f8c13cddace7c08556ac450c0b786660b3
-F src/vdbe.c bde1dad84ea4b0de4ac590d0d29522e45bfd1470
-F src/vdbe.h def669b9f2728589aabcb5db756429db02465c9a
-F src/where.c 9030d188139f4de73c4b238706afeae8bc4e2f26
+F src/vdbe.c caa269517b2392986c8f55401f272e76b9de82d9
+F src/vdbe.h b8706429131c14b307a07aab7e47f95a9da53610
+F src/where.c b054f2f23127bd57eb5f973bcd38764b875d73fe
 F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
 F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
@@ -63,7 +63,7 @@ F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895
 F test/conflict.test 5149646703d3930c9111068b5cda7e2e938476e3
 F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a
 F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
-F test/expr.test 846795016b5993a7411f772eebe82ab67bd7230a
+F test/expr.test bfb773721e69566d2a7565c7f03b466e1f33a53f
 F test/func.test d34e461f0acb0cf2978a4b3a3e098460f2ea8fbc
 F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
 F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
@@ -89,11 +89,11 @@ F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
 F test/select1.test 6ba20b52d563b7fb917d8a61a7560d02f90a1a52
 F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4
 F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
-F test/select4.test c2313f8c16ca298b0b1ce9cc3c0cfed0939ffea9
+F test/select4.test 0bc0065102573c6b71003292fd4a5def63923c61
 F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac
 F test/select6.test efb8d0c07a440441db87db2c4ade6904e1407e85
 F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b
-F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78
+F test/subselect.test f3bc1dcbddddcea08d818fcff75228ad3464fc83
 F test/table.test d9fd161dc9a2dbe0795d836336019ea6d0952ef8
 F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
 F test/tclsqlite.test 79deeffd7cd637ca0f06c5dbbf2f44d272079533
@@ -101,8 +101,8 @@ F test/temptable.test daa83489eea2e9aaeeece09675c28be84c72cb67
 F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6
 F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee
 F test/trigger1.test a0550c5cce97170dbebb88eee09506d9ad1174e9
-F test/trigger2.test 2d23ad8a7e74c64018b6c7dff79bbf2574ffd776
-F test/unique.test 07776624b82221a80c8b4138ce0dd8b0853bb3ea
+F test/trigger2.test 7f2b0a9b20004449c78b834c2f22494db3b2e63a
+F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44
 F test/update.test a0aa0bf83e6fad8407d0e4ad25ebb09b513f5bf4
 F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
 F test/view.test b9851e9142de5e5831fdf18f125cbe1256cb550a
@@ -135,7 +135,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 99bd1f5b9a1a20bfeefe15c00d96a34a5f40923e
-R 289d0473700b92bde61e98c6617d8be0
+P d8d04c14f18d1feba89ccea0be70530a18248c51
+R 2ee233b2a9ebb3e4a8fe3e93b3747a0c
 U drh
-Z f910247eaeb3d662acb5a7b6aeac9b33
+Z f507bf5d6b83b961339e312ca54783e8
index 65d176ce964aa984dbbe9be63221bbb705dfe070..f0e8ac5b721a4d7d6e16313b292985037041a089 100644 (file)
@@ -1 +1 @@
-d8d04c14f18d1feba89ccea0be70530a18248c51
\ No newline at end of file
+9051173742f1b0e15a809d12a0c9c98fd2c4614d
\ No newline at end of file
index 94aa06df50c96b73e69331a93efa68a696a88d82..89508d26c5ab69d4bebfaad14a2621d3ab5e1f21 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.63 2002/05/24 02:04:33 drh Exp $
+** $Id: expr.c,v 1.64 2002/05/26 20:54:33 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -818,7 +818,13 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
     case TK_REM:
     case TK_BITAND:
     case TK_BITOR:
-    case TK_SLASH: {
+    case TK_SLASH:
+    case TK_LT:
+    case TK_LE:
+    case TK_GT:
+    case TK_GE:
+    case TK_NE:
+    case TK_EQ: {
       sqliteExprCode(pParse, pExpr->pLeft);
       sqliteExprCode(pParse, pExpr->pRight);
       sqliteVdbeAddOp(v, op, 0, 0);
@@ -837,21 +843,6 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
       sqliteVdbeAddOp(v, OP_Concat, 2, 0);
       break;
     }
-    case TK_LT:
-    case TK_LE:
-    case TK_GT:
-    case TK_GE:
-    case TK_NE:
-    case TK_EQ: {
-      int dest;
-      sqliteVdbeAddOp(v, OP_Integer, 1, 0);
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteExprCode(pParse, pExpr->pRight);
-      dest = sqliteVdbeCurrentAddr(v) + 2;
-      sqliteVdbeAddOp(v, op, 0, dest);
-      sqliteVdbeAddOp(v, OP_AddImm, -1, 0);
-      break;
-    }
     case TK_UMINUS: {
       assert( pExpr->pLeft );
       if( pExpr->pLeft->op==TK_FLOAT || pExpr->pLeft->op==TK_INTEGER ){
@@ -881,7 +872,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
       sqliteVdbeAddOp(v, OP_Integer, 1, 0);
       sqliteExprCode(pParse, pExpr->pLeft);
       dest = sqliteVdbeCurrentAddr(v) + 2;
-      sqliteVdbeAddOp(v, op, 0, dest);
+      sqliteVdbeAddOp(v, op, 1, dest);
       sqliteVdbeAddOp(v, OP_AddImm, -1, 0);
       break;
     }
@@ -913,20 +904,27 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
       sqliteVdbeAddOp(v, OP_Integer, 1, 0);
       sqliteExprCode(pParse, pExpr->pLeft);
       addr = sqliteVdbeCurrentAddr(v);
+      sqliteVdbeAddOp(v, OP_NotNull, -1, addr+4);
+      sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+      sqliteVdbeAddOp(v, OP_String, 0, 0);
+      sqliteVdbeAddOp(v, OP_Goto, 0, addr+6);
       if( pExpr->pSelect ){
-        sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+2);
+        sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+6);
       }else{
-        sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+2);
+        sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+6);
       }
       sqliteVdbeAddOp(v, OP_AddImm, -1, 0);
       break;
     }
     case TK_BETWEEN: {
-      int lbl = sqliteVdbeMakeLabel(v);
-      sqliteVdbeAddOp(v, OP_Integer, 0, 0);
-      sqliteExprIfFalse(pParse, pExpr, lbl);
-      sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
-      sqliteVdbeResolveLabel(v, lbl);
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteVdbeAddOp(v, OP_Dup, 0, 0);
+      sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
+      sqliteVdbeAddOp(v, OP_Ge, 0, 0);
+      sqliteVdbeAddOp(v, OP_Pull, 1, 0);
+      sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
+      sqliteVdbeAddOp(v, OP_Le, 0, 0);
+      sqliteVdbeAddOp(v, OP_And, 0, 0);
       break;
     }
     case TK_AS: {
@@ -935,44 +933,54 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
     }
     case TK_CASE: {
       int expr_end_label;
-      int next_when_label;
+      int null_result_label;
+      int jumpInst;
+      int addr;
+      int nExpr;
       int i;
 
       assert(pExpr->pList);
       assert((pExpr->pList->nExpr % 2) == 0);
       assert(pExpr->pList->nExpr > 0);
-      expr_end_label = sqliteVdbeMakeLabel(pParse->pVdbe);
+      nExpr = pExpr->pList->nExpr;
+      expr_end_label = sqliteVdbeMakeLabel(v);
+      null_result_label = sqliteVdbeMakeLabel(v);
       if( pExpr->pLeft ){
         sqliteExprCode(pParse, pExpr->pLeft);
+        sqliteVdbeAddOp(v, OP_IsNull, -1, expr_end_label);
       }
-      for(i=0; i<pExpr->pList->nExpr; i=i+2){
-        if( i!=0 ){
-          sqliteVdbeResolveLabel(pParse->pVdbe, next_when_label);
-        }
-        next_when_label = sqliteVdbeMakeLabel(pParse->pVdbe);
+      for(i=0; i<nExpr; i=i+2){
+        sqliteExprCode(pParse, pExpr->pList->a[i].pExpr);
+        sqliteVdbeAddOp(v, OP_IsNull, -1, null_result_label);
         if( pExpr->pLeft ){
-          sqliteVdbeAddOp(pParse->pVdbe, OP_Dup, 0, 1);
-          sqliteExprCode(pParse, pExpr->pList->a[i].pExpr);
-          sqliteVdbeAddOp(pParse->pVdbe, OP_Ne, 0, next_when_label);
+          sqliteVdbeAddOp(v, OP_Dup, 1, 1);
+          jumpInst = sqliteVdbeAddOp(v, OP_Ne, 0, 0);
         }else{
-          sqliteExprIfFalse(pParse, pExpr->pList->a[i].pExpr, next_when_label);
-        }
-        if( pExpr->pLeft ){
-          sqliteVdbeAddOp(pParse->pVdbe, OP_Pop, 1, 0);
+          jumpInst = sqliteVdbeAddOp(v, OP_IfNot, 0, 0);
         }
         sqliteExprCode(pParse, pExpr->pList->a[i+1].pExpr);
-        sqliteVdbeAddOp(pParse->pVdbe, OP_Goto, 0, expr_end_label);
-      }
-      sqliteVdbeResolveLabel(pParse->pVdbe, next_when_label);
-      if( pExpr->pLeft ){
-        sqliteVdbeAddOp(pParse->pVdbe, OP_Pop, 1, 0);
+        sqliteVdbeAddOp(v, OP_Goto, 0, expr_end_label);
+        if( i>=nExpr-2 ){
+          sqliteVdbeResolveLabel(v, null_result_label);
+          sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+          if( pExpr->pRight!=0 ){
+            sqliteVdbeAddOp(v, OP_String, 0, 0);
+            sqliteVdbeAddOp(v, OP_Goto, 0, expr_end_label);
+          }
+        }
+        addr = sqliteVdbeCurrentAddr(v);
+        sqliteVdbeChangeP2(v, jumpInst, addr);
       }
       if( pExpr->pRight ){
         sqliteExprCode(pParse, pExpr->pRight);
       }else{
-        sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0);
+        sqliteVdbeAddOp(v, OP_String, 0, 0);
+      }
+      sqliteVdbeResolveLabel(v, expr_end_label);
+      if( pExpr->pLeft ){
+        sqliteVdbeAddOp(v, OP_Pull, 1, 0);
+        sqliteVdbeAddOp(v, OP_Pop, 1, 0);
       }
-      sqliteVdbeResolveLabel(pParse->pVdbe, expr_end_label);
     }
     break;
   }
@@ -982,8 +990,11 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
 ** Generate code for a boolean expression such that a jump is made
 ** to the label "dest" if the expression is true but execution
 ** continues straight thru if the expression is false.
+**
+** If the expression evaluates to NULL (neither true nor false), then
+** take the jump if the jumpIfNull flag is true.
 */
-void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
+void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
   Vdbe *v = pParse->pVdbe;
   int op = 0;
   if( v==0 || pExpr==0 ) return;
@@ -1001,18 +1012,18 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
   switch( pExpr->op ){
     case TK_AND: {
       int d2 = sqliteVdbeMakeLabel(v);
-      sqliteExprIfFalse(pParse, pExpr->pLeft, d2);
-      sqliteExprIfTrue(pParse, pExpr->pRight, dest);
+      sqliteExprIfFalse(pParse, pExpr->pLeft, d2, !jumpIfNull);
+      sqliteExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
       sqliteVdbeResolveLabel(v, d2);
       break;
     }
     case TK_OR: {
-      sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
-      sqliteExprIfTrue(pParse, pExpr->pRight, dest);
+      sqliteExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
+      sqliteExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
       break;
     }
     case TK_NOT: {
-      sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
+      sqliteExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
       break;
     }
     case TK_LT:
@@ -1023,17 +1034,22 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
     case TK_EQ: {
       sqliteExprCode(pParse, pExpr->pLeft);
       sqliteExprCode(pParse, pExpr->pRight);
-      sqliteVdbeAddOp(v, op, 0, dest);
+      sqliteVdbeAddOp(v, op, jumpIfNull, dest);
       break;
     }
     case TK_ISNULL:
     case TK_NOTNULL: {
       sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteVdbeAddOp(v, op, 0, dest);
+      sqliteVdbeAddOp(v, op, 1, dest);
       break;
     }
     case TK_IN: {
+      int addr;
       sqliteExprCode(pParse, pExpr->pLeft);
+      addr = sqliteVdbeCurrentAddr(v);
+      sqliteVdbeAddOp(v, OP_NotNull, -1, addr+3);
+      sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+      sqliteVdbeAddOp(v, OP_Goto, 0, jumpIfNull ? dest : addr+4);
       if( pExpr->pSelect ){
         sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, dest);
       }else{
@@ -1042,21 +1058,21 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
       break;
     }
     case TK_BETWEEN: {
-      int lbl = sqliteVdbeMakeLabel(v);
+      int addr;
       sqliteExprCode(pParse, pExpr->pLeft);
       sqliteVdbeAddOp(v, OP_Dup, 0, 0);
       sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
-      sqliteVdbeAddOp(v, OP_Lt, 0, lbl);
+      addr = sqliteVdbeAddOp(v, OP_Lt, !jumpIfNull, 0);
       sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
-      sqliteVdbeAddOp(v, OP_Le, 0, dest);
+      sqliteVdbeAddOp(v, OP_Le, jumpIfNull, dest);
       sqliteVdbeAddOp(v, OP_Integer, 0, 0);
-      sqliteVdbeResolveLabel(v, lbl);
+      sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
       sqliteVdbeAddOp(v, OP_Pop, 1, 0);
       break;
     }
     default: {
       sqliteExprCode(pParse, pExpr);
-      sqliteVdbeAddOp(v, OP_If, 0, dest);
+      sqliteVdbeAddOp(v, OP_If, jumpIfNull, dest);
       break;
     }
   }
@@ -1066,8 +1082,11 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
 ** Generate code for a boolean expression such that a jump is made
 ** to the label "dest" if the expression is false but execution
 ** continues straight thru if the expression is true.
+**
+** If the expression evaluates to NULL (neither true nor false) then
+** jump if jumpIfNull is true or fall through if jumpIfNull is false.
 */
-void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
+void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
   Vdbe *v = pParse->pVdbe;
   int op = 0;
   if( v==0 || pExpr==0 ) return;
@@ -1084,19 +1103,19 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
   }
   switch( pExpr->op ){
     case TK_AND: {
-      sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
-      sqliteExprIfFalse(pParse, pExpr->pRight, dest);
+      sqliteExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
+      sqliteExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
       break;
     }
     case TK_OR: {
       int d2 = sqliteVdbeMakeLabel(v);
-      sqliteExprIfTrue(pParse, pExpr->pLeft, d2);
-      sqliteExprIfFalse(pParse, pExpr->pRight, dest);
+      sqliteExprIfTrue(pParse, pExpr->pLeft, d2, !jumpIfNull);
+      sqliteExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
       sqliteVdbeResolveLabel(v, d2);
       break;
     }
     case TK_NOT: {
-      sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
+      sqliteExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
       break;
     }
     case TK_LT:
@@ -1107,17 +1126,22 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
     case TK_EQ: {
       sqliteExprCode(pParse, pExpr->pLeft);
       sqliteExprCode(pParse, pExpr->pRight);
-      sqliteVdbeAddOp(v, op, 0, dest);
+      sqliteVdbeAddOp(v, op, jumpIfNull, dest);
       break;
     }
     case TK_ISNULL:
     case TK_NOTNULL: {
       sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteVdbeAddOp(v, op, 0, dest);
+      sqliteVdbeAddOp(v, op, 1, dest);
       break;
     }
     case TK_IN: {
+      int addr;
       sqliteExprCode(pParse, pExpr->pLeft);
+      addr = sqliteVdbeCurrentAddr(v);
+      sqliteVdbeAddOp(v, OP_NotNull, -1, addr+3);
+      sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+      sqliteVdbeAddOp(v, OP_Goto, 0, jumpIfNull ? dest : addr+4);
       if( pExpr->pSelect ){
         sqliteVdbeAddOp(v, OP_NotFound, pExpr->iTable, dest);
       }else{
@@ -1131,17 +1155,17 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
       sqliteVdbeAddOp(v, OP_Dup, 0, 0);
       sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
       addr = sqliteVdbeCurrentAddr(v);
-      sqliteVdbeAddOp(v, OP_Ge, 0, addr+3);
+      sqliteVdbeAddOp(v, OP_Ge, !jumpIfNull, addr+3);
       sqliteVdbeAddOp(v, OP_Pop, 1, 0);
       sqliteVdbeAddOp(v, OP_Goto, 0, dest);
       sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
-      sqliteVdbeAddOp(v, OP_Gt, 0, dest);
+      sqliteVdbeAddOp(v, OP_Gt, jumpIfNull, dest);
       break;
     }
     default: {
       sqliteExprCode(pParse, pExpr);
       sqliteVdbeAddOp(v, OP_Not, 0, 0);
-      sqliteVdbeAddOp(v, OP_If, 0, dest);
+      sqliteVdbeAddOp(v, OP_If, jumpIfNull, dest);
       break;
     }
   }
index 53c3e7696006a557c632a563b5d93f1f5613d5d3..1c9b9b4cbb466a4ef872d3221fec85e40d06f960 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.58 2002/05/24 02:04:33 drh Exp $
+** $Id: insert.c,v 1.59 2002/05/26 20:54:33 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -290,14 +290,12 @@ void sqliteInsert(
       if( srcTab>=0 ){
         sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
       }else{
-        int addr;
         sqliteExprCode(pParse, pList->a[keyColumn].pExpr);
 
         /* If the PRIMARY KEY expression is NULL, then use OP_NewRecno
         ** to generate a unique primary key value.
         */
-        addr = sqliteVdbeAddOp(v, OP_Dup, 0, 1);
-        sqliteVdbeAddOp(v, OP_NotNull, 0, addr+4);
+        sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3);
         sqliteVdbeAddOp(v, OP_Pop, 1, 0);
         sqliteVdbeAddOp(v, OP_NewRecno, base, 0);
       }
@@ -499,7 +497,7 @@ void sqliteGenerateConstraintChecks(
   int iCur;
   Index *pIdx;
   int seenReplace = 0;
-  int jumpInst;
+  int jumpInst1, jumpInst2;
   int contAddr;
   int hasTwoRecnos = (isUpdate && recnoChng);
 
@@ -527,7 +525,7 @@ void sqliteGenerateConstraintChecks(
       onError = OE_Abort;
     }
     sqliteVdbeAddOp(v, OP_Dup, nCol-1-i, 1);
-    addr = sqliteVdbeAddOp(v, OP_NotNull, 0, 0);
+    addr = sqliteVdbeAddOp(v, OP_NotNull, 1, 0);
     switch( onError ){
       case OE_Rollback:
       case OE_Abort:
@@ -565,14 +563,13 @@ void sqliteGenerateConstraintChecks(
       if( onError==OE_Default ) onError = OE_Abort;
     }
     if( onError!=OE_Replace ){
-      int jumpInst2;
       if( isUpdate ){
         sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
         sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
-        jumpInst2 = sqliteVdbeAddOp(v, OP_Eq, 0, 0);
+        jumpInst1 = sqliteVdbeAddOp(v, OP_Eq, 0, 0);
       }
       sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
-      jumpInst = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
+      jumpInst2 = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
       switch( onError ){
         case OE_Rollback:
         case OE_Abort:
@@ -588,9 +585,9 @@ void sqliteGenerateConstraintChecks(
         default: assert(0);
       }
       contAddr = sqliteVdbeCurrentAddr(v);
-      sqliteVdbeChangeP2(v, jumpInst, contAddr);
+      sqliteVdbeChangeP2(v, jumpInst2, contAddr);
       if( isUpdate ){
-        sqliteVdbeChangeP2(v, jumpInst2, contAddr);
+        sqliteVdbeChangeP2(v, jumpInst1, contAddr);
         sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
         sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
       }
@@ -609,7 +606,7 @@ void sqliteGenerateConstraintChecks(
         sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1);
       }
     }
-    sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
+    jumpInst1 = sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
     onError = pIdx->onError;
     if( onError==OE_None ) continue;
     if( overrideError!=OE_Default ){
@@ -619,7 +616,7 @@ void sqliteGenerateConstraintChecks(
       if( onError==OE_Default ) onError = OE_Abort;
     }
     sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1);
-    jumpInst = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
+    jumpInst2 = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
     switch( onError ){
       case OE_Rollback:
       case OE_Abort:
@@ -645,7 +642,8 @@ void sqliteGenerateConstraintChecks(
       default: assert(0);
     }
     contAddr = sqliteVdbeCurrentAddr(v);
-    sqliteVdbeChangeP2(v, jumpInst, contAddr);
+    sqliteVdbeChangeP2(v, jumpInst1, contAddr);
+    sqliteVdbeChangeP2(v, jumpInst2, contAddr);
   }
 }
 
index e4117f9d67954e2806d7ce31c1667fc270c069ee..3be8650cefad0026732337e2ed4a77be8f50be99 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.85 2002/05/25 00:18:21 drh Exp $
+** $Id: select.c,v 1.86 2002/05/26 20:54:34 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -325,13 +325,12 @@ static int selectInnerLoop(
   ** and this row has been seen before, then do not make this row
   ** part of the result.
   */
-  if( distinct>=0 ){
-    int lbl = sqliteVdbeMakeLabel(v);
+  if( distinct>=0 && pEList && pEList->nExpr>0 ){
+    sqliteVdbeAddOp(v, OP_IsNull, -pEList->nExpr, sqliteVdbeCurrentAddr(v)+7);
     sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1);
-    sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl);
+    sqliteVdbeAddOp(v, OP_Distinct, distinct, sqliteVdbeCurrentAddr(v)+3);
     sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0);
     sqliteVdbeAddOp(v, OP_Goto, 0, iContinue);
-    sqliteVdbeResolveLabel(v, lbl);
     sqliteVdbeAddOp(v, OP_String, 0, 0);
     sqliteVdbeAddOp(v, OP_PutStrKey, distinct, 0);
   }
@@ -359,8 +358,8 @@ static int selectInnerLoop(
   ** table iParm.
   */
   if( eDest==SRT_Union ){
-    sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0);
-    sqliteVdbeAddOp(v, OP_String, iParm, 0);
+    sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 1);
+    sqliteVdbeAddOp(v, OP_String, 0, 0);
     sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0);
   }else 
 
@@ -378,7 +377,7 @@ static int selectInnerLoop(
   ** the temporary table iParm.
   */
   if( eDest==SRT_Except ){
-    int addr = sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+    int addr = sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 1);
     sqliteVdbeAddOp(v, OP_NotFound, iParm, addr+3);
     sqliteVdbeAddOp(v, OP_Delete, iParm, 0);
   }else 
@@ -389,6 +388,7 @@ static int selectInnerLoop(
   */
   if( eDest==SRT_Set ){
     assert( nColumn==1 );
+    sqliteVdbeAddOp(v, OP_IsNull, -1, sqliteVdbeCurrentAddr(v)+3);
     sqliteVdbeAddOp(v, OP_String, 0, 0);
     sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0);
   }else 
@@ -1738,7 +1738,7 @@ int sqliteSelect(
     startagg = sqliteVdbeAddOp(v, OP_AggNext, 0, endagg);
     pParse->useAgg = 1;
     if( pHaving ){
-      sqliteExprIfFalse(pParse, pHaving, startagg);
+      sqliteExprIfFalse(pParse, pHaving, startagg, 1);
     }
     if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
                     startagg, endagg) ){
index 77cfe6282079d80577887e8e883d1f5fb6a58c80..4fec92231366cdc9b5ebf4ef374b84fd1f5684ea 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.116 2002/05/25 00:18:21 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.117 2002/05/26 20:54:34 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -817,8 +817,8 @@ void sqliteUpdate(Parse*, Token*, ExprList*, Expr*, int);
 WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int);
 void sqliteWhereEnd(WhereInfo*);
 void sqliteExprCode(Parse*, Expr*);
-void sqliteExprIfTrue(Parse*, Expr*, int);
-void sqliteExprIfFalse(Parse*, Expr*, int);
+void sqliteExprIfTrue(Parse*, Expr*, int, int);
+void sqliteExprIfFalse(Parse*, Expr*, int, int);
 Table *sqliteFindTable(sqlite*,const char*);
 Index *sqliteFindIndex(sqlite*,const char*);
 void sqliteUnlinkAndDeleteIndex(sqlite*,Index*);
index 48e7ecf792ee0b0004c47bbf0c6df5d11f685eb2..82ba258a01fc44eb5f69e7c8dcc64a5769b8e1d2 100644 (file)
@@ -587,7 +587,7 @@ int sqliteCodeRowTrigger(
         sqliteExprDelete(whenExpr);
         return 1;
       }
-      sqliteExprIfFalse(pParse, whenExpr, endTrigger);
+      sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
       sqliteExprDelete(whenExpr);
 
       codeTriggerProgram(pParse, pTrigger->step_list, orconf); 
index af42f9b52f3a41f99ff503b5093b053f320d8fe6..fa558eb7cc4764efff5551a2108a35e8fc73801f 100644 (file)
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.148 2002/05/24 20:31:37 drh Exp $
+** $Id: vdbe.c,v 1.149 2002/05/26 20:54:34 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1068,17 +1068,17 @@ static char *zOpName[] = { 0,
   "AggFunc",           "AggInit",           "AggPush",           "AggPop",
   "SetInsert",         "SetFound",          "SetNotFound",       "MakeRecord",
   "MakeKey",           "MakeIdxKey",        "IncrKey",           "Goto",
-  "If",                "Halt",              "ColumnCount",       "ColumnName",
-  "Callback",          "NullCallback",      "Integer",           "String",
-  "Pop",               "Dup",               "Pull",              "Push",
-  "MustBeInt",         "Add",               "AddImm",            "Subtract",
-  "Multiply",          "Divide",            "Remainder",         "BitAnd",
-  "BitOr",             "BitNot",            "ShiftLeft",         "ShiftRight",
-  "AbsValue",          "Eq",                "Ne",                "Lt",
-  "Le",                "Gt",                "Ge",                "IsNull",
-  "NotNull",           "Negative",          "And",               "Or",
-  "Not",               "Concat",            "Noop",              "Function",
-  "Limit",           
+  "If",                "IfNot",             "Halt",              "ColumnCount",
+  "ColumnName",        "Callback",          "NullCallback",      "Integer",
+  "String",            "Pop",               "Dup",               "Pull",
+  "Push",              "MustBeInt",         "Add",               "AddImm",
+  "Subtract",          "Multiply",          "Divide",            "Remainder",
+  "BitAnd",            "BitOr",             "BitNot",            "ShiftLeft",
+  "ShiftRight",        "AbsValue",          "Eq",                "Ne",
+  "Lt",                "Le",                "Gt",                "Ge",
+  "IsNull",            "NotNull",           "Negative",          "And",
+  "Or",                "Not",               "Concat",            "Noop",
+  "Function",          "Limit",           
 };
 
 /*
@@ -1280,6 +1280,7 @@ int sqliteVdbeExec(
   sqlite *db = p->db;        /* The database */
   char **zStack;             /* Text stack */
   Stack *aStack;             /* Additional stack information */
+  unsigned uniqueCnt = 0;     /* Used by OP_MakeRecord when P2!=0 */
   int errorAction = OE_Abort; /* Recovery action to do in case of an error */
   int undoTransOnError = 0;   /* If error, either ROLLBACK or COMMIT */
   char zBuf[100];             /* Space to sprintf() an integer */
@@ -1719,6 +1720,7 @@ case OP_Concat: {
 ** and push the result back onto the stack.  If either element
 ** is a string then it is converted to a double using the atof()
 ** function before the addition.
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: Multiply * * *
 **
@@ -1726,6 +1728,7 @@ case OP_Concat: {
 ** and push the result back onto the stack.  If either element
 ** is a string then it is converted to a double using the atof()
 ** function before the multiplication.
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: Subtract * * *
 **
@@ -1735,6 +1738,7 @@ case OP_Concat: {
 ** and push the result back onto the stack.  If either element
 ** is a string then it is converted to a double using the atof()
 ** function before the subtraction.
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: Divide * * *
 **
@@ -1744,6 +1748,7 @@ case OP_Concat: {
 ** and push the result back onto the stack.  If either element
 ** is a string then it is converted to a double using the atof()
 ** function before the division.  Division by zero returns NULL.
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: Remainder * * *
 **
@@ -1753,6 +1758,7 @@ case OP_Concat: {
 ** and push the remainder after division onto the stack.  If either element
 ** is a string then it is converted to a double using the atof()
 ** function before the division.  Division by zero returns NULL.
+** If either operand is NULL, the result is NULL.
 */
 case OP_Add:
 case OP_Subtract:
@@ -1762,7 +1768,11 @@ case OP_Remainder: {
   int tos = p->tos;
   int nos = tos - 1;
   VERIFY( if( nos<0 ) goto not_enough_stack; )
-  if( (aStack[tos].flags & aStack[nos].flags & STK_Int)==STK_Int ){
+  if( ((aStack[tos].flags | aStack[nos].flags) & STK_Null)!=0 ){
+    POPSTACK;
+    Release(p, nos);
+    aStack[nos].flags = STK_Null;
+  }else if( (aStack[tos].flags & aStack[nos].flags & STK_Int)==STK_Int ){
     int a, b;
     a = aStack[tos].i;
     b = aStack[nos].i;
@@ -1838,7 +1848,9 @@ case OP_Function: {
   VERIFY( if( n<0 ) goto bad_instruction; )
   VERIFY( if( p->tos+1<n ) goto not_enough_stack; )
   for(i=p->tos-n+1; i<=p->tos; i++){
-    if( (aStack[i].flags & STK_Null)==0 ){
+    if( aStack[i].flags & STK_Null ){
+      zStack[i] = 0;
+    }else{
       if( Stringify(p, i) ) goto no_mem;
     }
   }
@@ -1872,24 +1884,28 @@ case OP_Function: {
 ** Pop the top two elements from the stack.  Convert both elements
 ** to integers.  Push back onto the stack the bit-wise AND of the
 ** two elements.
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: BitOr * * *
 **
 ** Pop the top two elements from the stack.  Convert both elements
 ** to integers.  Push back onto the stack the bit-wise OR of the
 ** two elements.
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: ShiftLeft * * *
 **
 ** Pop the top two elements from the stack.  Convert both elements
 ** to integers.  Push back onto the stack the top element shifted
 ** left by N bits where N is the second element on the stack.
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: ShiftRight * * *
 **
 ** Pop the top two elements from the stack.  Convert both elements
 ** to integers.  Push back onto the stack the top element shifted
 ** right by N bits where N is the second element on the stack.
+** If either operand is NULL, the result is NULL.
 */
 case OP_BitAnd:
 case OP_BitOr:
@@ -1899,6 +1915,12 @@ case OP_ShiftRight: {
   int nos = tos - 1;
   int a, b;
   VERIFY( if( nos<0 ) goto not_enough_stack; )
+  if( (aStack[tos].flags | aStack[nos].flags) & STK_Null ){
+    POPSTACK;
+    Release(p,nos);
+    aStack[nos].flags = STK_Null;
+    break;
+  }
   Integerify(p, tos);
   Integerify(p, nos);
   a = aStack[tos].i;
@@ -1973,40 +1995,82 @@ mismatch:
   break;
 }
 
-/* Opcode: Eq * P2 *
+/* Opcode: Eq P1 P2 *
 **
 ** Pop the top two elements from the stack.  If they are equal, then
 ** jump to instruction P2.  Otherwise, continue to the next instruction.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** If P2 is zero, do not jump.  Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not.  Push a
+** NULL if either operand was NULL.
 */
-/* Opcode: Ne * P2 *
+/* Opcode: Ne P1 P2 *
 **
 ** Pop the top two elements from the stack.  If they are not equal, then
 ** jump to instruction P2.  Otherwise, continue to the next instruction.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** If P2 is zero, do not jump.  Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not.  Push a
+** NULL if either operand was NULL.
 */
-/* Opcode: Lt * P2 *
+/* Opcode: Lt P1 P2 *
 **
 ** Pop the top two elements from the stack.  If second element (the
 ** next on stack) is less than the first (the top of stack), then
 ** jump to instruction P2.  Otherwise, continue to the next instruction.
 ** In other words, jump if NOS<TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** If P2 is zero, do not jump.  Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not.  Push a
+** NULL if either operand was NULL.
 */
-/* Opcode: Le * P2 *
+/* Opcode: Le P1 P2 *
 **
 ** Pop the top two elements from the stack.  If second element (the
 ** next on stack) is less than or equal to the first (the top of stack),
 ** then jump to instruction P2. In other words, jump if NOS<=TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** If P2 is zero, do not jump.  Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not.  Push a
+** NULL if either operand was NULL.
 */
-/* Opcode: Gt * P2 *
+/* Opcode: Gt P1 P2 *
 **
 ** Pop the top two elements from the stack.  If second element (the
 ** next on stack) is greater than the first (the top of stack),
 ** then jump to instruction P2. In other words, jump if NOS>TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** If P2 is zero, do not jump.  Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not.  Push a
+** NULL if either operand was NULL.
 */
-/* Opcode: Ge * P2 *
+/* Opcode: Ge P1 P2 *
 **
 ** Pop the top two elements from the stack.  If second element (the next
 ** on stack) is greater than or equal to the first (the top of stack),
 ** then jump to instruction P2. In other words, jump if NOS>=TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** If P2 is zero, do not jump.  Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not.  Push a
+** NULL if either operand was NULL.
 */
 case OP_Eq:
 case OP_Ne:
@@ -2021,7 +2085,17 @@ case OP_Ge: {
   VERIFY( if( nos<0 ) goto not_enough_stack; )
   ft = aStack[tos].flags;
   fn = aStack[nos].flags;
-  if( (ft & fn & STK_Int)==STK_Int ){
+  if( (ft | fn) & STK_Null ){
+    POPSTACK;
+    POPSTACK;
+    if( pOp->p2 ){
+      if( pOp->p1 ) pc = pOp->p2-1;
+    }else{
+      p->tos++;
+      aStack[nos].flags = STK_Null;
+    }
+    break;
+  }else if( (ft & fn & STK_Int)==STK_Int ){
     c = aStack[nos].i - aStack[tos].i;
   }else if( (ft & STK_Int)!=0 && (fn & STK_Str)!=0 && isInteger(zStack[nos]) ){
     Integerify(p, nos);
@@ -2043,7 +2117,13 @@ case OP_Ge: {
   }
   POPSTACK;
   POPSTACK;
-  if( c ) pc = pOp->p2-1;
+  if( pOp->p2 ){
+    if( c ) pc = pOp->p2-1;
+  }else{
+    p->tos++;
+    aStack[nos].flags = STK_Int;
+    aStack[nos].i = c;
+  }
   break;
 }
 
@@ -2052,12 +2132,14 @@ case OP_Ge: {
 ** Pop two values off the stack.  Take the logical AND of the
 ** two values and push the resulting boolean value back onto the
 ** stack. 
+** If either operand is NULL, the result is NULL.
 */
 /* Opcode: Or * * *
 **
 ** Pop two values off the stack.  Take the logical OR of the
 ** two values and push the resulting boolean value back onto the
 ** stack. 
+** If either operand is NULL, the result is NULL.
 */
 case OP_And:
 case OP_Or: {
@@ -2065,6 +2147,12 @@ case OP_Or: {
   int nos = tos - 1;
   int c;
   VERIFY( if( nos<0 ) goto not_enough_stack; )
+  if( (aStack[tos].flags | aStack[nos].flags) & STK_Null ){
+    POPSTACK;
+    Release(p, nos);     
+    aStack[nos].flags = STK_Null;
+    break;
+  }
   Integerify(p, tos);
   Integerify(p, nos);
   if( pOp->opcode==OP_And ){
@@ -2082,12 +2170,14 @@ case OP_Or: {
 /* Opcode: Negative * * *
 **
 ** Treat the top of the stack as a numeric quantity.  Replace it
-** with its additive inverse.
+** with its additive inverse.  If the top of the stack is NULL
+** its value is unchanged.
 */
 /* Opcode: AbsValue * * *
 **
 ** Treat the top of the stack as a numeric quantity.  Replace it
-** with its absolute value.
+** with its absolute value. If the top of the stack is NULL
+** its value is unchanged.
 */
 case OP_Negative:
 case OP_AbsValue: {
@@ -2105,6 +2195,8 @@ case OP_AbsValue: {
       aStack[tos].i = -aStack[tos].i;
     }
     aStack[tos].flags = STK_Int;
+  }else if( aStack[tos].flags & STK_Null ){
+    /* Do nothing */
   }else{
     Realify(p, tos);
     Release(p, tos);
@@ -2119,11 +2211,13 @@ case OP_AbsValue: {
 /* Opcode: Not * * *
 **
 ** Interpret the top of the stack as a boolean value.  Replace it
-** with its complement.
+** with its complement.  If the top of the stack is NULL its value
+** is unchanged.
 */
 case OP_Not: {
   int tos = p->tos;
   VERIFY( if( p->tos<0 ) goto not_enough_stack; )
+  if( aStack[tos].flags & STK_Null ) break;  /* Do nothing to NULLs */
   Integerify(p, tos);
   Release(p, tos);
   aStack[tos].i = !aStack[tos].i;
@@ -2134,11 +2228,13 @@ case OP_Not: {
 /* Opcode: BitNot * * *
 **
 ** Interpret the top of the stack as an value.  Replace it
-** with its ones-complement.
+** with its ones-complement.  If the top of the stack is NULL its
+** value is unchanged.
 */
 case OP_BitNot: {
   int tos = p->tos;
   VERIFY( if( p->tos<0 ) goto not_enough_stack; )
+  if( aStack[tos].flags & STK_Null ) break;  /* Do nothing to NULLs */
   Integerify(p, tos);
   Release(p, tos);
   aStack[tos].i = ~aStack[tos].i;
@@ -2155,60 +2251,92 @@ case OP_Noop: {
   break;
 }
 
-/* Opcode: If * P2 *
+/* Opcode: If P1 P2 *
 **
 ** Pop a single boolean from the stack.  If the boolean popped is
 ** true, then jump to p2.  Otherwise continue to the next instruction.
 ** An integer is false if zero and true otherwise.  A string is
 ** false if it has zero length and true otherwise.
+**
+** If the value popped of the stack is NULL, then take the jump if P1
+** is true and fall through if P1 is false.
+*/
+/* Opcode: IfNot P1 P2 *
+**
+** Pop a single boolean from the stack.  If the boolean popped is
+** false, then jump to p2.  Otherwise continue to the next instruction.
+** An integer is false if zero and true otherwise.  A string is
+** false if it has zero length and true otherwise.
+**
+** If the value popped of the stack is NULL, then take the jump if P1
+** is true and fall through if P1 is false.
 */
-case OP_If: {
+case OP_If:
+case OP_IfNot: {
   int c;
   VERIFY( if( p->tos<0 ) goto not_enough_stack; )
-  Integerify(p, p->tos);
-  c = aStack[p->tos].i;
+  if( aStack[p->tos].flags & STK_Null ){
+    c = pOp->p1;
+  }else{
+    Integerify(p, p->tos);
+    c = aStack[p->tos].i;
+    if( pOp->opcode==OP_IfNot ) c = !c;
+  }
   POPSTACK;
   if( c ) pc = pOp->p2-1;
   break;
 }
 
-/* Opcode: IsNull * P2 *
+/* Opcode: IsNull P1 P2 *
 **
-** Pop a single value from the stack.  If the value popped is NULL
-** then jump to p2.  Otherwise continue to the next 
-** instruction.
+** If any of the top abs(P1) values on the stack are NULL, then jump
+** to P2.  The stack is popped P1 times if P1>0.  If P1<0 then all values
+** are left unchanged on the stack.
 */
 case OP_IsNull: {
-  int c;
-  VERIFY( if( p->tos<0 ) goto not_enough_stack; )
-  c = (aStack[p->tos].flags & STK_Null)!=0;
-  POPSTACK;
-  if( c ) pc = pOp->p2-1;
+  int i, cnt;
+  cnt = pOp->p1;
+  if( cnt<0 ) cnt = -cnt;
+  VERIFY( if( p->tos+1-cnt<0 ) goto not_enough_stack; )
+  for(i=0; i<cnt; i++){
+    if( aStack[p->tos-i].flags & STK_Null ){
+      pc = pOp->p2-1;
+      break;
+    }
+  }
+  if( pOp->p1>0 ) PopStack(p, cnt);
   break;
 }
 
-/* Opcode: NotNull * P2 *
+/* Opcode: NotNull P1 P2 *
 **
-** Pop a single value from the stack.  If the value popped is not
-** NULL, then jump to p2.  Otherwise continue to the next 
-** instruction.
+** Jump to P2 if the top value on the stack is not NULL.  Pop the
+** stack if P1 is greater than zero.  If P1 is less than or equal to
+** zero then leave the value on the stack.
 */
 case OP_NotNull: {
-  int c;
   VERIFY( if( p->tos<0 ) goto not_enough_stack; )
-  c = (aStack[p->tos].flags & STK_Null)==0;
-  POPSTACK;
-  if( c ) pc = pOp->p2-1;
+  if( (aStack[p->tos].flags & STK_Null)==0 ) pc = pOp->p2-1;
+  if( pOp->p1>0 ){ POPSTACK; }
   break;
 }
 
-/* Opcode: MakeRecord P1 * *
+/* Opcode: MakeRecord P1 P2 *
 **
 ** Convert the top P1 entries of the stack into a single entry
 ** suitable for use as a data record in a database table.  The
 ** details of the format are irrelavant as long as the OP_Column
 ** opcode can decode the record later.  Refer to source code
 ** comments for the details of the record format.
+**
+** If P2 is true (non-zero) and one or more of the P1 entries
+** that go into building the record is NULL, then add some extra
+** bytes to the record to make it distinct for other entries created
+** during the same run of the VDBE.  The extra bytes added are a
+** counter that is reset with each run of the VDBE, so records
+** created this way will not necessarily be distinct across runs.
+** But they should be distinct for transient tables (created using
+** OP_OpenTemp) which is what they are intended for.
 */
 case OP_MakeRecord: {
   char *zNewRecord;
@@ -2217,6 +2345,8 @@ case OP_MakeRecord: {
   int i, j;
   int idxWidth;
   u32 addr;
+  int addUnique = 0;   /* True to cause bytes to be added to make the
+                       ** generated record distinct */
 
   /* Assuming the record contains N fields, the record format looks
   ** like this:
@@ -2240,11 +2370,14 @@ case OP_MakeRecord: {
   VERIFY( if( p->tos+1<nField ) goto not_enough_stack; )
   nByte = 0;
   for(i=p->tos-nField+1; i<=p->tos; i++){
-    if( (aStack[i].flags & STK_Null)==0 ){
+    if( (aStack[i].flags & STK_Null) ){
+      addUnique = pOp->p2;
+    }else{
       if( Stringify(p, i) ) goto no_mem;
       nByte += aStack[i].n;
     }
   }
+  if( addUnique ) nByte += sizeof(uniqueCnt);
   if( nByte + nField + 1 < 256 ){
     idxWidth = 1;
   }else if( nByte + 2*nField + 2 < 65536 ){
@@ -2260,7 +2393,7 @@ case OP_MakeRecord: {
   zNewRecord = sqliteMalloc( nByte );
   if( zNewRecord==0 ) goto no_mem;
   j = 0;
-  addr = idxWidth*(nField+1);
+  addr = idxWidth*(nField+1) + addUnique*sizeof(uniqueCnt);
   for(i=p->tos-nField+1; i<=p->tos; i++){
     zNewRecord[j++] = addr & 0xff;
     if( idxWidth>1 ){
@@ -2280,6 +2413,11 @@ case OP_MakeRecord: {
       zNewRecord[j++] = (addr>>16)&0xff;
     }
   }
+  if( addUnique ){
+    memcpy(&zNewRecord[j], &uniqueCnt, sizeof(uniqueCnt));
+    uniqueCnt++;
+    j += sizeof(uniqueCnt);
+  }
   for(i=p->tos-nField+1; i<=p->tos; i++){
     if( (aStack[i].flags & STK_Null)==0 ){
       memcpy(&zNewRecord[j], zStack[i], aStack[i].n);
@@ -2311,7 +2449,7 @@ case OP_MakeRecord: {
 **
 ** See also: MakeIdxKey, SortMakeKey
 */
-/* Opcode: MakeIdxKey P1 * *
+/* Opcode: MakeIdxKey P1 P2 *
 **
 ** Convert the top P1 entries of the stack into a single entry suitable
 ** for use as the key in an index.  In addition, take one additional integer
@@ -2327,6 +2465,12 @@ case OP_MakeRecord: {
 ** in the stack is the first field and the top of the stack becomes the
 ** last.
 **
+** If P2 is not zero and one or more of the P1 entries that go into the
+** generated key is NULL, then jump to P2 after the new key has been
+** pushed on the stack.  In other words, jump to P2 if the key is
+** guaranteed to be unique.  This jump can be used to skip a subsequent
+** uniqueness test.
+**
 ** See also:  MakeKey, SortMakeKey
 */
 case OP_MakeIdxKey:
@@ -2336,6 +2480,7 @@ case OP_MakeKey: {
   int nField;
   int addRowid;
   int i, j;
+  int containsNull = 0;
 
   addRowid = pOp->opcode==OP_MakeIdxKey;
   nField = pOp->p1;
@@ -2347,6 +2492,7 @@ case OP_MakeKey: {
     char *z;
     if( flags & STK_Null ){
       nByte += 2;
+      containsNull = 1;
     }else if( flags & STK_Real ){
       z = aStack[i].z;
       sqliteRealToSortable(aStack[i].r, &z[1]);
@@ -2406,8 +2552,11 @@ case OP_MakeKey: {
     Integerify(p, p->tos-nField);
     iKey = intToKey(aStack[p->tos-nField].i);
     memcpy(&zNewKey[j], &iKey, sizeof(u32));
+    PopStack(p, nField+1);
+    if( pOp->p2 && containsNull ) pc = pOp->p2 - 1;
+  }else{
+    if( pOp->p2==0 ) PopStack(p, nField+addRowid);
   }
-  if( pOp->p2==0 ) PopStack(p, nField+addRowid);
   VERIFY( NeedStack(p, p->tos+1); )
   p->tos++;
   aStack[p->tos].n = nByte;
@@ -4373,7 +4522,9 @@ case OP_AggFunc: {
   VERIFY( if( p->tos+1<n ) goto not_enough_stack; )
   VERIFY( if( aStack[p->tos].flags!=STK_Int ) goto bad_instruction; )
   for(i=p->tos-n; i<p->tos; i++){
-    if( (aStack[i].flags & STK_Null)==0 ){
+    if( aStack[i].flags & STK_Null ){
+      zStack[i] = 0;
+    }else{
       if( Stringify(p, i) ) goto no_mem;
     }
   }
index 23410437e8ce77e682bfaaeacc0dab28fb95b247..42e9a07b0d96c65589deb04fc2d9812048f9c610 100644 (file)
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.52 2002/05/23 22:07:03 drh Exp $
+** $Id: vdbe.h,v 1.53 2002/05/26 20:54:34 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -157,53 +157,54 @@ typedef struct VdbeOp VdbeOp;
 
 #define OP_Goto               76
 #define OP_If                 77
-#define OP_Halt               78
-
-#define OP_ColumnCount        79
-#define OP_ColumnName         80
-#define OP_Callback           81
-#define OP_NullCallback       82
-
-#define OP_Integer            83
-#define OP_String             84
-#define OP_Pop                85
-#define OP_Dup                86
-#define OP_Pull               87
-#define OP_Push               88
-#define OP_MustBeInt          89
-
-#define OP_Add                90
-#define OP_AddImm             91
-#define OP_Subtract           92
-#define OP_Multiply           93
-#define OP_Divide             94
-#define OP_Remainder          95
-#define OP_BitAnd             96
-#define OP_BitOr              97
-#define OP_BitNot             98
-#define OP_ShiftLeft          99
-#define OP_ShiftRight        100
-#define OP_AbsValue          101
-#define OP_Eq                102
-#define OP_Ne                103
-#define OP_Lt                104
-#define OP_Le                105
-#define OP_Gt                106
-#define OP_Ge                107
-#define OP_IsNull            108
-#define OP_NotNull           109
-#define OP_Negative          110
-#define OP_And               111
-#define OP_Or                112
-#define OP_Not               113
-#define OP_Concat            114
-#define OP_Noop              115
-#define OP_Function          116
-
-#define OP_Limit             117
-
-
-#define OP_MAX               117
+#define OP_IfNot              78
+#define OP_Halt               79
+
+#define OP_ColumnCount        80
+#define OP_ColumnName         81
+#define OP_Callback           82
+#define OP_NullCallback       83
+
+#define OP_Integer            84
+#define OP_String             85
+#define OP_Pop                86
+#define OP_Dup                87
+#define OP_Pull               88
+#define OP_Push               89
+#define OP_MustBeInt          90
+
+#define OP_Add                91
+#define OP_AddImm             92
+#define OP_Subtract           93
+#define OP_Multiply           94
+#define OP_Divide             95
+#define OP_Remainder          96
+#define OP_BitAnd             97
+#define OP_BitOr              98
+#define OP_BitNot             99
+#define OP_ShiftLeft         100
+#define OP_ShiftRight        101
+#define OP_AbsValue          102
+#define OP_Eq                103
+#define OP_Ne                104
+#define OP_Lt                105
+#define OP_Le                106
+#define OP_Gt                107
+#define OP_Ge                108
+#define OP_IsNull            109
+#define OP_NotNull           110
+#define OP_Negative          111
+#define OP_And               112
+#define OP_Or                113
+#define OP_Not               114
+#define OP_Concat            115
+#define OP_Noop              116
+#define OP_Function          117
+
+#define OP_Limit             118
+
+
+#define OP_MAX               118
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
index a3576cae441afaf9ec73e9890bababb7abd193cc..0f14df9deefb9d5cf373d490555db443f432b567 100644 (file)
@@ -13,7 +13,7 @@
 ** the WHERE clause of SQL statements.  Also found here are subroutines
 ** to generate VDBE code to evaluate expressions.
 **
-** $Id: where.c,v 1.47 2002/05/24 20:31:38 drh Exp $
+** $Id: where.c,v 1.48 2002/05/26 20:54:34 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -199,7 +199,7 @@ WhereInfo *sqliteWhereBegin(
   ** expression and either jump over all of the code or fall thru.
   */
   if( pWhere && sqliteExprIsConstant(pWhere) ){
-    sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak);
+    sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1);
   }
 
   /* Split the WHERE clause into as many as 32 separate subexpressions
@@ -795,7 +795,7 @@ WhereInfo *sqliteWhereBegin(
         haveKey = 0;
         sqliteVdbeAddOp(v, OP_MoveTo, base+idx, 0);
       }
-      sqliteExprIfFalse(pParse, aExpr[j].p, cont);
+      sqliteExprIfFalse(pParse, aExpr[j].p, cont, 1);
       aExpr[j].p = 0;
     }
     brk = cont;
index 784b802986b7d6bc343ec43b0d4353817d483b9c..0d93df6627dc630f1b9072a660165778caa60f33 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing expressions.
 #
-# $Id: expr.test,v 1.19 2002/03/24 13:13:29 drh Exp $
+# $Id: expr.test,v 1.20 2002/05/26 20:54:34 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -22,8 +22,7 @@ execsql {CREATE TABLE test1(i1 int, i2 int, r1 real, r2 real, t1 text, t2 text)}
 execsql {INSERT INTO test1 VALUES(1,2,1.1,2.2,'hello','world')}
 proc test_expr {name settings expr result} {
   do_test $name [format {
-    execsql {UPDATE test1 SET %s}
-    execsql {SELECT %s FROM test1}
+    execsql {BEGIN; UPDATE test1 SET %s; SELECT %s FROM test1; ROLLBACK;}
   } $settings $expr] $result
 }
 
@@ -63,7 +62,7 @@ test_expr expr-1.33 {i1=1, i2=2} {i1=1 OR i2=1} {1}
 test_expr expr-1.34 {i1=1, i2=2} {i1=2 OR i2=2} {1}
 test_expr expr-1.35 {i1=1, i2=2} {i1-i2=-1} {1}
 test_expr expr-1.36 {i1=1, i2=0} {not i1} {0}
-test_expr expr-1.37 {i1=1, i2=NULL} {not i2} {1}
+test_expr expr-1.37 {i1=1, i2=0} {not i2} {1}
 test_expr expr-1.38 {i1=1} {-i1} {-1}
 test_expr expr-1.39 {i1=1} {+i1} {1}
 test_expr expr-1.40 {i1=1, i2=2} {+(i2+i1)} {3}
@@ -320,8 +319,8 @@ proc test_expr2 {name expr result} {
 test_expr2 expr-7.2  {a<10 AND a>8}                  {9}
 test_expr2 expr-7.3  {a<=10 AND a>=8}                {8 9 10}
 test_expr2 expr-7.4  {a>=8 AND a<=10}                {8 9 10}
-test_expr2 expr-7.5  {a>=20 OR a<=1}                 {{} 1 20}
-test_expr2 expr-7.6  {b!=4 AND a<=3}                 {{} 1 3}
+test_expr2 expr-7.5  {a>=20 OR a<=1}                 {1 20}
+test_expr2 expr-7.6  {b!=4 AND a<=3}                 {1 3}
 test_expr2 expr-7.7  {b==8 OR b==16 OR b==32}        {3 4 5}
 test_expr2 expr-7.8  {NOT b<>8 OR b==1024}           {3 10}
 test_expr2 expr-7.9  {b LIKE '10%'}                  {10 20}
@@ -332,13 +331,13 @@ test_expr2 expr-7.13 {b GLOB '*1[456]'}              {4}
 test_expr2 expr-7.14 {a ISNULL}                      {{}}
 test_expr2 expr-7.15 {a NOTNULL AND a<3}             {1 2}
 test_expr2 expr-7.16 {a AND a<3}                     {1 2}
-test_expr2 expr-7.17 {NOT a}                         {{}}
+test_expr2 expr-7.17 {NOT a}                         {}
 test_expr2 expr-7.18 {a==11 OR (b>1000 AND b<2000)}  {10 11}
-test_expr2 expr-7.19 {a<=1 OR a>=20}                 {{} 1 20}
-test_expr2 expr-7.20 {a<1 OR a>20}                   {{}}
-test_expr2 expr-7.21 {a>19 OR a<1}                   {{} 20}
+test_expr2 expr-7.19 {a<=1 OR a>=20}                 {1 20}
+test_expr2 expr-7.20 {a<1 OR a>20}                   {}
+test_expr2 expr-7.21 {a>19 OR a<1}                   {20}
 test_expr2 expr-7.22 {a!=1 OR a=100} \
-                         {{} 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
+                         {2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
 test_expr2 expr-7.23 {(a notnull AND a<4) OR a==8}   {1 2 3 8}
 test_expr2 expr-7.24 {a LIKE '2_' OR a==8}           {8 20}
 test_expr2 expr-7.25 {a GLOB '2?' OR a==8}           {8 20}
index 3a32dc0de005aa16bdaed97fb6e5afa12a8e6659..2b47b35743ee0120abd5f58547523f926b030428 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this file is testing UNION, INTERSECT and EXCEPT operators
 # in SELECT statements.
 #
-# $Id: select4.test,v 1.6 2002/05/24 16:14:16 drh Exp $
+# $Id: select4.test,v 1.7 2002/05/26 20:54:35 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -249,6 +249,48 @@ do_test select4-6.2 {
   }
 } {0 1 1 1 2 2 3 4 3 7 4 8 5 15}
 
+# NULLs are distinct.  Make sure the UNION operator recognizes this
+#
+do_test select4-6.3 {
+  execsql {
+    SELECT NULL UNION SELECT NULL UNION
+    SELECT 1 UNION SELECT 2 AS 'x'
+    ORDER BY x;
+  }
+} {{} {} 1 2}
+do_test select4-6.3 {
+  execsql {
+    SELECT NULL UNION ALL SELECT NULL UNION ALL
+    SELECT 1 UNION ALL SELECT 2 AS 'x'
+    ORDER BY x;
+  }
+} {{} {} 1 2}
+
+# Make sure the DISTINCT keyword treats NULLs as DISTINCT
+#
+do_test select4-6.4 {
+  execsql {
+    SELECT * FROM (
+       SELECT NULL, 1 UNION ALL SELECT NULL, 1
+    );
+  }
+} {{} 1 {} 1}
+do_test select4-6.5 {
+  execsql {
+    SELECT DISTINCT * FROM (
+       SELECT NULL, 1 UNION ALL SELECT NULL, 1
+    );
+  }
+} {{} 1 {} 1}
+do_test select4-6.6 {
+  execsql {
+    SELECT DISTINCT * FROM (
+       SELECT 1,2  UNION ALL SELECT 1,2
+    );
+  }
+} {1 2}
+
+
 # Make sure column names are correct when a compound select appears as
 # an expression in the WHERE clause.
 #
index e2f2559f5f6b5424892465f6a9b3015fae130488..392ab5319ae8d0622ef29aa28a24d76aaf4033f9 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this file is testing SELECT statements that are part of
 # expressions.
 #
-# $Id: subselect.test,v 1.4 2001/09/16 00:13:28 drh Exp $
+# $Id: subselect.test,v 1.5 2002/05/26 20:54:35 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -55,9 +55,8 @@ do_test subselect-1.3c {
 # NULL as the result.  Check it out.
 #
 do_test subselect-1.4 {
-  execsql {INSERT INTO t1 VALUES(NULL,8)}
-  execsql {SELECT b from t1 where a = (SELECT a FROM t1 WHERE b=5)}
-} {8}
+  execsql {SELECT b from t1 where a = coalesce((SELECT a FROM t1 WHERE b=5),1)}
+} {2}
 
 # Try multiple subselects within a single expression.
 #
index adce114509bdb659ddd8ed0e94559a0430e25140..ae103ea85b6784e411e48d8be5ab60a73b8212ab 100644 (file)
@@ -77,7 +77,7 @@ foreach tbl_defn [ list \
 
     CREATE TRIGGER before_update_row BEFORE UPDATE ON tbl FOR EACH ROW 
       BEGIN
-      INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog), 
+      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
          old.a, old.b, 
          (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
          new.a, new.b);
@@ -85,7 +85,7 @@ foreach tbl_defn [ list \
 
     CREATE TRIGGER after_update_row AFTER UPDATE ON tbl FOR EACH ROW 
       BEGIN
-      INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog), 
+      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
          old.a, old.b, 
          (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
          new.a, new.b);
@@ -94,7 +94,7 @@ foreach tbl_defn [ list \
     CREATE TRIGGER conditional_update_row AFTER UPDATE ON tbl FOR EACH ROW
       WHEN old.a = 1
       BEGIN
-      INSERT INTO clog VALUES ( (SELECT max(idx) + 1 FROM clog), 
+      INSERT INTO clog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM clog), 
          old.a, old.b, 
          (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
          new.a, new.b);
@@ -120,7 +120,7 @@ foreach tbl_defn [ list \
     INSERT INTO tbl VALUES (300, 200);
     CREATE TRIGGER delete_before_row BEFORE DELETE ON tbl FOR EACH ROW
       BEGIN
-      INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog), 
+      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
          old.a, old.b, 
          (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
          0, 0);
@@ -128,7 +128,7 @@ foreach tbl_defn [ list \
 
     CREATE TRIGGER delete_after_row AFTER DELETE ON tbl FOR EACH ROW
       BEGIN
-      INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog), 
+      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
          old.a, old.b, 
          (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
          0, 0);
@@ -148,7 +148,7 @@ foreach tbl_defn [ list \
     DELETE FROM rlog;
     CREATE TRIGGER insert_before_row BEFORE INSERT ON tbl FOR EACH ROW
       BEGIN
-      INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog), 
+      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
          0, 0,
          (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
          new.a, new.b);
@@ -156,7 +156,7 @@ foreach tbl_defn [ list \
 
     CREATE TRIGGER insert_after_row AFTER INSERT ON tbl FOR EACH ROW
       BEGIN
-      INSERT INTO rlog VALUES ( (SELECT max(idx) + 1 FROM rlog), 
+      INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
          0, 0,
          (SELECT sum(a) FROM tbl), (SELECT sum(b) FROM tbl), 
          new.a, new.b);
index 146ddf02853a5653668e2fe043305139e3292263..27cb3e716f57c049522297a1afb9e66437f0b213 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this file is testing the CREATE UNIQUE INDEX statement,
 # and primary keys, and the UNIQUE constraint on table columns
 #
-# $Id: unique.test,v 1.3 2001/12/21 14:30:44 drh Exp $
+# $Id: unique.test,v 1.4 2002/05/26 20:54:35 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -162,4 +162,37 @@ do_test unique-3.4 {
   }
 } {1 {constraint failed}}
 
+# Make sure NULLs are distinct as far as the UNIQUE tests are
+# concerned.
+#
+do_test unique-4.1 {
+  execsql {
+    CREATE TABLE t4(a UNIQUE, b, c, UNIQUE(b,c));
+    INSERT INTO t4 VALUES(1,2,3);
+    INSERT INTO t4 VALUES(NULL, 2, NULL);
+    SELECT * FROM t4;
+  }
+} {1 2 3 {} 2 {}}
+do_test unique-4.2 {
+  catchsql {
+    INSERT INTO t4 VALUES(NULL, 3, 4);
+  }
+} {0 {}}
+do_test unique-4.3 {
+  execsql {
+    SELECT * FROM t4
+  }
+} {1 2 3 {} 2 {} {} 3 4}
+do_test unique-4.4 {
+  catchsql {
+    INSERT INTO t4 VALUES(2, 2, NULL);
+  }
+} {0 {}}
+do_test unique-4.5 {
+  execsql {
+    SELECT * FROM t4
+  }
+} {1 2 3 {} 2 {} {} 3 4 2 2 {}}
+
+
 finish_test