From: shess Date: Fri, 27 Apr 2007 22:02:57 +0000 (+0000) Subject: Break interior-node and leaf-node readers apart in loadSegment(). X-Git-Tag: version-3.4.0~197 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ffcadb57edf0efdc704ebed3c7e9d9b2003ecb4;p=thirdparty%2Fsqlite.git 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 --- diff --git a/ext/fts2/fts2.c b/ext/fts2/fts2.c index e2e4dbac59..b13f252d62 100644 --- a/ext/fts2/fts2.c +++ b/ext/fts2/fts2.c @@ -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; - 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); - } - - rc = loadSegmentLeaf(v, pData, nData, pTerm, nTerm, out); - 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; + } - /* 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 loadSegmentLeaves(v, iBlockid, iBlockid, pTerm, nTerm, out); } - 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 ){ diff --git a/manifest b/manifest index c1e7fbf99c..561012ca34 100644 --- a/manifest +++ b/manifest @@ -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 diff --git a/manifest.uuid b/manifest.uuid index a93d1e8cee..2812757684 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ff1f4e744728c8f55afae265246797b30fe98fb0 \ No newline at end of file +9466367d65f43d58020e709428268dc2ff98aa35 \ No newline at end of file