1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-23 11:22:09 +03:00

Add the contentless_delete=1 option to fts5. For creating contentless tables that support DELETE and REPLACE statements.

FossilOrigin-Name: d66b182d2bc6ce0772e69401b7affe1adbc1b128c4631cb3c17f98dde72af00a
This commit is contained in:
dan
2023-07-27 19:13:35 +00:00
14 changed files with 2451 additions and 258 deletions

View File

@ -154,6 +154,10 @@ typedef struct Fts5Config Fts5Config;
** attempt to merge together. A value of 1 sets the object to use the
** compile time default. Zero disables auto-merge altogether.
**
** bContentlessDelete:
** True if the contentless_delete option was present in the CREATE
** VIRTUAL TABLE statement.
**
** zContent:
**
** zContentRowid:
@ -188,6 +192,7 @@ struct Fts5Config {
int nPrefix; /* Number of prefix indexes */
int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
int eContent; /* An FTS5_CONTENT value */
int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */
char *zContent; /* content table */
char *zContentRowid; /* "content_rowid=" option value */
int bColumnsize; /* "columnsize=" option value (dflt==1) */
@ -209,6 +214,7 @@ struct Fts5Config {
char *zRank; /* Name of rank function */
char *zRankArgs; /* Arguments to rank function */
int bSecureDelete; /* 'secure-delete' */
int nDeleteMerge; /* 'deletemerge' */
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
char **pzErrmsg;
@ -531,6 +537,9 @@ int sqlite3Fts5IndexReset(Fts5Index *p);
int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
/*
** End of interface to code in fts5_index.c.
**************************************************************************/
@ -615,6 +624,11 @@ int sqlite3Fts5HashWrite(
*/
void sqlite3Fts5HashClear(Fts5Hash*);
/*
** Return true if the hash is empty, false otherwise.
*/
int sqlite3Fts5HashIsEmpty(Fts5Hash*);
int sqlite3Fts5HashQuery(
Fts5Hash*, /* Hash table to query */
int nPre,
@ -636,6 +650,7 @@ void sqlite3Fts5HashScanEntry(Fts5Hash *,
);
/*
** End of interface to code in fts5_hash.c.
**************************************************************************/

View File

@ -22,6 +22,8 @@
#define FTS5_DEFAULT_CRISISMERGE 16
#define FTS5_DEFAULT_HASHSIZE (1024*1024)
#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */
/* Maximum allowed page size */
#define FTS5_MAX_PAGE_SIZE (64*1024)
@ -352,6 +354,16 @@ static int fts5ConfigParseSpecial(
return rc;
}
if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
*pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
rc = SQLITE_ERROR;
}else{
pConfig->bContentlessDelete = (zArg[0]=='1');
}
return rc;
}
if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
if( pConfig->zContentRowid ){
*pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
@ -596,6 +608,28 @@ int sqlite3Fts5ConfigParse(
sqlite3_free(zTwo);
}
/* We only allow contentless_delete=1 if the table is indeed contentless. */
if( rc==SQLITE_OK
&& pRet->bContentlessDelete
&& pRet->eContent!=FTS5_CONTENT_NONE
){
*pzErr = sqlite3_mprintf(
"contentless_delete=1 requires a contentless table"
);
rc = SQLITE_ERROR;
}
/* We only allow contentless_delete=1 if columnsize=0 is not present.
**
** This restriction may be removed at some point.
*/
if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
*pzErr = sqlite3_mprintf(
"contentless_delete=1 is incompatible with columnsize=0"
);
rc = SQLITE_ERROR;
}
/* If a tokenizer= option was successfully parsed, the tokenizer has
** already been allocated. Otherwise, allocate an instance of the default
** tokenizer (unicode61) now. */
@ -890,6 +924,18 @@ int sqlite3Fts5ConfigSetValue(
}
}
else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){
int nVal = -1;
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
nVal = sqlite3_value_int(pVal);
}else{
*pbBadkey = 1;
}
if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE;
if( nVal>100 ) nVal = 0;
pConfig->nDeleteMerge = nVal;
}
else if( 0==sqlite3_stricmp(zKey, "rank") ){
const char *zIn = (const char*)sqlite3_value_text(pVal);
char *zRank;
@ -938,6 +984,7 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE;
zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
if( zSql ){

View File

@ -2477,7 +2477,7 @@ Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
return pRet;
}
#ifdef SQLITE_TEST
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
sqlite3_int64 nByte = 0;
Fts5ExprTerm *p;
@ -2844,14 +2844,14 @@ static void fts5ExprFold(
sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics));
}
}
#endif /* ifdef SQLITE_TEST */
#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */
/*
** This is called during initialization to register the fts5_expr() scalar
** UDF with the SQLite handle passed as the only argument.
*/
int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
#ifdef SQLITE_TEST
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
struct Fts5ExprFunc {
const char *z;
void (*x)(sqlite3_context*,int,sqlite3_value**);

View File

@ -475,7 +475,6 @@ static int fts5HashEntrySort(
pList = fts5HashEntryMerge(pList, ap[i]);
}
pHash->nEntry = 0;
sqlite3_free(ap);
*ppSorted = pList;
return SQLITE_OK;
@ -529,6 +528,28 @@ int sqlite3Fts5HashScanInit(
return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan);
}
#ifdef SQLITE_DEBUG
static int fts5HashCount(Fts5Hash *pHash){
int nEntry = 0;
int ii;
for(ii=0; ii<pHash->nSlot; ii++){
Fts5HashEntry *p = 0;
for(p=pHash->aSlot[ii]; p; p=p->pHashNext){
nEntry++;
}
}
return nEntry;
}
#endif
/*
** Return true if the hash table is empty, false otherwise.
*/
int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){
assert( pHash->nEntry==fts5HashCount(pHash) );
return pHash->nEntry==0;
}
void sqlite3Fts5HashScanNext(Fts5Hash *p){
assert( !sqlite3Fts5HashScanEof(p) );
p->pScan = p->pScan->pScanNext;

File diff suppressed because it is too large Load Diff

View File

@ -1624,7 +1624,6 @@ static int fts5UpdateMethod(
int eType0; /* value_type() of apVal[0] */
int rc = SQLITE_OK; /* Return code */
int bUpdateOrDelete = 0;
/* A transaction must be open when this is called. */
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
@ -1654,7 +1653,14 @@ static int fts5UpdateMethod(
if( pConfig->eContent!=FTS5_CONTENT_NORMAL
&& 0==sqlite3_stricmp("delete", z)
){
rc = fts5SpecialDelete(pTab, apVal);
if( pConfig->bContentlessDelete ){
fts5SetVtabError(pTab,
"'delete' may not be used with a contentless_delete=1 table"
);
rc = SQLITE_ERROR;
}else{
rc = fts5SpecialDelete(pTab, apVal);
}
}else{
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
}
@ -1671,7 +1677,7 @@ static int fts5UpdateMethod(
** Cases 3 and 4 may violate the rowid constraint.
*/
int eConflict = SQLITE_ABORT;
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
}
@ -1679,8 +1685,12 @@ static int fts5UpdateMethod(
assert( nArg!=1 || eType0==SQLITE_INTEGER );
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
** This is not suported. */
if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
** This is not suported. Except - DELETE is supported if the CREATE
** VIRTUAL TABLE statement contained "contentless_delete=1". */
if( eType0==SQLITE_INTEGER
&& pConfig->eContent==FTS5_CONTENT_NONE
&& (nArg>1 || pConfig->bContentlessDelete==0)
){
pTab->p.base.zErrMsg = sqlite3_mprintf(
"cannot %s contentless fts5 table: %s",
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
@ -1767,8 +1777,7 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
fts5TripCursors(pTab);
rc = sqlite3Fts5StorageSync(pTab->pStorage);
rc = sqlite3Fts5FlushToDisk(&pTab->p);
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}

View File

@ -77,10 +77,10 @@ static int fts5StorageGetStmt(
"INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
"REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
"DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */
"DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
"SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
"SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
"REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
"SELECT %s FROM %s AS T", /* SCAN */
@ -128,6 +128,19 @@ static int fts5StorageGetStmt(
break;
}
case FTS5_STMT_REPLACE_DOCSIZE:
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName,
(pC->bContentlessDelete ? ",?" : "")
);
break;
case FTS5_STMT_LOOKUP_DOCSIZE:
zSql = sqlite3_mprintf(azStmt[eStmt],
(pC->bContentlessDelete ? ",origin" : ""),
pC->zDb, pC->zName
);
break;
default:
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
break;
@ -317,9 +330,11 @@ int sqlite3Fts5StorageOpen(
}
if( rc==SQLITE_OK && pConfig->bColumnsize ){
rc = sqlite3Fts5CreateTable(
pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
);
const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB";
if( pConfig->bContentlessDelete ){
zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER";
}
rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5CreateTable(
@ -396,7 +411,7 @@ static int fts5StorageDeleteFromIndex(
){
Fts5Config *pConfig = p->pConfig;
sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
int rc; /* Return code */
int rc = SQLITE_OK; /* Return code */
int rc2; /* sqlite3_reset() return code */
int iCol;
Fts5InsertCtx ctx;
@ -412,7 +427,6 @@ static int fts5StorageDeleteFromIndex(
ctx.pStorage = p;
ctx.iCol = -1;
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
if( pConfig->abUnindexed[iCol-1]==0 ){
const char *zText;
@ -449,6 +463,37 @@ static int fts5StorageDeleteFromIndex(
return rc;
}
/*
** This function is called to process a DELETE on a contentless_delete=1
** table. It adds the tombstone required to delete the entry with rowid
** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs,
** an SQLite error code.
*/
static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){
i64 iOrigin = 0;
sqlite3_stmt *pLookup = 0;
int rc = SQLITE_OK;
assert( p->pConfig->bContentlessDelete );
assert( p->pConfig->eContent==FTS5_CONTENT_NONE );
/* Look up the origin of the document in the %_docsize table. Store
** this in stack variable iOrigin. */
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pLookup, 1, iDel);
if( SQLITE_ROW==sqlite3_step(pLookup) ){
iOrigin = sqlite3_column_int64(pLookup, 1);
}
rc = sqlite3_reset(pLookup);
}
if( rc==SQLITE_OK && iOrigin!=0 ){
rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel);
}
return rc;
}
/*
** Insert a record into the %_docsize table. Specifically, do:
@ -469,10 +514,17 @@ static int fts5StorageInsertDocsize(
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);
sqlite3_step(pReplace);
rc = sqlite3_reset(pReplace);
sqlite3_bind_null(pReplace, 2);
if( p->pConfig->bContentlessDelete ){
i64 iOrigin = 0;
rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin);
sqlite3_bind_int64(pReplace, 3, iOrigin);
}
if( rc==SQLITE_OK ){
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
sqlite3_step(pReplace);
rc = sqlite3_reset(pReplace);
sqlite3_bind_null(pReplace, 2);
}
}
}
return rc;
@ -536,7 +588,15 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
/* Delete the index records */
if( rc==SQLITE_OK ){
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
}
if( rc==SQLITE_OK ){
if( p->pConfig->bContentlessDelete ){
rc = fts5StorageContentlessDelete(p, iDel);
}else{
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
}
}
/* Delete the %_docsize record */

View File

@ -0,0 +1,271 @@
# 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.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
# Check that it is not possible to specify "contentless_delete=1" for
# anything other than a contentless table.
#
set res(0) {0 {}}
set res(1) {1 {contentless_delete=1 requires a contentless table}}
foreach {tn sql bError} {
1 "(a, b, contentless_delete=1)" 1
2 "(a, b, contentless_delete=1, content=abc)" 1
3 "(a, b, contentless_delete=1, content=)" 0
4 "(content=, contentless_delete=1, a)" 0
5 "(content='', contentless_delete=1, hello)" 0
} {
execsql { BEGIN }
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
execsql { ROLLBACK }
}
# Check that it is not possible to specify "contentless_delete=1"
# along with columnsize=1.
#
set res(0) {0 {}}
set res(1) {1 {contentless_delete=1 is incompatible with columnsize=0}}
foreach {tn sql bError} {
2 "(a, b, content='', contentless_delete=1, columnsize=0)" 1
} {
execsql { BEGIN }
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
execsql { ROLLBACK }
}
# Check that if contentless_delete=1 is specified, then the "origin"
# column is added to the %_docsize table.
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE x1 USING fts5(c, content='');
CREATE VIRTUAL TABLE x2 USING fts5(c, content='', contentless_delete=1);
}
do_execsql_test 3.1 {
SELECT sql FROM sqlite_schema WHERE name IN ('x1_docsize', 'x2_docsize');
} {
{CREATE TABLE 'x1_docsize'(id INTEGER PRIMARY KEY, sz BLOB)}
{CREATE TABLE 'x2_docsize'(id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER)}
}
do_execsql_test 3.2.1 {
SELECT hex(block) FROM x1_data WHERE id=10
} {00000000000000}
do_execsql_test 3.2.2 {
SELECT hex(block) FROM x2_data WHERE id=10
} {00000000FF000001000000}
do_execsql_test 3.3 {
INSERT INTO x2 VALUES('first text');
INSERT INTO x2 VALUES('second text');
}
do_execsql_test 3.4 {
SELECT id, origin FROM x2_docsize
} {1 1 2 2}
do_execsql_test 3.5 {
SELECT level, segment, loc1, loc2 FROM fts5_structure(
(SELECT block FROM x2_data WHERE id=10)
)
} {
0 0 1 1
0 1 2 2
}
do_execsql_test 3.6 {
INSERT INTO x2(x2) VALUES('optimize');
}
do_execsql_test 3.7 {
SELECT level, segment, loc1, loc2 FROM fts5_structure(
(SELECT block FROM x2_data WHERE id=10)
)
} {
1 0 1 2
}
do_execsql_test 3.8 {
DELETE FROM x2 WHERE rowid=2;
}
do_execsql_test 3.9 {
SELECT rowid FROM x2('text')
} {1}
#--------------------------------------------------------------------------
reset_db
proc document {n} {
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
set ret [list]
for {set ii 0} {$ii < $n} {incr ii} {
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
}
set ret
}
set nRow 1000
do_execsql_test 4.0 {
CREATE TABLE t1(x);
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(ft, rank) VALUES('pgsz', 100);
}
do_test 4.1 {
for {set ii 0} {$ii < $nRow} {incr ii} {
set doc [document 6]
execsql {
INSERT INTO t1 VALUES($doc);
INSERT INTO ft VALUES($doc);
}
}
} {}
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
set L2 [execsql {SELECT rowid FROM ft($v)}]
do_test 4.2.$v { set L1 } $L2
}
do_test 4.3 {
for {set ii 1} {$ii < $nRow} {incr ii 2} {
execsql {
DELETE FROM ft WHERE rowid=$ii;
DELETE FROM t1 WHERE rowid=$ii;
}
}
} {}
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
set L2 [execsql {SELECT rowid FROM ft($v)}]
do_test 4.4.$v { set L1 } $L2
}
do_execsql_test 4.5 {
INSERT INTO ft(ft) VALUES('optimize');
} {}
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
set L2 [execsql {SELECT rowid FROM ft($v)}]
do_test 4.6.$v { set L1 } $L2
}
#execsql_pp { SELECT fts5_decode(id, block) FROM ft_data }
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
INSERT INTO ft(rowid, x) VALUES(3, 'one two five');
INSERT INTO ft(rowid, x) VALUES(4, 'one two seven');
INSERT INTO ft(rowid, x) VALUES(5, 'one two eight');
}
do_execsql_test 5.1 {
DELETE FROM ft WHERE rowid=2
}
do_execsql_test 5.2 {
SELECT rowid FROM ft
} {1 3 4 5}
do_catchsql_test 5.3 {
UPDATE ft SET x='four six' WHERE rowid=3
} {1 {cannot UPDATE contentless fts5 table: ft}}
do_execsql_test 5.4 {
SELECT rowid FROM ft('one');
} {1 3 4 5}
do_execsql_test 5.5 {
REPLACE INTO ft(rowid, x) VALUES(3, 'four six');
SELECT rowid FROM ft('one');
} {1 4 5}
do_execsql_test 5.6 {
REPLACE INTO ft(rowid, x) VALUES(6, 'one two eleven');
SELECT rowid FROM ft('one');
} {1 4 5 6}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
}
do_test 6.1 {
db eval { SELECT rowid FROM ft('one two') } {
if {$rowid==1} {
db eval { INSERT INTO ft(rowid, x) VALUES(3, 'one two four') }
}
}
} {}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
}
set lRowid [list -450 0 1 2 42]
do_test 7.1 {
execsql BEGIN
foreach r $lRowid {
execsql { INSERT INTO ft(rowid, x) VALUES($r, 'one one one'); }
}
execsql COMMIT
} {}
do_test 7.2 {
execsql BEGIN
foreach r $lRowid {
execsql { REPLACE INTO ft(rowid, x) VALUES($r, 'two two two'); }
}
execsql COMMIT
} {}
do_execsql_test 7.3 { SELECT rowid FROM ft('one'); } {}
do_execsql_test 7.4 { SELECT rowid FROM ft('two'); } $lRowid
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft VALUES('hello world');
INSERT INTO ft VALUES('one two three');
}
do_catchsql_test 8.1 {
INSERT INTO ft(ft, rowid, x) VALUES('delete', 1, 'hello world');
} {1 {'delete' may not be used with a contentless_delete=1 table}}
do_execsql_test 8.2 {
BEGIN;
INSERT INTO ft(rowid, x) VALUES(3, 'four four four');
DELETE FROM ft WHERE rowid=3;
COMMIT;
SELECT rowid FROM ft('four');
} {}
finish_test

