From: shess Date: Tue, 22 Jul 2008 23:41:26 +0000 (+0000) Subject: Delete all fts2 index data the table becomes empty. X-Git-Tag: version-3.6.10~720 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=08904673c8fc8b6b5be50c50a411ebe458cd2e50;p=thirdparty%2Fsqlite.git Delete all fts2 index data the table becomes empty. Backports check-in (5413) from fts3. (CVS 5457) FossilOrigin-Name: 4c98179be258319f441ae4e123cf59af77e96409 --- diff --git a/ext/fts2/fts2.c b/ext/fts2/fts2.c index a7a6fc86b0..524e1d83d4 100644 --- a/ext/fts2/fts2.c +++ b/ext/fts2/fts2.c @@ -1774,10 +1774,12 @@ typedef enum fulltext_statement { CONTENT_SELECT_STMT, CONTENT_UPDATE_STMT, CONTENT_DELETE_STMT, + CONTENT_EXISTS_STMT, BLOCK_INSERT_STMT, BLOCK_SELECT_STMT, BLOCK_DELETE_STMT, + BLOCK_DELETE_ALL_STMT, SEGDIR_MAX_INDEX_STMT, SEGDIR_SET_STMT, @@ -1786,6 +1788,7 @@ typedef enum fulltext_statement { SEGDIR_DELETE_STMT, SEGDIR_SELECT_SEGMENT_STMT, SEGDIR_SELECT_ALL_STMT, + SEGDIR_DELETE_ALL_STMT, MAX_STMT /* Always at end! */ } fulltext_statement; @@ -1800,10 +1803,12 @@ static const char *const fulltext_zStatement[MAX_STMT] = { /* CONTENT_SELECT */ "select * from %_content where rowid = ?", /* CONTENT_UPDATE */ NULL, /* generated in contentUpdateStatement() */ /* CONTENT_DELETE */ "delete from %_content where rowid = ?", + /* CONTENT_EXISTS */ "select rowid from %_content limit 1", /* BLOCK_INSERT */ "insert into %_segments values (?)", /* BLOCK_SELECT */ "select block from %_segments where rowid = ?", /* BLOCK_DELETE */ "delete from %_segments where rowid between ? and ?", + /* BLOCK_DELETE_ALL */ "delete from %_segments", /* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?", /* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)", @@ -1824,6 +1829,7 @@ static const char *const fulltext_zStatement[MAX_STMT] = { /* SEGDIR_SELECT_ALL */ "select start_block, leaves_end_block, root from %_segdir " " order by level desc, idx asc", + /* SEGDIR_DELETE_ALL */ "delete from %_segdir", }; /* @@ -2100,6 +2106,25 @@ static int content_delete(fulltext_vtab *v, sqlite_int64 iRow){ return sql_single_step(s); } +/* Returns SQLITE_ROW if any rows exist in %_content, SQLITE_DONE if +** no rows exist, and any error in case of failure. +*/ +static int content_exists(fulltext_vtab *v){ + sqlite3_stmt *s; + int rc = sql_get_statement(v, CONTENT_EXISTS_STMT, &s); + if( rc!=SQLITE_OK ) return rc; + + rc = sqlite3_step(s); + if( rc!=SQLITE_ROW ) return rc; + + /* We expect only one row. We must execute another sqlite3_step() + * to complete the iteration; otherwise the table will remain locked. */ + rc = sqlite3_step(s); + if( rc==SQLITE_DONE ) return SQLITE_ROW; + if( rc==SQLITE_ROW ) return SQLITE_ERROR; + return rc; +} + /* insert into %_segments values ([pData]) ** returns assigned rowid in *piBlockid */ @@ -2273,6 +2298,23 @@ static int segdir_delete(fulltext_vtab *v, int iLevel){ return sql_single_step(s); } +/* Delete entire fts index, SQLITE_OK on success, relevant error on +** failure. +*/ +static int segdir_delete_all(fulltext_vtab *v){ + sqlite3_stmt *s; + int rc = sql_get_statement(v, SEGDIR_DELETE_ALL_STMT, &s); + if( rc!=SQLITE_OK ) return rc; + + rc = sql_single_step(s); + if( rc!=SQLITE_OK ) return rc; + + rc = sql_get_statement(v, BLOCK_DELETE_ALL_STMT, &s); + if( rc!=SQLITE_OK ) return rc; + + return sql_single_step(s); +} + /* TODO(shess) clearPendingTerms() is far down the file because ** writeZeroSegment() is far down the file because LeafWriter is far ** down the file. Consider refactoring the code to move the non-vtab @@ -5775,6 +5817,23 @@ static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg, if( nArg<2 ){ rc = index_delete(v, sqlite3_value_int64(ppArg[0])); + if( rc==SQLITE_OK ){ + /* If we just deleted the last row in the table, clear out the + ** index data. + */ + rc = content_exists(v); + if( rc==SQLITE_ROW ){ + rc = SQLITE_OK; + }else if( rc==SQLITE_DONE ){ + /* Clear the pending terms so we don't flush a useless level-0 + ** segment when the transaction closes. + */ + rc = clearPendingTerms(v); + if( rc==SQLITE_OK ){ + rc = segdir_delete_all(v); + } + } + } } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){ /* An update: * ppArg[0] = old rowid diff --git a/manifest b/manifest index 7ea48d4c7e..f373014583 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C fts2\sfunctions\sfor\stesting\sscripts.\r\nBackports\s(5340)\sfrom\sfts3.\s(CVS\s5456) -D 2008-07-22T23:32:28 +C Delete\sall\sfts2\sindex\sdata\sthe\stable\sbecomes\sempty.\r\nBackports\scheck-in\s(5413)\sfrom\sfts3.\s(CVS\s5457) +D 2008-07-22T23:41:26 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 77ff156061bb870aa0a8b3d545c670d08070f7e6 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -39,7 +39,7 @@ F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9 F ext/fts2/README.tokenizers 21e3684ea5a095b55d70f6878b4ce6af5932dfb7 F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts2/fts2.c af6d11365c4ae66be9779dde0948887e92d8b867 +F ext/fts2/fts2.c c0d287669e35e3f0998b42fb06ebd2f89e5b2593 F ext/fts2/fts2.h da5f76c65163301d1068a971fd32f4119e3c95fa F ext/fts2/fts2_hash.c 2689e42e1107ea67207f725cf69cf8972d00cf93 F ext/fts2/fts2_hash.h 9a5b1be94664139f93217a0770d7144425cffb3a @@ -316,6 +316,7 @@ F test/fts2m.test 4b30142ead6f3ed076e880a2a464064c5ad58c51 F test/fts2n.test 12b9c5352128cebd1c6b8395e43788d4b09087c2 F test/fts2o.test c6a79567d85403dc4d15b89f3f9799a0a0aef065 F test/fts2p.test 4b48c35c91e6a7dbf5ac8d1e5691823cc999aafb +F test/fts2q.test ee2cb138a599a07de3a03cd7a71a4e9e40120f93 F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a F test/fts3.test 6ee4c38b0864583c80e82a2d4372f63aae8b10c7 F test/fts3aa.test 432d1d5c41939bb5405d4d6c80a9ec759b363393 @@ -610,7 +611,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 3f614453d2d7c753a5963b027fe8618b50b4f6b9 -R 76720fcdb90c8738e414944367c3b0e7 +P 4e47394be9dfbf0f9309e55eb6c6a3a517ea2006 +R 47eed6cf50908f452be50506a6925d0d U shess -Z 807f812f3e691d0e714ba8b89e45124c +Z f9e0e9386065f5f55b31594485ae0dd8 diff --git a/manifest.uuid b/manifest.uuid index c80705daff..7800c6c661 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4e47394be9dfbf0f9309e55eb6c6a3a517ea2006 \ No newline at end of file +4c98179be258319f441ae4e123cf59af77e96409 \ No newline at end of file diff --git a/test/fts2q.test b/test/fts2q.test new file mode 100644 index 0000000000..fdce634a87 --- /dev/null +++ b/test/fts2q.test @@ -0,0 +1,128 @@ +# 2008 June 26 +# +# 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. The focus +# of this script is testing the FTS2 module's optimize() function. +# +# $Id: fts2q.test,v 1.1 2008/07/22 23:41:26 shess Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# If SQLITE_ENABLE_FTS2 is not defined, omit this file. +ifcapable !fts2 { + finish_test + return +} + +#************************************************************************* +# Probe to see if support for the FTS2 dump_* functions is compiled in. +# TODO(shess): Change main.mk to do the right thing and remove this test. +db eval { + DROP TABLE IF EXISTS t1; + CREATE VIRTUAL TABLE t1 USING fts2(c); + INSERT INTO t1 (rowid, c) VALUES (1, 'x'); +} + +set s {SELECT dump_terms(t1, 1) FROM t1 LIMIT 1} +set r {1 {unable to use function dump_terms in the requested context}} +if {[catchsql $s]==$r} { + finish_test + return +} + +#************************************************************************* +# Utility function to check for the expected terms in the segment +# level/index. _all version does same but for entire index. +proc check_terms {test level index terms} { + # TODO(shess): Figure out why uplevel in do_test can't catch + # $level and $index directly. + set ::level $level + set ::index $index + do_test $test.terms { + execsql { + SELECT dump_terms(t1, $::level, $::index) FROM t1 LIMIT 1; + } + } [list $terms] +} +proc check_terms_all {test terms} { + do_test $test.terms { + execsql { + SELECT dump_terms(t1) FROM t1 LIMIT 1; + } + } [list $terms] +} + +# Utility function to check for the expected doclist for the term in +# segment level/index. _all version does same for entire index. +proc check_doclist {test level index term doclist} { + # TODO(shess): Again, why can't the non-:: versions work? + set ::term $term + set ::level $level + set ::index $index + do_test $test { + execsql { + SELECT dump_doclist(t1, $::term, $::level, $::index) FROM t1 LIMIT 1; + } + } [list $doclist] +} +proc check_doclist_all {test term doclist} { + set ::term $term + do_test $test { + execsql { + SELECT dump_doclist(t1, $::term) FROM t1 LIMIT 1; + } + } [list $doclist] +} + +#************************************************************************* +# Test results when all rows are deleted and one is added back. +# Previously older segments would continue to exist, but now the index +# should be dropped when the table is empty. The results should look +# exactly like we never added the earlier rows in the first place. +db eval { + DROP TABLE IF EXISTS t1; + CREATE VIRTUAL TABLE t1 USING fts2(c); + INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test'); + INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test'); + INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test'); + DELETE FROM t1 WHERE 1=1; -- Delete each row rather than dropping table. + INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test'); +} + +# Should be a single initial segment. +do_test fts2q-1.segments { + execsql { + SELECT level, idx FROM t1_segdir ORDER BY level, idx; + } +} {0 0} +do_test fts2q-1.matches { + execsql { + SELECT OFFSETS(t1) FROM t1 + WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY rowid; + } +} {{0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}} + +check_terms_all fts2q-1.1 {a is test this} +check_doclist_all fts2q-1.1.1 a {[1 0[2]]} +check_doclist_all fts2q-1.1.2 is {[1 0[1]]} +check_doclist_all fts2q-1.1.3 test {[1 0[3]]} +check_doclist_all fts2q-1.1.4 this {[1 0[0]]} + +check_terms fts2q-1.2 0 0 {a is test this} +check_doclist fts2q-1.2.1 0 0 a {[1 0[2]]} +check_doclist fts2q-1.2.2 0 0 is {[1 0[1]]} +check_doclist fts2q-1.2.3 0 0 test {[1 0[3]]} +check_doclist fts2q-1.2.4 0 0 this {[1 0[0]]} + +# TODO(shess): optimize() tests here. + +finish_test