| Index: third_party/sqlite/src/ext/fts3/fts3_aux.c
|
| diff --git a/third_party/sqlite/src/ext/fts3/fts3_aux.c b/third_party/sqlite/src/ext/fts3/fts3_aux.c
|
| index 6108689ae1040ead900eaed6d035bf00908a6ac2..c68b1a9d9be93f40b6ff81a0dddc0bfe7e0d4cfe 100644
|
| --- a/third_party/sqlite/src/ext/fts3/fts3_aux.c
|
| +++ b/third_party/sqlite/src/ext/fts3/fts3_aux.c
|
| @@ -11,10 +11,9 @@
|
| ******************************************************************************
|
| **
|
| */
|
| -
|
| +#include "fts3Int.h"
|
| #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
|
|
| -#include "fts3Int.h"
|
| #include <string.h>
|
| #include <assert.h>
|
|
|
| @@ -28,10 +27,11 @@ struct Fts3auxTable {
|
|
|
| struct Fts3auxCursor {
|
| sqlite3_vtab_cursor base; /* Base class used by SQLite core */
|
| - Fts3SegReaderCursor csr; /* Must be right after "base" */
|
| + Fts3MultiSegReader csr; /* Must be right after "base" */
|
| Fts3SegFilter filter;
|
| char *zStop;
|
| int nStop; /* Byte-length of string zStop */
|
| + int iLangid; /* Language id to query */
|
| int isEof; /* True if cursor is at EOF */
|
| sqlite3_int64 iRowid; /* Current rowid */
|
|
|
| @@ -46,7 +46,8 @@ struct Fts3auxCursor {
|
| /*
|
| ** Schema of the terms table.
|
| */
|
| -#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)"
|
| +#define FTS3_AUX_SCHEMA \
|
| + "CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)"
|
|
|
| /*
|
| ** This function does all the work for both the xConnect and xCreate methods.
|
| @@ -71,20 +72,29 @@ static int fts3auxConnectMethod(
|
|
|
| UNUSED_PARAMETER(pUnused);
|
|
|
| - /* The user should specify a single argument - the name of an fts3 table. */
|
| - if( argc!=4 ){
|
| - *pzErr = sqlite3_mprintf(
|
| - "wrong number of arguments to fts4aux constructor"
|
| - );
|
| - return SQLITE_ERROR;
|
| - }
|
| + /* The user should invoke this in one of two forms:
|
| + **
|
| + ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table);
|
| + ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table-db, fts4-table);
|
| + */
|
| + if( argc!=4 && argc!=5 ) goto bad_args;
|
|
|
| zDb = argv[1];
|
| - nDb = strlen(zDb);
|
| - zFts3 = argv[3];
|
| - nFts3 = strlen(zFts3);
|
| + nDb = (int)strlen(zDb);
|
| + if( argc==5 ){
|
| + if( nDb==4 && 0==sqlite3_strnicmp("temp", zDb, 4) ){
|
| + zDb = argv[3];
|
| + nDb = (int)strlen(zDb);
|
| + zFts3 = argv[4];
|
| + }else{
|
| + goto bad_args;
|
| + }
|
| + }else{
|
| + zFts3 = argv[3];
|
| + }
|
| + nFts3 = (int)strlen(zFts3);
|
|
|
| - rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
|
| + rc = sqlite3_declare_vtab(db, FTS3_AUX_SCHEMA);
|
| if( rc!=SQLITE_OK ) return rc;
|
|
|
| nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
|
| @@ -96,6 +106,7 @@ static int fts3auxConnectMethod(
|
| p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
|
| p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
|
| p->pFts3Tab->db = db;
|
| + p->pFts3Tab->nIndex = 1;
|
|
|
| memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
|
| memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
|
| @@ -103,6 +114,10 @@ static int fts3auxConnectMethod(
|
|
|
| *ppVtab = (sqlite3_vtab *)p;
|
| return SQLITE_OK;
|
| +
|
| + bad_args:
|
| + *pzErr = sqlite3_mprintf("invalid arguments to fts4aux constructor");
|
| + return SQLITE_ERROR;
|
| }
|
|
|
| /*
|
| @@ -139,6 +154,8 @@ static int fts3auxBestIndexMethod(
|
| int iEq = -1;
|
| int iGe = -1;
|
| int iLe = -1;
|
| + int iLangid = -1;
|
| + int iNext = 1; /* Next free argvIndex value */
|
|
|
| UNUSED_PARAMETER(pVTab);
|
|
|
| @@ -150,36 +167,48 @@ static int fts3auxBestIndexMethod(
|
| pInfo->orderByConsumed = 1;
|
| }
|
|
|
| - /* Search for equality and range constraints on the "term" column. */
|
| + /* Search for equality and range constraints on the "term" column.
|
| + ** And equality constraints on the hidden "languageid" column. */
|
| for(i=0; i<pInfo->nConstraint; i++){
|
| - if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){
|
| + if( pInfo->aConstraint[i].usable ){
|
| int op = pInfo->aConstraint[i].op;
|
| - if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
|
| - if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
|
| - if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
|
| - if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
|
| - if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
|
| + int iCol = pInfo->aConstraint[i].iColumn;
|
| +
|
| + if( iCol==0 ){
|
| + if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
|
| + if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
|
| + if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
|
| + if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
|
| + if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
|
| + }
|
| + if( iCol==4 ){
|
| + if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iLangid = i;
|
| + }
|
| }
|
| }
|
|
|
| if( iEq>=0 ){
|
| pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
|
| - pInfo->aConstraintUsage[iEq].argvIndex = 1;
|
| + pInfo->aConstraintUsage[iEq].argvIndex = iNext++;
|
| pInfo->estimatedCost = 5;
|
| }else{
|
| pInfo->idxNum = 0;
|
| pInfo->estimatedCost = 20000;
|
| if( iGe>=0 ){
|
| pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
|
| - pInfo->aConstraintUsage[iGe].argvIndex = 1;
|
| + pInfo->aConstraintUsage[iGe].argvIndex = iNext++;
|
| pInfo->estimatedCost /= 2;
|
| }
|
| if( iLe>=0 ){
|
| pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
|
| - pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0);
|
| + pInfo->aConstraintUsage[iLe].argvIndex = iNext++;
|
| pInfo->estimatedCost /= 2;
|
| }
|
| }
|
| + if( iLangid>=0 ){
|
| + pInfo->aConstraintUsage[iLangid].argvIndex = iNext++;
|
| + pInfo->estimatedCost--;
|
| + }
|
|
|
| return SQLITE_OK;
|
| }
|
| @@ -339,16 +368,38 @@ static int fts3auxFilterMethod(
|
| Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
|
| Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
|
| int rc;
|
| - int isScan;
|
| + int isScan = 0;
|
| + int iLangVal = 0; /* Language id to query */
|
| +
|
| + int iEq = -1; /* Index of term=? value in apVal */
|
| + int iGe = -1; /* Index of term>=? value in apVal */
|
| + int iLe = -1; /* Index of term<=? value in apVal */
|
| + int iLangid = -1; /* Index of languageid=? value in apVal */
|
| + int iNext = 0;
|
|
|
| UNUSED_PARAMETER(nVal);
|
| + UNUSED_PARAMETER(idxStr);
|
|
|
| assert( idxStr==0 );
|
| assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0
|
| || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
|
| || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
|
| );
|
| - isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT);
|
| +
|
| + if( idxNum==FTS4AUX_EQ_CONSTRAINT ){
|
| + iEq = iNext++;
|
| + }else{
|
| + isScan = 1;
|
| + if( idxNum & FTS4AUX_GE_CONSTRAINT ){
|
| + iGe = iNext++;
|
| + }
|
| + if( idxNum & FTS4AUX_LE_CONSTRAINT ){
|
| + iLe = iNext++;
|
| + }
|
| + }
|
| + if( iNext<nVal ){
|
| + iLangid = iNext++;
|
| + }
|
|
|
| /* In case this cursor is being reused, close and zero it. */
|
| testcase(pCsr->filter.zTerm);
|
| @@ -360,22 +411,35 @@ static int fts3auxFilterMethod(
|
| pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
|
| if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
|
|
|
| - if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){
|
| + if( iEq>=0 || iGe>=0 ){
|
| const unsigned char *zStr = sqlite3_value_text(apVal[0]);
|
| + assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) );
|
| if( zStr ){
|
| pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
|
| pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
|
| if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
|
| }
|
| }
|
| - if( idxNum&FTS4AUX_LE_CONSTRAINT ){
|
| - int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
|
| - pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
|
| - pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
|
| +
|
| + if( iLe>=0 ){
|
| + pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe]));
|
| + pCsr->nStop = sqlite3_value_bytes(apVal[iLe]);
|
| if( pCsr->zStop==0 ) return SQLITE_NOMEM;
|
| }
|
| +
|
| + if( iLangid>=0 ){
|
| + iLangVal = sqlite3_value_int(apVal[iLangid]);
|
| +
|
| + /* If the user specified a negative value for the languageid, use zero
|
| + ** instead. This works, as the "languageid=?" constraint will also
|
| + ** be tested by the VDBE layer. The test will always be false (since
|
| + ** this module will not return a row with a negative languageid), and
|
| + ** so the overall query will return zero rows. */
|
| + if( iLangVal<0 ) iLangVal = 0;
|
| + }
|
| + pCsr->iLangid = iLangVal;
|
|
|
| - rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL,
|
| + rc = sqlite3Fts3SegReaderCursor(pFts3, iLangVal, 0, FTS3_SEGCURSOR_ALL,
|
| pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
|
| );
|
| if( rc==SQLITE_OK ){
|
| @@ -399,24 +463,37 @@ static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){
|
| */
|
| static int fts3auxColumnMethod(
|
| sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
| - sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
|
| + sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
|
| int iCol /* Index of column to read value from */
|
| ){
|
| Fts3auxCursor *p = (Fts3auxCursor *)pCursor;
|
|
|
| assert( p->isEof==0 );
|
| - if( iCol==0 ){ /* Column "term" */
|
| - sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
|
| - }else if( iCol==1 ){ /* Column "col" */
|
| - if( p->iCol ){
|
| - sqlite3_result_int(pContext, p->iCol-1);
|
| - }else{
|
| - sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC);
|
| - }
|
| - }else if( iCol==2 ){ /* Column "documents" */
|
| - sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc);
|
| - }else{ /* Column "occurrences" */
|
| - sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc);
|
| + switch( iCol ){
|
| + case 0: /* term */
|
| + sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
|
| + break;
|
| +
|
| + case 1: /* col */
|
| + if( p->iCol ){
|
| + sqlite3_result_int(pCtx, p->iCol-1);
|
| + }else{
|
| + sqlite3_result_text(pCtx, "*", -1, SQLITE_STATIC);
|
| + }
|
| + break;
|
| +
|
| + case 2: /* documents */
|
| + sqlite3_result_int64(pCtx, p->aStat[p->iCol].nDoc);
|
| + break;
|
| +
|
| + case 3: /* occurrences */
|
| + sqlite3_result_int64(pCtx, p->aStat[p->iCol].nOcc);
|
| + break;
|
| +
|
| + default: /* languageid */
|
| + assert( iCol==4 );
|
| + sqlite3_result_int(pCtx, p->iLangid);
|
| + break;
|
| }
|
|
|
| return SQLITE_OK;
|
| @@ -459,7 +536,10 @@ int sqlite3Fts3InitAux(sqlite3 *db){
|
| 0, /* xCommit */
|
| 0, /* xRollback */
|
| 0, /* xFindFunction */
|
| - 0 /* xRename */
|
| + 0, /* xRename */
|
| + 0, /* xSavepoint */
|
| + 0, /* xRelease */
|
| + 0 /* xRollbackTo */
|
| };
|
| int rc; /* Return code */
|
|
|
|
|