View File

@ -0,0 +1,208 @@
# 2023 July 19
#
# 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.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless2
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
proc vocab {} {
list aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp
}
proc document {nToken} {
set doc [list]
set vocab [vocab]
for {set ii 0} {$ii < $nToken} {incr ii} {
lappend doc [lindex $vocab [expr int(rand()*[llength $vocab])]]
}
set doc
}
db func document document
proc contains {doc token} {
expr {[lsearch $doc $token]>=0}
}
db func contains contains
proc do_compare_tables_test {tn} {
uplevel [list do_test $tn {
foreach v [vocab] {
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $v) }]
set l2 [execsql { SELECT rowid FROM t2($v) }]
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
set w "[string range $v 0 1]*"
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
set l2 [execsql { SELECT rowid FROM t2($w) }]
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
set w "[string range $v 0 0]*"
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
set l2 [execsql { SELECT rowid FROM t2($w) }]
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
set l1 [execsql {
SELECT rowid FROM t1 WHERE contains(doc, $v) ORDER BY rowid DESC
}]
set l2 [execsql { SELECT rowid FROM t2($v) ORDER BY rowid DESC }]
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
}
set {} {}
} {}]
}
proc lshuffle {in} {
set L [list]
set ret [list]
foreach elem $in { lappend L [list [expr rand()] $elem] }
foreach pair [lsort -index 0 $L] { lappend ret [lindex $pair 1] }
set ret
}
expr srand(0)
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t2 USING fts5(
doc, prefix=2, content=, contentless_delete=1
);
CREATE TABLE t1(doc);
CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
DELETE FROM t2 WHERE rowid = old.rowid;
END;
}
set SMALLEST64 -9223372036854775808
set LARGEST64 9223372036854775807
foreach {tn r1 r2} {
1 0 50
2 $SMALLEST64 $SMALLEST64+50
3 $LARGEST64-50 $LARGEST64
4 -50 -1
} {
set r1 [expr $r1]
set r2 [expr $r2]
do_test 1.1.$tn {
execsql BEGIN
for {set ii $r1} {$ii <= $r2} {incr ii} {
execsql { INSERT INTO t1(rowid, doc) VALUES ($ii, document(8)); }
}
execsql COMMIT
} {}
}
do_test 1.2 {
db eval { SELECT rowid, doc FROM t1 } {
execsql { INSERT INTO t2(rowid, doc) VALUES($rowid, $doc) }
}
} {}
foreach {tn rowid} {
1 $SMALLEST64
2 0
3 -5
4 -30
5 $LARGEST64
6 $LARGEST64-1
} {
set rowid [expr $rowid]
do_execsql_test 1.3.$tn.1 {
DELETE FROM t1 WHERE rowid=$rowid
}
do_compare_tables_test 1.3.$tn.2
}
set iTest 1
foreach r [lshuffle [execsql {SELECT rowid FROM t1}]] {
if {($iTest % 50)==0} {
execsql { INSERT INTO t2(t2) VALUES('optimize') }
}
if {($iTest % 5)==0} {
execsql { INSERT INTO t2(t2, rank) VALUES('merge', 5) }
}
do_execsql_test 1.4.$iTest.1($r) {
DELETE FROM t1 WHERE rowid=$r
}
do_compare_tables_test 1.4.$iTest.2
incr iTest
}
do_execsql_test 1.5 {
SELECT * FROM t1
} {}
#-------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
}
do_execsql_test 2.1 {
BEGIN;
DELETE FROM t2 WHERE rowid=32;
DELETE FROM t2 WHERE rowid=64;
DELETE FROM t2 WHERE rowid=96;
DELETE FROM t2 WHERE rowid=128;
DELETE FROM t2 WHERE rowid=160;
DELETE FROM t2 WHERE rowid=192;
COMMIT;
}
do_execsql_test 2.2 {
SELECT * FROM t2('128');
} {}
#-------------------------------------------------------------------------
foreach {tn step} {
1 3
2 7
3 15
} {
set step [expr $step]
reset_db
db func document document
do_execsql_test 3.$tn.0 {
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
INSERT INTO t2(t2, rank) VALUES('pgsz', 100);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
}
do_execsql_test 3.$tn.1 {
DELETE FROM t2 WHERE (rowid % $step)==0
}
do_execsql_test 3.$tn.2 {
SELECT * FROM t2( $step * 5 )
} {}
}
finish_test

