Begin adding interface for auxiliary functions.
FossilOrigin-Name: 1e2a7ba0889093416455f488fca893eaeb195d45
This commit is contained in:
parent
48eecfb8b9
commit
9cfd51f587
356
ext/fts5/fts5.c
356
ext/fts5/fts5.c
@ -17,6 +17,34 @@
|
||||
|
||||
typedef struct Fts5Table Fts5Table;
|
||||
typedef struct Fts5Cursor Fts5Cursor;
|
||||
typedef struct Fts5Global Fts5Global;
|
||||
typedef struct Fts5Auxiliary Fts5Auxiliary;
|
||||
|
||||
/*
|
||||
** A single object of this type is allocated when the FTS5 module is
|
||||
** registered with a database handle. It is used to store pointers to
|
||||
** all registered FTS5 extensions - tokenizers and auxiliary functions.
|
||||
*/
|
||||
struct Fts5Global {
|
||||
sqlite3 *db; /* Associated database connection */
|
||||
i64 iNextId; /* Used to allocate unique cursor ids */
|
||||
Fts5Auxiliary *pAux; /* First in list of all aux. functions */
|
||||
Fts5Cursor *pCsr; /* First in list of all open cursors */
|
||||
};
|
||||
|
||||
/*
|
||||
** Each auxiliary function registered with the FTS5 module is represented
|
||||
** by an object of the following type. All such objects are stored as part
|
||||
** of the Fts5Global.pAux list.
|
||||
*/
|
||||
struct Fts5Auxiliary {
|
||||
Fts5Global *pGlobal; /* Global context for this function */
|
||||
char *zFunc; /* Function name (nul-terminated) */
|
||||
void *pUserData; /* User-data pointer */
|
||||
fts5_extension_function xFunc; /* Callback function */
|
||||
void (*xDestroy)(void*); /* Destructor function */
|
||||
Fts5Auxiliary *pNext; /* Next registered auxiliary function */
|
||||
};
|
||||
|
||||
/*
|
||||
** Virtual-table object.
|
||||
@ -26,6 +54,12 @@ struct Fts5Table {
|
||||
Fts5Config *pConfig; /* Virtual table configuration */
|
||||
Fts5Index *pIndex; /* Full-text index */
|
||||
Fts5Storage *pStorage; /* Document store */
|
||||
Fts5Global *pGlobal; /* Global (connection wide) data */
|
||||
};
|
||||
|
||||
struct Fts5MatchPhrase {
|
||||
Fts5Buffer *pPoslist; /* Pointer to current poslist */
|
||||
int nTerm; /* Size of phrase in terms */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -37,7 +71,12 @@ struct Fts5Cursor {
|
||||
sqlite3_stmt *pStmt; /* Statement used to read %_content */
|
||||
int bEof; /* True at EOF */
|
||||
Fts5Expr *pExpr; /* Expression for MATCH queries */
|
||||
int bSeekRequired;
|
||||
int bSeekRequired; /* True if seek is required */
|
||||
Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */
|
||||
|
||||
/* Variables used by auxiliary functions */
|
||||
i64 iCsrId; /* Cursor id */
|
||||
Fts5Auxiliary *pAux; /* Currently executing function */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -108,6 +147,7 @@ static int fts5InitVtab(
|
||||
}else{
|
||||
memset(pTab, 0, sizeof(Fts5Table));
|
||||
pTab->pConfig = pConfig;
|
||||
pTab->pGlobal = (Fts5Global*)pAux;
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,11 +274,17 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
** Implementation of xOpen method.
|
||||
*/
|
||||
static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
|
||||
Fts5Cursor *pCsr;
|
||||
int rc = SQLITE_OK;
|
||||
Fts5Table *pTab = (Fts5Table*)pVTab;
|
||||
Fts5Cursor *pCsr; /* New cursor object */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
|
||||
pCsr = (Fts5Cursor*)sqlite3_malloc(sizeof(Fts5Cursor));
|
||||
if( pCsr ){
|
||||
Fts5Global *pGlobal = pTab->pGlobal;
|
||||
memset(pCsr, 0, sizeof(Fts5Cursor));
|
||||
pCsr->pNext = pGlobal->pCsr;
|
||||
pGlobal->pCsr = pCsr;
|
||||
pCsr->iCsrId = ++pGlobal->iNextId;
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
@ -260,11 +306,17 @@ static int fts5StmtType(int idxNum){
|
||||
static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
|
||||
Fts5Cursor **pp;
|
||||
if( pCsr->pStmt ){
|
||||
int eStmt = fts5StmtType(pCsr->idxNum);
|
||||
sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt);
|
||||
}
|
||||
sqlite3Fts5ExprFree(pCsr->pExpr);
|
||||
|
||||
/* Remove the cursor from the Fts5Global.pCsr list */
|
||||
for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext);
|
||||
*pp = pCsr->pNext;
|
||||
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -373,22 +425,14 @@ static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xColumn method, called by SQLite to request a value from
|
||||
** the row that the supplied cursor currently points to.
|
||||
/*
|
||||
** If the cursor requires seeking (bSeekRequired flag is set), seek it.
|
||||
** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise.
|
||||
*/
|
||||
static int fts5ColumnMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
|
||||
int iCol /* Index of column to read value from */
|
||||
){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
|
||||
int ePlan = FTS5_PLAN(pCsr->idxNum);
|
||||
static int fts5SeekCursor(Fts5Cursor *pCsr){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( pCsr->bEof==0 );
|
||||
if( pCsr->bSeekRequired ){
|
||||
assert( ePlan==FTS5_PLAN_MATCH && pCsr->pExpr );
|
||||
assert( pCsr->pExpr );
|
||||
sqlite3_reset(pCsr->pStmt);
|
||||
sqlite3_bind_int64(pCsr->pStmt, 1, sqlite3Fts5ExprRowid(pCsr->pExpr));
|
||||
rc = sqlite3_step(pCsr->pStmt);
|
||||
@ -401,9 +445,35 @@ static int fts5ColumnMethod(
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
|
||||
/*
|
||||
** This is the xColumn method, called by SQLite to request a value from
|
||||
** the row that the supplied cursor currently points to.
|
||||
*/
|
||||
static int fts5ColumnMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
|
||||
int iCol /* Index of column to read value from */
|
||||
){
|
||||
Fts5Config *pConfig = ((Fts5Table*)(pCursor->pVtab))->pConfig;
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( pCsr->bEof==0 );
|
||||
|
||||
if( iCol==pConfig->nCol ){
|
||||
/* User is requesting the value of the special column with the same name
|
||||
** as the table. Return the cursor integer id number. This value is only
|
||||
** useful in that it may be passed as the first argument to an FTS5
|
||||
** auxiliary function. */
|
||||
sqlite3_result_int64(pCtx, pCsr->iCsrId);
|
||||
}else{
|
||||
rc = fts5SeekCursor(pCsr);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -513,6 +583,121 @@ static int fts5RollbackMethod(sqlite3_vtab *pVtab){
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void *fts5ApiUserData(Fts5Context *pCtx){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
return pCsr->pAux->pUserData;
|
||||
}
|
||||
|
||||
static int fts5ApiColumnCount(Fts5Context *pCtx){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
return ((Fts5Table*)(pCsr->base.pVtab))->pConfig->nCol;
|
||||
}
|
||||
|
||||
static int fts5ApiColumnAvgSize(Fts5Context *pCtx, int iCol, int *pnToken){
|
||||
assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fts5ApiTokenize(
|
||||
Fts5Context *pCtx,
|
||||
const char *pText, int nText,
|
||||
void *pUserData,
|
||||
int (*xToken)(void*, const char*, int, int, int, int)
|
||||
){
|
||||
assert( 0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fts5ApiPhraseCount(Fts5Context *pCtx){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
return sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
|
||||
}
|
||||
|
||||
static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase);
|
||||
}
|
||||
|
||||
static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
return sqlite3Fts5ExprRowid(pCsr->pExpr);
|
||||
}
|
||||
|
||||
static int fts5ApiColumnText(
|
||||
Fts5Context *pCtx,
|
||||
int iCol,
|
||||
const char **pz,
|
||||
int *pn
|
||||
){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
int rc = fts5SeekCursor(pCsr);
|
||||
if( rc==SQLITE_OK ){
|
||||
*pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol);
|
||||
*pn = sqlite3_column_bytes(pCsr->pStmt, iCol);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){
|
||||
assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fts5ApiPoslist(
|
||||
Fts5Context *pCtx,
|
||||
int iPhrase,
|
||||
int *pi,
|
||||
int *piCol,
|
||||
int *piOff
|
||||
){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
const u8 *a; int n; /* Poslist for phrase iPhrase */
|
||||
n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, &a);
|
||||
return sqlite3Fts5PoslistNext(a, n, pi, piCol, piOff);
|
||||
}
|
||||
|
||||
static void fts5ApiCallback(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
static const Fts5ExtensionApi sApi = {
|
||||
1, /* iVersion */
|
||||
fts5ApiUserData,
|
||||
fts5ApiColumnCount,
|
||||
fts5ApiColumnAvgSize,
|
||||
fts5ApiTokenize,
|
||||
fts5ApiPhraseCount,
|
||||
fts5ApiPhraseSize,
|
||||
fts5ApiRowid,
|
||||
fts5ApiColumnText,
|
||||
fts5ApiColumnSize,
|
||||
fts5ApiPoslist,
|
||||
};
|
||||
|
||||
Fts5Auxiliary *pAux;
|
||||
Fts5Cursor *pCsr;
|
||||
i64 iCsrId;
|
||||
|
||||
assert( argc>=1 );
|
||||
pAux = (Fts5Auxiliary*)sqlite3_user_data(context);
|
||||
iCsrId = sqlite3_value_int64(argv[0]);
|
||||
|
||||
for(pCsr=pAux->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
|
||||
if( pCsr->iCsrId==iCsrId ) break;
|
||||
}
|
||||
if( pCsr==0 ){
|
||||
char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
}else{
|
||||
assert( pCsr->pAux==0 );
|
||||
pCsr->pAux = pAux;
|
||||
pAux->xFunc(&sApi, (Fts5Context*)pCsr, context, argc-1, &argv[1]);
|
||||
pCsr->pAux = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This routine implements the xFindFunction method for the FTS3
|
||||
** virtual table.
|
||||
@ -522,8 +707,19 @@ static int fts5FindFunctionMethod(
|
||||
int nArg, /* Number of SQL function arguments */
|
||||
const char *zName, /* Name of SQL function */
|
||||
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
|
||||
void **ppArg /* Unused */
|
||||
void **ppArg /* OUT: User data for *pxFunc */
|
||||
){
|
||||
Fts5Table *pTab = (Fts5Table*)pVtab;
|
||||
Fts5Auxiliary *pAux;
|
||||
|
||||
for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){
|
||||
if( sqlite3_stricmp(zName, pAux->zFunc)==0 ){
|
||||
*pxFunc = fts5ApiCallback;
|
||||
*ppArg = (void*)pAux;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* No function of the specified name was found. Return 0. */
|
||||
return 0;
|
||||
}
|
||||
@ -567,37 +763,99 @@ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static const sqlite3_module fts5Module = {
|
||||
/* iVersion */ 2,
|
||||
/* xCreate */ fts5CreateMethod,
|
||||
/* xConnect */ fts5ConnectMethod,
|
||||
/* xBestIndex */ fts5BestIndexMethod,
|
||||
/* xDisconnect */ fts5DisconnectMethod,
|
||||
/* xDestroy */ fts5DestroyMethod,
|
||||
/* xOpen */ fts5OpenMethod,
|
||||
/* xClose */ fts5CloseMethod,
|
||||
/* xFilter */ fts5FilterMethod,
|
||||
/* xNext */ fts5NextMethod,
|
||||
/* xEof */ fts5EofMethod,
|
||||
/* xColumn */ fts5ColumnMethod,
|
||||
/* xRowid */ fts5RowidMethod,
|
||||
/* xUpdate */ fts5UpdateMethod,
|
||||
/* xBegin */ fts5BeginMethod,
|
||||
/* xSync */ fts5SyncMethod,
|
||||
/* xCommit */ fts5CommitMethod,
|
||||
/* xRollback */ fts5RollbackMethod,
|
||||
/* xFindFunction */ fts5FindFunctionMethod,
|
||||
/* xRename */ fts5RenameMethod,
|
||||
/* xSavepoint */ fts5SavepointMethod,
|
||||
/* xRelease */ fts5ReleaseMethod,
|
||||
/* xRollbackTo */ fts5RollbackToMethod,
|
||||
};
|
||||
/*
|
||||
** Register a new auxiliary function with global context pGlobal.
|
||||
*/
|
||||
int sqlite3Fts5CreateAux(
|
||||
Fts5Global *pGlobal, /* Global context (one per db handle) */
|
||||
const char *zName, /* Name of new function */
|
||||
void *pUserData, /* User data for aux. function */
|
||||
fts5_extension_function xFunc, /* Aux. function implementation */
|
||||
void(*xDestroy)(void*) /* Destructor for pUserData */
|
||||
){
|
||||
int rc = sqlite3_overload_function(pGlobal->db, zName, -1);
|
||||
if( rc==SQLITE_OK ){
|
||||
Fts5Auxiliary *pAux;
|
||||
int nByte; /* Bytes of space to allocate */
|
||||
|
||||
nByte = sizeof(Fts5Auxiliary) + strlen(zName) + 1;
|
||||
pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte);
|
||||
if( pAux ){
|
||||
memset(pAux, 0, nByte);
|
||||
pAux->zFunc = (char*)&pAux[1];
|
||||
strcpy(pAux->zFunc, zName);
|
||||
pAux->pGlobal = pGlobal;
|
||||
pAux->pUserData = pUserData;
|
||||
pAux->xFunc = xFunc;
|
||||
pAux->xDestroy = xDestroy;
|
||||
pAux->pNext = pGlobal->pAux;
|
||||
pGlobal->pAux = pAux;
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
int sqlite3Fts5Init(sqlite3 *db){
|
||||
int rc;
|
||||
rc = sqlite3_create_module_v2(db, "fts5", &fts5Module, 0, 0);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(db);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void fts5ModuleDestroy(void *pCtx){
|
||||
Fts5Auxiliary *pAux;
|
||||
Fts5Auxiliary *pNext;
|
||||
Fts5Global *pGlobal = (Fts5Global*)pCtx;
|
||||
for(pAux=pGlobal->pAux; pAux; pAux=pNext){
|
||||
pNext = pAux->pNext;
|
||||
if( pAux->xDestroy ){
|
||||
pAux->xDestroy(pAux->pUserData);
|
||||
}
|
||||
sqlite3_free(pAux);
|
||||
}
|
||||
sqlite3_free(pGlobal);
|
||||
}
|
||||
|
||||
|
||||
int sqlite3Fts5Init(sqlite3 *db){
|
||||
static const sqlite3_module fts5Mod = {
|
||||
/* iVersion */ 2,
|
||||
/* xCreate */ fts5CreateMethod,
|
||||
/* xConnect */ fts5ConnectMethod,
|
||||
/* xBestIndex */ fts5BestIndexMethod,
|
||||
/* xDisconnect */ fts5DisconnectMethod,
|
||||
/* xDestroy */ fts5DestroyMethod,
|
||||
/* xOpen */ fts5OpenMethod,
|
||||
/* xClose */ fts5CloseMethod,
|
||||
/* xFilter */ fts5FilterMethod,
|
||||
/* xNext */ fts5NextMethod,
|
||||
/* xEof */ fts5EofMethod,
|
||||
/* xColumn */ fts5ColumnMethod,
|
||||
/* xRowid */ fts5RowidMethod,
|
||||
/* xUpdate */ fts5UpdateMethod,
|
||||
/* xBegin */ fts5BeginMethod,
|
||||
/* xSync */ fts5SyncMethod,
|
||||
/* xCommit */ fts5CommitMethod,
|
||||
/* xRollback */ fts5RollbackMethod,
|
||||
/* xFindFunction */ fts5FindFunctionMethod,
|
||||
/* xRename */ fts5RenameMethod,
|
||||
/* xSavepoint */ fts5SavepointMethod,
|
||||
/* xRelease */ fts5ReleaseMethod,
|
||||
/* xRollbackTo */ fts5RollbackToMethod,
|
||||
};
|
||||
|
||||
int rc;
|
||||
Fts5Global *pGlobal = 0;
|
||||
pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
|
||||
|
||||
if( pGlobal==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
void *p = (void*)pGlobal;
|
||||
memset(pGlobal, 0, sizeof(Fts5Global));
|
||||
pGlobal->db = db;
|
||||
rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(db);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(pGlobal);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
87
ext/fts5/fts5.h
Normal file
87
ext/fts5/fts5.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
** 2014 May 31
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** Interfaces to extend FTS5. Using the interfaces defined in this file,
|
||||
** FTS5 may be extended with:
|
||||
**
|
||||
** * custom tokenizers, and
|
||||
** * custom auxiliary functions.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FTS5_H
|
||||
#define _FTS5_H
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*************************************************************************
|
||||
** CUSTOM AUXILIARY FUNCTIONS
|
||||
**
|
||||
** Virtual table implemenations may overload SQL functions by implementing
|
||||
** the sqlite3_module.xFindFunction() method.
|
||||
*/
|
||||
|
||||
typedef struct Fts5ExtensionApi Fts5ExtensionApi;
|
||||
typedef struct Fts5Context Fts5Context;
|
||||
|
||||
typedef void (*fts5_extension_function)(
|
||||
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 */
|
||||
);
|
||||
|
||||
/*
|
||||
** xColumnCount:
|
||||
** Returns the number of columns in the FTS5 table.
|
||||
**
|
||||
** xPhraseCount:
|
||||
** Returns the number of phrases in the current query expression.
|
||||
**
|
||||
** xPhraseSize:
|
||||
** Returns the number of tokens in phrase iPhrase of the query. Phrases
|
||||
** are numbered starting from zero.
|
||||
**
|
||||
** xRowid:
|
||||
** Returns the rowid of the current row.
|
||||
**
|
||||
** xPoslist:
|
||||
** Iterate through instances of phrase iPhrase in the current row.
|
||||
*/
|
||||
struct Fts5ExtensionApi {
|
||||
int iVersion; /* Currently always set to 1 */
|
||||
|
||||
void *(*xUserData)(Fts5Context*);
|
||||
|
||||
int (*xColumnCount)(Fts5Context*);
|
||||
int (*xColumnAvgSize)(Fts5Context*, int iCol, int *pnToken);
|
||||
int (*xTokenize)(Fts5Context*,
|
||||
const char *pText, int nText, /* Text to tokenize */
|
||||
void *pCtx, /* Context passed to xToken() */
|
||||
int (*xToken)(void*, const char*, int, int, int, int) /* Callback */
|
||||
);
|
||||
|
||||
int (*xPhraseCount)(Fts5Context*);
|
||||
int (*xPhraseSize)(Fts5Context*, int iPhrase);
|
||||
|
||||
sqlite3_int64 (*xRowid)(Fts5Context*);
|
||||
int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
|
||||
int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
|
||||
int (*xPoslist)(Fts5Context*, int iPhrase, int *pi, int *piCol, int *piOff);
|
||||
};
|
||||
|
||||
/*
|
||||
** CUSTOM AUXILIARY FUNCTIONS
|
||||
*************************************************************************/
|
||||
#endif /* _FTS5_H */
|
||||
|
@ -14,6 +14,7 @@
|
||||
#ifndef _FTS5INT_H
|
||||
#define _FTS5INT_H
|
||||
|
||||
#include "fts5.h"
|
||||
#include "sqliteInt.h"
|
||||
#include "fts3_tokenizer.h"
|
||||
|
||||
@ -122,6 +123,12 @@ struct Fts5PoslistWriter {
|
||||
};
|
||||
int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64);
|
||||
|
||||
int sqlite3Fts5PoslistNext(
|
||||
const u8 *a, int n, /* Buffer containing poslist */
|
||||
int *pi, /* IN/OUT: Offset within a[] */
|
||||
int *piCol, /* IN/OUT: Current column */
|
||||
int *piOff /* IN/OUT: Current token offset */
|
||||
);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_buffer.c.
|
||||
@ -331,6 +338,10 @@ void sqlite3Fts5ExprFree(Fts5Expr*);
|
||||
/* Called during startup to register a UDF with SQLite */
|
||||
int sqlite3Fts5ExprInit(sqlite3*);
|
||||
|
||||
int sqlite3Fts5ExprPhraseCount(Fts5Expr*);
|
||||
int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase);
|
||||
int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **);
|
||||
|
||||
/*******************************************
|
||||
** The fts5_expr.c API above this point is used by the other hand-written
|
||||
** C code in this module. The interfaces below this point are called by
|
||||
@ -373,4 +384,31 @@ void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
|
||||
** End of interface to code in fts5_expr.c.
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
** Interface to code in fts5.c.
|
||||
*/
|
||||
typedef struct Fts5Global Fts5Global;
|
||||
|
||||
int sqlite3Fts5CreateAux(
|
||||
Fts5Global*,
|
||||
const char*,
|
||||
void*,
|
||||
fts5_extension_function,
|
||||
void(*)(void*)
|
||||
);
|
||||
/*
|
||||
** End of interface to code in fts5.c.
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
** Interface to code in fts5_aux.c.
|
||||
*/
|
||||
|
||||
int sqlite3Fts5AuxInit(Fts5Global*);
|
||||
/*
|
||||
** End of interface to code in fts5_expr.c.
|
||||
**************************************************************************/
|
||||
|
||||
#endif
|
||||
|
144
ext/fts5/fts5_aux.c
Normal file
144
ext/fts5/fts5_aux.c
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
** 2014 May 31
|
||||
**
|
||||
** 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 "fts5Int.h"
|
||||
|
||||
static void fts5SnippetFunction(
|
||||
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 */
|
||||
){
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
static void fts5TestFunction(
|
||||
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 */
|
||||
){
|
||||
Fts5Buffer s; /* Build up text to return here */
|
||||
int nCol; /* Number of columns in table */
|
||||
int nPhrase; /* Number of phrases in query */
|
||||
i64 iRowid; /* Rowid of current row */
|
||||
const char *zReq = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
|
||||
if( nVal>=1 ){
|
||||
zReq = (const char*)sqlite3_value_text(apVal[0]);
|
||||
}
|
||||
|
||||
memset(&s, 0, sizeof(Fts5Buffer));
|
||||
|
||||
if( zReq==0 ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "columncount ");
|
||||
}
|
||||
if( 0==zReq || 0==sqlite3_stricmp(zReq, "columncount") ){
|
||||
nCol = pApi->xColumnCount(pFts);
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "%d", nCol);
|
||||
}
|
||||
|
||||
if( zReq==0 ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, " phrasecount ");
|
||||
}
|
||||
nPhrase = pApi->xPhraseCount(pFts);
|
||||
if( 0==zReq || 0==sqlite3_stricmp(zReq, "phrasecount") ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "%d", nPhrase);
|
||||
}
|
||||
|
||||
if( zReq==0 ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, " phrasesize ");
|
||||
}
|
||||
if( 0==zReq || 0==sqlite3_stricmp(zReq, "phrasesize") ){
|
||||
if( nPhrase==1 ){
|
||||
int nSize = pApi->xPhraseSize(pFts, 0);
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "%d", nSize);
|
||||
}else{
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "{");
|
||||
for(i=0; i<nPhrase; i++){
|
||||
int nSize = pApi->xPhraseSize(pFts, i);
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "%s%d", (i==0?"":" "), nSize);
|
||||
}
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "}");
|
||||
}
|
||||
}
|
||||
|
||||
if( zReq==0 ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, " poslist ");
|
||||
}
|
||||
if( 0==zReq || 0==sqlite3_stricmp(zReq, "poslist") ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "{");
|
||||
for(i=0; i<nPhrase; i++){
|
||||
int j = 0;
|
||||
int iOff = 0;
|
||||
int iCol = 0;
|
||||
int bFirst = 1;
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "%s{", (i==0?"":" "));
|
||||
while( 0==pApi->xPoslist(pFts, i, &j, &iCol, &iOff) ){
|
||||
sqlite3Fts5BufferAppendPrintf(
|
||||
&rc, &s, "%s%d.%d", (bFirst?"":" "), iCol, iOff
|
||||
);
|
||||
bFirst = 0;
|
||||
}
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "}");
|
||||
}
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "}");
|
||||
}
|
||||
|
||||
if( zReq==0 ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, " rowid ");
|
||||
}
|
||||
if( 0==zReq || 0==sqlite3_stricmp(zReq, "rowid") ){
|
||||
iRowid = pApi->xRowid(pFts);
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &s, "%lld", iRowid);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_text(pCtx, (const char*)s.p, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
}
|
||||
sqlite3Fts5BufferFree(&s);
|
||||
}
|
||||
|
||||
int sqlite3Fts5AuxInit(Fts5Global *pGlobal){
|
||||
struct Builtin {
|
||||
const char *zFunc; /* Function name (nul-terminated) */
|
||||
void *pUserData; /* User-data pointer */
|
||||
fts5_extension_function xFunc;/* Callback function */
|
||||
void (*xDestroy)(void*); /* Destructor function */
|
||||
} aBuiltin [] = {
|
||||
{ "snippet", 0, fts5SnippetFunction, 0 },
|
||||
{ "fts5_test", 0, fts5TestFunction, 0 },
|
||||
};
|
||||
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int i; /* To iterate through builtin functions */
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<sizeof(aBuiltin)/sizeof(aBuiltin[0]); i++){
|
||||
rc = sqlite3Fts5CreateAux(pGlobal,
|
||||
aBuiltin[i].zFunc,
|
||||
aBuiltin[i].pUserData,
|
||||
aBuiltin[i].xFunc,
|
||||
aBuiltin[i].xDestroy
|
||||
);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -197,3 +197,29 @@ int sqlite3Fts5PoslistWriterAppend(
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3Fts5PoslistNext(
|
||||
const u8 *a, int n, /* Buffer containing poslist */
|
||||
int *pi, /* IN/OUT: Offset within a[] */
|
||||
int *piCol, /* IN/OUT: Current column */
|
||||
int *piOff /* IN/OUT: Current token offset */
|
||||
){
|
||||
int i = *pi;
|
||||
int iVal;
|
||||
if( i>=n ){
|
||||
/* EOF */
|
||||
return 1;
|
||||
}
|
||||
i += getVarint32(&a[i], iVal);
|
||||
if( iVal==1 ){
|
||||
i += getVarint32(&a[i], iVal);
|
||||
*piCol = iVal;
|
||||
*piOff = 0;
|
||||
i += getVarint32(&a[i], iVal);
|
||||
}
|
||||
*piOff += (iVal-2);
|
||||
*pi = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,6 +33,8 @@ struct Fts5Expr {
|
||||
Fts5Index *pIndex;
|
||||
Fts5ExprNode *pRoot;
|
||||
int bAsc;
|
||||
int nPhrase; /* Number of phrases in expression */
|
||||
Fts5ExprPhrase **apPhrase; /* Pointers to phrase objects */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -92,6 +94,8 @@ struct Fts5Parse {
|
||||
Fts5Config *pConfig;
|
||||
char *zErr;
|
||||
int rc;
|
||||
int nPhrase; /* Size of apPhrase array */
|
||||
Fts5ExprPhrase **apPhrase; /* Array of all phrases */
|
||||
Fts5ExprNode *pExpr; /* Result of a successful parse */
|
||||
};
|
||||
|
||||
@ -211,9 +215,13 @@ int sqlite3Fts5ExprNew(
|
||||
}else{
|
||||
pNew->pRoot = sParse.pExpr;
|
||||
pNew->pIndex = 0;
|
||||
pNew->apPhrase = sParse.apPhrase;
|
||||
pNew->nPhrase = sParse.nPhrase;
|
||||
sParse.apPhrase = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(sParse.apPhrase);
|
||||
*pzErr = sParse.zErr;
|
||||
return sParse.rc;
|
||||
}
|
||||
@ -236,6 +244,7 @@ void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){
|
||||
void sqlite3Fts5ExprFree(Fts5Expr *p){
|
||||
if( p ){
|
||||
sqlite3Fts5ParseNodeFree(p->pRoot);
|
||||
sqlite3_free(p->apPhrase);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
@ -959,6 +968,17 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm(
|
||||
int rc; /* Tokenize return code */
|
||||
char *z = 0;
|
||||
|
||||
if( pPhrase==0 ){
|
||||
if( (pParse->nPhrase % 8)==0 ){
|
||||
int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
|
||||
Fts5ExprPhrase **apNew;
|
||||
apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte);
|
||||
if( apNew==0 ) return 0;
|
||||
pParse->apPhrase = apNew;
|
||||
}
|
||||
pParse->nPhrase++;
|
||||
}
|
||||
|
||||
pParse->rc = fts5ParseStringFromToken(pToken, &z);
|
||||
if( z==0 ) return 0;
|
||||
sqlite3Fts5Dequote(z);
|
||||
@ -974,6 +994,8 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm(
|
||||
sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix;
|
||||
}
|
||||
|
||||
|
||||
pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
|
||||
sqlite3_free(z);
|
||||
return sCtx.pPhrase;
|
||||
}
|
||||
@ -1354,3 +1376,33 @@ int sqlite3Fts5ExprInit(sqlite3 *db){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of phrases in expression pExpr.
|
||||
*/
|
||||
int sqlite3Fts5ExprPhraseCount(Fts5Expr *pExpr){
|
||||
return pExpr->nPhrase;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of terms in the iPhrase'th phrase in pExpr.
|
||||
*/
|
||||
int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){
|
||||
if( iPhrase<0 || iPhrase>=pExpr->nPhrase ) return 0;
|
||||
return pExpr->apPhrase[iPhrase]->nTerm;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to access the current position list for phrase
|
||||
** iPhrase.
|
||||
*/
|
||||
int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){
|
||||
if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
|
||||
*pa = 0;
|
||||
return 0;
|
||||
}else{
|
||||
Fts5ExprPhrase *pPhrase = pExpr->apPhrase[iPhrase];
|
||||
*pa = pPhrase->poslist.p;
|
||||
return pPhrase->poslist.n;
|
||||
}
|
||||
}
|
||||
|
||||
|
155
ext/fts5/fts5parse.y
Normal file
155
ext/fts5/fts5parse.y
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
** 2014 May 31
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
%token_prefix FTS5_
|
||||
|
||||
// The type of the data attached to each token is Token. This is also the
|
||||
// default type for non-terminals.
|
||||
//
|
||||
%token_type {Fts5Token}
|
||||
%default_type {Fts5Token}
|
||||
|
||||
// The generated parser function takes a 4th argument as follows:
|
||||
%extra_argument {Fts5Parse *pParse}
|
||||
|
||||
// This code runs whenever there is a syntax error
|
||||
//
|
||||
%syntax_error {
|
||||
sqlite3Fts5ParseError(
|
||||
pParse, "fts5: syntax error near \"%.*s\"",TOKEN.n,TOKEN.p
|
||||
);
|
||||
}
|
||||
%stack_overflow {
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
// The name of the generated procedure that implements the parser
|
||||
// is as follows:
|
||||
%name sqlite3Fts5Parser
|
||||
|
||||
// The following text is included near the beginning of the C source
|
||||
// code file that implements the parser.
|
||||
//
|
||||
%include {
|
||||
#include "fts5Int.h"
|
||||
#include "fts5parse.h"
|
||||
|
||||
/*
|
||||
** Disable all error recovery processing in the parser push-down
|
||||
** automaton.
|
||||
*/
|
||||
#define YYNOERRORRECOVERY 1
|
||||
|
||||
/*
|
||||
** Make yytestcase() the same as testcase()
|
||||
*/
|
||||
#define yytestcase(X) testcase(X)
|
||||
|
||||
} // end %include
|
||||
|
||||
%left OR.
|
||||
%left AND.
|
||||
%left NOT.
|
||||
%left COLON.
|
||||
|
||||
input ::= expr(X). { sqlite3Fts5ParseFinished(pParse, X); }
|
||||
|
||||
%type cnearset {Fts5ExprNode*}
|
||||
%type expr {Fts5ExprNode*}
|
||||
%type exprlist {Fts5ExprNode*}
|
||||
%destructor cnearset { sqlite3Fts5ParseNodeFree($$); }
|
||||
%destructor expr { sqlite3Fts5ParseNodeFree($$); }
|
||||
%destructor exprlist { sqlite3Fts5ParseNodeFree($$); }
|
||||
|
||||
expr(A) ::= expr(X) AND expr(Y). {
|
||||
A = sqlite3Fts5ParseNode(pParse, FTS5_AND, X, Y, 0);
|
||||
}
|
||||
expr(A) ::= expr(X) OR expr(Y). {
|
||||
A = sqlite3Fts5ParseNode(pParse, FTS5_OR, X, Y, 0);
|
||||
}
|
||||
expr(A) ::= expr(X) NOT expr(Y). {
|
||||
A = sqlite3Fts5ParseNode(pParse, FTS5_NOT, X, Y, 0);
|
||||
}
|
||||
|
||||
expr(A) ::= LP expr(X) RP. {A = X;}
|
||||
expr(A) ::= exprlist(X). {A = X;}
|
||||
|
||||
exprlist(A) ::= cnearset(X). {A = X;}
|
||||
exprlist(A) ::= exprlist(X) cnearset(Y). {
|
||||
A = sqlite3Fts5ParseNode(pParse, FTS5_AND, X, Y, 0);
|
||||
}
|
||||
|
||||
cnearset(A) ::= nearset(X). {
|
||||
A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, X);
|
||||
}
|
||||
cnearset(A) ::= STRING(X) COLON nearset(Y). {
|
||||
sqlite3Fts5ParseSetColumn(pParse, Y, &X);
|
||||
A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, Y);
|
||||
}
|
||||
|
||||
%type nearset {Fts5ExprNearset*}
|
||||
%type nearphrases {Fts5ExprNearset*}
|
||||
%destructor nearset { sqlite3Fts5ParseNearsetFree($$); }
|
||||
%destructor nearphrases { sqlite3Fts5ParseNearsetFree($$); }
|
||||
|
||||
nearset(A) ::= phrase(X). { A = sqlite3Fts5ParseNearset(pParse, 0, X); }
|
||||
nearset(A) ::= STRING(X) LP nearphrases(Y) neardist_opt(Z) RP. {
|
||||
sqlite3Fts5ParseNear(pParse, &X);
|
||||
sqlite3Fts5ParseSetDistance(pParse, Y, &Z);
|
||||
A = Y;
|
||||
}
|
||||
|
||||
nearphrases(A) ::= phrase(X). {
|
||||
A = sqlite3Fts5ParseNearset(pParse, 0, X);
|
||||
}
|
||||
nearphrases(A) ::= nearphrases(X) phrase(Y). {
|
||||
A = sqlite3Fts5ParseNearset(pParse, X, Y);
|
||||
}
|
||||
|
||||
/*
|
||||
** The optional ", <integer>" at the end of the NEAR() arguments.
|
||||
*/
|
||||
neardist_opt(A) ::= . { A.p = 0; A.n = 0; }
|
||||
neardist_opt(A) ::= COMMA STRING(X). { A = X; }
|
||||
|
||||
/*
|
||||
** A phrase. A set of primitives connected by "+" operators. Examples:
|
||||
**
|
||||
** "the" + "quick brown" + fo *
|
||||
** "the quick brown fo" *
|
||||
** the+quick+brown+fo*
|
||||
*/
|
||||
%type phrase {Fts5ExprPhrase*}
|
||||
%destructor phrase { sqlite3Fts5ParsePhraseFree($$); }
|
||||
|
||||
phrase(A) ::= phrase(X) PLUS STRING(Y) star_opt(Z). {
|
||||
A = sqlite3Fts5ParseTerm(pParse, X, &Y, Z);
|
||||
}
|
||||
phrase(A) ::= STRING(Y) star_opt(Z). {
|
||||
A = sqlite3Fts5ParseTerm(pParse, 0, &Y, Z);
|
||||
}
|
||||
|
||||
/*
|
||||
** Optional "*" character.
|
||||
*/
|
||||
%type star_opt {int}
|
||||
|
||||
star_opt(A) ::= STAR. { A = 1; }
|
||||
star_opt(A) ::= . { A = 0; }
|
||||
|
||||
|
||||
|
||||
|
7
main.mk
7
main.mk
@ -73,6 +73,7 @@ LIBOBJ+= vdbe.o parse.o \
|
||||
vdbetrace.o wal.o walker.o where.o utf.o vtab.o
|
||||
|
||||
LIBOBJ += fts5.o
|
||||
LIBOBJ += fts5_aux.o
|
||||
LIBOBJ += fts5_buffer.o
|
||||
LIBOBJ += fts5_config.o
|
||||
LIBOBJ += fts5_expr.o
|
||||
@ -385,7 +386,8 @@ EXTHDR += \
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/icu/sqliteicu.h
|
||||
EXTHDR += \
|
||||
$(TOP)/ext/fts5/fts5Int.h
|
||||
$(TOP)/ext/fts5/fts5Int.h \
|
||||
$(TOP)/ext/fts5/fts5.h
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
@ -573,6 +575,9 @@ rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
|
||||
|
||||
# FTS5 things
|
||||
#
|
||||
fts5_aux.o: $(TOP)/ext/fts5/fts5_aux.c $(HDR) $(EXTHDR)
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_aux.c
|
||||
|
||||
fts5_buffer.o: $(TOP)/ext/fts5/fts5_buffer.c $(HDR) $(EXTHDR)
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_buffer.c
|
||||
|
||||
|
25
manifest
25
manifest
@ -1,5 +1,5 @@
|
||||
C Support\s"ORDER\sBY\srowid\sASC".
|
||||
D 2014-07-10T20:21:12.482
|
||||
C Begin\sadding\sinterface\sfor\sauxiliary\sfunctions.
|
||||
D 2014-07-16T19:15:57.212
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -103,13 +103,16 @@ F ext/fts3/tool/fts3view.c 6cfc5b67a5f0e09c0d698f9fd012c784bfaa9197
|
||||
F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
|
||||
F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
|
||||
F ext/fts3/unicode/mkunicode.tcl dc6f268eb526710e2c6e496c372471d773d0c368
|
||||
F ext/fts5/fts5.c 1af3184dd9c0e5c1686f71202d6b6cac8f225f05
|
||||
F ext/fts5/fts5Int.h bb716a6e6a376a7c8211e55e5577c6c020d176c2
|
||||
F ext/fts5/fts5_buffer.c 83b463a179ad4348fa87796fce78b0e4ef6b898a
|
||||
F ext/fts5/fts5.c 20bcb1e10756c72b550947236960edf96929ca2f
|
||||
F ext/fts5/fts5.h cda3b9d73e6ffa6d0cd35b7da6b808bf3a1ada32
|
||||
F ext/fts5/fts5Int.h 2d4c1e1ebdf18278776fcd8a64233ff3c04ea51f
|
||||
F ext/fts5/fts5_aux.c 53ab338c6a469dc67e7a6bd8685ce727beee8403
|
||||
F ext/fts5/fts5_buffer.c b7aa6cdf4a63642fcc12359cedc4be748ca400cc
|
||||
F ext/fts5/fts5_config.c 94f1b4cb4de6a7cd5780c14adb0198e289df8cef
|
||||
F ext/fts5/fts5_expr.c 0dc31b06d444cad097bec05699797590729d2638
|
||||
F ext/fts5/fts5_expr.c e4e4e6d32beff1ab0d076f8fbf5cf3b2241d4dbc
|
||||
F ext/fts5/fts5_index.c 9ff3008e903aa9077b0a7a7aa76ab6080eb07a36
|
||||
F ext/fts5/fts5_storage.c 7848d8f8528d798bba159900ea310a6d4a279da8
|
||||
F ext/fts5/fts5parse.y 777da8e5819f75c217982c79c29d014c293acac9
|
||||
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
|
||||
F ext/icu/icu.c d415ccf984defeb9df2c0e1afcfaa2f6dc05eacb
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
@ -153,7 +156,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk c5524f888196af43a9b5dfae878205044f549dbf
|
||||
F main.mk cffc02a30f1af82d35410674f70a0286587add81
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@ -594,7 +597,7 @@ F test/fts4noti.test 524807f0c36d49deea7920cdd4cd687408b58849
|
||||
F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36
|
||||
F test/fts5aa.test c8d3b9694f6b2864161c7437408464a535d19343
|
||||
F test/fts5ab.test dc04ed48cf93ca957d174406e6c192f2ff4f3397
|
||||
F test/fts5ac.test 28203ba2334030514d7a6271c5fb1ba3cbc219b1
|
||||
F test/fts5ac.test 398a2d8d9576e0579a0f0955fabd8410ace969e4
|
||||
F test/fts5ad.test 2ed38bbc865678cb2905247120d02ebba7f20e07
|
||||
F test/fts5ea.test ff43b40f8879ba50b82def70f2ab67c195d1a1d4
|
||||
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
|
||||
@ -1191,7 +1194,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 75ebd3cd5904a4f89f7f3a9b25d32b2a42a31310
|
||||
R 5a76d2f2fc0d7fcaa9a60fabc7fdb146
|
||||
P b96b5e166990e4ec363b24f66e04cfa5f00f6342
|
||||
R ff6cbab233811678a295f9640beec5d4
|
||||
U dan
|
||||
Z 870004bd588f44c77d8063239acbea69
|
||||
Z 5e7398b52fb14b2e0bc342aa9223ff97
|
||||
|
@ -1 +1 @@
|
||||
b96b5e166990e4ec363b24f66e04cfa5f00f6342
|
||||
1e2a7ba0889093416455f488fca893eaeb195d45
|
140
test/fts5ac.test
140
test/fts5ac.test
@ -138,9 +138,7 @@ do_test 1.1 {
|
||||
} {}
|
||||
|
||||
proc phrasematch {phrase value} {
|
||||
if {[string first $phrase $value]>=0} {
|
||||
return 1
|
||||
}
|
||||
if {[string first $phrase $value]>=0} { return 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -177,9 +175,9 @@ proc nearmatch {nNear phraselist value} {
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# nearset aCol ?-near N? ?-col C? -- phrase1 phrase2...
|
||||
# poslist aCol ?-near N? ?-col C? -- phrase1 phrase2...
|
||||
#
|
||||
proc nearset {aCol args} {
|
||||
proc poslist {aCol args} {
|
||||
set O(-near) 10
|
||||
set O(-col) -1
|
||||
|
||||
@ -191,44 +189,121 @@ proc nearset {aCol args} {
|
||||
set O($k) $v
|
||||
}
|
||||
|
||||
# Set phraselist to be a list of phrases. nPhrase its length.
|
||||
set phraselist [lrange $args [expr $nOpt+1] end]
|
||||
set nPhrase [llength $phraselist]
|
||||
|
||||
for {set j 0} {$j < [llength $aCol]} {incr j} {
|
||||
for {set i 0} {$i < $nPhrase} {incr i} {
|
||||
set A($j,$i) [list]
|
||||
}
|
||||
}
|
||||
|
||||
set bMatch 0
|
||||
set iCol -1
|
||||
foreach col $aCol {
|
||||
incr iCol
|
||||
if {$O(-col)>=0 && $O(-col)!=$iCol} continue
|
||||
|
||||
if {[nearmatch $O(-near) $phraselist $col]} {
|
||||
set bMatch 1
|
||||
break
|
||||
set nToken [llength $col]
|
||||
|
||||
set iFL [expr $O(-near) >= $nToken ? $nToken - 1 : $O(-near)]
|
||||
for { } {$iFL < $nToken} {incr iFL} {
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
set B($iPhrase) [list]
|
||||
}
|
||||
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
set p [lindex $phraselist $iPhrase]
|
||||
set nPm1 [expr {[llength $p] - 1}]
|
||||
set iFirst [expr $iFL - $O(-near) - [llength $p]]
|
||||
|
||||
for {set i $iFirst} {$i <= $iFL} {incr i} {
|
||||
if {[lrange $col $i [expr $i+$nPm1]] == $p} { lappend B($iPhrase) $i }
|
||||
}
|
||||
if {[llength $B($iPhrase)] == 0} break
|
||||
}
|
||||
|
||||
if {$iPhrase==$nPhrase} {
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
set A($iCol,$iPhrase) [concat $A($iCol,$iPhrase) $B($iPhrase)]
|
||||
set A($iCol,$iPhrase) [lsort -integer -uniq $A($iCol,$iPhrase)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $bMatch
|
||||
}
|
||||
|
||||
proc matchdata {expr {bAsc 0}} {
|
||||
set tclexpr [db one {SELECT fts5_expr_tcl($expr, 'nearset $cols', 'x', 'y')}]
|
||||
set res [list]
|
||||
foreach {id x y} $::data {
|
||||
set cols [list $x $y]
|
||||
if $tclexpr {
|
||||
lappend res $id
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
set plist [list]
|
||||
for {set iCol 0} {$iCol < [llength $aCol]} {incr iCol} {
|
||||
foreach a $A($iCol,$iPhrase) {
|
||||
lappend plist "$iCol.$a"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# puts $tclexpr
|
||||
|
||||
if {$bAsc} {
|
||||
set res [lsort -integer -increasing $res]
|
||||
} else {
|
||||
set res [lsort -integer -decreasing $res]
|
||||
lappend res $plist
|
||||
}
|
||||
|
||||
return $res
|
||||
}
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# nearset aCol ?-near N? ?-col C? -- phrase1 phrase2...
|
||||
#
|
||||
proc nearset {args} {
|
||||
set plist [poslist {*}$args]
|
||||
return [expr [llength [lindex $plist 0]]>0]
|
||||
}
|
||||
|
||||
# Argument $expr is an FTS5 match expression designed to be executed against
|
||||
# an FTS5 table with the following schema:
|
||||
#
|
||||
# CREATE VIRTUAL TABLE xy USING fts5(x, y);
|
||||
#
|
||||
# Assuming the table contains the same records as stored int the global
|
||||
# $::data array (see above), this function returns a list containing one
|
||||
# element for each match in the dataset. The elements are themselves lists
|
||||
# formatted as follows:
|
||||
#
|
||||
# <rowid> {<phrase 0 matches> <phrase 1 matches>...}
|
||||
#
|
||||
# where each <phrase X matches> element is a list of phrase matches in the
|
||||
# same form as returned by auxiliary scalar function fts5_test().
|
||||
#
|
||||
proc matchdata {bPos expr {bAsc 0}} {
|
||||
|
||||
set tclexpr [db one {SELECT fts5_expr_tcl($expr, 'nearset $cols', 'x', 'y')}]
|
||||
set res [list]
|
||||
|
||||
#puts $tclexpr
|
||||
foreach {id x y} $::data {
|
||||
set cols [list $x $y]
|
||||
if $tclexpr {
|
||||
if {$bPos} {
|
||||
set N [regexp -all -inline {\[nearset [^\]]*\]} $tclexpr]
|
||||
set rowres [list]
|
||||
foreach phrase $N {
|
||||
set cmd "poslist [string range $phrase 9 end-1]"
|
||||
lappend rowres [eval $cmd]
|
||||
}
|
||||
if {[string first "\{" $rowres]<0} { set rowres "{{$rowres}}" }
|
||||
lappend res [list $id $rowres]
|
||||
} else {
|
||||
lappend res $id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if {$bAsc} {
|
||||
set res [lsort -integer -increasing -index 0 $res]
|
||||
} else {
|
||||
set res [lsort -integer -decreasing -index 0 $res]
|
||||
}
|
||||
|
||||
return [concat {*}$res]
|
||||
}
|
||||
|
||||
|
||||
foreach {tn phrase} {
|
||||
1 "o"
|
||||
2 "b q"
|
||||
@ -243,10 +318,10 @@ foreach {tn phrase} {
|
||||
} {
|
||||
|
||||
set expr "\"$phrase\""
|
||||
set res [matchdata $expr]
|
||||
set res [matchdata 1 $expr]
|
||||
|
||||
do_execsql_test 1.2.$tn.[llength $res] {
|
||||
SELECT rowid FROM xx WHERE xx match $expr
|
||||
SELECT rowid, fts5_test(xx, 'poslist') FROM xx WHERE xx match $expr
|
||||
} $res
|
||||
}
|
||||
|
||||
@ -261,6 +336,10 @@ do_test 2.5 { nearmatch 400 {a b} {a x x b} } 1
|
||||
do_test 2.6 { nearmatch 0 {a} {a x x b} } 1
|
||||
do_test 2.7 { nearmatch 0 {b} {a x x b} } 1
|
||||
|
||||
do_test 2.8 { poslist {{a b c}} -- a } {0.0}
|
||||
do_test 2.9 { poslist {{a b c}} -- c } {0.2}
|
||||
|
||||
|
||||
foreach {tn expr tclexpr} {
|
||||
1 {a b} {[N $x -- {a}] && [N $x -- {b}]}
|
||||
} {
|
||||
@ -275,7 +354,6 @@ foreach {bAsc sql} {
|
||||
} {
|
||||
foreach {tn expr} {
|
||||
0.1 x
|
||||
|
||||
1 { NEAR(r c) }
|
||||
2 { NEAR(r c, 5) }
|
||||
3 { NEAR(r c, 3) }
|
||||
@ -297,12 +375,10 @@ foreach {bAsc sql} {
|
||||
18 { c NOT (b OR a) }
|
||||
19 { c NOT b OR a AND d }
|
||||
} {
|
||||
set res [matchdata $expr $bAsc]
|
||||
set res [matchdata 0 $expr $bAsc]
|
||||
do_execsql_test 4.$bAsc.$tn.[llength $res] $sql $res
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user