]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Min() and max() functions honor the distinction between TEXT and NUMERIC
authordrh <drh@noemail.net>
Wed, 25 Feb 2004 13:47:31 +0000 (13:47 +0000)
committerdrh <drh@noemail.net>
Wed, 25 Feb 2004 13:47:31 +0000 (13:47 +0000)
data.  Ticket #623.  typeof() is now a user function.  Some tests are
now failing due to ticket #521. (CVS 1272)

FossilOrigin-Name: adbe31adf1ad0ca723203ca3d7dc480324c60d43

manifest
manifest.uuid
src/copy.c
src/expr.c
src/func.c
src/main.c
src/select.c
src/sqlite.h.in
src/sqliteInt.h
test/minmax.test

index 280c9aef3edf6969a1eb74976a9a5f4ff9855dab..205eb0c57789d61bca1e219b3082f137f5df4bae 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sthe\sreturn\stype\sof\ssqliteRunVacuum.\s\sTicket\s#627.\s(CVS\s1271)
-D 2004-02-25T02:33:35
+C Min()\sand\smax()\sfunctions\shonor\sthe\sdistinction\sbetween\sTEXT\sand\sNUMERIC\ndata.\s\sTicket\s#623.\s\stypeof()\sis\snow\sa\suser\sfunction.\s\sSome\stests\sare\nnow\sfailing\sdue\sto\sticket\s#521.\s(CVS\s1272)
+D 2004-02-25T13:47:31
 F Makefile.in cfd75c46b335881999333a9e4b982fa8491f200b
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -28,16 +28,16 @@ F src/btree.c 0a40efb01fa3a431a16d8604f603431d8c9cebfa
 F src/btree.h 41cb3ff6ebc3f6da2d0a074e39ff8c7a2287469f
 F src/btree_rb.c 32b2cb4285c0fbd53b89de021637b63d52257e54
 F src/build.c c8ab8b467d9a64254b0d4d42083f6313b3a980d1
-F src/copy.c e4dc49e7e1661818c72852e348d0cb0ef2b42bc1
+F src/copy.c 750e13828c3e4a293123e36aaa7cf0f22466248a
 F src/date.c 3025642cee50d5c41aef4a22cbc41aa7e543c922
 F src/delete.c 82001c74882319f94dab5f6b92a27311b31092ae
 F src/encode.c 9e70ea1e4e746f23f18180949e94f1bb1c2220d3
-F src/expr.c 61b71ce2e93b0faca39db9e9c06e9a089d25a04f
-F src/func.c a2265f29e6a286203c9dfeb835d9a50439617805
+F src/expr.c 95ea5d47d11b5085aaeeb77d60b17c2cba13383a
+F src/func.c 424256b469717367f3939725a36a6f3c3d7b5f60
 F src/hash.c 9b56ef3b291e25168f630d5643a4264ec011c70e
 F src/hash.h 3247573ab95b9dd90bcca0307a75d9a16da1ccc7
 F src/insert.c c0485ee2d1b99322894e2d1e0b576fd05ed75616
-F src/main.c 0f77633b37540fabd45e68c5137f32f4cd99470a
+F src/main.c af984c8dbfe769fb88fb0ac70e5f813e50800c1b
 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
 F src/os.c f5fc4954725b2fcd852979f2746085fe8ca27710
 F src/os.h 250a3789be609adfee5c5aa20137ce8683276f24
@@ -47,10 +47,10 @@ F src/parse.y 023720cb8c3bef74e51738bca78335d0dc6d2cfd
 F src/pragma.c 621d319580e9e23712ec232e8be1786cdae06b36
 F src/printf.c f201a5a316afc474d29d51e07501536e8998194d
 F src/random.c 775913e0b7fbd6295d21f12a7bd35b46387c44b2
-F src/select.c 902000034e44817e2822d72870c15eff842dea9e
+F src/select.c a8b0f9bfe92001d2399d33832bd6ec57ba492ae7
 F src/shell.c b19e750ffcccf49b626f4b6fefe89c1dbae47e82
