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:
26
manifest
26
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
|
||||
|
@@ -1 +1 @@
|
||||
9c9f4a867a4ea708847cbb0839b7279eb33ecd7c
|
||||
adbe31adf1ad0ca723203ca3d7dc480324c60d43
|
@@ -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;
|
||||
|
53
src/expr.c
53
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 <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
|
||||
|
165
src/func.c
165
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 <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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
33
src/select.c
33
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; 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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*);
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user