Tests and fixes for fts5 external content tables.

FossilOrigin-Name: 047aaf830d1e72f0fdad3832a0b617e769d66468
This commit is contained in:
dan 2015-01-05 20:41:39 +00:00
parent 0fbc269fef
commit ded4f41d1a
8 changed files with 254 additions and 101 deletions

View File

@ -256,6 +256,12 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
# define fts5CheckTransactionState(x,y,z) # define fts5CheckTransactionState(x,y,z)
#endif #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 ** 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 /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
** by rowid (ePlan==FTS5_PLAN_ROWID). */ ** by rowid (ePlan==FTS5_PLAN_ROWID). */
int eStmt = fts5StmtType(idxNum); 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( rc==SQLITE_OK ){
if( ePlan==FTS5_PLAN_ROWID ){ if( ePlan==FTS5_PLAN_ROWID ){
sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
@ -995,7 +1003,9 @@ static int fts5SeekCursor(Fts5Cursor *pCsr){
if( pCsr->pStmt==0 ){ if( pCsr->pStmt==0 ){
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
int eStmt = fts5StmtType(pCsr->idxNum); 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) ); assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) );
} }
@ -1100,18 +1110,6 @@ static int fts5UpdateMethod(
*/ */
assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) ); 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]); eType0 = sqlite3_value_type(apVal[0]);
eConflict = sqlite3_vtab_on_conflict(pConfig->db); eConflict = sqlite3_vtab_on_conflict(pConfig->db);
@ -1119,10 +1117,31 @@ static int fts5UpdateMethod(
assert( pVtab->zErrMsg==0 ); assert( pVtab->zErrMsg==0 );
if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){ if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ if( fts5IsContentless(pTab) ){
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel); 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 ){ if( rc==SQLITE_OK && nArg>1 ){
rc = sqlite3Fts5StorageInsert(pTab->pStorage, apVal, eConflict, pRowid); rc = sqlite3Fts5StorageInsert(pTab->pStorage, apVal, eConflict, pRowid);
} }
@ -1328,11 +1347,17 @@ static int fts5ApiColumnText(
const char **pz, const char **pz,
int *pn int *pn
){ ){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
int rc = fts5SeekCursor(pCsr); if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){
if( rc==SQLITE_OK ){ *pz = 0;
*pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); *pn = 0;
*pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); }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; return rc;
} }
@ -1566,7 +1591,8 @@ static int fts5ColumnMethod(
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */ 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; Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc = SQLITE_OK; int rc = SQLITE_OK;
@ -1597,7 +1623,7 @@ static int fts5ColumnMethod(
fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg);
} }
} }
}else{ }else if( !fts5IsContentless(pTab) ){
rc = fts5SeekCursor(pCsr); rc = fts5SeekCursor(pCsr);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));

View File

@ -76,9 +76,9 @@ struct Fts5Config {
char **azCol; /* Column names */ char **azCol; /* Column names */
int nPrefix; /* Number of prefix indexes */ int nPrefix; /* Number of prefix indexes */
int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
int bExternalContent; /* Content is external */ int eContent; /* An FTS5_CONTENT value */
char *zContent; /* "content=" option value (or NULL) */ char *zContent; /* content table */
char *zContentRowid; /* "content_rowid=" option value (or NULL) */ char *zContentRowid; /* "content_rowid=" option value */
Fts5Tokenizer *pTok; Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi; fts5_tokenizer *pTokApi;
@ -90,6 +90,12 @@ struct Fts5Config {
char *zRankArgs; /* Arguments to rank function */ char *zRankArgs; /* Arguments to rank function */
}; };
#define FTS5_CONTENT_NORMAL 0
#define FTS5_CONTENT_NONE 1
#define FTS5_CONTENT_EXTERNAL 2
int sqlite3Fts5ConfigParse( int sqlite3Fts5ConfigParse(
Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** 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 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*); void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*);
int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol); int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol);

View File

