From: drh Date: Sat, 16 Sep 2006 21:45:14 +0000 (+0000) Subject: Add the sqlite3_overload_function() API - part of the virtual table X-Git-Tag: version-3.6.10~2741 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b7481e70c558eb01d0dcc8b2e5f0511e0d5dfdac;p=thirdparty%2Fsqlite.git Add the sqlite3_overload_function() API - part of the virtual table interface. (CVS 3426) FossilOrigin-Name: aa7728f9f5b80dbb1b3db124f84b9166bf72bdd3 --- diff --git a/ext/fts1/fts1.c b/ext/fts1/fts1.c index a36bae35be..389b12163e 100644 --- a/ext/fts1/fts1.c +++ b/ext/fts1/fts1.c @@ -908,11 +908,14 @@ typedef struct Query { typedef struct Snippet { int nMatch; /* Total number of matches */ int nAlloc; /* Space allocated for aMatch[] */ - struct { /* One entry for each matching term */ - int iCol; /* The column that contains the match */ - int iTerm; /* The index in Query.pTerms[] of the matching term */ - int iStart; /* The offset to the first character of the term */ - int nByte; /* Number of bytes in the term */ + struct snippetMatch { /* One entry for each matching term */ + char exemplar; /* True if this match should be shown in the snippet */ + short int iCol; /* The column that contains the match */ + short int iTerm; /* The index in Query.pTerms[] of the matching term */ + short int nByte; /* Number of bytes in the term */ + short int nContext; /* Number of bytes of context for this match */ + int iStart; /* The offset to the first character of the term */ + int iContext; /* Start of the context */ } *aMatch; /* Points to space obtained from malloc */ char *zOffset; /* Text rendering of aMatch[] */ int nOffset; /* strlen(zOffset) */ @@ -2011,6 +2014,7 @@ static void snippetAppendMatch( int iStart, int nByte /* Offset and size of the match */ ){ int i; + struct snippetMatch *pMatch; if( p->nMatch+1>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + 10; p->aMatch = realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) ); @@ -2021,10 +2025,12 @@ static void snippetAppendMatch( } } i = p->nMatch++; - p->aMatch[i].iCol = iCol; - p->aMatch[i].iTerm = iTerm; - p->aMatch[i].iStart = iStart; - p->aMatch[i].nByte = nByte; + pMatch = &p->aMatch[i]; + pMatch->exemplar = 0; + pMatch->iCol = iCol; + pMatch->iTerm = iTerm; + pMatch->iStart = iStart; + pMatch->nByte = nByte; } /* @@ -2143,20 +2149,49 @@ static void snippetAllOffsets(fulltext_cursor *p){ */ static void snippetOffsetText(Snippet *p){ int i; + int cnt = 0; StringBuffer sb; char zBuf[200]; if( p->zOffset ) return; initStringBuffer(&sb); for(i=0; inMatch; i++){ + struct snippetMatch *pMatch = &p->aMatch[i]; zBuf[0] = ' '; - sprintf(&zBuf[i>0], "%d %d %d %d", p->aMatch[i].iCol, - p->aMatch[i].iTerm, p->aMatch[i].iStart, p->aMatch[i].nByte); + sprintf(&zBuf[cnt>0], "%d %d %d %d", pMatch->iCol, + pMatch->iTerm, pMatch->iStart, pMatch->nByte); append(&sb, zBuf); + cnt++; } p->zOffset = sb.s; p->nOffset = sb.len; } +/* +** Scan all matches in Snippet and mark the exemplars. Exemplars are +** matches that we definitely want to include in the snippet. +** +** Generally speaking, each keyword in the search phrase will have +** a single exemplar. When a keyword matches at multiple points +** within the document, the trick is figuring which of these matches +** should be the examplar. +*/ +static void snippetFindExemplars(Snippet *p, Query *pQ){ + int i, j; + for(i=0; inTerms; i++){ + for(j=0; jnMatch; j++){ + if( p->aMatch[j].iTerm==i ){ + p->aMatch[j].exemplar = 1; + break; + } + } + } +} + +static void snippetText(Snippet *p, Query *pQ){ + +} + + /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. diff --git a/manifest b/manifest index 0f884772a7..9263f99918 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sdocumentation\sfor\sthe\ssqlite3_extended_result_codes()\sAPI.\s(CVS\s3425) -D 2006-09-15T16:58:49 +C Add\sthe\ssqlite3_overload_function()\sAPI\s-\spart\sof\sthe\svirtual\stable\ninterface.\s(CVS\s3426) +D 2006-09-16T21:45:14 F Makefile.in cabd42d34340f49260bc2a7668c38eba8d4cfd99 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -21,7 +21,7 @@ F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b F ext/fts1/ft_hash.h 1a35e654a235c2c662d3ca0dfc3138ad60b8b7d5 -F ext/fts1/fts1.c ff2b92dd289123bcbd9959207341b4e77fc42317 +F ext/fts1/fts1.c 10d0c351fb1ee51ef3b8bd3eb29d1f7f91773ddb F ext/fts1/fts1.h 6060b8f62c1d925ea8356cb1a6598073eb9159a6 F ext/fts1/fts1_hash.c 3196cee866edbebb1c0521e21672e6d599965114 F ext/fts1/fts1_hash.h 957d378355ed29f672cd5add012ce8b088a5e089 @@ -57,13 +57,13 @@ F src/date.c d5519023569adf30892ff7be6deadf25ecdf1ecd F src/delete.c 804384761144fe1a5035b99f4bd7d706976831bd F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b F src/expr.c 0546cc60f08c426d96092dea0789d085aed3580e -F src/func.c dd9cea8ed3246d7a4c49fd01034d470d5702b8b0 +F src/func.c af537156dfeb91dbac8ffed4db6c4fa1a3164cc9 F src/hash.c 449f3d6620193aa557f5d86cbc5cc6b87702b185 F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564 F src/insert.c e9526ced19978a55687b55faea969b6ff2a53fb4 F src/legacy.c 2631df6a861f830d6b1c0fe92b9fdd745b2c0cd6 F src/loadext.c 5ffbf47d9ed168507e38ab7d09b1827ea3c9ca6d -F src/main.c e284169854d429cc6550a22b8b136ca70164ce5b +F src/main.c b9cf8dd198b0463b3de8954744e7cac6a1218126 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217 F src/os.c 59f05de8c5777c34876607114a2fbe55ae578235 F src/os.h 3fd6a022bafd620fdfd779a51dccb42f31c97f75 @@ -86,9 +86,9 @@ F src/random.c d40f8d356cecbd351ccfab6eaedd7ec1b54f5261 F src/select.c 0d4724930a1f34c747105ed1802fa4af0d8eb519 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c 672326e8d90394218509f1820ab0835e7ed2bc06 -F src/sqlite.h.in 0d1b16b059bfcd286779ef40dd567f1e642f0df5 +F src/sqlite.h.in bf935004029631fd93d119bcf2f7259b9cb9ad5e F src/sqlite3ext.h 11a046b3519c4b9b7709e6d6a95c3a36366f684a -F src/sqliteInt.h 74b39f55f30c3cff8935eb7adbd8842bca68331e +F src/sqliteInt.h c1d28fb13003c03fe7787d7ecc065d30530afc90 F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06 F src/tclsqlite.c e029f739bed90071789fe81a226d53e97a80a4d8 F src/test1.c 47f1d62d90fbf131dc5bbcd1b1aa18791fa3bc79 @@ -115,7 +115,7 @@ F src/vacuum.c fb65647c362589ed4ebb342c85665cadbcbf980c F src/vdbe.c a77869949ddd0afe01443611edb949e24e67c91c F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa F src/vdbeInt.h e3eaab262b67b84474625cfc38aec1125c32834b -F src/vdbeapi.c d5d3cb0701b66e541059a53c8a32d034bc86b7f7 +F src/vdbeapi.c f1858a5edc3a5e32d038514dd9e7e9091400a782 F src/vdbeaux.c 7d15dcf0eab43b0f6b1bb840dc2a3126eb370f5b F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c 26623176bf1c616aa478da958fac49502491a921 @@ -192,8 +192,8 @@ F test/fkey1.test 153004438d51e6769fb1ce165f6313972d6263ce F test/format4.test bf3bed3b13c63abfb3cfec232597a319a31d0bcc F test/fts1a.test 54fd9451c00fb91074d5abdc207b05dcba6d2d65 F test/fts1b.test 5742c32c69ec9667c8d32df5bc79aa416d5f363a -F test/fts1c.test 324895adf9855751f0ea7a6b735f8b7fcf28c3f8 -F test/func.test 7f2c91a948a0a177635835dc9afa078413c54ae1 +F test/fts1c.test 65a4e5a900ca0e0c9cd05612f9baf958d67a9d44 +F test/func.test 0ed54b5aeaad319f68016c033acfebef56f5874a F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d F test/index.test e65df12bed94b2903ee89987115e1578687e9266 @@ -315,7 +315,7 @@ F test/vacuum.test 37f998b841cb335397c26d9bbc3457182af2565f F test/vacuum2.test 5aea8c88a65cb29f7d175296e7c819c6158d838c F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/view.test 852bd4101e6d171c46ad682eb5c5faf662b2eba4 -F test/vtab1.test c7275c6c6575dfbbd975757f646f94477a6ea5c0 +F test/vtab1.test 30f82f7c3ee7ff9c0bba293d83b249488349c371 F test/vtab2.test 43763026b124e68785de05d3fbf957a4b5f81a0d F test/vtab3.test f38d6d7d19f08bffdadce4d5b8cba078f8118587 F test/vtab4.test a9d7104d41a787754a734740d7aa61c807a69f87 @@ -399,7 +399,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 5a18dd88498ca35ca1333d88c4635868d0b61073 -R db2e22fec0c46956a77b6266569df6f6 +P a6b3f6bed209dc27d36cd4e159159f73266e9911 +R 41c034b9dce9d7c6fde9c3e87e61b1e4 U drh -Z b17dc9813a2e708d175a29c8b1fb19be +Z 9301301f42636d8c9fe0c165b69e993c diff --git a/manifest.uuid b/manifest.uuid index e6133445b8..4b286c870d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a6b3f6bed209dc27d36cd4e159159f73266e9911 \ No newline at end of file +aa7728f9f5b80dbb1b3db124f84b9166bf72bdd3 \ No newline at end of file diff --git a/src/func.c b/src/func.c index 179d5d3663..11a6e1ad13 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.133 2006/08/19 11:34:01 drh Exp $ +** $Id: func.c,v 1.134 2006/09/16 21:45:14 drh Exp $ */ #include "sqliteInt.h" #include @@ -548,19 +548,6 @@ static void versionFunc( sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC); } -/* -** The MATCH() function is unimplemented. If anybody tries to use it, -** return an error. -*/ -static void matchStub( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - static const char zErr[] = "MATCH is not implemented"; - sqlite3_result_error(context, zErr, sizeof(zErr)-1); -} - /* ** EXPERIMENTAL - This is not an official function. The interface may @@ -1043,7 +1030,6 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ { "last_insert_rowid", 0, 1, SQLITE_UTF8, 0, last_insert_rowid }, { "changes", 0, 1, SQLITE_UTF8, 0, changes }, { "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes }, - { "match", 2, 0, SQLITE_UTF8, 0, matchStub }, #ifdef SQLITE_SOUNDEX { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc}, #endif @@ -1116,6 +1102,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ } } sqlite3RegisterDateTimeFunctions(db); + sqlite3_overload_function(db, "MATCH", 2); #ifdef SQLITE_SSE (void)sqlite3SseFunctions(db); #endif diff --git a/src/main.c b/src/main.c index 476577f118..8b2663d0b4 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.357 2006/09/15 07:28:50 drh Exp $ +** $Id: main.c,v 1.358 2006/09/16 21:45:14 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -541,6 +541,32 @@ int sqlite3_create_function16( } #endif + +/* +** Declare that a function has been overloaded by a virtual table. +** +** If the function already exists as a regular global function, then +** this routine is a no-op. If the function does not exist, then create +** a new one that always throws a run-time error. +** +** When virtual tables intend to provide an overloaded function, they +** should call this routine to make sure the global function exists. +** A global function must exist in order for name resolution to work +** properly. +*/ +int sqlite3_overload_function( + sqlite3 *db, + const char *zName, + int nArg +){ + int nName = strlen(zName); + if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ + sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, + 0, sqlite3InvalidFunction, 0, 0); + } + return sqlite3ApiExit(db, SQLITE_OK); +} + #ifndef SQLITE_OMIT_TRACE /* ** Register a trace function. The pArg from the previously registered trace @@ -908,8 +934,8 @@ static int openDatabase( ** is accessed. */ if( !sqlite3MallocFailed() ){ - sqlite3RegisterBuiltinFunctions(db); sqlite3Error(db, SQLITE_OK, 0); + sqlite3RegisterBuiltinFunctions(db); } db->magic = SQLITE_MAGIC_OPEN; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 3028148840..f54bf816b7 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.193 2006/09/15 07:28:50 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.194 2006/09/16 21:45:14 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -1782,6 +1782,24 @@ struct sqlite3_vtab_cursor { */ int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable); +/* +** Virtual tables can provide alternative implementations of functions +** using the xFindFunction method. But global versions of those functions +** must exist in order to be overloaded. +** +** This API makes sure a global version of a function with a particular +** name and number of parameters exists. If no such function exists +** before this API is called, a new function is created. The implementation +** of the new function always causes an exception to be thrown. So +** the new function is not good for anything by itself. Its only +** purpose is to be a place-holder function that can be overloaded +** by virtual tables. +** +** This API should be considered part of the virtual table interface, +** which is experimental and subject to change. +*/ +int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); + /* ** The interface to the virtual-table mechanism defined above (back up ** to a comment remarkably similar to this one) is currently considered diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3aa94ff34a..4f2cc4b873 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.527 2006/09/15 07:28:50 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.528 2006/09/16 21:45:14 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1874,6 +1874,7 @@ int sqlite3VtabCallConnect(Parse*, Table*); int sqlite3VtabCallDestroy(sqlite3*, int, const char *); int sqlite3VtabBegin(sqlite3 *, sqlite3_vtab *); FuncDef *sqlite3VtabOverloadFunction(FuncDef*, int nArg, Expr*); +void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**); #ifdef SQLITE_SSE #include "sseInt.h" diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 983f164cbd..4758e96730 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -267,6 +267,27 @@ void *sqlite3_user_data(sqlite3_context *p){ return p->pFunc->pUserData; } +/* +** The following is the implementation of an SQL function that always +** fails with an error message stating that the function is used in the +** wrong context. The sqlite3_overload_function() API might construct +** SQL function that use this routine so that the functions will exist +** for name resolution but are actually overloaded by the xFindFunction +** method of virtual tables. +*/ +void sqlite3InvalidFunction( + sqlite3_context *context, /* The function calling context */ + int argc, /* Number of arguments to the function */ + sqlite3_value **argv /* Value of each argument */ +){ + const char *zName = context->pFunc->zName; + char *zErr; + zErr = sqlite3MPrintf( + "unable to use function %s in the requested context", zName); + sqlite3_result_error(context, zErr, -1); + sqliteFree(zErr); +} + /* ** Allocate or return the aggregate context for a user function. A new ** context is allocated on the first call. Subsequent calls return the diff --git a/test/fts1c.test b/test/fts1c.test index 76bd854974..db0f19e0f6 100644 --- a/test/fts1c.test +++ b/test/fts1c.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS1 module. # -# $Id: fts1c.test,v 1.4 2006/09/15 07:28:51 drh Exp $ +# $Id: fts1c.test,v 1.5 2006/09/16 21:45:14 drh Exp $ # set testdir [file dirname $argv0] @@ -351,9 +351,9 @@ Dick Jenkins: East Marketeast WK '); INSERT INTO email([from],[to],subject,body) VALUES('david.forster@enron.com', 'eol.wide@enron.com', 'Change to Stack Manager', 'Effective immediately, there is a change to the Stack Manager which will -affect Inactive Children. +affect any Inactive Child. -Inactive Children with links to Parent products will not have their +An inactive Child with links to Parent products will not have their calculated prices updated until the Child product is Activated. When the Child Product is activated, the price will be recalculated and @@ -1066,5 +1066,56 @@ do_test fts1c-1.9 { } } {6 17 24 25 38 40 42 73 74} +# Some simple tests of the automatic "offset" column. In the sample +# data set above, only one message, number 20, contains the words +# "gas" and "reminder" in both body and subject. +# +do_test fts1c-2.1 { + execsql { + SELECT rowid, offset FROM email + WHERE _all MATCH 'gas reminder' + } +} {20 {2 0 42 3 2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}} +do_test fts1c-2.2 { + execsql { + SELECT rowid, offset FROM email + WHERE _all MATCH 'subject:gas reminder' + } +} {20 {2 0 42 3 2 1 54 8 3 1 54 8}} +do_test fts1c-2.3 { + execsql { + SELECT rowid, offset FROM email + WHERE _all MATCH 'body:gas reminder' + } +} {20 {2 1 54 8 3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}} +do_test fts1c-2.4 { + execsql { + SELECT rowid, offset FROM email + WHERE subject MATCH 'gas reminder' + } +} {20 {2 0 42 3 2 1 54 8}} +do_test fts1c-2.5 { + execsql { + SELECT rowid, offset FROM email + WHERE body MATCH 'gas reminder' + } +} {20 {3 0 42 3 3 1 54 8 3 0 129 3 3 0 143 3 3 0 240 3}} + +# Document 32 contains 5 instances of the world "child". But only +# 3 of them are paired with "product". Make sure only those instances +# that match the phrase appear in the offset list. +# +do_test fts1c-3.1 { + execsql { + SELECT rowid, offset FROM email + WHERE body MATCH 'child product' AND +rowid=32 + } +} {32 {3 0 94 5 3 0 114 5 3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7 3 1 493 7}} +do_test fts1c-3.2 { + execsql { + SELECT rowid, offset FROM email + WHERE body MATCH '"child product"' + } +} {32 {3 0 207 5 3 1 213 7 3 0 245 5 3 1 251 7 3 0 409 5 3 1 415 7}} finish_test diff --git a/test/func.test b/test/func.test index d6d609a280..4eca470b7d 100644 --- a/test/func.test +++ b/test/func.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing built-in functions. # -# $Id: func.test,v 1.54 2006/08/19 11:34:02 drh Exp $ +# $Id: func.test,v 1.55 2006/09/16 21:45:14 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -659,12 +659,12 @@ do_test func-19.2 { catchsql { SELECT 'abc' MATCH 'xyz'; } -} {1 {MATCH is not implemented}} +} {1 {unable to use function MATCH in the requested context}} do_test func-19.3 { catchsql { SELECT 'abc' NOT MATCH 'xyz'; } -} {1 {MATCH is not implemented}} +} {1 {unable to use function MATCH in the requested context}} do_test func-19.4 { catchsql { SELECT match(1,2,3); diff --git a/test/vtab1.test b/test/vtab1.test index 7804587abf..0ad04442c9 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is creating and dropping virtual tables. # -# $Id: vtab1.test,v 1.37 2006/09/02 22:14:59 drh Exp $ +# $Id: vtab1.test,v 1.38 2006/09/16 21:45:14 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -389,7 +389,7 @@ do_test vtab1-3.12 { catchsql { SELECT * FROM t1 WHERE a MATCH 'string'; } -} {1 {MATCH is not implemented}} +} {1 {unable to use function MATCH in the requested context}} do_test vtab1-3.13 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'treal'} \