diff --git a/manifest b/manifest index b2ce488ca9..9a8c52ed43 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sextra\s\\\scharacters\sfrom\stemporary\sfilenames\sunder\swindows.\n(Ticket\s#52)\s(CVS\s596) -D 2002-05-29T12:44:53 +C Built-in\saggregate\sfunctions\s(MIN,\sSUM,\sAVG,\setc)\sshould\signore\sNULL\sentires.\s(CVS\s597) +D 2002-05-29T23:22:23 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -24,7 +24,7 @@ F src/build.c 36e42718a7a94f554ea39508993378482f5335c7 F src/delete.c a2b098cbbf518e6b641847e26de85827793bc523 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760 F src/expr.c 1a7a2f5f2b7dd37659783cdb6efaac74a092ba71 -F src/func.c 16e994eea8cceb0cd451938e63d337f7d9d089bf +F src/func.c 2cd4922913234ad384ccb75dd41bc35259a8338c F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 F src/insert.c 24b4e146319bada6f82a1d5eae6b38b3065d132f @@ -64,7 +64,7 @@ F test/conflict.test 5149646703d3930c9111068b5cda7e2e938476e3 F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8 F test/expr.test 518ee35ec8d2c8883544d75a031219c75391fb21 -F test/func.test 1505ac9ebc11ca5c0300192d1519a542d5cbcfe1 +F test/func.test 628ab513b0d9c54251a63e026a26b7b4347e54ab F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa F test/index.test c8a471243bbf878974b99baf5badd59407237cf3 F test/insert.test 58d44c19b3557f67f4aeb5110ed9ef02038c3684 @@ -76,7 +76,7 @@ F test/limit.test 6f98bcefc92209103bb3764c81975a6ec21d6702 F test/lock.test 3fcfd46a73119f6a18094673328a32c7b3047a8f F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85 F test/malloc.test 7ba32a9ebd3aeed52ae4aaa6d42ca37e444536fd -F test/minmax.test c50bcc760433acd7ca8e22bc4c525b9ae82762e2 +F test/minmax.test a234053455ffd42d785ba6edc5425374e064ff0d F test/misc1.test df281e9b26cd1db5808939c7cf2703072d555be0 F test/misuse.test a3aa2b18a97e4c409a1fcaff5151a4dd804a0162 F test/notnull.test b1f3e42fc475b0b5827b27b2e9b562081995ff30 @@ -86,7 +86,7 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05 F test/quote.test 08f23385c685d3dc7914ec760d492cacea7f6e3d F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274 -F test/select1.test 1842aa108d0d780b71fd1250d3518188498d737b +F test/select1.test a19a8026b5c2c5bdf5384d761f3d446954b7ebf9 F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4 F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18 F test/select4.test 2ea8c7b7feceb853da167a39e4cce996a5c3ad88 @@ -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 5e74d0964b8fd99eda798e3737217aa499cc1726 -R b2a667c33a3dc036c20c07778d3a668a +P ed11abc81e638c21ec1aa0445a6d59de91343095 +R 0dbe1e91bf7fd44e5ab325282618e3b3 U drh -Z 79cd389a43b2907c33c18067e98b1634 +Z c098e63f0d01ca947f8ca615049ab0d0 diff --git a/manifest.uuid b/manifest.uuid index d7a3d1e8d6..14072de20a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ed11abc81e638c21ec1aa0445a6d59de91343095 \ No newline at end of file +19ae12bef210ae5fe171f10833faa38d640c129f \ No newline at end of file diff --git a/src/func.c b/src/func.c index cf523e3b8d..8ba7db9de6 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.17 2002/05/26 21:34:58 drh Exp $ +** $Id: func.c,v 1.18 2002/05/29 23:22:23 drh Exp $ */ #include #include @@ -254,6 +254,7 @@ static void nullifFunc(sqlite_func *context, int argc, const char **argv){ typedef struct SumCtx SumCtx; struct SumCtx { double sum; /* Sum of terms */ + int cnt; /* Number of elements summed */ }; /* @@ -261,12 +262,12 @@ struct SumCtx { */ static void sumStep(sqlite_func *context, int argc, const char **argv){ SumCtx *p; - double x; if( argc<1 ) return; p = sqlite_aggregate_context(context, sizeof(*p)); - if( p==0 ) return; - x = argv[0] ? atof(argv[0]) : 0.0; - p->sum += x; + if( p && argv[0] ){ + p->sum += atof(argv[0]); + p->cnt++; + } } static void sumFinalize(sqlite_func *context){ SumCtx *p; @@ -275,11 +276,9 @@ static void sumFinalize(sqlite_func *context){ } static void avgFinalize(sqlite_func *context){ SumCtx *p; - double rN; p = sqlite_aggregate_context(context, sizeof(*p)); - rN = sqlite_aggregate_count(context); - if( p && rN>0.0 ){ - sqlite_set_result_double(context, p->sum/rN); + if( p && p->cnt>0 ){ + sqlite_set_result_double(context, p->sum/(double)p->cnt); } } @@ -291,6 +290,7 @@ typedef struct StdDevCtx StdDevCtx; struct StdDevCtx { double sum; /* Sum of terms */ double sum2; /* Sum of the squares of terms */ + int cnt; /* Number of terms counted */ }; #if 0 /* Omit because math library is required */ @@ -302,17 +302,20 @@ static void stdDevStep(sqlite_func *context, int argc, const char **argv){ double x; if( argc<1 ) return; p = sqlite_aggregate_context(context, sizeof(*p)); - if( p==0 ) return; - x = argv[0] ? atof(argv[0]) : 0.0; - p->sum += x; - p->sum2 += x*x; + if( p && argv[0] ){ + x = atof(argv[0]); + p->sum += x; + p->sum2 += x*x; + p->cnt++; + } } static void stdDevFinalize(sqlite_func *context){ double rN = sqlite_aggregate_count(context); StdDevCtx *p = sqlite_aggregate_context(context, sizeof(*p)); - if( p && rN>1.0 ){ + if( p && p->cnt>1 ){ + double rCnt = cnt; sqlite_set_result_double(context, - sqrt((p->sum2 - p->sum*p->sum/rN)/(rN-1.0))); + sqrt((p->sum2 - p->sum*p->sum/rCnt)/(rCnt-1.0))); } } #endif @@ -348,7 +351,6 @@ static void countFinalize(sqlite_func *context){ */ typedef struct MinMaxCtx MinMaxCtx; struct MinMaxCtx { - int isNull; /* True if the result should be NULL */ char *z; /* The best so far */ char zBuf[28]; /* Space that can be used for storage */ }; @@ -359,11 +361,7 @@ struct MinMaxCtx { static void minStep(sqlite_func *context, int argc, const char **argv){ MinMaxCtx *p; p = sqlite_aggregate_context(context, sizeof(*p)); - if( p==0 || argc<1 ) return; - if( argv[0]==0 || p->isNull ){ - p->isNull = 1; - return; - } + if( p==0 || argc<1 || argv[0]==0 ) return; if( sqlite_aggregate_count(context)==1 || sqliteCompare(argv[0],p->z)<0 ){ int len; if( p->z && p->z!=p->zBuf ){ @@ -382,11 +380,7 @@ static void minStep(sqlite_func *context, int argc, const char **argv){ static void maxStep(sqlite_func *context, int argc, const char **argv){ MinMaxCtx *p; p = sqlite_aggregate_context(context, sizeof(*p)); - if( p==0 || argc<1 ) return; - if( argv[0]==0 || p->isNull ){ - p->isNull = 1; - return; - } + if( p==0 || argc<1 || argv[0]==0 ) return; if( sqlite_aggregate_count(context)==1 || sqliteCompare(argv[0],p->z)>0 ){ int len; if( p->z && p->z!=p->zBuf ){ @@ -405,7 +399,7 @@ static void maxStep(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 && !p->isNull ){ + if( p && p->z ){ sqlite_set_result_string(context, p->z, strlen(p->z)); } if( p && p->z && p->z!=p->zBuf ){ @@ -438,6 +432,7 @@ void sqliteRegisterBuildinFunctions(sqlite *db){ { "coalesce", -1, ifnullFunc }, { "coalesce", 0, 0 }, { "coalesce", 1, 0 }, + { "ifnull", 2, ifnullFunc }, { "random", -1, randomFunc }, { "like", 2, likeFunc }, { "glob", 2, globFunc }, diff --git a/test/func.test b/test/func.test index 79821ed166..98e2a97f3c 100644 --- a/test/func.test +++ b/test/func.test @@ -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.11 2002/05/27 01:04:51 drh Exp $ +# $Id: func.test,v 1.12 2002/05/29 23:22:23 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -236,5 +236,13 @@ do_test func-7.1 { execsql {SELECT last_insert_rowid()} } [db last_insert_rowid] +# Tests for aggregate functions and how they handle NULLs. +# +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} + finish_test diff --git a/test/minmax.test b/test/minmax.test index 1355121b1d..8d86f1f775 100644 --- a/test/minmax.test +++ b/test/minmax.test @@ -13,7 +13,7 @@ # aggregate min() and max() functions and which are handled as # as a special case. # -# $Id: minmax.test,v 1.2 2002/05/27 01:04:51 drh Exp $ +# $Id: minmax.test,v 1.3 2002/05/29 23:22:23 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -127,6 +127,6 @@ do_test minmax-4.1 { SELECT coalesce(min(x),-1), coalesce(max(x),-1) FROM (SELECT * FROM t1 UNION SELECT NULL as 'x', NULL as 'y') } -} {-1 -1} +} {1 20} finish_test diff --git a/test/select1.test b/test/select1.test index f3ab52029f..6d3e45d360 100644 --- a/test/select1.test +++ b/test/select1.test @@ -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.28 2002/05/27 03:25:52 drh Exp $ +# $Id: select1.test,v 1.29 2002/05/29 23:22:23 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} -} {xyzzy} +} {11} do_test select1-2.8.2 { execsql {SELECT min(coalesce(a,'xyzzy')) FROM t3} } {11} @@ -188,7 +188,7 @@ do_test select1-2.13 { } {0 34} do_test select1-2.13.1 { execsql {SELECT coalesce(max(a),'xyzzy') FROM t3} -} {xyzzy} +} {abc} do_test select1-2.13.2 { execsql {SELECT max(coalesce(a,'xyzzy')) FROM t3} } {xyzzy}