-F src/sqlite.h.in 64f016cd5ce190643a0f47760188fdf4e0b2227e
-F src/sqliteInt.h 1ef4dcb7a5525c91e3d338f9435c84930c11aeb2
+F src/sqlite.h.in 8a83091fbbbd73d30a9743310ed0089e3f1fda0f
+F src/sqliteInt.h 235ce244b62bb26cc9ab394fb7a0724dd4e65c83
 F src/table.c d845cb101b5afc1f7fea083c99e3d2fa7998d895
 F src/tclsqlite.c b84dafe3a8532ff534c36e96bd38880e4b9cedf3
 F src/test1.c 9aa62b89d420e6763b5e7ae89a47f6cf87370477
@@ -108,7 +108,7 @@ F test/main.test 6a851b5992c4881a725a3d9647e629199df8de9d
 F test/malloc.test 2cfcffb7c858640e01e6520ee1cd54ca57d98e80
 F test/memdb.test 6ece25c7c0e6500199d3662607a3edca081abb2a
 F test/memleak.test 4d5d374c8ea1fc5ac634aed58cac1047848ce65e
-F test/minmax.test 6680b8d79b9b6e026a476ebfb91f310f7774568e
+F test/minmax.test d7da9183013ac814a5b032b3542f9caf4c88af42
 F test/misc1.test 0b98d493b0cf55cb5f53e1f3df8107c166eecb5a
 F test/misc2.test 10c2ce26407d37411b96273e552d5095393732be
 F test/misc3.test bd371567b6fec7c1d7fe42a172a551226d271dd2
@@ -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 114b72f8608b8e08fad863a1446fb1ef59610efd
-R e0dfbd5e11231f51ee512fc59088c2fb
+P 9c9f4a867a4ea708847cbb0839b7279eb33ecd7c
+R 5415532acf52c246928d5fc32e241eec
 U drh
-Z ea543ed6ac4d345acdce58082a3273de
+Z 719381690dd478a52c92cb1074f23dac
index 9054a2c06eef06907437b28d0510278379c8c55e..2f6ee2c2c90d10f24190634a7f37bb0eef24554d 100644 (file)
@@ -1 +1 @@
-9c9f4a867a4ea708847cbb0839b7279eb33ecd7c
\ No newline at end of file
+adbe31adf1ad0ca723203ca3d7dc480324c60d43
\ No newline at end of file
index 140e6a9f842fbda88aedf1520d58fb31370767ae..b712b5b731122f0a7515efa7bff0c8580945f0e2 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the COPY command.
 **
