const char *zTerm, /* Term to query for */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
+ int isScan, /* True to scan from zTerm to EOF */
Fts3SegReaderCursor *pCsr /* Cursor object to populate */
){
int rc = SQLITE_OK;
assert( FTS3_SEGCURSOR_PENDING<0 );
assert( FTS3_SEGCURSOR_ALL<0 );
assert( iLevel==FTS3_SEGCURSOR_ALL || (zTerm==0 && isPrefix==1) );
+ assert( isPrefix==0 || isScan==0 );
+
memset(pCsr, 0, sizeof(Fts3SegReaderCursor));
/* If iLevel is less than 0, include a seg-reader for the pending-terms. */
- if( iLevel<0 ){
+ assert( isScan==0 || fts3HashCount(&p->pendingTerms)==0 );
+ if( iLevel<0 && isScan==0 ){
rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pPending);
if( rc==SQLITE_OK && pPending ){
int nByte = (sizeof(Fts3SegReader *) * 16);
sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0);
rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi);
if( rc!=SQLITE_OK ) goto finished;
- if( isPrefix==0 ) iLeavesEndBlock = iStartBlock;
+ if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock;
}
-
+
rc = sqlite3Fts3SegReaderNew(iAge, iStartBlock, iLeavesEndBlock,
iEndBlock, zRoot, nRoot, &pCsr->apSegment[pCsr->nSegment]
);
int i;
int nCost = 0;
rc = sqlite3Fts3SegReaderCursor(
- p, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, pSegcsr);
+ p, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr);
for(i=0; rc==SQLITE_OK && i<pSegcsr->nSegment; i++){
rc = sqlite3Fts3SegReaderCost(pCsr, pSegcsr->apSegment[i], &nCost);
int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *);
void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *);
int sqlite3Fts3SegReaderCursor(
- Fts3Table *, int, const char *, int, int, Fts3SegReaderCursor *);
+ Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
#define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002
#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
#define FTS3_SEGMENT_PREFIX 0x00000008
+#define FTS3_SEGMENT_SCAN 0x00000010
/* Type passed as 4th argument to SegmentReaderIterate() */
struct Fts3SegFilter {
struct Fts3auxCursor {
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
-
+ Fts3SegReaderCursor csr; /* Must be right after "base" */
Fts3SegFilter filter;
- Fts3SegReaderCursor csr;
+ char *zStop;
+ int nStop;
int isEof;
-
sqlite3_int64 iRowid;
sqlite3_int64 nDoc;
sqlite3_int64 nOcc;
return SQLITE_OK;
}
+#define FTS4AUX_EQ_CONSTRAINT 1
+#define FTS4AUX_GE_CONSTRAINT 2
+#define FTS4AUX_LE_CONSTRAINT 4
+
/*
** xBestIndex - Analyze a WHERE and ORDER BY clause.
*/
sqlite3_vtab *pVTab,
sqlite3_index_info *pInfo
){
+ int i;
+ int iEq = -1;
+ int iGe = -1;
+ int iLe = -1;
/* This vtab delivers always results in "ORDER BY term ASC" order. */
if( pInfo->nOrderBy==1
pInfo->orderByConsumed = 1;
}
+ /* Search for equality and range constraints on the "term" column. */
+ for(i=0; i<pInfo->nConstraint; i++){
+ if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){
+ int op = pInfo->aConstraint[i].op;
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
+ }
+ }
+
+ if( iEq>=0 ){
+ pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
+ pInfo->aConstraintUsage[iEq].argvIndex = 1;
+ }else{
+ pInfo->idxNum = 0;
+ if( iGe>=0 ){
+ pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
+ pInfo->aConstraintUsage[iGe].argvIndex = 1;
+ }
+ if( iLe>=0 ){
+ pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
+ pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0);
+ }
+ }
+
pInfo->estimatedCost = 20000;
return SQLITE_OK;
}
sqlite3Fts3SegmentsClose(pFts3);
sqlite3Fts3SegReaderFinish(&pCsr->csr);
+ sqlite3_free((void *)pCsr->filter.zTerm);
+ sqlite3_free(pCsr->zStop);
sqlite3_free(pCsr);
return SQLITE_OK;
}
int nDoclist = pCsr->csr.nDoclist;
char *aDoclist = pCsr->csr.aDoclist;
+ if( pCsr->zStop ){
+ int n = (pCsr->nStop<pCsr->csr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm;
+ int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n);
+ if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){
+ pCsr->isEof = 1;
+ return SQLITE_OK;
+ }
+ }
+
/* Now count the number of documents and positions in the doclist
** in pCsr->csr.aDoclist[]. Store the number of documents in pCsr->nDoc
** and the number of occurrences in pCsr->nOcc. */
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
int rc;
+ int isScan;
+ assert( idxStr==0 );
+ assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0
+ || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
+ || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
+ );
+ isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT);
+
+ /* In case this cursor is being reused, close and zero it. */
+ testcase(pCsr->filter.zTerm);
sqlite3Fts3SegReaderFinish(&pCsr->csr);
- memset(&pCsr->csr, 0, sizeof(Fts3SegReaderCursor));
- pCsr->isEof = 0;
- pCsr->iRowid = 0;
+ sqlite3_free((void *)pCsr->filter.zTerm);
+ memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr);
+
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
+ if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
+
+ if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){
+ const char *zStr = sqlite3_value_text(apVal[0]);
+ if( zStr ){
+ pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
+ pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
+ if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
+ }
+ }
+ if( idxNum&FTS4AUX_LE_CONSTRAINT ){
+ int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
+ pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
+ pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
+ if( pCsr->zStop==0 ) return SQLITE_NOMEM;
+ }
- rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL, 0, 0,1,&pCsr->csr);
+ rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL,
+ pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
+ );
if( rc==SQLITE_OK ){
rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter);
}
int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
+ int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
Fts3SegReader **apSegment = pCsr->apSegment;
int nSegment = pCsr->nSegment;
** Similarly, if this is a search for an exact match, and the first term
** of segment apSegment[0] is not a match, exit early.
*/
- if( pFilter->zTerm ){
+ if( pFilter->zTerm && !isScan ){
if( pCsr->nTerm<pFilter->nTerm
|| (!isPrefix && pCsr->nTerm>pFilter->nTerm)
|| memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm)
Fts3SegFilter filter; /* Segment term filter condition */
Fts3SegReaderCursor csr; /* Cursor to iterate through level(s) */
- rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, &csr);
+ rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, 0, &csr);
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
if( iLevel==FTS3_SEGCURSOR_ALL ){
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-C Merge\sin\sthe\sblocking-checkpoint\senhancement,\sincluding\sthe\snew\nsqlite3_wal_checkpoint_v2()\sinterface\sand\sthe\nPRAGMA\swal_checkpoint(full)\sstatement.
-D 2011-02-02T16:34:08.739
+C Optimize\shandling\sof\sequality\sand\srange\sconstraints\son\sthe\s"term"\scolumn\sof\san\sfts4aux\stable.
+D 2011-02-02T17:30:43.438
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in de6498556d536ae60bb8bb10e8c1ba011448658c
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts3/fts3.c 871600c06569af007c9f7d46786a6a8618140862
+F ext/fts3/fts3.c f93f5d614e92b0221e954ea3bd800f11f6f00191
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
-F ext/fts3/fts3Int.h d833c1df0dc9d1b3c9069981001e6db16bf241ce
-F ext/fts3/fts3_aux.c d7f99095879d68b3b14b48a5370d9c6188566ecc
+F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e
+F ext/fts3/fts3_aux.c 03bc785b04965325ac48a6a6fb0e7babb7751298
F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93
F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
-F ext/fts3/fts3_write.c 58a4d891bd438ee279e82be7f2188b1885f56873
+F ext/fts3/fts3_write.c 8cf14b0e1eee656cda0197e99754f980c216dd9a
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9
F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa
-F test/fts3aux1.test 9cf2e8499a494b92b77aaf1e5e1f50e4c4389549
+F test/fts3aux1.test 709323862e88b3e4be8278151bee2c56087feeed
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test ef36e5ddf9811c9801f52b2988bca1fce7dc8ce8
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P c6d9f7d8c48e1ff405e1c1d98a166974fc829f58 ebf74015f09fe241c1c6902dc8954f2b59ab41ec
-R 0634baae0b43c2b82e8b9905e8c6ee5b
-U drh
-Z d29f42a4b972ec3903a5f796629ef74c
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.10 (Darwin)
-
-iEYEARECAAYFAk1Jh4EACgkQoxKgR168RlEDdgCfTySKHScQj7r5Q2WPZ7harLi+
-uBIAn0+3gnlHjhVd4fzemcV4FGDrMm/Y
-=REwF
------END PGP SIGNATURE-----
+P bac7342c368a7c4f5f2878e08d9581dcbf57dd58
+R c5cbd8730aa7fc425fd495ad36fc71db
+U dan
+Z 56b8613b352790d458992ad5625bb3db
-bac7342c368a7c4f5f2878e08d9581dcbf57dd58
\ No newline at end of file
+386701ded2bcee5309ec9f285d94e6eb1eade193
\ No newline at end of file
SELECT * FROM terms;
} {a 256 1024 b 256 768}
+#-------------------------------------------------------------------------
+# The following tests verify that the fts4aux module uses the full-text
+# index to reduce the number of rows scanned in the following circumstances:
+#
+# * when there is equality comparison against the term column using the
+# BINARY collating sequence.
+#
+# * when there is a range constraint on the term column using the BINARY
+# collating sequence.
+#
+# And also uses the full-text index to optimize ORDER BY clauses of the
+# form "ORDER BY term ASC" or equivalent.
+#
+# Test organization is:
+#
+# fts3aux1-2.1.*: equality constraints.
+# fts3aux1-2.2.*: range constraints.
+# fts3aux1-2.3.*: ORDER BY optimization.
+#
+
+do_execsql_test 2.0 {
+ DROP TABLE t1;
+ DROP TABLE terms;
+
+ CREATE VIRTUAL TABLE x1 USING fts4(x);
+ INSERT INTO x1(x1) VALUES('nodesize=24');
+ CREATE VIRTUAL TABLE terms USING fts4aux(x1);
+
+ INSERT INTO x1 VALUES('braes brag bragged bragger bragging');
+ INSERT INTO x1 VALUES('brags braid braided braiding braids');
+ INSERT INTO x1 VALUES('brain brainchild brained braining brains');
+ INSERT INTO x1 VALUES('brainstem brainstems brainstorm brainstorms');
+}
+
+proc rec {varname x} {
+ global $varname
+ incr $varname
+ return 1
+}
+db func rec rec
+
+# Use EQP to show that the WHERE expression "term='braid'" uses a different
+# index number (1) than "+term='braid'" (0).
+#
+do_execsql_test 2.1.1.1 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term='braid'
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} }
+do_execsql_test 2.1.1.2 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term='braid'
+} {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}}
+
+# Now show that using "term='braid'" means the virtual table returns
+# only 1 row to SQLite, but "+term='braid'" means all 19 are returned.
+#
+do_test 2.1.2.1 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term='braid' }
+ set cnt
+} {1}
+do_test 2.1.2.2 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term='braid' }
+ set cnt
+} {19}
+
+# Similar to the test immediately above, but using a term ("breakfast") that
+# is not featured in the dataset.
+#
+do_test 2.1.3.1 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term='breakfast' }
+ set cnt
+} {0}
+do_test 2.1.3.2 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term='breakfast' }
+ set cnt
+} {19}
+
+do_execsql_test 2.1.4.1 { SELECT * FROM terms WHERE term='braid' } {braid 1 1}
+do_execsql_test 2.1.4.2 { SELECT * FROM terms WHERE +term='braid' } {braid 1 1}
+do_execsql_test 2.1.4.3 { SELECT * FROM terms WHERE term='breakfast' } {}
+do_execsql_test 2.1.4.4 { SELECT * FROM terms WHERE +term='breakfast' } {}
+
+do_execsql_test 2.1.4.5 { SELECT * FROM terms WHERE term='cba' } {}
+do_execsql_test 2.1.4.6 { SELECT * FROM terms WHERE +term='cba' } {}
+do_execsql_test 2.1.4.7 { SELECT * FROM terms WHERE term='abc' } {}
+do_execsql_test 2.1.4.8 { SELECT * FROM terms WHERE +term='abc' } {}
+
+do_execsql_test 2.2.1.1 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term>'brain'
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2: (~0 rows)} }
+do_execsql_test 2.2.1.2 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term>'brain'
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} }
+
+do_execsql_test 2.2.1.3 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term<'brain'
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4: (~0 rows)} }
+do_execsql_test 2.2.1.4 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term<'brain'
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} }
+
+do_execsql_test 2.2.1.5 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term BETWEEN 'brags' AND 'brain'
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6: (~0 rows)} }
+do_execsql_test 2.2.1.6 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term BETWEEN 'brags' AND 'brain'
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} }
+
+do_test 2.2.2.1 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term>'brain' }
+ set cnt
+} {9}
+do_test 2.2.2.2 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term>'brain' }
+ set cnt
+} {19}
+do_execsql_test 2.2.2.3 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND term>'brain'
+} {
+ brainchild 1 1 brained 1 1 braining 1 1 brains 1 1
+ brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1
+}
+do_execsql_test 2.2.2.4 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND +term>'brain'
+} {
+ brainchild 1 1 brained 1 1 braining 1 1 brains 1 1
+ brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1
+}
+do_execsql_test 2.2.2.5 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND term>='brain'
+} {
+ brain 1 1
+ brainchild 1 1 brained 1 1 braining 1 1 brains 1 1
+ brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1
+}
+do_execsql_test 2.2.2.6 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND +term>='brain'
+} {
+ brain 1 1
+ brainchild 1 1 brained 1 1 braining 1 1 brains 1 1
+ brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1
+}
+
+do_execsql_test 2.2.2.7 {
+ SELECT * FROM terms WHERE term>='abc'
+} {
+ braes 1 1 brag 1 1 bragged 1 1 bragger 1 1
+ bragging 1 1 brags 1 1 braid 1 1 braided 1 1
+ braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1
+ brained 1 1 braining 1 1 brains 1 1 brainstem 1 1
+ brainstems 1 1 brainstorm 1 1 brainstorms 1 1
+}
+do_execsql_test 2.2.2.8 {
+ SELECT * FROM terms WHERE +term>='abc'
+} {
+ braes 1 1 brag 1 1 bragged 1 1 bragger 1 1
+ bragging 1 1 brags 1 1 braid 1 1 braided 1 1
+ braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1
+ brained 1 1 braining 1 1 brains 1 1 brainstem 1 1
+ brainstems 1 1 brainstorm 1 1 brainstorms 1 1
+}
+
+do_execsql_test 2.2.2.9 {
+ SELECT * FROM terms WHERE term>='brainstorms'
+} {brainstorms 1 1}
+do_execsql_test 2.2.2.10 {
+ SELECT * FROM terms WHERE term>='brainstorms'
+} {brainstorms 1 1}
+do_execsql_test 2.2.2.11 { SELECT * FROM terms WHERE term>'brainstorms' } {}
+do_execsql_test 2.2.2.12 { SELECT * FROM terms WHERE term>'brainstorms' } {}
+
+do_execsql_test 2.2.2.13 { SELECT * FROM terms WHERE term>'cba' } {}
+do_execsql_test 2.2.2.14 { SELECT * FROM terms WHERE term>'cba' } {}
+
+do_test 2.2.3.1 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term<'brain' }
+ set cnt
+} {11}
+do_test 2.2.3.2 {
+ set cnt 0
+ execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term<'brain' }
+ set cnt
+} {19}
+do_execsql_test 2.2.3.3 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND term<'brain'
+} {
+ braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1
+ brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1
+}
+do_execsql_test 2.2.3.4 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND +term<'brain'
+} {
+ braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1
+ brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1
+}
+do_execsql_test 2.2.3.5 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND term<='brain'
+} {
+ braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1
+ brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1
+ brain 1 1
+}
+do_execsql_test 2.2.3.6 {
+ SELECT * FROM terms WHERE rec('cnt', term) AND +term<='brain'
+} {
+ braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1
+ brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1
+ brain 1 1
+}
+
+do_test 2.2.4.1 {
+ set cnt 0
+ execsql {
+ SELECT * FROM terms
+ WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain'
+ }
+ set cnt
+} {6}
+do_test 2.2.4.2 {
+ set cnt 0
+ execsql {
+ SELECT * FROM terms
+ WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain'
+ }
+ set cnt
+} {19}
+do_execsql_test 2.2.4.3 {
+ SELECT * FROM terms
+ WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain'
+} {
+ brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1
+}
+do_execsql_test 2.2.4.4 {
+ SELECT * FROM terms
+ WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain'
+} {
+ brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1
+}
+do_execsql_test 2.2.4.5 {
+ SELECT * FROM terms
+ WHERE rec('cnt', term) AND term > 'brags' AND term < 'brain'
+} {
+ braid 1 1 braided 1 1 braiding 1 1 braids 1 1
+}
+do_execsql_test 2.2.4.6 {
+ SELECT * FROM terms
+ WHERE rec('cnt', term) AND +term > 'brags' AND +term < 'brain'
+} {
+ braid 1 1 braided 1 1 braiding 1 1 braids 1 1
+}
+
+do_execsql_test 2.3.1.1 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms ORDER BY term ASC;
+} {
+ 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}
+}
+do_execsql_test 2.3.1.2 {
+ EXPLAIN QUERY PLAN SELECT * FROM terms ORDER BY term DESC;
+} {
+ 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}
+ 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+}
+
finish_test