From 0bce8354b4a4cc53a32a548108477cb3b33f2d0a Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 28 Feb 2002 00:41:10 +0000 Subject: [PATCH] Completely remove the old SQL function system and replace it with the new user functions. The code currently compiles but it coredumps on the test suite. Do not use in its present state. (CVS 400) FossilOrigin-Name: 50797fee5066ec9ea23b720e5ab7e8fc8ccc1988 --- manifest | 26 ++--- manifest.uuid | 2 +- src/expr.c | 221 +++++------------------------------------ src/func.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++-- src/main.c | 20 ++-- src/select.c | 69 +++++-------- src/sqliteInt.h | 34 ++----- src/util.c | 12 ++- src/vdbe.c | 52 +++++----- src/vdbe.h | 4 +- 10 files changed, 374 insertions(+), 325 deletions(-) diff --git a/manifest b/manifest index 76b44c7fac..08cd03cf2e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Try\sto\sreduce\sthe\snumber\sof\smalloc()\sfor\suser-defined\sfunctions.\s\sBegin\ntransfering\sbuilt-in\sfunctions\sover\sto\sthe\suser-define\sfunction\nmechanism.\s(CVS\s399) -D 2002-02-27T19:50:59 +C Completely\sremove\sthe\sold\sSQL\sfunction\ssystem\sand\sreplace\sit\swith\sthe\nnew\suser\sfunctions.\s\sThe\scode\scurrently\scompiles\sbut\sit\scoredumps\son\sthe\ntest\ssuite.\s\sDo\snot\suse\sin\sits\spresent\sstate.\s(CVS\s400) +D 2002-02-28T00:41:10 F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -23,12 +23,12 @@ F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 F src/build.c 7ada2426caba70cb1072ba268bedb694b5018065 F src/delete.c 950d8f9097361419f1963875f9943344b469cf02 -F src/expr.c ea2209a6bdefea23db988b470fc16dbf9d5b3a5a -F src/func.c 3aeff18b277d9fdece117497dfff8119113ea412 +F src/expr.c 32a2b5826b892a61e153df0ff5778e01d1ba9ad9 +F src/func.c 709784ce09c6156c52cbf860f6bcc7c2349d0b65 F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 F src/insert.c 164d2d5e943268a8ff0594e1947599e04df0ce11 -F src/main.c 98f6c6f288e8c9e986634504d9882f348a56edb6 +F src/main.c 5651146585ae613e759fcf372ee064e4940c2463 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6 @@ -37,11 +37,11 @@ F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283 F src/parse.y fc460cda6f475beae963c7f9c737cf13f44f3420 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c a8e90e8f8366b72830010dd6a9bfbf121d2e9370 +F src/select.c 0aeaf678ba3621b0bb11c220892321d29cfe15aa F src/shell.c 9f8249ca5b8f8aad40becd778c151b58c0d6109e F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in a9b5772604265f98f3120573ef29e37b9d917216 -F src/sqliteInt.h 4e746aa13c5fa39a31a89b73afcd33fa164e8b4f +F src/sqliteInt.h 9cd512d5be2d2838950e5ace7f92e14ab4804e70 F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f @@ -50,9 +50,9 @@ F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af F src/update.c 18971d265b0341574b7e3f73116e7947ddab0997 -F src/util.c 40a12067fa7b18c7e0461b3256b1353c755b59ac -F src/vdbe.c d1efbaf24a745cf895432baf7642846e34a60f40 -F src/vdbe.h 785b2f175f8be2af285d137125f30042ce8a53b5 +F src/util.c 1c01f0a54a77ae9fb638d026d18093ee1b61e3b3 +F src/vdbe.c 1c16db3ba6222e3ae04cf4461305c6828ff6d018 +F src/vdbe.h 8ab845e63e196e8eb5e51ce69f43b233e51db07e F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585 F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 @@ -127,7 +127,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 633951f0fa11c91f93aa2862df84691750c01e73 -R 1ad564fc9fb52e423ae79061556e1e37 +P c4f9e017b449d4036fa8d2bf77b931d4c31d74f7 +R 8c438145064b7ce85d82d8ac4ca5e8e8 U drh -Z 9b3029f554c840c37489da7d32038cb8 +Z 324db426e1592b4f7c305e457c298b2e diff --git a/manifest.uuid b/manifest.uuid index 1ea5eee158..df3abacc50 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4f9e017b449d4036fa8d2bf77b931d4c31d74f7 \ No newline at end of file +50797fee5066ec9ea23b720e5ab7e8fc8ccc1988 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 563291c983..ccfc6833d9 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.48 2002/02/27 19:00:21 drh Exp $ +** $Id: expr.c,v 1.49 2002/02/28 00:41:10 drh Exp $ */ #include "sqliteInt.h" @@ -631,47 +631,6 @@ int sqliteExprResolveIds( return 0; } -#if 0 /* NOT USED */ -/* -** Compare a token against a string. Return TRUE if they match. -*/ -static int sqliteTokenCmp(Token *pToken, const char *zStr){ - int n = strlen(zStr); - if( n!=pToken->n ) return 0; - return sqliteStrNICmp(pToken->z, zStr, n)==0; -} -#endif - -/* -** Convert a function name into its integer identifier. Return the -** identifier. Return FN_Unknown if the function name is unknown. -*/ -int sqliteFuncId(Token *pToken){ - static const struct { - char *zName; - int len; - int id; - } aFunc[] = { - { "count", 5, FN_Count }, - { "min", 3, FN_Min }, - { "max", 3, FN_Max }, - { "sum", 3, FN_Sum }, - { "avg", 3, FN_Avg }, - { "length", 6, FN_Length }, - { "substr", 6, FN_Substr }, - { "abs", 3, FN_Abs }, - { "round", 5, FN_Round }, - }; - int i; - for(i=0; in - && sqliteStrNICmp(pToken->z, aFunc[i].zName, aFunc[i].len)==0 ){ - return aFunc[i].id; - } - } - return FN_Unknown; -} - /* ** Error check the functions in an expression. Make sure all ** function names are recognized and all functions have the correct @@ -686,67 +645,24 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ if( pExpr==0 ) return 0; switch( pExpr->op ){ case TK_FUNCTION: { - int id = sqliteFuncId(&pExpr->token); int n = pExpr->pList ? pExpr->pList->nExpr : 0; int no_such_func = 0; - int too_many_args = 0; - int too_few_args = 0; int wrong_num_args = 0; int is_agg = 0; int i; - pExpr->iColumn = id; - switch( id ){ - case FN_Unknown: { - UserFunc *pUser = sqliteFindUserFunction(pParse->db, - pExpr->token.z, pExpr->token.n, n, 0); - if( pUser==0 ){ - pUser = sqliteFindUserFunction(pParse->db, - pExpr->token.z, pExpr->token.n, -1, 0); - if( pUser==0 ){ - no_such_func = 1; - }else{ - wrong_num_args = 1; - } - }else{ - is_agg = pUser->xFunc==0; - } - break; + FuncDef *pDef; + + pDef = sqliteFindFunction(pParse->db, pExpr->token.z, pExpr->token.n,n,0); + if( pDef==0 ){ + pDef = sqliteFindFunction(pParse->db, + pExpr->token.z, pExpr->token.n, -1, 0); + if( pDef==0 ){ + no_such_func = 1; + }else{ + wrong_num_args = 1; } - case FN_Count: { - too_many_args = n>1; - is_agg = 1; - break; - } - case FN_Max: - case FN_Min: { - too_few_args = n<1; - is_agg = n==1; - break; - } - case FN_Avg: - case FN_Sum: { - too_many_args = n>1; - too_few_args = n<1; - is_agg = 1; - break; - } - case FN_Abs: - case FN_Length: { - too_few_args = n<1; - too_many_args = n>1; - break; - } - case FN_Round: { - too_few_args = n<1; - too_many_args = n>2; - break; - } - case FN_Substr: { - too_few_args = n<3; - too_many_args = n>3; - break; - } - default: break; + }else{ + is_agg = pDef->xFunc==0; } if( is_agg && !allowAgg ){ sqliteSetNString(&pParse->zErrMsg, "misuse of aggregate function ", -1, @@ -759,16 +675,6 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ pExpr->token.z, pExpr->token.n, 0); pParse->nErr++; nErr++; - }else if( too_many_args ){ - sqliteSetNString(&pParse->zErrMsg, "too many arguments to function ",-1, - pExpr->token.z, pExpr->token.n, "()", 2, 0); - pParse->nErr++; - nErr++; - }else if( too_few_args ){ - sqliteSetNString(&pParse->zErrMsg, "too few arguments to function ",-1, - pExpr->token.z, pExpr->token.n, "()", 2, 0); - pParse->nErr++; - nErr++; }else if( wrong_num_args ){ sqliteSetNString(&pParse->zErrMsg, "wrong number of arguments to function ",-1, @@ -943,74 +849,20 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ } case TK_AGG_FUNCTION: { sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg); - if( pExpr->iColumn==FN_Avg ){ - assert( pParse->iAggCount>=0 && pParse->iAggCountnAgg ); - sqliteVdbeAddOp(v, OP_AggGet, 0, pParse->iAggCount); - sqliteVdbeAddOp(v, OP_Divide, 0, 0); - } break; } case TK_FUNCTION: { - int id = pExpr->iColumn; - int op; int i; ExprList *pList = pExpr->pList; - switch( id ){ - case FN_Min: - case FN_Max: { - op = id==FN_Min ? OP_Min : OP_Max; - for(i=0; inExpr; i++){ - sqliteExprCode(pParse, pList->a[i].pExpr); - if( i>0 ){ - sqliteVdbeAddOp(v, op, 0, 0); - } - } - break; - } - case FN_Abs: { - sqliteExprCode(pParse, pList->a[0].pExpr); - sqliteVdbeAddOp(v, OP_AbsValue, 0, 0); - break; - } - case FN_Round: { - if( pList->nExpr==2 ){ - sqliteExprCode(pParse, pList->a[1].pExpr); - }else{ - sqliteVdbeAddOp(v, OP_Integer, 0, 0); - } - sqliteExprCode(pParse, pList->a[0].pExpr); - sqliteVdbeAddOp(v, OP_Precision, 0, 0); - break; - } - case FN_Length: { - sqliteExprCode(pParse, pList->a[0].pExpr); - sqliteVdbeAddOp(v, OP_Strlen, 0, 0); - break; - } - case FN_Substr: { - for(i=0; inExpr; i++){ - sqliteExprCode(pParse, pList->a[i].pExpr); - } - sqliteVdbeAddOp(v, OP_Substr, 0, 0); - break; - } - case FN_Unknown: { - UserFunc *pUser; - pUser = sqliteFindUserFunction(pParse->db, + FuncDef *pDef; + pDef = sqliteFindFunction(pParse->db, pExpr->token.z, pExpr->token.n, pList->nExpr, 0); - assert( pUser!=0 ); - for(i=0; inExpr; i++){ - sqliteExprCode(pParse, pList->a[i].pExpr); - } - sqliteVdbeAddOp(v, OP_UserFunc, pList->nExpr, 0); - sqliteVdbeChangeP3(v, -1, (char*)pUser, P3_POINTER); - break; - } - default: { - /* Can't happen! */ - break; - } + assert( pDef!=0 ); + for(i=0; inExpr; i++){ + sqliteExprCode(pParse, pList->a[i].pExpr); } + sqliteVdbeAddOp(v, OP_Function, pList->nExpr, 0); + sqliteVdbeChangeP3(v, -1, (char*)pDef, P3_POINTER); break; } case TK_SELECT: { @@ -1315,21 +1167,6 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ break; } case TK_AGG_FUNCTION: { - if( pExpr->iColumn==FN_Count || pExpr->iColumn==FN_Avg ){ - if( pParse->iAggCount>=0 ){ - i = pParse->iAggCount; - }else{ - i = appendAggInfo(pParse); - if( i<0 ) return 1; - pParse->aAgg[i].isAgg = 1; - pParse->aAgg[i].pExpr = 0; - pParse->iAggCount = i; - } - if( pExpr->iColumn==FN_Count ){ - pExpr->iAgg = i; - break; - } - } aAgg = pParse->aAgg; for(i=0; inAgg; i++){ if( !aAgg[i].isAgg ) continue; @@ -1342,12 +1179,8 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ if( i<0 ) return 1; pParse->aAgg[i].isAgg = 1; pParse->aAgg[i].pExpr = pExpr; - if( pExpr->iColumn==FN_Unknown ){ - pParse->aAgg[i].pUser = sqliteFindUserFunction(pParse->db, + pParse->aAgg[i].pFunc = sqliteFindFunction(pParse->db, pExpr->token.z, pExpr->token.n, pExpr->pList->nExpr, 0); - }else{ - pParse->aAgg[i].pUser = 0; - } } pExpr->iAgg = i; break; @@ -1374,10 +1207,10 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ /* ** Locate a user function given a name and a number of arguments. -** Return a pointer to the UserFunc structure that defines that +** Return a pointer to the FuncDef structure that defines that ** function, or return NULL if the function does not exist. ** -** If the createFlag argument is true, then a new (blank) UserFunc +** If the createFlag argument is true, then a new (blank) FuncDef ** structure is created and liked into the "db" structure if a ** no matching function previously existed. When createFlag is true ** and the nArg parameter is -1, then only a function that accepts @@ -1387,15 +1220,15 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ ** function found is returned. A function is valid if either xFunc ** or xStep is non-zero. */ -UserFunc *sqliteFindUserFunction( +FuncDef *sqliteFindFunction( sqlite *db, /* An open database */ const char *zName, /* Name of the function. Not null-terminated */ int nName, /* Number of characters in the name */ int nArg, /* Number of arguments. -1 means any number */ int createFlag /* Create new entry if true and does not otherwise exist */ ){ - UserFunc *pFirst, *p, *pMaybe; - pFirst = p = (UserFunc*)sqliteHashFind(&db->userFunc, zName, nName); + FuncDef *pFirst, *p, *pMaybe; + pFirst = p = (FuncDef*)sqliteHashFind(&db->aFunc, zName, nName); if( p && !createFlag && nArg<0 ){ while( p && p->xFunc==0 && p->xStep==0 ){ p = p->pNext; } return p; @@ -1416,7 +1249,7 @@ UserFunc *sqliteFindUserFunction( p = sqliteMalloc( sizeof(*p) ); p->nArg = nArg; p->pNext = pFirst; - sqliteHashInsert(&db->userFunc, zName, nName, (void*)p); + sqliteHashInsert(&db->aFunc, zName, nName, (void*)p); } return p; } diff --git a/src/func.c b/src/func.c index 881b26ab3c..0f49b549d8 100644 --- a/src/func.c +++ b/src/func.c @@ -16,12 +16,130 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.5 2002/02/27 19:50:59 drh Exp $ +** $Id: func.c,v 1.6 2002/02/28 00:41:10 drh Exp $ */ #include #include #include -#include "sqlite.h" +#include +#include "sqliteInt.h" + +/* +** Implementation of the non-aggregate min() and max() functions +*/ +static void minFunc(sqlite_func *context, int argc, const char **argv){ + const char *zBest; + int i; + + zBest = argv[0]; + for(i=1; i0 ){ + zBest = argv[i]; + } + } + sqlite_set_result_string(context, zBest, -1); +} + +/* +** Implementation of the length() function +*/ +static void lengthFunc(sqlite_func *context, int argc, const char **argv){ + const char *z; + int len; + + assert( argc==1 ); + z = argv[0]; + if( z==0 ){ + len = 0; + }else{ +#ifdef SQLITE_UTF8 + for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; } +#else + len = strlen(z); +#endif + } + sqlite_set_result_int(context, len); +} + +/* +** Implementation of the abs() function +*/ +static void absFunc(sqlite_func *context, int argc, const char **argv){ + const char *z; + assert( argc==1 ); + z = argv[0]; + if( z && z[0]=='-' && isdigit(z[1]) ) z++; + sqlite_set_result_string(context, z, -1); +} + +/* +** Implementation of the substr() function +*/ +static void substrFunc(sqlite_func *context, int argc, const char **argv){ + const char *z; +#ifdef SQLITE_UTF8 + const char *z2; + int i; +#endif + int p1, p2, len; + assert( argc==3 ); + z = argv[0]; + if( z==0 ) return; + p1 = atoi(argv[1]?argv[1]:0); + p2 = atoi(argv[2]?argv[2]:0); +#ifdef SQLITE_UTF8 + for(len=0, z2=z; *z2; z2++){ if( (0xc0&*z)!=0x80 ) len++; } +#else + len = strlen(z); +#endif + if( p1<0 ){ + p1 = len-p1; + }else if( p1>0 ){ + p1--; + } + if( p1+p2>len ){ + p2 = len-p1; + } +#ifdef SQLITE_UTF8 + for(i=0; i30 ) n = 30; + if( n<0 ) n = 0; + r = argv[0] ? atof(argv[0]) : 0.0; + sprintf(zBuf,"%.*f",n,r); + sqlite_set_result_string(context, zBuf, -1); +} /* ** Implementation of the upper() and lower() SQL functions. @@ -117,7 +235,100 @@ static void stdDevFinalize(sqlite_func *context){ } } +/* +** The following structure keeps track of state information for the +** count() aggregate function. +*/ +typedef struct CountCtx CountCtx; +struct CountCtx { + int n; +}; +/* +** Routines to implement the count() aggregate function. +*/ +static void countStep(sqlite_func *context, int argc, const char **argv){ + CountCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( (argc==0 || argv[0]) && p ){ + p->n++; + } +} +static void countFinalize(sqlite_func *context){ + CountCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p ){ + sqlite_set_result_int(context, p->n); + } +} + +/* +** This function tracks state information for the min() and max() +** aggregate functions. +*/ +typedef struct MinMaxCtx MinMaxCtx; +struct MinMaxCtx { + char *z; /* The best so far */ + char zBuf[28]; /* Space that can be used for storage */ +}; + +/* +** Routines to implement min() and max() aggregate functions. +*/ +static void minStep(sqlite_func *context, int argc, const char **argv){ + MinMaxCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p==0 || argc<1 ) return; + if( sqlite_aggregate_count(context)==1 || sqliteCompare(argv[0],p->z)<0 ){ + if( p->z && p->z!=p->zBuf ){ + sqliteFree(p->z); + } + if( argv[0] ){ + int len = strlen(argv[0]); + if( len < sizeof(p->zBuf) ){ + p->z = p->zBuf; + }else{ + p->z = sqliteMalloc( len+1 ); + if( p->z==0 ) return; + } + strcpy(p->z, argv[0]); + }else{ + p->z = 0; + } + } +} +static void maxStep(sqlite_func *context, int argc, const char **argv){ + MinMaxCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p==0 || argc<1 ) return; + if( sqlite_aggregate_count(context)==1 || sqliteCompare(argv[0],p->z)>0 ){ + if( p->z && p->z!=p->zBuf ){ + sqliteFree(p->z); + } + if( argv[0] ){ + int len = strlen(argv[0]); + if( len < sizeof(p->zBuf) ){ + p->z = p->zBuf; + }else{ + p->z = sqliteMalloc( len+1 ); + if( p->z==0 ) return; + } + strcpy(p->z, argv[0]); + }else{ + p->z = 0; + } + } +} +static void minMaxFinalize(sqlite_func *context){ + MinMaxCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p && p->z ){ + sqlite_set_result_string(context, p->z, strlen(p->z)); + } + if( p && p->z && p->z!=p->zBuf ){ + sqliteFree(p->z); + } +} /* ** This function registered all of the above C functions as SQL @@ -125,9 +336,43 @@ static void stdDevFinalize(sqlite_func *context){ ** external linkage. */ void sqliteRegisterBuildinFunctions(sqlite *db){ - sqlite_create_function(db, "upper", 1, upperFunc, 0); - sqlite_create_function(db, "lower", 1, lowerFunc, 0); - sqlite_create_aggregate(db, "stddev", 1, stdDevStep, stdDevFinalize, 0); - sqlite_create_aggregate(db, "x_sum", 1, sumStep, sumFinalize, 0); - sqlite_create_aggregate(db, "x_avg", 1, sumStep, avgFinalize, 0); + static struct { + char *zName; + int nArg; + void (*xFunc)(sqlite_func*,int,const char**); + } aFuncs[] = { + { "min", -1, minFunc }, + { "max", -1, maxFunc }, + { "length", 1, lengthFunc }, + { "substr", 3, substrFunc }, + { "abs", 1, absFunc }, + { "round", 1, roundFunc }, + { "round", 2, roundFunc }, + { "upper", 1, upperFunc }, + { "lower", 1, lowerFunc }, + }; + static struct { + char *zName; + int nArg; + void (*xStep)(sqlite_func*,int,const char**); + void (*xFinalize)(sqlite_func*); + } aAggs[] = { + { "min", 1, minStep, minMaxFinalize }, + { "max", 1, maxStep, minMaxFinalize }, + { "sum", 1, sumStep, sumFinalize }, + { "avg", 1, sumStep, avgFinalize }, + { "count", 0, countStep, countFinalize }, + { "count", 1, countStep, countFinalize }, + { "stddev", 1, stdDevStep, stdDevFinalize }, + }; + int i; + + for(i=0; iidxHash, SQLITE_HASH_STRING, 0); sqliteHashInit(&db->tblDrop, SQLITE_HASH_POINTER, 0); sqliteHashInit(&db->idxDrop, SQLITE_HASH_POINTER, 0); - sqliteHashInit(&db->userFunc, SQLITE_HASH_STRING, 1); + sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1); sqliteRegisterBuildinFunctions(db); db->onError = OE_Default; db->priorNewRowid = 0; @@ -416,14 +416,14 @@ void sqlite_close(sqlite *db){ if( db->pBeTemp ){ sqliteBtreeClose(db->pBeTemp); } - for(i=sqliteHashFirst(&db->userFunc); i; i=sqliteHashNext(i)){ - UserFunc *pFunc, *pNext; - for(pFunc = (UserFunc*)sqliteHashData(i); pFunc; pFunc=pNext){ + for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){ + FuncDef *pFunc, *pNext; + for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){ pNext = pFunc->pNext; sqliteFree(pFunc); } } - sqliteHashClear(&db->userFunc); + sqliteHashClear(&db->aFunc); sqliteFree(db); } @@ -646,9 +646,9 @@ int sqlite_create_function( void (*xFunc)(sqlite_func*,int,const char**), /* The implementation */ void *pUserData /* User data */ ){ - UserFunc *p; + FuncDef *p; if( db==0 || zName==0 ) return 1; - p = sqliteFindUserFunction(db, zName, strlen(zName), nArg, 1); + p = sqliteFindFunction(db, zName, strlen(zName), nArg, 1); if( p==0 ) return 1; p->xFunc = xFunc; p->xStep = 0; @@ -664,9 +664,9 @@ int sqlite_create_aggregate( void (*xFinalize)(sqlite_func*), /* The finalizer */ void *pUserData /* User data */ ){ - UserFunc *p; + FuncDef *p; if( db==0 || zName==0 ) return 1; - p = sqliteFindUserFunction(db, zName, strlen(zName), nArg, 1); + p = sqliteFindFunction(db, zName, strlen(zName), nArg, 1); if( p==0 ) return 1; p->xFunc = 0; p->xStep = xStep; diff --git a/src/select.c b/src/select.c index 2df1b67710..c5f97c0b2e 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.68 2002/02/27 19:00:22 drh Exp $ +** $Id: select.c,v 1.69 2002/02/28 00:41:11 drh Exp $ */ #include "sqliteInt.h" @@ -78,7 +78,6 @@ static void sqliteAggregateInfoReset(Parse *pParse){ sqliteFree(pParse->aAgg); pParse->aAgg = 0; pParse->nAgg = 0; - pParse->iAggCount = -1; pParse->useAgg = 0; } @@ -847,8 +846,14 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ pExpr = p->pEList->a[0].pExpr; if( pExpr->op!=TK_AGG_FUNCTION ) return 0; if( pExpr->pList==0 || pExpr->pList->nExpr!=1 ) return 0; - if( pExpr->iColumn!=FN_Min && pExpr->iColumn!=FN_Max ) return 0; - seekOp = pExpr->iColumn==FN_Min ? OP_Rewind : OP_Last; + if( pExpr->token.n!=3 ) return 0; + if( sqliteStrNICmp(pExpr->token.z,"min",3)==0 ){ + seekOp = OP_Rewind; + }else if( sqliteStrNICmp(pExpr->token.z,"max",3)==0 ){ + seekOp = OP_Last; + }else{ + return 0; + } pExpr = pExpr->pList->a[0].pExpr; if( pExpr->op!=TK_COLUMN ) return 0; iCol = pExpr->iColumn; @@ -1151,7 +1156,7 @@ int sqliteSelect( */ sqliteAggregateInfoReset(pParse); if( isAgg ){ - assert( pParse->nAgg==0 && pParse->iAggCount<0 ); + assert( pParse->nAgg==0 ); for(i=0; inExpr; i++){ if( sqliteExprAnalyzeAggregates(pParse, pEList->a[i].pExpr) ){ goto select_end; @@ -1198,27 +1203,15 @@ int sqliteSelect( if( isAgg ){ sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg); for(i=0; inAgg; i++){ - UserFunc *pUser; - if( (pUser = pParse->aAgg[i].pUser)!=0 && pUser->xFinalize!=0 ){ + FuncDef *pFunc; + if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){ sqliteVdbeAddOp(v, OP_AggInit, 0, i); - sqliteVdbeChangeP3(v, -1, (char*)pUser, P3_POINTER); + sqliteVdbeChangeP3(v, -1, (char*)pFunc, P3_POINTER); } } if( pGroupBy==0 ){ sqliteVdbeAddOp(v, OP_String, 0, 0); sqliteVdbeAddOp(v, OP_AggFocus, 0, 0); - for(i=0; inAgg; i++){ - Expr *pE; - if( !pParse->aAgg[i].isAgg ) continue; - pE = pParse->aAgg[i].pExpr; - assert( pE==0 || pE->op==TK_AGG_FUNCTION ); - assert( pE==0 || (pE->pList!=0 && pE->pList->nExpr==1) ); - if( pE==0 || pE->iColumn==FN_Sum ){ - sqliteVdbeAddOp(v, OP_Integer, 0, 0); - sqliteVdbeAddOp(v, OP_AggSet, 0, i); - continue; - } - } } } @@ -1268,36 +1261,20 @@ int sqliteSelect( } for(i=0; inAgg; i++){ Expr *pE; - int op, j; + int j; if( !pParse->aAgg[i].isAgg ) continue; pE = pParse->aAgg[i].pExpr; - if( pE==0 ){ - sqliteVdbeAddOp(v, OP_AggIncr, 1, i); - continue; - } assert( pE->op==TK_AGG_FUNCTION ); - assert( pE->pList!=0 ); - for(j=0; jpList->nExpr; j++){ - sqliteExprCode(pParse, pE->pList->a[j].pExpr); - } - switch( pE->iColumn ){ - case FN_Min: op = OP_Min; break; - case FN_Max: op = OP_Max; break; - case FN_Avg: op = OP_Add; break; - case FN_Sum: op = OP_Add; break; - case FN_Unknown: op = OP_AggFunc; break; - } - if( op!=OP_AggFunc ){ - sqliteVdbeAddOp(v, OP_AggGet, 0, i); - sqliteVdbeAddOp(v, op, 0, 0); - sqliteVdbeAddOp(v, OP_AggSet, 0, i); - }else{ - sqliteVdbeAddOp(v, OP_Integer, i, 0); - sqliteVdbeAddOp(v, OP_AggFunc, 0, pE->pList->nExpr); - assert( pParse->aAgg[i].pUser!=0 ); - assert( pParse->aAgg[i].pUser->xStep!=0 ); - sqliteVdbeChangeP3(v, -1, (char*)pParse->aAgg[i].pUser, P3_POINTER); + if( pE->pList ){ + for(j=0; jpList->nExpr; j++){ + sqliteExprCode(pParse, pE->pList->a[j].pExpr); + } } + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeAddOp(v, OP_AggFunc, 0, pE->pList->nExpr); + assert( pParse->aAgg[i].pFunc!=0 ); + assert( pParse->aAgg[i].pFunc->xStep!=0 ); + sqliteVdbeChangeP3(v, -1, (char*)pParse->aAgg[i].pFunc, P3_POINTER); } } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3734450a55..a39272077a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.95 2002/02/27 19:00:22 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.96 2002/02/28 00:41:11 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -127,21 +127,6 @@ extern int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */ */ #define ArraySize(X) (sizeof(X)/sizeof(X[0])) -/* -** Integer identifiers for built-in SQL functions. -*/ -#define FN_Unknown 0 /* Not a built-in. Might be user defined */ -#define FN_Count 1 -#define FN_Min 2 -#define FN_Max 3 -#define FN_Sum 4 -#define FN_Avg 5 -#define FN_Fcnt 6 -#define FN_Length 7 -#define FN_Substr 8 -#define FN_Abs 9 -#define FN_Round 10 - /* ** Forward references to structures */ @@ -158,7 +143,7 @@ typedef struct WhereInfo WhereInfo; typedef struct WhereLevel WhereLevel; typedef struct Select Select; typedef struct AggExpr AggExpr; -typedef struct UserFunc UserFunc; +typedef struct FuncDef FuncDef; /* ** Each database is an instance of the following structure @@ -177,7 +162,7 @@ struct sqlite { Hash idxHash; /* All (named) indices indexed by name */ Hash tblDrop; /* Uncommitted DROP TABLEs */ Hash idxDrop; /* Uncommitted DROP INDEXs */ - Hash userFunc; /* User defined functions */ + Hash aFunc; /* All functions that can be in SQL exprs */ int lastRowid; /* ROWID of most recent insert */ int priorNewRowid; /* Last randomly generated ROWID */ int onError; /* Default conflict algorithm */ @@ -200,18 +185,18 @@ struct sqlite { #define SQLITE_ResultDetails 0x00000100 /* Details added to result set */ /* -** Each user-defined function is defined by an instance of the following -** structure. A pointer to this structure is stored in the sqlite.userFunc +** Each SQL function is defined by an instance of the following +** structure. A pointer to this structure is stored in the sqlite.aFunc ** hash table. When multiple functions have the same name, the hash table ** points to a linked list of these structures. */ -struct UserFunc { +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 */ void *pUserData; /* User data parameter */ - UserFunc *pNext; /* Next function with same name */ + FuncDef *pNext; /* Next function with same name */ }; /* @@ -510,7 +495,7 @@ struct Select { struct AggExpr { int isAgg; /* if TRUE contains an aggregate function */ Expr *pExpr; /* The expression */ - UserFunc *pUser; /* User-defined aggregate function */ + FuncDef *pFunc; /* Information about the aggregate function */ }; /* @@ -541,7 +526,6 @@ struct Parse { int nSet; /* Number of sets used so far */ int nAgg; /* Number of aggregate expressions */ AggExpr *aAgg; /* An array of aggregate expressions */ - int iAggCount; /* Index of the count(*) aggregate in aAgg[] */ int useAgg; /* If true, extract field values from the aggregator ** while generating expressions. Normally false */ int schemaVerified; /* True if an OP_VerifySchema has been coded someplace @@ -652,5 +636,5 @@ Expr *sqliteExprDup(Expr*); ExprList *sqliteExprListDup(ExprList*); IdList *sqliteIdListDup(IdList*); Select *sqliteSelectDup(Select*); -UserFunc *sqliteFindUserFunction(sqlite*,const char*,int,int,int); +FuncDef *sqliteFindFunction(sqlite*,const char*,int,int,int); void sqliteRegisterBuildinFunctions(sqlite*); diff --git a/src/util.c b/src/util.c index b3c3477a26..dffe611822 100644 --- a/src/util.c +++ b/src/util.c @@ -14,7 +14,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.38 2002/02/27 01:47:12 drh Exp $ +** $Id: util.c,v 1.39 2002/02/28 00:41:11 drh Exp $ */ #include "sqliteInt.h" #include @@ -659,8 +659,14 @@ static int isNum(const char *z){ */ int sqliteCompare(const char *atext, const char *btext){ int result; - int isNumA = isNum(atext); - int isNumB = isNum(btext); + int isNumA, isNumB; + if( atext==0 ){ + return -(btext!=0); + }else if( btext==0 ){ + return 1; + } + isNumA = isNum(atext); + isNumB = isNum(btext); if( isNumA ){ if( !isNumB ){ result = -1; diff --git a/src/vdbe.c b/src/vdbe.c index 0d134ae039..a65ff35b81 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.125 2002/02/27 19:50:59 drh Exp $ +** $Id: vdbe.c,v 1.126 2002/02/28 00:41:11 drh Exp $ */ #include "sqliteInt.h" #include @@ -140,14 +140,14 @@ typedef struct Mem Mem; /* The following STK_ value appears only in AggElem.aMem.s.flag fields. ** It indicates that the corresponding AggElem.aMem.z points to a -** user-defined aggregate context that needs to be finalized. +** aggregate function context that needs to be finalized. */ #define STK_AggCtx 0x0040 /* zStack[] points to an agg function context */ /* -** The "context" argument for a user-defined function. A pointer to an +** The "context" argument for a installable function. A pointer to an ** instance of this structure is the first argument to the routines used -** implement user-defined SQL functions. +** implement the SQL functions. ** ** There is a typedef for this structure in sqlite.h. So all routines, ** even the public interface to SQLite, can use a pointer to this structure. @@ -158,7 +158,7 @@ typedef struct Mem Mem; ** (Stack) which are only defined there. */ struct sqlite_func { - UserFunc *pFunc; /* Pointer to function information. MUST BE FIRST */ + FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */ Stack s; /* Small strings, ints, and double values go here */ char *z; /* Space for holding dynamic string results */ void *pAgg; /* Aggregate context */ @@ -181,7 +181,7 @@ struct Agg { AggElem *pCurrent; /* The AggElem currently in focus */ HashElem *pSearch; /* The hash element for pCurrent */ Hash hash; /* Hash table of all aggregate elements */ - UserFunc **apFunc; /* Information about user-defined aggregate functions */ + FuncDef **apFunc; /* Information about aggregate functions */ }; struct AggElem { char *zKey; /* The key to this AggElem */ @@ -523,7 +523,7 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){ } /* -** The following group or routines are employed by user-defined functions +** The following group or routines are employed by installable functions ** to return their results. ** ** The sqlite_set_result_string() routine can be used to return a string @@ -628,8 +628,8 @@ void *sqlite_aggregate_context(sqlite_func *p, int nByte){ } /* -** Return the number of times the Step function of a user-defined -** aggregate has been called. +** Return the number of times the Step function of a aggregate has been +** called. ** ** This routine is defined here in vdbe.c because it depends on knowing ** the internals of the sqlite_func structure which is only defined in that @@ -643,7 +643,7 @@ int sqlite_aggregate_count(sqlite_func *p){ /* ** Reset an Agg structure. Delete all its contents. ** -** For user-defined aggregate functions, if the step function has been +** For installable aggregate functions, if the step function has been ** called, make sure the finalizer function has also been called. The ** finalizer might need to free memory that was allocated as part of its ** private context. If the finalizer has not been called yet, call it @@ -1061,7 +1061,7 @@ static char *zOpName[] = { 0, "Ne", "Lt", "Le", "Gt", "Ge", "IsNull", "NotNull", "Negative", "And", "Or", "Not", "Concat", - "Noop", "Strlen", "Substr", "UserFunc", + "Noop", "Strlen", "Substr", "Function", "Limit", }; @@ -1865,15 +1865,15 @@ case OP_Max: { break; } -/* Opcode: UserFunc P1 * P3 +/* Opcode: Function P1 * P3 ** -** Invoke a user function (P3 is a pointer to a UserFunc structure that +** Invoke a user function (P3 is a pointer to a Function structure that ** defines the function) with P1 string arguments taken from the stack. ** Pop all arguments from the stack and push back the result. ** ** See also: AggFunc */ -case OP_UserFunc: { +case OP_Function: { int n, i; sqlite_func ctx; @@ -1885,7 +1885,7 @@ case OP_UserFunc: { if( Stringify(p, i) ) goto no_mem; } } - ctx.pFunc = (UserFunc*)pOp->p3; + ctx.pFunc = (FuncDef*)pOp->p3; ctx.s.flags = STK_Null; ctx.z = 0; ctx.isError = 0; @@ -4404,22 +4404,22 @@ case OP_AggReset: { /* Opcode: AggInit * P2 P3 ** -** Initialize the function parameters for a user-defined aggregate function. -** The user-defined aggregate will operate out of aggregate column P2. -** P3 is a pointer to the UserFunc structure for the function. +** Initialize the function parameters for an aggregate function. +** The aggregate will operate out of aggregate column P2. +** P3 is a pointer to the FuncDef structure for the function. */ case OP_AggInit: { int i = pOp->p2; VERIFY( if( i<0 || i>=p->agg.nMem ) goto bad_instruction; ) - p->agg.apFunc[i] = (UserFunc*)pOp->p3; + p->agg.apFunc[i] = (FuncDef*)pOp->p3; break; } /* Opcode: AggFunc * P2 P3 ** -** Execute the step function for a user-defined aggregate. The -** function has P2 arguments. P3 is a pointer to the UserFunc -** structure that specifies the user-defined function. +** Execute the step function for an aggregate. The +** function has P2 arguments. P3 is a pointer to the FuncDef +** structure that specifies the function. ** ** The top of the stack must be an integer which is the index of ** the aggregate column that corresponds to this aggregate function. @@ -4442,7 +4442,7 @@ case OP_AggFunc: { } i = aStack[p->tos].i; VERIFY( if( i<0 || i>=p->agg.nMem ) goto bad_instruction; ) - ctx.pFunc = (UserFunc*)pOp->p3; + ctx.pFunc = (FuncDef*)pOp->p3; pMem = &p->agg.pCurrent->aMem[i]; ctx.z = pMem->s.z; ctx.pAgg = pMem->z; @@ -4609,17 +4609,21 @@ case OP_AggNext: { p->agg.pCurrent = sqliteHashData(p->agg.pSearch); aMem = p->agg.pCurrent->aMem; for(i=0; iagg.nMem; i++){ + int freeCtx; if( p->agg.apFunc[i]==0 ) continue; if( p->agg.apFunc[i]->xFinalize==0 ) continue; if( (aMem[i].s.flags & STK_AggCtx)==0 ) continue; ctx.s.flags = STK_Null; ctx.z = 0; ctx.pAgg = (void*)aMem[i].z; + freeCtx = aMem[i].z && aMem[i].z!=aMem[i].s.z; ctx.cnt = aMem[i].s.i; ctx.isStep = 0; ctx.pFunc = p->agg.apFunc[i]; (*p->agg.apFunc[i]->xFinalize)(&ctx); - sqliteFree( aMem[i].z ); + if( freeCtx ){ + sqliteFree( aMem[i].z ); + } aMem[i].s = ctx.s; aMem[i].z = ctx.z; if( (aMem[i].s.flags & STK_Str) && diff --git a/src/vdbe.h b/src/vdbe.h index bb2bf2fbe8..8d26764eb8 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.47 2002/02/27 19:00:22 drh Exp $ +** $Id: vdbe.h,v 1.48 2002/02/28 00:41:11 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -202,7 +202,7 @@ typedef struct VdbeOp VdbeOp; #define OP_Noop 117 #define OP_Strlen 118 #define OP_Substr 119 -#define OP_UserFunc 120 +#define OP_Function 120 #define OP_Limit 121