-** $Id: copy.c,v 1.8 2004/02/24 01:05:32 drh Exp $
+** $Id: copy.c,v 1.9 2004/02/25 13:47:31 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -37,7 +37,6 @@ void sqliteCopy(
   int i;
   Vdbe *v;
   int addr, end;
-  Index *pIdx;
   char *zFile = 0;
   const char *zDb;
   sqlite *db = pParse->db;
index 7b96ad3f9051ac559eaf7347ed76a10b77220382..dca48e35e32fe0857515b910f04550f19ad801d5 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.111 2004/02/22 20:05:01 drh Exp $
+** $Id: expr.c,v 1.112 2004/02/25 13:47:31 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -823,7 +823,6 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
     case TK_FUNCTION: {
       int n = pExpr->pList ? pExpr->pList->nExpr : 0;  /* Number of arguments */
       int no_such_func = 0;       /* True if no such function exists */
-      int is_type_of = 0;         /* True if is the special TypeOf() function */
       int wrong_num_args = 0;     /* True if wrong number of arguments */
       int is_agg = 0;             /* True if is an aggregate function */
       int i;
@@ -836,11 +835,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
       if( pDef==0 ){
         pDef = sqliteFindFunction(pParse->db, zId, nId, -1, 0);
         if( pDef==0 ){
-          if( n==1 && nId==6 && sqliteStrNICmp(zId, "typeof", 6)==0 ){
-            is_type_of = 1;
-          }else {
-            no_such_func = 1;
-          }
+          no_such_func = 1;
         }else{
           wrong_num_args = 1;
         }
@@ -868,16 +863,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
                                allowAgg && !is_agg, pIsAgg);
       }
       if( pDef==0 ){
-        if( is_type_of ){
-          pExpr->op = TK_STRING;
-          if( sqliteExprType(pExpr->pList->a[0].pExpr)==SQLITE_SO_NUM ){
-            pExpr->token.z = "numeric";
-            pExpr->token.n = 7;
-          }else{
-            pExpr->token.z = "text";
-            pExpr->token.n = 4;
-          }
-        }
+        /* Already reported an error */
       }else if( pDef->dataType>=0 ){
         if( pDef->dataType<n ){
           pExpr->dataType = 
@@ -1155,7 +1141,6 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
     case TK_GLOB:
     case TK_LIKE:
     case TK_FUNCTION: {
-      int i;
       ExprList *pList = pExpr->pList;
       int nExpr = pList ? pList->nExpr : 0;
       FuncDef *pDef;
@@ -1164,9 +1149,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
       getFunctionName(pExpr, &zId, &nId);
       pDef = sqliteFindFunction(pParse->db, zId, nId, nExpr, 0);
       assert( pDef!=0 );
-      for(i=0; i<nExpr; i++){
-        sqliteExprCode(pParse, pList->a[i].pExpr);
-      }
+      nExpr = sqliteExprCodeExprList(pParse, pList, pDef->includeTypes);
       sqliteVdbeOp3(v, OP_Function, nExpr, 0, (char*)pDef, P3_POINTER);
       break;
     }
@@ -1270,6 +1253,36 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
   }
 }
 
+/*
+** Generate code that pushes the value of every element of the given
+** expression list onto the stack.  If the includeTypes flag is true,
+** then also push a string that is the datatype of each element onto
+** the stack after the value.
+**
+** Return the number of elements pushed onto the stack.
+*/
+int sqliteExprCodeExprList(
+  Parse *pParse,     /* Parsing context */
+  ExprList *pList,   /* The expression list to be coded */
+  int includeTypes   /* TRUE to put datatypes on the stack too */
+){
+  struct ExprList_item *pItem;
+  int i, n;
+  Vdbe *v;
+  if( pList==0 ) return 0;
+  v = sqliteGetVdbe(pParse);
+  n = pList->nExpr;
+  for(pItem=pList->a, i=0; i<n; i++, pItem++){
+    sqliteExprCode(pParse, pItem->pExpr);
+    if( includeTypes ){
+      sqliteVdbeOp3(v, OP_String, 0, 0, 
+         sqliteExprType(pItem->pExpr)==SQLITE_SO_NUM ? "numeric" : "text",
+         P3_STATIC);
+    }
+  }
+  return includeTypes ? n*2 : n;
+}
+
 /*
 ** Generate code for a boolean expression such that a jump is made
 ** to the label "dest" if the expression is true but execution
index e5c1761e176a4340f4e7a7fe7c15ba1a507526a0..fd581f8a168ec04b141b384302305ecc23c8e3d1 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.41 2004/02/22 17:49:34 drh Exp $
+** $Id: func.c,v 1.42 2004/02/25 13:47:32 drh Exp $
 */
 #include <ctype.h>
 #include <math.h>
 /*
 ** Implementation of the non-aggregate min() and max() functions
 */
-static void minFunc(sqlite_func *context, int argc, const char **argv){
+static void minmaxFunc(sqlite_func *context, int argc, const char **argv){
   const char *zBest; 
   int i;
+  int (*xCompare)(const char*, const char*);
+  int mask;    /* 0 for min() or 0xffffffff for max() */
 
   if( argc==0 ) return;
+  mask = (int)sqlite_user_data(context);
   zBest = argv[0];
   if( zBest==0 ) return;
-  for(i=1; i<argc; i++){
+  if( argv[1][0]=='n' ){
+    xCompare = sqliteCompare;
+  }else{
+    xCompare = strcmp;
+  }
+  for(i=2; i<argc; i+=2){
     if( argv[i]==0 ) return;
-    if( sqliteCompare(argv[i], zBest)<0 ){
+    if( (xCompare(argv[i], zBest)^mask)<0 ){
       zBest = argv[i];
     }
   }
   sqlite_set_result_string(context, zBest, -1);
 }
-static void maxFunc(sqlite_func *context, int argc, const char **argv){
-  const char *zBest; 
-  int i;
 
-  if( argc==0 ) return;
-  zBest = argv[0];
-  if( zBest==0 ) return;
-  for(i=1; i<argc; i++){
-    if( argv[i]==0 ) return;
-    if( sqliteCompare(argv[i], zBest)>0 ){
-      zBest = argv[i];
-    }
-  }
-  sqlite_set_result_string(context, zBest, -1);
+/*
+** Return the type of the argument.
+*/
+static void typeofFunc(sqlite_func *context, int argc, const char **argv){
+  assert( argc==2 );
+  sqlite_set_result_string(context, argv[1], -1);
 }
 
 /*
@@ -501,32 +502,21 @@ struct MinMaxCtx {
 /*
 ** Routines to implement min() and max() aggregate functions.
 */
-static void minStep(sqlite_func *context, int argc, const char **argv){
+static void minmaxStep(sqlite_func *context, int argc, const char **argv){
   MinMaxCtx *p;
-  p = sqlite_aggregate_context(context, sizeof(*p));
-  if( p==0 || argc<1 || argv[0]==0 ) return;
-  if( p->z==0 || sqliteCompare(argv[0],p->z)<0 ){
-    int len;
-    if( !p->zBuf[0] ){
-      sqliteFree(p->z);
-    }
-    len = strlen(argv[0]);
-    if( len < sizeof(p->zBuf)-1 ){
-      p->z = &p->zBuf[1];
-      p->zBuf[0] = 1;
-    }else{
-      p->z = sqliteMalloc( len+1 );
-      p->zBuf[0] = 0;
-      if( p->z==0 ) return;
-    }
-    strcpy(p->z, argv[0]);
+  int (*xCompare)(const char*, const char*);
+  int mask;    /* 0 for min() or 0xffffffff for max() */
+
+  assert( argc==2 );
+  if( argv[1][0]=='n' ){
+    xCompare = sqliteCompare;
+  }else{
+    xCompare = strcmp;
   }
-}
-static void maxStep(sqlite_func *context, int argc, const char **argv){
-  MinMaxCtx *p;
+  mask = (int)sqlite_user_data(context);
   p = sqlite_aggregate_context(context, sizeof(*p));
   if( p==0 || argc<1 || argv[0]==0 ) return;
-  if( p->z==0 || sqliteCompare(argv[0],p->z)>0 ){
+  if( p->z==0 || (xCompare(argv[0],p->z)^mask)<0 ){
     int len;
     if( !p->zBuf[0] ){
       sqliteFree(p->z);
@@ -562,77 +552,86 @@ static void minMaxFinalize(sqlite_func *context){
 void sqliteRegisterBuiltinFunctions(sqlite *db){
   static struct {
      char *zName;
-     int nArg;
-     int dataType;
+     signed char nArg;
+     signed char dataType;
+     u8 argType;               /* 0: none.  1: db  2: (-1) */
      void (*xFunc)(sqlite_func*,int,const char**);
   } aFuncs[] = {
-    { "min",       -1, SQLITE_ARGS,    minFunc    },
-    { "min",        0, 0,              0          },
-    { "max",       -1, SQLITE_ARGS,    maxFunc    },
-    { "max",        0, 0,              0          },
-    { "length",     1, SQLITE_NUMERIC, lengthFunc },
-    { "substr",     3, SQLITE_TEXT,    substrFunc },
-    { "abs",        1, SQLITE_NUMERIC, absFunc    },
-    { "round",      1, SQLITE_NUMERIC, roundFunc  },
-    { "round",      2, SQLITE_NUMERIC, roundFunc  },
-    { "upper",      1, SQLITE_TEXT,    upperFunc  },
-    { "lower",      1, SQLITE_TEXT,    lowerFunc  },
-    { "coalesce",  -1, SQLITE_ARGS,    ifnullFunc },
-    { "coalesce",   0, 0,              0          },
-    { "coalesce",   1, 0,              0          },
-    { "ifnull",     2, SQLITE_ARGS,    ifnullFunc },
-    { "random",    -1, SQLITE_NUMERIC, randomFunc },
-    { "like",       2, SQLITE_NUMERIC, likeFunc   },
-    { "glob",       2, SQLITE_NUMERIC, globFunc   },
-    { "nullif",     2, SQLITE_ARGS,    nullifFunc },
-    { "sqlite_version",0,SQLITE_TEXT,  versionFunc},
-    { "quote",      1, SQLITE_ARGS,    quoteFunc  },
+    { "min",       -1, SQLITE_ARGS,    0, minmaxFunc },
+    { "min",        0, 0,              0, 0          },
+    { "max",       -1, SQLITE_ARGS,    2, minmaxFunc },
+    { "max",        0, 0,              2, 0          },
+    { "typeof",     1, SQLITE_TEXT,    0, typeofFunc },
+    { "length",     1, SQLITE_NUMERIC, 0, lengthFunc },
+    { "substr",     3, SQLITE_TEXT,    0, substrFunc },
+    { "abs",        1, SQLITE_NUMERIC, 0, absFunc    },
+    { "round",      1, SQLITE_NUMERIC, 0, roundFunc  },
+    { "round",      2, SQLITE_NUMERIC, 0, roundFunc  },
+    { "upper",      1, SQLITE_TEXT,    0, upperFunc  },
+    { "lower",      1, SQLITE_TEXT,    0, lowerFunc  },
+    { "coalesce",  -1, SQLITE_ARGS,    0, ifnullFunc },
+    { "coalesce",   0, 0,              0, 0          },
+    { "coalesce",   1, 0,              0, 0          },
+    { "ifnull",     2, SQLITE_ARGS,    0, ifnullFunc },
+    { "random",    -1, SQLITE_NUMERIC, 0, randomFunc },
+    { "like",       2, SQLITE_NUMERIC, 0, likeFunc   },
+    { "glob",       2, SQLITE_NUMERIC, 0, globFunc   },
+    { "nullif",     2, SQLITE_ARGS,    0, nullifFunc },
+    { "sqlite_version",0,SQLITE_TEXT,  0, versionFunc},
+    { "quote",      1, SQLITE_ARGS,    0, quoteFunc  },
+    { "last_insert_rowid", 0, SQLITE_NUMERIC, 1, last_insert_rowid },
+    { "change_count",      0, SQLITE_NUMERIC, 1, change_count      },
+    { "last_statement_change_count",
+                           0, SQLITE_NUMERIC, 1, last_statement_change_count },
 #ifdef SQLITE_SOUNDEX
-    { "soundex",    1, SQLITE_TEXT,    soundexFunc},
+    { "soundex",    1, SQLITE_TEXT,    0, soundexFunc},
 #endif
 #ifdef SQLITE_TEST
-    { "randstr",    2, SQLITE_TEXT,    randStr    },
+    { "randstr",    2, SQLITE_TEXT,    0, randStr    },
 #endif
   };
   static struct {
     char *zName;
-    int nArg;
-    int dataType;
+    signed char nArg;
+    signed char dataType;
+    u8 argType;
     void (*xStep)(sqlite_func*,int,const char**);
     void (*xFinalize)(sqlite_func*);
   } aAggs[] = {
-    { "min",    1, 0,              minStep,      minMaxFinalize },
-    { "max",    1, 0,              maxStep,      minMaxFinalize },
-    { "sum",    1, SQLITE_NUMERIC, sumStep,      sumFinalize    },
-    { "avg",    1, SQLITE_NUMERIC, sumStep,      avgFinalize    },
-    { "count",  0, SQLITE_NUMERIC, countStep,    countFinalize  },
-    { "count",  1, SQLITE_NUMERIC, countStep,    countFinalize  },
+    { "min",    1, 0,              0, minmaxStep,   minMaxFinalize },
+    { "max",    1, 0,              2, minmaxStep,   minMaxFinalize },
+    { "sum",    1, SQLITE_NUMERIC, 0, sumStep,      sumFinalize    },
+    { "avg",    1, SQLITE_NUMERIC, 0, sumStep,      avgFinalize    },
+    { "count",  0, SQLITE_NUMERIC, 0, countStep,    countFinalize  },
+    { "count",  1, SQLITE_NUMERIC, 0, countStep,    countFinalize  },
 #if 0
-    { "stddev", 1, SQLITE_NUMERIC, stdDevStep,   stdDevFinalize },
+    { "stddev", 1, SQLITE_NUMERIC, 0, stdDevStep,   stdDevFinalize },
 #endif
   };
+  static const char *azTypeFuncs[] = { "min", "max", "typeof" };
   int i;
 
   for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
+    void *pArg = aFuncs[i].argType==2 ? (void*)(-1) : db;
     sqlite_create_function(db, aFuncs[i].zName,
-           aFuncs[i].nArg, aFuncs[i].xFunc, 0);
+           aFuncs[i].nArg, aFuncs[i].xFunc, pArg);
     if( aFuncs[i].xFunc ){
       sqlite_function_type(db, aFuncs[i].zName, aFuncs[i].dataType);
     }
   }
-  sqlite_create_function(db, "last_insert_rowid", 0, 
-           last_insert_rowid, db);
-  sqlite_function_type(db, "last_insert_rowid", SQLITE_NUMERIC);
-  sqlite_create_function(db, "change_count", 0, change_count, db);
-  sqlite_function_type(db, "change_count", SQLITE_NUMERIC);
-  sqlite_create_function(db, "last_statement_change_count", 0, 
-           last_statement_change_count, db);
-  sqlite_function_type(db, "last_statement_change_count", SQLITE_NUMERIC);
-
   for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
+    void *pArg = aAggs[i].argType==2 ? (void*)(-1) : db;
     sqlite_create_aggregate(db, aAggs[i].zName,
-           aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0);
+           aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, pArg);
     sqlite_function_type(db, aAggs[i].zName, aAggs[i].dataType);
   }
+  for(i=0; i<sizeof(azTypeFuncs)/sizeof(azTypeFuncs[0]); i++){
+    int n = strlen(azTypeFuncs[i]);
+    FuncDef *p = sqliteHashFind(&db->aFunc, azTypeFuncs[i], n);
+    while( p ){
+      p->includeTypes = 1;
+      p = p->pNext;
+    }
+  }
   sqliteRegisterDateTimeFunctions(db);
 }
index bd62f07530a7f4d4f15f45270251dfbd85c6ed23..ea1e6df8b7d403a5708e0c7a3c7dc90cf8600655 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.159 2004/02/20 23:34:07 drh Exp $
+** $Id: main.c,v 1.160 2004/02/25 13:47:32 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -953,7 +953,7 @@ const char *sqlite_libencoding(void){ return sqlite_encoding; }
 ** sqlite_create_aggregate(), and vice versa.
 **
 ** If nArg is -1 it means that this function will accept any number
-** of arguments, including 0.
+** of arguments, including 0.  The maximum allowed value of nArg is 127.
 */
 int sqlite_create_function(
   sqlite *db,          /* Add the function to this database connection */
@@ -965,6 +965,7 @@ int sqlite_create_function(
   FuncDef *p;
   int nName;
   if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1;
+  if( nArg<-1 || nArg>127 ) return 1;
   nName = strlen(zName);
   if( nName>255 ) return 1;
   p = sqliteFindFunction(db, zName, nName, nArg, 1);
@@ -986,6 +987,7 @@ int sqlite_create_aggregate(
   FuncDef *p;
   int nName;
   if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1;
+  if( nArg<-1 || nArg>127 ) return 1;
   nName = strlen(zName);
   if( nName>255 ) return 1;
   p = sqliteFindFunction(db, zName, nName, nArg, 1);
index 1a8886686a1177278cd18da68238017f9a5aacb5..01949aac8d254567be2dd979e9d634a1b2854c59 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.158 2004/02/22 20:05:01 drh Exp $
+** $Id: select.c,v 1.159 2004/02/25 13:47:33 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -2309,6 +2309,7 @@ int sqliteSelect(
   ** processing.  
   */
   else{
+    AggExpr *pAgg;
     if( pGroupBy ){
       int lbl1;
       for(i=0; i<pGroupBy->nExpr; i++){
@@ -2318,29 +2319,27 @@ int sqliteSelect(
       if( pParse->db->file_format>=4 ) sqliteAddKeyType(v, pGroupBy);
       lbl1 = sqliteVdbeMakeLabel(v);
       sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1);
-      for(i=0; i<pParse->nAgg; i++){
-        if( pParse->aAgg[i].isAgg ) continue;
-        sqliteExprCode(pParse, pParse->aAgg[i].pExpr);
+      for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
+        if( pAgg->isAgg ) continue;
+        sqliteExprCode(pParse, pAgg->pExpr);
         sqliteVdbeAddOp(v, OP_AggSet, 0, i);
       }
       sqliteVdbeResolveLabel(v, lbl1);
     }
-    for(i=0; i<pParse->nAgg; i++){
+    for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
       Expr *pE;
-      int j;
-      if( !pParse->aAgg[i].isAgg ) continue;
-      pE = pParse->aAgg[i].pExpr;
+      int nExpr;
+      FuncDef *pDef;
+      if( !pAgg->isAgg ) continue;
+      assert( pAgg->pFunc!=0 );
+      assert( pAgg->pFunc->xStep!=0 );
+      pDef = pAgg->pFunc;
+      pE = pAgg->pExpr;
+      assert( pE!=0 );
       assert( pE->op==TK_AGG_FUNCTION );
-      if( pE->pList ){
-        for(j=0; j<pE->pList->nExpr; j++){
-          sqliteExprCode(pParse, pE->pList->a[j].pExpr);
-        }
-      }
+      nExpr = sqliteExprCodeExprList(pParse, pE->pList, pDef->includeTypes);
       sqliteVdbeAddOp(v, OP_Integer, i, 0);
-      sqliteVdbeAddOp(v, OP_AggFunc, 0, pE->pList ? pE->pList->nExpr : 0);
-      assert( pParse->aAgg[i].pFunc!=0 );
-      assert( pParse->aAgg[i].pFunc->xStep!=0 );
-      sqliteVdbeChangeP3(v, -1, (char*)pParse->aAgg[i].pFunc, P3_POINTER);
+      sqliteVdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_POINTER);
     }
   }
 
index 32946ce6cd0963b72a6b16b6d5212608a8def930..32c3ba8ab89b31d416cf483984818d25b488e341 100644 (file)
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the SQLite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.57 2004/02/12 20:49:36 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.58 2004/02/25 13:47:33 drh Exp $
 */
 #ifndef _SQLITE_H_
 #define _SQLITE_H_
@@ -440,13 +440,12 @@ int sqlite_create_aggregate(
 ** Use the following routine to define the datatype returned by a
 ** user-defined function.  The second argument can be one of the
 ** constants SQLITE_NUMERIC, SQLITE_TEXT, or SQLITE_ARGS or it
-** can be an integer greater than or equal to zero.  The datatype
-** will be numeric or text (the only two types supported) if the
-** argument is SQLITE_NUMERIC or SQLITE_TEXT.  If the argument is
-** SQLITE_ARGS, then the datatype is numeric if any argument to the
-** function is numeric and is text otherwise.  If the second argument
-** is an integer, then the datatype of the result is the same as the
-** parameter to the function that corresponds to that integer.
+** can be an integer greater than or equal to zero.  When the datatype
+** parameter is non-negative, the type of the result will be the
+** same as the datatype-th argument.  If datatype==SQLITE_NUMERIC
+** then the result is always numeric.  If datatype==SQLITE_TEXT then
+** the result is always text.  If datatype==SQLITE_ARGS then the result
+** is numeric if any argument is numeric and is text otherwise.
 */
 int sqlite_function_type(
   sqlite *db,               /* The database there the function is registered */
index 60d4bc547163a3dbfb314870ddac45e37a8ea21e..4c2b6434005a3035e64473cdc0e50902961e8690 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.219 2004/02/24 01:05:33 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.220 2004/02/25 13:47:33 drh Exp $
 */
 #include "config.h"
 #include "sqlite.h"
@@ -423,10 +423,12 @@ struct FuncDef {
   void (*xFunc)(sqlite_func*,int,const char**);  /* Regular function */
   void (*xStep)(sqlite_func*,int,const char**);  /* Aggregate function step */
   void (*xFinalize)(sqlite_func*);           /* Aggregate function finializer */
-  int nArg;                                  /* Number of arguments */
-  int dataType;                              /* Datatype of the result */
-  void *pUserData;                           /* User data parameter */
-  FuncDef *pNext;                            /* Next function with same name */
+  signed char nArg;         /* Number of arguments.  -1 means unlimited */
+  signed char dataType;     /* Arg that determines datatype.  -1=NUMERIC, */
+                            /* -2=TEXT. -3=SQLITE_ARGS */
+  u8 includeTypes;          /* Add datatypes to args of xFunc and xStep */
+  void *pUserData;          /* User data parameter */
+  FuncDef *pNext;           /* Next function with same name */
 };
 
 /*
@@ -1172,6 +1174,7 @@ void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int);
 WhereInfo *sqliteWhereBegin(Parse*, SrcList*, Expr*, int, ExprList**);
 void sqliteWhereEnd(WhereInfo*);
 void sqliteExprCode(Parse*, Expr*);
+int sqliteExprCodeExprList(Parse*, ExprList*, int);
 void sqliteExprIfTrue(Parse*, Expr*, int, int);
 void sqliteExprIfFalse(Parse*, Expr*, int, int);
 Table *sqliteFindTable(sqlite*,const char*, const char*);
index 9adb23a77eab352c9381b482f7969f7d0dbdecd0..28c2946aa2b028fb0feaa5ff9541a5d0ade93a84 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.7 2004/01/30 02:01:05 drh Exp $
+# $Id: minmax.test,v 1.8 2004/02/25 13:47:34 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -124,7 +124,7 @@ do_test minmax-3.3 {
 
 do_test minmax-4.1 {
   execsql {
-    SELECT coalesce(min(x),-1), coalesce(max(x),-1) FROM
+    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}
@@ -236,5 +236,27 @@ do_test minmax-7.4 {
   }
 } 1
 
+# Make sure min(x) and max(x) work correctly when the datatype is
+# TEXT instead of NUMERIC.  Ticket #623.
+#
+do_test minmax-8.1 {
+  execsql {
+    CREATE TABLE t4(a TEXT);
+    INSERT INTO t4 VALUES('1234');
+    INSERT INTO t4 VALUES('234');
+    INSERT INTO t4 VALUES('34');
+    SELECT min(a), max(a) FROM t4;
+  }
+} {1234 34}
+do_test minmax-8.2 {
+  execsql {
+    CREATE TABLE t5(a INTEGER);
+    INSERT INTO t5 VALUES('1234');
+    INSERT INTO t5 VALUES('234');
+    INSERT INTO t5 VALUES('34');
+    SELECT min(a), max(a) FROM t5;
+  }
+} {34 1234}
+
 
 finish_test