]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Added support for user-defined normal functions. Support for user-defined
authordrh <drh@noemail.net>
Sat, 23 Feb 2002 23:45:45 +0000 (23:45 +0000)
committerdrh <drh@noemail.net>
Sat, 23 Feb 2002 23:45:45 +0000 (23:45 +0000)
aggregates is pending. (CVS 390)

FossilOrigin-Name: c490a1ff951c5d4a2de8e4f8d349189bfaef7f74

13 files changed:
manifest
manifest.uuid
src/expr.c
src/hash.c
src/hash.h
src/main.c
src/sqlite.h.in
src/sqliteInt.h
src/table.c
src/vdbe.c
src/vdbe.h
test/select1.test
www/changes.tcl

index 6d41043211c6a4a6b627f03c6a148b7e8131924c..f0269573f9c05c56a6fdfb932514c532edae421e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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
index ed839134c2f1be45785f3b45f6a54abd9b73801c..5cdd6e2062832499af9bb1adc795caa45510d448 100644 (file)
@@ -1 +1 @@
-67a135a051e7c96ddbfe85976539b4b8372c7026
\ No newline at end of file
+c490a1ff951c5d4a2de8e4f8d349189bfaef7f74
\ No newline at end of file
index 40a4a388eb2df0080038dac778e8b335f649c93f..d986c7c9a0b4aa7ce51e8e8485f1bf3655258223 100644 (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: { 
-          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;
@@ -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;
+}
index 965308e4f54e841490756702ab5eb764fdc9fe2e..ddac9c04822a1d2f90cca871699dc57b82a366e7 100644 (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++;
index d5c7f625102ddb948549fc8ab09e25c51b09136a..e51396e356fd33c2be7a20ae1eb7e4d71a2452ce 100644 (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*);
 
index 4969f3551fc36ec8c432d813406c49998020852a..4253ae12514b7a4867e20507ffc5d21a4fc6d038 100644 (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;
+}
index 12d07ac18237dad24e5eca9151416ffe429f68af..14c5f55792aedd4e5324ea7fc3f2d3099b582b90 100644 (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
index 59d17221b3e1d5d98db82b90fd7d46a627918ccb..82d7121782326bd9a83e2367fdb72b35989bbabd 100644 (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);
index e957cf3d56ee864dcd80e5ab09d897ad1cdc7241..cc01f7c2be88b66fbbf9b188fadff345c7b9a822 100644 (file)
@@ -17,6 +17,7 @@
 ** if they are not used.
 */
 #include <stdlib.h>
+#include <string.h>
 #include "sqlite.h"
 
 /*
index acacaa91d3dfbe975352a99e2c7c1effbbf41600..5d6faa929b1a63b2cb9e7bfe7bd540bc1c0701d8 100644 (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
index 4bde7b342cbb7ba856b351169ae2904ae0044112..a3b66a29c6df7063717d8a5861315474fdff2db6 100644 (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
index 399d64bdcb3264c814c0da048ecbfdcfb1776143..b1b18bf67558adfbea4e6f64d1429c2512f1b43d 100644 (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;
index 64e5dcd7d9b71d9ea9a97f3a939a76a5656119f0..1e22c76dd8e852c622a1a140ba0a571fba9cbd6c 100644 (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)} {