1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Instead of disallowing writes to fts5 tables if there are fts5vocab cursors open on them (commit [c49a6ed7]), abort any fts5vocab queries if the on-disk structure of the fts5 table changes.

FossilOrigin-Name: 9dbdc9001e3258e71ca995fbcdebf66ab95890ded87fa7125c6cb4bd43010aaf
This commit is contained in:
dan
2021-07-07 11:51:03 +00:00
parent 9289f5103d
commit cc516af4cc
8 changed files with 65 additions and 32 deletions

View File

@ -435,6 +435,9 @@ void sqlite3Fts5IndexCloseReader(Fts5Index*);
*/
const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*);
int sqlite3Fts5IterNextScan(Fts5IndexIter*);
void *sqlite3Fts5StructureRef(Fts5Index*);
void sqlite3Fts5StructureRelease(void*);
int sqlite3Fts5StructureTest(Fts5Index*, void*);
/*
@ -564,7 +567,6 @@ int sqlite3Fts5GetTokenizer(
Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64);
int sqlite3Fts5FlushToDisk(Fts5Table*);
void sqlite3Fts5VocabLock(Fts5Table*, int);
/*
** End of interface to code in fts5.c.

View File

@ -821,6 +821,22 @@ static void fts5StructureRef(Fts5Structure *pStruct){
pStruct->nRef++;
}
void *sqlite3Fts5StructureRef(Fts5Index *p){
fts5StructureRef(p->pStruct);
return (void*)p->pStruct;
}
void sqlite3Fts5StructureRelease(void *p){
if( p ){
fts5StructureRelease((Fts5Structure*)p);
}
}
int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){
if( p->pStruct!=(Fts5Structure*)pStruct ){
return SQLITE_ABORT;
}
return SQLITE_OK;
}
/*
** Deserialize and return the structure record currently stored in serialized
** form within buffer pData/nData.

View File

@ -117,7 +117,6 @@ struct Fts5FullTable {
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
int nVocabLock; /* Number of locks held by fts5vocab csrs */
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
@ -1635,9 +1634,7 @@ static int fts5UpdateMethod(
assert( pTab->p.pConfig->pzErrmsg==0 );
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
/* Return an error if there are any fts5vocab cursors open. Put any active
** fts5 cursors into REQUIRE_SEEK state. */
if( pTab->nVocabLock ) return SQLITE_LOCKED;
/* Put any active cursors into REQUIRE_SEEK state. */
fts5TripCursors(pTab);
eType0 = sqlite3_value_type(apVal[0]);
@ -2561,17 +2558,6 @@ int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage);
}
/*
** Take (bUnlock==0) or release (bUnlock==1) a vocab lock on the table
** passed as the only argument. It is not possible to modify the
** structure of the table if there are one or more vocab locks.
*/
void sqlite3Fts5VocabLock(Fts5Table *pVtab, int bUnlock){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
pTab->nVocabLock += (bUnlock ? -1 : +1);
assert( pTab->nVocabLock>=0 );
}
/*
** The xSavepoint() method.
**

View File

@ -60,6 +60,7 @@ struct Fts5VocabCursor {
int bEof; /* True if this cursor is at EOF */
Fts5IndexIter *pIter; /* Term/rowid iterator object */
void *pStruct; /* From sqlite3Fts5StructureRef() */
int nLeTerm; /* Size of zLeTerm in bytes */
char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */
@ -382,7 +383,6 @@ static int fts5VocabOpenMethod(
pCsr->pStmt = pStmt;
pCsr->aCnt = (i64*)&pCsr[1];
pCsr->aDoc = &pCsr->aCnt[pFts5->pConfig->nCol];
sqlite3Fts5VocabLock(pFts5, 0);
}else{
sqlite3_finalize(pStmt);
}
@ -394,6 +394,8 @@ static int fts5VocabOpenMethod(
static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
pCsr->rowid = 0;
sqlite3Fts5IterClose(pCsr->pIter);
sqlite3Fts5StructureRelease(pCsr->pStruct);
pCsr->pStruct = 0;
pCsr->pIter = 0;
sqlite3_free(pCsr->zLeTerm);
pCsr->nLeTerm = -1;
@ -408,7 +410,6 @@ static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
fts5VocabResetCursor(pCsr);
sqlite3Fts5VocabLock(pCsr->pFts5, 1);
sqlite3Fts5BufferFree(&pCsr->term);
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr);
@ -472,9 +473,11 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
int rc = SQLITE_OK;
int nCol = pCsr->pFts5->pConfig->nCol;
int rc;
rc = sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct);
if( rc!=SQLITE_OK ) return rc;
pCsr->rowid++;
if( pTab->eType==FTS5_VOCAB_INSTANCE ){
@ -648,6 +651,9 @@ static int fts5VocabFilterMethod(
if( rc==SQLITE_OK ){
Fts5Index *pIndex = pCsr->pFts5->pIndex;
rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
if( rc==SQLITE_OK ){
pCsr->pStruct = sqlite3Fts5StructureRef(pIndex);
}
}
if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){
rc = fts5VocabInstanceNewTerm(pCsr);

View File

@ -15364,7 +15364,7 @@ do_execsql_test 79.1 {
do_catchsql_test 79.2 {
INSERT INTO t1(t1) SELECT 'merge' FROM t2;
} {1 {database table is locked}}
} {1 {query aborted}}
sqlite3_fts5_may_be_corrupt 0
finish_test

View File

@ -234,4 +234,27 @@ ifcapable fts3 {
} {1 {no such fts5 table: main.nosuchtable}}
}
#-------------------------------------------------------------------------
# Check that the fts5 table cannot be written while there are vocab
# cursors open.
reset_db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a);
CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance);
INSERT INTO t1 VALUES('one'), ('two'), ('three'), ('four');
}
do_test 5.1 {
list [catch {
db eval { SELECT * FROM v1 } {
db eval {INSERT INTO t1 VALUES('five')}
}
} msg] $msg
} {1 {query aborted}}
do_execsql_test 5.2 {
SELECT * FROM t1
} {one two three four five}
finish_test