Fix fts3 to not have the VACUUM bug from fts2. %_content.docid is an

alias to fix the rowid for documents, %_segments.blockid is an alias
to fix the rowid for segment blocks.  Unit test for the problem. (CVS 4280)

FossilOrigin-Name: 6eb2d74a8cfce322930f05c97d4ec255f3711efb
This commit is contained in:
shess 2007-08-23 20:23:37 +00:00
parent 6897ca30b5
commit 6beeb0329a
4 changed files with 154 additions and 24 deletions

View File

@ -1772,13 +1772,14 @@ typedef enum fulltext_statement {
*/
static const char *const fulltext_zStatement[MAX_STMT] = {
/* CONTENT_INSERT */ NULL, /* generated in contentInsertStatement() */
/* CONTENT_SELECT */ "select * from %_content where rowid = ?",
/* CONTENT_SELECT */ NULL, /* generated in contentSelectStatement() */
/* CONTENT_UPDATE */ NULL, /* generated in contentUpdateStatement() */
/* CONTENT_DELETE */ "delete from %_content where rowid = ?",
/* CONTENT_DELETE */ "delete from %_content where docid = ?",
/* BLOCK_INSERT */ "insert into %_segments values (?)",
/* BLOCK_SELECT */ "select block from %_segments where rowid = ?",
/* BLOCK_DELETE */ "delete from %_segments where rowid between ? and ?",
/* BLOCK_INSERT */
"insert into %_segments (blockid, block) values (null, ?)",
/* BLOCK_SELECT */ "select block from %_segments where blockid = ?",
/* BLOCK_DELETE */ "delete from %_segments where blockid between ? and ?",
/* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?",
/* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)",
@ -1821,7 +1822,7 @@ struct fulltext_vtab {
sqlite3_stmt *pLeafSelectStmts[MERGE_COUNT];
/* The statement used to prepare pLeafSelectStmts. */
#define LEAF_SELECT \
"select block from %_segments where rowid between ? and ? order by rowid"
"select block from %_segments where blockid between ? and ? order by blockid"
/* These buffer pending index updates during transactions.
** nPendingData estimates the memory size of the pending data. It
@ -1863,14 +1864,14 @@ static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
static const sqlite3_module fts3Module; /* forward declaration */
/* Return a dynamically generated statement of the form
* insert into %_content (rowid, ...) values (?, ...)
* insert into %_content (docid, ...) values (?, ...)
*/
static const char *contentInsertStatement(fulltext_vtab *v){
StringBuffer sb;
int i;
initStringBuffer(&sb);
append(&sb, "insert into %_content (rowid, ");
append(&sb, "insert into %_content (docid, ");
appendList(&sb, v->nColumn, v->azContentColumn);
append(&sb, ") values (?");
for(i=0; i<v->nColumn; ++i)
@ -1879,9 +1880,21 @@ static const char *contentInsertStatement(fulltext_vtab *v){
return stringBufferData(&sb);
}
/* Return a dynamically generated statement of the form
* select <content columns> from %_content where docid = ?
*/
static const char *contentSelectStatement(fulltext_vtab *v){
StringBuffer sb;
initStringBuffer(&sb);
append(&sb, "SELECT ");
appendList(&sb, v->nColumn, v->azContentColumn);
append(&sb, " FROM %_content WHERE docid = ?");
return stringBufferData(&sb);
}
/* Return a dynamically generated statement of the form
* update %_content set [col_0] = ?, [col_1] = ?, ...
* where rowid = ?
* where docid = ?
*/
static const char *contentUpdateStatement(fulltext_vtab *v){
StringBuffer sb;
@ -1896,7 +1909,7 @@ static const char *contentUpdateStatement(fulltext_vtab *v){
append(&sb, v->azContentColumn[i]);
append(&sb, " = ?");
}
append(&sb, " where rowid = ?");
append(&sb, " where docid = ?");
return stringBufferData(&sb);
}
@ -1913,6 +1926,8 @@ static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
switch( iStmt ){
case CONTENT_INSERT_STMT:
zStmt = contentInsertStatement(v); break;
case CONTENT_SELECT_STMT:
zStmt = contentSelectStatement(v); break;
case CONTENT_UPDATE_STMT:
zStmt = contentUpdateStatement(v); break;
default:
@ -2806,6 +2821,7 @@ static int fulltextCreate(sqlite3 *db, void *pAux,
initStringBuffer(&schema);
append(&schema, "CREATE TABLE %_content(");
append(&schema, " docid INTEGER PRIMARY KEY,");
appendList(&schema, spec.nColumn, spec.azContentColumn);
append(&schema, ")");
rc = sql_exec(db, spec.zDb, spec.zName, stringBufferData(&schema));
@ -2813,7 +2829,11 @@ static int fulltextCreate(sqlite3 *db, void *pAux,
if( rc!=SQLITE_OK ) goto out;
rc = sql_exec(db, spec.zDb, spec.zName,
"create table %_segments(block blob);");
"create table %_segments("
" blockid INTEGER PRIMARY KEY,"
" block blob"
");"
);
if( rc!=SQLITE_OK ) goto out;
rc = sql_exec(db, spec.zDb, spec.zName,
@ -3662,15 +3682,18 @@ static int fulltextFilter(
fulltext_cursor *c = (fulltext_cursor *) pCursor;
fulltext_vtab *v = cursor_vtab(c);
int rc;
char *zSql;
StringBuffer sb;
TRACE(("FTS3 Filter %p\n",pCursor));
zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
idxNum==QUERY_GENERIC ? "" : "where rowid=?");
initStringBuffer(&sb);
append(&sb, "SELECT docid, ");
appendList(&sb, v->nColumn, v->azContentColumn);
append(&sb, " FROM %_content");
if( idxNum!=QUERY_GENERIC ) append(&sb, " WHERE docid = ?");
sqlite3_finalize(c->pStmt);
rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
sqlite3_free(zSql);
rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, stringBufferData(&sb));
stringBufferDestroy(&sb);
if( rc!=SQLITE_OK ) return rc;
c->iCursorType = idxNum;

View File

@ -1,5 +1,5 @@
C Add\ssome\swarm-body\stests\sfor\srollback\sjournal\si/o\senhancements.\s(CVS\s4279)
D 2007-08-23T16:27:21
C Fix\sfts3\sto\snot\shave\sthe\sVACUUM\sbug\sfrom\sfts2.\s\s%_content.docid\sis\san\nalias\sto\sfix\sthe\srowid\sfor\sdocuments,\s%_segments.blockid\sis\san\salias\nto\sfix\sthe\srowid\sfor\ssegment\sblocks.\s\sUnit\stest\sfor\sthe\sproblem.\s(CVS\s4280)
D 2007-08-23T20:23:37
F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe
F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@ -49,7 +49,7 @@ F ext/fts2/fts2_tokenizer1.c 8a545c232bdffafd117c4eeaf59789691909f26a
F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
F ext/fts3/README.tokenizers a97c9a55b3422f6cb04af9de9296fe2447ea4a78
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 64ebaf649e779ef22cade60e973090e0a924746a
F ext/fts3/fts3.c 2098c9b08503b70d4f0c60c5a4665d7413c31a97
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3_hash.c 84654768178452b00bbc986dd878a8299dc1e3dc
F ext/fts3/fts3_hash.h af585d6867d478fc0457f64cfaae60e09541e63a
@ -296,6 +296,7 @@ F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
F test/fts3an.test 2da4df52fe8ea8389f6fa7a01e4c1a0f091118d6
F test/fts3ao.test 0aa29dd4fc1c8d46b1f7cfe5926f7ac97551bea9
F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa
F test/fts3b.test 94cd8a2fb709c99c1617df01f6908de77892d8bc
F test/func.test 605989453d1b42cec1d05c17aa232dc98e3e04e6
F test/fuzz.test 62fc19dd36a427777fd671b569df07166548628a
F test/fuzz2.test ea38692ce2da99ad79fe0be5eb1a452c1c4d37bb
@ -560,7 +561,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
P 595568492e63822caed5b6970542dcee4615dc4d
R 481110817a2ed016cc610c9d83519f44
U danielk1977
Z bffb4d15c81e758cd2ec1b776561f96d
P ff3770f855c1dd75025b1f2496f8c75e9f17ee44
R d6799fcfef1763dfc3645c2d81885228
U shess
Z 2f4ef0343bc2683827077b02b21cd810

View File

@ -1 +1 @@
ff3770f855c1dd75025b1f2496f8c75e9f17ee44
6eb2d74a8cfce322930f05c97d4ec255f3711efb

106
test/fts3b.test Normal file
View File

@ -0,0 +1,106 @@
# 2007 August 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 implements regression tests for SQLite library. This
# script tests for the fts2 rowid-versus-vacuum problem (ticket #2566).
#
# $Id: fts3b.test,v 1.1 2007/08/23 20:23:37 shess Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
ifcapable !fts3 {
finish_test
return
}
db eval {
CREATE VIRTUAL TABLE t1 USING fts3(c);
INSERT INTO t1 (c) VALUES('this is a test');
INSERT INTO t1 (c) VALUES('that was a test');
INSERT INTO t1 (c) VALUES('this is fun');
DELETE FROM t1 WHERE c = 'that was a test';
}
# Baseline test.
do_test fts3b-1.1 {
execsql {
SELECT rowid FROM t1 WHERE c MATCH 'this';
}
} {1 3}
db eval {VACUUM}
# The VACUUM renumbered the t1_content table in fts2, which breaks
# this.
do_test fts3b-1.2 {
execsql {
SELECT rowid FROM t1 WHERE c MATCH 'this';
}
} {1 3}
# The t2 table is unfortunately pretty contrived. We need documents
# that are bigger than ROOT_MAX (1024) to force segments out of the
# segdir and into %_segments. We also need to force segment merging
# to generate a hole in the %_segments table, which needs more than 16
# docs. Beyond that, to test correct operation of BLOCK_SELECT_STMT,
# we need to merge a mult-level tree, which is where the 10,000 comes
# from. Which is slow, thus the set of transactions, with the 500
# being a number such that 10,000/500 > 16.
set text {
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas
iaculis mollis ipsum. Praesent rhoncus placerat justo. Duis non quam
sed turpis posuere placerat. Curabitur et lorem in lorem porttitor
aliquet. Pellentesque bibendum tincidunt diam. Vestibulum blandit
ante nec elit. In sapien diam, facilisis eget, dictum sed, viverra
at, felis. Vestibulum magna. Sed magna dolor, vestibulum rhoncus,
ornare vel, vulputate sit amet, felis. Integer malesuada, tellus at
luctus gravida, diam nunc porta nibh, nec imperdiet massa metus eu
lectus. Aliquam nisi. Nunc fringilla nulla at lectus. Suspendisse
potenti. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Pellentesque odio nulla, feugiat eu,
suscipit nec, consequat quis, risus.
}
append text $text
db eval {CREATE VIRTUAL TABLE t2 USING fts3(c)}
set res {}
db eval {BEGIN}
for {set ii 0} {$ii<10000} {incr ii} {
db eval {INSERT INTO t2 (c) VALUES ($text)}
lappend res [expr {$ii+1}]
if {($ii%500)==0} {
db eval {
COMMIT;
BEGIN;
}
}
}
db eval {COMMIT}
do_test fts3b-2.1 {
execsql {
SELECT rowid FROM t2 WHERE c MATCH 'lorem';
}
} $res
db eval {VACUUM}
# The VACUUM renumbered the t2_segment table in fts2, which would
# break the following.
do_test fts3b-2.2 {
execsql {
SELECT rowid FROM t2 WHERE c MATCH 'lorem';
}
} $res
finish_test