@ -222,21 +222,23 @@ static void fts5HighlightFunction(
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
if( rc==SQLITE_OK ){ if( ctx.zIn ){
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); if( rc==SQLITE_OK ){
} rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
}
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx, fts5HighlightCb); rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
} }
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
}else{ }else{
sqlite3_result_error_code(pCtx, rc); sqlite3_result_error_code(pCtx, rc);
}
sqlite3_free(ctx.zOut);
} }
sqlite3_free(ctx.zOut);
} }
/* /*
** End of highlight() implementation. ** End of highlight() implementation.
@ -275,7 +277,6 @@ static void fts5SnippetFunction(
memset(&ctx, 0, sizeof(HighlightContext)); memset(&ctx, 0, sizeof(HighlightContext));
iCol = sqlite3_value_int(apVal[0]); 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.zOpen = (const char*)sqlite3_value_text(apVal[1]);
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
zEllips = (const char*)sqlite3_value_text(apVal[3]); zEllips = (const char*)sqlite3_value_text(apVal[3]);
@ -328,39 +329,41 @@ static void fts5SnippetFunction(
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn); rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
} }
if( rc==SQLITE_OK ){ if( ctx.zIn ){
rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); if( rc==SQLITE_OK ){
} rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
}
if( (iBestStart+nToken-1)>iBestLast ){ if( (iBestStart+nToken-1)>iBestLast ){
iBestStart -= (iBestStart+nToken-1-iBestLast) / 2; iBestStart -= (iBestStart+nToken-1-iBestLast) / 2;
} }
if( iBestStart+nToken>nColSize ){ if( iBestStart+nToken>nColSize ){
iBestStart = nColSize - nToken; iBestStart = nColSize - nToken;
} }
if( iBestStart<0 ) iBestStart = 0; if( iBestStart<0 ) iBestStart = 0;
ctx.iRangeStart = iBestStart; ctx.iRangeStart = iBestStart;
ctx.iRangeEnd = iBestStart + nToken - 1; ctx.iRangeEnd = iBestStart + nToken - 1;
if( iBestStart>0 ){ if( iBestStart>0 ){
fts5HighlightAppend(&rc, &ctx, zEllips, -1); fts5HighlightAppend(&rc, &ctx, zEllips, -1);
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx, fts5HighlightCb); rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
} }
if( ctx.iRangeEnd>=(nColSize-1) ){ if( ctx.iRangeEnd>=(nColSize-1) ){
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
}else{ }else{
fts5HighlightAppend(&rc, &ctx, zEllips, -1); fts5HighlightAppend(&rc, &ctx, zEllips, -1);
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
}else{ }else{
sqlite3_result_error_code(pCtx, rc); sqlite3_result_error_code(pCtx, rc);
}
sqlite3_free(ctx.zOut);
} }
sqlite3_free(ctx.zOut);
sqlite3_free(aSeen); sqlite3_free(aSeen);
} }

View File

@ -334,12 +334,19 @@ static int fts5ConfigParseSpecial(
if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){ if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){
int rc = SQLITE_OK; int rc = SQLITE_OK;
if( pConfig->zContent ){ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
*pzErr = sqlite3_mprintf("multiple content=... directives"); *pzErr = sqlite3_mprintf("multiple content=... directives");
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
}else{ }else{
pConfig->zContent = sqlite3_mprintf("%Q.%Q", pConfig->zDb, zArg); if( zArg[0] ){
pConfig->bExternalContent = 1; 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; if( pConfig->zContent==0 ) rc = SQLITE_NOMEM;
} }
return rc; return rc;
@ -473,7 +480,7 @@ int sqlite3Fts5ConfigParse(
} }
/* If no zContent option was specified, fill in the default values. */ /* 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); pRet->zContent = sqlite3_mprintf("%Q.'%q_content'", pRet->zDb, pRet->zName);
if( pRet->zContent==0 ){ if( pRet->zContent==0 ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;

View File

@ -54,7 +54,8 @@ struct Fts5Storage {
static int fts5StorageGetStmt( static int fts5StorageGetStmt(
Fts5Storage *p, /* Storage handle */ Fts5Storage *p, /* Storage handle */
int eStmt, /* FTS5_STMT_XXX constant */ 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; int rc = SQLITE_OK;
@ -117,6 +118,9 @@ static int fts5StorageGetStmt(
}else{ }else{
rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0); rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0);
sqlite3_free(zSql); 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; p->pIndex = pIndex;
if( bCreate ){ if( bCreate ){
if( pConfig->bExternalContent==0 ){ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10); char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
if( zDefn==0 ){ if( zDefn==0 ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
@ -254,7 +258,9 @@ int sqlite3Fts5StorageClose(Fts5Storage *p, int bDestroy){
/* If required, remove the shadow tables from the database */ /* If required, remove the shadow tables from the database */
if( bDestroy ){ 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, "docsize");
if( rc==SQLITE_OK ) rc = sqlite3Fts5DropTable(p->pConfig, "config"); 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 */ sqlite3_stmt *pSeek; /* SELECT to read row iDel from %_data */
int rc; /* Return code */ int rc; /* Return code */
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek); rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
int rc2; int rc2;
sqlite3_bind_int64(pSeek, 1, iDel); sqlite3_bind_int64(pSeek, 1, iDel);
@ -338,7 +344,7 @@ static int fts5StorageInsertDocsize(
Fts5Buffer *pBuf /* sz value */ Fts5Buffer *pBuf /* sz value */
){ ){
sqlite3_stmt *pReplace = 0; 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 ){ if( rc==SQLITE_OK ){
sqlite3_bind_int64(pReplace, 1, iRowid); sqlite3_bind_int64(pReplace, 1, iRowid);
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); 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 */ /* Delete the %_docsize record */
if( rc==SQLITE_OK ){ 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 ){ if( rc==SQLITE_OK ){
sqlite3_bind_int64(pDel, 1, iDel); sqlite3_bind_int64(pDel, 1, iDel);
@ -434,7 +440,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){
/* Delete the %_content record */ /* Delete the %_content record */
if( rc==SQLITE_OK ){ 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 ){ if( rc==SQLITE_OK ){
sqlite3_bind_int64(pDel, 1, iDel); sqlite3_bind_int64(pDel, 1, iDel);
@ -459,7 +465,7 @@ int sqlite3Fts5StorageSpecialDelete(
int rc; int rc;
sqlite3_stmt *pDel; sqlite3_stmt *pDel;
assert( p->pConfig->bExternalContent ); assert( p->pConfig->eContent!=FTS5_CONTENT_NORMAL );
rc = fts5StorageLoadTotals(p, 1); rc = fts5StorageLoadTotals(p, 1);
/* Delete the index records */ /* Delete the index records */
@ -477,14 +483,14 @@ int sqlite3Fts5StorageSpecialDelete(
(void*)&ctx, (void*)&ctx,
fts5StorageInsertCallback fts5StorageInsertCallback
); );
p->aTotalSize[iCol-1] -= (i64)ctx.szCol; p->aTotalSize[iCol] -= (i64)ctx.szCol;
} }
p->nTotalRow--; p->nTotalRow--;
} }
/* Delete the %_docsize record */ /* Delete the %_docsize record */
if( rc==SQLITE_OK ){ 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 ){ if( rc==SQLITE_OK ){
sqlite3_bind_int64(pDel, 1, iDel); sqlite3_bind_int64(pDel, 1, iDel);
@ -509,7 +515,7 @@ int sqlite3Fts5StorageSpecialDelete(
*/ */
static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
sqlite3_stmt *pReplace = 0; 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 ){ if( rc==SQLITE_OK ){
sqlite3_bind_null(pReplace, 1); sqlite3_bind_null(pReplace, 1);
sqlite3_bind_null(pReplace, 2); sqlite3_bind_null(pReplace, 2);
@ -543,8 +549,8 @@ int sqlite3Fts5StorageInsert(
rc = fts5StorageLoadTotals(p, 1); rc = fts5StorageLoadTotals(p, 1);
/* Insert the new row into the %_content table. */ /* Insert the new row into the %_content table. */
if( rc==SQLITE_OK && pConfig->bExternalContent==0 ){ if( rc==SQLITE_OK ){
if( pConfig->bExternalContent ){ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
*piRowid = sqlite3_value_int64(apVal[1]); *piRowid = sqlite3_value_int64(apVal[1]);
}else{ }else{
@ -560,7 +566,7 @@ int sqlite3Fts5StorageInsert(
eStmt = FTS5_STMT_INSERT_CONTENT; eStmt = FTS5_STMT_INSERT_CONTENT;
} }
if( rc==SQLITE_OK ){ 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++){ for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
rc = sqlite3_bind_value(pInsert, i, apVal[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 /* Generate the expected index checksum based on the contents of the
** %_content table. This block stores the checksum in ctx.cksum. */ ** %_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 ){ if( rc==SQLITE_OK ){
int rc2; int rc2;
while( SQLITE_ROW==sqlite3_step(pScan) ){ 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 ** Obtain an SQLite statement handle that may be used to read data from the
** %_content table. ** %_content table.
*/ */
int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt **pp){ int sqlite3Fts5StorageStmt(
Fts5Storage *p,
int eStmt,
sqlite3_stmt **pp,
char **pzErrMsg
){
int rc; int rc;
assert( eStmt==FTS5_STMT_SCAN_ASC assert( eStmt==FTS5_STMT_SCAN_ASC
|| eStmt==FTS5_STMT_SCAN_DESC || eStmt==FTS5_STMT_SCAN_DESC
|| eStmt==FTS5_STMT_LOOKUP || eStmt==FTS5_STMT_LOOKUP
); );
rc = fts5StorageGetStmt(p, eStmt, pp); rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
assert( p->aStmt[eStmt]==*pp ); assert( p->aStmt[eStmt]==*pp );
p->aStmt[eStmt] = 0; p->aStmt[eStmt] = 0;
@ -805,7 +816,7 @@ static int fts5StorageDecodeSizeArray(
int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
int nCol = p->pConfig->nCol; int nCol = p->pConfig->nCol;
sqlite3_stmt *pLookup = 0; 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 ){ if( rc==SQLITE_OK ){
int bCorrupt = 1; int bCorrupt = 1;
sqlite3_bind_int64(pLookup, 1, iRowid); sqlite3_bind_int64(pLookup, 1, iRowid);
@ -873,7 +884,7 @@ int sqlite3Fts5StorageConfigValue(
sqlite3_value *pVal sqlite3_value *pVal
){ ){
sqlite3_stmt *pReplace = 0; 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 ){ if( rc==SQLITE_OK ){
sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_TRANSIENT); sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_TRANSIENT);
sqlite3_bind_value(pReplace, 2, pVal); sqlite3_bind_value(pReplace, 2, pVal);

View File

@ -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

View File

@ -1,5 +1,5 @@
C Add\ssupport\sfor\sexternal\scontent\stables\sto\sfts5. C Tests\sand\sfixes\sfor\sfts5\sexternal\scontent\stables.
D 2015-01-03T20:44:58.134 D 2015-01-05T20:41:39.791
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 7cd23e4fc91004a6bd081623e1bc6932e44828c0 F Makefile.in 7cd23e4fc91004a6bd081623e1bc6932e44828c0
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 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/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
F ext/fts3/unicode/mkunicode.tcl 4199cb887040ee3c3cd59a5171ddb0566904586e F ext/fts3/unicode/mkunicode.tcl 4199cb887040ee3c3cd59a5171ddb0566904586e
F ext/fts5/extract_api_docs.tcl 6320db4a1d0722a4e2069e661381ad75e9889786 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/fts5.h 4f9d2c477c0ee1907164642471329a82cb6b203b
F ext/fts5/fts5Int.h 8062dc2363c863dc8a5b2e5651cb8c966bd6c4cb F ext/fts5/fts5Int.h 9aafe97064e9c3380991abad4f51bee51021d18d
F ext/fts5/fts5_aux.c 445e54031ff94174673f4f5aac6c064df20a2a6b F ext/fts5/fts5_aux.c a74523025a553f57c99c699b9e2d83c4506503b4
F ext/fts5/fts5_buffer.c 1bc5c762bb2e9b4a40b2e8a820a31b809e72eec1 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_expr.c 317093f00a2ccdaaee0a5290f9f228c600189c41
F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279 F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279
F ext/fts5/fts5_index.c 4a8e8535b4303400ddb5f6fb08152da0d88ebf6f 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_tcl.c 664e710e2bbeed505cb91848772ca7538623a67f
F ext/fts5/fts5_tokenize.c 5a0ad46408d09bcda2bf0addb5af42fdb75ebabb F ext/fts5/fts5_tokenize.c 5a0ad46408d09bcda2bf0addb5af42fdb75ebabb
F ext/fts5/fts5_unicode2.c 9c7dd640d1f014bf5c3ee029759adfbb4d7e95a9 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/fts5ak.test df2669fb76684f03d03918dfb2cf692012251b1f
F ext/fts5/test/fts5al.test bc873766fec3baae05ba6e76b379bc2f5e8eaf75 F ext/fts5/test/fts5al.test bc873766fec3baae05ba6e76b379bc2f5e8eaf75
F ext/fts5/test/fts5auxdata.test fec4c9113176d351e567eab65fe9917e5ea0ab05 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/fts5ea.test 0ef2c89e14c6360ad3905fae44409420d6b5a5c8
F ext/fts5/test/fts5fault1.test b95ed600b88bbbce5390f9097a5a5b7b01b3b9f7 F ext/fts5/test/fts5fault1.test b95ed600b88bbbce5390f9097a5a5b7b01b3b9f7
F ext/fts5/test/fts5porter.test d8f7591b733bcc1f02ca0dd313bc891a4b289562 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-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 1cd15a1759004d5d321056905dbb6acff20dc7d9 P 17ef5b59f789e9fa35c4f053246d819987fd06f8
R d21eb6bee3b06e51f22d32d1e0bd7016 R 6bfe2a49f6feaf1db299d5e29da25a24
U dan U dan
Z e88e77f44b464406d3184a89736eaa7d Z 3d2200ed8057fd64a39f743bdc333945

View File

@ -1 +1 @@
17ef5b59f789e9fa35c4f053246d819987fd06f8 047aaf830d1e72f0fdad3832a0b617e769d66468