1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

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
This commit is contained in:
drh
2004-02-25 13:47:31 +00:00
parent d41d73d556
commit 268380ca9e
10 changed files with 191 additions and 155 deletions

View File

@@ -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

View File

@@ -1 +1 @@
9c9f4a867a4ea708847cbb0839b7279eb33ecd7c
adbe31adf1ad0ca723203ca3d7dc480324c60d43

View 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;

View 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;
}
}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

View 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>
@@ -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; 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;
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{
p->z = sqliteMalloc( len+1 );
p->zBuf[0] = 0;
if( p->z==0 ) return;
xCompare = strcmp;
}
strcpy(p->z, argv[0]);
}
}
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);
}

View 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);

View 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);
}
}

View 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 */

View 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,8 +423,10 @@ 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 */
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*);

View 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