From e3602be8fd576880855289c201022ddc06e0135e Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 9 Sep 2008 12:31:33 +0000 Subject: [PATCH] Calling sqlite3_create_function with nArg==(-1) does not override prior calls on the same function name with nArg>=0. Ticket #3345. Add the new -argcount option to the "function" method in the TCL interface. (CVS 5684) FossilOrigin-Name: 5aa5b8044a14f59559c1839dc0799b0d2f990809 --- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/callback.c | 19 ++++++++++++------- src/tclsqlite.c | 26 ++++++++++++++++++++------ test/like.test | 44 ++++++++++++++++++++++++++++++++++++++++++-- test/tclsqlite.test | 4 ++-- 6 files changed, 87 insertions(+), 28 deletions(-) diff --git a/manifest b/manifest index 87e9c7e73e..4865c0b563 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sC++ism\sin\spager.c\s(variable\suseAtomicWrite\snot\sdeclard\sat\sthe\stop\sof\sits\sscope).\s(CVS\s5683) -D 2008-09-08T15:35:07 +C Calling\ssqlite3_create_function\swith\snArg==(-1)\sdoes\snot\soverride\sprior\ncalls\son\sthe\ssame\sfunction\sname\swith\snArg>=0.\s\sTicket\s#3345.\s\sAdd\sthe\nnew\s-argcount\soption\sto\sthe\s"function"\smethod\sin\sthe\sTCL\sinterface.\s(CVS\s5684) +D 2008-09-09T12:31:34 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in d15a7ebfe5e057a72a49805ffb302dbb601c8329 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -103,7 +103,7 @@ F src/btree.c 6ea37de364f483496f60e47c996b429a9f313d54 F src/btree.h 6371c5e599fab391a150c96afbc10062b276d107 F src/btreeInt.h ab18c7b4980314e9e4b402e5dcde09f3c2545576 F src/build.c 160c71acca8f643f436ed6c1ee2f684c88df4dfe -F src/callback.c cfc8d4f95fd831e25532d0fee2af5306283f4835 +F src/callback.c 7a40fd44da3eb89e7f6eff30aa6f940c45d73a97 F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c F src/date.c 5c092296c03d658e84884121a694150964d6861d F src/delete.c bae6684aa02e1f7cf6328023157c91d9cf94200b @@ -155,7 +155,7 @@ F src/sqliteInt.h 4a43cd9dcb7d5a6664a981f08e64d555acbca1bc F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8 -F src/tclsqlite.c 7f5c49f4347c197e869fef86b35b2251ca45f7c2 +F src/tclsqlite.c a6a346f42322ae1df980228306da6c3cea4dd680 F src/test1.c 349606445a5a938e9fa9b5e410413632c6c25f87 F src/test2.c eaa77124786649eedf47d3c5e94d8070c0da228f F src/test3.c e85b7ce5c28c3ce7fbdbf7f98e1467b19786c62b @@ -391,7 +391,7 @@ F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19 F test/jrnlmode.test 1ab9729d6bbb6fb355cbb4bb41f1f0c271fc251a F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51 F test/laststmtchanges.test 18ead86c8a87ade949a1d5658f6dc4bb111d1b02 -F test/like.test 2a3ddbd5d91503f914eabae67a47c4196fe33a58 +F test/like.test fef924922828d5a2a5bff80b9bdd9ff57a1ca500 F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da F test/limit.test 2db7b3b34fb925b8e847d583d2eb67531d0ce67e F test/loadext.test 4972cb5a12ba70f23f827452b8265be08bb6d8cd @@ -503,7 +503,7 @@ F test/substr.test 4be572ac017143e59b4058dc75c91a0d0dc6d4e0 F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3 F test/table.test 13b1c2e2fb4727b35ee1fb7641fc469214fd2455 F test/tableapi.test 505031f15b18a750184d967d2c896cf88fcc969c -F test/tclsqlite.test 9689ded2f8909362e869eb8bd8c2f5219f4f12af +F test/tclsqlite.test b2074cf37a6f7b3fb0544a7bac7bc8ff3ac6ea77 F test/tempdb.test b88ac8a19823cf771d742bf61eef93ef337c06b1 F test/temptable.test 19b851b9e3e64d91e9867619b2a3f5fffee6e125 F test/tester.tcl 12fd8394caeb71f7d961707da8668756389bc9d3 @@ -633,7 +633,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 8b600ed083d48784df4b1da1320a01bebbf233d7 -R ffda790045fe743907465f52ace9412e -U danielk1977 -Z 3f4db5a2e713c7718c372d8ef3c17aff +P a6dee85b823355cb381163c93c8366aa4395ae6a +R ef041795a1f812a73913c8f5ffd83ed9 +U drh +Z ef224dd97bd8e2824d90a691c3098d84 diff --git a/manifest.uuid b/manifest.uuid index 03fed3fad1..84670ba53a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a6dee85b823355cb381163c93c8366aa4395ae6a \ No newline at end of file +5aa5b8044a14f59559c1839dc0799b0d2f990809 \ No newline at end of file diff --git a/src/callback.c b/src/callback.c index e891f3321d..8e8cd1751c 100644 --- a/src/callback.c +++ b/src/callback.c @@ -13,7 +13,7 @@ ** This file contains functions used to access the internal hash tables ** of user defined functions and collation sequences. ** -** $Id: callback.c,v 1.30 2008/09/01 18:34:20 danielk1977 Exp $ +** $Id: callback.c,v 1.31 2008/09/09 12:31:34 drh Exp $ */ #include "sqliteInt.h" @@ -341,7 +341,8 @@ FuncDef *sqlite3FindFunction( if( nArg<-1 ) nArg = -1; h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a); - + /* First search for a match amongst the application-defined functions. + */ p = functionSearch(&db->aFunc, h, zName, nName); while( p ){ int score = matchQuality(p, nArg, enc); @@ -352,9 +353,13 @@ FuncDef *sqlite3FindFunction( p = p->pNext; } - /* If the createFlag parameter is false and no match was found amongst - ** the custom functions stored in sqlite3.aFunc, try to find a built-in - ** function to use. + /* If no match is found, search the built-in functions. + ** + ** Except, if createFlag is true, that means that we are trying to + ** install a new function. Whatever FuncDef structure is returned will + ** have fields overwritten with new information appropriate for the + ** new function. But the FuncDefs for built-in functions are read-only. + ** So we must not search for built-ins when creating a new function. */ if( !createFlag && !pBest ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); @@ -369,11 +374,11 @@ FuncDef *sqlite3FindFunction( } } - /* If the createFlag parameter is true, and the seach did not reveal an + /* If the createFlag parameter is true and the search did not reveal an ** exact match for the name, number of arguments and encoding, then add a ** new entry to the hash table and return it. */ - if( createFlag && bestScore<6 && + if( createFlag && (bestScore<6 || pBest->nArg!=nArg) && (pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){ pBest->zName = (char *)&pBest[1]; pBest->nArg = nArg; diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 083b31c290..152a444641 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -12,7 +12,7 @@ ** A TCL Interface to SQLite. Append this file to sqlite3.c and ** compile the whole thing to build a TCL-enabled version of SQLite. ** -** $Id: tclsqlite.c,v 1.223 2008/09/03 01:08:01 drh Exp $ +** $Id: tclsqlite.c,v 1.224 2008/09/09 12:31:34 drh Exp $ */ #include "tcl.h" #include @@ -1872,7 +1872,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* - ** $db function NAME SCRIPT + ** $db function NAME [-argcount N] SCRIPT ** ** Create a new SQL function called NAME. Whenever that function is ** called, invoke SCRIPT to evaluate the function. @@ -1881,12 +1881,26 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ SqlFunc *pFunc; Tcl_Obj *pScript; char *zName; - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT"); + int nArg = -1; + if( objc==6 ){ + const char *z = Tcl_GetString(objv[3]); + int n = strlen(z); + if( n>2 && strncmp(z, "-argcount",n)==0 ){ + if( Tcl_GetIntFromObj(interp, objv[4], &nArg) ) return TCL_ERROR; + if( nArg<0 ){ + Tcl_AppendResult(interp, "number of arguments must be non-negative", + (char*)0); + return TCL_ERROR; + } + } + pScript = objv[5]; + }else if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "NAME [-argcount N] SCRIPT"); return TCL_ERROR; + }else{ + pScript = objv[3]; } zName = Tcl_GetStringFromObj(objv[2], 0); - pScript = objv[3]; pFunc = findSqlFunc(pDb, zName); if( pFunc==0 ) return TCL_ERROR; if( pFunc->pScript ){ @@ -1895,7 +1909,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ pFunc->pScript = pScript; Tcl_IncrRefCount(pScript); pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript); - rc = sqlite3_create_function(pDb->db, zName, -1, SQLITE_UTF8, + rc = sqlite3_create_function(pDb->db, zName, nArg, SQLITE_UTF8, pFunc, tclSqlFunc, 0, 0); if( rc!=SQLITE_OK ){ rc = TCL_ERROR; diff --git a/test/like.test b/test/like.test index 57f1086d91..38951cb03d 100644 --- a/test/like.test +++ b/test/like.test @@ -13,7 +13,7 @@ # in particular the optimizations that occur to help those operators # run faster. # -# $Id: like.test,v 1.9 2008/02/23 21:55:40 drh Exp $ +# $Id: like.test,v 1.10 2008/09/09 12:31:34 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -120,7 +120,7 @@ do_test like-2.3 { proc test_match {a b} { return [string match $a $b] } - db function match test_match + db function match -argcount 2 test_match execsql { SELECT x FROM t1 WHERE x MATCH '*abc*' ORDER BY 1; } @@ -511,4 +511,44 @@ do_test like-7.1 { } } {a} +# ticket #3345. +# +# Overloading the LIKE function with -1 for the number of arguments +# will overload both the 2-argument and the 3-argument LIKE. +# +do_test like-8.1 { + db eval { + CREATE TABLE t8(x); + INSERT INTO t8 VALUES('abcdef'); + INSERT INTO t8 VALUES('ghijkl'); + INSERT INTO t8 VALUES('mnopqr'); + SELECT 1, x FROM t8 WHERE x LIKE '%h%'; + SELECT 2, x FROM t8 WHERE x LIKE '%h%' ESCAPE 'x'; + } +} {1 ghijkl 2 ghijkl} +do_test like-8.2 { + proc newlike {args} {return 1} ;# Alternative LIKE is always return TRUE + db function like newlike ;# Uses -1 for nArg in sqlite3_create_function + db cache flush + db eval { + SELECT 1, x FROM t8 WHERE x LIKE '%h%'; + SELECT 2, x FROM t8 WHERE x LIKE '%h%' ESCAPE 'x'; + } +} {1 ghijkl 2 ghijkl} +do_test like-8.3 { + db function like -argcount 2 newlike + db eval { + SELECT 1, x FROM t8 WHERE x LIKE '%h%'; + SELECT 2, x FROM t8 WHERE x LIKE '%h%' ESCAPE 'x'; + } +} {1 abcdef 1 ghijkl 1 mnopqr 2 ghijkl} +do_test like-8.4 { + db function like -argcount 3 newlike + db eval { + SELECT 1, x FROM t8 WHERE x LIKE '%h%'; + SELECT 2, x FROM t8 WHERE x LIKE '%h%' ESCAPE 'x'; + } +} {1 abcdef 1 ghijkl 1 mnopqr 2 abcdef 2 ghijkl 2 mnopqr} + + finish_test diff --git a/test/tclsqlite.test b/test/tclsqlite.test index 741a07eac5..c95b7acb03 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -15,7 +15,7 @@ # interface is pretty well tested. This file contains some addition # tests for fringe issues that the main test suite does not cover. # -# $Id: tclsqlite.test,v 1.68 2008/09/03 00:43:15 drh Exp $ +# $Id: tclsqlite.test,v 1.69 2008/09/09 12:31:34 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -118,7 +118,7 @@ do_test tcl-1.14 { do_test tcl-1.15 { set v [catch {db function} msg] lappend v $msg -} {1 {wrong # args: should be "db function NAME SCRIPT"}} +} {1 {wrong # args: should be "db function NAME [-argcount N] SCRIPT"}} do_test tcl-1.16 { set v [catch {db last_insert_rowid xyz} msg] lappend v $msg