]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
The MIN() aggregate function returns NULL if any element in the result
authordrh <drh@noemail.net>
Wed, 23 Jun 2004 21:16:51 +0000 (21:16 +0000)
committerdrh <drh@noemail.net>
Wed, 23 Jun 2004 21:16:51 +0000 (21:16 +0000)
was NULL.  This makes MIN() consistent with ORDER BY which sorts NULL first.
Ticket #777. (CVS 1679)

FossilOrigin-Name: 78ced6e3092d69e7cb77c5c2acff70f3c92e6523

manifest
manifest.uuid
src/func.c
test/func.test
test/minmax.test
test/null.test
test/select1.test

index 7269a518f78017db662fb759f42a7c902c95ea9b..916e0026282f188c53445ecefdd14cb004cf1da4 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\ssource\scode\sto\sthe\ssqlite_analyzer\sutility.\s(CVS\s1637)
-D 2004-06-19T11:57:40
+C The\sMIN()\saggregate\sfunction\sreturns\sNULL\sif\sany\selement\sin\sthe\sresult\nwas\sNULL.\s\sThis\smakes\sMIN()\sconsistent\swith\sORDER\sBY\swhich\ssorts\sNULL\sfirst.\nTicket\s#777.\s(CVS\s1679)
+D 2004-06-23T21:16:52
 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -32,7 +32,7 @@ F src/date.c f055419d602bde622c70f831350b6b52f2235de0
 F src/delete.c 82001c74882319f94dab5f6b92a27311b31092ae
 F src/encode.c fc8c51f0b61bc803ccdec092e130bebe762b0a2f
 F src/expr.c 8c3f5603c3e98b1c146d18076ba3e82cdbb59c11
-F src/func.c 34fead7a33e82095f6412d3fafd379d47864b3be
+F src/func.c 522d58abf2ec9f93fe3aa08b12611aede570d125
 F src/hash.c 9b56ef3b291e25168f630d5643a4264ec011c70e
 F src/hash.h 3247573ab95b9dd90bcca0307a75d9a16da1ccc7
 F src/insert.c c0485ee2d1b99322894e2d1e0b576fd05ed75616
@@ -88,7 +88,7 @@ F test/delete.test 92256384f1801760180ded129f7427884cf28886
 F test/expr.test ad985242e140f87eeef329d98257b8369a2066dc
 F test/fkey1.test d65c824459916249bee501532d6154ddab0b5db7
 F test/format3.test 149cc166c97923fa60def047e90dd3fb32bba916
-F test/func.test 000515779001ac6899eec4b54e65c6e2501279d4
+F test/func.test cf6434042bf14dcfa124ce2835b049018248e9f6
 F test/hook.test 1a67ce0cd64a6455d016962542f2822458dccc49
 F test/in.test 0de39b02ceeca90993b096822fb5a884661c5b47
 F test/index.test 9295deefbdb6dedbe01be8905f0c448fe5bd4079
@@ -107,13 +107,13 @@ F test/main.test 6a851b5992c4881a725a3d9647e629199df8de9d
 F test/malloc.test 2cfcffb7c858640e01e6520ee1cd54ca57d98e80
 F test/memdb.test 6ece25c7c0e6500199d3662607a3edca081abb2a
 F test/memleak.test 4d5d374c8ea1fc5ac634aed58cac1047848ce65e
-F test/minmax.test 9dcf52f713b1b9e61d0a88a51eb8bb2e3c52d0ab
+F test/minmax.test db8cd0e865682143dca77447d944fb25685e1cd3
 F test/misc1.test 0b98d493b0cf55cb5f53e1f3df8107c166eecb5a
 F test/misc2.test 10c2ce26407d37411b96273e552d5095393732be
 F test/misc3.test 3b5e369514a3ba3f919fb7eafa7d027440b5079e
 F test/misuse.test 1095f26d1aed406c65e1d2eba651c4bb7c38cbff
 F test/notnull.test 7a08117a71e74b0321aaa937dbeb41a09d6eb1d0
-F test/null.test c14d0f4739f21e929b8115b72bf0c765b6bb1721
+F test/null.test b5c066aad5f6a948b9bf372b289c28faffe174dc
 F test/pager.test 331519008889d45f6df6697395e5bce6ee602fd9
 F test/pragma.test 24a3f7a697b45cb90d664ebce5566bec7ac41571
 F test/printf.test 46b3d07d59d871d0831b4a657f6dfcafe0574850
@@ -121,7 +121,7 @@ F test/progress.test 701b6115c2613128ececdfe1398a1bd0e1a4cfb3 x
 F test/quick.test 5a6bccf5c02f16841a79fbac7409a02138880c10
 F test/quote.test 08f23385c685d3dc7914ec760d492cacea7f6e3d
 F test/rowid.test 77f7e8c7ca626a15ff91a536595b695cfce7c845
