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

Added support for user-defined normal functions. Support for user-defined

aggregates is pending. (CVS 390)

FossilOrigin-Name: c490a1ff951c5d4a2de8e4f8d349189bfaef7f74
This commit is contained in:
drh
2002-02-23 23:45:45 +00:00
parent b29b0a5227
commit 8e0a2f903a
13 changed files with 380 additions and 45 deletions

View File

@@ -1,5 +1,5 @@
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
@@ -23,11 +23,11 @@ F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d
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
@@ -39,9 +39,9 @@ F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
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
@@ -50,8 +50,8 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
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
@@ -81,7 +81,7 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
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
@@ -110,7 +110,7 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
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
@@ -125,7 +125,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
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

View File

@@ -1 +1 @@
67a135a051e7c96ddbfe85976539b4b8372c7026
c490a1ff951c5d4a2de8e4f8d349189bfaef7f74

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.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"
@@ -606,29 +606,40 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
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;
}
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;
@@ -652,7 +663,13 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
}
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++;
@@ -667,6 +684,12 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
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;
@@ -886,6 +909,18 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
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;
@@ -1245,3 +1280,52 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
}
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;
}

View File

@@ -12,7 +12,7 @@
** 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>
@@ -286,7 +286,7 @@ void *sqliteHashFind(const Hash *pH, const void *pKey, int nKey){
** 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 */
@@ -320,7 +320,7 @@ void *sqliteHashInsert(Hash *pH, void *pKey, int nKey, void *data){
}
memcpy((void*)new_elem->pKey, pKey, nKey);
}else{
new_elem->pKey = pKey;
new_elem->pKey = (const void*)pKey;
}
new_elem->nKey = nKey;
pH->count++;

View File

@@ -12,7 +12,7 @@
** 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_
@@ -79,7 +79,7 @@ struct HashElem {
** 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*);

View File

@@ -14,8 +14,9 @@
** 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"
@@ -292,6 +293,30 @@ const char sqlite_encoding[] = "UTF-8";
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.
@@ -313,6 +338,9 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
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;
@@ -408,11 +436,20 @@ int sqlite_last_insert_rowid(sqlite *db){
** 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);
}
@@ -613,3 +650,47 @@ void sqlite_freemem(void *p){ free(p); }
*/
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;
}

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.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_
@@ -362,6 +362,41 @@ int sqlite_get_table_vprintf(
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

View File

@@ -11,7 +11,7 @@
*************************************************************************
** 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"
@@ -130,7 +130,7 @@ extern int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
/*
** 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
@@ -158,6 +158,7 @@ typedef struct WhereInfo WhereInfo;
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
@@ -176,6 +177,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 */
int lastRowid; /* ROWID of most recent insert */
int priorNewRowid; /* Last randomly generated ROWID */
int onError; /* Default conflict algorithm */
@@ -198,9 +200,18 @@ struct sqlite {
#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
@@ -634,3 +645,4 @@ void sqliteEndWriteOperation(Parse*);
void sqliteExprMoveStrings(Expr*, int);
void sqliteExprListMoveStrings(ExprList*, int);
void sqliteSelectMoveStrings(Select*, int);
UserFunc *sqliteFindUserFunction(sqlite*,const char*,int,int,int);

View File

@@ -17,6 +17,7 @@
** if they are not used.
*/
#include <stdlib.h>
#include <string.h>
#include "sqlite.h"
/*

View File

@@ -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.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>
@@ -138,6 +138,16 @@ typedef struct Mem Mem;
#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
@@ -492,6 +502,73 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){
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.
*/
@@ -891,7 +968,7 @@ static char *zOpName[] = { 0,
"Le", "Gt", "Ge", "IsNull",
"NotNull", "Negative", "And", "Or",
"Not", "Concat", "Noop", "Strlen",
"Substr", "Limit",
"Substr", "UserFunc", "UserAgg", "Limit",
};
/*
@@ -1694,6 +1771,47 @@ case OP_Max: {
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

View File

@@ -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.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_
@@ -198,13 +198,14 @@ typedef struct VdbeOp VdbeOp;
#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

View File

@@ -11,7 +11,7 @@
# 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
@@ -183,7 +183,7 @@ do_test select1-2.19 {
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
#
@@ -222,7 +222,7 @@ do_test select1-3.8 {
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
#
@@ -241,7 +241,7 @@ do_test select1-4.3 {
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;

View File

@@ -25,6 +25,9 @@ chng {2002 Feb * (2.3.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)} {