From ded4f41d1a1dbedfa381cda102e77018236a4eff Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 5 Jan 2015 20:41:39 +0000 Subject: [PATCH] Tests and fixes for fts5 external content tables. FossilOrigin-Name: 047aaf830d1e72f0fdad3832a0b617e769d66468 --- ext/fts5/fts5.c | 70 ++++++++++++++++-------- ext/fts5/fts5Int.h | 14 +++-- ext/fts5/fts5_aux.c | 85 +++++++++++++++-------------- ext/fts5/fts5_config.c | 15 ++++-- ext/fts5/fts5_storage.c | 49 ++++++++++------- ext/fts5/test/fts5content.test | 99 ++++++++++++++++++++++++++++++++++ manifest | 21 ++++---- manifest.uuid | 2 +- 8 files changed, 254 insertions(+), 101 deletions(-) create mode 100644 ext/fts5/test/fts5content.test diff --git a/ext/fts5/fts5.c b/ext/fts5/fts5.c index 7ad9176f4e..ab2216e25d 100644 --- a/ext/fts5/fts5.c +++ b/ext/fts5/fts5.c @@ -256,6 +256,12 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ # define fts5CheckTransactionState(x,y,z) #endif +/* +** Return true if pTab is a contentless table. +*/ +static int fts5IsContentless(Fts5Table *pTab){ + return pTab->pConfig->eContent==FTS5_CONTENT_NONE; +} /* ** Close a virtual table handle opened by fts5InitVtab(). If the bDestroy @@ -917,7 +923,9 @@ static int fts5FilterMethod( /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup ** by rowid (ePlan==FTS5_PLAN_ROWID). */ int eStmt = fts5StmtType(idxNum); - rc = sqlite3Fts5StorageStmt(pTab->pStorage, eStmt, &pCsr->pStmt); + rc = sqlite3Fts5StorageStmt( + pTab->pStorage, eStmt, &pCsr->pStmt, &pTab->base.zErrMsg + ); if( rc==SQLITE_OK ){ if( ePlan==FTS5_PLAN_ROWID ){ sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); @@ -995,7 +1003,9 @@ static int fts5SeekCursor(Fts5Cursor *pCsr){ if( pCsr->pStmt==0 ){ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); int eStmt = fts5StmtType(pCsr->idxNum); - rc = sqlite3Fts5StorageStmt(pTab->pStorage, eStmt, &pCsr->pStmt); + rc = sqlite3Fts5StorageStmt( + pTab->pStorage, eStmt, &pCsr->pStmt, &pTab->base.zErrMsg + ); assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ); } @@ -1100,18 +1110,6 @@ static int fts5UpdateMethod( */ assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) ); - if( nArg>1 ){ - sqlite3_value *pCmd = sqlite3_value_type(apVal[2 + pConfig->nCol]); - if( SQLITE_NULL!=sqlite3_value_type(pCmd) ){ - const char *z = sqlite3_value_text(pCmd); - if( pConfig->bExternalContent && sqlite3_stricmp("delete", z) ){ - return fts5SpecialDelete(pTab, apVal, pRowid); - }else{ - return fts5SpecialInsert(pTab, pCmd, apVal[2 + pConfig->nCol + 1]); - } - } - } - eType0 = sqlite3_value_type(apVal[0]); eConflict = sqlite3_vtab_on_conflict(pConfig->db); @@ -1119,10 +1117,31 @@ static int fts5UpdateMethod( assert( pVtab->zErrMsg==0 ); if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){ - i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel); + if( fts5IsContentless(pTab) ){ + pTab->base.zErrMsg = sqlite3_mprintf( + "cannot %s contentless fts5 table: %s", + (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName + ); + rc = SQLITE_ERROR; + }else{ + i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel); + } + }else if( nArg>1 ){ + sqlite3_value *pCmd = apVal[2 + pConfig->nCol]; + if( SQLITE_NULL!=sqlite3_value_type(pCmd) ){ + const char *z = sqlite3_value_text(pCmd); + if( pConfig->eContent!=FTS5_CONTENT_NORMAL + && 0==sqlite3_stricmp("delete", z) + ){ + return fts5SpecialDelete(pTab, apVal, pRowid); + }else{ + return fts5SpecialInsert(pTab, pCmd, apVal[2 + pConfig->nCol + 1]); + } + } } + if( rc==SQLITE_OK && nArg>1 ){ rc = sqlite3Fts5StorageInsert(pTab->pStorage, apVal, eConflict, pRowid); } @@ -1328,11 +1347,17 @@ static int fts5ApiColumnText( const char **pz, int *pn ){ + int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - int rc = fts5SeekCursor(pCsr); - if( rc==SQLITE_OK ){ - *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); - *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); + if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ + *pz = 0; + *pn = 0; + }else{ + rc = fts5SeekCursor(pCsr); + if( rc==SQLITE_OK ){ + *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); + *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); + } } return rc; } @@ -1566,7 +1591,8 @@ static int fts5ColumnMethod( sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ int iCol /* Index of column to read value from */ ){ - Fts5Config *pConfig = ((Fts5Table*)(pCursor->pVtab))->pConfig; + Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); + Fts5Config *pConfig = pTab->pConfig; Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc = SQLITE_OK; @@ -1597,7 +1623,7 @@ static int fts5ColumnMethod( fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); } } - }else{ + }else if( !fts5IsContentless(pTab) ){ rc = fts5SeekCursor(pCsr); if( rc==SQLITE_OK ){ sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index ef5b9e56c7..30dafe9fff 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -76,9 +76,9 @@ struct Fts5Config { char **azCol; /* Column names */ int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ - int bExternalContent; /* Content is external */ - char *zContent; /* "content=" option value (or NULL) */ - char *zContentRowid; /* "content_rowid=" option value (or NULL) */ + int eContent; /* An FTS5_CONTENT value */ + char *zContent; /* content table */ + char *zContentRowid; /* "content_rowid=" option value */ Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; @@ -90,6 +90,12 @@ struct Fts5Config { char *zRankArgs; /* Arguments to rank function */ }; +#define FTS5_CONTENT_NORMAL 0 +#define FTS5_CONTENT_NONE 1 +#define FTS5_CONTENT_EXTERNAL 2 + + + int sqlite3Fts5ConfigParse( Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** ); @@ -401,7 +407,7 @@ int sqlite3Fts5StorageInsert(Fts5Storage *p, sqlite3_value **apVal, int, i64*); int sqlite3Fts5StorageIntegrity(Fts5Storage *p); -int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt **); +int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol); diff --git a/ext/fts5/fts5_aux.c b/ext/fts5/fts5_aux.c index 64904210f6..9ddb589085 100644 --- a/ext/fts5/fts5_aux.c +++ b/ext/fts5/fts5_aux.c @@ -222,21 +222,23 @@ static void fts5HighlightFunction( ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); - if( rc==SQLITE_OK ){ - rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); - } + if( ctx.zIn ){ + if( rc==SQLITE_OK ){ + rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); + } - if( rc==SQLITE_OK ){ - rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx, fts5HighlightCb); - } - fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); + if( rc==SQLITE_OK ){ + rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); + } + fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); - if( rc==SQLITE_OK ){ - sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); - }else{ - sqlite3_result_error_code(pCtx, rc); + if( rc==SQLITE_OK ){ + sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); + }else{ + sqlite3_result_error_code(pCtx, rc); + } + sqlite3_free(ctx.zOut); } - sqlite3_free(ctx.zOut); } /* ** End of highlight() implementation. @@ -275,7 +277,6 @@ static void fts5SnippetFunction( memset(&ctx, 0, sizeof(HighlightContext)); iCol = sqlite3_value_int(apVal[0]); - rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); zEllips = (const char*)sqlite3_value_text(apVal[3]); @@ -328,39 +329,41 @@ static void fts5SnippetFunction( if( rc==SQLITE_OK ){ rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn); } - if( rc==SQLITE_OK ){ - rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); - } + if( ctx.zIn ){ + if( rc==SQLITE_OK ){ + rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); + } - if( (iBestStart+nToken-1)>iBestLast ){ - iBestStart -= (iBestStart+nToken-1-iBestLast) / 2; - } - if( iBestStart+nToken>nColSize ){ - iBestStart = nColSize - nToken; - } - if( iBestStart<0 ) iBestStart = 0; + if( (iBestStart+nToken-1)>iBestLast ){ + iBestStart -= (iBestStart+nToken-1-iBestLast) / 2; + } + if( iBestStart+nToken>nColSize ){ + iBestStart = nColSize - nToken; + } + if( iBestStart<0 ) iBestStart = 0; - ctx.iRangeStart = iBestStart; - ctx.iRangeEnd = iBestStart + nToken - 1; + ctx.iRangeStart = iBestStart; + ctx.iRangeEnd = iBestStart + nToken - 1; - if( iBestStart>0 ){ - fts5HighlightAppend(&rc, &ctx, zEllips, -1); - } - if( rc==SQLITE_OK ){ - rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx, fts5HighlightCb); - } - if( ctx.iRangeEnd>=(nColSize-1) ){ - fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); - }else{ - fts5HighlightAppend(&rc, &ctx, zEllips, -1); - } + if( iBestStart>0 ){ + fts5HighlightAppend(&rc, &ctx, zEllips, -1); + } + if( rc==SQLITE_OK ){ + rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); + } + if( ctx.iRangeEnd>=(nColSize-1) ){ + fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); + }else{ + fts5HighlightAppend(&rc, &ctx, zEllips, -1); + } - if( rc==SQLITE_OK ){ - sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); - }else{ - sqlite3_result_error_code(pCtx, rc); + if( rc==SQLITE_OK ){ + sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); + }else{ + sqlite3_result_error_code(pCtx, rc); + } + sqlite3_free(ctx.zOut); } - sqlite3_free(ctx.zOut); sqlite3_free(aSeen); } diff --git a/ext/fts5/fts5_config.c b/ext/fts5/fts5_config.c index 07255c400a..7a2eba27ae 100644 --- a/ext/fts5/fts5_config.c +++ b/ext/fts5/fts5_config.c @@ -334,12 +334,19 @@ static int fts5ConfigParseSpecial( if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){ int rc = SQLITE_OK; - if( pConfig->zContent ){ + if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ *pzErr = sqlite3_mprintf("multiple content=... directives"); rc = SQLITE_ERROR; }else{ - pConfig->zContent = sqlite3_mprintf("%Q.%Q", pConfig->zDb, zArg); - pConfig->bExternalContent = 1; + if( zArg[0] ){ + pConfig->eContent = FTS5_CONTENT_EXTERNAL; + pConfig->zContent = sqlite3_mprintf("%Q.%Q", pConfig->zDb, zArg); + }else{ + pConfig->eContent = FTS5_CONTENT_NONE; + pConfig->zContent = sqlite3_mprintf( + "%Q.'%q_docsize'", pConfig->zDb, pConfig->zName + ); + } if( pConfig->zContent==0 ) rc = SQLITE_NOMEM; } return rc; @@ -473,7 +480,7 @@ int sqlite3Fts5ConfigParse( } /* If no zContent option was specified, fill in the default values. */ - if( rc==SQLITE_OK && pRet->zContent==0 ){ + if( rc==SQLITE_OK && pRet->eContent==FTS5_CONTENT_NORMAL ){ pRet->zContent = sqlite3_mprintf("%Q.'%q_content'", pRet->zDb, pRet->zName); if( pRet->zContent==0 ){ rc = SQLITE_NOMEM; diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c index 0bbf25fdab..e3119c7de4 100644 --- a/ext/fts5/fts5_storage.c +++ b/ext/fts5/fts5_storage.c @@ -54,7 +54,8 @@ struct Fts5Storage { static int fts5StorageGetStmt( Fts5Storage *p, /* Storage handle */ int eStmt, /* FTS5_STMT_XXX constant */ - sqlite3_stmt **ppStmt /* OUT: Prepared statement handle */ + sqlite3_stmt **ppStmt, /* OUT: Prepared statement handle */ + char **pzErrMsg /* OUT: Error message (if any) */ ){ int rc = SQLITE_OK; @@ -117,6 +118,9 @@ static int fts5StorageGetStmt( }else{ rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0); sqlite3_free(zSql); + if( rc!=SQLITE_OK && pzErrMsg ){ + *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); + } } } @@ -205,7 +209,7 @@ int sqlite3Fts5StorageOpen( p->pIndex = pIndex; if( bCreate ){ - if( pConfig->bExternalContent==0 ){ + if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10); if( zDefn==0 ){ rc = SQLITE_NOMEM; @@ -254,7 +258,9 @@ int sqlite3Fts5StorageClose(Fts5Storage *p, int bDestroy){ /* If required, remove the shadow tables from the database */ if( bDestroy ){ - rc = sqlite3Fts5DropTable(p->pConfig, "content"); + if( p->pConfig->eContent==FTS5_CONTENT_NORMAL ){ + rc = sqlite3Fts5DropTable(p->pConfig, "content"); + } if( rc==SQLITE_OK ) rc = sqlite3Fts5DropTable(p->pConfig, "docsize"); if( rc==SQLITE_OK ) rc = sqlite3Fts5DropTable(p->pConfig, "config"); } @@ -298,7 +304,7 @@ static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){ sqlite3_stmt *pSeek; /* SELECT to read row iDel from %_data */ int rc; /* Return code */ - rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek); + rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0); if( rc==SQLITE_OK ){ int rc2; sqlite3_bind_int64(pSeek, 1, iDel); @@ -338,7 +344,7 @@ static int fts5StorageInsertDocsize( Fts5Buffer *pBuf /* sz value */ ){ sqlite3_stmt *pReplace = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace); + int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0); if( rc==SQLITE_OK ){ sqlite3_bind_int64(pReplace, 1, iRowid); sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); @@ -424,7 +430,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){ /* Delete the %_docsize record */ if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel); + rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); } if( rc==SQLITE_OK ){ sqlite3_bind_int64(pDel, 1, iDel); @@ -434,7 +440,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){ /* Delete the %_content record */ if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel); + rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); } if( rc==SQLITE_OK ){ sqlite3_bind_int64(pDel, 1, iDel); @@ -459,7 +465,7 @@ int sqlite3Fts5StorageSpecialDelete( int rc; sqlite3_stmt *pDel; - assert( p->pConfig->bExternalContent ); + assert( p->pConfig->eContent!=FTS5_CONTENT_NORMAL ); rc = fts5StorageLoadTotals(p, 1); /* Delete the index records */ @@ -477,14 +483,14 @@ int sqlite3Fts5StorageSpecialDelete( (void*)&ctx, fts5StorageInsertCallback ); - p->aTotalSize[iCol-1] -= (i64)ctx.szCol; + p->aTotalSize[iCol] -= (i64)ctx.szCol; } p->nTotalRow--; } /* Delete the %_docsize record */ if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel); + rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); } if( rc==SQLITE_OK ){ sqlite3_bind_int64(pDel, 1, iDel); @@ -509,7 +515,7 @@ int sqlite3Fts5StorageSpecialDelete( */ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ sqlite3_stmt *pReplace = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace); + int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0); if( rc==SQLITE_OK ){ sqlite3_bind_null(pReplace, 1); sqlite3_bind_null(pReplace, 2); @@ -543,8 +549,8 @@ int sqlite3Fts5StorageInsert( rc = fts5StorageLoadTotals(p, 1); /* Insert the new row into the %_content table. */ - if( rc==SQLITE_OK && pConfig->bExternalContent==0 ){ - if( pConfig->bExternalContent ){ + if( rc==SQLITE_OK ){ + if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ *piRowid = sqlite3_value_int64(apVal[1]); }else{ @@ -560,7 +566,7 @@ int sqlite3Fts5StorageInsert( eStmt = FTS5_STMT_INSERT_CONTENT; } if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, eStmt, &pInsert); + rc = fts5StorageGetStmt(p, eStmt, &pInsert, 0); } for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ rc = sqlite3_bind_value(pInsert, i, apVal[i]); @@ -682,7 +688,7 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ /* Generate the expected index checksum based on the contents of the ** %_content table. This block stores the checksum in ctx.cksum. */ - rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN_ASC, &pScan); + rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN_ASC, &pScan, 0); if( rc==SQLITE_OK ){ int rc2; while( SQLITE_ROW==sqlite3_step(pScan) ){ @@ -745,13 +751,18 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ ** Obtain an SQLite statement handle that may be used to read data from the ** %_content table. */ -int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt **pp){ +int sqlite3Fts5StorageStmt( + Fts5Storage *p, + int eStmt, + sqlite3_stmt **pp, + char **pzErrMsg +){ int rc; assert( eStmt==FTS5_STMT_SCAN_ASC || eStmt==FTS5_STMT_SCAN_DESC || eStmt==FTS5_STMT_LOOKUP ); - rc = fts5StorageGetStmt(p, eStmt, pp); + rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg); if( rc==SQLITE_OK ){ assert( p->aStmt[eStmt]==*pp ); p->aStmt[eStmt] = 0; @@ -805,7 +816,7 @@ static int fts5StorageDecodeSizeArray( int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ int nCol = p->pConfig->nCol; sqlite3_stmt *pLookup = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup); + int rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); if( rc==SQLITE_OK ){ int bCorrupt = 1; sqlite3_bind_int64(pLookup, 1, iRowid); @@ -873,7 +884,7 @@ int sqlite3Fts5StorageConfigValue( sqlite3_value *pVal ){ sqlite3_stmt *pReplace = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace); + int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0); if( rc==SQLITE_OK ){ sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_TRANSIENT); sqlite3_bind_value(pReplace, 2, pVal); diff --git a/ext/fts5/test/fts5content.test b/ext/fts5/test/fts5content.test new file mode 100644 index 0000000000..4940a6b03c --- /dev/null +++ b/ext/fts5/test/fts5content.test @@ -0,0 +1,99 @@ +# 2014 Dec 20 +# +# 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. +# +#*********************************************************************** +# +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. .. test] +} +source $testdir/tester.tcl +set testprefix fts5content + +do_execsql_test 1.1 { + CREATE VIRTUAL TABLE f1 USING fts5(a, b, content=''); + INSERT INTO f1(rowid, a, b) VALUES(1, 'one', 'o n e'); + INSERT INTO f1(rowid, a, b) VALUES(2, 'two', 't w o'); + INSERT INTO f1(rowid, a, b) VALUES(3, 'three', 't h r e e'); +} + +do_execsql_test 1.2 { + SELECT rowid FROM f1 WHERE f1 MATCH 'o'; +} {2 1} + +do_execsql_test 1.3 { + INSERT INTO f1(a, b) VALUES('four', 'f o u r'); + SELECT rowid FROM f1 WHERE f1 MATCH 'o'; +} {4 2 1} + +do_execsql_test 1.4 { + SELECT rowid, a, b FROM f1 WHERE f1 MATCH 'o'; +} {4 {} {} 2 {} {} 1 {} {}} + +do_execsql_test 1.5 { + SELECT rowid, highlight(f1, 0, '[', ']') FROM f1 WHERE f1 MATCH 'o'; +} {4 {} 2 {} 1 {}} + +do_execsql_test 1.6 { + SELECT rowid, highlight(f1, 0, '[', ']') IS NULL FROM f1 WHERE f1 MATCH 'o'; +} {4 1 2 1 1 1} + +do_execsql_test 1.7 { + SELECT rowid, snippet(f1, -1, '[', ']', '...', 5) IS NULL + FROM f1 WHERE f1 MATCH 'o'; +} {4 1 2 1 1 1} + +do_execsql_test 1.8 { + SELECT rowid, snippet(f1, 1, '[', ']', '...', 5) IS NULL + FROM f1 WHERE f1 MATCH 'o'; +} {4 1 2 1 1 1} + +do_execsql_test 1.9 { + SELECT rowid FROM f1; +} {4 3 2 1} + +do_execsql_test 1.10 { + SELECT * FROM f1; +} {{} {} {} {} {} {} {} {}} + +do_execsql_test 1.11 { + SELECT rowid, a, b FROM f1 ORDER BY rowid ASC; +} {1 {} {} 2 {} {} 3 {} {} 4 {} {}} + +do_execsql_test 1.12 { + SELECT a IS NULL FROM f1; +} {1 1 1 1} + +do_catchsql_test 1.13 { + DELETE FROM f1 WHERE rowid = 2; +} {1 {cannot DELETE from contentless fts5 table: f1}} + +do_catchsql_test 1.14 { + UPDATE f1 SET a = 'a b c' WHERE rowid = 2; +} {1 {cannot UPDATE contentless fts5 table: f1}} + +do_execsql_test 1.15 { + INSERT INTO f1(f1, rowid, a, b) VALUES('delete', 2, 'two', 't w o'); +} {} + +db eval { SELECT fts5_decode(id, block) AS d FROM f1_data } { puts $d } + +breakpoint +do_execsql_test 1.16 { + SELECT rowid FROM f1 WHERE f1 MATCH 'o'; +} {4 1} +do_execsql_test 1.17 { + SELECT rowid FROM f1; +} {4 3 1} + + + + +finish_test diff --git a/manifest b/manifest index 90fe542994..8c6f633d3c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\sexternal\scontent\stables\sto\sfts5. -D 2015-01-03T20:44:58.134 +C Tests\sand\sfixes\sfor\sfts5\sexternal\scontent\stables. +D 2015-01-05T20:41:39.791 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 7cd23e4fc91004a6bd081623e1bc6932e44828c0 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -104,16 +104,16 @@ F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl 4199cb887040ee3c3cd59a5171ddb0566904586e F ext/fts5/extract_api_docs.tcl 6320db4a1d0722a4e2069e661381ad75e9889786 -F ext/fts5/fts5.c 16177d7f81af1852cf7f477b5ae119215ad6044a +F ext/fts5/fts5.c e2c19b2c5ab96650732bb6904892a6fb9a27ab42 F ext/fts5/fts5.h 4f9d2c477c0ee1907164642471329a82cb6b203b -F ext/fts5/fts5Int.h 8062dc2363c863dc8a5b2e5651cb8c966bd6c4cb -F ext/fts5/fts5_aux.c 445e54031ff94174673f4f5aac6c064df20a2a6b +F ext/fts5/fts5Int.h 9aafe97064e9c3380991abad4f51bee51021d18d +F ext/fts5/fts5_aux.c a74523025a553f57c99c699b9e2d83c4506503b4 F ext/fts5/fts5_buffer.c 1bc5c762bb2e9b4a40b2e8a820a31b809e72eec1 -F ext/fts5/fts5_config.c 16d647c7bfe50d4e823267188e12e2d001d655e0 +F ext/fts5/fts5_config.c 630f92bb0a301c0b4e37a05ec4e38dc51ceeba37 F ext/fts5/fts5_expr.c 317093f00a2ccdaaee0a5290f9f228c600189c41 F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279 F ext/fts5/fts5_index.c 4a8e8535b4303400ddb5f6fb08152da0d88ebf6f -F ext/fts5/fts5_storage.c b95fcca70f94656854e7afcfbb9896455f6b034d +F ext/fts5/fts5_storage.c 68ce8ec98b009cbd350ff73df06a97b1a012e122 F ext/fts5/fts5_tcl.c 664e710e2bbeed505cb91848772ca7538623a67f F ext/fts5/fts5_tokenize.c 5a0ad46408d09bcda2bf0addb5af42fdb75ebabb F ext/fts5/fts5_unicode2.c 9c7dd640d1f014bf5c3ee029759adfbb4d7e95a9 @@ -131,6 +131,7 @@ F ext/fts5/test/fts5aj.test 1a64ab4144f54bd12a520683950bf8460dd74fb3 F ext/fts5/test/fts5ak.test df2669fb76684f03d03918dfb2cf692012251b1f F ext/fts5/test/fts5al.test bc873766fec3baae05ba6e76b379bc2f5e8eaf75 F ext/fts5/test/fts5auxdata.test fec4c9113176d351e567eab65fe9917e5ea0ab05 +F ext/fts5/test/fts5content.test 0f267ba2086f2dff81484c8ee71fa0d3990c41f7 F ext/fts5/test/fts5ea.test 0ef2c89e14c6360ad3905fae44409420d6b5a5c8 F ext/fts5/test/fts5fault1.test b95ed600b88bbbce5390f9097a5a5b7b01b3b9f7 F ext/fts5/test/fts5porter.test d8f7591b733bcc1f02ca0dd313bc891a4b289562 @@ -1270,7 +1271,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 1cd15a1759004d5d321056905dbb6acff20dc7d9 -R d21eb6bee3b06e51f22d32d1e0bd7016 +P 17ef5b59f789e9fa35c4f053246d819987fd06f8 +R 6bfe2a49f6feaf1db299d5e29da25a24 U dan -Z e88e77f44b464406d3184a89736eaa7d +Z 3d2200ed8057fd64a39f743bdc333945 diff --git a/manifest.uuid b/manifest.uuid index 49cb51dbf0..ccfd516ce0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -17ef5b59f789e9fa35c4f053246d819987fd06f8 \ No newline at end of file +047aaf830d1e72f0fdad3832a0b617e769d66468 \ No newline at end of file