View File

@ -0,0 +1,196 @@
# 2023 July 21
#
# 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.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless3
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
BEGIN;
INSERT INTO ft VALUES('one one one');
INSERT INTO ft VALUES('two two two');
INSERT INTO ft VALUES('three three three');
INSERT INTO ft VALUES('four four four');
INSERT INTO ft VALUES('five five five');
INSERT INTO ft VALUES('six six six');
INSERT INTO ft VALUES('seven seven seven');
INSERT INTO ft VALUES('eight eight eight');
INSERT INTO ft VALUES('nine nine nine');
COMMIT;
DELETE FROM ft WHERE rowid=3;
}
proc myhex {hex} { binary decode hex $hex }
db func myhex myhex
do_execsql_test 1.1 {
UPDATE ft_data SET block =
myhex('04000000 00000001' ||
'01020304 01020304 01020304 01020304' ||
'01020304 01020304 01020304 01020304'
)
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.2 {
DELETE FROM ft WHERE rowid=1
}
do_execsql_test 1.3 {
SELECT rowid FROM ft('two');
} {2}
do_execsql_test 1.3 {
UPDATE ft_data SET block =
myhex('08000000 00000001' ||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304' ||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304'
)
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.4 {
SELECT rowid FROM ft('two');
} {2}
do_execsql_test 1.5 {
DELETE FROM ft WHERE rowid=4
}
do_execsql_test 1.6 {
UPDATE ft_data SET block = myhex('04000000 00000000')
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.7 {
SELECT rowid FROM ft('two');
} {2}
do_execsql_test 1.8 {
UPDATE ft_data SET block = myhex('04000000 00000000')
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.9 {
DELETE FROM ft WHERE rowid=8
} {}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
INSERT INTO ft VALUES('one one one');
INSERT INTO ft VALUES('two two two');
INSERT INTO ft VALUES('three three three');
INSERT INTO ft VALUES('four four four');
INSERT INTO ft VALUES('five five five');
INSERT INTO ft VALUES('six six six');
INSERT INTO ft VALUES('seven seven seven');
INSERT INTO ft VALUES('eight eight eight');
INSERT INTO ft VALUES('nine nine nine');
}
do_execsql_test 2.1 {
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 2.2 {
SELECT count(*) FROM ft_data
} {3}
do_execsql_test 2.3 {
DELETE FROM ft WHERE rowid=5
}
do_execsql_test 2.4 {
SELECT count(*) FROM ft_data
} {4}
# Check that an 'optimize' works (rewrites the index) if there is a single
# segment with one or more tombstone hash pages.
do_execsql_test 2.5 {
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 2.6 {
SELECT count(*) FROM ft_data
} {3}
# Check that an 'optimize' is a no-op if there is a single segment
# and no tombstone hash pages.
do_execsql_test 2.7 {
INSERT INTO ft(ft) VALUES('optimize');
SELECT rowid FROM ft_data;
} [db eval {SELECT rowid FROM ft_data}]
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
INSERT INTO ft(ft, rank) VALUES('pgsz', 64);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO ft(rowid, x) SELECT i, i||' '||i||' '||i||' '||i FROM s;
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 3.1 {
SELECT count(*) FROM ft_data
} {200}
do_execsql_test 3.2 {
DELETE FROM ft WHERE (rowid % 50)==0;
SELECT count(*) FROM ft_data;
} {203}
do_execsql_test 3.3 {
INSERT INTO ft(ft, rank) VALUES('merge', 500);
SELECT rowid FROM ft_data;
} [db eval {SELECT rowid FROM ft_data}]
do_execsql_test 3.4 {
INSERT INTO ft(ft, rank) VALUES('merge', -1000);
SELECT count(*) FROM ft_data;
} {197}
do_execsql_test 3.5 {
DELETE FROM ft WHERE (rowid % 50)==1;
SELECT count(*) FROM ft_data;
} {200}
do_execsql_test 3.6 {
SELECT level, segment, npgtombstone FROM fts5_structure(
(SELECT block FROM ft_data WHERE id=10)
)
} {1 0 3}
do_test 3.6 {
while 1 {
set nChange [db total_changes]
execsql { INSERT INTO ft(ft, rank) VALUES('merge', -5) }
if {([db total_changes] - $nChange)<2} break
}
} {}
do_execsql_test 3.7 {
SELECT level, segment, npgtombstone FROM fts5_structure(
(SELECT block FROM ft_data WHERE id=10)
)
} {2 0 0}
finish_test

View File

@ -0,0 +1,248 @@
# 2023 July 21
#
# 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.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless4
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
proc document {n} {
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
set ret [list]
for {set ii 0} {$ii < $n} {incr ii} {
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
}
set ret
}
db func document document
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(ft, rank) VALUES('pgsz', 240);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO ft SELECT document(12) FROM s;
}
do_execsql_test 1.1 {
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 1.2 {
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {0 0 1000 0}
do_execsql_test 1.3 {
DELETE FROM ft WHERE rowid < 50
}
do_execsql_test 1.4 {
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {0 0 1000 49}
do_execsql_test 1.5 {
DELETE FROM ft WHERE rowid < 1000
}
do_execsql_test 1.6 {
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {1 0 1 0}
#--------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
}
do_test 2.1 {
for {set ii 0} {$ii < 5000} {incr ii} {
execsql { INSERT INTO ft VALUES( document(12) ) }
}
} {}
do_execsql_test 2.2 {
SELECT sum(nentry) - sum(nentrytombstone) FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {5000}
for {set ii 5000} {$ii >= 0} {incr ii -100} {
do_execsql_test 2.3.$ii {
DELETE FROM ft WHERE rowid > $ii
}
do_execsql_test 2.3.$ii.2 {
SELECT
CAST((total(nentry) - total(nentrytombstone)) AS integer)
FROM
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
} $ii
}
execsql_pp {
SELECT * FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
}
do_test 2.4 {
for {set ii 0} {$ii < 5000} {incr ii} {
execsql { INSERT INTO ft VALUES( document(12) ) }
}
} {}
for {set ii 1} {$ii <= 5000} {incr ii 10} {
do_execsql_test 2.3.$ii {
DELETE FROM ft WHERE rowid = $ii;
INSERT INTO ft VALUES( document(12) );
INSERT INTO ft(ft, rank) VALUES('merge', -10);
}
do_execsql_test 2.3.$ii.2 {
SELECT
CAST((total(nentry) - total(nentrytombstone)) AS integer)
FROM
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
} 5000
}
#-------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO ft SELECT document(12) FROM s;
}
do_catchsql_test 3.1 {
INSERT INTO ft(ft, rank) VALUES('deletemerge', 'text');
} {1 {SQL logic error}}
do_catchsql_test 3.2 {
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
} {0 {}}
do_execsql_test 3.3 {
SELECT * FROM ft_config WHERE k='deletemerge'
} {deletemerge 50}
do_catchsql_test 3.4 {
INSERT INTO ft(ft, rank) VALUES('deletemerge', 101);
} {0 {}}
do_execsql_test 3.5 {
SELECT * FROM ft_config WHERE k='deletemerge'
} {deletemerge 101}
do_execsql_test 3.6 {
DELETE FROM ft WHERE rowid<95
}
do_execsql_test 3.7 {
SELECT nentrytombstone, nentry FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {94 100}
do_execsql_test 3.8 {
DELETE FROM ft WHERE rowid=95
}
do_execsql_test 3.9 {
SELECT nentrytombstone, nentry FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {95 100}
do_execsql_test 3.10 {
DELETE FROM ft;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO ft SELECT document(12) FROM s;
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
}
do_execsql_test 3.11 {
DELETE FROM ft WHERE rowid<95
}
do_execsql_test 3.12 {
SELECT nentrytombstone, nentry FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {0 6}
#-------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
INSERT INTO x1(x1, rank) VALUES('usermerge', 16);
INSERT INTO x1(x1, rank) VALUES('deletemerge', 40);
INSERT INTO x1 VALUES('one');
INSERT INTO x1 VALUES('two');
INSERT INTO x1 VALUES('three');
INSERT INTO x1 VALUES('four');
INSERT INTO x1 VALUES('five');
INSERT INTO x1 VALUES('six');
INSERT INTO x1 VALUES('seven');
INSERT INTO x1 VALUES('eight');
INSERT INTO x1 VALUES('nine');
INSERT INTO x1 VALUES('ten');
}
do_execsql_test 4.1 {
SELECT level, segment FROM fts5_structure((
SELECT block FROM x1_data WHERE id=10
))
} {
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
}
for {set ii 1} {$ii < 4} {incr ii} {
do_execsql_test 4.2.$ii {
DELETE FROM x1 WHERE rowid = $ii;
INSERT INTO x1(x1, rank) VALUES('merge', 5);
SELECT level, segment FROM fts5_structure((
SELECT block FROM x1_data WHERE id=10
))
} {
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
}
}
do_execsql_test 4.3 {
DELETE FROM x1 WHERE rowid = $ii;
INSERT INTO x1(x1, rank) VALUES('merge', 5);
SELECT level, segment, nentry FROM fts5_structure((
SELECT block FROM x1_data WHERE id=10
))
} {
1 0 6
}
finish_test

View File

@ -0,0 +1,111 @@
# 2023 July 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.
#
#*************************************************************************
#
# This file is focused on OOM errors. Particularly those that may occur
# when using contentless_delete=1 databases.
#
source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5faultF
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
faultsim_save_and_close
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
CREATE VIRTUAL TABLE t1 USING fts5(x, y, content=, contentless_delete=1)
}
} -test {
faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}}
}
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
BEGIN;
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
COMMIT;
DELETE FROM t1 WHERE rowid IN (2, 4);
}
do_faultsim_test 2 -prep {
sqlite3 db test.db
execsql { SELECT rowid FROM t1 }
} -body {
execsql {
SELECT rowid FROM t1('b c');
}
} -test {
faultsim_test_result {0 {1 3}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
BEGIN;
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
COMMIT;
}
faultsim_save_and_close
do_faultsim_test 3 -prep {
faultsim_restore_and_reopen
execsql { SELECT rowid FROM t1 }
} -body {
execsql {
INSERT INTO t1(rowid, doc) VALUES(5, 'a b c d');
}
} -test {
faultsim_test_result {0 {}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t1(rowid, doc) SELECT i, 'a b c d' FROM s;
}
do_execsql_test 4.1 { DELETE FROM t1 WHERE rowid <= 25 }
faultsim_save_and_close
do_faultsim_test 4 -faults oom-t* -prep {
faultsim_restore_and_reopen
execsql { SELECT rowid FROM t1 }
} -body {
execsql {
DELETE FROM t1 WHERE rowid < 100
}
} -test {
faultsim_test_result {0 {}}
}
finish_test

View File

@ -1,5 +1,5 @@
C Dynamically\sdetermine\swhether\sthe\swasm.xWrap()\sargc\scheck\scan\sbe\sapplied,\sdepending\son\show\sthe\swasm\senvironment\sexposes\sits\sexports.
D 2023-07-27T17:50:10.521
C Add\sthe\scontentless_delete=1\soption\sto\sfts5.\sFor\screating\scontentless\stables\sthat\ssupport\sDELETE\sand\sREPLACE\sstatements.
D 2023-07-27T19:13:35.663
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -86,15 +86,15 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d
F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
F ext/fts5/fts5.h c132a9323f22a972c4c93a8d5a3d901113a6e612faf30ca8e695788438c5ca2a
F ext/fts5/fts5Int.h ed48a096418ff4a7c02ac9bd1e8d40c46de21b79a132b8b08d3f32233703de7d
F ext/fts5/fts5Int.h 78a63cc0795186cde5384816a9403a68c65774b35d952e05b81a1b4b158e07c8
F ext/fts5/fts5_aux.c 572d5ec92ba7301df2fea3258576332f2f4d2dfd66d8263afd157d9deceac480
F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5
F ext/fts5/fts5_config.c 051056a9052f5d3a4d1c695f996fd364f920e341f136c60ab2c04aa7e267113f
F ext/fts5/fts5_expr.c 58fb8ceddfb1cefcd54510f9f2f33c220ef9d1b3fa77462111f5ae2a825ab7b1
F ext/fts5/fts5_hash.c d4fb70940359f2120ccd1de7ffe64cc3efe65de9e8995b822cd536ff64c96982
F ext/fts5/fts5_index.c 430d3edfc5b51d02f0671e2b8268c12bf485f7dc80db2b61c996e74814bc46b0
F ext/fts5/fts5_main.c b4dba04a36aaf9b8e8cef0100b6dbb422cc74753eacc11d6401cac7a87c0f38d
F ext/fts5/fts5_storage.c 76c6085239eb44424004c022e9da17a5ecd5aaec859fba90ad47d3b08f4c8082
F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081
F ext/fts5/fts5_expr.c 2473c13542f463cae4b938c498d6193c90d38ea1a2a4f9849c0479736e50d24d
F ext/fts5/fts5_hash.c 65e7707bc8774706574346d18c20218facf87de3599b995963c3e6d6809f203d
F ext/fts5/fts5_index.c 182cf576bae17682adf2fe8cefb8bc3b46bea27e6a227e444859b3c065322687
F ext/fts5/fts5_main.c 2f87ee44fdb21539c264541149f07f70e065d58f37420063e5ddef80ba0f5ede
F ext/fts5/fts5_storage.c 3c9b41fce41b6410f2e8f82eb035c6a29b2560483f773e6dc98cf3cb2e4ddbb5
F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
F ext/fts5/fts5_test_tok.c a2bed8edb25f6432e8cdb62aad5916935c19dba8dac2b8324950cfff397e25ff
@ -132,6 +132,10 @@ F ext/fts5/test/fts5config.test 60094712debc59286c59aef0e6cf511c37d866802776a825
F ext/fts5/test/fts5conflict.test 655925678e630d3cdf145d18725a558971806416f453ac8410ca8c04d934238d
F ext/fts5/test/fts5connect.test 08030168fc96fc278fa81f28654fb7e90566f33aff269c073e19b3ae9126b2f4
F ext/fts5/test/fts5content.test 213506436fb2c87567b8e31f6d43ab30aab99354cec74ed679f22aad0cdbf283
F ext/fts5/test/fts5contentless.test 9a42a86822670792ba632f5c57459addeb774d93b29d5e6ddae08faa64c2b6d9
F ext/fts5/test/fts5contentless2.test 12c778d134a121b8bad000fbf3ae900d53226fee840ce36fe941b92737f1fda7
F ext/fts5/test/fts5contentless3.test 487dce16b6677f68b44d7cbd158b9b7275d25e2c14d713f9188d9645bb699286
F ext/fts5/test/fts5contentless4.test 0f43ededc2874f65d7da99b641a82239854d98d3fa43db729f284b723f23b69f
F ext/fts5/test/fts5corrupt.test 77ae6f41a7eba10620efb921cf7dbe218b0ef232b04519deb43581cb17a57ebe
F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec994e16ab132952e7805f
F ext/fts5/test/fts5corrupt3.test 7da9895dafa404efd20728f66ff4b94399788bdc042c36fe2689801bba2ccd78
@ -159,6 +163,7 @@ F ext/fts5/test/fts5faultA.test be4487576bff8c22cee6597d1893b312f306504a8c6ccd3c
F ext/fts5/test/fts5faultB.test d606bdb8e81aaeb6f41de3fc9fc7ae315733f0903fbff05cf54f5b045b729ab5
F ext/fts5/test/fts5faultD.test e7ed7895abfe6bc98a5e853826f6b74956e7ba7f594f1860bbf9e504b9647996
F ext/fts5/test/fts5faultE.test 844586ce71dab4be85bb86880e87b624d089f851654cd22e4710c77eb8ce7075
F ext/fts5/test/fts5faultF.test 4abef99f86e99d9f0c6460dd68c586a766b6b9f1f660ada55bf2e8266bd1bbc1
F ext/fts5/test/fts5first.test 3fcf2365c00a15fc9704233674789a3b95131d12de18a9b996159f6909dc8079
F ext/fts5/test/fts5full.test e1701a112354e0ff9a1fdffb0c940c576530c33732ee20ac5e8361777070d717
F ext/fts5/test/fts5fuzz1.test 238d8c45f3b81342aa384de3e581ff2fa330bf922a7b69e484bbc06051a1080e
@ -2044,9 +2049,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 2c5dd34199f5bcf729be814b8b46d9997821fe3a39ab12779c93df1bb2fd108d
Q +86bb464f31b77ac7a89ee9a50c3d988a186f84fa0bbee16b15fcbbac33523fa1
R 26c73044c10199d992b00944e358a20b
U stephan
Z 7df747b854c1185863732d1df816e30a
P fd59226b34fffb1479fb2d7bd7c0aff982aa4a1a73e6c0d81de6eaf9c075998c 719973d7f5a47b110e9919fcb96d21feab1e41356dbb3ec674c1116c17bbb778
R aea8a90a4ab6b65ce2d69f610973d78d
T +closed 719973d7f5a47b110e9919fcb96d21feab1e41356dbb3ec674c1116c17bbb778
U dan
Z dc969c62dd2115415f4e48c1e3d95206
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
fd59226b34fffb1479fb2d7bd7c0aff982aa4a1a73e6c0d81de6eaf9c075998c
d66b182d2bc6ce0772e69401b7affe1adbc1b128c4631cb3c17f98dde72af00a