Fts5Index *pIndex; /* Full-text index */
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
+ Fts5Cursor *pSortCsr; /* Sort data from this cursor */
};
struct Fts5MatchPhrase {
int nTerm; /* Size of phrase in terms */
};
+/*
+** Variable pStmt is set to a compiled SQL statement of the form:
+**
+** SELECT rowid, <fts> FROM <fts> ORDER BY +rank;
+**
+*/
+struct Fts5Sorter {
+ sqlite3_stmt *pStmt;
+ i64 iRowid; /* Current rowid */
+ u8 *aPoslist; /* Position lists for current row */
+ int nIdx; /* Number of entries in aIdx[] */
+ int aIdx[0]; /* Offsets into aPoslist for current row */
+};
+
/*
** Virtual-table cursor object.
*/
int idxNum; /* idxNum passed to xFilter() */
sqlite3_stmt *pStmt; /* Statement used to read %_content */
Fts5Expr *pExpr; /* Expression for MATCH queries */
+ Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */
int csrflags; /* Mask of cursor flags (see below) */
Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */
+ Fts5Auxiliary *pRank; /* Rank callback (or NULL) */
/* Variables used by auxiliary functions */
i64 iCsrId; /* Cursor id */
Fts5Auxiliary *pAux; /* Currently executing extension function */
- Fts5Auxdata *pAuxdata; /* First in linked list of aux-data */
+ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */
int *aColumnSize; /* Values for xColumnSize() */
};
/*
** The three query plans xBestIndex may choose between.
*/
-#define FTS5_PLAN_SCAN 1 /* No usable constraint */
-#define FTS5_PLAN_MATCH 2 /* (<tbl> MATCH ?) */
-#define FTS5_PLAN_ROWID 3 /* (rowid = ?) */
+#define FTS5_PLAN_SCAN 1 /* No usable constraint */
+#define FTS5_PLAN_MATCH 2 /* (<tbl> MATCH ?) */
+#define FTS5_PLAN_SORTED_MATCH 3 /* (<tbl> MATCH ? ORDER BY rank) */
+#define FTS5_PLAN_ROWID 4 /* (rowid = ?) */
+#define FTS5_PLAN_SOURCE 5 /* A source cursor for SORTED_MATCH */
#define FTS5_PLAN(idxNum) ((idxNum) & 0x7)
pInfo->estimatedCost = 10000000.0;
}
- if( pInfo->nOrderBy==1 && pInfo->aOrderBy[0].iColumn<0 ){
- pInfo->orderByConsumed = 1;
- ePlan |= pInfo->aOrderBy[0].desc ? FTS5_ORDER_DESC : FTS5_ORDER_ASC;
+ if( pInfo->nOrderBy==1 ){
+ int iSort = pInfo->aOrderBy[0].iColumn;
+ if( iSort<0 ){
+ /* ORDER BY rowid [ASC|DESC] */
+ pInfo->orderByConsumed = 1;
+ }else if( iSort==(pConfig->nCol+1) && ePlan==FTS5_PLAN_MATCH ){
+ /* ORDER BY rank [ASC|DESC] */
+ pInfo->orderByConsumed = 1;
+ ePlan = FTS5_PLAN_SORTED_MATCH;
+ }
+
+ if( pInfo->orderByConsumed ){
+ ePlan |= pInfo->aOrderBy[0].desc ? FTS5_ORDER_DESC : FTS5_ORDER_ASC;
+ }
}
pInfo->idxNum = ePlan;
int eStmt = fts5StmtType(pCsr->idxNum);
sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt);
}
- sqlite3Fts5ExprFree(pCsr->pExpr);
+ if( pCsr->pSorter ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ sqlite3_finalize(pSorter->pStmt);
+ sqlite3_free(pSorter);
+ }
+
+ if( pCsr->idxNum!=FTS5_PLAN_SOURCE ){
+ sqlite3Fts5ExprFree(pCsr->pExpr);
+ }
for(pData=pCsr->pAuxdata; pData; pData=pNext){
pNext = pData->pNext;
return SQLITE_OK;
}
+static int fts5SorterNext(Fts5Cursor *pCsr){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int rc;
+
+ rc = sqlite3_step(pSorter->pStmt);
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ }else if( rc==SQLITE_ROW ){
+ rc = SQLITE_OK;
+ pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0);
+ }
+
+ return rc;
+}
/*
** Advance the cursor to the next row in the table that matches the
int ePlan = FTS5_PLAN(pCsr->idxNum);
int rc = SQLITE_OK;
- if( ePlan!=FTS5_PLAN_MATCH ){
- rc = sqlite3_step(pCsr->pStmt);
- if( rc!=SQLITE_ROW ){
- CsrFlagSet(pCsr, FTS5CSR_EOF);
- rc = sqlite3_reset(pCsr->pStmt);
- }else{
- rc = SQLITE_OK;
- }
- }else{
- rc = sqlite3Fts5ExprNext(pCsr->pExpr);
- if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
- CsrFlagSet(pCsr, FTS5CSR_EOF);
+ switch( ePlan ){
+ case FTS5_PLAN_MATCH:
+ case FTS5_PLAN_SOURCE:
+ rc = sqlite3Fts5ExprNext(pCsr->pExpr);
+ if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ }
+ CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE );
+ break;
+
+ case FTS5_PLAN_SORTED_MATCH: {
+ rc = fts5SorterNext(pCsr);
+ break;
}
- CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE );
+
+ default:
+ rc = sqlite3_step(pCsr->pStmt);
+ if( rc!=SQLITE_ROW ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ rc = sqlite3_reset(pCsr->pStmt);
+ }else{
+ rc = SQLITE_OK;
+ }
+ break;
}
return rc;
}
+static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bAsc){
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Sorter *pSorter;
+ int nPhrase;
+ int nByte;
+ int rc = SQLITE_OK;
+ char *zSql;
+
+ nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+ nByte = sizeof(Fts5Sorter) + sizeof(int) * nPhrase;
+ pSorter = (Fts5Sorter*)sqlite3_malloc(nByte);
+ if( pSorter==0 ) return SQLITE_NOMEM;
+ memset(pSorter, 0, nByte);
+
+ zSql = sqlite3_mprintf("SELECT rowid, %Q FROM %Q.%Q ORDER BY +%s %s",
+ pConfig->zName, pConfig->zDb, pConfig->zName, FTS5_RANK_NAME,
+ bAsc ? "ASC" : "DESC"
+ );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pSorter->pStmt, 0);
+ sqlite3_free(zSql);
+ }
+
+ pCsr->pSorter = pSorter;
+ if( rc==SQLITE_OK ){
+ assert( pTab->pSortCsr==0 );
+ pTab->pSortCsr = pCsr;
+ rc = fts5SorterNext(pCsr);
+ pTab->pSortCsr = 0;
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pSorter->pStmt);
+ sqlite3_free(pSorter);
+ pCsr->pSorter = 0;
+ }
+
+ return rc;
+}
+
static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bAsc){
int rc;
rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, bAsc);
){
Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- int rc = SQLITE_OK;
- int ePlan = FTS5_PLAN(idxNum);
- int eStmt = fts5StmtType(idxNum);
int bAsc = ((idxNum & FTS5_ORDER_ASC) ? 1 : 0);
+ int rc = SQLITE_OK;
- pCsr->idxNum = idxNum;
assert( pCsr->pStmt==0 );
assert( pCsr->pExpr==0 );
assert( pCsr->csrflags==0 );
+ assert( pCsr->pRank==0 );
- rc = sqlite3Fts5StorageStmt(pTab->pStorage, eStmt, &pCsr->pStmt);
- if( rc==SQLITE_OK ){
- if( ePlan==FTS5_PLAN_MATCH ){
- char **pzErr = &pTab->base.zErrMsg;
- const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
- rc = sqlite3Fts5ExprNew(pTab->pConfig, zExpr, &pCsr->pExpr, pzErr);
- if( rc==SQLITE_OK ){
- rc = fts5CursorFirst(pTab, pCsr, bAsc);
- }
- }else{
- if( ePlan==FTS5_PLAN_ROWID ){
- sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ if( pTab->pSortCsr ){
+ pCsr->idxNum = FTS5_PLAN_SOURCE;
+ pCsr->pRank = pTab->pSortCsr->pRank;
+ pCsr->pExpr = pTab->pSortCsr->pExpr;
+ rc = fts5CursorFirst(pTab, pCsr, bAsc);
+ }else{
+ int ePlan = FTS5_PLAN(idxNum);
+ int eStmt = fts5StmtType(idxNum);
+ pCsr->idxNum = idxNum;
+ rc = sqlite3Fts5StorageStmt(pTab->pStorage, eStmt, &pCsr->pStmt);
+ if( rc==SQLITE_OK ){
+ if( ePlan==FTS5_PLAN_MATCH || ePlan==FTS5_PLAN_SORTED_MATCH ){
+ char **pzErr = &pTab->base.zErrMsg;
+ const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
+ pCsr->pRank = pTab->pGlobal->pAux;
+ rc = sqlite3Fts5ExprNew(pTab->pConfig, zExpr, &pCsr->pExpr, pzErr);
+ if( rc==SQLITE_OK ){
+ if( ePlan==FTS5_PLAN_MATCH ){
+ rc = fts5CursorFirst(pTab, pCsr, bAsc);
+ }else{
+ rc = fts5CursorFirstSorted(pTab, pCsr, bAsc);
+ }
+ }
+ }else{
+ if( ePlan==FTS5_PLAN_ROWID ){
+ sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ }
+ rc = fts5NextMethod(pCursor);
}
- rc = fts5NextMethod(pCursor);
}
}
int ePlan = FTS5_PLAN(pCsr->idxNum);
assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
- if( ePlan!=FTS5_PLAN_MATCH ){
- *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
- }else{
- *pRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
+ switch( ePlan ){
+ case FTS5_PLAN_SOURCE:
+ case FTS5_PLAN_MATCH:
+ *pRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
+ break;
+
+ case FTS5_PLAN_SORTED_MATCH:
+ *pRowid = pCsr->pSorter->iRowid;
+ break;
+
+ default:
+ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
+ break;
}
return SQLITE_OK;
return rc;
}
-/*
-** 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( CsrFlagTest(pCsr, FTS5CSR_EOF)==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;
-}
-
/*
** This function is called to handle an FTS INSERT command. In other words,
** an INSERT statement of the form:
int eConflict; /* ON CONFLICT for this DML */
int rc = SQLITE_OK; /* Return code */
- assert( nArg==1 || nArg==(2 + pConfig->nCol + 1) );
+ /* A delete specifies a single argument - the rowid of the row to remove.
+ ** Update and insert operations pass:
+ **
+ ** 1. The "old" rowid, or NULL.
+ ** 2. The "new" rowid.
+ ** 3. Values for each of the nCol matchable columns.
+ ** 4. Values for the two hidden columns (<tablename> and "rank").
+ */
+ assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) );
if( nArg>1 && SQLITE_NULL!=sqlite3_value_type(apVal[2 + pConfig->nCol]) ){
return fts5SpecialCommand(pTab, apVal[2 + pConfig->nCol]);
return rc;
}
+static void fts5ApiInvoke(
+ Fts5Auxiliary *pAux,
+ Fts5Cursor *pCsr,
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( pCsr->pAux==0 );
+ pCsr->pAux = pAux;
+ pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv);
+ pCsr->pAux = 0;
+}
+
static void fts5ApiCallback(
sqlite3_context *context,
int argc,
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(&sFts5Api, (Fts5Context*)pCsr, context, argc-1, &argv[1]);
- pCsr->pAux = 0;
+ fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[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( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
+
+ if( iCol==pConfig->nCol ){
+ if( FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_SOURCE ){
+ /* todo */
+ }else{
+ /* 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 if( iCol==pConfig->nCol+1 ){
+ /* The value of the "rank" column. */
+ if( pCsr->pRank ){
+ fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, 0, 0);
+ }
+ }else{
+ rc = fts5SeekCursor(pCsr);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
+ }
+ }
+ return rc;
+}
+
/*
** This routine implements the xFindFunction method for the FTS3
-C Add\sthe\s"loadfts"\sprogram,\sfor\sperformance\stesting\sthe\sloading\sof\sdata\sinto\sfts3/fts4/fts5\stables.
-D 2014-07-28T20:14:02.001
+C Add\shidden\scolumn\s"rank".\sCurrently\sthis\salways\sreturns\sthe\ssame\svalue\sas\sthe\sbm25()\sfunction.
+D 2014-07-30T19:41:58.841
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
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 1496aff16dd9b0a013d14b6c8cf5b7df8c170abe
+F ext/fts5/fts5.c f786dd1a3d091f7cb6572f6ee95f10f09de52224
F ext/fts5/fts5.h 8ace10d5b249a3baa983c79e7a1306d2a79cfd6a
-F ext/fts5/fts5Int.h 92fb9c4f759674ef569aebc338f363e167a8933c
-F ext/fts5/fts5_aux.c 243156c197384e17983d6a3ed149fa2270b5bb85
+F ext/fts5/fts5Int.h 9a195c1706876c538902f007149b39e982e9da53
+F ext/fts5/fts5_aux.c 3cae4225d458af41b64bb40ed9dcc052eb41b9a0
F ext/fts5/fts5_buffer.c 248c61ac9fec001602efc72a45704f3b8d367c00
-F ext/fts5/fts5_config.c 2138741013e189724b5d40ea7af0f48952a44916
-F ext/fts5/fts5_expr.c e426baa54b9473cb31b8d891d7d1b923bfb5d017
+F ext/fts5/fts5_config.c f4ebf143e141b8c77355e3b15aba81b7be51d710
+F ext/fts5/fts5_expr.c e764d75c58a3accda795f1da1b45960ac87dc77a
F ext/fts5/fts5_index.c 68d2d41b5c6d2f8838c3d6ebdc8b242718b8e997
F ext/fts5/fts5_storage.c 2866e7e1de9dc851756c3a9c76b6e1d75e0facb7
F ext/fts5/fts5parse.y 777da8e5819f75c217982c79c29d014c293acac9
F test/fts4merge4.test d895b1057a7798b67e03455d0fa50e9ea836c47b
F test/fts4noti.test 524807f0c36d49deea7920cdd4cd687408b58849
F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36
-F test/fts5aa.test a2c7bbc18f25f0b57ea8fc483c8a8830273b9ed4
+F test/fts5aa.test f90836c002804a82386d66c79b380128c5f3005e
F test/fts5ab.test dc04ed48cf93ca957d174406e6c192f2ff4f3397
F test/fts5ac.test 9be418d037763f4cc5d86f4239db41fc86bb4f85
F test/fts5ad.test 2ed38bbc865678cb2905247120d02ebba7f20e07
-F test/fts5ae.test 24b337571c51a10da1ae439b96b70317813a2fd4
+F test/fts5ae.test cb37b3135a00d3afd5492ec534ecf654be5ff69e
F test/fts5af.test 5f53d0a52280b63caf5a519d6994c4d428835155
F test/fts5ea.test ff43b40f8879ba50b82def70f2ab67c195d1a1d4
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 71d32f53e81921e43c933cc968cb1c18d83fe1e0
-R 378763b2640fc19d9f72a0522c9f77b1
+P 770b9540c19ad1e3d24adff382332bf032065efd
+R b6911e3c357a5fea00c97182f6d0088b
U dan
-Z 3cf4ed481646bab9077300595c244e00
+Z 526c63f1e91d68c01d64fde27fedef0b