Extra tests for fts3. And fixes for conflict-handling related problems in fts3.
FossilOrigin-Name: fb4a355871d9482ccb28b6ba03b842b3cc87b696
This commit is contained in:
parent
b061d058cb
commit
a311b80392
@ -3544,8 +3544,19 @@ static int fts3RenameMethod(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
return sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
|
||||
}
|
||||
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static const sqlite3_module fts3Module = {
|
||||
/* iVersion */ 0,
|
||||
/* iVersion */ 1,
|
||||
/* xCreate */ fts3CreateMethod,
|
||||
/* xConnect */ fts3ConnectMethod,
|
||||
/* xBestIndex */ fts3BestIndexMethod,
|
||||
@ -3565,6 +3576,9 @@ static const sqlite3_module fts3Module = {
|
||||
/* xRollback */ fts3RollbackMethod,
|
||||
/* xFindFunction */ fts3FindFunctionMethod,
|
||||
/* xRename */ fts3RenameMethod,
|
||||
/* xSavepoint */ fts3SavepointMethod,
|
||||
/* xRelease */ fts3ReleaseMethod,
|
||||
/* xRollbackTo */ fts3RollbackToMethod,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3611,6 +3625,11 @@ int sqlite3Fts3Init(sqlite3 *db){
|
||||
sqlite3Fts3IcuTokenizerModule(&pIcu);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
rc = sqlite3Fts3InitTerm(db);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
#endif
|
||||
|
||||
rc = sqlite3Fts3InitAux(db);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
|
346
ext/fts3/fts3_term.c
Normal file
346
ext/fts3/fts3_term.c
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
** 2011 Jan 27
|
||||
**
|
||||
** 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 !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include "fts3Int.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct Fts3termTable Fts3termTable;
|
||||
typedef struct Fts3termCursor Fts3termCursor;
|
||||
|
||||
struct Fts3termTable {
|
||||
sqlite3_vtab base; /* Base class used by SQLite core */
|
||||
Fts3Table *pFts3Tab;
|
||||
};
|
||||
|
||||
struct Fts3termCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
|
||||
Fts3SegReaderCursor csr; /* Must be right after "base" */
|
||||
Fts3SegFilter filter;
|
||||
|
||||
int isEof; /* True if cursor is at EOF */
|
||||
char *pNext;
|
||||
|
||||
sqlite3_int64 iRowid; /* Current 'rowid' value */
|
||||
sqlite3_int64 iDocid; /* Current 'docid' value */
|
||||
int iCol; /* Current 'col' value */
|
||||
int iPos; /* Current 'pos' value */
|
||||
};
|
||||
|
||||
/*
|
||||
** Schema of the terms table.
|
||||
*/
|
||||
#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, docid, col, pos)"
|
||||
|
||||
/*
|
||||
** This function does all the work for both the xConnect and xCreate methods.
|
||||
** These tables have no persistent representation of their own, so xConnect
|
||||
** and xCreate are identical operations.
|
||||
*/
|
||||
static int fts3termConnectMethod(
|
||||
sqlite3 *db, /* Database connection */
|
||||
void *pUnused, /* Unused */
|
||||
int argc, /* Number of elements in argv array */
|
||||
const char * const *argv, /* xCreate/xConnect argument array */
|
||||
sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
|
||||
char **pzErr /* OUT: sqlite3_malloc'd error message */
|
||||
){
|
||||
char const *zDb; /* Name of database (e.g. "main") */
|
||||
char const *zFts3; /* Name of fts3 table */
|
||||
int nDb; /* Result of strlen(zDb) */
|
||||
int nFts3; /* Result of strlen(zFts3) */
|
||||
int nByte; /* Bytes of space to allocate here */
|
||||
int rc; /* value returned by declare_vtab() */
|
||||
Fts3termTable *p; /* Virtual table object to return */
|
||||
|
||||
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 fts4term constructor"
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
zDb = argv[1];
|
||||
nDb = strlen(zDb);
|
||||
zFts3 = argv[3];
|
||||
nFts3 = strlen(zFts3);
|
||||
|
||||
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
nByte = sizeof(Fts3termTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
|
||||
p = (Fts3termTable *)sqlite3_malloc(nByte);
|
||||
if( !p ) return SQLITE_NOMEM;
|
||||
memset(p, 0, nByte);
|
||||
|
||||
p->pFts3Tab = (Fts3Table *)&p[1];
|
||||
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
|
||||
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
|
||||
p->pFts3Tab->db = db;
|
||||
|
||||
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
|
||||
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
|
||||
sqlite3Fts3Dequote((char *)p->pFts3Tab->zName);
|
||||
|
||||
*ppVtab = (sqlite3_vtab *)p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work for both the xDisconnect and xDestroy methods.
|
||||
** These tables have no persistent representation of their own, so xDisconnect
|
||||
** and xDestroy are identical operations.
|
||||
*/
|
||||
static int fts3termDisconnectMethod(sqlite3_vtab *pVtab){
|
||||
Fts3termTable *p = (Fts3termTable *)pVtab;
|
||||
Fts3Table *pFts3 = p->pFts3Tab;
|
||||
int i;
|
||||
|
||||
/* Free any prepared statements held */
|
||||
for(i=0; i<SizeofArray(pFts3->aStmt); i++){
|
||||
sqlite3_finalize(pFts3->aStmt[i]);
|
||||
}
|
||||
sqlite3_free(pFts3->zSegmentsTbl);
|
||||
sqlite3_free(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#define FTS4AUX_EQ_CONSTRAINT 1
|
||||
#define FTS4AUX_GE_CONSTRAINT 2
|
||||
#define FTS4AUX_LE_CONSTRAINT 4
|
||||
|
||||
/*
|
||||
** xBestIndex - Analyze a WHERE and ORDER BY clause.
|
||||
*/
|
||||
static int fts3termBestIndexMethod(
|
||||
sqlite3_vtab *pVTab,
|
||||
sqlite3_index_info *pInfo
|
||||
){
|
||||
UNUSED_PARAMETER(pVTab);
|
||||
UNUSED_PARAMETER(pInfo);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xOpen - Open a cursor.
|
||||
*/
|
||||
static int fts3termOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
|
||||
Fts3termCursor *pCsr; /* Pointer to cursor object to return */
|
||||
|
||||
UNUSED_PARAMETER(pVTab);
|
||||
|
||||
pCsr = (Fts3termCursor *)sqlite3_malloc(sizeof(Fts3termCursor));
|
||||
if( !pCsr ) return SQLITE_NOMEM;
|
||||
memset(pCsr, 0, sizeof(Fts3termCursor));
|
||||
|
||||
*ppCsr = (sqlite3_vtab_cursor *)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xClose - Close a cursor.
|
||||
*/
|
||||
static int fts3termCloseMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
|
||||
sqlite3Fts3SegmentsClose(pFts3);
|
||||
sqlite3Fts3SegReaderFinish(&pCsr->csr);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xNext - Advance the cursor to the next row, if any.
|
||||
*/
|
||||
static int fts3termNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
|
||||
int rc;
|
||||
sqlite3_int64 v;
|
||||
|
||||
/* Increment our pretend rowid value. */
|
||||
pCsr->iRowid++;
|
||||
|
||||
/* Advance to the next term in the full-text index. */
|
||||
if( pCsr->csr.aDoclist==0
|
||||
|| pCsr->pNext>=&pCsr->csr.aDoclist[pCsr->csr.nDoclist-1]
|
||||
){
|
||||
rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
pCsr->isEof = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
pCsr->iCol = 0;
|
||||
pCsr->iPos = 0;
|
||||
pCsr->iDocid = 0;
|
||||
pCsr->pNext = pCsr->csr.aDoclist;
|
||||
|
||||
/* Read docid */
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &pCsr->iDocid);
|
||||
}
|
||||
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
if( v==0 ){
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
pCsr->iDocid += v;
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
pCsr->iCol = 0;
|
||||
pCsr->iPos = 0;
|
||||
}
|
||||
|
||||
if( v==1 ){
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
pCsr->iCol += v;
|
||||
pCsr->iPos = 0;
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
}
|
||||
|
||||
pCsr->iPos += (v - 2);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xFilter - Initialize a cursor to point at the start of its data.
|
||||
*/
|
||||
static int fts3termFilterMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
|
||||
int idxNum, /* Strategy index */
|
||||
const char *idxStr, /* Unused */
|
||||
int nVal, /* Number of elements in apVal */
|
||||
sqlite3_value **apVal /* Arguments for the indexing scheme */
|
||||
){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
|
||||
int rc;
|
||||
|
||||
UNUSED_PARAMETER(nVal);
|
||||
UNUSED_PARAMETER(idxNum);
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
UNUSED_PARAMETER(apVal);
|
||||
|
||||
assert( idxStr==0 && idxNum==0 );
|
||||
|
||||
/* In case this cursor is being reused, close and zero it. */
|
||||
testcase(pCsr->filter.zTerm);
|
||||
sqlite3Fts3SegReaderFinish(&pCsr->csr);
|
||||
memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr);
|
||||
|
||||
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
|
||||
pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL,
|
||||
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3termNextMethod(pCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** xEof - Return true if the cursor is at EOF, or false otherwise.
|
||||
*/
|
||||
static int fts3termEofMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
return pCsr->isEof;
|
||||
}
|
||||
|
||||
/*
|
||||
** xColumn - Return a column value.
|
||||
*/
|
||||
static int fts3termColumnMethod(
|
||||
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 */
|
||||
){
|
||||
Fts3termCursor *p = (Fts3termCursor *)pCursor;
|
||||
|
||||
assert( iCol>=0 && iCol<=3 );
|
||||
switch( iCol ){
|
||||
case 0:
|
||||
sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
|
||||
break;
|
||||
case 1:
|
||||
sqlite3_result_int64(pCtx, p->iDocid);
|
||||
break;
|
||||
case 2:
|
||||
sqlite3_result_int64(pCtx, p->iCol);
|
||||
break;
|
||||
default:
|
||||
sqlite3_result_int64(pCtx, p->iPos);
|
||||
break;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xRowid - Return the current rowid for the cursor.
|
||||
*/
|
||||
static int fts3termRowidMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite_int64 *pRowid /* OUT: Rowid value */
|
||||
){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
*pRowid = pCsr->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the fts3term module with database connection db. Return SQLITE_OK
|
||||
** if successful or an error code if sqlite3_create_module() fails.
|
||||
*/
|
||||
int sqlite3Fts3InitTerm(sqlite3 *db){
|
||||
static const sqlite3_module fts3term_module = {
|
||||
0, /* iVersion */
|
||||
fts3termConnectMethod, /* xCreate */
|
||||
fts3termConnectMethod, /* xConnect */
|
||||
fts3termBestIndexMethod, /* xBestIndex */
|
||||
fts3termDisconnectMethod, /* xDisconnect */
|
||||
fts3termDisconnectMethod, /* xDestroy */
|
||||
fts3termOpenMethod, /* xOpen */
|
||||
fts3termCloseMethod, /* xClose */
|
||||
fts3termFilterMethod, /* xFilter */
|
||||
fts3termNextMethod, /* xNext */
|
||||
fts3termEofMethod, /* xEof */
|
||||
fts3termColumnMethod, /* xColumn */
|
||||
fts3termRowidMethod, /* xRowid */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindFunction */
|
||||
0 /* xRename */
|
||||
};
|
||||
int rc; /* Return code */
|
||||
|
||||
rc = sqlite3_create_module(db, "fts4term", &fts3term_module, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
|
@ -2711,15 +2711,31 @@ int sqlite3Fts3UpdateMethod(
|
||||
** modify the database file.
|
||||
*/
|
||||
if( nArg>1 ){
|
||||
sqlite3_int64 iNewRowid;
|
||||
/* Find the value object that holds the new rowid value. */
|
||||
sqlite3_value *pNewRowid = apVal[3+p->nColumn];
|
||||
if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
|
||||
pNewRowid = apVal[1];
|
||||
}
|
||||
|
||||
if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && (
|
||||
sqlite3_value_type(apVal[0])==SQLITE_NULL
|
||||
|| sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid)
|
||||
)){
|
||||
/* The new rowid is not NULL (in this case the rowid will be
|
||||
** automatically assigned and there is no chance of a conflict), and
|
||||
** the statement is either an INSERT or an UPDATE that modifies the
|
||||
** rowid column. So if the conflict mode is REPLACE, then delete any
|
||||
** existing row with rowid=pNewRowid.
|
||||
**
|
||||
** Or, if the conflict mode is not REPLACE, insert the new record into
|
||||
** the %_content table. If we hit the duplicate rowid constraint (or any
|
||||
** other error) while doing so, return immediately.
|
||||
**
|
||||
** This branch may also run if pNewRowid contains a value that cannot
|
||||
** be losslessly converted to an integer. In this case, the eventual
|
||||
** call to fts3InsertData() (either just below or further on in this
|
||||
** function) will return SQLITE_MISMATCH.
|
||||
*/
|
||||
if( sqlite3_vtab_on_conflict(p->db)==SQLITE_REPLACE ){
|
||||
rc = fts3DeleteByRowid(p, pNewRowid, &nChng, aSzDel);
|
||||
}else{
|
||||
|
1
main.mk
1
main.mk
@ -299,6 +299,7 @@ TESTSRC2 = \
|
||||
$(TOP)/ext/fts3/fts3.c \
|
||||
$(TOP)/ext/fts3/fts3_aux.c \
|
||||
$(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_term.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/fts3/fts3_write.c \
|
||||
$(TOP)/ext/async/sqlite3async.c
|
||||
|
32
manifest
32
manifest
@ -1,5 +1,5 @@
|
||||
C Add\ssupport\sfor\son\sconflict\sclauses\sto\sfts3/fts4.
|
||||
D 2011-04-25T18:49:57.773
|
||||
C Extra\stests\sfor\sfts3.\sAnd\sfixes\sfor\sconflict-handling\srelated\sproblems\sin\sfts3.
|
||||
D 2011-04-26T19:21:34.191
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -61,7 +61,7 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
|
||||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 94fa15fc9d6290e2ba042c24fc83e272c86a40c6
|
||||
F ext/fts3/fts3.c ce37973c86f15711a020fa629d8f95cfd642ebc3
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e
|
||||
F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf
|
||||
@ -71,10 +71,11 @@ F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
|
||||
F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295
|
||||
F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2
|
||||
F ext/fts3/fts3_snippet.c e857c6a89d81d3b89df59f3b44b35c68d8ed5c62
|
||||
F ext/fts3/fts3_term.c c1dbc904ab1c2d687b97643c671795456228ab22
|
||||
F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
|
||||
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
|
||||
F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
|
||||
F ext/fts3/fts3_write.c 388a7c7119f322d8fd4a5c19c9bd5793da47ccce
|
||||
F ext/fts3/fts3_write.c b4dfd76d61adb183b87c56573a1bdd0e1d1501da
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
@ -101,7 +102,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
|
||||
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F main.mk bd4e376deea4704b2bd9c77a4e6f0fa3de25c495
|
||||
F main.mk 496cec8b7890e39127532294e28e5e1d1b1beae1
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@ -178,9 +179,9 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff
|
||||
F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79
|
||||
F src/sqlite.h.in 92f2daa48c1926d79db79229fb583cdb22d2d4c5
|
||||
F src/sqlite.h.in 3dc514ef85adfdb6377abee4fb780b420fc43f5e
|
||||
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
|
||||
F src/sqliteInt.h 5facb244a286e5c9ecd2f59758019f24a9245c8e
|
||||
F src/sqliteInt.h 1ec9fa7b728c486e526ec012f73fdfb244238dfc
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@ -235,15 +236,15 @@ F src/update.c 5bcb56e5c7380a2eecb0e71891dbd4ad7437748f
|
||||
F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60
|
||||
F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a
|
||||
F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
|
||||
F src/vdbe.c ac7aab1148964422b0a91ae5d50d31724fbd82ec
|
||||
F src/vdbe.c b6396cb75bead0d163577b834cbcf4dcd0cea231
|
||||
F src/vdbe.h 8a675fefdf7119441fe817c800a9a52440c2e797
|
||||
F src/vdbeInt.h fe8f58d305e629fff02f61f655aca1d299f1f6ae
|
||||
F src/vdbeapi.c e0e2672e0a96ae3f8575c8ecd02912a3e8a554a1
|
||||
F src/vdbeaux.c 9ae5074b19bdff2d8806a278533956fb281510d5
|
||||
F src/vdbeaux.c 5c4cd4be10b8247061f97b77fa2b0a23728d43ed
|
||||
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
|
||||
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
|
||||
F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
|
||||
F src/vtab.c 0e89db3e7416ccdab5138883d69ed8006a7e992c
|
||||
F src/vtab.c bcfd5a8b0a4951a60658cdd887a929f6c3816fdf
|
||||
F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794
|
||||
F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
@ -455,7 +456,7 @@ F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0
|
||||
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
|
||||
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
|
||||
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
|
||||
F test/fts3conf.test 2dc3bce3fe20d1e9b0ecd27d4040d07a2b79d16b
|
||||
F test/fts3conf.test 03e5baecc3a1c82fc50fc75789bc1e13861f47fe
|
||||
F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf
|
||||
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
|
||||
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
|
||||
@ -931,10 +932,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P ddeea5ab5f6c0c4a86cdfbbb9f24d9d54bf8d301
|
||||
R ca5f1cf4bf0f7df53478a4e115a67980
|
||||
T *branch * vtab-conflict
|
||||
T *sym-vtab-conflict *
|
||||
T -sym-trunk *
|
||||
P 6d2633a6d0a9bb88fb1a6adac0827dc51df2d4d2
|
||||
R 3d729f606d16bb9d30fc86b95bfd6047
|
||||
U dan
|
||||
Z dd80c4f8ae24b83ff1c8f809d0d54000
|
||||
Z dd1f59cf11e82349f934a1bcc8d1254a
|
||||
|
@ -1 +1 @@
|
||||
6d2633a6d0a9bb88fb1a6adac0827dc51df2d4d2
|
||||
fb4a355871d9482ccb28b6ba03b842b3cc87b696
|
@ -4607,6 +4607,11 @@ struct sqlite3_module {
|
||||
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void **ppArg);
|
||||
int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
|
||||
/* The methods above are in version 0 of the sqlite_module object. Those
|
||||
** below are for version 1 and greater. */
|
||||
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
|
||||
int (*xRelease)(sqlite3_vtab *pVTab, int);
|
||||
int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -3049,6 +3049,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
# define sqlite3VtabLock(X)
|
||||
# define sqlite3VtabUnlock(X)
|
||||
# define sqlite3VtabUnlockList(X)
|
||||
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
|
||||
#else
|
||||
void sqlite3VtabClear(sqlite3 *db, Table*);
|
||||
int sqlite3VtabSync(sqlite3 *db, char **);
|
||||
@ -3057,6 +3058,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
void sqlite3VtabLock(VTable *);
|
||||
void sqlite3VtabUnlock(VTable *);
|
||||
void sqlite3VtabUnlockList(sqlite3*);
|
||||
int sqlite3VtabSavepoint(sqlite3 *, int, int);
|
||||
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
|
||||
#endif
|
||||
void sqlite3VtabMakeWritable(Parse*,Table*);
|
||||
|
@ -2821,7 +2821,11 @@ case OP_Transaction: {
|
||||
db->nStatement++;
|
||||
p->iStatement = db->nSavepoint + db->nStatement;
|
||||
}
|
||||
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
|
||||
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
|
||||
}
|
||||
|
||||
/* Store the current value of the database handles deferred constraint
|
||||
** counter. If the statement transaction needs to be rolled back,
|
||||
|
@ -2013,6 +2013,15 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
|
||||
db->nStatement--;
|
||||
p->iStatement = 0;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the statement transaction is being rolled back, also restore the
|
||||
** database handles deferred constraint counter to the value it had when
|
||||
** the statement transaction was opened. */
|
||||
|
27
src/vtab.c
27
src/vtab.c
@ -836,7 +836,6 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
|
||||
if( pModule->xBegin ){
|
||||
int i;
|
||||
|
||||
|
||||
/* If pVtab is already in the aVTrans array, return early */
|
||||
for(i=0; i<db->nVTrans; i++){
|
||||
if( db->aVTrans[i]==pVTab ){
|
||||
@ -853,6 +852,32 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
|
||||
int i;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
|
||||
sqlite3_vtab *pVtab = db->aVTrans[i]->pVtab;
|
||||
sqlite3_module *pMod = db->aVTrans[i]->pMod->pModule;
|
||||
if( pMod->iVersion>=1 ){
|
||||
switch( op ){
|
||||
case SAVEPOINT_BEGIN:
|
||||
rc = pMod->xSavepoint(pVtab, iSavepoint);
|
||||
break;
|
||||
case SAVEPOINT_ROLLBACK:
|
||||
rc = pMod->xRollbackTo(pVtab, iSavepoint);
|
||||
break;
|
||||
default:
|
||||
rc = pMod->xRelease(pVtab, iSavepoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The first parameter (pDef) is a function implementation. The
|
||||
** second parameter (pExpr) is the first argument to this function.
|
||||
|
@ -22,7 +22,43 @@ ifcapable !fts3 {
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
|
||||
proc fts3_integrity {tn db tbl} {
|
||||
|
||||
if {[sqlite3_get_autocommit $db]==0} {
|
||||
error "fts3_integrity does not work with an open transaction"
|
||||
}
|
||||
|
||||
set sql [db one {SELECT sql FROM sqlite_master WHERE name = $tbl}]
|
||||
regexp -nocase {[^(]* using (.*)} $sql -> tail
|
||||
set cols [list]
|
||||
$db eval "PRAGMA table_info($tbl)" {
|
||||
lappend cols $name
|
||||
}
|
||||
set cols [join [concat docid $cols] ,]
|
||||
|
||||
$db eval [subst {
|
||||
CREATE VIRTUAL TABLE fts3check USING fts4term($tbl);
|
||||
CREATE VIRTUAL TABLE temp.fts3check2 USING $tail;
|
||||
INSERT INTO temp.fts3check2($cols) SELECT docid, * FROM $tbl;
|
||||
CREATE VIRTUAL TABLE temp.fts3check3 USING fts4term(fts3check2);
|
||||
}]
|
||||
|
||||
set m1 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check}]
|
||||
set m2 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check3}]
|
||||
|
||||
$db eval {
|
||||
DROP TABLE fts3check;
|
||||
DROP TABLE temp.fts3check2;
|
||||
DROP TABLE temp.fts3check3;
|
||||
}
|
||||
|
||||
uplevel [list do_test $tn [list set {} $m1] $m2]
|
||||
}
|
||||
|
||||
|
||||
|
||||
do_execsql_test 1.0.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3(x);
|
||||
INSERT INTO t1(rowid, x) VALUES(1, 'a b c d');
|
||||
INSERT INTO t1(rowid, x) VALUES(2, 'e f g h');
|
||||
@ -36,6 +72,9 @@ db_save_and_close
|
||||
set T1 "INTO t1(rowid, x) VALUES(1, 'x')"
|
||||
set T2 "INTO t1(rowid, x) SELECT * FROM source"
|
||||
|
||||
set T3 "t1 SET docid = 2 WHERE docid = 1"
|
||||
set T4 "t1 SET docid = CASE WHEN docid = 1 THEN 4 ELSE 3 END WHERE docid <=2"
|
||||
|
||||
foreach {tn sql constraint data} [subst {
|
||||
1 "INSERT OR ROLLBACK $T1" 1 {{a b c d} {e f g h}}
|
||||
2 "INSERT OR ABORT $T1" 1 {{a b c d} {e f g h} {i j k l}}
|
||||
@ -48,6 +87,18 @@ foreach {tn sql constraint data} [subst {
|
||||
8 "INSERT OR FAIL $T2" 1 {{a b c d} {e f g h} {i j k l} z}
|
||||
9 "INSERT OR IGNORE $T2" 0 {{a b c d} {e f g h} {i j k l} z}
|
||||
10 "INSERT OR REPLACE $T2" 0 {{a b c d} y {i j k l} z}
|
||||
|
||||
11 "UPDATE OR ROLLBACK $T3" 1 {{a b c d} {e f g h}}
|
||||
12 "UPDATE OR ABORT $T3" 1 {{a b c d} {e f g h} {i j k l}}
|
||||
13 "UPDATE OR FAIL $T3" 1 {{a b c d} {e f g h} {i j k l}}
|
||||
14 "UPDATE OR IGNORE $T3" 0 {{a b c d} {e f g h} {i j k l}}
|
||||
15 "UPDATE OR REPLACE $T3" 0 {{a b c d} {i j k l}}
|
||||
|
||||
16 "UPDATE OR ROLLBACK $T4" 1 {{a b c d} {e f g h}}
|
||||
17 "UPDATE OR ABORT $T4" 1 {{a b c d} {e f g h} {i j k l}}
|
||||
18 "UPDATE OR FAIL $T4" 1 {{e f g h} {i j k l} {a b c d}}
|
||||
19 "UPDATE OR IGNORE $T4" 0 {{e f g h} {i j k l} {a b c d}}
|
||||
20 "UPDATE OR REPLACE $T4" 0 {{e f g h} {a b c d}}
|
||||
}] {
|
||||
db_restore_and_reopen
|
||||
execsql {
|
||||
@ -58,6 +109,10 @@ foreach {tn sql constraint data} [subst {
|
||||
set R(1) {1 {constraint failed}}
|
||||
do_catchsql_test 1.$tn.1 $sql $R($constraint)
|
||||
do_catchsql_test 1.$tn.2 { SELECT * FROM t1 } [list 0 $data]
|
||||
catchsql COMMIT
|
||||
|
||||
fts3_integrity 1.$tn.3 db t1
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user