Add code to parse a rank() function specification. And a tcl interface to add auxiliary functions to fts5.

FossilOrigin-Name: 9c1697a2aa1f601e6eb11704abe63a73c8105447
This commit is contained in:
dan 2014-12-01 20:05:00 +00:00
parent ca7fad3d2c
commit 89a89560d6
9 changed files with 669 additions and 21 deletions

View File

@ -898,7 +898,7 @@ static int fts5SeekCursor(Fts5Cursor *pCsr){
** INSERT Directives" section of the documentation. It should be updated if
** more commands are added to this function.
*/
static int fts5SpecialCommand(
static int fts5SpecialInsert(
Fts5Table *pTab, /* Fts5 table object */
sqlite3_value *pCmd, /* Value inserted into special column */
sqlite3_value *pVal /* Value inserted into rowid column */
@ -911,10 +911,12 @@ static int fts5SpecialCommand(
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
}else{
rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, z, pVal, &bError);
if( rc==SQLITE_OK && bError ){
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal);
if( rc==SQLITE_OK ){
if( bError ){
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal);
}
}
}
return rc;
@ -951,7 +953,7 @@ static int fts5UpdateMethod(
assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) );
if( nArg>1 && SQLITE_NULL!=sqlite3_value_type(apVal[2 + pConfig->nCol]) ){
return fts5SpecialCommand(pTab,
return fts5SpecialInsert(pTab,
apVal[2 + pConfig->nCol], apVal[2 + pConfig->nCol + 1]
);
}
@ -1676,7 +1678,7 @@ static void fts5Fts5Func(
char buf[8];
assert( nArg==0 );
assert( sizeof(buf)>=sizeof(pGlobal) );
memcpy(buf, pGlobal, sizeof(pGlobal));
memcpy(buf, (void*)&pGlobal, sizeof(pGlobal));
sqlite3_result_blob(pCtx, buf, sizeof(pGlobal), SQLITE_TRANSIENT);
}

View File

@ -81,6 +81,8 @@ struct Fts5Config {
int iCookie; /* Incremented when %_config is modified */
int pgsz; /* Approximate page size used in %_data */
int nAutomerge; /* 'automerge' setting */
char *zRank; /* Name of rank function */
char *zRankArgs; /* Arguments to rank function */
};
int sqlite3Fts5ConfigParse(

View File

@ -230,6 +230,8 @@ void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
}
sqlite3_free(pConfig->azCol);
sqlite3_free(pConfig->aPrefix);
sqlite3_free(pConfig->zRank);
sqlite3_free(pConfig->zRankArgs);
sqlite3_free(pConfig);
}
}
@ -302,6 +304,190 @@ int sqlite3Fts5Tokenize(
return pConfig->pTokApi->xTokenize(pConfig->pTok, pCtx, pText, nText, xToken);
}
/*
** Argument pIn points to a character that is part of a nul-terminated
** string. Return a pointer to the first character following *pIn in
** the string that is not a white-space character.
*/
static const char *fts5ConfigSkipWhitespace(const char *pIn){
const char *p = pIn;
if( p ){
while( *p==' ' ){ p++; }
}
return p;
}
/*
** Argument pIn points to a character that is part of a nul-terminated
** string. Return a pointer to the first character following *pIn in
** the string that is not a "bareword" character.
*/
static const char *fts5ConfigSkipBareword(const char *pIn){
const char *p = pIn;
while( *p && *p!=' ' && *p!=':' && *p!='!' && *p!='@'
&& *p!='#' && *p!='$' && *p!='%' && *p!='^' && *p!='&'
&& *p!='*' && *p!='(' && *p!=')'
){
p++;
}
if( p==pIn ) p = 0;
return p;
}
static int fts5_isdigit(char a){
return (a>='0' && a<='9');
}
static const char *fts5ConfigSkipLiteral(const char *pIn){
const char *p = pIn;
if( p ){
switch( *p ){
case 'n': case 'N':
if( sqlite3_strnicmp("null", p, 4)==0 ){
p = &p[4];
}else{
p = 0;
}
break;
case 'x': case 'X':
p++;
if( *p=='\'' ){
p++;
while( (*p>='a' && *p<='f')
|| (*p>='A' && *p<='F')
|| (*p>='0' && *p<='9')
){
p++;
}
if( *p=='\'' && 0==((p-pIn)%2) ){
p++;
}else{
p = 0;
}
}else{
p = 0;
}
break;
case '\'':
p++;
while( p ){
if( *p=='\'' ){
p++;
if( *p!='\'' ) break;
}
p++;
if( *p==0 ) p = 0;
}
break;
default:
/* maybe a number */
if( *p=='+' || *p=='-' ) p++;
while( fts5_isdigit(*p) ) p++;
/* At this point, if the literal was an integer, the parse is
** finished. Or, if it is a floating point value, it may continue
** with either a decimal point or an 'E' character. */
if( *p=='.' && fts5_isdigit(p[1]) ){
p += 2;
while( fts5_isdigit(*p) ) p++;
}
break;
}
}
return p;
}
/*
** Argument pIn points to the first character in what is expected to be
** a comma-separated list of SQL literals followed by a ')' character.
** If it actually is this, return a pointer to the ')'. Otherwise, return
** NULL to indicate a parse error.
*/
static const char *fts5ConfigSkipArgs(const char *pIn){
const char *p = pIn;
while( 1 ){
p = fts5ConfigSkipWhitespace(p);
p = fts5ConfigSkipLiteral(p);
p = fts5ConfigSkipWhitespace(p);
if( p==0 || *p==')' ) break;
if( *p!=',' ){
p = 0;
break;
}
p++;
}
return p;
}
/*
** Parameter zIn contains a rank() function specification. The format of
** this is:
**
** + Bareword (function name)
** + Open parenthesis - "("
** + Zero or more SQL literals in a comma separated list
** + Close parenthesis - ")"
*/
static int fts5ConfigParseRank(
const char *zIn, /* Input string */
char **pzRank, /* OUT: Rank function name */
char **pzRankArgs /* OUT: Rank function arguments */
){
const char *p = zIn;
const char *pRank;
char *zRank = 0;
char *zRankArgs = 0;
int rc = SQLITE_OK;
*pzRank = 0;
*pzRankArgs = 0;
p = fts5ConfigSkipWhitespace(p);
pRank = p;
p = fts5ConfigSkipBareword(p);
if( p ){
zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank);
if( zRank ) memcpy(zRank, pRank, p-pRank);
}else{
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
p = fts5ConfigSkipWhitespace(p);
if( *p!='(' ) rc = SQLITE_ERROR;
p++;
}
if( rc==SQLITE_OK ){
const char *pArgs = p;
p = fts5ConfigSkipArgs(p);
if( p==0 ){
rc = SQLITE_ERROR;
}else{
zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs);
if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs);
}
}
if( rc!=SQLITE_OK ){
sqlite3_free(zRank);
assert( zRankArgs==0 );
}else{
*pzRank = zRank;
*pzRankArgs = zRankArgs;
}
return rc;
}
int sqlite3Fts5ConfigSetValue(
Fts5Config *pConfig,
const char *zKey,
@ -339,7 +525,19 @@ int sqlite3Fts5ConfigSetValue(
}
else if( 0==sqlite3_stricmp(zKey, "rank") ){
// todo
const char *zIn = (const char*)sqlite3_value_text(pVal);
char *zRank;
char *zRankArgs;
rc = fts5ConfigParseRank(zIn, &zRank, &zRankArgs);
if( rc==SQLITE_OK ){
sqlite3_free(pConfig->zRank);
sqlite3_free(pConfig->zRankArgs);
pConfig->zRank = zRank;
pConfig->zRankArgs = zRankArgs;
}else if( rc==SQLITE_ERROR ){
rc = SQLITE_OK;
if( pbBadkey ) *pbBadkey = 1;
}
}else{
if( pbBadkey ) *pbBadkey = 1;
}

341
ext/fts5/fts5_tcl.c Normal file
View File

@ -0,0 +1,341 @@
/*
** 2014 Dec 01
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
*/
#include "fts5.h"
#include <tcl.h>
#include <string.h>
#include <assert.h>
/*************************************************************************
** This is a copy of the first part of the SqliteDb structure in
** tclsqlite.c. We need it here so that the get_sqlite_pointer routine
** can extract the sqlite3* pointer from an existing Tcl SQLite
** connection.
*/
struct SqliteDb {
sqlite3 *db;
};
/*
** Decode a pointer to an sqlite3 object.
*/
static int f5tDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **ppDb){
struct SqliteDb *p;
Tcl_CmdInfo cmdInfo;
char *z = Tcl_GetString(pObj);
if( Tcl_GetCommandInfo(interp, z, &cmdInfo) ){
p = (struct SqliteDb*)cmdInfo.objClientData;
*ppDb = p->db;
return TCL_OK;
}
return TCL_ERROR;
}
/* End of code that accesses the SqliteDb struct.
**************************************************************************/
typedef struct F5tFunction F5tFunction;
struct F5tFunction {
Tcl_Interp *interp;
Tcl_Obj *pScript;
};
typedef struct F5tApi F5tApi;
struct F5tApi {
const Fts5ExtensionApi *pApi;
Fts5Context *pFts;
};
/*
** api sub-command...
**
** Description...
*/
static int xF5tApi(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
struct Sub {
const char *zName;
int nArg;
const char *zMsg;
} aSub[] = {
{ "xRowid", 0, "" },
{ "xInstCount", 0, "" },
{ "xInst", 1, "IDX" },
{ "xColumnText", 1, "COL" },
{ "xColumnSize", 1, "COL" },
};
int rc;
int iSub = 0;
F5tApi *p = (F5tApi*)clientData;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
return TCL_ERROR;
}
rc = Tcl_GetIndexFromObjStruct(
interp, objv[1], aSub, sizeof(aSub[0]), "SUB-COMMAND", 0, &iSub
);
if( rc!=TCL_OK ) return rc;
if( aSub[iSub].nArg!=objc-2 ){
Tcl_WrongNumArgs(interp, 1, objv, aSub[iSub].zMsg);
return TCL_ERROR;
}
switch( iSub ){
case 0: { /* xRowid */
sqlite3_int64 iRowid = p->pApi->xRowid(p->pFts);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iRowid));
break;
}
case 1: { /* xInstCount */
int nInst;
rc = p->pApi->xInstCount(p->pFts, &nInst);
if( rc==SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewIntObj(nInst));
}
break;
}
case 2: { /* xInst */
int iIdx, ip, ic, io;
if( Tcl_GetIntFromObj(interp, objv[2], &iIdx) ){
return TCL_ERROR;
}
rc = p->pApi->xInst(p->pFts, iIdx, &ip, &ic, &io);
if( rc==SQLITE_OK ){
Tcl_Obj *pList = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ip));
Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ic));
Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(io));
Tcl_SetObjResult(interp, pList);
}
break;
}
case 3: { /* xColumnText */
const char *z = 0;
int n = 0;
int iCol;
if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
return TCL_ERROR;
}
rc = p->pApi->xColumnText(p->pFts, iCol, &z, &n);
if( rc==SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(z, n));
}
break;
}
case 4: { /* xColumnSize */
int n = 0;
int iCol;
if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
return TCL_ERROR;
}
rc = p->pApi->xColumnSize(p->pFts, iCol, &n);
if( rc==SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
}
break;
}
default:
assert( 0 );
break;
}
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error in api call", 0);
return TCL_ERROR;
}
return TCL_OK;
}
static void xF5tFunction(
const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
Fts5Context *pFts, /* First arg to pass to pApi functions */
sqlite3_context *pCtx, /* Context for returning result/error */
int nVal, /* Number of values in apVal[] array */
sqlite3_value **apVal /* Array of trailing arguments */
){
F5tFunction *p = (F5tFunction*)pApi->xUserData(pFts);
Tcl_Obj *pEval; /* Script to evaluate */
int i;
int rc;
static sqlite3_int64 iCmd = 0;
char zCmd[64];
F5tApi sApi;
sApi.pApi = pApi;
sApi.pFts = pFts;
sprintf(zCmd, "f5t_%lld", iCmd++);
Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0);
pEval = Tcl_DuplicateObj(p->pScript);
Tcl_IncrRefCount(pEval);
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1));
for(i=0; i<nVal; i++){
Tcl_Obj *pObj = 0;
switch( sqlite3_value_type(apVal[i]) ){
case SQLITE_TEXT:
pObj = Tcl_NewStringObj((const char*)sqlite3_value_text(apVal[i]), -1);
break;
case SQLITE_BLOB:
pObj = Tcl_NewByteArrayObj(
sqlite3_value_blob(apVal[i]), sqlite3_value_bytes(apVal[i])
);
break;
case SQLITE_INTEGER:
pObj = Tcl_NewWideIntObj(sqlite3_value_int64(apVal[i]));
break;
case SQLITE_FLOAT:
pObj = Tcl_NewDoubleObj(sqlite3_value_double(apVal[i]));
break;
default:
pObj = Tcl_NewObj();
break;
}
Tcl_ListObjAppendElement(p->interp, pEval, pObj);
}
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_GLOBAL_ONLY);
Tcl_DecrRefCount(pEval);
Tcl_DeleteCommand(p->interp, zCmd);
if( rc!=TCL_OK ){
sqlite3_result_error(pCtx, Tcl_GetStringResult(p->interp), -1);
}else{
Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
int n;
const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
char c = zType[0];
if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
/* Only return a BLOB type if the Tcl variable is a bytearray and
** has no string representation. */
unsigned char *data = Tcl_GetByteArrayFromObj(pVar, &n);
sqlite3_result_blob(pCtx, data, n, SQLITE_TRANSIENT);
}else if( c=='b' && strcmp(zType,"boolean")==0 ){
Tcl_GetIntFromObj(0, pVar, &n);
sqlite3_result_int(pCtx, n);
}else if( c=='d' && strcmp(zType,"double")==0 ){
double r;
Tcl_GetDoubleFromObj(0, pVar, &r);
sqlite3_result_double(pCtx, r);
}else if( (c=='w' && strcmp(zType,"wideInt")==0) ||
(c=='i' && strcmp(zType,"int")==0) ){
Tcl_WideInt v;
Tcl_GetWideIntFromObj(0, pVar, &v);
sqlite3_result_int64(pCtx, v);
}else{
unsigned char *data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
sqlite3_result_text(pCtx, (char *)data, n, SQLITE_TRANSIENT);
}
}
}
static void xF5tDestroy(void *pCtx){
F5tFunction *p = (F5tFunction*)pCtx;
Tcl_DecrRefCount(p->pScript);
ckfree(p);
}
/*
** sqlite3_fts5_create_function DB NAME SCRIPT
**
** Description...
*/
static int f5tCreateFunction(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
char *zName;
Tcl_Obj *pScript;
sqlite3 *db = 0;
sqlite3_stmt *pStmt = 0;
fts5_api *pApi = 0;
F5tFunction *pCtx = 0;
int rc;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT");
return TCL_ERROR;
}
if( f5tDbPointer(interp, objv[1], &db) ){
return TCL_ERROR;
}
zName = Tcl_GetString(objv[2]);
pScript = objv[3];
rc = sqlite3_prepare_v2(db, "SELECT fts5()", -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
return TCL_ERROR;
}
if( SQLITE_ROW==sqlite3_step(pStmt) ){
const void *pPtr = sqlite3_column_blob(pStmt, 0);
memcpy((void*)&pApi, pPtr, sizeof(pApi));
}
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
return TCL_ERROR;
}
pCtx = (F5tFunction*)ckalloc(sizeof(F5tFunction));
pCtx->interp = interp;
pCtx->pScript = pScript;
Tcl_IncrRefCount(pScript);
rc = pApi->xCreateFunction(
pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy
);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** Entry point.
*/
int Fts5tcl_Init(Tcl_Interp *interp){
static struct Cmd {
char *zName;
Tcl_ObjCmdProc *xProc;
void *clientData;
} aCmd[] = {
{ "sqlite3_fts5_create_function", f5tCreateFunction, 0 }
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
struct Cmd *p = &aCmd[i];
Tcl_CreateObjCommand(interp, p->zName, p->xProc, p->clientData, 0);
}
return TCL_OK;
}

