aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordan <dan@noemail.net>2012-03-14 20:01:52 +0000
committerdan <dan@noemail.net>2012-03-14 20:01:52 +0000
commit5730ef599ce6570a54d35d0abf3e7d17b933a4d8 (patch)
tree944a116ff8ce43016de75ec72992a87bef278e9d
parenta2af0aecdb3ccf3dff03034b03cb00ea052dd2fe (diff)
downloadsqlite-5730ef599ce6570a54d35d0abf3e7d17b933a4d8.tar.gz
sqlite-5730ef599ce6570a54d35d0abf3e7d17b933a4d8.zip
Add tests for incremental merge code.
FossilOrigin-Name: 570473729d6561d81e6e5f8884fd18487008636e
-rw-r--r--ext/fts3/fts3Int.h2
-rw-r--r--ext/fts3/fts3_write.c192
-rw-r--r--manifest21
-rw-r--r--manifest.uuid2
-rw-r--r--test/fts3_common.tcl63
-rw-r--r--test/fts4merge.test80
-rw-r--r--test/fts4merge2.test38
-rw-r--r--test/permutations.test2
8 files changed, 265 insertions, 135 deletions
diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h
index b34ca6305..1cd14d304 100644
--- a/ext/fts3/fts3Int.h
+++ b/ext/fts3/fts3Int.h
@@ -124,7 +124,7 @@ extern const sqlite3_api_routines *sqlite3_api;
# define NEVER(X) (0)
#else
# define ALWAYS(x) (x)
-# define NEVER(X) (x)
+# define NEVER(x) (x)
#endif
/*
diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c
index 35da283df..08f07c10b 100644
--- a/ext/fts3/fts3_write.c
+++ b/ext/fts3/fts3_write.c
@@ -3272,10 +3272,16 @@ typedef struct LayerWriter LayerWriter;
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 {
@@ -3405,7 +3411,7 @@ static int fts3IncrmergePush(
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;
@@ -3565,9 +3571,12 @@ static void fts3IncrmergeRelease(
/* 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. */
@@ -3628,22 +3637,20 @@ static int fts3TermCmp(
}
-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;
}
/*
@@ -3682,16 +3689,14 @@ static int fts3IncrmergeLoad(
}
/* 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);
@@ -3705,7 +3710,7 @@ static int fts3IncrmergeLoad(
}
nodeReaderRelease(&reader);
}
- if( aLeaf!=aRoot ) sqlite3_free(aLeaf);
+ sqlite3_free(aLeaf);
}
if( rc==SQLITE_OK && bAppendable ){
@@ -3866,9 +3871,8 @@ static int fts3IncrmergeWriter(
/* 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;
@@ -4010,6 +4014,7 @@ static int fts3TruncateNode(
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);
@@ -4029,68 +4034,66 @@ static int fts3TruncateSegment(
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);
}
}
@@ -4122,7 +4125,7 @@ static int fts3IncrmergeChomp(
|| (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 );
@@ -4149,10 +4152,10 @@ static int fts3IncrmergeChomp(
/*
** 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){
@@ -4178,8 +4181,7 @@ 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);
@@ -4195,6 +4197,8 @@ static int fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
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 ){
@@ -4230,7 +4234,10 @@ static int fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
** 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;
@@ -4252,11 +4259,12 @@ static int fts3DoIncrmerge(Fts3Table *p, const char *zParam){
}
}
- 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;
}
@@ -4476,7 +4484,7 @@ static int fts3DeleteByRowid(
** tables. The schema of the virtual table being:
**
** CREATE TABLE <table name>(
-** <user COLUMns>,
+** <user columns>,
** <table name> HIDDEN,
** docid HIDDEN,
** <langid> HIDDEN
diff --git a/manifest b/manifest
index 2ce91db0a..06d7ae13c 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-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
@@ -65,7 +65,7 @@ F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
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
@@ -78,7 +78,7 @@ F ext/fts3/fts3_test.c 6b7cc68aef4efb084e1449f7d20c4b20d3bdf6b4
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
@@ -448,7 +448,7 @@ F test/fts2q.test b2fbbe038b7a31a52a6079b215e71226d8c6a682
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
@@ -497,7 +497,8 @@ F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659
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
@@ -636,7 +637,7 @@ F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0
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
@@ -993,7 +994,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
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
diff --git a/manifest.uuid b/manifest.uuid
index 137182d9b..9cec3849f 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-36ae510de45be44efd34cff242d02fb21b7419ac \ No newline at end of file
+570473729d6561d81e6e5f8884fd18487008636e \ No newline at end of file
diff --git a/test/fts3_common.tcl b/test/fts3_common.tcl
index 23a8cfca6..6c9c2fcec 100644
--- a/test/fts3_common.tcl
+++ b/test/fts3_common.tcl
@@ -14,6 +14,67 @@
# 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
#
@@ -98,7 +159,7 @@ proc 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
diff --git a/test/fts4merge.test b/test/fts4merge.test
index 805a78cf9..a5995921f 100644
--- a/test/fts4merge.test
+++ b/test/fts4merge.test
@@ -23,35 +23,6 @@ ifcapable !fts3 {
return
}
-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) }
- }
-}
-
#-------------------------------------------------------------------------
# Test cases 1.*
#
@@ -98,6 +69,57 @@ do_execsql_test 1.5 {
3 {0 1 2}
}
+#-------------------------------------------------------------------------
+# Test cases 2.* test that errors in the xxx part of the 'merge=xxx' are
+# handled correctly.
+#
+do_execsql_test 2.0 { CREATE VIRTUAL TABLE t2 USING fts4 }
+
+foreach {tn arg} {
+ 1 {merge=abc}
+ 2 {merge=%%%}
+ 3 {merge=,}
+ 4 {merge=5,}
+ 5 {merge=6,%}
+ 6 {merge=6,six}
+ 7 {merge=6,1}
+ 8 {merge=6,0}
+} {
+ do_catchsql_test 2.$tn {
+ INSERT INTO t2(t2) VALUES($arg);
+ } {1 {SQL logic error or missing database}}
+}
+
+#-------------------------------------------------------------------------
+# Test cases 3.*
+#
+do_test 3.0 {
+ reset_db
+ execsql { PRAGMA page_size = 512 }
+ fts3_build_db_2 30040
+} {}
+do_test 3.1 { fts3_integrity_check t2 } {ok}
+
+do_execsql_test 3.2 {
+ SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level
+} {
+ 0 {0 1 2 3 4 5 6 7}
+ 1 {0 1 2 3 4}
+ 2 {0 1 2 3 4}
+ 3 {0 1 2 3 4 5 6}
+}
+
+do_execsql_test 3.3 {
+ INSERT INTO t2(t2) VALUES('merge=1000000,2');
+ SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level
+} {
+ 0 {0 1}
+ 1 {0 1}
+ 2 0
+ 3 {0 1}
+ 4 {0 1}
+ 5 0
+}
finish_test
diff --git a/test/fts4merge2.test b/test/fts4merge2.test
new file mode 100644
index 000000000..308b6929f
--- /dev/null
+++ b/test/fts4merge2.test
@@ -0,0 +1,38 @@
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/fts3_common.tcl
+source $testdir/malloc_common.tcl
+set ::testprefix fts4merge2
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+do_test 1.0 {
+ fts3_build_db_1 1000
+ faultsim_save_and_close
+} {}
+
+do_faultsim_test 1.1 -faults oom-* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql { INSERT INTO t1(t1) VALUES('merge=32,4') }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+do_faultsim_test 1.2 -faults oom-t* -prep {
+ if {$iFail<100} {set iFail 803}
+ faultsim_restore_and_reopen
+} -body {
+ execsql { INSERT INTO t1(t1) VALUES('merge=1,2') }
+ execsql { INSERT INTO t1(t1) VALUES('merge=1,2') }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+finish_test
diff --git a/test/permutations.test b/test/permutations.test
index 26c1b2a51..6f66e85e9 100644
--- a/test/permutations.test
+++ b/test/permutations.test
@@ -184,7 +184,7 @@ test_suite "fts3" -prefix "" -description {
fts3aux1.test fts3comp1.test fts3auto.test
fts4aa.test fts4content.test
fts3conf.test fts3prefix.test fts3fault2.test fts3corrupt.test
- fts3corrupt2.test fts3first.test fts4langid.test
+ fts3corrupt2.test fts3first.test fts4langid.test fts4merge.test
}