typedef struct Blob Blob;
typedef struct NodeReader NodeReader;
+/*
+** An instance of the following structure is used as a dynamic buffer
+** to build up nodes or other blobs of data in.
+**
+** The function blobGrowBuffer() is used to extend the allocation.
+*/
struct Blob {
- char *a;
- int n;
- int nAlloc;
+ char *a; /* Pointer to allocation */
+ int n; /* Number of valid bytes of data in a[] */
+ int nAlloc; /* Allocated size of a[] (nAlloc>=n) */
};
struct LayerWriter {
int iLayer;
assert( nTerm>0 );
- for(iLayer=1; iLayer<FTS_MAX_APPENDABLE_HEIGHT; iLayer++){
+ for(iLayer=1; ALWAYS(iLayer<FTS_MAX_APPENDABLE_HEIGHT); iLayer++){
sqlite3_int64 iNextPtr = 0;
LayerWriter *pLayer = &pWriter->aLayer[iLayer];
int rc = SQLITE_OK;
/* Find the root node */
for(iRoot=FTS_MAX_APPENDABLE_HEIGHT-1; iRoot>=0; iRoot--){
- if( pWriter->aLayer[iRoot].block.n>0 ) break;
- assert( pWriter->aLayer[iRoot].block.nAlloc==0 );
- assert( pWriter->aLayer[iRoot].key.nAlloc==0 );
+ LayerWriter *pLayer = &pWriter->aLayer[iRoot];
+ if( pLayer->block.n>0 ) break;
+ assert( *pRc || pLayer->block.nAlloc==0 );
+ assert( *pRc || pLayer->key.nAlloc==0 );
+ sqlite3_free(pLayer->block.a);
+ sqlite3_free(pLayer->key.a);
}
/* Empty output segment. This is a no-op. */
}
-static int fts3IsAppendable(Fts3Table *p, sqlite3_int64 iEnd, int *pRc){
+static int fts3IsAppendable(Fts3Table *p, sqlite3_int64 iEnd, int *pbRes){
int bRes = 0;
- if( *pRc==SQLITE_OK ){
- sqlite3_stmt *pCheck = 0;
- int rc;
+ sqlite3_stmt *pCheck = 0;
+ int rc;
- rc = fts3SqlStmt(p, SQL_SEGMENT_IS_APPENDABLE, &pCheck, 0);
- if( rc==SQLITE_OK ){
- sqlite3_bind_int64(pCheck, 1, iEnd);
- if( SQLITE_ROW==sqlite3_step(pCheck) ) bRes = 1;
- rc = sqlite3_reset(pCheck);
- }
- *pRc = rc;
+ rc = fts3SqlStmt(p, SQL_SEGMENT_IS_APPENDABLE, &pCheck, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pCheck, 1, iEnd);
+ if( SQLITE_ROW==sqlite3_step(pCheck) ) bRes = 1;
+ rc = sqlite3_reset(pCheck);
}
- return bRes;
+ *pbRes = bRes;
+ return rc;
}
/*
}
/* Check for the zero-length marker in the %_segments table */
- bAppendable = fts3IsAppendable(p, iEnd, &rc);
+ rc = fts3IsAppendable(p, iEnd, &bAppendable);
/* Check that zKey/nKey is larger than the largest key the candidate */
if( rc==SQLITE_OK && bAppendable ){
- char *aLeaf = (char *)aRoot;
- int nLeaf = nRoot;
+ char *aLeaf = 0;
+ int nLeaf = 0;
- if( aRoot[0] ){
- rc = sqlite3Fts3ReadBlock(p, iLeafEnd, &aLeaf, &nLeaf, 0);
- }
+ rc = sqlite3Fts3ReadBlock(p, iLeafEnd, &aLeaf, &nLeaf, 0);
if( rc==SQLITE_OK ){
NodeReader reader;
for(rc = nodeReaderInit(&reader, aLeaf, nLeaf);
}
nodeReaderRelease(&reader);
}
- if( aLeaf!=aRoot ) sqlite3_free(aLeaf);
+ sqlite3_free(aLeaf);
}
if( rc==SQLITE_OK && bAppendable ){
/* Insert the marker in the %_segments table to make sure nobody tries
** to steal the space just allocated. This is also used to identify
** appendable segments. */
- if( rc==SQLITE_OK ){
- rc = fts3WriteSegment(p, pWriter->iEnd, 0, 0);
- }
+ rc = fts3WriteSegment(p, pWriter->iEnd, 0, 0);
+ if( rc!=SQLITE_OK ) return rc;
pWriter->iAbsLevel = iAbsLevel;
pWriter->nLeafEst = nLeafEst;
pNew, &prev, reader.term.a, reader.term.n,
reader.aDoclist, reader.nDoclist
);
+ if( rc!=SQLITE_OK ) break;
}
if( bStarted==0 ){
fts3StartNode(pNew, (int)aNode[0], reader.iChild);
const char *zTerm, /* Remove terms smaller than this */
int nTerm /* Number of bytes in buffer zTerm */
){
- int rc; /* Return code */
+ int rc = SQLITE_OK; /* Return code */
Blob root = {0,0,0}; /* New root page image */
Blob block = {0,0,0}; /* Buffer used for any other block */
-
+ sqlite3_int64 iBlock = 0; /* Block id */
+ sqlite3_int64 iNewStart = 0; /* New value for iStartBlock */
+ sqlite3_int64 iOldStart = 0; /* Old value for iStartBlock */
+ int rc2; /* sqlite3_reset() return code */
sqlite3_stmt *pFetch = 0; /* Statement used to fetch segdir */
- rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR, &pFetch, 0);
- if( rc==SQLITE_OK ){
- sqlite3_int64 iBlock = 0; /* Block id */
- sqlite3_int64 iNewStart = 0;
- sqlite3_int64 iOldStart = 0;
- int rc2; /* sqlite3_reset() return code */
-
- sqlite3_bind_int64(pFetch, 1, iAbsLevel);
- sqlite3_bind_int(pFetch, 2, iIdx);
- if( SQLITE_ROW==sqlite3_step(pFetch) ){
- const char *aRoot = sqlite3_column_blob(pFetch, 4);
- int nRoot = sqlite3_column_bytes(pFetch, 4);
- iOldStart = sqlite3_column_int64(pFetch, 1);
- rc = fts3TruncateNode(aRoot, nRoot, &root, zTerm, nTerm, &iBlock);
- }
- rc2 = sqlite3_reset(pFetch);
- if( rc==SQLITE_OK ) rc = rc2;
+ assert( p->aStmt[SQL_SELECT_SEGDIR] );
+ pFetch = p->aStmt[SQL_SELECT_SEGDIR];
- while( rc==SQLITE_OK && iBlock ){
- char *aBlock = 0;
- int nBlock = 0;
- iNewStart = iBlock;
+ sqlite3_bind_int64(pFetch, 1, iAbsLevel);
+ sqlite3_bind_int(pFetch, 2, iIdx);
+ if( SQLITE_ROW==sqlite3_step(pFetch) ){
+ const char *aRoot = sqlite3_column_blob(pFetch, 4);
+ int nRoot = sqlite3_column_bytes(pFetch, 4);
+ iOldStart = sqlite3_column_int64(pFetch, 1);
+ rc = fts3TruncateNode(aRoot, nRoot, &root, zTerm, nTerm, &iBlock);
+ }
+ rc2 = sqlite3_reset(pFetch);
+ if( rc==SQLITE_OK ) rc = rc2;
- rc = sqlite3Fts3ReadBlock(p, iBlock, &aBlock, &nBlock, 0);
- if( rc==SQLITE_OK ){
- rc = fts3TruncateNode(aBlock, nBlock, &block, zTerm, nTerm, &iBlock);
- }
- if( rc==SQLITE_OK ){
- rc = fts3WriteSegment(p, iNewStart, block.a, block.n);
- }
- sqlite3_free(aBlock);
+ while( rc==SQLITE_OK && iBlock ){
+ char *aBlock = 0;
+ int nBlock = 0;
+ iNewStart = iBlock;
+
+ rc = sqlite3Fts3ReadBlock(p, iBlock, &aBlock, &nBlock, 0);
+ if( rc==SQLITE_OK ){
+ rc = fts3TruncateNode(aBlock, nBlock, &block, zTerm, nTerm, &iBlock);
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts3WriteSegment(p, iNewStart, block.a, block.n);
}
+ sqlite3_free(aBlock);
+ }
- /* Variable iNewStart now contains the first valid leaf node. */
- if( rc==SQLITE_OK && iNewStart ){
- sqlite3_stmt *pDel = 0;
- rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDel, 0);
- if( rc==SQLITE_OK ){
- sqlite3_bind_int64(pDel, 1, iOldStart);
- sqlite3_bind_int64(pDel, 2, iNewStart-1);
- sqlite3_step(pDel);
- rc = sqlite3_reset(pDel);
- }
+ /* Variable iNewStart now contains the first valid leaf node. */
+ if( rc==SQLITE_OK && iNewStart ){
+ sqlite3_stmt *pDel = 0;
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDel, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iOldStart);
+ sqlite3_bind_int64(pDel, 2, iNewStart-1);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
}
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pChomp = 0;
+ rc = fts3SqlStmt(p, SQL_CHOMP_SEGDIR, &pChomp, 0);
if( rc==SQLITE_OK ){
- sqlite3_stmt *pChomp = 0;
- rc = fts3SqlStmt(p, SQL_CHOMP_SEGDIR, &pChomp, 0);
- if( rc==SQLITE_OK ){
- sqlite3_bind_int64(pChomp, 1, iNewStart);
- sqlite3_bind_blob(pChomp, 2, root.a, root.n, SQLITE_STATIC);
- sqlite3_bind_int64(pChomp, 3, iAbsLevel);
- sqlite3_bind_int(pChomp, 4, iIdx);
- sqlite3_step(pChomp);
- rc = sqlite3_reset(pChomp);
- }
+ sqlite3_bind_int64(pChomp, 1, iNewStart);
+ sqlite3_bind_blob(pChomp, 2, root.a, root.n, SQLITE_STATIC);
+ sqlite3_bind_int64(pChomp, 3, iAbsLevel);
+ sqlite3_bind_int(pChomp, 4, iIdx);
+ sqlite3_step(pChomp);
+ rc = sqlite3_reset(pChomp);
}
}
|| (pCsr->apSegment[1]->iIdx==0 && pCsr->apSegment[0]->iIdx==1)
);
- for(i=1; i>=0; i--){
+ for(i=1; i>=0 && rc==SQLITE_OK; i--){
Fts3SegReader *pSeg = pCsr->apSegment[0];
if( pSeg->iIdx!=i ) pSeg = pCsr->apSegment[1];
assert( pSeg->iIdx==i );
/*
** Attempt an incremental merge that writes nMerge leaf pages.
**
-** Incremental merges happen two segments at a time. The two
+** Incremental merges happen two segments at a time. The two
** segments to be merged are the two oldest segments (the ones with
** the smallest index) in the highest level that has at least
-** nMin segments. Multiple segment pair merges might occur in
+** nMin segments. Multiple segment pair merges might occur in
** an attempt to write the quota of nMerge leaf pages.
*/
static int fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
return sqlite3_reset(pFindLevel);
}
iAbsLevel = sqlite3_column_int64(pFindLevel, 0);
- rc = sqlite3_reset(pFindLevel);
- if( rc!=SQLITE_OK ) return rc;
+ sqlite3_reset(pFindLevel);
/* Allocate space for the cursor, filter and writer objects */
pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc);
if( rc==SQLITE_OK ){
rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter);
+ }
+ if( rc==SQLITE_OK ){
if( SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr)) ){
rc = fts3IncrmergeWriter(p, iAbsLevel, pCsr->zTerm,pCsr->nTerm,pWriter);
if( rc==SQLITE_OK ){
** written for the merge, and the minimum number of segments on a level
** before it will be selected for a merge, respectively.
*/
-static int fts3DoIncrmerge(Fts3Table *p, const char *zParam){
+static int fts3DoIncrmerge(
+ Fts3Table *p, /* FTS3 table handle */
+ const char *zParam /* Nul-terminated string containing "A,B" */
+){
int rc;
int nMin = (FTS3_MERGE_COUNT / 2);
int nMerge = 0;
}
}
- if( z[0]!='\0' ) return SQLITE_ERROR;
- if( nMin<2 ) nMin = 2;
-
- rc = fts3Incrmerge(p, nMerge, nMin);
- sqlite3Fts3SegmentsClose(p);
+ if( z[0]!='\0' || nMin<2 ){
+ rc = SQLITE_ERROR;
+ }else{
+ rc = fts3Incrmerge(p, nMerge, nMin);
+ sqlite3Fts3SegmentsClose(p);
+ }
return rc;
}
** tables. The schema of the virtual table being:
**
** CREATE TABLE <table name>(
-** <user COLUMns>,
+** <user columns>,
** <table name> HIDDEN,
** docid HIDDEN,
** <langid> HIDDEN
-C Avoid\sallocating\sa\slarge\sobject\son\sthe\sstack\sin\sthe\sincremental\smerge\scode.\sUse\ssqlite3_malloc()\sinstead.
-D 2012-03-14T12:17:40.405
+C Add\stests\sfor\sincremental\smerge\scode.
+D 2012-03-14T20:01:52.250
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 3f79a373e57c3b92dabf76f40b065e719d31ac34
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 806632fd0020eed966ab82ea25fe09f1a4c86907
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
-F ext/fts3/fts3Int.h cc8991daf660b926ef77a004fe92b97adaf10d97
+F ext/fts3/fts3Int.h 1da6d2af6b079cdd74cd2350761182dedb8bd892
F ext/fts3/fts3_aux.c 72de4cb43db7bfc2f68fbda04b7d8095ae9a6239
F ext/fts3/fts3_expr.c dbc7ba4c3a6061adde0f38ed8e9b349568299551
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
F ext/fts3/fts3_tokenizer.c 3da7254a9881f7e270ab28e2004e0d22b3212bce
F ext/fts3/fts3_tokenizer.h 66dec98e365854b6cd2d54f1a96bb6d428fc5a68
F ext/fts3/fts3_tokenizer1.c 0dde8f307b8045565cf63797ba9acfaff1c50c68
-F ext/fts3/fts3_write.c 772b8c32b93c60b85b60c635de2ff5b3f49fd779
+F ext/fts3/fts3_write.c 355121666034ece7d2472caa3f322d0cb61b0f0d
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e
F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
-F test/fts3_common.tcl 91c29230c428443e6552add9b18cf94a3dc23074
+F test/fts3_common.tcl 1b2e522476236a018c1a4b8ae4f7599ce29b7be0
F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0
F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9
F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63
F test/fts4aa.test 6e7f90420b837b2c685f3bcbe84c868492d40a68
F test/fts4content.test 17b2360f7d1a9a7e5aa8022783f5c5731b6dfd4f
F test/fts4langid.test fabdd5a8db0fa00292e0704809f566e3fb6dba3a
-F test/fts4merge.test 3af8fa8fd9f27a9eb402b9ae399aa53fbaae8481
+F test/fts4merge.test d841f283bf9557d549afde513fe38d0b48adc4f7
+F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
-F test/permutations.test 2b5a1b64a8e5114757457fbce9010387d1fe7682
+F test/permutations.test 0ab1e7748de5d29c4c648ba5ce3b983ab80653d1
F test/pragma.test f11c59ec935a52edb4d3d5676d456588121fcefa
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P f97b12e0955c4c29f9c31a186d72d87f7407782e
-R 86bb2bcb41aa5bde569f19117ae756eb
+P 36ae510de45be44efd34cff242d02fb21b7419ac
+R 326f29ae3378aab5424c0bd2deaa1142
U dan
-Z da9e601d088fda8ad57ce1fc3070902a
+Z 9fe983241614f7794efd59c0386308c4
# to use Tcl.
#
+
+#-------------------------------------------------------------------------
+# USAGE: fts3_build_db_1 N
+#
+# Build a sample FTS table in the database opened by database connection
+# [db]. The name of the new table is "t1".
+#
+proc fts3_build_db_1 {n} {
+
+ if {$n > 10000} {error "n must be <= 10000"}
+
+ db eval { CREATE VIRTUAL TABLE t1 USING fts4(x, y) }
+
+ set xwords [list zero one two three four five six seven eight nine ten]
+ set ywords [list alpha beta gamma delta epsilon zeta eta theta iota kappa]
+
+ for {set i 0} {$i < $n} {incr i} {
+ set x ""
+ set y ""
+
+ set x [list]
+ lappend x [lindex $xwords [expr ($i / 1000) % 10]]
+ lappend x [lindex $xwords [expr ($i / 100) % 10]]
+ lappend x [lindex $xwords [expr ($i / 10) % 10]]
+ lappend x [lindex $xwords [expr ($i / 1) % 10]]
+
+ set y [list]
+ lappend y [lindex $ywords [expr ($i / 1000) % 10]]
+ lappend y [lindex $ywords [expr ($i / 100) % 10]]
+ lappend y [lindex $ywords [expr ($i / 10) % 10]]
+ lappend y [lindex $ywords [expr ($i / 1) % 10]]
+
+ db eval { INSERT INTO t1(docid, x, y) VALUES($i, $x, $y) }
+ }
+}
+
+#-------------------------------------------------------------------------
+# USAGE: fts3_build_db_2 N
+#
+# Build a sample FTS table in the database opened by database connection
+# [db]. The name of the new table is "t2".
+#
+proc fts3_build_db_2 {n} {
+
+ if {$n > 100000} {error "n must be <= 100000"}
+
+ db eval { CREATE VIRTUAL TABLE t2 USING fts4 }
+
+ set chars [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 ""]
+
+ for {set i 0} {$i < $n} {incr i} {
+ set word ""
+ set n [llength $chars]
+ append word [lindex $chars [expr {($i / 1) % $n}]]
+ append word [lindex $chars [expr {($i / $n) % $n}]]
+ append word [lindex $chars [expr {($i / ($n*$n)) % $n}]]
+
+ db eval { INSERT INTO t2(docid, content) VALUES($i, $word) }
+ }
+}
+
#-------------------------------------------------------------------------
# USAGE: fts3_integrity_check TBL
#
set es "Error at docid=$iDoc col=$iCol pos=$pos. Index is missing"
lappend errors $es
} else {
- if {$C($iDoc,$iCol,$pos) != "$term"} {
+ if {[string compare $C($iDoc,$iCol,$pos) $term]} {
set es "Error at docid=$iDoc col=$iCol pos=$pos. Index "
append es "has \"$C($iDoc,$iCol,$pos)\", document has \"$term\""
lappend errors $es