View File

@ -312,7 +312,8 @@ TESTSRC += \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/wholenumber.c \
$(TOP)/ext/misc/vfslog.c
$(TOP)/ext/misc/vfslog.c \
$(TOP)/ext/fts5/fts5_tcl.c
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c

View File

@ -1,5 +1,5 @@
C Add\sa\scookie\smechanism\sto\sensure\sthat\sthe\s%_config\stable\sis\sre-read\sas\srequired.
D 2014-11-28T20:01:13.778
C Add\scode\sto\sparse\sa\srank()\sfunction\sspecification.\sAnd\sa\stcl\sinterface\sto\sadd\sauxiliary\sfunctions\sto\sfts5.
D 2014-12-01T20:05:00.761
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -104,16 +104,17 @@ F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
F ext/fts3/unicode/mkunicode.tcl dc6f268eb526710e2c6e496c372471d773d0c368
F ext/fts5/extract_api_docs.tcl 6320db4a1d0722a4e2069e661381ad75e9889786
F ext/fts5/fts5.c b3a2574be6921512133d228a922bc0bfb221c569
F ext/fts5/fts5.c 07f81ce7ebbffdd0acdad9eb090ff506fa503a10
F ext/fts5/fts5.h 72fc1e9995b1ddc254a487b9528614a83bd3dfb6
F ext/fts5/fts5Int.h a466dd67c909ac05ce8330acf13c7c5bfd244e15
F ext/fts5/fts5Int.h e16cf2213ae748ccc2c890f404fc341eb941d10b
F ext/fts5/fts5_aux.c 0e3e5fea6bf5772805afe14c95cb5f16e03e4b3f
F ext/fts5/fts5_buffer.c c79d67a5a611521f1f3b9d495981f22c02ef4bdb
F ext/fts5/fts5_config.c c95d89bd3ee119681f0aeff0fa34ee9cd18fc430
F ext/fts5/fts5_config.c bb87c2b915ae94002d94d02a6b1f81a0dac9c6db
F ext/fts5/fts5_expr.c d317be07d70223a6865444f17982570260b690a5
F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279
F ext/fts5/fts5_index.c 7e7023f3a29f104b44df2ca2474b296b8dfe447c
F ext/fts5/fts5_storage.c 0198c5976cefa5e8d3f1cfffa3587d0dd594fb2a
F ext/fts5/fts5_tcl.c 5272224faf9be129679da5e19d788f0307afc375
F ext/fts5/fts5_tokenize.c 8360c0d1ae0d4696f3cc13f7c67a2db6011cdc5b
F ext/fts5/fts5parse.y 777da8e5819f75c217982c79c29d014c293acac9
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
@ -159,7 +160,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 8a02fddafc05159c4b7d65200e912cf549f978c1
F main.mk 863a6f5cdcc3a47a9dcbedc9af37d3c0d4172935
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@ -244,7 +245,7 @@ F src/sqliteInt.h fccdc735c27b3dc12322fec7cdad8bc76be8d00b
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c e87c99e28a145943666b51b212dacae35fcea0bd
F src/tclsqlite.c 3a274c56cfc66b1f957afef201547213fc2ccecc
F src/test1.c 3c8bc491d2f8de5adbbf306533cefc343c733927
F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
@ -609,7 +610,7 @@ F test/fts5ah.test 788e923e60b5e7a559f672cfbf262b8b260ea176
F test/fts5ai.test aa2b5fd0f8d2cf59ac0211111e63cbca3b40ed7d
F test/fts5aj.test bc3d91bd012c7ca175cdf266c2074920bb5fa5ba
F test/fts5ak.test e55bb0f3fac1291d32bc9485a3ee55a7d76f4d5f
F test/fts5al.test 455b2bdc9f6ffb965a38a970a60c5075ee1e23bb
F test/fts5al.test d716a933bb88eb6986b02b985924fa42960b6eec
F test/fts5ea.test afaf3497b43add578384dc1fd26b0342738abe87
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
@ -1206,7 +1207,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 83491c56661ca78f96020ba68184bb3fb19e674f
R 14f6d2fef178e1939a8a8ad40901ad6e
P bb4a37b53de60da9ec8b9317eec14afa99690828
R efa8336057fcd1502b8cbf6d797345c7
U dan
Z bcf001d05010ed5ade28bb9d53b64e80
Z dc9192af5fedea55ad78c651e89e8c7b