-F test/select1.test 0d708cec567104653ec9aa49fecf3444a2e7d150
+F test/select1.test e40a88c85f98fb87f60db1abefae0bab7264c9a3
 F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4
 F test/select3.test 445a1a3dde4e2fd32541b311f55da5e2f8079d76
 F test/select4.test e7e9a32fa745246cb99fadbeb63af4843a17925b
@@ -189,7 +189,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da
 F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
-P 28b737b5a0e07a142396809f8a7dbd985d433c0d
-R fb6b4bcc22cce49b0e4adc90b58e43f9
+P 06bf4c7e41839eb003f3df05a80789a1f2b21b8f
+R dafb0c51f2a215a538fc0976e7da3c17
 U drh
-Z f092da110df516aacd5c626d43efe1ef
+Z 693b4009cfcb767a325ed3f699f12e33
index d3b64433f67479c97e50786ede79f11c79cbd6a5..eee7045f3fd559c5063296c510f89ab3ce5cc264 100644 (file)
@@ -1 +1 @@
-06bf4c7e41839eb003f3df05a80789a1f2b21b8f
\ No newline at end of file
+78ced6e3092d69e7cb77c5c2acff70f3c92e6523
\ No newline at end of file
index e6613c5b1d4dd77552660628e31188ca755fc824..b43ce1f8ee63172e0697e84cd66ee2f5b96027f6 100644 (file)
@@ -16,7 +16,7 @@
 ** sqliteRegisterBuildinFunctions() found at the bottom of the file.
 ** All other code has file scope.
 **
-** $Id: func.c,v 1.43 2004/02/25 22:51:06 rdc Exp $
+** $Id: func.c,v 1.43.2.1 2004/06/23 21:16:52 drh Exp $
 */
 #include <ctype.h>
 #include <math.h>
@@ -523,20 +523,29 @@ static void minmaxStep(sqlite_func *context, int argc, const char **argv){
     xCompare = strcmp;
   }
   mask = (int)sqlite_user_data(context);
+  assert( mask==0 || mask==-1 );
   p = sqlite_aggregate_context(context, sizeof(*p));
