Break interior-node and leaf-node readers apart in loadSegment().

Previously, the code looped until the block was a leaf node as
indicated by a leading NUL.  Now the code loops until it finds a block
in the range of leaf nodes for this segment, then reads it using
LeavesReader.  This will make it easier to traverse a range of leaves
when doing a prefix search. (CVS 3884)

FossilOrigin-Name: 9466367d65f43d58020e709428268dc2ff98aa35
This commit is contained in:
shess 2007-04-27 22:02:57 +00:00
parent d1e3a616ca
commit 8ffcadb57e
3 changed files with 105 additions and 45 deletions

View File

@ -1634,7 +1634,8 @@ static const char *const fulltext_zStatement[MAX_STMT] = {
"select min(start_block), max(end_block) from %_segdir "
" where level = ? and start_block <> 0",
/* SEGDIR_DELETE */ "delete from %_segdir where level = ?",
/* SEGDIR_SELECT_ALL */ "select root from %_segdir order by level desc, idx",
/* SEGDIR_SELECT_ALL */
"select root, leaves_end_block from %_segdir order by level desc, idx",
};
/*
@ -4807,6 +4808,23 @@ static int leavesReaderAtEnd(LeavesReader *pReader){
return pReader->eof;
}
/* loadSegmentLeaves() may not read all the way to SQLITE_DONE, thus
** leaving the statement handle open, which locks the table.
*/
/* TODO(shess) This "solution" is not satisfactory. Really, there
** should be check-in function for all statement handles which
** arranges to call sqlite3_reset(). This most likely will require
** modification to control flow all over the place, though, so for now
** just punt.
**
** Note the the current system assumes that segment merges will run to
** completion, which is why this particular probably hasn't arisen in
** this case. Probably a brittle assumption.
*/
static int leavesReaderReset(LeavesReader *pReader){
return sqlite3_reset(pReader->pStmt);
}
static void leavesReaderDestroy(LeavesReader *pReader){
leafReaderDestroy(&pReader->leafReader);
dataBufferDestroy(&pReader->rootData);
@ -5133,6 +5151,25 @@ static int loadSegmentLeaf(fulltext_vtab *v, const char *pData, int nData,
if( rc!=SQLITE_OK ) return rc;
rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, out);
leavesReaderReset(&reader);
leavesReaderDestroy(&reader);
return rc;
}
/* Call loadSegmentLeavesInt() with the leaf nodes from iStartLeaf to
** iEndLeaf (inclusive) as input, and merge the resulting doclist into
** out.
*/
static int loadSegmentLeaves(fulltext_vtab *v,
sqlite_int64 iStartLeaf, sqlite_int64 iEndLeaf,
const char *pTerm, int nTerm,
DataBuffer *out){
LeavesReader reader;
int rc = leavesReaderInit(v, 0, iStartLeaf, iEndLeaf, NULL, 0, &reader);
if( rc!=SQLITE_OK ) return rc;
rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, out);
leavesReaderReset(&reader);
leavesReaderDestroy(&reader);
return rc;
}
@ -5143,9 +5180,9 @@ static int loadSegmentLeaf(fulltext_vtab *v, const char *pData, int nData,
** than there are terms (that block contains terms >= the last
** interior-node term).
*/
static void getNextChild(const char *pData, int nData,
const char *pTerm, int nTerm,
sqlite_int64 *piBlockid){
static void getChildContaining(const char *pData, int nData,
const char *pTerm, int nTerm,
sqlite_int64 *piBlockid){
InteriorReader reader;
assert( nData>1 );
@ -5161,53 +5198,74 @@ static void getNextChild(const char *pData, int nData,
interiorReaderDestroy(&reader);
}
/* Read block at iBlockid and pass it with other params to
** getChildContaining().
*/
static int loadAndGetChildContaining(fulltext_vtab *v, sqlite_int64 iBlockid,
const char *pTerm, int nTerm,
sqlite_int64 *piBlockid){
sqlite3_stmt *s = NULL;
int rc;
assert( iBlockid!=0 );
assert( pTerm!=NULL );
assert( nTerm!=0 ); /* TODO(shess) Why not allow this? */
assert( piBlockid!=NULL );
rc = sql_get_statement(v, BLOCK_SELECT_STMT, &s);
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3_bind_int64(s, 1, iBlockid);
if( rc!=SQLITE_OK ) return rc;
rc = sql_step_statement(v, BLOCK_SELECT_STMT, &s);
if( rc==SQLITE_DONE ) return SQLITE_ERROR;
if( rc!=SQLITE_ROW ) return rc;
getChildContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0),
pTerm, nTerm, piBlockid);
/* 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_ROW ) return SQLITE_ERROR;
if( rc!=SQLITE_DONE ) return rc;
return SQLITE_OK;
}
/* Traverse the tree represented by pData[nData] looking for
** pTerm[nTerm], merging its doclist over *out if found (any duplicate
** doclists read from the segment rooted at pData will overwrite those
** in *out).
*/
static int loadSegment(fulltext_vtab *v, const char *pData, int nData,
sqlite_int64 iLeavesEnd,
const char *pTerm, int nTerm, DataBuffer *out){
int rc;
sqlite3_stmt *s = NULL;
assert( nData>1 );
/* This code should never be called with buffered updates. */
assert( v->nPendingData<0 );
/* Process data as an interior node until we reach a leaf. */
while( *pData!='\0' ){
/* Special case where root is a leaf. */
if( *pData=='\0' ){
return loadSegmentLeaf(v, pData, nData, pTerm, nTerm, out);
}else{
int rc;
sqlite_int64 iBlockid;
getNextChild(pData, nData, pTerm, nTerm, &iBlockid);
rc = sql_get_statement(v, BLOCK_SELECT_STMT, &s);
if( rc!=SQLITE_OK ) return rc;
/* Process pData as an interior node, then loop down the tree
** until we find a leaf node to scan for the term.
*/
getChildContaining(pData, nData, pTerm, nTerm, &iBlockid);
while( iBlockid>iLeavesEnd ){
rc = loadAndGetChildContaining(v, iBlockid, pTerm, nTerm, &iBlockid);
if( rc!=SQLITE_OK ) return rc;
}
rc = sqlite3_bind_int64(s, 1, iBlockid);
if( rc!=SQLITE_OK ) return rc;
rc = sql_step_statement(v, BLOCK_SELECT_STMT, &s);
if( rc==SQLITE_DONE ) return SQLITE_ERROR;
if( rc!=SQLITE_ROW ) return rc;
pData = sqlite3_column_blob(s, 0);
nData = sqlite3_column_bytes(s, 0);
return loadSegmentLeaves(v, iBlockid, iBlockid, pTerm, nTerm, out);
}
rc = loadSegmentLeaf(v, pData, nData, pTerm, nTerm, out);
if( rc!=SQLITE_OK ) return rc;
/* If we selected a child node, we need to finish that select. */
if( s!=NULL ){
/* 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_ROW ) return SQLITE_ERROR;
if( rc!=SQLITE_DONE ) return rc;
}
return SQLITE_OK;
}
/* Scan the database and merge together the posting lists for the term
@ -5230,8 +5288,10 @@ static int termSelect(fulltext_vtab *v, int iColumn,
** elements for given docids overwrite older elements.
*/
while( (rc=sql_step_statement(v, SEGDIR_SELECT_ALL_STMT, &s))==SQLITE_ROW ){
rc = loadSegment(v, sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0),
pTerm, nTerm, &doclist);
const char *pData = sqlite3_column_blob(s, 0);
const int nData = sqlite3_column_bytes(s, 0);
const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, &doclist);
if( rc!=SQLITE_OK ) goto err;
}
if( rc==SQLITE_DONE ){

View File

@ -1,5 +1,5 @@
C Internationalize\sthe\sTRIM\sfunctions.\s\sTicket\s#2323.\s(CVS\s3883)
D 2007-04-27T21:59:53
C Break\sinterior-node\sand\sleaf-node\sreaders\sapart\sin\sloadSegment().\nPreviously,\sthe\scode\slooped\suntil\sthe\sblock\swas\sa\sleaf\snode\sas\nindicated\sby\sa\sleading\sNUL.\s\sNow\sthe\scode\sloops\suntil\sit\sfinds\sa\sblock\nin\sthe\srange\sof\sleaf\snodes\sfor\sthis\ssegment,\sthen\sreads\sit\susing\nLeavesReader.\s\sThis\swill\smake\sit\seasier\sto\straverse\sa\srange\sof\sleaves\nwhen\sdoing\sa\sprefix\ssearch.\s(CVS\s3884)
D 2007-04-27T22:02:58
F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@ -34,7 +34,7 @@ F ext/fts1/fulltext.h 08525a47852d1d62a0be81d3fc3fe2d23b094efd
F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d
F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9
F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts2/fts2.c c1e7528d9f49f1c982bb0b75f262b115a508d140
F ext/fts2/fts2.c b45e07a23646e82525657af9bab063e10f9acffc
F ext/fts2/fts2.h 591916a822cfb6426518fdbf6069359119bc46eb
F ext/fts2/fts2_hash.c b3f22116d4ef0bc8f2da6e3fdc435c86d0951a9b
F ext/fts2/fts2_hash.h e283308156018329f042816eb09334df714e105e
@ -464,7 +464,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
P 25935db73877c0cb132acb30c2fed2544d0e5e32
R fdd67fb21752d8fbe0a5631485c66773
U drh
Z dbf7b152ffcec38cc7febead2efd96a3
P ff1f4e744728c8f55afae265246797b30fe98fb0
R 99c2fc3fe139fed22b3873b01510508d
U shess
Z 5fe07eef28bba3b5b8361756bba83b2b

View File

@ -1 +1 @@
ff1f4e744728c8f55afae265246797b30fe98fb0
9466367d65f43d58020e709428268dc2ff98aa35