-C Modify\slemon\sto\suse\smuch\sless\smemory\sfor\sits\sparser\stables.\s\sThis\sreduces\nthe\ssize\sof\sthe\slibrary\sby\s50K,\swhich\sis\simportant\sfor\san\sembedded\slibrary.\s(CVS\s389)
-D 2002-02-23T19:39:47
+C Added\ssupport\sfor\suser-defined\snormal\sfunctions.\s\sSupport\sfor\suser-defined\naggregates\sis\spending.\s(CVS\s390)
+D 2002-02-23T23:45:45
F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
F src/build.c 1da051784be0155ae579d47890db74f0186f9b9f
F src/delete.c 950d8f9097361419f1963875f9943344b469cf02
-F src/expr.c 6b641c43941094a5d1f7a96657d8a34d07188856
-F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
-F src/hash.h d1ce47900c7325af5e41c4feb4855c4bf2b841e7
+F src/expr.c 4f9db24c4e90585fd046703d4f91c10b453867fa
+F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
+F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
F src/insert.c 164d2d5e943268a8ff0594e1947599e04df0ce11
-F src/main.c abc0732d4caa676ff8337f278b01f1f1b57538f5
+F src/main.c 0fa2298ab8980cb446dc81086ce36f905f607f70
F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea
F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6
F src/select.c 61d4a739956aaeb124cdf12c34c66e99ae34212c
F src/shell.c cbf48bf0ca35c4e0d8a7d2a86f7724f52c525cd7
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
-F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c
-F src/sqliteInt.h 81dcdf77391471443d53e4b96ac5e78a10e9df4b
-F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a
+F src/sqlite.h.in f1421919a4437a377fb712b98835a224482e776e
+F src/sqliteInt.h b089e9226fbb88b25810d2f52285929dcf6999c6
+F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52
F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af
F src/update.c 18971d265b0341574b7e3f73116e7947ddab0997
F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6
-F src/vdbe.c 44832d804e109248e9e2abd40daee5f8ac735450
-F src/vdbe.h 002bb8cf884034bea25a9fe901a9c5e9d29bc045
+F src/vdbe.c 7d4c6e2f4861bed0ef5abd5d39153bfd401e30ff
+F src/vdbe.h c3be021687ab2eb8517052a24e1df7e7355f0d77
F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585
F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05
F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
-F test/select1.test fd2936aa907559153c78edf2740ea65eb9a614f5
+F test/select1.test 7d5ae792d6dbfa2c1b6345a32b154b7ba8d24bbc
F test/select2.test ed2c1882857106b85478f54f67000e14966be4c4
F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
F www/c_interface.tcl 63efc40f09e2f0d8fea43d174103248b160fdf0e
-F www/changes.tcl 4aee975940a59d43736cdd4cd352c51e1e6426ba
+F www/changes.tcl 0c40569ef5c92af1fb211ea1f679113a44c60f19
F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 8da0ac9a8bb859377613dd18f4f423eb49c7338b
-R a10568441a27cde980e4058838f315f0
+P 67a135a051e7c96ddbfe85976539b4b8372c7026
+R fa8d1484ff86c96c0829999f970d3d5e
U drh
-Z 77767b7d4f55ba40f2761ab26d448e78
+Z f2c150ed8d00dcdf045fbfc7cf498205
-67a135a051e7c96ddbfe85976539b4b8372c7026
\ No newline at end of file
+c490a1ff951c5d4a2de8e4f8d349189bfaef7f74
\ No newline at end of file
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
-** $Id: expr.c,v 1.42 2002/02/23 02:32:10 drh Exp $
+** $Id: expr.c,v 1.43 2002/02/23 23:45:45 drh Exp $
*/
#include "sqliteInt.h"
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: {
- no_such_func = 1;
+ 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;
}
case FN_Count: {
- no_such_func = !allowAgg;
too_many_args = n>1;
is_agg = 1;
break;
}
case FN_Max:
case FN_Min: {
- too_few_args = allowAgg ? n<1 : n<2;
+ too_few_args = n<1;
is_agg = n==1;
break;
}
case FN_Avg:
case FN_Sum: {
- no_such_func = !allowAgg;
too_many_args = n>1;
too_few_args = n<1;
is_agg = 1;
}
default: break;
}
- if( no_such_func ){
+ if( is_agg && !allowAgg ){
+ sqliteSetNString(&pParse->zErrMsg, "misuse of aggregate function ", -1,
+ pExpr->token.z, pExpr->token.n, "()", 2, 0);
+ pParse->nErr++;
+ nErr++;
+ is_agg = 0;
+ }else if( no_such_func ){
sqliteSetNString(&pParse->zErrMsg, "no such function: ", -1,
pExpr->token.z, pExpr->token.n, 0);
pParse->nErr++;
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,
+ pExpr->token.z, pExpr->token.n, "()", 2, 0);
+ pParse->nErr++;
+ nErr++;
}
if( is_agg ) pExpr->op = TK_AGG_FUNCTION;
if( is_agg && pIsAgg ) *pIsAgg = 1;
sqliteVdbeAddOp(v, OP_Substr, 0, 0);
break;
}
+ case FN_Unknown: {
+ UserFunc *pUser;
+ pUser = sqliteFindUserFunction(pParse->db,
+ pExpr->token.z, pExpr->token.n, pList->nExpr, 0);
+ assert( pUser!=0 );
+ for(i=0; i<pList->nExpr; i++){
+ sqliteExprCode(pParse, pList->a[i].pExpr);
+ }
+ sqliteVdbeAddOp(v, OP_UserFunc, pList->nExpr, 0);
+ sqliteVdbeChangeP3(v, -1, (char*)pUser->xFunc, P3_POINTER);
+ break;
+ }
default: {
/* Can't happen! */
break;
}
return nErr;
}
+
+/*
+** Locate a user function given a name and a number of arguments.
+** Return a pointer to the UserFunc 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
+** 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
+** any number of arguments will be returned.
+**
+** If createFlag is false and nArg is -1, then the first valid
+** function found is returned. A function is valid if either xFunc
+** or xStep is non-zero.
+*/
+UserFunc *sqliteFindUserFunction(
+ 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);
+ if( !createFlag && nArg<0 ){
+ while( p && p->xFunc==0 && p->xStep==0 ){ p = p->pNext; }
+ return p;
+ }
+ pMaybe = 0;
+ while( p && p->nArg!=nArg ){
+ if( p->nArg<0 && !createFlag && (p->xFunc || p->xStep) ) pMaybe = p;
+ p = p->pNext;
+ }
+ if( p && !createFlag && p->xFunc==0 && p->xStep==0 ){
+ return 0;
+ }
+ if( p==0 && pMaybe ){
+ assert( createFlag==0 );
+ return pMaybe;
+ }
+ if( p==0 && createFlag ){
+ p = sqliteMalloc( sizeof(*p) );
+ p->nArg = nArg;
+ p->pNext = pFirst;
+ sqliteHashInsert(&db->userFunc, zName, nName, (void*)p);
+ }
+ return p;
+}
** This is the implementation of generic hash-tables
** used in SQLite.
**
-** $Id: hash.c,v 1.6 2002/01/14 09:28:20 drh Exp $
+** $Id: hash.c,v 1.7 2002/02/23 23:45:45 drh Exp $
*/
#include "sqliteInt.h"
#include <assert.h>
** If the "data" parameter to this function is NULL, then the
** element corresponding to "key" is removed from the hash table.
*/
-void *sqliteHashInsert(Hash *pH, void *pKey, int nKey, void *data){
+void *sqliteHashInsert(Hash *pH, const void *pKey, int nKey, void *data){
int hraw; /* Raw hash value of the key */
int h; /* the hash of the key modulo hash table size */
HashElem *elem; /* Used to loop thru the element list */
}
memcpy((void*)new_elem->pKey, pKey, nKey);
}else{
- new_elem->pKey = pKey;
+ new_elem->pKey = (const void*)pKey;
}
new_elem->nKey = nKey;
pH->count++;
** This is the header file for the generic hash-table implemenation
** used in SQLite.
**
-** $Id: hash.h,v 1.3 2002/02/03 03:34:09 drh Exp $
+** $Id: hash.h,v 1.4 2002/02/23 23:45:45 drh Exp $
*/
#ifndef _SQLITE_HASH_H_
#define _SQLITE_HASH_H_
** Access routines. To delete, insert a NULL pointer.
*/
void sqliteHashInit(Hash*, int keytype, int copyKey);
-void *sqliteHashInsert(Hash*, void *pKey, int nKey, void *pData);
+void *sqliteHashInsert(Hash*, const void *pKey, int nKey, void *pData);
void *sqliteHashFind(const Hash*, const void *pKey, int nKey);
void sqliteHashClear(Hash*);
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.61 2002/02/21 12:01:27 drh Exp $
+** $Id: main.c,v 1.62 2002/02/23 23:45:45 drh Exp $
*/
+#include <ctype.h>
#include "sqliteInt.h"
#include "os.h"
const char sqlite_encoding[] = "iso8859";
#endif
+/*
+** Implementation of the upper() and lower() SQL functions.
+*/
+static void upperFunc(void *context, int argc, const char **argv){
+ char *z;
+ int i;
+ if( argc<1 || argv[0]==0 ) return;
+ z = sqlite_set_result_string(context, argv[0], -1);
+ if( z==0 ) return;
+ for(i=0; z[i]; i++){
+ if( islower(z[i]) ) z[i] = toupper(z[i]);
+ }
+}
+static void lowerFunc(void *context, int argc, const char **argv){
+ char *z;
+ int i;
+ if( argc<1 || argv[0]==0 ) return;
+ z = sqlite_set_result_string(context, argv[0], -1);
+ if( z==0 ) return;
+ for(i=0; z[i]; i++){
+ if( isupper(z[i]) ) z[i] = tolower(z[i]);
+ }
+}
+
/*
** Open a new SQLite database. Construct an "sqlite" structure to define
** the state of this database and return a pointer to that structure.
sqliteHashInit(&db->idxHash, 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);
+ sqlite_create_function(db, "upper", 1, upperFunc);
+ sqlite_create_function(db, "lower", 1, lowerFunc);
db->onError = OE_Default;
db->priorNewRowid = 0;
** Close an existing SQLite database
*/
void sqlite_close(sqlite *db){
+ HashElem *i;
sqliteBtreeClose(db->pBe);
clearHashTable(db, 0);
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){
+ pNext = pFunc->pNext;
+ sqliteFree(pFunc);
+ }
+ }
+ sqliteHashClear(&db->userFunc);
sqliteFree(db);
}
*/
const char *sqlite_libversion(void){ return sqlite_version; }
const char *sqlite_libencoding(void){ return sqlite_encoding; }
+
+/*
+** Create new user-defined functions. The sqlite_create_function()
+** routine creates a regular function and sqlite_create_aggregate()
+** creates an aggregate function.
+**
+** Passing a NULL xFunc argument or NULL xStep and xFinalize arguments
+** disables the function. Calling sqlite_create_function() with the
+** same name and number of arguments as a prior call to
+** sqlite_create_aggregate() disables the prior call to
+** sqlite_create_aggregate(), and vice versa.
+**
+** If nArg is -1 it means that this function will accept any number
+** of arguments, including 0.
+*/
+int sqlite_create_function(
+ sqlite *db, /* Add the function to this database connection */
+ const char *zName, /* Name of the function to add */
+ int nArg, /* Number of arguments */
+ void (*xFunc)(void*,int,const char**) /* Implementation of the function */
+){
+ UserFunc *p;
+ if( db==0 || zName==0 ) return 1;
+ p = sqliteFindUserFunction(db, zName, strlen(zName), nArg, 1);
+ p->xFunc = xFunc;
+ p->xStep = 0;
+ p->xFinalize = 0;
+ return 0;
+}
+int sqlite_create_aggregate(
+ sqlite *db, /* Add the function to this database connection */
+ const char *zName, /* Name of the function to add */
+ int nArg, /* Number of arguments */
+ void *(*xStep)(void*,int,const char**), /* The step function */
+ void (*xFinalize)(void*,void*) /* The finalizer */
+){
+ UserFunc *p;
+ if( db==0 || zName==0 ) return 1;
+ p = sqliteFindUserFunction(db, zName, strlen(zName), nArg, 1);
+ p->xFunc = 0;
+ p->xStep = xStep;
+ p->xFinalize = xFinalize;
+ return 0;
+}
** This header file defines the interface that the SQLite library
** presents to client programs.
**
-** @(#) $Id: sqlite.h.in,v 1.25 2002/01/16 21:00:27 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.26 2002/02/23 23:45:45 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
va_list ap /* Arguments to the format string */
);
+/*
+** Use the following routines to create new user-defined functions. See
+** the documentation for details.
+*/
+int sqlite_create_function(
+ sqlite*, /* Database where the new function is registered */
+ const char *zName, /* Name of the new function */
+ int nArg, /* Number of arguments. -1 means any number */
+ void (*xFunc)(void*,int,const char**) /* C code to implement the function */
+);
+int sqlite_create_aggregate(
+ sqlite*, /* Database where the new function is registered */
+ const char *zName, /* Name of the function */
+ int nArg, /* Number of arguments */
+ void *(*xStep)(void*,int,const char**), /* Called for each row */
+ void (*xFinalize)(void*,void*) /* Called once to get final result */
+);
+
+/*
+** The user function implementations call one of the following four routines
+** in order to return their results. The first parameter to each of these
+** routines is a copy of the first argument to xFunc() or the second argument
+** to xFinalize(). The second parameter to these routines is the result
+** to be returned. A NULL can be passed as the second parameter to
+** sqlite_set_result_string() in order to return a NULL result.
+**
+** The 3rd argument to _string and _error is the number of characters to
+** take from the string. If this argument is negative, then all characters
+** up to and including the first '\000' are used.
+*/
+char *sqlite_set_result_string(void*,const char*,int);
+void sqlite_set_result_int(void*,int);
+void sqlite_set_result_double(void*,double);
+void sqlite_set_result_error(void*,const char*,int);
+
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.90 2002/02/23 02:32:10 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.91 2002/02/23 23:45:45 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
/*
** Integer identifiers for built-in SQL functions.
*/
-#define FN_Unknown 0
+#define FN_Unknown 0 /* Not a built-in. Might be user defined */
#define FN_Count 1
#define FN_Min 2
#define FN_Max 3
typedef struct WhereLevel WhereLevel;
typedef struct Select Select;
typedef struct AggExpr AggExpr;
+typedef struct UserFunc UserFunc;
/*
** Each database is an instance of the following structure
Hash idxHash; /* All (named) indices indexed by name */
Hash tblDrop; /* Uncommitted DROP TABLEs */
Hash idxDrop; /* Uncommitted DROP INDEXs */
+ Hash userFunc; /* User defined functions */
int lastRowid; /* ROWID of most recent insert */
int priorNewRowid; /* Last randomly generated ROWID */
int onError; /* Default conflict algorithm */
#define SQLITE_ResultDetails 0x00000100 /* Details added to result set */
/*
-** Current file format version
+** Each user-defined function is defined by an instance of the following
+** structure. A pointer to this structure is stored in the sqlite.userFunc
+** hash table. When multiple functions have the same name, the hash table
+** points to a linked list of these structures.
*/
-#define SQLITE_FileFormat 2
+struct UserFunc {
+ void (*xFunc)(void*,int,const char**); /* Regular function */
+ void *(*xStep)(void*,int,const char**); /* Aggregate function step */
+ void (*xFinalize)(void*,void*); /* Aggregate function finializer */
+ int nArg; /* Number of arguments */
+ UserFunc *pNext; /* Next function with same name */
+};
/*
** information about each column of an SQL table is held in an instance
void sqliteExprMoveStrings(Expr*, int);
void sqliteExprListMoveStrings(ExprList*, int);
void sqliteSelectMoveStrings(Select*, int);
+UserFunc *sqliteFindUserFunction(sqlite*,const char*,int,int,int);
** if they are not used.
*/
#include <stdlib.h>
+#include <string.h>
#include "sqlite.h"
/*
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.121 2002/02/21 12:01:27 drh Exp $
+** $Id: vdbe.c,v 1.122 2002/02/23 23:45:45 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */
#define STK_Static 0x0020 /* zStack[] points to a static string */
+/*
+** The "context" argument for a user-defined function.
+*/
+struct UserFuncContext {
+ Stack s; /* Small string, integer, and floating point values go here */
+ char *z; /* Space for holding dynamic string results */
+ int isError; /* Set to true for an error */
+};
+typedef struct UserFuncContext UserFuncContext;
+
/*
** An Agg structure describes an Aggregator. Each Agg consists of
** zero or more Aggregator elements (AggElem). Each AggElem contains
z[j] = 0;
}
+/*
+** The following group or routines are employed by user-defined functions
+** to return their results.
+**
+** The sqlite_set_result_string() routine can be used to return a string
+** value or to return a NULL. To return a NULL, pass in NULL for zResult.
+** A copy is made of the string before this routine returns so it is safe
+** to pass in a ephemeral string.
+**
+** sqlite_set_result_error() works like sqlite_set_result_string() except
+** that it signals a fatal error. The string argument, if any, is the
+** error message. If the argument is NULL a generic substitute error message
+** is used.
+**
+** The sqlite_set_result_int() and sqlite_set_result_double() set the return
+** value of the user function to an integer or a double.
+*/
+char *sqlite_set_result_string(void *context, const char *zResult, int n){
+ UserFuncContext *p = (UserFuncContext*)context;
+ if( p->s.flags & STK_Dyn ){
+ sqliteFree(p->z);
+ }
+ if( zResult==0 ){
+ p->s.flags = STK_Null;
+ n = 0;
+ p->z = 0;
+ }else{
+ if( n<0 ) n = strlen(zResult);
+ if( n<NBFS-1 ){
+ memcpy(p->s.z, zResult, n);
+ p->s.z[n] = 0;
+ p->s.flags = STK_Str;
+ p->z = p->s.z;
+ }else{
+ p->z = sqliteMalloc( n+1 );
+ if( p->z ){
+ memcpy(p->z, zResult, n);
+ p->z[n] = 0;
+ }
+ p->s.flags = STK_Str | STK_Dyn;
+ }
+ }
+ p->s.n = n;
+ return p->z;
+}
+void sqlite_set_result_int(void *context, int iResult){
+ UserFuncContext *p = (UserFuncContext*)context;
+ if( p->s.flags & STK_Dyn ){
+ sqliteFree(p->z);
+ }
+ p->s.i = iResult;
+ p->s.flags = STK_Int;
+}
+void sqlite_set_result_double(void *context, double rResult){
+ UserFuncContext *p = (UserFuncContext*)context;
+ if( p->s.flags & STK_Dyn ){
+ sqliteFree(p->z);
+ }
+ p->s.r = rResult;
+ p->s.flags = STK_Real;
+}
+void sqlite_set_result_error(void *context, const char *zMsg, int n){
+ UserFuncContext *p = (UserFuncContext*)context;
+ sqlite_set_result_string(context, zMsg, n);
+ p->isError = 1;
+}
+
/*
** Reset an Agg structure. Delete all its contents.
*/
"Le", "Gt", "Ge", "IsNull",
"NotNull", "Negative", "And", "Or",
"Not", "Concat", "Noop", "Strlen",
- "Substr", "Limit",
+ "Substr", "UserFunc", "UserAgg", "Limit",
};
/*
break;
}
+/* Opcode: UserFunc P1 * P3
+**
+** Invoke a user function (P3 is a pointer to the function) with
+** P1 string arguments taken from the stack. Pop all arguments from
+** the stack and push back the result.
+*/
+case OP_UserFunc: {
+ int n, i;
+ UserFuncContext ctx;
+ void (*xFunc)(void*,int,const char**);
+ n = pOp->p1;
+ VERIFY( if( n<=0 ) goto bad_instruction; )
+ VERIFY( if( p->tos+1<n ) goto not_enough_stack; )
+ for(i=p->tos-n+1; i<=p->tos; i++){
+ if( (aStack[i].flags & STK_Null)==0 ){
+ if( Stringify(p, i) ) goto no_mem;
+ }
+ }
+ xFunc = (void(*)(void*,int,const char**))pOp->p3;
+ ctx.s.flags = STK_Null;
+ ctx.isError = 0;
+ xFunc((void*)&ctx, n, (const char**)&zStack[p->tos-n+1]);
+ PopStack(p, n);
+ VERIFY( NeedStack(p, p->tos+1); )
+ p->tos++;
+ aStack[p->tos] = ctx.s;
+ if( ctx.s.flags & STK_Dyn ){
+ zStack[p->tos] = ctx.z;
+ }else if( ctx.s.flags & STK_Str ){
+ zStack[p->tos] = aStack[p->tos].z;
+ }else{
+ zStack[p->tos] = 0;
+ }
+ if( ctx.isError ){
+ sqliteSetString(pzErrMsg,
+ zStack[p->tos] ? zStack[p->tos] : "user function error", 0);
+ rc = SQLITE_ERROR;
+ }
+ break;
+}
+
/* Opcode: BitAnd * * *
**
** Pop the top two elements from the stack. Convert both elements
** 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.44 2002/02/21 12:01:28 drh Exp $
+** $Id: vdbe.h,v 1.45 2002/02/23 23:45:46 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
#define OP_Not 113
#define OP_Concat 114
#define OP_Noop 115
-
#define OP_Strlen 116
#define OP_Substr 117
+#define OP_UserFunc 118
+#define OP_UserAgg 119
-#define OP_Limit 118
+#define OP_Limit 120
-#define OP_MAX 118
+#define OP_MAX 120
/*
** Prototypes for the VDBE interface. See comments on the implementation
# This file implements regression tests for SQLite library. The
# focus of this file is testing the SELECT statement.
#
-# $Id: select1.test,v 1.18 2002/01/22 14:11:30 drh Exp $
+# $Id: select1.test,v 1.19 2002/02/23 23:45:47 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_test select1-2.20 {
set v [catch {execsql {SELECT SUM(min(f1)) FROM test1}} msg]
lappend v $msg
-} {1 {too few arguments to function min()}}
+} {1 {misuse of aggregate function min()}}
# WHERE clause expressions
#
do_test select1-3.9 {
set v [catch {execsql {SELECT f1 FROM test1 WHERE count(f1,f2)!=11}} msg]
lappend v $msg
-} {1 {no such function: count}}
+} {1 {misuse of aggregate function count()}}
# ORDER BY expressions
#
do_test select1-4.4 {
set v [catch {execsql {SELECT f1 FROM test1 ORDER BY min(f1)}} msg]
lappend v $msg
-} {1 {too few arguments to function min()}}
+} {1 {misuse of aggregate function min()}}
do_test select1-4.5 {
catchsql {
SELECT f1 FROM test1 ORDER BY 8.4;
<li>Automatically generated ROWIDs are now sequential.</li>
<li>Do not allow dot-commands of the command-line shell to occur in the
middle of a real SQL command.</li>
+<li>Modifications to the "lemon" parser generator so that the parser tables
+ are 4 times smaller.</li>
+<li>Added support for user-defined functions implemented in C.</li>
}
chng {2002 Feb 18 (2.3.3)} {