mirror of https://github.com/sqlite/sqlite
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:
parent
ca7fad3d2c
commit
89a89560d6
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
3
main.mk
3
main.mk
|
@ -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
|
||||
|
|
23
manifest
23
manifest
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
bb4a37b53de60da9ec8b9317eec14afa99690828
|
||||
9c1697a2aa1f601e6eb11704abe63a73c8105447
|
|
@ -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);
|
||||
|
|
101
test/fts5al.test
101
test/fts5al.test
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue