Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch fts4-docid-range-constraints Excluding Merge-Ins
This is equivalent to a diff from 500c5932fe to 24aa20da22
2013-10-03
| ||
20:41 | Have FTS take advantage of "docid<?" constraints when they are present. Extend the FTS "incremental doclist" optimization so that it is used for tokens within multi-token phrases. (check-in: baf8ce5916 user: dan tags: trunk) | |
20:28 | Merge latest trunk changes. (Closed-Leaf check-in: 24aa20da22 user: dan tags: fts4-docid-range-constraints) | |
19:27 | Allow FTS4 multi-token phrases to use a combination of in-memory and incrementally loaded doclists. This allows phrases to (partially) benefit from incremental doclists without disabling the deferred token optimization. (check-in: f6819c5f33 user: dan tags: fts4-docid-range-constraints) | |
19:21 | Experimental branch allowing different postulated scan rates for each index. (check-in: d59d97b0a8 user: drh tags: index-scan-rate) | |
15:39 | The sqlite3FixInit() routine cannot fail. So change the return type from "int" to "void". (check-in: 500c5932fe user: drh tags: trunk) | |
14:08 | Rework the PRAGMA implementation to only call sqlite3ReadSchema() from a single place, based on a flag in the pragma table, rather than separately from each case which needs the schema. (check-in: 8338232a11 user: drh tags: trunk) | |
Changes to ext/fts3/fts3.c.
︙ | ︙ | |||
1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 | ** 2. Full-text search using a MATCH operator on a non-docid column. ** 3. Linear scan of %_content table. */ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ Fts3Table *p = (Fts3Table *)pVTab; int i; /* Iterator variable */ int iCons = -1; /* Index of constraint to use */ int iLangidCons = -1; /* Index of langid=x constraint, if present */ /* By default use a full table scan. This is an expensive option, ** so search through the constraints to see if a more efficient ** strategy is possible. */ pInfo->idxNum = FTS3_FULLSCAN_SEARCH; pInfo->estimatedCost = 5000000; for(i=0; i<pInfo->nConstraint; i++){ struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; if( pCons->usable==0 ) continue; /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */ | > > > > > > > < | < < | 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 | ** 2. Full-text search using a MATCH operator on a non-docid column. ** 3. Linear scan of %_content table. */ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ Fts3Table *p = (Fts3Table *)pVTab; int i; /* Iterator variable */ int iCons = -1; /* Index of constraint to use */ int iLangidCons = -1; /* Index of langid=x constraint, if present */ int iDocidGe = -1; /* Index of docid>=x constraint, if present */ int iDocidLe = -1; /* Index of docid<=x constraint, if present */ int iIdx; /* By default use a full table scan. This is an expensive option, ** so search through the constraints to see if a more efficient ** strategy is possible. */ pInfo->idxNum = FTS3_FULLSCAN_SEARCH; pInfo->estimatedCost = 5000000; for(i=0; i<pInfo->nConstraint; i++){ int bDocid; /* True if this constraint is on docid */ struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; if( pCons->usable==0 ) continue; bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1); /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */ if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){ pInfo->idxNum = FTS3_DOCID_SEARCH; pInfo->estimatedCost = 1.0; iCons = i; } /* A MATCH constraint. Use a full-text search. ** |
︙ | ︙ | |||
1498 1499 1500 1501 1502 1503 1504 | /* Equality constraint on the langid column */ if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && pCons->iColumn==p->nColumn + 2 ){ iLangidCons = i; } | | > > > > > > > > > > > > > > > | > | > > > > > > > > | 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 | /* Equality constraint on the langid column */ if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && pCons->iColumn==p->nColumn + 2 ){ iLangidCons = i; } if( bDocid ){ switch( pCons->op ){ case SQLITE_INDEX_CONSTRAINT_GE: case SQLITE_INDEX_CONSTRAINT_GT: iDocidGe = i; break; case SQLITE_INDEX_CONSTRAINT_LE: case SQLITE_INDEX_CONSTRAINT_LT: iDocidLe = i; break; } } } iIdx = 1; if( iCons>=0 ){ pInfo->aConstraintUsage[iCons].argvIndex = iIdx++; pInfo->aConstraintUsage[iCons].omit = 1; } if( iLangidCons>=0 ){ pInfo->idxNum |= FTS3_HAVE_LANGID; pInfo->aConstraintUsage[iLangidCons].argvIndex = iIdx++; } if( iDocidGe>=0 ){ pInfo->idxNum |= FTS3_HAVE_DOCID_GE; pInfo->aConstraintUsage[iDocidGe].argvIndex = iIdx++; } if( iDocidLe>=0 ){ pInfo->idxNum |= FTS3_HAVE_DOCID_LE; pInfo->aConstraintUsage[iDocidLe].argvIndex = iIdx++; } /* Regardless of the strategy selected, FTS can deliver rows in rowid (or ** docid) order. Both ascending and descending are possible. */ if( pInfo->nOrderBy==1 ){ struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0]; |
︙ | ︙ | |||
2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 | } }else{ rc = fts3EvalNext((Fts3Cursor *)pCursor); } assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); return rc; } /* ** This is the xFilter interface for the virtual table. See ** the virtual table xFilter method documentation for additional ** information. ** ** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 | } }else{ rc = fts3EvalNext((Fts3Cursor *)pCursor); } assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); return rc; } /* ** The following are copied from sqliteInt.h. ** ** Constants for the largest and smallest possible 64-bit signed integers. ** These macros are designed to work correctly on both 32-bit and 64-bit ** compilers. */ #ifndef SQLITE_AMALGAMATION # define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) # define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) #endif /* ** If the numeric type of argument pVal is "integer", then return it ** converted to a 64-bit signed integer. Otherwise, return a copy of ** the second parameter, iDefault. */ static sqlite3_int64 fts3DocidRange(sqlite3_value *pVal, i64 iDefault){ if( pVal ){ int eType = sqlite3_value_numeric_type(pVal); if( eType==SQLITE_INTEGER ){ return sqlite3_value_int64(pVal); } } return iDefault; } /* ** This is the xFilter interface for the virtual table. See ** the virtual table xFilter method documentation for additional ** information. ** ** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against |
︙ | ︙ | |||
2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 | int idxNum, /* Strategy index */ const char *idxStr, /* Unused */ int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ int rc; char *zSql; /* SQL statement used to access %_content */ Fts3Table *p = (Fts3Table *)pCursor->pVtab; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(nVal); | > > > > > > > > | > | > > | > > > | > > > > | | | | | | | 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 | int idxNum, /* Strategy index */ const char *idxStr, /* Unused */ int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ int rc; char *zSql; /* SQL statement used to access %_content */ int eSearch;; Fts3Table *p = (Fts3Table *)pCursor->pVtab; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; sqlite3_value *pCons = 0; /* The MATCH or rowid constraint, if any */ sqlite3_value *pLangid = 0; /* The "langid = ?" constraint, if any */ sqlite3_value *pDocidGe = 0; /* The "docid >= ?" constraint, if any */ sqlite3_value *pDocidLe = 0; /* The "docid <= ?" constraint, if any */ int iIdx; UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(nVal); eSearch = (idxNum & 0x0000FFFF); assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); assert( p->pSegments==0 ); /* Collect arguments into local variables */ iIdx = 0; if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++]; if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++]; if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++]; if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++]; assert( iIdx==nVal ); /* In case the cursor has been used before, clear it now. */ sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr->aDoclist); sqlite3Fts3ExprFree(pCsr->pExpr); memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); /* Set the lower and upper bounds on docids to return */ pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64); pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64); if( idxStr ){ pCsr->bDesc = (idxStr[0]=='D'); }else{ pCsr->bDesc = p->bDescIdx; } pCsr->eSearch = (i16)eSearch; if( eSearch!=FTS3_DOCID_SEARCH && eSearch!=FTS3_FULLSCAN_SEARCH ){ int iCol = eSearch-FTS3_FULLTEXT_SEARCH; const char *zQuery = (const char *)sqlite3_value_text(pCons); if( zQuery==0 && sqlite3_value_type(pCons)!=SQLITE_NULL ){ return SQLITE_NOMEM; } pCsr->iLangid = 0; if( pLangid ) pCsr->iLangid = sqlite3_value_int(pLangid); assert( p->base.zErrMsg==0 ); rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid, p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr, &p->base.zErrMsg ); if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
3033 3034 3035 3036 3037 3038 3039 | } /* Compile a SELECT statement for this cursor. For a full-table-scan, the ** statement loops through all rows of the %_content table. For a ** full-text query or docid lookup, the statement retrieves a single ** row by docid. */ | | | | | 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 | } /* Compile a SELECT statement for this cursor. For a full-table-scan, the ** statement loops through all rows of the %_content table. For a ** full-text query or docid lookup, the statement retrieves a single ** row by docid. */ if( eSearch==FTS3_FULLSCAN_SEARCH ){ zSql = sqlite3_mprintf( "SELECT %s ORDER BY rowid %s", p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") ); if( zSql ){ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); sqlite3_free(zSql); }else{ rc = SQLITE_NOMEM; } }else if( eSearch==FTS3_DOCID_SEARCH ){ rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt); if( rc==SQLITE_OK ){ rc = sqlite3_bind_value(pCsr->pStmt, 1, pCons); } } if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); } |
︙ | ︙ | |||
3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 | sqlite3_free(aPoslist); } } return SQLITE_OK; } /* ** This function is called for each Fts3Phrase in a full-text query ** expression to initialize the mechanism for returning rows. Once this ** function has been called successfully on an Fts3Phrase, it may be ** used with fts3EvalPhraseNext() to iterate through the matching docids. ** ** If parameter bOptOk is true, then the phrase may (or may not) use the ** incremental loading strategy. Otherwise, the entire doclist is loaded into ** memory within this call. ** ** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code. */ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){ | > > > > > > < < > > > > > > > > > | | | > | > > > > | > > > > | | > > > > | < < | > > | 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 | sqlite3_free(aPoslist); } } return SQLITE_OK; } /* ** Maximum number of tokens a phrase may have to be considered for the ** incremental doclists strategy. */ #define MAX_INCR_PHRASE_TOKENS 4 /* ** This function is called for each Fts3Phrase in a full-text query ** expression to initialize the mechanism for returning rows. Once this ** function has been called successfully on an Fts3Phrase, it may be ** used with fts3EvalPhraseNext() to iterate through the matching docids. ** ** If parameter bOptOk is true, then the phrase may (or may not) use the ** incremental loading strategy. Otherwise, the entire doclist is loaded into ** memory within this call. ** ** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code. */ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; int rc = SQLITE_OK; /* Error code */ int i; /* Determine if doclists may be loaded from disk incrementally. This is ** possible if the bOptOk argument is true, the FTS doclists will be ** scanned in forward order, and the phrase consists of ** MAX_INCR_PHRASE_TOKENS or fewer tokens, none of which are are "^first" ** tokens or prefix tokens that cannot use a prefix-index. */ int bHaveIncr = 0; int bIncrOk = (bOptOk && pCsr->bDesc==pTab->bDescIdx && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0 && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0 #ifdef SQLITE_TEST && pTab->bNoIncrDoclist==0 #endif ); for(i=0; bIncrOk==1 && i<p->nToken; i++){ Fts3PhraseToken *pToken = &p->aToken[i]; if( pToken->bFirst || (pToken->pSegcsr!=0 && !pToken->pSegcsr->bLookup) ){ bIncrOk = 0; } if( pToken->pSegcsr ) bHaveIncr = 1; } if( bIncrOk && bHaveIncr ){ /* Use the incremental approach. */ int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn); for(i=0; rc==SQLITE_OK && i<p->nToken; i++){ Fts3PhraseToken *pToken = &p->aToken[i]; Fts3MultiSegReader *pSegcsr = pToken->pSegcsr; if( pSegcsr ){ rc = sqlite3Fts3MsrIncrStart(pTab, pSegcsr, iCol, pToken->z, pToken->n); } } p->bIncr = 1; }else{ /* Load the full doclist for the phrase into memory. */ rc = fts3EvalPhraseLoad(pCsr, p); p->bIncr = 0; } assert( rc!=SQLITE_OK || p->nToken<1 || p->aToken[0].pSegcsr==0 || p->bIncr ); |
︙ | ︙ | |||
4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 | p += sqlite3Fts3GetVarint(p, &iVar); *piDocid += ((bDescIdx ? -1 : 1) * iVar); } } *ppIter = p; } /* ** Attempt to move the phrase iterator to point to the next matching docid. ** If an error occurs, return an SQLite error code. Otherwise, return ** SQLITE_OK. ** ** If there is no "next" entry and no error occurs, then *pbEof is set to ** 1 before returning. Otherwise, if no error occurs and the iterator is ** successfully advanced, *pbEof is set to 0. */ static int fts3EvalPhraseNext( Fts3Cursor *pCsr, /* FTS Cursor handle */ Fts3Phrase *p, /* Phrase object to advance to next docid */ u8 *pbEof /* OUT: Set to 1 if EOF */ ){ int rc = SQLITE_OK; Fts3Doclist *pDL = &p->doclist; Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; if( p->bIncr ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 | p += sqlite3Fts3GetVarint(p, &iVar); *piDocid += ((bDescIdx ? -1 : 1) * iVar); } } *ppIter = p; } /* ** Advance the iterator pDL to the next entry in pDL->aAll/nAll. Set *pbEof ** to true if EOF is reached. */ static void fts3EvalDlPhraseNext( Fts3Table *pTab, Fts3Doclist *pDL, u8 *pbEof ){ char *pIter; /* Used to iterate through aAll */ char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */ if( pDL->pNextDocid ){ pIter = pDL->pNextDocid; }else{ pIter = pDL->aAll; } if( pIter>=pEnd ){ /* We have already reached the end of this doclist. EOF. */ *pbEof = 1; }else{ sqlite3_int64 iDelta; pIter += sqlite3Fts3GetVarint(pIter, &iDelta); if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){ pDL->iDocid += iDelta; }else{ pDL->iDocid -= iDelta; } pDL->pList = pIter; fts3PoslistCopy(0, &pIter); pDL->nList = (int)(pIter - pDL->pList); /* pIter now points just past the 0x00 that terminates the position- ** list for document pDL->iDocid. However, if this position-list was ** edited in place by fts3EvalNearTrim(), then pIter may not actually ** point to the start of the next docid value. The following line deals ** with this case by advancing pIter past the zero-padding added by ** fts3EvalNearTrim(). */ while( pIter<pEnd && *pIter==0 ) pIter++; pDL->pNextDocid = pIter; assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter ); *pbEof = 0; } } /* ** Helper type used by fts3EvalIncrPhraseNext() and incrPhraseTokenNext(). */ typedef struct TokenDoclist TokenDoclist; struct TokenDoclist { int bIgnore; sqlite3_int64 iDocid; char *pList; int nList; }; /* ** Token pToken is an incrementally loaded token that is part of a ** multi-token phrase. Advance it to the next matching document in the ** database and populate output variable *p with the details of the new ** entry. Or, if the iterator has reached EOF, set *pbEof to true. ** ** If an error occurs, return an SQLite error code. Otherwise, return ** SQLITE_OK. */ static int incrPhraseTokenNext( Fts3Table *pTab, /* Virtual table handle */ Fts3Phrase *pPhrase, /* Phrase to advance token of */ int iToken, /* Specific token to advance */ TokenDoclist *p, /* OUT: Docid and doclist for new entry */ u8 *pbEof /* OUT: True if iterator is at EOF */ ){ int rc = SQLITE_OK; if( pPhrase->iDoclistToken==iToken ){ assert( p->bIgnore==0 ); assert( pPhrase->aToken[iToken].pSegcsr==0 ); fts3EvalDlPhraseNext(pTab, &pPhrase->doclist, pbEof); p->pList = pPhrase->doclist.pList; p->nList = pPhrase->doclist.nList; p->iDocid = pPhrase->doclist.iDocid; }else{ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; assert( pToken->pDeferred==0 ); assert( pToken->pSegcsr || pPhrase->iDoclistToken>=0 ); if( pToken->pSegcsr ){ assert( p->bIgnore==0 ); rc = sqlite3Fts3MsrIncrNext( pTab, pToken->pSegcsr, &p->iDocid, &p->pList, &p->nList ); if( p->pList==0 ) *pbEof = 1; }else{ p->bIgnore = 1; } } return rc; } /* ** The phrase iterator passed as the second argument: ** ** * features at least one token that uses an incremental doclist, and ** ** * does not contain any deferred tokens. ** ** Advance it to the next matching documnent in the database and populate ** the Fts3Doclist.pList and nList fields. ** ** If there is no "next" entry and no error occurs, then *pbEof is set to ** 1 before returning. Otherwise, if no error occurs and the iterator is ** successfully advanced, *pbEof is set to 0. ** ** If an error occurs, return an SQLite error code. Otherwise, return ** SQLITE_OK. */ static int fts3EvalIncrPhraseNext( Fts3Cursor *pCsr, /* FTS Cursor handle */ Fts3Phrase *p, /* Phrase object to advance to next docid */ u8 *pbEof /* OUT: Set to 1 if EOF */ ){ int rc = SQLITE_OK; Fts3Doclist *pDL = &p->doclist; Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; u8 bEof = 0; /* This is only called if it is guaranteed that the phrase has at least ** one incremental token. In which case the bIncr flag is set. */ assert( p->bIncr==1 ); if( p->nToken==1 && p->bIncr ){ rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr, &pDL->iDocid, &pDL->pList, &pDL->nList ); if( pDL->pList==0 ) bEof = 1; }else{ int bDescDoclist = pCsr->bDesc; struct TokenDoclist a[MAX_INCR_PHRASE_TOKENS]; memset(a, 0, sizeof(a)); assert( p->nToken<=MAX_INCR_PHRASE_TOKENS ); assert( p->iDoclistToken<MAX_INCR_PHRASE_TOKENS ); while( bEof==0 ){ int bMaxSet = 0; sqlite3_int64 iMax; /* Largest docid for all iterators */ int i; /* Used to iterate through tokens */ /* Advance the iterator for each token in the phrase once. */ for(i=0; rc==SQLITE_OK && i<p->nToken; i++){ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof); if( a[i].bIgnore==0 && (bMaxSet==0 || DOCID_CMP(iMax, a[i].iDocid)<0) ){ iMax = a[i].iDocid; bMaxSet = 1; } } assert( rc!=SQLITE_OK || a[p->nToken-1].bIgnore==0 ); assert( rc!=SQLITE_OK || bMaxSet ); /* Keep advancing iterators until they all point to the same document */ for(i=0; i<p->nToken; i++){ while( rc==SQLITE_OK && bEof==0 && a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0 ){ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof); if( DOCID_CMP(a[i].iDocid, iMax)>0 ){ iMax = a[i].iDocid; i = 0; } } } /* Check if the current entries really are a phrase match */ if( bEof==0 ){ int nList = 0; int nByte = a[p->nToken-1].nList; char *aDoclist = sqlite3_malloc(nByte+1); if( !aDoclist ) return SQLITE_NOMEM; memcpy(aDoclist, a[p->nToken-1].pList, nByte+1); for(i=0; i<(p->nToken-1); i++){ if( a[i].bIgnore==0 ){ char *pL = a[i].pList; char *pR = aDoclist; char *pOut = aDoclist; int nDist = p->nToken-1-i; int res = fts3PoslistPhraseMerge(&pOut, nDist, 0, 1, &pL, &pR); if( res==0 ) break; nList = (pOut - aDoclist); } } if( i==(p->nToken-1) ){ pDL->iDocid = iMax; pDL->pList = aDoclist; pDL->nList = nList; pDL->bFreeList = 1; break; } sqlite3_free(aDoclist); } } } *pbEof = bEof; return rc; } /* ** Attempt to move the phrase iterator to point to the next matching docid. ** If an error occurs, return an SQLite error code. Otherwise, return ** SQLITE_OK. ** ** If there is no "next" entry and no error occurs, then *pbEof is set to ** 1 before returning. Otherwise, if no error occurs and the iterator is ** successfully advanced, *pbEof is set to 0. */ static int fts3EvalPhraseNext( Fts3Cursor *pCsr, /* FTS Cursor handle */ Fts3Phrase *p, /* Phrase object to advance to next docid */ u8 *pbEof /* OUT: Set to 1 if EOF */ ){ int rc = SQLITE_OK; Fts3Doclist *pDL = &p->doclist; Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; if( p->bIncr ){ rc = fts3EvalIncrPhraseNext(pCsr, p, pbEof); }else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){ sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll, &pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof ); pDL->pList = pDL->pNextDocid; }else{ fts3EvalDlPhraseNext(pTab, pDL, pbEof); } return rc; } /* ** |
︙ | ︙ | |||
4168 4169 4170 4171 4172 4173 4174 | ** ** If an error occurs within this function, *pRc is set to an SQLite error ** code before returning. */ static void fts3EvalStartReaders( Fts3Cursor *pCsr, /* FTS Cursor handle */ Fts3Expr *pExpr, /* Expression to initialize phrases in */ | < | | | | 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 | ** ** If an error occurs within this function, *pRc is set to an SQLite error ** code before returning. */ static void fts3EvalStartReaders( Fts3Cursor *pCsr, /* FTS Cursor handle */ Fts3Expr *pExpr, /* Expression to initialize phrases in */ int *pRc /* IN/OUT: Error code */ ){ if( pExpr && SQLITE_OK==*pRc ){ if( pExpr->eType==FTSQUERY_PHRASE ){ int i; int nToken = pExpr->pPhrase->nToken; for(i=0; i<nToken; i++){ if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break; } pExpr->bDeferred = (i==nToken); *pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase); }else{ fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc); fts3EvalStartReaders(pCsr, pExpr->pRight, pRc); pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred); } } } /* ** An array of the following structures is assembled as part of the process |
︙ | ︙ | |||
4424 4425 4426 4427 4428 4429 4430 | pToken->pSegcsr = 0; }else{ /* Set nLoad4 to the value of (4^nOther) for the next iteration of the ** for-loop. Except, limit the value to 2^24 to prevent it from ** overflowing the 32-bit integer it is stored in. */ if( ii<12 ) nLoad4 = nLoad4*4; | | | 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 | pToken->pSegcsr = 0; }else{ /* Set nLoad4 to the value of (4^nOther) for the next iteration of the ** for-loop. Except, limit the value to 2^24 to prevent it from ** overflowing the 32-bit integer it is stored in. */ if( ii<12 ) nLoad4 = nLoad4*4; if( ii==0 || (pTC->pPhrase->nToken>1 && ii!=nToken-1) ){ /* Either this is the cheapest token in the entire query, or it is ** part of a multi-token phrase. Either way, the entire doclist will ** (eventually) be loaded into memory. It may as well be now. */ Fts3PhraseToken *pToken = pTC->pToken; int nList = 0; char *pList = 0; rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList); |
︙ | ︙ | |||
4504 4505 4506 4507 4508 4509 4510 | } sqlite3_free(aTC); } } #endif | | | 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 | } sqlite3_free(aTC); } } #endif fts3EvalStartReaders(pCsr, pCsr->pExpr, &rc); return rc; } /* ** Invalidate the current position list for phrase pPhrase. */ static void fts3EvalInvalidatePoslist(Fts3Phrase *pPhrase){ |
︙ | ︙ | |||
4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 | fts3EvalNextRow(pCsr, pExpr, &rc); pCsr->isEof = pExpr->bEof; pCsr->isRequireSeek = 1; pCsr->isMatchinfoNeeded = 1; pCsr->iPrevId = pExpr->iDocid; }while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) ); } return rc; } /* ** Restart interation for expression pExpr so that the next call to ** fts3EvalNext() visits the first row. Do not allow incremental ** loading or merging of phrase doclists for this iteration. | > > > > > > > > > > | 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 | fts3EvalNextRow(pCsr, pExpr, &rc); pCsr->isEof = pExpr->bEof; pCsr->isRequireSeek = 1; pCsr->isMatchinfoNeeded = 1; pCsr->iPrevId = pExpr->iDocid; }while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) ); } /* Check if the cursor is past the end of the docid range specified ** by Fts3Cursor.iMinDocid/iMaxDocid. If so, set the EOF flag. */ if( rc==SQLITE_OK && ( (pCsr->bDesc==0 && pCsr->iPrevId>pCsr->iMaxDocid) || (pCsr->bDesc!=0 && pCsr->iPrevId<pCsr->iMinDocid) )){ pCsr->isEof = 1; } return rc; } /* ** Restart interation for expression pExpr so that the next call to ** fts3EvalNext() visits the first row. Do not allow incremental ** loading or merging of phrase doclists for this iteration. |
︙ | ︙ | |||
5010 5011 5012 5013 5014 5015 5016 | ){ if( pExpr && *pRc==SQLITE_OK ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase ){ fts3EvalInvalidatePoslist(pPhrase); if( pPhrase->bIncr ){ | > | | > > | < | | > > | 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 | ){ if( pExpr && *pRc==SQLITE_OK ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase ){ fts3EvalInvalidatePoslist(pPhrase); if( pPhrase->bIncr ){ int i; for(i=0; i<pPhrase->nToken; i++){ Fts3PhraseToken *pToken = &pPhrase->aToken[i]; assert( pToken->pDeferred==0 ); if( pToken->pSegcsr ){ sqlite3Fts3MsrIncrRestart(pToken->pSegcsr); } } *pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase); } pPhrase->doclist.pNextDocid = 0; pPhrase->doclist.iDocid = 0; } pExpr->iDocid = 0; pExpr->bEof = 0; pExpr->bStart = 0; |
︙ | ︙ |
Changes to ext/fts3/fts3Int.h.
︙ | ︙ | |||
263 264 265 266 267 268 269 270 271 272 273 274 275 276 | ** methods of the virtual table are called at appropriate times. These ** values do not contribute to FTS functionality; they are used for ** verifying the operation of the SQLite core. */ int inTransaction; /* True after xBegin but before xCommit/xRollback */ int mxSavepoint; /* Largest valid xSavepoint integer */ #endif }; /* ** When the core wants to read from the virtual table, it creates a ** virtual table cursor (an instance of the following structure) using ** the xOpen method. Cursors are destroyed using the xClose method. */ | > > > > > > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | ** methods of the virtual table are called at appropriate times. These ** values do not contribute to FTS functionality; they are used for ** verifying the operation of the SQLite core. */ int inTransaction; /* True after xBegin but before xCommit/xRollback */ int mxSavepoint; /* Largest valid xSavepoint integer */ #endif #ifdef SQLITE_TEST /* True to disable the incremental doclist optimization. This is controled ** by special insert command 'test-no-incr-doclist'. */ int bNoIncrDoclist; #endif }; /* ** When the core wants to read from the virtual table, it creates a ** virtual table cursor (an instance of the following structure) using ** the xOpen method. Cursors are destroyed using the xClose method. */ |
︙ | ︙ | |||
288 289 290 291 292 293 294 | char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ u8 bDesc; /* True to sort in descending order */ int eEvalmode; /* An FTS3_EVAL_XX constant */ int nRowAvg; /* Average size of database rows, in pages */ sqlite3_int64 nDoc; /* Documents in table */ | | > | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ u8 bDesc; /* True to sort in descending order */ int eEvalmode; /* An FTS3_EVAL_XX constant */ int nRowAvg; /* Average size of database rows, in pages */ sqlite3_int64 nDoc; /* Documents in table */ i64 iMinDocid; /* Minimum docid to return */ i64 iMaxDocid; /* Maximum docid to return */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ int nMatchinfo; /* Number of elements in aMatchinfo[] */ char *zMatchinfo; /* Matchinfo specification */ }; #define FTS3_EVAL_FILTER 0 |
︙ | ︙ | |||
318 319 320 321 322 323 324 325 326 327 328 329 330 331 | ** indicating that all columns should be searched, ** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4. */ #define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ struct Fts3Doclist { char *aAll; /* Array containing doclist (or NULL) */ int nAll; /* Size of a[] in bytes */ char *pNextDocid; /* Pointer to next docid */ sqlite3_int64 iDocid; /* Current docid (if pList!=0) */ | > > > > > > > > > | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | ** indicating that all columns should be searched, ** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4. */ #define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ /* ** The lower 16-bits of the sqlite3_index_info.idxNum value set by ** the xBestIndex() method contains the Fts3Cursor.eSearch value described ** above. The upper 16-bits contain a combination of the following ** bits, used to describe extra constraints on full-text searches. */ #define FTS3_HAVE_LANGID 0x00010000 /* languageid=? */ #define FTS3_HAVE_DOCID_GE 0x00020000 /* docid>=? */ #define FTS3_HAVE_DOCID_LE 0x00040000 /* docid<=? */ struct Fts3Doclist { char *aAll; /* Array containing doclist (or NULL) */ int nAll; /* Size of a[] in bytes */ char *pNextDocid; /* Pointer to next docid */ sqlite3_int64 iDocid; /* Current docid (if pList!=0) */ |
︙ | ︙ |
Changes to ext/fts3/fts3_write.c.
︙ | ︙ | |||
5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 | rc = fts3DoAutoincrmerge(p, &zVal[10]); #ifdef SQLITE_TEST }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ p->nNodeSize = atoi(&zVal[9]); rc = SQLITE_OK; }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ p->nMaxPendingData = atoi(&zVal[11]); rc = SQLITE_OK; #endif }else{ rc = SQLITE_ERROR; } return rc; | > > > | 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 | rc = fts3DoAutoincrmerge(p, &zVal[10]); #ifdef SQLITE_TEST }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ p->nNodeSize = atoi(&zVal[9]); rc = SQLITE_OK; }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ p->nMaxPendingData = atoi(&zVal[11]); rc = SQLITE_OK; }else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){ p->bNoIncrDoclist = atoi(&zVal[21]); rc = SQLITE_OK; #endif }else{ rc = SQLITE_ERROR; } return rc; |
︙ | ︙ |
Added test/fts4docid.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | # 2012 March 26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #************************************************************************* # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/fts3_common.tcl set ::testprefix fts4docid # If SQLITE_ENABLE_FTS3 is defined, omit this file. ifcapable !fts3 { finish_test return } # Initialize a table with pseudo-randomly generated data. # do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts4; } do_test 1.1 { foreach {docid content} { 0 {F N K B T I K V B A} 1 {D M J E S P H E L O} 2 {W U T Q T Q T L H G} 3 {D W H M B R S Z B K} 4 {F Q I N P Q J L Z D} 5 {J O Q E Y A O E L B} 6 {O V R A C R K C Y H} 7 {Z J H T Q Q O R A G} 8 {L K J W G D Y W B M} 9 {K E Y I A Q R Q T S} 10 {N P H Y Z M R T I C} 11 {E X H O I S E S Z F} 12 {B Y Q T J X C L L J} 13 {Q D C U U A Q E Z U} 14 {S I T C J R X S J M} 15 {M X M K E X L H Q Y} 16 {O W E I C H U Y S Y} 17 {P V V E M T H C C S} 18 {L Y A M I E N M X O} 19 {S Y R U L S Q Y F P} 20 {U J S T T J J S V X} 21 {T E I W P O V A A P} 22 {W D K H D H F G O J} 23 {T X Y P G M J U I L} 24 {F V X E B C N B K W} 25 {E B A Y N N T Z I C} 26 {G E E B C P U D H G} 27 {J D J K N S B Q T M} 28 {Q T G M D O D Y V G} 29 {P X W I W V P W Z G} } { execsql { INSERT INTO t1(docid, content) VALUES($docid, $content) } } } {} # Quick test regarding affinites and the docid/rowid column. do_execsql_test 2.1.1 { SELECT docid FROM t1 WHERE docid = 5 } {5} do_execsql_test 2.1.2 { SELECT docid FROM t1 WHERE docid = '5' } {5} do_execsql_test 2.1.3 { SELECT docid FROM t1 WHERE docid = +5 } {5} do_execsql_test 2.1.4 { SELECT docid FROM t1 WHERE docid = +'5' } {5} do_execsql_test 2.1.5 { SELECT docid FROM t1 WHERE docid < 5 } {0 1 2 3 4} do_execsql_test 2.1.6 { SELECT docid FROM t1 WHERE docid < '5' } {0 1 2 3 4} do_execsql_test 2.2.1 { SELECT rowid FROM t1 WHERE rowid = 5 } {5} do_execsql_test 2.2.2 { SELECT rowid FROM t1 WHERE rowid = '5' } {5} do_execsql_test 2.2.3 { SELECT rowid FROM t1 WHERE rowid = +5 } {5} do_execsql_test 2.2.4 { SELECT rowid FROM t1 WHERE rowid = +'5' } {5} do_execsql_test 2.2.5 { SELECT rowid FROM t1 WHERE rowid < 5 } {0 1 2 3 4} do_execsql_test 2.2.6 { SELECT rowid FROM t1 WHERE rowid < '5' } {0 1 2 3 4} #------------------------------------------------------------------------- # Now test a bunch of full-text queries featuring range constraints on # the docid field. Each query is run so that the range constraint: # # * is on the docid field, # * is on the docid field with a unary +, # * is on the rowid field, # * is on the rowid field with a unary +. # # Queries are run with both "ORDER BY docid DESC" and "ORDER BY docid ASC" # clauses. # foreach {tn where result} { 1 {WHERE t1 MATCH 'O' AND xxx < 17} {1 5 6 7 11 16} 2 {WHERE t1 MATCH 'O' AND xxx < 4123456789123456} {1 5 6 7 11 16 18 21 22 28} 3 {WHERE t1 MATCH 'O' AND xxx < 1} {} 4 {WHERE t1 MATCH 'O' AND xxx < -4123456789123456} {} 5 {WHERE t1 MATCH 'O' AND xxx > 17} {18 21 22 28} 6 {WHERE t1 MATCH 'O' AND xxx > 4123456789123456} {} 7 {WHERE t1 MATCH 'O' AND xxx > 1} {5 6 7 11 16 18 21 22 28} 8 {WHERE t1 MATCH 'O' AND xxx > -4123456789123456} {1 5 6 7 11 16 18 21 22 28} 9 {WHERE t1 MATCH '"Q T"' AND xxx < 27} {2 9 12} 10 {WHERE t1 MATCH '"Q T"' AND xxx <= 27} {2 9 12 27} 11 {WHERE t1 MATCH '"Q T"' AND xxx > 27} {28} 12 {WHERE t1 MATCH '"Q T"' AND xxx >= 27} {27 28} } { foreach {tn2 ref order} { 1 docid "ORDER BY docid ASC" 2 +docid "ORDER BY docid ASC" 3 rowid "ORDER BY docid ASC" 4 +rowid "ORDER BY docid ASC" 5 docid "ORDER BY docid DESC" 6 +docid "ORDER BY docid DESC" 7 rowid "ORDER BY docid DESC" 8 +rowid "ORDER BY docid DESC" } { set w [string map "xxx $ref" $where] set q "SELECT docid FROM t1 $w $order" if {$tn2<5} { set r [lsort -integer -increasing $result] } else { set r [lsort -integer -decreasing $result] } do_execsql_test 3.$tn.$tn2 $q $r } } finish_test |
Added test/fts4incr.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | # 2012 March 26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #************************************************************************* # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/fts3_common.tcl set ::testprefix fts4incr # If SQLITE_ENABLE_FTS3 is defined, omit this file. ifcapable !fts3 { finish_test return } # Create the fts_kjv_genesis procedure which fills and FTS3/4 table # with the complete text of the Book of Genesis. # source $testdir/genesis.tcl do_test 1.0 { execsql { CREATE VIRTUAL TABLE t1 USING fts4(words) } fts_kjv_genesis } {} do_execsql_test 1.1 { SELECT min(docid), max(docid) FROM t1; } {1001001 1050026} foreach {tn q res} { 1 { SELECT count(*) FROM t1 WHERE t1 MATCH 'and' AND docid < 1010000} 224 2 { SELECT count(*) FROM t1 WHERE t1 MATCH '"in the"' AND docid < 1010000} 47 3 { SELECT count(*) FROM t1 WHERE t1 MATCH '"And God"' AND docid < 1010000} 33 4 { SELECT count(*) FROM t1 WHERE t1 MATCH '"land of canaan"' AND docid < 1030000 } 7 } { foreach s {0 1} { execsql "INSERT INTO t1(t1) VALUES('test-no-incr-doclist=$s')" do_execsql_test 2.$tn.$s $q $res set t($s) [lindex [time [list execsql $q] 100] 0] } puts "with optimization: $t(0) without: $t(1)" } finish_test |