-  if( p==0 || argc<1 || argv[0]==0 ) return;
+  if( p==0 || argc<1 ) return;
+  if( mask==0 && p->zBuf[0]>=2 ) return;
+  if( argv[0]==0 ){
+    if( mask==0 ){
+      p->zBuf[0] |= 2;
+    }
+    return;
+  }
+  assert( p->zBuf[0]<2 );
   if( p->z==0 || (xCompare(argv[0],p->z)^mask)<0 ){
     int len;
-    if( !p->zBuf[0] ){
+    if( p->zBuf[0]!=0 ){
       sqliteFree(p->z);
     }
     len = strlen(argv[0]);
     if( len < sizeof(p->zBuf)-1 ){
       p->z = &p->zBuf[1];
-      p->zBuf[0] = 1;
+      p->zBuf[0] = 0;
     }else{
       p->z = sqliteMalloc( len+1 );
-      p->zBuf[0] = 0;
+      p->zBuf[0] = 1;
       if( p->z==0 ) return;
     }
     strcpy(p->z, argv[0]);
@@ -545,10 +554,10 @@ static void minmaxStep(sqlite_func *context, int argc, const char **argv){
 static void minMaxFinalize(sqlite_func *context){
   MinMaxCtx *p;
   p = sqlite_aggregate_context(context, sizeof(*p));
-  if( p && p->z ){
+  if( p && p->z && p->zBuf[0]<2 ){
     sqlite_set_result_string(context, p->z, strlen(p->z));
   }
-  if( p && !p->zBuf[0] ){
+  if( p && (p->zBuf[0]&1)!=0 ){
     sqliteFree(p->z);
   }
 }
@@ -621,7 +630,12 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){
   int i;
 
   for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
-    void *pArg = aFuncs[i].argType==2 ? (void*)(-1) : db;
+    void *pArg;
+    switch( aFuncs[i].argType ){
+      case 0:  pArg = 0;           break;
+      case 1:  pArg = db;          break;
+      case 2:  pArg = (void*)(-1); break;
+    }
     sqlite_create_function(db, aFuncs[i].zName,
            aFuncs[i].nArg, aFuncs[i].xFunc, pArg);
     if( aFuncs[i].xFunc ){
@@ -629,7 +643,12 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){
     }
   }
   for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
-    void *pArg = aAggs[i].argType==2 ? (void*)(-1) : db;
+    void *pArg;
+    switch( aAggs[i].argType ){
+      case 0:  pArg = 0;           break;
+      case 1:  pArg = db;          break;
+      case 2:  pArg = (void*)(-1); break;
+    }
     sqlite_create_aggregate(db, aAggs[i].zName,
            aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, pArg);
     sqlite_function_type(db, aAggs[i].zName, aAggs[i].dataType);
index fcb82f9b95ea0d7ace7efd3de2a6456452c85c9b..51140e9d41d91ec8390b4256127c56db130014c7 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing built-in functions.
 #
-# $Id: func.test,v 1.16 2002/11/04 19:32:26 drh Exp $
+# $Id: func.test,v 1.16.2.1 2004/06/23 21:16:52 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -252,7 +252,7 @@ do_test func-8.1 {
   execsql {
     SELECT sum(a), count(a), round(avg(a),2), min(a), max(a), count(*) FROM t2;
   }
-} {68236 3 22745.33 1 67890 5}
+} {68236 3 22745.33 {} 67890 5}
 do_test func-8.2 {
   execsql {
     SELECT max('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t2;
@@ -263,7 +263,12 @@ do_test func-8.3 {
     CREATE TEMP TABLE t3 AS SELECT a FROM t2 ORDER BY a DESC;
     SELECT min('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3;
   }
-} {z+1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP}
+} {{}}
+do_test func-8.4 {
+  execsql {
+    SELECT max('z+'||a||'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP') FROM t3;
+  }
+} {z+67890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP}
 
 # How do you test the random() function in a meaningful, deterministic way?
 #
index 5517ad99e6b3c8481dbb3711cccfdbeecff95f6d..da3b9008d91cb4febdeeb8c7fe98fd19ed126a9d 100644 (file)
@@ -13,7 +13,7 @@
 # aggregate min() and max() functions and which are handled as
 # as a special case.
 #
-# $Id: minmax.test,v 1.9 2004/03/13 14:00:37 drh Exp $
+# $Id: minmax.test,v 1.9.2.1 2004/06/23 21:16:52 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -127,7 +127,7 @@ do_test minmax-4.1 {
     SELECT coalesce(min(x+0),-1), coalesce(max(x+0),-1) FROM
       (SELECT * FROM t1 UNION SELECT NULL as 'x', NULL as 'y')
   }
-} {1 20}
+} {-1 20}
 do_test minmax-4.2 {
   execsql {
     SELECT y, sum(x) FROM
@@ -276,4 +276,34 @@ do_test minmax-9.2 {
   }
 } {{}}
 
+# If there is a NULL in an aggregate max(), ignore it.  If a NULL
+# occurs in an aggregate min(), then the result will be NULL because
+# NULL compares less than all other values.
+#
+do_test minmax-10.1 {
+  execsql {
+    CREATE TABLE t6(x);
+    INSERT INTO t6 VALUES(1);
+    INSERT INTO t6 VALUES(2);
+    INSERT INTO t6 VALUES(NULL);
+    SELECT coalesce(min(x),-1) FROM t6;
+  }
+} {-1}
+do_test minmax-10.2 {
+  execsql {
+    SELECT max(x) FROM t6;
+  }
+} {2}
+do_test minmax-10.3 {
+  execsql {
+    CREATE INDEX i6 ON t6(x);
+    SELECT coalesce(min(x),-1) FROM t6;
+  }
+} {-1}
+do_test minmax-10.4 {
+  execsql {
+    SELECT max(x) FROM t6;
+  }
+} {2}
+
 finish_test
index 9705921715071935e4837ece8c1393e0dfa7f1b2..e4ea8ccc9d9d7ae36d2640d3bc85b9e3660998b6 100644 (file)
@@ -94,13 +94,14 @@ do_test null-2.8 {
 } {1 0 0 1 0 0 0}
 
 # Check to see that NULL values are ignored in aggregate functions.
+# (except for min().)
 #
 do_test null-3.1 {
   execsql {
     select count(*), count(b), count(c), sum(b), sum(c), 
            avg(b), avg(c), min(b), max(b) from t1;
   }
-} {7 4 6 2 3 0.5 0.5 0 1}
+} {7 4 6 2 3 0.5 0.5 {} 1}
 
 # Check to see how WHERE clauses handle NULL values.  A NULL value
 # is the same as UNKNOWN.  The WHERE clause should only select those
index b7624c0836267ba510d90e1f2eefea9e1f1ef348..0cd6f71b54c1757c77612944530cca962b5a0fe9 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: select1.test,v 1.30 2002/06/02 16:09:03 drh Exp $
+# $Id: select1.test,v 1.30.2.1 2004/06/23 21:16:52 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -159,7 +159,7 @@ do_test select1-2.8 {
 } {0 {11 33}}
 do_test select1-2.8.1 {
   execsql {SELECT coalesce(min(a),'xyzzy') FROM t3}
-} {11}
+} {xyzzy}
 do_test select1-2.8.2 {
   execsql {SELECT min(coalesce(a,'xyzzy')) FROM t3}
 } {11}