From: drh Date: Wed, 25 Feb 2004 13:47:31 +0000 (+0000) Subject: Min() and max() functions honor the distinction between TEXT and NUMERIC X-Git-Tag: version-3.6.10~4796 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=268380ca9ed4e5c1ac172f9650d6192bafd0346d;p=thirdparty%2Fsqlite.git Min() and max() functions honor the distinction between TEXT and NUMERIC data. Ticket #623. typeof() is now a user function. Some tests are now failing due to ticket #521. (CVS 1272) FossilOrigin-Name: adbe31adf1ad0ca723203ca3d7dc480324c60d43 --- diff --git a/manifest b/manifest index 280c9aef3e..205eb0c577 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 9054a2c06e..2f6ee2c2c9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9c9f4a867a4ea708847cbb0839b7279eb33ecd7c \ No newline at end of file +adbe31adf1ad0ca723203ca3d7dc480324c60d43 \ No newline at end of file diff --git a/src/copy.c b/src/copy.c index 140e6a9f84..b712b5b731 100644 --- a/src/copy.c +++ b/src/copy.c @@ -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; diff --git a/src/expr.c b/src/expr.c index 7b96ad3f90..dca48e35e3 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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 @@ -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->dataTypedataType = @@ -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; ia[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; ipExpr); + 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 diff --git a/src/func.c b/src/func.c index e5c1761e17..fd581f8a16 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.41 2004/02/22 17:49:34 drh Exp $ +** $Id: func.c,v 1.42 2004/02/25 13:47:32 drh Exp $ */ #include #include @@ -28,35 +28,36 @@ /* ** 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; i0 ){ - 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; iaFunc, azTypeFuncs[i], n); + while( p ){ + p->includeTypes = 1; + p = p->pNext; + } + } sqliteRegisterDateTimeFunctions(db); } diff --git a/src/main.c b/src/main.c index bd62f07530..ea1e6df8b7 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/select.c b/src/select.c index 1a8886686a..01949aac8d 100644 --- a/src/select.c +++ b/src/select.c @@ -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; inExpr; 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; inAgg; i++){ - if( pParse->aAgg[i].isAgg ) continue; - sqliteExprCode(pParse, pParse->aAgg[i].pExpr); + for(i=0, pAgg=pParse->aAgg; inAgg; i++, pAgg++){ + if( pAgg->isAgg ) continue; + sqliteExprCode(pParse, pAgg->pExpr); sqliteVdbeAddOp(v, OP_AggSet, 0, i); } sqliteVdbeResolveLabel(v, lbl1); } - for(i=0; inAgg; i++){ + for(i=0, pAgg=pParse->aAgg; inAgg; 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; jpList->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); } } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 32946ce6cd..32c3ba8ab8 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -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 */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 60d4bc5471..4c2b643400 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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*); diff --git a/test/minmax.test b/test/minmax.test index 9adb23a77e..28c2946aa2 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.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