View File

@ -1 +1 @@
bb4a37b53de60da9ec8b9317eec14afa99690828
9c1697a2aa1f601e6eb11704abe63a73c8105447

View File

@ -3698,6 +3698,7 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
extern int SqlitetestSyscall_Init(Tcl_Interp*);
extern int Fts5tcl_Init(Tcl_Interp *);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
@ -3740,6 +3741,7 @@ static void init_all(Tcl_Interp *interp){
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
SqlitetestSyscall_Init(interp);
Fts5tcl_Init(interp);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
Sqlitetestfts3_Init(interp);

View File

@ -39,5 +39,106 @@ do_execsql_test 1.3 {
SELECT * FROM ft1_config;
} {pgsz 64}
#--------------------------------------------------------------------------
# Test the logic for parsing the rank() function definition.
#
foreach {tn defn} {
1 "fname()"
2 "fname(1)"
3 "fname(1,2)"
4 "fname(null,NULL,nUlL)"
5 " fname ( null , NULL , nUlL ) "
6 "fname('abc')"
7 "fname('a''bc')"
8 "fname('''abc')"
9 "fname('abc''')"
7 "fname( 'a''bc' )"
8 "fname('''abc' )"
9 "fname( 'abc''' )"
10 "fname(X'1234ab')"
11 "myfunc(1.2)"
12 "myfunc(-1.0)"
13 "myfunc(.01,'abc')"
} {
do_execsql_test 2.1.$tn {
INSERT INTO ft1(ft1, rank) VALUES('rank', $defn);
}
}
foreach {tn defn} {
1 ""
2 "fname"
3 "fname(X'234ab')"
4 "myfunc(-1.,'abc')"
} {
do_test 2.2.$tn {
catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) }
} {1 {SQL logic error or missing database}}
}
#-------------------------------------------------------------------------
#
do_execsql_test 3.1 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1 VALUES('q w e r t y');
INSERT INTO t1 VALUES('y t r e w q');
}
proc argtest {cmd args} { return $args }
sqlite3_fts5_create_function db argtest argtest
do_execsql_test 3.2.1 {
SELECT argtest(t1, 123) FROM t1 WHERE t1 MATCH 'q'
} {123 123}
do_execsql_test 3.2.2 {
SELECT argtest(t1, 123, 456) FROM t1 WHERE t1 MATCH 'q'
} {{123 456} {123 456}}
proc rowidtest {cmd} { $cmd xRowid }
sqlite3_fts5_create_function db rowidtest rowidtest
do_execsql_test 3.3.1 {
SELECT rowidtest(t1) FROM t1 WHERE t1 MATCH 'q'
} {2 1}
proc insttest {cmd} {
set res [list]
for {set i 0} {$i < [$cmd xInstCount]} {incr i} {
lappend res [$cmd xInst $i]
}
set res
}
sqlite3_fts5_create_function db insttest insttest
do_execsql_test 3.4.1 {
SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'q'
} {
{{0 0 5}}
{{0 0 0}}
}
do_execsql_test 3.4.2 {
SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'r+e OR w'
} {
{{0 0 2} {1 0 4}}
{{1 0 1}}
}
proc coltest {cmd} {
list [$cmd xColumnSize 0] [$cmd xColumnText 0]
}
sqlite3_fts5_create_function db coltest coltest
do_execsql_test 3.4.1 {
SELECT coltest(t1) FROM t1 WHERE t1 MATCH 'q'
} {
{6 {y t r e w q}} {6 {q w e r t y}}
}
finish_test