| File: | root/firefox-clang/third_party/sqlite3/ext/fts5.c |
| Warning: | line 23621, column 16 Array access (from variable 'apVal') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | ||||
| 2 | /* | |||
| 3 | ** This, the "fts5.c" source file, is a composite file that is itself | |||
| 4 | ** assembled from the following files: | |||
| 5 | ** | |||
| 6 | ** fts5.h | |||
| 7 | ** fts5Int.h | |||
| 8 | ** fts5parse.h <--- Generated from fts5parse.y by Lemon | |||
| 9 | ** fts5parse.c <--- Generated from fts5parse.y by Lemon | |||
| 10 | ** fts5_aux.c | |||
| 11 | ** fts5_buffer.c | |||
| 12 | ** fts5_config.c | |||
| 13 | ** fts5_expr.c | |||
| 14 | ** fts5_hash.c | |||
| 15 | ** fts5_index.c | |||
| 16 | ** fts5_main.c | |||
| 17 | ** fts5_storage.c | |||
| 18 | ** fts5_tokenize.c | |||
| 19 | ** fts5_unicode2.c | |||
| 20 | ** fts5_varint.c | |||
| 21 | ** fts5_vocab.c | |||
| 22 | */ | |||
| 23 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) | |||
| 24 | ||||
| 25 | #if !defined(NDEBUG1) && !defined(SQLITE_DEBUG) | |||
| 26 | # define NDEBUG1 1 | |||
| 27 | #endif | |||
| 28 | #if defined(NDEBUG1) && defined(SQLITE_DEBUG) | |||
| 29 | # undef NDEBUG1 | |||
| 30 | #endif | |||
| 31 | ||||
| 32 | #ifdef HAVE_STDINT_H1 | |||
| 33 | #include <stdint.h> | |||
| 34 | #endif | |||
| 35 | #ifdef HAVE_INTTYPES_H1 | |||
| 36 | #include <inttypes.h> | |||
| 37 | #endif | |||
| 38 | #line 1 "fts5.h" | |||
| 39 | /* | |||
| 40 | ** 2014 May 31 | |||
| 41 | ** | |||
| 42 | ** The author disclaims copyright to this source code. In place of | |||
| 43 | ** a legal notice, here is a blessing: | |||
| 44 | ** | |||
| 45 | ** May you do good and not evil. | |||
| 46 | ** May you find forgiveness for yourself and forgive others. | |||
| 47 | ** May you share freely, never taking more than you give. | |||
| 48 | ** | |||
| 49 | ****************************************************************************** | |||
| 50 | ** | |||
| 51 | ** Interfaces to extend FTS5. Using the interfaces defined in this file, | |||
| 52 | ** FTS5 may be extended with: | |||
| 53 | ** | |||
| 54 | ** * custom tokenizers, and | |||
| 55 | ** * custom auxiliary functions. | |||
| 56 | */ | |||
| 57 | ||||
| 58 | ||||
| 59 | #ifndef _FTS5_H | |||
| 60 | #define _FTS5_H | |||
| 61 | ||||
| 62 | #include "sqlite3.h" | |||
| 63 | ||||
| 64 | #ifdef __cplusplus | |||
| 65 | extern "C" { | |||
| 66 | #endif | |||
| 67 | ||||
| 68 | /************************************************************************* | |||
| 69 | ** CUSTOM AUXILIARY FUNCTIONS | |||
| 70 | ** | |||
| 71 | ** Virtual table implementations may overload SQL functions by implementing | |||
| 72 | ** the sqlite3_module.xFindFunction() method. | |||
| 73 | */ | |||
| 74 | ||||
| 75 | typedef struct Fts5ExtensionApi Fts5ExtensionApi; | |||
| 76 | typedef struct Fts5Context Fts5Context; | |||
| 77 | typedef struct Fts5PhraseIter Fts5PhraseIter; | |||
| 78 | ||||
| 79 | typedef void (*fts5_extension_function)( | |||
| 80 | const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ | |||
| 81 | Fts5Context *pFts, /* First arg to pass to pApi functions */ | |||
| 82 | sqlite3_context *pCtx, /* Context for returning result/error */ | |||
| 83 | int nVal, /* Number of values in apVal[] array */ | |||
| 84 | sqlite3_value **apVal /* Array of trailing arguments */ | |||
| 85 | ); | |||
| 86 | ||||
| 87 | struct Fts5PhraseIter { | |||
| 88 | const unsigned char *a; | |||
| 89 | const unsigned char *b; | |||
| 90 | }; | |||
| 91 | ||||
| 92 | /* | |||
| 93 | ** EXTENSION API FUNCTIONS | |||
| 94 | ** | |||
| 95 | ** xUserData(pFts): | |||
| 96 | ** Return a copy of the pUserData pointer passed to the xCreateFunction() | |||
| 97 | ** API when the extension function was registered. | |||
| 98 | ** | |||
| 99 | ** xColumnTotalSize(pFts, iCol, pnToken): | |||
| 100 | ** If parameter iCol is less than zero, set output variable *pnToken | |||
| 101 | ** to the total number of tokens in the FTS5 table. Or, if iCol is | |||
| 102 | ** non-negative but less than the number of columns in the table, return | |||
| 103 | ** the total number of tokens in column iCol, considering all rows in | |||
| 104 | ** the FTS5 table. | |||
| 105 | ** | |||
| 106 | ** If parameter iCol is greater than or equal to the number of columns | |||
| 107 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. | |||
| 108 | ** an OOM condition or IO error), an appropriate SQLite error code is | |||
| 109 | ** returned. | |||
| 110 | ** | |||
| 111 | ** xColumnCount(pFts): | |||
| 112 | ** Return the number of columns in the table. | |||
| 113 | ** | |||
| 114 | ** xColumnSize(pFts, iCol, pnToken): | |||
| 115 | ** If parameter iCol is less than zero, set output variable *pnToken | |||
| 116 | ** to the total number of tokens in the current row. Or, if iCol is | |||
| 117 | ** non-negative but less than the number of columns in the table, set | |||
| 118 | ** *pnToken to the number of tokens in column iCol of the current row. | |||
| 119 | ** | |||
| 120 | ** If parameter iCol is greater than or equal to the number of columns | |||
| 121 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. | |||
| 122 | ** an OOM condition or IO error), an appropriate SQLite error code is | |||
| 123 | ** returned. | |||
| 124 | ** | |||
| 125 | ** This function may be quite inefficient if used with an FTS5 table | |||
| 126 | ** created with the "columnsize=0" option. | |||
| 127 | ** | |||
| 128 | ** xColumnText: | |||
| 129 | ** If parameter iCol is less than zero, or greater than or equal to the | |||
| 130 | ** number of columns in the table, SQLITE_RANGE is returned. | |||
| 131 | ** | |||
| 132 | ** Otherwise, this function attempts to retrieve the text of column iCol of | |||
| 133 | ** the current document. If successful, (*pz) is set to point to a buffer | |||
| 134 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes | |||
| 135 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, | |||
| 136 | ** if an error occurs, an SQLite error code is returned and the final values | |||
| 137 | ** of (*pz) and (*pn) are undefined. | |||
| 138 | ** | |||
| 139 | ** xPhraseCount: | |||
| 140 | ** Returns the number of phrases in the current query expression. | |||
| 141 | ** | |||
| 142 | ** xPhraseSize: | |||
| 143 | ** If parameter iCol is less than zero, or greater than or equal to the | |||
| 144 | ** number of phrases in the current query, as returned by xPhraseCount, | |||
| 145 | ** 0 is returned. Otherwise, this function returns the number of tokens in | |||
| 146 | ** phrase iPhrase of the query. Phrases are numbered starting from zero. | |||
| 147 | ** | |||
| 148 | ** xInstCount: | |||
| 149 | ** Set *pnInst to the total number of occurrences of all phrases within | |||
| 150 | ** the query within the current row. Return SQLITE_OK if successful, or | |||
| 151 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. | |||
| 152 | ** | |||
| 153 | ** This API can be quite slow if used with an FTS5 table created with the | |||
| 154 | ** "detail=none" or "detail=column" option. If the FTS5 table is created | |||
| 155 | ** with either "detail=none" or "detail=column" and "content=" option | |||
| 156 | ** (i.e. if it is a contentless table), then this API always returns 0. | |||
| 157 | ** | |||
| 158 | ** xInst: | |||
| 159 | ** Query for the details of phrase match iIdx within the current row. | |||
| 160 | ** Phrase matches are numbered starting from zero, so the iIdx argument | |||
| 161 | ** should be greater than or equal to zero and smaller than the value | |||
| 162 | ** output by xInstCount(). If iIdx is less than zero or greater than | |||
| 163 | ** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. | |||
| 164 | ** | |||
| 165 | ** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol | |||
| 166 | ** to the column in which it occurs and *piOff the token offset of the | |||
| 167 | ** first token of the phrase. SQLITE_OK is returned if successful, or an | |||
| 168 | ** error code (i.e. SQLITE_NOMEM) if an error occurs. | |||
| 169 | ** | |||
| 170 | ** This API can be quite slow if used with an FTS5 table created with the | |||
| 171 | ** "detail=none" or "detail=column" option. | |||
| 172 | ** | |||
| 173 | ** xRowid: | |||
| 174 | ** Returns the rowid of the current row. | |||
| 175 | ** | |||
| 176 | ** xTokenize: | |||
| 177 | ** Tokenize text using the tokenizer belonging to the FTS5 table. | |||
| 178 | ** | |||
| 179 | ** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback): | |||
| 180 | ** This API function is used to query the FTS table for phrase iPhrase | |||
| 181 | ** of the current query. Specifically, a query equivalent to: | |||
| 182 | ** | |||
| 183 | ** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid | |||
| 184 | ** | |||
| 185 | ** with $p set to a phrase equivalent to the phrase iPhrase of the | |||
| 186 | ** current query is executed. Any column filter that applies to | |||
| 187 | ** phrase iPhrase of the current query is included in $p. For each | |||
| 188 | ** row visited, the callback function passed as the fourth argument | |||
| 189 | ** is invoked. The context and API objects passed to the callback | |||
| 190 | ** function may be used to access the properties of each matched row. | |||
| 191 | ** Invoking Api.xUserData() returns a copy of the pointer passed as | |||
| 192 | ** the third argument to pUserData. | |||
| 193 | ** | |||
| 194 | ** If parameter iPhrase is less than zero, or greater than or equal to | |||
| 195 | ** the number of phrases in the query, as returned by xPhraseCount(), | |||
| 196 | ** this function returns SQLITE_RANGE. | |||
| 197 | ** | |||
| 198 | ** If the callback function returns any value other than SQLITE_OK, the | |||
| 199 | ** query is abandoned and the xQueryPhrase function returns immediately. | |||
| 200 | ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. | |||
| 201 | ** Otherwise, the error code is propagated upwards. | |||
| 202 | ** | |||
| 203 | ** If the query runs to completion without incident, SQLITE_OK is returned. | |||
| 204 | ** Or, if some error occurs before the query completes or is aborted by | |||
| 205 | ** the callback, an SQLite error code is returned. | |||
| 206 | ** | |||
| 207 | ** | |||
| 208 | ** xSetAuxdata(pFts5, pAux, xDelete) | |||
| 209 | ** | |||
| 210 | ** Save the pointer passed as the second argument as the extension function's | |||
| 211 | ** "auxiliary data". The pointer may then be retrieved by the current or any | |||
| 212 | ** future invocation of the same fts5 extension function made as part of | |||
| 213 | ** the same MATCH query using the xGetAuxdata() API. | |||
| 214 | ** | |||
| 215 | ** Each extension function is allocated a single auxiliary data slot for | |||
| 216 | ** each FTS query (MATCH expression). If the extension function is invoked | |||
| 217 | ** more than once for a single FTS query, then all invocations share a | |||
| 218 | ** single auxiliary data context. | |||
| 219 | ** | |||
| 220 | ** If there is already an auxiliary data pointer when this function is | |||
| 221 | ** invoked, then it is replaced by the new pointer. If an xDelete callback | |||
| 222 | ** was specified along with the original pointer, it is invoked at this | |||
| 223 | ** point. | |||
| 224 | ** | |||
| 225 | ** The xDelete callback, if one is specified, is also invoked on the | |||
| 226 | ** auxiliary data pointer after the FTS5 query has finished. | |||
| 227 | ** | |||
| 228 | ** If an error (e.g. an OOM condition) occurs within this function, | |||
| 229 | ** the auxiliary data is set to NULL and an error code returned. If the | |||
| 230 | ** xDelete parameter was not NULL, it is invoked on the auxiliary data | |||
| 231 | ** pointer before returning. | |||
| 232 | ** | |||
| 233 | ** | |||
| 234 | ** xGetAuxdata(pFts5, bClear) | |||
| 235 | ** | |||
| 236 | ** Returns the current auxiliary data pointer for the fts5 extension | |||
| 237 | ** function. See the xSetAuxdata() method for details. | |||
| 238 | ** | |||
| 239 | ** If the bClear argument is non-zero, then the auxiliary data is cleared | |||
| 240 | ** (set to NULL) before this function returns. In this case the xDelete, | |||
| 241 | ** if any, is not invoked. | |||
| 242 | ** | |||
| 243 | ** | |||
| 244 | ** xRowCount(pFts5, pnRow) | |||
| 245 | ** | |||
| 246 | ** This function is used to retrieve the total number of rows in the table. | |||
| 247 | ** In other words, the same value that would be returned by: | |||
| 248 | ** | |||
| 249 | ** SELECT count(*) FROM ftstable; | |||
| 250 | ** | |||
| 251 | ** xPhraseFirst() | |||
| 252 | ** This function is used, along with type Fts5PhraseIter and the xPhraseNext | |||
| 253 | ** method, to iterate through all instances of a single query phrase within | |||
| 254 | ** the current row. This is the same information as is accessible via the | |||
| 255 | ** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient | |||
| 256 | ** to use, this API may be faster under some circumstances. To iterate | |||
| 257 | ** through instances of phrase iPhrase, use the following code: | |||
| 258 | ** | |||
| 259 | ** Fts5PhraseIter iter; | |||
| 260 | ** int iCol, iOff; | |||
| 261 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); | |||
| 262 | ** iCol>=0; | |||
| 263 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) | |||
| 264 | ** ){ | |||
| 265 | ** // An instance of phrase iPhrase at offset iOff of column iCol | |||
| 266 | ** } | |||
| 267 | ** | |||
| 268 | ** The Fts5PhraseIter structure is defined above. Applications should not | |||
| 269 | ** modify this structure directly - it should only be used as shown above | |||
| 270 | ** with the xPhraseFirst() and xPhraseNext() API methods (and by | |||
| 271 | ** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). | |||
| 272 | ** | |||
| 273 | ** This API can be quite slow if used with an FTS5 table created with the | |||
| 274 | ** "detail=none" or "detail=column" option. If the FTS5 table is created | |||
| 275 | ** with either "detail=none" or "detail=column" and "content=" option | |||
| 276 | ** (i.e. if it is a contentless table), then this API always iterates | |||
| 277 | ** through an empty set (all calls to xPhraseFirst() set iCol to -1). | |||
| 278 | ** | |||
| 279 | ** In all cases, matches are visited in (column ASC, offset ASC) order. | |||
| 280 | ** i.e. all those in column 0, sorted by offset, followed by those in | |||
| 281 | ** column 1, etc. | |||
| 282 | ** | |||
| 283 | ** xPhraseNext() | |||
| 284 | ** See xPhraseFirst above. | |||
| 285 | ** | |||
| 286 | ** xPhraseFirstColumn() | |||
| 287 | ** This function and xPhraseNextColumn() are similar to the xPhraseFirst() | |||
| 288 | ** and xPhraseNext() APIs described above. The difference is that instead | |||
| 289 | ** of iterating through all instances of a phrase in the current row, these | |||
| 290 | ** APIs are used to iterate through the set of columns in the current row | |||
| 291 | ** that contain one or more instances of a specified phrase. For example: | |||
| 292 | ** | |||
| 293 | ** Fts5PhraseIter iter; | |||
| 294 | ** int iCol; | |||
| 295 | ** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); | |||
| 296 | ** iCol>=0; | |||
| 297 | ** pApi->xPhraseNextColumn(pFts, &iter, &iCol) | |||
| 298 | ** ){ | |||
| 299 | ** // Column iCol contains at least one instance of phrase iPhrase | |||
| 300 | ** } | |||
| 301 | ** | |||
| 302 | ** This API can be quite slow if used with an FTS5 table created with the | |||
| 303 | ** "detail=none" option. If the FTS5 table is created with either | |||
| 304 | ** "detail=none" "content=" option (i.e. if it is a contentless table), | |||
| 305 | ** then this API always iterates through an empty set (all calls to | |||
| 306 | ** xPhraseFirstColumn() set iCol to -1). | |||
| 307 | ** | |||
| 308 | ** The information accessed using this API and its companion | |||
| 309 | ** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext | |||
| 310 | ** (or xInst/xInstCount). The chief advantage of this API is that it is | |||
| 311 | ** significantly more efficient than those alternatives when used with | |||
| 312 | ** "detail=column" tables. | |||
| 313 | ** | |||
| 314 | ** xPhraseNextColumn() | |||
| 315 | ** See xPhraseFirstColumn above. | |||
| 316 | ** | |||
| 317 | ** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) | |||
| 318 | ** This is used to access token iToken of phrase iPhrase of the current | |||
| 319 | ** query. Before returning, output parameter *ppToken is set to point | |||
| 320 | ** to a buffer containing the requested token, and *pnToken to the | |||
| 321 | ** size of this buffer in bytes. | |||
| 322 | ** | |||
| 323 | ** If iPhrase or iToken are less than zero, or if iPhrase is greater than | |||
| 324 | ** or equal to the number of phrases in the query as reported by | |||
| 325 | ** xPhraseCount(), or if iToken is equal to or greater than the number of | |||
| 326 | ** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken | |||
| 327 | are both zeroed. | |||
| 328 | ** | |||
| 329 | ** The output text is not a copy of the query text that specified the | |||
| 330 | ** token. It is the output of the tokenizer module. For tokendata=1 | |||
| 331 | ** tables, this includes any embedded 0x00 and trailing data. | |||
| 332 | ** | |||
| 333 | ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) | |||
| 334 | ** This is used to access token iToken of phrase hit iIdx within the | |||
| 335 | ** current row. If iIdx is less than zero or greater than or equal to the | |||
| 336 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, | |||
| 337 | ** output variable (*ppToken) is set to point to a buffer containing the | |||
| 338 | ** matching document token, and (*pnToken) to the size of that buffer in | |||
| 339 | ** bytes. | |||
| 340 | ** | |||
| 341 | ** The output text is not a copy of the document text that was tokenized. | |||
| 342 | ** It is the output of the tokenizer module. For tokendata=1 tables, this | |||
| 343 | ** includes any embedded 0x00 and trailing data. | |||
| 344 | ** | |||
| 345 | ** This API may be slow in some cases if the token identified by parameters | |||
| 346 | ** iIdx and iToken matched a prefix token in the query. In most cases, the | |||
| 347 | ** first call to this API for each prefix token in the query is forced | |||
| 348 | ** to scan the portion of the full-text index that matches the prefix | |||
| 349 | ** token to collect the extra data required by this API. If the prefix | |||
| 350 | ** token matches a large number of token instances in the document set, | |||
| 351 | ** this may be a performance problem. | |||
| 352 | ** | |||
| 353 | ** If the user knows in advance that a query may use this API for a | |||
| 354 | ** prefix token, FTS5 may be configured to collect all required data as part | |||
| 355 | ** of the initial querying of the full-text index, avoiding the second scan | |||
| 356 | ** entirely. This also causes prefix queries that do not use this API to | |||
| 357 | ** run more slowly and use more memory. FTS5 may be configured in this way | |||
| 358 | ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] | |||
| 359 | ** option, or on a per-query basis using the | |||
| 360 | ** [fts5_insttoken | fts5_insttoken()] user function. | |||
| 361 | ** | |||
| 362 | ** This API can be quite slow if used with an FTS5 table created with the | |||
| 363 | ** "detail=none" or "detail=column" option. | |||
| 364 | ** | |||
| 365 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) | |||
| 366 | ** If parameter iCol is less than zero, or greater than or equal to the | |||
| 367 | ** number of columns in the table, SQLITE_RANGE is returned. | |||
| 368 | ** | |||
| 369 | ** Otherwise, this function attempts to retrieve the locale associated | |||
| 370 | ** with column iCol of the current row. Usually, there is no associated | |||
| 371 | ** locale, and output parameters (*pzLocale) and (*pnLocale) are set | |||
| 372 | ** to NULL and 0, respectively. However, if the fts5_locale() function | |||
| 373 | ** was used to associate a locale with the value when it was inserted | |||
| 374 | ** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated | |||
| 375 | ** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) | |||
| 376 | ** is set to the size in bytes of the buffer, not including the | |||
| 377 | ** nul-terminator. | |||
| 378 | ** | |||
| 379 | ** If successful, SQLITE_OK is returned. Or, if an error occurs, an | |||
| 380 | ** SQLite error code is returned. The final value of the output parameters | |||
| 381 | ** is undefined in this case. | |||
| 382 | ** | |||
| 383 | ** xTokenize_v2: | |||
| 384 | ** Tokenize text using the tokenizer belonging to the FTS5 table. This | |||
| 385 | ** API is the same as the xTokenize() API, except that it allows a tokenizer | |||
| 386 | ** locale to be specified. | |||
| 387 | */ | |||
| 388 | struct Fts5ExtensionApi { | |||
| 389 | int iVersion; /* Currently always set to 4 */ | |||
| 390 | ||||
| 391 | void *(*xUserData)(Fts5Context*); | |||
| 392 | ||||
| 393 | int (*xColumnCount)(Fts5Context*); | |||
| 394 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); | |||
| 395 | int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken); | |||
| 396 | ||||
| 397 | int (*xTokenize)(Fts5Context*, | |||
| 398 | const char *pText, int nText, /* Text to tokenize */ | |||
| 399 | void *pCtx, /* Context passed to xToken() */ | |||
| 400 | int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ | |||
| 401 | ); | |||
| 402 | ||||
| 403 | int (*xPhraseCount)(Fts5Context*); | |||
| 404 | int (*xPhraseSize)(Fts5Context*, int iPhrase); | |||
| 405 | ||||
| 406 | int (*xInstCount)(Fts5Context*, int *pnInst); | |||
| 407 | int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff); | |||
| 408 | ||||
| 409 | sqlite3_int64 (*xRowid)(Fts5Context*); | |||
| 410 | int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn); | |||
| 411 | int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken); | |||
| 412 | ||||
| 413 | int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData, | |||
| 414 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) | |||
| 415 | ); | |||
| 416 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); | |||
| 417 | void *(*xGetAuxdata)(Fts5Context*, int bClear); | |||
| 418 | ||||
| 419 | int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); | |||
| 420 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); | |||
| 421 | ||||
| 422 | int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); | |||
| 423 | void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); | |||
| 424 | ||||
| 425 | /* Below this point are iVersion>=3 only */ | |||
| 426 | int (*xQueryToken)(Fts5Context*, | |||
| 427 | int iPhrase, int iToken, | |||
| 428 | const char **ppToken, int *pnToken | |||
| 429 | ); | |||
| 430 | int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); | |||
| 431 | ||||
| 432 | /* Below this point are iVersion>=4 only */ | |||
| 433 | int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); | |||
| 434 | int (*xTokenize_v2)(Fts5Context*, | |||
| 435 | const char *pText, int nText, /* Text to tokenize */ | |||
| 436 | const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ | |||
| 437 | void *pCtx, /* Context passed to xToken() */ | |||
| 438 | int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ | |||
| 439 | ); | |||
| 440 | }; | |||
| 441 | ||||
| 442 | /* | |||
| 443 | ** CUSTOM AUXILIARY FUNCTIONS | |||
| 444 | *************************************************************************/ | |||
| 445 | ||||
| 446 | /************************************************************************* | |||
| 447 | ** CUSTOM TOKENIZERS | |||
| 448 | ** | |||
| 449 | ** Applications may also register custom tokenizer types. A tokenizer | |||
| 450 | ** is registered by providing fts5 with a populated instance of the | |||
| 451 | ** following structure. All structure methods must be defined, setting | |||
| 452 | ** any member of the fts5_tokenizer struct to NULL leads to undefined | |||
| 453 | ** behaviour. The structure methods are expected to function as follows: | |||
| 454 | ** | |||
| 455 | ** xCreate: | |||
| 456 | ** This function is used to allocate and initialize a tokenizer instance. | |||
| 457 | ** A tokenizer instance is required to actually tokenize text. | |||
| 458 | ** | |||
| 459 | ** The first argument passed to this function is a copy of the (void*) | |||
| 460 | ** pointer provided by the application when the fts5_tokenizer_v2 object | |||
| 461 | ** was registered with FTS5 (the third argument to xCreateTokenizer()). | |||
| 462 | ** The second and third arguments are an array of nul-terminated strings | |||
| 463 | ** containing the tokenizer arguments, if any, specified following the | |||
| 464 | ** tokenizer name as part of the CREATE VIRTUAL TABLE statement used | |||
| 465 | ** to create the FTS5 table. | |||
| 466 | ** | |||
| 467 | ** The final argument is an output variable. If successful, (*ppOut) | |||
| 468 | ** should be set to point to the new tokenizer handle and SQLITE_OK | |||
| 469 | ** returned. If an error occurs, some value other than SQLITE_OK should | |||
| 470 | ** be returned. In this case, fts5 assumes that the final value of *ppOut | |||
| 471 | ** is undefined. | |||
| 472 | ** | |||
| 473 | ** xDelete: | |||
| 474 | ** This function is invoked to delete a tokenizer handle previously | |||
| 475 | ** allocated using xCreate(). Fts5 guarantees that this function will | |||
| 476 | ** be invoked exactly once for each successful call to xCreate(). | |||
| 477 | ** | |||
| 478 | ** xTokenize: | |||
| 479 | ** This function is expected to tokenize the nText byte string indicated | |||
| 480 | ** by argument pText. pText may or may not be nul-terminated. The first | |||
| 481 | ** argument passed to this function is a pointer to an Fts5Tokenizer object | |||
| 482 | ** returned by an earlier call to xCreate(). | |||
| 483 | ** | |||
| 484 | ** The third argument indicates the reason that FTS5 is requesting | |||
| 485 | ** tokenization of the supplied text. This is always one of the following | |||
| 486 | ** four values: | |||
| 487 | ** | |||
| 488 | ** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into | |||
| 489 | ** or removed from the FTS table. The tokenizer is being invoked to | |||
| 490 | ** determine the set of tokens to add to (or delete from) the | |||
| 491 | ** FTS index. | |||
| 492 | ** | |||
| 493 | ** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed | |||
| 494 | ** against the FTS index. The tokenizer is being called to tokenize | |||
| 495 | ** a bareword or quoted string specified as part of the query. | |||
| 496 | ** | |||
| 497 | ** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as | |||
| 498 | ** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is | |||
| 499 | ** followed by a "*" character, indicating that the last token | |||
| 500 | ** returned by the tokenizer will be treated as a token prefix. | |||
| 501 | ** | |||
| 502 | ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to | |||
| 503 | ** satisfy an fts5_api.xTokenize() request made by an auxiliary | |||
| 504 | ** function. Or an fts5_api.xColumnSize() request made by the same | |||
| 505 | ** on a columnsize=0 database. | |||
| 506 | ** </ul> | |||
| 507 | ** | |||
| 508 | ** The sixth and seventh arguments passed to xTokenize() - pLocale and | |||
| 509 | ** nLocale - are a pointer to a buffer containing the locale to use for | |||
| 510 | ** tokenization (e.g. "en_US") and its size in bytes, respectively. The | |||
| 511 | ** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in | |||
| 512 | ** which case nLocale is always 0) to indicate that the tokenizer should | |||
| 513 | ** use its default locale. | |||
| 514 | ** | |||
| 515 | ** For each token in the input string, the supplied callback xToken() must | |||
| 516 | ** be invoked. The first argument to it should be a copy of the pointer | |||
| 517 | ** passed as the second argument to xTokenize(). The third and fourth | |||
| 518 | ** arguments are a pointer to a buffer containing the token text, and the | |||
| 519 | ** size of the token in bytes. The 4th and 5th arguments are the byte offsets | |||
| 520 | ** of the first byte of and first byte immediately following the text from | |||
| 521 | ** which the token is derived within the input. | |||
| 522 | ** | |||
| 523 | ** The second argument passed to the xToken() callback ("tflags") should | |||
| 524 | ** normally be set to 0. The exception is if the tokenizer supports | |||
| 525 | ** synonyms. In this case see the discussion below for details. | |||
| 526 | ** | |||
| 527 | ** FTS5 assumes the xToken() callback is invoked for each token in the | |||
| 528 | ** order that they occur within the input text. | |||
| 529 | ** | |||
| 530 | ** If an xToken() callback returns any value other than SQLITE_OK, then | |||
| 531 | ** the tokenization should be abandoned and the xTokenize() method should | |||
| 532 | ** immediately return a copy of the xToken() return value. Or, if the | |||
| 533 | ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, | |||
| 534 | ** if an error occurs with the xTokenize() implementation itself, it | |||
| 535 | ** may abandon the tokenization and return any error code other than | |||
| 536 | ** SQLITE_OK or SQLITE_DONE. | |||
| 537 | ** | |||
| 538 | ** If the tokenizer is registered using an fts5_tokenizer_v2 object, | |||
| 539 | ** then the xTokenize() method has two additional arguments - pLocale | |||
| 540 | ** and nLocale. These specify the locale that the tokenizer should use | |||
| 541 | ** for the current request. If pLocale and nLocale are both 0, then the | |||
| 542 | ** tokenizer should use its default locale. Otherwise, pLocale points to | |||
| 543 | ** an nLocale byte buffer containing the name of the locale to use as utf-8 | |||
| 544 | ** text. pLocale is not nul-terminated. | |||
| 545 | ** | |||
| 546 | ** FTS5_TOKENIZER | |||
| 547 | ** | |||
| 548 | ** There is also an fts5_tokenizer object. This is an older, deprecated, | |||
| 549 | ** version of fts5_tokenizer_v2. It is similar except that: | |||
| 550 | ** | |||
| 551 | ** <ul> | |||
| 552 | ** <li> There is no "iVersion" field, and | |||
| 553 | ** <li> The xTokenize() method does not take a locale argument. | |||
| 554 | ** </ul> | |||
| 555 | ** | |||
| 556 | ** Legacy fts5_tokenizer tokenizers must be registered using the | |||
| 557 | ** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). | |||
| 558 | ** | |||
| 559 | ** Tokenizer implementations registered using either API may be retrieved | |||
| 560 | ** using both xFindTokenizer() and xFindTokenizer_v2(). | |||
| 561 | ** | |||
| 562 | ** SYNONYM SUPPORT | |||
| 563 | ** | |||
| 564 | ** Custom tokenizers may also support synonyms. Consider a case in which a | |||
| 565 | ** user wishes to query for a phrase such as "first place". Using the | |||
| 566 | ** built-in tokenizers, the FTS5 query 'first + place' will match instances | |||
| 567 | ** of "first place" within the document set, but not alternative forms | |||
| 568 | ** such as "1st place". In some applications, it would be better to match | |||
| 569 | ** all instances of "first place" or "1st place" regardless of which form | |||
| 570 | ** the user specified in the MATCH query text. | |||
| 571 | ** | |||
| 572 | ** There are several ways to approach this in FTS5: | |||
| 573 | ** | |||
| 574 | ** <ol><li> By mapping all synonyms to a single token. In this case, using | |||
| 575 | ** the above example, this means that the tokenizer returns the | |||
| 576 | ** same token for inputs "first" and "1st". Say that token is in | |||
| 577 | ** fact "first", so that when the user inserts the document "I won | |||
| 578 | ** 1st place" entries are added to the index for tokens "i", "won", | |||
| 579 | ** "first" and "place". If the user then queries for '1st + place', | |||
| 580 | ** the tokenizer substitutes "first" for "1st" and the query works | |||
| 581 | ** as expected. | |||
| 582 | ** | |||
| 583 | ** <li> By querying the index for all synonyms of each query term | |||
| 584 | ** separately. In this case, when tokenizing query text, the | |||
| 585 | ** tokenizer may provide multiple synonyms for a single term | |||
| 586 | ** within the document. FTS5 then queries the index for each | |||
| 587 | ** synonym individually. For example, faced with the query: | |||
| 588 | ** | |||
| 589 | ** <codeblock> | |||
| 590 | ** ... MATCH 'first place'</codeblock> | |||
| 591 | ** | |||
| 592 | ** the tokenizer offers both "1st" and "first" as synonyms for the | |||
| 593 | ** first token in the MATCH query and FTS5 effectively runs a query | |||
| 594 | ** similar to: | |||
| 595 | ** | |||
| 596 | ** <codeblock> | |||
| 597 | ** ... MATCH '(first OR 1st) place'</codeblock> | |||
| 598 | ** | |||
| 599 | ** except that, for the purposes of auxiliary functions, the query | |||
| 600 | ** still appears to contain just two phrases - "(first OR 1st)" | |||
| 601 | ** being treated as a single phrase. | |||
| 602 | ** | |||
| 603 | ** <li> By adding multiple synonyms for a single term to the FTS index. | |||
| 604 | ** Using this method, when tokenizing document text, the tokenizer | |||
| 605 | ** provides multiple synonyms for each token. So that when a | |||
| 606 | ** document such as "I won first place" is tokenized, entries are | |||
| 607 | ** added to the FTS index for "i", "won", "first", "1st" and | |||
| 608 | ** "place". | |||
| 609 | ** | |||
| 610 | ** This way, even if the tokenizer does not provide synonyms | |||
| 611 | ** when tokenizing query text (it should not - to do so would be | |||
| 612 | ** inefficient), it doesn't matter if the user queries for | |||
| 613 | ** 'first + place' or '1st + place', as there are entries in the | |||
| 614 | ** FTS index corresponding to both forms of the first token. | |||
| 615 | ** </ol> | |||
| 616 | ** | |||
| 617 | ** Whether it is parsing document or query text, any call to xToken that | |||
| 618 | ** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit | |||
| 619 | ** is considered to supply a synonym for the previous token. For example, | |||
| 620 | ** when parsing the document "I won first place", a tokenizer that supports | |||
| 621 | ** synonyms would call xToken() 5 times, as follows: | |||
| 622 | ** | |||
| 623 | ** <codeblock> | |||
| 624 | ** xToken(pCtx, 0, "i", 1, 0, 1); | |||
| 625 | ** xToken(pCtx, 0, "won", 3, 2, 5); | |||
| 626 | ** xToken(pCtx, 0, "first", 5, 6, 11); | |||
| 627 | ** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11); | |||
| 628 | ** xToken(pCtx, 0, "place", 5, 12, 17); | |||
| 629 | **</codeblock> | |||
| 630 | ** | |||
| 631 | ** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time | |||
| 632 | ** xToken() is called. Multiple synonyms may be specified for a single token | |||
| 633 | ** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence. | |||
| 634 | ** There is no limit to the number of synonyms that may be provided for a | |||
| 635 | ** single token. | |||
| 636 | ** | |||
| 637 | ** In many cases, method (1) above is the best approach. It does not add | |||
| 638 | ** extra data to the FTS index or require FTS5 to query for multiple terms, | |||
| 639 | ** so it is efficient in terms of disk space and query speed. However, it | |||
| 640 | ** does not support prefix queries very well. If, as suggested above, the | |||
| 641 | ** token "first" is substituted for "1st" by the tokenizer, then the query: | |||
| 642 | ** | |||
| 643 | ** <codeblock> | |||
| 644 | ** ... MATCH '1s*'</codeblock> | |||
| 645 | ** | |||
| 646 | ** will not match documents that contain the token "1st" (as the tokenizer | |||
| 647 | ** will probably not map "1s" to any prefix of "first"). | |||
| 648 | ** | |||
| 649 | ** For full prefix support, method (3) may be preferred. In this case, | |||
| 650 | ** because the index contains entries for both "first" and "1st", prefix | |||
| 651 | ** queries such as 'fi*' or '1s*' will match correctly. However, because | |||
| 652 | ** extra entries are added to the FTS index, this method uses more space | |||
| 653 | ** within the database. | |||
| 654 | ** | |||
| 655 | ** Method (2) offers a midpoint between (1) and (3). Using this method, | |||
| 656 | ** a query such as '1s*' will match documents that contain the literal | |||
| 657 | ** token "1st", but not "first" (assuming the tokenizer is not able to | |||
| 658 | ** provide synonyms for prefixes). However, a non-prefix query like '1st' | |||
| 659 | ** will match against "1st" and "first". This method does not require | |||
| 660 | ** extra disk space, as no extra entries are added to the FTS index. | |||
| 661 | ** On the other hand, it may require more CPU cycles to run MATCH queries, | |||
| 662 | ** as separate queries of the FTS index are required for each synonym. | |||
| 663 | ** | |||
| 664 | ** When using methods (2) or (3), it is important that the tokenizer only | |||
| 665 | ** provide synonyms when tokenizing document text (method (3)) or query | |||
| 666 | ** text (method (2)), not both. Doing so will not cause any errors, but is | |||
| 667 | ** inefficient. | |||
| 668 | */ | |||
| 669 | typedef struct Fts5Tokenizer Fts5Tokenizer; | |||
| 670 | typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; | |||
| 671 | struct fts5_tokenizer_v2 { | |||
| 672 | int iVersion; /* Currently always 2 */ | |||
| 673 | ||||
| 674 | int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); | |||
| 675 | void (*xDelete)(Fts5Tokenizer*); | |||
| 676 | int (*xTokenize)(Fts5Tokenizer*, | |||
| 677 | void *pCtx, | |||
| 678 | int flags, /* Mask of FTS5_TOKENIZE_* flags */ | |||
| 679 | const char *pText, int nText, | |||
| 680 | const char *pLocale, int nLocale, | |||
| 681 | int (*xToken)( | |||
| 682 | void *pCtx, /* Copy of 2nd argument to xTokenize() */ | |||
| 683 | int tflags, /* Mask of FTS5_TOKEN_* flags */ | |||
| 684 | const char *pToken, /* Pointer to buffer containing token */ | |||
| 685 | int nToken, /* Size of token in bytes */ | |||
| 686 | int iStart, /* Byte offset of token within input text */ | |||
| 687 | int iEnd /* Byte offset of end of token within input text */ | |||
| 688 | ) | |||
| 689 | ); | |||
| 690 | }; | |||
| 691 | ||||
| 692 | /* | |||
| 693 | ** New code should use the fts5_tokenizer_v2 type to define tokenizer | |||
| 694 | ** implementations. The following type is included for legacy applications | |||
| 695 | ** that still use it. | |||
| 696 | */ | |||
| 697 | typedef struct fts5_tokenizer fts5_tokenizer; | |||
| 698 | struct fts5_tokenizer { | |||
| 699 | int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); | |||
| 700 | void (*xDelete)(Fts5Tokenizer*); | |||
| 701 | int (*xTokenize)(Fts5Tokenizer*, | |||
| 702 | void *pCtx, | |||
| 703 | int flags, /* Mask of FTS5_TOKENIZE_* flags */ | |||
| 704 | const char *pText, int nText, | |||
| 705 | int (*xToken)( | |||
| 706 | void *pCtx, /* Copy of 2nd argument to xTokenize() */ | |||
| 707 | int tflags, /* Mask of FTS5_TOKEN_* flags */ | |||
| 708 | const char *pToken, /* Pointer to buffer containing token */ | |||
| 709 | int nToken, /* Size of token in bytes */ | |||
| 710 | int iStart, /* Byte offset of token within input text */ | |||
| 711 | int iEnd /* Byte offset of end of token within input text */ | |||
| 712 | ) | |||
| 713 | ); | |||
| 714 | }; | |||
| 715 | ||||
| 716 | ||||
| 717 | /* Flags that may be passed as the third argument to xTokenize() */ | |||
| 718 | #define FTS5_TOKENIZE_QUERY0x0001 0x0001 | |||
| 719 | #define FTS5_TOKENIZE_PREFIX0x0002 0x0002 | |||
| 720 | #define FTS5_TOKENIZE_DOCUMENT0x0004 0x0004 | |||
| 721 | #define FTS5_TOKENIZE_AUX0x0008 0x0008 | |||
| 722 | ||||
| 723 | /* Flags that may be passed by the tokenizer implementation back to FTS5 | |||
| 724 | ** as the third argument to the supplied xToken callback. */ | |||
| 725 | #define FTS5_TOKEN_COLOCATED0x0001 0x0001 /* Same position as prev. token */ | |||
| 726 | ||||
| 727 | /* | |||
| 728 | ** END OF CUSTOM TOKENIZERS | |||
| 729 | *************************************************************************/ | |||
| 730 | ||||
| 731 | /************************************************************************* | |||
| 732 | ** FTS5 EXTENSION REGISTRATION API | |||
| 733 | */ | |||
| 734 | typedef struct fts5_api fts5_api; | |||
| 735 | struct fts5_api { | |||
| 736 | int iVersion; /* Currently always set to 3 */ | |||
| 737 | ||||
| 738 | /* Create a new tokenizer */ | |||
| 739 | int (*xCreateTokenizer)( | |||
| 740 | fts5_api *pApi, | |||
| 741 | const char *zName, | |||
| 742 | void *pUserData, | |||
| 743 | fts5_tokenizer *pTokenizer, | |||
| 744 | void (*xDestroy)(void*) | |||
| 745 | ); | |||
| 746 | ||||
| 747 | /* Find an existing tokenizer */ | |||
| 748 | int (*xFindTokenizer)( | |||
| 749 | fts5_api *pApi, | |||
| 750 | const char *zName, | |||
| 751 | void **ppUserData, | |||
| 752 | fts5_tokenizer *pTokenizer | |||
| 753 | ); | |||
| 754 | ||||
| 755 | /* Create a new auxiliary function */ | |||
| 756 | int (*xCreateFunction)( | |||
| 757 | fts5_api *pApi, | |||
| 758 | const char *zName, | |||
| 759 | void *pUserData, | |||
| 760 | fts5_extension_function xFunction, | |||
| 761 | void (*xDestroy)(void*) | |||
| 762 | ); | |||
| 763 | ||||
| 764 | /* APIs below this point are only available if iVersion>=3 */ | |||
| 765 | ||||
| 766 | /* Create a new tokenizer */ | |||
| 767 | int (*xCreateTokenizer_v2)( | |||
| 768 | fts5_api *pApi, | |||
| 769 | const char *zName, | |||
| 770 | void *pUserData, | |||
| 771 | fts5_tokenizer_v2 *pTokenizer, | |||
| 772 | void (*xDestroy)(void*) | |||
| 773 | ); | |||
| 774 | ||||
| 775 | /* Find an existing tokenizer */ | |||
| 776 | int (*xFindTokenizer_v2)( | |||
| 777 | fts5_api *pApi, | |||
| 778 | const char *zName, | |||
| 779 | void **ppUserData, | |||
| 780 | fts5_tokenizer_v2 **ppTokenizer | |||
| 781 | ); | |||
| 782 | }; | |||
| 783 | ||||
| 784 | /* | |||
| 785 | ** END OF REGISTRATION API | |||
| 786 | *************************************************************************/ | |||
| 787 | ||||
| 788 | #ifdef __cplusplus | |||
| 789 | } /* end of the 'extern "C"' block */ | |||
| 790 | #endif | |||
| 791 | ||||
| 792 | #endif /* _FTS5_H */ | |||
| 793 | ||||
| 794 | #line 1 "fts5Int.h" | |||
| 795 | /* | |||
| 796 | ** 2014 May 31 | |||
| 797 | ** | |||
| 798 | ** The author disclaims copyright to this source code. In place of | |||
| 799 | ** a legal notice, here is a blessing: | |||
| 800 | ** | |||
| 801 | ** May you do good and not evil. | |||
| 802 | ** May you find forgiveness for yourself and forgive others. | |||
| 803 | ** May you share freely, never taking more than you give. | |||
| 804 | ** | |||
| 805 | ****************************************************************************** | |||
| 806 | ** | |||
| 807 | */ | |||
| 808 | #ifndef _FTS5INT_H | |||
| 809 | #define _FTS5INT_H | |||
| 810 | ||||
| 811 | /* #include "fts5.h" */ | |||
| 812 | #include "sqlite3ext.h" | |||
| 813 | SQLITE_EXTENSION_INIT1const sqlite3_api_routines *sqlite3_api=0; | |||
| 814 | ||||
| 815 | #include <string.h> | |||
| 816 | #include <assert.h> | |||
| 817 | #include <stddef.h> | |||
| 818 | ||||
| 819 | #ifndef SQLITE_AMALGAMATION | |||
| 820 | ||||
| 821 | typedef unsigned char u8; | |||
| 822 | typedef unsigned int u32; | |||
| 823 | typedef unsigned short u16; | |||
| 824 | typedef short i16; | |||
| 825 | typedef sqlite3_int64 i64; | |||
| 826 | typedef sqlite3_uint64 u64; | |||
| 827 | ||||
| 828 | #ifndef ArraySize | |||
| 829 | # define ArraySize(x)((int)(sizeof(x) / sizeof(x[0]))) ((int)(sizeof(x) / sizeof(x[0]))) | |||
| 830 | #endif | |||
| 831 | ||||
| 832 | #define testcase(x) | |||
| 833 | ||||
| 834 | #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) | |||
| 835 | # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 | |||
| 836 | #endif | |||
| 837 | #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) | |||
| 838 | # define ALWAYS(X)(X) (1) | |||
| 839 | # define NEVER(X)(X) (0) | |||
| 840 | #elif !defined(NDEBUG1) | |||
| 841 | # define ALWAYS(X)(X) ((X)?1:(assert(0)((void) (0)),0)) | |||
| 842 | # define NEVER(X)(X) ((X)?(assert(0)((void) (0)),1):0) | |||
| 843 | #else | |||
| 844 | # define ALWAYS(X)(X) (X) | |||
| 845 | # define NEVER(X)(X) (X) | |||
| 846 | #endif | |||
| 847 | ||||
| 848 | #define MIN(x,y)(((x) < (y)) ? (x) : (y)) (((x) < (y)) ? (x) : (y)) | |||
| 849 | #define MAX(x,y)(((x) > (y)) ? (x) : (y)) (((x) > (y)) ? (x) : (y)) | |||
| 850 | ||||
| 851 | /* | |||
| 852 | ** Constants for the largest and smallest possible 64-bit signed integers. | |||
| 853 | */ | |||
| 854 | # define LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)) (0xffffffff|(((i64)0x7fffffff)<<32)) | |||
| 855 | # define SMALLEST_INT64(((i64)-1) - (0xffffffff|(((i64)0x7fffffff)<<32))) (((i64)-1) - LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32))) | |||
| 856 | ||||
| 857 | /* | |||
| 858 | ** This macro is used in a single assert() within fts5 to check that an | |||
| 859 | ** allocation is aligned to an 8-byte boundary. But it is a complicated | |||
| 860 | ** macro to get right for multiple platforms without generating warnings. | |||
| 861 | ** So instead of reproducing the entire definition from sqliteInt.h, we | |||
| 862 | ** just do without this assert() for the rare non-amalgamation builds. | |||
| 863 | */ | |||
| 864 | #define EIGHT_BYTE_ALIGNMENT(x)1 1 | |||
| 865 | ||||
| 866 | /* | |||
| 867 | ** Macros needed to provide flexible arrays in a portable way | |||
| 868 | */ | |||
| 869 | #ifndef offsetof | |||
| 870 | # define offsetof(ST,M)__builtin_offsetof(ST, M) ((size_t)((char*)&((ST*)0)->M - (char*)0)) | |||
| 871 | #endif | |||
| 872 | #if defined(__STDC_VERSION__201710L) && (__STDC_VERSION__201710L >= 199901L) | |||
| 873 | # define FLEXARRAY | |||
| 874 | #else | |||
| 875 | # define FLEXARRAY 1 | |||
| 876 | #endif | |||
| 877 | ||||
| 878 | #endif /* SQLITE_AMALGAMATION */ | |||
| 879 | ||||
| 880 | /* | |||
| 881 | ** Constants for the largest and smallest possible 32-bit signed integers. | |||
| 882 | */ | |||
| 883 | # define LARGEST_INT32((int)(0x7fffffff)) ((int)(0x7fffffff)) | |||
| 884 | # define SMALLEST_INT32((int)((-1) - ((int)(0x7fffffff)))) ((int)((-1) - LARGEST_INT32((int)(0x7fffffff)))) | |||
| 885 | ||||
| 886 | /* Truncate very long tokens to this many bytes. Hard limit is | |||
| 887 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset | |||
| 888 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ | |||
| 889 | #define FTS5_MAX_TOKEN_SIZE32768 32768 | |||
| 890 | ||||
| 891 | /* | |||
| 892 | ** Maximum number of prefix indexes on single FTS5 table. This must be | |||
| 893 | ** less than 32. If it is set to anything large than that, an #error | |||
| 894 | ** directive in fts5_index.c will cause the build to fail. | |||
| 895 | */ | |||
| 896 | #define FTS5_MAX_PREFIX_INDEXES31 31 | |||
| 897 | ||||
| 898 | /* | |||
| 899 | ** Maximum segments permitted in a single index | |||
| 900 | */ | |||
| 901 | #define FTS5_MAX_SEGMENT2000 2000 | |||
| 902 | ||||
| 903 | #define FTS5_DEFAULT_NEARDIST10 10 | |||
| 904 | #define FTS5_DEFAULT_RANK"bm25" "bm25" | |||
| 905 | ||||
| 906 | /* Name of rank and rowid columns */ | |||
| 907 | #define FTS5_RANK_NAME"rank" "rank" | |||
| 908 | #define FTS5_ROWID_NAME"rowid" "rowid" | |||
| 909 | ||||
| 910 | #ifdef SQLITE_DEBUG | |||
| 911 | # define FTS5_CORRUPT(11 | (1<<8)) sqlite3Fts5Corrupt() | |||
| 912 | static int sqlite3Fts5Corrupt(void); | |||
| 913 | #else | |||
| 914 | # define FTS5_CORRUPT(11 | (1<<8)) SQLITE_CORRUPT_VTAB(11 | (1<<8)) | |||
| 915 | #endif | |||
| 916 | ||||
| 917 | /* | |||
| 918 | ** The assert_nc() macro is similar to the assert() macro, except that it | |||
| 919 | ** is used for assert() conditions that are true only if it can be | |||
| 920 | ** guranteed that the database is not corrupt. | |||
| 921 | */ | |||
| 922 | #ifdef SQLITE_DEBUG | |||
| 923 | extern int sqlite3_fts5_may_be_corrupt; | |||
| 924 | # define assert_nc(x)((void) (0)) assert(sqlite3_fts5_may_be_corrupt || (x))((void) (0)) | |||
| 925 | #else | |||
| 926 | # define assert_nc(x)((void) (0)) assert(x)((void) (0)) | |||
| 927 | #endif | |||
| 928 | ||||
| 929 | /* | |||
| 930 | ** A version of memcmp() that does not cause asan errors if one of the pointer | |||
| 931 | ** parameters is NULL and the number of bytes to compare is zero. | |||
| 932 | */ | |||
| 933 | #define fts5Memcmp(s1, s2, n)((n)<=0 ? 0 : memcmp((s1), (s2), (n))) ((n)<=0 ? 0 : memcmp((s1), (s2), (n))) | |||
| 934 | ||||
| 935 | /* Mark a function parameter as unused, to suppress nuisance compiler | |||
| 936 | ** warnings. */ | |||
| 937 | #ifndef UNUSED_PARAM | |||
| 938 | # define UNUSED_PARAM(X)(void)(X) (void)(X) | |||
| 939 | #endif | |||
| 940 | ||||
| 941 | #ifndef UNUSED_PARAM2 | |||
| 942 | # define UNUSED_PARAM2(X, Y)(void)(X), (void)(Y) (void)(X), (void)(Y) | |||
| 943 | #endif | |||
| 944 | ||||
| 945 | typedef struct Fts5Global Fts5Global; | |||
| 946 | typedef struct Fts5Colset Fts5Colset; | |||
| 947 | ||||
| 948 | /* If a NEAR() clump or phrase may only match a specific set of columns, | |||
| 949 | ** then an object of the following type is used to record the set of columns. | |||
| 950 | ** Each entry in the aiCol[] array is a column that may be matched. | |||
| 951 | ** | |||
| 952 | ** This object is used by fts5_expr.c and fts5_index.c. | |||
| 953 | */ | |||
| 954 | struct Fts5Colset { | |||
| 955 | int nCol; | |||
| 956 | int aiCol[FLEXARRAY]; | |||
| 957 | }; | |||
| 958 | ||||
| 959 | /* Size (int bytes) of a complete Fts5Colset object with N columns. */ | |||
| 960 | #define SZ_FTS5COLSET(N)(sizeof(i64)*((N+2)/2)) (sizeof(i64)*((N+2)/2)) | |||
| 961 | ||||
| 962 | /************************************************************************** | |||
| 963 | ** Interface to code in fts5_config.c. fts5_config.c contains contains code | |||
| 964 | ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. | |||
| 965 | */ | |||
| 966 | ||||
| 967 | typedef struct Fts5Config Fts5Config; | |||
| 968 | typedef struct Fts5TokenizerConfig Fts5TokenizerConfig; | |||
| 969 | ||||
| 970 | struct Fts5TokenizerConfig { | |||
| 971 | Fts5Tokenizer *pTok; | |||
| 972 | fts5_tokenizer_v2 *pApi2; | |||
| 973 | fts5_tokenizer *pApi1; | |||
| 974 | const char **azArg; | |||
| 975 | int nArg; | |||
| 976 | int ePattern; /* FTS_PATTERN_XXX constant */ | |||
| 977 | const char *pLocale; /* Current locale to use */ | |||
| 978 | int nLocale; /* Size of pLocale in bytes */ | |||
| 979 | }; | |||
| 980 | ||||
| 981 | /* | |||
| 982 | ** An instance of the following structure encodes all information that can | |||
| 983 | ** be gleaned from the CREATE VIRTUAL TABLE statement. | |||
| 984 | ** | |||
| 985 | ** And all information loaded from the %_config table. | |||
| 986 | ** | |||
| 987 | ** nAutomerge: | |||
| 988 | ** The minimum number of segments that an auto-merge operation should | |||
| 989 | ** attempt to merge together. A value of 1 sets the object to use the | |||
| 990 | ** compile time default. Zero disables auto-merge altogether. | |||
| 991 | ** | |||
| 992 | ** bContentlessDelete: | |||
| 993 | ** True if the contentless_delete option was present in the CREATE | |||
| 994 | ** VIRTUAL TABLE statement. | |||
| 995 | ** | |||
| 996 | ** zContent: | |||
| 997 | ** | |||
| 998 | ** zContentRowid: | |||
| 999 | ** The value of the content_rowid= option, if one was specified. Or | |||
| 1000 | ** the string "rowid" otherwise. This text is not quoted - if it is | |||
| 1001 | ** used as part of an SQL statement it needs to be quoted appropriately. | |||
| 1002 | ** | |||
| 1003 | ** zContentExprlist: | |||
| 1004 | ** | |||
| 1005 | ** pzErrmsg: | |||
| 1006 | ** This exists in order to allow the fts5_index.c module to return a | |||
| 1007 | ** decent error message if it encounters a file-format version it does | |||
| 1008 | ** not understand. | |||
| 1009 | ** | |||
| 1010 | ** bColumnsize: | |||
| 1011 | ** True if the %_docsize table is created. | |||
| 1012 | ** | |||
| 1013 | ** bPrefixIndex: | |||
| 1014 | ** This is only used for debugging. If set to false, any prefix indexes | |||
| 1015 | ** are ignored. This value is configured using: | |||
| 1016 | ** | |||
| 1017 | ** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex); | |||
| 1018 | ** | |||
| 1019 | ** bLocale: | |||
| 1020 | ** Set to true if locale=1 was specified when the table was created. | |||
| 1021 | */ | |||
| 1022 | struct Fts5Config { | |||
| 1023 | sqlite3 *db; /* Database handle */ | |||
| 1024 | Fts5Global *pGlobal; /* Global fts5 object for handle db */ | |||
| 1025 | char *zDb; /* Database holding FTS index (e.g. "main") */ | |||
| 1026 | char *zName; /* Name of FTS index */ | |||
| 1027 | int nCol; /* Number of columns */ | |||
| 1028 | char **azCol; /* Column names */ | |||
| 1029 | u8 *abUnindexed; /* True for unindexed columns */ | |||
| 1030 | int nPrefix; /* Number of prefix indexes */ | |||
| 1031 | int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ | |||
| 1032 | int eContent; /* An FTS5_CONTENT value */ | |||
| 1033 | int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ | |||
| 1034 | int bContentlessUnindexed; /* "contentless_unindexed=" option (dflt=0) */ | |||
| 1035 | char *zContent; /* content table */ | |||
| 1036 | char *zContentRowid; /* "content_rowid=" option value */ | |||
| 1037 | int bColumnsize; /* "columnsize=" option value (dflt==1) */ | |||
| 1038 | int bTokendata; /* "tokendata=" option value (dflt==0) */ | |||
| 1039 | int bLocale; /* "locale=" option value (dflt==0) */ | |||
| 1040 | int eDetail; /* FTS5_DETAIL_XXX value */ | |||
| 1041 | char *zContentExprlist; | |||
| 1042 | Fts5TokenizerConfig t; | |||
| 1043 | int bLock; /* True when table is preparing statement */ | |||
| 1044 | ||||
| 1045 | ||||
| 1046 | /* Values loaded from the %_config table */ | |||
| 1047 | int iVersion; /* fts5 file format 'version' */ | |||
| 1048 | int iCookie; /* Incremented when %_config is modified */ | |||
| 1049 | int pgsz; /* Approximate page size used in %_data */ | |||
| 1050 | int nAutomerge; /* 'automerge' setting */ | |||
| 1051 | int nCrisisMerge; /* Maximum allowed segments per level */ | |||
| 1052 | int nUsermerge; /* 'usermerge' setting */ | |||
| 1053 | int nHashSize; /* Bytes of memory for in-memory hash */ | |||
| 1054 | char *zRank; /* Name of rank function */ | |||
| 1055 | char *zRankArgs; /* Arguments to rank function */ | |||
| 1056 | int bSecureDelete; /* 'secure-delete' */ | |||
| 1057 | int nDeleteMerge; /* 'deletemerge' */ | |||
| 1058 | int bPrefixInsttoken; /* 'prefix-insttoken' */ | |||
| 1059 | ||||
| 1060 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ | |||
| 1061 | char **pzErrmsg; | |||
| 1062 | ||||
| 1063 | #ifdef SQLITE_DEBUG | |||
| 1064 | int bPrefixIndex; /* True to use prefix-indexes */ | |||
| 1065 | #endif | |||
| 1066 | }; | |||
| 1067 | ||||
| 1068 | /* Current expected value of %_config table 'version' field. And | |||
| 1069 | ** the expected version if the 'secure-delete' option has ever been | |||
| 1070 | ** set on the table. */ | |||
| 1071 | #define FTS5_CURRENT_VERSION4 4 | |||
| 1072 | #define FTS5_CURRENT_VERSION_SECUREDELETE5 5 | |||
| 1073 | ||||
| 1074 | #define FTS5_CONTENT_NORMAL0 0 | |||
| 1075 | #define FTS5_CONTENT_NONE1 1 | |||
| 1076 | #define FTS5_CONTENT_EXTERNAL2 2 | |||
| 1077 | #define FTS5_CONTENT_UNINDEXED3 3 | |||
| 1078 | ||||
| 1079 | #define FTS5_DETAIL_FULL0 0 | |||
| 1080 | #define FTS5_DETAIL_NONE1 1 | |||
| 1081 | #define FTS5_DETAIL_COLUMNS2 2 | |||
| 1082 | ||||
| 1083 | #define FTS5_PATTERN_NONE0 0 | |||
| 1084 | #define FTS5_PATTERN_LIKE65 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */ | |||
| 1085 | #define FTS5_PATTERN_GLOB66 66 /* matches SQLITE_INDEX_CONSTRAINT_GLOB */ | |||
| 1086 | ||||
| 1087 | static int sqlite3Fts5ConfigParse( | |||
| 1088 | Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** | |||
| 1089 | ); | |||
| 1090 | static void sqlite3Fts5ConfigFree(Fts5Config*); | |||
| 1091 | ||||
| 1092 | static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig); | |||
| 1093 | ||||
| 1094 | static int sqlite3Fts5Tokenize( | |||
| 1095 | Fts5Config *pConfig, /* FTS5 Configuration object */ | |||
| 1096 | int flags, /* FTS5_TOKENIZE_* flags */ | |||
| 1097 | const char *pText, int nText, /* Text to tokenize */ | |||
| 1098 | void *pCtx, /* Context passed to xToken() */ | |||
| 1099 | int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ | |||
| 1100 | ); | |||
| 1101 | ||||
| 1102 | static void sqlite3Fts5Dequote(char *z); | |||
| 1103 | ||||
| 1104 | /* Load the contents of the %_config table */ | |||
| 1105 | static int sqlite3Fts5ConfigLoad(Fts5Config*, int); | |||
| 1106 | ||||
| 1107 | /* Set the value of a single config attribute */ | |||
| 1108 | static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, int*); | |||
| 1109 | ||||
| 1110 | static int sqlite3Fts5ConfigParseRank(const char*, char**, char**); | |||
| 1111 | ||||
| 1112 | static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...); | |||
| 1113 | ||||
| 1114 | /* | |||
| 1115 | ** End of interface to code in fts5_config.c. | |||
| 1116 | **************************************************************************/ | |||
| 1117 | ||||
| 1118 | /************************************************************************** | |||
| 1119 | ** Interface to code in fts5_buffer.c. | |||
| 1120 | */ | |||
| 1121 | ||||
| 1122 | /* | |||
| 1123 | ** Buffer object for the incremental building of string data. | |||
| 1124 | */ | |||
| 1125 | typedef struct Fts5Buffer Fts5Buffer; | |||
| 1126 | struct Fts5Buffer { | |||
| 1127 | u8 *p; | |||
| 1128 | int n; | |||
| 1129 | int nSpace; | |||
| 1130 | }; | |||
| 1131 | ||||
| 1132 | static int sqlite3Fts5BufferSize(int*, Fts5Buffer*, u32); | |||
| 1133 | static void sqlite3Fts5BufferAppendVarint(int*, Fts5Buffer*, i64); | |||
| 1134 | static void sqlite3Fts5BufferAppendBlob(int*, Fts5Buffer*, u32, const u8*); | |||
| 1135 | static void sqlite3Fts5BufferAppendString(int *, Fts5Buffer*, const char*); | |||
| 1136 | static void sqlite3Fts5BufferFree(Fts5Buffer*); | |||
| 1137 | static void sqlite3Fts5BufferZero(Fts5Buffer*); | |||
| 1138 | static void sqlite3Fts5BufferSet(int*, Fts5Buffer*, int, const u8*); | |||
| 1139 | static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...); | |||
| 1140 | ||||
| 1141 | static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...); | |||
| 1142 | ||||
| 1143 | #define fts5BufferZero(x)sqlite3Fts5BufferZero(x) sqlite3Fts5BufferZero(x) | |||
| 1144 | #define fts5BufferAppendVarint(a,b,c)sqlite3Fts5BufferAppendVarint(a,b,(i64)c) sqlite3Fts5BufferAppendVarint(a,b,(i64)c) | |||
| 1145 | #define fts5BufferFree(a)sqlite3Fts5BufferFree(a) sqlite3Fts5BufferFree(a) | |||
| 1146 | #define fts5BufferAppendBlob(a,b,c,d)sqlite3Fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d) | |||
| 1147 | #define fts5BufferSet(a,b,c,d)sqlite3Fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d) | |||
| 1148 | ||||
| 1149 | #define fts5BufferGrow(pRc,pBuf,nn)( (u32)((pBuf)->n) + (u32)(nn) <= (u32)((pBuf)->nSpace ) ? 0 : sqlite3Fts5BufferSize((pRc),(pBuf),(nn)+(pBuf)->n) ) ( \ | |||
| 1150 | (u32)((pBuf)->n) + (u32)(nn) <= (u32)((pBuf)->nSpace) ? 0 : \ | |||
| 1151 | sqlite3Fts5BufferSize((pRc),(pBuf),(nn)+(pBuf)->n) \ | |||
| 1152 | ) | |||
| 1153 | ||||
| 1154 | /* Write and decode big-endian 32-bit integer values */ | |||
| 1155 | static void sqlite3Fts5Put32(u8*, int); | |||
| 1156 | static int sqlite3Fts5Get32(const u8*); | |||
| 1157 | ||||
| 1158 | #define FTS5_POS2COLUMN(iPos)(int)((iPos >> 32) & 0x7FFFFFFF) (int)((iPos >> 32) & 0x7FFFFFFF) | |||
| 1159 | #define FTS5_POS2OFFSET(iPos)(int)(iPos & 0x7FFFFFFF) (int)(iPos & 0x7FFFFFFF) | |||
| 1160 | ||||
| 1161 | typedef struct Fts5PoslistReader Fts5PoslistReader; | |||
| 1162 | struct Fts5PoslistReader { | |||
| 1163 | /* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */ | |||
| 1164 | const u8 *a; /* Position list to iterate through */ | |||
| 1165 | int n; /* Size of buffer at a[] in bytes */ | |||
| 1166 | int i; /* Current offset in a[] */ | |||
| 1167 | ||||
| 1168 | u8 bFlag; /* For client use (any custom purpose) */ | |||
| 1169 | ||||
| 1170 | /* Output variables */ | |||
| 1171 | u8 bEof; /* Set to true at EOF */ | |||
| 1172 | i64 iPos; /* (iCol<<32) + iPos */ | |||
| 1173 | }; | |||
| 1174 | static int sqlite3Fts5PoslistReaderInit( | |||
| 1175 | const u8 *a, int n, /* Poslist buffer to iterate through */ | |||
| 1176 | Fts5PoslistReader *pIter /* Iterator object to initialize */ | |||
| 1177 | ); | |||
| 1178 | static int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader*); | |||
| 1179 | ||||
| 1180 | typedef struct Fts5PoslistWriter Fts5PoslistWriter; | |||
| 1181 | struct Fts5PoslistWriter { | |||
| 1182 | i64 iPrev; | |||
| 1183 | }; | |||
| 1184 | static int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64); | |||
| 1185 | static void sqlite3Fts5PoslistSafeAppend(Fts5Buffer*, i64*, i64); | |||
| 1186 | ||||
| 1187 | static int sqlite3Fts5PoslistNext64( | |||
| 1188 | const u8 *a, int n, /* Buffer containing poslist */ | |||
| 1189 | int *pi, /* IN/OUT: Offset within a[] */ | |||
| 1190 | i64 *piOff /* IN/OUT: Current offset */ | |||
| 1191 | ); | |||
| 1192 | ||||
| 1193 | /* Malloc utility */ | |||
| 1194 | static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte); | |||
| 1195 | static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn); | |||
| 1196 | ||||
| 1197 | /* Character set tests (like isspace(), isalpha() etc.) */ | |||
| 1198 | static int sqlite3Fts5IsBareword(char t); | |||
| 1199 | ||||
| 1200 | ||||
| 1201 | /* Bucket of terms object used by the integrity-check in offsets=0 mode. */ | |||
| 1202 | typedef struct Fts5Termset Fts5Termset; | |||
| 1203 | static int sqlite3Fts5TermsetNew(Fts5Termset**); | |||
| 1204 | static int sqlite3Fts5TermsetAdd(Fts5Termset*, int, const char*, int, int *pbPresent); | |||
| 1205 | static void sqlite3Fts5TermsetFree(Fts5Termset*); | |||
| 1206 | ||||
| 1207 | /* | |||
| 1208 | ** End of interface to code in fts5_buffer.c. | |||
| 1209 | **************************************************************************/ | |||
| 1210 | ||||
| 1211 | /************************************************************************** | |||
| 1212 | ** Interface to code in fts5_index.c. fts5_index.c contains contains code | |||
| 1213 | ** to access the data stored in the %_data table. | |||
| 1214 | */ | |||
| 1215 | ||||
| 1216 | typedef struct Fts5Index Fts5Index; | |||
| 1217 | typedef struct Fts5IndexIter Fts5IndexIter; | |||
| 1218 | ||||
| 1219 | struct Fts5IndexIter { | |||
| 1220 | i64 iRowid; | |||
| 1221 | const u8 *pData; | |||
| 1222 | int nData; | |||
| 1223 | u8 bEof; | |||
| 1224 | }; | |||
| 1225 | ||||
| 1226 | #define sqlite3Fts5IterEof(x)((x)->bEof) ((x)->bEof) | |||
| 1227 | ||||
| 1228 | /* | |||
| 1229 | ** Values used as part of the flags argument passed to IndexQuery(). | |||
| 1230 | */ | |||
| 1231 | #define FTS5INDEX_QUERY_PREFIX0x0001 0x0001 /* Prefix query */ | |||
| 1232 | #define FTS5INDEX_QUERY_DESC0x0002 0x0002 /* Docs in descending rowid order */ | |||
| 1233 | #define FTS5INDEX_QUERY_TEST_NOIDX0x0004 0x0004 /* Do not use prefix index */ | |||
| 1234 | #define FTS5INDEX_QUERY_SCAN0x0008 0x0008 /* Scan query (fts5vocab) */ | |||
| 1235 | ||||
| 1236 | /* The following are used internally by the fts5_index.c module. They are | |||
| 1237 | ** defined here only to make it easier to avoid clashes with the flags | |||
| 1238 | ** above. */ | |||
| 1239 | #define FTS5INDEX_QUERY_SKIPEMPTY0x0010 0x0010 | |||
| 1240 | #define FTS5INDEX_QUERY_NOOUTPUT0x0020 0x0020 | |||
| 1241 | #define FTS5INDEX_QUERY_SKIPHASH0x0040 0x0040 | |||
| 1242 | #define FTS5INDEX_QUERY_NOTOKENDATA0x0080 0x0080 | |||
| 1243 | #define FTS5INDEX_QUERY_SCANONETERM0x0100 0x0100 | |||
| 1244 | ||||
| 1245 | /* | |||
| 1246 | ** Create/destroy an Fts5Index object. | |||
| 1247 | */ | |||
| 1248 | static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); | |||
| 1249 | static int sqlite3Fts5IndexClose(Fts5Index *p); | |||
| 1250 | ||||
| 1251 | /* | |||
| 1252 | ** Return a simple checksum value based on the arguments. | |||
| 1253 | */ | |||
| 1254 | static u64 sqlite3Fts5IndexEntryCksum( | |||
| 1255 | i64 iRowid, | |||
| 1256 | int iCol, | |||
| 1257 | int iPos, | |||
| 1258 | int iIdx, | |||
| 1259 | const char *pTerm, | |||
| 1260 | int nTerm | |||
| 1261 | ); | |||
| 1262 | ||||
| 1263 | /* | |||
| 1264 | ** Argument p points to a buffer containing utf-8 text that is n bytes in | |||
| 1265 | ** size. Return the number of bytes in the nChar character prefix of the | |||
| 1266 | ** buffer, or 0 if there are less than nChar characters in total. | |||
| 1267 | */ | |||
| 1268 | static int sqlite3Fts5IndexCharlenToBytelen( | |||
| 1269 | const char *p, | |||
| 1270 | int nByte, | |||
| 1271 | int nChar | |||
| 1272 | ); | |||
| 1273 | ||||
| 1274 | /* | |||
| 1275 | ** Open a new iterator to iterate though all rowids that match the | |||
| 1276 | ** specified token or token prefix. | |||
| 1277 | */ | |||
| 1278 | static int sqlite3Fts5IndexQuery( | |||
| 1279 | Fts5Index *p, /* FTS index to query */ | |||
| 1280 | const char *pToken, int nToken, /* Token (or prefix) to query for */ | |||
| 1281 | int flags, /* Mask of FTS5INDEX_QUERY_X flags */ | |||
| 1282 | Fts5Colset *pColset, /* Match these columns only */ | |||
| 1283 | Fts5IndexIter **ppIter /* OUT: New iterator object */ | |||
| 1284 | ); | |||
| 1285 | ||||
| 1286 | /* | |||
| 1287 | ** The various operations on open token or token prefix iterators opened | |||
| 1288 | ** using sqlite3Fts5IndexQuery(). | |||
| 1289 | */ | |||
| 1290 | static int sqlite3Fts5IterNext(Fts5IndexIter*); | |||
| 1291 | static int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch); | |||
| 1292 | ||||
| 1293 | /* | |||
| 1294 | ** Close an iterator opened by sqlite3Fts5IndexQuery(). | |||
| 1295 | */ | |||
| 1296 | static void sqlite3Fts5IterClose(Fts5IndexIter*); | |||
| 1297 | ||||
| 1298 | /* | |||
| 1299 | ** Close the reader blob handle, if it is open. | |||
| 1300 | */ | |||
| 1301 | static void sqlite3Fts5IndexCloseReader(Fts5Index*); | |||
| 1302 | ||||
| 1303 | /* | |||
| 1304 | ** This interface is used by the fts5vocab module. | |||
| 1305 | */ | |||
| 1306 | static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); | |||
| 1307 | static int sqlite3Fts5IterNextScan(Fts5IndexIter*); | |||
| 1308 | static void *sqlite3Fts5StructureRef(Fts5Index*); | |||
| 1309 | static void sqlite3Fts5StructureRelease(void*); | |||
| 1310 | static int sqlite3Fts5StructureTest(Fts5Index*, void*); | |||
| 1311 | ||||
| 1312 | /* | |||
| 1313 | ** Used by xInstToken(): | |||
| 1314 | */ | |||
| 1315 | static int sqlite3Fts5IterToken( | |||
| 1316 | Fts5IndexIter *pIndexIter, | |||
| 1317 | const char *pToken, int nToken, | |||
| 1318 | i64 iRowid, | |||
| 1319 | int iCol, | |||
| 1320 | int iOff, | |||
| 1321 | const char **ppOut, int *pnOut | |||
| 1322 | ); | |||
| 1323 | ||||
| 1324 | /* | |||
| 1325 | ** Insert or remove data to or from the index. Each time a document is | |||
| 1326 | ** added to or removed from the index, this function is called one or more | |||
| 1327 | ** times. | |||
| 1328 | ** | |||
| 1329 | ** For an insert, it must be called once for each token in the new document. | |||
| 1330 | ** If the operation is a delete, it must be called (at least) once for each | |||
| 1331 | ** unique token in the document with an iCol value less than zero. The iPos | |||
| 1332 | ** argument is ignored for a delete. | |||
| 1333 | */ | |||
| 1334 | static int sqlite3Fts5IndexWrite( | |||
| 1335 | Fts5Index *p, /* Index to write to */ | |||
| 1336 | int iCol, /* Column token appears in (-ve -> delete) */ | |||
| 1337 | int iPos, /* Position of token within column */ | |||
| 1338 | const char *pToken, int nToken /* Token to add or remove to or from index */ | |||
| 1339 | ); | |||
| 1340 | ||||
| 1341 | /* | |||
| 1342 | ** Indicate that subsequent calls to sqlite3Fts5IndexWrite() pertain to | |||
| 1343 | ** document iDocid. | |||
| 1344 | */ | |||
| 1345 | static int sqlite3Fts5IndexBeginWrite( | |||
| 1346 | Fts5Index *p, /* Index to write to */ | |||
| 1347 | int bDelete, /* True if current operation is a delete */ | |||
| 1348 | i64 iDocid /* Docid to add or remove data from */ | |||
| 1349 | ); | |||
| 1350 | ||||
| 1351 | /* | |||
| 1352 | ** Flush any data stored in the in-memory hash tables to the database. | |||
| 1353 | ** Also close any open blob handles. | |||
| 1354 | */ | |||
| 1355 | static int sqlite3Fts5IndexSync(Fts5Index *p); | |||
| 1356 | ||||
| 1357 | /* | |||
| 1358 | ** Discard any data stored in the in-memory hash tables. Do not write it | |||
| 1359 | ** to the database. Additionally, assume that the contents of the %_data | |||
| 1360 | ** table may have changed on disk. So any in-memory caches of %_data | |||
| 1361 | ** records must be invalidated. | |||
| 1362 | */ | |||
| 1363 | static int sqlite3Fts5IndexRollback(Fts5Index *p); | |||
| 1364 | ||||
| 1365 | /* | |||
| 1366 | ** Get or set the "averages" values. | |||
| 1367 | */ | |||
| 1368 | static int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize); | |||
| 1369 | static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int); | |||
| 1370 | ||||
| 1371 | /* | |||
| 1372 | ** Functions called by the storage module as part of integrity-check. | |||
| 1373 | */ | |||
| 1374 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum, int bUseCksum); | |||
| 1375 | ||||
| 1376 | /* | |||
| 1377 | ** Called during virtual module initialization to register UDF | |||
| 1378 | ** fts5_decode() with SQLite | |||
| 1379 | */ | |||
| 1380 | static int sqlite3Fts5IndexInit(sqlite3*); | |||
| 1381 | ||||
| 1382 | static int sqlite3Fts5IndexSetCookie(Fts5Index*, int); | |||
| 1383 | ||||
| 1384 | /* | |||
| 1385 | ** Return the total number of entries read from the %_data table by | |||
| 1386 | ** this connection since it was created. | |||
| 1387 | */ | |||
| 1388 | static int sqlite3Fts5IndexReads(Fts5Index *p); | |||
| 1389 | ||||
| 1390 | static int sqlite3Fts5IndexReinit(Fts5Index *p); | |||
| 1391 | static int sqlite3Fts5IndexOptimize(Fts5Index *p); | |||
| 1392 | static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge); | |||
| 1393 | static int sqlite3Fts5IndexReset(Fts5Index *p); | |||
| 1394 | ||||
| 1395 | static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); | |||
| 1396 | ||||
| 1397 | static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); | |||
| 1398 | static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); | |||
| 1399 | ||||
| 1400 | static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); | |||
| 1401 | ||||
| 1402 | /* Used to populate hash tables for xInstToken in detail=none/column mode. */ | |||
| 1403 | static int sqlite3Fts5IndexIterWriteTokendata( | |||
| 1404 | Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff | |||
| 1405 | ); | |||
| 1406 | ||||
| 1407 | /* | |||
| 1408 | ** End of interface to code in fts5_index.c. | |||
| 1409 | **************************************************************************/ | |||
| 1410 | ||||
| 1411 | /************************************************************************** | |||
| 1412 | ** Interface to code in fts5_varint.c. | |||
| 1413 | */ | |||
| 1414 | static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v); | |||
| 1415 | static int sqlite3Fts5GetVarintLen(u32 iVal); | |||
| 1416 | static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*); | |||
| 1417 | static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); | |||
| 1418 | ||||
| 1419 | #define fts5GetVarint32(a,b)sqlite3Fts5GetVarint32(a,(u32*)&(b)) sqlite3Fts5GetVarint32(a,(u32*)&(b)) | |||
| 1420 | #define fts5GetVarintsqlite3Fts5GetVarint sqlite3Fts5GetVarint | |||
| 1421 | ||||
| 1422 | #define fts5FastGetVarint32(a, iOff, nVal){ nVal = (a)[iOff++]; if( nVal & 0x80 ){ iOff--; iOff += sqlite3Fts5GetVarint32 (&(a)[iOff],(u32*)&(nVal)); } } { \ | |||
| 1423 | nVal = (a)[iOff++]; \ | |||
| 1424 | if( nVal & 0x80 ){ \ | |||
| 1425 | iOff--; \ | |||
| 1426 | iOff += fts5GetVarint32(&(a)[iOff], nVal)sqlite3Fts5GetVarint32(&(a)[iOff],(u32*)&(nVal)); \ | |||
| 1427 | } \ | |||
| 1428 | } | |||
| 1429 | ||||
| 1430 | ||||
| 1431 | /* | |||
| 1432 | ** End of interface to code in fts5_varint.c. | |||
| 1433 | **************************************************************************/ | |||
| 1434 | ||||
| 1435 | ||||
| 1436 | /************************************************************************** | |||
| 1437 | ** Interface to code in fts5_main.c. | |||
| 1438 | */ | |||
| 1439 | ||||
| 1440 | /* | |||
| 1441 | ** Virtual-table object. | |||
| 1442 | */ | |||
| 1443 | typedef struct Fts5Table Fts5Table; | |||
| 1444 | struct Fts5Table { | |||
| 1445 | sqlite3_vtab base; /* Base class used by SQLite core */ | |||
| 1446 | Fts5Config *pConfig; /* Virtual table configuration */ | |||
| 1447 | Fts5Index *pIndex; /* Full-text index */ | |||
| 1448 | }; | |||
| 1449 | ||||
| 1450 | static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig); | |||
| 1451 | ||||
| 1452 | static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); | |||
| 1453 | ||||
| 1454 | static int sqlite3Fts5FlushToDisk(Fts5Table*); | |||
| 1455 | ||||
| 1456 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); | |||
| 1457 | static void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc); | |||
| 1458 | ||||
| 1459 | static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal); | |||
| 1460 | static int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal, | |||
| 1461 | const char **ppText, int *pnText, const char **ppLoc, int *pnLoc | |||
| 1462 | ); | |||
| 1463 | ||||
| 1464 | /* | |||
| 1465 | ** End of interface to code in fts5.c. | |||
| 1466 | **************************************************************************/ | |||
| 1467 | ||||
| 1468 | /************************************************************************** | |||
| 1469 | ** Interface to code in fts5_hash.c. | |||
| 1470 | */ | |||
| 1471 | typedef struct Fts5Hash Fts5Hash; | |||
| 1472 | ||||
| 1473 | /* | |||
| 1474 | ** Create a hash table, free a hash table. | |||
| 1475 | */ | |||
| 1476 | static int sqlite3Fts5HashNew(Fts5Config*, Fts5Hash**, int *pnSize); | |||
| 1477 | static void sqlite3Fts5HashFree(Fts5Hash*); | |||
| 1478 | ||||
| 1479 | static int sqlite3Fts5HashWrite( | |||
| 1480 | Fts5Hash*, | |||
| 1481 | i64 iRowid, /* Rowid for this entry */ | |||
| 1482 | int iCol, /* Column token appears in (-ve -> delete) */ | |||
| 1483 | int iPos, /* Position of token within column */ | |||
| 1484 | char bByte, | |||
| 1485 | const char *pToken, int nToken /* Token to add or remove to or from index */ | |||
| 1486 | ); | |||
| 1487 | ||||
| 1488 | /* | |||
| 1489 | ** Empty (but do not delete) a hash table. | |||
| 1490 | */ | |||
| 1491 | static void sqlite3Fts5HashClear(Fts5Hash*); | |||
| 1492 | ||||
| 1493 | /* | |||
| 1494 | ** Return true if the hash is empty, false otherwise. | |||
| 1495 | */ | |||
| 1496 | static int sqlite3Fts5HashIsEmpty(Fts5Hash*); | |||
| 1497 | ||||
| 1498 | static int sqlite3Fts5HashQuery( | |||
| 1499 | Fts5Hash*, /* Hash table to query */ | |||
| 1500 | int nPre, | |||
| 1501 | const char *pTerm, int nTerm, /* Query term */ | |||
| 1502 | void **ppObj, /* OUT: Pointer to doclist for pTerm */ | |||
| 1503 | int *pnDoclist /* OUT: Size of doclist in bytes */ | |||
| 1504 | ); | |||
| 1505 | ||||
| 1506 | static int sqlite3Fts5HashScanInit( | |||
| 1507 | Fts5Hash*, /* Hash table to query */ | |||
| 1508 | const char *pTerm, int nTerm /* Query prefix */ | |||
| 1509 | ); | |||
| 1510 | static void sqlite3Fts5HashScanNext(Fts5Hash*); | |||
| 1511 | static int sqlite3Fts5HashScanEof(Fts5Hash*); | |||
| 1512 | static void sqlite3Fts5HashScanEntry(Fts5Hash *, | |||
| 1513 | const char **pzTerm, /* OUT: term (nul-terminated) */ | |||
| 1514 | int *pnTerm, /* OUT: Size of term in bytes */ | |||
| 1515 | const u8 **ppDoclist, /* OUT: pointer to doclist */ | |||
| 1516 | int *pnDoclist /* OUT: size of doclist in bytes */ | |||
| 1517 | ); | |||
| 1518 | ||||
| 1519 | ||||
| 1520 | ||||
| 1521 | /* | |||
| 1522 | ** End of interface to code in fts5_hash.c. | |||
| 1523 | **************************************************************************/ | |||
| 1524 | ||||
| 1525 | /************************************************************************** | |||
| 1526 | ** Interface to code in fts5_storage.c. fts5_storage.c contains contains | |||
| 1527 | ** code to access the data stored in the %_content and %_docsize tables. | |||
| 1528 | */ | |||
| 1529 | ||||
| 1530 | #define FTS5_STMT_SCAN_ASC0 0 /* SELECT rowid, * FROM ... ORDER BY 1 ASC */ | |||
| 1531 | #define FTS5_STMT_SCAN_DESC1 1 /* SELECT rowid, * FROM ... ORDER BY 1 DESC */ | |||
| 1532 | #define FTS5_STMT_LOOKUP2 2 /* SELECT rowid, * FROM ... WHERE rowid=? */ | |||
| 1533 | ||||
| 1534 | typedef struct Fts5Storage Fts5Storage; | |||
| 1535 | ||||
| 1536 | static int sqlite3Fts5StorageOpen(Fts5Config*, Fts5Index*, int, Fts5Storage**, char**); | |||
| 1537 | static int sqlite3Fts5StorageClose(Fts5Storage *p); | |||
| 1538 | static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); | |||
| 1539 | ||||
| 1540 | static int sqlite3Fts5DropAll(Fts5Config*); | |||
| 1541 | static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); | |||
| 1542 | ||||
| 1543 | static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); | |||
| 1544 | static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*); | |||
| 1545 | static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); | |||
| 1546 | ||||
| 1547 | static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); | |||
| 1548 | ||||
| 1549 | static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); | |||
| 1550 | static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); | |||
| 1551 | ||||
| 1552 | static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol); | |||
| 1553 | static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnAvg); | |||
| 1554 | static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow); | |||
| 1555 | ||||
| 1556 | static int sqlite3Fts5StorageSync(Fts5Storage *p); | |||
| 1557 | static int sqlite3Fts5StorageRollback(Fts5Storage *p); | |||
| 1558 | ||||
| 1559 | static int sqlite3Fts5StorageConfigValue( | |||
| 1560 | Fts5Storage *p, const char*, sqlite3_value*, int | |||
| 1561 | ); | |||
| 1562 | ||||
| 1563 | static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p); | |||
| 1564 | static int sqlite3Fts5StorageRebuild(Fts5Storage *p); | |||
| 1565 | static int sqlite3Fts5StorageOptimize(Fts5Storage *p); | |||
| 1566 | static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge); | |||
| 1567 | static int sqlite3Fts5StorageReset(Fts5Storage *p); | |||
| 1568 | ||||
| 1569 | static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage*); | |||
| 1570 | static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel); | |||
| 1571 | ||||
| 1572 | /* | |||
| 1573 | ** End of interface to code in fts5_storage.c. | |||
| 1574 | **************************************************************************/ | |||
| 1575 | ||||
| 1576 | ||||
| 1577 | /************************************************************************** | |||
| 1578 | ** Interface to code in fts5_expr.c. | |||
| 1579 | */ | |||
| 1580 | typedef struct Fts5Expr Fts5Expr; | |||
| 1581 | typedef struct Fts5ExprNode Fts5ExprNode; | |||
| 1582 | typedef struct Fts5Parse Fts5Parse; | |||
| 1583 | typedef struct Fts5Token Fts5Token; | |||
| 1584 | typedef struct Fts5ExprPhrase Fts5ExprPhrase; | |||
| 1585 | typedef struct Fts5ExprNearset Fts5ExprNearset; | |||
| 1586 | ||||
| 1587 | struct Fts5Token { | |||
| 1588 | const char *p; /* Token text (not NULL terminated) */ | |||
| 1589 | int n; /* Size of buffer p in bytes */ | |||
| 1590 | }; | |||
| 1591 | ||||
| 1592 | /* Parse a MATCH expression. */ | |||
| 1593 | static int sqlite3Fts5ExprNew( | |||
| 1594 | Fts5Config *pConfig, | |||
| 1595 | int bPhraseToAnd, | |||
| 1596 | int iCol, /* Column on LHS of MATCH operator */ | |||
| 1597 | const char *zExpr, | |||
| 1598 | Fts5Expr **ppNew, | |||
| 1599 | char **pzErr | |||
| 1600 | ); | |||
| 1601 | static int sqlite3Fts5ExprPattern( | |||
| 1602 | Fts5Config *pConfig, | |||
| 1603 | int bGlob, | |||
| 1604 | int iCol, | |||
| 1605 | const char *zText, | |||
| 1606 | Fts5Expr **pp | |||
| 1607 | ); | |||
| 1608 | ||||
| 1609 | /* | |||
| 1610 | ** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc); | |||
| 1611 | ** rc==SQLITE_OK && 0==sqlite3Fts5ExprEof(pExpr); | |||
| 1612 | ** rc = sqlite3Fts5ExprNext(pExpr) | |||
| 1613 | ** ){ | |||
| 1614 | ** // The document with rowid iRowid matches the expression! | |||
| 1615 | ** i64 iRowid = sqlite3Fts5ExprRowid(pExpr); | |||
| 1616 | ** } | |||
| 1617 | */ | |||
| 1618 | static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, i64, int bDesc); | |||
| 1619 | static int sqlite3Fts5ExprNext(Fts5Expr*, i64 iMax); | |||
| 1620 | static int sqlite3Fts5ExprEof(Fts5Expr*); | |||
| 1621 | static i64 sqlite3Fts5ExprRowid(Fts5Expr*); | |||
| 1622 | ||||
| 1623 | static void sqlite3Fts5ExprFree(Fts5Expr*); | |||
| 1624 | static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2); | |||
| 1625 | ||||
| 1626 | /* Called during startup to register a UDF with SQLite */ | |||
| 1627 | static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*); | |||
| 1628 | ||||
| 1629 | static int sqlite3Fts5ExprPhraseCount(Fts5Expr*); | |||
| 1630 | static int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase); | |||
| 1631 | static int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **); | |||
| 1632 | ||||
| 1633 | typedef struct Fts5PoslistPopulator Fts5PoslistPopulator; | |||
| 1634 | static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr*, int); | |||
| 1635 | static int sqlite3Fts5ExprPopulatePoslists( | |||
| 1636 | Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int | |||
| 1637 | ); | |||
| 1638 | static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64); | |||
| 1639 | ||||
| 1640 | static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); | |||
| 1641 | ||||
| 1642 | static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); | |||
| 1643 | ||||
| 1644 | static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); | |||
| 1645 | static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*); | |||
| 1646 | static void sqlite3Fts5ExprClearTokens(Fts5Expr*); | |||
| 1647 | ||||
| 1648 | /******************************************* | |||
| 1649 | ** The fts5_expr.c API above this point is used by the other hand-written | |||
| 1650 | ** C code in this module. The interfaces below this point are called by | |||
| 1651 | ** the parser code in fts5parse.y. */ | |||
| 1652 | ||||
| 1653 | static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...); | |||
| 1654 | ||||
| 1655 | static Fts5ExprNode *sqlite3Fts5ParseNode( | |||
| 1656 | Fts5Parse *pParse, | |||
| 1657 | int eType, | |||
| 1658 | Fts5ExprNode *pLeft, | |||
| 1659 | Fts5ExprNode *pRight, | |||
| 1660 | Fts5ExprNearset *pNear | |||
| 1661 | ); | |||
| 1662 | ||||
| 1663 | static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( | |||
| 1664 | Fts5Parse *pParse, | |||
| 1665 | Fts5ExprNode *pLeft, | |||
| 1666 | Fts5ExprNode *pRight | |||
| 1667 | ); | |||
| 1668 | ||||
| 1669 | static Fts5ExprPhrase *sqlite3Fts5ParseTerm( | |||
| 1670 | Fts5Parse *pParse, | |||
| 1671 | Fts5ExprPhrase *pPhrase, | |||
| 1672 | Fts5Token *pToken, | |||
| 1673 | int bPrefix | |||
| 1674 | ); | |||
| 1675 | ||||
| 1676 | static void sqlite3Fts5ParseSetCaret(Fts5ExprPhrase*); | |||
| 1677 | ||||
| 1678 | static Fts5ExprNearset *sqlite3Fts5ParseNearset( | |||
| 1679 | Fts5Parse*, | |||
| 1680 | Fts5ExprNearset*, | |||
| 1681 | Fts5ExprPhrase* | |||
| 1682 | ); | |||
| 1683 | ||||
| 1684 | static Fts5Colset *sqlite3Fts5ParseColset( | |||
| 1685 | Fts5Parse*, | |||
| 1686 | Fts5Colset*, | |||
| 1687 | Fts5Token * | |||
| 1688 | ); | |||
| 1689 | ||||
| 1690 | static void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*); | |||
| 1691 | static void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*); | |||
| 1692 | static void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); | |||
| 1693 | ||||
| 1694 | static void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); | |||
| 1695 | static void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNode*, Fts5Colset*); | |||
| 1696 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse*, Fts5Colset*); | |||
| 1697 | static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p); | |||
| 1698 | static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*); | |||
| 1699 | ||||
| 1700 | /* | |||
| 1701 | ** End of interface to code in fts5_expr.c. | |||
| 1702 | **************************************************************************/ | |||
| 1703 | ||||
| 1704 | ||||
| 1705 | ||||
| 1706 | /************************************************************************** | |||
| 1707 | ** Interface to code in fts5_aux.c. | |||
| 1708 | */ | |||
| 1709 | ||||
| 1710 | static int sqlite3Fts5AuxInit(fts5_api*); | |||
| 1711 | /* | |||
| 1712 | ** End of interface to code in fts5_aux.c. | |||
| 1713 | **************************************************************************/ | |||
| 1714 | ||||
| 1715 | /************************************************************************** | |||
| 1716 | ** Interface to code in fts5_tokenizer.c. | |||
| 1717 | */ | |||
| 1718 | ||||
| 1719 | static int sqlite3Fts5TokenizerInit(fts5_api*); | |||
| 1720 | static int sqlite3Fts5TokenizerPattern( | |||
| 1721 | int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), | |||
| 1722 | Fts5Tokenizer *pTok | |||
| 1723 | ); | |||
| 1724 | static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig*); | |||
| 1725 | /* | |||
| 1726 | ** End of interface to code in fts5_tokenizer.c. | |||
| 1727 | **************************************************************************/ | |||
| 1728 | ||||
| 1729 | /************************************************************************** | |||
| 1730 | ** Interface to code in fts5_vocab.c. | |||
| 1731 | */ | |||
| 1732 | ||||
| 1733 | static int sqlite3Fts5VocabInit(Fts5Global*, sqlite3*); | |||
| 1734 | ||||
| 1735 | /* | |||
| 1736 | ** End of interface to code in fts5_vocab.c. | |||
| 1737 | **************************************************************************/ | |||
| 1738 | ||||
| 1739 | ||||
| 1740 | /************************************************************************** | |||
| 1741 | ** Interface to automatically generated code in fts5_unicode2.c. | |||
| 1742 | */ | |||
| 1743 | static int sqlite3Fts5UnicodeIsdiacritic(int c); | |||
| 1744 | static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic); | |||
| 1745 | ||||
| 1746 | static int sqlite3Fts5UnicodeCatParse(const char*, u8*); | |||
| 1747 | static int sqlite3Fts5UnicodeCategory(u32 iCode); | |||
| 1748 | static void sqlite3Fts5UnicodeAscii(u8*, u8*); | |||
| 1749 | /* | |||
| 1750 | ** End of interface to code in fts5_unicode2.c. | |||
| 1751 | **************************************************************************/ | |||
| 1752 | ||||
| 1753 | #endif | |||
| 1754 | ||||
| 1755 | #line 1 "fts5parse.h" | |||
| 1756 | #define FTS5_OR1 1 | |||
| 1757 | #define FTS5_AND2 2 | |||
| 1758 | #define FTS5_NOT3 3 | |||
| 1759 | #define FTS5_TERM4 4 | |||
| 1760 | #define FTS5_COLON5 5 | |||
| 1761 | #define FTS5_MINUS6 6 | |||
| 1762 | #define FTS5_LCP7 7 | |||
| 1763 | #define FTS5_RCP8 8 | |||
| 1764 | #define FTS5_STRING9 9 | |||
| 1765 | #define FTS5_LP10 10 | |||
| 1766 | #define FTS5_RP11 11 | |||
| 1767 | #define FTS5_CARET12 12 | |||
| 1768 | #define FTS5_COMMA13 13 | |||
| 1769 | #define FTS5_PLUS14 14 | |||
| 1770 | #define FTS5_STAR15 15 | |||
| 1771 | ||||
| 1772 | #line 1 "fts5parse.c" | |||
| 1773 | /* This file is automatically generated by Lemon from input grammar | |||
| 1774 | ** source file "fts5parse.y". | |||
| 1775 | */ | |||
| 1776 | /* | |||
| 1777 | ** 2000-05-29 | |||
| 1778 | ** | |||
| 1779 | ** The author disclaims copyright to this source code. In place of | |||
| 1780 | ** a legal notice, here is a blessing: | |||
| 1781 | ** | |||
| 1782 | ** May you do good and not evil. | |||
| 1783 | ** May you find forgiveness for yourself and forgive others. | |||
| 1784 | ** May you share freely, never taking more than you give. | |||
| 1785 | ** | |||
| 1786 | ************************************************************************* | |||
| 1787 | ** Driver template for the LEMON parser generator. | |||
| 1788 | ** | |||
| 1789 | ** The "lemon" program processes an LALR(1) input grammar file, then uses | |||
| 1790 | ** this template to construct a parser. The "lemon" program inserts text | |||
| 1791 | ** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the | |||
| 1792 | ** interstitial "-" characters) contained in this template is changed into | |||
| 1793 | ** the value of the %name directive from the grammar. Otherwise, the content | |||
| 1794 | ** of this template is copied straight through into the generate parser | |||
| 1795 | ** source file. | |||
| 1796 | ** | |||
| 1797 | ** The following is the concatenation of all %include directives from the | |||
| 1798 | ** input grammar file: | |||
| 1799 | */ | |||
| 1800 | /************ Begin %include sections from the grammar ************************/ | |||
| 1801 | #line 47 "fts5parse.y" | |||
| 1802 | ||||
| 1803 | /* #include "fts5Int.h" */ | |||
| 1804 | /* #include "fts5parse.h" */ | |||
| 1805 | ||||
| 1806 | /* | |||
| 1807 | ** Disable all error recovery processing in the parser push-down | |||
| 1808 | ** automaton. | |||
| 1809 | */ | |||
| 1810 | #define fts5YYNOERRORRECOVERY1 1 | |||
| 1811 | ||||
| 1812 | /* | |||
| 1813 | ** Make fts5yytestcase() the same as testcase() | |||
| 1814 | */ | |||
| 1815 | #define fts5yytestcase(X) testcase(X) | |||
| 1816 | ||||
| 1817 | /* | |||
| 1818 | ** Indicate that sqlite3ParserFree() will never be called with a null | |||
| 1819 | ** pointer. | |||
| 1820 | */ | |||
| 1821 | #define fts5YYPARSEFREENOTNULL1 1 | |||
| 1822 | ||||
| 1823 | /* | |||
| 1824 | ** Alternative datatype for the argument to the malloc() routine passed | |||
| 1825 | ** into sqlite3ParserAlloc(). The default is size_t. | |||
| 1826 | */ | |||
| 1827 | #define fts5YYMALLOCARGTYPEu64 u64 | |||
| 1828 | ||||
| 1829 | #line 58 "fts5parse.c" | |||
| 1830 | /**************** End of %include directives **********************************/ | |||
| 1831 | /* These constants specify the various numeric values for terminal symbols. | |||
| 1832 | ***************** Begin token definitions *************************************/ | |||
| 1833 | #ifndef FTS5_OR1 | |||
| 1834 | #define FTS5_OR1 1 | |||
| 1835 | #define FTS5_AND2 2 | |||
| 1836 | #define FTS5_NOT3 3 | |||
| 1837 | #define FTS5_TERM4 4 | |||
| 1838 | #define FTS5_COLON5 5 | |||
| 1839 | #define FTS5_MINUS6 6 | |||
| 1840 | #define FTS5_LCP7 7 | |||
| 1841 | #define FTS5_RCP8 8 | |||
| 1842 | #define FTS5_STRING9 9 | |||
| 1843 | #define FTS5_LP10 10 | |||
| 1844 | #define FTS5_RP11 11 | |||
| 1845 | #define FTS5_CARET12 12 | |||
| 1846 | #define FTS5_COMMA13 13 | |||
| 1847 | #define FTS5_PLUS14 14 | |||
| 1848 | #define FTS5_STAR15 15 | |||
| 1849 | #endif | |||
| 1850 | /**************** End token definitions ***************************************/ | |||
| 1851 | ||||
| 1852 | /* The next sections is a series of control #defines. | |||
| 1853 | ** various aspects of the generated parser. | |||
| 1854 | ** fts5YYCODETYPE is the data type used to store the integer codes | |||
| 1855 | ** that represent terminal and non-terminal symbols. | |||
| 1856 | ** "unsigned char" is used if there are fewer than | |||
| 1857 | ** 256 symbols. Larger types otherwise. | |||
| 1858 | ** fts5YYNOCODE is a number of type fts5YYCODETYPE that is not used for | |||
| 1859 | ** any terminal or nonterminal symbol. | |||
| 1860 | ** fts5YYFALLBACK If defined, this indicates that one or more tokens | |||
| 1861 | ** (also known as: "terminal symbols") have fall-back | |||
| 1862 | ** values which should be used if the original symbol | |||
| 1863 | ** would not parse. This permits keywords to sometimes | |||
| 1864 | ** be used as identifiers, for example. | |||
| 1865 | ** fts5YYACTIONTYPE is the data type used for "action codes" - numbers | |||
| 1866 | ** that indicate what to do in response to the next | |||
| 1867 | ** token. | |||
| 1868 | ** sqlite3Fts5ParserFTS5TOKENTYPE is the data type used for minor type for terminal | |||
| 1869 | ** symbols. Background: A "minor type" is a semantic | |||
| 1870 | ** value associated with a terminal or non-terminal | |||
| 1871 | ** symbols. For example, for an "ID" terminal symbol, | |||
| 1872 | ** the minor type might be the name of the identifier. | |||
| 1873 | ** Each non-terminal can have a different minor type. | |||
| 1874 | ** Terminal symbols all have the same minor type, though. | |||
| 1875 | ** This macros defines the minor type for terminal | |||
| 1876 | ** symbols. | |||
| 1877 | ** fts5YYMINORTYPE is the data type used for all minor types. | |||
| 1878 | ** This is typically a union of many types, one of | |||
| 1879 | ** which is sqlite3Fts5ParserFTS5TOKENTYPE. The entry in the union | |||
| 1880 | ** for terminal symbols is called "fts5yy0". | |||
| 1881 | ** fts5YYSTACKDEPTH is the maximum depth of the parser's stack. If | |||
| 1882 | ** zero the stack is dynamically sized using realloc() | |||
| 1883 | ** sqlite3Fts5ParserARG_SDECL A static variable declaration for the %extra_argument | |||
| 1884 | ** sqlite3Fts5ParserARG_PDECL A parameter declaration for the %extra_argument | |||
| 1885 | ** sqlite3Fts5ParserARG_PARAM Code to pass %extra_argument as a subroutine parameter | |||
| 1886 | ** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser | |||
| 1887 | ** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser | |||
| 1888 | ** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context | |||
| 1889 | ** fts5YYREALLOC Name of the realloc() function to use | |||
| 1890 | ** fts5YYFREE Name of the free() function to use | |||
| 1891 | ** fts5YYDYNSTACK True if stack space should be extended on heap | |||
| 1892 | ** fts5YYERRORSYMBOL is the code number of the error symbol. If not | |||
| 1893 | ** defined, then do no error processing. | |||
| 1894 | ** fts5YYNSTATE the combined number of states. | |||
| 1895 | ** fts5YYNRULE the number of rules in the grammar | |||
| 1896 | ** fts5YYNFTS5TOKEN Number of terminal symbols | |||
| 1897 | ** fts5YY_MAX_SHIFT Maximum value for shift actions | |||
| 1898 | ** fts5YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions | |||
| 1899 | ** fts5YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions | |||
| 1900 | ** fts5YY_ERROR_ACTION The fts5yy_action[] code for syntax error | |||
| 1901 | ** fts5YY_ACCEPT_ACTION The fts5yy_action[] code for accept | |||
| 1902 | ** fts5YY_NO_ACTION The fts5yy_action[] code for no-op | |||
| 1903 | ** fts5YY_MIN_REDUCE Minimum value for reduce actions | |||
| 1904 | ** fts5YY_MAX_REDUCE Maximum value for reduce actions | |||
| 1905 | ** fts5YY_MIN_DSTRCTR Minimum symbol value that has a destructor | |||
| 1906 | ** fts5YY_MAX_DSTRCTR Maximum symbol value that has a destructor | |||
| 1907 | */ | |||
| 1908 | #ifndef INTERFACE1 | |||
| 1909 | # define INTERFACE1 1 | |||
| 1910 | #endif | |||
| 1911 | /************* Begin control #defines *****************************************/ | |||
| 1912 | #define fts5YYCODETYPEunsigned char unsigned char | |||
| 1913 | #define fts5YYNOCODE27 27 | |||
| 1914 | #define fts5YYACTIONTYPEunsigned char unsigned char | |||
| 1915 | #define sqlite3Fts5ParserFTS5TOKENTYPEFts5Token Fts5Token | |||
| 1916 | typedef union { | |||
| 1917 | int fts5yyinit; | |||
| 1918 | sqlite3Fts5ParserFTS5TOKENTYPEFts5Token fts5yy0; | |||
| 1919 | int fts5yy4; | |||
| 1920 | Fts5Colset* fts5yy11; | |||
| 1921 | Fts5ExprNode* fts5yy24; | |||
| 1922 | Fts5ExprNearset* fts5yy46; | |||
| 1923 | Fts5ExprPhrase* fts5yy53; | |||
| 1924 | } fts5YYMINORTYPE; | |||
| 1925 | #ifndef fts5YYSTACKDEPTH100 | |||
| 1926 | #define fts5YYSTACKDEPTH100 100 | |||
| 1927 | #endif | |||
| 1928 | #define sqlite3Fts5ParserARG_SDECLFts5Parse *pParse; Fts5Parse *pParse; | |||
| 1929 | #define sqlite3Fts5ParserARG_PDECL,Fts5Parse *pParse ,Fts5Parse *pParse | |||
| 1930 | #define sqlite3Fts5ParserARG_PARAM,pParse ,pParse | |||
| 1931 | #define sqlite3Fts5ParserARG_FETCHFts5Parse *pParse=fts5yypParser->pParse; Fts5Parse *pParse=fts5yypParser->pParse; | |||
| 1932 | #define sqlite3Fts5ParserARG_STOREfts5yypParser->pParse=pParse; fts5yypParser->pParse=pParse; | |||
| 1933 | #undef fts5YYREALLOCrealloc | |||
| 1934 | #define fts5YYREALLOCrealloc realloc | |||
| 1935 | #undef fts5YYFREEfree | |||
| 1936 | #define fts5YYFREEfree free | |||
| 1937 | #undef fts5YYDYNSTACK0 | |||
| 1938 | #define fts5YYDYNSTACK0 0 | |||
| 1939 | #undef fts5YYSIZELIMIT | |||
| 1940 | #define sqlite3Fts5ParserCTX(P)0 0 | |||
| 1941 | #define sqlite3Fts5ParserCTX_SDECL | |||
| 1942 | #define sqlite3Fts5ParserCTX_PDECL | |||
| 1943 | #define sqlite3Fts5ParserCTX_PARAM | |||
| 1944 | #define sqlite3Fts5ParserCTX_FETCH | |||
| 1945 | #define sqlite3Fts5ParserCTX_STORE | |||
| 1946 | #undef fts5YYERRORSYMBOL | |||
| 1947 | #undef fts5YYERRSYMDT | |||
| 1948 | #undef fts5YYFALLBACK | |||
| 1949 | #define fts5YYNSTATE35 35 | |||
| 1950 | #define fts5YYNRULE28 28 | |||
| 1951 | #define fts5YYNRULE_WITH_ACTION28 28 | |||
| 1952 | #define fts5YYNFTS5TOKEN16 16 | |||
| 1953 | #define fts5YY_MAX_SHIFT34 34 | |||
| 1954 | #define fts5YY_MIN_SHIFTREDUCE52 52 | |||
| 1955 | #define fts5YY_MAX_SHIFTREDUCE79 79 | |||
| 1956 | #define fts5YY_ERROR_ACTION80 80 | |||
| 1957 | #define fts5YY_ACCEPT_ACTION81 81 | |||
| 1958 | #define fts5YY_NO_ACTION82 82 | |||
| 1959 | #define fts5YY_MIN_REDUCE83 83 | |||
| 1960 | #define fts5YY_MAX_REDUCE110 110 | |||
| 1961 | #define fts5YY_MIN_DSTRCTR16 16 | |||
| 1962 | #define fts5YY_MAX_DSTRCTR24 24 | |||
| 1963 | /************* End control #defines *******************************************/ | |||
| 1964 | #define fts5YY_NLOOKAHEAD((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]))) ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]))) | |||
| 1965 | ||||
| 1966 | /* Define the fts5yytestcase() macro to be a no-op if is not already defined | |||
| 1967 | ** otherwise. | |||
| 1968 | ** | |||
| 1969 | ** Applications can choose to define fts5yytestcase() in the %include section | |||
| 1970 | ** to a macro that can assist in verifying code coverage. For production | |||
| 1971 | ** code the fts5yytestcase() macro should be turned off. But it is useful | |||
| 1972 | ** for testing. | |||
| 1973 | */ | |||
| 1974 | #ifndef fts5yytestcase | |||
| 1975 | # define fts5yytestcase(X) | |||
| 1976 | #endif | |||
| 1977 | ||||
| 1978 | /* Macro to determine if stack space has the ability to grow using | |||
| 1979 | ** heap memory. | |||
| 1980 | */ | |||
| 1981 | #if fts5YYSTACKDEPTH100<=0 || fts5YYDYNSTACK0 | |||
| 1982 | # define fts5YYGROWABLESTACK0 1 | |||
| 1983 | #else | |||
| 1984 | # define fts5YYGROWABLESTACK0 0 | |||
| 1985 | #endif | |||
| 1986 | ||||
| 1987 | /* Guarantee a minimum number of initial stack slots. | |||
| 1988 | */ | |||
| 1989 | #if fts5YYSTACKDEPTH100<=0 | |||
| 1990 | # undef fts5YYSTACKDEPTH100 | |||
| 1991 | # define fts5YYSTACKDEPTH100 2 /* Need a minimum stack size */ | |||
| 1992 | #endif | |||
| 1993 | ||||
| 1994 | ||||
| 1995 | /* Next are the tables used to determine what action to take based on the | |||
| 1996 | ** current state and lookahead token. These tables are used to implement | |||
| 1997 | ** functions that take a state number and lookahead value and return an | |||
| 1998 | ** action integer. | |||
| 1999 | ** | |||
| 2000 | ** Suppose the action integer is N. Then the action is determined as | |||
| 2001 | ** follows | |||
| 2002 | ** | |||
| 2003 | ** 0 <= N <= fts5YY_MAX_SHIFT Shift N. That is, push the lookahead | |||
| 2004 | ** token onto the stack and goto state N. | |||
| 2005 | ** | |||
| 2006 | ** N between fts5YY_MIN_SHIFTREDUCE Shift to an arbitrary state then | |||
| 2007 | ** and fts5YY_MAX_SHIFTREDUCE reduce by rule N-fts5YY_MIN_SHIFTREDUCE. | |||
| 2008 | ** | |||
| 2009 | ** N == fts5YY_ERROR_ACTION A syntax error has occurred. | |||
| 2010 | ** | |||
| 2011 | ** N == fts5YY_ACCEPT_ACTION The parser accepts its input. | |||
| 2012 | ** | |||
| 2013 | ** N == fts5YY_NO_ACTION No such action. Denotes unused | |||
| 2014 | ** slots in the fts5yy_action[] table. | |||
| 2015 | ** | |||
| 2016 | ** N between fts5YY_MIN_REDUCE Reduce by rule N-fts5YY_MIN_REDUCE | |||
| 2017 | ** and fts5YY_MAX_REDUCE | |||
| 2018 | ** | |||
| 2019 | ** The action table is constructed as a single large table named fts5yy_action[]. | |||
| 2020 | ** Given state S and lookahead X, the action is computed as either: | |||
| 2021 | ** | |||
| 2022 | ** (A) N = fts5yy_action[ fts5yy_shift_ofst[S] + X ] | |||
| 2023 | ** (B) N = fts5yy_default[S] | |||
| 2024 | ** | |||
| 2025 | ** The (A) formula is preferred. The B formula is used instead if | |||
| 2026 | ** fts5yy_lookahead[fts5yy_shift_ofst[S]+X] is not equal to X. | |||
| 2027 | ** | |||
| 2028 | ** The formulas above are for computing the action when the lookahead is | |||
| 2029 | ** a terminal symbol. If the lookahead is a non-terminal (as occurs after | |||
| 2030 | ** a reduce action) then the fts5yy_reduce_ofst[] array is used in place of | |||
| 2031 | ** the fts5yy_shift_ofst[] array. | |||
| 2032 | ** | |||
| 2033 | ** The following are the tables generated in this section: | |||
| 2034 | ** | |||
| 2035 | ** fts5yy_action[] A single table containing all actions. | |||
| 2036 | ** fts5yy_lookahead[] A table containing the lookahead for each entry in | |||
| 2037 | ** fts5yy_action. Used to detect hash collisions. | |||
| 2038 | ** fts5yy_shift_ofst[] For each state, the offset into fts5yy_action for | |||
| 2039 | ** shifting terminals. | |||
| 2040 | ** fts5yy_reduce_ofst[] For each state, the offset into fts5yy_action for | |||
| 2041 | ** shifting non-terminals after a reduce. | |||
| 2042 | ** fts5yy_default[] Default action for each state. | |||
| 2043 | ** | |||
| 2044 | *********** Begin parsing tables **********************************************/ | |||
| 2045 | #define fts5YY_ACTTAB_COUNT(105) (105) | |||
| 2046 | static const fts5YYACTIONTYPEunsigned char fts5yy_action[] = { | |||
| 2047 | /* 0 */ 81, 20, 96, 6, 28, 99, 98, 26, 26, 18, | |||
| 2048 | /* 10 */ 96, 6, 28, 17, 98, 56, 26, 19, 96, 6, | |||
| 2049 | /* 20 */ 28, 14, 98, 14, 26, 31, 92, 96, 6, 28, | |||
| 2050 | /* 30 */ 108, 98, 25, 26, 21, 96, 6, 28, 78, 98, | |||
| 2051 | /* 40 */ 58, 26, 29, 96, 6, 28, 107, 98, 22, 26, | |||
| 2052 | /* 50 */ 24, 16, 12, 11, 1, 13, 13, 24, 16, 23, | |||
| 2053 | /* 60 */ 11, 33, 34, 13, 97, 8, 27, 32, 98, 7, | |||
| 2054 | /* 70 */ 26, 3, 4, 5, 3, 4, 5, 3, 83, 4, | |||
| 2055 | /* 80 */ 5, 3, 63, 5, 3, 62, 12, 2, 86, 13, | |||
| 2056 | /* 90 */ 9, 30, 10, 10, 54, 57, 75, 78, 78, 53, | |||
| 2057 | /* 100 */ 57, 15, 82, 82, 71, | |||
| 2058 | }; | |||
| 2059 | static const fts5YYCODETYPEunsigned char fts5yy_lookahead[] = { | |||
| 2060 | /* 0 */ 16, 17, 18, 19, 20, 22, 22, 24, 24, 17, | |||
| 2061 | /* 10 */ 18, 19, 20, 7, 22, 9, 24, 17, 18, 19, | |||
| 2062 | /* 20 */ 20, 9, 22, 9, 24, 13, 17, 18, 19, 20, | |||
| 2063 | /* 30 */ 26, 22, 24, 24, 17, 18, 19, 20, 15, 22, | |||
| 2064 | /* 40 */ 9, 24, 17, 18, 19, 20, 26, 22, 21, 24, | |||
| 2065 | /* 50 */ 6, 7, 9, 9, 10, 12, 12, 6, 7, 21, | |||
| 2066 | /* 60 */ 9, 24, 25, 12, 18, 5, 20, 14, 22, 5, | |||
| 2067 | /* 70 */ 24, 3, 1, 2, 3, 1, 2, 3, 0, 1, | |||
| 2068 | /* 80 */ 2, 3, 11, 2, 3, 11, 9, 10, 5, 12, | |||
| 2069 | /* 90 */ 23, 24, 10, 10, 8, 9, 9, 15, 15, 8, | |||
| 2070 | /* 100 */ 9, 9, 27, 27, 11, 27, 27, 27, 27, 27, | |||
| 2071 | /* 110 */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, | |||
| 2072 | /* 120 */ 27, | |||
| 2073 | }; | |||
| 2074 | #define fts5YY_SHIFT_COUNT(34) (34) | |||
| 2075 | #define fts5YY_SHIFT_MIN(0) (0) | |||
| 2076 | #define fts5YY_SHIFT_MAX(93) (93) | |||
| 2077 | static const unsigned char fts5yy_shift_ofst[] = { | |||
| 2078 | /* 0 */ 44, 44, 44, 44, 44, 44, 51, 77, 43, 12, | |||
| 2079 | /* 10 */ 14, 83, 82, 14, 23, 23, 31, 31, 71, 74, | |||
| 2080 | /* 20 */ 78, 81, 86, 91, 6, 53, 53, 60, 64, 68, | |||
| 2081 | /* 30 */ 53, 87, 92, 53, 93, | |||
| 2082 | }; | |||
| 2083 | #define fts5YY_REDUCE_COUNT(17) (17) | |||
| 2084 | #define fts5YY_REDUCE_MIN(-17) (-17) | |||
| 2085 | #define fts5YY_REDUCE_MAX(67) (67) | |||
| 2086 | static const signed char fts5yy_reduce_ofst[] = { | |||
| 2087 | /* 0 */ -16, -8, 0, 9, 17, 25, 46, -17, -17, 37, | |||
| 2088 | /* 10 */ 67, 4, 4, 8, 4, 20, 27, 38, | |||
| 2089 | }; | |||
| 2090 | static const fts5YYACTIONTYPEunsigned char fts5yy_default[] = { | |||
| 2091 | /* 0 */ 80, 80, 80, 80, 80, 80, 95, 80, 80, 105, | |||
| 2092 | /* 10 */ 80, 110, 110, 80, 110, 110, 80, 80, 80, 80, | |||
| 2093 | /* 20 */ 80, 91, 80, 80, 80, 101, 100, 80, 80, 90, | |||
| 2094 | /* 30 */ 103, 80, 80, 104, 80, | |||
| 2095 | }; | |||
| 2096 | /********** End of lemon-generated parsing tables *****************************/ | |||
| 2097 | ||||
| 2098 | /* The next table maps tokens (terminal symbols) into fallback tokens. | |||
| 2099 | ** If a construct like the following: | |||
| 2100 | ** | |||
| 2101 | ** %fallback ID X Y Z. | |||
| 2102 | ** | |||
| 2103 | ** appears in the grammar, then ID becomes a fallback token for X, Y, | |||
| 2104 | ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser | |||
| 2105 | ** but it does not parse, the type of the token is changed to ID and | |||
| 2106 | ** the parse is retried before an error is thrown. | |||
| 2107 | ** | |||
| 2108 | ** This feature can be used, for example, to cause some keywords in a language | |||
| 2109 | ** to revert to identifiers if they keyword does not apply in the context where | |||
| 2110 | ** it appears. | |||
| 2111 | */ | |||
| 2112 | #ifdef fts5YYFALLBACK | |||
| 2113 | static const fts5YYCODETYPEunsigned char fts5yyFallback[] = { | |||
| 2114 | }; | |||
| 2115 | #endif /* fts5YYFALLBACK */ | |||
| 2116 | ||||
| 2117 | /* The following structure represents a single element of the | |||
| 2118 | ** parser's stack. Information stored includes: | |||
| 2119 | ** | |||
| 2120 | ** + The state number for the parser at this level of the stack. | |||
| 2121 | ** | |||
| 2122 | ** + The value of the token stored at this level of the stack. | |||
| 2123 | ** (In other words, the "major" token.) | |||
| 2124 | ** | |||
| 2125 | ** + The semantic value stored at this level of the stack. This is | |||
| 2126 | ** the information used by the action routines in the grammar. | |||
| 2127 | ** It is sometimes called the "minor" token. | |||
| 2128 | ** | |||
| 2129 | ** After the "shift" half of a SHIFTREDUCE action, the stateno field | |||
| 2130 | ** actually contains the reduce action for the second half of the | |||
| 2131 | ** SHIFTREDUCE. | |||
| 2132 | */ | |||
| 2133 | struct fts5yyStackEntry { | |||
| 2134 | fts5YYACTIONTYPEunsigned char stateno; /* The state-number, or reduce action in SHIFTREDUCE */ | |||
| 2135 | fts5YYCODETYPEunsigned char major; /* The major token value. This is the code | |||
| 2136 | ** number for the token at this stack level */ | |||
| 2137 | fts5YYMINORTYPE minor; /* The user-supplied minor token value. This | |||
| 2138 | ** is the value of the token */ | |||
| 2139 | }; | |||
| 2140 | typedef struct fts5yyStackEntry fts5yyStackEntry; | |||
| 2141 | ||||
| 2142 | /* The state of the parser is completely contained in an instance of | |||
| 2143 | ** the following structure */ | |||
| 2144 | struct fts5yyParser { | |||
| 2145 | fts5yyStackEntry *fts5yytos; /* Pointer to top element of the stack */ | |||
| 2146 | #ifdef fts5YYTRACKMAXSTACKDEPTH | |||
| 2147 | int fts5yyhwm; /* High-water mark of the stack */ | |||
| 2148 | #endif | |||
| 2149 | #ifndef fts5YYNOERRORRECOVERY1 | |||
| 2150 | int fts5yyerrcnt; /* Shifts left before out of the error */ | |||
| 2151 | #endif | |||
| 2152 | sqlite3Fts5ParserARG_SDECLFts5Parse *pParse; /* A place to hold %extra_argument */ | |||
| 2153 | sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */ | |||
| 2154 | fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ | |||
| 2155 | fts5yyStackEntry *fts5yystack; /* The parser stack */ | |||
| 2156 | fts5yyStackEntry fts5yystk0[fts5YYSTACKDEPTH100]; /* Initial stack space */ | |||
| 2157 | }; | |||
| 2158 | typedef struct fts5yyParser fts5yyParser; | |||
| 2159 | ||||
| 2160 | #include <assert.h> | |||
| 2161 | #ifndef NDEBUG1 | |||
| 2162 | #include <stdio.h> | |||
| 2163 | static FILE *fts5yyTraceFILE = 0; | |||
| 2164 | static char *fts5yyTracePrompt = 0; | |||
| 2165 | #endif /* NDEBUG */ | |||
| 2166 | ||||
| 2167 | #ifndef NDEBUG1 | |||
| 2168 | /* | |||
| 2169 | ** Turn parser tracing on by giving a stream to which to write the trace | |||
| 2170 | ** and a prompt to preface each trace message. Tracing is turned off | |||
| 2171 | ** by making either argument NULL | |||
| 2172 | ** | |||
| 2173 | ** Inputs: | |||
| 2174 | ** <ul> | |||
| 2175 | ** <li> A FILE* to which trace output should be written. | |||
| 2176 | ** If NULL, then tracing is turned off. | |||
| 2177 | ** <li> A prefix string written at the beginning of every | |||
| 2178 | ** line of trace output. If NULL, then tracing is | |||
| 2179 | ** turned off. | |||
| 2180 | ** </ul> | |||
| 2181 | ** | |||
| 2182 | ** Outputs: | |||
| 2183 | ** None. | |||
| 2184 | */ | |||
| 2185 | static void sqlite3Fts5ParserTrace(FILE *TraceFILE, char *zTracePrompt){ | |||
| 2186 | fts5yyTraceFILE = TraceFILE; | |||
| 2187 | fts5yyTracePrompt = zTracePrompt; | |||
| 2188 | if( fts5yyTraceFILE==0 ) fts5yyTracePrompt = 0; | |||
| 2189 | else if( fts5yyTracePrompt==0 ) fts5yyTraceFILE = 0; | |||
| 2190 | } | |||
| 2191 | #endif /* NDEBUG */ | |||
| 2192 | ||||
| 2193 | #if defined(fts5YYCOVERAGE) || !defined(NDEBUG1) | |||
| 2194 | /* For tracing shifts, the names of all terminals and nonterminals | |||
| 2195 | ** are required. The following table supplies these names */ | |||
| 2196 | static const char *const fts5yyTokenName[] = { | |||
| 2197 | /* 0 */ "$", | |||
| 2198 | /* 1 */ "OR", | |||
| 2199 | /* 2 */ "AND", | |||
| 2200 | /* 3 */ "NOT", | |||
| 2201 | /* 4 */ "TERM", | |||
| 2202 | /* 5 */ "COLON", | |||
| 2203 | /* 6 */ "MINUS", | |||
| 2204 | /* 7 */ "LCP", | |||
| 2205 | /* 8 */ "RCP", | |||
| 2206 | /* 9 */ "STRING", | |||
| 2207 | /* 10 */ "LP", | |||
| 2208 | /* 11 */ "RP", | |||
| 2209 | /* 12 */ "CARET", | |||
| 2210 | /* 13 */ "COMMA", | |||
| 2211 | /* 14 */ "PLUS", | |||
| 2212 | /* 15 */ "STAR", | |||
| 2213 | /* 16 */ "input", | |||
| 2214 | /* 17 */ "expr", | |||
| 2215 | /* 18 */ "cnearset", | |||
| 2216 | /* 19 */ "exprlist", | |||
| 2217 | /* 20 */ "colset", | |||
| 2218 | /* 21 */ "colsetlist", | |||
| 2219 | /* 22 */ "nearset", | |||
| 2220 | /* 23 */ "nearphrases", | |||
| 2221 | /* 24 */ "phrase", | |||
| 2222 | /* 25 */ "neardist_opt", | |||
| 2223 | /* 26 */ "star_opt", | |||
| 2224 | }; | |||
| 2225 | #endif /* defined(fts5YYCOVERAGE) || !defined(NDEBUG) */ | |||
| 2226 | ||||
| 2227 | #ifndef NDEBUG1 | |||
| 2228 | /* For tracing reduce actions, the names of all rules are required. | |||
| 2229 | */ | |||
| 2230 | static const char *const fts5yyRuleName[] = { | |||
| 2231 | /* 0 */ "input ::= expr", | |||
| 2232 | /* 1 */ "colset ::= MINUS LCP colsetlist RCP", | |||
| 2233 | /* 2 */ "colset ::= LCP colsetlist RCP", | |||
| 2234 | /* 3 */ "colset ::= STRING", | |||
| 2235 | /* 4 */ "colset ::= MINUS STRING", | |||
| 2236 | /* 5 */ "colsetlist ::= colsetlist STRING", | |||
| 2237 | /* 6 */ "colsetlist ::= STRING", | |||
| 2238 | /* 7 */ "expr ::= expr AND expr", | |||
| 2239 | /* 8 */ "expr ::= expr OR expr", | |||
| 2240 | /* 9 */ "expr ::= expr NOT expr", | |||
| 2241 | /* 10 */ "expr ::= colset COLON LP expr RP", | |||
| 2242 | /* 11 */ "expr ::= LP expr RP", | |||
| 2243 | /* 12 */ "expr ::= exprlist", | |||
| 2244 | /* 13 */ "exprlist ::= cnearset", | |||
| 2245 | /* 14 */ "exprlist ::= exprlist cnearset", | |||
| 2246 | /* 15 */ "cnearset ::= nearset", | |||
| 2247 | /* 16 */ "cnearset ::= colset COLON nearset", | |||
| 2248 | /* 17 */ "nearset ::= phrase", | |||
| 2249 | /* 18 */ "nearset ::= CARET phrase", | |||
| 2250 | /* 19 */ "nearset ::= STRING LP nearphrases neardist_opt RP", | |||
| 2251 | /* 20 */ "nearphrases ::= phrase", | |||
| 2252 | /* 21 */ "nearphrases ::= nearphrases phrase", | |||
| 2253 | /* 22 */ "neardist_opt ::=", | |||
| 2254 | /* 23 */ "neardist_opt ::= COMMA STRING", | |||
| 2255 | /* 24 */ "phrase ::= phrase PLUS STRING star_opt", | |||
| 2256 | /* 25 */ "phrase ::= STRING star_opt", | |||
| 2257 | /* 26 */ "star_opt ::= STAR", | |||
| 2258 | /* 27 */ "star_opt ::=", | |||
| 2259 | }; | |||
| 2260 | #endif /* NDEBUG */ | |||
| 2261 | ||||
| 2262 | ||||
| 2263 | #if fts5YYGROWABLESTACK0 | |||
| 2264 | /* | |||
| 2265 | ** Try to increase the size of the parser stack. Return the number | |||
| 2266 | ** of errors. Return 0 on success. | |||
| 2267 | */ | |||
| 2268 | static int fts5yyGrowStack(fts5yyParser *p)1{ | |||
| 2269 | int oldSize = 1 + (int)(p->fts5yystackEnd - p->fts5yystack); | |||
| 2270 | int newSize; | |||
| 2271 | int idx; | |||
| 2272 | fts5yyStackEntry *pNew; | |||
| 2273 | #ifdef fts5YYSIZELIMIT | |||
| 2274 | int nLimit = fts5YYSIZELIMIT(sqlite3Fts5ParserCTX(p)0); | |||
| 2275 | #endif | |||
| 2276 | ||||
| 2277 | newSize = oldSize*2 + 100; | |||
| 2278 | #ifdef fts5YYSIZELIMIT | |||
| 2279 | if( newSize>nLimit ){ | |||
| 2280 | newSize = nLimit; | |||
| 2281 | if( newSize<=oldSize ) return 1; | |||
| 2282 | } | |||
| 2283 | #endif | |||
| 2284 | idx = (int)(p->fts5yytos - p->fts5yystack); | |||
| 2285 | if( p->fts5yystack==p->fts5yystk0 ){ | |||
| 2286 | pNew = fts5YYREALLOCrealloc(0, newSize*sizeof(pNew[0]), sqlite3Fts5ParserCTX(p)0); | |||
| 2287 | if( pNew==0 ) return 1; | |||
| 2288 | memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0])); | |||
| 2289 | }else{ | |||
| 2290 | pNew = fts5YYREALLOCrealloc(p->fts5yystack, newSize*sizeof(pNew[0]), sqlite3Fts5ParserCTX(p)0); | |||
| 2291 | if( pNew==0 ) return 1; | |||
| 2292 | } | |||
| 2293 | p->fts5yystack = pNew; | |||
| 2294 | p->fts5yytos = &p->fts5yystack[idx]; | |||
| 2295 | #ifndef NDEBUG1 | |||
| 2296 | if( fts5yyTraceFILE ){ | |||
| 2297 | fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", | |||
| 2298 | fts5yyTracePrompt, oldSize, newSize); | |||
| 2299 | } | |||
| 2300 | #endif | |||
| 2301 | p->fts5yystackEnd = &p->fts5yystack[newSize-1]; | |||
| 2302 | return 0; | |||
| 2303 | } | |||
| 2304 | #endif /* fts5YYGROWABLESTACK */ | |||
| 2305 | ||||
| 2306 | #if !fts5YYGROWABLESTACK0 | |||
| 2307 | /* For builds that do no have a growable stack, fts5yyGrowStack always | |||
| 2308 | ** returns an error. | |||
| 2309 | */ | |||
| 2310 | # define fts5yyGrowStack(X)1 1 | |||
| 2311 | #endif | |||
| 2312 | ||||
| 2313 | /* Datatype of the argument to the memory allocated passed as the | |||
| 2314 | ** second argument to sqlite3Fts5ParserAlloc() below. This can be changed by | |||
| 2315 | ** putting an appropriate #define in the %include section of the input | |||
| 2316 | ** grammar. | |||
| 2317 | */ | |||
| 2318 | #ifndef fts5YYMALLOCARGTYPEu64 | |||
| 2319 | # define fts5YYMALLOCARGTYPEu64 size_t | |||
| 2320 | #endif | |||
| 2321 | ||||
| 2322 | /* Initialize a new parser that has already been allocated. | |||
| 2323 | */ | |||
| 2324 | static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PDECL){ | |||
| 2325 | fts5yyParser *fts5yypParser = (fts5yyParser*)fts5yypRawParser; | |||
| 2326 | sqlite3Fts5ParserCTX_STORE | |||
| 2327 | #ifdef fts5YYTRACKMAXSTACKDEPTH | |||
| 2328 | fts5yypParser->fts5yyhwm = 0; | |||
| 2329 | #endif | |||
| 2330 | fts5yypParser->fts5yystack = fts5yypParser->fts5yystk0; | |||
| 2331 | fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH100-1]; | |||
| 2332 | #ifndef fts5YYNOERRORRECOVERY1 | |||
| 2333 | fts5yypParser->fts5yyerrcnt = -1; | |||
| 2334 | #endif | |||
| 2335 | fts5yypParser->fts5yytos = fts5yypParser->fts5yystack; | |||
| 2336 | fts5yypParser->fts5yystack[0].stateno = 0; | |||
| 2337 | fts5yypParser->fts5yystack[0].major = 0; | |||
| 2338 | } | |||
| 2339 | ||||
| 2340 | #ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK | |||
| 2341 | /* | |||
| 2342 | ** This function allocates a new parser. | |||
| 2343 | ** The only argument is a pointer to a function which works like | |||
| 2344 | ** malloc. | |||
| 2345 | ** | |||
| 2346 | ** Inputs: | |||
| 2347 | ** A pointer to the function used to allocate memory. | |||
| 2348 | ** | |||
| 2349 | ** Outputs: | |||
| 2350 | ** A pointer to a parser. This pointer is used in subsequent calls | |||
| 2351 | ** to sqlite3Fts5Parser and sqlite3Fts5ParserFree. | |||
| 2352 | */ | |||
| 2353 | static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(fts5YYMALLOCARGTYPEu64) sqlite3Fts5ParserCTX_PDECL){ | |||
| 2354 | fts5yyParser *fts5yypParser; | |||
| 2355 | fts5yypParser = (fts5yyParser*)(*mallocProc)( (fts5YYMALLOCARGTYPEu64)sizeof(fts5yyParser) ); | |||
| 2356 | if( fts5yypParser ){ | |||
| 2357 | sqlite3Fts5ParserCTX_STORE | |||
| 2358 | sqlite3Fts5ParserInit(fts5yypParser sqlite3Fts5ParserCTX_PARAM); | |||
| 2359 | } | |||
| 2360 | return (void*)fts5yypParser; | |||
| 2361 | } | |||
| 2362 | #endif /* sqlite3Fts5Parser_ENGINEALWAYSONSTACK */ | |||
| 2363 | ||||
| 2364 | ||||
| 2365 | /* The following function deletes the "minor type" or semantic value | |||
| 2366 | ** associated with a symbol. The symbol can be either a terminal | |||
| 2367 | ** or nonterminal. "fts5yymajor" is the symbol code, and "fts5yypminor" is | |||
| 2368 | ** a pointer to the value to be deleted. The code used to do the | |||
| 2369 | ** deletions is derived from the %destructor and/or %token_destructor | |||
| 2370 | ** directives of the input grammar. | |||
| 2371 | */ | |||
| 2372 | static void fts5yy_destructor( | |||
| 2373 | fts5yyParser *fts5yypParser, /* The parser */ | |||
| 2374 | fts5YYCODETYPEunsigned char fts5yymajor, /* Type code for object to destroy */ | |||
| 2375 | fts5YYMINORTYPE *fts5yypminor /* The object to be destroyed */ | |||
| 2376 | ){ | |||
| 2377 | sqlite3Fts5ParserARG_FETCHFts5Parse *pParse=fts5yypParser->pParse; | |||
| 2378 | sqlite3Fts5ParserCTX_FETCH | |||
| 2379 | switch( fts5yymajor ){ | |||
| 2380 | /* Here is inserted the actions which take place when a | |||
| 2381 | ** terminal or non-terminal is destroyed. This can happen | |||
| 2382 | ** when the symbol is popped from the stack during a | |||
| 2383 | ** reduce or during error processing or when a parser is | |||
| 2384 | ** being destroyed before it is finished parsing. | |||
| 2385 | ** | |||
| 2386 | ** Note: during a reduce, the only symbols destroyed are those | |||
| 2387 | ** which appear on the RHS of the rule, but which are *not* used | |||
| 2388 | ** inside the C code. | |||
| 2389 | */ | |||
| 2390 | /********* Begin destructor definitions ***************************************/ | |||
| 2391 | case 16: /* input */ | |||
| 2392 | { | |||
| 2393 | #line 83 "fts5parse.y" | |||
| 2394 | (void)pParse; | |||
| 2395 | #line 624 "fts5parse.c" | |||
| 2396 | } | |||
| 2397 | break; | |||
| 2398 | case 17: /* expr */ | |||
| 2399 | case 18: /* cnearset */ | |||
| 2400 | case 19: /* exprlist */ | |||
| 2401 | { | |||
| 2402 | #line 89 "fts5parse.y" | |||
| 2403 | sqlite3Fts5ParseNodeFree((fts5yypminor->fts5yy24)); | |||
| 2404 | #line 633 "fts5parse.c" | |||
| 2405 | } | |||
| 2406 | break; | |||
| 2407 | case 20: /* colset */ | |||
| 2408 | case 21: /* colsetlist */ | |||
| 2409 | { | |||
| 2410 | #line 93 "fts5parse.y" | |||
| 2411 | sqlite3_freesqlite3_api->free((fts5yypminor->fts5yy11)); | |||
| 2412 | #line 641 "fts5parse.c" | |||
| 2413 | } | |||
| 2414 | break; | |||
| 2415 | case 22: /* nearset */ | |||
| 2416 | case 23: /* nearphrases */ | |||
| 2417 | { | |||
| 2418 | #line 148 "fts5parse.y" | |||
| 2419 | sqlite3Fts5ParseNearsetFree((fts5yypminor->fts5yy46)); | |||
| 2420 | #line 649 "fts5parse.c" | |||
| 2421 | } | |||
| 2422 | break; | |||
| 2423 | case 24: /* phrase */ | |||
| 2424 | { | |||
| 2425 | #line 183 "fts5parse.y" | |||
| 2426 | sqlite3Fts5ParsePhraseFree((fts5yypminor->fts5yy53)); | |||
| 2427 | #line 656 "fts5parse.c" | |||
| 2428 | } | |||
| 2429 | break; | |||
| 2430 | /********* End destructor definitions *****************************************/ | |||
| 2431 | default: break; /* If no destructor action specified: do nothing */ | |||
| 2432 | } | |||
| 2433 | } | |||
| 2434 | ||||
| 2435 | /* | |||
| 2436 | ** Pop the parser's stack once. | |||
| 2437 | ** | |||
| 2438 | ** If there is a destructor routine associated with the token which | |||
| 2439 | ** is popped from the stack, then call it. | |||
| 2440 | */ | |||
| 2441 | static void fts5yy_pop_parser_stack(fts5yyParser *pParser){ | |||
| 2442 | fts5yyStackEntry *fts5yytos; | |||
| 2443 | assert( pParser->fts5yytos!=0 )((void) (0)); | |||
| 2444 | assert( pParser->fts5yytos > pParser->fts5yystack )((void) (0)); | |||
| 2445 | fts5yytos = pParser->fts5yytos--; | |||
| 2446 | #ifndef NDEBUG1 | |||
| 2447 | if( fts5yyTraceFILE ){ | |||
| 2448 | fprintf(fts5yyTraceFILE,"%sPopping %s\n", | |||
| 2449 | fts5yyTracePrompt, | |||
| 2450 | fts5yyTokenName[fts5yytos->major]); | |||
| 2451 | } | |||
| 2452 | #endif | |||
| 2453 | fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor); | |||
| 2454 | } | |||
| 2455 | ||||
| 2456 | /* | |||
| 2457 | ** Clear all secondary memory allocations from the parser | |||
| 2458 | */ | |||
| 2459 | static void sqlite3Fts5ParserFinalize(void *p){ | |||
| 2460 | fts5yyParser *pParser = (fts5yyParser*)p; | |||
| 2461 | ||||
| 2462 | /* In-lined version of calling fts5yy_pop_parser_stack() for each | |||
| 2463 | ** element left in the stack */ | |||
| 2464 | fts5yyStackEntry *fts5yytos = pParser->fts5yytos; | |||
| 2465 | while( fts5yytos>pParser->fts5yystack ){ | |||
| 2466 | #ifndef NDEBUG1 | |||
| 2467 | if( fts5yyTraceFILE ){ | |||
| 2468 | fprintf(fts5yyTraceFILE,"%sPopping %s\n", | |||
| 2469 | fts5yyTracePrompt, | |||
| 2470 | fts5yyTokenName[fts5yytos->major]); | |||
| 2471 | } | |||
| 2472 | #endif | |||
| 2473 | if( fts5yytos->major>=fts5YY_MIN_DSTRCTR16 ){ | |||
| 2474 | fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor); | |||
| 2475 | } | |||
| 2476 | fts5yytos--; | |||
| 2477 | } | |||
| 2478 | ||||
| 2479 | #if fts5YYGROWABLESTACK0 | |||
| 2480 | if( pParser->fts5yystack!=pParser->fts5yystk0 ){ | |||
| 2481 | fts5YYFREEfree(pParser->fts5yystack, sqlite3Fts5ParserCTX(pParser)0); | |||
| 2482 | } | |||
| 2483 | #endif | |||
| 2484 | } | |||
| 2485 | ||||
| 2486 | #ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK | |||
| 2487 | /* | |||
| 2488 | ** Deallocate and destroy a parser. Destructors are called for | |||
| 2489 | ** all stack elements before shutting the parser down. | |||
| 2490 | ** | |||
| 2491 | ** If the fts5YYPARSEFREENEVERNULL macro exists (for example because it | |||
| 2492 | ** is defined in a %include section of the input grammar) then it is | |||
| 2493 | ** assumed that the input pointer is never NULL. | |||
| 2494 | */ | |||
| 2495 | static void sqlite3Fts5ParserFree( | |||
| 2496 | void *p, /* The parser to be deleted */ | |||
| 2497 | void (*freeProc)(void*) /* Function used to reclaim memory */ | |||
| 2498 | ){ | |||
| 2499 | #ifndef fts5YYPARSEFREENEVERNULL | |||
| 2500 | if( p==0 ) return; | |||
| 2501 | #endif | |||
| 2502 | sqlite3Fts5ParserFinalize(p); | |||
| 2503 | (*freeProc)(p); | |||
| 2504 | } | |||
| 2505 | #endif /* sqlite3Fts5Parser_ENGINEALWAYSONSTACK */ | |||
| 2506 | ||||
| 2507 | /* | |||
| 2508 | ** Return the peak depth of the stack for a parser. | |||
| 2509 | */ | |||
| 2510 | #ifdef fts5YYTRACKMAXSTACKDEPTH | |||
| 2511 | static int sqlite3Fts5ParserStackPeak(void *p){ | |||
| 2512 | fts5yyParser *pParser = (fts5yyParser*)p; | |||
| 2513 | return pParser->fts5yyhwm; | |||
| 2514 | } | |||
| 2515 | #endif | |||
| 2516 | ||||
| 2517 | /* This array of booleans keeps track of the parser statement | |||
| 2518 | ** coverage. The element fts5yycoverage[X][Y] is set when the parser | |||
| 2519 | ** is in state X and has a lookahead token Y. In a well-tested | |||
| 2520 | ** systems, every element of this matrix should end up being set. | |||
| 2521 | */ | |||
| 2522 | #if defined(fts5YYCOVERAGE) | |||
| 2523 | static unsigned char fts5yycoverage[fts5YYNSTATE35][fts5YYNFTS5TOKEN16]; | |||
| 2524 | #endif | |||
| 2525 | ||||
| 2526 | /* | |||
| 2527 | ** Write into out a description of every state/lookahead combination that | |||
| 2528 | ** | |||
| 2529 | ** (1) has not been used by the parser, and | |||
| 2530 | ** (2) is not a syntax error. | |||
| 2531 | ** | |||
| 2532 | ** Return the number of missed state/lookahead combinations. | |||
| 2533 | */ | |||
| 2534 | #if defined(fts5YYCOVERAGE) | |||
| 2535 | static int sqlite3Fts5ParserCoverage(FILE *out){ | |||
| 2536 | int stateno, iLookAhead, i; | |||
| 2537 | int nMissed = 0; | |||
| 2538 | for(stateno=0; stateno<fts5YYNSTATE35; stateno++){ | |||
| 2539 | i = fts5yy_shift_ofst[stateno]; | |||
| 2540 | for(iLookAhead=0; iLookAhead<fts5YYNFTS5TOKEN16; iLookAhead++){ | |||
| 2541 | if( fts5yy_lookahead[i+iLookAhead]!=iLookAhead ) continue; | |||
| 2542 | if( fts5yycoverage[stateno][iLookAhead]==0 ) nMissed++; | |||
| 2543 | if( out ){ | |||
| 2544 | fprintf(out,"State %d lookahead %s %s\n", stateno, | |||
| 2545 | fts5yyTokenName[iLookAhead], | |||
| 2546 | fts5yycoverage[stateno][iLookAhead] ? "ok" : "missed"); | |||
| 2547 | } | |||
| 2548 | } | |||
| 2549 | } | |||
| 2550 | return nMissed; | |||
| 2551 | } | |||
| 2552 | #endif | |||
| 2553 | ||||
| 2554 | /* | |||
| 2555 | ** Find the appropriate action for a parser given the terminal | |||
| 2556 | ** look-ahead token iLookAhead. | |||
| 2557 | */ | |||
| 2558 | static fts5YYACTIONTYPEunsigned char fts5yy_find_shift_action( | |||
| 2559 | fts5YYCODETYPEunsigned char iLookAhead, /* The look-ahead token */ | |||
| 2560 | fts5YYACTIONTYPEunsigned char stateno /* Current state number */ | |||
| 2561 | ){ | |||
| 2562 | int i; | |||
| 2563 | ||||
| 2564 | if( stateno>fts5YY_MAX_SHIFT34 ) return stateno; | |||
| 2565 | assert( stateno <= fts5YY_SHIFT_COUNT )((void) (0)); | |||
| 2566 | #if defined(fts5YYCOVERAGE) | |||
| 2567 | fts5yycoverage[stateno][iLookAhead] = 1; | |||
| 2568 | #endif | |||
| 2569 | do{ | |||
| 2570 | i = fts5yy_shift_ofst[stateno]; | |||
| 2571 | assert( i>=0 )((void) (0)); | |||
| 2572 | assert( i<=fts5YY_ACTTAB_COUNT )((void) (0)); | |||
| 2573 | assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD )((void) (0)); | |||
| 2574 | assert( iLookAhead!=fts5YYNOCODE )((void) (0)); | |||
| 2575 | assert( iLookAhead < fts5YYNFTS5TOKEN )((void) (0)); | |||
| 2576 | i += iLookAhead; | |||
| 2577 | assert( i<(int)fts5YY_NLOOKAHEAD )((void) (0)); | |||
| 2578 | if( fts5yy_lookahead[i]!=iLookAhead ){ | |||
| 2579 | #ifdef fts5YYFALLBACK | |||
| 2580 | fts5YYCODETYPEunsigned char iFallback; /* Fallback token */ | |||
| 2581 | assert( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0]) )((void) (0)); | |||
| 2582 | iFallback = fts5yyFallback[iLookAhead]; | |||
| 2583 | if( iFallback!=0 ){ | |||
| 2584 | #ifndef NDEBUG1 | |||
| 2585 | if( fts5yyTraceFILE ){ | |||
| 2586 | fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n", | |||
| 2587 | fts5yyTracePrompt, fts5yyTokenName[iLookAhead], fts5yyTokenName[iFallback]); | |||
| 2588 | } | |||
| 2589 | #endif | |||
| 2590 | assert( fts5yyFallback[iFallback]==0 )((void) (0)); /* Fallback loop must terminate */ | |||
| 2591 | iLookAhead = iFallback; | |||
| 2592 | continue; | |||
| 2593 | } | |||
| 2594 | #endif | |||
| 2595 | #ifdef fts5YYWILDCARD | |||
| 2596 | { | |||
| 2597 | int j = i - iLookAhead + fts5YYWILDCARD; | |||
| 2598 | assert( j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) )((void) (0)); | |||
| 2599 | if( fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0 ){ | |||
| 2600 | #ifndef NDEBUG1 | |||
| 2601 | if( fts5yyTraceFILE ){ | |||
| 2602 | fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n", | |||
| 2603 | fts5yyTracePrompt, fts5yyTokenName[iLookAhead], | |||
| 2604 | fts5yyTokenName[fts5YYWILDCARD]); | |||
| 2605 | } | |||
| 2606 | #endif /* NDEBUG */ | |||
| 2607 | return fts5yy_action[j]; | |||
| 2608 | } | |||
| 2609 | } | |||
| 2610 | #endif /* fts5YYWILDCARD */ | |||
| 2611 | return fts5yy_default[stateno]; | |||
| 2612 | }else{ | |||
| 2613 | assert( i>=0 && i<(int)(sizeof(fts5yy_action)/sizeof(fts5yy_action[0])) )((void) (0)); | |||
| 2614 | return fts5yy_action[i]; | |||
| 2615 | } | |||
| 2616 | }while(1); | |||
| 2617 | } | |||
| 2618 | ||||
| 2619 | /* | |||
| 2620 | ** Find the appropriate action for a parser given the non-terminal | |||
| 2621 | ** look-ahead token iLookAhead. | |||
| 2622 | */ | |||
| 2623 | static fts5YYACTIONTYPEunsigned char fts5yy_find_reduce_action( | |||
| 2624 | fts5YYACTIONTYPEunsigned char stateno, /* Current state number */ | |||
| 2625 | fts5YYCODETYPEunsigned char iLookAhead /* The look-ahead token */ | |||
| 2626 | ){ | |||
| 2627 | int i; | |||
| 2628 | #ifdef fts5YYERRORSYMBOL | |||
| 2629 | if( stateno>fts5YY_REDUCE_COUNT(17) ){ | |||
| 2630 | return fts5yy_default[stateno]; | |||
| 2631 | } | |||
| 2632 | #else | |||
| 2633 | assert( stateno<=fts5YY_REDUCE_COUNT )((void) (0)); | |||
| 2634 | #endif | |||
| 2635 | i = fts5yy_reduce_ofst[stateno]; | |||
| 2636 | assert( iLookAhead!=fts5YYNOCODE )((void) (0)); | |||
| 2637 | i += iLookAhead; | |||
| 2638 | #ifdef fts5YYERRORSYMBOL | |||
| 2639 | if( i<0 || i>=fts5YY_ACTTAB_COUNT(105) || fts5yy_lookahead[i]!=iLookAhead ){ | |||
| 2640 | return fts5yy_default[stateno]; | |||
| 2641 | } | |||
| 2642 | #else | |||
| 2643 | assert( i>=0 && i<fts5YY_ACTTAB_COUNT )((void) (0)); | |||
| 2644 | assert( fts5yy_lookahead[i]==iLookAhead )((void) (0)); | |||
| 2645 | #endif | |||
| 2646 | return fts5yy_action[i]; | |||
| 2647 | } | |||
| 2648 | ||||
| 2649 | /* | |||
| 2650 | ** The following routine is called if the stack overflows. | |||
| 2651 | */ | |||
| 2652 | static void fts5yyStackOverflow(fts5yyParser *fts5yypParser){ | |||
| 2653 | sqlite3Fts5ParserARG_FETCHFts5Parse *pParse=fts5yypParser->pParse; | |||
| 2654 | sqlite3Fts5ParserCTX_FETCH | |||
| 2655 | #ifndef NDEBUG1 | |||
| 2656 | if( fts5yyTraceFILE ){ | |||
| 2657 | fprintf(fts5yyTraceFILE,"%sStack Overflow!\n",fts5yyTracePrompt); | |||
| 2658 | } | |||
| 2659 | #endif | |||
| 2660 | while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ) fts5yy_pop_parser_stack(fts5yypParser); | |||
| 2661 | /* Here code is inserted which will execute if the parser | |||
| 2662 | ** stack every overflows */ | |||
| 2663 | /******** Begin %stack_overflow code ******************************************/ | |||
| 2664 | #line 36 "fts5parse.y" | |||
| 2665 | ||||
| 2666 | sqlite3Fts5ParseError(pParse, "fts5: parser stack overflow"); | |||
| 2667 | #line 896 "fts5parse.c" | |||
| 2668 | /******** End %stack_overflow code ********************************************/ | |||
| 2669 | sqlite3Fts5ParserARG_STOREfts5yypParser->pParse=pParse; /* Suppress warning about unused %extra_argument var */ | |||
| 2670 | sqlite3Fts5ParserCTX_STORE | |||
| 2671 | } | |||
| 2672 | ||||
| 2673 | /* | |||
| 2674 | ** Print tracing information for a SHIFT action | |||
| 2675 | */ | |||
| 2676 | #ifndef NDEBUG1 | |||
| 2677 | static void fts5yyTraceShift(fts5yyParser *fts5yypParser, int fts5yyNewState, const char *zTag){ | |||
| 2678 | if( fts5yyTraceFILE ){ | |||
| 2679 | if( fts5yyNewState<fts5YYNSTATE35 ){ | |||
| 2680 | fprintf(fts5yyTraceFILE,"%s%s '%s', go to state %d\n", | |||
| 2681 | fts5yyTracePrompt, zTag, fts5yyTokenName[fts5yypParser->fts5yytos->major], | |||
| 2682 | fts5yyNewState); | |||
| 2683 | }else{ | |||
| 2684 | fprintf(fts5yyTraceFILE,"%s%s '%s', pending reduce %d\n", | |||
| 2685 | fts5yyTracePrompt, zTag, fts5yyTokenName[fts5yypParser->fts5yytos->major], | |||
| 2686 | fts5yyNewState - fts5YY_MIN_REDUCE83); | |||
| 2687 | } | |||
| 2688 | } | |||
| 2689 | } | |||
| 2690 | #else | |||
| 2691 | # define fts5yyTraceShift(X,Y,Z) | |||
| 2692 | #endif | |||
| 2693 | ||||
| 2694 | /* | |||
| 2695 | ** Perform a shift action. | |||
| 2696 | */ | |||
| 2697 | static void fts5yy_shift( | |||
| 2698 | fts5yyParser *fts5yypParser, /* The parser to be shifted */ | |||
| 2699 | fts5YYACTIONTYPEunsigned char fts5yyNewState, /* The new state to shift in */ | |||
| 2700 | fts5YYCODETYPEunsigned char fts5yyMajor, /* The major token to shift in */ | |||
| 2701 | sqlite3Fts5ParserFTS5TOKENTYPEFts5Token fts5yyMinor /* The minor token to shift in */ | |||
| 2702 | ){ | |||
| 2703 | fts5yyStackEntry *fts5yytos; | |||
| 2704 | fts5yypParser->fts5yytos++; | |||
| 2705 | #ifdef fts5YYTRACKMAXSTACKDEPTH | |||
| 2706 | if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ | |||
| 2707 | fts5yypParser->fts5yyhwm++; | |||
| 2708 | assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) )((void) (0)); | |||
| 2709 | } | |||
| 2710 | #endif | |||
| 2711 | fts5yytos = fts5yypParser->fts5yytos; | |||
| 2712 | if( fts5yytos>fts5yypParser->fts5yystackEnd ){ | |||
| 2713 | if( fts5yyGrowStack(fts5yypParser)1 ){ | |||
| 2714 | fts5yypParser->fts5yytos--; | |||
| 2715 | fts5yyStackOverflow(fts5yypParser); | |||
| 2716 | return; | |||
| 2717 | } | |||
| 2718 | fts5yytos = fts5yypParser->fts5yytos; | |||
| 2719 | assert( fts5yytos <= fts5yypParser->fts5yystackEnd )((void) (0)); | |||
| 2720 | } | |||
| 2721 | if( fts5yyNewState > fts5YY_MAX_SHIFT34 ){ | |||
| 2722 | fts5yyNewState += fts5YY_MIN_REDUCE83 - fts5YY_MIN_SHIFTREDUCE52; | |||
| 2723 | } | |||
| 2724 | fts5yytos->stateno = fts5yyNewState; | |||
| 2725 | fts5yytos->major = fts5yyMajor; | |||
| 2726 | fts5yytos->minor.fts5yy0 = fts5yyMinor; | |||
| 2727 | fts5yyTraceShift(fts5yypParser, fts5yyNewState, "Shift"); | |||
| 2728 | } | |||
| 2729 | ||||
| 2730 | /* For rule J, fts5yyRuleInfoLhs[J] contains the symbol on the left-hand side | |||
| 2731 | ** of that rule */ | |||
| 2732 | static const fts5YYCODETYPEunsigned char fts5yyRuleInfoLhs[] = { | |||
| 2733 | 16, /* (0) input ::= expr */ | |||
| 2734 | 20, /* (1) colset ::= MINUS LCP colsetlist RCP */ | |||
| 2735 | 20, /* (2) colset ::= LCP colsetlist RCP */ | |||
| 2736 | 20, /* (3) colset ::= STRING */ | |||
| 2737 | 20, /* (4) colset ::= MINUS STRING */ | |||
| 2738 | 21, /* (5) colsetlist ::= colsetlist STRING */ | |||
| 2739 | 21, /* (6) colsetlist ::= STRING */ | |||
| 2740 | 17, /* (7) expr ::= expr AND expr */ | |||
| 2741 | 17, /* (8) expr ::= expr OR expr */ | |||
| 2742 | 17, /* (9) expr ::= expr NOT expr */ | |||
| 2743 | 17, /* (10) expr ::= colset COLON LP expr RP */ | |||
| 2744 | 17, /* (11) expr ::= LP expr RP */ | |||
| 2745 | 17, /* (12) expr ::= exprlist */ | |||
| 2746 | 19, /* (13) exprlist ::= cnearset */ | |||
| 2747 | 19, /* (14) exprlist ::= exprlist cnearset */ | |||
| 2748 | 18, /* (15) cnearset ::= nearset */ | |||
| 2749 | 18, /* (16) cnearset ::= colset COLON nearset */ | |||
| 2750 | 22, /* (17) nearset ::= phrase */ | |||
| 2751 | 22, /* (18) nearset ::= CARET phrase */ | |||
| 2752 | 22, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ | |||
| 2753 | 23, /* (20) nearphrases ::= phrase */ | |||
| 2754 | 23, /* (21) nearphrases ::= nearphrases phrase */ | |||
| 2755 | 25, /* (22) neardist_opt ::= */ | |||
| 2756 | 25, /* (23) neardist_opt ::= COMMA STRING */ | |||
| 2757 | 24, /* (24) phrase ::= phrase PLUS STRING star_opt */ | |||
| 2758 | 24, /* (25) phrase ::= STRING star_opt */ | |||
| 2759 | 26, /* (26) star_opt ::= STAR */ | |||
| 2760 | 26, /* (27) star_opt ::= */ | |||
| 2761 | }; | |||
| 2762 | ||||
| 2763 | /* For rule J, fts5yyRuleInfoNRhs[J] contains the negative of the number | |||
| 2764 | ** of symbols on the right-hand side of that rule. */ | |||
| 2765 | static const signed char fts5yyRuleInfoNRhs[] = { | |||
| 2766 | -1, /* (0) input ::= expr */ | |||
| 2767 | -4, /* (1) colset ::= MINUS LCP colsetlist RCP */ | |||
| 2768 | -3, /* (2) colset ::= LCP colsetlist RCP */ | |||
| 2769 | -1, /* (3) colset ::= STRING */ | |||
| 2770 | -2, /* (4) colset ::= MINUS STRING */ | |||
| 2771 | -2, /* (5) colsetlist ::= colsetlist STRING */ | |||
| 2772 | -1, /* (6) colsetlist ::= STRING */ | |||
| 2773 | -3, /* (7) expr ::= expr AND expr */ | |||
| 2774 | -3, /* (8) expr ::= expr OR expr */ | |||
| 2775 | -3, /* (9) expr ::= expr NOT expr */ | |||
| 2776 | -5, /* (10) expr ::= colset COLON LP expr RP */ | |||
| 2777 | -3, /* (11) expr ::= LP expr RP */ | |||
| 2778 | -1, /* (12) expr ::= exprlist */ | |||
| 2779 | -1, /* (13) exprlist ::= cnearset */ | |||
| 2780 | -2, /* (14) exprlist ::= exprlist cnearset */ | |||
| 2781 | -1, /* (15) cnearset ::= nearset */ | |||
| 2782 | -3, /* (16) cnearset ::= colset COLON nearset */ | |||
| 2783 | -1, /* (17) nearset ::= phrase */ | |||
| 2784 | -2, /* (18) nearset ::= CARET phrase */ | |||
| 2785 | -5, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ | |||
| 2786 | -1, /* (20) nearphrases ::= phrase */ | |||
| 2787 | -2, /* (21) nearphrases ::= nearphrases phrase */ | |||
| 2788 | 0, /* (22) neardist_opt ::= */ | |||
| 2789 | -2, /* (23) neardist_opt ::= COMMA STRING */ | |||
| 2790 | -4, /* (24) phrase ::= phrase PLUS STRING star_opt */ | |||
| 2791 | -2, /* (25) phrase ::= STRING star_opt */ | |||
| 2792 | -1, /* (26) star_opt ::= STAR */ | |||
| 2793 | 0, /* (27) star_opt ::= */ | |||
| 2794 | }; | |||
| 2795 | ||||
| 2796 | static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */ | |||
| 2797 | ||||
| 2798 | /* | |||
| 2799 | ** Perform a reduce action and the shift that must immediately | |||
| 2800 | ** follow the reduce. | |||
| 2801 | ** | |||
| 2802 | ** The fts5yyLookahead and fts5yyLookaheadToken parameters provide reduce actions | |||
| 2803 | ** access to the lookahead token (if any). The fts5yyLookahead will be fts5YYNOCODE | |||
| 2804 | ** if the lookahead token has already been consumed. As this procedure is | |||
| 2805 | ** only called from one place, optimizing compilers will in-line it, which | |||
| 2806 | ** means that the extra parameters have no performance impact. | |||
| 2807 | */ | |||
| 2808 | static fts5YYACTIONTYPEunsigned char fts5yy_reduce( | |||
| 2809 | fts5yyParser *fts5yypParser, /* The parser */ | |||
| 2810 | unsigned int fts5yyruleno, /* Number of the rule by which to reduce */ | |||
| 2811 | int fts5yyLookahead, /* Lookahead token, or fts5YYNOCODE if none */ | |||
| 2812 | sqlite3Fts5ParserFTS5TOKENTYPEFts5Token fts5yyLookaheadToken /* Value of the lookahead token */ | |||
| 2813 | sqlite3Fts5ParserCTX_PDECL /* %extra_context */ | |||
| 2814 | ){ | |||
| 2815 | int fts5yygoto; /* The next state */ | |||
| 2816 | fts5YYACTIONTYPEunsigned char fts5yyact; /* The next action */ | |||
| 2817 | fts5yyStackEntry *fts5yymsp; /* The top of the parser's stack */ | |||
| 2818 | int fts5yysize; /* Amount to pop the stack */ | |||
| 2819 | sqlite3Fts5ParserARG_FETCHFts5Parse *pParse=fts5yypParser->pParse; | |||
| 2820 | (void)fts5yyLookahead; | |||
| 2821 | (void)fts5yyLookaheadToken; | |||
| 2822 | fts5yymsp = fts5yypParser->fts5yytos; | |||
| 2823 | ||||
| 2824 | switch( fts5yyruleno ){ | |||
| 2825 | /* Beginning here are the reduction cases. A typical example | |||
| 2826 | ** follows: | |||
| 2827 | ** case 0: | |||
| 2828 | ** #line <lineno> <grammarfile> | |||
| 2829 | ** { ... } // User supplied code | |||
| 2830 | ** #line <lineno> <thisfile> | |||
| 2831 | ** break; | |||
| 2832 | */ | |||
| 2833 | /********** Begin reduce actions **********************************************/ | |||
| 2834 | fts5YYMINORTYPE fts5yylhsminor; | |||
| 2835 | case 0: /* input ::= expr */ | |||
| 2836 | #line 82 "fts5parse.y" | |||
| 2837 | { sqlite3Fts5ParseFinished(pParse, fts5yymsp[0].minor.fts5yy24); } | |||
| 2838 | #line 1067 "fts5parse.c" | |||
| 2839 | break; | |||
| 2840 | case 1: /* colset ::= MINUS LCP colsetlist RCP */ | |||
| 2841 | #line 97 "fts5parse.y" | |||
| 2842 | { | |||
| 2843 | fts5yymsp[-3].minor.fts5yy11 = sqlite3Fts5ParseColsetInvert(pParse, fts5yymsp[-1].minor.fts5yy11); | |||
| 2844 | } | |||
| 2845 | #line 1074 "fts5parse.c" | |||
| 2846 | break; | |||
| 2847 | case 2: /* colset ::= LCP colsetlist RCP */ | |||
| 2848 | #line 100 "fts5parse.y" | |||
| 2849 | { fts5yymsp[-2].minor.fts5yy11 = fts5yymsp[-1].minor.fts5yy11; } | |||
| 2850 | #line 1079 "fts5parse.c" | |||
| 2851 | break; | |||
| 2852 | case 3: /* colset ::= STRING */ | |||
| 2853 | #line 101 "fts5parse.y" | |||
| 2854 | { | |||
| 2855 | fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0); | |||
| 2856 | } | |||
| 2857 | #line 1086 "fts5parse.c" | |||
| 2858 | fts5yymsp[0].minor.fts5yy11 = fts5yylhsminor.fts5yy11; | |||
| 2859 | break; | |||
| 2860 | case 4: /* colset ::= MINUS STRING */ | |||
| 2861 | #line 104 "fts5parse.y" | |||
| 2862 | { | |||
| 2863 | fts5yymsp[-1].minor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0); | |||
| 2864 | fts5yymsp[-1].minor.fts5yy11 = sqlite3Fts5ParseColsetInvert(pParse, fts5yymsp[-1].minor.fts5yy11); | |||
| 2865 | } | |||
| 2866 | #line 1095 "fts5parse.c" | |||
| 2867 | break; | |||
| 2868 | case 5: /* colsetlist ::= colsetlist STRING */ | |||
| 2869 | #line 109 "fts5parse.y" | |||
| 2870 | { | |||
| 2871 | fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, fts5yymsp[-1].minor.fts5yy11, &fts5yymsp[0].minor.fts5yy0); } | |||
| 2872 | #line 1101 "fts5parse.c" | |||
| 2873 | fts5yymsp[-1].minor.fts5yy11 = fts5yylhsminor.fts5yy11; | |||
| 2874 | break; | |||
| 2875 | case 6: /* colsetlist ::= STRING */ | |||
| 2876 | #line 111 "fts5parse.y" | |||
| 2877 | { | |||
| 2878 | fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0); | |||
| 2879 | } | |||
| 2880 | #line 1109 "fts5parse.c" | |||
| 2881 | fts5yymsp[0].minor.fts5yy11 = fts5yylhsminor.fts5yy11; | |||
| 2882 | break; | |||
| 2883 | case 7: /* expr ::= expr AND expr */ | |||
| 2884 | #line 115 "fts5parse.y" | |||
| 2885 | { | |||
| 2886 | fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_AND2, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0); | |||
| 2887 | } | |||
| 2888 | #line 1117 "fts5parse.c" | |||
| 2889 | fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2890 | break; | |||
| 2891 | case 8: /* expr ::= expr OR expr */ | |||
| 2892 | #line 118 "fts5parse.y" | |||
| 2893 | { | |||
| 2894 | fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_OR1, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0); | |||
| 2895 | } | |||
| 2896 | #line 1125 "fts5parse.c" | |||
| 2897 | fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2898 | break; | |||
| 2899 | case 9: /* expr ::= expr NOT expr */ | |||
| 2900 | #line 121 "fts5parse.y" | |||
| 2901 | { | |||
| 2902 | fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_NOT3, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0); | |||
| 2903 | } | |||
| 2904 | #line 1133 "fts5parse.c" | |||
| 2905 | fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2906 | break; | |||
| 2907 | case 10: /* expr ::= colset COLON LP expr RP */ | |||
| 2908 | #line 125 "fts5parse.y" | |||
| 2909 | { | |||
| 2910 | sqlite3Fts5ParseSetColset(pParse, fts5yymsp[-1].minor.fts5yy24, fts5yymsp[-4].minor.fts5yy11); | |||
| 2911 | fts5yylhsminor.fts5yy24 = fts5yymsp[-1].minor.fts5yy24; | |||
| 2912 | } | |||
| 2913 | #line 1142 "fts5parse.c" | |||
| 2914 | fts5yymsp[-4].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2915 | break; | |||
| 2916 | case 11: /* expr ::= LP expr RP */ | |||
| 2917 | #line 129 "fts5parse.y" | |||
| 2918 | {fts5yymsp[-2].minor.fts5yy24 = fts5yymsp[-1].minor.fts5yy24;} | |||
| 2919 | #line 1148 "fts5parse.c" | |||
| 2920 | break; | |||
| 2921 | case 12: /* expr ::= exprlist */ | |||
| 2922 | case 13: /* exprlist ::= cnearset */ fts5yytestcase(fts5yyruleno==13); | |||
| 2923 | #line 130 "fts5parse.y" | |||
| 2924 | {fts5yylhsminor.fts5yy24 = fts5yymsp[0].minor.fts5yy24;} | |||
| 2925 | #line 1154 "fts5parse.c" | |||
| 2926 | fts5yymsp[0].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2927 | break; | |||
| 2928 | case 14: /* exprlist ::= exprlist cnearset */ | |||
| 2929 | #line 133 "fts5parse.y" | |||
| 2930 | { | |||
| 2931 | fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseImplicitAnd(pParse, fts5yymsp[-1].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24); | |||
| 2932 | } | |||
| 2933 | #line 1162 "fts5parse.c" | |||
| 2934 | fts5yymsp[-1].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2935 | break; | |||
| 2936 | case 15: /* cnearset ::= nearset */ | |||
| 2937 | #line 137 "fts5parse.y" | |||
| 2938 | { | |||
| 2939 | fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING9, 0, 0, fts5yymsp[0].minor.fts5yy46); | |||
| 2940 | } | |||
| 2941 | #line 1170 "fts5parse.c" | |||
| 2942 | fts5yymsp[0].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2943 | break; | |||
| 2944 | case 16: /* cnearset ::= colset COLON nearset */ | |||
| 2945 | #line 140 "fts5parse.y" | |||
| 2946 | { | |||
| 2947 | fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING9, 0, 0, fts5yymsp[0].minor.fts5yy46); | |||
| 2948 | sqlite3Fts5ParseSetColset(pParse, fts5yylhsminor.fts5yy24, fts5yymsp[-2].minor.fts5yy11); | |||
| 2949 | } | |||
| 2950 | #line 1179 "fts5parse.c" | |||
| 2951 | fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24; | |||
| 2952 | break; | |||
| 2953 | case 17: /* nearset ::= phrase */ | |||
| 2954 | #line 151 "fts5parse.y" | |||
| 2955 | { fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53); } | |||
| 2956 | #line 1185 "fts5parse.c" | |||
| 2957 | fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46; | |||
| 2958 | break; | |||
| 2959 | case 18: /* nearset ::= CARET phrase */ | |||
| 2960 | #line 152 "fts5parse.y" | |||
| 2961 | { | |||
| 2962 | sqlite3Fts5ParseSetCaret(fts5yymsp[0].minor.fts5yy53); | |||
| 2963 | fts5yymsp[-1].minor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53); | |||
| 2964 | } | |||
| 2965 | #line 1194 "fts5parse.c" | |||
| 2966 | break; | |||
| 2967 | case 19: /* nearset ::= STRING LP nearphrases neardist_opt RP */ | |||
| 2968 | #line 156 "fts5parse.y" | |||
| 2969 | { | |||
| 2970 | sqlite3Fts5ParseNear(pParse, &fts5yymsp[-4].minor.fts5yy0); | |||
| 2971 | sqlite3Fts5ParseSetDistance(pParse, fts5yymsp[-2].minor.fts5yy46, &fts5yymsp[-1].minor.fts5yy0); | |||
| 2972 | fts5yylhsminor.fts5yy46 = fts5yymsp[-2].minor.fts5yy46; | |||
| 2973 | } | |||
| 2974 | #line 1203 "fts5parse.c" | |||
| 2975 | fts5yymsp[-4].minor.fts5yy46 = fts5yylhsminor.fts5yy46; | |||
| 2976 | break; | |||
| 2977 | case 20: /* nearphrases ::= phrase */ | |||
| 2978 | #line 162 "fts5parse.y" | |||
| 2979 | { | |||
| 2980 | fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53); | |||
| 2981 | } | |||
| 2982 | #line 1211 "fts5parse.c" | |||
| 2983 | fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46; | |||
| 2984 | break; | |||
| 2985 | case 21: /* nearphrases ::= nearphrases phrase */ | |||
| 2986 | #line 165 "fts5parse.y" | |||
| 2987 | { | |||
| 2988 | fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, fts5yymsp[-1].minor.fts5yy46, fts5yymsp[0].minor.fts5yy53); | |||
| 2989 | } | |||
| 2990 | #line 1219 "fts5parse.c" | |||
| 2991 | fts5yymsp[-1].minor.fts5yy46 = fts5yylhsminor.fts5yy46; | |||
| 2992 | break; | |||
| 2993 | case 22: /* neardist_opt ::= */ | |||
| 2994 | #line 172 "fts5parse.y" | |||
| 2995 | { fts5yymsp[1].minor.fts5yy0.p = 0; fts5yymsp[1].minor.fts5yy0.n = 0; } | |||
| 2996 | #line 1225 "fts5parse.c" | |||
| 2997 | break; | |||
| 2998 | case 23: /* neardist_opt ::= COMMA STRING */ | |||
| 2999 | #line 173 "fts5parse.y" | |||
| 3000 | { fts5yymsp[-1].minor.fts5yy0 = fts5yymsp[0].minor.fts5yy0; } | |||
| 3001 | #line 1230 "fts5parse.c" | |||
| 3002 | break; | |||
| 3003 | case 24: /* phrase ::= phrase PLUS STRING star_opt */ | |||
| 3004 | #line 185 "fts5parse.y" | |||
| 3005 | { | |||
| 3006 | fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, fts5yymsp[-3].minor.fts5yy53, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4); | |||
| 3007 | } | |||
| 3008 | #line 1237 "fts5parse.c" | |||
| 3009 | fts5yymsp[-3].minor.fts5yy53 = fts5yylhsminor.fts5yy53; | |||
| 3010 | break; | |||
| 3011 | case 25: /* phrase ::= STRING star_opt */ | |||
| 3012 | #line 188 "fts5parse.y" | |||
| 3013 | { | |||
| 3014 | fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, 0, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4); | |||
| 3015 | } | |||
| 3016 | #line 1245 "fts5parse.c" | |||
| 3017 | fts5yymsp[-1].minor.fts5yy53 = fts5yylhsminor.fts5yy53; | |||
| 3018 | break; | |||
| 3019 | case 26: /* star_opt ::= STAR */ | |||
| 3020 | #line 196 "fts5parse.y" | |||
| 3021 | { fts5yymsp[0].minor.fts5yy4 = 1; } | |||
| 3022 | #line 1251 "fts5parse.c" | |||
| 3023 | break; | |||
| 3024 | case 27: /* star_opt ::= */ | |||
| 3025 | #line 197 "fts5parse.y" | |||
| 3026 | { fts5yymsp[1].minor.fts5yy4 = 0; } | |||
| 3027 | #line 1256 "fts5parse.c" | |||
| 3028 | break; | |||
| 3029 | default: | |||
| 3030 | break; | |||
| 3031 | /********** End reduce actions ************************************************/ | |||
| 3032 | }; | |||
| 3033 | assert( fts5yyruleno<sizeof(fts5yyRuleInfoLhs)/sizeof(fts5yyRuleInfoLhs[0]) )((void) (0)); | |||
| 3034 | fts5yygoto = fts5yyRuleInfoLhs[fts5yyruleno]; | |||
| 3035 | fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; | |||
| 3036 | fts5yyact = fts5yy_find_reduce_action(fts5yymsp[fts5yysize].stateno,(fts5YYCODETYPEunsigned char)fts5yygoto); | |||
| 3037 | ||||
| 3038 | /* There are no SHIFTREDUCE actions on nonterminals because the table | |||
| 3039 | ** generator has simplified them to pure REDUCE actions. */ | |||
| 3040 | assert( !(fts5yyact>fts5YY_MAX_SHIFT && fts5yyact<=fts5YY_MAX_SHIFTREDUCE) )((void) (0)); | |||
| 3041 | ||||
| 3042 | /* It is not possible for a REDUCE to be followed by an error */ | |||
| 3043 | assert( fts5yyact!=fts5YY_ERROR_ACTION )((void) (0)); | |||
| 3044 | ||||
| 3045 | fts5yymsp += fts5yysize+1; | |||
| 3046 | fts5yypParser->fts5yytos = fts5yymsp; | |||
| 3047 | fts5yymsp->stateno = (fts5YYACTIONTYPEunsigned char)fts5yyact; | |||
| 3048 | fts5yymsp->major = (fts5YYCODETYPEunsigned char)fts5yygoto; | |||
| 3049 | fts5yyTraceShift(fts5yypParser, fts5yyact, "... then shift"); | |||
| 3050 | return fts5yyact; | |||
| 3051 | } | |||
| 3052 | ||||
| 3053 | /* | |||
| 3054 | ** The following code executes when the parse fails | |||
| 3055 | */ | |||
| 3056 | #ifndef fts5YYNOERRORRECOVERY1 | |||
| 3057 | static void fts5yy_parse_failed( | |||
| 3058 | fts5yyParser *fts5yypParser /* The parser */ | |||
| 3059 | ){ | |||
| 3060 | sqlite3Fts5ParserARG_FETCHFts5Parse *pParse=fts5yypParser->pParse; | |||
| 3061 | sqlite3Fts5ParserCTX_FETCH | |||
| 3062 | #ifndef NDEBUG1 | |||
| 3063 | if( fts5yyTraceFILE ){ | |||
| 3064 | fprintf(fts5yyTraceFILE,"%sFail!\n",fts5yyTracePrompt); | |||
| 3065 | } | |||
| 3066 | #endif | |||
| 3067 | while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ) fts5yy_pop_parser_stack(fts5yypParser); | |||
| 3068 | /* Here code is inserted which will be executed whenever the | |||
| 3069 | ** parser fails */ | |||
| 3070 | /************ Begin %parse_failure code ***************************************/ | |||
| 3071 | /************ End %parse_failure code *****************************************/ | |||
| 3072 | sqlite3Fts5ParserARG_STOREfts5yypParser->pParse=pParse; /* Suppress warning about unused %extra_argument variable */ | |||
| 3073 | sqlite3Fts5ParserCTX_STORE | |||
| 3074 | } | |||
| 3075 | #endif /* fts5YYNOERRORRECOVERY */ | |||
| 3076 | ||||
| 3077 | /* | |||
| 3078 | ** The following code executes when a syntax error first occurs. | |||
| 3079 | */ | |||
| 3080 | static void fts5yy_syntax_error( | |||
| 3081 | fts5yyParser *fts5yypParser, /* The parser */ | |||
| 3082 | int fts5yymajor, /* The major type of the error token */ | |||
| 3083 | sqlite3Fts5ParserFTS5TOKENTYPEFts5Token fts5yyminor /* The minor type of the error token */ | |||
| 3084 | ){ | |||
| 3085 | sqlite3Fts5ParserARG_FETCHFts5Parse *pParse=fts5yypParser->pParse; | |||
| 3086 | sqlite3Fts5ParserCTX_FETCH | |||
| 3087 | #define FTS5TOKENfts5yyminor fts5yyminor | |||
| 3088 | /************ Begin %syntax_error code ****************************************/ | |||
| 3089 | #line 30 "fts5parse.y" | |||
| 3090 | ||||
| 3091 | UNUSED_PARAM(fts5yymajor)(void)(fts5yymajor); /* Silence a compiler warning */ | |||
| 3092 | sqlite3Fts5ParseError( | |||
| 3093 | pParse, "fts5: syntax error near \"%.*s\"",FTS5TOKENfts5yyminor.n,FTS5TOKENfts5yyminor.p | |||
| 3094 | ); | |||
| 3095 | #line 1324 "fts5parse.c" | |||
| 3096 | /************ End %syntax_error code ******************************************/ | |||
| 3097 | sqlite3Fts5ParserARG_STOREfts5yypParser->pParse=pParse; /* Suppress warning about unused %extra_argument variable */ | |||
| 3098 | sqlite3Fts5ParserCTX_STORE | |||
| 3099 | } | |||
| 3100 | ||||
| 3101 | /* | |||
| 3102 | ** The following is executed when the parser accepts | |||
| 3103 | */ | |||
| 3104 | static void fts5yy_accept( | |||
| 3105 | fts5yyParser *fts5yypParser /* The parser */ | |||
| 3106 | ){ | |||
| 3107 | sqlite3Fts5ParserARG_FETCHFts5Parse *pParse=fts5yypParser->pParse; | |||
| 3108 | sqlite3Fts5ParserCTX_FETCH | |||
| 3109 | #ifndef NDEBUG1 | |||
| 3110 | if( fts5yyTraceFILE ){ | |||
| 3111 | fprintf(fts5yyTraceFILE,"%sAccept!\n",fts5yyTracePrompt); | |||
| 3112 | } | |||
| 3113 | #endif | |||
| 3114 | #ifndef fts5YYNOERRORRECOVERY1 | |||
| 3115 | fts5yypParser->fts5yyerrcnt = -1; | |||
| 3116 | #endif | |||
| 3117 | assert( fts5yypParser->fts5yytos==fts5yypParser->fts5yystack )((void) (0)); | |||
| 3118 | /* Here code is inserted which will be executed whenever the | |||
| 3119 | ** parser accepts */ | |||
| 3120 | /*********** Begin %parse_accept code *****************************************/ | |||
| 3121 | /*********** End %parse_accept code *******************************************/ | |||
| 3122 | sqlite3Fts5ParserARG_STOREfts5yypParser->pParse=pParse; /* Suppress warning about unused %extra_argument variable */ | |||
| 3123 | sqlite3Fts5ParserCTX_STORE | |||
| 3124 | } | |||
| 3125 | ||||
| 3126 | /* The main parser program. | |||
| 3127 | ** The first argument is a pointer to a structure obtained from | |||
| 3128 | ** "sqlite3Fts5ParserAlloc" which describes the current state of the parser. | |||
| 3129 | ** The second argument is the major token number. The third is | |||
| 3130 | ** the minor token. The fourth optional argument is whatever the | |||
| 3131 | ** user wants (and specified in the grammar) and is available for | |||
| 3132 | ** use by the action routines. | |||
| 3133 | ** | |||
| 3134 | ** Inputs: | |||
| 3135 | ** <ul> | |||
| 3136 | ** <li> A pointer to the parser (an opaque structure.) | |||
| 3137 | ** <li> The major token number. | |||
| 3138 | ** <li> The minor token number. | |||
| 3139 | ** <li> An option argument of a grammar-specified type. | |||
| 3140 | ** </ul> | |||
| 3141 | ** | |||
| 3142 | ** Outputs: | |||
| 3143 | ** None. | |||
| 3144 | */ | |||
| 3145 | static void sqlite3Fts5Parser( | |||
| 3146 | void *fts5yyp, /* The parser */ | |||
| 3147 | int fts5yymajor, /* The major token code number */ | |||
| 3148 | sqlite3Fts5ParserFTS5TOKENTYPEFts5Token fts5yyminor /* The value for the token */ | |||
| 3149 | sqlite3Fts5ParserARG_PDECL,Fts5Parse *pParse /* Optional %extra_argument parameter */ | |||
| 3150 | ){ | |||
| 3151 | fts5YYMINORTYPE fts5yyminorunion; | |||
| 3152 | fts5YYACTIONTYPEunsigned char fts5yyact; /* The parser action. */ | |||
| 3153 | #if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY1) | |||
| 3154 | int fts5yyendofinput; /* True if we are at the end of input */ | |||
| 3155 | #endif | |||
| 3156 | #ifdef fts5YYERRORSYMBOL | |||
| 3157 | int fts5yyerrorhit = 0; /* True if fts5yymajor has invoked an error */ | |||
| 3158 | #endif | |||
| 3159 | fts5yyParser *fts5yypParser = (fts5yyParser*)fts5yyp; /* The parser */ | |||
| 3160 | sqlite3Fts5ParserCTX_FETCH | |||
| 3161 | sqlite3Fts5ParserARG_STOREfts5yypParser->pParse=pParse; | |||
| 3162 | ||||
| 3163 | assert( fts5yypParser->fts5yytos!=0 )((void) (0)); | |||
| 3164 | #if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY1) | |||
| 3165 | fts5yyendofinput = (fts5yymajor==0); | |||
| 3166 | #endif | |||
| 3167 | ||||
| 3168 | fts5yyact = fts5yypParser->fts5yytos->stateno; | |||
| 3169 | #ifndef NDEBUG1 | |||
| 3170 | if( fts5yyTraceFILE ){ | |||
| 3171 | if( fts5yyact < fts5YY_MIN_REDUCE83 ){ | |||
| 3172 | fprintf(fts5yyTraceFILE,"%sInput '%s' in state %d\n", | |||
| 3173 | fts5yyTracePrompt,fts5yyTokenName[fts5yymajor],fts5yyact); | |||
| 3174 | }else{ | |||
| 3175 | fprintf(fts5yyTraceFILE,"%sInput '%s' with pending reduce %d\n", | |||
| 3176 | fts5yyTracePrompt,fts5yyTokenName[fts5yymajor],fts5yyact-fts5YY_MIN_REDUCE83); | |||
| 3177 | } | |||
| 3178 | } | |||
| 3179 | #endif | |||
| 3180 | ||||
| 3181 | while(1){ /* Exit by "break" */ | |||
| 3182 | assert( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystack )((void) (0)); | |||
| 3183 | assert( fts5yyact==fts5yypParser->fts5yytos->stateno )((void) (0)); | |||
| 3184 | fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPEunsigned char)fts5yymajor,fts5yyact); | |||
| 3185 | if( fts5yyact >= fts5YY_MIN_REDUCE83 ){ | |||
| 3186 | unsigned int fts5yyruleno = fts5yyact - fts5YY_MIN_REDUCE83; /* Reduce by this rule */ | |||
| 3187 | #ifndef NDEBUG1 | |||
| 3188 | assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) )((void) (0)); | |||
| 3189 | if( fts5yyTraceFILE ){ | |||
| 3190 | int fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; | |||
| 3191 | if( fts5yysize ){ | |||
| 3192 | fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", | |||
| 3193 | fts5yyTracePrompt, | |||
| 3194 | fts5yyruleno, fts5yyRuleName[fts5yyruleno], | |||
| 3195 | fts5yyruleno<fts5YYNRULE_WITH_ACTION28 ? "" : " without external action", | |||
| 3196 | fts5yypParser->fts5yytos[fts5yysize].stateno); | |||
| 3197 | }else{ | |||
| 3198 | fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s.\n", | |||
| 3199 | fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno], | |||
| 3200 | fts5yyruleno<fts5YYNRULE_WITH_ACTION28 ? "" : " without external action"); | |||
| 3201 | } | |||
| 3202 | } | |||
| 3203 | #endif /* NDEBUG */ | |||
| 3204 | ||||
| 3205 | /* Check that the stack is large enough to grow by a single entry | |||
| 3206 | ** if the RHS of the rule is empty. This ensures that there is room | |||
| 3207 | ** enough on the stack to push the LHS value */ | |||
| 3208 | if( fts5yyRuleInfoNRhs[fts5yyruleno]==0 ){ | |||
| 3209 | #ifdef fts5YYTRACKMAXSTACKDEPTH | |||
| 3210 | if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ | |||
| 3211 | fts5yypParser->fts5yyhwm++; | |||
| 3212 | assert( fts5yypParser->fts5yyhwm ==((void) (0)) | |||
| 3213 | (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack))((void) (0)); | |||
| 3214 | } | |||
| 3215 | #endif | |||
| 3216 | if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ | |||
| 3217 | if( fts5yyGrowStack(fts5yypParser)1 ){ | |||
| 3218 | fts5yyStackOverflow(fts5yypParser); | |||
| 3219 | break; | |||
| 3220 | } | |||
| 3221 | } | |||
| 3222 | } | |||
| 3223 | fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM); | |||
| 3224 | }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE79 ){ | |||
| 3225 | fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPEunsigned char)fts5yymajor,fts5yyminor); | |||
| 3226 | #ifndef fts5YYNOERRORRECOVERY1 | |||
| 3227 | fts5yypParser->fts5yyerrcnt--; | |||
| 3228 | #endif | |||
| 3229 | break; | |||
| 3230 | }else if( fts5yyact==fts5YY_ACCEPT_ACTION81 ){ | |||
| 3231 | fts5yypParser->fts5yytos--; | |||
| 3232 | fts5yy_accept(fts5yypParser); | |||
| 3233 | return; | |||
| 3234 | }else{ | |||
| 3235 | assert( fts5yyact == fts5YY_ERROR_ACTION )((void) (0)); | |||
| 3236 | fts5yyminorunion.fts5yy0 = fts5yyminor; | |||
| 3237 | #ifdef fts5YYERRORSYMBOL | |||
| 3238 | int fts5yymx; | |||
| 3239 | #endif | |||
| 3240 | #ifndef NDEBUG1 | |||
| 3241 | if( fts5yyTraceFILE ){ | |||
| 3242 | fprintf(fts5yyTraceFILE,"%sSyntax Error!\n",fts5yyTracePrompt); | |||
| 3243 | } | |||
| 3244 | #endif | |||
| 3245 | #ifdef fts5YYERRORSYMBOL | |||
| 3246 | /* A syntax error has occurred. | |||
| 3247 | ** The response to an error depends upon whether or not the | |||
| 3248 | ** grammar defines an error token "ERROR". | |||
| 3249 | ** | |||
| 3250 | ** This is what we do if the grammar does define ERROR: | |||
| 3251 | ** | |||
| 3252 | ** * Call the %syntax_error function. | |||
| 3253 | ** | |||
| 3254 | ** * Begin popping the stack until we enter a state where | |||
| 3255 | ** it is legal to shift the error symbol, then shift | |||
| 3256 | ** the error symbol. | |||
| 3257 | ** | |||
| 3258 | ** * Set the error count to three. | |||
| 3259 | ** | |||
| 3260 | ** * Begin accepting and shifting new tokens. No new error | |||
| 3261 | ** processing will occur until three tokens have been | |||
| 3262 | ** shifted successfully. | |||
| 3263 | ** | |||
| 3264 | */ | |||
| 3265 | if( fts5yypParser->fts5yyerrcnt<0 ){ | |||
| 3266 | fts5yy_syntax_error(fts5yypParser,fts5yymajor,fts5yyminor); | |||
| 3267 | } | |||
| 3268 | fts5yymx = fts5yypParser->fts5yytos->major; | |||
| 3269 | if( fts5yymx==fts5YYERRORSYMBOL || fts5yyerrorhit ){ | |||
| 3270 | #ifndef NDEBUG1 | |||
| 3271 | if( fts5yyTraceFILE ){ | |||
| 3272 | fprintf(fts5yyTraceFILE,"%sDiscard input token %s\n", | |||
| 3273 | fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]); | |||
| 3274 | } | |||
| 3275 | #endif | |||
| 3276 | fts5yy_destructor(fts5yypParser, (fts5YYCODETYPEunsigned char)fts5yymajor, &fts5yyminorunion); | |||
| 3277 | fts5yymajor = fts5YYNOCODE27; | |||
| 3278 | }else{ | |||
| 3279 | while( fts5yypParser->fts5yytos > fts5yypParser->fts5yystack ){ | |||
| 3280 | fts5yyact = fts5yy_find_reduce_action(fts5yypParser->fts5yytos->stateno, | |||
| 3281 | fts5YYERRORSYMBOL); | |||
| 3282 | if( fts5yyact<=fts5YY_MAX_SHIFTREDUCE79 ) break; | |||
| 3283 | fts5yy_pop_parser_stack(fts5yypParser); | |||
| 3284 | } | |||
| 3285 | if( fts5yypParser->fts5yytos <= fts5yypParser->fts5yystack || fts5yymajor==0 ){ | |||
| 3286 | fts5yy_destructor(fts5yypParser,(fts5YYCODETYPEunsigned char)fts5yymajor,&fts5yyminorunion); | |||
| 3287 | fts5yy_parse_failed(fts5yypParser); | |||
| 3288 | #ifndef fts5YYNOERRORRECOVERY1 | |||
| 3289 | fts5yypParser->fts5yyerrcnt = -1; | |||
| 3290 | #endif | |||
| 3291 | fts5yymajor = fts5YYNOCODE27; | |||
| 3292 | }else if( fts5yymx!=fts5YYERRORSYMBOL ){ | |||
| 3293 | fts5yy_shift(fts5yypParser,fts5yyact,fts5YYERRORSYMBOL,fts5yyminor); | |||
| 3294 | } | |||
| 3295 | } | |||
| 3296 | fts5yypParser->fts5yyerrcnt = 3; | |||
| 3297 | fts5yyerrorhit = 1; | |||
| 3298 | if( fts5yymajor==fts5YYNOCODE27 ) break; | |||
| 3299 | fts5yyact = fts5yypParser->fts5yytos->stateno; | |||
| 3300 | #elif defined(fts5YYNOERRORRECOVERY1) | |||
| 3301 | /* If the fts5YYNOERRORRECOVERY macro is defined, then do not attempt to | |||
| 3302 | ** do any kind of error recovery. Instead, simply invoke the syntax | |||
| 3303 | ** error routine and continue going as if nothing had happened. | |||
| 3304 | ** | |||
| 3305 | ** Applications can set this macro (for example inside %include) if | |||
| 3306 | ** they intend to abandon the parse upon the first syntax error seen. | |||
| 3307 | */ | |||
| 3308 | fts5yy_syntax_error(fts5yypParser,fts5yymajor, fts5yyminor); | |||
| 3309 | fts5yy_destructor(fts5yypParser,(fts5YYCODETYPEunsigned char)fts5yymajor,&fts5yyminorunion); | |||
| 3310 | break; | |||
| 3311 | #else /* fts5YYERRORSYMBOL is not defined */ | |||
| 3312 | /* This is what we do if the grammar does not define ERROR: | |||
| 3313 | ** | |||
| 3314 | ** * Report an error message, and throw away the input token. | |||
| 3315 | ** | |||
| 3316 | ** * If the input token is $, then fail the parse. | |||
| 3317 | ** | |||
| 3318 | ** As before, subsequent error messages are suppressed until | |||
| 3319 | ** three input tokens have been successfully shifted. | |||
| 3320 | */ | |||
| 3321 | if( fts5yypParser->fts5yyerrcnt<=0 ){ | |||
| 3322 | fts5yy_syntax_error(fts5yypParser,fts5yymajor, fts5yyminor); | |||
| 3323 | } | |||
| 3324 | fts5yypParser->fts5yyerrcnt = 3; | |||
| 3325 | fts5yy_destructor(fts5yypParser,(fts5YYCODETYPEunsigned char)fts5yymajor,&fts5yyminorunion); | |||
| 3326 | if( fts5yyendofinput ){ | |||
| 3327 | fts5yy_parse_failed(fts5yypParser); | |||
| 3328 | #ifndef fts5YYNOERRORRECOVERY1 | |||
| 3329 | fts5yypParser->fts5yyerrcnt = -1; | |||
| 3330 | #endif | |||
| 3331 | } | |||
| 3332 | break; | |||
| 3333 | #endif | |||
| 3334 | } | |||
| 3335 | } | |||
| 3336 | #ifndef NDEBUG1 | |||
| 3337 | if( fts5yyTraceFILE ){ | |||
| 3338 | fts5yyStackEntry *i; | |||
| 3339 | char cDiv = '['; | |||
| 3340 | fprintf(fts5yyTraceFILE,"%sReturn. Stack=",fts5yyTracePrompt); | |||
| 3341 | for(i=&fts5yypParser->fts5yystack[1]; i<=fts5yypParser->fts5yytos; i++){ | |||
| 3342 | fprintf(fts5yyTraceFILE,"%c%s", cDiv, fts5yyTokenName[i->major]); | |||
| 3343 | cDiv = ' '; | |||
| 3344 | } | |||
| 3345 | fprintf(fts5yyTraceFILE,"]\n"); | |||
| 3346 | } | |||
| 3347 | #endif | |||
| 3348 | return; | |||
| 3349 | } | |||
| 3350 | ||||
| 3351 | /* | |||
| 3352 | ** Return the fallback token corresponding to canonical token iToken, or | |||
| 3353 | ** 0 if iToken has no fallback. | |||
| 3354 | */ | |||
| 3355 | static int sqlite3Fts5ParserFallback(int iToken){ | |||
| 3356 | #ifdef fts5YYFALLBACK | |||
| 3357 | assert( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) )((void) (0)); | |||
| 3358 | return fts5yyFallback[iToken]; | |||
| 3359 | #else | |||
| 3360 | (void)iToken; | |||
| 3361 | return 0; | |||
| 3362 | #endif | |||
| 3363 | } | |||
| 3364 | ||||
| 3365 | #line 1 "fts5_aux.c" | |||
| 3366 | /* | |||
| 3367 | ** 2014 May 31 | |||
| 3368 | ** | |||
| 3369 | ** The author disclaims copyright to this source code. In place of | |||
| 3370 | ** a legal notice, here is a blessing: | |||
| 3371 | ** | |||
| 3372 | ** May you do good and not evil. | |||
| 3373 | ** May you find forgiveness for yourself and forgive others. | |||
| 3374 | ** May you share freely, never taking more than you give. | |||
| 3375 | ** | |||
| 3376 | ****************************************************************************** | |||
| 3377 | */ | |||
| 3378 | ||||
| 3379 | ||||
| 3380 | /* #include "fts5Int.h" */ | |||
| 3381 | #include <math.h> /* amalgamator: keep */ | |||
| 3382 | ||||
| 3383 | /* | |||
| 3384 | ** Object used to iterate through all "coalesced phrase instances" in | |||
| 3385 | ** a single column of the current row. If the phrase instances in the | |||
| 3386 | ** column being considered do not overlap, this object simply iterates | |||
| 3387 | ** through them. Or, if they do overlap (share one or more tokens in | |||
| 3388 | ** common), each set of overlapping instances is treated as a single | |||
| 3389 | ** match. See documentation for the highlight() auxiliary function for | |||
| 3390 | ** details. | |||
| 3391 | ** | |||
| 3392 | ** Usage is: | |||
| 3393 | ** | |||
| 3394 | ** for(rc = fts5CInstIterNext(pApi, pFts, iCol, &iter); | |||
| 3395 | ** (rc==SQLITE_OK && 0==fts5CInstIterEof(&iter); | |||
| 3396 | ** rc = fts5CInstIterNext(&iter) | |||
| 3397 | ** ){ | |||
| 3398 | ** printf("instance starts at %d, ends at %d\n", iter.iStart, iter.iEnd); | |||
| 3399 | ** } | |||
| 3400 | ** | |||
| 3401 | */ | |||
| 3402 | typedef struct CInstIter CInstIter; | |||
| 3403 | struct CInstIter { | |||
| 3404 | const Fts5ExtensionApi *pApi; /* API offered by current FTS version */ | |||
| 3405 | Fts5Context *pFts; /* First arg to pass to pApi functions */ | |||
| 3406 | int iCol; /* Column to search */ | |||
| 3407 | int iInst; /* Next phrase instance index */ | |||
| 3408 | int nInst; /* Total number of phrase instances */ | |||
| 3409 | ||||
| 3410 | /* Output variables */ | |||
| 3411 | int iStart; /* First token in coalesced phrase instance */ | |||
| 3412 | int iEnd; /* Last token in coalesced phrase instance */ | |||
| 3413 | }; | |||
| 3414 | ||||
| 3415 | /* | |||
| 3416 | ** Advance the iterator to the next coalesced phrase instance. Return | |||
| 3417 | ** an SQLite error code if an error occurs, or SQLITE_OK otherwise. | |||
| 3418 | */ | |||
| 3419 | static int fts5CInstIterNext(CInstIter *pIter){ | |||
| 3420 | int rc = SQLITE_OK0; | |||
| 3421 | pIter->iStart = -1; | |||
| 3422 | pIter->iEnd = -1; | |||
| 3423 | ||||
| 3424 | while( rc==SQLITE_OK0 && pIter->iInst<pIter->nInst ){ | |||
| 3425 | int ip; int ic; int io; | |||
| 3426 | rc = pIter->pApi->xInst(pIter->pFts, pIter->iInst, &ip, &ic, &io); | |||
| 3427 | if( rc==SQLITE_OK0 ){ | |||
| 3428 | if( ic==pIter->iCol ){ | |||
| 3429 | int iEnd = io - 1 + pIter->pApi->xPhraseSize(pIter->pFts, ip); | |||
| 3430 | if( pIter->iStart<0 ){ | |||
| 3431 | pIter->iStart = io; | |||
| 3432 | pIter->iEnd = iEnd; | |||
| 3433 | }else if( io<=pIter->iEnd ){ | |||
| 3434 | if( iEnd>pIter->iEnd ) pIter->iEnd = iEnd; | |||
| 3435 | }else{ | |||
| 3436 | break; | |||
| 3437 | } | |||
| 3438 | } | |||
| 3439 | pIter->iInst++; | |||
| 3440 | } | |||
| 3441 | } | |||
| 3442 | ||||
| 3443 | return rc; | |||
| 3444 | } | |||
| 3445 | ||||
| 3446 | /* | |||
| 3447 | ** Initialize the iterator object indicated by the final parameter to | |||
| 3448 | ** iterate through coalesced phrase instances in column iCol. | |||
| 3449 | */ | |||
| 3450 | static int fts5CInstIterInit( | |||
| 3451 | const Fts5ExtensionApi *pApi, | |||
| 3452 | Fts5Context *pFts, | |||
| 3453 | int iCol, | |||
| 3454 | CInstIter *pIter | |||
| 3455 | ){ | |||
| 3456 | int rc; | |||
| 3457 | ||||
| 3458 | memset(pIter, 0, sizeof(CInstIter)); | |||
| 3459 | pIter->pApi = pApi; | |||
| 3460 | pIter->pFts = pFts; | |||
| 3461 | pIter->iCol = iCol; | |||
| 3462 | rc = pApi->xInstCount(pFts, &pIter->nInst); | |||
| 3463 | ||||
| 3464 | if( rc==SQLITE_OK0 ){ | |||
| 3465 | rc = fts5CInstIterNext(pIter); | |||
| 3466 | } | |||
| 3467 | ||||
| 3468 | return rc; | |||
| 3469 | } | |||
| 3470 | ||||
| 3471 | ||||
| 3472 | ||||
| 3473 | /************************************************************************* | |||
| 3474 | ** Start of highlight() implementation. | |||
| 3475 | */ | |||
| 3476 | typedef struct HighlightContext HighlightContext; | |||
| 3477 | struct HighlightContext { | |||
| 3478 | /* Constant parameters to fts5HighlightCb() */ | |||
| 3479 | int iRangeStart; /* First token to include */ | |||
| 3480 | int iRangeEnd; /* If non-zero, last token to include */ | |||
| 3481 | const char *zOpen; /* Opening highlight */ | |||
| 3482 | const char *zClose; /* Closing highlight */ | |||
| 3483 | const char *zIn; /* Input text */ | |||
| 3484 | int nIn; /* Size of input text in bytes */ | |||
| 3485 | ||||
| 3486 | /* Variables modified by fts5HighlightCb() */ | |||
| 3487 | CInstIter iter; /* Coalesced Instance Iterator */ | |||
| 3488 | int iPos; /* Current token offset in zIn[] */ | |||
| 3489 | int iOff; /* Have copied up to this offset in zIn[] */ | |||
| 3490 | int bOpen; /* True if highlight is open */ | |||
| 3491 | char *zOut; /* Output value */ | |||
| 3492 | }; | |||
| 3493 | ||||
| 3494 | /* | |||
| 3495 | ** Append text to the HighlightContext output string - p->zOut. Argument | |||
| 3496 | ** z points to a buffer containing n bytes of text to append. If n is | |||
| 3497 | ** negative, everything up until the first '\0' is appended to the output. | |||
| 3498 | ** | |||
| 3499 | ** If *pRc is set to any value other than SQLITE_OK when this function is | |||
| 3500 | ** called, it is a no-op. If an error (i.e. an OOM condition) is encountered, | |||
| 3501 | ** *pRc is set to an error code before returning. | |||
| 3502 | */ | |||
| 3503 | static void fts5HighlightAppend( | |||
| 3504 | int *pRc, | |||
| 3505 | HighlightContext *p, | |||
| 3506 | const char *z, int n | |||
| 3507 | ){ | |||
| 3508 | if( *pRc==SQLITE_OK0 && z ){ | |||
| 3509 | if( n<0 ) n = (int)strlen(z); | |||
| 3510 | p->zOut = sqlite3_mprintfsqlite3_api->mprintf("%z%.*s", p->zOut, n, z); | |||
| 3511 | if( p->zOut==0 ) *pRc = SQLITE_NOMEM7; | |||
| 3512 | } | |||
| 3513 | } | |||
| 3514 | ||||
| 3515 | /* | |||
| 3516 | ** Tokenizer callback used by implementation of highlight() function. | |||
| 3517 | */ | |||
| 3518 | static int fts5HighlightCb( | |||
| 3519 | void *pContext, /* Pointer to HighlightContext object */ | |||
| 3520 | int tflags, /* Mask of FTS5_TOKEN_* flags */ | |||
| 3521 | const char *pToken, /* Buffer containing token */ | |||
| 3522 | int nToken, /* Size of token in bytes */ | |||
| 3523 | int iStartOff, /* Start byte offset of token */ | |||
| 3524 | int iEndOff /* End byte offset of token */ | |||
| 3525 | ){ | |||
| 3526 | HighlightContext *p = (HighlightContext*)pContext; | |||
| 3527 | int rc = SQLITE_OK0; | |||
| 3528 | int iPos; | |||
| 3529 | ||||
| 3530 | UNUSED_PARAM2(pToken, nToken)(void)(pToken), (void)(nToken); | |||
| 3531 | ||||
| 3532 | if( tflags & FTS5_TOKEN_COLOCATED0x0001 ) return SQLITE_OK0; | |||
| 3533 | iPos = p->iPos++; | |||
| 3534 | ||||
| 3535 | if( p->iRangeEnd>=0 ){ | |||
| 3536 | if( iPos<p->iRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK0; | |||
| 3537 | if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff; | |||
| 3538 | } | |||
| 3539 | ||||
| 3540 | /* If the parenthesis is open, and this token is not part of the current | |||
| 3541 | ** phrase, and the starting byte offset of this token is past the point | |||
| 3542 | ** that has currently been copied into the output buffer, close the | |||
| 3543 | ** parenthesis. */ | |||
| 3544 | if( p->bOpen | |||
| 3545 | && (iPos<=p->iter.iStart || p->iter.iStart<0) | |||
| 3546 | && iStartOff>p->iOff | |||
| 3547 | ){ | |||
| 3548 | fts5HighlightAppend(&rc, p, p->zClose, -1); | |||
| 3549 | p->bOpen = 0; | |||
| 3550 | } | |||
| 3551 | ||||
| 3552 | /* If this is the start of a new phrase, and the highlight is not open: | |||
| 3553 | ** | |||
| 3554 | ** * copy text from the input up to the start of the phrase, and | |||
| 3555 | ** * open the highlight. | |||
| 3556 | */ | |||
| 3557 | if( iPos==p->iter.iStart && p->bOpen==0 ){ | |||
| 3558 | fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff); | |||
| 3559 | fts5HighlightAppend(&rc, p, p->zOpen, -1); | |||
| 3560 | p->iOff = iStartOff; | |||
| 3561 | p->bOpen = 1; | |||
| 3562 | } | |||
| 3563 | ||||
| 3564 | if( iPos==p->iter.iEnd ){ | |||
| 3565 | if( p->bOpen==0 ){ | |||
| 3566 | assert( p->iRangeEnd>=0 )((void) (0)); | |||
| 3567 | fts5HighlightAppend(&rc, p, p->zOpen, -1); | |||
| 3568 | p->bOpen = 1; | |||
| 3569 | } | |||
| 3570 | fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); | |||
| 3571 | p->iOff = iEndOff; | |||
| 3572 | ||||
| 3573 | if( rc==SQLITE_OK0 ){ | |||
| 3574 | rc = fts5CInstIterNext(&p->iter); | |||
| 3575 | } | |||
| 3576 | } | |||
| 3577 | ||||
| 3578 | if( iPos==p->iRangeEnd ){ | |||
| 3579 | if( p->bOpen ){ | |||
| 3580 | if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){ | |||
| 3581 | fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); | |||
| 3582 | p->iOff = iEndOff; | |||
| 3583 | } | |||
| 3584 | fts5HighlightAppend(&rc, p, p->zClose, -1); | |||
| 3585 | p->bOpen = 0; | |||
| 3586 | } | |||
| 3587 | fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); | |||
| 3588 | p->iOff = iEndOff; | |||
| 3589 | } | |||
| 3590 | ||||
| 3591 | return rc; | |||
| 3592 | } | |||
| 3593 | ||||
| 3594 | ||||
| 3595 | /* | |||
| 3596 | ** Implementation of highlight() function. | |||
| 3597 | */ | |||
| 3598 | static void fts5HighlightFunction( | |||
| 3599 | const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ | |||
| 3600 | Fts5Context *pFts, /* First arg to pass to pApi functions */ | |||
| 3601 | sqlite3_context *pCtx, /* Context for returning result/error */ | |||
| 3602 | int nVal, /* Number of values in apVal[] array */ | |||
| 3603 | sqlite3_value **apVal /* Array of trailing arguments */ | |||
| 3604 | ){ | |||
| 3605 | HighlightContext ctx; | |||
| 3606 | int rc; | |||
| 3607 | int iCol; | |||
| 3608 | ||||
| 3609 | if( nVal!=3 ){ | |||
| 3610 | const char *zErr = "wrong number of arguments to function highlight()"; | |||
| 3611 | sqlite3_result_errorsqlite3_api->result_error(pCtx, zErr, -1); | |||
| 3612 | return; | |||
| 3613 | } | |||
| 3614 | ||||
| 3615 | iCol = sqlite3_value_intsqlite3_api->value_int(apVal[0]); | |||
| 3616 | memset(&ctx, 0, sizeof(HighlightContext)); | |||
| 3617 | ctx.zOpen = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[1]); | |||
| 3618 | ctx.zClose = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[2]); | |||
| 3619 | ctx.iRangeEnd = -1; | |||
| 3620 | rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); | |||
| 3621 | if( rc==SQLITE_RANGE25 ){ | |||
| 3622 | sqlite3_result_textsqlite3_api->result_text(pCtx, "", -1, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 3623 | rc = SQLITE_OK0; | |||
| 3624 | }else if( ctx.zIn ){ | |||
| 3625 | const char *pLoc = 0; /* Locale of column iCol */ | |||
| 3626 | int nLoc = 0; /* Size of pLoc in bytes */ | |||
| 3627 | if( rc==SQLITE_OK0 ){ | |||
| 3628 | rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); | |||
| 3629 | } | |||
| 3630 | ||||
| 3631 | if( rc==SQLITE_OK0 ){ | |||
| 3632 | rc = pApi->xColumnLocale(pFts, iCol, &pLoc, &nLoc); | |||
| 3633 | } | |||
| 3634 | if( rc==SQLITE_OK0 ){ | |||
| 3635 | rc = pApi->xTokenize_v2( | |||
| 3636 | pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx, fts5HighlightCb | |||
| 3637 | ); | |||
| 3638 | } | |||
| 3639 | if( ctx.bOpen ){ | |||
| 3640 | fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); | |||
| 3641 | } | |||
| 3642 | fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); | |||
| 3643 | ||||
| 3644 | if( rc==SQLITE_OK0 ){ | |||
| 3645 | sqlite3_result_textsqlite3_api->result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 3646 | } | |||
| 3647 | sqlite3_freesqlite3_api->free(ctx.zOut); | |||
| 3648 | } | |||
| 3649 | if( rc!=SQLITE_OK0 ){ | |||
| 3650 | sqlite3_result_error_codesqlite3_api->result_error_code(pCtx, rc); | |||
| 3651 | } | |||
| 3652 | } | |||
| 3653 | /* | |||
| 3654 | ** End of highlight() implementation. | |||
| 3655 | **************************************************************************/ | |||
| 3656 | ||||
| 3657 | /* | |||
| 3658 | ** Context object passed to the fts5SentenceFinderCb() function. | |||
| 3659 | */ | |||
| 3660 | typedef struct Fts5SFinder Fts5SFinder; | |||
| 3661 | struct Fts5SFinder { | |||
| 3662 | int iPos; /* Current token position */ | |||
| 3663 | int nFirstAlloc; /* Allocated size of aFirst[] */ | |||
| 3664 | int nFirst; /* Number of entries in aFirst[] */ | |||
| 3665 | int *aFirst; /* Array of first token in each sentence */ | |||
| 3666 | const char *zDoc; /* Document being tokenized */ | |||
| 3667 | }; | |||
| 3668 | ||||
| 3669 | /* | |||
| 3670 | ** Add an entry to the Fts5SFinder.aFirst[] array. Grow the array if | |||
| 3671 | ** necessary. Return SQLITE_OK if successful, or SQLITE_NOMEM if an | |||
| 3672 | ** error occurs. | |||
| 3673 | */ | |||
| 3674 | static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){ | |||
| 3675 | if( p->nFirstAlloc==p->nFirst ){ | |||
| 3676 | int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64; | |||
| 3677 | int *aNew; | |||
| 3678 | ||||
| 3679 | aNew = (int*)sqlite3_realloc64sqlite3_api->realloc64(p->aFirst, nNew*sizeof(int)); | |||
| 3680 | if( aNew==0 ) return SQLITE_NOMEM7; | |||
| 3681 | p->aFirst = aNew; | |||
| 3682 | p->nFirstAlloc = nNew; | |||
| 3683 | } | |||
| 3684 | p->aFirst[p->nFirst++] = iAdd; | |||
| 3685 | return SQLITE_OK0; | |||
| 3686 | } | |||
| 3687 | ||||
| 3688 | /* | |||
| 3689 | ** This function is an xTokenize() callback used by the auxiliary snippet() | |||
| 3690 | ** function. Its job is to identify tokens that are the first in a sentence. | |||
| 3691 | ** For each such token, an entry is added to the SFinder.aFirst[] array. | |||
| 3692 | */ | |||
| 3693 | static int fts5SentenceFinderCb( | |||
| 3694 | void *pContext, /* Pointer to HighlightContext object */ | |||
| 3695 | int tflags, /* Mask of FTS5_TOKEN_* flags */ | |||
| 3696 | const char *pToken, /* Buffer containing token */ | |||
| 3697 | int nToken, /* Size of token in bytes */ | |||
| 3698 | int iStartOff, /* Start offset of token */ | |||
| 3699 | int iEndOff /* End offset of token */ | |||
| 3700 | ){ | |||
| 3701 | int rc = SQLITE_OK0; | |||
| 3702 | ||||
| 3703 | UNUSED_PARAM2(pToken, nToken)(void)(pToken), (void)(nToken); | |||
| 3704 | UNUSED_PARAM(iEndOff)(void)(iEndOff); | |||
| 3705 | ||||
| 3706 | if( (tflags & FTS5_TOKEN_COLOCATED0x0001)==0 ){ | |||
| 3707 | Fts5SFinder *p = (Fts5SFinder*)pContext; | |||
| 3708 | if( p->iPos>0 ){ | |||
| 3709 | int i; | |||
| 3710 | char c = 0; | |||
| 3711 | for(i=iStartOff-1; i>=0; i--){ | |||
| 3712 | c = p->zDoc[i]; | |||
| 3713 | if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) break; | |||
| 3714 | } | |||
| 3715 | if( i!=iStartOff-1 && (c=='.' || c==':') ){ | |||
| 3716 | rc = fts5SentenceFinderAdd(p, p->iPos); | |||
| 3717 | } | |||
| 3718 | }else{ | |||
| 3719 | rc = fts5SentenceFinderAdd(p, 0); | |||
| 3720 | } | |||
| 3721 | p->iPos++; | |||
| 3722 | } | |||
| 3723 | return rc; | |||
| 3724 | } | |||
| 3725 | ||||
| 3726 | static int fts5SnippetScore( | |||
| 3727 | const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ | |||
| 3728 | Fts5Context *pFts, /* First arg to pass to pApi functions */ | |||
| 3729 | int nDocsize, /* Size of column in tokens */ | |||
| 3730 | unsigned char *aSeen, /* Array with one element per query phrase */ | |||
| 3731 | int iCol, /* Column to score */ | |||
| 3732 | int iPos, /* Starting offset to score */ | |||
| 3733 | int nToken, /* Max tokens per snippet */ | |||
| 3734 | int *pnScore, /* OUT: Score */ | |||
| 3735 | int *piPos /* OUT: Adjusted offset */ | |||
| 3736 | ){ | |||
| 3737 | int rc; | |||
| 3738 | int i; | |||
| 3739 | int ip = 0; | |||
| 3740 | int ic = 0; | |||
| 3741 | int iOff = 0; | |||
| 3742 | int iFirst = -1; | |||
| 3743 | int nInst; | |||
| 3744 | int nScore = 0; | |||
| 3745 | int iLast = 0; | |||
| 3746 | sqlite3_int64 iEnd = (sqlite3_int64)iPos + nToken; | |||
| 3747 | ||||
| 3748 | rc = pApi->xInstCount(pFts, &nInst); | |||
| 3749 | for(i=0; i<nInst && rc==SQLITE_OK0; i++){ | |||
| 3750 | rc = pApi->xInst(pFts, i, &ip, &ic, &iOff); | |||
| 3751 | if( rc==SQLITE_OK0 && ic==iCol && iOff>=iPos && iOff<iEnd ){ | |||
| 3752 | nScore += (aSeen[ip] ? 1 : 1000); | |||
| 3753 | aSeen[ip] = 1; | |||
| 3754 | if( iFirst<0 ) iFirst = iOff; | |||
| 3755 | iLast = iOff + pApi->xPhraseSize(pFts, ip); | |||
| 3756 | } | |||
| 3757 | } | |||
| 3758 | ||||
| 3759 | *pnScore = nScore; | |||
| 3760 | if( piPos ){ | |||
| 3761 | sqlite3_int64 iAdj = iFirst - (nToken - (iLast-iFirst)) / 2; | |||
| 3762 | if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken; | |||
| 3763 | if( iAdj<0 ) iAdj = 0; | |||
| 3764 | *piPos = (int)iAdj; | |||
| 3765 | } | |||
| 3766 | ||||
| 3767 | return rc; | |||
| 3768 | } | |||
| 3769 | ||||
| 3770 | /* | |||
| 3771 | ** Return the value in pVal interpreted as utf-8 text. Except, if pVal | |||
| 3772 | ** contains a NULL value, return a pointer to a static string zero | |||
| 3773 | ** bytes in length instead of a NULL pointer. | |||
| 3774 | */ | |||
| 3775 | static const char *fts5ValueToText(sqlite3_value *pVal){ | |||
| 3776 | const char *zRet = (const char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 3777 | return zRet ? zRet : ""; | |||
| 3778 | } | |||
| 3779 | ||||
| 3780 | /* | |||
| 3781 | ** Implementation of snippet() function. | |||
| 3782 | */ | |||
| 3783 | static void fts5SnippetFunction( | |||
| 3784 | const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ | |||
| 3785 | Fts5Context *pFts, /* First arg to pass to pApi functions */ | |||
| 3786 | sqlite3_context *pCtx, /* Context for returning result/error */ | |||
| 3787 | int nVal, /* Number of values in apVal[] array */ | |||
| 3788 | sqlite3_value **apVal /* Array of trailing arguments */ | |||
| 3789 | ){ | |||
| 3790 | HighlightContext ctx; | |||
| 3791 | int rc = SQLITE_OK0; /* Return code */ | |||
| 3792 | int iCol; /* 1st argument to snippet() */ | |||
| 3793 | const char *zEllips; /* 4th argument to snippet() */ | |||
| 3794 | int nToken; /* 5th argument to snippet() */ | |||
| 3795 | int nInst = 0; /* Number of instance matches this row */ | |||
| 3796 | int i; /* Used to iterate through instances */ | |||
| 3797 | int nPhrase; /* Number of phrases in query */ | |||
| 3798 | unsigned char *aSeen; /* Array of "seen instance" flags */ | |||
| 3799 | int iBestCol; /* Column containing best snippet */ | |||
| 3800 | int iBestStart = 0; /* First token of best snippet */ | |||
| 3801 | int nBestScore = 0; /* Score of best snippet */ | |||
| 3802 | int nColSize = 0; /* Total size of iBestCol in tokens */ | |||
| 3803 | Fts5SFinder sFinder; /* Used to find the beginnings of sentences */ | |||
| 3804 | int nCol; | |||
| 3805 | ||||
| 3806 | if( nVal!=5 ){ | |||
| 3807 | const char *zErr = "wrong number of arguments to function snippet()"; | |||
| 3808 | sqlite3_result_errorsqlite3_api->result_error(pCtx, zErr, -1); | |||
| 3809 | return; | |||
| 3810 | } | |||
| 3811 | ||||
| 3812 | nCol = pApi->xColumnCount(pFts); | |||
| 3813 | memset(&ctx, 0, sizeof(HighlightContext)); | |||
| 3814 | iCol = sqlite3_value_intsqlite3_api->value_int(apVal[0]); | |||
| 3815 | ctx.zOpen = fts5ValueToText(apVal[1]); | |||
| 3816 | ctx.zClose = fts5ValueToText(apVal[2]); | |||
| 3817 | ctx.iRangeEnd = -1; | |||
| 3818 | zEllips = fts5ValueToText(apVal[3]); | |||
| 3819 | nToken = sqlite3_value_intsqlite3_api->value_int(apVal[4]); | |||
| 3820 | ||||
| 3821 | iBestCol = (iCol>=0 ? iCol : 0); | |||
| 3822 | nPhrase = pApi->xPhraseCount(pFts); | |||
| 3823 | aSeen = sqlite3_malloc64sqlite3_api->malloc64(nPhrase); | |||
| 3824 | if( aSeen==0 ){ | |||
| 3825 | rc = SQLITE_NOMEM7; | |||
| 3826 | } | |||
| 3827 | if( rc==SQLITE_OK0 ){ | |||
| 3828 | rc = pApi->xInstCount(pFts, &nInst); | |||
| 3829 | } | |||
| 3830 | ||||
| 3831 | memset(&sFinder, 0, sizeof(Fts5SFinder)); | |||
| 3832 | for(i=0; i<nCol; i++){ | |||
| 3833 | if( iCol<0 || iCol==i ){ | |||
| 3834 | const char *pLoc = 0; /* Locale of column iCol */ | |||
| 3835 | int nLoc = 0; /* Size of pLoc in bytes */ | |||
| 3836 | int nDoc; | |||
| 3837 | int nDocsize; | |||
| 3838 | int ii; | |||
| 3839 | sFinder.iPos = 0; | |||
| 3840 | sFinder.nFirst = 0; | |||
| 3841 | rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc); | |||
| 3842 | if( rc!=SQLITE_OK0 ) break; | |||
| 3843 | rc = pApi->xColumnLocale(pFts, i, &pLoc, &nLoc); | |||
| 3844 | if( rc!=SQLITE_OK0 ) break; | |||
| 3845 | rc = pApi->xTokenize_v2(pFts, | |||
| 3846 | sFinder.zDoc, nDoc, pLoc, nLoc, (void*)&sFinder, fts5SentenceFinderCb | |||
| 3847 | ); | |||
| 3848 | if( rc!=SQLITE_OK0 ) break; | |||
| 3849 | rc = pApi->xColumnSize(pFts, i, &nDocsize); | |||
| 3850 | if( rc!=SQLITE_OK0 ) break; | |||
| 3851 | ||||
| 3852 | for(ii=0; rc==SQLITE_OK0 && ii<nInst; ii++){ | |||
| 3853 | int ip, ic, io; | |||
| 3854 | int iAdj; | |||
| 3855 | int nScore; | |||
| 3856 | int jj; | |||
| 3857 | ||||
| 3858 | rc = pApi->xInst(pFts, ii, &ip, &ic, &io); | |||
| 3859 | if( ic!=i ) continue; | |||
| 3860 | if( io>nDocsize ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 3861 | if( rc!=SQLITE_OK0 ) continue; | |||
| 3862 | memset(aSeen, 0, nPhrase); | |||
| 3863 | rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i, | |||
| 3864 | io, nToken, &nScore, &iAdj | |||
| 3865 | ); | |||
| 3866 | if( rc==SQLITE_OK0 && nScore>nBestScore ){ | |||
| 3867 | nBestScore = nScore; | |||
| 3868 | iBestCol = i; | |||
| 3869 | iBestStart = iAdj; | |||
| 3870 | nColSize = nDocsize; | |||
| 3871 | } | |||
| 3872 | ||||
| 3873 | if( rc==SQLITE_OK0 && sFinder.nFirst && nDocsize>nToken ){ | |||
| 3874 | for(jj=0; jj<(sFinder.nFirst-1); jj++){ | |||
| 3875 | if( sFinder.aFirst[jj+1]>io ) break; | |||
| 3876 | } | |||
| 3877 | ||||
| 3878 | if( sFinder.aFirst[jj]<io ){ | |||
| 3879 | memset(aSeen, 0, nPhrase); | |||
| 3880 | rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i, | |||
| 3881 | sFinder.aFirst[jj], nToken, &nScore, 0 | |||
| 3882 | ); | |||
| 3883 | ||||
| 3884 | nScore += (sFinder.aFirst[jj]==0 ? 120 : 100); | |||
| 3885 | if( rc==SQLITE_OK0 && nScore>nBestScore ){ | |||
| 3886 | nBestScore = nScore; | |||
| 3887 | iBestCol = i; | |||
| 3888 | iBestStart = sFinder.aFirst[jj]; | |||
| 3889 | nColSize = nDocsize; | |||
| 3890 | } | |||
| 3891 | } | |||
| 3892 | } | |||
| 3893 | } | |||
| 3894 | } | |||
| 3895 | } | |||
| 3896 | ||||
| 3897 | if( rc==SQLITE_OK0 ){ | |||
| 3898 | rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn); | |||
| 3899 | } | |||
| 3900 | if( rc==SQLITE_OK0 && nColSize==0 ){ | |||
| 3901 | rc = pApi->xColumnSize(pFts, iBestCol, &nColSize); | |||
| 3902 | } | |||
| 3903 | if( ctx.zIn ){ | |||
| 3904 | const char *pLoc = 0; /* Locale of column iBestCol */ | |||
| 3905 | int nLoc = 0; /* Bytes in pLoc */ | |||
| 3906 | ||||
| 3907 | if( rc==SQLITE_OK0 ){ | |||
| 3908 | rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); | |||
| 3909 | } | |||
| 3910 | ||||
| 3911 | ctx.iRangeStart = iBestStart; | |||
| 3912 | ctx.iRangeEnd = iBestStart + nToken - 1; | |||
| 3913 | ||||
| 3914 | if( iBestStart>0 ){ | |||
| 3915 | fts5HighlightAppend(&rc, &ctx, zEllips, -1); | |||
| 3916 | } | |||
| 3917 | ||||
| 3918 | /* Advance iterator ctx.iter so that it points to the first coalesced | |||
| 3919 | ** phrase instance at or following position iBestStart. */ | |||
| 3920 | while( ctx.iter.iStart>=0 && ctx.iter.iStart<iBestStart && rc==SQLITE_OK0 ){ | |||
| 3921 | rc = fts5CInstIterNext(&ctx.iter); | |||
| 3922 | } | |||
| 3923 | ||||
| 3924 | if( rc==SQLITE_OK0 ){ | |||
| 3925 | rc = pApi->xColumnLocale(pFts, iBestCol, &pLoc, &nLoc); | |||
| 3926 | } | |||
| 3927 | if( rc==SQLITE_OK0 ){ | |||
| 3928 | rc = pApi->xTokenize_v2( | |||
| 3929 | pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx,fts5HighlightCb | |||
| 3930 | ); | |||
| 3931 | } | |||
| 3932 | if( ctx.bOpen ){ | |||
| 3933 | fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); | |||
| 3934 | } | |||
| 3935 | if( ctx.iRangeEnd>=(nColSize-1) ){ | |||
| 3936 | fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); | |||
| 3937 | }else{ | |||
| 3938 | fts5HighlightAppend(&rc, &ctx, zEllips, -1); | |||
| 3939 | } | |||
| 3940 | } | |||
| 3941 | if( rc==SQLITE_OK0 ){ | |||
| 3942 | sqlite3_result_textsqlite3_api->result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 3943 | }else{ | |||
| 3944 | sqlite3_result_error_codesqlite3_api->result_error_code(pCtx, rc); | |||
| 3945 | } | |||
| 3946 | sqlite3_freesqlite3_api->free(ctx.zOut); | |||
| 3947 | sqlite3_freesqlite3_api->free(aSeen); | |||
| 3948 | sqlite3_freesqlite3_api->free(sFinder.aFirst); | |||
| 3949 | } | |||
| 3950 | ||||
| 3951 | /************************************************************************/ | |||
| 3952 | ||||
| 3953 | /* | |||
| 3954 | ** The first time the bm25() function is called for a query, an instance | |||
| 3955 | ** of the following structure is allocated and populated. | |||
| 3956 | */ | |||
| 3957 | typedef struct Fts5Bm25Data Fts5Bm25Data; | |||
| 3958 | struct Fts5Bm25Data { | |||
| 3959 | int nPhrase; /* Number of phrases in query */ | |||
| 3960 | double avgdl; /* Average number of tokens in each row */ | |||
| 3961 | double *aIDF; /* IDF for each phrase */ | |||
| 3962 | double *aFreq; /* Array used to calculate phrase freq. */ | |||
| 3963 | }; | |||
| 3964 | ||||
| 3965 | /* | |||
| 3966 | ** Callback used by fts5Bm25GetData() to count the number of rows in the | |||
| 3967 | ** table matched by each individual phrase within the query. | |||
| 3968 | */ | |||
| 3969 | static int fts5CountCb( | |||
| 3970 | const Fts5ExtensionApi *pApi, | |||
| 3971 | Fts5Context *pFts, | |||
| 3972 | void *pUserData /* Pointer to sqlite3_int64 variable */ | |||
| 3973 | ){ | |||
| 3974 | sqlite3_int64 *pn = (sqlite3_int64*)pUserData; | |||
| 3975 | UNUSED_PARAM2(pApi, pFts)(void)(pApi), (void)(pFts); | |||
| 3976 | (*pn)++; | |||
| 3977 | return SQLITE_OK0; | |||
| 3978 | } | |||
| 3979 | ||||
| 3980 | /* | |||
| 3981 | ** Set *ppData to point to the Fts5Bm25Data object for the current query. | |||
| 3982 | ** If the object has not already been allocated, allocate and populate it | |||
| 3983 | ** now. | |||
| 3984 | */ | |||
| 3985 | static int fts5Bm25GetData( | |||
| 3986 | const Fts5ExtensionApi *pApi, | |||
| 3987 | Fts5Context *pFts, | |||
| 3988 | Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ | |||
| 3989 | ){ | |||
| 3990 | int rc = SQLITE_OK0; /* Return code */ | |||
| 3991 | Fts5Bm25Data *p; /* Object to return */ | |||
| 3992 | ||||
| 3993 | p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0); | |||
| 3994 | if( p==0 ){ | |||
| 3995 | int nPhrase; /* Number of phrases in query */ | |||
| 3996 | sqlite3_int64 nRow = 0; /* Number of rows in table */ | |||
| 3997 | sqlite3_int64 nToken = 0; /* Number of tokens in table */ | |||
| 3998 | sqlite3_int64 nByte; /* Bytes of space to allocate */ | |||
| 3999 | int i; | |||
| 4000 | ||||
| 4001 | /* Allocate the Fts5Bm25Data object */ | |||
| 4002 | nPhrase = pApi->xPhraseCount(pFts); | |||
| 4003 | nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double); | |||
| 4004 | p = (Fts5Bm25Data*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 4005 | if( p==0 ){ | |||
| 4006 | rc = SQLITE_NOMEM7; | |||
| 4007 | }else{ | |||
| 4008 | memset(p, 0, (size_t)nByte); | |||
| 4009 | p->nPhrase = nPhrase; | |||
| 4010 | p->aIDF = (double*)&p[1]; | |||
| 4011 | p->aFreq = &p->aIDF[nPhrase]; | |||
| 4012 | } | |||
| 4013 | ||||
| 4014 | /* Calculate the average document length for this FTS5 table */ | |||
| 4015 | if( rc==SQLITE_OK0 ) rc = pApi->xRowCount(pFts, &nRow); | |||
| 4016 | assert( rc!=SQLITE_OK || nRow>0 )((void) (0)); | |||
| 4017 | if( rc==SQLITE_OK0 ) rc = pApi->xColumnTotalSize(pFts, -1, &nToken); | |||
| 4018 | if( rc==SQLITE_OK0 ) p->avgdl = (double)nToken / (double)nRow; | |||
| 4019 | ||||
| 4020 | /* Calculate an IDF for each phrase in the query */ | |||
| 4021 | for(i=0; rc==SQLITE_OK0 && i<nPhrase; i++){ | |||
| 4022 | sqlite3_int64 nHit = 0; | |||
| 4023 | rc = pApi->xQueryPhrase(pFts, i, (void*)&nHit, fts5CountCb); | |||
| 4024 | if( rc==SQLITE_OK0 ){ | |||
| 4025 | /* Calculate the IDF (Inverse Document Frequency) for phrase i. | |||
| 4026 | ** This is done using the standard BM25 formula as found on wikipedia: | |||
| 4027 | ** | |||
| 4028 | ** IDF = log( (N - nHit + 0.5) / (nHit + 0.5) ) | |||
| 4029 | ** | |||
| 4030 | ** where "N" is the total number of documents in the set and nHit | |||
| 4031 | ** is the number that contain at least one instance of the phrase | |||
| 4032 | ** under consideration. | |||
| 4033 | ** | |||
| 4034 | ** The problem with this is that if (N < 2*nHit), the IDF is | |||
| 4035 | ** negative. Which is undesirable. So the minimum allowable IDF is | |||
| 4036 | ** (1e-6) - roughly the same as a term that appears in just over | |||
| 4037 | ** half of set of 5,000,000 documents. */ | |||
| 4038 | double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); | |||
| 4039 | if( idf<=0.0 ) idf = 1e-6; | |||
| 4040 | p->aIDF[i] = idf; | |||
| 4041 | } | |||
| 4042 | } | |||
| 4043 | ||||
| 4044 | if( rc!=SQLITE_OK0 ){ | |||
| 4045 | sqlite3_freesqlite3_api->free(p); | |||
| 4046 | }else{ | |||
| 4047 | rc = pApi->xSetAuxdata(pFts, p, sqlite3_freesqlite3_api->free); | |||
| 4048 | } | |||
| 4049 | if( rc!=SQLITE_OK0 ) p = 0; | |||
| 4050 | } | |||
| 4051 | *ppData = p; | |||
| 4052 | return rc; | |||
| 4053 | } | |||
| 4054 | ||||
| 4055 | /* | |||
| 4056 | ** Implementation of bm25() function. | |||
| 4057 | */ | |||
| 4058 | static void fts5Bm25Function( | |||
| 4059 | const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ | |||
| 4060 | Fts5Context *pFts, /* First arg to pass to pApi functions */ | |||
| 4061 | sqlite3_context *pCtx, /* Context for returning result/error */ | |||
| 4062 | int nVal, /* Number of values in apVal[] array */ | |||
| 4063 | sqlite3_value **apVal /* Array of trailing arguments */ | |||
| 4064 | ){ | |||
| 4065 | const double k1 = 1.2; /* Constant "k1" from BM25 formula */ | |||
| 4066 | const double b = 0.75; /* Constant "b" from BM25 formula */ | |||
| 4067 | int rc; /* Error code */ | |||
| 4068 | double score = 0.0; /* SQL function return value */ | |||
| 4069 | Fts5Bm25Data *pData; /* Values allocated/calculated once only */ | |||
| 4070 | int i; /* Iterator variable */ | |||
| 4071 | int nInst = 0; /* Value returned by xInstCount() */ | |||
| 4072 | double D = 0.0; /* Total number of tokens in row */ | |||
| 4073 | double *aFreq = 0; /* Array of phrase freq. for current row */ | |||
| 4074 | ||||
| 4075 | /* Calculate the phrase frequency (symbol "f(qi,D)" in the documentation) | |||
| 4076 | ** for each phrase in the query for the current row. */ | |||
| 4077 | rc = fts5Bm25GetData(pApi, pFts, &pData); | |||
| 4078 | if( rc==SQLITE_OK0 ){ | |||
| 4079 | aFreq = pData->aFreq; | |||
| 4080 | memset(aFreq, 0, sizeof(double) * pData->nPhrase); | |||
| 4081 | rc = pApi->xInstCount(pFts, &nInst); | |||
| 4082 | } | |||
| 4083 | for(i=0; rc==SQLITE_OK0 && i<nInst; i++){ | |||
| 4084 | int ip; int ic; int io; | |||
| 4085 | rc = pApi->xInst(pFts, i, &ip, &ic, &io); | |||
| 4086 | if( rc==SQLITE_OK0 ){ | |||
| 4087 | double w = (nVal > ic) ? sqlite3_value_doublesqlite3_api->value_double(apVal[ic]) : 1.0; | |||
| 4088 | aFreq[ip] += w; | |||
| 4089 | } | |||
| 4090 | } | |||
| 4091 | ||||
| 4092 | /* Figure out the total size of the current row in tokens. */ | |||
| 4093 | if( rc==SQLITE_OK0 ){ | |||
| 4094 | int nTok; | |||
| 4095 | rc = pApi->xColumnSize(pFts, -1, &nTok); | |||
| 4096 | D = (double)nTok; | |||
| 4097 | } | |||
| 4098 | ||||
| 4099 | /* Determine and return the BM25 score for the current row. Or, if an | |||
| 4100 | ** error has occurred, throw an exception. */ | |||
| 4101 | if( rc==SQLITE_OK0 ){ | |||
| 4102 | for(i=0; i<pData->nPhrase; i++){ | |||
| 4103 | score += pData->aIDF[i] * ( | |||
| 4104 | ( aFreq[i] * (k1 + 1.0) ) / | |||
| 4105 | ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) | |||
| 4106 | ); | |||
| 4107 | } | |||
| 4108 | sqlite3_result_doublesqlite3_api->result_double(pCtx, -1.0 * score); | |||
| 4109 | }else{ | |||
| 4110 | sqlite3_result_error_codesqlite3_api->result_error_code(pCtx, rc); | |||
| 4111 | } | |||
| 4112 | } | |||
| 4113 | ||||
| 4114 | /* | |||
| 4115 | ** Implementation of fts5_get_locale() function. | |||
| 4116 | */ | |||
| 4117 | static void fts5GetLocaleFunction( | |||
| 4118 | const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ | |||
| 4119 | Fts5Context *pFts, /* First arg to pass to pApi functions */ | |||
| 4120 | sqlite3_context *pCtx, /* Context for returning result/error */ | |||
| 4121 | int nVal, /* Number of values in apVal[] array */ | |||
| 4122 | sqlite3_value **apVal /* Array of trailing arguments */ | |||
| 4123 | ){ | |||
| 4124 | int iCol = 0; | |||
| 4125 | int eType = 0; | |||
| 4126 | int rc = SQLITE_OK0; | |||
| 4127 | const char *zLocale = 0; | |||
| 4128 | int nLocale = 0; | |||
| 4129 | ||||
| 4130 | /* xColumnLocale() must be available */ | |||
| 4131 | assert( pApi->iVersion>=4 )((void) (0)); | |||
| 4132 | ||||
| 4133 | if( nVal!=1 ){ | |||
| 4134 | const char *z = "wrong number of arguments to function fts5_get_locale()"; | |||
| 4135 | sqlite3_result_errorsqlite3_api->result_error(pCtx, z, -1); | |||
| 4136 | return; | |||
| 4137 | } | |||
| 4138 | ||||
| 4139 | eType = sqlite3_value_numeric_typesqlite3_api->value_numeric_type(apVal[0]); | |||
| 4140 | if( eType!=SQLITE_INTEGER1 ){ | |||
| 4141 | const char *z = "non-integer argument passed to function fts5_get_locale()"; | |||
| 4142 | sqlite3_result_errorsqlite3_api->result_error(pCtx, z, -1); | |||
| 4143 | return; | |||
| 4144 | } | |||
| 4145 | ||||
| 4146 | iCol = sqlite3_value_intsqlite3_api->value_int(apVal[0]); | |||
| 4147 | if( iCol<0 || iCol>=pApi->xColumnCount(pFts) ){ | |||
| 4148 | sqlite3_result_error_codesqlite3_api->result_error_code(pCtx, SQLITE_RANGE25); | |||
| 4149 | return; | |||
| 4150 | } | |||
| 4151 | ||||
| 4152 | rc = pApi->xColumnLocale(pFts, iCol, &zLocale, &nLocale); | |||
| 4153 | if( rc!=SQLITE_OK0 ){ | |||
| 4154 | sqlite3_result_error_codesqlite3_api->result_error_code(pCtx, rc); | |||
| 4155 | return; | |||
| 4156 | } | |||
| 4157 | ||||
| 4158 | sqlite3_result_textsqlite3_api->result_text(pCtx, zLocale, nLocale, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 4159 | } | |||
| 4160 | ||||
| 4161 | static int sqlite3Fts5AuxInit(fts5_api *pApi){ | |||
| 4162 | struct Builtin { | |||
| 4163 | const char *zFunc; /* Function name (nul-terminated) */ | |||
| 4164 | void *pUserData; /* User-data pointer */ | |||
| 4165 | fts5_extension_function xFunc;/* Callback function */ | |||
| 4166 | void (*xDestroy)(void*); /* Destructor function */ | |||
| 4167 | } aBuiltin [] = { | |||
| 4168 | { "snippet", 0, fts5SnippetFunction, 0 }, | |||
| 4169 | { "highlight", 0, fts5HighlightFunction, 0 }, | |||
| 4170 | { "bm25", 0, fts5Bm25Function, 0 }, | |||
| 4171 | { "fts5_get_locale", 0, fts5GetLocaleFunction, 0 }, | |||
| 4172 | }; | |||
| 4173 | int rc = SQLITE_OK0; /* Return code */ | |||
| 4174 | int i; /* To iterate through builtin functions */ | |||
| 4175 | ||||
| 4176 | for(i=0; rc==SQLITE_OK0 && i<ArraySize(aBuiltin)((int)(sizeof(aBuiltin) / sizeof(aBuiltin[0]))); i++){ | |||
| 4177 | rc = pApi->xCreateFunction(pApi, | |||
| 4178 | aBuiltin[i].zFunc, | |||
| 4179 | aBuiltin[i].pUserData, | |||
| 4180 | aBuiltin[i].xFunc, | |||
| 4181 | aBuiltin[i].xDestroy | |||
| 4182 | ); | |||
| 4183 | } | |||
| 4184 | ||||
| 4185 | return rc; | |||
| 4186 | } | |||
| 4187 | ||||
| 4188 | #line 1 "fts5_buffer.c" | |||
| 4189 | /* | |||
| 4190 | ** 2014 May 31 | |||
| 4191 | ** | |||
| 4192 | ** The author disclaims copyright to this source code. In place of | |||
| 4193 | ** a legal notice, here is a blessing: | |||
| 4194 | ** | |||
| 4195 | ** May you do good and not evil. | |||
| 4196 | ** May you find forgiveness for yourself and forgive others. | |||
| 4197 | ** May you share freely, never taking more than you give. | |||
| 4198 | ** | |||
| 4199 | ****************************************************************************** | |||
| 4200 | */ | |||
| 4201 | ||||
| 4202 | ||||
| 4203 | ||||
| 4204 | /* #include "fts5Int.h" */ | |||
| 4205 | ||||
| 4206 | static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){ | |||
| 4207 | if( (u32)pBuf->nSpace<nByte ){ | |||
| 4208 | u64 nNew = pBuf->nSpace ? pBuf->nSpace : 64; | |||
| 4209 | u8 *pNew; | |||
| 4210 | while( nNew<nByte ){ | |||
| 4211 | nNew = nNew * 2; | |||
| 4212 | } | |||
| 4213 | pNew = sqlite3_realloc64sqlite3_api->realloc64(pBuf->p, nNew); | |||
| 4214 | if( pNew==0 ){ | |||
| 4215 | *pRc = SQLITE_NOMEM7; | |||
| 4216 | return 1; | |||
| 4217 | }else{ | |||
| 4218 | pBuf->nSpace = (int)nNew; | |||
| 4219 | pBuf->p = pNew; | |||
| 4220 | } | |||
| 4221 | } | |||
| 4222 | return 0; | |||
| 4223 | } | |||
| 4224 | ||||
| 4225 | ||||
| 4226 | /* | |||
| 4227 | ** Encode value iVal as an SQLite varint and append it to the buffer object | |||
| 4228 | ** pBuf. If an OOM error occurs, set the error code in p. | |||
| 4229 | */ | |||
| 4230 | static void sqlite3Fts5BufferAppendVarint(int *pRc, Fts5Buffer *pBuf, i64 iVal){ | |||
| 4231 | if( fts5BufferGrow(pRc, pBuf, 9)( (u32)((pBuf)->n) + (u32)(9) <= (u32)((pBuf)->nSpace ) ? 0 : sqlite3Fts5BufferSize((pRc),(pBuf),(9)+(pBuf)->n) ) ) return; | |||
| 4232 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iVal); | |||
| 4233 | } | |||
| 4234 | ||||
| 4235 | static void sqlite3Fts5Put32(u8 *aBuf, int iVal){ | |||
| 4236 | aBuf[0] = (iVal>>24) & 0x00FF; | |||
| 4237 | aBuf[1] = (iVal>>16) & 0x00FF; | |||
| 4238 | aBuf[2] = (iVal>> 8) & 0x00FF; | |||
| 4239 | aBuf[3] = (iVal>> 0) & 0x00FF; | |||
| 4240 | } | |||
| 4241 | ||||
| 4242 | static int sqlite3Fts5Get32(const u8 *aBuf){ | |||
| 4243 | return (int)((((u32)aBuf[0])<<24) + (aBuf[1]<<16) + (aBuf[2]<<8) + aBuf[3]); | |||
| 4244 | } | |||
| 4245 | ||||
| 4246 | /* | |||
| 4247 | ** Append buffer nData/pData to buffer pBuf. If an OOM error occurs, set | |||
| 4248 | ** the error code in p. If an error has already occurred when this function | |||
| 4249 | ** is called, it is a no-op. | |||
| 4250 | */ | |||
| 4251 | static void sqlite3Fts5BufferAppendBlob( | |||
| 4252 | int *pRc, | |||
| 4253 | Fts5Buffer *pBuf, | |||
| 4254 | u32 nData, | |||
| 4255 | const u8 *pData | |||
| 4256 | ){ | |||
| 4257 | if( nData ){ | |||
| 4258 | if( fts5BufferGrow(pRc, pBuf, nData)( (u32)((pBuf)->n) + (u32)(nData) <= (u32)((pBuf)->nSpace ) ? 0 : sqlite3Fts5BufferSize((pRc),(pBuf),(nData)+(pBuf)-> n) ) ) return; | |||
| 4259 | assert( pBuf->p!=0 )((void) (0)); | |||
| 4260 | memcpy(&pBuf->p[pBuf->n], pData, nData); | |||
| 4261 | pBuf->n += nData; | |||
| 4262 | } | |||
| 4263 | } | |||
| 4264 | ||||
| 4265 | /* | |||
| 4266 | ** Append the nul-terminated string zStr to the buffer pBuf. This function | |||
| 4267 | ** ensures that the byte following the buffer data is set to 0x00, even | |||
| 4268 | ** though this byte is not included in the pBuf->n count. | |||
| 4269 | */ | |||
| 4270 | static void sqlite3Fts5BufferAppendString( | |||
| 4271 | int *pRc, | |||
| 4272 | Fts5Buffer *pBuf, | |||
| 4273 | const char *zStr | |||
| 4274 | ){ | |||
| 4275 | int nStr = (int)strlen(zStr); | |||
| 4276 | sqlite3Fts5BufferAppendBlob(pRc, pBuf, nStr+1, (const u8*)zStr); | |||
| 4277 | pBuf->n--; | |||
| 4278 | } | |||
| 4279 | ||||
| 4280 | /* | |||
| 4281 | ** Argument zFmt is a printf() style format string. This function performs | |||
| 4282 | ** the printf() style processing, then appends the results to buffer pBuf. | |||
| 4283 | ** | |||
| 4284 | ** Like sqlite3Fts5BufferAppendString(), this function ensures that the byte | |||
| 4285 | ** following the buffer data is set to 0x00, even though this byte is not | |||
| 4286 | ** included in the pBuf->n count. | |||
| 4287 | */ | |||
| 4288 | static void sqlite3Fts5BufferAppendPrintf( | |||
| 4289 | int *pRc, | |||
| 4290 | Fts5Buffer *pBuf, | |||
| 4291 | char *zFmt, ... | |||
| 4292 | ){ | |||
| 4293 | if( *pRc==SQLITE_OK0 ){ | |||
| 4294 | char *zTmp; | |||
| 4295 | va_list ap; | |||
| 4296 | va_start(ap, zFmt)__builtin_va_start(ap, zFmt); | |||
| 4297 | zTmp = sqlite3_vmprintfsqlite3_api->vmprintf(zFmt, ap); | |||
| 4298 | va_end(ap)__builtin_va_end(ap); | |||
| 4299 | ||||
| 4300 | if( zTmp==0 ){ | |||
| 4301 | *pRc = SQLITE_NOMEM7; | |||
| 4302 | }else{ | |||
| 4303 | sqlite3Fts5BufferAppendString(pRc, pBuf, zTmp); | |||
| 4304 | sqlite3_freesqlite3_api->free(zTmp); | |||
| 4305 | } | |||
| 4306 | } | |||
| 4307 | } | |||
| 4308 | ||||
| 4309 | static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...){ | |||
| 4310 | char *zRet = 0; | |||
| 4311 | if( *pRc==SQLITE_OK0 ){ | |||
| 4312 | va_list ap; | |||
| 4313 | va_start(ap, zFmt)__builtin_va_start(ap, zFmt); | |||
| 4314 | zRet = sqlite3_vmprintfsqlite3_api->vmprintf(zFmt, ap); | |||
| 4315 | va_end(ap)__builtin_va_end(ap); | |||
| 4316 | if( zRet==0 ){ | |||
| 4317 | *pRc = SQLITE_NOMEM7; | |||
| 4318 | } | |||
| 4319 | } | |||
| 4320 | return zRet; | |||
| 4321 | } | |||
| 4322 | ||||
| 4323 | ||||
| 4324 | /* | |||
| 4325 | ** Free any buffer allocated by pBuf. Zero the structure before returning. | |||
| 4326 | */ | |||
| 4327 | static void sqlite3Fts5BufferFree(Fts5Buffer *pBuf){ | |||
| 4328 | sqlite3_freesqlite3_api->free(pBuf->p); | |||
| 4329 | memset(pBuf, 0, sizeof(Fts5Buffer)); | |||
| 4330 | } | |||
| 4331 | ||||
| 4332 | /* | |||
| 4333 | ** Zero the contents of the buffer object. But do not free the associated | |||
| 4334 | ** memory allocation. | |||
| 4335 | */ | |||
| 4336 | static void sqlite3Fts5BufferZero(Fts5Buffer *pBuf){ | |||
| 4337 | pBuf->n = 0; | |||
| 4338 | } | |||
| 4339 | ||||
| 4340 | /* | |||
| 4341 | ** Set the buffer to contain nData/pData. If an OOM error occurs, leave an | |||
| 4342 | ** the error code in p. If an error has already occurred when this function | |||
| 4343 | ** is called, it is a no-op. | |||
| 4344 | */ | |||
| 4345 | static void sqlite3Fts5BufferSet( | |||
| 4346 | int *pRc, | |||
| 4347 | Fts5Buffer *pBuf, | |||
| 4348 | int nData, | |||
| 4349 | const u8 *pData | |||
| 4350 | ){ | |||
| 4351 | pBuf->n = 0; | |||
| 4352 | sqlite3Fts5BufferAppendBlob(pRc, pBuf, nData, pData); | |||
| 4353 | } | |||
| 4354 | ||||
| 4355 | static int sqlite3Fts5PoslistNext64( | |||
| 4356 | const u8 *a, int n, /* Buffer containing poslist */ | |||
| 4357 | int *pi, /* IN/OUT: Offset within a[] */ | |||
| 4358 | i64 *piOff /* IN/OUT: Current offset */ | |||
| 4359 | ){ | |||
| 4360 | int i = *pi; | |||
| 4361 | assert( a!=0 || i==0 )((void) (0)); | |||
| 4362 | if( i>=n ){ | |||
| 4363 | /* EOF */ | |||
| 4364 | *piOff = -1; | |||
| 4365 | return 1; | |||
| 4366 | }else{ | |||
| 4367 | i64 iOff = *piOff; | |||
| 4368 | u32 iVal; | |||
| 4369 | assert( a!=0 )((void) (0)); | |||
| 4370 | fts5FastGetVarint32(a, i, iVal){ iVal = (a)[i++]; if( iVal & 0x80 ){ i--; i += sqlite3Fts5GetVarint32 (&(a)[i],(u32*)&(iVal)); } }; | |||
| 4371 | if( iVal<=1 ){ | |||
| 4372 | if( iVal==0 ){ | |||
| 4373 | *pi = i; | |||
| 4374 | return 0; | |||
| 4375 | } | |||
| 4376 | fts5FastGetVarint32(a, i, iVal){ iVal = (a)[i++]; if( iVal & 0x80 ){ i--; i += sqlite3Fts5GetVarint32 (&(a)[i],(u32*)&(iVal)); } }; | |||
| 4377 | iOff = ((i64)iVal) << 32; | |||
| 4378 | assert( iOff>=0 )((void) (0)); | |||
| 4379 | fts5FastGetVarint32(a, i, iVal){ iVal = (a)[i++]; if( iVal & 0x80 ){ i--; i += sqlite3Fts5GetVarint32 (&(a)[i],(u32*)&(iVal)); } }; | |||
| 4380 | if( iVal<2 ){ | |||
| 4381 | /* This is a corrupt record. So stop parsing it here. */ | |||
| 4382 | *piOff = -1; | |||
| 4383 | return 1; | |||
| 4384 | } | |||
| 4385 | *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); | |||
| 4386 | }else{ | |||
| 4387 | *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF); | |||
| 4388 | } | |||
| 4389 | *pi = i; | |||
| 4390 | assert_nc( *piOff>=iOff )((void) (0)); | |||
| 4391 | return 0; | |||
| 4392 | } | |||
| 4393 | } | |||
| 4394 | ||||
| 4395 | ||||
| 4396 | /* | |||
| 4397 | ** Advance the iterator object passed as the only argument. Return true | |||
| 4398 | ** if the iterator reaches EOF, or false otherwise. | |||
| 4399 | */ | |||
| 4400 | static int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){ | |||
| 4401 | if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos) ){ | |||
| 4402 | pIter->bEof = 1; | |||
| 4403 | } | |||
| 4404 | return pIter->bEof; | |||
| 4405 | } | |||
| 4406 | ||||
| 4407 | static int sqlite3Fts5PoslistReaderInit( | |||
| 4408 | const u8 *a, int n, /* Poslist buffer to iterate through */ | |||
| 4409 | Fts5PoslistReader *pIter /* Iterator object to initialize */ | |||
| 4410 | ){ | |||
| 4411 | memset(pIter, 0, sizeof(*pIter)); | |||
| 4412 | pIter->a = a; | |||
| 4413 | pIter->n = n; | |||
| 4414 | sqlite3Fts5PoslistReaderNext(pIter); | |||
| 4415 | return pIter->bEof; | |||
| 4416 | } | |||
| 4417 | ||||
| 4418 | /* | |||
| 4419 | ** Append position iPos to the position list being accumulated in buffer | |||
| 4420 | ** pBuf, which must be already be large enough to hold the new data. | |||
| 4421 | ** The previous position written to this list is *piPrev. *piPrev is set | |||
| 4422 | ** to iPos before returning. | |||
| 4423 | */ | |||
| 4424 | static void sqlite3Fts5PoslistSafeAppend( | |||
| 4425 | Fts5Buffer *pBuf, | |||
| 4426 | i64 *piPrev, | |||
| 4427 | i64 iPos | |||
| 4428 | ){ | |||
| 4429 | if( iPos>=*piPrev ){ | |||
| 4430 | static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; | |||
| 4431 | if( (iPos & colmask) != (*piPrev & colmask) ){ | |||
| 4432 | pBuf->p[pBuf->n++] = 1; | |||
| 4433 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); | |||
| 4434 | *piPrev = (iPos & colmask); | |||
| 4435 | } | |||
| 4436 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); | |||
| 4437 | *piPrev = iPos; | |||
| 4438 | } | |||
| 4439 | } | |||
| 4440 | ||||
| 4441 | static int sqlite3Fts5PoslistWriterAppend( | |||
| 4442 | Fts5Buffer *pBuf, | |||
| 4443 | Fts5PoslistWriter *pWriter, | |||
| 4444 | i64 iPos | |||
| 4445 | ){ | |||
| 4446 | int rc = 0; /* Initialized only to suppress erroneous warning from Clang */ | |||
| 4447 | if( fts5BufferGrow(&rc, pBuf, 5+5+5)( (u32)((pBuf)->n) + (u32)(5+5+5) <= (u32)((pBuf)->nSpace ) ? 0 : sqlite3Fts5BufferSize((&rc),(pBuf),(5+5+5)+(pBuf) ->n) ) ) return rc; | |||
| 4448 | sqlite3Fts5PoslistSafeAppend(pBuf, &pWriter->iPrev, iPos); | |||
| 4449 | return SQLITE_OK0; | |||
| 4450 | } | |||
| 4451 | ||||
| 4452 | static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte){ | |||
| 4453 | void *pRet = 0; | |||
| 4454 | if( *pRc==SQLITE_OK0 ){ | |||
| 4455 | pRet = sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 4456 | if( pRet==0 ){ | |||
| 4457 | if( nByte>0 ) *pRc = SQLITE_NOMEM7; | |||
| 4458 | }else{ | |||
| 4459 | memset(pRet, 0, (size_t)nByte); | |||
| 4460 | } | |||
| 4461 | } | |||
| 4462 | return pRet; | |||
| 4463 | } | |||
| 4464 | ||||
| 4465 | /* | |||
| 4466 | ** Return a nul-terminated copy of the string indicated by pIn. If nIn | |||
| 4467 | ** is non-negative, then it is the length of the string in bytes. Otherwise, | |||
| 4468 | ** the length of the string is determined using strlen(). | |||
| 4469 | ** | |||
| 4470 | ** It is the responsibility of the caller to eventually free the returned | |||
| 4471 | ** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned. | |||
| 4472 | */ | |||
| 4473 | static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){ | |||
| 4474 | char *zRet = 0; | |||
| 4475 | if( *pRc==SQLITE_OK0 ){ | |||
| 4476 | if( nIn<0 ){ | |||
| 4477 | nIn = (int)strlen(pIn); | |||
| 4478 | } | |||
| 4479 | zRet = (char*)sqlite3_malloc64sqlite3_api->malloc64((i64)nIn+1); | |||
| 4480 | if( zRet ){ | |||
| 4481 | memcpy(zRet, pIn, nIn); | |||
| 4482 | zRet[nIn] = '\0'; | |||
| 4483 | }else{ | |||
| 4484 | *pRc = SQLITE_NOMEM7; | |||
| 4485 | } | |||
| 4486 | } | |||
| 4487 | return zRet; | |||
| 4488 | } | |||
| 4489 | ||||
| 4490 | ||||
| 4491 | /* | |||
| 4492 | ** Return true if character 't' may be part of an FTS5 bareword, or false | |||
| 4493 | ** otherwise. Characters that may be part of barewords: | |||
| 4494 | ** | |||
| 4495 | ** * All non-ASCII characters, | |||
| 4496 | ** * The 52 upper and lower case ASCII characters, and | |||
| 4497 | ** * The 10 integer ASCII characters. | |||
| 4498 | ** * The underscore character "_" (0x5F). | |||
| 4499 | ** * The unicode "substitute" character (0x1A). | |||
| 4500 | */ | |||
| 4501 | static int sqlite3Fts5IsBareword(char t){ | |||
| 4502 | u8 aBareword[128] = { | |||
| 4503 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ | |||
| 4504 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ | |||
| 4505 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 .. 0x2F */ | |||
| 4506 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30 .. 0x3F */ | |||
| 4507 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 .. 0x4F */ | |||
| 4508 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x50 .. 0x5F */ | |||
| 4509 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 .. 0x6F */ | |||
| 4510 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 /* 0x70 .. 0x7F */ | |||
| 4511 | }; | |||
| 4512 | ||||
| 4513 | return (t & 0x80) || aBareword[(int)t]; | |||
| 4514 | } | |||
| 4515 | ||||
| 4516 | ||||
| 4517 | /************************************************************************* | |||
| 4518 | */ | |||
| 4519 | typedef struct Fts5TermsetEntry Fts5TermsetEntry; | |||
| 4520 | struct Fts5TermsetEntry { | |||
| 4521 | char *pTerm; | |||
| 4522 | int nTerm; | |||
| 4523 | int iIdx; /* Index (main or aPrefix[] entry) */ | |||
| 4524 | Fts5TermsetEntry *pNext; | |||
| 4525 | }; | |||
| 4526 | ||||
| 4527 | struct Fts5Termset { | |||
| 4528 | Fts5TermsetEntry *apHash[512]; | |||
| 4529 | }; | |||
| 4530 | ||||
| 4531 | static int sqlite3Fts5TermsetNew(Fts5Termset **pp){ | |||
| 4532 | int rc = SQLITE_OK0; | |||
| 4533 | *pp = sqlite3Fts5MallocZero(&rc, sizeof(Fts5Termset)); | |||
| 4534 | return rc; | |||
| 4535 | } | |||
| 4536 | ||||
| 4537 | static int sqlite3Fts5TermsetAdd( | |||
| 4538 | Fts5Termset *p, | |||
| 4539 | int iIdx, | |||
| 4540 | const char *pTerm, int nTerm, | |||
| 4541 | int *pbPresent | |||
| 4542 | ){ | |||
| 4543 | int rc = SQLITE_OK0; | |||
| 4544 | *pbPresent = 0; | |||
| 4545 | if( p ){ | |||
| 4546 | int i; | |||
| 4547 | u32 hash = 13; | |||
| 4548 | Fts5TermsetEntry *pEntry; | |||
| 4549 | ||||
| 4550 | /* Calculate a hash value for this term. This is the same hash checksum | |||
| 4551 | ** used by the fts5_hash.c module. This is not important for correct | |||
| 4552 | ** operation of the module, but is necessary to ensure that some tests | |||
| 4553 | ** designed to produce hash table collisions really do work. */ | |||
| 4554 | for(i=nTerm-1; i>=0; i--){ | |||
| 4555 | hash = (hash << 3) ^ hash ^ pTerm[i]; | |||
| 4556 | } | |||
| 4557 | hash = (hash << 3) ^ hash ^ iIdx; | |||
| 4558 | hash = hash % ArraySize(p->apHash)((int)(sizeof(p->apHash) / sizeof(p->apHash[0]))); | |||
| 4559 | ||||
| 4560 | for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){ | |||
| 4561 | if( pEntry->iIdx==iIdx | |||
| 4562 | && pEntry->nTerm==nTerm | |||
| 4563 | && memcmp(pEntry->pTerm, pTerm, nTerm)==0 | |||
| 4564 | ){ | |||
| 4565 | *pbPresent = 1; | |||
| 4566 | break; | |||
| 4567 | } | |||
| 4568 | } | |||
| 4569 | ||||
| 4570 | if( pEntry==0 ){ | |||
| 4571 | pEntry = sqlite3Fts5MallocZero(&rc, sizeof(Fts5TermsetEntry) + nTerm); | |||
| 4572 | if( pEntry ){ | |||
| 4573 | pEntry->pTerm = (char*)&pEntry[1]; | |||
| 4574 | pEntry->nTerm = nTerm; | |||
| 4575 | pEntry->iIdx = iIdx; | |||
| 4576 | memcpy(pEntry->pTerm, pTerm, nTerm); | |||
| 4577 | pEntry->pNext = p->apHash[hash]; | |||
| 4578 | p->apHash[hash] = pEntry; | |||
| 4579 | } | |||
| 4580 | } | |||
| 4581 | } | |||
| 4582 | ||||
| 4583 | return rc; | |||
| 4584 | } | |||
| 4585 | ||||
| 4586 | static void sqlite3Fts5TermsetFree(Fts5Termset *p){ | |||
| 4587 | if( p ){ | |||
| 4588 | u32 i; | |||
| 4589 | for(i=0; i<ArraySize(p->apHash)((int)(sizeof(p->apHash) / sizeof(p->apHash[0]))); i++){ | |||
| 4590 | Fts5TermsetEntry *pEntry = p->apHash[i]; | |||
| 4591 | while( pEntry ){ | |||
| 4592 | Fts5TermsetEntry *pDel = pEntry; | |||
| 4593 | pEntry = pEntry->pNext; | |||
| 4594 | sqlite3_freesqlite3_api->free(pDel); | |||
| 4595 | } | |||
| 4596 | } | |||
| 4597 | sqlite3_freesqlite3_api->free(p); | |||
| 4598 | } | |||
| 4599 | } | |||
| 4600 | ||||
| 4601 | #line 1 "fts5_config.c" | |||
| 4602 | /* | |||
| 4603 | ** 2014 Jun 09 | |||
| 4604 | ** | |||
| 4605 | ** The author disclaims copyright to this source code. In place of | |||
| 4606 | ** a legal notice, here is a blessing: | |||
| 4607 | ** | |||
| 4608 | ** May you do good and not evil. | |||
| 4609 | ** May you find forgiveness for yourself and forgive others. | |||
| 4610 | ** May you share freely, never taking more than you give. | |||
| 4611 | ** | |||
| 4612 | ****************************************************************************** | |||
| 4613 | ** | |||
| 4614 | ** This is an SQLite module implementing full-text search. | |||
| 4615 | */ | |||
| 4616 | ||||
| 4617 | ||||
| 4618 | /* #include "fts5Int.h" */ | |||
| 4619 | ||||
| 4620 | #define FTS5_DEFAULT_PAGE_SIZE4050 4050 | |||
| 4621 | #define FTS5_DEFAULT_AUTOMERGE4 4 | |||
| 4622 | #define FTS5_DEFAULT_USERMERGE4 4 | |||
| 4623 | #define FTS5_DEFAULT_CRISISMERGE16 16 | |||
| 4624 | #define FTS5_DEFAULT_HASHSIZE(1024*1024) (1024*1024) | |||
| 4625 | ||||
| 4626 | #define FTS5_DEFAULT_DELETE_AUTOMERGE10 10 /* default 10% */ | |||
| 4627 | ||||
| 4628 | /* Maximum allowed page size */ | |||
| 4629 | #define FTS5_MAX_PAGE_SIZE(64*1024) (64*1024) | |||
| 4630 | ||||
| 4631 | static int fts5_iswhitespace(char x){ | |||
| 4632 | return (x==' '); | |||
| 4633 | } | |||
| 4634 | ||||
| 4635 | static int fts5_isopenquote(char x){ | |||
| 4636 | return (x=='"' || x=='\'' || x=='[' || x=='`'); | |||
| 4637 | } | |||
| 4638 | ||||
| 4639 | /* | |||
| 4640 | ** Argument pIn points to a character that is part of a nul-terminated | |||
| 4641 | ** string. Return a pointer to the first character following *pIn in | |||
| 4642 | ** the string that is not a white-space character. | |||
| 4643 | */ | |||
| 4644 | static const char *fts5ConfigSkipWhitespace(const char *pIn){ | |||
| 4645 | const char *p = pIn; | |||
| 4646 | if( p ){ | |||
| 4647 | while( fts5_iswhitespace(*p) ){ p++; } | |||
| 4648 | } | |||
| 4649 | return p; | |||
| 4650 | } | |||
| 4651 | ||||
| 4652 | /* | |||
| 4653 | ** Argument pIn points to a character that is part of a nul-terminated | |||
| 4654 | ** string. Return a pointer to the first character following *pIn in | |||
| 4655 | ** the string that is not a "bareword" character. | |||
| 4656 | */ | |||
| 4657 | static const char *fts5ConfigSkipBareword(const char *pIn){ | |||
| 4658 | const char *p = pIn; | |||
| 4659 | while ( sqlite3Fts5IsBareword(*p) ) p++; | |||
| 4660 | if( p==pIn ) p = 0; | |||
| 4661 | return p; | |||
| 4662 | } | |||
| 4663 | ||||
| 4664 | static int fts5_isdigit(char a){ | |||
| 4665 | return (a>='0' && a<='9'); | |||
| 4666 | } | |||
| 4667 | ||||
| 4668 | ||||
| 4669 | ||||
| 4670 | static const char *fts5ConfigSkipLiteral(const char *pIn){ | |||
| 4671 | const char *p = pIn; | |||
| 4672 | switch( *p ){ | |||
| 4673 | case 'n': case 'N': | |||
| 4674 | if( sqlite3_strnicmpsqlite3_api->strnicmp("null", p, 4)==0 ){ | |||
| 4675 | p = &p[4]; | |||
| 4676 | }else{ | |||
| 4677 | p = 0; | |||
| 4678 | } | |||
| 4679 | break; | |||
| 4680 | ||||
| 4681 | case 'x': case 'X': | |||
| 4682 | p++; | |||
| 4683 | if( *p=='\'' ){ | |||
| 4684 | p++; | |||
| 4685 | while( (*p>='a' && *p<='f') | |||
| 4686 | || (*p>='A' && *p<='F') | |||
| 4687 | || (*p>='0' && *p<='9') | |||
| 4688 | ){ | |||
| 4689 | p++; | |||
| 4690 | } | |||
| 4691 | if( *p=='\'' && 0==((p-pIn)%2) ){ | |||
| 4692 | p++; | |||
| 4693 | }else{ | |||
| 4694 | p = 0; | |||
| 4695 | } | |||
| 4696 | }else{ | |||
| 4697 | p = 0; | |||
| 4698 | } | |||
| 4699 | break; | |||
| 4700 | ||||
| 4701 | case '\'': | |||
| 4702 | p++; | |||
| 4703 | while( p ){ | |||
| 4704 | if( *p=='\'' ){ | |||
| 4705 | p++; | |||
| 4706 | if( *p!='\'' ) break; | |||
| 4707 | } | |||
| 4708 | p++; | |||
| 4709 | if( *p==0 ) p = 0; | |||
| 4710 | } | |||
| 4711 | break; | |||
| 4712 | ||||
| 4713 | default: | |||
| 4714 | /* maybe a number */ | |||
| 4715 | if( *p=='+' || *p=='-' ) p++; | |||
| 4716 | while( fts5_isdigit(*p) ) p++; | |||
| 4717 | ||||
| 4718 | /* At this point, if the literal was an integer, the parse is | |||
| 4719 | ** finished. Or, if it is a floating point value, it may continue | |||
| 4720 | ** with either a decimal point or an 'E' character. */ | |||
| 4721 | if( *p=='.' && fts5_isdigit(p[1]) ){ | |||
| 4722 | p += 2; | |||
| 4723 | while( fts5_isdigit(*p) ) p++; | |||
| 4724 | } | |||
| 4725 | if( p==pIn ) p = 0; | |||
| 4726 | ||||
| 4727 | break; | |||
| 4728 | } | |||
| 4729 | ||||
| 4730 | return p; | |||
| 4731 | } | |||
| 4732 | ||||
| 4733 | /* | |||
| 4734 | ** The first character of the string pointed to by argument z is guaranteed | |||
| 4735 | ** to be an open-quote character (see function fts5_isopenquote()). | |||
| 4736 | ** | |||
| 4737 | ** This function searches for the corresponding close-quote character within | |||
| 4738 | ** the string and, if found, dequotes the string in place and adds a new | |||
| 4739 | ** nul-terminator byte. | |||
| 4740 | ** | |||
| 4741 | ** If the close-quote is found, the value returned is the byte offset of | |||
| 4742 | ** the character immediately following it. Or, if the close-quote is not | |||
| 4743 | ** found, -1 is returned. If -1 is returned, the buffer is left in an | |||
| 4744 | ** undefined state. | |||
| 4745 | */ | |||
| 4746 | static int fts5Dequote(char *z){ | |||
| 4747 | char q; | |||
| 4748 | int iIn = 1; | |||
| 4749 | int iOut = 0; | |||
| 4750 | q = z[0]; | |||
| 4751 | ||||
| 4752 | /* Set stack variable q to the close-quote character */ | |||
| 4753 | assert( q=='[' || q=='\'' || q=='"' || q=='`' )((void) (0)); | |||
| 4754 | if( q=='[' ) q = ']'; | |||
| 4755 | ||||
| 4756 | while( z[iIn] ){ | |||
| 4757 | if( z[iIn]==q ){ | |||
| 4758 | if( z[iIn+1]!=q ){ | |||
| 4759 | /* Character iIn was the close quote. */ | |||
| 4760 | iIn++; | |||
| 4761 | break; | |||
| 4762 | }else{ | |||
| 4763 | /* Character iIn and iIn+1 form an escaped quote character. Skip | |||
| 4764 | ** the input cursor past both and copy a single quote character | |||
| 4765 | ** to the output buffer. */ | |||
| 4766 | iIn += 2; | |||
| 4767 | z[iOut++] = q; | |||
| 4768 | } | |||
| 4769 | }else{ | |||
| 4770 | z[iOut++] = z[iIn++]; | |||
| 4771 | } | |||
| 4772 | } | |||
| 4773 | ||||
| 4774 | z[iOut] = '\0'; | |||
| 4775 | return iIn; | |||
| 4776 | } | |||
| 4777 | ||||
| 4778 | /* | |||
| 4779 | ** Convert an SQL-style quoted string into a normal string by removing | |||
| 4780 | ** the quote characters. The conversion is done in-place. If the | |||
| 4781 | ** input does not begin with a quote character, then this routine | |||
| 4782 | ** is a no-op. | |||
| 4783 | ** | |||
| 4784 | ** Examples: | |||
| 4785 | ** | |||
| 4786 | ** "abc" becomes abc | |||
| 4787 | ** 'xyz' becomes xyz | |||
| 4788 | ** [pqr] becomes pqr | |||
| 4789 | ** `mno` becomes mno | |||
| 4790 | */ | |||
| 4791 | static void sqlite3Fts5Dequote(char *z){ | |||
| 4792 | char quote; /* Quote character (if any ) */ | |||
| 4793 | ||||
| 4794 | assert( 0==fts5_iswhitespace(z[0]) )((void) (0)); | |||
| 4795 | quote = z[0]; | |||
| 4796 | if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ | |||
| 4797 | fts5Dequote(z); | |||
| 4798 | } | |||
| 4799 | } | |||
| 4800 | ||||
| 4801 | ||||
| 4802 | struct Fts5Enum { | |||
| 4803 | const char *zName; | |||
| 4804 | int eVal; | |||
| 4805 | }; | |||
| 4806 | typedef struct Fts5Enum Fts5Enum; | |||
| 4807 | ||||
| 4808 | static int fts5ConfigSetEnum( | |||
| 4809 | const Fts5Enum *aEnum, | |||
| 4810 | const char *zEnum, | |||
| 4811 | int *peVal | |||
| 4812 | ){ | |||
| 4813 | int nEnum = (int)strlen(zEnum); | |||
| 4814 | int i; | |||
| 4815 | int iVal = -1; | |||
| 4816 | ||||
| 4817 | for(i=0; aEnum[i].zName; i++){ | |||
| 4818 | if( sqlite3_strnicmpsqlite3_api->strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){ | |||
| 4819 | if( iVal>=0 ) return SQLITE_ERROR1; | |||
| 4820 | iVal = aEnum[i].eVal; | |||
| 4821 | } | |||
| 4822 | } | |||
| 4823 | ||||
| 4824 | *peVal = iVal; | |||
| 4825 | return iVal<0 ? SQLITE_ERROR1 : SQLITE_OK0; | |||
| 4826 | } | |||
| 4827 | ||||
| 4828 | /* | |||
| 4829 | ** Parse a "special" CREATE VIRTUAL TABLE directive and update | |||
| 4830 | ** configuration object pConfig as appropriate. | |||
| 4831 | ** | |||
| 4832 | ** If successful, object pConfig is updated and SQLITE_OK returned. If | |||
| 4833 | ** an error occurs, an SQLite error code is returned and an error message | |||
| 4834 | ** may be left in *pzErr. It is the responsibility of the caller to | |||
| 4835 | ** eventually free any such error message using sqlite3_free(). | |||
| 4836 | */ | |||
| 4837 | static int fts5ConfigParseSpecial( | |||
| 4838 | Fts5Config *pConfig, /* Configuration object to update */ | |||
| 4839 | const char *zCmd, /* Special command to parse */ | |||
| 4840 | const char *zArg, /* Argument to parse */ | |||
| 4841 | char **pzErr /* OUT: Error message */ | |||
| 4842 | ){ | |||
| 4843 | int rc = SQLITE_OK0; | |||
| 4844 | int nCmd = (int)strlen(zCmd); | |||
| 4845 | ||||
| 4846 | if( sqlite3_strnicmpsqlite3_api->strnicmp("prefix", zCmd, nCmd)==0 ){ | |||
| 4847 | const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES31; | |||
| 4848 | const char *p; | |||
| 4849 | int bFirst = 1; | |||
| 4850 | if( pConfig->aPrefix==0 ){ | |||
| 4851 | pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte); | |||
| 4852 | if( rc ) return rc; | |||
| 4853 | } | |||
| 4854 | ||||
| 4855 | p = zArg; | |||
| 4856 | while( 1 ){ | |||
| 4857 | int nPre = 0; | |||
| 4858 | ||||
| 4859 | while( p[0]==' ' ) p++; | |||
| 4860 | if( bFirst==0 && p[0]==',' ){ | |||
| 4861 | p++; | |||
| 4862 | while( p[0]==' ' ) p++; | |||
| 4863 | }else if( p[0]=='\0' ){ | |||
| 4864 | break; | |||
| 4865 | } | |||
| 4866 | if( p[0]<'0' || p[0]>'9' ){ | |||
| 4867 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed prefix=... directive"); | |||
| 4868 | rc = SQLITE_ERROR1; | |||
| 4869 | break; | |||
| 4870 | } | |||
| 4871 | ||||
| 4872 | if( pConfig->nPrefix==FTS5_MAX_PREFIX_INDEXES31 ){ | |||
| 4873 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 4874 | "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES31 | |||
| 4875 | ); | |||
| 4876 | rc = SQLITE_ERROR1; | |||
| 4877 | break; | |||
| 4878 | } | |||
| 4879 | ||||
| 4880 | while( p[0]>='0' && p[0]<='9' && nPre<1000 ){ | |||
| 4881 | nPre = nPre*10 + (p[0] - '0'); | |||
| 4882 | p++; | |||
| 4883 | } | |||
| 4884 | ||||
| 4885 | if( nPre<=0 || nPre>=1000 ){ | |||
| 4886 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("prefix length out of range (max 999)"); | |||
| 4887 | rc = SQLITE_ERROR1; | |||
| 4888 | break; | |||
| 4889 | } | |||
| 4890 | ||||
| 4891 | pConfig->aPrefix[pConfig->nPrefix] = nPre; | |||
| 4892 | pConfig->nPrefix++; | |||
| 4893 | bFirst = 0; | |||
| 4894 | } | |||
| 4895 | assert( pConfig->nPrefix<=FTS5_MAX_PREFIX_INDEXES )((void) (0)); | |||
| 4896 | return rc; | |||
| 4897 | } | |||
| 4898 | ||||
| 4899 | if( sqlite3_strnicmpsqlite3_api->strnicmp("tokenize", zCmd, nCmd)==0 ){ | |||
| 4900 | const char *p = (const char*)zArg; | |||
| 4901 | sqlite3_int64 nArg = strlen(zArg) + 1; | |||
| 4902 | char **azArg = sqlite3Fts5MallocZero(&rc, (sizeof(char*) + 2) * nArg); | |||
| 4903 | ||||
| 4904 | if( azArg ){ | |||
| 4905 | char *pSpace = (char*)&azArg[nArg]; | |||
| 4906 | if( pConfig->t.azArg ){ | |||
| 4907 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("multiple tokenize=... directives"); | |||
| 4908 | rc = SQLITE_ERROR1; | |||
| 4909 | }else{ | |||
| 4910 | for(nArg=0; p && *p; nArg++){ | |||
| 4911 | const char *p2 = fts5ConfigSkipWhitespace(p); | |||
| 4912 | if( *p2=='\'' ){ | |||
| 4913 | p = fts5ConfigSkipLiteral(p2); | |||
| 4914 | }else{ | |||
| 4915 | p = fts5ConfigSkipBareword(p2); | |||
| 4916 | } | |||
| 4917 | if( p ){ | |||
| 4918 | memcpy(pSpace, p2, p-p2); | |||
| 4919 | azArg[nArg] = pSpace; | |||
| 4920 | sqlite3Fts5Dequote(pSpace); | |||
| 4921 | pSpace += (p - p2) + 1; | |||
| 4922 | p = fts5ConfigSkipWhitespace(p); | |||
| 4923 | } | |||
| 4924 | } | |||
| 4925 | if( p==0 ){ | |||
| 4926 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("parse error in tokenize directive"); | |||
| 4927 | rc = SQLITE_ERROR1; | |||
| 4928 | }else{ | |||
| 4929 | pConfig->t.azArg = (const char**)azArg; | |||
| 4930 | pConfig->t.nArg = nArg; | |||
| 4931 | azArg = 0; | |||
| 4932 | } | |||
| 4933 | } | |||
| 4934 | } | |||
| 4935 | sqlite3_freesqlite3_api->free(azArg); | |||
| 4936 | ||||
| 4937 | return rc; | |||
| 4938 | } | |||
| 4939 | ||||
| 4940 | if( sqlite3_strnicmpsqlite3_api->strnicmp("content", zCmd, nCmd)==0 ){ | |||
| 4941 | if( pConfig->eContent!=FTS5_CONTENT_NORMAL0 ){ | |||
| 4942 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("multiple content=... directives"); | |||
| 4943 | rc = SQLITE_ERROR1; | |||
| 4944 | }else{ | |||
| 4945 | if( zArg[0] ){ | |||
| 4946 | pConfig->eContent = FTS5_CONTENT_EXTERNAL2; | |||
| 4947 | pConfig->zContent = sqlite3Fts5Mprintf(&rc, "%Q.%Q", pConfig->zDb,zArg); | |||
| 4948 | }else{ | |||
| 4949 | pConfig->eContent = FTS5_CONTENT_NONE1; | |||
| 4950 | } | |||
| 4951 | } | |||
| 4952 | return rc; | |||
| 4953 | } | |||
| 4954 | ||||
| 4955 | if( sqlite3_strnicmpsqlite3_api->strnicmp("contentless_delete", zCmd, nCmd)==0 ){ | |||
| 4956 | if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ | |||
| 4957 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed contentless_delete=... directive"); | |||
| 4958 | rc = SQLITE_ERROR1; | |||
| 4959 | }else{ | |||
| 4960 | pConfig->bContentlessDelete = (zArg[0]=='1'); | |||
| 4961 | } | |||
| 4962 | return rc; | |||
| 4963 | } | |||
| 4964 | ||||
| 4965 | if( sqlite3_strnicmpsqlite3_api->strnicmp("contentless_unindexed", zCmd, nCmd)==0 ){ | |||
| 4966 | if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ | |||
| 4967 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed contentless_delete=... directive"); | |||
| 4968 | rc = SQLITE_ERROR1; | |||
| 4969 | }else{ | |||
| 4970 | pConfig->bContentlessUnindexed = (zArg[0]=='1'); | |||
| 4971 | } | |||
| 4972 | return rc; | |||
| 4973 | } | |||
| 4974 | ||||
| 4975 | if( sqlite3_strnicmpsqlite3_api->strnicmp("content_rowid", zCmd, nCmd)==0 ){ | |||
| 4976 | if( pConfig->zContentRowid ){ | |||
| 4977 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("multiple content_rowid=... directives"); | |||
| 4978 | rc = SQLITE_ERROR1; | |||
| 4979 | }else{ | |||
| 4980 | pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1); | |||
| 4981 | } | |||
| 4982 | return rc; | |||
| 4983 | } | |||
| 4984 | ||||
| 4985 | if( sqlite3_strnicmpsqlite3_api->strnicmp("columnsize", zCmd, nCmd)==0 ){ | |||
| 4986 | if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ | |||
| 4987 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed columnsize=... directive"); | |||
| 4988 | rc = SQLITE_ERROR1; | |||
| 4989 | }else{ | |||
| 4990 | pConfig->bColumnsize = (zArg[0]=='1'); | |||
| 4991 | } | |||
| 4992 | return rc; | |||
| 4993 | } | |||
| 4994 | ||||
| 4995 | if( sqlite3_strnicmpsqlite3_api->strnicmp("locale", zCmd, nCmd)==0 ){ | |||
| 4996 | if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ | |||
| 4997 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed locale=... directive"); | |||
| 4998 | rc = SQLITE_ERROR1; | |||
| 4999 | }else{ | |||
| 5000 | pConfig->bLocale = (zArg[0]=='1'); | |||
| 5001 | } | |||
| 5002 | return rc; | |||
| 5003 | } | |||
| 5004 | ||||
| 5005 | if( sqlite3_strnicmpsqlite3_api->strnicmp("detail", zCmd, nCmd)==0 ){ | |||
| 5006 | const Fts5Enum aDetail[] = { | |||
| 5007 | { "none", FTS5_DETAIL_NONE1 }, | |||
| 5008 | { "full", FTS5_DETAIL_FULL0 }, | |||
| 5009 | { "columns", FTS5_DETAIL_COLUMNS2 }, | |||
| 5010 | { 0, 0 } | |||
| 5011 | }; | |||
| 5012 | ||||
| 5013 | if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){ | |||
| 5014 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed detail=... directive"); | |||
| 5015 | } | |||
| 5016 | return rc; | |||
| 5017 | } | |||
| 5018 | ||||
| 5019 | if( sqlite3_strnicmpsqlite3_api->strnicmp("tokendata", zCmd, nCmd)==0 ){ | |||
| 5020 | if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ | |||
| 5021 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed tokendata=... directive"); | |||
| 5022 | rc = SQLITE_ERROR1; | |||
| 5023 | }else{ | |||
| 5024 | pConfig->bTokendata = (zArg[0]=='1'); | |||
| 5025 | } | |||
| 5026 | return rc; | |||
| 5027 | } | |||
| 5028 | ||||
| 5029 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); | |||
| 5030 | return SQLITE_ERROR1; | |||
| 5031 | } | |||
| 5032 | ||||
| 5033 | /* | |||
| 5034 | ** Gobble up the first bareword or quoted word from the input buffer zIn. | |||
| 5035 | ** Return a pointer to the character immediately following the last in | |||
| 5036 | ** the gobbled word if successful, or a NULL pointer otherwise (failed | |||
| 5037 | ** to find close-quote character). | |||
| 5038 | ** | |||
| 5039 | ** Before returning, set pzOut to point to a new buffer containing a | |||
| 5040 | ** nul-terminated, dequoted copy of the gobbled word. If the word was | |||
| 5041 | ** quoted, *pbQuoted is also set to 1 before returning. | |||
| 5042 | ** | |||
| 5043 | ** If *pRc is other than SQLITE_OK when this function is called, it is | |||
| 5044 | ** a no-op (NULL is returned). Otherwise, if an OOM occurs within this | |||
| 5045 | ** function, *pRc is set to SQLITE_NOMEM before returning. *pRc is *not* | |||
| 5046 | ** set if a parse error (failed to find close quote) occurs. | |||
| 5047 | */ | |||
| 5048 | static const char *fts5ConfigGobbleWord( | |||
| 5049 | int *pRc, /* IN/OUT: Error code */ | |||
| 5050 | const char *zIn, /* Buffer to gobble string/bareword from */ | |||
| 5051 | char **pzOut, /* OUT: malloc'd buffer containing str/bw */ | |||
| 5052 | int *pbQuoted /* OUT: Set to true if dequoting required */ | |||
| 5053 | ){ | |||
| 5054 | const char *zRet = 0; | |||
| 5055 | ||||
| 5056 | sqlite3_int64 nIn = strlen(zIn); | |||
| 5057 | char *zOut = sqlite3_malloc64sqlite3_api->malloc64(nIn+1); | |||
| 5058 | ||||
| 5059 | assert( *pRc==SQLITE_OK )((void) (0)); | |||
| 5060 | *pbQuoted = 0; | |||
| 5061 | *pzOut = 0; | |||
| 5062 | ||||
| 5063 | if( zOut==0 ){ | |||
| 5064 | *pRc = SQLITE_NOMEM7; | |||
| 5065 | }else{ | |||
| 5066 | memcpy(zOut, zIn, (size_t)(nIn+1)); | |||
| 5067 | if( fts5_isopenquote(zOut[0]) ){ | |||
| 5068 | int ii = fts5Dequote(zOut); | |||
| 5069 | zRet = &zIn[ii]; | |||
| 5070 | *pbQuoted = 1; | |||
| 5071 | }else{ | |||
| 5072 | zRet = fts5ConfigSkipBareword(zIn); | |||
| 5073 | if( zRet ){ | |||
| 5074 | zOut[zRet-zIn] = '\0'; | |||
| 5075 | } | |||
| 5076 | } | |||
| 5077 | } | |||
| 5078 | ||||
| 5079 | if( zRet==0 ){ | |||
| 5080 | sqlite3_freesqlite3_api->free(zOut); | |||
| 5081 | }else{ | |||
| 5082 | *pzOut = zOut; | |||
| 5083 | } | |||
| 5084 | ||||
| 5085 | return zRet; | |||
| 5086 | } | |||
| 5087 | ||||
| 5088 | static int fts5ConfigParseColumn( | |||
| 5089 | Fts5Config *p, | |||
| 5090 | char *zCol, | |||
| 5091 | char *zArg, | |||
| 5092 | char **pzErr, | |||
| 5093 | int *pbUnindexed | |||
| 5094 | ){ | |||
| 5095 | int rc = SQLITE_OK0; | |||
| 5096 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(zCol, FTS5_RANK_NAME"rank") | |||
| 5097 | || 0==sqlite3_stricmpsqlite3_api->stricmp(zCol, FTS5_ROWID_NAME"rowid") | |||
| 5098 | ){ | |||
| 5099 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("reserved fts5 column name: %s", zCol); | |||
| 5100 | rc = SQLITE_ERROR1; | |||
| 5101 | }else if( zArg ){ | |||
| 5102 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(zArg, "unindexed") ){ | |||
| 5103 | p->abUnindexed[p->nCol] = 1; | |||
| 5104 | *pbUnindexed = 1; | |||
| 5105 | }else{ | |||
| 5106 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("unrecognized column option: %s", zArg); | |||
| 5107 | rc = SQLITE_ERROR1; | |||
| 5108 | } | |||
| 5109 | } | |||
| 5110 | ||||
| 5111 | p->azCol[p->nCol++] = zCol; | |||
| 5112 | return rc; | |||
| 5113 | } | |||
| 5114 | ||||
| 5115 | /* | |||
| 5116 | ** Populate the Fts5Config.zContentExprlist string. | |||
| 5117 | */ | |||
| 5118 | static int fts5ConfigMakeExprlist(Fts5Config *p){ | |||
| 5119 | int i; | |||
| 5120 | int rc = SQLITE_OK0; | |||
| 5121 | Fts5Buffer buf = {0, 0, 0}; | |||
| 5122 | ||||
| 5123 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid); | |||
| 5124 | if( p->eContent!=FTS5_CONTENT_NONE1 ){ | |||
| 5125 | assert( p->eContent==FTS5_CONTENT_EXTERNAL((void) (0)) | |||
| 5126 | || p->eContent==FTS5_CONTENT_NORMAL((void) (0)) | |||
| 5127 | || p->eContent==FTS5_CONTENT_UNINDEXED((void) (0)) | |||
| 5128 | )((void) (0)); | |||
| 5129 | for(i=0; i<p->nCol; i++){ | |||
| 5130 | if( p->eContent==FTS5_CONTENT_EXTERNAL2 ){ | |||
| 5131 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]); | |||
| 5132 | }else if( p->eContent==FTS5_CONTENT_NORMAL0 || p->abUnindexed[i] ){ | |||
| 5133 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i); | |||
| 5134 | }else{ | |||
| 5135 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); | |||
| 5136 | } | |||
| 5137 | } | |||
| 5138 | } | |||
| 5139 | if( p->eContent==FTS5_CONTENT_NORMAL0 && p->bLocale ){ | |||
| 5140 | for(i=0; i<p->nCol; i++){ | |||
| 5141 | if( p->abUnindexed[i]==0 ){ | |||
| 5142 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i); | |||
| 5143 | }else{ | |||
| 5144 | sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); | |||
| 5145 | } | |||
| 5146 | } | |||
| 5147 | } | |||
| 5148 | ||||
| 5149 | assert( p->zContentExprlist==0 )((void) (0)); | |||
| 5150 | p->zContentExprlist = (char*)buf.p; | |||
| 5151 | return rc; | |||
| 5152 | } | |||
| 5153 | ||||
| 5154 | /* | |||
| 5155 | ** Arguments nArg/azArg contain the string arguments passed to the xCreate | |||
| 5156 | ** or xConnect method of the virtual table. This function attempts to | |||
| 5157 | ** allocate an instance of Fts5Config containing the results of parsing | |||
| 5158 | ** those arguments. | |||
| 5159 | ** | |||
| 5160 | ** If successful, SQLITE_OK is returned and *ppOut is set to point to the | |||
| 5161 | ** new Fts5Config object. If an error occurs, an SQLite error code is | |||
| 5162 | ** returned, *ppOut is set to NULL and an error message may be left in | |||
| 5163 | ** *pzErr. It is the responsibility of the caller to eventually free any | |||
| 5164 | ** such error message using sqlite3_free(). | |||
| 5165 | */ | |||
| 5166 | static int sqlite3Fts5ConfigParse( | |||
| 5167 | Fts5Global *pGlobal, | |||
| 5168 | sqlite3 *db, | |||
| 5169 | int nArg, /* Number of arguments */ | |||
| 5170 | const char **azArg, /* Array of nArg CREATE VIRTUAL TABLE args */ | |||
| 5171 | Fts5Config **ppOut, /* OUT: Results of parse */ | |||
| 5172 | char **pzErr /* OUT: Error message */ | |||
| 5173 | ){ | |||
| 5174 | int rc = SQLITE_OK0; /* Return code */ | |||
| 5175 | Fts5Config *pRet; /* New object to return */ | |||
| 5176 | int i; | |||
| 5177 | sqlite3_int64 nByte; | |||
| 5178 | int bUnindexed = 0; /* True if there are one or more UNINDEXED */ | |||
| 5179 | ||||
| 5180 | *ppOut = pRet = (Fts5Config*)sqlite3_malloc64sqlite3_api->malloc64(sizeof(Fts5Config)); | |||
| 5181 | if( pRet==0 ) return SQLITE_NOMEM7; | |||
| 5182 | memset(pRet, 0, sizeof(Fts5Config)); | |||
| 5183 | pRet->pGlobal = pGlobal; | |||
| 5184 | pRet->db = db; | |||
| 5185 | pRet->iCookie = -1; | |||
| 5186 | ||||
| 5187 | nByte = nArg * (sizeof(char*) + sizeof(u8)); | |||
| 5188 | pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 5189 | pRet->abUnindexed = pRet->azCol ? (u8*)&pRet->azCol[nArg] : 0; | |||
| 5190 | pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1); | |||
| 5191 | pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1); | |||
| 5192 | pRet->bColumnsize = 1; | |||
| 5193 | pRet->eDetail = FTS5_DETAIL_FULL0; | |||
| 5194 | #ifdef SQLITE_DEBUG | |||
| 5195 | pRet->bPrefixIndex = 1; | |||
| 5196 | #endif | |||
| 5197 | if( rc==SQLITE_OK0 && sqlite3_stricmpsqlite3_api->stricmp(pRet->zName, FTS5_RANK_NAME"rank")==0 ){ | |||
| 5198 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("reserved fts5 table name: %s", pRet->zName); | |||
| 5199 | rc = SQLITE_ERROR1; | |||
| 5200 | } | |||
| 5201 | ||||
| 5202 | assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK )((void) (0)); | |||
| 5203 | for(i=3; rc==SQLITE_OK0 && i<nArg; i++){ | |||
| 5204 | const char *zOrig = azArg[i]; | |||
| 5205 | const char *z; | |||
| 5206 | char *zOne = 0; | |||
| 5207 | char *zTwo = 0; | |||
| 5208 | int bOption = 0; | |||
| 5209 | int bMustBeCol = 0; | |||
| 5210 | ||||
| 5211 | z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol); | |||
| 5212 | z = fts5ConfigSkipWhitespace(z); | |||
| 5213 | if( z && *z=='=' ){ | |||
| 5214 | bOption = 1; | |||
| 5215 | assert( zOne!=0 )((void) (0)); | |||
| 5216 | z++; | |||
| 5217 | if( bMustBeCol ) z = 0; | |||
| 5218 | } | |||
| 5219 | z = fts5ConfigSkipWhitespace(z); | |||
| 5220 | if( z && z[0] ){ | |||
| 5221 | int bDummy; | |||
| 5222 | z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy); | |||
| 5223 | if( z && z[0] ) z = 0; | |||
| 5224 | } | |||
| 5225 | ||||
| 5226 | if( rc==SQLITE_OK0 ){ | |||
| 5227 | if( z==0 ){ | |||
| 5228 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("parse error in \"%s\"", zOrig); | |||
| 5229 | rc = SQLITE_ERROR1; | |||
| 5230 | }else{ | |||
| 5231 | if( bOption ){ | |||
| 5232 | rc = fts5ConfigParseSpecial(pRet, | |||
| 5233 | ALWAYS(zOne)(zOne)?zOne:"", | |||
| 5234 | zTwo?zTwo:"", | |||
| 5235 | pzErr | |||
| 5236 | ); | |||
| 5237 | }else{ | |||
| 5238 | rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr, &bUnindexed); | |||
| 5239 | zOne = 0; | |||
| 5240 | } | |||
| 5241 | } | |||
| 5242 | } | |||
| 5243 | ||||
| 5244 | sqlite3_freesqlite3_api->free(zOne); | |||
| 5245 | sqlite3_freesqlite3_api->free(zTwo); | |||
| 5246 | } | |||
| 5247 | ||||
| 5248 | /* We only allow contentless_delete=1 if the table is indeed contentless. */ | |||
| 5249 | if( rc==SQLITE_OK0 | |||
| 5250 | && pRet->bContentlessDelete | |||
| 5251 | && pRet->eContent!=FTS5_CONTENT_NONE1 | |||
| 5252 | ){ | |||
| 5253 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 5254 | "contentless_delete=1 requires a contentless table" | |||
| 5255 | ); | |||
| 5256 | rc = SQLITE_ERROR1; | |||
| 5257 | } | |||
| 5258 | ||||
| 5259 | /* We only allow contentless_delete=1 if columnsize=0 is not present. | |||
| 5260 | ** | |||
| 5261 | ** This restriction may be removed at some point. | |||
| 5262 | */ | |||
| 5263 | if( rc==SQLITE_OK0 && pRet->bContentlessDelete && pRet->bColumnsize==0 ){ | |||
| 5264 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 5265 | "contentless_delete=1 is incompatible with columnsize=0" | |||
| 5266 | ); | |||
| 5267 | rc = SQLITE_ERROR1; | |||
| 5268 | } | |||
| 5269 | ||||
| 5270 | /* We only allow contentless_unindexed=1 if the table is actually a | |||
| 5271 | ** contentless one. | |||
| 5272 | */ | |||
| 5273 | if( rc==SQLITE_OK0 | |||
| 5274 | && pRet->bContentlessUnindexed | |||
| 5275 | && pRet->eContent!=FTS5_CONTENT_NONE1 | |||
| 5276 | ){ | |||
| 5277 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 5278 | "contentless_unindexed=1 requires a contentless table" | |||
| 5279 | ); | |||
| 5280 | rc = SQLITE_ERROR1; | |||
| 5281 | } | |||
| 5282 | ||||
| 5283 | /* If no zContent option was specified, fill in the default values. */ | |||
| 5284 | if( rc==SQLITE_OK0 && pRet->zContent==0 ){ | |||
| 5285 | const char *zTail = 0; | |||
| 5286 | assert( pRet->eContent==FTS5_CONTENT_NORMAL((void) (0)) | |||
| 5287 | || pRet->eContent==FTS5_CONTENT_NONE((void) (0)) | |||
| 5288 | )((void) (0)); | |||
| 5289 | if( pRet->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 5290 | zTail = "content"; | |||
| 5291 | }else if( bUnindexed && pRet->bContentlessUnindexed ){ | |||
| 5292 | pRet->eContent = FTS5_CONTENT_UNINDEXED3; | |||
| 5293 | zTail = "content"; | |||
| 5294 | }else if( pRet->bColumnsize ){ | |||
| 5295 | zTail = "docsize"; | |||
| 5296 | } | |||
| 5297 | ||||
| 5298 | if( zTail ){ | |||
| 5299 | pRet->zContent = sqlite3Fts5Mprintf( | |||
| 5300 | &rc, "%Q.'%q_%s'", pRet->zDb, pRet->zName, zTail | |||
| 5301 | ); | |||
| 5302 | } | |||
| 5303 | } | |||
| 5304 | ||||
| 5305 | if( rc==SQLITE_OK0 && pRet->zContentRowid==0 ){ | |||
| 5306 | pRet->zContentRowid = sqlite3Fts5Strndup(&rc, "rowid", -1); | |||
| 5307 | } | |||
| 5308 | ||||
| 5309 | /* Formulate the zContentExprlist text */ | |||
| 5310 | if( rc==SQLITE_OK0 ){ | |||
| 5311 | rc = fts5ConfigMakeExprlist(pRet); | |||
| 5312 | } | |||
| 5313 | ||||
| 5314 | if( rc!=SQLITE_OK0 ){ | |||
| 5315 | sqlite3Fts5ConfigFree(pRet); | |||
| 5316 | *ppOut = 0; | |||
| 5317 | } | |||
| 5318 | return rc; | |||
| 5319 | } | |||
| 5320 | ||||
| 5321 | /* | |||
| 5322 | ** Free the configuration object passed as the only argument. | |||
| 5323 | */ | |||
| 5324 | static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){ | |||
| 5325 | if( pConfig ){ | |||
| 5326 | int i; | |||
| 5327 | if( pConfig->t.pTok ){ | |||
| 5328 | if( pConfig->t.pApi1 ){ | |||
| 5329 | pConfig->t.pApi1->xDelete(pConfig->t.pTok); | |||
| 5330 | }else{ | |||
| 5331 | pConfig->t.pApi2->xDelete(pConfig->t.pTok); | |||
| 5332 | } | |||
| 5333 | } | |||
| 5334 | sqlite3_freesqlite3_api->free((char*)pConfig->t.azArg); | |||
| 5335 | sqlite3_freesqlite3_api->free(pConfig->zDb); | |||
| 5336 | sqlite3_freesqlite3_api->free(pConfig->zName); | |||
| 5337 | for(i=0; i<pConfig->nCol; i++){ | |||
| 5338 | sqlite3_freesqlite3_api->free(pConfig->azCol[i]); | |||
| 5339 | } | |||
| 5340 | sqlite3_freesqlite3_api->free(pConfig->azCol); | |||
| 5341 | sqlite3_freesqlite3_api->free(pConfig->aPrefix); | |||
| 5342 | sqlite3_freesqlite3_api->free(pConfig->zRank); | |||
| 5343 | sqlite3_freesqlite3_api->free(pConfig->zRankArgs); | |||
| 5344 | sqlite3_freesqlite3_api->free(pConfig->zContent); | |||
| 5345 | sqlite3_freesqlite3_api->free(pConfig->zContentRowid); | |||
| 5346 | sqlite3_freesqlite3_api->free(pConfig->zContentExprlist); | |||
| 5347 | sqlite3_freesqlite3_api->free(pConfig); | |||
| 5348 | } | |||
| 5349 | } | |||
| 5350 | ||||
| 5351 | /* | |||
| 5352 | ** Call sqlite3_declare_vtab() based on the contents of the configuration | |||
| 5353 | ** object passed as the only argument. Return SQLITE_OK if successful, or | |||
| 5354 | ** an SQLite error code if an error occurs. | |||
| 5355 | */ | |||
| 5356 | static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){ | |||
| 5357 | int i; | |||
| 5358 | int rc = SQLITE_OK0; | |||
| 5359 | char *zSql; | |||
| 5360 | ||||
| 5361 | zSql = sqlite3Fts5Mprintf(&rc, "CREATE TABLE x("); | |||
| 5362 | for(i=0; zSql && i<pConfig->nCol; i++){ | |||
| 5363 | const char *zSep = (i==0?"":", "); | |||
| 5364 | zSql = sqlite3Fts5Mprintf(&rc, "%z%s%Q", zSql, zSep, pConfig->azCol[i]); | |||
| 5365 | } | |||
| 5366 | zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)", | |||
| 5367 | zSql, pConfig->zName, FTS5_RANK_NAME"rank" | |||
| 5368 | ); | |||
| 5369 | ||||
| 5370 | assert( zSql || rc==SQLITE_NOMEM )((void) (0)); | |||
| 5371 | if( zSql ){ | |||
| 5372 | rc = sqlite3_declare_vtabsqlite3_api->declare_vtab(pConfig->db, zSql); | |||
| 5373 | sqlite3_freesqlite3_api->free(zSql); | |||
| 5374 | } | |||
| 5375 | ||||
| 5376 | return rc; | |||
| 5377 | } | |||
| 5378 | ||||
| 5379 | /* | |||
| 5380 | ** Tokenize the text passed via the second and third arguments. | |||
| 5381 | ** | |||
| 5382 | ** The callback is invoked once for each token in the input text. The | |||
| 5383 | ** arguments passed to it are, in order: | |||
| 5384 | ** | |||
| 5385 | ** void *pCtx // Copy of 4th argument to sqlite3Fts5Tokenize() | |||
| 5386 | ** const char *pToken // Pointer to buffer containing token | |||
| 5387 | ** int nToken // Size of token in bytes | |||
| 5388 | ** int iStart // Byte offset of start of token within input text | |||
| 5389 | ** int iEnd // Byte offset of end of token within input text | |||
| 5390 | ** int iPos // Position of token in input (first token is 0) | |||
| 5391 | ** | |||
| 5392 | ** If the callback returns a non-zero value the tokenization is abandoned | |||
| 5393 | ** and no further callbacks are issued. | |||
| 5394 | ** | |||
| 5395 | ** This function returns SQLITE_OK if successful or an SQLite error code | |||
| 5396 | ** if an error occurs. If the tokenization was abandoned early because | |||
| 5397 | ** the callback returned SQLITE_DONE, this is not an error and this function | |||
| 5398 | ** still returns SQLITE_OK. Or, if the tokenization was abandoned early | |||
| 5399 | ** because the callback returned another non-zero value, it is assumed | |||
| 5400 | ** to be an SQLite error code and returned to the caller. | |||
| 5401 | */ | |||
| 5402 | static int sqlite3Fts5Tokenize( | |||
| 5403 | Fts5Config *pConfig, /* FTS5 Configuration object */ | |||
| 5404 | int flags, /* FTS5_TOKENIZE_* flags */ | |||
| 5405 | const char *pText, int nText, /* Text to tokenize */ | |||
| 5406 | void *pCtx, /* Context passed to xToken() */ | |||
| 5407 | int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ | |||
| 5408 | ){ | |||
| 5409 | int rc = SQLITE_OK0; | |||
| 5410 | if( pText ){ | |||
| 5411 | if( pConfig->t.pTok==0 ){ | |||
| 5412 | rc = sqlite3Fts5LoadTokenizer(pConfig); | |||
| 5413 | } | |||
| 5414 | if( rc==SQLITE_OK0 ){ | |||
| 5415 | if( pConfig->t.pApi1 ){ | |||
| 5416 | rc = pConfig->t.pApi1->xTokenize( | |||
| 5417 | pConfig->t.pTok, pCtx, flags, pText, nText, xToken | |||
| 5418 | ); | |||
| 5419 | }else{ | |||
| 5420 | rc = pConfig->t.pApi2->xTokenize(pConfig->t.pTok, pCtx, flags, | |||
| 5421 | pText, nText, pConfig->t.pLocale, pConfig->t.nLocale, xToken | |||
| 5422 | ); | |||
| 5423 | } | |||
| 5424 | } | |||
| 5425 | } | |||
| 5426 | return rc; | |||
| 5427 | } | |||
| 5428 | ||||
| 5429 | /* | |||
| 5430 | ** Argument pIn points to the first character in what is expected to be | |||
| 5431 | ** a comma-separated list of SQL literals followed by a ')' character. | |||
| 5432 | ** If it actually is this, return a pointer to the ')'. Otherwise, return | |||
| 5433 | ** NULL to indicate a parse error. | |||
| 5434 | */ | |||
| 5435 | static const char *fts5ConfigSkipArgs(const char *pIn){ | |||
| 5436 | const char *p = pIn; | |||
| 5437 | ||||
| 5438 | while( 1 ){ | |||
| 5439 | p = fts5ConfigSkipWhitespace(p); | |||
| 5440 | p = fts5ConfigSkipLiteral(p); | |||
| 5441 | p = fts5ConfigSkipWhitespace(p); | |||
| 5442 | if( p==0 || *p==')' ) break; | |||
| 5443 | if( *p!=',' ){ | |||
| 5444 | p = 0; | |||
| 5445 | break; | |||
| 5446 | } | |||
| 5447 | p++; | |||
| 5448 | } | |||
| 5449 | ||||
| 5450 | return p; | |||
| 5451 | } | |||
| 5452 | ||||
| 5453 | /* | |||
| 5454 | ** Parameter zIn contains a rank() function specification. The format of | |||
| 5455 | ** this is: | |||
| 5456 | ** | |||
| 5457 | ** + Bareword (function name) | |||
| 5458 | ** + Open parenthesis - "(" | |||
| 5459 | ** + Zero or more SQL literals in a comma separated list | |||
| 5460 | ** + Close parenthesis - ")" | |||
| 5461 | */ | |||
| 5462 | static int sqlite3Fts5ConfigParseRank( | |||
| 5463 | const char *zIn, /* Input string */ | |||
| 5464 | char **pzRank, /* OUT: Rank function name */ | |||
| 5465 | char **pzRankArgs /* OUT: Rank function arguments */ | |||
| 5466 | ){ | |||
| 5467 | const char *p = zIn; | |||
| 5468 | const char *pRank; | |||
| 5469 | char *zRank = 0; | |||
| 5470 | char *zRankArgs = 0; | |||
| 5471 | int rc = SQLITE_OK0; | |||
| 5472 | ||||
| 5473 | *pzRank = 0; | |||
| 5474 | *pzRankArgs = 0; | |||
| 5475 | ||||
| 5476 | if( p==0 ){ | |||
| 5477 | rc = SQLITE_ERROR1; | |||
| 5478 | }else{ | |||
| 5479 | p = fts5ConfigSkipWhitespace(p); | |||
| 5480 | pRank = p; | |||
| 5481 | p = fts5ConfigSkipBareword(p); | |||
| 5482 | ||||
| 5483 | if( p ){ | |||
| 5484 | zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank); | |||
| 5485 | if( zRank ) memcpy(zRank, pRank, p-pRank); | |||
| 5486 | }else{ | |||
| 5487 | rc = SQLITE_ERROR1; | |||
| 5488 | } | |||
| 5489 | ||||
| 5490 | if( rc==SQLITE_OK0 ){ | |||
| 5491 | p = fts5ConfigSkipWhitespace(p); | |||
| 5492 | if( *p!='(' ) rc = SQLITE_ERROR1; | |||
| 5493 | p++; | |||
| 5494 | } | |||
| 5495 | if( rc==SQLITE_OK0 ){ | |||
| 5496 | const char *pArgs; | |||
| 5497 | p = fts5ConfigSkipWhitespace(p); | |||
| 5498 | pArgs = p; | |||
| 5499 | if( *p!=')' ){ | |||
| 5500 | p = fts5ConfigSkipArgs(p); | |||
| 5501 | if( p==0 ){ | |||
| 5502 | rc = SQLITE_ERROR1; | |||
| 5503 | }else{ | |||
| 5504 | zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs); | |||
| 5505 | if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs); | |||
| 5506 | } | |||
| 5507 | } | |||
| 5508 | } | |||
| 5509 | } | |||
| 5510 | ||||
| 5511 | if( rc!=SQLITE_OK0 ){ | |||
| 5512 | sqlite3_freesqlite3_api->free(zRank); | |||
| 5513 | assert( zRankArgs==0 )((void) (0)); | |||
| 5514 | }else{ | |||
| 5515 | *pzRank = zRank; | |||
| 5516 | *pzRankArgs = zRankArgs; | |||
| 5517 | } | |||
| 5518 | return rc; | |||
| 5519 | } | |||
| 5520 | ||||
| 5521 | static int sqlite3Fts5ConfigSetValue( | |||
| 5522 | Fts5Config *pConfig, | |||
| 5523 | const char *zKey, | |||
| 5524 | sqlite3_value *pVal, | |||
| 5525 | int *pbBadkey | |||
| 5526 | ){ | |||
| 5527 | int rc = SQLITE_OK0; | |||
| 5528 | ||||
| 5529 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "pgsz") ){ | |||
| 5530 | int pgsz = 0; | |||
| 5531 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5532 | pgsz = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5533 | } | |||
| 5534 | if( pgsz<32 || pgsz>FTS5_MAX_PAGE_SIZE(64*1024) ){ | |||
| 5535 | *pbBadkey = 1; | |||
| 5536 | }else{ | |||
| 5537 | pConfig->pgsz = pgsz; | |||
| 5538 | } | |||
| 5539 | } | |||
| 5540 | ||||
| 5541 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "hashsize") ){ | |||
| 5542 | int nHashSize = -1; | |||
| 5543 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5544 | nHashSize = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5545 | } | |||
| 5546 | if( nHashSize<=0 ){ | |||
| 5547 | *pbBadkey = 1; | |||
| 5548 | }else{ | |||
| 5549 | pConfig->nHashSize = nHashSize; | |||
| 5550 | } | |||
| 5551 | } | |||
| 5552 | ||||
| 5553 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "automerge") ){ | |||
| 5554 | int nAutomerge = -1; | |||
| 5555 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5556 | nAutomerge = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5557 | } | |||
| 5558 | if( nAutomerge<0 || nAutomerge>64 ){ | |||
| 5559 | *pbBadkey = 1; | |||
| 5560 | }else{ | |||
| 5561 | if( nAutomerge==1 ) nAutomerge = FTS5_DEFAULT_AUTOMERGE4; | |||
| 5562 | pConfig->nAutomerge = nAutomerge; | |||
| 5563 | } | |||
| 5564 | } | |||
| 5565 | ||||
| 5566 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "usermerge") ){ | |||
| 5567 | int nUsermerge = -1; | |||
| 5568 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5569 | nUsermerge = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5570 | } | |||
| 5571 | if( nUsermerge<2 || nUsermerge>16 ){ | |||
| 5572 | *pbBadkey = 1; | |||
| 5573 | }else{ | |||
| 5574 | pConfig->nUsermerge = nUsermerge; | |||
| 5575 | } | |||
| 5576 | } | |||
| 5577 | ||||
| 5578 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "crisismerge") ){ | |||
| 5579 | int nCrisisMerge = -1; | |||
| 5580 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5581 | nCrisisMerge = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5582 | } | |||
| 5583 | if( nCrisisMerge<0 ){ | |||
| 5584 | *pbBadkey = 1; | |||
| 5585 | }else{ | |||
| 5586 | if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE16; | |||
| 5587 | if( nCrisisMerge>=FTS5_MAX_SEGMENT2000 ) nCrisisMerge = FTS5_MAX_SEGMENT2000-1; | |||
| 5588 | pConfig->nCrisisMerge = nCrisisMerge; | |||
| 5589 | } | |||
| 5590 | } | |||
| 5591 | ||||
| 5592 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "deletemerge") ){ | |||
| 5593 | int nVal = -1; | |||
| 5594 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5595 | nVal = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5596 | }else{ | |||
| 5597 | *pbBadkey = 1; | |||
| 5598 | } | |||
| 5599 | if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE10; | |||
| 5600 | if( nVal>100 ) nVal = 0; | |||
| 5601 | pConfig->nDeleteMerge = nVal; | |||
| 5602 | } | |||
| 5603 | ||||
| 5604 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "rank") ){ | |||
| 5605 | const char *zIn = (const char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 5606 | char *zRank; | |||
| 5607 | char *zRankArgs; | |||
| 5608 | rc = sqlite3Fts5ConfigParseRank(zIn, &zRank, &zRankArgs); | |||
| 5609 | if( rc==SQLITE_OK0 ){ | |||
| 5610 | sqlite3_freesqlite3_api->free(pConfig->zRank); | |||
| 5611 | sqlite3_freesqlite3_api->free(pConfig->zRankArgs); | |||
| 5612 | pConfig->zRank = zRank; | |||
| 5613 | pConfig->zRankArgs = zRankArgs; | |||
| 5614 | }else if( rc==SQLITE_ERROR1 ){ | |||
| 5615 | rc = SQLITE_OK0; | |||
| 5616 | *pbBadkey = 1; | |||
| 5617 | } | |||
| 5618 | } | |||
| 5619 | ||||
| 5620 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "secure-delete") ){ | |||
| 5621 | int bVal = -1; | |||
| 5622 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5623 | bVal = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5624 | } | |||
| 5625 | if( bVal<0 ){ | |||
| 5626 | *pbBadkey = 1; | |||
| 5627 | }else{ | |||
| 5628 | pConfig->bSecureDelete = (bVal ? 1 : 0); | |||
| 5629 | } | |||
| 5630 | } | |||
| 5631 | ||||
| 5632 | else if( 0==sqlite3_stricmpsqlite3_api->stricmp(zKey, "insttoken") ){ | |||
| 5633 | int bVal = -1; | |||
| 5634 | if( SQLITE_INTEGER1==sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal) ){ | |||
| 5635 | bVal = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5636 | } | |||
| 5637 | if( bVal<0 ){ | |||
| 5638 | *pbBadkey = 1; | |||
| 5639 | }else{ | |||
| 5640 | pConfig->bPrefixInsttoken = (bVal ? 1 : 0); | |||
| 5641 | } | |||
| 5642 | ||||
| 5643 | }else{ | |||
| 5644 | *pbBadkey = 1; | |||
| 5645 | } | |||
| 5646 | return rc; | |||
| 5647 | } | |||
| 5648 | ||||
| 5649 | /* | |||
| 5650 | ** Load the contents of the %_config table into memory. | |||
| 5651 | */ | |||
| 5652 | static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ | |||
| 5653 | const char *zSelect = "SELECT k, v FROM %Q.'%q_config'"; | |||
| 5654 | char *zSql; | |||
| 5655 | sqlite3_stmt *p = 0; | |||
| 5656 | int rc = SQLITE_OK0; | |||
| 5657 | int iVersion = 0; | |||
| 5658 | ||||
| 5659 | /* Set default values */ | |||
| 5660 | pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE4050; | |||
| 5661 | pConfig->nAutomerge = FTS5_DEFAULT_AUTOMERGE4; | |||
| 5662 | pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE4; | |||
| 5663 | pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE16; | |||
| 5664 | pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE(1024*1024); | |||
| 5665 | pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE10; | |||
| 5666 | ||||
| 5667 | zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName); | |||
| 5668 | if( zSql ){ | |||
| 5669 | rc = sqlite3_prepare_v2sqlite3_api->prepare_v2(pConfig->db, zSql, -1, &p, 0); | |||
| 5670 | sqlite3_freesqlite3_api->free(zSql); | |||
| 5671 | } | |||
| 5672 | ||||
| 5673 | assert( rc==SQLITE_OK || p==0 )((void) (0)); | |||
| 5674 | if( rc==SQLITE_OK0 ){ | |||
| 5675 | while( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(p) ){ | |||
| 5676 | const char *zK = (const char*)sqlite3_column_textsqlite3_api->column_text(p, 0); | |||
| 5677 | sqlite3_value *pVal = sqlite3_column_valuesqlite3_api->column_value(p, 1); | |||
| 5678 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(zK, "version") ){ | |||
| 5679 | iVersion = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 5680 | }else{ | |||
| 5681 | int bDummy = 0; | |||
| 5682 | sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy); | |||
| 5683 | } | |||
| 5684 | } | |||
| 5685 | rc = sqlite3_finalizesqlite3_api->finalize(p); | |||
| 5686 | } | |||
| 5687 | ||||
| 5688 | if( rc==SQLITE_OK0 | |||
| 5689 | && iVersion!=FTS5_CURRENT_VERSION4 | |||
| 5690 | && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE5 | |||
| 5691 | ){ | |||
| 5692 | rc = SQLITE_ERROR1; | |||
| 5693 | sqlite3Fts5ConfigErrmsg(pConfig, "invalid fts5 file format " | |||
| 5694 | "(found %d, expected %d or %d) - run 'rebuild'", | |||
| 5695 | iVersion, FTS5_CURRENT_VERSION4, FTS5_CURRENT_VERSION_SECUREDELETE5 | |||
| 5696 | ); | |||
| 5697 | }else{ | |||
| 5698 | pConfig->iVersion = iVersion; | |||
| 5699 | } | |||
| 5700 | ||||
| 5701 | if( rc==SQLITE_OK0 ){ | |||
| 5702 | pConfig->iCookie = iCookie; | |||
| 5703 | } | |||
| 5704 | return rc; | |||
| 5705 | } | |||
| 5706 | ||||
| 5707 | /* | |||
| 5708 | ** Set (*pConfig->pzErrmsg) to point to an sqlite3_malloc()ed buffer | |||
| 5709 | ** containing the error message created using printf() style formatting | |||
| 5710 | ** string zFmt and its trailing arguments. | |||
| 5711 | */ | |||
| 5712 | static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){ | |||
| 5713 | va_list ap; /* ... printf arguments */ | |||
| 5714 | char *zMsg = 0; | |||
| 5715 | ||||
| 5716 | va_start(ap, zFmt)__builtin_va_start(ap, zFmt); | |||
| 5717 | zMsg = sqlite3_vmprintfsqlite3_api->vmprintf(zFmt, ap); | |||
| 5718 | if( pConfig->pzErrmsg ){ | |||
| 5719 | assert( *pConfig->pzErrmsg==0 )((void) (0)); | |||
| 5720 | *pConfig->pzErrmsg = zMsg; | |||
| 5721 | }else{ | |||
| 5722 | sqlite3_freesqlite3_api->free(zMsg); | |||
| 5723 | } | |||
| 5724 | ||||
| 5725 | va_end(ap)__builtin_va_end(ap); | |||
| 5726 | } | |||
| 5727 | ||||
| 5728 | #line 1 "fts5_expr.c" | |||
| 5729 | /* | |||
| 5730 | ** 2014 May 31 | |||
| 5731 | ** | |||
| 5732 | ** The author disclaims copyright to this source code. In place of | |||
| 5733 | ** a legal notice, here is a blessing: | |||
| 5734 | ** | |||
| 5735 | ** May you do good and not evil. | |||
| 5736 | ** May you find forgiveness for yourself and forgive others. | |||
| 5737 | ** May you share freely, never taking more than you give. | |||
| 5738 | ** | |||
| 5739 | ****************************************************************************** | |||
| 5740 | ** | |||
| 5741 | */ | |||
| 5742 | ||||
| 5743 | ||||
| 5744 | ||||
| 5745 | /* #include "fts5Int.h" */ | |||
| 5746 | /* #include "fts5parse.h" */ | |||
| 5747 | ||||
| 5748 | #ifndef SQLITE_FTS5_MAX_EXPR_DEPTH256 | |||
| 5749 | # define SQLITE_FTS5_MAX_EXPR_DEPTH256 256 | |||
| 5750 | #endif | |||
| 5751 | ||||
| 5752 | /* | |||
| 5753 | ** All token types in the generated fts5parse.h file are greater than 0. | |||
| 5754 | */ | |||
| 5755 | #define FTS5_EOF0 0 | |||
| 5756 | ||||
| 5757 | #define FTS5_LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)) (0xffffffff|(((i64)0x7fffffff)<<32)) | |||
| 5758 | ||||
| 5759 | typedef struct Fts5ExprTerm Fts5ExprTerm; | |||
| 5760 | ||||
| 5761 | /* | |||
| 5762 | ** Functions generated by lemon from fts5parse.y. | |||
| 5763 | */ | |||
| 5764 | static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64)); | |||
| 5765 | static void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*)); | |||
| 5766 | static void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*); | |||
| 5767 | #ifndef NDEBUG1 | |||
| 5768 | #include <stdio.h> | |||
| 5769 | static void sqlite3Fts5ParserTrace(FILE*, char*); | |||
| 5770 | #endif | |||
| 5771 | static int sqlite3Fts5ParserFallback(int); | |||
| 5772 | ||||
| 5773 | ||||
| 5774 | struct Fts5Expr { | |||
| 5775 | Fts5Index *pIndex; | |||
| 5776 | Fts5Config *pConfig; | |||
| 5777 | Fts5ExprNode *pRoot; | |||
| 5778 | int bDesc; /* Iterate in descending rowid order */ | |||
| 5779 | int nPhrase; /* Number of phrases in expression */ | |||
| 5780 | Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */ | |||
| 5781 | }; | |||
| 5782 | ||||
| 5783 | /* | |||
| 5784 | ** eType: | |||
| 5785 | ** Expression node type. Usually one of: | |||
| 5786 | ** | |||
| 5787 | ** FTS5_AND (nChild, apChild valid) | |||
| 5788 | ** FTS5_OR (nChild, apChild valid) | |||
| 5789 | ** FTS5_NOT (nChild, apChild valid) | |||
| 5790 | ** FTS5_STRING (pNear valid) | |||
| 5791 | ** FTS5_TERM (pNear valid) | |||
| 5792 | ** | |||
| 5793 | ** An expression node with eType==0 may also exist. It always matches zero | |||
| 5794 | ** rows. This is created when a phrase containing no tokens is parsed. | |||
| 5795 | ** e.g. "". | |||
| 5796 | ** | |||
| 5797 | ** iHeight: | |||
| 5798 | ** Distance from this node to furthest leaf. This is always 0 for nodes | |||
| 5799 | ** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one | |||
| 5800 | ** greater than the largest child value. | |||
| 5801 | */ | |||
| 5802 | struct Fts5ExprNode { | |||
| 5803 | int eType; /* Node type */ | |||
| 5804 | int bEof; /* True at EOF */ | |||
| 5805 | int bNomatch; /* True if entry is not a match */ | |||
| 5806 | int iHeight; /* Distance to tree leaf nodes */ | |||
| 5807 | ||||
| 5808 | /* Next method for this node. */ | |||
| 5809 | int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64); | |||
| 5810 | ||||
| 5811 | i64 iRowid; /* Current rowid */ | |||
| 5812 | Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ | |||
| 5813 | ||||
| 5814 | /* Child nodes. For a NOT node, this array always contains 2 entries. For | |||
| 5815 | ** AND or OR nodes, it contains 2 or more entries. */ | |||
| 5816 | int nChild; /* Number of child nodes */ | |||
| 5817 | Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */ | |||
| 5818 | }; | |||
| 5819 | ||||
| 5820 | /* Size (in bytes) of an Fts5ExprNode object that holds up to N children */ | |||
| 5821 | #define SZ_FTS5EXPRNODE(N)(__builtin_offsetof(Fts5ExprNode, apChild) + (N)*sizeof(Fts5ExprNode *)) \ | |||
| 5822 | (offsetof(Fts5ExprNode,apChild)__builtin_offsetof(Fts5ExprNode, apChild) + (N)*sizeof(Fts5ExprNode*)) | |||
| 5823 | ||||
| 5824 | #define Fts5NodeIsString(p)((p)->eType==4 || (p)->eType==9) ((p)->eType==FTS5_TERM4 || (p)->eType==FTS5_STRING9) | |||
| 5825 | ||||
| 5826 | /* | |||
| 5827 | ** Invoke the xNext method of an Fts5ExprNode object. This macro should be | |||
| 5828 | ** used as if it has the same signature as the xNext() methods themselves. | |||
| 5829 | */ | |||
| 5830 | #define fts5ExprNodeNext(a,b,c,d)(b)->xNext((a), (b), (c), (d)) (b)->xNext((a), (b), (c), (d)) | |||
| 5831 | ||||
| 5832 | /* | |||
| 5833 | ** An instance of the following structure represents a single search term | |||
| 5834 | ** or term prefix. | |||
| 5835 | */ | |||
| 5836 | struct Fts5ExprTerm { | |||
| 5837 | u8 bPrefix; /* True for a prefix term */ | |||
| 5838 | u8 bFirst; /* True if token must be first in column */ | |||
| 5839 | char *pTerm; /* Term data */ | |||
| 5840 | int nQueryTerm; /* Effective size of term in bytes */ | |||
| 5841 | int nFullTerm; /* Size of term in bytes incl. tokendata */ | |||
| 5842 | Fts5IndexIter *pIter; /* Iterator for this term */ | |||
| 5843 | Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */ | |||
| 5844 | }; | |||
| 5845 | ||||
| 5846 | /* | |||
| 5847 | ** A phrase. One or more terms that must appear in a contiguous sequence | |||
| 5848 | ** within a document for it to match. | |||
| 5849 | */ | |||
| 5850 | struct Fts5ExprPhrase { | |||
| 5851 | Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ | |||
| 5852 | Fts5Buffer poslist; /* Current position list */ | |||
| 5853 | int nTerm; /* Number of entries in aTerm[] */ | |||
| 5854 | Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */ | |||
| 5855 | }; | |||
| 5856 | ||||
| 5857 | /* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */ | |||
| 5858 | #define SZ_FTS5EXPRPHRASE(N)(__builtin_offsetof(Fts5ExprPhrase, aTerm) + (N)*sizeof(Fts5ExprTerm )) \ | |||
| 5859 | (offsetof(Fts5ExprPhrase,aTerm)__builtin_offsetof(Fts5ExprPhrase, aTerm) + (N)*sizeof(Fts5ExprTerm)) | |||
| 5860 | ||||
| 5861 | /* | |||
| 5862 | ** One or more phrases that must appear within a certain token distance of | |||
| 5863 | ** each other within each matching document. | |||
| 5864 | */ | |||
| 5865 | struct Fts5ExprNearset { | |||
| 5866 | int nNear; /* NEAR parameter */ | |||
| 5867 | Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ | |||
| 5868 | int nPhrase; /* Number of entries in aPhrase[] array */ | |||
| 5869 | Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */ | |||
| 5870 | }; | |||
| 5871 | ||||
| 5872 | /* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */ | |||
| 5873 | #define SZ_FTS5EXPRNEARSET(N)(__builtin_offsetof(Fts5ExprNearset, apPhrase)+(N)*sizeof(Fts5ExprPhrase *)) \ | |||
| 5874 | (offsetof(Fts5ExprNearset,apPhrase)__builtin_offsetof(Fts5ExprNearset, apPhrase)+(N)*sizeof(Fts5ExprPhrase*)) | |||
| 5875 | ||||
| 5876 | /* | |||
| 5877 | ** Parse context. | |||
| 5878 | */ | |||
| 5879 | struct Fts5Parse { | |||
| 5880 | Fts5Config *pConfig; | |||
| 5881 | char *zErr; | |||
| 5882 | int rc; | |||
| 5883 | int nPhrase; /* Size of apPhrase array */ | |||
| 5884 | Fts5ExprPhrase **apPhrase; /* Array of all phrases */ | |||
| 5885 | Fts5ExprNode *pExpr; /* Result of a successful parse */ | |||
| 5886 | int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ | |||
| 5887 | }; | |||
| 5888 | ||||
| 5889 | /* | |||
| 5890 | ** Check that the Fts5ExprNode.iHeight variables are set correctly in | |||
| 5891 | ** the expression tree passed as the only argument. | |||
| 5892 | */ | |||
| 5893 | #ifndef NDEBUG1 | |||
| 5894 | static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){ | |||
| 5895 | if( rc==SQLITE_OK0 ){ | |||
| 5896 | if( p->eType==FTS5_TERM4 || p->eType==FTS5_STRING9 || p->eType==0 ){ | |||
| 5897 | assert( p->iHeight==0 )((void) (0)); | |||
| 5898 | }else{ | |||
| 5899 | int ii; | |||
| 5900 | int iMaxChild = 0; | |||
| 5901 | for(ii=0; ii<p->nChild; ii++){ | |||
| 5902 | Fts5ExprNode *pChild = p->apChild[ii]; | |||
| 5903 | iMaxChild = MAX(iMaxChild, pChild->iHeight)(((iMaxChild) > (pChild->iHeight)) ? (iMaxChild) : (pChild ->iHeight)); | |||
| 5904 | assert_expr_depth_ok(SQLITE_OK, pChild); | |||
| 5905 | } | |||
| 5906 | assert( p->iHeight==iMaxChild+1 )((void) (0)); | |||
| 5907 | } | |||
| 5908 | } | |||
| 5909 | } | |||
| 5910 | #else | |||
| 5911 | # define assert_expr_depth_ok(rc, p) | |||
| 5912 | #endif | |||
| 5913 | ||||
| 5914 | static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ | |||
| 5915 | va_list ap; | |||
| 5916 | va_start(ap, zFmt)__builtin_va_start(ap, zFmt); | |||
| 5917 | if( pParse->rc==SQLITE_OK0 ){ | |||
| 5918 | assert( pParse->zErr==0 )((void) (0)); | |||
| 5919 | pParse->zErr = sqlite3_vmprintfsqlite3_api->vmprintf(zFmt, ap); | |||
| 5920 | pParse->rc = SQLITE_ERROR1; | |||
| 5921 | } | |||
| 5922 | va_end(ap)__builtin_va_end(ap); | |||
| 5923 | } | |||
| 5924 | ||||
| 5925 | static int fts5ExprIsspace(char t){ | |||
| 5926 | return t==' ' || t=='\t' || t=='\n' || t=='\r'; | |||
| 5927 | } | |||
| 5928 | ||||
| 5929 | /* | |||
| 5930 | ** Read the first token from the nul-terminated string at *pz. | |||
| 5931 | */ | |||
| 5932 | static int fts5ExprGetToken( | |||
| 5933 | Fts5Parse *pParse, | |||
| 5934 | const char **pz, /* IN/OUT: Pointer into buffer */ | |||
| 5935 | Fts5Token *pToken | |||
| 5936 | ){ | |||
| 5937 | const char *z = *pz; | |||
| 5938 | int tok; | |||
| 5939 | ||||
| 5940 | /* Skip past any whitespace */ | |||
| 5941 | while( fts5ExprIsspace(*z) ) z++; | |||
| 5942 | ||||
| 5943 | pToken->p = z; | |||
| 5944 | pToken->n = 1; | |||
| 5945 | switch( *z ){ | |||
| 5946 | case '(': tok = FTS5_LP10; break; | |||
| 5947 | case ')': tok = FTS5_RP11; break; | |||
| 5948 | case '{': tok = FTS5_LCP7; break; | |||
| 5949 | case '}': tok = FTS5_RCP8; break; | |||
| 5950 | case ':': tok = FTS5_COLON5; break; | |||
| 5951 | case ',': tok = FTS5_COMMA13; break; | |||
| 5952 | case '+': tok = FTS5_PLUS14; break; | |||
| 5953 | case '*': tok = FTS5_STAR15; break; | |||
| 5954 | case '-': tok = FTS5_MINUS6; break; | |||
| 5955 | case '^': tok = FTS5_CARET12; break; | |||
| 5956 | case '\0': tok = FTS5_EOF0; break; | |||
| 5957 | ||||
| 5958 | case '"': { | |||
| 5959 | const char *z2; | |||
| 5960 | tok = FTS5_STRING9; | |||
| 5961 | ||||
| 5962 | for(z2=&z[1]; 1; z2++){ | |||
| 5963 | if( z2[0]=='"' ){ | |||
| 5964 | z2++; | |||
| 5965 | if( z2[0]!='"' ) break; | |||
| 5966 | } | |||
| 5967 | if( z2[0]=='\0' ){ | |||
| 5968 | sqlite3Fts5ParseError(pParse, "unterminated string"); | |||
| 5969 | return FTS5_EOF0; | |||
| 5970 | } | |||
| 5971 | } | |||
| 5972 | pToken->n = (z2 - z); | |||
| 5973 | break; | |||
| 5974 | } | |||
| 5975 | ||||
| 5976 | default: { | |||
| 5977 | const char *z2; | |||
| 5978 | if( sqlite3Fts5IsBareword(z[0])==0 ){ | |||
| 5979 | sqlite3Fts5ParseError(pParse, "fts5: syntax error near \"%.1s\"", z); | |||
| 5980 | return FTS5_EOF0; | |||
| 5981 | } | |||
| 5982 | tok = FTS5_STRING9; | |||
| 5983 | for(z2=&z[1]; sqlite3Fts5IsBareword(*z2); z2++); | |||
| 5984 | pToken->n = (z2 - z); | |||
| 5985 | if( pToken->n==2 && memcmp(pToken->p, "OR", 2)==0 ) tok = FTS5_OR1; | |||
| 5986 | if( pToken->n==3 && memcmp(pToken->p, "NOT", 3)==0 ) tok = FTS5_NOT3; | |||
| 5987 | if( pToken->n==3 && memcmp(pToken->p, "AND", 3)==0 ) tok = FTS5_AND2; | |||
| 5988 | break; | |||
| 5989 | } | |||
| 5990 | } | |||
| 5991 | ||||
| 5992 | *pz = &pToken->p[pToken->n]; | |||
| 5993 | return tok; | |||
| 5994 | } | |||
| 5995 | ||||
| 5996 | static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc64sqlite3_api->malloc64((sqlite3_int64)t);} | |||
| 5997 | static void fts5ParseFree(void *p){ sqlite3_freesqlite3_api->free(p); } | |||
| 5998 | ||||
| 5999 | static int sqlite3Fts5ExprNew( | |||
| 6000 | Fts5Config *pConfig, /* FTS5 Configuration */ | |||
| 6001 | int bPhraseToAnd, | |||
| 6002 | int iCol, | |||
| 6003 | const char *zExpr, /* Expression text */ | |||
| 6004 | Fts5Expr **ppNew, | |||
| 6005 | char **pzErr | |||
| 6006 | ){ | |||
| 6007 | Fts5Parse sParse; | |||
| 6008 | Fts5Token token; | |||
| 6009 | const char *z = zExpr; | |||
| 6010 | int t; /* Next token type */ | |||
| 6011 | void *pEngine; | |||
| 6012 | Fts5Expr *pNew; | |||
| 6013 | ||||
| 6014 | *ppNew = 0; | |||
| 6015 | *pzErr = 0; | |||
| 6016 | memset(&sParse, 0, sizeof(sParse)); | |||
| 6017 | sParse.bPhraseToAnd = bPhraseToAnd; | |||
| 6018 | pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc); | |||
| 6019 | if( pEngine==0 ){ return SQLITE_NOMEM7; } | |||
| 6020 | sParse.pConfig = pConfig; | |||
| 6021 | ||||
| 6022 | do { | |||
| 6023 | t = fts5ExprGetToken(&sParse, &z, &token); | |||
| 6024 | sqlite3Fts5Parser(pEngine, t, token, &sParse); | |||
| 6025 | }while( sParse.rc==SQLITE_OK0 && t!=FTS5_EOF0 ); | |||
| 6026 | sqlite3Fts5ParserFree(pEngine, fts5ParseFree); | |||
| 6027 | ||||
| 6028 | assert( sParse.pExpr || sParse.rc!=SQLITE_OK )((void) (0)); | |||
| 6029 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); | |||
| 6030 | ||||
| 6031 | /* If the LHS of the MATCH expression was a user column, apply the | |||
| 6032 | ** implicit column-filter. */ | |||
| 6033 | if( sParse.rc==SQLITE_OK0 && iCol<pConfig->nCol ){ | |||
| 6034 | int n = SZ_FTS5COLSET(1)(sizeof(i64)*((1 +2)/2)); | |||
| 6035 | Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); | |||
| 6036 | if( pColset ){ | |||
| 6037 | pColset->nCol = 1; | |||
| 6038 | pColset->aiCol[0] = iCol; | |||
| 6039 | sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); | |||
| 6040 | } | |||
| 6041 | } | |||
| 6042 | ||||
| 6043 | assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 )((void) (0)); | |||
| 6044 | if( sParse.rc==SQLITE_OK0 ){ | |||
| 6045 | *ppNew = pNew = sqlite3_malloc64sqlite3_api->malloc64(sizeof(Fts5Expr)); | |||
| 6046 | if( pNew==0 ){ | |||
| 6047 | sParse.rc = SQLITE_NOMEM7; | |||
| 6048 | sqlite3Fts5ParseNodeFree(sParse.pExpr); | |||
| 6049 | }else{ | |||
| 6050 | pNew->pRoot = sParse.pExpr; | |||
| 6051 | pNew->pIndex = 0; | |||
| 6052 | pNew->pConfig = pConfig; | |||
| 6053 | pNew->apExprPhrase = sParse.apPhrase; | |||
| 6054 | pNew->nPhrase = sParse.nPhrase; | |||
| 6055 | pNew->bDesc = 0; | |||
| 6056 | sParse.apPhrase = 0; | |||
| 6057 | } | |||
| 6058 | }else{ | |||
| 6059 | sqlite3Fts5ParseNodeFree(sParse.pExpr); | |||
| 6060 | } | |||
| 6061 | ||||
| 6062 | sqlite3_freesqlite3_api->free(sParse.apPhrase); | |||
| 6063 | if( 0==*pzErr ){ | |||
| 6064 | *pzErr = sParse.zErr; | |||
| 6065 | }else{ | |||
| 6066 | sqlite3_freesqlite3_api->free(sParse.zErr); | |||
| 6067 | } | |||
| 6068 | return sParse.rc; | |||
| 6069 | } | |||
| 6070 | ||||
| 6071 | /* | |||
| 6072 | ** Assuming that buffer z is at least nByte bytes in size and contains a | |||
| 6073 | ** valid utf-8 string, return the number of characters in the string. | |||
| 6074 | */ | |||
| 6075 | static int fts5ExprCountChar(const char *z, int nByte){ | |||
| 6076 | int nRet = 0; | |||
| 6077 | int ii; | |||
| 6078 | for(ii=0; ii<nByte; ii++){ | |||
| 6079 | if( (z[ii] & 0xC0)!=0x80 ) nRet++; | |||
| 6080 | } | |||
| 6081 | return nRet; | |||
| 6082 | } | |||
| 6083 | ||||
| 6084 | /* | |||
| 6085 | ** This function is only called when using the special 'trigram' tokenizer. | |||
| 6086 | ** Argument zText contains the text of a LIKE or GLOB pattern matched | |||
| 6087 | ** against column iCol. This function creates and compiles an FTS5 MATCH | |||
| 6088 | ** expression that will match a superset of the rows matched by the LIKE or | |||
| 6089 | ** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error | |||
| 6090 | ** code. | |||
| 6091 | */ | |||
| 6092 | static int sqlite3Fts5ExprPattern( | |||
| 6093 | Fts5Config *pConfig, int bGlob, int iCol, const char *zText, Fts5Expr **pp | |||
| 6094 | ){ | |||
| 6095 | i64 nText = strlen(zText); | |||
| 6096 | char *zExpr = (char*)sqlite3_malloc64sqlite3_api->malloc64(nText*4 + 1); | |||
| 6097 | int rc = SQLITE_OK0; | |||
| 6098 | ||||
| 6099 | if( zExpr==0 ){ | |||
| 6100 | rc = SQLITE_NOMEM7; | |||
| 6101 | }else{ | |||
| 6102 | char aSpec[3]; | |||
| 6103 | int iOut = 0; | |||
| 6104 | int i = 0; | |||
| 6105 | int iFirst = 0; | |||
| 6106 | ||||
| 6107 | if( bGlob==0 ){ | |||
| 6108 | aSpec[0] = '_'; | |||
| 6109 | aSpec[1] = '%'; | |||
| 6110 | aSpec[2] = 0; | |||
| 6111 | }else{ | |||
| 6112 | aSpec[0] = '*'; | |||
| 6113 | aSpec[1] = '?'; | |||
| 6114 | aSpec[2] = '['; | |||
| 6115 | } | |||
| 6116 | ||||
| 6117 | while( i<=nText ){ | |||
| 6118 | if( i==nText | |||
| 6119 | || zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2] | |||
| 6120 | ){ | |||
| 6121 | ||||
| 6122 | if( fts5ExprCountChar(&zText[iFirst], i-iFirst)>=3 ){ | |||
| 6123 | int jj; | |||
| 6124 | zExpr[iOut++] = '"'; | |||
| 6125 | for(jj=iFirst; jj<i; jj++){ | |||
| 6126 | zExpr[iOut++] = zText[jj]; | |||
| 6127 | if( zText[jj]=='"' ) zExpr[iOut++] = '"'; | |||
| 6128 | } | |||
| 6129 | zExpr[iOut++] = '"'; | |||
| 6130 | zExpr[iOut++] = ' '; | |||
| 6131 | } | |||
| 6132 | if( zText[i]==aSpec[2] ){ | |||
| 6133 | i += 2; | |||
| 6134 | if( zText[i-1]=='^' ) i++; | |||
| 6135 | while( i<nText && zText[i]!=']' ) i++; | |||
| 6136 | } | |||
| 6137 | iFirst = i+1; | |||
| 6138 | } | |||
| 6139 | i++; | |||
| 6140 | } | |||
| 6141 | if( iOut>0 ){ | |||
| 6142 | int bAnd = 0; | |||
| 6143 | if( pConfig->eDetail!=FTS5_DETAIL_FULL0 ){ | |||
| 6144 | bAnd = 1; | |||
| 6145 | if( pConfig->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 6146 | iCol = pConfig->nCol; | |||
| 6147 | } | |||
| 6148 | } | |||
| 6149 | zExpr[iOut] = '\0'; | |||
| 6150 | rc = sqlite3Fts5ExprNew(pConfig, bAnd, iCol, zExpr, pp,pConfig->pzErrmsg); | |||
| 6151 | }else{ | |||
| 6152 | *pp = 0; | |||
| 6153 | } | |||
| 6154 | sqlite3_freesqlite3_api->free(zExpr); | |||
| 6155 | } | |||
| 6156 | ||||
| 6157 | return rc; | |||
| 6158 | } | |||
| 6159 | ||||
| 6160 | /* | |||
| 6161 | ** Free the expression node object passed as the only argument. | |||
| 6162 | */ | |||
| 6163 | static void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){ | |||
| 6164 | if( p ){ | |||
| 6165 | int i; | |||
| 6166 | for(i=0; i<p->nChild; i++){ | |||
| 6167 | sqlite3Fts5ParseNodeFree(p->apChild[i]); | |||
| 6168 | } | |||
| 6169 | sqlite3Fts5ParseNearsetFree(p->pNear); | |||
| 6170 | sqlite3_freesqlite3_api->free(p); | |||
| 6171 | } | |||
| 6172 | } | |||
| 6173 | ||||
| 6174 | /* | |||
| 6175 | ** Free the expression object passed as the only argument. | |||
| 6176 | */ | |||
| 6177 | static void sqlite3Fts5ExprFree(Fts5Expr *p){ | |||
| 6178 | if( p ){ | |||
| 6179 | sqlite3Fts5ParseNodeFree(p->pRoot); | |||
| 6180 | sqlite3_freesqlite3_api->free(p->apExprPhrase); | |||
| 6181 | sqlite3_freesqlite3_api->free(p); | |||
| 6182 | } | |||
| 6183 | } | |||
| 6184 | ||||
| 6185 | static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ | |||
| 6186 | Fts5Parse sParse; | |||
| 6187 | memset(&sParse, 0, sizeof(sParse)); | |||
| 6188 | ||||
| 6189 | if( *pp1 && p2 ){ | |||
| 6190 | Fts5Expr *p1 = *pp1; | |||
| 6191 | int nPhrase = p1->nPhrase + p2->nPhrase; | |||
| 6192 | ||||
| 6193 | p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND2, p1->pRoot, p2->pRoot,0); | |||
| 6194 | p2->pRoot = 0; | |||
| 6195 | ||||
| 6196 | if( sParse.rc==SQLITE_OK0 ){ | |||
| 6197 | Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc64sqlite3_api->realloc64( | |||
| 6198 | p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*) | |||
| 6199 | ); | |||
| 6200 | if( ap==0 ){ | |||
| 6201 | sParse.rc = SQLITE_NOMEM7; | |||
| 6202 | }else{ | |||
| 6203 | int i; | |||
| 6204 | memmove(&ap[p2->nPhrase], ap, p1->nPhrase*sizeof(Fts5ExprPhrase*)); | |||
| 6205 | for(i=0; i<p2->nPhrase; i++){ | |||
| 6206 | ap[i] = p2->apExprPhrase[i]; | |||
| 6207 | } | |||
| 6208 | p1->nPhrase = nPhrase; | |||
| 6209 | p1->apExprPhrase = ap; | |||
| 6210 | } | |||
| 6211 | } | |||
| 6212 | sqlite3_freesqlite3_api->free(p2->apExprPhrase); | |||
| 6213 | sqlite3_freesqlite3_api->free(p2); | |||
| 6214 | }else if( p2 ){ | |||
| 6215 | *pp1 = p2; | |||
| 6216 | } | |||
| 6217 | ||||
| 6218 | return sParse.rc; | |||
| 6219 | } | |||
| 6220 | ||||
| 6221 | /* | |||
| 6222 | ** Argument pTerm must be a synonym iterator. Return the current rowid | |||
| 6223 | ** that it points to. | |||
| 6224 | */ | |||
| 6225 | static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){ | |||
| 6226 | i64 iRet = 0; | |||
| 6227 | int bRetValid = 0; | |||
| 6228 | Fts5ExprTerm *p; | |||
| 6229 | ||||
| 6230 | assert( pTerm )((void) (0)); | |||
| 6231 | assert( pTerm->pSynonym )((void) (0)); | |||
| 6232 | assert( bDesc==0 || bDesc==1 )((void) (0)); | |||
| 6233 | for(p=pTerm; p; p=p->pSynonym){ | |||
| 6234 | if( 0==sqlite3Fts5IterEof(p->pIter)((p->pIter)->bEof) ){ | |||
| 6235 | i64 iRowid = p->pIter->iRowid; | |||
| 6236 | if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){ | |||
| 6237 | iRet = iRowid; | |||
| 6238 | bRetValid = 1; | |||
| 6239 | } | |||
| 6240 | } | |||
| 6241 | } | |||
| 6242 | ||||
| 6243 | if( pbEof && bRetValid==0 ) *pbEof = 1; | |||
| 6244 | return iRet; | |||
| 6245 | } | |||
| 6246 | ||||
| 6247 | /* | |||
| 6248 | ** Argument pTerm must be a synonym iterator. | |||
| 6249 | */ | |||
| 6250 | static int fts5ExprSynonymList( | |||
| 6251 | Fts5ExprTerm *pTerm, | |||
| 6252 | i64 iRowid, | |||
| 6253 | Fts5Buffer *pBuf, /* Use this buffer for space if required */ | |||
| 6254 | u8 **pa, int *pn | |||
| 6255 | ){ | |||
| 6256 | Fts5PoslistReader aStatic[4]; | |||
| 6257 | Fts5PoslistReader *aIter = aStatic; | |||
| 6258 | int nIter = 0; | |||
| 6259 | int nAlloc = 4; | |||
| 6260 | int rc = SQLITE_OK0; | |||
| 6261 | Fts5ExprTerm *p; | |||
| 6262 | ||||
| 6263 | assert( pTerm->pSynonym )((void) (0)); | |||
| 6264 | for(p=pTerm; p; p=p->pSynonym){ | |||
| 6265 | Fts5IndexIter *pIter = p->pIter; | |||
| 6266 | if( sqlite3Fts5IterEof(pIter)((pIter)->bEof)==0 && pIter->iRowid==iRowid ){ | |||
| 6267 | if( pIter->nData==0 ) continue; | |||
| 6268 | if( nIter==nAlloc ){ | |||
| 6269 | sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; | |||
| 6270 | Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 6271 | if( aNew==0 ){ | |||
| 6272 | rc = SQLITE_NOMEM7; | |||
| 6273 | goto synonym_poslist_out; | |||
| 6274 | } | |||
| 6275 | memcpy(aNew, aIter, sizeof(Fts5PoslistReader) * nIter); | |||
| 6276 | nAlloc = nAlloc*2; | |||
| 6277 | if( aIter!=aStatic ) sqlite3_freesqlite3_api->free(aIter); | |||
| 6278 | aIter = aNew; | |||
| 6279 | } | |||
| 6280 | sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &aIter[nIter]); | |||
| 6281 | assert( aIter[nIter].bEof==0 )((void) (0)); | |||
| 6282 | nIter++; | |||
| 6283 | } | |||
| 6284 | } | |||
| 6285 | ||||
| 6286 | if( nIter==1 ){ | |||
| 6287 | *pa = (u8*)aIter[0].a; | |||
| 6288 | *pn = aIter[0].n; | |||
| 6289 | }else{ | |||
| 6290 | Fts5PoslistWriter writer = {0}; | |||
| 6291 | i64 iPrev = -1; | |||
| 6292 | fts5BufferZero(pBuf)sqlite3Fts5BufferZero(pBuf); | |||
| 6293 | while( 1 ){ | |||
| 6294 | int i; | |||
| 6295 | i64 iMin = FTS5_LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)); | |||
| 6296 | for(i=0; i<nIter; i++){ | |||
| 6297 | if( aIter[i].bEof==0 ){ | |||
| 6298 | if( aIter[i].iPos==iPrev ){ | |||
| 6299 | if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) continue; | |||
| 6300 | } | |||
| 6301 | if( aIter[i].iPos<iMin ){ | |||
| 6302 | iMin = aIter[i].iPos; | |||
| 6303 | } | |||
| 6304 | } | |||
| 6305 | } | |||
| 6306 | if( iMin==FTS5_LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)) || rc!=SQLITE_OK0 ) break; | |||
| 6307 | rc = sqlite3Fts5PoslistWriterAppend(pBuf, &writer, iMin); | |||
| 6308 | iPrev = iMin; | |||
| 6309 | } | |||
| 6310 | if( rc==SQLITE_OK0 ){ | |||
| 6311 | *pa = pBuf->p; | |||
| 6312 | *pn = pBuf->n; | |||
| 6313 | } | |||
| 6314 | } | |||
| 6315 | ||||
| 6316 | synonym_poslist_out: | |||
| 6317 | if( aIter!=aStatic ) sqlite3_freesqlite3_api->free(aIter); | |||
| 6318 | return rc; | |||
| 6319 | } | |||
| 6320 | ||||
| 6321 | ||||
| 6322 | /* | |||
| 6323 | ** All individual term iterators in pPhrase are guaranteed to be valid and | |||
| 6324 | ** pointing to the same rowid when this function is called. This function | |||
| 6325 | ** checks if the current rowid really is a match, and if so populates | |||
| 6326 | ** the pPhrase->poslist buffer accordingly. Output parameter *pbMatch | |||
| 6327 | ** is set to true if this is really a match, or false otherwise. | |||
| 6328 | ** | |||
| 6329 | ** SQLITE_OK is returned if an error occurs, or an SQLite error code | |||
| 6330 | ** otherwise. It is not considered an error code if the current rowid is | |||
| 6331 | ** not a match. | |||
| 6332 | */ | |||
| 6333 | static int fts5ExprPhraseIsMatch( | |||
| 6334 | Fts5ExprNode *pNode, /* Node pPhrase belongs to */ | |||
| 6335 | Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */ | |||
| 6336 | int *pbMatch /* OUT: Set to true if really a match */ | |||
| 6337 | ){ | |||
| 6338 | Fts5PoslistWriter writer = {0}; | |||
| 6339 | Fts5PoslistReader aStatic[4]; | |||
| 6340 | Fts5PoslistReader *aIter = aStatic; | |||
| 6341 | int i; | |||
| 6342 | int rc = SQLITE_OK0; | |||
| 6343 | int bFirst = pPhrase->aTerm[0].bFirst; | |||
| 6344 | ||||
| 6345 | fts5BufferZero(&pPhrase->poslist)sqlite3Fts5BufferZero(&pPhrase->poslist); | |||
| 6346 | ||||
| 6347 | /* If the aStatic[] array is not large enough, allocate a large array | |||
| 6348 | ** using sqlite3_malloc(). This approach could be improved upon. */ | |||
| 6349 | if( pPhrase->nTerm>ArraySize(aStatic)((int)(sizeof(aStatic) / sizeof(aStatic[0]))) ){ | |||
| 6350 | sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm; | |||
| 6351 | aIter = (Fts5PoslistReader*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 6352 | if( !aIter ) return SQLITE_NOMEM7; | |||
| 6353 | } | |||
| 6354 | memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm); | |||
| 6355 | ||||
| 6356 | /* Initialize a term iterator for each term in the phrase */ | |||
| 6357 | for(i=0; i<pPhrase->nTerm; i++){ | |||
| 6358 | Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; | |||
| 6359 | int n = 0; | |||
| 6360 | int bFlag = 0; | |||
| 6361 | u8 *a = 0; | |||
| 6362 | if( pTerm->pSynonym ){ | |||
| 6363 | Fts5Buffer buf = {0, 0, 0}; | |||
| 6364 | rc = fts5ExprSynonymList(pTerm, pNode->iRowid, &buf, &a, &n); | |||
| 6365 | if( rc ){ | |||
| 6366 | sqlite3_freesqlite3_api->free(a); | |||
| 6367 | goto ismatch_out; | |||
| 6368 | } | |||
| 6369 | if( a==buf.p ) bFlag = 1; | |||
| 6370 | }else{ | |||
| 6371 | a = (u8*)pTerm->pIter->pData; | |||
| 6372 | n = pTerm->pIter->nData; | |||
| 6373 | } | |||
| 6374 | sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); | |||
| 6375 | aIter[i].bFlag = (u8)bFlag; | |||
| 6376 | if( aIter[i].bEof ) goto ismatch_out; | |||
| 6377 | } | |||
| 6378 | ||||
| 6379 | while( 1 ){ | |||
| 6380 | int bMatch; | |||
| 6381 | i64 iPos = aIter[0].iPos; | |||
| 6382 | do { | |||
| 6383 | bMatch = 1; | |||
| 6384 | for(i=0; i<pPhrase->nTerm; i++){ | |||
| 6385 | Fts5PoslistReader *pPos = &aIter[i]; | |||
| 6386 | i64 iAdj = iPos + i; | |||
| 6387 | if( pPos->iPos!=iAdj ){ | |||
| 6388 | bMatch = 0; | |||
| 6389 | while( pPos->iPos<iAdj ){ | |||
| 6390 | if( sqlite3Fts5PoslistReaderNext(pPos) ) goto ismatch_out; | |||
| 6391 | } | |||
| 6392 | if( pPos->iPos>iAdj ) iPos = pPos->iPos-i; | |||
| 6393 | } | |||
| 6394 | } | |||
| 6395 | }while( bMatch==0 ); | |||
| 6396 | ||||
| 6397 | /* Append position iPos to the output */ | |||
| 6398 | if( bFirst==0 || FTS5_POS2OFFSET(iPos)(int)(iPos & 0x7FFFFFFF)==0 ){ | |||
| 6399 | rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos); | |||
| 6400 | if( rc!=SQLITE_OK0 ) goto ismatch_out; | |||
| 6401 | } | |||
| 6402 | ||||
| 6403 | for(i=0; i<pPhrase->nTerm; i++){ | |||
| 6404 | if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out; | |||
| 6405 | } | |||
| 6406 | } | |||
| 6407 | ||||
| 6408 | ismatch_out: | |||
| 6409 | *pbMatch = (pPhrase->poslist.n>0); | |||
| 6410 | for(i=0; i<pPhrase->nTerm; i++){ | |||
| 6411 | if( aIter[i].bFlag ) sqlite3_freesqlite3_api->free((u8*)aIter[i].a); | |||
| 6412 | } | |||
| 6413 | if( aIter!=aStatic ) sqlite3_freesqlite3_api->free(aIter); | |||
| 6414 | return rc; | |||
| 6415 | } | |||
| 6416 | ||||
| 6417 | typedef struct Fts5LookaheadReader Fts5LookaheadReader; | |||
| 6418 | struct Fts5LookaheadReader { | |||
| 6419 | const u8 *a; /* Buffer containing position list */ | |||
| 6420 | int n; /* Size of buffer a[] in bytes */ | |||
| 6421 | int i; /* Current offset in position list */ | |||
| 6422 | i64 iPos; /* Current position */ | |||
| 6423 | i64 iLookahead; /* Next position */ | |||
| 6424 | }; | |||
| 6425 | ||||
| 6426 | #define FTS5_LOOKAHEAD_EOF(((i64)1) << 62) (((i64)1) << 62) | |||
| 6427 | ||||
| 6428 | static int fts5LookaheadReaderNext(Fts5LookaheadReader *p){ | |||
| 6429 | p->iPos = p->iLookahead; | |||
| 6430 | if( sqlite3Fts5PoslistNext64(p->a, p->n, &p->i, &p->iLookahead) ){ | |||
| 6431 | p->iLookahead = FTS5_LOOKAHEAD_EOF(((i64)1) << 62); | |||
| 6432 | } | |||
| 6433 | return (p->iPos==FTS5_LOOKAHEAD_EOF(((i64)1) << 62)); | |||
| 6434 | } | |||
| 6435 | ||||
| 6436 | static int fts5LookaheadReaderInit( | |||
| 6437 | const u8 *a, int n, /* Buffer to read position list from */ | |||
| 6438 | Fts5LookaheadReader *p /* Iterator object to initialize */ | |||
| 6439 | ){ | |||
| 6440 | memset(p, 0, sizeof(Fts5LookaheadReader)); | |||
| 6441 | p->a = a; | |||
| 6442 | p->n = n; | |||
| 6443 | fts5LookaheadReaderNext(p); | |||
| 6444 | return fts5LookaheadReaderNext(p); | |||
| 6445 | } | |||
| 6446 | ||||
| 6447 | typedef struct Fts5NearTrimmer Fts5NearTrimmer; | |||
| 6448 | struct Fts5NearTrimmer { | |||
| 6449 | Fts5LookaheadReader reader; /* Input iterator */ | |||
| 6450 | Fts5PoslistWriter writer; /* Writer context */ | |||
| 6451 | Fts5Buffer *pOut; /* Output poslist */ | |||
| 6452 | }; | |||
| 6453 | ||||
| 6454 | /* | |||
| 6455 | ** The near-set object passed as the first argument contains more than | |||
| 6456 | ** one phrase. All phrases currently point to the same row. The | |||
| 6457 | ** Fts5ExprPhrase.poslist buffers are populated accordingly. This function | |||
| 6458 | ** tests if the current row contains instances of each phrase sufficiently | |||
| 6459 | ** close together to meet the NEAR constraint. Non-zero is returned if it | |||
| 6460 | ** does, or zero otherwise. | |||
| 6461 | ** | |||
| 6462 | ** If in/out parameter (*pRc) is set to other than SQLITE_OK when this | |||
| 6463 | ** function is called, it is a no-op. Or, if an error (e.g. SQLITE_NOMEM) | |||
| 6464 | ** occurs within this function (*pRc) is set accordingly before returning. | |||
| 6465 | ** The return value is undefined in both these cases. | |||
| 6466 | ** | |||
| 6467 | ** If no error occurs and non-zero (a match) is returned, the position-list | |||
| 6468 | ** of each phrase object is edited to contain only those entries that | |||
| 6469 | ** meet the constraint before returning. | |||
| 6470 | */ | |||
| 6471 | static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){ | |||
| 6472 | Fts5NearTrimmer aStatic[4]; | |||
| 6473 | Fts5NearTrimmer *a = aStatic; | |||
| 6474 | Fts5ExprPhrase **apPhrase = pNear->apPhrase; | |||
| 6475 | ||||
| 6476 | int i; | |||
| 6477 | int rc = *pRc; | |||
| 6478 | int bMatch; | |||
| 6479 | ||||
| 6480 | assert( pNear->nPhrase>1 )((void) (0)); | |||
| 6481 | ||||
| 6482 | /* If the aStatic[] array is not large enough, allocate a large array | |||
| 6483 | ** using sqlite3_malloc(). This approach could be improved upon. */ | |||
| 6484 | if( pNear->nPhrase>ArraySize(aStatic)((int)(sizeof(aStatic) / sizeof(aStatic[0]))) ){ | |||
| 6485 | sqlite3_int64 nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase; | |||
| 6486 | a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 6487 | }else{ | |||
| 6488 | memset(aStatic, 0, sizeof(aStatic)); | |||
| 6489 | } | |||
| 6490 | if( rc!=SQLITE_OK0 ){ | |||
| 6491 | *pRc = rc; | |||
| 6492 | return 0; | |||
| 6493 | } | |||
| 6494 | ||||
| 6495 | /* Initialize a lookahead iterator for each phrase. After passing the | |||
| 6496 | ** buffer and buffer size to the lookaside-reader init function, zero | |||
| 6497 | ** the phrase poslist buffer. The new poslist for the phrase (containing | |||
| 6498 | ** the same entries as the original with some entries removed on account | |||
| 6499 | ** of the NEAR constraint) is written over the original even as it is | |||
| 6500 | ** being read. This is safe as the entries for the new poslist are a | |||
| 6501 | ** subset of the old, so it is not possible for data yet to be read to | |||
| 6502 | ** be overwritten. */ | |||
| 6503 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 6504 | Fts5Buffer *pPoslist = &apPhrase[i]->poslist; | |||
| 6505 | fts5LookaheadReaderInit(pPoslist->p, pPoslist->n, &a[i].reader); | |||
| 6506 | pPoslist->n = 0; | |||
| 6507 | a[i].pOut = pPoslist; | |||
| 6508 | } | |||
| 6509 | ||||
| 6510 | while( 1 ){ | |||
| 6511 | int iAdv; | |||
| 6512 | i64 iMin; | |||
| 6513 | i64 iMax; | |||
| 6514 | ||||
| 6515 | /* This block advances the phrase iterators until they point to a set of | |||
| 6516 | ** entries that together comprise a match. */ | |||
| 6517 | iMax = a[0].reader.iPos; | |||
| 6518 | do { | |||
| 6519 | bMatch = 1; | |||
| 6520 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 6521 | Fts5LookaheadReader *pPos = &a[i].reader; | |||
| 6522 | iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear; | |||
| 6523 | if( pPos->iPos<iMin || pPos->iPos>iMax ){ | |||
| 6524 | bMatch = 0; | |||
| 6525 | while( pPos->iPos<iMin ){ | |||
| 6526 | if( fts5LookaheadReaderNext(pPos) ) goto ismatch_out; | |||
| 6527 | } | |||
| 6528 | if( pPos->iPos>iMax ) iMax = pPos->iPos; | |||
| 6529 | } | |||
| 6530 | } | |||
| 6531 | }while( bMatch==0 ); | |||
| 6532 | ||||
| 6533 | /* Add an entry to each output position list */ | |||
| 6534 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 6535 | i64 iPos = a[i].reader.iPos; | |||
| 6536 | Fts5PoslistWriter *pWriter = &a[i].writer; | |||
| 6537 | if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){ | |||
| 6538 | sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos); | |||
| 6539 | } | |||
| 6540 | } | |||
| 6541 | ||||
| 6542 | iAdv = 0; | |||
| 6543 | iMin = a[0].reader.iLookahead; | |||
| 6544 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 6545 | if( a[i].reader.iLookahead < iMin ){ | |||
| 6546 | iMin = a[i].reader.iLookahead; | |||
| 6547 | iAdv = i; | |||
| 6548 | } | |||
| 6549 | } | |||
| 6550 | if( fts5LookaheadReaderNext(&a[iAdv].reader) ) goto ismatch_out; | |||
| 6551 | } | |||
| 6552 | ||||
| 6553 | ismatch_out: { | |||
| 6554 | int bRet = a[0].pOut->n>0; | |||
| 6555 | *pRc = rc; | |||
| 6556 | if( a!=aStatic ) sqlite3_freesqlite3_api->free(a); | |||
| 6557 | return bRet; | |||
| 6558 | } | |||
| 6559 | } | |||
| 6560 | ||||
| 6561 | /* | |||
| 6562 | ** Advance iterator pIter until it points to a value equal to or laster | |||
| 6563 | ** than the initial value of *piLast. If this means the iterator points | |||
| 6564 | ** to a value laster than *piLast, update *piLast to the new lastest value. | |||
| 6565 | ** | |||
| 6566 | ** If the iterator reaches EOF, set *pbEof to true before returning. If | |||
| 6567 | ** an error occurs, set *pRc to an error code. If either *pbEof or *pRc | |||
| 6568 | ** are set, return a non-zero value. Otherwise, return zero. | |||
| 6569 | */ | |||
| 6570 | static int fts5ExprAdvanceto( | |||
| 6571 | Fts5IndexIter *pIter, /* Iterator to advance */ | |||
| 6572 | int bDesc, /* True if iterator is "rowid DESC" */ | |||
| 6573 | i64 *piLast, /* IN/OUT: Lastest rowid seen so far */ | |||
| 6574 | int *pRc, /* OUT: Error code */ | |||
| 6575 | int *pbEof /* OUT: Set to true if EOF */ | |||
| 6576 | ){ | |||
| 6577 | i64 iLast = *piLast; | |||
| 6578 | i64 iRowid; | |||
| 6579 | ||||
| 6580 | iRowid = pIter->iRowid; | |||
| 6581 | if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){ | |||
| 6582 | int rc = sqlite3Fts5IterNextFrom(pIter, iLast); | |||
| 6583 | if( rc || sqlite3Fts5IterEof(pIter)((pIter)->bEof) ){ | |||
| 6584 | *pRc = rc; | |||
| 6585 | *pbEof = 1; | |||
| 6586 | return 1; | |||
| 6587 | } | |||
| 6588 | iRowid = pIter->iRowid; | |||
| 6589 | assert( (bDesc==0 && iRowid>=iLast) || (bDesc==1 && iRowid<=iLast) )((void) (0)); | |||
| 6590 | } | |||
| 6591 | *piLast = iRowid; | |||
| 6592 | ||||
| 6593 | return 0; | |||
| 6594 | } | |||
| 6595 | ||||
| 6596 | static int fts5ExprSynonymAdvanceto( | |||
| 6597 | Fts5ExprTerm *pTerm, /* Term iterator to advance */ | |||
| 6598 | int bDesc, /* True if iterator is "rowid DESC" */ | |||
| 6599 | i64 *piLast, /* IN/OUT: Lastest rowid seen so far */ | |||
| 6600 | int *pRc /* OUT: Error code */ | |||
| 6601 | ){ | |||
| 6602 | int rc = SQLITE_OK0; | |||
| 6603 | i64 iLast = *piLast; | |||
| 6604 | Fts5ExprTerm *p; | |||
| 6605 | int bEof = 0; | |||
| 6606 | ||||
| 6607 | for(p=pTerm; rc==SQLITE_OK0 && p; p=p->pSynonym){ | |||
| 6608 | if( sqlite3Fts5IterEof(p->pIter)((p->pIter)->bEof)==0 ){ | |||
| 6609 | i64 iRowid = p->pIter->iRowid; | |||
| 6610 | if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){ | |||
| 6611 | rc = sqlite3Fts5IterNextFrom(p->pIter, iLast); | |||
| 6612 | } | |||
| 6613 | } | |||
| 6614 | } | |||
| 6615 | ||||
| 6616 | if( rc!=SQLITE_OK0 ){ | |||
| 6617 | *pRc = rc; | |||
| 6618 | bEof = 1; | |||
| 6619 | }else{ | |||
| 6620 | *piLast = fts5ExprSynonymRowid(pTerm, bDesc, &bEof); | |||
| 6621 | } | |||
| 6622 | return bEof; | |||
| 6623 | } | |||
| 6624 | ||||
| 6625 | ||||
| 6626 | static int fts5ExprNearTest( | |||
| 6627 | int *pRc, | |||
| 6628 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ | |||
| 6629 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_STRING) */ | |||
| 6630 | ){ | |||
| 6631 | Fts5ExprNearset *pNear = pNode->pNear; | |||
| 6632 | int rc = *pRc; | |||
| 6633 | ||||
| 6634 | if( pExpr->pConfig->eDetail!=FTS5_DETAIL_FULL0 ){ | |||
| 6635 | Fts5ExprTerm *pTerm; | |||
| 6636 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; | |||
| 6637 | pPhrase->poslist.n = 0; | |||
| 6638 | for(pTerm=&pPhrase->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ | |||
| 6639 | Fts5IndexIter *pIter = pTerm->pIter; | |||
| 6640 | if( sqlite3Fts5IterEof(pIter)((pIter)->bEof)==0 ){ | |||
| 6641 | if( pIter->iRowid==pNode->iRowid && pIter->nData>0 ){ | |||
| 6642 | pPhrase->poslist.n = 1; | |||
| 6643 | } | |||
| 6644 | } | |||
| 6645 | } | |||
| 6646 | return pPhrase->poslist.n; | |||
| 6647 | }else{ | |||
| 6648 | int i; | |||
| 6649 | ||||
| 6650 | /* Check that each phrase in the nearset matches the current row. | |||
| 6651 | ** Populate the pPhrase->poslist buffers at the same time. If any | |||
| 6652 | ** phrase is not a match, break out of the loop early. */ | |||
| 6653 | for(i=0; rc==SQLITE_OK0 && i<pNear->nPhrase; i++){ | |||
| 6654 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |||
| 6655 | if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym | |||
| 6656 | || pNear->pColset || pPhrase->aTerm[0].bFirst | |||
| 6657 | ){ | |||
| 6658 | int bMatch = 0; | |||
| 6659 | rc = fts5ExprPhraseIsMatch(pNode, pPhrase, &bMatch); | |||
| 6660 | if( bMatch==0 ) break; | |||
| 6661 | }else{ | |||
| 6662 | Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter; | |||
| 6663 | fts5BufferSet(&rc, &pPhrase->poslist, pIter->nData, pIter->pData)sqlite3Fts5BufferSet(&rc,&pPhrase->poslist,pIter-> nData,pIter->pData); | |||
| 6664 | } | |||
| 6665 | } | |||
| 6666 | ||||
| 6667 | *pRc = rc; | |||
| 6668 | if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){ | |||
| 6669 | return 1; | |||
| 6670 | } | |||
| 6671 | return 0; | |||
| 6672 | } | |||
| 6673 | } | |||
| 6674 | ||||
| 6675 | ||||
| 6676 | /* | |||
| 6677 | ** Initialize all term iterators in the pNear object. If any term is found | |||
| 6678 | ** to match no documents at all, return immediately without initializing any | |||
| 6679 | ** further iterators. | |||
| 6680 | ** | |||
| 6681 | ** If an error occurs, return an SQLite error code. Otherwise, return | |||
| 6682 | ** SQLITE_OK. It is not considered an error if some term matches zero | |||
| 6683 | ** documents. | |||
| 6684 | */ | |||
| 6685 | static int fts5ExprNearInitAll( | |||
| 6686 | Fts5Expr *pExpr, | |||
| 6687 | Fts5ExprNode *pNode | |||
| 6688 | ){ | |||
| 6689 | Fts5ExprNearset *pNear = pNode->pNear; | |||
| 6690 | int i; | |||
| 6691 | ||||
| 6692 | assert( pNode->bNomatch==0 )((void) (0)); | |||
| 6693 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 6694 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |||
| 6695 | if( pPhrase->nTerm==0 ){ | |||
| 6696 | pNode->bEof = 1; | |||
| 6697 | return SQLITE_OK0; | |||
| 6698 | }else{ | |||
| 6699 | int j; | |||
| 6700 | for(j=0; j<pPhrase->nTerm; j++){ | |||
| 6701 | Fts5ExprTerm *pTerm = &pPhrase->aTerm[j]; | |||
| 6702 | Fts5ExprTerm *p; | |||
| 6703 | int bHit = 0; | |||
| 6704 | ||||
| 6705 | for(p=pTerm; p; p=p->pSynonym){ | |||
| 6706 | int rc; | |||
| 6707 | if( p->pIter ){ | |||
| 6708 | sqlite3Fts5IterClose(p->pIter); | |||
| 6709 | p->pIter = 0; | |||
| 6710 | } | |||
| 6711 | rc = sqlite3Fts5IndexQuery( | |||
| 6712 | pExpr->pIndex, p->pTerm, p->nQueryTerm, | |||
| 6713 | (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX0x0001 : 0) | | |||
| 6714 | (pExpr->bDesc ? FTS5INDEX_QUERY_DESC0x0002 : 0), | |||
| 6715 | pNear->pColset, | |||
| 6716 | &p->pIter | |||
| 6717 | ); | |||
| 6718 | assert( (rc==SQLITE_OK)==(p->pIter!=0) )((void) (0)); | |||
| 6719 | if( rc!=SQLITE_OK0 ) return rc; | |||
| 6720 | if( 0==sqlite3Fts5IterEof(p->pIter)((p->pIter)->bEof) ){ | |||
| 6721 | bHit = 1; | |||
| 6722 | } | |||
| 6723 | } | |||
| 6724 | ||||
| 6725 | if( bHit==0 ){ | |||
| 6726 | pNode->bEof = 1; | |||
| 6727 | return SQLITE_OK0; | |||
| 6728 | } | |||
| 6729 | } | |||
| 6730 | } | |||
| 6731 | } | |||
| 6732 | ||||
| 6733 | pNode->bEof = 0; | |||
| 6734 | return SQLITE_OK0; | |||
| 6735 | } | |||
| 6736 | ||||
| 6737 | /* | |||
| 6738 | ** If pExpr is an ASC iterator, this function returns a value with the | |||
| 6739 | ** same sign as: | |||
| 6740 | ** | |||
| 6741 | ** (iLhs - iRhs) | |||
| 6742 | ** | |||
| 6743 | ** Otherwise, if this is a DESC iterator, the opposite is returned: | |||
| 6744 | ** | |||
| 6745 | ** (iRhs - iLhs) | |||
| 6746 | */ | |||
| 6747 | static int fts5RowidCmp( | |||
| 6748 | Fts5Expr *pExpr, | |||
| 6749 | i64 iLhs, | |||
| 6750 | i64 iRhs | |||
| 6751 | ){ | |||
| 6752 | assert( pExpr->bDesc==0 || pExpr->bDesc==1 )((void) (0)); | |||
| 6753 | if( pExpr->bDesc==0 ){ | |||
| 6754 | if( iLhs<iRhs ) return -1; | |||
| 6755 | return (iLhs > iRhs); | |||
| 6756 | }else{ | |||
| 6757 | if( iLhs>iRhs ) return -1; | |||
| 6758 | return (iLhs < iRhs); | |||
| 6759 | } | |||
| 6760 | } | |||
| 6761 | ||||
| 6762 | static void fts5ExprSetEof(Fts5ExprNode *pNode){ | |||
| 6763 | int i; | |||
| 6764 | pNode->bEof = 1; | |||
| 6765 | pNode->bNomatch = 0; | |||
| 6766 | for(i=0; i<pNode->nChild; i++){ | |||
| 6767 | fts5ExprSetEof(pNode->apChild[i]); | |||
| 6768 | } | |||
| 6769 | } | |||
| 6770 | ||||
| 6771 | static void fts5ExprNodeZeroPoslist(Fts5ExprNode *pNode){ | |||
| 6772 | if( pNode->eType==FTS5_STRING9 || pNode->eType==FTS5_TERM4 ){ | |||
| 6773 | Fts5ExprNearset *pNear = pNode->pNear; | |||
| 6774 | int i; | |||
| 6775 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 6776 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |||
| 6777 | pPhrase->poslist.n = 0; | |||
| 6778 | } | |||
| 6779 | }else{ | |||
| 6780 | int i; | |||
| 6781 | for(i=0; i<pNode->nChild; i++){ | |||
| 6782 | fts5ExprNodeZeroPoslist(pNode->apChild[i]); | |||
| 6783 | } | |||
| 6784 | } | |||
| 6785 | } | |||
| 6786 | ||||
| 6787 | ||||
| 6788 | ||||
| 6789 | /* | |||
| 6790 | ** Compare the values currently indicated by the two nodes as follows: | |||
| 6791 | ** | |||
| 6792 | ** res = (*p1) - (*p2) | |||
| 6793 | ** | |||
| 6794 | ** Nodes that point to values that come later in the iteration order are | |||
| 6795 | ** considered to be larger. Nodes at EOF are the largest of all. | |||
| 6796 | ** | |||
| 6797 | ** This means that if the iteration order is ASC, then numerically larger | |||
| 6798 | ** rowids are considered larger. Or if it is the default DESC, numerically | |||
| 6799 | ** smaller rowids are larger. | |||
| 6800 | */ | |||
| 6801 | static int fts5NodeCompare( | |||
| 6802 | Fts5Expr *pExpr, | |||
| 6803 | Fts5ExprNode *p1, | |||
| 6804 | Fts5ExprNode *p2 | |||
| 6805 | ){ | |||
| 6806 | if( p2->bEof ) return -1; | |||
| 6807 | if( p1->bEof ) return +1; | |||
| 6808 | return fts5RowidCmp(pExpr, p1->iRowid, p2->iRowid); | |||
| 6809 | } | |||
| 6810 | ||||
| 6811 | /* | |||
| 6812 | ** All individual term iterators in pNear are guaranteed to be valid when | |||
| 6813 | ** this function is called. This function checks if all term iterators | |||
| 6814 | ** point to the same rowid, and if not, advances them until they do. | |||
| 6815 | ** If an EOF is reached before this happens, *pbEof is set to true before | |||
| 6816 | ** returning. | |||
| 6817 | ** | |||
| 6818 | ** SQLITE_OK is returned if an error occurs, or an SQLite error code | |||
| 6819 | ** otherwise. It is not considered an error code if an iterator reaches | |||
| 6820 | ** EOF. | |||
| 6821 | */ | |||
| 6822 | static int fts5ExprNodeTest_STRING( | |||
| 6823 | Fts5Expr *pExpr, /* Expression pPhrase belongs to */ | |||
| 6824 | Fts5ExprNode *pNode | |||
| 6825 | ){ | |||
| 6826 | Fts5ExprNearset *pNear = pNode->pNear; | |||
| 6827 | Fts5ExprPhrase *pLeft = pNear->apPhrase[0]; | |||
| 6828 | int rc = SQLITE_OK0; | |||
| 6829 | i64 iLast; /* Lastest rowid any iterator points to */ | |||
| 6830 | int i, j; /* Phrase and token index, respectively */ | |||
| 6831 | int bMatch; /* True if all terms are at the same rowid */ | |||
| 6832 | const int bDesc = pExpr->bDesc; | |||
| 6833 | ||||
| 6834 | /* Check that this node should not be FTS5_TERM */ | |||
| 6835 | assert( pNear->nPhrase>1((void) (0)) | |||
| 6836 | || pNear->apPhrase[0]->nTerm>1((void) (0)) | |||
| 6837 | || pNear->apPhrase[0]->aTerm[0].pSynonym((void) (0)) | |||
| 6838 | || pNear->apPhrase[0]->aTerm[0].bFirst((void) (0)) | |||
| 6839 | )((void) (0)); | |||
| 6840 | ||||
| 6841 | /* Initialize iLast, the "lastest" rowid any iterator points to. If the | |||
| 6842 | ** iterator skips through rowids in the default ascending order, this means | |||
| 6843 | ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it | |||
| 6844 | ** means the minimum rowid. */ | |||
| 6845 | if( pLeft->aTerm[0].pSynonym ){ | |||
| 6846 | iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0); | |||
| 6847 | }else{ | |||
| 6848 | iLast = pLeft->aTerm[0].pIter->iRowid; | |||
| 6849 | } | |||
| 6850 | ||||
| 6851 | do { | |||
| 6852 | bMatch = 1; | |||
| 6853 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 6854 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |||
| 6855 | for(j=0; j<pPhrase->nTerm; j++){ | |||
| 6856 | Fts5ExprTerm *pTerm = &pPhrase->aTerm[j]; | |||
| 6857 | if( pTerm->pSynonym ){ | |||
| 6858 | i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0); | |||
| 6859 | if( iRowid==iLast ) continue; | |||
| 6860 | bMatch = 0; | |||
| 6861 | if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){ | |||
| 6862 | pNode->bNomatch = 0; | |||
| 6863 | pNode->bEof = 1; | |||
| 6864 | return rc; | |||
| 6865 | } | |||
| 6866 | }else{ | |||
| 6867 | Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; | |||
| 6868 | if( pIter->iRowid==iLast ) continue; | |||
| 6869 | bMatch = 0; | |||
| 6870 | if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){ | |||
| 6871 | return rc; | |||
| 6872 | } | |||
| 6873 | } | |||
| 6874 | } | |||
| 6875 | } | |||
| 6876 | }while( bMatch==0 ); | |||
| 6877 | ||||
| 6878 | pNode->iRowid = iLast; | |||
| 6879 | pNode->bNomatch = ((0==fts5ExprNearTest(&rc, pExpr, pNode)) && rc==SQLITE_OK0); | |||
| 6880 | assert( pNode->bEof==0 || pNode->bNomatch==0 )((void) (0)); | |||
| 6881 | ||||
| 6882 | return rc; | |||
| 6883 | } | |||
| 6884 | ||||
| 6885 | /* | |||
| 6886 | ** Advance the first term iterator in the first phrase of pNear. Set output | |||
| 6887 | ** variable *pbEof to true if it reaches EOF or if an error occurs. | |||
| 6888 | ** | |||
| 6889 | ** Return SQLITE_OK if successful, or an SQLite error code if an error | |||
| 6890 | ** occurs. | |||
| 6891 | */ | |||
| 6892 | static int fts5ExprNodeNext_STRING( | |||
| 6893 | Fts5Expr *pExpr, /* Expression pPhrase belongs to */ | |||
| 6894 | Fts5ExprNode *pNode, /* FTS5_STRING or FTS5_TERM node */ | |||
| 6895 | int bFromValid, | |||
| 6896 | i64 iFrom | |||
| 6897 | ){ | |||
| 6898 | Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0]; | |||
| 6899 | int rc = SQLITE_OK0; | |||
| 6900 | ||||
| 6901 | pNode->bNomatch = 0; | |||
| 6902 | if( pTerm->pSynonym ){ | |||
| 6903 | int bEof = 1; | |||
| 6904 | Fts5ExprTerm *p; | |||
| 6905 | ||||
| 6906 | /* Find the firstest rowid any synonym points to. */ | |||
| 6907 | i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0); | |||
| 6908 | ||||
| 6909 | /* Advance each iterator that currently points to iRowid. Or, if iFrom | |||
| 6910 | ** is valid - each iterator that points to a rowid before iFrom. */ | |||
| 6911 | for(p=pTerm; p; p=p->pSynonym){ | |||
| 6912 | if( sqlite3Fts5IterEof(p->pIter)((p->pIter)->bEof)==0 ){ | |||
| 6913 | i64 ii = p->pIter->iRowid; | |||
| 6914 | if( ii==iRowid | |||
| 6915 | || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc) | |||
| 6916 | ){ | |||
| 6917 | if( bFromValid ){ | |||
| 6918 | rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom); | |||
| 6919 | }else{ | |||
| 6920 | rc = sqlite3Fts5IterNext(p->pIter); | |||
| 6921 | } | |||
| 6922 | if( rc!=SQLITE_OK0 ) break; | |||
| 6923 | if( sqlite3Fts5IterEof(p->pIter)((p->pIter)->bEof)==0 ){ | |||
| 6924 | bEof = 0; | |||
| 6925 | } | |||
| 6926 | }else{ | |||
| 6927 | bEof = 0; | |||
| 6928 | } | |||
| 6929 | } | |||
| 6930 | } | |||
| 6931 | ||||
| 6932 | /* Set the EOF flag if either all synonym iterators are at EOF or an | |||
| 6933 | ** error has occurred. */ | |||
| 6934 | pNode->bEof = (rc || bEof); | |||
| 6935 | }else{ | |||
| 6936 | Fts5IndexIter *pIter = pTerm->pIter; | |||
| 6937 | ||||
| 6938 | assert( Fts5NodeIsString(pNode) )((void) (0)); | |||
| 6939 | if( bFromValid ){ | |||
| 6940 | rc = sqlite3Fts5IterNextFrom(pIter, iFrom); | |||
| 6941 | }else{ | |||
| 6942 | rc = sqlite3Fts5IterNext(pIter); | |||
| 6943 | } | |||
| 6944 | ||||
| 6945 | pNode->bEof = (rc || sqlite3Fts5IterEof(pIter)((pIter)->bEof)); | |||
| 6946 | } | |||
| 6947 | ||||
| 6948 | if( pNode->bEof==0 ){ | |||
| 6949 | assert( rc==SQLITE_OK )((void) (0)); | |||
| 6950 | rc = fts5ExprNodeTest_STRING(pExpr, pNode); | |||
| 6951 | } | |||
| 6952 | ||||
| 6953 | return rc; | |||
| 6954 | } | |||
| 6955 | ||||
| 6956 | ||||
| 6957 | static int fts5ExprNodeTest_TERM( | |||
| 6958 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ | |||
| 6959 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */ | |||
| 6960 | ){ | |||
| 6961 | /* As this "NEAR" object is actually a single phrase that consists | |||
| 6962 | ** of a single term only, grab pointers into the poslist managed by the | |||
| 6963 | ** fts5_index.c iterator object. This is much faster than synthesizing | |||
| 6964 | ** a new poslist the way we have to for more complicated phrase or NEAR | |||
| 6965 | ** expressions. */ | |||
| 6966 | Fts5ExprPhrase *pPhrase = pNode->pNear->apPhrase[0]; | |||
| 6967 | Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter; | |||
| 6968 | ||||
| 6969 | assert( pNode->eType==FTS5_TERM )((void) (0)); | |||
| 6970 | assert( pNode->pNear->nPhrase==1 && pPhrase->nTerm==1 )((void) (0)); | |||
| 6971 | assert( pPhrase->aTerm[0].pSynonym==0 )((void) (0)); | |||
| 6972 | ||||
| 6973 | pPhrase->poslist.n = pIter->nData; | |||
| 6974 | if( pExpr->pConfig->eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 6975 | pPhrase->poslist.p = (u8*)pIter->pData; | |||
| 6976 | } | |||
| 6977 | pNode->iRowid = pIter->iRowid; | |||
| 6978 | pNode->bNomatch = (pPhrase->poslist.n==0); | |||
| 6979 | return SQLITE_OK0; | |||
| 6980 | } | |||
| 6981 | ||||
| 6982 | /* | |||
| 6983 | ** xNext() method for a node of type FTS5_TERM. | |||
| 6984 | */ | |||
| 6985 | static int fts5ExprNodeNext_TERM( | |||
| 6986 | Fts5Expr *pExpr, | |||
| 6987 | Fts5ExprNode *pNode, | |||
| 6988 | int bFromValid, | |||
| 6989 | i64 iFrom | |||
| 6990 | ){ | |||
| 6991 | int rc; | |||
| 6992 | Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter; | |||
| 6993 | ||||
| 6994 | assert( pNode->bEof==0 )((void) (0)); | |||
| 6995 | if( bFromValid ){ | |||
| 6996 | rc = sqlite3Fts5IterNextFrom(pIter, iFrom); | |||
| 6997 | }else{ | |||
| 6998 | rc = sqlite3Fts5IterNext(pIter); | |||
| 6999 | } | |||
| 7000 | if( rc==SQLITE_OK0 && sqlite3Fts5IterEof(pIter)((pIter)->bEof)==0 ){ | |||
| 7001 | rc = fts5ExprNodeTest_TERM(pExpr, pNode); | |||
| 7002 | }else{ | |||
| 7003 | pNode->bEof = 1; | |||
| 7004 | pNode->bNomatch = 0; | |||
| 7005 | } | |||
| 7006 | return rc; | |||
| 7007 | } | |||
| 7008 | ||||
| 7009 | static void fts5ExprNodeTest_OR( | |||
| 7010 | Fts5Expr *pExpr, /* Expression of which pNode is a part */ | |||
| 7011 | Fts5ExprNode *pNode /* Expression node to test */ | |||
| 7012 | ){ | |||
| 7013 | Fts5ExprNode *pNext = pNode->apChild[0]; | |||
| 7014 | int i; | |||
| 7015 | ||||
| 7016 | for(i=1; i<pNode->nChild; i++){ | |||
| 7017 | Fts5ExprNode *pChild = pNode->apChild[i]; | |||
| 7018 | int cmp = fts5NodeCompare(pExpr, pNext, pChild); | |||
| 7019 | if( cmp>0 || (cmp==0 && pChild->bNomatch==0) ){ | |||
| 7020 | pNext = pChild; | |||
| 7021 | } | |||
| 7022 | } | |||
| 7023 | pNode->iRowid = pNext->iRowid; | |||
| 7024 | pNode->bEof = pNext->bEof; | |||
| 7025 | pNode->bNomatch = pNext->bNomatch; | |||
| 7026 | } | |||
| 7027 | ||||
| 7028 | static int fts5ExprNodeNext_OR( | |||
| 7029 | Fts5Expr *pExpr, | |||
| 7030 | Fts5ExprNode *pNode, | |||
| 7031 | int bFromValid, | |||
| 7032 | i64 iFrom | |||
| 7033 | ){ | |||
| 7034 | int i; | |||
| 7035 | i64 iLast = pNode->iRowid; | |||
| 7036 | ||||
| 7037 | for(i=0; i<pNode->nChild; i++){ | |||
| 7038 | Fts5ExprNode *p1 = pNode->apChild[i]; | |||
| 7039 | assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 )((void) (0)); | |||
| 7040 | if( p1->bEof==0 ){ | |||
| 7041 | if( (p1->iRowid==iLast) | |||
| 7042 | || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0) | |||
| 7043 | ){ | |||
| 7044 | int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom)(p1)->xNext((pExpr), (p1), (bFromValid), (iFrom)); | |||
| 7045 | if( rc!=SQLITE_OK0 ){ | |||
| 7046 | pNode->bNomatch = 0; | |||
| 7047 | return rc; | |||
| 7048 | } | |||
| 7049 | } | |||
| 7050 | } | |||
| 7051 | } | |||
| 7052 | ||||
| 7053 | fts5ExprNodeTest_OR(pExpr, pNode); | |||
| 7054 | return SQLITE_OK0; | |||
| 7055 | } | |||
| 7056 | ||||
| 7057 | /* | |||
| 7058 | ** Argument pNode is an FTS5_AND node. | |||
| 7059 | */ | |||
| 7060 | static int fts5ExprNodeTest_AND( | |||
| 7061 | Fts5Expr *pExpr, /* Expression pPhrase belongs to */ | |||
| 7062 | Fts5ExprNode *pAnd /* FTS5_AND node to advance */ | |||
| 7063 | ){ | |||
| 7064 | int iChild; | |||
| 7065 | i64 iLast = pAnd->iRowid; | |||
| 7066 | int rc = SQLITE_OK0; | |||
| 7067 | int bMatch; | |||
| 7068 | ||||
| 7069 | assert( pAnd->bEof==0 )((void) (0)); | |||
| 7070 | do { | |||
| 7071 | pAnd->bNomatch = 0; | |||
| 7072 | bMatch = 1; | |||
| 7073 | for(iChild=0; iChild<pAnd->nChild; iChild++){ | |||
| 7074 | Fts5ExprNode *pChild = pAnd->apChild[iChild]; | |||
| 7075 | int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid); | |||
| 7076 | if( cmp>0 ){ | |||
| 7077 | /* Advance pChild until it points to iLast or laster */ | |||
| 7078 | rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast)(pChild)->xNext((pExpr), (pChild), (1), (iLast)); | |||
| 7079 | if( rc!=SQLITE_OK0 ){ | |||
| 7080 | pAnd->bNomatch = 0; | |||
| 7081 | return rc; | |||
| 7082 | } | |||
| 7083 | } | |||
| 7084 | ||||
| 7085 | /* If the child node is now at EOF, so is the parent AND node. Otherwise, | |||
| 7086 | ** the child node is guaranteed to have advanced at least as far as | |||
| 7087 | ** rowid iLast. So if it is not at exactly iLast, pChild->iRowid is the | |||
| 7088 | ** new lastest rowid seen so far. */ | |||
| 7089 | assert( pChild->bEof || fts5RowidCmp(pExpr, iLast, pChild->iRowid)<=0 )((void) (0)); | |||
| 7090 | if( pChild->bEof ){ | |||
| 7091 | fts5ExprSetEof(pAnd); | |||
| 7092 | bMatch = 1; | |||
| 7093 | break; | |||
| 7094 | }else if( iLast!=pChild->iRowid ){ | |||
| 7095 | bMatch = 0; | |||
| 7096 | iLast = pChild->iRowid; | |||
| 7097 | } | |||
| 7098 | ||||
| 7099 | if( pChild->bNomatch ){ | |||
| 7100 | pAnd->bNomatch = 1; | |||
| 7101 | } | |||
| 7102 | } | |||
| 7103 | }while( bMatch==0 ); | |||
| 7104 | ||||
| 7105 | if( pAnd->bNomatch && pAnd!=pExpr->pRoot ){ | |||
| 7106 | fts5ExprNodeZeroPoslist(pAnd); | |||
| 7107 | } | |||
| 7108 | pAnd->iRowid = iLast; | |||
| 7109 | return SQLITE_OK0; | |||
| 7110 | } | |||
| 7111 | ||||
| 7112 | static int fts5ExprNodeNext_AND( | |||
| 7113 | Fts5Expr *pExpr, | |||
| 7114 | Fts5ExprNode *pNode, | |||
| 7115 | int bFromValid, | |||
| 7116 | i64 iFrom | |||
| 7117 | ){ | |||
| 7118 | int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom)(pNode->apChild[0])->xNext((pExpr), (pNode->apChild[ 0]), (bFromValid), (iFrom)); | |||
| 7119 | if( rc==SQLITE_OK0 ){ | |||
| 7120 | rc = fts5ExprNodeTest_AND(pExpr, pNode); | |||
| 7121 | }else{ | |||
| 7122 | pNode->bNomatch = 0; | |||
| 7123 | } | |||
| 7124 | return rc; | |||
| 7125 | } | |||
| 7126 | ||||
| 7127 | static int fts5ExprNodeTest_NOT( | |||
| 7128 | Fts5Expr *pExpr, /* Expression pPhrase belongs to */ | |||
| 7129 | Fts5ExprNode *pNode /* FTS5_NOT node to advance */ | |||
| 7130 | ){ | |||
| 7131 | int rc = SQLITE_OK0; | |||
| 7132 | Fts5ExprNode *p1 = pNode->apChild[0]; | |||
| 7133 | Fts5ExprNode *p2 = pNode->apChild[1]; | |||
| 7134 | assert( pNode->nChild==2 )((void) (0)); | |||
| 7135 | ||||
| 7136 | while( rc==SQLITE_OK0 && p1->bEof==0 ){ | |||
| 7137 | int cmp = fts5NodeCompare(pExpr, p1, p2); | |||
| 7138 | if( cmp>0 ){ | |||
| 7139 | rc = fts5ExprNodeNext(pExpr, p2, 1, p1->iRowid)(p2)->xNext((pExpr), (p2), (1), (p1->iRowid)); | |||
| 7140 | cmp = fts5NodeCompare(pExpr, p1, p2); | |||
| 7141 | } | |||
| 7142 | assert( rc!=SQLITE_OK || cmp<=0 )((void) (0)); | |||
| 7143 | if( cmp || p2->bNomatch ) break; | |||
| 7144 | rc = fts5ExprNodeNext(pExpr, p1, 0, 0)(p1)->xNext((pExpr), (p1), (0), (0)); | |||
| 7145 | } | |||
| 7146 | pNode->bEof = p1->bEof; | |||
| 7147 | pNode->bNomatch = p1->bNomatch; | |||
| 7148 | pNode->iRowid = p1->iRowid; | |||
| 7149 | if( p1->bEof ){ | |||
| 7150 | fts5ExprNodeZeroPoslist(p2); | |||
| 7151 | } | |||
| 7152 | return rc; | |||
| 7153 | } | |||
| 7154 | ||||
| 7155 | static int fts5ExprNodeNext_NOT( | |||
| 7156 | Fts5Expr *pExpr, | |||
| 7157 | Fts5ExprNode *pNode, | |||
| 7158 | int bFromValid, | |||
| 7159 | i64 iFrom | |||
| 7160 | ){ | |||
| 7161 | int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom)(pNode->apChild[0])->xNext((pExpr), (pNode->apChild[ 0]), (bFromValid), (iFrom)); | |||
| 7162 | if( rc==SQLITE_OK0 ){ | |||
| 7163 | rc = fts5ExprNodeTest_NOT(pExpr, pNode); | |||
| 7164 | } | |||
| 7165 | if( rc!=SQLITE_OK0 ){ | |||
| 7166 | pNode->bNomatch = 0; | |||
| 7167 | } | |||
| 7168 | return rc; | |||
| 7169 | } | |||
| 7170 | ||||
| 7171 | /* | |||
| 7172 | ** If pNode currently points to a match, this function returns SQLITE_OK | |||
| 7173 | ** without modifying it. Otherwise, pNode is advanced until it does point | |||
| 7174 | ** to a match or EOF is reached. | |||
| 7175 | */ | |||
| 7176 | static int fts5ExprNodeTest( | |||
| 7177 | Fts5Expr *pExpr, /* Expression of which pNode is a part */ | |||
| 7178 | Fts5ExprNode *pNode /* Expression node to test */ | |||
| 7179 | ){ | |||
| 7180 | int rc = SQLITE_OK0; | |||
| 7181 | if( pNode->bEof==0 ){ | |||
| 7182 | switch( pNode->eType ){ | |||
| 7183 | ||||
| 7184 | case FTS5_STRING9: { | |||
| 7185 | rc = fts5ExprNodeTest_STRING(pExpr, pNode); | |||
| 7186 | break; | |||
| 7187 | } | |||
| 7188 | ||||
| 7189 | case FTS5_TERM4: { | |||
| 7190 | rc = fts5ExprNodeTest_TERM(pExpr, pNode); | |||
| 7191 | break; | |||
| 7192 | } | |||
| 7193 | ||||
| 7194 | case FTS5_AND2: { | |||
| 7195 | rc = fts5ExprNodeTest_AND(pExpr, pNode); | |||
| 7196 | break; | |||
| 7197 | } | |||
| 7198 | ||||
| 7199 | case FTS5_OR1: { | |||
| 7200 | fts5ExprNodeTest_OR(pExpr, pNode); | |||
| 7201 | break; | |||
| 7202 | } | |||
| 7203 | ||||
| 7204 | default: assert( pNode->eType==FTS5_NOT )((void) (0)); { | |||
| 7205 | rc = fts5ExprNodeTest_NOT(pExpr, pNode); | |||
| 7206 | break; | |||
| 7207 | } | |||
| 7208 | } | |||
| 7209 | } | |||
| 7210 | return rc; | |||
| 7211 | } | |||
| 7212 | ||||
| 7213 | ||||
| 7214 | /* | |||
| 7215 | ** Set node pNode, which is part of expression pExpr, to point to the first | |||
| 7216 | ** match. If there are no matches, set the Node.bEof flag to indicate EOF. | |||
| 7217 | ** | |||
| 7218 | ** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise. | |||
| 7219 | ** It is not an error if there are no matches. | |||
| 7220 | */ | |||
| 7221 | static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){ | |||
| 7222 | int rc = SQLITE_OK0; | |||
| 7223 | pNode->bEof = 0; | |||
| 7224 | pNode->bNomatch = 0; | |||
| 7225 | ||||
| 7226 | if( Fts5NodeIsString(pNode)((pNode)->eType==4 || (pNode)->eType==9) ){ | |||
| 7227 | /* Initialize all term iterators in the NEAR object. */ | |||
| 7228 | rc = fts5ExprNearInitAll(pExpr, pNode); | |||
| 7229 | }else if( pNode->xNext==0 ){ | |||
| 7230 | pNode->bEof = 1; | |||
| 7231 | }else{ | |||
| 7232 | int i; | |||
| 7233 | int nEof = 0; | |||
| 7234 | for(i=0; i<pNode->nChild && rc==SQLITE_OK0; i++){ | |||
| 7235 | Fts5ExprNode *pChild = pNode->apChild[i]; | |||
| 7236 | rc = fts5ExprNodeFirst(pExpr, pNode->apChild[i]); | |||
| 7237 | assert( pChild->bEof==0 || pChild->bEof==1 )((void) (0)); | |||
| 7238 | nEof += pChild->bEof; | |||
| 7239 | } | |||
| 7240 | pNode->iRowid = pNode->apChild[0]->iRowid; | |||
| 7241 | ||||
| 7242 | switch( pNode->eType ){ | |||
| 7243 | case FTS5_AND2: | |||
| 7244 | if( nEof>0 ) fts5ExprSetEof(pNode); | |||
| 7245 | break; | |||
| 7246 | ||||
| 7247 | case FTS5_OR1: | |||
| 7248 | if( pNode->nChild==nEof ) fts5ExprSetEof(pNode); | |||
| 7249 | break; | |||
| 7250 | ||||
| 7251 | default: | |||
| 7252 | assert( pNode->eType==FTS5_NOT )((void) (0)); | |||
| 7253 | pNode->bEof = pNode->apChild[0]->bEof; | |||
| 7254 | break; | |||
| 7255 | } | |||
| 7256 | } | |||
| 7257 | ||||
| 7258 | if( rc==SQLITE_OK0 ){ | |||
| 7259 | rc = fts5ExprNodeTest(pExpr, pNode); | |||
| 7260 | } | |||
| 7261 | return rc; | |||
| 7262 | } | |||
| 7263 | ||||
| 7264 | ||||
| 7265 | /* | |||
| 7266 | ** Begin iterating through the set of documents in index pIdx matched by | |||
| 7267 | ** the MATCH expression passed as the first argument. If the "bDesc" | |||
| 7268 | ** parameter is passed a non-zero value, iteration is in descending rowid | |||
| 7269 | ** order. Or, if it is zero, in ascending order. | |||
| 7270 | ** | |||
| 7271 | ** If iterating in ascending rowid order (bDesc==0), the first document | |||
| 7272 | ** visited is that with the smallest rowid that is larger than or equal | |||
| 7273 | ** to parameter iFirst. Or, if iterating in ascending order (bDesc==1), | |||
| 7274 | ** then the first document visited must have a rowid smaller than or | |||
| 7275 | ** equal to iFirst. | |||
| 7276 | ** | |||
| 7277 | ** Return SQLITE_OK if successful, or an SQLite error code otherwise. It | |||
| 7278 | ** is not considered an error if the query does not match any documents. | |||
| 7279 | */ | |||
| 7280 | static int sqlite3Fts5ExprFirst( | |||
| 7281 | Fts5Expr *p, | |||
| 7282 | Fts5Index *pIdx, | |||
| 7283 | i64 iFirst, | |||
| 7284 | i64 iLast, | |||
| 7285 | int bDesc | |||
| 7286 | ){ | |||
| 7287 | Fts5ExprNode *pRoot = p->pRoot; | |||
| 7288 | int rc; /* Return code */ | |||
| 7289 | ||||
| 7290 | p->pIndex = pIdx; | |||
| 7291 | p->bDesc = bDesc; | |||
| 7292 | rc = fts5ExprNodeFirst(p, pRoot); | |||
| 7293 | ||||
| 7294 | /* If not at EOF but the current rowid occurs earlier than iFirst in | |||
| 7295 | ** the iteration order, move to document iFirst or later. */ | |||
| 7296 | if( rc==SQLITE_OK0 | |||
| 7297 | && 0==pRoot->bEof | |||
| 7298 | && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0 | |||
| 7299 | ){ | |||
| 7300 | rc = fts5ExprNodeNext(p, pRoot, 1, iFirst)(pRoot)->xNext((p), (pRoot), (1), (iFirst)); | |||
| 7301 | } | |||
| 7302 | ||||
| 7303 | /* If the iterator is not at a real match, skip forward until it is. */ | |||
| 7304 | while( pRoot->bNomatch && rc==SQLITE_OK0 ){ | |||
| 7305 | assert( pRoot->bEof==0 )((void) (0)); | |||
| 7306 | rc = fts5ExprNodeNext(p, pRoot, 0, 0)(pRoot)->xNext((p), (pRoot), (0), (0)); | |||
| 7307 | } | |||
| 7308 | if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){ | |||
| 7309 | pRoot->bEof = 1; | |||
| 7310 | } | |||
| 7311 | return rc; | |||
| 7312 | } | |||
| 7313 | ||||
| 7314 | /* | |||
| 7315 | ** Move to the next document | |||
| 7316 | ** | |||
| 7317 | ** Return SQLITE_OK if successful, or an SQLite error code otherwise. It | |||
| 7318 | ** is not considered an error if the query does not match any documents. | |||
| 7319 | */ | |||
| 7320 | static int sqlite3Fts5ExprNext(Fts5Expr *p, i64 iLast){ | |||
| 7321 | int rc; | |||
| 7322 | Fts5ExprNode *pRoot = p->pRoot; | |||
| 7323 | assert( pRoot->bEof==0 && pRoot->bNomatch==0 )((void) (0)); | |||
| 7324 | do { | |||
| 7325 | rc = fts5ExprNodeNext(p, pRoot, 0, 0)(pRoot)->xNext((p), (pRoot), (0), (0)); | |||
| 7326 | assert( pRoot->bNomatch==0 || (rc==SQLITE_OK && pRoot->bEof==0) )((void) (0)); | |||
| 7327 | }while( pRoot->bNomatch ); | |||
| 7328 | if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){ | |||
| 7329 | pRoot->bEof = 1; | |||
| 7330 | } | |||
| 7331 | return rc; | |||
| 7332 | } | |||
| 7333 | ||||
| 7334 | static int sqlite3Fts5ExprEof(Fts5Expr *p){ | |||
| 7335 | return p->pRoot->bEof; | |||
| 7336 | } | |||
| 7337 | ||||
| 7338 | static i64 sqlite3Fts5ExprRowid(Fts5Expr *p){ | |||
| 7339 | return p->pRoot->iRowid; | |||
| 7340 | } | |||
| 7341 | ||||
| 7342 | static int fts5ParseStringFromToken(Fts5Token *pToken, char **pz){ | |||
| 7343 | int rc = SQLITE_OK0; | |||
| 7344 | *pz = sqlite3Fts5Strndup(&rc, pToken->p, pToken->n); | |||
| 7345 | return rc; | |||
| 7346 | } | |||
| 7347 | ||||
| 7348 | /* | |||
| 7349 | ** Free the phrase object passed as the only argument. | |||
| 7350 | */ | |||
| 7351 | static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ | |||
| 7352 | if( pPhrase ){ | |||
| 7353 | int i; | |||
| 7354 | for(i=0; i<pPhrase->nTerm; i++){ | |||
| 7355 | Fts5ExprTerm *pSyn; | |||
| 7356 | Fts5ExprTerm *pNext; | |||
| 7357 | Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; | |||
| 7358 | sqlite3_freesqlite3_api->free(pTerm->pTerm); | |||
| 7359 | sqlite3Fts5IterClose(pTerm->pIter); | |||
| 7360 | for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){ | |||
| 7361 | pNext = pSyn->pSynonym; | |||
| 7362 | sqlite3Fts5IterClose(pSyn->pIter); | |||
| 7363 | fts5BufferFree((Fts5Buffer*)&pSyn[1])sqlite3Fts5BufferFree((Fts5Buffer*)&pSyn[1]); | |||
| 7364 | sqlite3_freesqlite3_api->free(pSyn); | |||
| 7365 | } | |||
| 7366 | } | |||
| 7367 | if( pPhrase->poslist.nSpace>0 ) fts5BufferFree(&pPhrase->poslist)sqlite3Fts5BufferFree(&pPhrase->poslist); | |||
| 7368 | sqlite3_freesqlite3_api->free(pPhrase); | |||
| 7369 | } | |||
| 7370 | } | |||
| 7371 | ||||
| 7372 | /* | |||
| 7373 | ** Set the "bFirst" flag on the first token of the phrase passed as the | |||
| 7374 | ** only argument. | |||
| 7375 | */ | |||
| 7376 | static void sqlite3Fts5ParseSetCaret(Fts5ExprPhrase *pPhrase){ | |||
| 7377 | if( pPhrase && pPhrase->nTerm ){ | |||
| 7378 | pPhrase->aTerm[0].bFirst = 1; | |||
| 7379 | } | |||
| 7380 | } | |||
| 7381 | ||||
| 7382 | /* | |||
| 7383 | ** If argument pNear is NULL, then a new Fts5ExprNearset object is allocated | |||
| 7384 | ** and populated with pPhrase. Or, if pNear is not NULL, phrase pPhrase is | |||
| 7385 | ** appended to it and the results returned. | |||
| 7386 | ** | |||
| 7387 | ** If an OOM error occurs, both the pNear and pPhrase objects are freed and | |||
| 7388 | ** NULL returned. | |||
| 7389 | */ | |||
| 7390 | static Fts5ExprNearset *sqlite3Fts5ParseNearset( | |||
| 7391 | Fts5Parse *pParse, /* Parse context */ | |||
| 7392 | Fts5ExprNearset *pNear, /* Existing nearset, or NULL */ | |||
| 7393 | Fts5ExprPhrase *pPhrase /* Recently parsed phrase */ | |||
| 7394 | ){ | |||
| 7395 | const int SZALLOC = 8; | |||
| 7396 | Fts5ExprNearset *pRet = 0; | |||
| 7397 | ||||
| 7398 | if( pParse->rc==SQLITE_OK0 ){ | |||
| 7399 | if( pNear==0 ){ | |||
| 7400 | sqlite3_int64 nByte; | |||
| 7401 | nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1)(__builtin_offsetof(Fts5ExprNearset, apPhrase)+(SZALLOC+1)*sizeof (Fts5ExprPhrase*)); | |||
| 7402 | pRet = sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 7403 | if( pRet==0 ){ | |||
| 7404 | pParse->rc = SQLITE_NOMEM7; | |||
| 7405 | }else{ | |||
| 7406 | memset(pRet, 0, (size_t)nByte); | |||
| 7407 | } | |||
| 7408 | }else if( (pNear->nPhrase % SZALLOC)==0 ){ | |||
| 7409 | int nNew = pNear->nPhrase + SZALLOC; | |||
| 7410 | sqlite3_int64 nByte; | |||
| 7411 | ||||
| 7412 | nByte = SZ_FTS5EXPRNEARSET(nNew+1)(__builtin_offsetof(Fts5ExprNearset, apPhrase)+(nNew+1)*sizeof (Fts5ExprPhrase*)); | |||
| 7413 | pRet = (Fts5ExprNearset*)sqlite3_realloc64sqlite3_api->realloc64(pNear, nByte); | |||
| 7414 | if( pRet==0 ){ | |||
| 7415 | pParse->rc = SQLITE_NOMEM7; | |||
| 7416 | } | |||
| 7417 | }else{ | |||
| 7418 | pRet = pNear; | |||
| 7419 | } | |||
| 7420 | } | |||
| 7421 | ||||
| 7422 | if( pRet==0 ){ | |||
| 7423 | assert( pParse->rc!=SQLITE_OK )((void) (0)); | |||
| 7424 | sqlite3Fts5ParseNearsetFree(pNear); | |||
| 7425 | sqlite3Fts5ParsePhraseFree(pPhrase); | |||
| 7426 | }else{ | |||
| 7427 | if( pRet->nPhrase>0 ){ | |||
| 7428 | Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1]; | |||
| 7429 | assert( pParse!=0 )((void) (0)); | |||
| 7430 | assert( pParse->apPhrase!=0 )((void) (0)); | |||
| 7431 | assert( pParse->nPhrase>=2 )((void) (0)); | |||
| 7432 | assert( pLast==pParse->apPhrase[pParse->nPhrase-2] )((void) (0)); | |||
| 7433 | if( pPhrase->nTerm==0 ){ | |||
| 7434 | fts5ExprPhraseFree(pPhrase); | |||
| 7435 | pRet->nPhrase--; | |||
| 7436 | pParse->nPhrase--; | |||
| 7437 | pPhrase = pLast; | |||
| 7438 | }else if( pLast->nTerm==0 ){ | |||
| 7439 | fts5ExprPhraseFree(pLast); | |||
| 7440 | pParse->apPhrase[pParse->nPhrase-2] = pPhrase; | |||
| 7441 | pParse->nPhrase--; | |||
| 7442 | pRet->nPhrase--; | |||
| 7443 | } | |||
| 7444 | } | |||
| 7445 | pRet->apPhrase[pRet->nPhrase++] = pPhrase; | |||
| 7446 | } | |||
| 7447 | return pRet; | |||
| 7448 | } | |||
| 7449 | ||||
| 7450 | typedef struct TokenCtx TokenCtx; | |||
| 7451 | struct TokenCtx { | |||
| 7452 | Fts5ExprPhrase *pPhrase; | |||
| 7453 | Fts5Config *pConfig; | |||
| 7454 | int rc; | |||
| 7455 | }; | |||
| 7456 | ||||
| 7457 | /* | |||
| 7458 | ** Callback for tokenizing terms used by ParseTerm(). | |||
| 7459 | */ | |||
| 7460 | static int fts5ParseTokenize( | |||
| 7461 | void *pContext, /* Pointer to Fts5InsertCtx object */ | |||
| 7462 | int tflags, /* Mask of FTS5_TOKEN_* flags */ | |||
| 7463 | const char *pToken, /* Buffer containing token */ | |||
| 7464 | int nToken, /* Size of token in bytes */ | |||
| 7465 | int iUnused1, /* Start offset of token */ | |||
| 7466 | int iUnused2 /* End offset of token */ | |||
| 7467 | ){ | |||
| 7468 | int rc = SQLITE_OK0; | |||
| 7469 | const int SZALLOC = 8; | |||
| 7470 | TokenCtx *pCtx = (TokenCtx*)pContext; | |||
| 7471 | Fts5ExprPhrase *pPhrase = pCtx->pPhrase; | |||
| 7472 | ||||
| 7473 | UNUSED_PARAM2(iUnused1, iUnused2)(void)(iUnused1), (void)(iUnused2); | |||
| 7474 | ||||
| 7475 | /* If an error has already occurred, this is a no-op */ | |||
| 7476 | if( pCtx->rc!=SQLITE_OK0 ) return pCtx->rc; | |||
| 7477 | if( nToken>FTS5_MAX_TOKEN_SIZE32768 ) nToken = FTS5_MAX_TOKEN_SIZE32768; | |||
| 7478 | ||||
| 7479 | if( pPhrase && pPhrase->nTerm>0 && (tflags & FTS5_TOKEN_COLOCATED0x0001) ){ | |||
| 7480 | Fts5ExprTerm *pSyn; | |||
| 7481 | sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1; | |||
| 7482 | pSyn = (Fts5ExprTerm*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 7483 | if( pSyn==0 ){ | |||
| 7484 | rc = SQLITE_NOMEM7; | |||
| 7485 | }else{ | |||
| 7486 | memset(pSyn, 0, (size_t)nByte); | |||
| 7487 | pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); | |||
| 7488 | pSyn->nFullTerm = pSyn->nQueryTerm = nToken; | |||
| 7489 | if( pCtx->pConfig->bTokendata ){ | |||
| 7490 | pSyn->nQueryTerm = (int)strlen(pSyn->pTerm); | |||
| 7491 | } | |||
| 7492 | memcpy(pSyn->pTerm, pToken, nToken); | |||
| 7493 | pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; | |||
| 7494 | pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; | |||
| 7495 | } | |||
| 7496 | }else{ | |||
| 7497 | Fts5ExprTerm *pTerm; | |||
| 7498 | if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ | |||
| 7499 | Fts5ExprPhrase *pNew; | |||
| 7500 | int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); | |||
| 7501 | ||||
| 7502 | pNew = (Fts5ExprPhrase*)sqlite3_realloc64sqlite3_api->realloc64(pPhrase, | |||
| 7503 | SZ_FTS5EXPRPHRASE(nNew+1)(__builtin_offsetof(Fts5ExprPhrase, aTerm) + (nNew+1)*sizeof( Fts5ExprTerm)) | |||
| 7504 | ); | |||
| 7505 | if( pNew==0 ){ | |||
| 7506 | rc = SQLITE_NOMEM7; | |||
| 7507 | }else{ | |||
| 7508 | if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1)(__builtin_offsetof(Fts5ExprPhrase, aTerm) + (1)*sizeof(Fts5ExprTerm ))); | |||
| 7509 | pCtx->pPhrase = pPhrase = pNew; | |||
| 7510 | pNew->nTerm = nNew - SZALLOC; | |||
| 7511 | } | |||
| 7512 | } | |||
| 7513 | ||||
| 7514 | if( rc==SQLITE_OK0 ){ | |||
| 7515 | pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; | |||
| 7516 | memset(pTerm, 0, sizeof(Fts5ExprTerm)); | |||
| 7517 | pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); | |||
| 7518 | pTerm->nFullTerm = pTerm->nQueryTerm = nToken; | |||
| 7519 | if( pCtx->pConfig->bTokendata && rc==SQLITE_OK0 ){ | |||
| 7520 | pTerm->nQueryTerm = (int)strlen(pTerm->pTerm); | |||
| 7521 | } | |||
| 7522 | } | |||
| 7523 | } | |||
| 7524 | ||||
| 7525 | pCtx->rc = rc; | |||
| 7526 | return rc; | |||
| 7527 | } | |||
| 7528 | ||||
| 7529 | ||||
| 7530 | /* | |||
| 7531 | ** Free the phrase object passed as the only argument. | |||
| 7532 | */ | |||
| 7533 | static void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase *pPhrase){ | |||
| 7534 | fts5ExprPhraseFree(pPhrase); | |||
| 7535 | } | |||
| 7536 | ||||
| 7537 | /* | |||
| 7538 | ** Free the phrase object passed as the second argument. | |||
| 7539 | */ | |||
| 7540 | static void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset *pNear){ | |||
| 7541 | if( pNear ){ | |||
| 7542 | int i; | |||
| 7543 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 7544 | fts5ExprPhraseFree(pNear->apPhrase[i]); | |||
| 7545 | } | |||
| 7546 | sqlite3_freesqlite3_api->free(pNear->pColset); | |||
| 7547 | sqlite3_freesqlite3_api->free(pNear); | |||
| 7548 | } | |||
| 7549 | } | |||
| 7550 | ||||
| 7551 | static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){ | |||
| 7552 | assert( pParse->pExpr==0 )((void) (0)); | |||
| 7553 | pParse->pExpr = p; | |||
| 7554 | } | |||
| 7555 | ||||
| 7556 | static int parseGrowPhraseArray(Fts5Parse *pParse){ | |||
| 7557 | if( (pParse->nPhrase % 8)==0 ){ | |||
| 7558 | sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); | |||
| 7559 | Fts5ExprPhrase **apNew; | |||
| 7560 | apNew = (Fts5ExprPhrase**)sqlite3_realloc64sqlite3_api->realloc64(pParse->apPhrase, nByte); | |||
| 7561 | if( apNew==0 ){ | |||
| 7562 | pParse->rc = SQLITE_NOMEM7; | |||
| 7563 | return SQLITE_NOMEM7; | |||
| 7564 | } | |||
| 7565 | pParse->apPhrase = apNew; | |||
| 7566 | } | |||
| 7567 | return SQLITE_OK0; | |||
| 7568 | } | |||
| 7569 | ||||
| 7570 | /* | |||
| 7571 | ** This function is called by the parser to process a string token. The | |||
| 7572 | ** string may or may not be quoted. In any case it is tokenized and a | |||
| 7573 | ** phrase object consisting of all tokens returned. | |||
| 7574 | */ | |||
| 7575 | static Fts5ExprPhrase *sqlite3Fts5ParseTerm( | |||
| 7576 | Fts5Parse *pParse, /* Parse context */ | |||
| 7577 | Fts5ExprPhrase *pAppend, /* Phrase to append to */ | |||
| 7578 | Fts5Token *pToken, /* String to tokenize */ | |||
| 7579 | int bPrefix /* True if there is a trailing "*" */ | |||
| 7580 | ){ | |||
| 7581 | Fts5Config *pConfig = pParse->pConfig; | |||
| 7582 | TokenCtx sCtx; /* Context object passed to callback */ | |||
| 7583 | int rc; /* Tokenize return code */ | |||
| 7584 | char *z = 0; | |||
| 7585 | ||||
| 7586 | memset(&sCtx, 0, sizeof(TokenCtx)); | |||
| 7587 | sCtx.pPhrase = pAppend; | |||
| 7588 | sCtx.pConfig = pConfig; | |||
| 7589 | ||||
| 7590 | rc = fts5ParseStringFromToken(pToken, &z); | |||
| 7591 | if( rc==SQLITE_OK0 ){ | |||
| 7592 | int flags = FTS5_TOKENIZE_QUERY0x0001 | (bPrefix ? FTS5_TOKENIZE_PREFIX0x0002 : 0); | |||
| 7593 | int n; | |||
| 7594 | sqlite3Fts5Dequote(z); | |||
| 7595 | n = (int)strlen(z); | |||
| 7596 | rc = sqlite3Fts5Tokenize(pConfig, flags, z, n, &sCtx, fts5ParseTokenize); | |||
| 7597 | } | |||
| 7598 | sqlite3_freesqlite3_api->free(z); | |||
| 7599 | if( rc || (rc = sCtx.rc) ){ | |||
| 7600 | pParse->rc = rc; | |||
| 7601 | fts5ExprPhraseFree(sCtx.pPhrase); | |||
| 7602 | sCtx.pPhrase = 0; | |||
| 7603 | }else{ | |||
| 7604 | ||||
| 7605 | if( pAppend==0 ){ | |||
| 7606 | if( parseGrowPhraseArray(pParse) ){ | |||
| 7607 | fts5ExprPhraseFree(sCtx.pPhrase); | |||
| 7608 | return 0; | |||
| 7609 | } | |||
| 7610 | pParse->nPhrase++; | |||
| 7611 | } | |||
| 7612 | ||||
| 7613 | if( sCtx.pPhrase==0 ){ | |||
| 7614 | /* This happens when parsing a token or quoted phrase that contains | |||
| 7615 | ** no token characters at all. (e.g ... MATCH '""'). */ | |||
| 7616 | sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1)(__builtin_offsetof(Fts5ExprPhrase, aTerm) + (1)*sizeof(Fts5ExprTerm ))); | |||
| 7617 | }else if( sCtx.pPhrase->nTerm ){ | |||
| 7618 | sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; | |||
| 7619 | } | |||
| 7620 | assert( pParse->apPhrase!=0 )((void) (0)); | |||
| 7621 | pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; | |||
| 7622 | } | |||
| 7623 | ||||
| 7624 | return sCtx.pPhrase; | |||
| 7625 | } | |||
| 7626 | ||||
| 7627 | /* | |||
| 7628 | ** Create a new FTS5 expression by cloning phrase iPhrase of the | |||
| 7629 | ** expression passed as the second argument. | |||
| 7630 | */ | |||
| 7631 | static int sqlite3Fts5ExprClonePhrase( | |||
| 7632 | Fts5Expr *pExpr, | |||
| 7633 | int iPhrase, | |||
| 7634 | Fts5Expr **ppNew | |||
| 7635 | ){ | |||
| 7636 | int rc = SQLITE_OK0; /* Return code */ | |||
| 7637 | Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */ | |||
| 7638 | Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ | |||
| 7639 | TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */ | |||
| 7640 | if( !pExpr || iPhrase<0 || iPhrase>=pExpr->nPhrase ){ | |||
| 7641 | rc = SQLITE_RANGE25; | |||
| 7642 | }else{ | |||
| 7643 | pOrig = pExpr->apExprPhrase[iPhrase]; | |||
| 7644 | pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); | |||
| 7645 | } | |||
| 7646 | if( rc==SQLITE_OK0 ){ | |||
| 7647 | pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, | |||
| 7648 | sizeof(Fts5ExprPhrase*)); | |||
| 7649 | } | |||
| 7650 | if( rc==SQLITE_OK0 ){ | |||
| 7651 | pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1)(__builtin_offsetof(Fts5ExprNode, apChild) + (1)*sizeof(Fts5ExprNode *))); | |||
| 7652 | } | |||
| 7653 | if( rc==SQLITE_OK0 ){ | |||
| 7654 | pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, | |||
| 7655 | SZ_FTS5EXPRNEARSET(2)(__builtin_offsetof(Fts5ExprNearset, apPhrase)+(2)*sizeof(Fts5ExprPhrase *))); | |||
| 7656 | } | |||
| 7657 | if( rc==SQLITE_OK0 && ALWAYS(pOrig!=0)(pOrig!=0) ){ | |||
| 7658 | Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; | |||
| 7659 | if( pColsetOrig ){ | |||
| 7660 | sqlite3_int64 nByte; | |||
| 7661 | Fts5Colset *pColset; | |||
| 7662 | nByte = SZ_FTS5COLSET(pColsetOrig->nCol)(sizeof(i64)*((pColsetOrig->nCol+2)/2)); | |||
| 7663 | pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 7664 | if( pColset ){ | |||
| 7665 | memcpy(pColset, pColsetOrig, (size_t)nByte); | |||
| 7666 | } | |||
| 7667 | pNew->pRoot->pNear->pColset = pColset; | |||
| 7668 | } | |||
| 7669 | } | |||
| 7670 | ||||
| 7671 | if( rc==SQLITE_OK0 ){ | |||
| 7672 | if( pOrig->nTerm ){ | |||
| 7673 | int i; /* Used to iterate through phrase terms */ | |||
| 7674 | sCtx.pConfig = pExpr->pConfig; | |||
| 7675 | for(i=0; rc==SQLITE_OK0 && i<pOrig->nTerm; i++){ | |||
| 7676 | int tflags = 0; | |||
| 7677 | Fts5ExprTerm *p; | |||
| 7678 | for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK0; p=p->pSynonym){ | |||
| 7679 | rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0); | |||
| 7680 | tflags = FTS5_TOKEN_COLOCATED0x0001; | |||
| 7681 | } | |||
| 7682 | if( rc==SQLITE_OK0 ){ | |||
| 7683 | sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; | |||
| 7684 | sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; | |||
| 7685 | } | |||
| 7686 | } | |||
| 7687 | }else{ | |||
| 7688 | /* This happens when parsing a token or quoted phrase that contains | |||
| 7689 | ** no token characters at all. (e.g ... MATCH '""'). */ | |||
| 7690 | sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1)(__builtin_offsetof(Fts5ExprPhrase, aTerm) + (1)*sizeof(Fts5ExprTerm ))); | |||
| 7691 | } | |||
| 7692 | } | |||
| 7693 | ||||
| 7694 | if( rc==SQLITE_OK0 && ALWAYS(sCtx.pPhrase)(sCtx.pPhrase) ){ | |||
| 7695 | /* All the allocations succeeded. Put the expression object together. */ | |||
| 7696 | pNew->pIndex = pExpr->pIndex; | |||
| 7697 | pNew->pConfig = pExpr->pConfig; | |||
| 7698 | pNew->nPhrase = 1; | |||
| 7699 | pNew->apExprPhrase[0] = sCtx.pPhrase; | |||
| 7700 | pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase; | |||
| 7701 | pNew->pRoot->pNear->nPhrase = 1; | |||
| 7702 | sCtx.pPhrase->pNode = pNew->pRoot; | |||
| 7703 | ||||
| 7704 | if( pOrig->nTerm==1 | |||
| 7705 | && pOrig->aTerm[0].pSynonym==0 | |||
| 7706 | && pOrig->aTerm[0].bFirst==0 | |||
| 7707 | ){ | |||
| 7708 | pNew->pRoot->eType = FTS5_TERM4; | |||
| 7709 | pNew->pRoot->xNext = fts5ExprNodeNext_TERM; | |||
| 7710 | }else{ | |||
| 7711 | pNew->pRoot->eType = FTS5_STRING9; | |||
| 7712 | pNew->pRoot->xNext = fts5ExprNodeNext_STRING; | |||
| 7713 | } | |||
| 7714 | }else{ | |||
| 7715 | sqlite3Fts5ExprFree(pNew); | |||
| 7716 | fts5ExprPhraseFree(sCtx.pPhrase); | |||
| 7717 | pNew = 0; | |||
| 7718 | } | |||
| 7719 | ||||
| 7720 | *ppNew = pNew; | |||
| 7721 | return rc; | |||
| 7722 | } | |||
| 7723 | ||||
| 7724 | ||||
| 7725 | /* | |||
| 7726 | ** Token pTok has appeared in a MATCH expression where the NEAR operator | |||
| 7727 | ** is expected. If token pTok does not contain "NEAR", store an error | |||
| 7728 | ** in the pParse object. | |||
| 7729 | */ | |||
| 7730 | static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token *pTok){ | |||
| 7731 | if( pTok->n!=4 || memcmp("NEAR", pTok->p, 4) ){ | |||
| 7732 | sqlite3Fts5ParseError( | |||
| 7733 | pParse, "fts5: syntax error near \"%.*s\"", pTok->n, pTok->p | |||
| 7734 | ); | |||
| 7735 | } | |||
| 7736 | } | |||
| 7737 | ||||
| 7738 | static void sqlite3Fts5ParseSetDistance( | |||
| 7739 | Fts5Parse *pParse, | |||
| 7740 | Fts5ExprNearset *pNear, | |||
| 7741 | Fts5Token *p | |||
| 7742 | ){ | |||
| 7743 | if( pNear ){ | |||
| 7744 | int nNear = 0; | |||
| 7745 | int i; | |||
| 7746 | if( p->n ){ | |||
| 7747 | for(i=0; i<p->n; i++){ | |||
| 7748 | char c = (char)p->p[i]; | |||
| 7749 | if( c<'0' || c>'9' ){ | |||
| 7750 | sqlite3Fts5ParseError( | |||
| 7751 | pParse, "expected integer, got \"%.*s\"", p->n, p->p | |||
| 7752 | ); | |||
| 7753 | return; | |||
| 7754 | } | |||
| 7755 | if( nNear<214748363 ) nNear = nNear * 10 + (p->p[i] - '0'); | |||
| 7756 | /* ^^^^^^^^^^^^^^^--- Prevent integer overflow */ | |||
| 7757 | } | |||
| 7758 | }else{ | |||
| 7759 | nNear = FTS5_DEFAULT_NEARDIST10; | |||
| 7760 | } | |||
| 7761 | pNear->nNear = nNear; | |||
| 7762 | } | |||
| 7763 | } | |||
| 7764 | ||||
| 7765 | /* | |||
| 7766 | ** The second argument passed to this function may be NULL, or it may be | |||
| 7767 | ** an existing Fts5Colset object. This function returns a pointer to | |||
| 7768 | ** a new colset object containing the contents of (p) with new value column | |||
| 7769 | ** number iCol appended. | |||
| 7770 | ** | |||
| 7771 | ** If an OOM error occurs, store an error code in pParse and return NULL. | |||
| 7772 | ** The old colset object (if any) is not freed in this case. | |||
| 7773 | */ | |||
| 7774 | static Fts5Colset *fts5ParseColset( | |||
| 7775 | Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ | |||
| 7776 | Fts5Colset *p, /* Existing colset object */ | |||
| 7777 | int iCol /* New column to add to colset object */ | |||
| 7778 | ){ | |||
| 7779 | int nCol = p ? p->nCol : 0; /* Num. columns already in colset object */ | |||
| 7780 | Fts5Colset *pNew; /* New colset object to return */ | |||
| 7781 | ||||
| 7782 | assert( pParse->rc==SQLITE_OK )((void) (0)); | |||
| 7783 | assert( iCol>=0 && iCol<pParse->pConfig->nCol )((void) (0)); | |||
| 7784 | ||||
| 7785 | pNew = sqlite3_realloc64sqlite3_api->realloc64(p, SZ_FTS5COLSET(nCol+1)(sizeof(i64)*((nCol+1 +2)/2))); | |||
| 7786 | if( pNew==0 ){ | |||
| 7787 | pParse->rc = SQLITE_NOMEM7; | |||
| 7788 | }else{ | |||
| 7789 | int *aiCol = pNew->aiCol; | |||
| 7790 | int i, j; | |||
| 7791 | for(i=0; i<nCol; i++){ | |||
| 7792 | if( aiCol[i]==iCol ) return pNew; | |||
| 7793 | if( aiCol[i]>iCol ) break; | |||
| 7794 | } | |||
| 7795 | for(j=nCol; j>i; j--){ | |||
| 7796 | aiCol[j] = aiCol[j-1]; | |||
| 7797 | } | |||
| 7798 | aiCol[i] = iCol; | |||
| 7799 | pNew->nCol = nCol+1; | |||
| 7800 | ||||
| 7801 | #ifndef NDEBUG1 | |||
| 7802 | /* Check that the array is in order and contains no duplicate entries. */ | |||
| 7803 | for(i=1; i<pNew->nCol; i++) assert( pNew->aiCol[i]>pNew->aiCol[i-1] )((void) (0)); | |||
| 7804 | #endif | |||
| 7805 | } | |||
| 7806 | ||||
| 7807 | return pNew; | |||
| 7808 | } | |||
| 7809 | ||||
| 7810 | /* | |||
| 7811 | ** Allocate and return an Fts5Colset object specifying the inverse of | |||
| 7812 | ** the colset passed as the second argument. Free the colset passed | |||
| 7813 | ** as the second argument before returning. | |||
| 7814 | */ | |||
| 7815 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ | |||
| 7816 | Fts5Colset *pRet; | |||
| 7817 | int nCol = pParse->pConfig->nCol; | |||
| 7818 | ||||
| 7819 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, | |||
| 7820 | SZ_FTS5COLSET(nCol+1)(sizeof(i64)*((nCol+1 +2)/2)) | |||
| 7821 | ); | |||
| 7822 | if( pRet ){ | |||
| 7823 | int i; | |||
| 7824 | int iOld = 0; | |||
| 7825 | for(i=0; i<nCol; i++){ | |||
| 7826 | if( iOld>=p->nCol || p->aiCol[iOld]!=i ){ | |||
| 7827 | pRet->aiCol[pRet->nCol++] = i; | |||
| 7828 | }else{ | |||
| 7829 | iOld++; | |||
| 7830 | } | |||
| 7831 | } | |||
| 7832 | } | |||
| 7833 | ||||
| 7834 | sqlite3_freesqlite3_api->free(p); | |||
| 7835 | return pRet; | |||
| 7836 | } | |||
| 7837 | ||||
| 7838 | static Fts5Colset *sqlite3Fts5ParseColset( | |||
| 7839 | Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ | |||
| 7840 | Fts5Colset *pColset, /* Existing colset object */ | |||
| 7841 | Fts5Token *p | |||
| 7842 | ){ | |||
| 7843 | Fts5Colset *pRet = 0; | |||
| 7844 | int iCol; | |||
| 7845 | char *z; /* Dequoted copy of token p */ | |||
| 7846 | ||||
| 7847 | z = sqlite3Fts5Strndup(&pParse->rc, p->p, p->n); | |||
| 7848 | if( pParse->rc==SQLITE_OK0 ){ | |||
| 7849 | Fts5Config *pConfig = pParse->pConfig; | |||
| 7850 | sqlite3Fts5Dequote(z); | |||
| 7851 | for(iCol=0; iCol<pConfig->nCol; iCol++){ | |||
| 7852 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(pConfig->azCol[iCol], z) ) break; | |||
| 7853 | } | |||
| 7854 | if( iCol==pConfig->nCol ){ | |||
| 7855 | sqlite3Fts5ParseError(pParse, "no such column: %s", z); | |||
| 7856 | }else{ | |||
| 7857 | pRet = fts5ParseColset(pParse, pColset, iCol); | |||
| 7858 | } | |||
| 7859 | sqlite3_freesqlite3_api->free(z); | |||
| 7860 | } | |||
| 7861 | ||||
| 7862 | if( pRet==0 ){ | |||
| 7863 | assert( pParse->rc!=SQLITE_OK )((void) (0)); | |||
| 7864 | sqlite3_freesqlite3_api->free(pColset); | |||
| 7865 | } | |||
| 7866 | ||||
| 7867 | return pRet; | |||
| 7868 | } | |||
| 7869 | ||||
| 7870 | /* | |||
| 7871 | ** If argument pOrig is NULL, or if (*pRc) is set to anything other than | |||
| 7872 | ** SQLITE_OK when this function is called, NULL is returned. | |||
| 7873 | ** | |||
| 7874 | ** Otherwise, a copy of (*pOrig) is made into memory obtained from | |||
| 7875 | ** sqlite3Fts5MallocZero() and a pointer to it returned. If the allocation | |||
| 7876 | ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. | |||
| 7877 | */ | |||
| 7878 | static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ | |||
| 7879 | Fts5Colset *pRet; | |||
| 7880 | if( pOrig ){ | |||
| 7881 | sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol)(sizeof(i64)*((pOrig->nCol+2)/2)); | |||
| 7882 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); | |||
| 7883 | if( pRet ){ | |||
| 7884 | memcpy(pRet, pOrig, (size_t)nByte); | |||
| 7885 | } | |||
| 7886 | }else{ | |||
| 7887 | pRet = 0; | |||
| 7888 | } | |||
| 7889 | return pRet; | |||
| 7890 | } | |||
| 7891 | ||||
| 7892 | /* | |||
| 7893 | ** Remove from colset pColset any columns that are not also in colset pMerge. | |||
| 7894 | */ | |||
| 7895 | static void fts5MergeColset(Fts5Colset *pColset, Fts5Colset *pMerge){ | |||
| 7896 | int iIn = 0; /* Next input in pColset */ | |||
| 7897 | int iMerge = 0; /* Next input in pMerge */ | |||
| 7898 | int iOut = 0; /* Next output slot in pColset */ | |||
| 7899 | ||||
| 7900 | while( iIn<pColset->nCol && iMerge<pMerge->nCol ){ | |||
| 7901 | int iDiff = pColset->aiCol[iIn] - pMerge->aiCol[iMerge]; | |||
| 7902 | if( iDiff==0 ){ | |||
| 7903 | pColset->aiCol[iOut++] = pMerge->aiCol[iMerge]; | |||
| 7904 | iMerge++; | |||
| 7905 | iIn++; | |||
| 7906 | }else if( iDiff>0 ){ | |||
| 7907 | iMerge++; | |||
| 7908 | }else{ | |||
| 7909 | iIn++; | |||
| 7910 | } | |||
| 7911 | } | |||
| 7912 | pColset->nCol = iOut; | |||
| 7913 | } | |||
| 7914 | ||||
| 7915 | /* | |||
| 7916 | ** Recursively apply colset pColset to expression node pNode and all of | |||
| 7917 | ** its decendents. If (*ppFree) is not NULL, it contains a spare copy | |||
| 7918 | ** of pColset. This function may use the spare copy and set (*ppFree) to | |||
| 7919 | ** zero, or it may create copies of pColset using fts5CloneColset(). | |||
| 7920 | */ | |||
| 7921 | static void fts5ParseSetColset( | |||
| 7922 | Fts5Parse *pParse, | |||
| 7923 | Fts5ExprNode *pNode, | |||
| 7924 | Fts5Colset *pColset, | |||
| 7925 | Fts5Colset **ppFree | |||
| 7926 | ){ | |||
| 7927 | if( pParse->rc==SQLITE_OK0 ){ | |||
| 7928 | assert( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING((void) (0)) | |||
| 7929 | || pNode->eType==FTS5_AND || pNode->eType==FTS5_OR((void) (0)) | |||
| 7930 | || pNode->eType==FTS5_NOT || pNode->eType==FTS5_EOF((void) (0)) | |||
| 7931 | )((void) (0)); | |||
| 7932 | if( pNode->eType==FTS5_STRING9 || pNode->eType==FTS5_TERM4 ){ | |||
| 7933 | Fts5ExprNearset *pNear = pNode->pNear; | |||
| 7934 | if( pNear->pColset ){ | |||
| 7935 | fts5MergeColset(pNear->pColset, pColset); | |||
| 7936 | if( pNear->pColset->nCol==0 ){ | |||
| 7937 | pNode->eType = FTS5_EOF0; | |||
| 7938 | pNode->xNext = 0; | |||
| 7939 | } | |||
| 7940 | }else if( *ppFree ){ | |||
| 7941 | pNear->pColset = pColset; | |||
| 7942 | *ppFree = 0; | |||
| 7943 | }else{ | |||
| 7944 | pNear->pColset = fts5CloneColset(&pParse->rc, pColset); | |||
| 7945 | } | |||
| 7946 | }else{ | |||
| 7947 | int i; | |||
| 7948 | assert( pNode->eType!=FTS5_EOF || pNode->nChild==0 )((void) (0)); | |||
| 7949 | for(i=0; i<pNode->nChild; i++){ | |||
| 7950 | fts5ParseSetColset(pParse, pNode->apChild[i], pColset, ppFree); | |||
| 7951 | } | |||
| 7952 | } | |||
| 7953 | } | |||
| 7954 | } | |||
| 7955 | ||||
| 7956 | /* | |||
| 7957 | ** Apply colset pColset to expression node pExpr and all of its descendents. | |||
| 7958 | */ | |||
| 7959 | static void sqlite3Fts5ParseSetColset( | |||
| 7960 | Fts5Parse *pParse, | |||
| 7961 | Fts5ExprNode *pExpr, | |||
| 7962 | Fts5Colset *pColset | |||
| 7963 | ){ | |||
| 7964 | Fts5Colset *pFree = pColset; | |||
| 7965 | if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 7966 | sqlite3Fts5ParseError(pParse, | |||
| 7967 | "fts5: column queries are not supported (detail=none)" | |||
| 7968 | ); | |||
| 7969 | }else{ | |||
| 7970 | fts5ParseSetColset(pParse, pExpr, pColset, &pFree); | |||
| 7971 | } | |||
| 7972 | sqlite3_freesqlite3_api->free(pFree); | |||
| 7973 | } | |||
| 7974 | ||||
| 7975 | static void fts5ExprAssignXNext(Fts5ExprNode *pNode){ | |||
| 7976 | switch( pNode->eType ){ | |||
| 7977 | case FTS5_STRING9: { | |||
| 7978 | Fts5ExprNearset *pNear = pNode->pNear; | |||
| 7979 | if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 | |||
| 7980 | && pNear->apPhrase[0]->aTerm[0].pSynonym==0 | |||
| 7981 | && pNear->apPhrase[0]->aTerm[0].bFirst==0 | |||
| 7982 | ){ | |||
| 7983 | pNode->eType = FTS5_TERM4; | |||
| 7984 | pNode->xNext = fts5ExprNodeNext_TERM; | |||
| 7985 | }else{ | |||
| 7986 | pNode->xNext = fts5ExprNodeNext_STRING; | |||
| 7987 | } | |||
| 7988 | break; | |||
| 7989 | }; | |||
| 7990 | ||||
| 7991 | case FTS5_OR1: { | |||
| 7992 | pNode->xNext = fts5ExprNodeNext_OR; | |||
| 7993 | break; | |||
| 7994 | }; | |||
| 7995 | ||||
| 7996 | case FTS5_AND2: { | |||
| 7997 | pNode->xNext = fts5ExprNodeNext_AND; | |||
| 7998 | break; | |||
| 7999 | }; | |||
| 8000 | ||||
| 8001 | default: assert( pNode->eType==FTS5_NOT )((void) (0)); { | |||
| 8002 | pNode->xNext = fts5ExprNodeNext_NOT; | |||
| 8003 | break; | |||
| 8004 | }; | |||
| 8005 | } | |||
| 8006 | } | |||
| 8007 | ||||
| 8008 | /* | |||
| 8009 | ** Add pSub as a child of p. | |||
| 8010 | */ | |||
| 8011 | static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ | |||
| 8012 | int ii = p->nChild; | |||
| 8013 | if( p->eType!=FTS5_NOT3 && pSub->eType==p->eType ){ | |||
| 8014 | int nByte = sizeof(Fts5ExprNode*) * pSub->nChild; | |||
| 8015 | memcpy(&p->apChild[p->nChild], pSub->apChild, nByte); | |||
| 8016 | p->nChild += pSub->nChild; | |||
| 8017 | sqlite3_freesqlite3_api->free(pSub); | |||
| 8018 | }else{ | |||
| 8019 | p->apChild[p->nChild++] = pSub; | |||
| 8020 | } | |||
| 8021 | for( ; ii<p->nChild; ii++){ | |||
| 8022 | p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1)(((p->iHeight) > (p->apChild[ii]->iHeight + 1)) ? (p->iHeight) : (p->apChild[ii]->iHeight + 1)); | |||
| 8023 | } | |||
| 8024 | } | |||
| 8025 | ||||
| 8026 | /* | |||
| 8027 | ** This function is used when parsing LIKE or GLOB patterns against | |||
| 8028 | ** trigram indexes that specify either detail=column or detail=none. | |||
| 8029 | ** It converts a phrase: | |||
| 8030 | ** | |||
| 8031 | ** abc + def + ghi | |||
| 8032 | ** | |||
| 8033 | ** into an AND tree: | |||
| 8034 | ** | |||
| 8035 | ** abc AND def AND ghi | |||
| 8036 | */ | |||
| 8037 | static Fts5ExprNode *fts5ParsePhraseToAnd( | |||
| 8038 | Fts5Parse *pParse, | |||
| 8039 | Fts5ExprNearset *pNear | |||
| 8040 | ){ | |||
| 8041 | int nTerm = pNear->apPhrase[0]->nTerm; | |||
| 8042 | int ii; | |||
| 8043 | int nByte; | |||
| 8044 | Fts5ExprNode *pRet; | |||
| 8045 | ||||
| 8046 | assert( pNear->nPhrase==1 )((void) (0)); | |||
| 8047 | assert( pParse->bPhraseToAnd )((void) (0)); | |||
| 8048 | ||||
| 8049 | nByte = SZ_FTS5EXPRNODE(nTerm+1)(__builtin_offsetof(Fts5ExprNode, apChild) + (nTerm+1)*sizeof (Fts5ExprNode*)); | |||
| 8050 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); | |||
| 8051 | if( pRet ){ | |||
| 8052 | pRet->eType = FTS5_AND2; | |||
| 8053 | pRet->nChild = nTerm; | |||
| 8054 | pRet->iHeight = 1; | |||
| 8055 | fts5ExprAssignXNext(pRet); | |||
| 8056 | pParse->nPhrase--; | |||
| 8057 | for(ii=0; ii<nTerm; ii++){ | |||
| 8058 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( | |||
| 8059 | &pParse->rc, SZ_FTS5EXPRPHRASE(1)(__builtin_offsetof(Fts5ExprPhrase, aTerm) + (1)*sizeof(Fts5ExprTerm )) | |||
| 8060 | ); | |||
| 8061 | if( pPhrase ){ | |||
| 8062 | if( parseGrowPhraseArray(pParse) ){ | |||
| 8063 | fts5ExprPhraseFree(pPhrase); | |||
| 8064 | }else{ | |||
| 8065 | Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii]; | |||
| 8066 | Fts5ExprTerm *pTo = &pPhrase->aTerm[0]; | |||
| 8067 | pParse->apPhrase[pParse->nPhrase++] = pPhrase; | |||
| 8068 | pPhrase->nTerm = 1; | |||
| 8069 | pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm); | |||
| 8070 | pTo->nQueryTerm = p->nQueryTerm; | |||
| 8071 | pTo->nFullTerm = p->nFullTerm; | |||
| 8072 | pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING9, | |||
| 8073 | 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) | |||
| 8074 | ); | |||
| 8075 | } | |||
| 8076 | } | |||
| 8077 | } | |||
| 8078 | ||||
| 8079 | if( pParse->rc ){ | |||
| 8080 | sqlite3Fts5ParseNodeFree(pRet); | |||
| 8081 | pRet = 0; | |||
| 8082 | }else{ | |||
| 8083 | sqlite3Fts5ParseNearsetFree(pNear); | |||
| 8084 | } | |||
| 8085 | } | |||
| 8086 | ||||
| 8087 | return pRet; | |||
| 8088 | } | |||
| 8089 | ||||
| 8090 | /* | |||
| 8091 | ** Allocate and return a new expression object. If anything goes wrong (i.e. | |||
| 8092 | ** OOM error), leave an error code in pParse and return NULL. | |||
| 8093 | */ | |||
| 8094 | static Fts5ExprNode *sqlite3Fts5ParseNode( | |||
| 8095 | Fts5Parse *pParse, /* Parse context */ | |||
| 8096 | int eType, /* FTS5_STRING, AND, OR or NOT */ | |||
| 8097 | Fts5ExprNode *pLeft, /* Left hand child expression */ | |||
| 8098 | Fts5ExprNode *pRight, /* Right hand child expression */ | |||
| 8099 | Fts5ExprNearset *pNear /* For STRING expressions, the near cluster */ | |||
| 8100 | ){ | |||
| 8101 | Fts5ExprNode *pRet = 0; | |||
| 8102 | ||||
| 8103 | if( pParse->rc==SQLITE_OK0 ){ | |||
| 8104 | int nChild = 0; /* Number of children of returned node */ | |||
| 8105 | sqlite3_int64 nByte; /* Bytes of space to allocate for this node */ | |||
| 8106 | ||||
| 8107 | assert( (eType!=FTS5_STRING && !pNear)((void) (0)) | |||
| 8108 | || (eType==FTS5_STRING && !pLeft && !pRight)((void) (0)) | |||
| 8109 | )((void) (0)); | |||
| 8110 | if( eType==FTS5_STRING9 && pNear==0 ) return 0; | |||
| 8111 | if( eType!=FTS5_STRING9 && pLeft==0 ) return pRight; | |||
| 8112 | if( eType!=FTS5_STRING9 && pRight==0 ) return pLeft; | |||
| 8113 | ||||
| 8114 | if( eType==FTS5_STRING9 | |||
| 8115 | && pParse->bPhraseToAnd | |||
| 8116 | && pNear->apPhrase[0]->nTerm>1 | |||
| 8117 | ){ | |||
| 8118 | pRet = fts5ParsePhraseToAnd(pParse, pNear); | |||
| 8119 | }else{ | |||
| 8120 | if( eType==FTS5_NOT3 ){ | |||
| 8121 | nChild = 2; | |||
| 8122 | }else if( eType==FTS5_AND2 || eType==FTS5_OR1 ){ | |||
| 8123 | nChild = 2; | |||
| 8124 | if( pLeft->eType==eType ) nChild += pLeft->nChild-1; | |||
| 8125 | if( pRight->eType==eType ) nChild += pRight->nChild-1; | |||
| 8126 | } | |||
| 8127 | ||||
| 8128 | nByte = SZ_FTS5EXPRNODE(nChild)(__builtin_offsetof(Fts5ExprNode, apChild) + (nChild)*sizeof( Fts5ExprNode*)); | |||
| 8129 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); | |||
| 8130 | ||||
| 8131 | if( pRet ){ | |||
| 8132 | pRet->eType = eType; | |||
| 8133 | pRet->pNear = pNear; | |||
| 8134 | fts5ExprAssignXNext(pRet); | |||
| 8135 | if( eType==FTS5_STRING9 ){ | |||
| 8136 | int iPhrase; | |||
| 8137 | for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){ | |||
| 8138 | pNear->apPhrase[iPhrase]->pNode = pRet; | |||
| 8139 | if( pNear->apPhrase[iPhrase]->nTerm==0 ){ | |||
| 8140 | pRet->xNext = 0; | |||
| 8141 | pRet->eType = FTS5_EOF0; | |||
| 8142 | } | |||
| 8143 | } | |||
| 8144 | ||||
| 8145 | if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL0 ){ | |||
| 8146 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; | |||
| 8147 | if( pNear->nPhrase!=1 | |||
| 8148 | || pPhrase->nTerm>1 | |||
| 8149 | || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) | |||
| 8150 | ){ | |||
| 8151 | sqlite3Fts5ParseError(pParse, | |||
| 8152 | "fts5: %s queries are not supported (detail!=full)", | |||
| 8153 | pNear->nPhrase==1 ? "phrase": "NEAR" | |||
| 8154 | ); | |||
| 8155 | sqlite3Fts5ParseNodeFree(pRet); | |||
| 8156 | pRet = 0; | |||
| 8157 | pNear = 0; | |||
| 8158 | assert( pLeft==0 && pRight==0 )((void) (0)); | |||
| 8159 | } | |||
| 8160 | } | |||
| 8161 | }else{ | |||
| 8162 | assert( pNear==0 )((void) (0)); | |||
| 8163 | fts5ExprAddChildren(pRet, pLeft); | |||
| 8164 | fts5ExprAddChildren(pRet, pRight); | |||
| 8165 | pLeft = pRight = 0; | |||
| 8166 | if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH256 ){ | |||
| 8167 | sqlite3Fts5ParseError(pParse, | |||
| 8168 | "fts5 expression tree is too large (maximum depth %d)", | |||
| 8169 | SQLITE_FTS5_MAX_EXPR_DEPTH256 | |||
| 8170 | ); | |||
| 8171 | sqlite3Fts5ParseNodeFree(pRet); | |||
| 8172 | pRet = 0; | |||
| 8173 | } | |||
| 8174 | } | |||
| 8175 | } | |||
| 8176 | } | |||
| 8177 | } | |||
| 8178 | ||||
| 8179 | if( pRet==0 ){ | |||
| 8180 | assert( pParse->rc!=SQLITE_OK )((void) (0)); | |||
| 8181 | sqlite3Fts5ParseNodeFree(pLeft); | |||
| 8182 | sqlite3Fts5ParseNodeFree(pRight); | |||
| 8183 | sqlite3Fts5ParseNearsetFree(pNear); | |||
| 8184 | } | |||
| 8185 | return pRet; | |||
| 8186 | } | |||
| 8187 | ||||
| 8188 | static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( | |||
| 8189 | Fts5Parse *pParse, /* Parse context */ | |||
| 8190 | Fts5ExprNode *pLeft, /* Left hand child expression */ | |||
| 8191 | Fts5ExprNode *pRight /* Right hand child expression */ | |||
| 8192 | ){ | |||
| 8193 | Fts5ExprNode *pRet = 0; | |||
| 8194 | Fts5ExprNode *pPrev; | |||
| 8195 | ||||
| 8196 | if( pParse->rc ){ | |||
| 8197 | sqlite3Fts5ParseNodeFree(pLeft); | |||
| 8198 | sqlite3Fts5ParseNodeFree(pRight); | |||
| 8199 | }else{ | |||
| 8200 | ||||
| 8201 | assert( pLeft->eType==FTS5_STRING((void) (0)) | |||
| 8202 | || pLeft->eType==FTS5_TERM((void) (0)) | |||
| 8203 | || pLeft->eType==FTS5_EOF((void) (0)) | |||
| 8204 | || pLeft->eType==FTS5_AND((void) (0)) | |||
| 8205 | )((void) (0)); | |||
| 8206 | assert( pRight->eType==FTS5_STRING((void) (0)) | |||
| 8207 | || pRight->eType==FTS5_TERM((void) (0)) | |||
| 8208 | || pRight->eType==FTS5_EOF((void) (0)) | |||
| 8209 | || (pRight->eType==FTS5_AND && pParse->bPhraseToAnd)((void) (0)) | |||
| 8210 | )((void) (0)); | |||
| 8211 | ||||
| 8212 | if( pLeft->eType==FTS5_AND2 ){ | |||
| 8213 | pPrev = pLeft->apChild[pLeft->nChild-1]; | |||
| 8214 | }else{ | |||
| 8215 | pPrev = pLeft; | |||
| 8216 | } | |||
| 8217 | assert( pPrev->eType==FTS5_STRING((void) (0)) | |||
| 8218 | || pPrev->eType==FTS5_TERM((void) (0)) | |||
| 8219 | || pPrev->eType==FTS5_EOF((void) (0)) | |||
| 8220 | )((void) (0)); | |||
| 8221 | ||||
| 8222 | if( pRight->eType==FTS5_EOF0 ){ | |||
| 8223 | assert( pParse->apPhrase!=0 )((void) (0)); | |||
| 8224 | assert( pParse->nPhrase>0 )((void) (0)); | |||
| 8225 | assert( pParse->apPhrase[pParse->nPhrase-1]==pRight->pNear->apPhrase[0] )((void) (0)); | |||
| 8226 | sqlite3Fts5ParseNodeFree(pRight); | |||
| 8227 | pRet = pLeft; | |||
| 8228 | pParse->nPhrase--; | |||
| 8229 | } | |||
| 8230 | else if( pPrev->eType==FTS5_EOF0 ){ | |||
| 8231 | Fts5ExprPhrase **ap; | |||
| 8232 | ||||
| 8233 | if( pPrev==pLeft ){ | |||
| 8234 | pRet = pRight; | |||
| 8235 | }else{ | |||
| 8236 | pLeft->apChild[pLeft->nChild-1] = pRight; | |||
| 8237 | pRet = pLeft; | |||
| 8238 | } | |||
| 8239 | ||||
| 8240 | ap = &pParse->apPhrase[pParse->nPhrase-1-pRight->pNear->nPhrase]; | |||
| 8241 | assert( ap[0]==pPrev->pNear->apPhrase[0] )((void) (0)); | |||
| 8242 | memmove(ap, &ap[1], sizeof(Fts5ExprPhrase*)*pRight->pNear->nPhrase); | |||
| 8243 | pParse->nPhrase--; | |||
| 8244 | ||||
| 8245 | sqlite3Fts5ParseNodeFree(pPrev); | |||
| 8246 | } | |||
| 8247 | else{ | |||
| 8248 | pRet = sqlite3Fts5ParseNode(pParse, FTS5_AND2, pLeft, pRight, 0); | |||
| 8249 | } | |||
| 8250 | } | |||
| 8251 | ||||
| 8252 | return pRet; | |||
| 8253 | } | |||
| 8254 | ||||
| 8255 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 8256 | static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ | |||
| 8257 | sqlite3_int64 nByte = 0; | |||
| 8258 | Fts5ExprTerm *p; | |||
| 8259 | char *zQuoted; | |||
| 8260 | ||||
| 8261 | /* Determine the maximum amount of space required. */ | |||
| 8262 | for(p=pTerm; p; p=p->pSynonym){ | |||
| 8263 | nByte += pTerm->nQueryTerm * 2 + 3 + 2; | |||
| 8264 | } | |||
| 8265 | zQuoted = sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 8266 | ||||
| 8267 | if( zQuoted ){ | |||
| 8268 | int i = 0; | |||
| 8269 | for(p=pTerm; p; p=p->pSynonym){ | |||
| 8270 | char *zIn = p->pTerm; | |||
| 8271 | char *zEnd = &zIn[p->nQueryTerm]; | |||
| 8272 | zQuoted[i++] = '"'; | |||
| 8273 | while( zIn<zEnd ){ | |||
| 8274 | if( *zIn=='"' ) zQuoted[i++] = '"'; | |||
| 8275 | zQuoted[i++] = *zIn++; | |||
| 8276 | } | |||
| 8277 | zQuoted[i++] = '"'; | |||
| 8278 | if( p->pSynonym ) zQuoted[i++] = '|'; | |||
| 8279 | } | |||
| 8280 | if( pTerm->bPrefix ){ | |||
| 8281 | zQuoted[i++] = ' '; | |||
| 8282 | zQuoted[i++] = '*'; | |||
| 8283 | } | |||
| 8284 | zQuoted[i++] = '\0'; | |||
| 8285 | } | |||
| 8286 | return zQuoted; | |||
| 8287 | } | |||
| 8288 | ||||
| 8289 | static char *fts5PrintfAppend(char *zApp, const char *zFmt, ...){ | |||
| 8290 | char *zNew; | |||
| 8291 | va_list ap; | |||
| 8292 | va_start(ap, zFmt)__builtin_va_start(ap, zFmt); | |||
| 8293 | zNew = sqlite3_vmprintfsqlite3_api->vmprintf(zFmt, ap); | |||
| 8294 | va_end(ap)__builtin_va_end(ap); | |||
| 8295 | if( zApp && zNew ){ | |||
| 8296 | char *zNew2 = sqlite3_mprintfsqlite3_api->mprintf("%s%s", zApp, zNew); | |||
| 8297 | sqlite3_freesqlite3_api->free(zNew); | |||
| 8298 | zNew = zNew2; | |||
| 8299 | } | |||
| 8300 | sqlite3_freesqlite3_api->free(zApp); | |||
| 8301 | return zNew; | |||
| 8302 | } | |||
| 8303 | ||||
| 8304 | /* | |||
| 8305 | ** Compose a tcl-readable representation of expression pExpr. Return a | |||
| 8306 | ** pointer to a buffer containing that representation. It is the | |||
| 8307 | ** responsibility of the caller to at some point free the buffer using | |||
| 8308 | ** sqlite3_free(). | |||
| 8309 | */ | |||
| 8310 | static char *fts5ExprPrintTcl( | |||
| 8311 | Fts5Config *pConfig, | |||
| 8312 | const char *zNearsetCmd, | |||
| 8313 | Fts5ExprNode *pExpr | |||
| 8314 | ){ | |||
| 8315 | char *zRet = 0; | |||
| 8316 | if( pExpr->eType==FTS5_STRING9 || pExpr->eType==FTS5_TERM4 ){ | |||
| 8317 | Fts5ExprNearset *pNear = pExpr->pNear; | |||
| 8318 | int i; | |||
| 8319 | int iTerm; | |||
| 8320 | ||||
| 8321 | zRet = fts5PrintfAppend(zRet, "%s ", zNearsetCmd); | |||
| 8322 | if( zRet==0 ) return 0; | |||
| 8323 | if( pNear->pColset ){ | |||
| 8324 | int *aiCol = pNear->pColset->aiCol; | |||
| 8325 | int nCol = pNear->pColset->nCol; | |||
| 8326 | if( nCol==1 ){ | |||
| 8327 | zRet = fts5PrintfAppend(zRet, "-col %d ", aiCol[0]); | |||
| 8328 | }else{ | |||
| 8329 | zRet = fts5PrintfAppend(zRet, "-col {%d", aiCol[0]); | |||
| 8330 | for(i=1; i<pNear->pColset->nCol; i++){ | |||
| 8331 | zRet = fts5PrintfAppend(zRet, " %d", aiCol[i]); | |||
| 8332 | } | |||
| 8333 | zRet = fts5PrintfAppend(zRet, "} "); | |||
| 8334 | } | |||
| 8335 | if( zRet==0 ) return 0; | |||
| 8336 | } | |||
| 8337 | ||||
| 8338 | if( pNear->nPhrase>1 ){ | |||
| 8339 | zRet = fts5PrintfAppend(zRet, "-near %d ", pNear->nNear); | |||
| 8340 | if( zRet==0 ) return 0; | |||
| 8341 | } | |||
| 8342 | ||||
| 8343 | zRet = fts5PrintfAppend(zRet, "--"); | |||
| 8344 | if( zRet==0 ) return 0; | |||
| 8345 | ||||
| 8346 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 8347 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |||
| 8348 | ||||
| 8349 | zRet = fts5PrintfAppend(zRet, " {"); | |||
| 8350 | for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){ | |||
| 8351 | Fts5ExprTerm *p = &pPhrase->aTerm[iTerm]; | |||
| 8352 | zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ", | |||
| 8353 | p->nQueryTerm, p->pTerm | |||
| 8354 | ); | |||
| 8355 | if( pPhrase->aTerm[iTerm].bPrefix ){ | |||
| 8356 | zRet = fts5PrintfAppend(zRet, "*"); | |||
| 8357 | } | |||
| 8358 | } | |||
| 8359 | ||||
| 8360 | if( zRet ) zRet = fts5PrintfAppend(zRet, "}"); | |||
| 8361 | if( zRet==0 ) return 0; | |||
| 8362 | } | |||
| 8363 | ||||
| 8364 | }else if( pExpr->eType==0 ){ | |||
| 8365 | zRet = sqlite3_mprintfsqlite3_api->mprintf("{}"); | |||
| 8366 | }else{ | |||
| 8367 | char const *zOp = 0; | |||
| 8368 | int i; | |||
| 8369 | switch( pExpr->eType ){ | |||
| 8370 | case FTS5_AND2: zOp = "AND"; break; | |||
| 8371 | case FTS5_NOT3: zOp = "NOT"; break; | |||
| 8372 | default: | |||
| 8373 | assert( pExpr->eType==FTS5_OR )((void) (0)); | |||
| 8374 | zOp = "OR"; | |||
| 8375 | break; | |||
| 8376 | } | |||
| 8377 | ||||
| 8378 | zRet = sqlite3_mprintfsqlite3_api->mprintf("%s", zOp); | |||
| 8379 | for(i=0; zRet && i<pExpr->nChild; i++){ | |||
| 8380 | char *z = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->apChild[i]); | |||
| 8381 | if( !z ){ | |||
| 8382 | sqlite3_freesqlite3_api->free(zRet); | |||
| 8383 | zRet = 0; | |||
| 8384 | }else{ | |||
| 8385 | zRet = fts5PrintfAppend(zRet, " [%z]", z); | |||
| 8386 | } | |||
| 8387 | } | |||
| 8388 | } | |||
| 8389 | ||||
| 8390 | return zRet; | |||
| 8391 | } | |||
| 8392 | ||||
| 8393 | static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){ | |||
| 8394 | char *zRet = 0; | |||
| 8395 | if( pExpr->eType==0 ){ | |||
| 8396 | return sqlite3_mprintfsqlite3_api->mprintf("\"\""); | |||
| 8397 | }else | |||
| 8398 | if( pExpr->eType==FTS5_STRING9 || pExpr->eType==FTS5_TERM4 ){ | |||
| 8399 | Fts5ExprNearset *pNear = pExpr->pNear; | |||
| 8400 | int i; | |||
| 8401 | int iTerm; | |||
| 8402 | ||||
| 8403 | if( pNear->pColset ){ | |||
| 8404 | int ii; | |||
| 8405 | Fts5Colset *pColset = pNear->pColset; | |||
| 8406 | if( pColset->nCol>1 ) zRet = fts5PrintfAppend(zRet, "{"); | |||
| 8407 | for(ii=0; ii<pColset->nCol; ii++){ | |||
| 8408 | zRet = fts5PrintfAppend(zRet, "%s%s", | |||
| 8409 | pConfig->azCol[pColset->aiCol[ii]], ii==pColset->nCol-1 ? "" : " " | |||
| 8410 | ); | |||
| 8411 | } | |||
| 8412 | if( zRet ){ | |||
| 8413 | zRet = fts5PrintfAppend(zRet, "%s : ", pColset->nCol>1 ? "}" : ""); | |||
| 8414 | } | |||
| 8415 | if( zRet==0 ) return 0; | |||
| 8416 | } | |||
| 8417 | ||||
| 8418 | if( pNear->nPhrase>1 ){ | |||
| 8419 | zRet = fts5PrintfAppend(zRet, "NEAR("); | |||
| 8420 | if( zRet==0 ) return 0; | |||
| 8421 | } | |||
| 8422 | ||||
| 8423 | for(i=0; i<pNear->nPhrase; i++){ | |||
| 8424 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |||
| 8425 | if( i!=0 ){ | |||
| 8426 | zRet = fts5PrintfAppend(zRet, " "); | |||
| 8427 | if( zRet==0 ) return 0; | |||
| 8428 | } | |||
| 8429 | for(iTerm=0; iTerm<pPhrase->nTerm; iTerm++){ | |||
| 8430 | char *zTerm = fts5ExprTermPrint(&pPhrase->aTerm[iTerm]); | |||
| 8431 | if( zTerm ){ | |||
| 8432 | zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" + ", zTerm); | |||
| 8433 | sqlite3_freesqlite3_api->free(zTerm); | |||
| 8434 | } | |||
| 8435 | if( zTerm==0 || zRet==0 ){ | |||
| 8436 | sqlite3_freesqlite3_api->free(zRet); | |||
| 8437 | return 0; | |||
| 8438 | } | |||
| 8439 | } | |||
| 8440 | } | |||
| 8441 | ||||
| 8442 | if( pNear->nPhrase>1 ){ | |||
| 8443 | zRet = fts5PrintfAppend(zRet, ", %d)", pNear->nNear); | |||
| 8444 | if( zRet==0 ) return 0; | |||
| 8445 | } | |||
| 8446 | ||||
| 8447 | }else{ | |||
| 8448 | char const *zOp = 0; | |||
| 8449 | int i; | |||
| 8450 | ||||
| 8451 | switch( pExpr->eType ){ | |||
| 8452 | case FTS5_AND2: zOp = " AND "; break; | |||
| 8453 | case FTS5_NOT3: zOp = " NOT "; break; | |||
| 8454 | default: | |||
| 8455 | assert( pExpr->eType==FTS5_OR )((void) (0)); | |||
| 8456 | zOp = " OR "; | |||
| 8457 | break; | |||
| 8458 | } | |||
| 8459 | ||||
| 8460 | for(i=0; i<pExpr->nChild; i++){ | |||
| 8461 | char *z = fts5ExprPrint(pConfig, pExpr->apChild[i]); | |||
| 8462 | if( z==0 ){ | |||
| 8463 | sqlite3_freesqlite3_api->free(zRet); | |||
| 8464 | zRet = 0; | |||
| 8465 | }else{ | |||
| 8466 | int e = pExpr->apChild[i]->eType; | |||
| 8467 | int b = (e!=FTS5_STRING9 && e!=FTS5_TERM4 && e!=FTS5_EOF0); | |||
| 8468 | zRet = fts5PrintfAppend(zRet, "%s%s%z%s", | |||
| 8469 | (i==0 ? "" : zOp), | |||
| 8470 | (b?"(":""), z, (b?")":"") | |||
| 8471 | ); | |||
| 8472 | } | |||
| 8473 | if( zRet==0 ) break; | |||
| 8474 | } | |||
| 8475 | } | |||
| 8476 | ||||
| 8477 | return zRet; | |||
| 8478 | } | |||
| 8479 | ||||
| 8480 | /* | |||
| 8481 | ** The implementation of user-defined scalar functions fts5_expr() (bTcl==0) | |||
| 8482 | ** and fts5_expr_tcl() (bTcl!=0). | |||
| 8483 | */ | |||
| 8484 | static void fts5ExprFunction( | |||
| 8485 | sqlite3_context *pCtx, /* Function call context */ | |||
| 8486 | int nArg, /* Number of args */ | |||
| 8487 | sqlite3_value **apVal, /* Function arguments */ | |||
| 8488 | int bTcl | |||
| 8489 | ){ | |||
| 8490 | Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_datasqlite3_api->user_data(pCtx); | |||
| 8491 | sqlite3 *db = sqlite3_context_db_handlesqlite3_api->context_db_handle(pCtx); | |||
| 8492 | const char *zExpr = 0; | |||
| 8493 | char *zErr = 0; | |||
| 8494 | Fts5Expr *pExpr = 0; | |||
| 8495 | int rc; | |||
| 8496 | int i; | |||
| 8497 | ||||
| 8498 | const char **azConfig; /* Array of arguments for Fts5Config */ | |||
| 8499 | const char *zNearsetCmd = "nearset"; | |||
| 8500 | int nConfig; /* Size of azConfig[] */ | |||
| 8501 | Fts5Config *pConfig = 0; | |||
| 8502 | int iArg = 1; | |||
| 8503 | ||||
| 8504 | if( nArg<1 ){ | |||
| 8505 | zErr = sqlite3_mprintfsqlite3_api->mprintf("wrong number of arguments to function %s", | |||
| 8506 | bTcl ? "fts5_expr_tcl" : "fts5_expr" | |||
| 8507 | ); | |||
| 8508 | sqlite3_result_errorsqlite3_api->result_error(pCtx, zErr, -1); | |||
| 8509 | sqlite3_freesqlite3_api->free(zErr); | |||
| 8510 | return; | |||
| 8511 | } | |||
| 8512 | ||||
| 8513 | if( bTcl && nArg>1 ){ | |||
| 8514 | zNearsetCmd = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[1]); | |||
| 8515 | iArg = 2; | |||
| 8516 | } | |||
| 8517 | ||||
| 8518 | nConfig = 3 + (nArg-iArg); | |||
| 8519 | azConfig = (const char**)sqlite3_malloc64sqlite3_api->malloc64(sizeof(char*) * nConfig); | |||
| 8520 | if( azConfig==0 ){ | |||
| 8521 | sqlite3_result_error_nomemsqlite3_api->result_error_nomem(pCtx); | |||
| 8522 | return; | |||
| 8523 | } | |||
| 8524 | azConfig[0] = 0; | |||
| 8525 | azConfig[1] = "main"; | |||
| 8526 | azConfig[2] = "tbl"; | |||
| 8527 | for(i=3; iArg<nArg; iArg++){ | |||
| 8528 | const char *z = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[iArg]); | |||
| 8529 | azConfig[i++] = (z ? z : ""); | |||
| 8530 | } | |||
| 8531 | ||||
| 8532 | zExpr = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[0]); | |||
| 8533 | if( zExpr==0 ) zExpr = ""; | |||
| 8534 | ||||
| 8535 | rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr); | |||
| 8536 | if( rc==SQLITE_OK0 ){ | |||
| 8537 | rc = sqlite3Fts5ExprNew(pConfig, 0, pConfig->nCol, zExpr, &pExpr, &zErr); | |||
| 8538 | } | |||
| 8539 | if( rc==SQLITE_OK0 ){ | |||
| 8540 | char *zText; | |||
| 8541 | if( pExpr->pRoot->xNext==0 ){ | |||
| 8542 | zText = sqlite3_mprintfsqlite3_api->mprintf(""); | |||
| 8543 | }else if( bTcl ){ | |||
| 8544 | zText = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pRoot); | |||
| 8545 | }else{ | |||
| 8546 | zText = fts5ExprPrint(pConfig, pExpr->pRoot); | |||
| 8547 | } | |||
| 8548 | if( zText==0 ){ | |||
| 8549 | rc = SQLITE_NOMEM7; | |||
| 8550 | }else{ | |||
| 8551 | sqlite3_result_textsqlite3_api->result_text(pCtx, zText, -1, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 8552 | sqlite3_freesqlite3_api->free(zText); | |||
| 8553 | } | |||
| 8554 | } | |||
| 8555 | ||||
| 8556 | if( rc!=SQLITE_OK0 ){ | |||
| 8557 | if( zErr ){ | |||
| 8558 | sqlite3_result_errorsqlite3_api->result_error(pCtx, zErr, -1); | |||
| 8559 | sqlite3_freesqlite3_api->free(zErr); | |||
| 8560 | }else{ | |||
| 8561 | sqlite3_result_error_codesqlite3_api->result_error_code(pCtx, rc); | |||
| 8562 | } | |||
| 8563 | } | |||
| 8564 | sqlite3_freesqlite3_api->free((void *)azConfig); | |||
| 8565 | sqlite3Fts5ConfigFree(pConfig); | |||
| 8566 | sqlite3Fts5ExprFree(pExpr); | |||
| 8567 | } | |||
| 8568 | ||||
| 8569 | static void fts5ExprFunctionHr( | |||
| 8570 | sqlite3_context *pCtx, /* Function call context */ | |||
| 8571 | int nArg, /* Number of args */ | |||
| 8572 | sqlite3_value **apVal /* Function arguments */ | |||
| 8573 | ){ | |||
| 8574 | fts5ExprFunction(pCtx, nArg, apVal, 0); | |||
| 8575 | } | |||
| 8576 | static void fts5ExprFunctionTcl( | |||
| 8577 | sqlite3_context *pCtx, /* Function call context */ | |||
| 8578 | int nArg, /* Number of args */ | |||
| 8579 | sqlite3_value **apVal /* Function arguments */ | |||
| 8580 | ){ | |||
| 8581 | fts5ExprFunction(pCtx, nArg, apVal, 1); | |||
| 8582 | } | |||
| 8583 | ||||
| 8584 | /* | |||
| 8585 | ** The implementation of an SQLite user-defined-function that accepts a | |||
| 8586 | ** single integer as an argument. If the integer is an alpha-numeric | |||
| 8587 | ** unicode code point, 1 is returned. Otherwise 0. | |||
| 8588 | */ | |||
| 8589 | static void fts5ExprIsAlnum( | |||
| 8590 | sqlite3_context *pCtx, /* Function call context */ | |||
| 8591 | int nArg, /* Number of args */ | |||
| 8592 | sqlite3_value **apVal /* Function arguments */ | |||
| 8593 | ){ | |||
| 8594 | int iCode; | |||
| 8595 | u8 aArr[32]; | |||
| 8596 | if( nArg!=1 ){ | |||
| 8597 | sqlite3_result_errorsqlite3_api->result_error(pCtx, | |||
| 8598 | "wrong number of arguments to function fts5_isalnum", -1 | |||
| 8599 | ); | |||
| 8600 | return; | |||
| 8601 | } | |||
| 8602 | memset(aArr, 0, sizeof(aArr)); | |||
| 8603 | sqlite3Fts5UnicodeCatParse("L*", aArr); | |||
| 8604 | sqlite3Fts5UnicodeCatParse("N*", aArr); | |||
| 8605 | sqlite3Fts5UnicodeCatParse("Co", aArr); | |||
| 8606 | iCode = sqlite3_value_intsqlite3_api->value_int(apVal[0]); | |||
| 8607 | sqlite3_result_intsqlite3_api->result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory((u32)iCode)]); | |||
| 8608 | } | |||
| 8609 | ||||
| 8610 | static void fts5ExprFold( | |||
| 8611 | sqlite3_context *pCtx, /* Function call context */ | |||
| 8612 | int nArg, /* Number of args */ | |||
| 8613 | sqlite3_value **apVal /* Function arguments */ | |||
| 8614 | ){ | |||
| 8615 | if( nArg!=1 && nArg!=2 ){ | |||
| 8616 | sqlite3_result_errorsqlite3_api->result_error(pCtx, | |||
| 8617 | "wrong number of arguments to function fts5_fold", -1 | |||
| 8618 | ); | |||
| 8619 | }else{ | |||
| 8620 | int iCode; | |||
| 8621 | int bRemoveDiacritics = 0; | |||
| 8622 | iCode = sqlite3_value_intsqlite3_api->value_int(apVal[0]); | |||
| 8623 | if( nArg==2 ) bRemoveDiacritics = sqlite3_value_intsqlite3_api->value_int(apVal[1]); | |||
| 8624 | sqlite3_result_intsqlite3_api->result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics)); | |||
| 8625 | } | |||
| 8626 | } | |||
| 8627 | #endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 8628 | ||||
| 8629 | /* | |||
| 8630 | ** This is called during initialization to register the fts5_expr() scalar | |||
| 8631 | ** UDF with the SQLite handle passed as the only argument. | |||
| 8632 | */ | |||
| 8633 | static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ | |||
| 8634 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 8635 | struct Fts5ExprFunc { | |||
| 8636 | const char *z; | |||
| 8637 | void (*x)(sqlite3_context*,int,sqlite3_value**); | |||
| 8638 | } aFunc[] = { | |||
| 8639 | { "fts5_expr", fts5ExprFunctionHr }, | |||
| 8640 | { "fts5_expr_tcl", fts5ExprFunctionTcl }, | |||
| 8641 | { "fts5_isalnum", fts5ExprIsAlnum }, | |||
| 8642 | { "fts5_fold", fts5ExprFold }, | |||
| 8643 | }; | |||
| 8644 | int i; | |||
| 8645 | int rc = SQLITE_OK0; | |||
| 8646 | void *pCtx = (void*)pGlobal; | |||
| 8647 | ||||
| 8648 | for(i=0; rc==SQLITE_OK0 && i<ArraySize(aFunc)((int)(sizeof(aFunc) / sizeof(aFunc[0]))); i++){ | |||
| 8649 | struct Fts5ExprFunc *p = &aFunc[i]; | |||
| 8650 | rc = sqlite3_create_functionsqlite3_api->create_function(db, p->z, -1, SQLITE_UTF81, pCtx, p->x, 0, 0); | |||
| 8651 | } | |||
| 8652 | #else | |||
| 8653 | int rc = SQLITE_OK0; | |||
| 8654 | UNUSED_PARAM2(pGlobal,db)(void)(pGlobal), (void)(db); | |||
| 8655 | #endif | |||
| 8656 | ||||
| 8657 | /* Avoid warnings indicating that sqlite3Fts5ParserTrace() and | |||
| 8658 | ** sqlite3Fts5ParserFallback() are unused */ | |||
| 8659 | #ifndef NDEBUG1 | |||
| 8660 | (void)sqlite3Fts5ParserTrace; | |||
| 8661 | #endif | |||
| 8662 | (void)sqlite3Fts5ParserFallback; | |||
| 8663 | ||||
| 8664 | return rc; | |||
| 8665 | } | |||
| 8666 | ||||
| 8667 | /* | |||
| 8668 | ** Return the number of phrases in expression pExpr. | |||
| 8669 | */ | |||
| 8670 | static int sqlite3Fts5ExprPhraseCount(Fts5Expr *pExpr){ | |||
| 8671 | return (pExpr ? pExpr->nPhrase : 0); | |||
| 8672 | } | |||
| 8673 | ||||
| 8674 | /* | |||
| 8675 | ** Return the number of terms in the iPhrase'th phrase in pExpr. | |||
| 8676 | */ | |||
| 8677 | static int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){ | |||
| 8678 | if( iPhrase<0 || iPhrase>=pExpr->nPhrase ) return 0; | |||
| 8679 | return pExpr->apExprPhrase[iPhrase]->nTerm; | |||
| 8680 | } | |||
| 8681 | ||||
| 8682 | /* | |||
| 8683 | ** This function is used to access the current position list for phrase | |||
| 8684 | ** iPhrase. | |||
| 8685 | */ | |||
| 8686 | static int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){ | |||
| 8687 | int nRet; | |||
| 8688 | Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase]; | |||
| 8689 | Fts5ExprNode *pNode = pPhrase->pNode; | |||
| 8690 | if( pNode->bEof==0 && pNode->iRowid==pExpr->pRoot->iRowid ){ | |||
| 8691 | *pa = pPhrase->poslist.p; | |||
| 8692 | nRet = pPhrase->poslist.n; | |||
| 8693 | }else{ | |||
| 8694 | *pa = 0; | |||
| 8695 | nRet = 0; | |||
| 8696 | } | |||
| 8697 | return nRet; | |||
| 8698 | } | |||
| 8699 | ||||
| 8700 | struct Fts5PoslistPopulator { | |||
| 8701 | Fts5PoslistWriter writer; | |||
| 8702 | int bOk; /* True if ok to populate */ | |||
| 8703 | int bMiss; | |||
| 8704 | }; | |||
| 8705 | ||||
| 8706 | /* | |||
| 8707 | ** Clear the position lists associated with all phrases in the expression | |||
| 8708 | ** passed as the first argument. Argument bLive is true if the expression | |||
| 8709 | ** might be pointing to a real entry, otherwise it has just been reset. | |||
| 8710 | ** | |||
| 8711 | ** At present this function is only used for detail=col and detail=none | |||
| 8712 | ** fts5 tables. This implies that all phrases must be at most 1 token | |||
| 8713 | ** in size, as phrase matches are not supported without detail=full. | |||
| 8714 | */ | |||
| 8715 | static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ | |||
| 8716 | Fts5PoslistPopulator *pRet; | |||
| 8717 | pRet = sqlite3_malloc64sqlite3_api->malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); | |||
| 8718 | if( pRet ){ | |||
| 8719 | int i; | |||
| 8720 | memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); | |||
| 8721 | for(i=0; i<pExpr->nPhrase; i++){ | |||
| 8722 | Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist; | |||
| 8723 | Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; | |||
| 8724 | assert( pExpr->apExprPhrase[i]->nTerm<=1 )((void) (0)); | |||
| 8725 | if( bLive && | |||
| 8726 | (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof) | |||
| 8727 | ){ | |||
| 8728 | pRet[i].bMiss = 1; | |||
| 8729 | }else{ | |||
| 8730 | pBuf->n = 0; | |||
| 8731 | } | |||
| 8732 | } | |||
| 8733 | } | |||
| 8734 | return pRet; | |||
| 8735 | } | |||
| 8736 | ||||
| 8737 | struct Fts5ExprCtx { | |||
| 8738 | Fts5Expr *pExpr; | |||
| 8739 | Fts5PoslistPopulator *aPopulator; | |||
| 8740 | i64 iOff; | |||
| 8741 | }; | |||
| 8742 | typedef struct Fts5ExprCtx Fts5ExprCtx; | |||
| 8743 | ||||
| 8744 | /* | |||
| 8745 | ** TODO: Make this more efficient! | |||
| 8746 | */ | |||
| 8747 | static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ | |||
| 8748 | int i; | |||
| 8749 | for(i=0; i<pColset->nCol; i++){ | |||
| 8750 | if( pColset->aiCol[i]==iCol ) return 1; | |||
| 8751 | } | |||
| 8752 | return 0; | |||
| 8753 | } | |||
| 8754 | ||||
| 8755 | /* | |||
| 8756 | ** pToken is a buffer nToken bytes in size that may or may not contain | |||
| 8757 | ** an embedded 0x00 byte. If it does, return the number of bytes in | |||
| 8758 | ** the buffer before the 0x00. If it does not, return nToken. | |||
| 8759 | */ | |||
| 8760 | static int fts5QueryTerm(const char *pToken, int nToken){ | |||
| 8761 | int ii; | |||
| 8762 | for(ii=0; ii<nToken && pToken[ii]; ii++){} | |||
| 8763 | return ii; | |||
| 8764 | } | |||
| 8765 | ||||
| 8766 | static int fts5ExprPopulatePoslistsCb( | |||
| 8767 | void *pCtx, /* Copy of 2nd argument to xTokenize() */ | |||
| 8768 | int tflags, /* Mask of FTS5_TOKEN_* flags */ | |||
| 8769 | const char *pToken, /* Pointer to buffer containing token */ | |||
| 8770 | int nToken, /* Size of token in bytes */ | |||
| 8771 | int iUnused1, /* Byte offset of token within input text */ | |||
| 8772 | int iUnused2 /* Byte offset of end of token within input text */ | |||
| 8773 | ){ | |||
| 8774 | Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx; | |||
| 8775 | Fts5Expr *pExpr = p->pExpr; | |||
| 8776 | int i; | |||
| 8777 | int nQuery = nToken; | |||
| 8778 | i64 iRowid = pExpr->pRoot->iRowid; | |||
| 8779 | ||||
| 8780 | UNUSED_PARAM2(iUnused1, iUnused2)(void)(iUnused1), (void)(iUnused2); | |||
| 8781 | ||||
| 8782 | if( nQuery>FTS5_MAX_TOKEN_SIZE32768 ) nQuery = FTS5_MAX_TOKEN_SIZE32768; | |||
| 8783 | if( pExpr->pConfig->bTokendata ){ | |||
| 8784 | nQuery = fts5QueryTerm(pToken, nQuery); | |||
| 8785 | } | |||
| 8786 | if( (tflags & FTS5_TOKEN_COLOCATED0x0001)==0 ) p->iOff++; | |||
| 8787 | for(i=0; i<pExpr->nPhrase; i++){ | |||
| 8788 | Fts5ExprTerm *pT; | |||
| 8789 | if( p->aPopulator[i].bOk==0 ) continue; | |||
| 8790 | for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ | |||
| 8791 | if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix)) | |||
| 8792 | && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 | |||
| 8793 | ){ | |||
| 8794 | int rc = sqlite3Fts5PoslistWriterAppend( | |||
| 8795 | &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff | |||
| 8796 | ); | |||
| 8797 | if( rc==SQLITE_OK0 && (pExpr->pConfig->bTokendata || pT->bPrefix) ){ | |||
| 8798 | int iCol = p->iOff>>32; | |||
| 8799 | int iTokOff = p->iOff & 0x7FFFFFFF; | |||
| 8800 | rc = sqlite3Fts5IndexIterWriteTokendata( | |||
| 8801 | pT->pIter, pToken, nToken, iRowid, iCol, iTokOff | |||
| 8802 | ); | |||
| 8803 | } | |||
| 8804 | if( rc ) return rc; | |||
| 8805 | break; | |||
| 8806 | } | |||
| 8807 | } | |||
| 8808 | } | |||
| 8809 | return SQLITE_OK0; | |||
| 8810 | } | |||
| 8811 | ||||
| 8812 | static int sqlite3Fts5ExprPopulatePoslists( | |||
| 8813 | Fts5Config *pConfig, | |||
| 8814 | Fts5Expr *pExpr, | |||
| 8815 | Fts5PoslistPopulator *aPopulator, | |||
| 8816 | int iCol, | |||
| 8817 | const char *z, int n | |||
| 8818 | ){ | |||
| 8819 | int i; | |||
| 8820 | Fts5ExprCtx sCtx; | |||
| 8821 | sCtx.pExpr = pExpr; | |||
| 8822 | sCtx.aPopulator = aPopulator; | |||
| 8823 | sCtx.iOff = (((i64)iCol) << 32) - 1; | |||
| 8824 | ||||
| 8825 | for(i=0; i<pExpr->nPhrase; i++){ | |||
| 8826 | Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; | |||
| 8827 | Fts5Colset *pColset = pNode->pNear->pColset; | |||
| 8828 | if( (pColset && 0==fts5ExprColsetTest(pColset, iCol)) | |||
| 8829 | || aPopulator[i].bMiss | |||
| 8830 | ){ | |||
| 8831 | aPopulator[i].bOk = 0; | |||
| 8832 | }else{ | |||
| 8833 | aPopulator[i].bOk = 1; | |||
| 8834 | } | |||
| 8835 | } | |||
| 8836 | ||||
| 8837 | return sqlite3Fts5Tokenize(pConfig, | |||
| 8838 | FTS5_TOKENIZE_DOCUMENT0x0004, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb | |||
| 8839 | ); | |||
| 8840 | } | |||
| 8841 | ||||
| 8842 | static void fts5ExprClearPoslists(Fts5ExprNode *pNode){ | |||
| 8843 | if( pNode->eType==FTS5_TERM4 || pNode->eType==FTS5_STRING9 ){ | |||
| 8844 | pNode->pNear->apPhrase[0]->poslist.n = 0; | |||
| 8845 | }else{ | |||
| 8846 | int i; | |||
| 8847 | for(i=0; i<pNode->nChild; i++){ | |||
| 8848 | fts5ExprClearPoslists(pNode->apChild[i]); | |||
| 8849 | } | |||
| 8850 | } | |||
| 8851 | } | |||
| 8852 | ||||
| 8853 | static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){ | |||
| 8854 | pNode->iRowid = iRowid; | |||
| 8855 | pNode->bEof = 0; | |||
| 8856 | switch( pNode->eType ){ | |||
| 8857 | case 0: | |||
| 8858 | case FTS5_TERM4: | |||
| 8859 | case FTS5_STRING9: | |||
| 8860 | return (pNode->pNear->apPhrase[0]->poslist.n>0); | |||
| 8861 | ||||
| 8862 | case FTS5_AND2: { | |||
| 8863 | int i; | |||
| 8864 | for(i=0; i<pNode->nChild; i++){ | |||
| 8865 | if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){ | |||
| 8866 | fts5ExprClearPoslists(pNode); | |||
| 8867 | return 0; | |||
| 8868 | } | |||
| 8869 | } | |||
| 8870 | break; | |||
| 8871 | } | |||
| 8872 | ||||
| 8873 | case FTS5_OR1: { | |||
| 8874 | int i; | |||
| 8875 | int bRet = 0; | |||
| 8876 | for(i=0; i<pNode->nChild; i++){ | |||
| 8877 | if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){ | |||
| 8878 | bRet = 1; | |||
| 8879 | } | |||
| 8880 | } | |||
| 8881 | return bRet; | |||
| 8882 | } | |||
| 8883 | ||||
| 8884 | default: { | |||
| 8885 | assert( pNode->eType==FTS5_NOT )((void) (0)); | |||
| 8886 | if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid) | |||
| 8887 | || 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid) | |||
| 8888 | ){ | |||
| 8889 | fts5ExprClearPoslists(pNode); | |||
| 8890 | return 0; | |||
| 8891 | } | |||
| 8892 | break; | |||
| 8893 | } | |||
| 8894 | } | |||
| 8895 | return 1; | |||
| 8896 | } | |||
| 8897 | ||||
| 8898 | static void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){ | |||
| 8899 | fts5ExprCheckPoslists(pExpr->pRoot, iRowid); | |||
| 8900 | } | |||
| 8901 | ||||
| 8902 | /* | |||
| 8903 | ** This function is only called for detail=columns tables. | |||
| 8904 | */ | |||
| 8905 | static int sqlite3Fts5ExprPhraseCollist( | |||
| 8906 | Fts5Expr *pExpr, | |||
| 8907 | int iPhrase, | |||
| 8908 | const u8 **ppCollist, | |||
| 8909 | int *pnCollist | |||
| 8910 | ){ | |||
| 8911 | Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase]; | |||
| 8912 | Fts5ExprNode *pNode = pPhrase->pNode; | |||
| 8913 | int rc = SQLITE_OK0; | |||
| 8914 | ||||
| 8915 | assert( iPhrase>=0 && iPhrase<pExpr->nPhrase )((void) (0)); | |||
| 8916 | assert( pExpr->pConfig->eDetail==FTS5_DETAIL_COLUMNS )((void) (0)); | |||
| 8917 | ||||
| 8918 | if( pNode->bEof==0 | |||
| 8919 | && pNode->iRowid==pExpr->pRoot->iRowid | |||
| 8920 | && pPhrase->poslist.n>0 | |||
| 8921 | ){ | |||
| 8922 | Fts5ExprTerm *pTerm = &pPhrase->aTerm[0]; | |||
| 8923 | if( pTerm->pSynonym ){ | |||
| 8924 | Fts5Buffer *pBuf = (Fts5Buffer*)&pTerm->pSynonym[1]; | |||
| 8925 | rc = fts5ExprSynonymList( | |||
| 8926 | pTerm, pNode->iRowid, pBuf, (u8**)ppCollist, pnCollist | |||
| 8927 | ); | |||
| 8928 | }else{ | |||
| 8929 | *ppCollist = pPhrase->aTerm[0].pIter->pData; | |||
| 8930 | *pnCollist = pPhrase->aTerm[0].pIter->nData; | |||
| 8931 | } | |||
| 8932 | }else{ | |||
| 8933 | *ppCollist = 0; | |||
| 8934 | *pnCollist = 0; | |||
| 8935 | } | |||
| 8936 | ||||
| 8937 | return rc; | |||
| 8938 | } | |||
| 8939 | ||||
| 8940 | /* | |||
| 8941 | ** Does the work of the fts5_api.xQueryToken() API method. | |||
| 8942 | */ | |||
| 8943 | static int sqlite3Fts5ExprQueryToken( | |||
| 8944 | Fts5Expr *pExpr, | |||
| 8945 | int iPhrase, | |||
| 8946 | int iToken, | |||
| 8947 | const char **ppOut, | |||
| 8948 | int *pnOut | |||
| 8949 | ){ | |||
| 8950 | Fts5ExprPhrase *pPhrase = 0; | |||
| 8951 | ||||
| 8952 | if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ | |||
| 8953 | return SQLITE_RANGE25; | |||
| 8954 | } | |||
| 8955 | pPhrase = pExpr->apExprPhrase[iPhrase]; | |||
| 8956 | if( iToken<0 || iToken>=pPhrase->nTerm ){ | |||
| 8957 | return SQLITE_RANGE25; | |||
| 8958 | } | |||
| 8959 | ||||
| 8960 | *ppOut = pPhrase->aTerm[iToken].pTerm; | |||
| 8961 | *pnOut = pPhrase->aTerm[iToken].nFullTerm; | |||
| 8962 | return SQLITE_OK0; | |||
| 8963 | } | |||
| 8964 | ||||
| 8965 | /* | |||
| 8966 | ** Does the work of the fts5_api.xInstToken() API method. | |||
| 8967 | */ | |||
| 8968 | static int sqlite3Fts5ExprInstToken( | |||
| 8969 | Fts5Expr *pExpr, | |||
| 8970 | i64 iRowid, | |||
| 8971 | int iPhrase, | |||
| 8972 | int iCol, | |||
| 8973 | int iOff, | |||
| 8974 | int iToken, | |||
| 8975 | const char **ppOut, | |||
| 8976 | int *pnOut | |||
| 8977 | ){ | |||
| 8978 | Fts5ExprPhrase *pPhrase = 0; | |||
| 8979 | Fts5ExprTerm *pTerm = 0; | |||
| 8980 | int rc = SQLITE_OK0; | |||
| 8981 | ||||
| 8982 | if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ | |||
| 8983 | return SQLITE_RANGE25; | |||
| 8984 | } | |||
| 8985 | pPhrase = pExpr->apExprPhrase[iPhrase]; | |||
| 8986 | if( iToken<0 || iToken>=pPhrase->nTerm ){ | |||
| 8987 | return SQLITE_RANGE25; | |||
| 8988 | } | |||
| 8989 | pTerm = &pPhrase->aTerm[iToken]; | |||
| 8990 | if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){ | |||
| 8991 | rc = sqlite3Fts5IterToken( | |||
| 8992 | pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm, | |||
| 8993 | iRowid, iCol, iOff+iToken, ppOut, pnOut | |||
| 8994 | ); | |||
| 8995 | }else{ | |||
| 8996 | *ppOut = pTerm->pTerm; | |||
| 8997 | *pnOut = pTerm->nFullTerm; | |||
| 8998 | } | |||
| 8999 | return rc; | |||
| 9000 | } | |||
| 9001 | ||||
| 9002 | /* | |||
| 9003 | ** Clear the token mappings for all Fts5IndexIter objects managed by | |||
| 9004 | ** the expression passed as the only argument. | |||
| 9005 | */ | |||
| 9006 | static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ | |||
| 9007 | int ii; | |||
| 9008 | for(ii=0; ii<pExpr->nPhrase; ii++){ | |||
| 9009 | Fts5ExprTerm *pT; | |||
| 9010 | for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){ | |||
| 9011 | sqlite3Fts5IndexIterClearTokendata(pT->pIter); | |||
| 9012 | } | |||
| 9013 | } | |||
| 9014 | } | |||
| 9015 | ||||
| 9016 | #line 1 "fts5_hash.c" | |||
| 9017 | /* | |||
| 9018 | ** 2014 August 11 | |||
| 9019 | ** | |||
| 9020 | ** The author disclaims copyright to this source code. In place of | |||
| 9021 | ** a legal notice, here is a blessing: | |||
| 9022 | ** | |||
| 9023 | ** May you do good and not evil. | |||
| 9024 | ** May you find forgiveness for yourself and forgive others. | |||
| 9025 | ** May you share freely, never taking more than you give. | |||
| 9026 | ** | |||
| 9027 | ****************************************************************************** | |||
| 9028 | ** | |||
| 9029 | */ | |||
| 9030 | ||||
| 9031 | ||||
| 9032 | ||||
| 9033 | /* #include "fts5Int.h" */ | |||
| 9034 | ||||
| 9035 | typedef struct Fts5HashEntry Fts5HashEntry; | |||
| 9036 | ||||
| 9037 | /* | |||
| 9038 | ** This file contains the implementation of an in-memory hash table used | |||
| 9039 | ** to accumulate "term -> doclist" content before it is flushed to a level-0 | |||
| 9040 | ** segment. | |||
| 9041 | */ | |||
| 9042 | ||||
| 9043 | ||||
| 9044 | struct Fts5Hash { | |||
| 9045 | int eDetail; /* Copy of Fts5Config.eDetail */ | |||
| 9046 | int *pnByte; /* Pointer to bytes counter */ | |||
| 9047 | int nEntry; /* Number of entries currently in hash */ | |||
| 9048 | int nSlot; /* Size of aSlot[] array */ | |||
| 9049 | Fts5HashEntry *pScan; /* Current ordered scan item */ | |||
| 9050 | Fts5HashEntry **aSlot; /* Array of hash slots */ | |||
| 9051 | }; | |||
| 9052 | ||||
| 9053 | /* | |||
| 9054 | ** Each entry in the hash table is represented by an object of the | |||
| 9055 | ** following type. Each object, its key, and its current data are stored | |||
| 9056 | ** in a single memory allocation. The key immediately follows the object | |||
| 9057 | ** in memory. The position list data immediately follows the key data | |||
| 9058 | ** in memory. | |||
| 9059 | ** | |||
| 9060 | ** The key is Fts5HashEntry.nKey bytes in size. It consists of a single | |||
| 9061 | ** byte identifying the index (either the main term index or a prefix-index), | |||
| 9062 | ** followed by the term data. For example: "0token". There is no | |||
| 9063 | ** nul-terminator - in this case nKey=6. | |||
| 9064 | ** | |||
| 9065 | ** The data that follows the key is in a similar, but not identical format | |||
| 9066 | ** to the doclist data stored in the database. It is: | |||
| 9067 | ** | |||
| 9068 | ** * Rowid, as a varint | |||
| 9069 | ** * Position list, without 0x00 terminator. | |||
| 9070 | ** * Size of previous position list and rowid, as a 4 byte | |||
| 9071 | ** big-endian integer. | |||
| 9072 | ** | |||
| 9073 | ** iRowidOff: | |||
| 9074 | ** Offset of last rowid written to data area. Relative to first byte of | |||
| 9075 | ** structure. | |||
| 9076 | ** | |||
| 9077 | ** nData: | |||
| 9078 | ** Bytes of data written since iRowidOff. | |||
| 9079 | */ | |||
| 9080 | struct Fts5HashEntry { | |||
| 9081 | Fts5HashEntry *pHashNext; /* Next hash entry with same hash-key */ | |||
| 9082 | Fts5HashEntry *pScanNext; /* Next entry in sorted order */ | |||
| 9083 | ||||
| 9084 | int nAlloc; /* Total size of allocation */ | |||
| 9085 | int iSzPoslist; /* Offset of space for 4-byte poslist size */ | |||
| 9086 | int nData; /* Total bytes of data (incl. structure) */ | |||
| 9087 | int nKey; /* Length of key in bytes */ | |||
| 9088 | u8 bDel; /* Set delete-flag @ iSzPoslist */ | |||
| 9089 | u8 bContent; /* Set content-flag (detail=none mode) */ | |||
| 9090 | i16 iCol; /* Column of last value written */ | |||
| 9091 | int iPos; /* Position of last value written */ | |||
| 9092 | i64 iRowid; /* Rowid of last value written */ | |||
| 9093 | }; | |||
| 9094 | ||||
| 9095 | /* | |||
| 9096 | ** Equivalent to: | |||
| 9097 | ** | |||
| 9098 | ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } | |||
| 9099 | */ | |||
| 9100 | #define fts5EntryKey(p)( ((char *)(&(p)[1])) ) ( ((char *)(&(p)[1])) ) | |||
| 9101 | ||||
| 9102 | ||||
| 9103 | /* | |||
| 9104 | ** Allocate a new hash table. | |||
| 9105 | */ | |||
| 9106 | static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){ | |||
| 9107 | int rc = SQLITE_OK0; | |||
| 9108 | Fts5Hash *pNew; | |||
| 9109 | ||||
| 9110 | *ppNew = pNew = (Fts5Hash*)sqlite3_malloc64sqlite3_api->malloc64(sizeof(Fts5Hash)); | |||
| 9111 | if( pNew==0 ){ | |||
| 9112 | rc = SQLITE_NOMEM7; | |||
| 9113 | }else{ | |||
| 9114 | sqlite3_int64 nByte; | |||
| 9115 | memset(pNew, 0, sizeof(Fts5Hash)); | |||
| 9116 | pNew->pnByte = pnByte; | |||
| 9117 | pNew->eDetail = pConfig->eDetail; | |||
| 9118 | ||||
| 9119 | pNew->nSlot = 1024; | |||
| 9120 | nByte = sizeof(Fts5HashEntry*) * pNew->nSlot; | |||
| 9121 | pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 9122 | if( pNew->aSlot==0 ){ | |||
| 9123 | sqlite3_freesqlite3_api->free(pNew); | |||
| 9124 | *ppNew = 0; | |||
| 9125 | rc = SQLITE_NOMEM7; | |||
| 9126 | }else{ | |||
| 9127 | memset(pNew->aSlot, 0, (size_t)nByte); | |||
| 9128 | } | |||
| 9129 | } | |||
| 9130 | return rc; | |||
| 9131 | } | |||
| 9132 | ||||
| 9133 | /* | |||
| 9134 | ** Free a hash table object. | |||
| 9135 | */ | |||
| 9136 | static void sqlite3Fts5HashFree(Fts5Hash *pHash){ | |||
| 9137 | if( pHash ){ | |||
| 9138 | sqlite3Fts5HashClear(pHash); | |||
| 9139 | sqlite3_freesqlite3_api->free(pHash->aSlot); | |||
| 9140 | sqlite3_freesqlite3_api->free(pHash); | |||
| 9141 | } | |||
| 9142 | } | |||
| 9143 | ||||
| 9144 | /* | |||
| 9145 | ** Empty (but do not delete) a hash table. | |||
| 9146 | */ | |||
| 9147 | static void sqlite3Fts5HashClear(Fts5Hash *pHash){ | |||
| 9148 | int i; | |||
| 9149 | for(i=0; i<pHash->nSlot; i++){ | |||
| 9150 | Fts5HashEntry *pNext; | |||
| 9151 | Fts5HashEntry *pSlot; | |||
| 9152 | for(pSlot=pHash->aSlot[i]; pSlot; pSlot=pNext){ | |||
| 9153 | pNext = pSlot->pHashNext; | |||
| 9154 | sqlite3_freesqlite3_api->free(pSlot); | |||
| 9155 | } | |||
| 9156 | } | |||
| 9157 | memset(pHash->aSlot, 0, pHash->nSlot * sizeof(Fts5HashEntry*)); | |||
| 9158 | pHash->nEntry = 0; | |||
| 9159 | } | |||
| 9160 | ||||
| 9161 | static unsigned int fts5HashKey(int nSlot, const u8 *p, int n){ | |||
| 9162 | int i; | |||
| 9163 | unsigned int h = 13; | |||
| 9164 | for(i=n-1; i>=0; i--){ | |||
| 9165 | h = (h << 3) ^ h ^ p[i]; | |||
| 9166 | } | |||
| 9167 | return (h % nSlot); | |||
| 9168 | } | |||
| 9169 | ||||
| 9170 | static unsigned int fts5HashKey2(int nSlot, u8 b, const u8 *p, int n){ | |||
| 9171 | int i; | |||
| 9172 | unsigned int h = 13; | |||
| 9173 | for(i=n-1; i>=0; i--){ | |||
| 9174 | h = (h << 3) ^ h ^ p[i]; | |||
| 9175 | } | |||
| 9176 | h = (h << 3) ^ h ^ b; | |||
| 9177 | return (h % nSlot); | |||
| 9178 | } | |||
| 9179 | ||||
| 9180 | /* | |||
| 9181 | ** Resize the hash table by doubling the number of slots. | |||
| 9182 | */ | |||
| 9183 | static int fts5HashResize(Fts5Hash *pHash){ | |||
| 9184 | int nNew = pHash->nSlot*2; | |||
| 9185 | int i; | |||
| 9186 | Fts5HashEntry **apNew; | |||
| 9187 | Fts5HashEntry **apOld = pHash->aSlot; | |||
| 9188 | ||||
| 9189 | apNew = (Fts5HashEntry**)sqlite3_malloc64sqlite3_api->malloc64(nNew*sizeof(Fts5HashEntry*)); | |||
| 9190 | if( !apNew ) return SQLITE_NOMEM7; | |||
| 9191 | memset(apNew, 0, nNew*sizeof(Fts5HashEntry*)); | |||
| 9192 | ||||
| 9193 | for(i=0; i<pHash->nSlot; i++){ | |||
| 9194 | while( apOld[i] ){ | |||
| 9195 | unsigned int iHash; | |||
| 9196 | Fts5HashEntry *p = apOld[i]; | |||
| 9197 | apOld[i] = p->pHashNext; | |||
| 9198 | iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p)( ((char *)(&(p)[1])) ), p->nKey); | |||
| 9199 | p->pHashNext = apNew[iHash]; | |||
| 9200 | apNew[iHash] = p; | |||
| 9201 | } | |||
| 9202 | } | |||
| 9203 | ||||
| 9204 | sqlite3_freesqlite3_api->free(apOld); | |||
| 9205 | pHash->nSlot = nNew; | |||
| 9206 | pHash->aSlot = apNew; | |||
| 9207 | return SQLITE_OK0; | |||
| 9208 | } | |||
| 9209 | ||||
| 9210 | static int fts5HashAddPoslistSize( | |||
| 9211 | Fts5Hash *pHash, | |||
| 9212 | Fts5HashEntry *p, | |||
| 9213 | Fts5HashEntry *p2 | |||
| 9214 | ){ | |||
| 9215 | int nRet = 0; | |||
| 9216 | if( p->iSzPoslist ){ | |||
| 9217 | u8 *pPtr = p2 ? (u8*)p2 : (u8*)p; | |||
| 9218 | int nData = p->nData; | |||
| 9219 | if( pHash->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 9220 | assert( nData==p->iSzPoslist )((void) (0)); | |||
| 9221 | if( p->bDel ){ | |||
| 9222 | pPtr[nData++] = 0x00; | |||
| 9223 | if( p->bContent ){ | |||
| 9224 | pPtr[nData++] = 0x00; | |||
| 9225 | } | |||
| 9226 | } | |||
| 9227 | }else{ | |||
| 9228 | int nSz = (nData - p->iSzPoslist - 1); /* Size in bytes */ | |||
| 9229 | int nPos = nSz*2 + p->bDel; /* Value of nPos field */ | |||
| 9230 | ||||
| 9231 | assert( p->bDel==0 || p->bDel==1 )((void) (0)); | |||
| 9232 | if( nPos<=127 ){ | |||
| 9233 | pPtr[p->iSzPoslist] = (u8)nPos; | |||
| 9234 | }else{ | |||
| 9235 | int nByte = sqlite3Fts5GetVarintLen((u32)nPos); | |||
| 9236 | memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz); | |||
| 9237 | sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos); | |||
| 9238 | nData += (nByte-1); | |||
| 9239 | } | |||
| 9240 | } | |||
| 9241 | ||||
| 9242 | nRet = nData - p->nData; | |||
| 9243 | if( p2==0 ){ | |||
| 9244 | p->iSzPoslist = 0; | |||
| 9245 | p->bDel = 0; | |||
| 9246 | p->bContent = 0; | |||
| 9247 | p->nData = nData; | |||
| 9248 | } | |||
| 9249 | } | |||
| 9250 | return nRet; | |||
| 9251 | } | |||
| 9252 | ||||
| 9253 | /* | |||
| 9254 | ** Add an entry to the in-memory hash table. The key is the concatenation | |||
| 9255 | ** of bByte and (pToken/nToken). The value is (iRowid/iCol/iPos). | |||
| 9256 | ** | |||
| 9257 | ** (bByte || pToken) -> (iRowid,iCol,iPos) | |||
| 9258 | ** | |||
| 9259 | ** Or, if iCol is negative, then the value is a delete marker. | |||
| 9260 | */ | |||
| 9261 | static int sqlite3Fts5HashWrite( | |||
| 9262 | Fts5Hash *pHash, | |||
| 9263 | i64 iRowid, /* Rowid for this entry */ | |||
| 9264 | int iCol, /* Column token appears in (-ve -> delete) */ | |||
| 9265 | int iPos, /* Position of token within column */ | |||
| 9266 | char bByte, /* First byte of token */ | |||
| 9267 | const char *pToken, int nToken /* Token to add or remove to or from index */ | |||
| 9268 | ){ | |||
| 9269 | unsigned int iHash; | |||
| 9270 | Fts5HashEntry *p; | |||
| 9271 | u8 *pPtr; | |||
| 9272 | int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */ | |||
| 9273 | int bNew; /* If non-delete entry should be written */ | |||
| 9274 | ||||
| 9275 | bNew = (pHash->eDetail==FTS5_DETAIL_FULL0); | |||
| 9276 | ||||
| 9277 | /* Attempt to locate an existing hash entry */ | |||
| 9278 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); | |||
| 9279 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ | |||
| 9280 | char *zKey = fts5EntryKey(p)( ((char *)(&(p)[1])) ); | |||
| 9281 | if( zKey[0]==bByte | |||
| 9282 | && p->nKey==nToken+1 | |||
| 9283 | && memcmp(&zKey[1], pToken, nToken)==0 | |||
| 9284 | ){ | |||
| 9285 | break; | |||
| 9286 | } | |||
| 9287 | } | |||
| 9288 | ||||
| 9289 | /* If an existing hash entry cannot be found, create a new one. */ | |||
| 9290 | if( p==0 ){ | |||
| 9291 | /* Figure out how much space to allocate */ | |||
| 9292 | char *zKey; | |||
| 9293 | sqlite3_int64 nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64; | |||
| 9294 | if( nByte<128 ) nByte = 128; | |||
| 9295 | ||||
| 9296 | /* Grow the Fts5Hash.aSlot[] array if necessary. */ | |||
| 9297 | if( (pHash->nEntry*2)>=pHash->nSlot ){ | |||
| 9298 | int rc = fts5HashResize(pHash); | |||
| 9299 | if( rc!=SQLITE_OK0 ) return rc; | |||
| 9300 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); | |||
| 9301 | } | |||
| 9302 | ||||
| 9303 | /* Allocate new Fts5HashEntry and add it to the hash table. */ | |||
| 9304 | p = (Fts5HashEntry*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 9305 | if( !p ) return SQLITE_NOMEM7; | |||
| 9306 | memset(p, 0, sizeof(Fts5HashEntry)); | |||
| 9307 | p->nAlloc = (int)nByte; | |||
| 9308 | zKey = fts5EntryKey(p)( ((char *)(&(p)[1])) ); | |||
| 9309 | zKey[0] = bByte; | |||
| 9310 | memcpy(&zKey[1], pToken, nToken); | |||
| 9311 | assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) )((void) (0)); | |||
| 9312 | p->nKey = nToken+1; | |||
| 9313 | zKey[nToken+1] = '\0'; | |||
| 9314 | p->nData = nToken+1 + sizeof(Fts5HashEntry); | |||
| 9315 | p->pHashNext = pHash->aSlot[iHash]; | |||
| 9316 | pHash->aSlot[iHash] = p; | |||
| 9317 | pHash->nEntry++; | |||
| 9318 | ||||
| 9319 | /* Add the first rowid field to the hash-entry */ | |||
| 9320 | p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid); | |||
| 9321 | p->iRowid = iRowid; | |||
| 9322 | ||||
| 9323 | p->iSzPoslist = p->nData; | |||
| 9324 | if( pHash->eDetail!=FTS5_DETAIL_NONE1 ){ | |||
| 9325 | p->nData += 1; | |||
| 9326 | p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL0 ? 0 : -1); | |||
| 9327 | } | |||
| 9328 | ||||
| 9329 | }else{ | |||
| 9330 | ||||
| 9331 | /* Appending to an existing hash-entry. Check that there is enough | |||
| 9332 | ** space to append the largest possible new entry. Worst case scenario | |||
| 9333 | ** is: | |||
| 9334 | ** | |||
| 9335 | ** + 9 bytes for a new rowid, | |||
| 9336 | ** + 4 byte reserved for the "poslist size" varint. | |||
| 9337 | ** + 1 byte for a "new column" byte, | |||
| 9338 | ** + 3 bytes for a new column number (16-bit max) as a varint, | |||
| 9339 | ** + 5 bytes for the new position offset (32-bit max). | |||
| 9340 | */ | |||
| 9341 | if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){ | |||
| 9342 | sqlite3_int64 nNew = p->nAlloc * 2; | |||
| 9343 | Fts5HashEntry *pNew; | |||
| 9344 | Fts5HashEntry **pp; | |||
| 9345 | pNew = (Fts5HashEntry*)sqlite3_realloc64sqlite3_api->realloc64(p, nNew); | |||
| 9346 | if( pNew==0 ) return SQLITE_NOMEM7; | |||
| 9347 | pNew->nAlloc = (int)nNew; | |||
| 9348 | for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext); | |||
| 9349 | *pp = pNew; | |||
| 9350 | p = pNew; | |||
| 9351 | } | |||
| 9352 | nIncr -= p->nData; | |||
| 9353 | } | |||
| 9354 | assert( (p->nAlloc - p->nData) >= (9 + 4 + 1 + 3 + 5) )((void) (0)); | |||
| 9355 | ||||
| 9356 | pPtr = (u8*)p; | |||
| 9357 | ||||
| 9358 | /* If this is a new rowid, append the 4-byte size field for the previous | |||
| 9359 | ** entry, and the new rowid for this entry. */ | |||
| 9360 | if( iRowid!=p->iRowid ){ | |||
| 9361 | u64 iDiff = (u64)iRowid - (u64)p->iRowid; | |||
| 9362 | fts5HashAddPoslistSize(pHash, p, 0); | |||
| 9363 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iDiff); | |||
| 9364 | p->iRowid = iRowid; | |||
| 9365 | bNew = 1; | |||
| 9366 | p->iSzPoslist = p->nData; | |||
| 9367 | if( pHash->eDetail!=FTS5_DETAIL_NONE1 ){ | |||
| 9368 | p->nData += 1; | |||
| 9369 | p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL0 ? 0 : -1); | |||
| 9370 | p->iPos = 0; | |||
| 9371 | } | |||
| 9372 | } | |||
| 9373 | ||||
| 9374 | if( iCol>=0 ){ | |||
| 9375 | if( pHash->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 9376 | p->bContent = 1; | |||
| 9377 | }else{ | |||
| 9378 | /* Append a new column value, if necessary */ | |||
| 9379 | assert_nc( iCol>=p->iCol )((void) (0)); | |||
| 9380 | if( iCol!=p->iCol ){ | |||
| 9381 | if( pHash->eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 9382 | pPtr[p->nData++] = 0x01; | |||
| 9383 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol); | |||
| 9384 | p->iCol = (i16)iCol; | |||
| 9385 | p->iPos = 0; | |||
| 9386 | }else{ | |||
| 9387 | bNew = 1; | |||
| 9388 | p->iCol = (i16)(iPos = iCol); | |||
| 9389 | } | |||
| 9390 | } | |||
| 9391 | ||||
| 9392 | /* Append the new position offset, if necessary */ | |||
| 9393 | if( bNew ){ | |||
| 9394 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2); | |||
| 9395 | p->iPos = iPos; | |||
| 9396 | } | |||
| 9397 | } | |||
| 9398 | }else{ | |||
| 9399 | /* This is a delete. Set the delete flag. */ | |||
| 9400 | p->bDel = 1; | |||
| 9401 | } | |||
| 9402 | ||||
| 9403 | nIncr += p->nData; | |||
| 9404 | *pHash->pnByte += nIncr; | |||
| 9405 | return SQLITE_OK0; | |||
| 9406 | } | |||
| 9407 | ||||
| 9408 | ||||
| 9409 | /* | |||
| 9410 | ** Arguments pLeft and pRight point to linked-lists of hash-entry objects, | |||
| 9411 | ** each sorted in key order. This function merges the two lists into a | |||
| 9412 | ** single list and returns a pointer to its first element. | |||
| 9413 | */ | |||
| 9414 | static Fts5HashEntry *fts5HashEntryMerge( | |||
| 9415 | Fts5HashEntry *pLeft, | |||
| 9416 | Fts5HashEntry *pRight | |||
| 9417 | ){ | |||
| 9418 | Fts5HashEntry *p1 = pLeft; | |||
| 9419 | Fts5HashEntry *p2 = pRight; | |||
| 9420 | Fts5HashEntry *pRet = 0; | |||
| 9421 | Fts5HashEntry **ppOut = &pRet; | |||
| 9422 | ||||
| 9423 | while( p1 || p2 ){ | |||
| 9424 | if( p1==0 ){ | |||
| 9425 | *ppOut = p2; | |||
| 9426 | p2 = 0; | |||
| 9427 | }else if( p2==0 ){ | |||
| 9428 | *ppOut = p1; | |||
| 9429 | p1 = 0; | |||
| 9430 | }else{ | |||
| 9431 | char *zKey1 = fts5EntryKey(p1)( ((char *)(&(p1)[1])) ); | |||
| 9432 | char *zKey2 = fts5EntryKey(p2)( ((char *)(&(p2)[1])) ); | |||
| 9433 | int nMin = MIN(p1->nKey, p2->nKey)(((p1->nKey) < (p2->nKey)) ? (p1->nKey) : (p2-> nKey)); | |||
| 9434 | ||||
| 9435 | int cmp = memcmp(zKey1, zKey2, nMin); | |||
| 9436 | if( cmp==0 ){ | |||
| 9437 | cmp = p1->nKey - p2->nKey; | |||
| 9438 | } | |||
| 9439 | assert( cmp!=0 )((void) (0)); | |||
| 9440 | ||||
| 9441 | if( cmp>0 ){ | |||
| 9442 | /* p2 is smaller */ | |||
| 9443 | *ppOut = p2; | |||
| 9444 | ppOut = &p2->pScanNext; | |||
| 9445 | p2 = p2->pScanNext; | |||
| 9446 | }else{ | |||
| 9447 | /* p1 is smaller */ | |||
| 9448 | *ppOut = p1; | |||
| 9449 | ppOut = &p1->pScanNext; | |||
| 9450 | p1 = p1->pScanNext; | |||
| 9451 | } | |||
| 9452 | *ppOut = 0; | |||
| 9453 | } | |||
| 9454 | } | |||
| 9455 | ||||
| 9456 | return pRet; | |||
| 9457 | } | |||
| 9458 | ||||
| 9459 | /* | |||
| 9460 | ** Link all tokens from hash table iHash into a list in sorted order. The | |||
| 9461 | ** tokens are not removed from the hash table. | |||
| 9462 | */ | |||
| 9463 | static int fts5HashEntrySort( | |||
| 9464 | Fts5Hash *pHash, | |||
| 9465 | const char *pTerm, int nTerm, /* Query prefix, if any */ | |||
| 9466 | Fts5HashEntry **ppSorted | |||
| 9467 | ){ | |||
| 9468 | const int nMergeSlot = 32; | |||
| 9469 | Fts5HashEntry **ap; | |||
| 9470 | Fts5HashEntry *pList; | |||
| 9471 | int iSlot; | |||
| 9472 | int i; | |||
| 9473 | ||||
| 9474 | *ppSorted = 0; | |||
| 9475 | ap = sqlite3_malloc64sqlite3_api->malloc64(sizeof(Fts5HashEntry*) * nMergeSlot); | |||
| 9476 | if( !ap ) return SQLITE_NOMEM7; | |||
| 9477 | memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot); | |||
| 9478 | ||||
| 9479 | for(iSlot=0; iSlot<pHash->nSlot; iSlot++){ | |||
| 9480 | Fts5HashEntry *pIter; | |||
| 9481 | for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ | |||
| 9482 | if( pTerm==0 | |||
| 9483 | || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter)( ((char *)(&(pIter)[1])) ), pTerm, nTerm)) | |||
| 9484 | ){ | |||
| 9485 | Fts5HashEntry *pEntry = pIter; | |||
| 9486 | pEntry->pScanNext = 0; | |||
| 9487 | for(i=0; ap[i]; i++){ | |||
| 9488 | pEntry = fts5HashEntryMerge(pEntry, ap[i]); | |||
| 9489 | ap[i] = 0; | |||
| 9490 | } | |||
| 9491 | ap[i] = pEntry; | |||
| 9492 | } | |||
| 9493 | } | |||
| 9494 | } | |||
| 9495 | ||||
| 9496 | pList = 0; | |||
| 9497 | for(i=0; i<nMergeSlot; i++){ | |||
| 9498 | pList = fts5HashEntryMerge(pList, ap[i]); | |||
| 9499 | } | |||
| 9500 | ||||
| 9501 | sqlite3_freesqlite3_api->free(ap); | |||
| 9502 | *ppSorted = pList; | |||
| 9503 | return SQLITE_OK0; | |||
| 9504 | } | |||
| 9505 | ||||
| 9506 | /* | |||
| 9507 | ** Query the hash table for a doclist associated with term pTerm/nTerm. | |||
| 9508 | */ | |||
| 9509 | static int sqlite3Fts5HashQuery( | |||
| 9510 | Fts5Hash *pHash, /* Hash table to query */ | |||
| 9511 | int nPre, | |||
| 9512 | const char *pTerm, int nTerm, /* Query term */ | |||
| 9513 | void **ppOut, /* OUT: Pointer to new object */ | |||
| 9514 | int *pnDoclist /* OUT: Size of doclist in bytes */ | |||
| 9515 | ){ | |||
| 9516 | unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm); | |||
| 9517 | char *zKey = 0; | |||
| 9518 | Fts5HashEntry *p; | |||
| 9519 | ||||
| 9520 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ | |||
| 9521 | zKey = fts5EntryKey(p)( ((char *)(&(p)[1])) ); | |||
| 9522 | if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break; | |||
| 9523 | } | |||
| 9524 | ||||
| 9525 | if( p ){ | |||
| 9526 | int nHashPre = sizeof(Fts5HashEntry) + nTerm; | |||
| 9527 | int nList = p->nData - nHashPre; | |||
| 9528 | u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64sqlite3_api->malloc64(nPre + nList + 10)); | |||
| 9529 | if( pRet ){ | |||
| 9530 | Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre]; | |||
| 9531 | memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList); | |||
| 9532 | nList += fts5HashAddPoslistSize(pHash, p, pFaux); | |||
| 9533 | *pnDoclist = nList; | |||
| 9534 | }else{ | |||
| 9535 | *pnDoclist = 0; | |||
| 9536 | return SQLITE_NOMEM7; | |||
| 9537 | } | |||
| 9538 | }else{ | |||
| 9539 | *ppOut = 0; | |||
| 9540 | *pnDoclist = 0; | |||
| 9541 | } | |||
| 9542 | ||||
| 9543 | return SQLITE_OK0; | |||
| 9544 | } | |||
| 9545 | ||||
| 9546 | static int sqlite3Fts5HashScanInit( | |||
| 9547 | Fts5Hash *p, /* Hash table to query */ | |||
| 9548 | const char *pTerm, int nTerm /* Query prefix */ | |||
| 9549 | ){ | |||
| 9550 | return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan); | |||
| 9551 | } | |||
| 9552 | ||||
| 9553 | #ifdef SQLITE_DEBUG | |||
| 9554 | static int fts5HashCount(Fts5Hash *pHash){ | |||
| 9555 | int nEntry = 0; | |||
| 9556 | int ii; | |||
| 9557 | for(ii=0; ii<pHash->nSlot; ii++){ | |||
| 9558 | Fts5HashEntry *p = 0; | |||
| 9559 | for(p=pHash->aSlot[ii]; p; p=p->pHashNext){ | |||
| 9560 | nEntry++; | |||
| 9561 | } | |||
| 9562 | } | |||
| 9563 | return nEntry; | |||
| 9564 | } | |||
| 9565 | #endif | |||
| 9566 | ||||
| 9567 | /* | |||
| 9568 | ** Return true if the hash table is empty, false otherwise. | |||
| 9569 | */ | |||
| 9570 | static int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){ | |||
| 9571 | assert( pHash->nEntry==fts5HashCount(pHash) )((void) (0)); | |||
| 9572 | return pHash->nEntry==0; | |||
| 9573 | } | |||
| 9574 | ||||
| 9575 | static void sqlite3Fts5HashScanNext(Fts5Hash *p){ | |||
| 9576 | assert( !sqlite3Fts5HashScanEof(p) )((void) (0)); | |||
| 9577 | p->pScan = p->pScan->pScanNext; | |||
| 9578 | } | |||
| 9579 | ||||
| 9580 | static int sqlite3Fts5HashScanEof(Fts5Hash *p){ | |||
| 9581 | return (p->pScan==0); | |||
| 9582 | } | |||
| 9583 | ||||
| 9584 | static void sqlite3Fts5HashScanEntry( | |||
| 9585 | Fts5Hash *pHash, | |||
| 9586 | const char **pzTerm, /* OUT: term (nul-terminated) */ | |||
| 9587 | int *pnTerm, /* OUT: Size of term in bytes */ | |||
| 9588 | const u8 **ppDoclist, /* OUT: pointer to doclist */ | |||
| 9589 | int *pnDoclist /* OUT: size of doclist in bytes */ | |||
| 9590 | ){ | |||
| 9591 | Fts5HashEntry *p; | |||
| 9592 | if( (p = pHash->pScan) ){ | |||
| 9593 | char *zKey = fts5EntryKey(p)( ((char *)(&(p)[1])) ); | |||
| 9594 | int nTerm = p->nKey; | |||
| 9595 | fts5HashAddPoslistSize(pHash, p, 0); | |||
| 9596 | *pzTerm = zKey; | |||
| 9597 | *pnTerm = nTerm; | |||
| 9598 | *ppDoclist = (const u8*)&zKey[nTerm]; | |||
| 9599 | *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm); | |||
| 9600 | }else{ | |||
| 9601 | *pzTerm = 0; | |||
| 9602 | *pnTerm = 0; | |||
| 9603 | *ppDoclist = 0; | |||
| 9604 | *pnDoclist = 0; | |||
| 9605 | } | |||
| 9606 | } | |||
| 9607 | ||||
| 9608 | #line 1 "fts5_index.c" | |||
| 9609 | /* | |||
| 9610 | ** 2014 May 31 | |||
| 9611 | ** | |||
| 9612 | ** The author disclaims copyright to this source code. In place of | |||
| 9613 | ** a legal notice, here is a blessing: | |||
| 9614 | ** | |||
| 9615 | ** May you do good and not evil. | |||
| 9616 | ** May you find forgiveness for yourself and forgive others. | |||
| 9617 | ** May you share freely, never taking more than you give. | |||
| 9618 | ** | |||
| 9619 | ****************************************************************************** | |||
| 9620 | ** | |||
| 9621 | ** Low level access to the FTS index stored in the database file. The | |||
| 9622 | ** routines in this file file implement all read and write access to the | |||
| 9623 | ** %_data table. Other parts of the system access this functionality via | |||
| 9624 | ** the interface defined in fts5Int.h. | |||
| 9625 | */ | |||
| 9626 | ||||
| 9627 | ||||
| 9628 | /* #include "fts5Int.h" */ | |||
| 9629 | ||||
| 9630 | /* | |||
| 9631 | ** Overview: | |||
| 9632 | ** | |||
| 9633 | ** The %_data table contains all the FTS indexes for an FTS5 virtual table. | |||
| 9634 | ** As well as the main term index, there may be up to 31 prefix indexes. | |||
| 9635 | ** The format is similar to FTS3/4, except that: | |||
| 9636 | ** | |||
| 9637 | ** * all segment b-tree leaf data is stored in fixed size page records | |||
| 9638 | ** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is | |||
| 9639 | ** taken to ensure it is possible to iterate in either direction through | |||
| 9640 | ** the entries in a doclist, or to seek to a specific entry within a | |||
| 9641 | ** doclist, without loading it into memory. | |||
| 9642 | ** | |||
| 9643 | ** * large doclists that span many pages have associated "doclist index" | |||
| 9644 | ** records that contain a copy of the first rowid on each page spanned by | |||
| 9645 | ** the doclist. This is used to speed up seek operations, and merges of | |||
| 9646 | ** large doclists with very small doclists. | |||
| 9647 | ** | |||
| 9648 | ** * extra fields in the "structure record" record the state of ongoing | |||
| 9649 | ** incremental merge operations. | |||
| 9650 | ** | |||
| 9651 | */ | |||
| 9652 | ||||
| 9653 | ||||
| 9654 | #define FTS5_OPT_WORK_UNIT1000 1000 /* Number of leaf pages per optimize step */ | |||
| 9655 | #define FTS5_WORK_UNIT64 64 /* Number of leaf pages in unit of work */ | |||
| 9656 | ||||
| 9657 | #define FTS5_MIN_DLIDX_SIZE4 4 /* Add dlidx if this many empty pages */ | |||
| 9658 | ||||
| 9659 | #define FTS5_MAIN_PREFIX'0' '0' | |||
| 9660 | ||||
| 9661 | #if FTS5_MAX_PREFIX_INDEXES31 > 31 | |||
| 9662 | # error "FTS5_MAX_PREFIX_INDEXES is too large" | |||
| 9663 | #endif | |||
| 9664 | ||||
| 9665 | #define FTS5_MAX_LEVEL64 64 | |||
| 9666 | ||||
| 9667 | /* | |||
| 9668 | ** There are two versions of the format used for the structure record: | |||
| 9669 | ** | |||
| 9670 | ** 1. the legacy format, that may be read by all fts5 versions, and | |||
| 9671 | ** | |||
| 9672 | ** 2. the V2 format, which is used by contentless_delete=1 databases. | |||
| 9673 | ** | |||
| 9674 | ** Both begin with a 4-byte "configuration cookie" value. Then, a legacy | |||
| 9675 | ** format structure record contains a varint - the number of levels in | |||
| 9676 | ** the structure. Whereas a V2 structure record contains the constant | |||
| 9677 | ** 4 bytes [0xff 0x00 0x00 0x01]. This is unambiguous as the value of a | |||
| 9678 | ** varint has to be at least 16256 to begin with "0xFF". And the default | |||
| 9679 | ** maximum number of levels is 64. | |||
| 9680 | ** | |||
| 9681 | ** See below for more on structure record formats. | |||
| 9682 | */ | |||
| 9683 | #define FTS5_STRUCTURE_V2"\xFF\x00\x00\x01" "\xFF\x00\x00\x01" | |||
| 9684 | ||||
| 9685 | /* | |||
| 9686 | ** Details: | |||
| 9687 | ** | |||
| 9688 | ** The %_data table managed by this module, | |||
| 9689 | ** | |||
| 9690 | ** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB); | |||
| 9691 | ** | |||
| 9692 | ** , contains the following 6 types of records. See the comments surrounding | |||
| 9693 | ** the FTS5_*_ROWID macros below for a description of how %_data rowids are | |||
| 9694 | ** assigned to each fo them. | |||
| 9695 | ** | |||
| 9696 | ** 1. Structure Records: | |||
| 9697 | ** | |||
| 9698 | ** The set of segments that make up an index - the index structure - are | |||
| 9699 | ** recorded in a single record within the %_data table. The record consists | |||
| 9700 | ** of a single 32-bit configuration cookie value followed by a list of | |||
| 9701 | ** SQLite varints. | |||
| 9702 | ** | |||
| 9703 | ** If the structure record is a V2 record, the configuration cookie is | |||
| 9704 | ** followed by the following 4 bytes: [0xFF 0x00 0x00 0x01]. | |||
| 9705 | ** | |||
| 9706 | ** Next, the record continues with three varints: | |||
| 9707 | ** | |||
| 9708 | ** + number of levels, | |||
| 9709 | ** + total number of segments on all levels, | |||
| 9710 | ** + value of write counter. | |||
| 9711 | ** | |||
| 9712 | ** Then, for each level from 0 to nMax: | |||
| 9713 | ** | |||
| 9714 | ** + number of input segments in ongoing merge. | |||
| 9715 | ** + total number of segments in level. | |||
| 9716 | ** + for each segment from oldest to newest: | |||
| 9717 | ** + segment id (always > 0) | |||
| 9718 | ** + first leaf page number (often 1, always greater than 0) | |||
| 9719 | ** + final leaf page number | |||
| 9720 | ** | |||
| 9721 | ** Then, for V2 structures only: | |||
| 9722 | ** | |||
| 9723 | ** + lower origin counter value, | |||
| 9724 | ** + upper origin counter value, | |||
| 9725 | ** + the number of tombstone hash pages. | |||
| 9726 | ** | |||
| 9727 | ** 2. The Averages Record: | |||
| 9728 | ** | |||
| 9729 | ** A single record within the %_data table. The data is a list of varints. | |||
| 9730 | ** The first value is the number of rows in the index. Then, for each column | |||
| 9731 | ** from left to right, the total number of tokens in the column for all | |||
| 9732 | ** rows of the table. | |||
| 9733 | ** | |||
| 9734 | ** 3. Segment leaves: | |||
| 9735 | ** | |||
| 9736 | ** TERM/DOCLIST FORMAT: | |||
| 9737 | ** | |||
| 9738 | ** Most of each segment leaf is taken up by term/doclist data. The | |||
| 9739 | ** general format of term/doclist, starting with the first term | |||
| 9740 | ** on the leaf page, is: | |||
| 9741 | ** | |||
| 9742 | ** varint : size of first term | |||
| 9743 | ** blob: first term data | |||
| 9744 | ** doclist: first doclist | |||
| 9745 | ** zero-or-more { | |||
| 9746 | ** varint: number of bytes in common with previous term | |||
| 9747 | ** varint: number of bytes of new term data (nNew) | |||
| 9748 | ** blob: nNew bytes of new term data | |||
| 9749 | ** doclist: next doclist | |||
| 9750 | ** } | |||
| 9751 | ** | |||
| 9752 | ** doclist format: | |||
| 9753 | ** | |||
| 9754 | ** varint: first rowid | |||
| 9755 | ** poslist: first poslist | |||
| 9756 | ** zero-or-more { | |||
| 9757 | ** varint: rowid delta (always > 0) | |||
| 9758 | ** poslist: next poslist | |||
| 9759 | ** } | |||
| 9760 | ** | |||
| 9761 | ** poslist format: | |||
| 9762 | ** | |||
| 9763 | ** varint: size of poslist in bytes multiplied by 2, not including | |||
| 9764 | ** this field. Plus 1 if this entry carries the "delete" flag. | |||
| 9765 | ** collist: collist for column 0 | |||
| 9766 | ** zero-or-more { | |||
| 9767 | ** 0x01 byte | |||
| 9768 | ** varint: column number (I) | |||
| 9769 | ** collist: collist for column I | |||
| 9770 | ** } | |||
| 9771 | ** | |||
| 9772 | ** collist format: | |||
| 9773 | ** | |||
| 9774 | ** varint: first offset + 2 | |||
| 9775 | ** zero-or-more { | |||
| 9776 | ** varint: offset delta + 2 | |||
| 9777 | ** } | |||
| 9778 | ** | |||
| 9779 | ** PAGE FORMAT | |||
| 9780 | ** | |||
| 9781 | ** Each leaf page begins with a 4-byte header containing 2 16-bit | |||
| 9782 | ** unsigned integer fields in big-endian format. They are: | |||
| 9783 | ** | |||
| 9784 | ** * The byte offset of the first rowid on the page, if it exists | |||
| 9785 | ** and occurs before the first term (otherwise 0). | |||
| 9786 | ** | |||
| 9787 | ** * The byte offset of the start of the page footer. If the page | |||
| 9788 | ** footer is 0 bytes in size, then this field is the same as the | |||
| 9789 | ** size of the leaf page in bytes. | |||
| 9790 | ** | |||
| 9791 | ** The page footer consists of a single varint for each term located | |||
| 9792 | ** on the page. Each varint is the byte offset of the current term | |||
| 9793 | ** within the page, delta-compressed against the previous value. In | |||
| 9794 | ** other words, the first varint in the footer is the byte offset of | |||
| 9795 | ** the first term, the second is the byte offset of the second less that | |||
| 9796 | ** of the first, and so on. | |||
| 9797 | ** | |||
| 9798 | ** The term/doclist format described above is accurate if the entire | |||
| 9799 | ** term/doclist data fits on a single leaf page. If this is not the case, | |||
| 9800 | ** the format is changed in two ways: | |||
| 9801 | ** | |||
| 9802 | ** + if the first rowid on a page occurs before the first term, it | |||
| 9803 | ** is stored as a literal value: | |||
| 9804 | ** | |||
| 9805 | ** varint: first rowid | |||
| 9806 | ** | |||
| 9807 | ** + the first term on each page is stored in the same way as the | |||
| 9808 | ** very first term of the segment: | |||
| 9809 | ** | |||
| 9810 | ** varint : size of first term | |||
| 9811 | ** blob: first term data | |||
| 9812 | ** | |||
| 9813 | ** 5. Segment doclist indexes: | |||
| 9814 | ** | |||
| 9815 | ** Doclist indexes are themselves b-trees, however they usually consist of | |||
| 9816 | ** a single leaf record only. The format of each doclist index leaf page | |||
| 9817 | ** is: | |||
| 9818 | ** | |||
| 9819 | ** * Flags byte. Bits are: | |||
| 9820 | ** 0x01: Clear if leaf is also the root page, otherwise set. | |||
| 9821 | ** | |||
| 9822 | ** * Page number of fts index leaf page. As a varint. | |||
| 9823 | ** | |||
| 9824 | ** * First rowid on page indicated by previous field. As a varint. | |||
| 9825 | ** | |||
| 9826 | ** * A list of varints, one for each subsequent termless page. A | |||
| 9827 | ** positive delta if the termless page contains at least one rowid, | |||
| 9828 | ** or an 0x00 byte otherwise. | |||
| 9829 | ** | |||
| 9830 | ** Internal doclist index nodes are: | |||
| 9831 | ** | |||
| 9832 | ** * Flags byte. Bits are: | |||
| 9833 | ** 0x01: Clear for root page, otherwise set. | |||
| 9834 | ** | |||
| 9835 | ** * Page number of first child page. As a varint. | |||
| 9836 | ** | |||
| 9837 | ** * Copy of first rowid on page indicated by previous field. As a varint. | |||
| 9838 | ** | |||
| 9839 | ** * A list of delta-encoded varints - the first rowid on each subsequent | |||
| 9840 | ** child page. | |||
| 9841 | ** | |||
| 9842 | ** 6. Tombstone Hash Page | |||
| 9843 | ** | |||
| 9844 | ** These records are only ever present in contentless_delete=1 tables. | |||
| 9845 | ** There are zero or more of these associated with each segment. They | |||
| 9846 | ** are used to store the tombstone rowids for rows contained in the | |||
| 9847 | ** associated segments. | |||
| 9848 | ** | |||
| 9849 | ** The set of nHashPg tombstone hash pages associated with a single | |||
| 9850 | ** segment together form a single hash table containing tombstone rowids. | |||
| 9851 | ** To find the page of the hash on which a key might be stored: | |||
| 9852 | ** | |||
| 9853 | ** iPg = (rowid % nHashPg) | |||
| 9854 | ** | |||
| 9855 | ** Then, within page iPg, which has nSlot slots: | |||
| 9856 | ** | |||
| 9857 | ** iSlot = (rowid / nHashPg) % nSlot | |||
| 9858 | ** | |||
| 9859 | ** Each tombstone hash page begins with an 8 byte header: | |||
| 9860 | ** | |||
| 9861 | ** 1-byte: Key-size (the size in bytes of each slot). Either 4 or 8. | |||
| 9862 | ** 1-byte: rowid-0-tombstone flag. This flag is only valid on the | |||
| 9863 | ** first tombstone hash page for each segment (iPg=0). If set, | |||
| 9864 | ** the hash table contains rowid 0. If clear, it does not. | |||
| 9865 | ** Rowid 0 is handled specially. | |||
| 9866 | ** 2-bytes: unused. | |||
| 9867 | ** 4-bytes: Big-endian integer containing number of entries on page. | |||
| 9868 | ** | |||
| 9869 | ** Following this are nSlot 4 or 8 byte slots (depending on the key-size | |||
| 9870 | ** in the first byte of the page header). The number of slots may be | |||
| 9871 | ** determined based on the size of the page record and the key-size: | |||
| 9872 | ** | |||
| 9873 | ** nSlot = (nByte - 8) / key-size | |||
| 9874 | */ | |||
| 9875 | ||||
| 9876 | /* | |||
| 9877 | ** Rowids for the averages and structure records in the %_data table. | |||
| 9878 | */ | |||
| 9879 | #define FTS5_AVERAGES_ROWID1 1 /* Rowid used for the averages record */ | |||
| 9880 | #define FTS5_STRUCTURE_ROWID10 10 /* The structure record */ | |||
| 9881 | ||||
| 9882 | /* | |||
| 9883 | ** Macros determining the rowids used by segment leaves and dlidx leaves | |||
| 9884 | ** and nodes. All nodes and leaves are stored in the %_data table with large | |||
| 9885 | ** positive rowids. | |||
| 9886 | ** | |||
| 9887 | ** Each segment has a unique non-zero 16-bit id. | |||
| 9888 | ** | |||
| 9889 | ** The rowid for each segment leaf is found by passing the segment id and | |||
| 9890 | ** the leaf page number to the FTS5_SEGMENT_ROWID macro. Leaves are numbered | |||
| 9891 | ** sequentially starting from 1. | |||
| 9892 | */ | |||
| 9893 | #define FTS5_DATA_ID_B16 16 /* Max seg id number 65535 */ | |||
| 9894 | #define FTS5_DATA_DLI_B1 1 /* Doclist-index flag (1 bit) */ | |||
| 9895 | #define FTS5_DATA_HEIGHT_B5 5 /* Max dlidx tree height of 32 */ | |||
| 9896 | #define FTS5_DATA_PAGE_B31 31 /* Max page number of 2147483648 */ | |||
| 9897 | ||||
| 9898 | #define fts5_dri(segid, dlidx, height, pgno)( ((i64)(segid) << (31 +5 +1)) + ((i64)(dlidx) << (31 + 5)) + ((i64)(height) << (31)) + ((i64)(pgno)) ) ( \ | |||
| 9899 | ((i64)(segid) << (FTS5_DATA_PAGE_B31+FTS5_DATA_HEIGHT_B5+FTS5_DATA_DLI_B1)) + \ | |||
| 9900 | ((i64)(dlidx) << (FTS5_DATA_PAGE_B31 + FTS5_DATA_HEIGHT_B5)) + \ | |||
| 9901 | ((i64)(height) << (FTS5_DATA_PAGE_B31)) + \ | |||
| 9902 | ((i64)(pgno)) \ | |||
| 9903 | ) | |||
| 9904 | ||||
| 9905 | #define FTS5_SEGMENT_ROWID(segid, pgno)( ((i64)(segid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pgno)) ) fts5_dri(segid, 0, 0, pgno)( ((i64)(segid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pgno)) ) | |||
| 9906 | #define FTS5_DLIDX_ROWID(segid, height, pgno)( ((i64)(segid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(height) << (31)) + ((i64)(pgno)) ) fts5_dri(segid, 1, height, pgno)( ((i64)(segid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(height) << (31)) + ((i64)(pgno)) ) | |||
| 9907 | #define FTS5_TOMBSTONE_ROWID(segid,ipg)( ((i64)(segid+(1<<16)) << (31 +5 +1)) + ((i64)(0 ) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(ipg) ) ) fts5_dri(segid+(1<<16), 0, 0, ipg)( ((i64)(segid+(1<<16)) << (31 +5 +1)) + ((i64)(0 ) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(ipg) ) ) | |||
| 9908 | ||||
| 9909 | #ifdef SQLITE_DEBUG | |||
| 9910 | static int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB(11 | (1<<8)); } | |||
| 9911 | #endif | |||
| 9912 | ||||
| 9913 | ||||
| 9914 | /* | |||
| 9915 | ** Each time a blob is read from the %_data table, it is padded with this | |||
| 9916 | ** many zero bytes. This makes it easier to decode the various record formats | |||
| 9917 | ** without overreading if the records are corrupt. | |||
| 9918 | */ | |||
| 9919 | #define FTS5_DATA_ZERO_PADDING8 8 | |||
| 9920 | #define FTS5_DATA_PADDING20 20 | |||
| 9921 | ||||
| 9922 | typedef struct Fts5Data Fts5Data; | |||
| 9923 | typedef struct Fts5DlidxIter Fts5DlidxIter; | |||
| 9924 | typedef struct Fts5DlidxLvl Fts5DlidxLvl; | |||
| 9925 | typedef struct Fts5DlidxWriter Fts5DlidxWriter; | |||
| 9926 | typedef struct Fts5Iter Fts5Iter; | |||
| 9927 | typedef struct Fts5PageWriter Fts5PageWriter; | |||
| 9928 | typedef struct Fts5SegIter Fts5SegIter; | |||
| 9929 | typedef struct Fts5DoclistIter Fts5DoclistIter; | |||
| 9930 | typedef struct Fts5SegWriter Fts5SegWriter; | |||
| 9931 | typedef struct Fts5Structure Fts5Structure; | |||
| 9932 | typedef struct Fts5StructureLevel Fts5StructureLevel; | |||
| 9933 | typedef struct Fts5StructureSegment Fts5StructureSegment; | |||
| 9934 | typedef struct Fts5TokenDataIter Fts5TokenDataIter; | |||
| 9935 | typedef struct Fts5TokenDataMap Fts5TokenDataMap; | |||
| 9936 | typedef struct Fts5TombstoneArray Fts5TombstoneArray; | |||
| 9937 | ||||
| 9938 | struct Fts5Data { | |||
| 9939 | u8 *p; /* Pointer to buffer containing record */ | |||
| 9940 | int nn; /* Size of record in bytes */ | |||
| 9941 | int szLeaf; /* Size of leaf without page-index */ | |||
| 9942 | }; | |||
| 9943 | ||||
| 9944 | /* | |||
| 9945 | ** One object per %_data table. | |||
| 9946 | ** | |||
| 9947 | ** nContentlessDelete: | |||
| 9948 | ** The number of contentless delete operations since the most recent | |||
| 9949 | ** call to fts5IndexFlush() or fts5IndexDiscardData(). This is tracked | |||
| 9950 | ** so that extra auto-merge work can be done by fts5IndexFlush() to | |||
| 9951 | ** account for the delete operations. | |||
| 9952 | */ | |||
| 9953 | struct Fts5Index { | |||
| 9954 | Fts5Config *pConfig; /* Virtual table configuration */ | |||
| 9955 | char *zDataTbl; /* Name of %_data table */ | |||
| 9956 | int nWorkUnit; /* Leaf pages in a "unit" of work */ | |||
| 9957 | ||||
| 9958 | /* | |||
| 9959 | ** Variables related to the accumulation of tokens and doclists within the | |||
| 9960 | ** in-memory hash tables before they are flushed to disk. | |||
| 9961 | */ | |||
| 9962 | Fts5Hash *pHash; /* Hash table for in-memory data */ | |||
| 9963 | int nPendingData; /* Current bytes of pending data */ | |||
| 9964 | i64 iWriteRowid; /* Rowid for current doc being written */ | |||
| 9965 | int bDelete; /* Current write is a delete */ | |||
| 9966 | int nContentlessDelete; /* Number of contentless delete ops */ | |||
| 9967 | int nPendingRow; /* Number of INSERT in hash table */ | |||
| 9968 | ||||
| 9969 | /* Error state. */ | |||
| 9970 | int rc; /* Current error code */ | |||
| 9971 | int flushRc; | |||
| 9972 | ||||
| 9973 | /* State used by the fts5DataXXX() functions. */ | |||
| 9974 | sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ | |||
| 9975 | sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ | |||
| 9976 | sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ | |||
| 9977 | sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ | |||
| 9978 | sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ | |||
| 9979 | sqlite3_stmt *pIdxSelect; | |||
| 9980 | sqlite3_stmt *pIdxNextSelect; | |||
| 9981 | int nRead; /* Total number of blocks read */ | |||
| 9982 | ||||
| 9983 | sqlite3_stmt *pDeleteFromIdx; | |||
| 9984 | ||||
| 9985 | sqlite3_stmt *pDataVersion; | |||
| 9986 | i64 iStructVersion; /* data_version when pStruct read */ | |||
| 9987 | Fts5Structure *pStruct; /* Current db structure (or NULL) */ | |||
| 9988 | }; | |||
| 9989 | ||||
| 9990 | struct Fts5DoclistIter { | |||
| 9991 | u8 *aEof; /* Pointer to 1 byte past end of doclist */ | |||
| 9992 | ||||
| 9993 | /* Output variables. aPoslist==0 at EOF */ | |||
| 9994 | i64 iRowid; | |||
| 9995 | u8 *aPoslist; | |||
| 9996 | int nPoslist; | |||
| 9997 | int nSize; | |||
| 9998 | }; | |||
| 9999 | ||||
| 10000 | /* | |||
| 10001 | ** The contents of the "structure" record for each index are represented | |||
| 10002 | ** using an Fts5Structure record in memory. Which uses instances of the | |||
| 10003 | ** other Fts5StructureXXX types as components. | |||
| 10004 | ** | |||
| 10005 | ** nOriginCntr: | |||
| 10006 | ** This value is set to non-zero for structure records created for | |||
| 10007 | ** contentlessdelete=1 tables only. In that case it represents the | |||
| 10008 | ** origin value to apply to the next top-level segment created. | |||
| 10009 | */ | |||
| 10010 | struct Fts5StructureSegment { | |||
| 10011 | int iSegid; /* Segment id */ | |||
| 10012 | int pgnoFirst; /* First leaf page number in segment */ | |||
| 10013 | int pgnoLast; /* Last leaf page number in segment */ | |||
| 10014 | ||||
| 10015 | /* contentlessdelete=1 tables only: */ | |||
| 10016 | u64 iOrigin1; | |||
| 10017 | u64 iOrigin2; | |||
| 10018 | int nPgTombstone; /* Number of tombstone hash table pages */ | |||
| 10019 | u64 nEntryTombstone; /* Number of tombstone entries that "count" */ | |||
| 10020 | u64 nEntry; /* Number of rows in this segment */ | |||
| 10021 | }; | |||
| 10022 | struct Fts5StructureLevel { | |||
| 10023 | int nMerge; /* Number of segments in incr-merge */ | |||
| 10024 | int nSeg; /* Total number of segments on level */ | |||
| 10025 | Fts5StructureSegment *aSeg; /* Array of segments. aSeg[0] is oldest. */ | |||
| 10026 | }; | |||
| 10027 | struct Fts5Structure { | |||
| 10028 | int nRef; /* Object reference count */ | |||
| 10029 | u64 nWriteCounter; /* Total leaves written to level 0 */ | |||
| 10030 | u64 nOriginCntr; /* Origin value for next top-level segment */ | |||
| 10031 | int nSegment; /* Total segments in this structure */ | |||
| 10032 | int nLevel; /* Number of levels in this index */ | |||
| 10033 | Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */ | |||
| 10034 | }; | |||
| 10035 | ||||
| 10036 | /* Size (in bytes) of an Fts5Structure object holding up to N levels */ | |||
| 10037 | #define SZ_FTS5STRUCTURE(N)(__builtin_offsetof(Fts5Structure, aLevel) + (N)*sizeof(Fts5StructureLevel )) \ | |||
| 10038 | (offsetof(Fts5Structure,aLevel)__builtin_offsetof(Fts5Structure, aLevel) + (N)*sizeof(Fts5StructureLevel)) | |||
| 10039 | ||||
| 10040 | /* | |||
| 10041 | ** An object of type Fts5SegWriter is used to write to segments. | |||
| 10042 | */ | |||
| 10043 | struct Fts5PageWriter { | |||
| 10044 | int pgno; /* Page number for this page */ | |||
| 10045 | int iPrevPgidx; /* Previous value written into pgidx */ | |||
| 10046 | Fts5Buffer buf; /* Buffer containing leaf data */ | |||
| 10047 | Fts5Buffer pgidx; /* Buffer containing page-index */ | |||
| 10048 | Fts5Buffer term; /* Buffer containing previous term on page */ | |||
| 10049 | }; | |||
| 10050 | struct Fts5DlidxWriter { | |||
| 10051 | int pgno; /* Page number for this page */ | |||
| 10052 | int bPrevValid; /* True if iPrev is valid */ | |||
| 10053 | i64 iPrev; /* Previous rowid value written to page */ | |||
| 10054 | Fts5Buffer buf; /* Buffer containing page data */ | |||
| 10055 | }; | |||
| 10056 | struct Fts5SegWriter { | |||
| 10057 | int iSegid; /* Segid to write to */ | |||
| 10058 | Fts5PageWriter writer; /* PageWriter object */ | |||
| 10059 | i64 iPrevRowid; /* Previous rowid written to current leaf */ | |||
| 10060 | u8 bFirstRowidInDoclist; /* True if next rowid is first in doclist */ | |||
| 10061 | u8 bFirstRowidInPage; /* True if next rowid is first in page */ | |||
| 10062 | /* TODO1: Can use (writer.pgidx.n==0) instead of bFirstTermInPage */ | |||
| 10063 | u8 bFirstTermInPage; /* True if next term will be first in leaf */ | |||
| 10064 | int nLeafWritten; /* Number of leaf pages written */ | |||
| 10065 | int nEmpty; /* Number of contiguous term-less nodes */ | |||
| 10066 | ||||
| 10067 | int nDlidx; /* Allocated size of aDlidx[] array */ | |||
| 10068 | Fts5DlidxWriter *aDlidx; /* Array of Fts5DlidxWriter objects */ | |||
| 10069 | ||||
| 10070 | /* Values to insert into the %_idx table */ | |||
| 10071 | Fts5Buffer btterm; /* Next term to insert into %_idx table */ | |||
| 10072 | int iBtPage; /* Page number corresponding to btterm */ | |||
| 10073 | }; | |||
| 10074 | ||||
| 10075 | typedef struct Fts5CResult Fts5CResult; | |||
| 10076 | struct Fts5CResult { | |||
| 10077 | u16 iFirst; /* aSeg[] index of firstest iterator */ | |||
| 10078 | u8 bTermEq; /* True if the terms are equal */ | |||
| 10079 | }; | |||
| 10080 | ||||
| 10081 | /* | |||
| 10082 | ** Object for iterating through a single segment, visiting each term/rowid | |||
| 10083 | ** pair in the segment. | |||
| 10084 | ** | |||
| 10085 | ** pSeg: | |||
| 10086 | ** The segment to iterate through. | |||
| 10087 | ** | |||
| 10088 | ** iLeafPgno: | |||
| 10089 | ** Current leaf page number within segment. | |||
| 10090 | ** | |||
| 10091 | ** iLeafOffset: | |||
| 10092 | ** Byte offset within the current leaf that is the first byte of the | |||
| 10093 | ** position list data (one byte passed the position-list size field). | |||
| 10094 | ** | |||
| 10095 | ** pLeaf: | |||
| 10096 | ** Buffer containing current leaf page data. Set to NULL at EOF. | |||
| 10097 | ** | |||
| 10098 | ** iTermLeafPgno, iTermLeafOffset: | |||
| 10099 | ** Leaf page number containing the last term read from the segment. And | |||
| 10100 | ** the offset immediately following the term data. | |||
| 10101 | ** | |||
| 10102 | ** flags: | |||
| 10103 | ** Mask of FTS5_SEGITER_XXX values. Interpreted as follows: | |||
| 10104 | ** | |||
| 10105 | ** FTS5_SEGITER_ONETERM: | |||
| 10106 | ** If set, set the iterator to point to EOF after the current doclist | |||
| 10107 | ** has been exhausted. Do not proceed to the next term in the segment. | |||
| 10108 | ** | |||
| 10109 | ** FTS5_SEGITER_REVERSE: | |||
| 10110 | ** This flag is only ever set if FTS5_SEGITER_ONETERM is also set. If | |||
| 10111 | ** it is set, iterate through rowid in descending order instead of the | |||
| 10112 | ** default ascending order. | |||
| 10113 | ** | |||
| 10114 | ** iRowidOffset/nRowidOffset/aRowidOffset: | |||
| 10115 | ** These are used if the FTS5_SEGITER_REVERSE flag is set. | |||
| 10116 | ** | |||
| 10117 | ** For each rowid on the page corresponding to the current term, the | |||
| 10118 | ** corresponding aRowidOffset[] entry is set to the byte offset of the | |||
| 10119 | ** start of the "position-list-size" field within the page. | |||
| 10120 | ** | |||
| 10121 | ** iTermIdx: | |||
| 10122 | ** Index of current term on iTermLeafPgno. | |||
| 10123 | ** | |||
| 10124 | ** apTombstone/nTombstone: | |||
| 10125 | ** These are used for contentless_delete=1 tables only. When the cursor | |||
| 10126 | ** is first allocated, the apTombstone[] array is allocated so that it | |||
| 10127 | ** is large enough for all tombstones hash pages associated with the | |||
| 10128 | ** segment. The pages themselves are loaded lazily from the database as | |||
| 10129 | ** they are required. | |||
| 10130 | */ | |||
| 10131 | struct Fts5SegIter { | |||
| 10132 | Fts5StructureSegment *pSeg; /* Segment to iterate through */ | |||
| 10133 | int flags; /* Mask of configuration flags */ | |||
| 10134 | int iLeafPgno; /* Current leaf page number */ | |||
| 10135 | Fts5Data *pLeaf; /* Current leaf data */ | |||
| 10136 | Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ | |||
| 10137 | i64 iLeafOffset; /* Byte offset within current leaf */ | |||
| 10138 | Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */ | |||
| 10139 | ||||
| 10140 | /* Next method */ | |||
| 10141 | void (*xNext)(Fts5Index*, Fts5SegIter*, int*); | |||
| 10142 | ||||
| 10143 | /* The page and offset from which the current term was read. The offset | |||
| 10144 | ** is the offset of the first rowid in the current doclist. */ | |||
| 10145 | int iTermLeafPgno; | |||
| 10146 | int iTermLeafOffset; | |||
| 10147 | ||||
| 10148 | int iPgidxOff; /* Next offset in pgidx */ | |||
| 10149 | int iEndofDoclist; | |||
| 10150 | ||||
| 10151 | /* The following are only used if the FTS5_SEGITER_REVERSE flag is set. */ | |||
| 10152 | int iRowidOffset; /* Current entry in aRowidOffset[] */ | |||
| 10153 | int nRowidOffset; /* Allocated size of aRowidOffset[] array */ | |||
| 10154 | int *aRowidOffset; /* Array of offset to rowid fields */ | |||
| 10155 | ||||
| 10156 | Fts5DlidxIter *pDlidx; /* If there is a doclist-index */ | |||
| 10157 | ||||
| 10158 | /* Variables populated based on current entry. */ | |||
| 10159 | Fts5Buffer term; /* Current term */ | |||
| 10160 | i64 iRowid; /* Current rowid */ | |||
| 10161 | int nPos; /* Number of bytes in current position list */ | |||
| 10162 | u8 bDel; /* True if the delete flag is set */ | |||
| 10163 | }; | |||
| 10164 | ||||
| 10165 | static int fts5IndexCorruptRowid(Fts5Index *pIdx, i64 iRowid){ | |||
| 10166 | pIdx->rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10167 | sqlite3Fts5ConfigErrmsg(pIdx->pConfig, | |||
| 10168 | "fts5: corruption found reading blob %lld from table \"%s\"", | |||
| 10169 | iRowid, pIdx->pConfig->zName | |||
| 10170 | ); | |||
| 10171 | return SQLITE_CORRUPT_VTAB(11 | (1<<8)); | |||
| 10172 | } | |||
| 10173 | #define FTS5_CORRUPT_ROWID(pIdx, iRowid)fts5IndexCorruptRowid(pIdx, iRowid) fts5IndexCorruptRowid(pIdx, iRowid) | |||
| 10174 | ||||
| 10175 | static int fts5IndexCorruptIter(Fts5Index *pIdx, Fts5SegIter *pIter){ | |||
| 10176 | pIdx->rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10177 | sqlite3Fts5ConfigErrmsg(pIdx->pConfig, | |||
| 10178 | "fts5: corruption on page %d, segment %d, table \"%s\"", | |||
| 10179 | pIter->iLeafPgno, pIter->pSeg->iSegid, pIdx->pConfig->zName | |||
| 10180 | ); | |||
| 10181 | return SQLITE_CORRUPT_VTAB(11 | (1<<8)); | |||
| 10182 | } | |||
| 10183 | #define FTS5_CORRUPT_ITER(pIdx, pIter)fts5IndexCorruptIter(pIdx, pIter) fts5IndexCorruptIter(pIdx, pIter) | |||
| 10184 | ||||
| 10185 | static int fts5IndexCorruptIdx(Fts5Index *pIdx){ | |||
| 10186 | pIdx->rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10187 | sqlite3Fts5ConfigErrmsg(pIdx->pConfig, | |||
| 10188 | "fts5: corruption in table \"%s\"", pIdx->pConfig->zName | |||
| 10189 | ); | |||
| 10190 | return SQLITE_CORRUPT_VTAB(11 | (1<<8)); | |||
| 10191 | } | |||
| 10192 | #define FTS5_CORRUPT_IDX(pIdx)fts5IndexCorruptIdx(pIdx) fts5IndexCorruptIdx(pIdx) | |||
| 10193 | ||||
| 10194 | ||||
| 10195 | /* | |||
| 10196 | ** Array of tombstone pages. Reference counted. | |||
| 10197 | */ | |||
| 10198 | struct Fts5TombstoneArray { | |||
| 10199 | int nRef; /* Number of pointers to this object */ | |||
| 10200 | int nTombstone; | |||
| 10201 | Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */ | |||
| 10202 | }; | |||
| 10203 | ||||
| 10204 | /* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */ | |||
| 10205 | #define SZ_FTS5TOMBSTONEARRAY(N)(__builtin_offsetof(Fts5TombstoneArray, apTombstone)+(N)*sizeof (Fts5Data*)) \ | |||
| 10206 | (offsetof(Fts5TombstoneArray,apTombstone)__builtin_offsetof(Fts5TombstoneArray, apTombstone)+(N)*sizeof(Fts5Data*)) | |||
| 10207 | ||||
| 10208 | /* | |||
| 10209 | ** Argument is a pointer to an Fts5Data structure that contains a | |||
| 10210 | ** leaf page. | |||
| 10211 | */ | |||
| 10212 | #define ASSERT_SZLEAF_OK(x)((void) (0)) assert( \((void) (0)) | |||
| 10213 | (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \((void) (0)) | |||
| 10214 | )((void) (0)) | |||
| 10215 | ||||
| 10216 | #define FTS5_SEGITER_ONETERM0x01 0x01 | |||
| 10217 | #define FTS5_SEGITER_REVERSE0x02 0x02 | |||
| 10218 | ||||
| 10219 | /* | |||
| 10220 | ** Argument is a pointer to an Fts5Data structure that contains a leaf | |||
| 10221 | ** page. This macro evaluates to true if the leaf contains no terms, or | |||
| 10222 | ** false if it contains at least one term. | |||
| 10223 | */ | |||
| 10224 | #define fts5LeafIsTermless(x)((x)->szLeaf >= (x)->nn) ((x)->szLeaf >= (x)->nn) | |||
| 10225 | ||||
| 10226 | #define fts5LeafTermOff(x, i)(fts5GetU16(&(x)->p[(x)->szLeaf + (i)*2])) (fts5GetU16(&(x)->p[(x)->szLeaf + (i)*2])) | |||
| 10227 | ||||
| 10228 | #define fts5LeafFirstRowidOff(x)(fts5GetU16((x)->p)) (fts5GetU16((x)->p)) | |||
| 10229 | ||||
| 10230 | /* | |||
| 10231 | ** Object for iterating through the merged results of one or more segments, | |||
| 10232 | ** visiting each term/rowid pair in the merged data. | |||
| 10233 | ** | |||
| 10234 | ** nSeg is always a power of two greater than or equal to the number of | |||
| 10235 | ** segments that this object is merging data from. Both the aSeg[] and | |||
| 10236 | ** aFirst[] arrays are sized at nSeg entries. The aSeg[] array is padded | |||
| 10237 | ** with zeroed objects - these are handled as if they were iterators opened | |||
| 10238 | ** on empty segments. | |||
| 10239 | ** | |||
| 10240 | ** The results of comparing segments aSeg[N] and aSeg[N+1], where N is an | |||
| 10241 | ** even number, is stored in aFirst[(nSeg+N)/2]. The "result" of the | |||
| 10242 | ** comparison in this context is the index of the iterator that currently | |||
| 10243 | ** points to the smaller term/rowid combination. Iterators at EOF are | |||
| 10244 | ** considered to be greater than all other iterators. | |||
| 10245 | ** | |||
| 10246 | ** aFirst[1] contains the index in aSeg[] of the iterator that points to | |||
| 10247 | ** the smallest key overall. aFirst[0] is unused. | |||
| 10248 | ** | |||
| 10249 | ** poslist: | |||
| 10250 | ** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered. | |||
| 10251 | ** There is no way to tell if this is populated or not. | |||
| 10252 | ** | |||
| 10253 | ** pColset: | |||
| 10254 | ** If not NULL, points to an object containing a set of column indices. | |||
| 10255 | ** Only matches that occur in one of these columns will be returned. | |||
| 10256 | ** The Fts5Iter does not own the Fts5Colset object, and so it is not | |||
| 10257 | ** freed when the iterator is closed - it is owned by the upper layer. | |||
| 10258 | */ | |||
| 10259 | struct Fts5Iter { | |||
| 10260 | Fts5IndexIter base; /* Base class containing output vars */ | |||
| 10261 | Fts5TokenDataIter *pTokenDataIter; | |||
| 10262 | ||||
| 10263 | Fts5Index *pIndex; /* Index that owns this iterator */ | |||
| 10264 | Fts5Buffer poslist; /* Buffer containing current poslist */ | |||
| 10265 | Fts5Colset *pColset; /* Restrict matches to these columns */ | |||
| 10266 | ||||
| 10267 | /* Invoked to set output variables. */ | |||
| 10268 | void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*); | |||
| 10269 | ||||
| 10270 | int nSeg; /* Size of aSeg[] array */ | |||
| 10271 | int bRev; /* True to iterate in reverse order */ | |||
| 10272 | u8 bSkipEmpty; /* True to skip deleted entries */ | |||
| 10273 | ||||
| 10274 | i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ | |||
| 10275 | Fts5CResult *aFirst; /* Current merge state (see above) */ | |||
| 10276 | Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */ | |||
| 10277 | }; | |||
| 10278 | ||||
| 10279 | /* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */ | |||
| 10280 | #define SZ_FTS5ITER(N)(__builtin_offsetof(Fts5Iter, aSeg)+(N)*sizeof(Fts5SegIter)) (offsetof(Fts5Iter,aSeg)__builtin_offsetof(Fts5Iter, aSeg)+(N)*sizeof(Fts5SegIter)) | |||
| 10281 | ||||
| 10282 | /* | |||
| 10283 | ** An instance of the following type is used to iterate through the contents | |||
| 10284 | ** of a doclist-index record. | |||
| 10285 | ** | |||
| 10286 | ** pData: | |||
| 10287 | ** Record containing the doclist-index data. | |||
| 10288 | ** | |||
| 10289 | ** bEof: | |||
| 10290 | ** Set to true once iterator has reached EOF. | |||
| 10291 | ** | |||
| 10292 | ** iOff: | |||
| 10293 | ** Set to the current offset within record pData. | |||
| 10294 | */ | |||
| 10295 | struct Fts5DlidxLvl { | |||
| 10296 | Fts5Data *pData; /* Data for current page of this level */ | |||
| 10297 | int iOff; /* Current offset into pData */ | |||
| 10298 | int bEof; /* At EOF already */ | |||
| 10299 | int iFirstOff; /* Used by reverse iterators */ | |||
| 10300 | ||||
| 10301 | /* Output variables */ | |||
| 10302 | int iLeafPgno; /* Page number of current leaf page */ | |||
| 10303 | i64 iRowid; /* First rowid on leaf iLeafPgno */ | |||
| 10304 | }; | |||
| 10305 | struct Fts5DlidxIter { | |||
| 10306 | int nLvl; | |||
| 10307 | int iSegid; | |||
| 10308 | Fts5DlidxLvl aLvl[FLEXARRAY]; | |||
| 10309 | }; | |||
| 10310 | ||||
| 10311 | /* Size (in bytes) of an Fts5DlidxIter object with up to N levels */ | |||
| 10312 | #define SZ_FTS5DLIDXITER(N)(__builtin_offsetof(Fts5DlidxIter, aLvl)+(N)*sizeof(Fts5DlidxLvl )) \ | |||
| 10313 | (offsetof(Fts5DlidxIter,aLvl)__builtin_offsetof(Fts5DlidxIter, aLvl)+(N)*sizeof(Fts5DlidxLvl)) | |||
| 10314 | ||||
| 10315 | static void fts5PutU16(u8 *aOut, u16 iVal){ | |||
| 10316 | aOut[0] = (iVal>>8); | |||
| 10317 | aOut[1] = (iVal&0xFF); | |||
| 10318 | } | |||
| 10319 | ||||
| 10320 | static u16 fts5GetU16(const u8 *aIn){ | |||
| 10321 | return ((u16)aIn[0] << 8) + aIn[1]; | |||
| 10322 | } | |||
| 10323 | ||||
| 10324 | /* | |||
| 10325 | ** The only argument points to a buffer at least 8 bytes in size. This | |||
| 10326 | ** function interprets the first 8 bytes of the buffer as a 64-bit big-endian | |||
| 10327 | ** unsigned integer and returns the result. | |||
| 10328 | */ | |||
| 10329 | static u64 fts5GetU64(u8 *a){ | |||
| 10330 | return ((u64)a[0] << 56) | |||
| 10331 | + ((u64)a[1] << 48) | |||
| 10332 | + ((u64)a[2] << 40) | |||
| 10333 | + ((u64)a[3] << 32) | |||
| 10334 | + ((u64)a[4] << 24) | |||
| 10335 | + ((u64)a[5] << 16) | |||
| 10336 | + ((u64)a[6] << 8) | |||
| 10337 | + ((u64)a[7] << 0); | |||
| 10338 | } | |||
| 10339 | ||||
| 10340 | /* | |||
| 10341 | ** The only argument points to a buffer at least 4 bytes in size. This | |||
| 10342 | ** function interprets the first 4 bytes of the buffer as a 32-bit big-endian | |||
| 10343 | ** unsigned integer and returns the result. | |||
| 10344 | */ | |||
| 10345 | static u32 fts5GetU32(const u8 *a){ | |||
| 10346 | return ((u32)a[0] << 24) | |||
| 10347 | + ((u32)a[1] << 16) | |||
| 10348 | + ((u32)a[2] << 8) | |||
| 10349 | + ((u32)a[3] << 0); | |||
| 10350 | } | |||
| 10351 | ||||
| 10352 | /* | |||
| 10353 | ** Write iVal, formated as a 64-bit big-endian unsigned integer, to the | |||
| 10354 | ** buffer indicated by the first argument. | |||
| 10355 | */ | |||
| 10356 | static void fts5PutU64(u8 *a, u64 iVal){ | |||
| 10357 | a[0] = ((iVal >> 56) & 0xFF); | |||
| 10358 | a[1] = ((iVal >> 48) & 0xFF); | |||
| 10359 | a[2] = ((iVal >> 40) & 0xFF); | |||
| 10360 | a[3] = ((iVal >> 32) & 0xFF); | |||
| 10361 | a[4] = ((iVal >> 24) & 0xFF); | |||
| 10362 | a[5] = ((iVal >> 16) & 0xFF); | |||
| 10363 | a[6] = ((iVal >> 8) & 0xFF); | |||
| 10364 | a[7] = ((iVal >> 0) & 0xFF); | |||
| 10365 | } | |||
| 10366 | ||||
| 10367 | /* | |||
| 10368 | ** Write iVal, formated as a 32-bit big-endian unsigned integer, to the | |||
| 10369 | ** buffer indicated by the first argument. | |||
| 10370 | */ | |||
| 10371 | static void fts5PutU32(u8 *a, u32 iVal){ | |||
| 10372 | a[0] = ((iVal >> 24) & 0xFF); | |||
| 10373 | a[1] = ((iVal >> 16) & 0xFF); | |||
| 10374 | a[2] = ((iVal >> 8) & 0xFF); | |||
| 10375 | a[3] = ((iVal >> 0) & 0xFF); | |||
| 10376 | } | |||
| 10377 | ||||
| 10378 | /* | |||
| 10379 | ** Allocate and return a buffer at least nByte bytes in size. | |||
| 10380 | ** | |||
| 10381 | ** If an OOM error is encountered, return NULL and set the error code in | |||
| 10382 | ** the Fts5Index handle passed as the first argument. | |||
| 10383 | */ | |||
| 10384 | static void *fts5IdxMalloc(Fts5Index *p, sqlite3_int64 nByte){ | |||
| 10385 | return sqlite3Fts5MallocZero(&p->rc, nByte); | |||
| 10386 | } | |||
| 10387 | ||||
| 10388 | /* | |||
| 10389 | ** Compare the contents of the pLeft buffer with the pRight/nRight blob. | |||
| 10390 | ** | |||
| 10391 | ** Return -ve if pLeft is smaller than pRight, 0 if they are equal or | |||
| 10392 | ** +ve if pRight is smaller than pLeft. In other words: | |||
| 10393 | ** | |||
| 10394 | ** res = *pLeft - *pRight | |||
| 10395 | */ | |||
| 10396 | #ifdef SQLITE_DEBUG | |||
| 10397 | static int fts5BufferCompareBlob( | |||
| 10398 | Fts5Buffer *pLeft, /* Left hand side of comparison */ | |||
| 10399 | const u8 *pRight, int nRight /* Right hand side of comparison */ | |||
| 10400 | ){ | |||
| 10401 | int nCmp = MIN(pLeft->n, nRight)(((pLeft->n) < (nRight)) ? (pLeft->n) : (nRight)); | |||
| 10402 | int res = memcmp(pLeft->p, pRight, nCmp); | |||
| 10403 | return (res==0 ? (pLeft->n - nRight) : res); | |||
| 10404 | } | |||
| 10405 | #endif | |||
| 10406 | ||||
| 10407 | /* | |||
| 10408 | ** Compare the contents of the two buffers using memcmp(). If one buffer | |||
| 10409 | ** is a prefix of the other, it is considered the lesser. | |||
| 10410 | ** | |||
| 10411 | ** Return -ve if pLeft is smaller than pRight, 0 if they are equal or | |||
| 10412 | ** +ve if pRight is smaller than pLeft. In other words: | |||
| 10413 | ** | |||
| 10414 | ** res = *pLeft - *pRight | |||
| 10415 | */ | |||
| 10416 | static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ | |||
| 10417 | int nCmp, res; | |||
| 10418 | nCmp = MIN(pLeft->n, pRight->n)(((pLeft->n) < (pRight->n)) ? (pLeft->n) : (pRight ->n)); | |||
| 10419 | assert( nCmp<=0 || pLeft->p!=0 )((void) (0)); | |||
| 10420 | assert( nCmp<=0 || pRight->p!=0 )((void) (0)); | |||
| 10421 | res = fts5Memcmp(pLeft->p, pRight->p, nCmp)((nCmp)<=0 ? 0 : memcmp((pLeft->p), (pRight->p), (nCmp ))); | |||
| 10422 | return (res==0 ? (pLeft->n - pRight->n) : res); | |||
| 10423 | } | |||
| 10424 | ||||
| 10425 | static int fts5LeafFirstTermOff(Fts5Data *pLeaf){ | |||
| 10426 | int ret; | |||
| 10427 | fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf], ret)sqlite3Fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf],(u32 *)&(ret)); | |||
| 10428 | return ret; | |||
| 10429 | } | |||
| 10430 | ||||
| 10431 | /* | |||
| 10432 | ** Close the read-only blob handle, if it is open. | |||
| 10433 | */ | |||
| 10434 | static void fts5IndexCloseReader(Fts5Index *p){ | |||
| 10435 | if( p->pReader ){ | |||
| 10436 | int rc; | |||
| 10437 | sqlite3_blob *pReader = p->pReader; | |||
| 10438 | p->pReader = 0; | |||
| 10439 | rc = sqlite3_blob_closesqlite3_api->blob_close(pReader); | |||
| 10440 | if( p->rc==SQLITE_OK0 ) p->rc = rc; | |||
| 10441 | } | |||
| 10442 | } | |||
| 10443 | ||||
| 10444 | /* | |||
| 10445 | ** Retrieve a record from the %_data table. | |||
| 10446 | ** | |||
| 10447 | ** If an error occurs, NULL is returned and an error left in the | |||
| 10448 | ** Fts5Index object. | |||
| 10449 | */ | |||
| 10450 | static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ | |||
| 10451 | Fts5Data *pRet = 0; | |||
| 10452 | if( p->rc==SQLITE_OK0 ){ | |||
| 10453 | int rc = SQLITE_OK0; | |||
| 10454 | ||||
| 10455 | if( p->pReader ){ | |||
| 10456 | /* This call may return SQLITE_ABORT if there has been a savepoint | |||
| 10457 | ** rollback since it was last used. In this case a new blob handle | |||
| 10458 | ** is required. */ | |||
| 10459 | sqlite3_blob *pBlob = p->pReader; | |||
| 10460 | p->pReader = 0; | |||
| 10461 | rc = sqlite3_blob_reopensqlite3_api->blob_reopen(pBlob, iRowid); | |||
| 10462 | assert( p->pReader==0 )((void) (0)); | |||
| 10463 | p->pReader = pBlob; | |||
| 10464 | if( rc!=SQLITE_OK0 ){ | |||
| 10465 | fts5IndexCloseReader(p); | |||
| 10466 | } | |||
| 10467 | if( rc==SQLITE_ABORT4 ) rc = SQLITE_OK0; | |||
| 10468 | } | |||
| 10469 | ||||
| 10470 | /* If the blob handle is not open at this point, open it and seek | |||
| 10471 | ** to the requested entry. */ | |||
| 10472 | if( p->pReader==0 && rc==SQLITE_OK0 ){ | |||
| 10473 | Fts5Config *pConfig = p->pConfig; | |||
| 10474 | rc = sqlite3_blob_opensqlite3_api->blob_open(pConfig->db, | |||
| 10475 | pConfig->zDb, p->zDataTbl, "block", iRowid, 0, &p->pReader | |||
| 10476 | ); | |||
| 10477 | } | |||
| 10478 | ||||
| 10479 | /* If either of the sqlite3_blob_open() or sqlite3_blob_reopen() calls | |||
| 10480 | ** above returned SQLITE_ERROR, return SQLITE_CORRUPT_VTAB instead. | |||
| 10481 | ** All the reasons those functions might return SQLITE_ERROR - missing | |||
| 10482 | ** table, missing row, non-blob/text in block column - indicate | |||
| 10483 | ** backing store corruption. */ | |||
| 10484 | if( rc==SQLITE_ERROR1 ) rc = FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 10485 | ||||
| 10486 | if( rc==SQLITE_OK0 ){ | |||
| 10487 | u8 *aOut = 0; /* Read blob data into this buffer */ | |||
| 10488 | i64 nByte = sqlite3_blob_bytessqlite3_api->blob_bytes(p->pReader); | |||
| 10489 | i64 szData = (sizeof(Fts5Data) + 7) & ~7; | |||
| 10490 | i64 nAlloc = szData + nByte + FTS5_DATA_PADDING20; | |||
| 10491 | pRet = (Fts5Data*)sqlite3_malloc64sqlite3_api->malloc64(nAlloc); | |||
| 10492 | if( pRet ){ | |||
| 10493 | pRet->nn = nByte; | |||
| 10494 | aOut = pRet->p = (u8*)pRet + szData; | |||
| 10495 | }else{ | |||
| 10496 | rc = SQLITE_NOMEM7; | |||
| 10497 | } | |||
| 10498 | ||||
| 10499 | if( rc==SQLITE_OK0 ){ | |||
| 10500 | rc = sqlite3_blob_readsqlite3_api->blob_read(p->pReader, aOut, nByte, 0); | |||
| 10501 | } | |||
| 10502 | if( rc!=SQLITE_OK0 ){ | |||
| 10503 | sqlite3_freesqlite3_api->free(pRet); | |||
| 10504 | pRet = 0; | |||
| 10505 | }else{ | |||
| 10506 | /* TODO1: Fix this */ | |||
| 10507 | pRet->p[nByte] = 0x00; | |||
| 10508 | pRet->p[nByte+1] = 0x00; | |||
| 10509 | pRet->szLeaf = fts5GetU16(&pRet->p[2]); | |||
| 10510 | } | |||
| 10511 | } | |||
| 10512 | p->rc = rc; | |||
| 10513 | p->nRead++; | |||
| 10514 | } | |||
| 10515 | ||||
| 10516 | assert( (pRet==0)==(p->rc!=SQLITE_OK) )((void) (0)); | |||
| 10517 | assert( pRet==0 || EIGHT_BYTE_ALIGNMENT( pRet->p ) )((void) (0)); | |||
| 10518 | return pRet; | |||
| 10519 | } | |||
| 10520 | ||||
| 10521 | ||||
| 10522 | /* | |||
| 10523 | ** Release a reference to data record returned by an earlier call to | |||
| 10524 | ** fts5DataRead(). | |||
| 10525 | */ | |||
| 10526 | static void fts5DataRelease(Fts5Data *pData){ | |||
| 10527 | sqlite3_freesqlite3_api->free(pData); | |||
| 10528 | } | |||
| 10529 | ||||
| 10530 | static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){ | |||
| 10531 | Fts5Data *pRet = fts5DataRead(p, iRowid); | |||
| 10532 | if( pRet ){ | |||
| 10533 | if( pRet->szLeaf<4 || pRet->szLeaf>pRet->nn ){ | |||
| 10534 | FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 10535 | fts5DataRelease(pRet); | |||
| 10536 | pRet = 0; | |||
| 10537 | } | |||
| 10538 | } | |||
| 10539 | return pRet; | |||
| 10540 | } | |||
| 10541 | ||||
| 10542 | static int fts5IndexPrepareStmt( | |||
| 10543 | Fts5Index *p, | |||
| 10544 | sqlite3_stmt **ppStmt, | |||
| 10545 | char *zSql | |||
| 10546 | ){ | |||
| 10547 | if( p->rc==SQLITE_OK0 ){ | |||
| 10548 | if( zSql ){ | |||
| 10549 | int rc = sqlite3_prepare_v3sqlite3_api->prepare_v3(p->pConfig->db, zSql, -1, | |||
| 10550 | SQLITE_PREPARE_PERSISTENT0x01|SQLITE_PREPARE_NO_VTAB0x04, | |||
| 10551 | ppStmt, 0); | |||
| 10552 | /* If this prepare() call fails with SQLITE_ERROR, then one of the | |||
| 10553 | ** %_idx or %_data tables has been removed or modified. Call this | |||
| 10554 | ** corruption. */ | |||
| 10555 | p->rc = (rc==SQLITE_ERROR1 ? SQLITE_CORRUPT11 : rc); | |||
| 10556 | }else{ | |||
| 10557 | p->rc = SQLITE_NOMEM7; | |||
| 10558 | } | |||
| 10559 | } | |||
| 10560 | sqlite3_freesqlite3_api->free(zSql); | |||
| 10561 | return p->rc; | |||
| 10562 | } | |||
| 10563 | ||||
| 10564 | ||||
| 10565 | /* | |||
| 10566 | ** INSERT OR REPLACE a record into the %_data table. | |||
| 10567 | */ | |||
| 10568 | static void fts5DataWrite(Fts5Index *p, i64 iRowid, const u8 *pData, int nData){ | |||
| 10569 | if( p->rc!=SQLITE_OK0 ) return; | |||
| 10570 | ||||
| 10571 | if( p->pWriter==0 ){ | |||
| 10572 | Fts5Config *pConfig = p->pConfig; | |||
| 10573 | fts5IndexPrepareStmt(p, &p->pWriter, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 10574 | "REPLACE INTO '%q'.'%q_data'(id, block) VALUES(?,?)", | |||
| 10575 | pConfig->zDb, pConfig->zName | |||
| 10576 | )); | |||
| 10577 | if( p->rc ) return; | |||
| 10578 | } | |||
| 10579 | ||||
| 10580 | sqlite3_bind_int64sqlite3_api->bind_int64(p->pWriter, 1, iRowid); | |||
| 10581 | sqlite3_bind_blobsqlite3_api->bind_blob(p->pWriter, 2, pData, nData, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 10582 | sqlite3_stepsqlite3_api->step(p->pWriter); | |||
| 10583 | p->rc = sqlite3_resetsqlite3_api->reset(p->pWriter); | |||
| 10584 | sqlite3_bind_nullsqlite3_api->bind_null(p->pWriter, 2); | |||
| 10585 | } | |||
| 10586 | ||||
| 10587 | /* | |||
| 10588 | ** Execute the following SQL: | |||
| 10589 | ** | |||
| 10590 | ** DELETE FROM %_data WHERE id BETWEEN $iFirst AND $iLast | |||
| 10591 | */ | |||
| 10592 | static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){ | |||
| 10593 | if( p->rc!=SQLITE_OK0 ) return; | |||
| 10594 | ||||
| 10595 | if( p->pDeleter==0 ){ | |||
| 10596 | Fts5Config *pConfig = p->pConfig; | |||
| 10597 | char *zSql = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 10598 | "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?", | |||
| 10599 | pConfig->zDb, pConfig->zName | |||
| 10600 | ); | |||
| 10601 | if( fts5IndexPrepareStmt(p, &p->pDeleter, zSql) ) return; | |||
| 10602 | } | |||
| 10603 | ||||
| 10604 | sqlite3_bind_int64sqlite3_api->bind_int64(p->pDeleter, 1, iFirst); | |||
| 10605 | sqlite3_bind_int64sqlite3_api->bind_int64(p->pDeleter, 2, iLast); | |||
| 10606 | sqlite3_stepsqlite3_api->step(p->pDeleter); | |||
| 10607 | p->rc = sqlite3_resetsqlite3_api->reset(p->pDeleter); | |||
| 10608 | } | |||
| 10609 | ||||
| 10610 | /* | |||
| 10611 | ** Remove all records associated with segment iSegid. | |||
| 10612 | */ | |||
| 10613 | static void fts5DataRemoveSegment(Fts5Index *p, Fts5StructureSegment *pSeg){ | |||
| 10614 | int iSegid = pSeg->iSegid; | |||
| 10615 | i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(0)) ); | |||
| 10616 | i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)( ((i64)(iSegid+1) << (31 +5 +1)) + ((i64)(0) << ( 31 + 5)) + ((i64)(0) << (31)) + ((i64)(0)) )-1; | |||
| 10617 | fts5DataDelete(p, iFirst, iLast); | |||
| 10618 | ||||
| 10619 | if( pSeg->nPgTombstone ){ | |||
| 10620 | i64 iTomb1 = FTS5_TOMBSTONE_ROWID(iSegid, 0)( ((i64)(iSegid+(1<<16)) << (31 +5 +1)) + ((i64)( 0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(0)) ); | |||
| 10621 | i64 iTomb2 = FTS5_TOMBSTONE_ROWID(iSegid, pSeg->nPgTombstone-1)( ((i64)(iSegid+(1<<16)) << (31 +5 +1)) + ((i64)( 0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pSeg ->nPgTombstone-1)) ); | |||
| 10622 | fts5DataDelete(p, iTomb1, iTomb2); | |||
| 10623 | } | |||
| 10624 | if( p->pIdxDeleter==0 ){ | |||
| 10625 | Fts5Config *pConfig = p->pConfig; | |||
| 10626 | fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 10627 | "DELETE FROM '%q'.'%q_idx' WHERE segid=?", | |||
| 10628 | pConfig->zDb, pConfig->zName | |||
| 10629 | )); | |||
| 10630 | } | |||
| 10631 | if( p->rc==SQLITE_OK0 ){ | |||
| 10632 | sqlite3_bind_intsqlite3_api->bind_int(p->pIdxDeleter, 1, iSegid); | |||
| 10633 | sqlite3_stepsqlite3_api->step(p->pIdxDeleter); | |||
| 10634 | p->rc = sqlite3_resetsqlite3_api->reset(p->pIdxDeleter); | |||
| 10635 | } | |||
| 10636 | } | |||
| 10637 | ||||
| 10638 | /* | |||
| 10639 | ** Release a reference to an Fts5Structure object returned by an earlier | |||
| 10640 | ** call to fts5StructureRead() or fts5StructureDecode(). | |||
| 10641 | */ | |||
| 10642 | static void fts5StructureRelease(Fts5Structure *pStruct){ | |||
| 10643 | if( pStruct && 0>=(--pStruct->nRef) ){ | |||
| 10644 | int i; | |||
| 10645 | assert( pStruct->nRef==0 )((void) (0)); | |||
| 10646 | for(i=0; i<pStruct->nLevel; i++){ | |||
| 10647 | sqlite3_freesqlite3_api->free(pStruct->aLevel[i].aSeg); | |||
| 10648 | } | |||
| 10649 | sqlite3_freesqlite3_api->free(pStruct); | |||
| 10650 | } | |||
| 10651 | } | |||
| 10652 | ||||
| 10653 | static void fts5StructureRef(Fts5Structure *pStruct){ | |||
| 10654 | pStruct->nRef++; | |||
| 10655 | } | |||
| 10656 | ||||
| 10657 | static void *sqlite3Fts5StructureRef(Fts5Index *p){ | |||
| 10658 | fts5StructureRef(p->pStruct); | |||
| 10659 | return (void*)p->pStruct; | |||
| 10660 | } | |||
| 10661 | static void sqlite3Fts5StructureRelease(void *p){ | |||
| 10662 | if( p ){ | |||
| 10663 | fts5StructureRelease((Fts5Structure*)p); | |||
| 10664 | } | |||
| 10665 | } | |||
| 10666 | static int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){ | |||
| 10667 | if( p->pStruct!=(Fts5Structure*)pStruct ){ | |||
| 10668 | return SQLITE_ABORT4; | |||
| 10669 | } | |||
| 10670 | return SQLITE_OK0; | |||
| 10671 | } | |||
| 10672 | ||||
| 10673 | /* | |||
| 10674 | ** Ensure that structure object (*pp) is writable. | |||
| 10675 | ** | |||
| 10676 | ** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If | |||
| 10677 | ** an error occurs, (*pRc) is set to an SQLite error code before returning. | |||
| 10678 | */ | |||
| 10679 | static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ | |||
| 10680 | Fts5Structure *p = *pp; | |||
| 10681 | if( *pRc==SQLITE_OK0 && p->nRef>1 ){ | |||
| 10682 | i64 nByte = SZ_FTS5STRUCTURE(p->nLevel)(__builtin_offsetof(Fts5Structure, aLevel) + (p->nLevel)*sizeof (Fts5StructureLevel)); | |||
| 10683 | Fts5Structure *pNew; | |||
| 10684 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); | |||
| 10685 | if( pNew ){ | |||
| 10686 | int i; | |||
| 10687 | memcpy(pNew, p, nByte); | |||
| 10688 | for(i=0; i<p->nLevel; i++) pNew->aLevel[i].aSeg = 0; | |||
| 10689 | for(i=0; i<p->nLevel; i++){ | |||
| 10690 | Fts5StructureLevel *pLvl = &pNew->aLevel[i]; | |||
| 10691 | nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg; | |||
| 10692 | pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(pRc, nByte); | |||
| 10693 | if( pLvl->aSeg==0 ){ | |||
| 10694 | for(i=0; i<p->nLevel; i++){ | |||
| 10695 | sqlite3_freesqlite3_api->free(pNew->aLevel[i].aSeg); | |||
| 10696 | } | |||
| 10697 | sqlite3_freesqlite3_api->free(pNew); | |||
| 10698 | return; | |||
| 10699 | } | |||
| 10700 | memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte); | |||
| 10701 | } | |||
| 10702 | p->nRef--; | |||
| 10703 | pNew->nRef = 1; | |||
| 10704 | } | |||
| 10705 | *pp = pNew; | |||
| 10706 | } | |||
| 10707 | } | |||
| 10708 | ||||
| 10709 | /* | |||
| 10710 | ** Deserialize and return the structure record currently stored in serialized | |||
| 10711 | ** form within buffer pData/nData. | |||
| 10712 | ** | |||
| 10713 | ** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array | |||
| 10714 | ** are over-allocated by one slot. This allows the structure contents | |||
| 10715 | ** to be more easily edited. | |||
| 10716 | ** | |||
| 10717 | ** If an error occurs, *ppOut is set to NULL and an SQLite error code | |||
| 10718 | ** returned. Otherwise, *ppOut is set to point to the new object and | |||
| 10719 | ** SQLITE_OK returned. | |||
| 10720 | */ | |||
| 10721 | static int fts5StructureDecode( | |||
| 10722 | const u8 *pData, /* Buffer containing serialized structure */ | |||
| 10723 | int nData, /* Size of buffer pData in bytes */ | |||
| 10724 | int *piCookie, /* Configuration cookie value */ | |||
| 10725 | Fts5Structure **ppOut /* OUT: Deserialized object */ | |||
| 10726 | ){ | |||
| 10727 | int rc = SQLITE_OK0; | |||
| 10728 | int i = 0; | |||
| 10729 | int iLvl; | |||
| 10730 | int nLevel = 0; | |||
| 10731 | int nSegment = 0; | |||
| 10732 | sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */ | |||
| 10733 | Fts5Structure *pRet = 0; /* Structure object to return */ | |||
| 10734 | int bStructureV2 = 0; /* True for FTS5_STRUCTURE_V2 */ | |||
| 10735 | u64 nOriginCntr = 0; /* Largest origin value seen so far */ | |||
| 10736 | ||||
| 10737 | /* Grab the cookie value */ | |||
| 10738 | if( piCookie ) *piCookie = sqlite3Fts5Get32(pData); | |||
| 10739 | i = 4; | |||
| 10740 | ||||
| 10741 | /* Check if this is a V2 structure record. Set bStructureV2 if it is. */ | |||
| 10742 | if( 0==memcmp(&pData[i], FTS5_STRUCTURE_V2"\xFF\x00\x00\x01", 4) ){ | |||
| 10743 | i += 4; | |||
| 10744 | bStructureV2 = 1; | |||
| 10745 | } | |||
| 10746 | ||||
| 10747 | /* Read the total number of levels and segments from the start of the | |||
| 10748 | ** structure record. */ | |||
| 10749 | i += fts5GetVarint32(&pData[i], nLevel)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(nLevel)); | |||
| 10750 | i += fts5GetVarint32(&pData[i], nSegment)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(nSegment)); | |||
| 10751 | if( nLevel>FTS5_MAX_SEGMENT2000 || nLevel<0 | |||
| 10752 | || nSegment>FTS5_MAX_SEGMENT2000 || nSegment<0 | |||
| 10753 | ){ | |||
| 10754 | return FTS5_CORRUPT(11 | (1<<8)); | |||
| 10755 | } | |||
| 10756 | nByte = SZ_FTS5STRUCTURE(nLevel)(__builtin_offsetof(Fts5Structure, aLevel) + (nLevel)*sizeof( Fts5StructureLevel)); | |||
| 10757 | pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 10758 | ||||
| 10759 | if( pRet ){ | |||
| 10760 | pRet->nRef = 1; | |||
| 10761 | pRet->nLevel = nLevel; | |||
| 10762 | pRet->nSegment = nSegment; | |||
| 10763 | i += sqlite3Fts5GetVarint(&pData[i], &pRet->nWriteCounter); | |||
| 10764 | ||||
| 10765 | for(iLvl=0; rc==SQLITE_OK0 && iLvl<nLevel; iLvl++){ | |||
| 10766 | Fts5StructureLevel *pLvl = &pRet->aLevel[iLvl]; | |||
| 10767 | int nTotal = 0; | |||
| 10768 | int iSeg; | |||
| 10769 | ||||
| 10770 | if( i>=nData ){ | |||
| 10771 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10772 | }else{ | |||
| 10773 | i += fts5GetVarint32(&pData[i], pLvl->nMerge)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(pLvl->nMerge )); | |||
| 10774 | i += fts5GetVarint32(&pData[i], nTotal)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(nTotal)); | |||
| 10775 | if( nTotal<pLvl->nMerge ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10776 | pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, | |||
| 10777 | nTotal * sizeof(Fts5StructureSegment) | |||
| 10778 | ); | |||
| 10779 | nSegment -= nTotal; | |||
| 10780 | } | |||
| 10781 | ||||
| 10782 | if( rc==SQLITE_OK0 ){ | |||
| 10783 | pLvl->nSeg = nTotal; | |||
| 10784 | for(iSeg=0; iSeg<nTotal; iSeg++){ | |||
| 10785 | Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; | |||
| 10786 | if( i>=nData ){ | |||
| 10787 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10788 | break; | |||
| 10789 | } | |||
| 10790 | assert( pSeg!=0 )((void) (0)); | |||
| 10791 | i += fts5GetVarint32(&pData[i], pSeg->iSegid)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(pSeg->iSegid )); | |||
| 10792 | i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(pSeg->pgnoFirst )); | |||
| 10793 | i += fts5GetVarint32(&pData[i], pSeg->pgnoLast)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(pSeg->pgnoLast )); | |||
| 10794 | if( bStructureV2 ){ | |||
| 10795 | i += fts5GetVarintsqlite3Fts5GetVarint(&pData[i], &pSeg->iOrigin1); | |||
| 10796 | i += fts5GetVarintsqlite3Fts5GetVarint(&pData[i], &pSeg->iOrigin2); | |||
| 10797 | i += fts5GetVarint32(&pData[i], pSeg->nPgTombstone)sqlite3Fts5GetVarint32(&pData[i],(u32*)&(pSeg->nPgTombstone )); | |||
| 10798 | i += fts5GetVarintsqlite3Fts5GetVarint(&pData[i], &pSeg->nEntryTombstone); | |||
| 10799 | i += fts5GetVarintsqlite3Fts5GetVarint(&pData[i], &pSeg->nEntry); | |||
| 10800 | nOriginCntr = MAX(nOriginCntr, pSeg->iOrigin2)(((nOriginCntr) > (pSeg->iOrigin2)) ? (nOriginCntr) : ( pSeg->iOrigin2)); | |||
| 10801 | } | |||
| 10802 | if( pSeg->pgnoLast<pSeg->pgnoFirst ){ | |||
| 10803 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10804 | break; | |||
| 10805 | } | |||
| 10806 | } | |||
| 10807 | if( iLvl>0 && pLvl[-1].nMerge && nTotal==0 ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10808 | if( iLvl==nLevel-1 && pLvl->nMerge ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10809 | } | |||
| 10810 | } | |||
| 10811 | if( nSegment!=0 && rc==SQLITE_OK0 ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 10812 | if( bStructureV2 ){ | |||
| 10813 | pRet->nOriginCntr = nOriginCntr+1; | |||
| 10814 | } | |||
| 10815 | ||||
| 10816 | if( rc!=SQLITE_OK0 ){ | |||
| 10817 | fts5StructureRelease(pRet); | |||
| 10818 | pRet = 0; | |||
| 10819 | } | |||
| 10820 | } | |||
| 10821 | ||||
| 10822 | *ppOut = pRet; | |||
| 10823 | return rc; | |||
| 10824 | } | |||
| 10825 | ||||
| 10826 | /* | |||
| 10827 | ** Add a level to the Fts5Structure.aLevel[] array of structure object | |||
| 10828 | ** (*ppStruct). | |||
| 10829 | */ | |||
| 10830 | static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ | |||
| 10831 | fts5StructureMakeWritable(pRc, ppStruct); | |||
| 10832 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK )((void) (0)); | |||
| 10833 | if( *pRc==SQLITE_OK0 ){ | |||
| 10834 | Fts5Structure *pStruct = *ppStruct; | |||
| 10835 | int nLevel = pStruct->nLevel; | |||
| 10836 | sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2)(__builtin_offsetof(Fts5Structure, aLevel) + (nLevel+2)*sizeof (Fts5StructureLevel)); | |||
| 10837 | ||||
| 10838 | pStruct = sqlite3_realloc64sqlite3_api->realloc64(pStruct, nByte); | |||
| 10839 | if( pStruct ){ | |||
| 10840 | memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); | |||
| 10841 | pStruct->nLevel++; | |||
| 10842 | *ppStruct = pStruct; | |||
| 10843 | }else{ | |||
| 10844 | *pRc = SQLITE_NOMEM7; | |||
| 10845 | } | |||
| 10846 | } | |||
| 10847 | } | |||
| 10848 | ||||
| 10849 | /* | |||
| 10850 | ** Extend level iLvl so that there is room for at least nExtra more | |||
| 10851 | ** segments. | |||
| 10852 | */ | |||
| 10853 | static void fts5StructureExtendLevel( | |||
| 10854 | int *pRc, | |||
| 10855 | Fts5Structure *pStruct, | |||
| 10856 | int iLvl, | |||
| 10857 | int nExtra, | |||
| 10858 | int bInsert | |||
| 10859 | ){ | |||
| 10860 | if( *pRc==SQLITE_OK0 ){ | |||
| 10861 | Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; | |||
| 10862 | Fts5StructureSegment *aNew; | |||
| 10863 | sqlite3_int64 nByte; | |||
| 10864 | ||||
| 10865 | nByte = (pLvl->nSeg + nExtra) * sizeof(Fts5StructureSegment); | |||
| 10866 | aNew = sqlite3_realloc64sqlite3_api->realloc64(pLvl->aSeg, nByte); | |||
| 10867 | if( aNew ){ | |||
| 10868 | if( bInsert==0 ){ | |||
| 10869 | memset(&aNew[pLvl->nSeg], 0, sizeof(Fts5StructureSegment) * nExtra); | |||
| 10870 | }else{ | |||
| 10871 | int nMove = pLvl->nSeg * sizeof(Fts5StructureSegment); | |||
| 10872 | memmove(&aNew[nExtra], aNew, nMove); | |||
| 10873 | memset(aNew, 0, sizeof(Fts5StructureSegment) * nExtra); | |||
| 10874 | } | |||
| 10875 | pLvl->aSeg = aNew; | |||
| 10876 | }else{ | |||
| 10877 | *pRc = SQLITE_NOMEM7; | |||
| 10878 | } | |||
| 10879 | } | |||
| 10880 | } | |||
| 10881 | ||||
| 10882 | static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){ | |||
| 10883 | Fts5Structure *pRet = 0; | |||
| 10884 | Fts5Config *pConfig = p->pConfig; | |||
| 10885 | int iCookie; /* Configuration cookie */ | |||
| 10886 | Fts5Data *pData; | |||
| 10887 | ||||
| 10888 | pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID10); | |||
| 10889 | if( p->rc==SQLITE_OK0 ){ | |||
| 10890 | /* TODO: Do we need this if the leaf-index is appended? Probably... */ | |||
| 10891 | memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING20); | |||
| 10892 | p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet); | |||
| 10893 | if( p->rc==SQLITE_OK0 ){ | |||
| 10894 | if( (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){ | |||
| 10895 | p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie); | |||
| 10896 | } | |||
| 10897 | }else if( p->rc==SQLITE_CORRUPT_VTAB(11 | (1<<8)) ){ | |||
| 10898 | sqlite3Fts5ConfigErrmsg(p->pConfig, | |||
| 10899 | "fts5: corrupt structure record for table \"%s\"", p->pConfig->zName | |||
| 10900 | ); | |||
| 10901 | } | |||
| 10902 | fts5DataRelease(pData); | |||
| 10903 | if( p->rc!=SQLITE_OK0 ){ | |||
| 10904 | fts5StructureRelease(pRet); | |||
| 10905 | pRet = 0; | |||
| 10906 | } | |||
| 10907 | } | |||
| 10908 | ||||
| 10909 | return pRet; | |||
| 10910 | } | |||
| 10911 | ||||
| 10912 | static i64 fts5IndexDataVersion(Fts5Index *p){ | |||
| 10913 | i64 iVersion = 0; | |||
| 10914 | ||||
| 10915 | if( p->rc==SQLITE_OK0 ){ | |||
| 10916 | if( p->pDataVersion==0 ){ | |||
| 10917 | p->rc = fts5IndexPrepareStmt(p, &p->pDataVersion, | |||
| 10918 | sqlite3_mprintfsqlite3_api->mprintf("PRAGMA %Q.data_version", p->pConfig->zDb) | |||
| 10919 | ); | |||
| 10920 | if( p->rc ) return 0; | |||
| 10921 | } | |||
| 10922 | ||||
| 10923 | if( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(p->pDataVersion) ){ | |||
| 10924 | iVersion = sqlite3_column_int64sqlite3_api->column_int64(p->pDataVersion, 0); | |||
| 10925 | } | |||
| 10926 | p->rc = sqlite3_resetsqlite3_api->reset(p->pDataVersion); | |||
| 10927 | } | |||
| 10928 | ||||
| 10929 | return iVersion; | |||
| 10930 | } | |||
| 10931 | ||||
| 10932 | /* | |||
| 10933 | ** Read, deserialize and return the structure record. | |||
| 10934 | ** | |||
| 10935 | ** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array | |||
| 10936 | ** are over-allocated as described for function fts5StructureDecode() | |||
| 10937 | ** above. | |||
| 10938 | ** | |||
| 10939 | ** If an error occurs, NULL is returned and an error code left in the | |||
| 10940 | ** Fts5Index handle. If an error has already occurred when this function | |||
| 10941 | ** is called, it is a no-op. | |||
| 10942 | */ | |||
| 10943 | static Fts5Structure *fts5StructureRead(Fts5Index *p){ | |||
| 10944 | ||||
| 10945 | if( p->pStruct==0 ){ | |||
| 10946 | p->iStructVersion = fts5IndexDataVersion(p); | |||
| 10947 | if( p->rc==SQLITE_OK0 ){ | |||
| 10948 | p->pStruct = fts5StructureReadUncached(p); | |||
| 10949 | } | |||
| 10950 | } | |||
| 10951 | ||||
| 10952 | #if 0 | |||
| 10953 | else{ | |||
| 10954 | Fts5Structure *pTest = fts5StructureReadUncached(p); | |||
| 10955 | if( pTest ){ | |||
| 10956 | int i, j; | |||
| 10957 | assert_nc( p->pStruct->nSegment==pTest->nSegment )((void) (0)); | |||
| 10958 | assert_nc( p->pStruct->nLevel==pTest->nLevel )((void) (0)); | |||
| 10959 | for(i=0; i<pTest->nLevel; i++){ | |||
| 10960 | assert_nc( p->pStruct->aLevel[i].nMerge==pTest->aLevel[i].nMerge )((void) (0)); | |||
| 10961 | assert_nc( p->pStruct->aLevel[i].nSeg==pTest->aLevel[i].nSeg )((void) (0)); | |||
| 10962 | for(j=0; j<pTest->aLevel[i].nSeg; j++){ | |||
| 10963 | Fts5StructureSegment *p1 = &pTest->aLevel[i].aSeg[j]; | |||
| 10964 | Fts5StructureSegment *p2 = &p->pStruct->aLevel[i].aSeg[j]; | |||
| 10965 | assert_nc( p1->iSegid==p2->iSegid )((void) (0)); | |||
| 10966 | assert_nc( p1->pgnoFirst==p2->pgnoFirst )((void) (0)); | |||
| 10967 | assert_nc( p1->pgnoLast==p2->pgnoLast )((void) (0)); | |||
| 10968 | } | |||
| 10969 | } | |||
| 10970 | fts5StructureRelease(pTest); | |||
| 10971 | } | |||
| 10972 | } | |||
| 10973 | #endif | |||
| 10974 | ||||
| 10975 | if( p->rc!=SQLITE_OK0 ) return 0; | |||
| 10976 | assert( p->iStructVersion!=0 )((void) (0)); | |||
| 10977 | assert( p->pStruct!=0 )((void) (0)); | |||
| 10978 | fts5StructureRef(p->pStruct); | |||
| 10979 | return p->pStruct; | |||
| 10980 | } | |||
| 10981 | ||||
| 10982 | static void fts5StructureInvalidate(Fts5Index *p){ | |||
| 10983 | if( p->pStruct ){ | |||
| 10984 | fts5StructureRelease(p->pStruct); | |||
| 10985 | p->pStruct = 0; | |||
| 10986 | } | |||
| 10987 | } | |||
| 10988 | ||||
| 10989 | /* | |||
| 10990 | ** Return the total number of segments in index structure pStruct. This | |||
| 10991 | ** function is only ever used as part of assert() conditions. | |||
| 10992 | */ | |||
| 10993 | #ifdef SQLITE_DEBUG | |||
| 10994 | static int fts5StructureCountSegments(Fts5Structure *pStruct){ | |||
| 10995 | int nSegment = 0; /* Total number of segments */ | |||
| 10996 | if( pStruct ){ | |||
| 10997 | int iLvl; /* Used to iterate through levels */ | |||
| 10998 | for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ | |||
| 10999 | nSegment += pStruct->aLevel[iLvl].nSeg; | |||
| 11000 | } | |||
| 11001 | } | |||
| 11002 | ||||
| 11003 | return nSegment; | |||
| 11004 | } | |||
| 11005 | #endif | |||
| 11006 | ||||
| 11007 | #define fts5BufferSafeAppendBlob(pBuf, pBlob, nBlob){ ((void) (0)); memcpy(&(pBuf)->p[(pBuf)->n], pBlob , nBlob); (pBuf)->n += nBlob; } { \ | |||
| 11008 | assert( (pBuf)->nSpace>=((pBuf)->n+nBlob) )((void) (0)); \ | |||
| 11009 | memcpy(&(pBuf)->p[(pBuf)->n], pBlob, nBlob); \ | |||
| 11010 | (pBuf)->n += nBlob; \ | |||
| 11011 | } | |||
| 11012 | ||||
| 11013 | #define fts5BufferSafeAppendVarint(pBuf, iVal){ (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf )->n], (iVal)); ((void) (0)); } { \ | |||
| 11014 | (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf)->n], (iVal)); \ | |||
| 11015 | assert( (pBuf)->nSpace>=(pBuf)->n )((void) (0)); \ | |||
| 11016 | } | |||
| 11017 | ||||
| 11018 | ||||
| 11019 | /* | |||
| 11020 | ** Serialize and store the "structure" record. | |||
| 11021 | ** | |||
| 11022 | ** If an error occurs, leave an error code in the Fts5Index object. If an | |||
| 11023 | ** error has already occurred, this function is a no-op. | |||
| 11024 | */ | |||
| 11025 | static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ | |||
| 11026 | if( p->rc==SQLITE_OK0 ){ | |||
| 11027 | Fts5Buffer buf; /* Buffer to serialize record into */ | |||
| 11028 | int iLvl; /* Used to iterate through levels */ | |||
| 11029 | int iCookie; /* Cookie value to store */ | |||
| 11030 | int nHdr = (pStruct->nOriginCntr>0 ? (4+4+9+9+9) : (4+9+9)); | |||
| 11031 | ||||
| 11032 | assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) )((void) (0)); | |||
| 11033 | memset(&buf, 0, sizeof(Fts5Buffer)); | |||
| 11034 | ||||
| 11035 | /* Append the current configuration cookie */ | |||
| 11036 | iCookie = p->pConfig->iCookie; | |||
| 11037 | if( iCookie<0 ) iCookie = 0; | |||
| 11038 | ||||
| 11039 | if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, nHdr) ){ | |||
| 11040 | sqlite3Fts5Put32(buf.p, iCookie); | |||
| 11041 | buf.n = 4; | |||
| 11042 | if( pStruct->nOriginCntr>0 ){ | |||
| 11043 | fts5BufferSafeAppendBlob(&buf, FTS5_STRUCTURE_V2, 4){ ((void) (0)); memcpy(&(&buf)->p[(&buf)->n ], "\xFF\x00\x00\x01", 4); (&buf)->n += 4; }; | |||
| 11044 | } | |||
| 11045 | fts5BufferSafeAppendVarint(&buf, pStruct->nLevel){ (&buf)->n += sqlite3Fts5PutVarint(&(&buf)-> p[(&buf)->n], (pStruct->nLevel)); ((void) (0)); }; | |||
| 11046 | fts5BufferSafeAppendVarint(&buf, pStruct->nSegment){ (&buf)->n += sqlite3Fts5PutVarint(&(&buf)-> p[(&buf)->n], (pStruct->nSegment)); ((void) (0)); }; | |||
| 11047 | fts5BufferSafeAppendVarint(&buf, (i64)pStruct->nWriteCounter){ (&buf)->n += sqlite3Fts5PutVarint(&(&buf)-> p[(&buf)->n], ((i64)pStruct->nWriteCounter)); ((void ) (0)); }; | |||
| 11048 | } | |||
| 11049 | ||||
| 11050 | for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ | |||
| 11051 | int iSeg; /* Used to iterate through segments */ | |||
| 11052 | Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; | |||
| 11053 | fts5BufferAppendVarint(&p->rc, &buf, pLvl->nMerge)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pLvl ->nMerge); | |||
| 11054 | fts5BufferAppendVarint(&p->rc, &buf, pLvl->nSeg)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pLvl ->nSeg); | |||
| 11055 | assert( pLvl->nMerge<=pLvl->nSeg )((void) (0)); | |||
| 11056 | ||||
| 11057 | for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){ | |||
| 11058 | Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; | |||
| 11059 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->iSegid)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->iSegid); | |||
| 11060 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoFirst)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->pgnoFirst); | |||
| 11061 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoLast)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->pgnoLast); | |||
| 11062 | if( pStruct->nOriginCntr>0 ){ | |||
| 11063 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin1)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->iOrigin1); | |||
| 11064 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin2)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->iOrigin2); | |||
| 11065 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->nPgTombstone)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->nPgTombstone); | |||
| 11066 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntryTombstone)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->nEntryTombstone); | |||
| 11067 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntry)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->nEntry); | |||
| 11068 | } | |||
| 11069 | } | |||
| 11070 | } | |||
| 11071 | ||||
| 11072 | fts5DataWrite(p, FTS5_STRUCTURE_ROWID10, buf.p, buf.n); | |||
| 11073 | fts5BufferFree(&buf)sqlite3Fts5BufferFree(&buf); | |||
| 11074 | } | |||
| 11075 | } | |||
| 11076 | ||||
| 11077 | #if 0 | |||
| 11078 | static void fts5DebugStructure(int*,Fts5Buffer*,Fts5Structure*); | |||
| 11079 | static void fts5PrintStructure(const char *zCaption, Fts5Structure *pStruct){ | |||
| 11080 | int rc = SQLITE_OK0; | |||
| 11081 | Fts5Buffer buf; | |||
| 11082 | memset(&buf, 0, sizeof(buf)); | |||
| 11083 | fts5DebugStructure(&rc, &buf, pStruct); | |||
| 11084 | fprintf(stdout, "%s: %s\n", zCaption, buf.p); | |||
| 11085 | fflush(stdout); | |||
| 11086 | fts5BufferFree(&buf)sqlite3Fts5BufferFree(&buf); | |||
| 11087 | } | |||
| 11088 | #else | |||
| 11089 | # define fts5PrintStructure(x,y) | |||
| 11090 | #endif | |||
| 11091 | ||||
| 11092 | static int fts5SegmentSize(Fts5StructureSegment *pSeg){ | |||
| 11093 | return 1 + pSeg->pgnoLast - pSeg->pgnoFirst; | |||
| 11094 | } | |||
| 11095 | ||||
| 11096 | /* | |||
| 11097 | ** Return a copy of index structure pStruct. Except, promote as many | |||
| 11098 | ** segments as possible to level iPromote. If an OOM occurs, NULL is | |||
| 11099 | ** returned. | |||
| 11100 | */ | |||
| 11101 | static void fts5StructurePromoteTo( | |||
| 11102 | Fts5Index *p, | |||
| 11103 | int iPromote, | |||
| 11104 | int szPromote, | |||
| 11105 | Fts5Structure *pStruct | |||
| 11106 | ){ | |||
| 11107 | int il, is; | |||
| 11108 | Fts5StructureLevel *pOut = &pStruct->aLevel[iPromote]; | |||
| 11109 | ||||
| 11110 | if( pOut->nMerge==0 ){ | |||
| 11111 | for(il=iPromote+1; il<pStruct->nLevel; il++){ | |||
| 11112 | Fts5StructureLevel *pLvl = &pStruct->aLevel[il]; | |||
| 11113 | if( pLvl->nMerge ) return; | |||
| 11114 | for(is=pLvl->nSeg-1; is>=0; is--){ | |||
| 11115 | int sz = fts5SegmentSize(&pLvl->aSeg[is]); | |||
| 11116 | if( sz>szPromote ) return; | |||
| 11117 | fts5StructureExtendLevel(&p->rc, pStruct, iPromote, 1, 1); | |||
| 11118 | if( p->rc ) return; | |||
| 11119 | memcpy(pOut->aSeg, &pLvl->aSeg[is], sizeof(Fts5StructureSegment)); | |||
| 11120 | pOut->nSeg++; | |||
| 11121 | pLvl->nSeg--; | |||
| 11122 | } | |||
| 11123 | } | |||
| 11124 | } | |||
| 11125 | } | |||
| 11126 | ||||
| 11127 | /* | |||
| 11128 | ** A new segment has just been written to level iLvl of index structure | |||
| 11129 | ** pStruct. This function determines if any segments should be promoted | |||
| 11130 | ** as a result. Segments are promoted in two scenarios: | |||
| 11131 | ** | |||
| 11132 | ** a) If the segment just written is smaller than one or more segments | |||
| 11133 | ** within the previous populated level, it is promoted to the previous | |||
| 11134 | ** populated level. | |||
| 11135 | ** | |||
| 11136 | ** b) If the segment just written is larger than the newest segment on | |||
| 11137 | ** the next populated level, then that segment, and any other adjacent | |||
| 11138 | ** segments that are also smaller than the one just written, are | |||
| 11139 | ** promoted. | |||
| 11140 | ** | |||
| 11141 | ** If one or more segments are promoted, the structure object is updated | |||
| 11142 | ** to reflect this. | |||
| 11143 | */ | |||
| 11144 | static void fts5StructurePromote( | |||
| 11145 | Fts5Index *p, /* FTS5 backend object */ | |||
| 11146 | int iLvl, /* Index level just updated */ | |||
| 11147 | Fts5Structure *pStruct /* Index structure */ | |||
| 11148 | ){ | |||
| 11149 | if( p->rc==SQLITE_OK0 ){ | |||
| 11150 | int iTst; | |||
| 11151 | int iPromote = -1; | |||
| 11152 | int szPromote = 0; /* Promote anything this size or smaller */ | |||
| 11153 | Fts5StructureSegment *pSeg; /* Segment just written */ | |||
| 11154 | int szSeg; /* Size of segment just written */ | |||
| 11155 | int nSeg = pStruct->aLevel[iLvl].nSeg; | |||
| 11156 | ||||
| 11157 | if( nSeg==0 ) return; | |||
| 11158 | pSeg = &pStruct->aLevel[iLvl].aSeg[pStruct->aLevel[iLvl].nSeg-1]; | |||
| 11159 | szSeg = (1 + pSeg->pgnoLast - pSeg->pgnoFirst); | |||
| 11160 | ||||
| 11161 | /* Check for condition (a) */ | |||
| 11162 | for(iTst=iLvl-1; iTst>=0 && pStruct->aLevel[iTst].nSeg==0; iTst--); | |||
| 11163 | if( iTst>=0 ){ | |||
| 11164 | int i; | |||
| 11165 | int szMax = 0; | |||
| 11166 | Fts5StructureLevel *pTst = &pStruct->aLevel[iTst]; | |||
| 11167 | assert( pTst->nMerge==0 )((void) (0)); | |||
| 11168 | for(i=0; i<pTst->nSeg; i++){ | |||
| 11169 | int sz = pTst->aSeg[i].pgnoLast - pTst->aSeg[i].pgnoFirst + 1; | |||
| 11170 | if( sz>szMax ) szMax = sz; | |||
| 11171 | } | |||
| 11172 | if( szMax>=szSeg ){ | |||
| 11173 | /* Condition (a) is true. Promote the newest segment on level | |||
| 11174 | ** iLvl to level iTst. */ | |||
| 11175 | iPromote = iTst; | |||
| 11176 | szPromote = szMax; | |||
| 11177 | } | |||
| 11178 | } | |||
| 11179 | ||||
| 11180 | /* If condition (a) is not met, assume (b) is true. StructurePromoteTo() | |||
| 11181 | ** is a no-op if it is not. */ | |||
| 11182 | if( iPromote<0 ){ | |||
| 11183 | iPromote = iLvl; | |||
| 11184 | szPromote = szSeg; | |||
| 11185 | } | |||
| 11186 | fts5StructurePromoteTo(p, iPromote, szPromote, pStruct); | |||
| 11187 | } | |||
| 11188 | } | |||
| 11189 | ||||
| 11190 | ||||
| 11191 | /* | |||
| 11192 | ** Advance the iterator passed as the only argument. If the end of the | |||
| 11193 | ** doclist-index page is reached, return non-zero. | |||
| 11194 | */ | |||
| 11195 | static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){ | |||
| 11196 | Fts5Data *pData = pLvl->pData; | |||
| 11197 | ||||
| 11198 | if( pLvl->iOff==0 ){ | |||
| 11199 | assert( pLvl->bEof==0 )((void) (0)); | |||
| 11200 | pLvl->iOff = 1; | |||
| 11201 | pLvl->iOff += fts5GetVarint32(&pData->p[1], pLvl->iLeafPgno)sqlite3Fts5GetVarint32(&pData->p[1],(u32*)&(pLvl-> iLeafPgno)); | |||
| 11202 | pLvl->iOff += fts5GetVarintsqlite3Fts5GetVarint(&pData->p[pLvl->iOff], (u64*)&pLvl->iRowid); | |||
| 11203 | pLvl->iFirstOff = pLvl->iOff; | |||
| 11204 | }else{ | |||
| 11205 | int iOff; | |||
| 11206 | for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){ | |||
| 11207 | if( pData->p[iOff] ) break; | |||
| 11208 | } | |||
| 11209 | ||||
| 11210 | if( iOff<pData->nn ){ | |||
| 11211 | u64 iVal; | |||
| 11212 | pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1; | |||
| 11213 | iOff += fts5GetVarintsqlite3Fts5GetVarint(&pData->p[iOff], &iVal); | |||
| 11214 | pLvl->iRowid += iVal; | |||
| 11215 | pLvl->iOff = iOff; | |||
| 11216 | }else{ | |||
| 11217 | pLvl->bEof = 1; | |||
| 11218 | } | |||
| 11219 | } | |||
| 11220 | ||||
| 11221 | return pLvl->bEof; | |||
| 11222 | } | |||
| 11223 | ||||
| 11224 | /* | |||
| 11225 | ** Advance the iterator passed as the only argument. | |||
| 11226 | */ | |||
| 11227 | static int fts5DlidxIterNextR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){ | |||
| 11228 | Fts5DlidxLvl *pLvl = &pIter->aLvl[iLvl]; | |||
| 11229 | ||||
| 11230 | assert( iLvl<pIter->nLvl )((void) (0)); | |||
| 11231 | if( fts5DlidxLvlNext(pLvl) ){ | |||
| 11232 | if( (iLvl+1) < pIter->nLvl ){ | |||
| 11233 | fts5DlidxIterNextR(p, pIter, iLvl+1); | |||
| 11234 | if( pLvl[1].bEof==0 ){ | |||
| 11235 | fts5DataRelease(pLvl->pData); | |||
| 11236 | memset(pLvl, 0, sizeof(Fts5DlidxLvl)); | |||
| 11237 | pLvl->pData = fts5DataRead(p, | |||
| 11238 | FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)( ((i64)(pIter->iSegid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(iLvl) << (31)) + ((i64)(pLvl[1].iLeafPgno )) ) | |||
| 11239 | ); | |||
| 11240 | if( pLvl->pData ) fts5DlidxLvlNext(pLvl); | |||
| 11241 | } | |||
| 11242 | } | |||
| 11243 | } | |||
| 11244 | ||||
| 11245 | return pIter->aLvl[0].bEof; | |||
| 11246 | } | |||
| 11247 | static int fts5DlidxIterNext(Fts5Index *p, Fts5DlidxIter *pIter){ | |||
| 11248 | return fts5DlidxIterNextR(p, pIter, 0); | |||
| 11249 | } | |||
| 11250 | ||||
| 11251 | /* | |||
| 11252 | ** The iterator passed as the first argument has the following fields set | |||
| 11253 | ** as follows. This function sets up the rest of the iterator so that it | |||
| 11254 | ** points to the first rowid in the doclist-index. | |||
| 11255 | ** | |||
| 11256 | ** pData: | |||
| 11257 | ** pointer to doclist-index record, | |||
| 11258 | ** | |||
| 11259 | ** When this function is called pIter->iLeafPgno is the page number the | |||
| 11260 | ** doclist is associated with (the one featuring the term). | |||
| 11261 | */ | |||
| 11262 | static int fts5DlidxIterFirst(Fts5DlidxIter *pIter){ | |||
| 11263 | int i; | |||
| 11264 | for(i=0; i<pIter->nLvl; i++){ | |||
| 11265 | fts5DlidxLvlNext(&pIter->aLvl[i]); | |||
| 11266 | } | |||
| 11267 | return pIter->aLvl[0].bEof; | |||
| 11268 | } | |||
| 11269 | ||||
| 11270 | ||||
| 11271 | static int fts5DlidxIterEof(Fts5Index *p, Fts5DlidxIter *pIter){ | |||
| 11272 | return p->rc!=SQLITE_OK0 || pIter->aLvl[0].bEof; | |||
| 11273 | } | |||
| 11274 | ||||
| 11275 | static void fts5DlidxIterLast(Fts5Index *p, Fts5DlidxIter *pIter){ | |||
| 11276 | int i; | |||
| 11277 | ||||
| 11278 | /* Advance each level to the last entry on the last page */ | |||
| 11279 | for(i=pIter->nLvl-1; p->rc==SQLITE_OK0 && i>=0; i--){ | |||
| 11280 | Fts5DlidxLvl *pLvl = &pIter->aLvl[i]; | |||
| 11281 | while( fts5DlidxLvlNext(pLvl)==0 ); | |||
| 11282 | pLvl->bEof = 0; | |||
| 11283 | ||||
| 11284 | if( i>0 ){ | |||
| 11285 | Fts5DlidxLvl *pChild = &pLvl[-1]; | |||
| 11286 | fts5DataRelease(pChild->pData); | |||
| 11287 | memset(pChild, 0, sizeof(Fts5DlidxLvl)); | |||
| 11288 | pChild->pData = fts5DataRead(p, | |||
| 11289 | FTS5_DLIDX_ROWID(pIter->iSegid, i-1, pLvl->iLeafPgno)( ((i64)(pIter->iSegid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(i-1) << (31)) + ((i64)(pLvl->iLeafPgno )) ) | |||
| 11290 | ); | |||
| 11291 | } | |||
| 11292 | } | |||
| 11293 | } | |||
| 11294 | ||||
| 11295 | /* | |||
| 11296 | ** Move the iterator passed as the only argument to the previous entry. | |||
| 11297 | */ | |||
| 11298 | static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){ | |||
| 11299 | int iOff = pLvl->iOff; | |||
| 11300 | ||||
| 11301 | assert( pLvl->bEof==0 )((void) (0)); | |||
| 11302 | if( iOff<=pLvl->iFirstOff ){ | |||
| 11303 | pLvl->bEof = 1; | |||
| 11304 | }else{ | |||
| 11305 | u8 *a = pLvl->pData->p; | |||
| 11306 | ||||
| 11307 | pLvl->iOff = 0; | |||
| 11308 | fts5DlidxLvlNext(pLvl); | |||
| 11309 | while( 1 ){ | |||
| 11310 | int nZero = 0; | |||
| 11311 | int ii = pLvl->iOff; | |||
| 11312 | u64 delta = 0; | |||
| 11313 | ||||
| 11314 | while( a[ii]==0 ){ | |||
| 11315 | nZero++; | |||
| 11316 | ii++; | |||
| 11317 | } | |||
| 11318 | ii += sqlite3Fts5GetVarint(&a[ii], &delta); | |||
| 11319 | ||||
| 11320 | if( ii>=iOff ) break; | |||
| 11321 | pLvl->iLeafPgno += nZero+1; | |||
| 11322 | pLvl->iRowid += delta; | |||
| 11323 | pLvl->iOff = ii; | |||
| 11324 | } | |||
| 11325 | } | |||
| 11326 | ||||
| 11327 | return pLvl->bEof; | |||
| 11328 | } | |||
| 11329 | ||||
| 11330 | static int fts5DlidxIterPrevR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){ | |||
| 11331 | Fts5DlidxLvl *pLvl = &pIter->aLvl[iLvl]; | |||
| 11332 | ||||
| 11333 | assert( iLvl<pIter->nLvl )((void) (0)); | |||
| 11334 | if( fts5DlidxLvlPrev(pLvl) ){ | |||
| 11335 | if( (iLvl+1) < pIter->nLvl ){ | |||
| 11336 | fts5DlidxIterPrevR(p, pIter, iLvl+1); | |||
| 11337 | if( pLvl[1].bEof==0 ){ | |||
| 11338 | fts5DataRelease(pLvl->pData); | |||
| 11339 | memset(pLvl, 0, sizeof(Fts5DlidxLvl)); | |||
| 11340 | pLvl->pData = fts5DataRead(p, | |||
| 11341 | FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)( ((i64)(pIter->iSegid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(iLvl) << (31)) + ((i64)(pLvl[1].iLeafPgno )) ) | |||
| 11342 | ); | |||
| 11343 | if( pLvl->pData ){ | |||
| 11344 | while( fts5DlidxLvlNext(pLvl)==0 ); | |||
| 11345 | pLvl->bEof = 0; | |||
| 11346 | } | |||
| 11347 | } | |||
| 11348 | } | |||
| 11349 | } | |||
| 11350 | ||||
| 11351 | return pIter->aLvl[0].bEof; | |||
| 11352 | } | |||
| 11353 | static int fts5DlidxIterPrev(Fts5Index *p, Fts5DlidxIter *pIter){ | |||
| 11354 | return fts5DlidxIterPrevR(p, pIter, 0); | |||
| 11355 | } | |||
| 11356 | ||||
| 11357 | /* | |||
| 11358 | ** Free a doclist-index iterator object allocated by fts5DlidxIterInit(). | |||
| 11359 | */ | |||
| 11360 | static void fts5DlidxIterFree(Fts5DlidxIter *pIter){ | |||
| 11361 | if( pIter ){ | |||
| 11362 | int i; | |||
| 11363 | for(i=0; i<pIter->nLvl; i++){ | |||
| 11364 | fts5DataRelease(pIter->aLvl[i].pData); | |||
| 11365 | } | |||
| 11366 | sqlite3_freesqlite3_api->free(pIter); | |||
| 11367 | } | |||
| 11368 | } | |||
| 11369 | ||||
| 11370 | static Fts5DlidxIter *fts5DlidxIterInit( | |||
| 11371 | Fts5Index *p, /* Fts5 Backend to iterate within */ | |||
| 11372 | int bRev, /* True for ORDER BY ASC */ | |||
| 11373 | int iSegid, /* Segment id */ | |||
| 11374 | int iLeafPg /* Leaf page number to load dlidx for */ | |||
| 11375 | ){ | |||
| 11376 | Fts5DlidxIter *pIter = 0; | |||
| 11377 | int i; | |||
| 11378 | int bDone = 0; | |||
| 11379 | ||||
| 11380 | for(i=0; p->rc==SQLITE_OK0 && bDone==0; i++){ | |||
| 11381 | sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1)(__builtin_offsetof(Fts5DlidxIter, aLvl)+(i+1)*sizeof(Fts5DlidxLvl )); | |||
| 11382 | Fts5DlidxIter *pNew; | |||
| 11383 | ||||
| 11384 | pNew = (Fts5DlidxIter*)sqlite3_realloc64sqlite3_api->realloc64(pIter, nByte); | |||
| 11385 | if( pNew==0 ){ | |||
| 11386 | p->rc = SQLITE_NOMEM7; | |||
| 11387 | }else{ | |||
| 11388 | i64 iRowid = FTS5_DLIDX_ROWID(iSegid, i, iLeafPg)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(i) << (31)) + ((i64)(iLeafPg)) ); | |||
| 11389 | Fts5DlidxLvl *pLvl = &pNew->aLvl[i]; | |||
| 11390 | pIter = pNew; | |||
| 11391 | memset(pLvl, 0, sizeof(Fts5DlidxLvl)); | |||
| 11392 | pLvl->pData = fts5DataRead(p, iRowid); | |||
| 11393 | if( pLvl->pData && (pLvl->pData->p[0] & 0x0001)==0 ){ | |||
| 11394 | bDone = 1; | |||
| 11395 | } | |||
| 11396 | pIter->nLvl = i+1; | |||
| 11397 | } | |||
| 11398 | } | |||
| 11399 | ||||
| 11400 | if( p->rc==SQLITE_OK0 ){ | |||
| 11401 | pIter->iSegid = iSegid; | |||
| 11402 | if( bRev==0 ){ | |||
| 11403 | fts5DlidxIterFirst(pIter); | |||
| 11404 | }else{ | |||
| 11405 | fts5DlidxIterLast(p, pIter); | |||
| 11406 | } | |||
| 11407 | } | |||
| 11408 | ||||
| 11409 | if( p->rc!=SQLITE_OK0 ){ | |||
| 11410 | fts5DlidxIterFree(pIter); | |||
| 11411 | pIter = 0; | |||
| 11412 | } | |||
| 11413 | ||||
| 11414 | return pIter; | |||
| 11415 | } | |||
| 11416 | ||||
| 11417 | static i64 fts5DlidxIterRowid(Fts5DlidxIter *pIter){ | |||
| 11418 | return pIter->aLvl[0].iRowid; | |||
| 11419 | } | |||
| 11420 | static int fts5DlidxIterPgno(Fts5DlidxIter *pIter){ | |||
| 11421 | return pIter->aLvl[0].iLeafPgno; | |||
| 11422 | } | |||
| 11423 | ||||
| 11424 | /* | |||
| 11425 | ** Load the next leaf page into the segment iterator. | |||
| 11426 | */ | |||
| 11427 | static void fts5SegIterNextPage( | |||
| 11428 | Fts5Index *p, /* FTS5 backend object */ | |||
| 11429 | Fts5SegIter *pIter /* Iterator to advance to next page */ | |||
| 11430 | ){ | |||
| 11431 | Fts5Data *pLeaf; | |||
| 11432 | Fts5StructureSegment *pSeg = pIter->pSeg; | |||
| 11433 | fts5DataRelease(pIter->pLeaf); | |||
| 11434 | pIter->iLeafPgno++; | |||
| 11435 | if( pIter->pNextLeaf ){ | |||
| 11436 | pIter->pLeaf = pIter->pNextLeaf; | |||
| 11437 | pIter->pNextLeaf = 0; | |||
| 11438 | }else if( pIter->iLeafPgno<=pSeg->pgnoLast ){ | |||
| 11439 | pIter->pLeaf = fts5LeafRead(p, | |||
| 11440 | FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)( ((i64)(pSeg->iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pIter->iLeafPgno )) ) | |||
| 11441 | ); | |||
| 11442 | }else{ | |||
| 11443 | pIter->pLeaf = 0; | |||
| 11444 | } | |||
| 11445 | pLeaf = pIter->pLeaf; | |||
| 11446 | ||||
| 11447 | if( pLeaf ){ | |||
| 11448 | pIter->iPgidxOff = pLeaf->szLeaf; | |||
| 11449 | if( fts5LeafIsTermless(pLeaf)((pLeaf)->szLeaf >= (pLeaf)->nn) ){ | |||
| 11450 | pIter->iEndofDoclist = pLeaf->nn+1; | |||
| 11451 | }else{ | |||
| 11452 | pIter->iPgidxOff += fts5GetVarint32(&pLeaf->p[pIter->iPgidxOff],sqlite3Fts5GetVarint32(&pLeaf->p[pIter->iPgidxOff], (u32*)&(pIter->iEndofDoclist)) | |||
| 11453 | pIter->iEndofDoclistsqlite3Fts5GetVarint32(&pLeaf->p[pIter->iPgidxOff], (u32*)&(pIter->iEndofDoclist)) | |||
| 11454 | )sqlite3Fts5GetVarint32(&pLeaf->p[pIter->iPgidxOff], (u32*)&(pIter->iEndofDoclist)); | |||
| 11455 | } | |||
| 11456 | } | |||
| 11457 | } | |||
| 11458 | ||||
| 11459 | /* | |||
| 11460 | ** Argument p points to a buffer containing a varint to be interpreted as a | |||
| 11461 | ** position list size field. Read the varint and return the number of bytes | |||
| 11462 | ** read. Before returning, set *pnSz to the number of bytes in the position | |||
| 11463 | ** list, and *pbDel to true if the delete flag is set, or false otherwise. | |||
| 11464 | */ | |||
| 11465 | static int fts5GetPoslistSize(const u8 *p, int *pnSz, int *pbDel){ | |||
| 11466 | int nSz; | |||
| 11467 | int n = 0; | |||
| 11468 | fts5FastGetVarint32(p, n, nSz){ nSz = (p)[n++]; if( nSz & 0x80 ){ n--; n += sqlite3Fts5GetVarint32 (&(p)[n],(u32*)&(nSz)); } }; | |||
| 11469 | assert_nc( nSz>=0 )((void) (0)); | |||
| 11470 | *pnSz = nSz/2; | |||
| 11471 | *pbDel = nSz & 0x0001; | |||
| 11472 | return n; | |||
| 11473 | } | |||
| 11474 | ||||
| 11475 | /* | |||
| 11476 | ** Fts5SegIter.iLeafOffset currently points to the first byte of a | |||
| 11477 | ** position-list size field. Read the value of the field and store it | |||
| 11478 | ** in the following variables: | |||
| 11479 | ** | |||
| 11480 | ** Fts5SegIter.nPos | |||
| 11481 | ** Fts5SegIter.bDel | |||
| 11482 | ** | |||
| 11483 | ** Leave Fts5SegIter.iLeafOffset pointing to the first byte of the | |||
| 11484 | ** position list content (if any). | |||
| 11485 | */ | |||
| 11486 | static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 11487 | if( p->rc==SQLITE_OK0 ){ | |||
| 11488 | int iOff = pIter->iLeafOffset; /* Offset to read at */ | |||
| 11489 | ASSERT_SZLEAF_OK(pIter->pLeaf)((void) (0)); | |||
| 11490 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 11491 | int iEod = MIN(pIter->iEndofDoclist, pIter->pLeaf->szLeaf)(((pIter->iEndofDoclist) < (pIter->pLeaf->szLeaf) ) ? (pIter->iEndofDoclist) : (pIter->pLeaf->szLeaf)); | |||
| 11492 | pIter->bDel = 0; | |||
| 11493 | pIter->nPos = 1; | |||
| 11494 | if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){ | |||
| 11495 | pIter->bDel = 1; | |||
| 11496 | iOff++; | |||
| 11497 | if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){ | |||
| 11498 | pIter->nPos = 1; | |||
| 11499 | iOff++; | |||
| 11500 | }else{ | |||
| 11501 | pIter->nPos = 0; | |||
| 11502 | } | |||
| 11503 | } | |||
| 11504 | }else{ | |||
| 11505 | int nSz; | |||
| 11506 | fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz){ nSz = (pIter->pLeaf->p)[iOff++]; if( nSz & 0x80 ) { iOff--; iOff += sqlite3Fts5GetVarint32(&(pIter->pLeaf ->p)[iOff],(u32*)&(nSz)); } }; | |||
| 11507 | pIter->bDel = (nSz & 0x0001); | |||
| 11508 | pIter->nPos = nSz>>1; | |||
| 11509 | assert_nc( pIter->nPos>=0 )((void) (0)); | |||
| 11510 | } | |||
| 11511 | pIter->iLeafOffset = iOff; | |||
| 11512 | } | |||
| 11513 | } | |||
| 11514 | ||||
| 11515 | static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 11516 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ | |||
| 11517 | i64 iOff = pIter->iLeafOffset; | |||
| 11518 | ||||
| 11519 | ASSERT_SZLEAF_OK(pIter->pLeaf)((void) (0)); | |||
| 11520 | while( iOff>=pIter->pLeaf->szLeaf ){ | |||
| 11521 | fts5SegIterNextPage(p, pIter); | |||
| 11522 | if( pIter->pLeaf==0 ){ | |||
| 11523 | if( p->rc==SQLITE_OK0 ) FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 11524 | return; | |||
| 11525 | } | |||
| 11526 | iOff = 4; | |||
| 11527 | a = pIter->pLeaf->p; | |||
| 11528 | } | |||
| 11529 | iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); | |||
| 11530 | pIter->iLeafOffset = iOff; | |||
| 11531 | } | |||
| 11532 | ||||
| 11533 | /* | |||
| 11534 | ** Fts5SegIter.iLeafOffset currently points to the first byte of the | |||
| 11535 | ** "nSuffix" field of a term. Function parameter nKeep contains the value | |||
| 11536 | ** of the "nPrefix" field (if there was one - it is passed 0 if this is | |||
| 11537 | ** the first term in the segment). | |||
| 11538 | ** | |||
| 11539 | ** This function populates: | |||
| 11540 | ** | |||
| 11541 | ** Fts5SegIter.term | |||
| 11542 | ** Fts5SegIter.rowid | |||
| 11543 | ** | |||
| 11544 | ** accordingly and leaves (Fts5SegIter.iLeafOffset) set to the content of | |||
| 11545 | ** the first position list. The position list belonging to document | |||
| 11546 | ** (Fts5SegIter.iRowid). | |||
| 11547 | */ | |||
| 11548 | static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ | |||
| 11549 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ | |||
| 11550 | i64 iOff = pIter->iLeafOffset; /* Offset to read at */ | |||
| 11551 | int nNew; /* Bytes of new data */ | |||
| 11552 | ||||
| 11553 | iOff += fts5GetVarint32(&a[iOff], nNew)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(nNew)); | |||
| 11554 | if( iOff+nNew>pIter->pLeaf->szLeaf || nKeep>pIter->term.n || nNew==0 ){ | |||
| 11555 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 11556 | return; | |||
| 11557 | } | |||
| 11558 | pIter->term.n = nKeep; | |||
| 11559 | fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff])sqlite3Fts5BufferAppendBlob(&p->rc,&pIter->term ,nNew,&a[iOff]); | |||
| 11560 | assert( pIter->term.n<=pIter->term.nSpace )((void) (0)); | |||
| 11561 | iOff += nNew; | |||
| 11562 | pIter->iTermLeafOffset = iOff; | |||
| 11563 | pIter->iTermLeafPgno = pIter->iLeafPgno; | |||
| 11564 | pIter->iLeafOffset = iOff; | |||
| 11565 | ||||
| 11566 | if( pIter->iPgidxOff>=pIter->pLeaf->nn ){ | |||
| 11567 | pIter->iEndofDoclist = pIter->pLeaf->nn+1; | |||
| 11568 | }else{ | |||
| 11569 | int nExtra; | |||
| 11570 | pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], nExtra)sqlite3Fts5GetVarint32(&a[pIter->iPgidxOff],(u32*)& (nExtra)); | |||
| 11571 | pIter->iEndofDoclist += nExtra; | |||
| 11572 | } | |||
| 11573 | ||||
| 11574 | fts5SegIterLoadRowid(p, pIter); | |||
| 11575 | } | |||
| 11576 | ||||
| 11577 | static void fts5SegIterNext(Fts5Index*, Fts5SegIter*, int*); | |||
| 11578 | static void fts5SegIterNext_Reverse(Fts5Index*, Fts5SegIter*, int*); | |||
| 11579 | static void fts5SegIterNext_None(Fts5Index*, Fts5SegIter*, int*); | |||
| 11580 | ||||
| 11581 | static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 11582 | if( pIter->flags & FTS5_SEGITER_REVERSE0x02 ){ | |||
| 11583 | pIter->xNext = fts5SegIterNext_Reverse; | |||
| 11584 | }else if( p->pConfig->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 11585 | pIter->xNext = fts5SegIterNext_None; | |||
| 11586 | }else{ | |||
| 11587 | pIter->xNext = fts5SegIterNext; | |||
| 11588 | } | |||
| 11589 | } | |||
| 11590 | ||||
| 11591 | /* | |||
| 11592 | ** Allocate a tombstone hash page array object (pIter->pTombArray) for | |||
| 11593 | ** the iterator passed as the second argument. If an OOM error occurs, | |||
| 11594 | ** leave an error in the Fts5Index object. | |||
| 11595 | */ | |||
| 11596 | static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 11597 | const i64 nTomb = (i64)pIter->pSeg->nPgTombstone; | |||
| 11598 | if( nTomb>0 ){ | |||
| 11599 | i64 nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1)(__builtin_offsetof(Fts5TombstoneArray, apTombstone)+(nTomb+1 )*sizeof(Fts5Data*)); | |||
| 11600 | Fts5TombstoneArray *pNew; | |||
| 11601 | pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); | |||
| 11602 | if( pNew ){ | |||
| 11603 | pNew->nTombstone = nTomb; | |||
| 11604 | pNew->nRef = 1; | |||
| 11605 | pIter->pTombArray = pNew; | |||
| 11606 | } | |||
| 11607 | } | |||
| 11608 | } | |||
| 11609 | ||||
| 11610 | /* | |||
| 11611 | ** Initialize the iterator object pIter to iterate through the entries in | |||
| 11612 | ** segment pSeg. The iterator is left pointing to the first entry when | |||
| 11613 | ** this function returns. | |||
| 11614 | ** | |||
| 11615 | ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If | |||
| 11616 | ** an error has already occurred when this function is called, it is a no-op. | |||
| 11617 | */ | |||
| 11618 | static void fts5SegIterInit( | |||
| 11619 | Fts5Index *p, /* FTS index object */ | |||
| 11620 | Fts5StructureSegment *pSeg, /* Description of segment */ | |||
| 11621 | Fts5SegIter *pIter /* Object to populate */ | |||
| 11622 | ){ | |||
| 11623 | if( pSeg->pgnoFirst==0 ){ | |||
| 11624 | /* This happens if the segment is being used as an input to an incremental | |||
| 11625 | ** merge and all data has already been "trimmed". See function | |||
| 11626 | ** fts5TrimSegments() for details. In this case leave the iterator empty. | |||
| 11627 | ** The caller will see the (pIter->pLeaf==0) and assume the iterator is | |||
| 11628 | ** at EOF already. */ | |||
| 11629 | assert( pIter->pLeaf==0 )((void) (0)); | |||
| 11630 | return; | |||
| 11631 | } | |||
| 11632 | ||||
| 11633 | if( p->rc==SQLITE_OK0 ){ | |||
| 11634 | memset(pIter, 0, sizeof(*pIter)); | |||
| 11635 | fts5SegIterSetNext(p, pIter); | |||
| 11636 | pIter->pSeg = pSeg; | |||
| 11637 | pIter->iLeafPgno = pSeg->pgnoFirst-1; | |||
| 11638 | do { | |||
| 11639 | fts5SegIterNextPage(p, pIter); | |||
| 11640 | }while( p->rc==SQLITE_OK0 && pIter->pLeaf && pIter->pLeaf->nn==4 ); | |||
| 11641 | } | |||
| 11642 | ||||
| 11643 | if( p->rc==SQLITE_OK0 && pIter->pLeaf ){ | |||
| 11644 | pIter->iLeafOffset = 4; | |||
| 11645 | assert( pIter->pLeaf!=0 )((void) (0)); | |||
| 11646 | assert_nc( pIter->pLeaf->nn>4 )((void) (0)); | |||
| 11647 | assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 )((void) (0)); | |||
| 11648 | pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; | |||
| 11649 | fts5SegIterLoadTerm(p, pIter, 0); | |||
| 11650 | fts5SegIterLoadNPos(p, pIter); | |||
| 11651 | fts5SegIterAllocTombstone(p, pIter); | |||
| 11652 | } | |||
| 11653 | } | |||
| 11654 | ||||
| 11655 | /* | |||
| 11656 | ** This function is only ever called on iterators created by calls to | |||
| 11657 | ** Fts5IndexQuery() with the FTS5INDEX_QUERY_DESC flag set. | |||
| 11658 | ** | |||
| 11659 | ** The iterator is in an unusual state when this function is called: the | |||
| 11660 | ** Fts5SegIter.iLeafOffset variable is set to the offset of the start of | |||
| 11661 | ** the position-list size field for the first relevant rowid on the page. | |||
| 11662 | ** Fts5SegIter.rowid is set, but nPos and bDel are not. | |||
| 11663 | ** | |||
| 11664 | ** This function advances the iterator so that it points to the last | |||
| 11665 | ** relevant rowid on the page and, if necessary, initializes the | |||
| 11666 | ** aRowidOffset[] and iRowidOffset variables. At this point the iterator | |||
| 11667 | ** is in its regular state - Fts5SegIter.iLeafOffset points to the first | |||
| 11668 | ** byte of the position list content associated with said rowid. | |||
| 11669 | */ | |||
| 11670 | static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 11671 | int eDetail = p->pConfig->eDetail; | |||
| 11672 | int n = pIter->pLeaf->szLeaf; | |||
| 11673 | int i = pIter->iLeafOffset; | |||
| 11674 | u8 *a = pIter->pLeaf->p; | |||
| 11675 | int iRowidOffset = 0; | |||
| 11676 | ||||
| 11677 | if( n>pIter->iEndofDoclist ){ | |||
| 11678 | n = pIter->iEndofDoclist; | |||
| 11679 | } | |||
| 11680 | ||||
| 11681 | ASSERT_SZLEAF_OK(pIter->pLeaf)((void) (0)); | |||
| 11682 | while( 1 ){ | |||
| 11683 | u64 iDelta = 0; | |||
| 11684 | ||||
| 11685 | if( i>=n ) break; | |||
| 11686 | if( eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 11687 | /* todo */ | |||
| 11688 | if( i<n && a[i]==0 ){ | |||
| 11689 | i++; | |||
| 11690 | if( i<n && a[i]==0 ) i++; | |||
| 11691 | } | |||
| 11692 | }else{ | |||
| 11693 | int nPos; | |||
| 11694 | int bDummy; | |||
| 11695 | i += fts5GetPoslistSize(&a[i], &nPos, &bDummy); | |||
| 11696 | i += nPos; | |||
| 11697 | } | |||
| 11698 | if( i>=n ) break; | |||
| 11699 | i += fts5GetVarintsqlite3Fts5GetVarint(&a[i], &iDelta); | |||
| 11700 | pIter->iRowid += iDelta; | |||
| 11701 | ||||
| 11702 | /* If necessary, grow the pIter->aRowidOffset[] array. */ | |||
| 11703 | if( iRowidOffset>=pIter->nRowidOffset ){ | |||
| 11704 | i64 nNew = pIter->nRowidOffset + 8; | |||
| 11705 | int *aNew = (int*)sqlite3_realloc64sqlite3_api->realloc64(pIter->aRowidOffset,nNew*sizeof(int)); | |||
| 11706 | if( aNew==0 ){ | |||
| 11707 | p->rc = SQLITE_NOMEM7; | |||
| 11708 | break; | |||
| 11709 | } | |||
| 11710 | pIter->aRowidOffset = aNew; | |||
| 11711 | pIter->nRowidOffset = nNew; | |||
| 11712 | } | |||
| 11713 | ||||
| 11714 | pIter->aRowidOffset[iRowidOffset++] = pIter->iLeafOffset; | |||
| 11715 | pIter->iLeafOffset = i; | |||
| 11716 | } | |||
| 11717 | pIter->iRowidOffset = iRowidOffset; | |||
| 11718 | fts5SegIterLoadNPos(p, pIter); | |||
| 11719 | } | |||
| 11720 | ||||
| 11721 | /* | |||
| 11722 | ** | |||
| 11723 | */ | |||
| 11724 | static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 11725 | assert( pIter->flags & FTS5_SEGITER_REVERSE )((void) (0)); | |||
| 11726 | assert( pIter->flags & FTS5_SEGITER_ONETERM )((void) (0)); | |||
| 11727 | ||||
| 11728 | fts5DataRelease(pIter->pLeaf); | |||
| 11729 | pIter->pLeaf = 0; | |||
| 11730 | while( p->rc==SQLITE_OK0 && pIter->iLeafPgno>pIter->iTermLeafPgno ){ | |||
| 11731 | Fts5Data *pNew; | |||
| 11732 | pIter->iLeafPgno--; | |||
| 11733 | pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID(( ((i64)(pIter->pSeg->iSegid) << (31 +5 +1)) + (( i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64 )(pIter->iLeafPgno)) ) | |||
| 11734 | pIter->pSeg->iSegid, pIter->iLeafPgno( ((i64)(pIter->pSeg->iSegid) << (31 +5 +1)) + (( i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64 )(pIter->iLeafPgno)) ) | |||
| 11735 | )( ((i64)(pIter->pSeg->iSegid) << (31 +5 +1)) + (( i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64 )(pIter->iLeafPgno)) )); | |||
| 11736 | if( pNew ){ | |||
| 11737 | /* iTermLeafOffset may be equal to szLeaf if the term is the last | |||
| 11738 | ** thing on the page - i.e. the first rowid is on the following page. | |||
| 11739 | ** In this case leave pIter->pLeaf==0, this iterator is at EOF. */ | |||
| 11740 | if( pIter->iLeafPgno==pIter->iTermLeafPgno ){ | |||
| 11741 | assert( pIter->pLeaf==0 )((void) (0)); | |||
| 11742 | if( pIter->iTermLeafOffset<pNew->szLeaf ){ | |||
| 11743 | pIter->pLeaf = pNew; | |||
| 11744 | pIter->iLeafOffset = pIter->iTermLeafOffset; | |||
| 11745 | } | |||
| 11746 | }else{ | |||
| 11747 | int iRowidOff; | |||
| 11748 | iRowidOff = fts5LeafFirstRowidOff(pNew)(fts5GetU16((pNew)->p)); | |||
| 11749 | if( iRowidOff ){ | |||
| 11750 | if( iRowidOff>=pNew->szLeaf ){ | |||
| 11751 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 11752 | }else{ | |||
| 11753 | pIter->pLeaf = pNew; | |||
| 11754 | pIter->iLeafOffset = iRowidOff; | |||
| 11755 | } | |||
| 11756 | } | |||
| 11757 | } | |||
| 11758 | ||||
| 11759 | if( pIter->pLeaf ){ | |||
| 11760 | u8 *a = &pIter->pLeaf->p[pIter->iLeafOffset]; | |||
| 11761 | pIter->iLeafOffset += fts5GetVarintsqlite3Fts5GetVarint(a, (u64*)&pIter->iRowid); | |||
| 11762 | break; | |||
| 11763 | }else{ | |||
| 11764 | fts5DataRelease(pNew); | |||
| 11765 | } | |||
| 11766 | } | |||
| 11767 | } | |||
| 11768 | ||||
| 11769 | if( pIter->pLeaf ){ | |||
| 11770 | pIter->iEndofDoclist = pIter->pLeaf->nn+1; | |||
| 11771 | fts5SegIterReverseInitPage(p, pIter); | |||
| 11772 | } | |||
| 11773 | } | |||
| 11774 | ||||
| 11775 | /* | |||
| 11776 | ** Return true if the iterator passed as the second argument currently | |||
| 11777 | ** points to a delete marker. A delete marker is an entry with a 0 byte | |||
| 11778 | ** position-list. | |||
| 11779 | */ | |||
| 11780 | static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5Iter *pIter){ | |||
| 11781 | Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; | |||
| 11782 | return (p->rc==SQLITE_OK0 && pSeg->pLeaf && pSeg->nPos==0); | |||
| 11783 | } | |||
| 11784 | ||||
| 11785 | /* | |||
| 11786 | ** Advance iterator pIter to the next entry. | |||
| 11787 | ** | |||
| 11788 | ** This version of fts5SegIterNext() is only used by reverse iterators. | |||
| 11789 | */ | |||
| 11790 | static void fts5SegIterNext_Reverse( | |||
| 11791 | Fts5Index *p, /* FTS5 backend object */ | |||
| 11792 | Fts5SegIter *pIter, /* Iterator to advance */ | |||
| 11793 | int *pbUnused /* Unused */ | |||
| 11794 | ){ | |||
| 11795 | assert( pIter->flags & FTS5_SEGITER_REVERSE )((void) (0)); | |||
| 11796 | assert( pIter->pNextLeaf==0 )((void) (0)); | |||
| 11797 | UNUSED_PARAM(pbUnused)(void)(pbUnused); | |||
| 11798 | ||||
| 11799 | if( pIter->iRowidOffset>0 ){ | |||
| 11800 | u8 *a = pIter->pLeaf->p; | |||
| 11801 | int iOff; | |||
| 11802 | u64 iDelta; | |||
| 11803 | ||||
| 11804 | pIter->iRowidOffset--; | |||
| 11805 | pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset]; | |||
| 11806 | fts5SegIterLoadNPos(p, pIter); | |||
| 11807 | iOff = pIter->iLeafOffset; | |||
| 11808 | if( p->pConfig->eDetail!=FTS5_DETAIL_NONE1 ){ | |||
| 11809 | iOff += pIter->nPos; | |||
| 11810 | } | |||
| 11811 | fts5GetVarintsqlite3Fts5GetVarint(&a[iOff], &iDelta); | |||
| 11812 | pIter->iRowid -= iDelta; | |||
| 11813 | }else{ | |||
| 11814 | fts5SegIterReverseNewPage(p, pIter); | |||
| 11815 | } | |||
| 11816 | } | |||
| 11817 | ||||
| 11818 | /* | |||
| 11819 | ** Advance iterator pIter to the next entry. | |||
| 11820 | ** | |||
| 11821 | ** This version of fts5SegIterNext() is only used if detail=none and the | |||
| 11822 | ** iterator is not a reverse direction iterator. | |||
| 11823 | */ | |||
| 11824 | static void fts5SegIterNext_None( | |||
| 11825 | Fts5Index *p, /* FTS5 backend object */ | |||
| 11826 | Fts5SegIter *pIter, /* Iterator to advance */ | |||
| 11827 | int *pbNewTerm /* OUT: Set for new term */ | |||
| 11828 | ){ | |||
| 11829 | int iOff; | |||
| 11830 | ||||
| 11831 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 11832 | assert( (pIter->flags & FTS5_SEGITER_REVERSE)==0 )((void) (0)); | |||
| 11833 | assert( p->pConfig->eDetail==FTS5_DETAIL_NONE )((void) (0)); | |||
| 11834 | ||||
| 11835 | ASSERT_SZLEAF_OK(pIter->pLeaf)((void) (0)); | |||
| 11836 | iOff = pIter->iLeafOffset; | |||
| 11837 | ||||
| 11838 | /* Next entry is on the next page */ | |||
| 11839 | while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ | |||
| 11840 | fts5SegIterNextPage(p, pIter); | |||
| 11841 | if( p->rc || pIter->pLeaf==0 ) return; | |||
| 11842 | pIter->iRowid = 0; | |||
| 11843 | iOff = 4; | |||
| 11844 | } | |||
| 11845 | ||||
| 11846 | if( iOff<pIter->iEndofDoclist ){ | |||
| 11847 | /* Next entry is on the current page */ | |||
| 11848 | u64 iDelta; | |||
| 11849 | iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta); | |||
| 11850 | pIter->iLeafOffset = iOff; | |||
| 11851 | pIter->iRowid += iDelta; | |||
| 11852 | }else if( (pIter->flags & FTS5_SEGITER_ONETERM0x01)==0 ){ | |||
| 11853 | if( pIter->pSeg ){ | |||
| 11854 | int nKeep = 0; | |||
| 11855 | if( iOff!=fts5LeafFirstTermOff(pIter->pLeaf) ){ | |||
| 11856 | iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep)sqlite3Fts5GetVarint32(&pIter->pLeaf->p[iOff],(u32* )&(nKeep)); | |||
| 11857 | } | |||
| 11858 | pIter->iLeafOffset = iOff; | |||
| 11859 | fts5SegIterLoadTerm(p, pIter, nKeep); | |||
| 11860 | }else{ | |||
| 11861 | const u8 *pList = 0; | |||
| 11862 | const char *zTerm = 0; | |||
| 11863 | int nTerm = 0; | |||
| 11864 | int nList; | |||
| 11865 | sqlite3Fts5HashScanNext(p->pHash); | |||
| 11866 | sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); | |||
| 11867 | if( pList==0 ) goto next_none_eof; | |||
| 11868 | pIter->pLeaf->p = (u8*)pList; | |||
| 11869 | pIter->pLeaf->nn = nList; | |||
| 11870 | pIter->pLeaf->szLeaf = nList; | |||
| 11871 | pIter->iEndofDoclist = nList; | |||
| 11872 | sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm); | |||
| 11873 | pIter->iLeafOffset = fts5GetVarintsqlite3Fts5GetVarint(pList, (u64*)&pIter->iRowid); | |||
| 11874 | } | |||
| 11875 | ||||
| 11876 | if( pbNewTerm ) *pbNewTerm = 1; | |||
| 11877 | }else{ | |||
| 11878 | goto next_none_eof; | |||
| 11879 | } | |||
| 11880 | ||||
| 11881 | fts5SegIterLoadNPos(p, pIter); | |||
| 11882 | ||||
| 11883 | return; | |||
| 11884 | next_none_eof: | |||
| 11885 | fts5DataRelease(pIter->pLeaf); | |||
| 11886 | pIter->pLeaf = 0; | |||
| 11887 | } | |||
| 11888 | ||||
| 11889 | ||||
| 11890 | /* | |||
| 11891 | ** Advance iterator pIter to the next entry. | |||
| 11892 | ** | |||
| 11893 | ** If an error occurs, Fts5Index.rc is set to an appropriate error code. It | |||
| 11894 | ** is not considered an error if the iterator reaches EOF. If an error has | |||
| 11895 | ** already occurred when this function is called, it is a no-op. | |||
| 11896 | */ | |||
| 11897 | static void fts5SegIterNext( | |||
| 11898 | Fts5Index *p, /* FTS5 backend object */ | |||
| 11899 | Fts5SegIter *pIter, /* Iterator to advance */ | |||
| 11900 | int *pbNewTerm /* OUT: Set for new term */ | |||
| 11901 | ){ | |||
| 11902 | Fts5Data *pLeaf = pIter->pLeaf; | |||
| 11903 | int iOff; | |||
| 11904 | int bNewTerm = 0; | |||
| 11905 | int nKeep = 0; | |||
| 11906 | u8 *a; | |||
| 11907 | int n; | |||
| 11908 | ||||
| 11909 | assert( pbNewTerm==0 || *pbNewTerm==0 )((void) (0)); | |||
| 11910 | assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE )((void) (0)); | |||
| 11911 | ||||
| 11912 | /* Search for the end of the position list within the current page. */ | |||
| 11913 | a = pLeaf->p; | |||
| 11914 | n = pLeaf->szLeaf; | |||
| 11915 | ||||
| 11916 | ASSERT_SZLEAF_OK(pLeaf)((void) (0)); | |||
| 11917 | iOff = pIter->iLeafOffset + pIter->nPos; | |||
| 11918 | ||||
| 11919 | if( iOff<n ){ | |||
| 11920 | /* The next entry is on the current page. */ | |||
| 11921 | assert_nc( iOff<=pIter->iEndofDoclist )((void) (0)); | |||
| 11922 | if( iOff>=pIter->iEndofDoclist ){ | |||
| 11923 | bNewTerm = 1; | |||
| 11924 | if( iOff!=fts5LeafFirstTermOff(pLeaf) ){ | |||
| 11925 | iOff += fts5GetVarint32(&a[iOff], nKeep)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(nKeep)); | |||
| 11926 | } | |||
| 11927 | }else{ | |||
| 11928 | u64 iDelta; | |||
| 11929 | iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta); | |||
| 11930 | pIter->iRowid += iDelta; | |||
| 11931 | assert_nc( iDelta>0 )((void) (0)); | |||
| 11932 | } | |||
| 11933 | pIter->iLeafOffset = iOff; | |||
| 11934 | ||||
| 11935 | }else if( pIter->pSeg==0 ){ | |||
| 11936 | const u8 *pList = 0; | |||
| 11937 | const char *zTerm = 0; | |||
| 11938 | int nTerm = 0; | |||
| 11939 | int nList = 0; | |||
| 11940 | assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm )((void) (0)); | |||
| 11941 | if( 0==(pIter->flags & FTS5_SEGITER_ONETERM0x01) ){ | |||
| 11942 | sqlite3Fts5HashScanNext(p->pHash); | |||
| 11943 | sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); | |||
| 11944 | } | |||
| 11945 | if( pList==0 ){ | |||
| 11946 | fts5DataRelease(pIter->pLeaf); | |||
| 11947 | pIter->pLeaf = 0; | |||
| 11948 | }else{ | |||
| 11949 | pIter->pLeaf->p = (u8*)pList; | |||
| 11950 | pIter->pLeaf->nn = nList; | |||
| 11951 | pIter->pLeaf->szLeaf = nList; | |||
| 11952 | pIter->iEndofDoclist = nList+1; | |||
| 11953 | sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm); | |||
| 11954 | pIter->iLeafOffset = fts5GetVarintsqlite3Fts5GetVarint(pList, (u64*)&pIter->iRowid); | |||
| 11955 | *pbNewTerm = 1; | |||
| 11956 | } | |||
| 11957 | }else{ | |||
| 11958 | iOff = 0; | |||
| 11959 | /* Next entry is not on the current page */ | |||
| 11960 | while( iOff==0 ){ | |||
| 11961 | fts5SegIterNextPage(p, pIter); | |||
| 11962 | pLeaf = pIter->pLeaf; | |||
| 11963 | if( pLeaf==0 ) break; | |||
| 11964 | ASSERT_SZLEAF_OK(pLeaf)((void) (0)); | |||
| 11965 | if( (iOff = fts5LeafFirstRowidOff(pLeaf)(fts5GetU16((pLeaf)->p))) && iOff<pLeaf->szLeaf ){ | |||
| 11966 | iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid); | |||
| 11967 | pIter->iLeafOffset = iOff; | |||
| 11968 | ||||
| 11969 | if( pLeaf->nn>pLeaf->szLeaf ){ | |||
| 11970 | pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(sqlite3Fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf],(u32 *)&(pIter->iEndofDoclist)) | |||
| 11971 | &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclistsqlite3Fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf],(u32 *)&(pIter->iEndofDoclist)) | |||
| 11972 | )sqlite3Fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf],(u32 *)&(pIter->iEndofDoclist)); | |||
| 11973 | } | |||
| 11974 | } | |||
| 11975 | else if( pLeaf->nn>pLeaf->szLeaf ){ | |||
| 11976 | pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(sqlite3Fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf],(u32 *)&(iOff)) | |||
| 11977 | &pLeaf->p[pLeaf->szLeaf], iOffsqlite3Fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf],(u32 *)&(iOff)) | |||
| 11978 | )sqlite3Fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf],(u32 *)&(iOff)); | |||
| 11979 | pIter->iLeafOffset = iOff; | |||
| 11980 | pIter->iEndofDoclist = iOff; | |||
| 11981 | bNewTerm = 1; | |||
| 11982 | } | |||
| 11983 | assert_nc( iOff<pLeaf->szLeaf )((void) (0)); | |||
| 11984 | if( iOff>pLeaf->szLeaf ){ | |||
| 11985 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 11986 | return; | |||
| 11987 | } | |||
| 11988 | } | |||
| 11989 | } | |||
| 11990 | ||||
| 11991 | /* Check if the iterator is now at EOF. If so, return early. */ | |||
| 11992 | if( pIter->pLeaf ){ | |||
| 11993 | if( bNewTerm ){ | |||
| 11994 | if( pIter->flags & FTS5_SEGITER_ONETERM0x01 ){ | |||
| 11995 | fts5DataRelease(pIter->pLeaf); | |||
| 11996 | pIter->pLeaf = 0; | |||
| 11997 | }else{ | |||
| 11998 | fts5SegIterLoadTerm(p, pIter, nKeep); | |||
| 11999 | fts5SegIterLoadNPos(p, pIter); | |||
| 12000 | if( pbNewTerm ) *pbNewTerm = 1; | |||
| 12001 | } | |||
| 12002 | }else{ | |||
| 12003 | /* The following could be done by calling fts5SegIterLoadNPos(). But | |||
| 12004 | ** this block is particularly performance critical, so equivalent | |||
| 12005 | ** code is inlined. */ | |||
| 12006 | int nSz; | |||
| 12007 | assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn )((void) (0)); | |||
| 12008 | fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz){ nSz = (pIter->pLeaf->p)[pIter->iLeafOffset++]; if( nSz & 0x80 ){ pIter->iLeafOffset--; pIter->iLeafOffset += sqlite3Fts5GetVarint32(&(pIter->pLeaf->p)[pIter ->iLeafOffset],(u32*)&(nSz)); } }; | |||
| 12009 | pIter->bDel = (nSz & 0x0001); | |||
| 12010 | pIter->nPos = nSz>>1; | |||
| 12011 | assert_nc( pIter->nPos>=0 )((void) (0)); | |||
| 12012 | } | |||
| 12013 | } | |||
| 12014 | } | |||
| 12015 | ||||
| 12016 | #define SWAPVAL(T, a, b){ T tmp; tmp=a; a=b; b=tmp; } { T tmp; tmp=a; a=b; b=tmp; } | |||
| 12017 | ||||
| 12018 | #define fts5IndexSkipVarint(a, iOff){ int iEnd = iOff+9; while( (a[iOff++] & 0x80) && iOff<iEnd ); } { \ | |||
| 12019 | int iEnd = iOff+9; \ | |||
| 12020 | while( (a[iOff++] & 0x80) && iOff<iEnd ); \ | |||
| 12021 | } | |||
| 12022 | ||||
| 12023 | /* | |||
| 12024 | ** Iterator pIter currently points to the first rowid in a doclist. This | |||
| 12025 | ** function sets the iterator up so that iterates in reverse order through | |||
| 12026 | ** the doclist. | |||
| 12027 | */ | |||
| 12028 | static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 12029 | Fts5DlidxIter *pDlidx = pIter->pDlidx; | |||
| 12030 | Fts5Data *pLast = 0; | |||
| 12031 | int pgnoLast = 0; | |||
| 12032 | ||||
| 12033 | if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION4 ){ | |||
| 12034 | int iSegid = pIter->pSeg->iSegid; | |||
| 12035 | pgnoLast = fts5DlidxIterPgno(pDlidx); | |||
| 12036 | pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pgnoLast)) )); | |||
| 12037 | }else{ | |||
| 12038 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ | |||
| 12039 | ||||
| 12040 | /* Currently, Fts5SegIter.iLeafOffset points to the first byte of | |||
| 12041 | ** position-list content for the current rowid. Back it up so that it | |||
| 12042 | ** points to the start of the position-list size field. */ | |||
| 12043 | int iPoslist; | |||
| 12044 | if( pIter->iTermLeafPgno==pIter->iLeafPgno ){ | |||
| 12045 | iPoslist = pIter->iTermLeafOffset; | |||
| 12046 | }else{ | |||
| 12047 | iPoslist = 4; | |||
| 12048 | } | |||
| 12049 | fts5IndexSkipVarint(pLeaf->p, iPoslist){ int iEnd = iPoslist+9; while( (pLeaf->p[iPoslist++] & 0x80) && iPoslist<iEnd ); }; | |||
| 12050 | pIter->iLeafOffset = iPoslist; | |||
| 12051 | ||||
| 12052 | /* If this condition is true then the largest rowid for the current | |||
| 12053 | ** term may not be stored on the current page. So search forward to | |||
| 12054 | ** see where said rowid really is. */ | |||
| 12055 | if( pIter->iEndofDoclist>=pLeaf->szLeaf ){ | |||
| 12056 | int pgno; | |||
| 12057 | Fts5StructureSegment *pSeg = pIter->pSeg; | |||
| 12058 | ||||
| 12059 | /* The last rowid in the doclist may not be on the current page. Search | |||
| 12060 | ** forward to find the page containing the last rowid. */ | |||
| 12061 | for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){ | |||
| 12062 | i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno)( ((i64)(pSeg->iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pgno)) ); | |||
| 12063 | Fts5Data *pNew = fts5LeafRead(p, iAbs); | |||
| 12064 | if( pNew ){ | |||
| 12065 | int iRowid, bTermless; | |||
| 12066 | iRowid = fts5LeafFirstRowidOff(pNew)(fts5GetU16((pNew)->p)); | |||
| 12067 | bTermless = fts5LeafIsTermless(pNew)((pNew)->szLeaf >= (pNew)->nn); | |||
| 12068 | if( iRowid ){ | |||
| 12069 | SWAPVAL(Fts5Data*, pNew, pLast){ Fts5Data* tmp; tmp=pNew; pNew=pLast; pLast=tmp; }; | |||
| 12070 | pgnoLast = pgno; | |||
| 12071 | } | |||
| 12072 | fts5DataRelease(pNew); | |||
| 12073 | if( bTermless==0 ) break; | |||
| 12074 | } | |||
| 12075 | } | |||
| 12076 | } | |||
| 12077 | } | |||
| 12078 | ||||
| 12079 | /* If pLast is NULL at this point, then the last rowid for this doclist | |||
| 12080 | ** lies on the page currently indicated by the iterator. In this case | |||
| 12081 | ** pIter->iLeafOffset is already set to point to the position-list size | |||
| 12082 | ** field associated with the first relevant rowid on the page. | |||
| 12083 | ** | |||
| 12084 | ** Or, if pLast is non-NULL, then it is the page that contains the last | |||
| 12085 | ** rowid. In this case configure the iterator so that it points to the | |||
| 12086 | ** first rowid on this page. | |||
| 12087 | */ | |||
| 12088 | if( pLast ){ | |||
| 12089 | int iOff; | |||
| 12090 | fts5DataRelease(pIter->pLeaf); | |||
| 12091 | pIter->pLeaf = pLast; | |||
| 12092 | pIter->iLeafPgno = pgnoLast; | |||
| 12093 | if( p->rc==SQLITE_OK0 ){ | |||
| 12094 | iOff = fts5LeafFirstRowidOff(pLast)(fts5GetU16((pLast)->p)); | |||
| 12095 | if( iOff>pLast->szLeaf ){ | |||
| 12096 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 12097 | return; | |||
| 12098 | } | |||
| 12099 | iOff += fts5GetVarintsqlite3Fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); | |||
| 12100 | pIter->iLeafOffset = iOff; | |||
| 12101 | ||||
| 12102 | if( fts5LeafIsTermless(pLast)((pLast)->szLeaf >= (pLast)->nn) ){ | |||
| 12103 | pIter->iEndofDoclist = pLast->nn+1; | |||
| 12104 | }else{ | |||
| 12105 | pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast); | |||
| 12106 | } | |||
| 12107 | } | |||
| 12108 | } | |||
| 12109 | ||||
| 12110 | fts5SegIterReverseInitPage(p, pIter); | |||
| 12111 | } | |||
| 12112 | ||||
| 12113 | /* | |||
| 12114 | ** Iterator pIter currently points to the first rowid of a doclist. | |||
| 12115 | ** There is a doclist-index associated with the final term on the current | |||
| 12116 | ** page. If the current term is the last term on the page, load the | |||
| 12117 | ** doclist-index from disk and initialize an iterator at (pIter->pDlidx). | |||
| 12118 | */ | |||
| 12119 | static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){ | |||
| 12120 | int iSeg = pIter->pSeg->iSegid; | |||
| 12121 | int bRev = (pIter->flags & FTS5_SEGITER_REVERSE0x02); | |||
| 12122 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ | |||
| 12123 | ||||
| 12124 | assert( pIter->flags & FTS5_SEGITER_ONETERM )((void) (0)); | |||
| 12125 | assert( pIter->pDlidx==0 )((void) (0)); | |||
| 12126 | ||||
| 12127 | /* Check if the current doclist ends on this page. If it does, return | |||
| 12128 | ** early without loading the doclist-index (as it belongs to a different | |||
| 12129 | ** term. */ | |||
| 12130 | if( pIter->iTermLeafPgno==pIter->iLeafPgno | |||
| 12131 | && pIter->iEndofDoclist<pLeaf->szLeaf | |||
| 12132 | ){ | |||
| 12133 | return; | |||
| 12134 | } | |||
| 12135 | ||||
| 12136 | pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno); | |||
| 12137 | } | |||
| 12138 | ||||
| 12139 | /* | |||
| 12140 | ** The iterator object passed as the second argument currently contains | |||
| 12141 | ** no valid values except for the Fts5SegIter.pLeaf member variable. This | |||
| 12142 | ** function searches the leaf page for a term matching (pTerm/nTerm). | |||
| 12143 | ** | |||
| 12144 | ** If the specified term is found on the page, then the iterator is left | |||
| 12145 | ** pointing to it. If argument bGe is zero and the term is not found, | |||
| 12146 | ** the iterator is left pointing at EOF. | |||
| 12147 | ** | |||
| 12148 | ** If bGe is non-zero and the specified term is not found, then the | |||
| 12149 | ** iterator is left pointing to the smallest term in the segment that | |||
| 12150 | ** is larger than the specified term, even if this term is not on the | |||
| 12151 | ** current page. | |||
| 12152 | */ | |||
| 12153 | static void fts5LeafSeek( | |||
| 12154 | Fts5Index *p, /* Leave any error code here */ | |||
| 12155 | int bGe, /* True for a >= search */ | |||
| 12156 | Fts5SegIter *pIter, /* Iterator to seek */ | |||
| 12157 | const u8 *pTerm, int nTerm /* Term to search for */ | |||
| 12158 | ){ | |||
| 12159 | u32 iOff; | |||
| 12160 | const u8 *a = pIter->pLeaf->p; | |||
| 12161 | u32 n = (u32)pIter->pLeaf->nn; | |||
| 12162 | ||||
| 12163 | u32 nMatch = 0; | |||
| 12164 | u32 nKeep = 0; | |||
| 12165 | u32 nNew = 0; | |||
| 12166 | u32 iTermOff; | |||
| 12167 | u32 iPgidx; /* Current offset in pgidx */ | |||
| 12168 | int bEndOfPage = 0; | |||
| 12169 | ||||
| 12170 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 12171 | ||||
| 12172 | iPgidx = (u32)pIter->pLeaf->szLeaf; | |||
| 12173 | iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff)sqlite3Fts5GetVarint32(&a[iPgidx],(u32*)&(iTermOff)); | |||
| 12174 | iOff = iTermOff; | |||
| 12175 | if( iOff>n ){ | |||
| 12176 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 12177 | return; | |||
| 12178 | } | |||
| 12179 | ||||
| 12180 | while( 1 ){ | |||
| 12181 | ||||
| 12182 | /* Figure out how many new bytes are in this term */ | |||
| 12183 | fts5FastGetVarint32(a, iOff, nNew){ nNew = (a)[iOff++]; if( nNew & 0x80 ){ iOff--; iOff += sqlite3Fts5GetVarint32 (&(a)[iOff],(u32*)&(nNew)); } }; | |||
| 12184 | if( nKeep<nMatch ){ | |||
| 12185 | goto search_failed; | |||
| 12186 | } | |||
| 12187 | if( (iOff+nNew)>n ){ | |||
| 12188 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 12189 | return; | |||
| 12190 | } | |||
| 12191 | ||||
| 12192 | assert( nKeep>=nMatch )((void) (0)); | |||
| 12193 | if( nKeep==nMatch ){ | |||
| 12194 | u32 nCmp; | |||
| 12195 | u32 i; | |||
| 12196 | nCmp = (u32)MIN(nNew, nTerm-nMatch)(((nNew) < (nTerm-nMatch)) ? (nNew) : (nTerm-nMatch)); | |||
| 12197 | for(i=0; i<nCmp; i++){ | |||
| 12198 | if( a[iOff+i]!=pTerm[nMatch+i] ) break; | |||
| 12199 | } | |||
| 12200 | nMatch += i; | |||
| 12201 | ||||
| 12202 | if( (u32)nTerm==nMatch ){ | |||
| 12203 | if( i==nNew ){ | |||
| 12204 | goto search_success; | |||
| 12205 | }else{ | |||
| 12206 | goto search_failed; | |||
| 12207 | } | |||
| 12208 | }else if( i<nNew && a[iOff+i]>pTerm[nMatch] ){ | |||
| 12209 | goto search_failed; | |||
| 12210 | } | |||
| 12211 | } | |||
| 12212 | ||||
| 12213 | if( iPgidx>=n ){ | |||
| 12214 | bEndOfPage = 1; | |||
| 12215 | break; | |||
| 12216 | } | |||
| 12217 | ||||
| 12218 | iPgidx += fts5GetVarint32(&a[iPgidx], nKeep)sqlite3Fts5GetVarint32(&a[iPgidx],(u32*)&(nKeep)); | |||
| 12219 | iTermOff += nKeep; | |||
| 12220 | iOff = iTermOff; | |||
| 12221 | ||||
| 12222 | if( iOff>=n ){ | |||
| 12223 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 12224 | return; | |||
| 12225 | } | |||
| 12226 | ||||
| 12227 | /* Read the nKeep field of the next term. */ | |||
| 12228 | fts5FastGetVarint32(a, iOff, nKeep){ nKeep = (a)[iOff++]; if( nKeep & 0x80 ){ iOff--; iOff += sqlite3Fts5GetVarint32(&(a)[iOff],(u32*)&(nKeep)); } }; | |||
| 12229 | } | |||
| 12230 | ||||
| 12231 | search_failed: | |||
| 12232 | if( bGe==0 ){ | |||
| 12233 | fts5DataRelease(pIter->pLeaf); | |||
| 12234 | pIter->pLeaf = 0; | |||
| 12235 | return; | |||
| 12236 | }else if( bEndOfPage ){ | |||
| 12237 | do { | |||
| 12238 | fts5SegIterNextPage(p, pIter); | |||
| 12239 | if( pIter->pLeaf==0 ) return; | |||
| 12240 | a = pIter->pLeaf->p; | |||
| 12241 | if( fts5LeafIsTermless(pIter->pLeaf)((pIter->pLeaf)->szLeaf >= (pIter->pLeaf)->nn)==0 ){ | |||
| 12242 | iPgidx = (u32)pIter->pLeaf->szLeaf; | |||
| 12243 | iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff)sqlite3Fts5GetVarint32(&pIter->pLeaf->p[iPgidx],(u32 *)&(iOff)); | |||
| 12244 | if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){ | |||
| 12245 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 12246 | return; | |||
| 12247 | }else{ | |||
| 12248 | nKeep = 0; | |||
| 12249 | iTermOff = iOff; | |||
| 12250 | n = (u32)pIter->pLeaf->nn; | |||
| 12251 | iOff += fts5GetVarint32(&a[iOff], nNew)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(nNew)); | |||
| 12252 | break; | |||
| 12253 | } | |||
| 12254 | } | |||
| 12255 | }while( 1 ); | |||
| 12256 | } | |||
| 12257 | ||||
| 12258 | search_success: | |||
| 12259 | if( (i64)iOff+nNew>n || nNew<1 ){ | |||
| 12260 | FTS5_CORRUPT_ITER(p, pIter)fts5IndexCorruptIter(p, pIter); | |||
| 12261 | return; | |||
| 12262 | } | |||
| 12263 | pIter->iLeafOffset = iOff + nNew; | |||
| 12264 | pIter->iTermLeafOffset = pIter->iLeafOffset; | |||
| 12265 | pIter->iTermLeafPgno = pIter->iLeafPgno; | |||
| 12266 | ||||
| 12267 | fts5BufferSet(&p->rc, &pIter->term, nKeep, pTerm)sqlite3Fts5BufferSet(&p->rc,&pIter->term,nKeep, pTerm); | |||
| 12268 | fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff])sqlite3Fts5BufferAppendBlob(&p->rc,&pIter->term ,nNew,&a[iOff]); | |||
| 12269 | ||||
| 12270 | if( iPgidx>=n ){ | |||
| 12271 | pIter->iEndofDoclist = pIter->pLeaf->nn+1; | |||
| 12272 | }else{ | |||
| 12273 | int nExtra; | |||
| 12274 | iPgidx += fts5GetVarint32(&a[iPgidx], nExtra)sqlite3Fts5GetVarint32(&a[iPgidx],(u32*)&(nExtra)); | |||
| 12275 | pIter->iEndofDoclist = iTermOff + nExtra; | |||
| 12276 | } | |||
| 12277 | pIter->iPgidxOff = iPgidx; | |||
| 12278 | ||||
| 12279 | fts5SegIterLoadRowid(p, pIter); | |||
| 12280 | fts5SegIterLoadNPos(p, pIter); | |||
| 12281 | } | |||
| 12282 | ||||
| 12283 | static sqlite3_stmt *fts5IdxSelectStmt(Fts5Index *p){ | |||
| 12284 | if( p->pIdxSelect==0 ){ | |||
| 12285 | Fts5Config *pConfig = p->pConfig; | |||
| 12286 | fts5IndexPrepareStmt(p, &p->pIdxSelect, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 12287 | "SELECT pgno FROM '%q'.'%q_idx' WHERE " | |||
| 12288 | "segid=? AND term<=? ORDER BY term DESC LIMIT 1", | |||
| 12289 | pConfig->zDb, pConfig->zName | |||
| 12290 | )); | |||
| 12291 | } | |||
| 12292 | return p->pIdxSelect; | |||
| 12293 | } | |||
| 12294 | ||||
| 12295 | /* | |||
| 12296 | ** Initialize the object pIter to point to term pTerm/nTerm within segment | |||
| 12297 | ** pSeg. If there is no such term in the index, the iterator is set to EOF. | |||
| 12298 | ** | |||
| 12299 | ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If | |||
| 12300 | ** an error has already occurred when this function is called, it is a no-op. | |||
| 12301 | */ | |||
| 12302 | static void fts5SegIterSeekInit( | |||
| 12303 | Fts5Index *p, /* FTS5 backend */ | |||
| 12304 | const u8 *pTerm, int nTerm, /* Term to seek to */ | |||
| 12305 | int flags, /* Mask of FTS5INDEX_XXX flags */ | |||
| 12306 | Fts5StructureSegment *pSeg, /* Description of segment */ | |||
| 12307 | Fts5SegIter *pIter /* Object to populate */ | |||
| 12308 | ){ | |||
| 12309 | int iPg = 1; | |||
| 12310 | int bGe = (flags & FTS5INDEX_QUERY_SCAN0x0008); | |||
| 12311 | int bDlidx = 0; /* True if there is a doclist-index */ | |||
| 12312 | sqlite3_stmt *pIdxSelect = 0; | |||
| 12313 | ||||
| 12314 | assert( bGe==0 || (flags & FTS5INDEX_QUERY_DESC)==0 )((void) (0)); | |||
| 12315 | assert( pTerm && nTerm )((void) (0)); | |||
| 12316 | memset(pIter, 0, sizeof(*pIter)); | |||
| 12317 | pIter->pSeg = pSeg; | |||
| 12318 | ||||
| 12319 | /* This block sets stack variable iPg to the leaf page number that may | |||
| 12320 | ** contain term (pTerm/nTerm), if it is present in the segment. */ | |||
| 12321 | pIdxSelect = fts5IdxSelectStmt(p); | |||
| 12322 | if( p->rc ) return; | |||
| 12323 | sqlite3_bind_intsqlite3_api->bind_int(pIdxSelect, 1, pSeg->iSegid); | |||
| 12324 | sqlite3_bind_blobsqlite3_api->bind_blob(pIdxSelect, 2, pTerm, nTerm, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 12325 | if( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pIdxSelect) ){ | |||
| 12326 | i64 val = sqlite3_column_intsqlite3_api->column_int(pIdxSelect, 0); | |||
| 12327 | iPg = (int)(val>>1); | |||
| 12328 | bDlidx = (val & 0x0001); | |||
| 12329 | } | |||
| 12330 | p->rc = sqlite3_resetsqlite3_api->reset(pIdxSelect); | |||
| 12331 | sqlite3_bind_nullsqlite3_api->bind_null(pIdxSelect, 2); | |||
| 12332 | ||||
| 12333 | if( iPg<pSeg->pgnoFirst ){ | |||
| 12334 | iPg = pSeg->pgnoFirst; | |||
| 12335 | bDlidx = 0; | |||
| 12336 | } | |||
| 12337 | ||||
| 12338 | pIter->iLeafPgno = iPg - 1; | |||
| 12339 | fts5SegIterNextPage(p, pIter); | |||
| 12340 | ||||
| 12341 | if( pIter->pLeaf ){ | |||
| 12342 | fts5LeafSeek(p, bGe, pIter, pTerm, nTerm); | |||
| 12343 | } | |||
| 12344 | ||||
| 12345 | if( p->rc==SQLITE_OK0 && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM0x0100)) ){ | |||
| 12346 | pIter->flags |= FTS5_SEGITER_ONETERM0x01; | |||
| 12347 | if( pIter->pLeaf ){ | |||
| 12348 | if( flags & FTS5INDEX_QUERY_DESC0x0002 ){ | |||
| 12349 | pIter->flags |= FTS5_SEGITER_REVERSE0x02; | |||
| 12350 | } | |||
| 12351 | if( bDlidx ){ | |||
| 12352 | fts5SegIterLoadDlidx(p, pIter); | |||
| 12353 | } | |||
| 12354 | if( flags & FTS5INDEX_QUERY_DESC0x0002 ){ | |||
| 12355 | fts5SegIterReverse(p, pIter); | |||
| 12356 | } | |||
| 12357 | } | |||
| 12358 | } | |||
| 12359 | ||||
| 12360 | fts5SegIterSetNext(p, pIter); | |||
| 12361 | if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM0x0100) ){ | |||
| 12362 | fts5SegIterAllocTombstone(p, pIter); | |||
| 12363 | } | |||
| 12364 | ||||
| 12365 | /* Either: | |||
| 12366 | ** | |||
| 12367 | ** 1) an error has occurred, or | |||
| 12368 | ** 2) the iterator points to EOF, or | |||
| 12369 | ** 3) the iterator points to an entry with term (pTerm/nTerm), or | |||
| 12370 | ** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points | |||
| 12371 | ** to an entry with a term greater than or equal to (pTerm/nTerm). | |||
| 12372 | */ | |||
| 12373 | assert_nc( p->rc!=SQLITE_OK /* 1 */((void) (0)) | |||
| 12374 | || pIter->pLeaf==0 /* 2 */((void) (0)) | |||
| 12375 | || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */((void) (0)) | |||
| 12376 | || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */((void) (0)) | |||
| 12377 | )((void) (0)); | |||
| 12378 | } | |||
| 12379 | ||||
| 12380 | ||||
| 12381 | /* | |||
| 12382 | ** SQL used by fts5SegIterNextInit() to find the page to open. | |||
| 12383 | */ | |||
| 12384 | static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){ | |||
| 12385 | if( p->pIdxNextSelect==0 ){ | |||
| 12386 | Fts5Config *pConfig = p->pConfig; | |||
| 12387 | fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 12388 | "SELECT pgno FROM '%q'.'%q_idx' WHERE " | |||
| 12389 | "segid=? AND term>? ORDER BY term ASC LIMIT 1", | |||
| 12390 | pConfig->zDb, pConfig->zName | |||
| 12391 | )); | |||
| 12392 | ||||
| 12393 | } | |||
| 12394 | return p->pIdxNextSelect; | |||
| 12395 | } | |||
| 12396 | ||||
| 12397 | /* | |||
| 12398 | ** This is similar to fts5SegIterSeekInit(), except that it initializes | |||
| 12399 | ** the segment iterator to point to the first term following the page | |||
| 12400 | ** with pToken/nToken on it. | |||
| 12401 | */ | |||
| 12402 | static void fts5SegIterNextInit( | |||
| 12403 | Fts5Index *p, | |||
| 12404 | const char *pTerm, int nTerm, | |||
| 12405 | Fts5StructureSegment *pSeg, /* Description of segment */ | |||
| 12406 | Fts5SegIter *pIter /* Object to populate */ | |||
| 12407 | ){ | |||
| 12408 | int iPg = -1; /* Page of segment to open */ | |||
| 12409 | int bDlidx = 0; | |||
| 12410 | sqlite3_stmt *pSel = 0; /* SELECT to find iPg */ | |||
| 12411 | ||||
| 12412 | pSel = fts5IdxNextStmt(p); | |||
| 12413 | if( pSel ){ | |||
| 12414 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 12415 | sqlite3_bind_intsqlite3_api->bind_int(pSel, 1, pSeg->iSegid); | |||
| 12416 | sqlite3_bind_blobsqlite3_api->bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 12417 | ||||
| 12418 | if( sqlite3_stepsqlite3_api->step(pSel)==SQLITE_ROW100 ){ | |||
| 12419 | i64 val = sqlite3_column_int64sqlite3_api->column_int64(pSel, 0); | |||
| 12420 | iPg = (int)(val>>1); | |||
| 12421 | bDlidx = (val & 0x0001); | |||
| 12422 | } | |||
| 12423 | p->rc = sqlite3_resetsqlite3_api->reset(pSel); | |||
| 12424 | sqlite3_bind_nullsqlite3_api->bind_null(pSel, 2); | |||
| 12425 | if( p->rc ) return; | |||
| 12426 | } | |||
| 12427 | ||||
| 12428 | memset(pIter, 0, sizeof(*pIter)); | |||
| 12429 | pIter->pSeg = pSeg; | |||
| 12430 | pIter->flags |= FTS5_SEGITER_ONETERM0x01; | |||
| 12431 | if( iPg>=0 ){ | |||
| 12432 | pIter->iLeafPgno = iPg - 1; | |||
| 12433 | fts5SegIterNextPage(p, pIter); | |||
| 12434 | fts5SegIterSetNext(p, pIter); | |||
| 12435 | } | |||
| 12436 | if( pIter->pLeaf ){ | |||
| 12437 | const u8 *a = pIter->pLeaf->p; | |||
| 12438 | int iTermOff = 0; | |||
| 12439 | ||||
| 12440 | pIter->iPgidxOff = pIter->pLeaf->szLeaf; | |||
| 12441 | pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff)sqlite3Fts5GetVarint32(&a[pIter->iPgidxOff],(u32*)& (iTermOff)); | |||
| 12442 | pIter->iLeafOffset = iTermOff; | |||
| 12443 | fts5SegIterLoadTerm(p, pIter, 0); | |||
| 12444 | fts5SegIterLoadNPos(p, pIter); | |||
| 12445 | if( bDlidx ) fts5SegIterLoadDlidx(p, pIter); | |||
| 12446 | ||||
| 12447 | assert( p->rc!=SQLITE_OK ||((void) (0)) | |||
| 12448 | fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0((void) (0)) | |||
| 12449 | )((void) (0)); | |||
| 12450 | } | |||
| 12451 | } | |||
| 12452 | ||||
| 12453 | /* | |||
| 12454 | ** Initialize the object pIter to point to term pTerm/nTerm within the | |||
| 12455 | ** in-memory hash table. If there is no such term in the hash-table, the | |||
| 12456 | ** iterator is set to EOF. | |||
| 12457 | ** | |||
| 12458 | ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If | |||
| 12459 | ** an error has already occurred when this function is called, it is a no-op. | |||
| 12460 | */ | |||
| 12461 | static void fts5SegIterHashInit( | |||
| 12462 | Fts5Index *p, /* FTS5 backend */ | |||
| 12463 | const u8 *pTerm, int nTerm, /* Term to seek to */ | |||
| 12464 | int flags, /* Mask of FTS5INDEX_XXX flags */ | |||
| 12465 | Fts5SegIter *pIter /* Object to populate */ | |||
| 12466 | ){ | |||
| 12467 | int nList = 0; | |||
| 12468 | const u8 *z = 0; | |||
| 12469 | int n = 0; | |||
| 12470 | Fts5Data *pLeaf = 0; | |||
| 12471 | ||||
| 12472 | assert( p->pHash )((void) (0)); | |||
| 12473 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 12474 | ||||
| 12475 | if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN0x0008) ){ | |||
| 12476 | const u8 *pList = 0; | |||
| 12477 | ||||
| 12478 | p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); | |||
| 12479 | sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList); | |||
| 12480 | if( pList ){ | |||
| 12481 | pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); | |||
| 12482 | if( pLeaf ){ | |||
| 12483 | pLeaf->p = (u8*)pList; | |||
| 12484 | } | |||
| 12485 | } | |||
| 12486 | ||||
| 12487 | /* The call to sqlite3Fts5HashScanInit() causes the hash table to | |||
| 12488 | ** fill the size field of all existing position lists. This means they | |||
| 12489 | ** can no longer be appended to. Since the only scenario in which they | |||
| 12490 | ** can be appended to is if the previous operation on this table was | |||
| 12491 | ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this | |||
| 12492 | ** possibility altogether. */ | |||
| 12493 | p->bDelete = 0; | |||
| 12494 | }else{ | |||
| 12495 | p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data), | |||
| 12496 | (const char*)pTerm, nTerm, (void**)&pLeaf, &nList | |||
| 12497 | ); | |||
| 12498 | if( pLeaf ){ | |||
| 12499 | pLeaf->p = (u8*)&pLeaf[1]; | |||
| 12500 | } | |||
| 12501 | z = pTerm; | |||
| 12502 | n = nTerm; | |||
| 12503 | pIter->flags |= FTS5_SEGITER_ONETERM0x01; | |||
| 12504 | } | |||
| 12505 | ||||
| 12506 | if( pLeaf ){ | |||
| 12507 | sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z); | |||
| 12508 | pLeaf->nn = pLeaf->szLeaf = nList; | |||
| 12509 | pIter->pLeaf = pLeaf; | |||
| 12510 | pIter->iLeafOffset = fts5GetVarintsqlite3Fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid); | |||
| 12511 | pIter->iEndofDoclist = pLeaf->nn; | |||
| 12512 | ||||
| 12513 | if( flags & FTS5INDEX_QUERY_DESC0x0002 ){ | |||
| 12514 | pIter->flags |= FTS5_SEGITER_REVERSE0x02; | |||
| 12515 | fts5SegIterReverseInitPage(p, pIter); | |||
| 12516 | }else{ | |||
| 12517 | fts5SegIterLoadNPos(p, pIter); | |||
| 12518 | } | |||
| 12519 | } | |||
| 12520 | ||||
| 12521 | fts5SegIterSetNext(p, pIter); | |||
| 12522 | } | |||
| 12523 | ||||
| 12524 | /* | |||
| 12525 | ** Array ap[] contains n elements. Release each of these elements using | |||
| 12526 | ** fts5DataRelease(). Then free the array itself using sqlite3_free(). | |||
| 12527 | */ | |||
| 12528 | static void fts5IndexFreeArray(Fts5Data **ap, int n){ | |||
| 12529 | if( ap ){ | |||
| 12530 | int ii; | |||
| 12531 | for(ii=0; ii<n; ii++){ | |||
| 12532 | fts5DataRelease(ap[ii]); | |||
| 12533 | } | |||
| 12534 | sqlite3_freesqlite3_api->free(ap); | |||
| 12535 | } | |||
| 12536 | } | |||
| 12537 | ||||
| 12538 | /* | |||
| 12539 | ** Decrement the ref-count of the object passed as the only argument. If it | |||
| 12540 | ** reaches 0, free it and its contents. | |||
| 12541 | */ | |||
| 12542 | static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){ | |||
| 12543 | if( p ){ | |||
| 12544 | p->nRef--; | |||
| 12545 | if( p->nRef<=0 ){ | |||
| 12546 | int ii; | |||
| 12547 | for(ii=0; ii<p->nTombstone; ii++){ | |||
| 12548 | fts5DataRelease(p->apTombstone[ii]); | |||
| 12549 | } | |||
| 12550 | sqlite3_freesqlite3_api->free(p); | |||
| 12551 | } | |||
| 12552 | } | |||
| 12553 | } | |||
| 12554 | ||||
| 12555 | /* | |||
| 12556 | ** Zero the iterator passed as the only argument. | |||
| 12557 | */ | |||
| 12558 | static void fts5SegIterClear(Fts5SegIter *pIter){ | |||
| 12559 | fts5BufferFree(&pIter->term)sqlite3Fts5BufferFree(&pIter->term); | |||
| 12560 | fts5DataRelease(pIter->pLeaf); | |||
| 12561 | fts5DataRelease(pIter->pNextLeaf); | |||
| 12562 | fts5TombstoneArrayDelete(pIter->pTombArray); | |||
| 12563 | fts5DlidxIterFree(pIter->pDlidx); | |||
| 12564 | sqlite3_freesqlite3_api->free(pIter->aRowidOffset); | |||
| 12565 | memset(pIter, 0, sizeof(Fts5SegIter)); | |||
| 12566 | } | |||
| 12567 | ||||
| 12568 | #ifdef SQLITE_DEBUG | |||
| 12569 | ||||
| 12570 | /* | |||
| 12571 | ** This function is used as part of the big assert() procedure implemented by | |||
| 12572 | ** fts5AssertMultiIterSetup(). It ensures that the result currently stored | |||
| 12573 | ** in *pRes is the correct result of comparing the current positions of the | |||
| 12574 | ** two iterators. | |||
| 12575 | */ | |||
| 12576 | static void fts5AssertComparisonResult( | |||
| 12577 | Fts5Iter *pIter, | |||
| 12578 | Fts5SegIter *p1, | |||
| 12579 | Fts5SegIter *p2, | |||
| 12580 | Fts5CResult *pRes | |||
| 12581 | ){ | |||
| 12582 | int i1 = p1 - pIter->aSeg; | |||
| 12583 | int i2 = p2 - pIter->aSeg; | |||
| 12584 | ||||
| 12585 | if( p1->pLeaf || p2->pLeaf ){ | |||
| 12586 | if( p1->pLeaf==0 ){ | |||
| 12587 | assert( pRes->iFirst==i2 )((void) (0)); | |||
| 12588 | }else if( p2->pLeaf==0 ){ | |||
| 12589 | assert( pRes->iFirst==i1 )((void) (0)); | |||
| 12590 | }else{ | |||
| 12591 | int nMin = MIN(p1->term.n, p2->term.n)(((p1->term.n) < (p2->term.n)) ? (p1->term.n) : ( p2->term.n)); | |||
| 12592 | int res = fts5Memcmp(p1->term.p, p2->term.p, nMin)((nMin)<=0 ? 0 : memcmp((p1->term.p), (p2->term.p), ( nMin))); | |||
| 12593 | if( res==0 ) res = p1->term.n - p2->term.n; | |||
| 12594 | ||||
| 12595 | if( res==0 ){ | |||
| 12596 | assert( pRes->bTermEq==1 )((void) (0)); | |||
| 12597 | assert( p1->iRowid!=p2->iRowid )((void) (0)); | |||
| 12598 | res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : 1; | |||
| 12599 | }else{ | |||
| 12600 | assert( pRes->bTermEq==0 )((void) (0)); | |||
| 12601 | } | |||
| 12602 | ||||
| 12603 | if( res<0 ){ | |||
| 12604 | assert( pRes->iFirst==i1 )((void) (0)); | |||
| 12605 | }else{ | |||
| 12606 | assert( pRes->iFirst==i2 )((void) (0)); | |||
| 12607 | } | |||
| 12608 | } | |||
| 12609 | } | |||
| 12610 | } | |||
| 12611 | ||||
| 12612 | /* | |||
| 12613 | ** This function is a no-op unless SQLITE_DEBUG is defined when this module | |||
| 12614 | ** is compiled. In that case, this function is essentially an assert() | |||
| 12615 | ** statement used to verify that the contents of the pIter->aFirst[] array | |||
| 12616 | ** are correct. | |||
| 12617 | */ | |||
| 12618 | static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5Iter *pIter){ | |||
| 12619 | if( p->rc==SQLITE_OK0 ){ | |||
| 12620 | Fts5SegIter *pFirst = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; | |||
| 12621 | int i; | |||
| 12622 | ||||
| 12623 | assert( (pFirst->pLeaf==0)==pIter->base.bEof )((void) (0)); | |||
| 12624 | ||||
| 12625 | /* Check that pIter->iSwitchRowid is set correctly. */ | |||
| 12626 | for(i=0; i<pIter->nSeg; i++){ | |||
| 12627 | Fts5SegIter *p1 = &pIter->aSeg[i]; | |||
| 12628 | assert( p1==pFirst((void) (0)) | |||
| 12629 | || p1->pLeaf==0((void) (0)) | |||
| 12630 | || fts5BufferCompare(&pFirst->term, &p1->term)((void) (0)) | |||
| 12631 | || p1->iRowid==pIter->iSwitchRowid((void) (0)) | |||
| 12632 | || (p1->iRowid<pIter->iSwitchRowid)==pIter->bRev((void) (0)) | |||
| 12633 | )((void) (0)); | |||
| 12634 | } | |||
| 12635 | ||||
| 12636 | for(i=0; i<pIter->nSeg; i+=2){ | |||
| 12637 | Fts5SegIter *p1 = &pIter->aSeg[i]; | |||
| 12638 | Fts5SegIter *p2 = &pIter->aSeg[i+1]; | |||
| 12639 | Fts5CResult *pRes = &pIter->aFirst[(pIter->nSeg + i) / 2]; | |||
| 12640 | fts5AssertComparisonResult(pIter, p1, p2, pRes); | |||
| 12641 | } | |||
| 12642 | ||||
| 12643 | for(i=1; i<(pIter->nSeg / 2); i+=2){ | |||
| 12644 | Fts5SegIter *p1 = &pIter->aSeg[ pIter->aFirst[i*2].iFirst ]; | |||
| 12645 | Fts5SegIter *p2 = &pIter->aSeg[ pIter->aFirst[i*2+1].iFirst ]; | |||
| 12646 | Fts5CResult *pRes = &pIter->aFirst[i]; | |||
| 12647 | fts5AssertComparisonResult(pIter, p1, p2, pRes); | |||
| 12648 | } | |||
| 12649 | } | |||
| 12650 | } | |||
| 12651 | #else | |||
| 12652 | # define fts5AssertMultiIterSetup(x,y) | |||
| 12653 | #endif | |||
| 12654 | ||||
| 12655 | /* | |||
| 12656 | ** Do the comparison necessary to populate pIter->aFirst[iOut]. | |||
| 12657 | ** | |||
| 12658 | ** If the returned value is non-zero, then it is the index of an entry | |||
| 12659 | ** in the pIter->aSeg[] array that is (a) not at EOF, and (b) pointing | |||
| 12660 | ** to a key that is a duplicate of another, higher priority, | |||
| 12661 | ** segment-iterator in the pSeg->aSeg[] array. | |||
| 12662 | */ | |||
| 12663 | static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ | |||
| 12664 | int i1; /* Index of left-hand Fts5SegIter */ | |||
| 12665 | int i2; /* Index of right-hand Fts5SegIter */ | |||
| 12666 | int iRes; | |||
| 12667 | Fts5SegIter *p1; /* Left-hand Fts5SegIter */ | |||
| 12668 | Fts5SegIter *p2; /* Right-hand Fts5SegIter */ | |||
| 12669 | Fts5CResult *pRes = &pIter->aFirst[iOut]; | |||
| 12670 | ||||
| 12671 | assert( iOut<pIter->nSeg && iOut>0 )((void) (0)); | |||
| 12672 | assert( pIter->bRev==0 || pIter->bRev==1 )((void) (0)); | |||
| 12673 | ||||
| 12674 | if( iOut>=(pIter->nSeg/2) ){ | |||
| 12675 | i1 = (iOut - pIter->nSeg/2) * 2; | |||
| 12676 | i2 = i1 + 1; | |||
| 12677 | }else{ | |||
| 12678 | i1 = pIter->aFirst[iOut*2].iFirst; | |||
| 12679 | i2 = pIter->aFirst[iOut*2+1].iFirst; | |||
| 12680 | } | |||
| 12681 | p1 = &pIter->aSeg[i1]; | |||
| 12682 | p2 = &pIter->aSeg[i2]; | |||
| 12683 | ||||
| 12684 | pRes->bTermEq = 0; | |||
| 12685 | if( p1->pLeaf==0 ){ /* If p1 is at EOF */ | |||
| 12686 | iRes = i2; | |||
| 12687 | }else if( p2->pLeaf==0 ){ /* If p2 is at EOF */ | |||
| 12688 | iRes = i1; | |||
| 12689 | }else{ | |||
| 12690 | int res = fts5BufferCompare(&p1->term, &p2->term); | |||
| 12691 | if( res==0 ){ | |||
| 12692 | assert_nc( i2>i1 )((void) (0)); | |||
| 12693 | assert_nc( i2!=0 )((void) (0)); | |||
| 12694 | pRes->bTermEq = 1; | |||
| 12695 | if( p1->iRowid==p2->iRowid ){ | |||
| 12696 | return i2; | |||
| 12697 | } | |||
| 12698 | res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1; | |||
| 12699 | } | |||
| 12700 | assert( res!=0 )((void) (0)); | |||
| 12701 | if( res<0 ){ | |||
| 12702 | iRes = i1; | |||
| 12703 | }else{ | |||
| 12704 | iRes = i2; | |||
| 12705 | } | |||
| 12706 | } | |||
| 12707 | ||||
| 12708 | pRes->iFirst = (u16)iRes; | |||
| 12709 | return 0; | |||
| 12710 | } | |||
| 12711 | ||||
| 12712 | /* | |||
| 12713 | ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. | |||
| 12714 | ** It is an error if leaf iLeafPgno does not exist. Unless the db is | |||
| 12715 | ** a 'secure-delete' db, if it contains no rowids then this is also an error. | |||
| 12716 | */ | |||
| 12717 | static void fts5SegIterGotoPage( | |||
| 12718 | Fts5Index *p, /* FTS5 backend object */ | |||
| 12719 | Fts5SegIter *pIter, /* Iterator to advance */ | |||
| 12720 | int iLeafPgno | |||
| 12721 | ){ | |||
| 12722 | assert( iLeafPgno>pIter->iLeafPgno )((void) (0)); | |||
| 12723 | ||||
| 12724 | if( iLeafPgno>pIter->pSeg->pgnoLast ){ | |||
| 12725 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 12726 | }else{ | |||
| 12727 | fts5DataRelease(pIter->pNextLeaf); | |||
| 12728 | pIter->pNextLeaf = 0; | |||
| 12729 | pIter->iLeafPgno = iLeafPgno-1; | |||
| 12730 | ||||
| 12731 | while( p->rc==SQLITE_OK0 ){ | |||
| 12732 | int iOff; | |||
| 12733 | fts5SegIterNextPage(p, pIter); | |||
| 12734 | if( pIter->pLeaf==0 ) break; | |||
| 12735 | iOff = fts5LeafFirstRowidOff(pIter->pLeaf)(fts5GetU16((pIter->pLeaf)->p)); | |||
| 12736 | if( iOff>0 ){ | |||
| 12737 | u8 *a = pIter->pLeaf->p; | |||
| 12738 | int n = pIter->pLeaf->szLeaf; | |||
| 12739 | if( iOff<4 || iOff>=n ){ | |||
| 12740 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 12741 | }else{ | |||
| 12742 | iOff += fts5GetVarintsqlite3Fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); | |||
| 12743 | pIter->iLeafOffset = iOff; | |||
| 12744 | fts5SegIterLoadNPos(p, pIter); | |||
| 12745 | } | |||
| 12746 | break; | |||
| 12747 | } | |||
| 12748 | } | |||
| 12749 | } | |||
| 12750 | } | |||
| 12751 | ||||
| 12752 | /* | |||
| 12753 | ** Advance the iterator passed as the second argument until it is at or | |||
| 12754 | ** past rowid iFrom. Regardless of the value of iFrom, the iterator is | |||
| 12755 | ** always advanced at least once. | |||
| 12756 | */ | |||
| 12757 | static void fts5SegIterNextFrom( | |||
| 12758 | Fts5Index *p, /* FTS5 backend object */ | |||
| 12759 | Fts5SegIter *pIter, /* Iterator to advance */ | |||
| 12760 | i64 iMatch /* Advance iterator at least this far */ | |||
| 12761 | ){ | |||
| 12762 | int bRev = (pIter->flags & FTS5_SEGITER_REVERSE0x02); | |||
| 12763 | Fts5DlidxIter *pDlidx = pIter->pDlidx; | |||
| 12764 | int iLeafPgno = pIter->iLeafPgno; | |||
| 12765 | int bMove = 1; | |||
| 12766 | ||||
| 12767 | assert( pIter->flags & FTS5_SEGITER_ONETERM )((void) (0)); | |||
| 12768 | assert( pIter->pDlidx )((void) (0)); | |||
| 12769 | assert( pIter->pLeaf )((void) (0)); | |||
| 12770 | ||||
| 12771 | if( bRev==0 ){ | |||
| 12772 | while( !fts5DlidxIterEof(p, pDlidx) && iMatch>fts5DlidxIterRowid(pDlidx) ){ | |||
| 12773 | iLeafPgno = fts5DlidxIterPgno(pDlidx); | |||
| 12774 | fts5DlidxIterNext(p, pDlidx); | |||
| 12775 | } | |||
| 12776 | assert_nc( iLeafPgno>=pIter->iLeafPgno || p->rc )((void) (0)); | |||
| 12777 | if( iLeafPgno>pIter->iLeafPgno ){ | |||
| 12778 | fts5SegIterGotoPage(p, pIter, iLeafPgno); | |||
| 12779 | bMove = 0; | |||
| 12780 | } | |||
| 12781 | }else{ | |||
| 12782 | assert( pIter->pNextLeaf==0 )((void) (0)); | |||
| 12783 | assert( iMatch<pIter->iRowid )((void) (0)); | |||
| 12784 | while( !fts5DlidxIterEof(p, pDlidx) && iMatch<fts5DlidxIterRowid(pDlidx) ){ | |||
| 12785 | fts5DlidxIterPrev(p, pDlidx); | |||
| 12786 | } | |||
| 12787 | iLeafPgno = fts5DlidxIterPgno(pDlidx); | |||
| 12788 | ||||
| 12789 | assert( fts5DlidxIterEof(p, pDlidx) || iLeafPgno<=pIter->iLeafPgno )((void) (0)); | |||
| 12790 | ||||
| 12791 | if( iLeafPgno<pIter->iLeafPgno ){ | |||
| 12792 | pIter->iLeafPgno = iLeafPgno+1; | |||
| 12793 | fts5SegIterReverseNewPage(p, pIter); | |||
| 12794 | bMove = 0; | |||
| 12795 | } | |||
| 12796 | } | |||
| 12797 | ||||
| 12798 | do{ | |||
| 12799 | if( bMove && p->rc==SQLITE_OK0 ) pIter->xNext(p, pIter, 0); | |||
| 12800 | if( pIter->pLeaf==0 ) break; | |||
| 12801 | if( bRev==0 && pIter->iRowid>=iMatch ) break; | |||
| 12802 | if( bRev!=0 && pIter->iRowid<=iMatch ) break; | |||
| 12803 | bMove = 1; | |||
| 12804 | }while( p->rc==SQLITE_OK0 ); | |||
| 12805 | } | |||
| 12806 | ||||
| 12807 | /* | |||
| 12808 | ** Free the iterator object passed as the second argument. | |||
| 12809 | */ | |||
| 12810 | static void fts5MultiIterFree(Fts5Iter *pIter){ | |||
| 12811 | if( pIter ){ | |||
| 12812 | int i; | |||
| 12813 | for(i=0; i<pIter->nSeg; i++){ | |||
| 12814 | fts5SegIterClear(&pIter->aSeg[i]); | |||
| 12815 | } | |||
| 12816 | fts5BufferFree(&pIter->poslist)sqlite3Fts5BufferFree(&pIter->poslist); | |||
| 12817 | sqlite3_freesqlite3_api->free(pIter); | |||
| 12818 | } | |||
| 12819 | } | |||
| 12820 | ||||
| 12821 | static void fts5MultiIterAdvanced( | |||
| 12822 | Fts5Index *p, /* FTS5 backend to iterate within */ | |||
| 12823 | Fts5Iter *pIter, /* Iterator to update aFirst[] array for */ | |||
| 12824 | int iChanged, /* Index of sub-iterator just advanced */ | |||
| 12825 | int iMinset /* Minimum entry in aFirst[] to set */ | |||
| 12826 | ){ | |||
| 12827 | int i; | |||
| 12828 | for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK0; i=i/2){ | |||
| 12829 | int iEq; | |||
| 12830 | if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){ | |||
| 12831 | Fts5SegIter *pSeg = &pIter->aSeg[iEq]; | |||
| 12832 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 12833 | pSeg->xNext(p, pSeg, 0); | |||
| 12834 | i = pIter->nSeg + iEq; | |||
| 12835 | } | |||
| 12836 | } | |||
| 12837 | } | |||
| 12838 | ||||
| 12839 | /* | |||
| 12840 | ** Sub-iterator iChanged of iterator pIter has just been advanced. It still | |||
| 12841 | ** points to the same term though - just a different rowid. This function | |||
| 12842 | ** attempts to update the contents of the pIter->aFirst[] accordingly. | |||
| 12843 | ** If it does so successfully, 0 is returned. Otherwise 1. | |||
| 12844 | ** | |||
| 12845 | ** If non-zero is returned, the caller should call fts5MultiIterAdvanced() | |||
| 12846 | ** on the iterator instead. That function does the same as this one, except | |||
| 12847 | ** that it deals with more complicated cases as well. | |||
| 12848 | */ | |||
| 12849 | static int fts5MultiIterAdvanceRowid( | |||
| 12850 | Fts5Iter *pIter, /* Iterator to update aFirst[] array for */ | |||
| 12851 | int iChanged, /* Index of sub-iterator just advanced */ | |||
| 12852 | Fts5SegIter **ppFirst | |||
| 12853 | ){ | |||
| 12854 | Fts5SegIter *pNew = &pIter->aSeg[iChanged]; | |||
| 12855 | ||||
| 12856 | if( pNew->iRowid==pIter->iSwitchRowid | |||
| 12857 | || (pNew->iRowid<pIter->iSwitchRowid)==pIter->bRev | |||
| 12858 | ){ | |||
| 12859 | int i; | |||
| 12860 | Fts5SegIter *pOther = &pIter->aSeg[iChanged ^ 0x0001]; | |||
| 12861 | pIter->iSwitchRowid = pIter->bRev ? SMALLEST_INT64(((i64)-1) - (0xffffffff|(((i64)0x7fffffff)<<32))) : LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)); | |||
| 12862 | for(i=(pIter->nSeg+iChanged)/2; 1; i=i/2){ | |||
| 12863 | Fts5CResult *pRes = &pIter->aFirst[i]; | |||
| 12864 | ||||
| 12865 | assert( pNew->pLeaf )((void) (0)); | |||
| 12866 | assert( pRes->bTermEq==0 || pOther->pLeaf )((void) (0)); | |||
| 12867 | ||||
| 12868 | if( pRes->bTermEq ){ | |||
| 12869 | if( pNew->iRowid==pOther->iRowid ){ | |||
| 12870 | return 1; | |||
| 12871 | }else if( (pOther->iRowid>pNew->iRowid)==pIter->bRev ){ | |||
| 12872 | pIter->iSwitchRowid = pOther->iRowid; | |||
| 12873 | pNew = pOther; | |||
| 12874 | }else if( (pOther->iRowid>pIter->iSwitchRowid)==pIter->bRev ){ | |||
| 12875 | pIter->iSwitchRowid = pOther->iRowid; | |||
| 12876 | } | |||
| 12877 | } | |||
| 12878 | pRes->iFirst = (u16)(pNew - pIter->aSeg); | |||
| 12879 | if( i==1 ) break; | |||
| 12880 | ||||
| 12881 | pOther = &pIter->aSeg[ pIter->aFirst[i ^ 0x0001].iFirst ]; | |||
| 12882 | } | |||
| 12883 | } | |||
| 12884 | ||||
| 12885 | *ppFirst = pNew; | |||
| 12886 | return 0; | |||
| 12887 | } | |||
| 12888 | ||||
| 12889 | /* | |||
| 12890 | ** Set the pIter->bEof variable based on the state of the sub-iterators. | |||
| 12891 | */ | |||
| 12892 | static void fts5MultiIterSetEof(Fts5Iter *pIter){ | |||
| 12893 | Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; | |||
| 12894 | pIter->base.bEof = pSeg->pLeaf==0; | |||
| 12895 | pIter->iSwitchRowid = pSeg->iRowid; | |||
| 12896 | } | |||
| 12897 | ||||
| 12898 | /* | |||
| 12899 | ** The argument to this macro must be an Fts5Data structure containing a | |||
| 12900 | ** tombstone hash page. This macro returns the key-size of the hash-page. | |||
| 12901 | */ | |||
| 12902 | #define TOMBSTONE_KEYSIZE(pPg)(pPg->p[0]==4 ? 4 : 8) (pPg->p[0]==4 ? 4 : 8) | |||
| 12903 | ||||
| 12904 | #define TOMBSTONE_NSLOT(pPg)((pPg->nn > 16) ? ((pPg->nn-8) / (pPg->p[0]==4 ? 4 : 8)) : 1) \ | |||
| 12905 | ((pPg->nn > 16) ? ((pPg->nn-8) / TOMBSTONE_KEYSIZE(pPg)(pPg->p[0]==4 ? 4 : 8)) : 1) | |||
| 12906 | ||||
| 12907 | /* | |||
| 12908 | ** Query a single tombstone hash table for rowid iRowid. Return true if | |||
| 12909 | ** it is found or false otherwise. The tombstone hash table is one of | |||
| 12910 | ** nHashTable tables. | |||
| 12911 | */ | |||
| 12912 | static int fts5IndexTombstoneQuery( | |||
| 12913 | Fts5Data *pHash, /* Hash table page to query */ | |||
| 12914 | int nHashTable, /* Number of pages attached to segment */ | |||
| 12915 | u64 iRowid /* Rowid to query hash for */ | |||
| 12916 | ){ | |||
| 12917 | const int szKey = TOMBSTONE_KEYSIZE(pHash)(pHash->p[0]==4 ? 4 : 8); | |||
| 12918 | const int nSlot = TOMBSTONE_NSLOT(pHash)((pHash->nn > 16) ? ((pHash->nn-8) / (pHash->p[0] ==4 ? 4 : 8)) : 1); | |||
| 12919 | int iSlot = (iRowid / nHashTable) % nSlot; | |||
| 12920 | int nCollide = nSlot; | |||
| 12921 | ||||
| 12922 | if( iRowid==0 ){ | |||
| 12923 | return pHash->p[1]; | |||
| 12924 | }else if( szKey==4 ){ | |||
| 12925 | u32 *aSlot = (u32*)&pHash->p[8]; | |||
| 12926 | while( aSlot[iSlot] ){ | |||
| 12927 | if( fts5GetU32((u8*)&aSlot[iSlot])==iRowid ) return 1; | |||
| 12928 | if( nCollide--==0 ) break; | |||
| 12929 | iSlot = (iSlot+1)%nSlot; | |||
| 12930 | } | |||
| 12931 | }else{ | |||
| 12932 | u64 *aSlot = (u64*)&pHash->p[8]; | |||
| 12933 | while( aSlot[iSlot] ){ | |||
| 12934 | if( fts5GetU64((u8*)&aSlot[iSlot])==iRowid ) return 1; | |||
| 12935 | if( nCollide--==0 ) break; | |||
| 12936 | iSlot = (iSlot+1)%nSlot; | |||
| 12937 | } | |||
| 12938 | } | |||
| 12939 | ||||
| 12940 | return 0; | |||
| 12941 | } | |||
| 12942 | ||||
| 12943 | /* | |||
| 12944 | ** Return true if the iterator passed as the only argument points | |||
| 12945 | ** to an segment entry for which there is a tombstone. Return false | |||
| 12946 | ** if there is no tombstone or if the iterator is already at EOF. | |||
| 12947 | */ | |||
| 12948 | static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ | |||
| 12949 | int iFirst = pIter->aFirst[1].iFirst; | |||
| 12950 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; | |||
| 12951 | Fts5TombstoneArray *pArray = pSeg->pTombArray; | |||
| 12952 | ||||
| 12953 | if( pSeg->pLeaf && pArray ){ | |||
| 12954 | /* Figure out which page the rowid might be present on. */ | |||
| 12955 | int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone; | |||
| 12956 | assert( iPg>=0 )((void) (0)); | |||
| 12957 | ||||
| 12958 | /* If tombstone hash page iPg has not yet been loaded from the | |||
| 12959 | ** database, load it now. */ | |||
| 12960 | if( pArray->apTombstone[iPg]==0 ){ | |||
| 12961 | pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex, | |||
| 12962 | FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)( ((i64)(pSeg->pSeg->iSegid+(1<<16)) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << ( 31)) + ((i64)(iPg)) ) | |||
| 12963 | ); | |||
| 12964 | if( pArray->apTombstone[iPg]==0 ) return 0; | |||
| 12965 | } | |||
| 12966 | ||||
| 12967 | return fts5IndexTombstoneQuery( | |||
| 12968 | pArray->apTombstone[iPg], | |||
| 12969 | pArray->nTombstone, | |||
| 12970 | pSeg->iRowid | |||
| 12971 | ); | |||
| 12972 | } | |||
| 12973 | ||||
| 12974 | return 0; | |||
| 12975 | } | |||
| 12976 | ||||
| 12977 | /* | |||
| 12978 | ** Move the iterator to the next entry. | |||
| 12979 | ** | |||
| 12980 | ** If an error occurs, an error code is left in Fts5Index.rc. It is not | |||
| 12981 | ** considered an error if the iterator reaches EOF, or if it is already at | |||
| 12982 | ** EOF when this function is called. | |||
| 12983 | */ | |||
| 12984 | static void fts5MultiIterNext( | |||
| 12985 | Fts5Index *p, | |||
| 12986 | Fts5Iter *pIter, | |||
| 12987 | int bFrom, /* True if argument iFrom is valid */ | |||
| 12988 | i64 iFrom /* Advance at least as far as this */ | |||
| 12989 | ){ | |||
| 12990 | int bUseFrom = bFrom; | |||
| 12991 | assert( pIter->base.bEof==0 )((void) (0)); | |||
| 12992 | while( p->rc==SQLITE_OK0 ){ | |||
| 12993 | int iFirst = pIter->aFirst[1].iFirst; | |||
| 12994 | int bNewTerm = 0; | |||
| 12995 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; | |||
| 12996 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 12997 | if( bUseFrom && pSeg->pDlidx ){ | |||
| 12998 | fts5SegIterNextFrom(p, pSeg, iFrom); | |||
| 12999 | }else{ | |||
| 13000 | pSeg->xNext(p, pSeg, &bNewTerm); | |||
| 13001 | } | |||
| 13002 | ||||
| 13003 | if( pSeg->pLeaf==0 || bNewTerm | |||
| 13004 | || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg) | |||
| 13005 | ){ | |||
| 13006 | fts5MultiIterAdvanced(p, pIter, iFirst, 1); | |||
| 13007 | fts5MultiIterSetEof(pIter); | |||
| 13008 | pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; | |||
| 13009 | if( pSeg->pLeaf==0 ) return; | |||
| 13010 | } | |||
| 13011 | ||||
| 13012 | fts5AssertMultiIterSetup(p, pIter); | |||
| 13013 | assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf )((void) (0)); | |||
| 13014 | if( (pIter->bSkipEmpty==0 || pSeg->nPos) | |||
| 13015 | && 0==fts5MultiIterIsDeleted(pIter) | |||
| 13016 | ){ | |||
| 13017 | pIter->xSetOutputs(pIter, pSeg); | |||
| 13018 | return; | |||
| 13019 | } | |||
| 13020 | bUseFrom = 0; | |||
| 13021 | } | |||
| 13022 | } | |||
| 13023 | ||||
| 13024 | static void fts5MultiIterNext2( | |||
| 13025 | Fts5Index *p, | |||
| 13026 | Fts5Iter *pIter, | |||
| 13027 | int *pbNewTerm /* OUT: True if *might* be new term */ | |||
| 13028 | ){ | |||
| 13029 | assert( pIter->bSkipEmpty )((void) (0)); | |||
| 13030 | if( p->rc==SQLITE_OK0 ){ | |||
| 13031 | *pbNewTerm = 0; | |||
| 13032 | do{ | |||
| 13033 | int iFirst = pIter->aFirst[1].iFirst; | |||
| 13034 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; | |||
| 13035 | int bNewTerm = 0; | |||
| 13036 | ||||
| 13037 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 13038 | pSeg->xNext(p, pSeg, &bNewTerm); | |||
| 13039 | if( pSeg->pLeaf==0 || bNewTerm | |||
| 13040 | || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg) | |||
| 13041 | ){ | |||
| 13042 | fts5MultiIterAdvanced(p, pIter, iFirst, 1); | |||
| 13043 | fts5MultiIterSetEof(pIter); | |||
| 13044 | *pbNewTerm = 1; | |||
| 13045 | } | |||
| 13046 | fts5AssertMultiIterSetup(p, pIter); | |||
| 13047 | ||||
| 13048 | }while( (fts5MultiIterIsEmpty(p, pIter) || fts5MultiIterIsDeleted(pIter)) | |||
| 13049 | && (p->rc==SQLITE_OK0) | |||
| 13050 | ); | |||
| 13051 | } | |||
| 13052 | } | |||
| 13053 | ||||
| 13054 | static void fts5IterSetOutputs_Noop(Fts5Iter *pUnused1, Fts5SegIter *pUnused2){ | |||
| 13055 | UNUSED_PARAM2(pUnused1, pUnused2)(void)(pUnused1), (void)(pUnused2); | |||
| 13056 | } | |||
| 13057 | ||||
| 13058 | static Fts5Iter *fts5MultiIterAlloc( | |||
| 13059 | Fts5Index *p, /* FTS5 backend to iterate within */ | |||
| 13060 | int nSeg | |||
| 13061 | ){ | |||
| 13062 | Fts5Iter *pNew; | |||
| 13063 | i64 nSlot; /* Power of two >= nSeg */ | |||
| 13064 | ||||
| 13065 | for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2); | |||
| 13066 | pNew = fts5IdxMalloc(p, | |||
| 13067 | SZ_FTS5ITER(nSlot)(__builtin_offsetof(Fts5Iter, aSeg)+(nSlot)*sizeof(Fts5SegIter )) + /* pNew + pNew->aSeg[] */ | |||
| 13068 | sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ | |||
| 13069 | ); | |||
| 13070 | if( pNew ){ | |||
| 13071 | pNew->nSeg = nSlot; | |||
| 13072 | pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot]; | |||
| 13073 | pNew->pIndex = p; | |||
| 13074 | pNew->xSetOutputs = fts5IterSetOutputs_Noop; | |||
| 13075 | } | |||
| 13076 | return pNew; | |||
| 13077 | } | |||
| 13078 | ||||
| 13079 | static void fts5PoslistCallback( | |||
| 13080 | Fts5Index *pUnused, | |||
| 13081 | void *pContext, | |||
| 13082 | const u8 *pChunk, int nChunk | |||
| 13083 | ){ | |||
| 13084 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 13085 | assert_nc( nChunk>=0 )((void) (0)); | |||
| 13086 | if( nChunk>0 ){ | |||
| 13087 | fts5BufferSafeAppendBlob((Fts5Buffer*)pContext, pChunk, nChunk){ ((void) (0)); memcpy(&((Fts5Buffer*)pContext)->p[((Fts5Buffer *)pContext)->n], pChunk, nChunk); ((Fts5Buffer*)pContext)-> n += nChunk; }; | |||
| 13088 | } | |||
| 13089 | } | |||
| 13090 | ||||
| 13091 | typedef struct PoslistCallbackCtx PoslistCallbackCtx; | |||
| 13092 | struct PoslistCallbackCtx { | |||
| 13093 | Fts5Buffer *pBuf; /* Append to this buffer */ | |||
| 13094 | Fts5Colset *pColset; /* Restrict matches to this column */ | |||
| 13095 | int eState; /* See above */ | |||
| 13096 | }; | |||
| 13097 | ||||
| 13098 | typedef struct PoslistOffsetsCtx PoslistOffsetsCtx; | |||
| 13099 | struct PoslistOffsetsCtx { | |||
| 13100 | Fts5Buffer *pBuf; /* Append to this buffer */ | |||
| 13101 | Fts5Colset *pColset; /* Restrict matches to this column */ | |||
| 13102 | int iRead; | |||
| 13103 | int iWrite; | |||
| 13104 | }; | |||
| 13105 | ||||
| 13106 | /* | |||
| 13107 | ** TODO: Make this more efficient! | |||
| 13108 | */ | |||
| 13109 | static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){ | |||
| 13110 | int i; | |||
| 13111 | for(i=0; i<pColset->nCol; i++){ | |||
| 13112 | if( pColset->aiCol[i]==iCol ) return 1; | |||
| 13113 | } | |||
| 13114 | return 0; | |||
| 13115 | } | |||
| 13116 | ||||
| 13117 | static void fts5PoslistOffsetsCallback( | |||
| 13118 | Fts5Index *pUnused, | |||
| 13119 | void *pContext, | |||
| 13120 | const u8 *pChunk, int nChunk | |||
| 13121 | ){ | |||
| 13122 | PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext; | |||
| 13123 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 13124 | assert_nc( nChunk>=0 )((void) (0)); | |||
| 13125 | if( nChunk>0 ){ | |||
| 13126 | int i = 0; | |||
| 13127 | while( i<nChunk ){ | |||
| 13128 | int iVal; | |||
| 13129 | i += fts5GetVarint32(&pChunk[i], iVal)sqlite3Fts5GetVarint32(&pChunk[i],(u32*)&(iVal)); | |||
| 13130 | iVal += pCtx->iRead - 2; | |||
| 13131 | pCtx->iRead = iVal; | |||
| 13132 | if( fts5IndexColsetTest(pCtx->pColset, iVal) ){ | |||
| 13133 | fts5BufferSafeAppendVarint(pCtx->pBuf, iVal + 2 - pCtx->iWrite){ (pCtx->pBuf)->n += sqlite3Fts5PutVarint(&(pCtx-> pBuf)->p[(pCtx->pBuf)->n], (iVal + 2 - pCtx->iWrite )); ((void) (0)); }; | |||
| 13134 | pCtx->iWrite = iVal; | |||
| 13135 | } | |||
| 13136 | } | |||
| 13137 | } | |||
| 13138 | } | |||
| 13139 | ||||
| 13140 | static void fts5PoslistFilterCallback( | |||
| 13141 | Fts5Index *pUnused, | |||
| 13142 | void *pContext, | |||
| 13143 | const u8 *pChunk, int nChunk | |||
| 13144 | ){ | |||
| 13145 | PoslistCallbackCtx *pCtx = (PoslistCallbackCtx*)pContext; | |||
| 13146 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 13147 | assert_nc( nChunk>=0 )((void) (0)); | |||
| 13148 | if( nChunk>0 ){ | |||
| 13149 | /* Search through to find the first varint with value 1. This is the | |||
| 13150 | ** start of the next columns hits. */ | |||
| 13151 | int i = 0; | |||
| 13152 | int iStart = 0; | |||
| 13153 | ||||
| 13154 | if( pCtx->eState==2 ){ | |||
| 13155 | int iCol; | |||
| 13156 | fts5FastGetVarint32(pChunk, i, iCol){ iCol = (pChunk)[i++]; if( iCol & 0x80 ){ i--; i += sqlite3Fts5GetVarint32 (&(pChunk)[i],(u32*)&(iCol)); } }; | |||
| 13157 | if( fts5IndexColsetTest(pCtx->pColset, iCol) ){ | |||
| 13158 | pCtx->eState = 1; | |||
| 13159 | fts5BufferSafeAppendVarint(pCtx->pBuf, 1){ (pCtx->pBuf)->n += sqlite3Fts5PutVarint(&(pCtx-> pBuf)->p[(pCtx->pBuf)->n], (1)); ((void) (0)); }; | |||
| 13160 | }else{ | |||
| 13161 | pCtx->eState = 0; | |||
| 13162 | } | |||
| 13163 | } | |||
| 13164 | ||||
| 13165 | do { | |||
| 13166 | while( i<nChunk && pChunk[i]!=0x01 ){ | |||
| 13167 | fts5IndexSkipVarint(pChunk, i){ int iEnd = i+9; while( (pChunk[i++] & 0x80) && i <iEnd ); }; | |||
| 13168 | } | |||
| 13169 | if( pCtx->eState ){ | |||
| 13170 | fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart){ ((void) (0)); memcpy(&(pCtx->pBuf)->p[(pCtx->pBuf )->n], &pChunk[iStart], i-iStart); (pCtx->pBuf)-> n += i-iStart; }; | |||
| 13171 | } | |||
| 13172 | if( i<nChunk ){ | |||
| 13173 | int iCol; | |||
| 13174 | iStart = i; | |||
| 13175 | i++; | |||
| 13176 | if( i>=nChunk ){ | |||
| 13177 | pCtx->eState = 2; | |||
| 13178 | }else{ | |||
| 13179 | fts5FastGetVarint32(pChunk, i, iCol){ iCol = (pChunk)[i++]; if( iCol & 0x80 ){ i--; i += sqlite3Fts5GetVarint32 (&(pChunk)[i],(u32*)&(iCol)); } }; | |||
| 13180 | pCtx->eState = fts5IndexColsetTest(pCtx->pColset, iCol); | |||
| 13181 | if( pCtx->eState ){ | |||
| 13182 | fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart){ ((void) (0)); memcpy(&(pCtx->pBuf)->p[(pCtx->pBuf )->n], &pChunk[iStart], i-iStart); (pCtx->pBuf)-> n += i-iStart; }; | |||
| 13183 | iStart = i; | |||
| 13184 | } | |||
| 13185 | } | |||
| 13186 | } | |||
| 13187 | }while( i<nChunk ); | |||
| 13188 | } | |||
| 13189 | } | |||
| 13190 | ||||
| 13191 | static void fts5ChunkIterate( | |||
| 13192 | Fts5Index *p, /* Index object */ | |||
| 13193 | Fts5SegIter *pSeg, /* Poslist of this iterator */ | |||
| 13194 | void *pCtx, /* Context pointer for xChunk callback */ | |||
| 13195 | void (*xChunk)(Fts5Index*, void*, const u8*, int) | |||
| 13196 | ){ | |||
| 13197 | int nRem = pSeg->nPos; /* Number of bytes still to come */ | |||
| 13198 | Fts5Data *pData = 0; | |||
| 13199 | u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset]; | |||
| 13200 | int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset)(((nRem) < (pSeg->pLeaf->szLeaf - pSeg->iLeafOffset )) ? (nRem) : (pSeg->pLeaf->szLeaf - pSeg->iLeafOffset )); | |||
| 13201 | int pgno = pSeg->iLeafPgno; | |||
| 13202 | int pgnoSave = 0; | |||
| 13203 | ||||
| 13204 | /* This function does not work with detail=none databases. */ | |||
| 13205 | assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE )((void) (0)); | |||
| 13206 | ||||
| 13207 | if( (pSeg->flags & FTS5_SEGITER_REVERSE0x02)==0 ){ | |||
| 13208 | pgnoSave = pgno+1; | |||
| 13209 | } | |||
| 13210 | ||||
| 13211 | while( 1 ){ | |||
| 13212 | xChunk(p, pCtx, pChunk, nChunk); | |||
| 13213 | nRem -= nChunk; | |||
| 13214 | fts5DataRelease(pData); | |||
| 13215 | if( nRem<=0 ){ | |||
| 13216 | break; | |||
| 13217 | }else if( pSeg->pSeg==0 ){ | |||
| 13218 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 13219 | return; | |||
| 13220 | }else{ | |||
| 13221 | pgno++; | |||
| 13222 | pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno)( ((i64)(pSeg->pSeg->iSegid) << (31 +5 +1)) + ((i64 )(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pgno )) )); | |||
| 13223 | if( pData==0 ) break; | |||
| 13224 | pChunk = &pData->p[4]; | |||
| 13225 | nChunk = MIN(nRem, pData->szLeaf - 4)(((nRem) < (pData->szLeaf - 4)) ? (nRem) : (pData->szLeaf - 4)); | |||
| 13226 | if( pgno==pgnoSave ){ | |||
| 13227 | assert( pSeg->pNextLeaf==0 )((void) (0)); | |||
| 13228 | pSeg->pNextLeaf = pData; | |||
| 13229 | pData = 0; | |||
| 13230 | } | |||
| 13231 | } | |||
| 13232 | } | |||
| 13233 | } | |||
| 13234 | ||||
| 13235 | /* | |||
| 13236 | ** Iterator pIter currently points to a valid entry (not EOF). This | |||
| 13237 | ** function appends the position list data for the current entry to | |||
| 13238 | ** buffer pBuf. It does not make a copy of the position-list size | |||
| 13239 | ** field. | |||
| 13240 | */ | |||
| 13241 | static void fts5SegiterPoslist( | |||
| 13242 | Fts5Index *p, | |||
| 13243 | Fts5SegIter *pSeg, | |||
| 13244 | Fts5Colset *pColset, | |||
| 13245 | Fts5Buffer *pBuf | |||
| 13246 | ){ | |||
| 13247 | assert( pBuf!=0 )((void) (0)); | |||
| 13248 | assert( pSeg!=0 )((void) (0)); | |||
| 13249 | if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING)( (u32)((pBuf)->n) + (u32)(pSeg->nPos+8) <= (u32)((pBuf )->nSpace) ? 0 : sqlite3Fts5BufferSize((&p->rc),(pBuf ),(pSeg->nPos+8)+(pBuf)->n) ) ){ | |||
| 13250 | assert( pBuf->p!=0 )((void) (0)); | |||
| 13251 | assert( pBuf->nSpace >= pBuf->n+pSeg->nPos+FTS5_DATA_ZERO_PADDING )((void) (0)); | |||
| 13252 | memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING8); | |||
| 13253 | if( pColset==0 ){ | |||
| 13254 | fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); | |||
| 13255 | }else{ | |||
| 13256 | if( p->pConfig->eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 13257 | PoslistCallbackCtx sCtx; | |||
| 13258 | sCtx.pBuf = pBuf; | |||
| 13259 | sCtx.pColset = pColset; | |||
| 13260 | sCtx.eState = fts5IndexColsetTest(pColset, 0); | |||
| 13261 | assert( sCtx.eState==0 || sCtx.eState==1 )((void) (0)); | |||
| 13262 | fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback); | |||
| 13263 | }else{ | |||
| 13264 | PoslistOffsetsCtx sCtx; | |||
| 13265 | memset(&sCtx, 0, sizeof(sCtx)); | |||
| 13266 | sCtx.pBuf = pBuf; | |||
| 13267 | sCtx.pColset = pColset; | |||
| 13268 | fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback); | |||
| 13269 | } | |||
| 13270 | } | |||
| 13271 | } | |||
| 13272 | } | |||
| 13273 | ||||
| 13274 | /* | |||
| 13275 | ** Parameter pPos points to a buffer containing a position list, size nPos. | |||
| 13276 | ** This function filters it according to pColset (which must be non-NULL) | |||
| 13277 | ** and sets pIter->base.pData/nData to point to the new position list. | |||
| 13278 | ** If memory is required for the new position list, use buffer pIter->poslist. | |||
| 13279 | ** Or, if the new position list is a contiguous subset of the input, set | |||
| 13280 | ** pIter->base.pData/nData to point directly to it. | |||
| 13281 | ** | |||
| 13282 | ** This function is a no-op if *pRc is other than SQLITE_OK when it is | |||
| 13283 | ** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM | |||
| 13284 | ** before returning. | |||
| 13285 | */ | |||
| 13286 | static void fts5IndexExtractColset( | |||
| 13287 | int *pRc, | |||
| 13288 | Fts5Colset *pColset, /* Colset to filter on */ | |||
| 13289 | const u8 *pPos, int nPos, /* Position list */ | |||
| 13290 | Fts5Iter *pIter | |||
| 13291 | ){ | |||
| 13292 | if( *pRc==SQLITE_OK0 ){ | |||
| 13293 | const u8 *p = pPos; | |||
| 13294 | const u8 *aCopy = p; | |||
| 13295 | const u8 *pEnd = &p[nPos]; /* One byte past end of position list */ | |||
| 13296 | int i = 0; | |||
| 13297 | int iCurrent = 0; | |||
| 13298 | ||||
| 13299 | if( pColset->nCol>1 && sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){ | |||
| 13300 | return; | |||
| 13301 | } | |||
| 13302 | ||||
| 13303 | while( 1 ){ | |||
| 13304 | while( pColset->aiCol[i]<iCurrent ){ | |||
| 13305 | i++; | |||
| 13306 | if( i==pColset->nCol ){ | |||
| 13307 | pIter->base.pData = pIter->poslist.p; | |||
| 13308 | pIter->base.nData = pIter->poslist.n; | |||
| 13309 | return; | |||
| 13310 | } | |||
| 13311 | } | |||
| 13312 | ||||
| 13313 | /* Advance pointer p until it points to pEnd or an 0x01 byte that is | |||
| 13314 | ** not part of a varint */ | |||
| 13315 | while( p<pEnd && *p!=0x01 ){ | |||
| 13316 | while( p<pEnd && (*p++ & 0x80) ); | |||
| 13317 | } | |||
| 13318 | ||||
| 13319 | if( pColset->aiCol[i]==iCurrent ){ | |||
| 13320 | if( pColset->nCol==1 ){ | |||
| 13321 | pIter->base.pData = aCopy; | |||
| 13322 | pIter->base.nData = p-aCopy; | |||
| 13323 | return; | |||
| 13324 | } | |||
| 13325 | fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy){ ((void) (0)); memcpy(&(&pIter->poslist)->p[(& pIter->poslist)->n], aCopy, p-aCopy); (&pIter->poslist )->n += p-aCopy; }; | |||
| 13326 | } | |||
| 13327 | if( p>=pEnd ){ | |||
| 13328 | pIter->base.pData = pIter->poslist.p; | |||
| 13329 | pIter->base.nData = pIter->poslist.n; | |||
| 13330 | return; | |||
| 13331 | } | |||
| 13332 | aCopy = p++; | |||
| 13333 | iCurrent = *p++; | |||
| 13334 | if( iCurrent & 0x80 ){ | |||
| 13335 | p--; | |||
| 13336 | p += fts5GetVarint32(p, iCurrent)sqlite3Fts5GetVarint32(p,(u32*)&(iCurrent)); | |||
| 13337 | } | |||
| 13338 | } | |||
| 13339 | } | |||
| 13340 | ||||
| 13341 | } | |||
| 13342 | ||||
| 13343 | /* | |||
| 13344 | ** xSetOutputs callback used by detail=none tables. | |||
| 13345 | */ | |||
| 13346 | static void fts5IterSetOutputs_None(Fts5Iter *pIter, Fts5SegIter *pSeg){ | |||
| 13347 | assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_NONE )((void) (0)); | |||
| 13348 | pIter->base.iRowid = pSeg->iRowid; | |||
| 13349 | pIter->base.nData = pSeg->nPos; | |||
| 13350 | } | |||
| 13351 | ||||
| 13352 | /* | |||
| 13353 | ** xSetOutputs callback used by detail=full and detail=col tables when no | |||
| 13354 | ** column filters are specified. | |||
| 13355 | */ | |||
| 13356 | static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){ | |||
| 13357 | pIter->base.iRowid = pSeg->iRowid; | |||
| 13358 | pIter->base.nData = pSeg->nPos; | |||
| 13359 | ||||
| 13360 | assert( pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_NONE )((void) (0)); | |||
| 13361 | assert( pIter->pColset==0 )((void) (0)); | |||
| 13362 | ||||
| 13363 | if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){ | |||
| 13364 | /* All data is stored on the current page. Populate the output | |||
| 13365 | ** variables to point into the body of the page object. */ | |||
| 13366 | pIter->base.pData = &pSeg->pLeaf->p[pSeg->iLeafOffset]; | |||
| 13367 | }else{ | |||
| 13368 | /* The data is distributed over two or more pages. Copy it into the | |||
| 13369 | ** Fts5Iter.poslist buffer and then set the output pointer to point | |||
| 13370 | ** to this buffer. */ | |||
| 13371 | fts5BufferZero(&pIter->poslist)sqlite3Fts5BufferZero(&pIter->poslist); | |||
| 13372 | fts5SegiterPoslist(pIter->pIndex, pSeg, 0, &pIter->poslist); | |||
| 13373 | pIter->base.pData = pIter->poslist.p; | |||
| 13374 | } | |||
| 13375 | } | |||
| 13376 | ||||
| 13377 | /* | |||
| 13378 | ** xSetOutputs callback used when the Fts5Colset object has nCol==0 (match | |||
| 13379 | ** against no columns at all). | |||
| 13380 | */ | |||
| 13381 | static void fts5IterSetOutputs_ZeroColset(Fts5Iter *pIter, Fts5SegIter *pSeg){ | |||
| 13382 | UNUSED_PARAM(pSeg)(void)(pSeg); | |||
| 13383 | pIter->base.nData = 0; | |||
| 13384 | } | |||
| 13385 | ||||
| 13386 | /* | |||
| 13387 | ** xSetOutputs callback used by detail=col when there is a column filter | |||
| 13388 | ** and there are 100 or more columns. Also called as a fallback from | |||
| 13389 | ** fts5IterSetOutputs_Col100 if the column-list spans more than one page. | |||
| 13390 | */ | |||
| 13391 | static void fts5IterSetOutputs_Col(Fts5Iter *pIter, Fts5SegIter *pSeg){ | |||
| 13392 | fts5BufferZero(&pIter->poslist)sqlite3Fts5BufferZero(&pIter->poslist); | |||
| 13393 | fts5SegiterPoslist(pIter->pIndex, pSeg, pIter->pColset, &pIter->poslist); | |||
| 13394 | pIter->base.iRowid = pSeg->iRowid; | |||
| 13395 | pIter->base.pData = pIter->poslist.p; | |||
| 13396 | pIter->base.nData = pIter->poslist.n; | |||
| 13397 | } | |||
| 13398 | ||||
| 13399 | /* | |||
| 13400 | ** xSetOutputs callback used when: | |||
| 13401 | ** | |||
| 13402 | ** * detail=col, | |||
| 13403 | ** * there is a column filter, and | |||
| 13404 | ** * the table contains 100 or fewer columns. | |||
| 13405 | ** | |||
| 13406 | ** The last point is to ensure all column numbers are stored as | |||
| 13407 | ** single-byte varints. | |||
| 13408 | */ | |||
| 13409 | static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){ | |||
| 13410 | ||||
| 13411 | assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS )((void) (0)); | |||
| 13412 | assert( pIter->pColset )((void) (0)); | |||
| 13413 | assert( pIter->poslist.nSpace>=pIter->pIndex->pConfig->nCol )((void) (0)); | |||
| 13414 | ||||
| 13415 | if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf | |||
| 13416 | || pSeg->nPos>pIter->pIndex->pConfig->nCol | |||
| 13417 | ){ | |||
| 13418 | fts5IterSetOutputs_Col(pIter, pSeg); | |||
| 13419 | }else{ | |||
| 13420 | u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset]; | |||
| 13421 | u8 *pEnd = (u8*)&a[pSeg->nPos]; | |||
| 13422 | int iPrev = 0; | |||
| 13423 | int *aiCol = pIter->pColset->aiCol; | |||
| 13424 | int *aiColEnd = &aiCol[pIter->pColset->nCol]; | |||
| 13425 | ||||
| 13426 | u8 *aOut = pIter->poslist.p; | |||
| 13427 | int iPrevOut = 0; | |||
| 13428 | ||||
| 13429 | pIter->base.iRowid = pSeg->iRowid; | |||
| 13430 | ||||
| 13431 | while( a<pEnd ){ | |||
| 13432 | iPrev += (int)a++[0] - 2; | |||
| 13433 | while( *aiCol<iPrev ){ | |||
| 13434 | aiCol++; | |||
| 13435 | if( aiCol==aiColEnd ) goto setoutputs_col_out; | |||
| 13436 | } | |||
| 13437 | if( *aiCol==iPrev ){ | |||
| 13438 | *aOut++ = (u8)((iPrev - iPrevOut) + 2); | |||
| 13439 | iPrevOut = iPrev; | |||
| 13440 | } | |||
| 13441 | } | |||
| 13442 | ||||
| 13443 | setoutputs_col_out: | |||
| 13444 | pIter->base.pData = pIter->poslist.p; | |||
| 13445 | pIter->base.nData = aOut - pIter->poslist.p; | |||
| 13446 | } | |||
| 13447 | } | |||
| 13448 | ||||
| 13449 | /* | |||
| 13450 | ** xSetOutputs callback used by detail=full when there is a column filter. | |||
| 13451 | */ | |||
| 13452 | static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ | |||
| 13453 | Fts5Colset *pColset = pIter->pColset; | |||
| 13454 | pIter->base.iRowid = pSeg->iRowid; | |||
| 13455 | ||||
| 13456 | assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_FULL )((void) (0)); | |||
| 13457 | assert( pColset )((void) (0)); | |||
| 13458 | ||||
| 13459 | if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){ | |||
| 13460 | /* All data is stored on the current page. Populate the output | |||
| 13461 | ** variables to point into the body of the page object. */ | |||
| 13462 | const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset]; | |||
| 13463 | int *pRc = &pIter->pIndex->rc; | |||
| 13464 | fts5BufferZero(&pIter->poslist)sqlite3Fts5BufferZero(&pIter->poslist); | |||
| 13465 | fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter); | |||
| 13466 | }else{ | |||
| 13467 | /* The data is distributed over two or more pages. Copy it into the | |||
| 13468 | ** Fts5Iter.poslist buffer and then set the output pointer to point | |||
| 13469 | ** to this buffer. */ | |||
| 13470 | fts5BufferZero(&pIter->poslist)sqlite3Fts5BufferZero(&pIter->poslist); | |||
| 13471 | fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist); | |||
| 13472 | pIter->base.pData = pIter->poslist.p; | |||
| 13473 | pIter->base.nData = pIter->poslist.n; | |||
| 13474 | } | |||
| 13475 | } | |||
| 13476 | ||||
| 13477 | static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ | |||
| 13478 | assert( pIter!=0 || (*pRc)!=SQLITE_OK )((void) (0)); | |||
| 13479 | if( *pRc==SQLITE_OK0 ){ | |||
| 13480 | Fts5Config *pConfig = pIter->pIndex->pConfig; | |||
| 13481 | if( pConfig->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 13482 | pIter->xSetOutputs = fts5IterSetOutputs_None; | |||
| 13483 | } | |||
| 13484 | ||||
| 13485 | else if( pIter->pColset==0 ){ | |||
| 13486 | pIter->xSetOutputs = fts5IterSetOutputs_Nocolset; | |||
| 13487 | } | |||
| 13488 | ||||
| 13489 | else if( pIter->pColset->nCol==0 ){ | |||
| 13490 | pIter->xSetOutputs = fts5IterSetOutputs_ZeroColset; | |||
| 13491 | } | |||
| 13492 | ||||
| 13493 | else if( pConfig->eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 13494 | pIter->xSetOutputs = fts5IterSetOutputs_Full; | |||
| 13495 | } | |||
| 13496 | ||||
| 13497 | else{ | |||
| 13498 | assert( pConfig->eDetail==FTS5_DETAIL_COLUMNS )((void) (0)); | |||
| 13499 | if( pConfig->nCol<=100 ){ | |||
| 13500 | pIter->xSetOutputs = fts5IterSetOutputs_Col100; | |||
| 13501 | sqlite3Fts5BufferSize(pRc, &pIter->poslist, pConfig->nCol); | |||
| 13502 | }else{ | |||
| 13503 | pIter->xSetOutputs = fts5IterSetOutputs_Col; | |||
| 13504 | } | |||
| 13505 | } | |||
| 13506 | } | |||
| 13507 | } | |||
| 13508 | ||||
| 13509 | /* | |||
| 13510 | ** All the component segment-iterators of pIter have been set up. This | |||
| 13511 | ** functions finishes setup for iterator pIter itself. | |||
| 13512 | */ | |||
| 13513 | static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){ | |||
| 13514 | int iIter; | |||
| 13515 | for(iIter=pIter->nSeg-1; iIter>0; iIter--){ | |||
| 13516 | int iEq; | |||
| 13517 | if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){ | |||
| 13518 | Fts5SegIter *pSeg = &pIter->aSeg[iEq]; | |||
| 13519 | if( p->rc==SQLITE_OK0 ) pSeg->xNext(p, pSeg, 0); | |||
| 13520 | fts5MultiIterAdvanced(p, pIter, iEq, iIter); | |||
| 13521 | } | |||
| 13522 | } | |||
| 13523 | fts5MultiIterSetEof(pIter); | |||
| 13524 | fts5AssertMultiIterSetup(p, pIter); | |||
| 13525 | ||||
| 13526 | if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter)) | |||
| 13527 | || fts5MultiIterIsDeleted(pIter) | |||
| 13528 | ){ | |||
| 13529 | fts5MultiIterNext(p, pIter, 0, 0); | |||
| 13530 | }else if( pIter->base.bEof==0 ){ | |||
| 13531 | Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; | |||
| 13532 | pIter->xSetOutputs(pIter, pSeg); | |||
| 13533 | } | |||
| 13534 | } | |||
| 13535 | ||||
| 13536 | /* | |||
| 13537 | ** Allocate a new Fts5Iter object. | |||
| 13538 | ** | |||
| 13539 | ** The new object will be used to iterate through data in structure pStruct. | |||
| 13540 | ** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel | |||
| 13541 | ** is zero or greater, data from the first nSegment segments on level iLevel | |||
| 13542 | ** is merged. | |||
| 13543 | ** | |||
| 13544 | ** The iterator initially points to the first term/rowid entry in the | |||
| 13545 | ** iterated data. | |||
| 13546 | */ | |||
| 13547 | static void fts5MultiIterNew( | |||
| 13548 | Fts5Index *p, /* FTS5 backend to iterate within */ | |||
| 13549 | Fts5Structure *pStruct, /* Structure of specific index */ | |||
| 13550 | int flags, /* FTS5INDEX_QUERY_XXX flags */ | |||
| 13551 | Fts5Colset *pColset, /* Colset to filter on (or NULL) */ | |||
| 13552 | const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */ | |||
| 13553 | int iLevel, /* Level to iterate (-1 for all) */ | |||
| 13554 | int nSegment, /* Number of segments to merge (iLevel>=0) */ | |||
| 13555 | Fts5Iter **ppOut /* New object */ | |||
| 13556 | ){ | |||
| 13557 | int nSeg = 0; /* Number of segment-iters in use */ | |||
| 13558 | int iIter = 0; /* */ | |||
| 13559 | int iSeg; /* Used to iterate through segments */ | |||
| 13560 | Fts5StructureLevel *pLvl; | |||
| 13561 | Fts5Iter *pNew; | |||
| 13562 | ||||
| 13563 | assert( (pTerm==0 && nTerm==0) || iLevel<0 )((void) (0)); | |||
| 13564 | ||||
| 13565 | /* Allocate space for the new multi-seg-iterator. */ | |||
| 13566 | if( p->rc==SQLITE_OK0 ){ | |||
| 13567 | if( iLevel<0 ){ | |||
| 13568 | assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) )((void) (0)); | |||
| 13569 | nSeg = pStruct->nSegment; | |||
| 13570 | nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH0x0040)); | |||
| 13571 | }else{ | |||
| 13572 | nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment)(((pStruct->aLevel[iLevel].nSeg) < (nSegment)) ? (pStruct ->aLevel[iLevel].nSeg) : (nSegment)); | |||
| 13573 | } | |||
| 13574 | } | |||
| 13575 | *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); | |||
| 13576 | if( pNew==0 ){ | |||
| 13577 | assert( p->rc!=SQLITE_OK )((void) (0)); | |||
| 13578 | goto fts5MultiIterNew_post_check; | |||
| 13579 | } | |||
| 13580 | pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC0x0002)); | |||
| 13581 | pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY0x0010)); | |||
| 13582 | pNew->pColset = pColset; | |||
| 13583 | if( (flags & FTS5INDEX_QUERY_NOOUTPUT0x0020)==0 ){ | |||
| 13584 | fts5IterSetOutputCb(&p->rc, pNew); | |||
| 13585 | } | |||
| 13586 | ||||
| 13587 | /* Initialize each of the component segment iterators. */ | |||
| 13588 | if( p->rc==SQLITE_OK0 ){ | |||
| 13589 | if( iLevel<0 ){ | |||
| 13590 | Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; | |||
| 13591 | if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH0x0040) ){ | |||
| 13592 | /* Add a segment iterator for the current contents of the hash table. */ | |||
| 13593 | Fts5SegIter *pIter = &pNew->aSeg[iIter++]; | |||
| 13594 | fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); | |||
| 13595 | } | |||
| 13596 | for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){ | |||
| 13597 | for(iSeg=pLvl->nSeg-1; iSeg>=0; iSeg--){ | |||
| 13598 | Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; | |||
| 13599 | Fts5SegIter *pIter = &pNew->aSeg[iIter++]; | |||
| 13600 | if( pTerm==0 ){ | |||
| 13601 | fts5SegIterInit(p, pSeg, pIter); | |||
| 13602 | }else{ | |||
| 13603 | fts5SegIterSeekInit(p, pTerm, nTerm, flags, pSeg, pIter); | |||
| 13604 | } | |||
| 13605 | } | |||
| 13606 | } | |||
| 13607 | }else{ | |||
| 13608 | pLvl = &pStruct->aLevel[iLevel]; | |||
| 13609 | for(iSeg=nSeg-1; iSeg>=0; iSeg--){ | |||
| 13610 | fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]); | |||
| 13611 | } | |||
| 13612 | } | |||
| 13613 | assert( iIter==nSeg )((void) (0)); | |||
| 13614 | } | |||
| 13615 | ||||
| 13616 | /* If the above was successful, each component iterator now points | |||
| 13617 | ** to the first entry in its segment. In this case initialize the | |||
| 13618 | ** aFirst[] array. Or, if an error has occurred, free the iterator | |||
| 13619 | ** object and set the output variable to NULL. */ | |||
| 13620 | if( p->rc==SQLITE_OK0 ){ | |||
| 13621 | fts5MultiIterFinishSetup(p, pNew); | |||
| 13622 | }else{ | |||
| 13623 | fts5MultiIterFree(pNew); | |||
| 13624 | *ppOut = 0; | |||
| 13625 | } | |||
| 13626 | ||||
| 13627 | fts5MultiIterNew_post_check: | |||
| 13628 | assert( (*ppOut)!=0 || p->rc!=SQLITE_OK )((void) (0)); | |||
| 13629 | return; | |||
| 13630 | } | |||
| 13631 | ||||
| 13632 | /* | |||
| 13633 | ** Create an Fts5Iter that iterates through the doclist provided | |||
| 13634 | ** as the second argument. | |||
| 13635 | */ | |||
| 13636 | static void fts5MultiIterNew2( | |||
| 13637 | Fts5Index *p, /* FTS5 backend to iterate within */ | |||
| 13638 | Fts5Data *pData, /* Doclist to iterate through */ | |||
| 13639 | int bDesc, /* True for descending rowid order */ | |||
| 13640 | Fts5Iter **ppOut /* New object */ | |||
| 13641 | ){ | |||
| 13642 | Fts5Iter *pNew; | |||
| 13643 | pNew = fts5MultiIterAlloc(p, 2); | |||
| 13644 | if( pNew ){ | |||
| 13645 | Fts5SegIter *pIter = &pNew->aSeg[1]; | |||
| 13646 | pIter->flags = FTS5_SEGITER_ONETERM0x01; | |||
| 13647 | if( pData->szLeaf>0 ){ | |||
| 13648 | pIter->pLeaf = pData; | |||
| 13649 | pIter->iLeafOffset = fts5GetVarintsqlite3Fts5GetVarint(pData->p, (u64*)&pIter->iRowid); | |||
| 13650 | pIter->iEndofDoclist = pData->nn; | |||
| 13651 | pNew->aFirst[1].iFirst = 1; | |||
| 13652 | if( bDesc ){ | |||
| 13653 | pNew->bRev = 1; | |||
| 13654 | pIter->flags |= FTS5_SEGITER_REVERSE0x02; | |||
| 13655 | fts5SegIterReverseInitPage(p, pIter); | |||
| 13656 | }else{ | |||
| 13657 | fts5SegIterLoadNPos(p, pIter); | |||
| 13658 | } | |||
| 13659 | pData = 0; | |||
| 13660 | }else{ | |||
| 13661 | pNew->base.bEof = 1; | |||
| 13662 | } | |||
| 13663 | fts5SegIterSetNext(p, pIter); | |||
| 13664 | ||||
| 13665 | *ppOut = pNew; | |||
| 13666 | } | |||
| 13667 | ||||
| 13668 | fts5DataRelease(pData); | |||
| 13669 | } | |||
| 13670 | ||||
| 13671 | /* | |||
| 13672 | ** Return true if the iterator is at EOF or if an error has occurred. | |||
| 13673 | ** False otherwise. | |||
| 13674 | */ | |||
| 13675 | static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){ | |||
| 13676 | assert( pIter!=0 || p->rc!=SQLITE_OK )((void) (0)); | |||
| 13677 | assert( p->rc!=SQLITE_OK((void) (0)) | |||
| 13678 | || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof((void) (0)) | |||
| 13679 | )((void) (0)); | |||
| 13680 | return (p->rc || pIter->base.bEof); | |||
| 13681 | } | |||
| 13682 | ||||
| 13683 | /* | |||
| 13684 | ** Return the rowid of the entry that the iterator currently points | |||
| 13685 | ** to. If the iterator points to EOF when this function is called the | |||
| 13686 | ** results are undefined. | |||
| 13687 | */ | |||
| 13688 | static i64 fts5MultiIterRowid(Fts5Iter *pIter){ | |||
| 13689 | assert( pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf )((void) (0)); | |||
| 13690 | return pIter->aSeg[ pIter->aFirst[1].iFirst ].iRowid; | |||
| 13691 | } | |||
| 13692 | ||||
| 13693 | /* | |||
| 13694 | ** Move the iterator to the next entry at or following iMatch. | |||
| 13695 | */ | |||
| 13696 | static void fts5MultiIterNextFrom( | |||
| 13697 | Fts5Index *p, | |||
| 13698 | Fts5Iter *pIter, | |||
| 13699 | i64 iMatch | |||
| 13700 | ){ | |||
| 13701 | while( 1 ){ | |||
| 13702 | i64 iRowid; | |||
| 13703 | fts5MultiIterNext(p, pIter, 1, iMatch); | |||
| 13704 | if( fts5MultiIterEof(p, pIter) ) break; | |||
| 13705 | iRowid = fts5MultiIterRowid(pIter); | |||
| 13706 | if( pIter->bRev==0 && iRowid>=iMatch ) break; | |||
| 13707 | if( pIter->bRev!=0 && iRowid<=iMatch ) break; | |||
| 13708 | } | |||
| 13709 | } | |||
| 13710 | ||||
| 13711 | /* | |||
| 13712 | ** Return a pointer to a buffer containing the term associated with the | |||
| 13713 | ** entry that the iterator currently points to. | |||
| 13714 | */ | |||
| 13715 | static const u8 *fts5MultiIterTerm(Fts5Iter *pIter, int *pn){ | |||
| 13716 | Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; | |||
| 13717 | *pn = p->term.n; | |||
| 13718 | return p->term.p; | |||
| 13719 | } | |||
| 13720 | ||||
| 13721 | /* | |||
| 13722 | ** Allocate a new segment-id for the structure pStruct. The new segment | |||
| 13723 | ** id must be between 1 and 65335 inclusive, and must not be used by | |||
| 13724 | ** any currently existing segment. If a free segment id cannot be found, | |||
| 13725 | ** SQLITE_FULL is returned. | |||
| 13726 | ** | |||
| 13727 | ** If an error has already occurred, this function is a no-op. 0 is | |||
| 13728 | ** returned in this case. | |||
| 13729 | */ | |||
| 13730 | static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){ | |||
| 13731 | int iSegid = 0; | |||
| 13732 | ||||
| 13733 | if( p->rc==SQLITE_OK0 ){ | |||
| 13734 | if( pStruct->nSegment>=FTS5_MAX_SEGMENT2000 ){ | |||
| 13735 | p->rc = SQLITE_FULL13; | |||
| 13736 | }else{ | |||
| 13737 | /* FTS5_MAX_SEGMENT is currently defined as 2000. So the following | |||
| 13738 | ** array is 63 elements, or 252 bytes, in size. */ | |||
| 13739 | u32 aUsed[(FTS5_MAX_SEGMENT2000+31) / 32]; | |||
| 13740 | int iLvl, iSeg; | |||
| 13741 | int i; | |||
| 13742 | u32 mask; | |||
| 13743 | memset(aUsed, 0, sizeof(aUsed)); | |||
| 13744 | for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ | |||
| 13745 | for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){ | |||
| 13746 | int iId = pStruct->aLevel[iLvl].aSeg[iSeg].iSegid; | |||
| 13747 | if( iId<=FTS5_MAX_SEGMENT2000 && iId>0 ){ | |||
| 13748 | aUsed[(iId-1) / 32] |= (u32)1 << ((iId-1) % 32); | |||
| 13749 | } | |||
| 13750 | } | |||
| 13751 | } | |||
| 13752 | ||||
| 13753 | for(i=0; aUsed[i]==0xFFFFFFFF; i++); | |||
| 13754 | mask = aUsed[i]; | |||
| 13755 | for(iSegid=0; mask & ((u32)1 << iSegid); iSegid++); | |||
| 13756 | iSegid += 1 + i*32; | |||
| 13757 | ||||
| 13758 | #ifdef SQLITE_DEBUG | |||
| 13759 | for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ | |||
| 13760 | for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){ | |||
| 13761 | assert_nc( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid )((void) (0)); | |||
| 13762 | } | |||
| 13763 | } | |||
| 13764 | assert_nc( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT )((void) (0)); | |||
| 13765 | ||||
| 13766 | { | |||
| 13767 | sqlite3_stmt *pIdxSelect = fts5IdxSelectStmt(p); | |||
| 13768 | if( p->rc==SQLITE_OK0 ){ | |||
| 13769 | u8 aBlob[2] = {0xff, 0xff}; | |||
| 13770 | sqlite3_bind_intsqlite3_api->bind_int(pIdxSelect, 1, iSegid); | |||
| 13771 | sqlite3_bind_blobsqlite3_api->bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 13772 | assert_nc( sqlite3_step(pIdxSelect)!=SQLITE_ROW )((void) (0)); | |||
| 13773 | p->rc = sqlite3_resetsqlite3_api->reset(pIdxSelect); | |||
| 13774 | sqlite3_bind_nullsqlite3_api->bind_null(pIdxSelect, 2); | |||
| 13775 | } | |||
| 13776 | } | |||
| 13777 | #endif | |||
| 13778 | } | |||
| 13779 | } | |||
| 13780 | ||||
| 13781 | return iSegid; | |||
| 13782 | } | |||
| 13783 | ||||
| 13784 | /* | |||
| 13785 | ** Discard all data currently cached in the hash-tables. | |||
| 13786 | */ | |||
| 13787 | static void fts5IndexDiscardData(Fts5Index *p){ | |||
| 13788 | assert( p->pHash || p->nPendingData==0 )((void) (0)); | |||
| 13789 | if( p->pHash ){ | |||
| 13790 | sqlite3Fts5HashClear(p->pHash); | |||
| 13791 | p->nPendingData = 0; | |||
| 13792 | p->nPendingRow = 0; | |||
| 13793 | p->flushRc = SQLITE_OK0; | |||
| 13794 | } | |||
| 13795 | p->nContentlessDelete = 0; | |||
| 13796 | } | |||
| 13797 | ||||
| 13798 | /* | |||
| 13799 | ** Return the size of the prefix, in bytes, that buffer | |||
| 13800 | ** (pNew/<length-unknown>) shares with buffer (pOld/nOld). | |||
| 13801 | ** | |||
| 13802 | ** Buffer (pNew/<length-unknown>) is guaranteed to be greater | |||
| 13803 | ** than buffer (pOld/nOld). | |||
| 13804 | */ | |||
| 13805 | static int fts5PrefixCompress(int nOld, const u8 *pOld, const u8 *pNew){ | |||
| 13806 | int i; | |||
| 13807 | for(i=0; i<nOld; i++){ | |||
| 13808 | if( pOld[i]!=pNew[i] ) break; | |||
| 13809 | } | |||
| 13810 | return i; | |||
| 13811 | } | |||
| 13812 | ||||
| 13813 | static void fts5WriteDlidxClear( | |||
| 13814 | Fts5Index *p, | |||
| 13815 | Fts5SegWriter *pWriter, | |||
| 13816 | int bFlush /* If true, write dlidx to disk */ | |||
| 13817 | ){ | |||
| 13818 | int i; | |||
| 13819 | assert( bFlush==0 || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n>0) )((void) (0)); | |||
| 13820 | for(i=0; i<pWriter->nDlidx; i++){ | |||
| 13821 | Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[i]; | |||
| 13822 | if( pDlidx->buf.n==0 ) break; | |||
| 13823 | if( bFlush ){ | |||
| 13824 | assert( pDlidx->pgno!=0 )((void) (0)); | |||
| 13825 | fts5DataWrite(p, | |||
| 13826 | FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno)( ((i64)(pWriter->iSegid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(i) << (31)) + ((i64)(pDlidx ->pgno)) ), | |||
| 13827 | pDlidx->buf.p, pDlidx->buf.n | |||
| 13828 | ); | |||
| 13829 | } | |||
| 13830 | sqlite3Fts5BufferZero(&pDlidx->buf); | |||
| 13831 | pDlidx->bPrevValid = 0; | |||
| 13832 | } | |||
| 13833 | } | |||
| 13834 | ||||
| 13835 | /* | |||
| 13836 | ** Grow the pWriter->aDlidx[] array to at least nLvl elements in size. | |||
| 13837 | ** Any new array elements are zeroed before returning. | |||
| 13838 | */ | |||
| 13839 | static int fts5WriteDlidxGrow( | |||
| 13840 | Fts5Index *p, | |||
| 13841 | Fts5SegWriter *pWriter, | |||
| 13842 | int nLvl | |||
| 13843 | ){ | |||
| 13844 | if( p->rc==SQLITE_OK0 && nLvl>=pWriter->nDlidx ){ | |||
| 13845 | Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc64sqlite3_api->realloc64( | |||
| 13846 | pWriter->aDlidx, sizeof(Fts5DlidxWriter) * nLvl | |||
| 13847 | ); | |||
| 13848 | if( aDlidx==0 ){ | |||
| 13849 | p->rc = SQLITE_NOMEM7; | |||
| 13850 | }else{ | |||
| 13851 | size_t nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx); | |||
| 13852 | memset(&aDlidx[pWriter->nDlidx], 0, nByte); | |||
| 13853 | pWriter->aDlidx = aDlidx; | |||
| 13854 | pWriter->nDlidx = nLvl; | |||
| 13855 | } | |||
| 13856 | } | |||
| 13857 | return p->rc; | |||
| 13858 | } | |||
| 13859 | ||||
| 13860 | /* | |||
| 13861 | ** If the current doclist-index accumulating in pWriter->aDlidx[] is large | |||
| 13862 | ** enough, flush it to disk and return 1. Otherwise discard it and return | |||
| 13863 | ** zero. | |||
| 13864 | */ | |||
| 13865 | static int fts5WriteFlushDlidx(Fts5Index *p, Fts5SegWriter *pWriter){ | |||
| 13866 | int bFlag = 0; | |||
| 13867 | ||||
| 13868 | /* If there were FTS5_MIN_DLIDX_SIZE or more empty leaf pages written | |||
| 13869 | ** to the database, also write the doclist-index to disk. */ | |||
| 13870 | if( pWriter->aDlidx[0].buf.n>0 && pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE4 ){ | |||
| 13871 | bFlag = 1; | |||
| 13872 | } | |||
| 13873 | fts5WriteDlidxClear(p, pWriter, bFlag); | |||
| 13874 | pWriter->nEmpty = 0; | |||
| 13875 | return bFlag; | |||
| 13876 | } | |||
| 13877 | ||||
| 13878 | /* | |||
| 13879 | ** This function is called whenever processing of the doclist for the | |||
| 13880 | ** last term on leaf page (pWriter->iBtPage) is completed. | |||
| 13881 | ** | |||
| 13882 | ** The doclist-index for that term is currently stored in-memory within the | |||
| 13883 | ** Fts5SegWriter.aDlidx[] array. If it is large enough, this function | |||
| 13884 | ** writes it out to disk. Or, if it is too small to bother with, discards | |||
| 13885 | ** it. | |||
| 13886 | ** | |||
| 13887 | ** Fts5SegWriter.btterm currently contains the first term on page iBtPage. | |||
| 13888 | */ | |||
| 13889 | static void fts5WriteFlushBtree(Fts5Index *p, Fts5SegWriter *pWriter){ | |||
| 13890 | int bFlag; | |||
| 13891 | ||||
| 13892 | assert( pWriter->iBtPage || pWriter->nEmpty==0 )((void) (0)); | |||
| 13893 | if( pWriter->iBtPage==0 ) return; | |||
| 13894 | bFlag = fts5WriteFlushDlidx(p, pWriter); | |||
| 13895 | ||||
| 13896 | if( p->rc==SQLITE_OK0 ){ | |||
| 13897 | const char *z = (pWriter->btterm.n>0?(const char*)pWriter->btterm.p:""); | |||
| 13898 | /* The following was already done in fts5WriteInit(): */ | |||
| 13899 | /* sqlite3_bind_int(p->pIdxWriter, 1, pWriter->iSegid); */ | |||
| 13900 | sqlite3_bind_blobsqlite3_api->bind_blob(p->pIdxWriter, 2, z, pWriter->btterm.n, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 13901 | sqlite3_bind_int64sqlite3_api->bind_int64(p->pIdxWriter, 3, bFlag + ((i64)pWriter->iBtPage<<1)); | |||
| 13902 | sqlite3_stepsqlite3_api->step(p->pIdxWriter); | |||
| 13903 | p->rc = sqlite3_resetsqlite3_api->reset(p->pIdxWriter); | |||
| 13904 | sqlite3_bind_nullsqlite3_api->bind_null(p->pIdxWriter, 2); | |||
| 13905 | } | |||
| 13906 | pWriter->iBtPage = 0; | |||
| 13907 | } | |||
| 13908 | ||||
| 13909 | /* | |||
| 13910 | ** This is called once for each leaf page except the first that contains | |||
| 13911 | ** at least one term. Argument (nTerm/pTerm) is the split-key - a term that | |||
| 13912 | ** is larger than all terms written to earlier leaves, and equal to or | |||
| 13913 | ** smaller than the first term on the new leaf. | |||
| 13914 | ** | |||
| 13915 | ** If an error occurs, an error code is left in Fts5Index.rc. If an error | |||
| 13916 | ** has already occurred when this function is called, it is a no-op. | |||
| 13917 | */ | |||
| 13918 | static void fts5WriteBtreeTerm( | |||
| 13919 | Fts5Index *p, /* FTS5 backend object */ | |||
| 13920 | Fts5SegWriter *pWriter, /* Writer object */ | |||
| 13921 | int nTerm, const u8 *pTerm /* First term on new page */ | |||
| 13922 | ){ | |||
| 13923 | fts5WriteFlushBtree(p, pWriter); | |||
| 13924 | if( p->rc==SQLITE_OK0 ){ | |||
| 13925 | fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm)sqlite3Fts5BufferSet(&p->rc,&pWriter->btterm,nTerm ,pTerm); | |||
| 13926 | pWriter->iBtPage = pWriter->writer.pgno; | |||
| 13927 | } | |||
| 13928 | } | |||
| 13929 | ||||
| 13930 | /* | |||
| 13931 | ** This function is called when flushing a leaf page that contains no | |||
| 13932 | ** terms at all to disk. | |||
| 13933 | */ | |||
| 13934 | static void fts5WriteBtreeNoTerm( | |||
| 13935 | Fts5Index *p, /* FTS5 backend object */ | |||
| 13936 | Fts5SegWriter *pWriter /* Writer object */ | |||
| 13937 | ){ | |||
| 13938 | /* If there were no rowids on the leaf page either and the doclist-index | |||
| 13939 | ** has already been started, append an 0x00 byte to it. */ | |||
| 13940 | if( pWriter->bFirstRowidInPage && pWriter->aDlidx[0].buf.n>0 ){ | |||
| 13941 | Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[0]; | |||
| 13942 | assert( pDlidx->bPrevValid )((void) (0)); | |||
| 13943 | sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, 0); | |||
| 13944 | } | |||
| 13945 | ||||
| 13946 | /* Increment the "number of sequential leaves without a term" counter. */ | |||
| 13947 | pWriter->nEmpty++; | |||
| 13948 | } | |||
| 13949 | ||||
| 13950 | static i64 fts5DlidxExtractFirstRowid(Fts5Buffer *pBuf){ | |||
| 13951 | i64 iRowid; | |||
| 13952 | int iOff; | |||
| 13953 | ||||
| 13954 | iOff = 1 + fts5GetVarintsqlite3Fts5GetVarint(&pBuf->p[1], (u64*)&iRowid); | |||
| 13955 | fts5GetVarintsqlite3Fts5GetVarint(&pBuf->p[iOff], (u64*)&iRowid); | |||
| 13956 | return iRowid; | |||
| 13957 | } | |||
| 13958 | ||||
| 13959 | /* | |||
| 13960 | ** Rowid iRowid has just been appended to the current leaf page. It is the | |||
| 13961 | ** first on the page. This function appends an appropriate entry to the current | |||
| 13962 | ** doclist-index. | |||
| 13963 | */ | |||
| 13964 | static void fts5WriteDlidxAppend( | |||
| 13965 | Fts5Index *p, | |||
| 13966 | Fts5SegWriter *pWriter, | |||
| 13967 | i64 iRowid | |||
| 13968 | ){ | |||
| 13969 | int i; | |||
| 13970 | int bDone = 0; | |||
| 13971 | ||||
| 13972 | for(i=0; p->rc==SQLITE_OK0 && bDone==0; i++){ | |||
| 13973 | i64 iVal; | |||
| 13974 | Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[i]; | |||
| 13975 | ||||
| 13976 | if( pDlidx->buf.n>=p->pConfig->pgsz ){ | |||
| 13977 | /* The current doclist-index page is full. Write it to disk and push | |||
| 13978 | ** a copy of iRowid (which will become the first rowid on the next | |||
| 13979 | ** doclist-index leaf page) up into the next level of the b-tree | |||
| 13980 | ** hierarchy. If the node being flushed is currently the root node, | |||
| 13981 | ** also push its first rowid upwards. */ | |||
| 13982 | pDlidx->buf.p[0] = 0x01; /* Not the root node */ | |||
| 13983 | fts5DataWrite(p, | |||
| 13984 | FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno)( ((i64)(pWriter->iSegid) << (31 +5 +1)) + ((i64)(1) << (31 + 5)) + ((i64)(i) << (31)) + ((i64)(pDlidx ->pgno)) ), | |||
| 13985 | pDlidx->buf.p, pDlidx->buf.n | |||
| 13986 | ); | |||
| 13987 | fts5WriteDlidxGrow(p, pWriter, i+2); | |||
| 13988 | pDlidx = &pWriter->aDlidx[i]; | |||
| 13989 | if( p->rc==SQLITE_OK0 && pDlidx[1].buf.n==0 ){ | |||
| 13990 | i64 iFirst = fts5DlidxExtractFirstRowid(&pDlidx->buf); | |||
| 13991 | ||||
| 13992 | /* This was the root node. Push its first rowid up to the new root. */ | |||
| 13993 | pDlidx[1].pgno = pDlidx->pgno; | |||
| 13994 | sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, 0); | |||
| 13995 | sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, pDlidx->pgno); | |||
| 13996 | sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, iFirst); | |||
| 13997 | pDlidx[1].bPrevValid = 1; | |||
| 13998 | pDlidx[1].iPrev = iFirst; | |||
| 13999 | } | |||
| 14000 | ||||
| 14001 | sqlite3Fts5BufferZero(&pDlidx->buf); | |||
| 14002 | pDlidx->bPrevValid = 0; | |||
| 14003 | pDlidx->pgno++; | |||
| 14004 | }else{ | |||
| 14005 | bDone = 1; | |||
| 14006 | } | |||
| 14007 | ||||
| 14008 | if( pDlidx->bPrevValid ){ | |||
| 14009 | iVal = (u64)iRowid - (u64)pDlidx->iPrev; | |||
| 14010 | }else{ | |||
| 14011 | i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno); | |||
| 14012 | assert( pDlidx->buf.n==0 )((void) (0)); | |||
| 14013 | sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone); | |||
| 14014 | sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno); | |||
| 14015 | iVal = iRowid; | |||
| 14016 | } | |||
| 14017 | ||||
| 14018 | sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iVal); | |||
| 14019 | pDlidx->bPrevValid = 1; | |||
| 14020 | pDlidx->iPrev = iRowid; | |||
| 14021 | } | |||
| 14022 | } | |||
| 14023 | ||||
| 14024 | static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){ | |||
| 14025 | static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 }; | |||
| 14026 | Fts5PageWriter *pPage = &pWriter->writer; | |||
| 14027 | i64 iRowid; | |||
| 14028 | ||||
| 14029 | assert( (pPage->pgidx.n==0)==(pWriter->bFirstTermInPage) )((void) (0)); | |||
| 14030 | ||||
| 14031 | /* Set the szLeaf header field. */ | |||
| 14032 | assert( 0==fts5GetU16(&pPage->buf.p[2]) )((void) (0)); | |||
| 14033 | fts5PutU16(&pPage->buf.p[2], (u16)pPage->buf.n); | |||
| 14034 | ||||
| 14035 | if( pWriter->bFirstTermInPage ){ | |||
| 14036 | /* No term was written to this page. */ | |||
| 14037 | assert( pPage->pgidx.n==0 )((void) (0)); | |||
| 14038 | fts5WriteBtreeNoTerm(p, pWriter); | |||
| 14039 | }else{ | |||
| 14040 | /* Append the pgidx to the page buffer. Set the szLeaf header field. */ | |||
| 14041 | fts5BufferAppendBlob(&p->rc, &pPage->buf, pPage->pgidx.n, pPage->pgidx.p)sqlite3Fts5BufferAppendBlob(&p->rc,&pPage->buf, pPage->pgidx.n,pPage->pgidx.p); | |||
| 14042 | } | |||
| 14043 | ||||
| 14044 | /* Write the page out to disk */ | |||
| 14045 | iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, pPage->pgno)( ((i64)(pWriter->iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pPage ->pgno)) ); | |||
| 14046 | fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n); | |||
| 14047 | ||||
| 14048 | /* Initialize the next page. */ | |||
| 14049 | fts5BufferZero(&pPage->buf)sqlite3Fts5BufferZero(&pPage->buf); | |||
| 14050 | fts5BufferZero(&pPage->pgidx)sqlite3Fts5BufferZero(&pPage->pgidx); | |||
| 14051 | fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero)sqlite3Fts5BufferAppendBlob(&p->rc,&pPage->buf, 4,zero); | |||
| 14052 | pPage->iPrevPgidx = 0; | |||
| 14053 | pPage->pgno++; | |||
| 14054 | ||||
| 14055 | /* Increase the leaves written counter */ | |||
| 14056 | pWriter->nLeafWritten++; | |||
| 14057 | ||||
| 14058 | /* The new leaf holds no terms or rowids */ | |||
| 14059 | pWriter->bFirstTermInPage = 1; | |||
| 14060 | pWriter->bFirstRowidInPage = 1; | |||
| 14061 | } | |||
| 14062 | ||||
| 14063 | /* | |||
| 14064 | ** Append term pTerm/nTerm to the segment being written by the writer passed | |||
| 14065 | ** as the second argument. | |||
| 14066 | ** | |||
| 14067 | ** If an error occurs, set the Fts5Index.rc error code. If an error has | |||
| 14068 | ** already occurred, this function is a no-op. | |||
| 14069 | */ | |||
| 14070 | static void fts5WriteAppendTerm( | |||
| 14071 | Fts5Index *p, | |||
| 14072 | Fts5SegWriter *pWriter, | |||
| 14073 | int nTerm, const u8 *pTerm | |||
| 14074 | ){ | |||
| 14075 | int nPrefix; /* Bytes of prefix compression for term */ | |||
| 14076 | Fts5PageWriter *pPage = &pWriter->writer; | |||
| 14077 | Fts5Buffer *pPgidx = &pWriter->writer.pgidx; | |||
| 14078 | int nMin = MIN(pPage->term.n, nTerm)(((pPage->term.n) < (nTerm)) ? (pPage->term.n) : (nTerm )); | |||
| 14079 | ||||
| 14080 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 14081 | assert( pPage->buf.n>=4 )((void) (0)); | |||
| 14082 | assert( pPage->buf.n>4 || pWriter->bFirstTermInPage )((void) (0)); | |||
| 14083 | ||||
| 14084 | /* If the current leaf page is full, flush it to disk. */ | |||
| 14085 | if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){ | |||
| 14086 | if( pPage->buf.n>4 ){ | |||
| 14087 | fts5WriteFlushLeaf(p, pWriter); | |||
| 14088 | if( p->rc!=SQLITE_OK0 ) return; | |||
| 14089 | } | |||
| 14090 | fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING)( (u32)((&pPage->buf)->n) + (u32)(nTerm+20) <= ( u32)((&pPage->buf)->nSpace) ? 0 : sqlite3Fts5BufferSize ((&p->rc),(&pPage->buf),(nTerm+20)+(&pPage-> buf)->n) ); | |||
| 14091 | } | |||
| 14092 | ||||
| 14093 | /* TODO1: Updating pgidx here. */ | |||
| 14094 | pPgidx->n += sqlite3Fts5PutVarint( | |||
| 14095 | &pPgidx->p[pPgidx->n], pPage->buf.n - pPage->iPrevPgidx | |||
| 14096 | ); | |||
| 14097 | pPage->iPrevPgidx = pPage->buf.n; | |||
| 14098 | #if 0 | |||
| 14099 | fts5PutU16(&pPgidx->p[pPgidx->n], pPage->buf.n); | |||
| 14100 | pPgidx->n += 2; | |||
| 14101 | #endif | |||
| 14102 | ||||
| 14103 | if( pWriter->bFirstTermInPage ){ | |||
| 14104 | nPrefix = 0; | |||
| 14105 | if( pPage->pgno!=1 ){ | |||
| 14106 | /* This is the first term on a leaf that is not the leftmost leaf in | |||
| 14107 | ** the segment b-tree. In this case it is necessary to add a term to | |||
| 14108 | ** the b-tree hierarchy that is (a) larger than the largest term | |||
| 14109 | ** already written to the segment and (b) smaller than or equal to | |||
| 14110 | ** this term. In other words, a prefix of (pTerm/nTerm) that is one | |||
| 14111 | ** byte longer than the longest prefix (pTerm/nTerm) shares with the | |||
| 14112 | ** previous term. | |||
| 14113 | ** | |||
| 14114 | ** Usually, the previous term is available in pPage->term. The exception | |||
| 14115 | ** is if this is the first term written in an incremental-merge step. | |||
| 14116 | ** In this case the previous term is not available, so just write a | |||
| 14117 | ** copy of (pTerm/nTerm) into the parent node. This is slightly | |||
| 14118 | ** inefficient, but still correct. */ | |||
| 14119 | int n = nTerm; | |||
| 14120 | if( pPage->term.n ){ | |||
| 14121 | n = 1 + fts5PrefixCompress(nMin, pPage->term.p, pTerm); | |||
| 14122 | } | |||
| 14123 | fts5WriteBtreeTerm(p, pWriter, n, pTerm); | |||
| 14124 | if( p->rc!=SQLITE_OK0 ) return; | |||
| 14125 | pPage = &pWriter->writer; | |||
| 14126 | } | |||
| 14127 | }else{ | |||
| 14128 | nPrefix = fts5PrefixCompress(nMin, pPage->term.p, pTerm); | |||
| 14129 | fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix)sqlite3Fts5BufferAppendVarint(&p->rc,&pPage->buf ,(i64)nPrefix); | |||
| 14130 | } | |||
| 14131 | ||||
| 14132 | /* Append the number of bytes of new data, then the term data itself | |||
| 14133 | ** to the page. */ | |||
| 14134 | fts5BufferAppendVarint(&p->rc, &pPage->buf, nTerm - nPrefix)sqlite3Fts5BufferAppendVarint(&p->rc,&pPage->buf ,(i64)nTerm - nPrefix); | |||
| 14135 | fts5BufferAppendBlob(&p->rc, &pPage->buf, nTerm - nPrefix, &pTerm[nPrefix])sqlite3Fts5BufferAppendBlob(&p->rc,&pPage->buf, nTerm - nPrefix,&pTerm[nPrefix]); | |||
| 14136 | ||||
| 14137 | /* Update the Fts5PageWriter.term field. */ | |||
| 14138 | fts5BufferSet(&p->rc, &pPage->term, nTerm, pTerm)sqlite3Fts5BufferSet(&p->rc,&pPage->term,nTerm, pTerm); | |||
| 14139 | pWriter->bFirstTermInPage = 0; | |||
| 14140 | ||||
| 14141 | pWriter->bFirstRowidInPage = 0; | |||
| 14142 | pWriter->bFirstRowidInDoclist = 1; | |||
| 14143 | ||||
| 14144 | assert( p->rc || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n==0) )((void) (0)); | |||
| 14145 | pWriter->aDlidx[0].pgno = pPage->pgno; | |||
| 14146 | } | |||
| 14147 | ||||
| 14148 | /* | |||
| 14149 | ** Append a rowid and position-list size field to the writers output. | |||
| 14150 | */ | |||
| 14151 | static void fts5WriteAppendRowid( | |||
| 14152 | Fts5Index *p, | |||
| 14153 | Fts5SegWriter *pWriter, | |||
| 14154 | i64 iRowid | |||
| 14155 | ){ | |||
| 14156 | if( p->rc==SQLITE_OK0 ){ | |||
| 14157 | Fts5PageWriter *pPage = &pWriter->writer; | |||
| 14158 | ||||
| 14159 | if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){ | |||
| 14160 | fts5WriteFlushLeaf(p, pWriter); | |||
| 14161 | } | |||
| 14162 | ||||
| 14163 | /* If this is to be the first rowid written to the page, set the | |||
| 14164 | ** rowid-pointer in the page-header. Also append a value to the dlidx | |||
| 14165 | ** buffer, in case a doclist-index is required. */ | |||
| 14166 | if( pWriter->bFirstRowidInPage ){ | |||
| 14167 | fts5PutU16(pPage->buf.p, (u16)pPage->buf.n); | |||
| 14168 | fts5WriteDlidxAppend(p, pWriter, iRowid); | |||
| 14169 | } | |||
| 14170 | ||||
| 14171 | /* Write the rowid. */ | |||
| 14172 | if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){ | |||
| 14173 | fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid)sqlite3Fts5BufferAppendVarint(&p->rc,&pPage->buf ,(i64)iRowid); | |||
| 14174 | }else{ | |||
| 14175 | assert_nc( p->rc || iRowid>pWriter->iPrevRowid )((void) (0)); | |||
| 14176 | fts5BufferAppendVarint(&p->rc, &pPage->buf,sqlite3Fts5BufferAppendVarint(&p->rc,&pPage->buf ,(i64)(u64)iRowid - (u64)pWriter->iPrevRowid) | |||
| 14177 | (u64)iRowid - (u64)pWriter->iPrevRowidsqlite3Fts5BufferAppendVarint(&p->rc,&pPage->buf ,(i64)(u64)iRowid - (u64)pWriter->iPrevRowid) | |||
| 14178 | )sqlite3Fts5BufferAppendVarint(&p->rc,&pPage->buf ,(i64)(u64)iRowid - (u64)pWriter->iPrevRowid); | |||
| 14179 | } | |||
| 14180 | pWriter->iPrevRowid = iRowid; | |||
| 14181 | pWriter->bFirstRowidInDoclist = 0; | |||
| 14182 | pWriter->bFirstRowidInPage = 0; | |||
| 14183 | } | |||
| 14184 | } | |||
| 14185 | ||||
| 14186 | static void fts5WriteAppendPoslistData( | |||
| 14187 | Fts5Index *p, | |||
| 14188 | Fts5SegWriter *pWriter, | |||
| 14189 | const u8 *aData, | |||
| 14190 | int nData | |||
| 14191 | ){ | |||
| 14192 | Fts5PageWriter *pPage = &pWriter->writer; | |||
| 14193 | const u8 *a = aData; | |||
| 14194 | int n = nData; | |||
| 14195 | ||||
| 14196 | assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK )((void) (0)); | |||
| 14197 | while( p->rc==SQLITE_OK0 | |||
| 14198 | && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz | |||
| 14199 | ){ | |||
| 14200 | int nReq = p->pConfig->pgsz - pPage->buf.n - pPage->pgidx.n; | |||
| 14201 | int nCopy = 0; | |||
| 14202 | while( nCopy<nReq ){ | |||
| 14203 | i64 dummy; | |||
| 14204 | nCopy += fts5GetVarintsqlite3Fts5GetVarint(&a[nCopy], (u64*)&dummy); | |||
| 14205 | } | |||
| 14206 | fts5BufferAppendBlob(&p->rc, &pPage->buf, nCopy, a)sqlite3Fts5BufferAppendBlob(&p->rc,&pPage->buf, nCopy,a); | |||
| 14207 | a += nCopy; | |||
| 14208 | n -= nCopy; | |||
| 14209 | fts5WriteFlushLeaf(p, pWriter); | |||
| 14210 | } | |||
| 14211 | if( n>0 ){ | |||
| 14212 | fts5BufferAppendBlob(&p->rc, &pPage->buf, n, a)sqlite3Fts5BufferAppendBlob(&p->rc,&pPage->buf, n,a); | |||
| 14213 | } | |||
| 14214 | } | |||
| 14215 | ||||
| 14216 | /* | |||
| 14217 | ** Flush any data cached by the writer object to the database. Free any | |||
| 14218 | ** allocations associated with the writer. | |||
| 14219 | */ | |||
| 14220 | static void fts5WriteFinish( | |||
| 14221 | Fts5Index *p, | |||
| 14222 | Fts5SegWriter *pWriter, /* Writer object */ | |||
| 14223 | int *pnLeaf /* OUT: Number of leaf pages in b-tree */ | |||
| 14224 | ){ | |||
| 14225 | int i; | |||
| 14226 | Fts5PageWriter *pLeaf = &pWriter->writer; | |||
| 14227 | if( p->rc==SQLITE_OK0 ){ | |||
| 14228 | assert( pLeaf->pgno>=1 )((void) (0)); | |||
| 14229 | if( pLeaf->buf.n>4 ){ | |||
| 14230 | fts5WriteFlushLeaf(p, pWriter); | |||
| 14231 | } | |||
| 14232 | *pnLeaf = pLeaf->pgno-1; | |||
| 14233 | if( pLeaf->pgno>1 ){ | |||
| 14234 | fts5WriteFlushBtree(p, pWriter); | |||
| 14235 | } | |||
| 14236 | } | |||
| 14237 | fts5BufferFree(&pLeaf->term)sqlite3Fts5BufferFree(&pLeaf->term); | |||
| 14238 | fts5BufferFree(&pLeaf->buf)sqlite3Fts5BufferFree(&pLeaf->buf); | |||
| 14239 | fts5BufferFree(&pLeaf->pgidx)sqlite3Fts5BufferFree(&pLeaf->pgidx); | |||
| 14240 | fts5BufferFree(&pWriter->btterm)sqlite3Fts5BufferFree(&pWriter->btterm); | |||
| 14241 | ||||
| 14242 | for(i=0; i<pWriter->nDlidx; i++){ | |||
| 14243 | sqlite3Fts5BufferFree(&pWriter->aDlidx[i].buf); | |||
| 14244 | } | |||
| 14245 | sqlite3_freesqlite3_api->free(pWriter->aDlidx); | |||
| 14246 | } | |||
| 14247 | ||||
| 14248 | static void fts5WriteInit( | |||
| 14249 | Fts5Index *p, | |||
| 14250 | Fts5SegWriter *pWriter, | |||
| 14251 | int iSegid | |||
| 14252 | ){ | |||
| 14253 | const int nBuffer = p->pConfig->pgsz + FTS5_DATA_PADDING20; | |||
| 14254 | ||||
| 14255 | memset(pWriter, 0, sizeof(Fts5SegWriter)); | |||
| 14256 | pWriter->iSegid = iSegid; | |||
| 14257 | ||||
| 14258 | fts5WriteDlidxGrow(p, pWriter, 1); | |||
| 14259 | pWriter->writer.pgno = 1; | |||
| 14260 | pWriter->bFirstTermInPage = 1; | |||
| 14261 | pWriter->iBtPage = 1; | |||
| 14262 | ||||
| 14263 | assert( pWriter->writer.buf.n==0 )((void) (0)); | |||
| 14264 | assert( pWriter->writer.pgidx.n==0 )((void) (0)); | |||
| 14265 | ||||
| 14266 | /* Grow the two buffers to pgsz + padding bytes in size. */ | |||
| 14267 | sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.pgidx, nBuffer); | |||
| 14268 | sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.buf, nBuffer); | |||
| 14269 | ||||
| 14270 | if( p->pIdxWriter==0 ){ | |||
| 14271 | Fts5Config *pConfig = p->pConfig; | |||
| 14272 | fts5IndexPrepareStmt(p, &p->pIdxWriter, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 14273 | "INSERT INTO '%q'.'%q_idx'(segid,term,pgno) VALUES(?,?,?)", | |||
| 14274 | pConfig->zDb, pConfig->zName | |||
| 14275 | )); | |||
| 14276 | } | |||
| 14277 | ||||
| 14278 | if( p->rc==SQLITE_OK0 ){ | |||
| 14279 | /* Initialize the 4-byte leaf-page header to 0x00. */ | |||
| 14280 | memset(pWriter->writer.buf.p, 0, 4); | |||
| 14281 | pWriter->writer.buf.n = 4; | |||
| 14282 | ||||
| 14283 | /* Bind the current output segment id to the index-writer. This is an | |||
| 14284 | ** optimization over binding the same value over and over as rows are | |||
| 14285 | ** inserted into %_idx by the current writer. */ | |||
| 14286 | sqlite3_bind_intsqlite3_api->bind_int(p->pIdxWriter, 1, pWriter->iSegid); | |||
| 14287 | } | |||
| 14288 | } | |||
| 14289 | ||||
| 14290 | /* | |||
| 14291 | ** Iterator pIter was used to iterate through the input segments of on an | |||
| 14292 | ** incremental merge operation. This function is called if the incremental | |||
| 14293 | ** merge step has finished but the input has not been completely exhausted. | |||
| 14294 | */ | |||
| 14295 | static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ | |||
| 14296 | int i; | |||
| 14297 | Fts5Buffer buf; | |||
| 14298 | memset(&buf, 0, sizeof(Fts5Buffer)); | |||
| 14299 | for(i=0; i<pIter->nSeg && p->rc==SQLITE_OK0; i++){ | |||
| 14300 | Fts5SegIter *pSeg = &pIter->aSeg[i]; | |||
| 14301 | if( pSeg->pSeg==0 ){ | |||
| 14302 | /* no-op */ | |||
| 14303 | }else if( pSeg->pLeaf==0 ){ | |||
| 14304 | /* All keys from this input segment have been transfered to the output. | |||
| 14305 | ** Set both the first and last page-numbers to 0 to indicate that the | |||
| 14306 | ** segment is now empty. */ | |||
| 14307 | pSeg->pSeg->pgnoLast = 0; | |||
| 14308 | pSeg->pSeg->pgnoFirst = 0; | |||
| 14309 | }else{ | |||
| 14310 | int iOff = pSeg->iTermLeafOffset; /* Offset on new first leaf page */ | |||
| 14311 | i64 iLeafRowid; | |||
| 14312 | Fts5Data *pData; | |||
| 14313 | int iId = pSeg->pSeg->iSegid; | |||
| 14314 | u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00}; | |||
| 14315 | ||||
| 14316 | iLeafRowid = FTS5_SEGMENT_ROWID(iId, pSeg->iTermLeafPgno)( ((i64)(iId) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pSeg->iTermLeafPgno )) ); | |||
| 14317 | pData = fts5LeafRead(p, iLeafRowid); | |||
| 14318 | if( pData ){ | |||
| 14319 | if( iOff>pData->szLeaf ){ | |||
| 14320 | /* This can occur if the pages that the segments occupy overlap - if | |||
| 14321 | ** a single page has been assigned to more than one segment. In | |||
| 14322 | ** this case a prior iteration of this loop may have corrupted the | |||
| 14323 | ** segment currently being trimmed. */ | |||
| 14324 | FTS5_CORRUPT_ROWID(p, iLeafRowid)fts5IndexCorruptRowid(p, iLeafRowid); | |||
| 14325 | }else{ | |||
| 14326 | fts5BufferZero(&buf)sqlite3Fts5BufferZero(&buf); | |||
| 14327 | fts5BufferGrow(&p->rc, &buf, pData->nn)( (u32)((&buf)->n) + (u32)(pData->nn) <= (u32)(( &buf)->nSpace) ? 0 : sqlite3Fts5BufferSize((&p-> rc),(&buf),(pData->nn)+(&buf)->n) ); | |||
| 14328 | fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr)sqlite3Fts5BufferAppendBlob(&p->rc,&buf,sizeof(aHdr ),aHdr); | |||
| 14329 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)pSeg ->term.n); | |||
| 14330 | fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p)sqlite3Fts5BufferAppendBlob(&p->rc,&buf,pSeg->term .n,pSeg->term.p); | |||
| 14331 | fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff])sqlite3Fts5BufferAppendBlob(&p->rc,&buf,pData-> szLeaf-iOff,&pData->p[iOff]); | |||
| 14332 | if( p->rc==SQLITE_OK0 ){ | |||
| 14333 | /* Set the szLeaf field */ | |||
| 14334 | fts5PutU16(&buf.p[2], (u16)buf.n); | |||
| 14335 | } | |||
| 14336 | ||||
| 14337 | /* Set up the new page-index array */ | |||
| 14338 | fts5BufferAppendVarint(&p->rc, &buf, 4)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)4); | |||
| 14339 | if( pSeg->iLeafPgno==pSeg->iTermLeafPgno | |||
| 14340 | && pSeg->iEndofDoclist<pData->szLeaf | |||
| 14341 | && pSeg->iPgidxOff<=pData->nn | |||
| 14342 | ){ | |||
| 14343 | int nDiff = pData->szLeaf - pSeg->iEndofDoclist; | |||
| 14344 | fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4)sqlite3Fts5BufferAppendVarint(&p->rc,&buf,(i64)buf .n - 1 - nDiff - 4); | |||
| 14345 | fts5BufferAppendBlob(&p->rc, &buf,sqlite3Fts5BufferAppendBlob(&p->rc,&buf,pData-> nn - pSeg->iPgidxOff,&pData->p[pSeg->iPgidxOff]) | |||
| 14346 | pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff]sqlite3Fts5BufferAppendBlob(&p->rc,&buf,pData-> nn - pSeg->iPgidxOff,&pData->p[pSeg->iPgidxOff]) | |||
| 14347 | )sqlite3Fts5BufferAppendBlob(&p->rc,&buf,pData-> nn - pSeg->iPgidxOff,&pData->p[pSeg->iPgidxOff]); | |||
| 14348 | } | |||
| 14349 | ||||
| 14350 | pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; | |||
| 14351 | fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1)( ((i64)(iId) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(1)) ), iLeafRowid); | |||
| 14352 | fts5DataWrite(p, iLeafRowid, buf.p, buf.n); | |||
| 14353 | } | |||
| 14354 | fts5DataRelease(pData); | |||
| 14355 | } | |||
| 14356 | } | |||
| 14357 | } | |||
| 14358 | fts5BufferFree(&buf)sqlite3Fts5BufferFree(&buf); | |||
| 14359 | } | |||
| 14360 | ||||
| 14361 | static void fts5MergeChunkCallback( | |||
| 14362 | Fts5Index *p, | |||
| 14363 | void *pCtx, | |||
| 14364 | const u8 *pChunk, int nChunk | |||
| 14365 | ){ | |||
| 14366 | Fts5SegWriter *pWriter = (Fts5SegWriter*)pCtx; | |||
| 14367 | fts5WriteAppendPoslistData(p, pWriter, pChunk, nChunk); | |||
| 14368 | } | |||
| 14369 | ||||
| 14370 | /* | |||
| 14371 | ** | |||
| 14372 | */ | |||
| 14373 | static void fts5IndexMergeLevel( | |||
| 14374 | Fts5Index *p, /* FTS5 backend object */ | |||
| 14375 | Fts5Structure **ppStruct, /* IN/OUT: Stucture of index */ | |||
| 14376 | int iLvl, /* Level to read input from */ | |||
| 14377 | int *pnRem /* Write up to this many output leaves */ | |||
| 14378 | ){ | |||
| 14379 | Fts5Structure *pStruct = *ppStruct; | |||
| 14380 | Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; | |||
| 14381 | Fts5StructureLevel *pLvlOut; | |||
| 14382 | Fts5Iter *pIter = 0; /* Iterator to read input data */ | |||
| 14383 | int nRem = pnRem ? *pnRem : 0; /* Output leaf pages left to write */ | |||
| 14384 | int nInput; /* Number of input segments */ | |||
| 14385 | Fts5SegWriter writer; /* Writer object */ | |||
| 14386 | Fts5StructureSegment *pSeg; /* Output segment */ | |||
| 14387 | Fts5Buffer term; | |||
| 14388 | int bOldest; /* True if the output segment is the oldest */ | |||
| 14389 | int eDetail = p->pConfig->eDetail; | |||
| 14390 | const int flags = FTS5INDEX_QUERY_NOOUTPUT0x0020; | |||
| 14391 | int bTermWritten = 0; /* True if current term already output */ | |||
| 14392 | ||||
| 14393 | assert( iLvl<pStruct->nLevel )((void) (0)); | |||
| 14394 | assert( pLvl->nMerge<=pLvl->nSeg )((void) (0)); | |||
| 14395 | ||||
| 14396 | memset(&writer, 0, sizeof(Fts5SegWriter)); | |||
| 14397 | memset(&term, 0, sizeof(Fts5Buffer)); | |||
| 14398 | if( pLvl->nMerge ){ | |||
| 14399 | pLvlOut = &pStruct->aLevel[iLvl+1]; | |||
| 14400 | assert( pLvlOut->nSeg>0 )((void) (0)); | |||
| 14401 | nInput = pLvl->nMerge; | |||
| 14402 | pSeg = &pLvlOut->aSeg[pLvlOut->nSeg-1]; | |||
| 14403 | ||||
| 14404 | fts5WriteInit(p, &writer, pSeg->iSegid); | |||
| 14405 | writer.writer.pgno = pSeg->pgnoLast+1; | |||
| 14406 | writer.iBtPage = 0; | |||
| 14407 | }else{ | |||
| 14408 | int iSegid = fts5AllocateSegid(p, pStruct); | |||
| 14409 | ||||
| 14410 | /* Extend the Fts5Structure object as required to ensure the output | |||
| 14411 | ** segment exists. */ | |||
| 14412 | if( iLvl==pStruct->nLevel-1 ){ | |||
| 14413 | fts5StructureAddLevel(&p->rc, ppStruct); | |||
| 14414 | pStruct = *ppStruct; | |||
| 14415 | } | |||
| 14416 | fts5StructureExtendLevel(&p->rc, pStruct, iLvl+1, 1, 0); | |||
| 14417 | if( p->rc ) return; | |||
| 14418 | pLvl = &pStruct->aLevel[iLvl]; | |||
| 14419 | pLvlOut = &pStruct->aLevel[iLvl+1]; | |||
| 14420 | ||||
| 14421 | fts5WriteInit(p, &writer, iSegid); | |||
| 14422 | ||||
| 14423 | /* Add the new segment to the output level */ | |||
| 14424 | pSeg = &pLvlOut->aSeg[pLvlOut->nSeg]; | |||
| 14425 | pLvlOut->nSeg++; | |||
| 14426 | pSeg->pgnoFirst = 1; | |||
| 14427 | pSeg->iSegid = iSegid; | |||
| 14428 | pStruct->nSegment++; | |||
| 14429 | ||||
| 14430 | /* Read input from all segments in the input level */ | |||
| 14431 | nInput = pLvl->nSeg; | |||
| 14432 | ||||
| 14433 | /* Set the range of origins that will go into the output segment. */ | |||
| 14434 | if( pStruct->nOriginCntr>0 ){ | |||
| 14435 | pSeg->iOrigin1 = pLvl->aSeg[0].iOrigin1; | |||
| 14436 | pSeg->iOrigin2 = pLvl->aSeg[pLvl->nSeg-1].iOrigin2; | |||
| 14437 | } | |||
| 14438 | } | |||
| 14439 | bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2); | |||
| 14440 | ||||
| 14441 | assert( iLvl>=0 )((void) (0)); | |||
| 14442 | for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, iLvl, nInput, &pIter); | |||
| 14443 | fts5MultiIterEof(p, pIter)==0; | |||
| 14444 | fts5MultiIterNext(p, pIter, 0, 0) | |||
| 14445 | ){ | |||
| 14446 | Fts5SegIter *pSegIter = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; | |||
| 14447 | int nPos; /* position-list size field value */ | |||
| 14448 | int nTerm; | |||
| 14449 | const u8 *pTerm; | |||
| 14450 | ||||
| 14451 | pTerm = fts5MultiIterTerm(pIter, &nTerm); | |||
| 14452 | if( nTerm!=term.n || fts5Memcmp(pTerm, term.p, nTerm)((nTerm)<=0 ? 0 : memcmp((pTerm), (term.p), (nTerm))) ){ | |||
| 14453 | if( pnRem && writer.nLeafWritten>nRem ){ | |||
| 14454 | break; | |||
| 14455 | } | |||
| 14456 | fts5BufferSet(&p->rc, &term, nTerm, pTerm)sqlite3Fts5BufferSet(&p->rc,&term,nTerm,pTerm); | |||
| 14457 | bTermWritten =0; | |||
| 14458 | } | |||
| 14459 | ||||
| 14460 | /* Check for key annihilation. */ | |||
| 14461 | if( pSegIter->nPos==0 && (bOldest || pSegIter->bDel==0) ) continue; | |||
| 14462 | ||||
| 14463 | if( p->rc==SQLITE_OK0 && bTermWritten==0 ){ | |||
| 14464 | /* This is a new term. Append a term to the output segment. */ | |||
| 14465 | fts5WriteAppendTerm(p, &writer, nTerm, pTerm); | |||
| 14466 | bTermWritten = 1; | |||
| 14467 | } | |||
| 14468 | ||||
| 14469 | /* Append the rowid to the output */ | |||
| 14470 | /* WRITEPOSLISTSIZE */ | |||
| 14471 | fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter)); | |||
| 14472 | ||||
| 14473 | if( eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 14474 | if( pSegIter->bDel ){ | |||
| 14475 | fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0)sqlite3Fts5BufferAppendVarint(&p->rc,&writer.writer .buf,(i64)0); | |||
| 14476 | if( pSegIter->nPos>0 ){ | |||
| 14477 | fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0)sqlite3Fts5BufferAppendVarint(&p->rc,&writer.writer .buf,(i64)0); | |||
| 14478 | } | |||
| 14479 | } | |||
| 14480 | }else{ | |||
| 14481 | /* Append the position-list data to the output */ | |||
| 14482 | nPos = pSegIter->nPos*2 + pSegIter->bDel; | |||
| 14483 | fts5BufferAppendVarint(&p->rc, &writer.writer.buf, nPos)sqlite3Fts5BufferAppendVarint(&p->rc,&writer.writer .buf,(i64)nPos); | |||
| 14484 | fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback); | |||
| 14485 | } | |||
| 14486 | } | |||
| 14487 | ||||
| 14488 | /* Flush the last leaf page to disk. Set the output segment b-tree height | |||
| 14489 | ** and last leaf page number at the same time. */ | |||
| 14490 | fts5WriteFinish(p, &writer, &pSeg->pgnoLast); | |||
| 14491 | ||||
| 14492 | assert( pIter!=0 || p->rc!=SQLITE_OK )((void) (0)); | |||
| 14493 | if( fts5MultiIterEof(p, pIter) ){ | |||
| 14494 | int i; | |||
| 14495 | ||||
| 14496 | /* Remove the redundant segments from the %_data table */ | |||
| 14497 | assert( pSeg->nEntry==0 )((void) (0)); | |||
| 14498 | for(i=0; i<nInput; i++){ | |||
| 14499 | Fts5StructureSegment *pOld = &pLvl->aSeg[i]; | |||
| 14500 | pSeg->nEntry += (pOld->nEntry - pOld->nEntryTombstone); | |||
| 14501 | fts5DataRemoveSegment(p, pOld); | |||
| 14502 | } | |||
| 14503 | ||||
| 14504 | /* Remove the redundant segments from the input level */ | |||
| 14505 | if( pLvl->nSeg!=nInput ){ | |||
| 14506 | int nMove = (pLvl->nSeg - nInput) * sizeof(Fts5StructureSegment); | |||
| 14507 | memmove(pLvl->aSeg, &pLvl->aSeg[nInput], nMove); | |||
| 14508 | } | |||
| 14509 | pStruct->nSegment -= nInput; | |||
| 14510 | pLvl->nSeg -= nInput; | |||
| 14511 | pLvl->nMerge = 0; | |||
| 14512 | if( pSeg->pgnoLast==0 ){ | |||
| 14513 | pLvlOut->nSeg--; | |||
| 14514 | pStruct->nSegment--; | |||
| 14515 | } | |||
| 14516 | }else{ | |||
| 14517 | assert( pSeg->pgnoLast>0 )((void) (0)); | |||
| 14518 | fts5TrimSegments(p, pIter); | |||
| 14519 | pLvl->nMerge = nInput; | |||
| 14520 | } | |||
| 14521 | ||||
| 14522 | fts5MultiIterFree(pIter); | |||
| 14523 | fts5BufferFree(&term)sqlite3Fts5BufferFree(&term); | |||
| 14524 | if( pnRem ) *pnRem -= writer.nLeafWritten; | |||
| 14525 | } | |||
| 14526 | ||||
| 14527 | /* | |||
| 14528 | ** If this is not a contentless_delete=1 table, or if the 'deletemerge' | |||
| 14529 | ** configuration option is set to 0, then this function always returns -1. | |||
| 14530 | ** Otherwise, it searches the structure object passed as the second argument | |||
| 14531 | ** for a level suitable for merging due to having a large number of | |||
| 14532 | ** tombstones in the tombstone hash. If one is found, its index is returned. | |||
| 14533 | ** Otherwise, if there is no suitable level, -1. | |||
| 14534 | */ | |||
| 14535 | static int fts5IndexFindDeleteMerge(Fts5Index *p, Fts5Structure *pStruct){ | |||
| 14536 | Fts5Config *pConfig = p->pConfig; | |||
| 14537 | int iRet = -1; | |||
| 14538 | if( pConfig->bContentlessDelete && pConfig->nDeleteMerge>0 ){ | |||
| 14539 | int ii; | |||
| 14540 | int nBest = 0; | |||
| 14541 | ||||
| 14542 | for(ii=0; ii<pStruct->nLevel; ii++){ | |||
| 14543 | Fts5StructureLevel *pLvl = &pStruct->aLevel[ii]; | |||
| 14544 | i64 nEntry = 0; | |||
| 14545 | i64 nTomb = 0; | |||
| 14546 | int iSeg; | |||
| 14547 | for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){ | |||
| 14548 | nEntry += pLvl->aSeg[iSeg].nEntry; | |||
| 14549 | nTomb += pLvl->aSeg[iSeg].nEntryTombstone; | |||
| 14550 | } | |||
| 14551 | assert_nc( nEntry>0 || pLvl->nSeg==0 )((void) (0)); | |||
| 14552 | if( nEntry>0 ){ | |||
| 14553 | int nPercent = (nTomb * 100) / nEntry; | |||
| 14554 | if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){ | |||
| 14555 | iRet = ii; | |||
| 14556 | nBest = nPercent; | |||
| 14557 | } | |||
| 14558 | } | |||
| 14559 | ||||
| 14560 | /* If pLvl is already the input level to an ongoing merge, look no | |||
| 14561 | ** further for a merge candidate. The caller should be allowed to | |||
| 14562 | ** continue merging from pLvl first. */ | |||
| 14563 | if( pLvl->nMerge ) break; | |||
| 14564 | } | |||
| 14565 | } | |||
| 14566 | return iRet; | |||
| 14567 | } | |||
| 14568 | ||||
| 14569 | /* | |||
| 14570 | ** Do up to nPg pages of automerge work on the index. | |||
| 14571 | ** | |||
| 14572 | ** Return true if any changes were actually made, or false otherwise. | |||
| 14573 | */ | |||
| 14574 | static int fts5IndexMerge( | |||
| 14575 | Fts5Index *p, /* FTS5 backend object */ | |||
| 14576 | Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ | |||
| 14577 | int nPg, /* Pages of work to do */ | |||
| 14578 | int nMin /* Minimum number of segments to merge */ | |||
| 14579 | ){ | |||
| 14580 | int nRem = nPg; | |||
| 14581 | int bRet = 0; | |||
| 14582 | Fts5Structure *pStruct = *ppStruct; | |||
| 14583 | while( nRem>0 && p->rc==SQLITE_OK0 ){ | |||
| 14584 | int iLvl; /* To iterate through levels */ | |||
| 14585 | int iBestLvl = 0; /* Level offering the most input segments */ | |||
| 14586 | int nBest = 0; /* Number of input segments on best level */ | |||
| 14587 | ||||
| 14588 | /* Set iBestLvl to the level to read input segments from. Or to -1 if | |||
| 14589 | ** there is no level suitable to merge segments from. */ | |||
| 14590 | assert( pStruct->nLevel>0 )((void) (0)); | |||
| 14591 | for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ | |||
| 14592 | Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; | |||
| 14593 | if( pLvl->nMerge ){ | |||
| 14594 | if( pLvl->nMerge>nBest ){ | |||
| 14595 | iBestLvl = iLvl; | |||
| 14596 | nBest = nMin; | |||
| 14597 | } | |||
| 14598 | break; | |||
| 14599 | } | |||
| 14600 | if( pLvl->nSeg>nBest ){ | |||
| 14601 | nBest = pLvl->nSeg; | |||
| 14602 | iBestLvl = iLvl; | |||
| 14603 | } | |||
| 14604 | } | |||
| 14605 | if( nBest<nMin ){ | |||
| 14606 | iBestLvl = fts5IndexFindDeleteMerge(p, pStruct); | |||
| 14607 | } | |||
| 14608 | ||||
| 14609 | if( iBestLvl<0 ) break; | |||
| 14610 | bRet = 1; | |||
| 14611 | fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem); | |||
| 14612 | if( p->rc==SQLITE_OK0 && pStruct->aLevel[iBestLvl].nMerge==0 ){ | |||
| 14613 | fts5StructurePromote(p, iBestLvl+1, pStruct); | |||
| 14614 | } | |||
| 14615 | ||||
| 14616 | if( nMin==1 ) nMin = 2; | |||
| 14617 | } | |||
| 14618 | *ppStruct = pStruct; | |||
| 14619 | return bRet; | |||
| 14620 | } | |||
| 14621 | ||||
| 14622 | /* | |||
| 14623 | ** A total of nLeaf leaf pages of data has just been flushed to a level-0 | |||
| 14624 | ** segment. This function updates the write-counter accordingly and, if | |||
| 14625 | ** necessary, performs incremental merge work. | |||
| 14626 | ** | |||
| 14627 | ** If an error occurs, set the Fts5Index.rc error code. If an error has | |||
| 14628 | ** already occurred, this function is a no-op. | |||
| 14629 | */ | |||
| 14630 | static void fts5IndexAutomerge( | |||
| 14631 | Fts5Index *p, /* FTS5 backend object */ | |||
| 14632 | Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ | |||
| 14633 | int nLeaf /* Number of output leaves just written */ | |||
| 14634 | ){ | |||
| 14635 | if( p->rc==SQLITE_OK0 && p->pConfig->nAutomerge>0 && ALWAYS((*ppStruct)!=0)((*ppStruct)!=0) ){ | |||
| 14636 | Fts5Structure *pStruct = *ppStruct; | |||
| 14637 | u64 nWrite; /* Initial value of write-counter */ | |||
| 14638 | int nWork; /* Number of work-quanta to perform */ | |||
| 14639 | int nRem; /* Number of leaf pages left to write */ | |||
| 14640 | ||||
| 14641 | /* Update the write-counter. While doing so, set nWork. */ | |||
| 14642 | nWrite = pStruct->nWriteCounter; | |||
| 14643 | nWork = (int)(((nWrite + nLeaf) / p->nWorkUnit) - (nWrite / p->nWorkUnit)); | |||
| 14644 | pStruct->nWriteCounter += nLeaf; | |||
| 14645 | nRem = (int)(p->nWorkUnit * nWork * pStruct->nLevel); | |||
| 14646 | ||||
| 14647 | fts5IndexMerge(p, ppStruct, nRem, p->pConfig->nAutomerge); | |||
| 14648 | } | |||
| 14649 | } | |||
| 14650 | ||||
| 14651 | static void fts5IndexCrisismerge( | |||
| 14652 | Fts5Index *p, /* FTS5 backend object */ | |||
| 14653 | Fts5Structure **ppStruct /* IN/OUT: Current structure of index */ | |||
| 14654 | ){ | |||
| 14655 | const int nCrisis = p->pConfig->nCrisisMerge; | |||
| 14656 | Fts5Structure *pStruct = *ppStruct; | |||
| 14657 | if( pStruct && pStruct->nLevel>0 ){ | |||
| 14658 | int iLvl = 0; | |||
| 14659 | while( p->rc==SQLITE_OK0 && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ | |||
| 14660 | fts5IndexMergeLevel(p, &pStruct, iLvl, 0); | |||
| 14661 | assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) )((void) (0)); | |||
| 14662 | fts5StructurePromote(p, iLvl+1, pStruct); | |||
| 14663 | iLvl++; | |||
| 14664 | } | |||
| 14665 | *ppStruct = pStruct; | |||
| 14666 | } | |||
| 14667 | } | |||
| 14668 | ||||
| 14669 | static int fts5IndexReturn(Fts5Index *p){ | |||
| 14670 | int rc = p->rc; | |||
| 14671 | p->rc = SQLITE_OK0; | |||
| 14672 | return rc; | |||
| 14673 | } | |||
| 14674 | ||||
| 14675 | /* | |||
| 14676 | ** Close the read-only blob handle, if it is open. | |||
| 14677 | */ | |||
| 14678 | static void sqlite3Fts5IndexCloseReader(Fts5Index *p){ | |||
| 14679 | fts5IndexCloseReader(p); | |||
| 14680 | fts5IndexReturn(p); | |||
| 14681 | } | |||
| 14682 | ||||
| 14683 | typedef struct Fts5FlushCtx Fts5FlushCtx; | |||
| 14684 | struct Fts5FlushCtx { | |||
| 14685 | Fts5Index *pIdx; | |||
| 14686 | Fts5SegWriter writer; | |||
| 14687 | }; | |||
| 14688 | ||||
| 14689 | /* | |||
| 14690 | ** Buffer aBuf[] contains a list of varints, all small enough to fit | |||
| 14691 | ** in a 32-bit integer. Return the size of the largest prefix of this | |||
| 14692 | ** list nMax bytes or less in size. | |||
| 14693 | */ | |||
| 14694 | static int fts5PoslistPrefix(const u8 *aBuf, int nMax){ | |||
| 14695 | int ret; | |||
| 14696 | u32 dummy; | |||
| 14697 | ret = fts5GetVarint32(aBuf, dummy)sqlite3Fts5GetVarint32(aBuf,(u32*)&(dummy)); | |||
| 14698 | if( ret<nMax ){ | |||
| 14699 | while( 1 ){ | |||
| 14700 | int i = fts5GetVarint32(&aBuf[ret], dummy)sqlite3Fts5GetVarint32(&aBuf[ret],(u32*)&(dummy)); | |||
| 14701 | if( (ret + i) > nMax ) break; | |||
| 14702 | ret += i; | |||
| 14703 | } | |||
| 14704 | } | |||
| 14705 | return ret; | |||
| 14706 | } | |||
| 14707 | ||||
| 14708 | /* | |||
| 14709 | ** Execute the SQL statement: | |||
| 14710 | ** | |||
| 14711 | ** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno); | |||
| 14712 | ** | |||
| 14713 | ** This is used when a secure-delete operation removes the last term | |||
| 14714 | ** from a segment leaf page. In that case the %_idx entry is removed | |||
| 14715 | ** too. This is done to ensure that if all instances of a token are | |||
| 14716 | ** removed from an fts5 database in secure-delete mode, no trace of | |||
| 14717 | ** the token itself remains in the database. | |||
| 14718 | */ | |||
| 14719 | static void fts5SecureDeleteIdxEntry( | |||
| 14720 | Fts5Index *p, /* FTS5 backend object */ | |||
| 14721 | int iSegid, /* Id of segment to delete entry for */ | |||
| 14722 | int iPgno /* Page number within segment */ | |||
| 14723 | ){ | |||
| 14724 | if( iPgno!=1 ){ | |||
| 14725 | assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE )((void) (0)); | |||
| 14726 | if( p->pDeleteFromIdx==0 ){ | |||
| 14727 | fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 14728 | "DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)", | |||
| 14729 | p->pConfig->zDb, p->pConfig->zName | |||
| 14730 | )); | |||
| 14731 | } | |||
| 14732 | if( p->rc==SQLITE_OK0 ){ | |||
| 14733 | sqlite3_bind_intsqlite3_api->bind_int(p->pDeleteFromIdx, 1, iSegid); | |||
| 14734 | sqlite3_bind_intsqlite3_api->bind_int(p->pDeleteFromIdx, 2, iPgno); | |||
| 14735 | sqlite3_stepsqlite3_api->step(p->pDeleteFromIdx); | |||
| 14736 | p->rc = sqlite3_resetsqlite3_api->reset(p->pDeleteFromIdx); | |||
| 14737 | } | |||
| 14738 | } | |||
| 14739 | } | |||
| 14740 | ||||
| 14741 | /* | |||
| 14742 | ** This is called when a secure-delete operation removes a position-list | |||
| 14743 | ** that overflows onto segment page iPgno of segment pSeg. This function | |||
| 14744 | ** rewrites node iPgno, and possibly one or more of its right-hand peers, | |||
| 14745 | ** to remove this portion of the position list. | |||
| 14746 | ** | |||
| 14747 | ** Output variable (*pbLastInDoclist) is set to true if the position-list | |||
| 14748 | ** removed is followed by a new term or the end-of-segment, or false if | |||
| 14749 | ** it is followed by another rowid/position list. | |||
| 14750 | */ | |||
| 14751 | static void fts5SecureDeleteOverflow( | |||
| 14752 | Fts5Index *p, | |||
| 14753 | Fts5StructureSegment *pSeg, | |||
| 14754 | int iPgno, | |||
| 14755 | int *pbLastInDoclist | |||
| 14756 | ){ | |||
| 14757 | const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE1); | |||
| 14758 | int pgno; | |||
| 14759 | Fts5Data *pLeaf = 0; | |||
| 14760 | assert( iPgno!=1 )((void) (0)); | |||
| 14761 | ||||
| 14762 | *pbLastInDoclist = 1; | |||
| 14763 | for(pgno=iPgno; p->rc==SQLITE_OK0 && pgno<=pSeg->pgnoLast; pgno++){ | |||
| 14764 | i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno)( ((i64)(pSeg->iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pgno)) ); | |||
| 14765 | int iNext = 0; | |||
| 14766 | u8 *aPg = 0; | |||
| 14767 | ||||
| 14768 | pLeaf = fts5DataRead(p, iRowid); | |||
| 14769 | if( pLeaf==0 ) break; | |||
| 14770 | aPg = pLeaf->p; | |||
| 14771 | ||||
| 14772 | iNext = fts5GetU16(&aPg[0]); | |||
| 14773 | if( iNext!=0 ){ | |||
| 14774 | *pbLastInDoclist = 0; | |||
| 14775 | } | |||
| 14776 | if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ | |||
| 14777 | fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext)sqlite3Fts5GetVarint32(&aPg[pLeaf->szLeaf],(u32*)& (iNext)); | |||
| 14778 | } | |||
| 14779 | ||||
| 14780 | if( iNext==0 ){ | |||
| 14781 | /* The page contains no terms or rowids. Replace it with an empty | |||
| 14782 | ** page and move on to the right-hand peer. */ | |||
| 14783 | const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04}; | |||
| 14784 | assert_nc( bDetailNone==0 || pLeaf->nn==4 )((void) (0)); | |||
| 14785 | if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty)); | |||
| 14786 | fts5DataRelease(pLeaf); | |||
| 14787 | pLeaf = 0; | |||
| 14788 | }else if( bDetailNone ){ | |||
| 14789 | break; | |||
| 14790 | }else if( iNext>=pLeaf->szLeaf || pLeaf->nn<pLeaf->szLeaf || iNext<4 ){ | |||
| 14791 | FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 14792 | break; | |||
| 14793 | }else{ | |||
| 14794 | int nShift = iNext - 4; | |||
| 14795 | int nPg; | |||
| 14796 | ||||
| 14797 | int nIdx = 0; | |||
| 14798 | u8 *aIdx = 0; | |||
| 14799 | ||||
| 14800 | /* Unless the current page footer is 0 bytes in size (in which case | |||
| 14801 | ** the new page footer will be as well), allocate and populate a | |||
| 14802 | ** buffer containing the new page footer. Set stack variables aIdx | |||
| 14803 | ** and nIdx accordingly. */ | |||
| 14804 | if( pLeaf->nn>pLeaf->szLeaf ){ | |||
| 14805 | int iFirst = 0; | |||
| 14806 | int i1 = pLeaf->szLeaf; | |||
| 14807 | int i2 = 0; | |||
| 14808 | ||||
| 14809 | i1 += fts5GetVarint32(&aPg[i1], iFirst)sqlite3Fts5GetVarint32(&aPg[i1],(u32*)&(iFirst)); | |||
| 14810 | if( iFirst<iNext ){ | |||
| 14811 | FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 14812 | break; | |||
| 14813 | } | |||
| 14814 | aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2); | |||
| 14815 | if( aIdx==0 ) break; | |||
| 14816 | i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift); | |||
| 14817 | if( i1<pLeaf->nn ){ | |||
| 14818 | memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1); | |||
| 14819 | i2 += (pLeaf->nn-i1); | |||
| 14820 | } | |||
| 14821 | nIdx = i2; | |||
| 14822 | } | |||
| 14823 | ||||
| 14824 | /* Modify the contents of buffer aPg[]. Set nPg to the new size | |||
| 14825 | ** in bytes. The new page is always smaller than the old. */ | |||
| 14826 | nPg = pLeaf->szLeaf - nShift; | |||
| 14827 | memmove(&aPg[4], &aPg[4+nShift], nPg-4); | |||
| 14828 | fts5PutU16(&aPg[2], nPg); | |||
| 14829 | if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4); | |||
| 14830 | if( nIdx>0 ){ | |||
| 14831 | memcpy(&aPg[nPg], aIdx, nIdx); | |||
| 14832 | nPg += nIdx; | |||
| 14833 | } | |||
| 14834 | sqlite3_freesqlite3_api->free(aIdx); | |||
| 14835 | ||||
| 14836 | /* Write the new page to disk and exit the loop */ | |||
| 14837 | assert( nPg>4 || fts5GetU16(aPg)==0 )((void) (0)); | |||
| 14838 | fts5DataWrite(p, iRowid, aPg, nPg); | |||
| 14839 | break; | |||
| 14840 | } | |||
| 14841 | } | |||
| 14842 | fts5DataRelease(pLeaf); | |||
| 14843 | } | |||
| 14844 | ||||
| 14845 | /* | |||
| 14846 | ** Completely remove the entry that pSeg currently points to from | |||
| 14847 | ** the database. | |||
| 14848 | */ | |||
| 14849 | static void fts5DoSecureDelete( | |||
| 14850 | Fts5Index *p, | |||
| 14851 | Fts5SegIter *pSeg | |||
| 14852 | ){ | |||
| 14853 | const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE1); | |||
| 14854 | int iSegid = pSeg->pSeg->iSegid; | |||
| 14855 | u8 *aPg = pSeg->pLeaf->p; | |||
| 14856 | int nPg = pSeg->pLeaf->nn; | |||
| 14857 | int iPgIdx = pSeg->pLeaf->szLeaf; /* Offset of page footer */ | |||
| 14858 | ||||
| 14859 | u64 iDelta = 0; | |||
| 14860 | int iNextOff = 0; | |||
| 14861 | int iOff = 0; | |||
| 14862 | int nIdx = 0; | |||
| 14863 | u8 *aIdx = 0; | |||
| 14864 | int bLastInDoclist = 0; | |||
| 14865 | int iIdx = 0; | |||
| 14866 | int iStart = 0; | |||
| 14867 | int iDelKeyOff = 0; /* Offset of deleted key, if any */ | |||
| 14868 | ||||
| 14869 | nIdx = nPg-iPgIdx; | |||
| 14870 | aIdx = sqlite3Fts5MallocZero(&p->rc, ((i64)nIdx)+16); | |||
| 14871 | if( p->rc ) return; | |||
| 14872 | memcpy(aIdx, &aPg[iPgIdx], nIdx); | |||
| 14873 | ||||
| 14874 | /* At this point segment iterator pSeg points to the entry | |||
| 14875 | ** this function should remove from the b-tree segment. | |||
| 14876 | ** | |||
| 14877 | ** In detail=full or detail=column mode, pSeg->iLeafOffset is the | |||
| 14878 | ** offset of the first byte in the position-list for the entry to | |||
| 14879 | ** remove. Immediately before this comes two varints that will also | |||
| 14880 | ** need to be removed: | |||
| 14881 | ** | |||
| 14882 | ** + the rowid or delta rowid value for the entry, and | |||
| 14883 | ** + the size of the position list in bytes. | |||
| 14884 | ** | |||
| 14885 | ** Or, in detail=none mode, there is a single varint prior to | |||
| 14886 | ** pSeg->iLeafOffset - the rowid or delta rowid value. | |||
| 14887 | ** | |||
| 14888 | ** This block sets the following variables: | |||
| 14889 | ** | |||
| 14890 | ** iStart: | |||
| 14891 | ** The offset of the first byte of the rowid or delta-rowid | |||
| 14892 | ** value for the doclist entry being removed. | |||
| 14893 | ** | |||
| 14894 | ** iDelta: | |||
| 14895 | ** The value of the rowid or delta-rowid value for the doclist | |||
| 14896 | ** entry being removed. | |||
| 14897 | ** | |||
| 14898 | ** iNextOff: | |||
| 14899 | ** The offset of the next entry following the position list | |||
| 14900 | ** for the one being removed. If the position list for this | |||
| 14901 | ** entry overflows onto the next leaf page, this value will be | |||
| 14902 | ** greater than pLeaf->szLeaf. | |||
| 14903 | */ | |||
| 14904 | { | |||
| 14905 | int iSOP; /* Start-Of-Position-list */ | |||
| 14906 | if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){ | |||
| 14907 | iStart = pSeg->iTermLeafOffset; | |||
| 14908 | }else{ | |||
| 14909 | iStart = fts5GetU16(&aPg[0]); | |||
| 14910 | } | |||
| 14911 | if( iStart>nPg ){ | |||
| 14912 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 14913 | sqlite3_freesqlite3_api->free(aIdx); | |||
| 14914 | return; | |||
| 14915 | } | |||
| 14916 | ||||
| 14917 | iSOP = iStart + fts5GetVarintsqlite3Fts5GetVarint(&aPg[iStart], &iDelta); | |||
| 14918 | assert_nc( iSOP<=pSeg->iLeafOffset )((void) (0)); | |||
| 14919 | ||||
| 14920 | if( bDetailNone ){ | |||
| 14921 | while( iSOP<pSeg->iLeafOffset ){ | |||
| 14922 | if( aPg[iSOP]==0x00 ) iSOP++; | |||
| 14923 | if( aPg[iSOP]==0x00 ) iSOP++; | |||
| 14924 | iStart = iSOP; | |||
| 14925 | iSOP = iStart + fts5GetVarintsqlite3Fts5GetVarint(&aPg[iStart], &iDelta); | |||
| 14926 | } | |||
| 14927 | ||||
| 14928 | iNextOff = iSOP; | |||
| 14929 | if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; | |||
| 14930 | if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; | |||
| 14931 | ||||
| 14932 | }else{ | |||
| 14933 | int nPos = 0; | |||
| 14934 | iSOP += fts5GetVarint32(&aPg[iSOP], nPos)sqlite3Fts5GetVarint32(&aPg[iSOP],(u32*)&(nPos)); | |||
| 14935 | while( iSOP<pSeg->iLeafOffset ){ | |||
| 14936 | iStart = iSOP + (nPos/2); | |||
| 14937 | iSOP = iStart + fts5GetVarintsqlite3Fts5GetVarint(&aPg[iStart], &iDelta); | |||
| 14938 | iSOP += fts5GetVarint32(&aPg[iSOP], nPos)sqlite3Fts5GetVarint32(&aPg[iSOP],(u32*)&(nPos)); | |||
| 14939 | } | |||
| 14940 | assert_nc( iSOP==pSeg->iLeafOffset )((void) (0)); | |||
| 14941 | iNextOff = iSOP + pSeg->nPos; | |||
| 14942 | } | |||
| 14943 | } | |||
| 14944 | ||||
| 14945 | iOff = iStart; | |||
| 14946 | ||||
| 14947 | /* If the position-list for the entry being removed flows over past | |||
| 14948 | ** the end of this page, delete the portion of the position-list on the | |||
| 14949 | ** next page and beyond. | |||
| 14950 | ** | |||
| 14951 | ** Set variable bLastInDoclist to true if this entry happens | |||
| 14952 | ** to be the last rowid in the doclist for its term. */ | |||
| 14953 | if( iNextOff>=iPgIdx ){ | |||
| 14954 | int pgno = pSeg->iLeafPgno+1; | |||
| 14955 | fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); | |||
| 14956 | iNextOff = iPgIdx; | |||
| 14957 | } | |||
| 14958 | ||||
| 14959 | if( pSeg->bDel==0 ){ | |||
| 14960 | if( iNextOff!=iPgIdx ){ | |||
| 14961 | /* Loop through the page-footer. If iNextOff (offset of the | |||
| 14962 | ** entry following the one we are removing) is equal to the | |||
| 14963 | ** offset of a key on this page, then the entry is the last | |||
| 14964 | ** in its doclist. */ | |||
| 14965 | int iKeyOff = 0; | |||
| 14966 | for(iIdx=0; iIdx<nIdx; /* no-op */){ | |||
| 14967 | u32 iVal = 0; | |||
| 14968 | iIdx += fts5GetVarint32(&aIdx[iIdx], iVal)sqlite3Fts5GetVarint32(&aIdx[iIdx],(u32*)&(iVal)); | |||
| 14969 | iKeyOff += iVal; | |||
| 14970 | if( iKeyOff==iNextOff ){ | |||
| 14971 | bLastInDoclist = 1; | |||
| 14972 | } | |||
| 14973 | } | |||
| 14974 | } | |||
| 14975 | ||||
| 14976 | /* If this is (a) the first rowid on a page and (b) is not followed by | |||
| 14977 | ** another position list on the same page, set the "first-rowid" field | |||
| 14978 | ** of the header to 0. */ | |||
| 14979 | if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){ | |||
| 14980 | fts5PutU16(&aPg[0], 0); | |||
| 14981 | } | |||
| 14982 | } | |||
| 14983 | ||||
| 14984 | if( pSeg->bDel ){ | |||
| 14985 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta); | |||
| 14986 | aPg[iOff++] = 0x01; | |||
| 14987 | }else if( bLastInDoclist==0 ){ | |||
| 14988 | if( iNextOff!=iPgIdx ){ | |||
| 14989 | u64 iNextDelta = 0; | |||
| 14990 | iNextOff += fts5GetVarintsqlite3Fts5GetVarint(&aPg[iNextOff], &iNextDelta); | |||
| 14991 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta); | |||
| 14992 | } | |||
| 14993 | }else if( | |||
| 14994 | pSeg->iLeafPgno==pSeg->iTermLeafPgno | |||
| 14995 | && iStart==pSeg->iTermLeafOffset | |||
| 14996 | ){ | |||
| 14997 | /* The entry being removed was the only position list in its | |||
| 14998 | ** doclist. Therefore the term needs to be removed as well. */ | |||
| 14999 | int iKey = 0; | |||
| 15000 | int iKeyOff = 0; | |||
| 15001 | ||||
| 15002 | /* Set iKeyOff to the offset of the term that will be removed - the | |||
| 15003 | ** last offset in the footer that is not greater than iStart. */ | |||
| 15004 | for(iIdx=0; iIdx<nIdx; iKey++){ | |||
| 15005 | u32 iVal = 0; | |||
| 15006 | iIdx += fts5GetVarint32(&aIdx[iIdx], iVal)sqlite3Fts5GetVarint32(&aIdx[iIdx],(u32*)&(iVal)); | |||
| 15007 | if( (iKeyOff+iVal)>(u32)iStart ) break; | |||
| 15008 | iKeyOff += iVal; | |||
| 15009 | } | |||
| 15010 | assert_nc( iKey>=1 )((void) (0)); | |||
| 15011 | ||||
| 15012 | /* Set iDelKeyOff to the value of the footer entry to remove from | |||
| 15013 | ** the page. */ | |||
| 15014 | iDelKeyOff = iOff = iKeyOff; | |||
| 15015 | ||||
| 15016 | if( iNextOff!=iPgIdx ){ | |||
| 15017 | /* This is the only position-list associated with the term, and there | |||
| 15018 | ** is another term following it on this page. So the subsequent term | |||
| 15019 | ** needs to be moved to replace the term associated with the entry | |||
| 15020 | ** being removed. */ | |||
| 15021 | u64 nPrefix = 0; | |||
| 15022 | u64 nSuffix = 0; | |||
| 15023 | u64 nPrefix2 = 0; | |||
| 15024 | u64 nSuffix2 = 0; | |||
| 15025 | ||||
| 15026 | iDelKeyOff = iNextOff; | |||
| 15027 | iNextOff += fts5GetVarintsqlite3Fts5GetVarint(&aPg[iNextOff], &nPrefix2); | |||
| 15028 | iNextOff += fts5GetVarintsqlite3Fts5GetVarint(&aPg[iNextOff], &nSuffix2); | |||
| 15029 | ||||
| 15030 | if( iKey!=1 ){ | |||
| 15031 | iKeyOff += fts5GetVarintsqlite3Fts5GetVarint(&aPg[iKeyOff], &nPrefix); | |||
| 15032 | } | |||
| 15033 | iKeyOff += fts5GetVarintsqlite3Fts5GetVarint(&aPg[iKeyOff], &nSuffix); | |||
| 15034 | ||||
| 15035 | nPrefix = MIN(nPrefix, nPrefix2)(((nPrefix) < (nPrefix2)) ? (nPrefix) : (nPrefix2)); | |||
| 15036 | nSuffix = (nPrefix2 + nSuffix2) - nPrefix; | |||
| 15037 | ||||
| 15038 | if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){ | |||
| 15039 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 15040 | }else{ | |||
| 15041 | if( iKey!=1 ){ | |||
| 15042 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); | |||
| 15043 | } | |||
| 15044 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); | |||
| 15045 | if( nPrefix2>(u64)pSeg->term.n ){ | |||
| 15046 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 15047 | }else if( nPrefix2>nPrefix ){ | |||
| 15048 | memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); | |||
| 15049 | iOff += (nPrefix2-nPrefix); | |||
| 15050 | } | |||
| 15051 | memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2); | |||
| 15052 | iOff += nSuffix2; | |||
| 15053 | iNextOff += nSuffix2; | |||
| 15054 | } | |||
| 15055 | } | |||
| 15056 | }else if( iStart==4 ){ | |||
| 15057 | int iPgno; | |||
| 15058 | ||||
| 15059 | assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno )((void) (0)); | |||
| 15060 | /* The entry being removed may be the only position list in | |||
| 15061 | ** its doclist. */ | |||
| 15062 | for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ | |||
| 15063 | Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(iPgno)) )); | |||
| 15064 | int bEmpty = (pPg && pPg->nn==4); | |||
| 15065 | fts5DataRelease(pPg); | |||
| 15066 | if( bEmpty==0 ) break; | |||
| 15067 | } | |||
| 15068 | ||||
| 15069 | if( iPgno==pSeg->iTermLeafPgno ){ | |||
| 15070 | i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pSeg->iTermLeafPgno )) ); | |||
| 15071 | Fts5Data *pTerm = fts5DataRead(p, iId); | |||
| 15072 | if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ | |||
| 15073 | u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; | |||
| 15074 | int nTermIdx = pTerm->nn - pTerm->szLeaf; | |||
| 15075 | int iTermIdx = 0; | |||
| 15076 | i64 iTermOff = 0; | |||
| 15077 | ||||
| 15078 | while( 1 ){ | |||
| 15079 | u32 iVal = 0; | |||
| 15080 | int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal)sqlite3Fts5GetVarint32(&aTermIdx[iTermIdx],(u32*)&(iVal )); | |||
| 15081 | iTermOff += iVal; | |||
| 15082 | if( (iTermIdx+nByte)>=nTermIdx ) break; | |||
| 15083 | iTermIdx += nByte; | |||
| 15084 | } | |||
| 15085 | nTermIdx = iTermIdx; | |||
| 15086 | ||||
| 15087 | if( iTermOff>pTerm->szLeaf ){ | |||
| 15088 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 15089 | }else{ | |||
| 15090 | memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); | |||
| 15091 | fts5PutU16(&pTerm->p[2], iTermOff); | |||
| 15092 | fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); | |||
| 15093 | if( nTermIdx==0 ){ | |||
| 15094 | fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); | |||
| 15095 | } | |||
| 15096 | } | |||
| 15097 | } | |||
| 15098 | fts5DataRelease(pTerm); | |||
| 15099 | } | |||
| 15100 | } | |||
| 15101 | ||||
| 15102 | /* Assuming no error has occurred, this block does final edits to the | |||
| 15103 | ** leaf page before writing it back to disk. Input variables are: | |||
| 15104 | ** | |||
| 15105 | ** nPg: Total initial size of leaf page. | |||
| 15106 | ** iPgIdx: Initial offset of page footer. | |||
| 15107 | ** | |||
| 15108 | ** iOff: Offset to move data to | |||
| 15109 | ** iNextOff: Offset to move data from | |||
| 15110 | */ | |||
| 15111 | if( p->rc==SQLITE_OK0 ){ | |||
| 15112 | const int nMove = nPg - iNextOff; /* Number of bytes to move */ | |||
| 15113 | int nShift = iNextOff - iOff; /* Distance to move them */ | |||
| 15114 | ||||
| 15115 | int iPrevKeyOut = 0; | |||
| 15116 | int iKeyIn = 0; | |||
| 15117 | ||||
| 15118 | if( nMove>0 ){ | |||
| 15119 | memmove(&aPg[iOff], &aPg[iNextOff], nMove); | |||
| 15120 | } | |||
| 15121 | iPgIdx -= nShift; | |||
| 15122 | nPg = iPgIdx; | |||
| 15123 | fts5PutU16(&aPg[2], iPgIdx); | |||
| 15124 | ||||
| 15125 | for(iIdx=0; iIdx<nIdx; /* no-op */){ | |||
| 15126 | u32 iVal = 0; | |||
| 15127 | iIdx += fts5GetVarint32(&aIdx[iIdx], iVal)sqlite3Fts5GetVarint32(&aIdx[iIdx],(u32*)&(iVal)); | |||
| 15128 | iKeyIn += iVal; | |||
| 15129 | if( iKeyIn!=iDelKeyOff ){ | |||
| 15130 | int iKeyOut = (iKeyIn - (iKeyIn>iOff ? nShift : 0)); | |||
| 15131 | nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOut - iPrevKeyOut); | |||
| 15132 | iPrevKeyOut = iKeyOut; | |||
| 15133 | } | |||
| 15134 | } | |||
| 15135 | ||||
| 15136 | if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ | |||
| 15137 | fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); | |||
| 15138 | } | |||
| 15139 | ||||
| 15140 | assert_nc( nPg>4 || fts5GetU16(aPg)==0 )((void) (0)); | |||
| 15141 | fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pSeg->iLeafPgno )) ), aPg, nPg); | |||
| 15142 | } | |||
| 15143 | sqlite3_freesqlite3_api->free(aIdx); | |||
| 15144 | } | |||
| 15145 | ||||
| 15146 | /* | |||
| 15147 | ** This is called as part of flushing a delete to disk in 'secure-delete' | |||
| 15148 | ** mode. It edits the segments within the database described by argument | |||
| 15149 | ** pStruct to remove the entries for term zTerm, rowid iRowid. | |||
| 15150 | ** | |||
| 15151 | ** Return SQLITE_OK if successful, or an SQLite error code if an error | |||
| 15152 | ** has occurred. Any error code is also stored in the Fts5Index handle. | |||
| 15153 | */ | |||
| 15154 | static int fts5FlushSecureDelete( | |||
| 15155 | Fts5Index *p, | |||
| 15156 | Fts5Structure *pStruct, | |||
| 15157 | const char *zTerm, | |||
| 15158 | int nTerm, | |||
| 15159 | i64 iRowid | |||
| 15160 | ){ | |||
| 15161 | const int f = FTS5INDEX_QUERY_SKIPHASH0x0040; | |||
| 15162 | Fts5Iter *pIter = 0; /* Used to find term instance */ | |||
| 15163 | ||||
| 15164 | /* If the version number has not been set to SECUREDELETE, do so now. */ | |||
| 15165 | if( p->pConfig->iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE5 ){ | |||
| 15166 | Fts5Config *pConfig = p->pConfig; | |||
| 15167 | sqlite3_stmt *pStmt = 0; | |||
| 15168 | fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 15169 | "REPLACE INTO %Q.'%q_config' VALUES ('version', %d)", | |||
| 15170 | pConfig->zDb, pConfig->zName, FTS5_CURRENT_VERSION_SECUREDELETE5 | |||
| 15171 | )); | |||
| 15172 | if( p->rc==SQLITE_OK0 ){ | |||
| 15173 | int rc; | |||
| 15174 | sqlite3_stepsqlite3_api->step(pStmt); | |||
| 15175 | rc = sqlite3_finalizesqlite3_api->finalize(pStmt); | |||
| 15176 | if( p->rc==SQLITE_OK0 ) p->rc = rc; | |||
| 15177 | pConfig->iCookie++; | |||
| 15178 | pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE5; | |||
| 15179 | } | |||
| 15180 | } | |||
| 15181 | ||||
| 15182 | fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); | |||
| 15183 | if( fts5MultiIterEof(p, pIter)==0 ){ | |||
| 15184 | i64 iThis = fts5MultiIterRowid(pIter); | |||
| 15185 | if( iThis<iRowid ){ | |||
| 15186 | fts5MultiIterNextFrom(p, pIter, iRowid); | |||
| 15187 | } | |||
| 15188 | ||||
| 15189 | if( p->rc==SQLITE_OK0 | |||
| 15190 | && fts5MultiIterEof(p, pIter)==0 | |||
| 15191 | && iRowid==fts5MultiIterRowid(pIter) | |||
| 15192 | ){ | |||
| 15193 | Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; | |||
| 15194 | fts5DoSecureDelete(p, pSeg); | |||
| 15195 | } | |||
| 15196 | } | |||
| 15197 | ||||
| 15198 | fts5MultiIterFree(pIter); | |||
| 15199 | return p->rc; | |||
| 15200 | } | |||
| 15201 | ||||
| 15202 | ||||
| 15203 | /* | |||
| 15204 | ** Flush the contents of in-memory hash table iHash to a new level-0 | |||
| 15205 | ** segment on disk. Also update the corresponding structure record. | |||
| 15206 | ** | |||
| 15207 | ** If an error occurs, set the Fts5Index.rc error code. If an error has | |||
| 15208 | ** already occurred, this function is a no-op. | |||
| 15209 | */ | |||
| 15210 | static void fts5FlushOneHash(Fts5Index *p){ | |||
| 15211 | Fts5Hash *pHash = p->pHash; | |||
| 15212 | Fts5Structure *pStruct; | |||
| 15213 | int iSegid; | |||
| 15214 | int pgnoLast = 0; /* Last leaf page number in segment */ | |||
| 15215 | ||||
| 15216 | /* Obtain a reference to the index structure and allocate a new segment-id | |||
| 15217 | ** for the new level-0 segment. */ | |||
| 15218 | pStruct = fts5StructureRead(p); | |||
| 15219 | fts5StructureInvalidate(p); | |||
| 15220 | ||||
| 15221 | if( sqlite3Fts5HashIsEmpty(pHash)==0 ){ | |||
| 15222 | iSegid = fts5AllocateSegid(p, pStruct); | |||
| 15223 | if( iSegid ){ | |||
| 15224 | const int pgsz = p->pConfig->pgsz; | |||
| 15225 | int eDetail = p->pConfig->eDetail; | |||
| 15226 | int bSecureDelete = p->pConfig->bSecureDelete; | |||
| 15227 | Fts5StructureSegment *pSeg; /* New segment within pStruct */ | |||
| 15228 | Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ | |||
| 15229 | Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ | |||
| 15230 | ||||
| 15231 | Fts5SegWriter writer; | |||
| 15232 | fts5WriteInit(p, &writer, iSegid); | |||
| 15233 | ||||
| 15234 | pBuf = &writer.writer.buf; | |||
| 15235 | pPgidx = &writer.writer.pgidx; | |||
| 15236 | ||||
| 15237 | /* fts5WriteInit() should have initialized the buffers to (most likely) | |||
| 15238 | ** the maximum space required. */ | |||
| 15239 | assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) )((void) (0)); | |||
| 15240 | assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) )((void) (0)); | |||
| 15241 | ||||
| 15242 | /* Begin scanning through hash table entries. This loop runs once for each | |||
| 15243 | ** term/doclist currently stored within the hash table. */ | |||
| 15244 | if( p->rc==SQLITE_OK0 ){ | |||
| 15245 | p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); | |||
| 15246 | } | |||
| 15247 | while( p->rc==SQLITE_OK0 && 0==sqlite3Fts5HashScanEof(pHash) ){ | |||
| 15248 | const char *zTerm; /* Buffer containing term */ | |||
| 15249 | int nTerm; /* Size of zTerm in bytes */ | |||
| 15250 | const u8 *pDoclist; /* Pointer to doclist for this term */ | |||
| 15251 | int nDoclist; /* Size of doclist in bytes */ | |||
| 15252 | ||||
| 15253 | /* Get the term and doclist for this entry. */ | |||
| 15254 | sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist); | |||
| 15255 | if( bSecureDelete==0 ){ | |||
| 15256 | fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); | |||
| 15257 | if( p->rc!=SQLITE_OK0 ) break; | |||
| 15258 | assert( writer.bFirstRowidInPage==0 )((void) (0)); | |||
| 15259 | } | |||
| 15260 | ||||
| 15261 | if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ | |||
| 15262 | /* The entire doclist will fit on the current leaf. */ | |||
| 15263 | fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist){ ((void) (0)); memcpy(&(pBuf)->p[(pBuf)->n], pDoclist , nDoclist); (pBuf)->n += nDoclist; }; | |||
| 15264 | }else{ | |||
| 15265 | int bTermWritten = !bSecureDelete; | |||
| 15266 | i64 iRowid = 0; | |||
| 15267 | i64 iPrev = 0; | |||
| 15268 | int iOff = 0; | |||
| 15269 | ||||
| 15270 | /* The entire doclist will not fit on this leaf. The following | |||
| 15271 | ** loop iterates through the poslists that make up the current | |||
| 15272 | ** doclist. */ | |||
| 15273 | while( p->rc==SQLITE_OK0 && iOff<nDoclist ){ | |||
| 15274 | u64 iDelta = 0; | |||
| 15275 | iOff += fts5GetVarintsqlite3Fts5GetVarint(&pDoclist[iOff], &iDelta); | |||
| 15276 | iRowid += iDelta; | |||
| 15277 | ||||
| 15278 | /* If in secure delete mode, and if this entry in the poslist is | |||
| 15279 | ** in fact a delete, then edit the existing segments directly | |||
| 15280 | ** using fts5FlushSecureDelete(). */ | |||
| 15281 | if( bSecureDelete ){ | |||
| 15282 | if( eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 15283 | if( iOff<nDoclist && pDoclist[iOff]==0x00 | |||
| 15284 | && !fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid) | |||
| 15285 | ){ | |||
| 15286 | iOff++; | |||
| 15287 | if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ | |||
| 15288 | iOff++; | |||
| 15289 | nDoclist = 0; | |||
| 15290 | }else{ | |||
| 15291 | continue; | |||
| 15292 | } | |||
| 15293 | } | |||
| 15294 | }else if( (pDoclist[iOff] & 0x01) | |||
| 15295 | && !fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid) | |||
| 15296 | ){ | |||
| 15297 | if( p->rc!=SQLITE_OK0 || pDoclist[iOff]==0x01 ){ | |||
| 15298 | iOff++; | |||
| 15299 | continue; | |||
| 15300 | } | |||
| 15301 | } | |||
| 15302 | } | |||
| 15303 | ||||
| 15304 | if( p->rc==SQLITE_OK0 && bTermWritten==0 ){ | |||
| 15305 | fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); | |||
| 15306 | bTermWritten = 1; | |||
| 15307 | assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 )((void) (0)); | |||
| 15308 | } | |||
| 15309 | ||||
| 15310 | if( writer.bFirstRowidInPage ){ | |||
| 15311 | fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ | |||
| 15312 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); | |||
| 15313 | writer.bFirstRowidInPage = 0; | |||
| 15314 | fts5WriteDlidxAppend(p, &writer, iRowid); | |||
| 15315 | }else{ | |||
| 15316 | u64 iRowidDelta = (u64)iRowid - (u64)iPrev; | |||
| 15317 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowidDelta); | |||
| 15318 | } | |||
| 15319 | if( p->rc!=SQLITE_OK0 ) break; | |||
| 15320 | assert( pBuf->n<=pBuf->nSpace )((void) (0)); | |||
| 15321 | iPrev = iRowid; | |||
| 15322 | ||||
| 15323 | if( eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 15324 | if( iOff<nDoclist && pDoclist[iOff]==0 ){ | |||
| 15325 | pBuf->p[pBuf->n++] = 0; | |||
| 15326 | iOff++; | |||
| 15327 | if( iOff<nDoclist && pDoclist[iOff]==0 ){ | |||
| 15328 | pBuf->p[pBuf->n++] = 0; | |||
| 15329 | iOff++; | |||
| 15330 | } | |||
| 15331 | } | |||
| 15332 | if( (pBuf->n + pPgidx->n)>=pgsz ){ | |||
| 15333 | fts5WriteFlushLeaf(p, &writer); | |||
| 15334 | } | |||
| 15335 | }else{ | |||
| 15336 | int bDel = 0; | |||
| 15337 | int nPos = 0; | |||
| 15338 | int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel); | |||
| 15339 | if( bDel && bSecureDelete ){ | |||
| 15340 | fts5BufferAppendVarint(&p->rc, pBuf, nPos*2)sqlite3Fts5BufferAppendVarint(&p->rc,pBuf,(i64)nPos*2); | |||
| 15341 | iOff += nCopy; | |||
| 15342 | nCopy = nPos; | |||
| 15343 | }else{ | |||
| 15344 | nCopy += nPos; | |||
| 15345 | } | |||
| 15346 | if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ | |||
| 15347 | /* The entire poslist will fit on the current leaf. So copy | |||
| 15348 | ** it in one go. */ | |||
| 15349 | fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy){ ((void) (0)); memcpy(&(pBuf)->p[(pBuf)->n], & pDoclist[iOff], nCopy); (pBuf)->n += nCopy; }; | |||
| 15350 | }else{ | |||
| 15351 | /* The entire poslist will not fit on this leaf. So it needs | |||
| 15352 | ** to be broken into sections. The only qualification being | |||
| 15353 | ** that each varint must be stored contiguously. */ | |||
| 15354 | const u8 *pPoslist = &pDoclist[iOff]; | |||
| 15355 | int iPos = 0; | |||
| 15356 | while( p->rc==SQLITE_OK0 ){ | |||
| 15357 | int nSpace = pgsz - pBuf->n - pPgidx->n; | |||
| 15358 | int n = 0; | |||
| 15359 | if( (nCopy - iPos)<=nSpace ){ | |||
| 15360 | n = nCopy - iPos; | |||
| 15361 | }else{ | |||
| 15362 | n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); | |||
| 15363 | } | |||
| 15364 | assert( n>0 )((void) (0)); | |||
| 15365 | fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n){ ((void) (0)); memcpy(&(pBuf)->p[(pBuf)->n], & pPoslist[iPos], n); (pBuf)->n += n; }; | |||
| 15366 | iPos += n; | |||
| 15367 | if( (pBuf->n + pPgidx->n)>=pgsz ){ | |||
| 15368 | fts5WriteFlushLeaf(p, &writer); | |||
| 15369 | } | |||
| 15370 | if( iPos>=nCopy ) break; | |||
| 15371 | } | |||
| 15372 | } | |||
| 15373 | iOff += nCopy; | |||
| 15374 | } | |||
| 15375 | } | |||
| 15376 | } | |||
| 15377 | ||||
| 15378 | /* TODO2: Doclist terminator written here. */ | |||
| 15379 | /* pBuf->p[pBuf->n++] = '\0'; */ | |||
| 15380 | assert( pBuf->n<=pBuf->nSpace )((void) (0)); | |||
| 15381 | if( p->rc==SQLITE_OK0 ) sqlite3Fts5HashScanNext(pHash); | |||
| 15382 | } | |||
| 15383 | fts5WriteFinish(p, &writer, &pgnoLast); | |||
| 15384 | ||||
| 15385 | assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 )((void) (0)); | |||
| 15386 | if( pgnoLast>0 ){ | |||
| 15387 | /* Update the Fts5Structure. It is written back to the database by the | |||
| 15388 | ** fts5StructureRelease() call below. */ | |||
| 15389 | if( pStruct->nLevel==0 ){ | |||
| 15390 | fts5StructureAddLevel(&p->rc, &pStruct); | |||
| 15391 | } | |||
| 15392 | fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); | |||
| 15393 | if( p->rc==SQLITE_OK0 ){ | |||
| 15394 | pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; | |||
| 15395 | pSeg->iSegid = iSegid; | |||
| 15396 | pSeg->pgnoFirst = 1; | |||
| 15397 | pSeg->pgnoLast = pgnoLast; | |||
| 15398 | if( pStruct->nOriginCntr>0 ){ | |||
| 15399 | pSeg->iOrigin1 = pStruct->nOriginCntr; | |||
| 15400 | pSeg->iOrigin2 = pStruct->nOriginCntr; | |||
| 15401 | pSeg->nEntry = p->nPendingRow; | |||
| 15402 | pStruct->nOriginCntr++; | |||
| 15403 | } | |||
| 15404 | pStruct->nSegment++; | |||
| 15405 | } | |||
| 15406 | fts5StructurePromote(p, 0, pStruct); | |||
| 15407 | } | |||
| 15408 | } | |||
| 15409 | } | |||
| 15410 | ||||
| 15411 | fts5IndexAutomerge(p, &pStruct, pgnoLast + p->nContentlessDelete); | |||
| 15412 | fts5IndexCrisismerge(p, &pStruct); | |||
| 15413 | fts5StructureWrite(p, pStruct); | |||
| 15414 | fts5StructureRelease(pStruct); | |||
| 15415 | } | |||
| 15416 | ||||
| 15417 | /* | |||
| 15418 | ** Flush any data stored in the in-memory hash tables to the database. | |||
| 15419 | */ | |||
| 15420 | static void fts5IndexFlush(Fts5Index *p){ | |||
| 15421 | /* Unless it is empty, flush the hash table to disk */ | |||
| 15422 | if( p->flushRc ){ | |||
| 15423 | p->rc = p->flushRc; | |||
| 15424 | return; | |||
| 15425 | } | |||
| 15426 | if( p->nPendingData || p->nContentlessDelete ){ | |||
| 15427 | assert( p->pHash )((void) (0)); | |||
| 15428 | fts5FlushOneHash(p); | |||
| 15429 | if( p->rc==SQLITE_OK0 ){ | |||
| 15430 | sqlite3Fts5HashClear(p->pHash); | |||
| 15431 | p->nPendingData = 0; | |||
| 15432 | p->nPendingRow = 0; | |||
| 15433 | p->nContentlessDelete = 0; | |||
| 15434 | }else if( p->nPendingData || p->nContentlessDelete ){ | |||
| 15435 | p->flushRc = p->rc; | |||
| 15436 | } | |||
| 15437 | } | |||
| 15438 | } | |||
| 15439 | ||||
| 15440 | static Fts5Structure *fts5IndexOptimizeStruct( | |||
| 15441 | Fts5Index *p, | |||
| 15442 | Fts5Structure *pStruct | |||
| 15443 | ){ | |||
| 15444 | Fts5Structure *pNew = 0; | |||
| 15445 | sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1)(__builtin_offsetof(Fts5Structure, aLevel) + (1)*sizeof(Fts5StructureLevel )); | |||
| 15446 | int nSeg = pStruct->nSegment; | |||
| 15447 | int i; | |||
| 15448 | ||||
| 15449 | /* Figure out if this structure requires optimization. A structure does | |||
| 15450 | ** not require optimization if either: | |||
| 15451 | ** | |||
| 15452 | ** 1. it consists of fewer than two segments, or | |||
| 15453 | ** 2. all segments are on the same level, or | |||
| 15454 | ** 3. all segments except one are currently inputs to a merge operation. | |||
| 15455 | ** | |||
| 15456 | ** In the first case, if there are no tombstone hash pages, return NULL. In | |||
| 15457 | ** the second, increment the ref-count on *pStruct and return a copy of the | |||
| 15458 | ** pointer to it. | |||
| 15459 | */ | |||
| 15460 | if( nSeg==0 ) return 0; | |||
| 15461 | for(i=0; i<pStruct->nLevel; i++){ | |||
| 15462 | int nThis = pStruct->aLevel[i].nSeg; | |||
| 15463 | int nMerge = pStruct->aLevel[i].nMerge; | |||
| 15464 | if( nThis>0 && (nThis==nSeg || (nThis==nSeg-1 && nMerge==nThis)) ){ | |||
| 15465 | if( nSeg==1 && nThis==1 && pStruct->aLevel[i].aSeg[0].nPgTombstone==0 ){ | |||
| 15466 | return 0; | |||
| 15467 | } | |||
| 15468 | fts5StructureRef(pStruct); | |||
| 15469 | return pStruct; | |||
| 15470 | } | |||
| 15471 | assert( pStruct->aLevel[i].nMerge<=nThis )((void) (0)); | |||
| 15472 | } | |||
| 15473 | ||||
| 15474 | nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); | |||
| 15475 | assert( nByte==(i64)SZ_FTS5STRUCTURE(pStruct->nLevel+2) )((void) (0)); | |||
| 15476 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); | |||
| 15477 | ||||
| 15478 | if( pNew ){ | |||
| 15479 | Fts5StructureLevel *pLvl; | |||
| 15480 | nByte = nSeg * sizeof(Fts5StructureSegment); | |||
| 15481 | pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL)(((pStruct->nLevel+1) < (64)) ? (pStruct->nLevel+1) : (64)); | |||
| 15482 | pNew->nRef = 1; | |||
| 15483 | pNew->nWriteCounter = pStruct->nWriteCounter; | |||
| 15484 | pNew->nOriginCntr = pStruct->nOriginCntr; | |||
| 15485 | pLvl = &pNew->aLevel[pNew->nLevel-1]; | |||
| 15486 | pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte); | |||
| 15487 | if( pLvl->aSeg ){ | |||
| 15488 | int iLvl, iSeg; | |||
| 15489 | int iSegOut = 0; | |||
| 15490 | /* Iterate through all segments, from oldest to newest. Add them to | |||
| 15491 | ** the new Fts5Level object so that pLvl->aSeg[0] is the oldest | |||
| 15492 | ** segment in the data structure. */ | |||
| 15493 | for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){ | |||
| 15494 | for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){ | |||
| 15495 | pLvl->aSeg[iSegOut] = pStruct->aLevel[iLvl].aSeg[iSeg]; | |||
| 15496 | iSegOut++; | |||
| 15497 | } | |||
| 15498 | } | |||
| 15499 | pNew->nSegment = pLvl->nSeg = nSeg; | |||
| 15500 | }else{ | |||
| 15501 | sqlite3_freesqlite3_api->free(pNew); | |||
| 15502 | pNew = 0; | |||
| 15503 | } | |||
| 15504 | } | |||
| 15505 | ||||
| 15506 | return pNew; | |||
| 15507 | } | |||
| 15508 | ||||
| 15509 | static int sqlite3Fts5IndexOptimize(Fts5Index *p){ | |||
| 15510 | Fts5Structure *pStruct; | |||
| 15511 | Fts5Structure *pNew = 0; | |||
| 15512 | ||||
| 15513 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 15514 | fts5IndexFlush(p); | |||
| 15515 | assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 )((void) (0)); | |||
| 15516 | pStruct = fts5StructureRead(p); | |||
| 15517 | assert( p->rc!=SQLITE_OK || pStruct!=0 )((void) (0)); | |||
| 15518 | fts5StructureInvalidate(p); | |||
| 15519 | ||||
| 15520 | if( pStruct ){ | |||
| 15521 | pNew = fts5IndexOptimizeStruct(p, pStruct); | |||
| 15522 | } | |||
| 15523 | fts5StructureRelease(pStruct); | |||
| 15524 | ||||
| 15525 | assert( pNew==0 || pNew->nSegment>0 )((void) (0)); | |||
| 15526 | if( pNew ){ | |||
| 15527 | int iLvl; | |||
| 15528 | for(iLvl=0; pNew->aLevel[iLvl].nSeg==0; iLvl++){} | |||
| 15529 | while( p->rc==SQLITE_OK0 && pNew->aLevel[iLvl].nSeg>0 ){ | |||
| 15530 | int nRem = FTS5_OPT_WORK_UNIT1000; | |||
| 15531 | fts5IndexMergeLevel(p, &pNew, iLvl, &nRem); | |||
| 15532 | } | |||
| 15533 | ||||
| 15534 | fts5StructureWrite(p, pNew); | |||
| 15535 | fts5StructureRelease(pNew); | |||
| 15536 | } | |||
| 15537 | ||||
| 15538 | return fts5IndexReturn(p); | |||
| 15539 | } | |||
| 15540 | ||||
| 15541 | /* | |||
| 15542 | ** This is called to implement the special "VALUES('merge', $nMerge)" | |||
| 15543 | ** INSERT command. | |||
| 15544 | */ | |||
| 15545 | static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ | |||
| 15546 | Fts5Structure *pStruct = 0; | |||
| 15547 | ||||
| 15548 | fts5IndexFlush(p); | |||
| 15549 | pStruct = fts5StructureRead(p); | |||
| 15550 | if( pStruct ){ | |||
| 15551 | int nMin = p->pConfig->nUsermerge; | |||
| 15552 | fts5StructureInvalidate(p); | |||
| 15553 | if( nMerge<0 ){ | |||
| 15554 | Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct); | |||
| 15555 | fts5StructureRelease(pStruct); | |||
| 15556 | pStruct = pNew; | |||
| 15557 | nMin = 1; | |||
| 15558 | nMerge = (nMerge==SMALLEST_INT32((int)((-1) - ((int)(0x7fffffff)))) ? LARGEST_INT32((int)(0x7fffffff)) : (nMerge*-1)); | |||
| 15559 | } | |||
| 15560 | if( pStruct && pStruct->nLevel ){ | |||
| 15561 | if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){ | |||
| 15562 | fts5StructureWrite(p, pStruct); | |||
| 15563 | } | |||
| 15564 | } | |||
| 15565 | fts5StructureRelease(pStruct); | |||
| 15566 | } | |||
| 15567 | return fts5IndexReturn(p); | |||
| 15568 | } | |||
| 15569 | ||||
| 15570 | static void fts5AppendRowid( | |||
| 15571 | Fts5Index *p, | |||
| 15572 | u64 iDelta, | |||
| 15573 | Fts5Iter *pUnused, | |||
| 15574 | Fts5Buffer *pBuf | |||
| 15575 | ){ | |||
| 15576 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 15577 | fts5BufferAppendVarint(&p->rc, pBuf, iDelta)sqlite3Fts5BufferAppendVarint(&p->rc,pBuf,(i64)iDelta); | |||
| 15578 | } | |||
| 15579 | ||||
| 15580 | static void fts5AppendPoslist( | |||
| 15581 | Fts5Index *p, | |||
| 15582 | u64 iDelta, | |||
| 15583 | Fts5Iter *pMulti, | |||
| 15584 | Fts5Buffer *pBuf | |||
| 15585 | ){ | |||
| 15586 | int nData = pMulti->base.nData; | |||
| 15587 | int nByte = nData + 9 + 9 + FTS5_DATA_ZERO_PADDING8; | |||
| 15588 | assert( nData>0 )((void) (0)); | |||
| 15589 | if( p->rc==SQLITE_OK0 && 0==fts5BufferGrow(&p->rc, pBuf, nByte)( (u32)((pBuf)->n) + (u32)(nByte) <= (u32)((pBuf)->nSpace ) ? 0 : sqlite3Fts5BufferSize((&p->rc),(pBuf),(nByte)+ (pBuf)->n) ) ){ | |||
| 15590 | fts5BufferSafeAppendVarint(pBuf, iDelta){ (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf )->n], (iDelta)); ((void) (0)); }; | |||
| 15591 | fts5BufferSafeAppendVarint(pBuf, nData*2){ (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf )->n], (nData*2)); ((void) (0)); }; | |||
| 15592 | fts5BufferSafeAppendBlob(pBuf, pMulti->base.pData, nData){ ((void) (0)); memcpy(&(pBuf)->p[(pBuf)->n], pMulti ->base.pData, nData); (pBuf)->n += nData; }; | |||
| 15593 | memset(&pBuf->p[pBuf->n], 0, FTS5_DATA_ZERO_PADDING8); | |||
| 15594 | } | |||
| 15595 | } | |||
| 15596 | ||||
| 15597 | ||||
| 15598 | static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ | |||
| 15599 | u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; | |||
| 15600 | ||||
| 15601 | assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) )((void) (0)); | |||
| 15602 | if( p>=pIter->aEof ){ | |||
| 15603 | pIter->aPoslist = 0; | |||
| 15604 | }else{ | |||
| 15605 | i64 iDelta; | |||
| 15606 | ||||
| 15607 | p += fts5GetVarintsqlite3Fts5GetVarint(p, (u64*)&iDelta); | |||
| 15608 | pIter->iRowid += iDelta; | |||
| 15609 | ||||
| 15610 | /* Read position list size */ | |||
| 15611 | if( p[0] & 0x80 ){ | |||
| 15612 | int nPos; | |||
| 15613 | pIter->nSize = fts5GetVarint32(p, nPos)sqlite3Fts5GetVarint32(p,(u32*)&(nPos)); | |||
| 15614 | pIter->nPoslist = (nPos>>1); | |||
| 15615 | }else{ | |||
| 15616 | pIter->nPoslist = ((int)(p[0])) >> 1; | |||
| 15617 | pIter->nSize = 1; | |||
| 15618 | } | |||
| 15619 | ||||
| 15620 | pIter->aPoslist = p; | |||
| 15621 | if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){ | |||
| 15622 | pIter->aPoslist = 0; | |||
| 15623 | } | |||
| 15624 | } | |||
| 15625 | } | |||
| 15626 | ||||
| 15627 | static void fts5DoclistIterInit( | |||
| 15628 | Fts5Buffer *pBuf, | |||
| 15629 | Fts5DoclistIter *pIter | |||
| 15630 | ){ | |||
| 15631 | memset(pIter, 0, sizeof(*pIter)); | |||
| 15632 | if( pBuf->n>0 ){ | |||
| 15633 | pIter->aPoslist = pBuf->p; | |||
| 15634 | pIter->aEof = &pBuf->p[pBuf->n]; | |||
| 15635 | fts5DoclistIterNext(pIter); | |||
| 15636 | } | |||
| 15637 | } | |||
| 15638 | ||||
| 15639 | #if 0 | |||
| 15640 | /* | |||
| 15641 | ** Append a doclist to buffer pBuf. | |||
| 15642 | ** | |||
| 15643 | ** This function assumes that space within the buffer has already been | |||
| 15644 | ** allocated. | |||
| 15645 | */ | |||
| 15646 | static void fts5MergeAppendDocid({ ((void) (0)); { ((Fts5Buffer *pBuf))->n += sqlite3Fts5PutVarint (&((Fts5Buffer *pBuf))->p[((Fts5Buffer *pBuf))->n], ((u64)(i64 iRowid) - (u64)(i64 *piLastRowid))); ((void) (0)) ; }; (i64 *piLastRowid) = (i64 iRowid); } | |||
| 15647 | Fts5Buffer *pBuf, /* Buffer to write to */{ ((void) (0)); { ((Fts5Buffer *pBuf))->n += sqlite3Fts5PutVarint (&((Fts5Buffer *pBuf))->p[((Fts5Buffer *pBuf))->n], ((u64)(i64 iRowid) - (u64)(i64 *piLastRowid))); ((void) (0)) ; }; (i64 *piLastRowid) = (i64 iRowid); } | |||
| 15648 | i64 *piLastRowid, /* IN/OUT: Previous rowid written (if any) */{ ((void) (0)); { ((Fts5Buffer *pBuf))->n += sqlite3Fts5PutVarint (&((Fts5Buffer *pBuf))->p[((Fts5Buffer *pBuf))->n], ((u64)(i64 iRowid) - (u64)(i64 *piLastRowid))); ((void) (0)) ; }; (i64 *piLastRowid) = (i64 iRowid); } | |||
| 15649 | i64 iRowid /* Rowid to append */{ ((void) (0)); { ((Fts5Buffer *pBuf))->n += sqlite3Fts5PutVarint (&((Fts5Buffer *pBuf))->p[((Fts5Buffer *pBuf))->n], ((u64)(i64 iRowid) - (u64)(i64 *piLastRowid))); ((void) (0)) ; }; (i64 *piLastRowid) = (i64 iRowid); } | |||
| 15650 | ){ ((void) (0)); { ((Fts5Buffer *pBuf))->n += sqlite3Fts5PutVarint (&((Fts5Buffer *pBuf))->p[((Fts5Buffer *pBuf))->n], ((u64)(i64 iRowid) - (u64)(i64 *piLastRowid))); ((void) (0)) ; }; (i64 *piLastRowid) = (i64 iRowid); }{ | |||
| 15651 | assert( pBuf->n!=0 || (*piLastRowid)==0 )((void) (0)); | |||
| 15652 | fts5BufferSafeAppendVarint(pBuf, iRowid - *piLastRowid){ (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf )->n], (iRowid - *piLastRowid)); ((void) (0)); }; | |||
| 15653 | *piLastRowid = iRowid; | |||
| 15654 | } | |||
| 15655 | #endif | |||
| 15656 | ||||
| 15657 | #define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid){ ((void) (0)); { ((pBuf))->n += sqlite3Fts5PutVarint(& ((pBuf))->p[((pBuf))->n], ((u64)(iRowid) - (u64)(iLastRowid ))); ((void) (0)); }; (iLastRowid) = (iRowid); } { \ | |||
| 15658 | assert( (pBuf)->n!=0 || (iLastRowid)==0 )((void) (0)); \ | |||
| 15659 | fts5BufferSafeAppendVarint((pBuf), (u64)(iRowid) - (u64)(iLastRowid)){ ((pBuf))->n += sqlite3Fts5PutVarint(&((pBuf))->p[ ((pBuf))->n], ((u64)(iRowid) - (u64)(iLastRowid))); ((void ) (0)); }; \ | |||
| 15660 | (iLastRowid) = (iRowid); \ | |||
| 15661 | } | |||
| 15662 | ||||
| 15663 | /* | |||
| 15664 | ** Swap the contents of buffer *p1 with that of *p2. | |||
| 15665 | */ | |||
| 15666 | static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){ | |||
| 15667 | Fts5Buffer tmp = *p1; | |||
| 15668 | *p1 = *p2; | |||
| 15669 | *p2 = tmp; | |||
| 15670 | } | |||
| 15671 | ||||
| 15672 | static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){ | |||
| 15673 | int i = *piOff; | |||
| 15674 | if( i>=pBuf->n ){ | |||
| 15675 | *piOff = -1; | |||
| 15676 | }else{ | |||
| 15677 | u64 iVal; | |||
| 15678 | *piOff = i + sqlite3Fts5GetVarint(&pBuf->p[i], &iVal); | |||
| 15679 | *piRowid += iVal; | |||
| 15680 | } | |||
| 15681 | } | |||
| 15682 | ||||
| 15683 | /* | |||
| 15684 | ** This is the equivalent of fts5MergePrefixLists() for detail=none mode. | |||
| 15685 | ** In this case the buffers consist of a delta-encoded list of rowids only. | |||
| 15686 | */ | |||
| 15687 | static void fts5MergeRowidLists( | |||
| 15688 | Fts5Index *p, /* FTS5 backend object */ | |||
| 15689 | Fts5Buffer *p1, /* First list to merge */ | |||
| 15690 | int nBuf, /* Number of entries in apBuf[] */ | |||
| 15691 | Fts5Buffer *aBuf /* Array of other lists to merge into p1 */ | |||
| 15692 | ){ | |||
| 15693 | int i1 = 0; | |||
| 15694 | int i2 = 0; | |||
| 15695 | i64 iRowid1 = 0; | |||
| 15696 | i64 iRowid2 = 0; | |||
| 15697 | i64 iOut = 0; | |||
| 15698 | Fts5Buffer *p2 = &aBuf[0]; | |||
| 15699 | Fts5Buffer out; | |||
| 15700 | ||||
| 15701 | (void)nBuf; | |||
| 15702 | memset(&out, 0, sizeof(out)); | |||
| 15703 | assert( nBuf==1 )((void) (0)); | |||
| 15704 | sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n); | |||
| 15705 | if( p->rc ) return; | |||
| 15706 | ||||
| 15707 | fts5NextRowid(p1, &i1, &iRowid1); | |||
| 15708 | fts5NextRowid(p2, &i2, &iRowid2); | |||
| 15709 | while( i1>=0 || i2>=0 ){ | |||
| 15710 | if( i1>=0 && (i2<0 || iRowid1<iRowid2) ){ | |||
| 15711 | assert( iOut==0 || iRowid1>iOut )((void) (0)); | |||
| 15712 | fts5BufferSafeAppendVarint(&out, iRowid1 - iOut){ (&out)->n += sqlite3Fts5PutVarint(&(&out)-> p[(&out)->n], (iRowid1 - iOut)); ((void) (0)); }; | |||
| 15713 | iOut = iRowid1; | |||
| 15714 | fts5NextRowid(p1, &i1, &iRowid1); | |||
| 15715 | }else{ | |||
| 15716 | assert( iOut==0 || iRowid2>iOut )((void) (0)); | |||
| 15717 | fts5BufferSafeAppendVarint(&out, iRowid2 - iOut){ (&out)->n += sqlite3Fts5PutVarint(&(&out)-> p[(&out)->n], (iRowid2 - iOut)); ((void) (0)); }; | |||
| 15718 | iOut = iRowid2; | |||
| 15719 | if( i1>=0 && iRowid1==iRowid2 ){ | |||
| 15720 | fts5NextRowid(p1, &i1, &iRowid1); | |||
| 15721 | } | |||
| 15722 | fts5NextRowid(p2, &i2, &iRowid2); | |||
| 15723 | } | |||
| 15724 | } | |||
| 15725 | ||||
| 15726 | fts5BufferSwap(&out, p1); | |||
| 15727 | fts5BufferFree(&out)sqlite3Fts5BufferFree(&out); | |||
| 15728 | } | |||
| 15729 | ||||
| 15730 | typedef struct PrefixMerger PrefixMerger; | |||
| 15731 | struct PrefixMerger { | |||
| 15732 | Fts5DoclistIter iter; /* Doclist iterator */ | |||
| 15733 | i64 iPos; /* For iterating through a position list */ | |||
| 15734 | int iOff; | |||
| 15735 | u8 *aPos; | |||
| 15736 | PrefixMerger *pNext; /* Next in docid/poslist order */ | |||
| 15737 | }; | |||
| 15738 | ||||
| 15739 | static void fts5PrefixMergerInsertByRowid( | |||
| 15740 | PrefixMerger **ppHead, | |||
| 15741 | PrefixMerger *p | |||
| 15742 | ){ | |||
| 15743 | if( p->iter.aPoslist ){ | |||
| 15744 | PrefixMerger **pp = ppHead; | |||
| 15745 | while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){ | |||
| 15746 | pp = &(*pp)->pNext; | |||
| 15747 | } | |||
| 15748 | p->pNext = *pp; | |||
| 15749 | *pp = p; | |||
| 15750 | } | |||
| 15751 | } | |||
| 15752 | ||||
| 15753 | static void fts5PrefixMergerInsertByPosition( | |||
| 15754 | PrefixMerger **ppHead, | |||
| 15755 | PrefixMerger *p | |||
| 15756 | ){ | |||
| 15757 | if( p->iPos>=0 ){ | |||
| 15758 | PrefixMerger **pp = ppHead; | |||
| 15759 | while( *pp && p->iPos>(*pp)->iPos ){ | |||
| 15760 | pp = &(*pp)->pNext; | |||
| 15761 | } | |||
| 15762 | p->pNext = *pp; | |||
| 15763 | *pp = p; | |||
| 15764 | } | |||
| 15765 | } | |||
| 15766 | ||||
| 15767 | ||||
| 15768 | /* | |||
| 15769 | ** Array aBuf[] contains nBuf doclists. These are all merged in with the | |||
| 15770 | ** doclist in buffer p1. | |||
| 15771 | */ | |||
| 15772 | static void fts5MergePrefixLists( | |||
| 15773 | Fts5Index *p, /* FTS5 backend object */ | |||
| 15774 | Fts5Buffer *p1, /* First list to merge */ | |||
| 15775 | int nBuf, /* Number of buffers in array aBuf[] */ | |||
| 15776 | Fts5Buffer *aBuf /* Other lists to merge in */ | |||
| 15777 | ){ | |||
| 15778 | #define fts5PrefixMergerNextPosition(p)sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,& (p)->iOff,&(p)->iPos) \ | |||
| 15779 | sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos) | |||
| 15780 | #define FTS5_MERGE_NLIST16 16 | |||
| 15781 | PrefixMerger aMerger[FTS5_MERGE_NLIST16]; | |||
| 15782 | PrefixMerger *pHead = 0; | |||
| 15783 | int i; | |||
| 15784 | int nOut = 0; | |||
| 15785 | Fts5Buffer out = {0, 0, 0}; | |||
| 15786 | Fts5Buffer tmp = {0, 0, 0}; | |||
| 15787 | i64 iLastRowid = 0; | |||
| 15788 | ||||
| 15789 | /* Initialize a doclist-iterator for each input buffer. Arrange them in | |||
| 15790 | ** a linked-list starting at pHead in ascending order of rowid. Avoid | |||
| 15791 | ** linking any iterators already at EOF into the linked list at all. */ | |||
| 15792 | assert( nBuf+1<=(int)(sizeof(aMerger)/sizeof(aMerger[0])) )((void) (0)); | |||
| 15793 | memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1)); | |||
| 15794 | pHead = &aMerger[nBuf]; | |||
| 15795 | fts5DoclistIterInit(p1, &pHead->iter); | |||
| 15796 | for(i=0; i<nBuf; i++){ | |||
| 15797 | fts5DoclistIterInit(&aBuf[i], &aMerger[i].iter); | |||
| 15798 | fts5PrefixMergerInsertByRowid(&pHead, &aMerger[i]); | |||
| 15799 | nOut += aBuf[i].n; | |||
| 15800 | } | |||
| 15801 | if( nOut==0 ) return; | |||
| 15802 | nOut += p1->n + 9 + 10*nBuf; | |||
| 15803 | ||||
| 15804 | /* The maximum size of the output is equal to the sum of the | |||
| 15805 | ** input sizes + 1 varint (9 bytes). The extra varint is because if the | |||
| 15806 | ** first rowid in one input is a large negative number, and the first in | |||
| 15807 | ** the other a non-negative number, the delta for the non-negative | |||
| 15808 | ** number will be larger on disk than the literal integer value | |||
| 15809 | ** was. | |||
| 15810 | ** | |||
| 15811 | ** Or, if the input position-lists are corrupt, then the output might | |||
| 15812 | ** include up to (nBuf+1) extra 10-byte positions created by interpreting -1 | |||
| 15813 | ** (the value PoslistNext64() uses for EOF) as a position and appending | |||
| 15814 | ** it to the output. This can happen at most once for each input | |||
| 15815 | ** position-list, hence (nBuf+1) 10 byte paddings. */ | |||
| 15816 | if( sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return; | |||
| 15817 | ||||
| 15818 | while( pHead ){ | |||
| 15819 | fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid){ ((void) (0)); { ((&out))->n += sqlite3Fts5PutVarint( &((&out))->p[((&out))->n], ((u64)(pHead-> iter.iRowid) - (u64)(iLastRowid))); ((void) (0)); }; (iLastRowid ) = (pHead->iter.iRowid); }; | |||
| 15820 | ||||
| 15821 | if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){ | |||
| 15822 | /* Merge data from two or more poslists */ | |||
| 15823 | i64 iPrev = 0; | |||
| 15824 | int nTmp = FTS5_DATA_ZERO_PADDING8; | |||
| 15825 | int nMerge = 0; | |||
| 15826 | PrefixMerger *pSave = pHead; | |||
| 15827 | PrefixMerger *pThis = 0; | |||
| 15828 | int nTail = 0; | |||
| 15829 | ||||
| 15830 | pHead = 0; | |||
| 15831 | while( pSave && pSave->iter.iRowid==iLastRowid ){ | |||
| 15832 | PrefixMerger *pNext = pSave->pNext; | |||
| 15833 | pSave->iOff = 0; | |||
| 15834 | pSave->iPos = 0; | |||
| 15835 | pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize]; | |||
| 15836 | fts5PrefixMergerNextPosition(pSave)sqlite3Fts5PoslistNext64((pSave)->aPos,(pSave)->iter.nPoslist ,&(pSave)->iOff,&(pSave)->iPos); | |||
| 15837 | nTmp += pSave->iter.nPoslist + 10; | |||
| 15838 | nMerge++; | |||
| 15839 | fts5PrefixMergerInsertByPosition(&pHead, pSave); | |||
| 15840 | pSave = pNext; | |||
| 15841 | } | |||
| 15842 | ||||
| 15843 | if( pHead==0 || pHead->pNext==0 ){ | |||
| 15844 | FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 15845 | break; | |||
| 15846 | } | |||
| 15847 | ||||
| 15848 | /* See the earlier comment in this function for an explanation of why | |||
| 15849 | ** corrupt input position lists might cause the output to consume | |||
| 15850 | ** at most nMerge*10 bytes of unexpected space. */ | |||
| 15851 | if( sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){ | |||
| 15852 | break; | |||
| 15853 | } | |||
| 15854 | fts5BufferZero(&tmp)sqlite3Fts5BufferZero(&tmp); | |||
| 15855 | ||||
| 15856 | pThis = pHead; | |||
| 15857 | pHead = pThis->pNext; | |||
| 15858 | sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); | |||
| 15859 | fts5PrefixMergerNextPosition(pThis)sqlite3Fts5PoslistNext64((pThis)->aPos,(pThis)->iter.nPoslist ,&(pThis)->iOff,&(pThis)->iPos); | |||
| 15860 | fts5PrefixMergerInsertByPosition(&pHead, pThis); | |||
| 15861 | ||||
| 15862 | while( pHead->pNext ){ | |||
| 15863 | pThis = pHead; | |||
| 15864 | if( pThis->iPos!=iPrev ){ | |||
| 15865 | sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); | |||
| 15866 | } | |||
| 15867 | fts5PrefixMergerNextPosition(pThis)sqlite3Fts5PoslistNext64((pThis)->aPos,(pThis)->iter.nPoslist ,&(pThis)->iOff,&(pThis)->iPos); | |||
| 15868 | pHead = pThis->pNext; | |||
| 15869 | fts5PrefixMergerInsertByPosition(&pHead, pThis); | |||
| 15870 | } | |||
| 15871 | ||||
| 15872 | if( pHead->iPos!=iPrev ){ | |||
| 15873 | sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos); | |||
| 15874 | } | |||
| 15875 | nTail = pHead->iter.nPoslist - pHead->iOff; | |||
| 15876 | ||||
| 15877 | /* WRITEPOSLISTSIZE */ | |||
| 15878 | assert_nc( tmp.n+nTail<=nTmp )((void) (0)); | |||
| 15879 | assert( tmp.n+nTail<=nTmp+nMerge*10 )((void) (0)); | |||
| 15880 | if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING8 ){ | |||
| 15881 | if( p->rc==SQLITE_OK0 ) FTS5_CORRUPT_IDX(p)fts5IndexCorruptIdx(p); | |||
| 15882 | break; | |||
| 15883 | } | |||
| 15884 | fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2){ (&out)->n += sqlite3Fts5PutVarint(&(&out)-> p[(&out)->n], ((tmp.n+nTail) * 2)); ((void) (0)); }; | |||
| 15885 | fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n){ ((void) (0)); memcpy(&(&out)->p[(&out)->n ], tmp.p, tmp.n); (&out)->n += tmp.n; }; | |||
| 15886 | if( nTail>0 ){ | |||
| 15887 | fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail){ ((void) (0)); memcpy(&(&out)->p[(&out)->n ], &pHead->aPos[pHead->iOff], nTail); (&out)-> n += nTail; }; | |||
| 15888 | } | |||
| 15889 | ||||
| 15890 | pHead = pSave; | |||
| 15891 | for(i=0; i<nBuf+1; i++){ | |||
| 15892 | PrefixMerger *pX = &aMerger[i]; | |||
| 15893 | if( pX->iter.aPoslist && pX->iter.iRowid==iLastRowid ){ | |||
| 15894 | fts5DoclistIterNext(&pX->iter); | |||
| 15895 | fts5PrefixMergerInsertByRowid(&pHead, pX); | |||
| 15896 | } | |||
| 15897 | } | |||
| 15898 | ||||
| 15899 | }else{ | |||
| 15900 | /* Copy poslist from pHead to output */ | |||
| 15901 | PrefixMerger *pThis = pHead; | |||
| 15902 | Fts5DoclistIter *pI = &pThis->iter; | |||
| 15903 | fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize){ ((void) (0)); memcpy(&(&out)->p[(&out)->n ], pI->aPoslist, pI->nPoslist+pI->nSize); (&out) ->n += pI->nPoslist+pI->nSize; }; | |||
| 15904 | fts5DoclistIterNext(pI); | |||
| 15905 | pHead = pThis->pNext; | |||
| 15906 | fts5PrefixMergerInsertByRowid(&pHead, pThis); | |||
| 15907 | } | |||
| 15908 | } | |||
| 15909 | ||||
| 15910 | fts5BufferFree(p1)sqlite3Fts5BufferFree(p1); | |||
| 15911 | fts5BufferFree(&tmp)sqlite3Fts5BufferFree(&tmp); | |||
| 15912 | memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING8); | |||
| 15913 | *p1 = out; | |||
| 15914 | } | |||
| 15915 | ||||
| 15916 | ||||
| 15917 | /* | |||
| 15918 | ** Iterate through a range of entries in the FTS index, invoking the xVisit | |||
| 15919 | ** callback for each of them. | |||
| 15920 | ** | |||
| 15921 | ** Parameter pToken points to an nToken buffer containing an FTS index term | |||
| 15922 | ** (i.e. a document term with the preceding 1 byte index identifier - | |||
| 15923 | ** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits | |||
| 15924 | ** all entries for terms that have pToken/nToken as a prefix. If bPrefix | |||
| 15925 | ** is false, then only entries with pToken/nToken as the entire key are | |||
| 15926 | ** visited. | |||
| 15927 | ** | |||
| 15928 | ** If the current table is a tokendata=1 table, then if bPrefix is true then | |||
| 15929 | ** each index term is treated separately. However, if bPrefix is false, then | |||
| 15930 | ** all index terms corresponding to pToken/nToken are collapsed into a single | |||
| 15931 | ** term before the callback is invoked. | |||
| 15932 | ** | |||
| 15933 | ** The callback invoked for each entry visited is specified by paramter xVisit. | |||
| 15934 | ** Each time it is invoked, it is passed a pointer to the Fts5Index object, | |||
| 15935 | ** a copy of the 7th paramter to this function (pCtx) and a pointer to the | |||
| 15936 | ** iterator that indicates the current entry. If the current entry is the | |||
| 15937 | ** first with a new term (i.e. different from that of the previous entry, | |||
| 15938 | ** including the very first term), then the final two parameters are passed | |||
| 15939 | ** a pointer to the term and its size in bytes, respectively. If the current | |||
| 15940 | ** entry is not the first associated with its term, these two parameters | |||
| 15941 | ** are passed 0. | |||
| 15942 | ** | |||
| 15943 | ** If parameter pColset is not NULL, then it is used to filter entries before | |||
| 15944 | ** the callback is invoked. | |||
| 15945 | */ | |||
| 15946 | static int fts5VisitEntries( | |||
| 15947 | Fts5Index *p, /* Fts5 index object */ | |||
| 15948 | Fts5Colset *pColset, /* Columns filter to apply, or NULL */ | |||
| 15949 | u8 *pToken, /* Buffer containing token */ | |||
| 15950 | int nToken, /* Size of buffer pToken in bytes */ | |||
| 15951 | int bPrefix, /* True for a prefix scan */ | |||
| 15952 | void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int), | |||
| 15953 | void *pCtx /* Passed as second argument to xVisit() */ | |||
| 15954 | ){ | |||
| 15955 | const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN0x0008 : 0) | |||
| 15956 | | FTS5INDEX_QUERY_SKIPEMPTY0x0010 | |||
| 15957 | | FTS5INDEX_QUERY_NOOUTPUT0x0020; | |||
| 15958 | Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ | |||
| 15959 | int bNewTerm = 1; | |||
| 15960 | Fts5Structure *pStruct = fts5StructureRead(p); | |||
| 15961 | ||||
| 15962 | fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); | |||
| 15963 | fts5IterSetOutputCb(&p->rc, p1); | |||
| 15964 | for( /* no-op */ ; | |||
| 15965 | fts5MultiIterEof(p, p1)==0; | |||
| 15966 | fts5MultiIterNext2(p, p1, &bNewTerm) | |||
| 15967 | ){ | |||
| 15968 | Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; | |||
| 15969 | int nNew = 0; | |||
| 15970 | const u8 *pNew = 0; | |||
| 15971 | ||||
| 15972 | p1->xSetOutputs(p1, pSeg); | |||
| 15973 | if( p->rc ) break; | |||
| 15974 | ||||
| 15975 | if( bNewTerm ){ | |||
| 15976 | nNew = pSeg->term.n; | |||
| 15977 | pNew = pSeg->term.p; | |||
| 15978 | if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break; | |||
| 15979 | } | |||
| 15980 | ||||
| 15981 | xVisit(p, pCtx, p1, pNew, nNew); | |||
| 15982 | } | |||
| 15983 | fts5MultiIterFree(p1); | |||
| 15984 | ||||
| 15985 | fts5StructureRelease(pStruct); | |||
| 15986 | return p->rc; | |||
| 15987 | } | |||
| 15988 | ||||
| 15989 | ||||
| 15990 | /* | |||
| 15991 | ** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an | |||
| 15992 | ** array of these for each row it visits (so all iRowid fields are the same). | |||
| 15993 | ** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an | |||
| 15994 | ** array of these for the entire query (in which case iRowid fields may take | |||
| 15995 | ** a variety of values). | |||
| 15996 | ** | |||
| 15997 | ** Each instance in the array indicates the iterator (and therefore term) | |||
| 15998 | ** associated with position iPos of rowid iRowid. This is used by the | |||
| 15999 | ** xInstToken() API. | |||
| 16000 | ** | |||
| 16001 | ** iRowid: | |||
| 16002 | ** Rowid for the current entry. | |||
| 16003 | ** | |||
| 16004 | ** iPos: | |||
| 16005 | ** Position of current entry within row. In the usual ((iCol<<32)+iOff) | |||
| 16006 | ** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()). | |||
| 16007 | ** | |||
| 16008 | ** iIter: | |||
| 16009 | ** If the Fts5TokenDataIter iterator that the entry is part of is | |||
| 16010 | ** actually an iterator (i.e. with nIter>0, not just a container for | |||
| 16011 | ** Fts5TokenDataMap structures), then this variable is an index into | |||
| 16012 | ** the apIter[] array. The corresponding term is that which the iterator | |||
| 16013 | ** at apIter[iIter] currently points to. | |||
| 16014 | ** | |||
| 16015 | ** Or, if the Fts5TokenDataIter iterator is just a container object | |||
| 16016 | ** (nIter==0), then iIter is an index into the term.p[] buffer where | |||
| 16017 | ** the term is stored. | |||
| 16018 | ** | |||
| 16019 | ** nByte: | |||
| 16020 | ** In the case where iIter is an index into term.p[], this variable | |||
| 16021 | ** is the size of the term in bytes. If iIter is an index into apIter[], | |||
| 16022 | ** this variable is unused. | |||
| 16023 | */ | |||
| 16024 | struct Fts5TokenDataMap { | |||
| 16025 | i64 iRowid; /* Row this token is located in */ | |||
| 16026 | i64 iPos; /* Position of token */ | |||
| 16027 | int iIter; /* Iterator token was read from */ | |||
| 16028 | int nByte; /* Length of token in bytes (or 0) */ | |||
| 16029 | }; | |||
| 16030 | ||||
| 16031 | /* | |||
| 16032 | ** An object used to supplement Fts5Iter for tokendata=1 iterators. | |||
| 16033 | ** | |||
| 16034 | ** This object serves two purposes. The first is as a container for an array | |||
| 16035 | ** of Fts5TokenDataMap structures, which are used to find the token required | |||
| 16036 | ** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and | |||
| 16037 | ** aMap[] variables. | |||
| 16038 | */ | |||
| 16039 | struct Fts5TokenDataIter { | |||
| 16040 | i64 nMapAlloc; /* Allocated size of aMap[] in entries */ | |||
| 16041 | i64 nMap; /* Number of valid entries in aMap[] */ | |||
| 16042 | Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */ | |||
| 16043 | ||||
| 16044 | /* The following are used for prefix-queries only. */ | |||
| 16045 | Fts5Buffer terms; | |||
| 16046 | ||||
| 16047 | /* The following are used for other full-token tokendata queries only. */ | |||
| 16048 | i64 nIter; | |||
| 16049 | i64 nIterAlloc; | |||
| 16050 | Fts5PoslistReader *aPoslistReader; | |||
| 16051 | int *aPoslistToIter; | |||
| 16052 | Fts5Iter *apIter[FLEXARRAY]; | |||
| 16053 | }; | |||
| 16054 | ||||
| 16055 | /* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */ | |||
| 16056 | #define SZ_FTS5TOKENDATAITER(N)(__builtin_offsetof(Fts5TokenDataIter, apIter) + (N)*sizeof(Fts5Iter )) \ | |||
| 16057 | (offsetof(Fts5TokenDataIter,apIter)__builtin_offsetof(Fts5TokenDataIter, apIter) + (N)*sizeof(Fts5Iter)) | |||
| 16058 | ||||
| 16059 | /* | |||
| 16060 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function | |||
| 16061 | ** merges the two arrays together and writes the result to output array | |||
| 16062 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. | |||
| 16063 | ** | |||
| 16064 | ** Duplicate entries are copied into the output. So the size of the output | |||
| 16065 | ** array is always (n1+n2) entries. | |||
| 16066 | */ | |||
| 16067 | static void fts5TokendataMerge( | |||
| 16068 | Fts5TokenDataMap *a1, int n1, /* Input array 1 */ | |||
| 16069 | Fts5TokenDataMap *a2, int n2, /* Input array 2 */ | |||
| 16070 | Fts5TokenDataMap *aOut /* Output array */ | |||
| 16071 | ){ | |||
| 16072 | int i1 = 0; | |||
| 16073 | int i2 = 0; | |||
| 16074 | ||||
| 16075 | assert( n1>=0 && n2>=0 )((void) (0)); | |||
| 16076 | while( i1<n1 || i2<n2 ){ | |||
| 16077 | Fts5TokenDataMap *pOut = &aOut[i1+i2]; | |||
| 16078 | if( i2>=n2 || (i1<n1 && ( | |||
| 16079 | a1[i1].iRowid<a2[i2].iRowid | |||
| 16080 | || (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos) | |||
| 16081 | ))){ | |||
| 16082 | memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap)); | |||
| 16083 | i1++; | |||
| 16084 | }else{ | |||
| 16085 | memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap)); | |||
| 16086 | i2++; | |||
| 16087 | } | |||
| 16088 | } | |||
| 16089 | } | |||
| 16090 | ||||
| 16091 | ||||
| 16092 | /* | |||
| 16093 | ** Append a mapping to the token-map belonging to object pT. | |||
| 16094 | */ | |||
| 16095 | static void fts5TokendataIterAppendMap( | |||
| 16096 | Fts5Index *p, | |||
| 16097 | Fts5TokenDataIter *pT, | |||
| 16098 | int iIter, | |||
| 16099 | int nByte, | |||
| 16100 | i64 iRowid, | |||
| 16101 | i64 iPos | |||
| 16102 | ){ | |||
| 16103 | if( p->rc==SQLITE_OK0 ){ | |||
| 16104 | if( pT->nMap==pT->nMapAlloc ){ | |||
| 16105 | i64 nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; | |||
| 16106 | i64 nAlloc = nNew * sizeof(Fts5TokenDataMap); | |||
| 16107 | Fts5TokenDataMap *aNew; | |||
| 16108 | ||||
| 16109 | aNew = (Fts5TokenDataMap*)sqlite3_realloc64sqlite3_api->realloc64(pT->aMap, nAlloc); | |||
| 16110 | if( aNew==0 ){ | |||
| 16111 | p->rc = SQLITE_NOMEM7; | |||
| 16112 | return; | |||
| 16113 | } | |||
| 16114 | ||||
| 16115 | pT->aMap = aNew; | |||
| 16116 | pT->nMapAlloc = nNew; | |||
| 16117 | } | |||
| 16118 | ||||
| 16119 | pT->aMap[pT->nMap].iRowid = iRowid; | |||
| 16120 | pT->aMap[pT->nMap].iPos = iPos; | |||
| 16121 | pT->aMap[pT->nMap].iIter = iIter; | |||
| 16122 | pT->aMap[pT->nMap].nByte = nByte; | |||
| 16123 | pT->nMap++; | |||
| 16124 | } | |||
| 16125 | } | |||
| 16126 | ||||
| 16127 | /* | |||
| 16128 | ** Sort the contents of the pT->aMap[] array. | |||
| 16129 | ** | |||
| 16130 | ** The sorting algorithm requires a malloc(). If this fails, an error code | |||
| 16131 | ** is left in Fts5Index.rc before returning. | |||
| 16132 | */ | |||
| 16133 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ | |||
| 16134 | Fts5TokenDataMap *aTmp = 0; | |||
| 16135 | i64 nByte = pT->nMap * sizeof(Fts5TokenDataMap); | |||
| 16136 | ||||
| 16137 | aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte); | |||
| 16138 | if( aTmp ){ | |||
| 16139 | Fts5TokenDataMap *a1 = pT->aMap; | |||
| 16140 | Fts5TokenDataMap *a2 = aTmp; | |||
| 16141 | i64 nHalf; | |||
| 16142 | ||||
| 16143 | for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){ | |||
| 16144 | int i1; | |||
| 16145 | for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){ | |||
| 16146 | int n1 = MIN(nHalf, pT->nMap-i1)(((nHalf) < (pT->nMap-i1)) ? (nHalf) : (pT->nMap-i1) ); | |||
| 16147 | int n2 = MIN(nHalf, pT->nMap-i1-n1)(((nHalf) < (pT->nMap-i1-n1)) ? (nHalf) : (pT->nMap- i1-n1)); | |||
| 16148 | fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]); | |||
| 16149 | } | |||
| 16150 | SWAPVAL(Fts5TokenDataMap*, a1, a2){ Fts5TokenDataMap* tmp; tmp=a1; a1=a2; a2=tmp; }; | |||
| 16151 | } | |||
| 16152 | ||||
| 16153 | if( a1!=pT->aMap ){ | |||
| 16154 | memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap)); | |||
| 16155 | } | |||
| 16156 | sqlite3_freesqlite3_api->free(aTmp); | |||
| 16157 | ||||
| 16158 | #ifdef SQLITE_DEBUG | |||
| 16159 | { | |||
| 16160 | int ii; | |||
| 16161 | for(ii=1; ii<pT->nMap; ii++){ | |||
| 16162 | Fts5TokenDataMap *p1 = &pT->aMap[ii-1]; | |||
| 16163 | Fts5TokenDataMap *p2 = &pT->aMap[ii]; | |||
| 16164 | assert( p1->iRowid<p2->iRowid((void) (0)) | |||
| 16165 | || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos)((void) (0)) | |||
| 16166 | )((void) (0)); | |||
| 16167 | } | |||
| 16168 | } | |||
| 16169 | #endif | |||
| 16170 | } | |||
| 16171 | } | |||
| 16172 | ||||
| 16173 | /* | |||
| 16174 | ** Delete an Fts5TokenDataIter structure and its contents. | |||
| 16175 | */ | |||
| 16176 | static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ | |||
| 16177 | if( pSet ){ | |||
| 16178 | int ii; | |||
| 16179 | for(ii=0; ii<pSet->nIter; ii++){ | |||
| 16180 | fts5MultiIterFree(pSet->apIter[ii]); | |||
| 16181 | } | |||
| 16182 | fts5BufferFree(&pSet->terms)sqlite3Fts5BufferFree(&pSet->terms); | |||
| 16183 | sqlite3_freesqlite3_api->free(pSet->aPoslistReader); | |||
| 16184 | sqlite3_freesqlite3_api->free(pSet->aMap); | |||
| 16185 | sqlite3_freesqlite3_api->free(pSet); | |||
| 16186 | } | |||
| 16187 | } | |||
| 16188 | ||||
| 16189 | ||||
| 16190 | /* | |||
| 16191 | ** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata() | |||
| 16192 | ** to pass data to prefixIterSetupTokendataCb(). | |||
| 16193 | */ | |||
| 16194 | typedef struct TokendataSetupCtx TokendataSetupCtx; | |||
| 16195 | struct TokendataSetupCtx { | |||
| 16196 | Fts5TokenDataIter *pT; /* Object being populated with mappings */ | |||
| 16197 | int iTermOff; /* Offset of current term in terms.p[] */ | |||
| 16198 | int nTermByte; /* Size of current term in bytes */ | |||
| 16199 | }; | |||
| 16200 | ||||
| 16201 | /* | |||
| 16202 | ** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This | |||
| 16203 | ** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each | |||
| 16204 | ** position in the current position-list. It doesn't matter that some of | |||
| 16205 | ** these may be out of order - they will be sorted later. | |||
| 16206 | */ | |||
| 16207 | static void prefixIterSetupTokendataCb( | |||
| 16208 | Fts5Index *p, | |||
| 16209 | void *pCtx, | |||
| 16210 | Fts5Iter *p1, | |||
| 16211 | const u8 *pNew, | |||
| 16212 | int nNew | |||
| 16213 | ){ | |||
| 16214 | TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx; | |||
| 16215 | int iPosOff = 0; | |||
| 16216 | i64 iPos = 0; | |||
| 16217 | ||||
| 16218 | if( pNew ){ | |||
| 16219 | pSetup->nTermByte = nNew-1; | |||
| 16220 | pSetup->iTermOff = pSetup->pT->terms.n; | |||
| 16221 | fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1)sqlite3Fts5BufferAppendBlob(&p->rc,&pSetup->pT-> terms,nNew-1,pNew+1); | |||
| 16222 | } | |||
| 16223 | ||||
| 16224 | while( 0==sqlite3Fts5PoslistNext64( | |||
| 16225 | p1->base.pData, p1->base.nData, &iPosOff, &iPos | |||
| 16226 | ) ){ | |||
| 16227 | fts5TokendataIterAppendMap(p, | |||
| 16228 | pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos | |||
| 16229 | ); | |||
| 16230 | } | |||
| 16231 | } | |||
| 16232 | ||||
| 16233 | ||||
| 16234 | /* | |||
| 16235 | ** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries(). | |||
| 16236 | */ | |||
| 16237 | typedef struct PrefixSetupCtx PrefixSetupCtx; | |||
| 16238 | struct PrefixSetupCtx { | |||
| 16239 | void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); | |||
| 16240 | void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); | |||
| 16241 | i64 iLastRowid; | |||
| 16242 | int nMerge; | |||
| 16243 | Fts5Buffer *aBuf; | |||
| 16244 | int nBuf; | |||
| 16245 | Fts5Buffer doclist; | |||
| 16246 | TokendataSetupCtx *pTokendata; | |||
| 16247 | }; | |||
| 16248 | ||||
| 16249 | /* | |||
| 16250 | ** fts5VisitEntries() callback used by fts5SetupPrefixIter() | |||
| 16251 | */ | |||
| 16252 | static void prefixIterSetupCb( | |||
| 16253 | Fts5Index *p, | |||
| 16254 | void *pCtx, | |||
| 16255 | Fts5Iter *p1, | |||
| 16256 | const u8 *pNew, | |||
| 16257 | int nNew | |||
| 16258 | ){ | |||
| 16259 | PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx; | |||
| 16260 | const int nMerge = pSetup->nMerge; | |||
| 16261 | ||||
| 16262 | if( p1->base.nData>0 ){ | |||
| 16263 | if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){ | |||
| 16264 | int i; | |||
| 16265 | for(i=0; p->rc==SQLITE_OK0 && pSetup->doclist.n; i++){ | |||
| 16266 | int i1 = i*nMerge; | |||
| 16267 | int iStore; | |||
| 16268 | assert( i1+nMerge<=pSetup->nBuf )((void) (0)); | |||
| 16269 | for(iStore=i1; iStore<i1+nMerge; iStore++){ | |||
| 16270 | if( pSetup->aBuf[iStore].n==0 ){ | |||
| 16271 | fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]); | |||
| 16272 | fts5BufferZero(&pSetup->doclist)sqlite3Fts5BufferZero(&pSetup->doclist); | |||
| 16273 | break; | |||
| 16274 | } | |||
| 16275 | } | |||
| 16276 | if( iStore==i1+nMerge ){ | |||
| 16277 | pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]); | |||
| 16278 | for(iStore=i1; iStore<i1+nMerge; iStore++){ | |||
| 16279 | fts5BufferZero(&pSetup->aBuf[iStore])sqlite3Fts5BufferZero(&pSetup->aBuf[iStore]); | |||
| 16280 | } | |||
| 16281 | } | |||
| 16282 | } | |||
| 16283 | pSetup->iLastRowid = 0; | |||
| 16284 | } | |||
| 16285 | ||||
| 16286 | pSetup->xAppend( | |||
| 16287 | p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist | |||
| 16288 | ); | |||
| 16289 | pSetup->iLastRowid = p1->base.iRowid; | |||
| 16290 | } | |||
| 16291 | ||||
| 16292 | if( pSetup->pTokendata ){ | |||
| 16293 | prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew); | |||
| 16294 | } | |||
| 16295 | } | |||
| 16296 | ||||
| 16297 | static void fts5SetupPrefixIter( | |||
| 16298 | Fts5Index *p, /* Index to read from */ | |||
| 16299 | int bDesc, /* True for "ORDER BY rowid DESC" */ | |||
| 16300 | int iIdx, /* Index to scan for data */ | |||
| 16301 | u8 *pToken, /* Buffer containing prefix to match */ | |||
| 16302 | int nToken, /* Size of buffer pToken in bytes */ | |||
| 16303 | Fts5Colset *pColset, /* Restrict matches to these columns */ | |||
| 16304 | Fts5Iter **ppIter /* OUT: New iterator */ | |||
| 16305 | ){ | |||
| 16306 | Fts5Structure *pStruct; | |||
| 16307 | PrefixSetupCtx s; | |||
| 16308 | TokendataSetupCtx s2; | |||
| 16309 | ||||
| 16310 | memset(&s, 0, sizeof(s)); | |||
| 16311 | memset(&s2, 0, sizeof(s2)); | |||
| 16312 | ||||
| 16313 | s.nMerge = 1; | |||
| 16314 | s.iLastRowid = 0; | |||
| 16315 | s.nBuf = 32; | |||
| 16316 | if( iIdx==0 | |||
| 16317 | && p->pConfig->eDetail==FTS5_DETAIL_FULL0 | |||
| 16318 | && p->pConfig->bPrefixInsttoken | |||
| 16319 | ){ | |||
| 16320 | s.pTokendata = &s2; | |||
| 16321 | s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1)(__builtin_offsetof(Fts5TokenDataIter, apIter) + (1)*sizeof(Fts5Iter ))); | |||
| 16322 | } | |||
| 16323 | ||||
| 16324 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 16325 | s.xMerge = fts5MergeRowidLists; | |||
| 16326 | s.xAppend = fts5AppendRowid; | |||
| 16327 | }else{ | |||
| 16328 | s.nMerge = FTS5_MERGE_NLIST16-1; | |||
| 16329 | s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ | |||
| 16330 | s.xMerge = fts5MergePrefixLists; | |||
| 16331 | s.xAppend = fts5AppendPoslist; | |||
| 16332 | } | |||
| 16333 | ||||
| 16334 | s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf); | |||
| 16335 | pStruct = fts5StructureRead(p); | |||
| 16336 | assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) )((void) (0)); | |||
| 16337 | ||||
| 16338 | if( p->rc==SQLITE_OK0 ){ | |||
| 16339 | void *pCtx = (void*)&s; | |||
| 16340 | int i; | |||
| 16341 | Fts5Data *pData; | |||
| 16342 | ||||
| 16343 | /* If iIdx is non-zero, then it is the number of a prefix-index for | |||
| 16344 | ** prefixes 1 character longer than the prefix being queried for. That | |||
| 16345 | ** index contains all the doclists required, except for the one | |||
| 16346 | ** corresponding to the prefix itself. That one is extracted from the | |||
| 16347 | ** main term index here. */ | |||
| 16348 | if( iIdx!=0 ){ | |||
| 16349 | pToken[0] = FTS5_MAIN_PREFIX'0'; | |||
| 16350 | fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx); | |||
| 16351 | } | |||
| 16352 | ||||
| 16353 | pToken[0] = FTS5_MAIN_PREFIX'0' + iIdx; | |||
| 16354 | fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx); | |||
| 16355 | ||||
| 16356 | assert( (s.nBuf%s.nMerge)==0 )((void) (0)); | |||
| 16357 | for(i=0; i<s.nBuf; i+=s.nMerge){ | |||
| 16358 | int iFree; | |||
| 16359 | if( p->rc==SQLITE_OK0 ){ | |||
| 16360 | s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]); | |||
| 16361 | } | |||
| 16362 | for(iFree=i; iFree<i+s.nMerge; iFree++){ | |||
| 16363 | fts5BufferFree(&s.aBuf[iFree])sqlite3Fts5BufferFree(&s.aBuf[iFree]); | |||
| 16364 | } | |||
| 16365 | } | |||
| 16366 | ||||
| 16367 | pData = fts5IdxMalloc(p, sizeof(*pData) | |||
| 16368 | + ((i64)s.doclist.n)+FTS5_DATA_ZERO_PADDING8); | |||
| 16369 | assert( pData!=0 || p->rc!=SQLITE_OK )((void) (0)); | |||
| 16370 | if( pData ){ | |||
| 16371 | pData->p = (u8*)&pData[1]; | |||
| 16372 | pData->nn = pData->szLeaf = s.doclist.n; | |||
| 16373 | if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n); | |||
| 16374 | fts5MultiIterNew2(p, pData, bDesc, ppIter); | |||
| 16375 | } | |||
| 16376 | ||||
| 16377 | assert( (*ppIter)!=0 || p->rc!=SQLITE_OK )((void) (0)); | |||
| 16378 | if( p->rc==SQLITE_OK0 && s.pTokendata ){ | |||
| 16379 | fts5TokendataIterSortMap(p, s2.pT); | |||
| 16380 | (*ppIter)->pTokenDataIter = s2.pT; | |||
| 16381 | s2.pT = 0; | |||
| 16382 | } | |||
| 16383 | } | |||
| 16384 | ||||
| 16385 | fts5TokendataIterDelete(s2.pT); | |||
| 16386 | fts5BufferFree(&s.doclist)sqlite3Fts5BufferFree(&s.doclist); | |||
| 16387 | fts5StructureRelease(pStruct); | |||
| 16388 | sqlite3_freesqlite3_api->free(s.aBuf); | |||
| 16389 | } | |||
| 16390 | ||||
| 16391 | ||||
| 16392 | /* | |||
| 16393 | ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain | |||
| 16394 | ** to the document with rowid iRowid. | |||
| 16395 | */ | |||
| 16396 | static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ | |||
| 16397 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 16398 | ||||
| 16399 | /* Allocate the hash table if it has not already been allocated */ | |||
| 16400 | if( p->pHash==0 ){ | |||
| 16401 | p->rc = sqlite3Fts5HashNew(p->pConfig, &p->pHash, &p->nPendingData); | |||
| 16402 | } | |||
| 16403 | ||||
| 16404 | /* Flush the hash table to disk if required */ | |||
| 16405 | if( iRowid<p->iWriteRowid | |||
| 16406 | || (iRowid==p->iWriteRowid && p->bDelete==0) | |||
| 16407 | || (p->nPendingData > p->pConfig->nHashSize) | |||
| 16408 | ){ | |||
| 16409 | fts5IndexFlush(p); | |||
| 16410 | } | |||
| 16411 | ||||
| 16412 | p->iWriteRowid = iRowid; | |||
| 16413 | p->bDelete = bDelete; | |||
| 16414 | if( bDelete==0 ){ | |||
| 16415 | p->nPendingRow++; | |||
| 16416 | } | |||
| 16417 | return fts5IndexReturn(p); | |||
| 16418 | } | |||
| 16419 | ||||
| 16420 | /* | |||
| 16421 | ** Commit data to disk. | |||
| 16422 | */ | |||
| 16423 | static int sqlite3Fts5IndexSync(Fts5Index *p){ | |||
| 16424 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 16425 | fts5IndexFlush(p); | |||
| 16426 | fts5IndexCloseReader(p); | |||
| 16427 | return fts5IndexReturn(p); | |||
| 16428 | } | |||
| 16429 | ||||
| 16430 | /* | |||
| 16431 | ** Discard any data stored in the in-memory hash tables. Do not write it | |||
| 16432 | ** to the database. Additionally, assume that the contents of the %_data | |||
| 16433 | ** table may have changed on disk. So any in-memory caches of %_data | |||
| 16434 | ** records must be invalidated. | |||
| 16435 | */ | |||
| 16436 | static int sqlite3Fts5IndexRollback(Fts5Index *p){ | |||
| 16437 | fts5IndexCloseReader(p); | |||
| 16438 | fts5IndexDiscardData(p); | |||
| 16439 | fts5StructureInvalidate(p); | |||
| 16440 | return fts5IndexReturn(p); | |||
| 16441 | } | |||
| 16442 | ||||
| 16443 | /* | |||
| 16444 | ** The %_data table is completely empty when this function is called. This | |||
| 16445 | ** function populates it with the initial structure objects for each index, | |||
| 16446 | ** and the initial version of the "averages" record (a zero-byte blob). | |||
| 16447 | */ | |||
| 16448 | static int sqlite3Fts5IndexReinit(Fts5Index *p){ | |||
| 16449 | Fts5Structure *pTmp; | |||
| 16450 | union { | |||
| 16451 | Fts5Structure sFts; | |||
| 16452 | u8 tmpSpace[SZ_FTS5STRUCTURE(1)(__builtin_offsetof(Fts5Structure, aLevel) + (1)*sizeof(Fts5StructureLevel ))]; | |||
| 16453 | } uFts; | |||
| 16454 | fts5StructureInvalidate(p); | |||
| 16455 | fts5IndexDiscardData(p); | |||
| 16456 | pTmp = &uFts.sFts; | |||
| 16457 | memset(uFts.tmpSpace, 0, sizeof(uFts.tmpSpace)); | |||
| 16458 | if( p->pConfig->bContentlessDelete ){ | |||
| 16459 | pTmp->nOriginCntr = 1; | |||
| 16460 | } | |||
| 16461 | fts5DataWrite(p, FTS5_AVERAGES_ROWID1, (const u8*)"", 0); | |||
| 16462 | fts5StructureWrite(p, pTmp); | |||
| 16463 | return fts5IndexReturn(p); | |||
| 16464 | } | |||
| 16465 | ||||
| 16466 | /* | |||
| 16467 | ** Open a new Fts5Index handle. If the bCreate argument is true, create | |||
| 16468 | ** and initialize the underlying %_data table. | |||
| 16469 | ** | |||
| 16470 | ** If successful, set *pp to point to the new object and return SQLITE_OK. | |||
| 16471 | ** Otherwise, set *pp to NULL and return an SQLite error code. | |||
| 16472 | */ | |||
| 16473 | static int sqlite3Fts5IndexOpen( | |||
| 16474 | Fts5Config *pConfig, | |||
| 16475 | int bCreate, | |||
| 16476 | Fts5Index **pp, | |||
| 16477 | char **pzErr | |||
| 16478 | ){ | |||
| 16479 | int rc = SQLITE_OK0; | |||
| 16480 | Fts5Index *p; /* New object */ | |||
| 16481 | ||||
| 16482 | *pp = p = (Fts5Index*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Index)); | |||
| 16483 | if( rc==SQLITE_OK0 ){ | |||
| 16484 | p->pConfig = pConfig; | |||
| 16485 | p->nWorkUnit = FTS5_WORK_UNIT64; | |||
| 16486 | p->zDataTbl = sqlite3Fts5Mprintf(&rc, "%s_data", pConfig->zName); | |||
| 16487 | if( p->zDataTbl && bCreate ){ | |||
| 16488 | rc = sqlite3Fts5CreateTable( | |||
| 16489 | pConfig, "data", "id INTEGER PRIMARY KEY, block BLOB", 0, pzErr | |||
| 16490 | ); | |||
| 16491 | if( rc==SQLITE_OK0 ){ | |||
| 16492 | rc = sqlite3Fts5CreateTable(pConfig, "idx", | |||
| 16493 | "segid, term, pgno, PRIMARY KEY(segid, term)", | |||
| 16494 | 1, pzErr | |||
| 16495 | ); | |||
| 16496 | } | |||
| 16497 | if( rc==SQLITE_OK0 ){ | |||
| 16498 | rc = sqlite3Fts5IndexReinit(p); | |||
| 16499 | } | |||
| 16500 | } | |||
| 16501 | } | |||
| 16502 | ||||
| 16503 | assert( rc!=SQLITE_OK || p->rc==SQLITE_OK )((void) (0)); | |||
| 16504 | if( rc ){ | |||
| 16505 | sqlite3Fts5IndexClose(p); | |||
| 16506 | *pp = 0; | |||
| 16507 | } | |||
| 16508 | return rc; | |||
| 16509 | } | |||
| 16510 | ||||
| 16511 | /* | |||
| 16512 | ** Close a handle opened by an earlier call to sqlite3Fts5IndexOpen(). | |||
| 16513 | */ | |||
| 16514 | static int sqlite3Fts5IndexClose(Fts5Index *p){ | |||
| 16515 | int rc = SQLITE_OK0; | |||
| 16516 | if( p ){ | |||
| 16517 | assert( p->pReader==0 )((void) (0)); | |||
| 16518 | fts5StructureInvalidate(p); | |||
| 16519 | sqlite3_finalizesqlite3_api->finalize(p->pWriter); | |||
| 16520 | sqlite3_finalizesqlite3_api->finalize(p->pDeleter); | |||
| 16521 | sqlite3_finalizesqlite3_api->finalize(p->pIdxWriter); | |||
| 16522 | sqlite3_finalizesqlite3_api->finalize(p->pIdxDeleter); | |||
| 16523 | sqlite3_finalizesqlite3_api->finalize(p->pIdxSelect); | |||
| 16524 | sqlite3_finalizesqlite3_api->finalize(p->pIdxNextSelect); | |||
| 16525 | sqlite3_finalizesqlite3_api->finalize(p->pDataVersion); | |||
| 16526 | sqlite3_finalizesqlite3_api->finalize(p->pDeleteFromIdx); | |||
| 16527 | sqlite3Fts5HashFree(p->pHash); | |||
| 16528 | sqlite3_freesqlite3_api->free(p->zDataTbl); | |||
| 16529 | sqlite3_freesqlite3_api->free(p); | |||
| 16530 | } | |||
| 16531 | return rc; | |||
| 16532 | } | |||
| 16533 | ||||
| 16534 | /* | |||
| 16535 | ** Argument p points to a buffer containing utf-8 text that is n bytes in | |||
| 16536 | ** size. Return the number of bytes in the nChar character prefix of the | |||
| 16537 | ** buffer, or 0 if there are less than nChar characters in total. | |||
| 16538 | */ | |||
| 16539 | static int sqlite3Fts5IndexCharlenToBytelen( | |||
| 16540 | const char *p, | |||
| 16541 | int nByte, | |||
| 16542 | int nChar | |||
| 16543 | ){ | |||
| 16544 | int n = 0; | |||
| 16545 | int i; | |||
| 16546 | for(i=0; i<nChar; i++){ | |||
| 16547 | if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */ | |||
| 16548 | if( (unsigned char)p[n++]>=0xc0 ){ | |||
| 16549 | if( n>=nByte ) return 0; | |||
| 16550 | while( (p[n] & 0xc0)==0x80 ){ | |||
| 16551 | n++; | |||
| 16552 | if( n>=nByte ){ | |||
| 16553 | if( i+1==nChar ) break; | |||
| 16554 | return 0; | |||
| 16555 | } | |||
| 16556 | } | |||
| 16557 | } | |||
| 16558 | } | |||
| 16559 | return n; | |||
| 16560 | } | |||
| 16561 | ||||
| 16562 | /* | |||
| 16563 | ** pIn is a UTF-8 encoded string, nIn bytes in size. Return the number of | |||
| 16564 | ** unicode characters in the string. | |||
| 16565 | */ | |||
| 16566 | static int fts5IndexCharlen(const char *pIn, int nIn){ | |||
| 16567 | int nChar = 0; | |||
| 16568 | int i = 0; | |||
| 16569 | while( i<nIn ){ | |||
| 16570 | if( (unsigned char)pIn[i++]>=0xc0 ){ | |||
| 16571 | while( i<nIn && (pIn[i] & 0xc0)==0x80 ) i++; | |||
| 16572 | } | |||
| 16573 | nChar++; | |||
| 16574 | } | |||
| 16575 | return nChar; | |||
| 16576 | } | |||
| 16577 | ||||
| 16578 | /* | |||
| 16579 | ** Insert or remove data to or from the index. Each time a document is | |||
| 16580 | ** added to or removed from the index, this function is called one or more | |||
| 16581 | ** times. | |||
| 16582 | ** | |||
| 16583 | ** For an insert, it must be called once for each token in the new document. | |||
| 16584 | ** If the operation is a delete, it must be called (at least) once for each | |||
| 16585 | ** unique token in the document with an iCol value less than zero. The iPos | |||
| 16586 | ** argument is ignored for a delete. | |||
| 16587 | */ | |||
| 16588 | static int sqlite3Fts5IndexWrite( | |||
| 16589 | Fts5Index *p, /* Index to write to */ | |||
| 16590 | int iCol, /* Column token appears in (-ve -> delete) */ | |||
| 16591 | int iPos, /* Position of token within column */ | |||
| 16592 | const char *pToken, int nToken /* Token to add or remove to or from index */ | |||
| 16593 | ){ | |||
| 16594 | int i; /* Used to iterate through indexes */ | |||
| 16595 | int rc = SQLITE_OK0; /* Return code */ | |||
| 16596 | Fts5Config *pConfig = p->pConfig; | |||
| 16597 | ||||
| 16598 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 16599 | assert( (iCol<0)==p->bDelete )((void) (0)); | |||
| 16600 | ||||
| 16601 | /* Add the entry to the main terms index. */ | |||
| 16602 | rc = sqlite3Fts5HashWrite( | |||
| 16603 | p->pHash, p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX'0', pToken, nToken | |||
| 16604 | ); | |||
| 16605 | ||||
| 16606 | for(i=0; i<pConfig->nPrefix && rc==SQLITE_OK0; i++){ | |||
| 16607 | const int nChar = pConfig->aPrefix[i]; | |||
| 16608 | int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar); | |||
| 16609 | if( nByte ){ | |||
| 16610 | rc = sqlite3Fts5HashWrite(p->pHash, | |||
| 16611 | p->iWriteRowid, iCol, iPos, (char)(FTS5_MAIN_PREFIX'0'+i+1), pToken, | |||
| 16612 | nByte | |||
| 16613 | ); | |||
| 16614 | } | |||
| 16615 | } | |||
| 16616 | ||||
| 16617 | return rc; | |||
| 16618 | } | |||
| 16619 | ||||
| 16620 | /* | |||
| 16621 | ** pToken points to a buffer of size nToken bytes containing a search | |||
| 16622 | ** term, including the index number at the start, used on a tokendata=1 | |||
| 16623 | ** table. This function returns true if the term in buffer pBuf matches | |||
| 16624 | ** token pToken/nToken. | |||
| 16625 | */ | |||
| 16626 | static int fts5IsTokendataPrefix( | |||
| 16627 | Fts5Buffer *pBuf, | |||
| 16628 | const u8 *pToken, | |||
| 16629 | int nToken | |||
| 16630 | ){ | |||
| 16631 | return ( | |||
| 16632 | pBuf->n>=nToken | |||
| 16633 | && 0==memcmp(pBuf->p, pToken, nToken) | |||
| 16634 | && (pBuf->n==nToken || pBuf->p[nToken]==0x00) | |||
| 16635 | ); | |||
| 16636 | } | |||
| 16637 | ||||
| 16638 | /* | |||
| 16639 | ** Ensure the segment-iterator passed as the only argument points to EOF. | |||
| 16640 | */ | |||
| 16641 | static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ | |||
| 16642 | fts5DataRelease(pSeg->pLeaf); | |||
| 16643 | pSeg->pLeaf = 0; | |||
| 16644 | } | |||
| 16645 | ||||
| 16646 | static void fts5IterClose(Fts5IndexIter *pIndexIter){ | |||
| 16647 | if( pIndexIter ){ | |||
| 16648 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | |||
| 16649 | Fts5Index *pIndex = pIter->pIndex; | |||
| 16650 | fts5TokendataIterDelete(pIter->pTokenDataIter); | |||
| 16651 | fts5MultiIterFree(pIter); | |||
| 16652 | fts5IndexCloseReader(pIndex); | |||
| 16653 | } | |||
| 16654 | } | |||
| 16655 | ||||
| 16656 | /* | |||
| 16657 | ** This function appends iterator pAppend to Fts5TokenDataIter pIn and | |||
| 16658 | ** returns the result. | |||
| 16659 | */ | |||
| 16660 | static Fts5TokenDataIter *fts5AppendTokendataIter( | |||
| 16661 | Fts5Index *p, /* Index object (for error code) */ | |||
| 16662 | Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */ | |||
| 16663 | Fts5Iter *pAppend /* Append this iterator */ | |||
| 16664 | ){ | |||
| 16665 | Fts5TokenDataIter *pRet = pIn; | |||
| 16666 | ||||
| 16667 | if( p->rc==SQLITE_OK0 ){ | |||
| 16668 | if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ | |||
| 16669 | i64 nAlloc = pIn ? pIn->nIterAlloc*2 : 16; | |||
| 16670 | i64 nByte = SZ_FTS5TOKENDATAITER(nAlloc+1)(__builtin_offsetof(Fts5TokenDataIter, apIter) + (nAlloc+1)*sizeof (Fts5Iter)); | |||
| 16671 | Fts5TokenDataIter *pNew; | |||
| 16672 | pNew = (Fts5TokenDataIter*)sqlite3_realloc64sqlite3_api->realloc64(pIn, nByte); | |||
| 16673 | ||||
| 16674 | if( pNew==0 ){ | |||
| 16675 | p->rc = SQLITE_NOMEM7; | |||
| 16676 | }else{ | |||
| 16677 | if( pIn==0 ) memset(pNew, 0, nByte); | |||
| 16678 | pRet = pNew; | |||
| 16679 | pNew->nIterAlloc = nAlloc; | |||
| 16680 | } | |||
| 16681 | } | |||
| 16682 | } | |||
| 16683 | if( p->rc ){ | |||
| 16684 | fts5IterClose((Fts5IndexIter*)pAppend); | |||
| 16685 | }else{ | |||
| 16686 | pRet->apIter[pRet->nIter++] = pAppend; | |||
| 16687 | } | |||
| 16688 | assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc )((void) (0)); | |||
| 16689 | ||||
| 16690 | return pRet; | |||
| 16691 | } | |||
| 16692 | ||||
| 16693 | /* | |||
| 16694 | ** The iterator passed as the only argument must be a tokendata=1 iterator | |||
| 16695 | ** (pIter->pTokenDataIter!=0). This function sets the iterator output | |||
| 16696 | ** variables (pIter->base.*) according to the contents of the current | |||
| 16697 | ** row. | |||
| 16698 | */ | |||
| 16699 | static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ | |||
| 16700 | int ii; | |||
| 16701 | int nHit = 0; | |||
| 16702 | i64 iRowid = SMALLEST_INT64(((i64)-1) - (0xffffffff|(((i64)0x7fffffff)<<32))); | |||
| 16703 | int iMin = 0; | |||
| 16704 | ||||
| 16705 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; | |||
| 16706 | ||||
| 16707 | pIter->base.nData = 0; | |||
| 16708 | pIter->base.pData = 0; | |||
| 16709 | ||||
| 16710 | for(ii=0; ii<pT->nIter; ii++){ | |||
| 16711 | Fts5Iter *p = pT->apIter[ii]; | |||
| 16712 | if( p->base.bEof==0 ){ | |||
| 16713 | if( nHit==0 || p->base.iRowid<iRowid ){ | |||
| 16714 | iRowid = p->base.iRowid; | |||
| 16715 | nHit = 1; | |||
| 16716 | pIter->base.pData = p->base.pData; | |||
| 16717 | pIter->base.nData = p->base.nData; | |||
| 16718 | iMin = ii; | |||
| 16719 | }else if( p->base.iRowid==iRowid ){ | |||
| 16720 | nHit++; | |||
| 16721 | } | |||
| 16722 | } | |||
| 16723 | } | |||
| 16724 | ||||
| 16725 | if( nHit==0 ){ | |||
| 16726 | pIter->base.bEof = 1; | |||
| 16727 | }else{ | |||
| 16728 | int eDetail = pIter->pIndex->pConfig->eDetail; | |||
| 16729 | pIter->base.bEof = 0; | |||
| 16730 | pIter->base.iRowid = iRowid; | |||
| 16731 | ||||
| 16732 | if( nHit==1 && eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 16733 | fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1); | |||
| 16734 | }else | |||
| 16735 | if( nHit>1 && eDetail!=FTS5_DETAIL_NONE1 ){ | |||
| 16736 | int nReader = 0; | |||
| 16737 | int nByte = 0; | |||
| 16738 | i64 iPrev = 0; | |||
| 16739 | ||||
| 16740 | /* Allocate array of iterators if they are not already allocated. */ | |||
| 16741 | if( pT->aPoslistReader==0 ){ | |||
| 16742 | pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero( | |||
| 16743 | &pIter->pIndex->rc, | |||
| 16744 | pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)) | |||
| 16745 | ); | |||
| 16746 | if( pT->aPoslistReader==0 ) return; | |||
| 16747 | pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter]; | |||
| 16748 | } | |||
| 16749 | ||||
| 16750 | /* Populate an iterator for each poslist that will be merged */ | |||
| 16751 | for(ii=0; ii<pT->nIter; ii++){ | |||
| 16752 | Fts5Iter *p = pT->apIter[ii]; | |||
| 16753 | if( iRowid==p->base.iRowid ){ | |||
| 16754 | pT->aPoslistToIter[nReader] = ii; | |||
| 16755 | sqlite3Fts5PoslistReaderInit( | |||
| 16756 | p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++] | |||
| 16757 | ); | |||
| 16758 | nByte += p->base.nData; | |||
| 16759 | } | |||
| 16760 | } | |||
| 16761 | ||||
| 16762 | /* Ensure the output buffer is large enough */ | |||
| 16763 | if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10)( (u32)((&pIter->poslist)->n) + (u32)(nByte+nHit*10 ) <= (u32)((&pIter->poslist)->nSpace) ? 0 : sqlite3Fts5BufferSize ((&pIter->pIndex->rc),(&pIter->poslist),(nByte +nHit*10)+(&pIter->poslist)->n) ) ){ | |||
| 16764 | return; | |||
| 16765 | } | |||
| 16766 | ||||
| 16767 | /* Ensure the token-mapping is large enough */ | |||
| 16768 | if( eDetail==FTS5_DETAIL_FULL0 && pT->nMapAlloc<(pT->nMap + nByte) ){ | |||
| 16769 | i64 nNew = (pT->nMapAlloc + nByte) * 2; | |||
| 16770 | Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc64sqlite3_api->realloc64( | |||
| 16771 | pT->aMap, nNew*sizeof(Fts5TokenDataMap) | |||
| 16772 | ); | |||
| 16773 | if( aNew==0 ){ | |||
| 16774 | pIter->pIndex->rc = SQLITE_NOMEM7; | |||
| 16775 | return; | |||
| 16776 | } | |||
| 16777 | pT->aMap = aNew; | |||
| 16778 | pT->nMapAlloc = nNew; | |||
| 16779 | } | |||
| 16780 | ||||
| 16781 | pIter->poslist.n = 0; | |||
| 16782 | ||||
| 16783 | while( 1 ){ | |||
| 16784 | i64 iMinPos = LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)); | |||
| 16785 | ||||
| 16786 | /* Find smallest position */ | |||
| 16787 | iMin = 0; | |||
| 16788 | for(ii=0; ii<nReader; ii++){ | |||
| 16789 | Fts5PoslistReader *pReader = &pT->aPoslistReader[ii]; | |||
| 16790 | if( pReader->bEof==0 ){ | |||
| 16791 | if( pReader->iPos<iMinPos ){ | |||
| 16792 | iMinPos = pReader->iPos; | |||
| 16793 | iMin = ii; | |||
| 16794 | } | |||
| 16795 | } | |||
| 16796 | } | |||
| 16797 | ||||
| 16798 | /* If all readers were at EOF, break out of the loop. */ | |||
| 16799 | if( iMinPos==LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)) ) break; | |||
| 16800 | ||||
| 16801 | sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos); | |||
| 16802 | sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]); | |||
| 16803 | ||||
| 16804 | if( eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 16805 | pT->aMap[pT->nMap].iPos = iMinPos; | |||
| 16806 | pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin]; | |||
| 16807 | pT->aMap[pT->nMap].iRowid = iRowid; | |||
| 16808 | pT->nMap++; | |||
| 16809 | } | |||
| 16810 | } | |||
| 16811 | ||||
| 16812 | pIter->base.pData = pIter->poslist.p; | |||
| 16813 | pIter->base.nData = pIter->poslist.n; | |||
| 16814 | } | |||
| 16815 | } | |||
| 16816 | } | |||
| 16817 | ||||
| 16818 | /* | |||
| 16819 | ** The iterator passed as the only argument must be a tokendata=1 iterator | |||
| 16820 | ** (pIter->pTokenDataIter!=0). This function advances the iterator. If | |||
| 16821 | ** argument bFrom is false, then the iterator is advanced to the next | |||
| 16822 | ** entry. Or, if bFrom is true, it is advanced to the first entry with | |||
| 16823 | ** a rowid of iFrom or greater. | |||
| 16824 | */ | |||
| 16825 | static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ | |||
| 16826 | int ii; | |||
| 16827 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; | |||
| 16828 | Fts5Index *pIndex = pIter->pIndex; | |||
| 16829 | ||||
| 16830 | for(ii=0; ii<pT->nIter; ii++){ | |||
| 16831 | Fts5Iter *p = pT->apIter[ii]; | |||
| 16832 | if( p->base.bEof==0 | |||
| 16833 | && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom)) | |||
| 16834 | ){ | |||
| 16835 | fts5MultiIterNext(pIndex, p, bFrom, iFrom); | |||
| 16836 | while( bFrom && p->base.bEof==0 | |||
| 16837 | && p->base.iRowid<iFrom | |||
| 16838 | && pIndex->rc==SQLITE_OK0 | |||
| 16839 | ){ | |||
| 16840 | fts5MultiIterNext(pIndex, p, 0, 0); | |||
| 16841 | } | |||
| 16842 | } | |||
| 16843 | } | |||
| 16844 | ||||
| 16845 | if( pIndex->rc==SQLITE_OK0 ){ | |||
| 16846 | fts5IterSetOutputsTokendata(pIter); | |||
| 16847 | } | |||
| 16848 | } | |||
| 16849 | ||||
| 16850 | /* | |||
| 16851 | ** If the segment-iterator passed as the first argument is at EOF, then | |||
| 16852 | ** set pIter->term to a copy of buffer pTerm. | |||
| 16853 | */ | |||
| 16854 | static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ | |||
| 16855 | if( pIter && pIter->aSeg[0].pLeaf==0 ){ | |||
| 16856 | fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p)sqlite3Fts5BufferSet(&pIter->pIndex->rc,&pIter-> aSeg[0].term,pTerm->n,pTerm->p); | |||
| 16857 | } | |||
| 16858 | } | |||
| 16859 | ||||
| 16860 | /* | |||
| 16861 | ** This function sets up an iterator to use for a non-prefix query on a | |||
| 16862 | ** tokendata=1 table. | |||
| 16863 | */ | |||
| 16864 | static Fts5Iter *fts5SetupTokendataIter( | |||
| 16865 | Fts5Index *p, /* FTS index to query */ | |||
| 16866 | const u8 *pToken, /* Buffer containing query term */ | |||
| 16867 | int nToken, /* Size of buffer pToken in bytes */ | |||
| 16868 | Fts5Colset *pColset /* Colset to filter on */ | |||
| 16869 | ){ | |||
| 16870 | Fts5Iter *pRet = 0; | |||
| 16871 | Fts5TokenDataIter *pSet = 0; | |||
| 16872 | Fts5Structure *pStruct = 0; | |||
| 16873 | const int flags = FTS5INDEX_QUERY_SCANONETERM0x0100 | FTS5INDEX_QUERY_SCAN0x0008; | |||
| 16874 | ||||
| 16875 | Fts5Buffer bSeek = {0, 0, 0}; | |||
| 16876 | Fts5Buffer *pSmall = 0; | |||
| 16877 | ||||
| 16878 | fts5IndexFlush(p); | |||
| 16879 | pStruct = fts5StructureRead(p); | |||
| 16880 | ||||
| 16881 | while( p->rc==SQLITE_OK0 ){ | |||
| 16882 | Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0; | |||
| 16883 | Fts5Iter *pNew = 0; | |||
| 16884 | Fts5SegIter *pNewIter = 0; | |||
| 16885 | Fts5SegIter *pPrevIter = 0; | |||
| 16886 | ||||
| 16887 | int iLvl, iSeg, ii; | |||
| 16888 | ||||
| 16889 | pNew = fts5MultiIterAlloc(p, pStruct->nSegment); | |||
| 16890 | if( pSmall ){ | |||
| 16891 | fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p)sqlite3Fts5BufferSet(&p->rc,&bSeek,pSmall->n,pSmall ->p); | |||
| 16892 | fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0")sqlite3Fts5BufferAppendBlob(&p->rc,&bSeek,1,(const u8*)"\0"); | |||
| 16893 | }else{ | |||
| 16894 | fts5BufferSet(&p->rc, &bSeek, nToken, pToken)sqlite3Fts5BufferSet(&p->rc,&bSeek,nToken,pToken); | |||
| 16895 | } | |||
| 16896 | if( p->rc ){ | |||
| 16897 | fts5IterClose((Fts5IndexIter*)pNew); | |||
| 16898 | break; | |||
| 16899 | } | |||
| 16900 | ||||
| 16901 | pNewIter = &pNew->aSeg[0]; | |||
| 16902 | pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0); | |||
| 16903 | for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ | |||
| 16904 | for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ | |||
| 16905 | Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; | |||
| 16906 | int bDone = 0; | |||
| 16907 | ||||
| 16908 | if( pPrevIter ){ | |||
| 16909 | if( fts5BufferCompare(pSmall, &pPrevIter->term) ){ | |||
| 16910 | memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter)); | |||
| 16911 | memset(pPrevIter, 0, sizeof(Fts5SegIter)); | |||
| 16912 | bDone = 1; | |||
| 16913 | }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){ | |||
| 16914 | fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter); | |||
| 16915 | bDone = 1; | |||
| 16916 | } | |||
| 16917 | } | |||
| 16918 | ||||
| 16919 | if( bDone==0 ){ | |||
| 16920 | fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); | |||
| 16921 | } | |||
| 16922 | ||||
| 16923 | if( pPrevIter ){ | |||
| 16924 | if( pPrevIter->pTombArray ){ | |||
| 16925 | pNewIter->pTombArray = pPrevIter->pTombArray; | |||
| 16926 | pNewIter->pTombArray->nRef++; | |||
| 16927 | } | |||
| 16928 | }else{ | |||
| 16929 | fts5SegIterAllocTombstone(p, pNewIter); | |||
| 16930 | } | |||
| 16931 | ||||
| 16932 | pNewIter++; | |||
| 16933 | if( pPrevIter ) pPrevIter++; | |||
| 16934 | if( p->rc ) break; | |||
| 16935 | } | |||
| 16936 | } | |||
| 16937 | fts5TokendataSetTermIfEof(pPrev, pSmall); | |||
| 16938 | ||||
| 16939 | pNew->bSkipEmpty = 1; | |||
| 16940 | pNew->pColset = pColset; | |||
| 16941 | fts5IterSetOutputCb(&p->rc, pNew); | |||
| 16942 | ||||
| 16943 | /* Loop through all segments in the new iterator. Find the smallest | |||
| 16944 | ** term that any segment-iterator points to. Iterator pNew will be | |||
| 16945 | ** used for this term. Also, set any iterator that points to a term that | |||
| 16946 | ** does not match pToken/nToken to point to EOF */ | |||
| 16947 | pSmall = 0; | |||
| 16948 | for(ii=0; ii<pNew->nSeg; ii++){ | |||
| 16949 | Fts5SegIter *pII = &pNew->aSeg[ii]; | |||
| 16950 | if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){ | |||
| 16951 | fts5SegIterSetEOF(pII); | |||
| 16952 | } | |||
| 16953 | if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){ | |||
| 16954 | pSmall = &pII->term; | |||
| 16955 | } | |||
| 16956 | } | |||
| 16957 | ||||
| 16958 | /* If pSmall is still NULL at this point, then the new iterator does | |||
| 16959 | ** not point to any terms that match the query. So delete it and break | |||
| 16960 | ** out of the loop - all required iterators have been collected. */ | |||
| 16961 | if( pSmall==0 ){ | |||
| 16962 | fts5IterClose((Fts5IndexIter*)pNew); | |||
| 16963 | break; | |||
| 16964 | } | |||
| 16965 | ||||
| 16966 | /* Append this iterator to the set and continue. */ | |||
| 16967 | pSet = fts5AppendTokendataIter(p, pSet, pNew); | |||
| 16968 | } | |||
| 16969 | ||||
| 16970 | if( p->rc==SQLITE_OK0 && pSet ){ | |||
| 16971 | int ii; | |||
| 16972 | for(ii=0; ii<pSet->nIter; ii++){ | |||
| 16973 | Fts5Iter *pIter = pSet->apIter[ii]; | |||
| 16974 | int iSeg; | |||
| 16975 | for(iSeg=0; iSeg<pIter->nSeg; iSeg++){ | |||
| 16976 | pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM0x01; | |||
| 16977 | } | |||
| 16978 | fts5MultiIterFinishSetup(p, pIter); | |||
| 16979 | } | |||
| 16980 | } | |||
| 16981 | ||||
| 16982 | if( p->rc==SQLITE_OK0 ){ | |||
| 16983 | pRet = fts5MultiIterAlloc(p, 0); | |||
| 16984 | } | |||
| 16985 | if( pRet ){ | |||
| 16986 | pRet->nSeg = 0; | |||
| 16987 | pRet->pTokenDataIter = pSet; | |||
| 16988 | if( pSet ){ | |||
| 16989 | fts5IterSetOutputsTokendata(pRet); | |||
| 16990 | }else{ | |||
| 16991 | pRet->base.bEof = 1; | |||
| 16992 | } | |||
| 16993 | }else{ | |||
| 16994 | fts5TokendataIterDelete(pSet); | |||
| 16995 | } | |||
| 16996 | ||||
| 16997 | fts5StructureRelease(pStruct); | |||
| 16998 | fts5BufferFree(&bSeek)sqlite3Fts5BufferFree(&bSeek); | |||
| 16999 | return pRet; | |||
| 17000 | } | |||
| 17001 | ||||
| 17002 | /* | |||
| 17003 | ** Open a new iterator to iterate though all rowid that match the | |||
| 17004 | ** specified token or token prefix. | |||
| 17005 | */ | |||
| 17006 | static int sqlite3Fts5IndexQuery( | |||
| 17007 | Fts5Index *p, /* FTS index to query */ | |||
| 17008 | const char *pToken, int nToken, /* Token (or prefix) to query for */ | |||
| 17009 | int flags, /* Mask of FTS5INDEX_QUERY_X flags */ | |||
| 17010 | Fts5Colset *pColset, /* Match these columns only */ | |||
| 17011 | Fts5IndexIter **ppIter /* OUT: New iterator object */ | |||
| 17012 | ){ | |||
| 17013 | Fts5Config *pConfig = p->pConfig; | |||
| 17014 | Fts5Iter *pRet = 0; | |||
| 17015 | Fts5Buffer buf = {0, 0, 0}; | |||
| 17016 | ||||
| 17017 | /* If the QUERY_SCAN flag is set, all other flags must be clear. */ | |||
| 17018 | assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN )((void) (0)); | |||
| 17019 | ||||
| 17020 | if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ | |||
| 17021 | int iIdx = 0; /* Index to search */ | |||
| 17022 | int iPrefixIdx = 0; /* +1 prefix index */ | |||
| 17023 | int bTokendata = pConfig->bTokendata; | |||
| 17024 | assert( buf.p!=0 )((void) (0)); | |||
| 17025 | if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); | |||
| 17026 | ||||
| 17027 | /* The NOTOKENDATA flag is set when each token in a tokendata=1 table | |||
| 17028 | ** should be treated individually, instead of merging all those with | |||
| 17029 | ** a common prefix into a single entry. This is used, for example, by | |||
| 17030 | ** queries performed as part of an integrity-check, or by the fts5vocab | |||
| 17031 | ** module. */ | |||
| 17032 | if( flags & (FTS5INDEX_QUERY_NOTOKENDATA0x0080|FTS5INDEX_QUERY_SCAN0x0008) ){ | |||
| 17033 | bTokendata = 0; | |||
| 17034 | } | |||
| 17035 | ||||
| 17036 | /* Figure out which index to search and set iIdx accordingly. If this | |||
| 17037 | ** is a prefix query for which there is no prefix index, set iIdx to | |||
| 17038 | ** greater than pConfig->nPrefix to indicate that the query will be | |||
| 17039 | ** satisfied by scanning multiple terms in the main index. | |||
| 17040 | ** | |||
| 17041 | ** If the QUERY_TEST_NOIDX flag was specified, then this must be a | |||
| 17042 | ** prefix-query. Instead of using a prefix-index (if one exists), | |||
| 17043 | ** evaluate the prefix query using the main FTS index. This is used | |||
| 17044 | ** for internal sanity checking by the integrity-check in debug | |||
| 17045 | ** mode only. */ | |||
| 17046 | #ifdef SQLITE_DEBUG | |||
| 17047 | if( pConfig->bPrefixIndex==0 || (flags & FTS5INDEX_QUERY_TEST_NOIDX0x0004) ){ | |||
| 17048 | assert( flags & FTS5INDEX_QUERY_PREFIX )((void) (0)); | |||
| 17049 | iIdx = 1+pConfig->nPrefix; | |||
| 17050 | }else | |||
| 17051 | #endif | |||
| 17052 | if( flags & FTS5INDEX_QUERY_PREFIX0x0001 ){ | |||
| 17053 | int nChar = fts5IndexCharlen(pToken, nToken); | |||
| 17054 | for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){ | |||
| 17055 | int nIdxChar = pConfig->aPrefix[iIdx-1]; | |||
| 17056 | if( nIdxChar==nChar ) break; | |||
| 17057 | if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; | |||
| 17058 | } | |||
| 17059 | } | |||
| 17060 | ||||
| 17061 | if( bTokendata && iIdx==0 ){ | |||
| 17062 | buf.p[0] = FTS5_MAIN_PREFIX'0'; | |||
| 17063 | pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); | |||
| 17064 | }else if( iIdx<=pConfig->nPrefix ){ | |||
| 17065 | /* Straight index lookup */ | |||
| 17066 | Fts5Structure *pStruct = fts5StructureRead(p); | |||
| 17067 | buf.p[0] = (u8)(FTS5_MAIN_PREFIX'0' + iIdx); | |||
| 17068 | if( pStruct ){ | |||
| 17069 | fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY0x0010, | |||
| 17070 | pColset, buf.p, nToken+1, -1, 0, &pRet | |||
| 17071 | ); | |||
| 17072 | fts5StructureRelease(pStruct); | |||
| 17073 | } | |||
| 17074 | }else{ | |||
| 17075 | /* Scan multiple terms in the main index for a prefix query. */ | |||
| 17076 | int bDesc = (flags & FTS5INDEX_QUERY_DESC0x0002)!=0; | |||
| 17077 | fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); | |||
| 17078 | if( pRet==0 ){ | |||
| 17079 | assert( p->rc!=SQLITE_OK )((void) (0)); | |||
| 17080 | }else{ | |||
| 17081 | assert( pRet->pColset==0 )((void) (0)); | |||
| 17082 | fts5IterSetOutputCb(&p->rc, pRet); | |||
| 17083 | if( p->rc==SQLITE_OK0 ){ | |||
| 17084 | Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; | |||
| 17085 | if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); | |||
| 17086 | } | |||
| 17087 | } | |||
| 17088 | } | |||
| 17089 | ||||
| 17090 | if( p->rc ){ | |||
| 17091 | fts5IterClose((Fts5IndexIter*)pRet); | |||
| 17092 | pRet = 0; | |||
| 17093 | fts5IndexCloseReader(p); | |||
| 17094 | } | |||
| 17095 | ||||
| 17096 | *ppIter = (Fts5IndexIter*)pRet; | |||
| 17097 | sqlite3Fts5BufferFree(&buf); | |||
| 17098 | } | |||
| 17099 | return fts5IndexReturn(p); | |||
| 17100 | } | |||
| 17101 | ||||
| 17102 | /* | |||
| 17103 | ** Return true if the iterator passed as the only argument is at EOF. | |||
| 17104 | */ | |||
| 17105 | /* | |||
| 17106 | ** Move to the next matching rowid. | |||
| 17107 | */ | |||
| 17108 | static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ | |||
| 17109 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | |||
| 17110 | assert( pIter->pIndex->rc==SQLITE_OK )((void) (0)); | |||
| 17111 | if( pIter->nSeg==0 ){ | |||
| 17112 | assert( pIter->pTokenDataIter )((void) (0)); | |||
| 17113 | fts5TokendataIterNext(pIter, 0, 0); | |||
| 17114 | }else{ | |||
| 17115 | fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); | |||
| 17116 | } | |||
| 17117 | return fts5IndexReturn(pIter->pIndex); | |||
| 17118 | } | |||
| 17119 | ||||
| 17120 | /* | |||
| 17121 | ** Move to the next matching term/rowid. Used by the fts5vocab module. | |||
| 17122 | */ | |||
| 17123 | static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){ | |||
| 17124 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | |||
| 17125 | Fts5Index *p = pIter->pIndex; | |||
| 17126 | ||||
| 17127 | assert( pIter->pIndex->rc==SQLITE_OK )((void) (0)); | |||
| 17128 | ||||
| 17129 | fts5MultiIterNext(p, pIter, 0, 0); | |||
| 17130 | if( p->rc==SQLITE_OK0 ){ | |||
| 17131 | Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; | |||
| 17132 | if( pSeg->pLeaf && pSeg->term.p[0]!=FTS5_MAIN_PREFIX'0' ){ | |||
| 17133 | fts5DataRelease(pSeg->pLeaf); | |||
| 17134 | pSeg->pLeaf = 0; | |||
| 17135 | pIter->base.bEof = 1; | |||
| 17136 | } | |||
| 17137 | } | |||
| 17138 | ||||
| 17139 | return fts5IndexReturn(pIter->pIndex); | |||
| 17140 | } | |||
| 17141 | ||||
| 17142 | /* | |||
| 17143 | ** Move to the next matching rowid that occurs at or after iMatch. The | |||
| 17144 | ** definition of "at or after" depends on whether this iterator iterates | |||
| 17145 | ** in ascending or descending rowid order. | |||
| 17146 | */ | |||
| 17147 | static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ | |||
| 17148 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | |||
| 17149 | if( pIter->nSeg==0 ){ | |||
| 17150 | assert( pIter->pTokenDataIter )((void) (0)); | |||
| 17151 | fts5TokendataIterNext(pIter, 1, iMatch); | |||
| 17152 | }else{ | |||
| 17153 | fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); | |||
| 17154 | } | |||
| 17155 | return fts5IndexReturn(pIter->pIndex); | |||
| 17156 | } | |||
| 17157 | ||||
| 17158 | /* | |||
| 17159 | ** Return the current term. | |||
| 17160 | */ | |||
| 17161 | static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ | |||
| 17162 | int n; | |||
| 17163 | const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); | |||
| 17164 | assert_nc( z || n<=1 )((void) (0)); | |||
| 17165 | *pn = n-1; | |||
| 17166 | return (z ? &z[1] : 0); | |||
| 17167 | } | |||
| 17168 | ||||
| 17169 | /* | |||
| 17170 | ** pIter is a prefix query. This function populates pIter->pTokenDataIter | |||
| 17171 | ** with an Fts5TokenDataIter object containing mappings for all rows | |||
| 17172 | ** matched by the query. | |||
| 17173 | */ | |||
| 17174 | static int fts5SetupPrefixIterTokendata( | |||
| 17175 | Fts5Iter *pIter, | |||
| 17176 | const char *pToken, /* Token prefix to search for */ | |||
| 17177 | int nToken /* Size of pToken in bytes */ | |||
| 17178 | ){ | |||
| 17179 | Fts5Index *p = pIter->pIndex; | |||
| 17180 | Fts5Buffer token = {0, 0, 0}; | |||
| 17181 | TokendataSetupCtx ctx; | |||
| 17182 | ||||
| 17183 | memset(&ctx, 0, sizeof(ctx)); | |||
| 17184 | ||||
| 17185 | fts5BufferGrow(&p->rc, &token, nToken+1)( (u32)((&token)->n) + (u32)(nToken+1) <= (u32)((& token)->nSpace) ? 0 : sqlite3Fts5BufferSize((&p->rc ),(&token),(nToken+1)+(&token)->n) ); | |||
| 17186 | assert( token.p!=0 || p->rc!=SQLITE_OK )((void) (0)); | |||
| 17187 | ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, | |||
| 17188 | SZ_FTS5TOKENDATAITER(1)(__builtin_offsetof(Fts5TokenDataIter, apIter) + (1)*sizeof(Fts5Iter ))); | |||
| 17189 | ||||
| 17190 | if( p->rc==SQLITE_OK0 ){ | |||
| 17191 | ||||
| 17192 | /* Fill in the token prefix to search for */ | |||
| 17193 | token.p[0] = FTS5_MAIN_PREFIX'0'; | |||
| 17194 | memcpy(&token.p[1], pToken, nToken); | |||
| 17195 | token.n = nToken+1; | |||
| 17196 | ||||
| 17197 | fts5VisitEntries( | |||
| 17198 | p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx | |||
| 17199 | ); | |||
| 17200 | ||||
| 17201 | fts5TokendataIterSortMap(p, ctx.pT); | |||
| 17202 | } | |||
| 17203 | ||||
| 17204 | if( p->rc==SQLITE_OK0 ){ | |||
| 17205 | pIter->pTokenDataIter = ctx.pT; | |||
| 17206 | }else{ | |||
| 17207 | fts5TokendataIterDelete(ctx.pT); | |||
| 17208 | } | |||
| 17209 | fts5BufferFree(&token)sqlite3Fts5BufferFree(&token); | |||
| 17210 | ||||
| 17211 | return fts5IndexReturn(p); | |||
| 17212 | } | |||
| 17213 | ||||
| 17214 | /* | |||
| 17215 | ** This is used by xInstToken() to access the token at offset iOff, column | |||
| 17216 | ** iCol of row iRowid. The token is returned via output variables *ppOut | |||
| 17217 | ** and *pnOut. The iterator passed as the first argument must be a tokendata=1 | |||
| 17218 | ** iterator (pIter->pTokenDataIter!=0). | |||
| 17219 | ** | |||
| 17220 | ** pToken/nToken: | |||
| 17221 | */ | |||
| 17222 | static int sqlite3Fts5IterToken( | |||
| 17223 | Fts5IndexIter *pIndexIter, | |||
| 17224 | const char *pToken, int nToken, | |||
| 17225 | i64 iRowid, | |||
| 17226 | int iCol, | |||
| 17227 | int iOff, | |||
| 17228 | const char **ppOut, int *pnOut | |||
| 17229 | ){ | |||
| 17230 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | |||
| 17231 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; | |||
| 17232 | i64 iPos = (((i64)iCol)<<32) + iOff; | |||
| 17233 | Fts5TokenDataMap *aMap = 0; | |||
| 17234 | int i1 = 0; | |||
| 17235 | int i2 = 0; | |||
| 17236 | int iTest = 0; | |||
| 17237 | ||||
| 17238 | assert( pT || (pToken && pIter->nSeg>0) )((void) (0)); | |||
| 17239 | if( pT==0 ){ | |||
| 17240 | int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken); | |||
| 17241 | if( rc!=SQLITE_OK0 ) return rc; | |||
| 17242 | pT = pIter->pTokenDataIter; | |||
| 17243 | } | |||
| 17244 | ||||
| 17245 | i2 = pT->nMap; | |||
| 17246 | aMap = pT->aMap; | |||
| 17247 | ||||
| 17248 | while( i2>i1 ){ | |||
| 17249 | iTest = (i1 + i2) / 2; | |||
| 17250 | ||||
| 17251 | if( aMap[iTest].iRowid<iRowid ){ | |||
| 17252 | i1 = iTest+1; | |||
| 17253 | }else if( aMap[iTest].iRowid>iRowid ){ | |||
| 17254 | i2 = iTest; | |||
| 17255 | }else{ | |||
| 17256 | if( aMap[iTest].iPos<iPos ){ | |||
| 17257 | if( aMap[iTest].iPos<0 ){ | |||
| 17258 | break; | |||
| 17259 | } | |||
| 17260 | i1 = iTest+1; | |||
| 17261 | }else if( aMap[iTest].iPos>iPos ){ | |||
| 17262 | i2 = iTest; | |||
| 17263 | }else{ | |||
| 17264 | break; | |||
| 17265 | } | |||
| 17266 | } | |||
| 17267 | } | |||
| 17268 | ||||
| 17269 | if( i2>i1 ){ | |||
| 17270 | if( pIter->nSeg==0 ){ | |||
| 17271 | Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; | |||
| 17272 | *ppOut = (const char*)pMap->aSeg[0].term.p+1; | |||
| 17273 | *pnOut = pMap->aSeg[0].term.n-1; | |||
| 17274 | }else{ | |||
| 17275 | Fts5TokenDataMap *p = &aMap[iTest]; | |||
| 17276 | *ppOut = (const char*)&pT->terms.p[p->iIter]; | |||
| 17277 | *pnOut = aMap[iTest].nByte; | |||
| 17278 | } | |||
| 17279 | } | |||
| 17280 | ||||
| 17281 | return SQLITE_OK0; | |||
| 17282 | } | |||
| 17283 | ||||
| 17284 | /* | |||
| 17285 | ** Clear any existing entries from the token-map associated with the | |||
| 17286 | ** iterator passed as the only argument. | |||
| 17287 | */ | |||
| 17288 | static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ | |||
| 17289 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | |||
| 17290 | if( pIter && pIter->pTokenDataIter | |||
| 17291 | && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL0) | |||
| 17292 | ){ | |||
| 17293 | pIter->pTokenDataIter->nMap = 0; | |||
| 17294 | } | |||
| 17295 | } | |||
| 17296 | ||||
| 17297 | /* | |||
| 17298 | ** Set a token-mapping for the iterator passed as the first argument. This | |||
| 17299 | ** is used in detail=column or detail=none mode when a token is requested | |||
| 17300 | ** using the xInstToken() API. In this case the caller tokenizers the | |||
| 17301 | ** current row and configures the token-mapping via multiple calls to this | |||
| 17302 | ** function. | |||
| 17303 | */ | |||
| 17304 | static int sqlite3Fts5IndexIterWriteTokendata( | |||
| 17305 | Fts5IndexIter *pIndexIter, | |||
| 17306 | const char *pToken, int nToken, | |||
| 17307 | i64 iRowid, int iCol, int iOff | |||
| 17308 | ){ | |||
| 17309 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; | |||
| 17310 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; | |||
| 17311 | Fts5Index *p = pIter->pIndex; | |||
| 17312 | i64 iPos = (((i64)iCol)<<32) + iOff; | |||
| 17313 | ||||
| 17314 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL )((void) (0)); | |||
| 17315 | assert( pIter->pTokenDataIter || pIter->nSeg>0 )((void) (0)); | |||
| 17316 | if( pIter->nSeg>0 ){ | |||
| 17317 | /* This is a prefix term iterator. */ | |||
| 17318 | if( pT==0 ){ | |||
| 17319 | pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, | |||
| 17320 | SZ_FTS5TOKENDATAITER(1)(__builtin_offsetof(Fts5TokenDataIter, apIter) + (1)*sizeof(Fts5Iter ))); | |||
| 17321 | pIter->pTokenDataIter = pT; | |||
| 17322 | } | |||
| 17323 | if( pT ){ | |||
| 17324 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); | |||
| 17325 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken)sqlite3Fts5BufferAppendBlob(&p->rc,&pT->terms,nToken ,(const u8*)pToken); | |||
| 17326 | } | |||
| 17327 | }else{ | |||
| 17328 | int ii; | |||
| 17329 | for(ii=0; ii<pT->nIter; ii++){ | |||
| 17330 | Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; | |||
| 17331 | if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; | |||
| 17332 | } | |||
| 17333 | if( ii<pT->nIter ){ | |||
| 17334 | fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos); | |||
| 17335 | } | |||
| 17336 | } | |||
| 17337 | return fts5IndexReturn(p); | |||
| 17338 | } | |||
| 17339 | ||||
| 17340 | /* | |||
| 17341 | ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery(). | |||
| 17342 | */ | |||
| 17343 | static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ | |||
| 17344 | if( pIndexIter ){ | |||
| 17345 | Fts5Index *pIndex = ((Fts5Iter*)pIndexIter)->pIndex; | |||
| 17346 | fts5IterClose(pIndexIter); | |||
| 17347 | fts5IndexReturn(pIndex); | |||
| 17348 | } | |||
| 17349 | } | |||
| 17350 | ||||
| 17351 | /* | |||
| 17352 | ** Read and decode the "averages" record from the database. | |||
| 17353 | ** | |||
| 17354 | ** Parameter anSize must point to an array of size nCol, where nCol is | |||
| 17355 | ** the number of user defined columns in the FTS table. | |||
| 17356 | */ | |||
| 17357 | static int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize){ | |||
| 17358 | int nCol = p->pConfig->nCol; | |||
| 17359 | Fts5Data *pData; | |||
| 17360 | ||||
| 17361 | *pnRow = 0; | |||
| 17362 | memset(anSize, 0, sizeof(i64) * nCol); | |||
| 17363 | pData = fts5DataRead(p, FTS5_AVERAGES_ROWID1); | |||
| 17364 | if( p->rc==SQLITE_OK0 && pData->nn ){ | |||
| 17365 | int i = 0; | |||
| 17366 | int iCol; | |||
| 17367 | i += fts5GetVarintsqlite3Fts5GetVarint(&pData->p[i], (u64*)pnRow); | |||
| 17368 | for(iCol=0; i<pData->nn && iCol<nCol; iCol++){ | |||
| 17369 | i += fts5GetVarintsqlite3Fts5GetVarint(&pData->p[i], (u64*)&anSize[iCol]); | |||
| 17370 | } | |||
| 17371 | } | |||
| 17372 | ||||
| 17373 | fts5DataRelease(pData); | |||
| 17374 | return fts5IndexReturn(p); | |||
| 17375 | } | |||
| 17376 | ||||
| 17377 | /* | |||
| 17378 | ** Replace the current "averages" record with the contents of the buffer | |||
| 17379 | ** supplied as the second argument. | |||
| 17380 | */ | |||
| 17381 | static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8 *pData, int nData){ | |||
| 17382 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 17383 | fts5DataWrite(p, FTS5_AVERAGES_ROWID1, pData, nData); | |||
| 17384 | return fts5IndexReturn(p); | |||
| 17385 | } | |||
| 17386 | ||||
| 17387 | /* | |||
| 17388 | ** Return the total number of blocks this module has read from the %_data | |||
| 17389 | ** table since it was created. | |||
| 17390 | */ | |||
| 17391 | static int sqlite3Fts5IndexReads(Fts5Index *p){ | |||
| 17392 | return p->nRead; | |||
| 17393 | } | |||
| 17394 | ||||
| 17395 | /* | |||
| 17396 | ** Set the 32-bit cookie value stored at the start of all structure | |||
| 17397 | ** records to the value passed as the second argument. | |||
| 17398 | ** | |||
| 17399 | ** Return SQLITE_OK if successful, or an SQLite error code if an error | |||
| 17400 | ** occurs. | |||
| 17401 | */ | |||
| 17402 | static int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){ | |||
| 17403 | int rc; /* Return code */ | |||
| 17404 | Fts5Config *pConfig = p->pConfig; /* Configuration object */ | |||
| 17405 | u8 aCookie[4]; /* Binary representation of iNew */ | |||
| 17406 | sqlite3_blob *pBlob = 0; | |||
| 17407 | ||||
| 17408 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 17409 | sqlite3Fts5Put32(aCookie, iNew); | |||
| 17410 | ||||
| 17411 | rc = sqlite3_blob_opensqlite3_api->blob_open(pConfig->db, pConfig->zDb, p->zDataTbl, | |||
| 17412 | "block", FTS5_STRUCTURE_ROWID10, 1, &pBlob | |||
| 17413 | ); | |||
| 17414 | if( rc==SQLITE_OK0 ){ | |||
| 17415 | sqlite3_blob_writesqlite3_api->blob_write(pBlob, aCookie, 4, 0); | |||
| 17416 | rc = sqlite3_blob_closesqlite3_api->blob_close(pBlob); | |||
| 17417 | } | |||
| 17418 | ||||
| 17419 | return rc; | |||
| 17420 | } | |||
| 17421 | ||||
| 17422 | static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){ | |||
| 17423 | Fts5Structure *pStruct; | |||
| 17424 | pStruct = fts5StructureRead(p); | |||
| 17425 | fts5StructureRelease(pStruct); | |||
| 17426 | return fts5IndexReturn(p); | |||
| 17427 | } | |||
| 17428 | ||||
| 17429 | /* | |||
| 17430 | ** Retrieve the origin value that will be used for the segment currently | |||
| 17431 | ** being accumulated in the in-memory hash table when it is flushed to | |||
| 17432 | ** disk. If successful, SQLITE_OK is returned and (*piOrigin) set to | |||
| 17433 | ** the queried value. Or, if an error occurs, an error code is returned | |||
| 17434 | ** and the final value of (*piOrigin) is undefined. | |||
| 17435 | */ | |||
| 17436 | static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin){ | |||
| 17437 | Fts5Structure *pStruct; | |||
| 17438 | pStruct = fts5StructureRead(p); | |||
| 17439 | if( pStruct ){ | |||
| 17440 | *piOrigin = pStruct->nOriginCntr; | |||
| 17441 | fts5StructureRelease(pStruct); | |||
| 17442 | } | |||
| 17443 | return fts5IndexReturn(p); | |||
| 17444 | } | |||
| 17445 | ||||
| 17446 | /* | |||
| 17447 | ** Buffer pPg contains a page of a tombstone hash table - one of nPg pages | |||
| 17448 | ** associated with the same segment. This function adds rowid iRowid to | |||
| 17449 | ** the hash table. The caller is required to guarantee that there is at | |||
| 17450 | ** least one free slot on the page. | |||
| 17451 | ** | |||
| 17452 | ** If parameter bForce is false and the hash table is deemed to be full | |||
| 17453 | ** (more than half of the slots are occupied), then non-zero is returned | |||
| 17454 | ** and iRowid not inserted. Or, if bForce is true or if the hash table page | |||
| 17455 | ** is not full, iRowid is inserted and zero returned. | |||
| 17456 | */ | |||
| 17457 | static int fts5IndexTombstoneAddToPage( | |||
| 17458 | Fts5Data *pPg, | |||
| 17459 | int bForce, | |||
| 17460 | int nPg, | |||
| 17461 | u64 iRowid | |||
| 17462 | ){ | |||
| 17463 | const int szKey = TOMBSTONE_KEYSIZE(pPg)(pPg->p[0]==4 ? 4 : 8); | |||
| 17464 | const int nSlot = TOMBSTONE_NSLOT(pPg)((pPg->nn > 16) ? ((pPg->nn-8) / (pPg->p[0]==4 ? 4 : 8)) : 1); | |||
| 17465 | const int nElem = fts5GetU32(&pPg->p[4]); | |||
| 17466 | int iSlot = (iRowid / nPg) % nSlot; | |||
| 17467 | int nCollide = nSlot; | |||
| 17468 | ||||
| 17469 | if( szKey==4 && iRowid>0xFFFFFFFF ) return 2; | |||
| 17470 | if( iRowid==0 ){ | |||
| 17471 | pPg->p[1] = 0x01; | |||
| 17472 | return 0; | |||
| 17473 | } | |||
| 17474 | ||||
| 17475 | if( bForce==0 && nElem>=(nSlot/2) ){ | |||
| 17476 | return 1; | |||
| 17477 | } | |||
| 17478 | ||||
| 17479 | fts5PutU32(&pPg->p[4], nElem+1); | |||
| 17480 | if( szKey==4 ){ | |||
| 17481 | u32 *aSlot = (u32*)&pPg->p[8]; | |||
| 17482 | while( aSlot[iSlot] ){ | |||
| 17483 | iSlot = (iSlot + 1) % nSlot; | |||
| 17484 | if( nCollide--==0 ) return 0; | |||
| 17485 | } | |||
| 17486 | fts5PutU32((u8*)&aSlot[iSlot], (u32)iRowid); | |||
| 17487 | }else{ | |||
| 17488 | u64 *aSlot = (u64*)&pPg->p[8]; | |||
| 17489 | while( aSlot[iSlot] ){ | |||
| 17490 | iSlot = (iSlot + 1) % nSlot; | |||
| 17491 | if( nCollide--==0 ) return 0; | |||
| 17492 | } | |||
| 17493 | fts5PutU64((u8*)&aSlot[iSlot], iRowid); | |||
| 17494 | } | |||
| 17495 | ||||
| 17496 | return 0; | |||
| 17497 | } | |||
| 17498 | ||||
| 17499 | /* | |||
| 17500 | ** This function attempts to build a new hash containing all the keys | |||
| 17501 | ** currently in the tombstone hash table for segment pSeg. The new | |||
| 17502 | ** hash will be stored in the nOut buffers passed in array apOut[]. | |||
| 17503 | ** All pages of the new hash use key-size szKey (4 or 8). | |||
| 17504 | ** | |||
| 17505 | ** Return 0 if the hash is successfully rebuilt into the nOut pages. | |||
| 17506 | ** Or non-zero if it is not (because one page became overfull). In this | |||
| 17507 | ** case the caller should retry with a larger nOut parameter. | |||
| 17508 | ** | |||
| 17509 | ** Parameter pData1 is page iPg1 of the hash table being rebuilt. | |||
| 17510 | */ | |||
| 17511 | static int fts5IndexTombstoneRehash( | |||
| 17512 | Fts5Index *p, | |||
| 17513 | Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ | |||
| 17514 | Fts5Data *pData1, /* One page of current hash - or NULL */ | |||
| 17515 | int iPg1, /* Which page of the current hash is pData1 */ | |||
| 17516 | int szKey, /* 4 or 8, the keysize */ | |||
| 17517 | int nOut, /* Number of output pages */ | |||
| 17518 | Fts5Data **apOut /* Array of output hash pages */ | |||
| 17519 | ){ | |||
| 17520 | int ii; | |||
| 17521 | int res = 0; | |||
| 17522 | ||||
| 17523 | /* Initialize the headers of all the output pages */ | |||
| 17524 | for(ii=0; ii<nOut; ii++){ | |||
| 17525 | apOut[ii]->p[0] = szKey; | |||
| 17526 | fts5PutU32(&apOut[ii]->p[4], 0); | |||
| 17527 | } | |||
| 17528 | ||||
| 17529 | /* Loop through the current pages of the hash table. */ | |||
| 17530 | for(ii=0; res==0 && ii<pSeg->nPgTombstone; ii++){ | |||
| 17531 | Fts5Data *pData = 0; /* Page ii of the current hash table */ | |||
| 17532 | Fts5Data *pFree = 0; /* Free this at the end of the loop */ | |||
| 17533 | ||||
| 17534 | if( iPg1==ii ){ | |||
| 17535 | pData = pData1; | |||
| 17536 | }else{ | |||
| 17537 | pFree = pData = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid, ii)( ((i64)(pSeg->iSegid+(1<<16)) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + (( i64)(ii)) )); | |||
| 17538 | } | |||
| 17539 | ||||
| 17540 | if( pData ){ | |||
| 17541 | int szKeyIn = TOMBSTONE_KEYSIZE(pData)(pData->p[0]==4 ? 4 : 8); | |||
| 17542 | int nSlotIn = (pData->nn - 8) / szKeyIn; | |||
| 17543 | int iIn; | |||
| 17544 | for(iIn=0; iIn<nSlotIn; iIn++){ | |||
| 17545 | u64 iVal = 0; | |||
| 17546 | ||||
| 17547 | /* Read the value from slot iIn of the input page into iVal. */ | |||
| 17548 | if( szKeyIn==4 ){ | |||
| 17549 | u32 *aSlot = (u32*)&pData->p[8]; | |||
| 17550 | if( aSlot[iIn] ) iVal = fts5GetU32((u8*)&aSlot[iIn]); | |||
| 17551 | }else{ | |||
| 17552 | u64 *aSlot = (u64*)&pData->p[8]; | |||
| 17553 | if( aSlot[iIn] ) iVal = fts5GetU64((u8*)&aSlot[iIn]); | |||
| 17554 | } | |||
| 17555 | ||||
| 17556 | /* If iVal is not 0 at this point, insert it into the new hash table */ | |||
| 17557 | if( iVal ){ | |||
| 17558 | Fts5Data *pPg = apOut[(iVal % nOut)]; | |||
| 17559 | res = fts5IndexTombstoneAddToPage(pPg, 0, nOut, iVal); | |||
| 17560 | if( res ) break; | |||
| 17561 | } | |||
| 17562 | } | |||
| 17563 | ||||
| 17564 | /* If this is page 0 of the old hash, copy the rowid-0-flag from the | |||
| 17565 | ** old hash to the new. */ | |||
| 17566 | if( ii==0 ){ | |||
| 17567 | apOut[0]->p[1] = pData->p[1]; | |||
| 17568 | } | |||
| 17569 | } | |||
| 17570 | fts5DataRelease(pFree); | |||
| 17571 | } | |||
| 17572 | ||||
| 17573 | return res; | |||
| 17574 | } | |||
| 17575 | ||||
| 17576 | /* | |||
| 17577 | ** This is called to rebuild the hash table belonging to segment pSeg. | |||
| 17578 | ** If parameter pData1 is not NULL, then one page of the existing hash table | |||
| 17579 | ** has already been loaded - pData1, which is page iPg1. The key-size for | |||
| 17580 | ** the new hash table is szKey (4 or 8). | |||
| 17581 | ** | |||
| 17582 | ** If successful, the new hash table is not written to disk. Instead, | |||
| 17583 | ** output parameter (*pnOut) is set to the number of pages in the new | |||
| 17584 | ** hash table, and (*papOut) to point to an array of buffers containing | |||
| 17585 | ** the new page data. | |||
| 17586 | ** | |||
| 17587 | ** If an error occurs, an error code is left in the Fts5Index object and | |||
| 17588 | ** both output parameters set to 0 before returning. | |||
| 17589 | */ | |||
| 17590 | static void fts5IndexTombstoneRebuild( | |||
| 17591 | Fts5Index *p, | |||
| 17592 | Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ | |||
| 17593 | Fts5Data *pData1, /* One page of current hash - or NULL */ | |||
| 17594 | int iPg1, /* Which page of the current hash is pData1 */ | |||
| 17595 | int szKey, /* 4 or 8, the keysize */ | |||
| 17596 | int *pnOut, /* OUT: Number of output pages */ | |||
| 17597 | Fts5Data ***papOut /* OUT: Output hash pages */ | |||
| 17598 | ){ | |||
| 17599 | const int MINSLOT = 32; | |||
| 17600 | int nSlotPerPage = MAX(MINSLOT, (p->pConfig->pgsz - 8) / szKey)(((MINSLOT) > ((p->pConfig->pgsz - 8) / szKey)) ? (MINSLOT ) : ((p->pConfig->pgsz - 8) / szKey)); | |||
| 17601 | int nSlot = 0; /* Number of slots in each output page */ | |||
| 17602 | int nOut = 0; | |||
| 17603 | ||||
| 17604 | /* Figure out how many output pages (nOut) and how many slots per | |||
| 17605 | ** page (nSlot). There are three possibilities: | |||
| 17606 | ** | |||
| 17607 | ** 1. The hash table does not yet exist. In this case the new hash | |||
| 17608 | ** table will consist of a single page with MINSLOT slots. | |||
| 17609 | ** | |||
| 17610 | ** 2. The hash table exists but is currently a single page. In this | |||
| 17611 | ** case an attempt is made to grow the page to accommodate the new | |||
| 17612 | ** entry. The page is allowed to grow up to nSlotPerPage (see above) | |||
| 17613 | ** slots. | |||
| 17614 | ** | |||
| 17615 | ** 3. The hash table already consists of more than one page, or of | |||
| 17616 | ** a single page already so large that it cannot be grown. In this | |||
| 17617 | ** case the new hash consists of (nPg*2+1) pages of nSlotPerPage | |||
| 17618 | ** slots each, where nPg is the current number of pages in the | |||
| 17619 | ** hash table. | |||
| 17620 | */ | |||
| 17621 | if( pSeg->nPgTombstone==0 ){ | |||
| 17622 | /* Case 1. */ | |||
| 17623 | nOut = 1; | |||
| 17624 | nSlot = MINSLOT; | |||
| 17625 | }else if( pSeg->nPgTombstone==1 ){ | |||
| 17626 | /* Case 2. */ | |||
| 17627 | int nElem = (int)fts5GetU32(&pData1->p[4]); | |||
| 17628 | assert( pData1 && iPg1==0 )((void) (0)); | |||
| 17629 | nOut = 1; | |||
| 17630 | nSlot = MAX(nElem*4, MINSLOT)(((nElem*4) > (MINSLOT)) ? (nElem*4) : (MINSLOT)); | |||
| 17631 | if( nSlot>nSlotPerPage ) nOut = 0; | |||
| 17632 | } | |||
| 17633 | if( nOut==0 ){ | |||
| 17634 | /* Case 3. */ | |||
| 17635 | nOut = (pSeg->nPgTombstone * 2 + 1); | |||
| 17636 | nSlot = nSlotPerPage; | |||
| 17637 | } | |||
| 17638 | ||||
| 17639 | /* Allocate the required array and output pages */ | |||
| 17640 | while( 1 ){ | |||
| 17641 | int res = 0; | |||
| 17642 | int ii = 0; | |||
| 17643 | int szPage = 0; | |||
| 17644 | Fts5Data **apOut = 0; | |||
| 17645 | ||||
| 17646 | /* Allocate space for the new hash table */ | |||
| 17647 | assert( nSlot>=MINSLOT )((void) (0)); | |||
| 17648 | apOut = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data*) * nOut); | |||
| 17649 | szPage = 8 + nSlot*szKey; | |||
| 17650 | for(ii=0; ii<nOut; ii++){ | |||
| 17651 | Fts5Data *pNew = (Fts5Data*)sqlite3Fts5MallocZero(&p->rc, | |||
| 17652 | sizeof(Fts5Data)+szPage | |||
| 17653 | ); | |||
| 17654 | if( pNew ){ | |||
| 17655 | pNew->nn = szPage; | |||
| 17656 | pNew->p = (u8*)&pNew[1]; | |||
| 17657 | apOut[ii] = pNew; | |||
| 17658 | } | |||
| 17659 | } | |||
| 17660 | ||||
| 17661 | /* Rebuild the hash table. */ | |||
| 17662 | if( p->rc==SQLITE_OK0 ){ | |||
| 17663 | res = fts5IndexTombstoneRehash(p, pSeg, pData1, iPg1, szKey, nOut, apOut); | |||
| 17664 | } | |||
| 17665 | if( res==0 ){ | |||
| 17666 | if( p->rc ){ | |||
| 17667 | fts5IndexFreeArray(apOut, nOut); | |||
| 17668 | apOut = 0; | |||
| 17669 | nOut = 0; | |||
| 17670 | } | |||
| 17671 | *pnOut = nOut; | |||
| 17672 | *papOut = apOut; | |||
| 17673 | break; | |||
| 17674 | } | |||
| 17675 | ||||
| 17676 | /* If control flows to here, it was not possible to rebuild the hash | |||
| 17677 | ** table. Free all buffers and then try again with more pages. */ | |||
| 17678 | assert( p->rc==SQLITE_OK )((void) (0)); | |||
| 17679 | fts5IndexFreeArray(apOut, nOut); | |||
| 17680 | nSlot = nSlotPerPage; | |||
| 17681 | nOut = nOut*2 + 1; | |||
| 17682 | } | |||
| 17683 | } | |||
| 17684 | ||||
| 17685 | ||||
| 17686 | /* | |||
| 17687 | ** Add a tombstone for rowid iRowid to segment pSeg. | |||
| 17688 | */ | |||
| 17689 | static void fts5IndexTombstoneAdd( | |||
| 17690 | Fts5Index *p, | |||
| 17691 | Fts5StructureSegment *pSeg, | |||
| 17692 | u64 iRowid | |||
| 17693 | ){ | |||
| 17694 | Fts5Data *pPg = 0; | |||
| 17695 | int iPg = -1; | |||
| 17696 | int szKey = 0; | |||
| 17697 | int nHash = 0; | |||
| 17698 | Fts5Data **apHash = 0; | |||
| 17699 | ||||
| 17700 | p->nContentlessDelete++; | |||
| 17701 | ||||
| 17702 | if( pSeg->nPgTombstone>0 ){ | |||
| 17703 | iPg = iRowid % pSeg->nPgTombstone; | |||
| 17704 | pPg = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg)( ((i64)(pSeg->iSegid+(1<<16)) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + (( i64)(iPg)) )); | |||
| 17705 | if( pPg==0 ){ | |||
| 17706 | assert( p->rc!=SQLITE_OK )((void) (0)); | |||
| 17707 | return; | |||
| 17708 | } | |||
| 17709 | ||||
| 17710 | if( 0==fts5IndexTombstoneAddToPage(pPg, 0, pSeg->nPgTombstone, iRowid) ){ | |||
| 17711 | fts5DataWrite(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg)( ((i64)(pSeg->iSegid+(1<<16)) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + (( i64)(iPg)) ), pPg->p, pPg->nn); | |||
| 17712 | fts5DataRelease(pPg); | |||
| 17713 | return; | |||
| 17714 | } | |||
| 17715 | } | |||
| 17716 | ||||
| 17717 | /* Have to rebuild the hash table. First figure out the key-size (4 or 8). */ | |||
| 17718 | szKey = pPg ? TOMBSTONE_KEYSIZE(pPg)(pPg->p[0]==4 ? 4 : 8) : 4; | |||
| 17719 | if( iRowid>0xFFFFFFFF ) szKey = 8; | |||
| 17720 | ||||
| 17721 | /* Rebuild the hash table */ | |||
| 17722 | fts5IndexTombstoneRebuild(p, pSeg, pPg, iPg, szKey, &nHash, &apHash); | |||
| 17723 | assert( p->rc==SQLITE_OK || (nHash==0 && apHash==0) )((void) (0)); | |||
| 17724 | ||||
| 17725 | /* If all has succeeded, write the new rowid into one of the new hash | |||
| 17726 | ** table pages, then write them all out to disk. */ | |||
| 17727 | if( nHash ){ | |||
| 17728 | int ii = 0; | |||
| 17729 | fts5IndexTombstoneAddToPage(apHash[iRowid % nHash], 1, nHash, iRowid); | |||
| 17730 | for(ii=0; ii<nHash; ii++){ | |||
| 17731 | i64 iTombstoneRowid = FTS5_TOMBSTONE_ROWID(pSeg->iSegid, ii)( ((i64)(pSeg->iSegid+(1<<16)) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + (( i64)(ii)) ); | |||
| 17732 | fts5DataWrite(p, iTombstoneRowid, apHash[ii]->p, apHash[ii]->nn); | |||
| 17733 | } | |||
| 17734 | pSeg->nPgTombstone = nHash; | |||
| 17735 | fts5StructureWrite(p, p->pStruct); | |||
| 17736 | } | |||
| 17737 | ||||
| 17738 | fts5DataRelease(pPg); | |||
| 17739 | fts5IndexFreeArray(apHash, nHash); | |||
| 17740 | } | |||
| 17741 | ||||
| 17742 | /* | |||
| 17743 | ** Add iRowid to the tombstone list of the segment or segments that contain | |||
| 17744 | ** rows from origin iOrigin. Return SQLITE_OK if successful, or an SQLite | |||
| 17745 | ** error code otherwise. | |||
| 17746 | */ | |||
| 17747 | static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid){ | |||
| 17748 | Fts5Structure *pStruct; | |||
| 17749 | pStruct = fts5StructureRead(p); | |||
| 17750 | if( pStruct ){ | |||
| 17751 | int bFound = 0; /* True after pSeg->nEntryTombstone incr. */ | |||
| 17752 | int iLvl; | |||
| 17753 | for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){ | |||
| 17754 | int iSeg; | |||
| 17755 | for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ | |||
| 17756 | Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; | |||
| 17757 | if( pSeg->iOrigin1<=(u64)iOrigin && pSeg->iOrigin2>=(u64)iOrigin ){ | |||
| 17758 | if( bFound==0 ){ | |||
| 17759 | pSeg->nEntryTombstone++; | |||
| 17760 | bFound = 1; | |||
| 17761 | } | |||
| 17762 | fts5IndexTombstoneAdd(p, pSeg, iRowid); | |||
| 17763 | } | |||
| 17764 | } | |||
| 17765 | } | |||
| 17766 | fts5StructureRelease(pStruct); | |||
| 17767 | } | |||
| 17768 | return fts5IndexReturn(p); | |||
| 17769 | } | |||
| 17770 | ||||
| 17771 | /************************************************************************* | |||
| 17772 | ************************************************************************** | |||
| 17773 | ** Below this point is the implementation of the integrity-check | |||
| 17774 | ** functionality. | |||
| 17775 | */ | |||
| 17776 | ||||
| 17777 | /* | |||
| 17778 | ** Return a simple checksum value based on the arguments. | |||
| 17779 | */ | |||
| 17780 | static u64 sqlite3Fts5IndexEntryCksum( | |||
| 17781 | i64 iRowid, | |||
| 17782 | int iCol, | |||
| 17783 | int iPos, | |||
| 17784 | int iIdx, | |||
| 17785 | const char *pTerm, | |||
| 17786 | int nTerm | |||
| 17787 | ){ | |||
| 17788 | int i; | |||
| 17789 | u64 ret = iRowid; | |||
| 17790 | ret += (ret<<3) + iCol; | |||
| 17791 | ret += (ret<<3) + iPos; | |||
| 17792 | if( iIdx>=0 ) ret += (ret<<3) + (FTS5_MAIN_PREFIX'0' + iIdx); | |||
| 17793 | for(i=0; i<nTerm; i++) ret += (ret<<3) + pTerm[i]; | |||
| 17794 | return ret; | |||
| 17795 | } | |||
| 17796 | ||||
| 17797 | #ifdef SQLITE_DEBUG | |||
| 17798 | /* | |||
| 17799 | ** This function is purely an internal test. It does not contribute to | |||
| 17800 | ** FTS functionality, or even the integrity-check, in any way. | |||
| 17801 | ** | |||
| 17802 | ** Instead, it tests that the same set of pgno/rowid combinations are | |||
| 17803 | ** visited regardless of whether the doclist-index identified by parameters | |||
| 17804 | ** iSegid/iLeaf is iterated in forwards or reverse order. | |||
| 17805 | */ | |||
| 17806 | static void fts5TestDlidxReverse( | |||
| 17807 | Fts5Index *p, | |||
| 17808 | int iSegid, /* Segment id to load from */ | |||
| 17809 | int iLeaf /* Load doclist-index for this leaf */ | |||
| 17810 | ){ | |||
| 17811 | Fts5DlidxIter *pDlidx = 0; | |||
| 17812 | u64 cksum1 = 13; | |||
| 17813 | u64 cksum2 = 13; | |||
| 17814 | ||||
| 17815 | for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iLeaf); | |||
| 17816 | fts5DlidxIterEof(p, pDlidx)==0; | |||
| 17817 | fts5DlidxIterNext(p, pDlidx) | |||
| 17818 | ){ | |||
| 17819 | i64 iRowid = fts5DlidxIterRowid(pDlidx); | |||
| 17820 | int pgno = fts5DlidxIterPgno(pDlidx); | |||
| 17821 | assert( pgno>iLeaf )((void) (0)); | |||
| 17822 | cksum1 += iRowid + ((i64)pgno<<32); | |||
| 17823 | } | |||
| 17824 | fts5DlidxIterFree(pDlidx); | |||
| 17825 | pDlidx = 0; | |||
| 17826 | ||||
| 17827 | for(pDlidx=fts5DlidxIterInit(p, 1, iSegid, iLeaf); | |||
| 17828 | fts5DlidxIterEof(p, pDlidx)==0; | |||
| 17829 | fts5DlidxIterPrev(p, pDlidx) | |||
| 17830 | ){ | |||
| 17831 | i64 iRowid = fts5DlidxIterRowid(pDlidx); | |||
| 17832 | int pgno = fts5DlidxIterPgno(pDlidx); | |||
| 17833 | assert( fts5DlidxIterPgno(pDlidx)>iLeaf )((void) (0)); | |||
| 17834 | cksum2 += iRowid + ((i64)pgno<<32); | |||
| 17835 | } | |||
| 17836 | fts5DlidxIterFree(pDlidx); | |||
| 17837 | pDlidx = 0; | |||
| 17838 | ||||
| 17839 | if( p->rc==SQLITE_OK0 && cksum1!=cksum2 ) p->rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 17840 | } | |||
| 17841 | ||||
| 17842 | static int fts5QueryCksum( | |||
| 17843 | Fts5Index *p, /* Fts5 index object */ | |||
| 17844 | int iIdx, | |||
| 17845 | const char *z, /* Index key to query for */ | |||
| 17846 | int n, /* Size of index key in bytes */ | |||
| 17847 | int flags, /* Flags for Fts5IndexQuery */ | |||
| 17848 | u64 *pCksum /* IN/OUT: Checksum value */ | |||
| 17849 | ){ | |||
| 17850 | int eDetail = p->pConfig->eDetail; | |||
| 17851 | u64 cksum = *pCksum; | |||
| 17852 | Fts5IndexIter *pIter = 0; | |||
| 17853 | int rc = sqlite3Fts5IndexQuery( | |||
| 17854 | p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA0x0080), 0, &pIter | |||
| 17855 | ); | |||
| 17856 | ||||
| 17857 | while( rc==SQLITE_OK0 && ALWAYS(pIter!=0)(pIter!=0) && 0==sqlite3Fts5IterEof(pIter)((pIter)->bEof) ){ | |||
| 17858 | i64 rowid = pIter->iRowid; | |||
| 17859 | ||||
| 17860 | if( eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 17861 | cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n); | |||
| 17862 | }else{ | |||
| 17863 | Fts5PoslistReader sReader; | |||
| 17864 | for(sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &sReader); | |||
| 17865 | sReader.bEof==0; | |||
| 17866 | sqlite3Fts5PoslistReaderNext(&sReader) | |||
| 17867 | ){ | |||
| 17868 | int iCol = FTS5_POS2COLUMN(sReader.iPos)(int)((sReader.iPos >> 32) & 0x7FFFFFFF); | |||
| 17869 | int iOff = FTS5_POS2OFFSET(sReader.iPos)(int)(sReader.iPos & 0x7FFFFFFF); | |||
| 17870 | cksum ^= sqlite3Fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n); | |||
| 17871 | } | |||
| 17872 | } | |||
| 17873 | if( rc==SQLITE_OK0 ){ | |||
| 17874 | rc = sqlite3Fts5IterNext(pIter); | |||
| 17875 | } | |||
| 17876 | } | |||
| 17877 | fts5IterClose(pIter); | |||
| 17878 | ||||
| 17879 | *pCksum = cksum; | |||
| 17880 | return rc; | |||
| 17881 | } | |||
| 17882 | ||||
| 17883 | /* | |||
| 17884 | ** Check if buffer z[], size n bytes, contains as series of valid utf-8 | |||
| 17885 | ** encoded codepoints. If so, return 0. Otherwise, if the buffer does not | |||
| 17886 | ** contain valid utf-8, return non-zero. | |||
| 17887 | */ | |||
| 17888 | static int fts5TestUtf8(const char *z, int n){ | |||
| 17889 | int i = 0; | |||
| 17890 | assert_nc( n>0 )((void) (0)); | |||
| 17891 | while( i<n ){ | |||
| 17892 | if( (z[i] & 0x80)==0x00 ){ | |||
| 17893 | i++; | |||
| 17894 | }else | |||
| 17895 | if( (z[i] & 0xE0)==0xC0 ){ | |||
| 17896 | if( i+1>=n || (z[i+1] & 0xC0)!=0x80 ) return 1; | |||
| 17897 | i += 2; | |||
| 17898 | }else | |||
| 17899 | if( (z[i] & 0xF0)==0xE0 ){ | |||
| 17900 | if( i+2>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1; | |||
| 17901 | i += 3; | |||
| 17902 | }else | |||
| 17903 | if( (z[i] & 0xF8)==0xF0 ){ | |||
| 17904 | if( i+3>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1; | |||
| 17905 | if( (z[i+2] & 0xC0)!=0x80 ) return 1; | |||
| 17906 | i += 3; | |||
| 17907 | }else{ | |||
| 17908 | return 1; | |||
| 17909 | } | |||
| 17910 | } | |||
| 17911 | ||||
| 17912 | return 0; | |||
| 17913 | } | |||
| 17914 | ||||
| 17915 | /* | |||
| 17916 | ** This function is also purely an internal test. It does not contribute to | |||
| 17917 | ** FTS functionality, or even the integrity-check, in any way. | |||
| 17918 | ** | |||
| 17919 | ** This function sets output variable (*pbFail) to true if the test fails. Or | |||
| 17920 | ** leaves it unchanged if the test succeeds. | |||
| 17921 | */ | |||
| 17922 | static void fts5TestTerm( | |||
| 17923 | Fts5Index *p, | |||
| 17924 | Fts5Buffer *pPrev, /* Previous term */ | |||
| 17925 | const char *z, int n, /* Possibly new term to test */ | |||
| 17926 | u64 expected, | |||
| 17927 | u64 *pCksum, | |||
| 17928 | int *pbFail | |||
| 17929 | ){ | |||
| 17930 | int rc = p->rc; | |||
| 17931 | if( pPrev->n==0 ){ | |||
| 17932 | fts5BufferSet(&rc, pPrev, n, (const u8*)z)sqlite3Fts5BufferSet(&rc,pPrev,n,(const u8*)z); | |||
| 17933 | }else | |||
| 17934 | if( *pbFail==0 | |||
| 17935 | && rc==SQLITE_OK0 | |||
| 17936 | && (pPrev->n!=n || memcmp(pPrev->p, z, n)) | |||
| 17937 | && (p->pHash==0 || p->pHash->nEntry==0) | |||
| 17938 | ){ | |||
| 17939 | u64 cksum3 = *pCksum; | |||
| 17940 | const char *zTerm = (const char*)&pPrev->p[1]; /* term sans prefix-byte */ | |||
| 17941 | int nTerm = pPrev->n-1; /* Size of zTerm in bytes */ | |||
| 17942 | int iIdx = (pPrev->p[0] - FTS5_MAIN_PREFIX'0'); | |||
| 17943 | int flags = (iIdx==0 ? 0 : FTS5INDEX_QUERY_PREFIX0x0001); | |||
| 17944 | u64 ck1 = 0; | |||
| 17945 | u64 ck2 = 0; | |||
| 17946 | ||||
| 17947 | /* Check that the results returned for ASC and DESC queries are | |||
| 17948 | ** the same. If not, call this corruption. */ | |||
| 17949 | rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, flags, &ck1); | |||
| 17950 | if( rc==SQLITE_OK0 ){ | |||
| 17951 | int f = flags|FTS5INDEX_QUERY_DESC0x0002; | |||
| 17952 | rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2); | |||
| 17953 | } | |||
| 17954 | if( rc==SQLITE_OK0 && ck1!=ck2 ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 17955 | ||||
| 17956 | /* If this is a prefix query, check that the results returned if the | |||
| 17957 | ** the index is disabled are the same. In both ASC and DESC order. | |||
| 17958 | ** | |||
| 17959 | ** This check may only be performed if the hash table is empty. This | |||
| 17960 | ** is because the hash table only supports a single scan query at | |||
| 17961 | ** a time, and the multi-iter loop from which this function is called | |||
| 17962 | ** is already performing such a scan. | |||
| 17963 | ** | |||
| 17964 | ** Also only do this if buffer zTerm contains nTerm bytes of valid | |||
| 17965 | ** utf-8. Otherwise, the last part of the buffer contents might contain | |||
| 17966 | ** a non-utf-8 sequence that happens to be a prefix of a valid utf-8 | |||
| 17967 | ** character stored in the main fts index, which will cause the | |||
| 17968 | ** test to fail. */ | |||
| 17969 | if( p->nPendingData==0 && 0==fts5TestUtf8(zTerm, nTerm) ){ | |||
| 17970 | if( iIdx>0 && rc==SQLITE_OK0 ){ | |||
| 17971 | int f = flags|FTS5INDEX_QUERY_TEST_NOIDX0x0004; | |||
| 17972 | ck2 = 0; | |||
| 17973 | rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2); | |||
| 17974 | if( rc==SQLITE_OK0 && ck1!=ck2 ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 17975 | } | |||
| 17976 | if( iIdx>0 && rc==SQLITE_OK0 ){ | |||
| 17977 | int f = flags|FTS5INDEX_QUERY_TEST_NOIDX0x0004|FTS5INDEX_QUERY_DESC0x0002; | |||
| 17978 | ck2 = 0; | |||
| 17979 | rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2); | |||
| 17980 | if( rc==SQLITE_OK0 && ck1!=ck2 ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 17981 | } | |||
| 17982 | } | |||
| 17983 | ||||
| 17984 | cksum3 ^= ck1; | |||
| 17985 | fts5BufferSet(&rc, pPrev, n, (const u8*)z)sqlite3Fts5BufferSet(&rc,pPrev,n,(const u8*)z); | |||
| 17986 | ||||
| 17987 | if( rc==SQLITE_OK0 && cksum3!=expected ){ | |||
| 17988 | *pbFail = 1; | |||
| 17989 | } | |||
| 17990 | *pCksum = cksum3; | |||
| 17991 | } | |||
| 17992 | p->rc = rc; | |||
| 17993 | } | |||
| 17994 | ||||
| 17995 | #else | |||
| 17996 | # define fts5TestDlidxReverse(x,y,z) | |||
| 17997 | # define fts5TestTerm(t,u,v,w,x,y,z) | |||
| 17998 | #endif | |||
| 17999 | ||||
| 18000 | /* | |||
| 18001 | ** Check that: | |||
| 18002 | ** | |||
| 18003 | ** 1) All leaves of pSeg between iFirst and iLast (inclusive) exist and | |||
| 18004 | ** contain zero terms. | |||
| 18005 | ** 2) All leaves of pSeg between iNoRowid and iLast (inclusive) exist and | |||
| 18006 | ** contain zero rowids. | |||
| 18007 | */ | |||
| 18008 | static void fts5IndexIntegrityCheckEmpty( | |||
| 18009 | Fts5Index *p, | |||
| 18010 | Fts5StructureSegment *pSeg, /* Segment to check internal consistency */ | |||
| 18011 | int iFirst, | |||
| 18012 | int iNoRowid, | |||
| 18013 | int iLast | |||
| 18014 | ){ | |||
| 18015 | int i; | |||
| 18016 | ||||
| 18017 | /* Now check that the iter.nEmpty leaves following the current leaf | |||
| 18018 | ** (a) exist and (b) contain no terms. */ | |||
| 18019 | for(i=iFirst; p->rc==SQLITE_OK0 && i<=iLast; i++){ | |||
| 18020 | Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i)( ((i64)(pSeg->iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(i)) )); | |||
| 18021 | if( pLeaf ){ | |||
| 18022 | if( !fts5LeafIsTermless(pLeaf)((pLeaf)->szLeaf >= (pLeaf)->nn) | |||
| 18023 | || (i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf)(fts5GetU16((pLeaf)->p))) | |||
| 18024 | ){ | |||
| 18025 | FTS5_CORRUPT_ROWID(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i))fts5IndexCorruptRowid(p, ( ((i64)(pSeg->iSegid) << ( 31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(i)) )); | |||
| 18026 | } | |||
| 18027 | } | |||
| 18028 | fts5DataRelease(pLeaf); | |||
| 18029 | } | |||
| 18030 | } | |||
| 18031 | ||||
| 18032 | static void fts5IntegrityCheckPgidx(Fts5Index *p, i64 iRowid, Fts5Data *pLeaf){ | |||
| 18033 | i64 iTermOff = 0; | |||
| 18034 | int ii; | |||
| 18035 | ||||
| 18036 | Fts5Buffer buf1 = {0,0,0}; | |||
| 18037 | Fts5Buffer buf2 = {0,0,0}; | |||
| 18038 | ||||
| 18039 | ii = pLeaf->szLeaf; | |||
| 18040 | while( ii<pLeaf->nn && p->rc==SQLITE_OK0 ){ | |||
| 18041 | int res; | |||
| 18042 | i64 iOff; | |||
| 18043 | int nIncr; | |||
| 18044 | ||||
| 18045 | ii += fts5GetVarint32(&pLeaf->p[ii], nIncr)sqlite3Fts5GetVarint32(&pLeaf->p[ii],(u32*)&(nIncr )); | |||
| 18046 | iTermOff += nIncr; | |||
| 18047 | iOff = iTermOff; | |||
| 18048 | ||||
| 18049 | if( iOff>=pLeaf->szLeaf ){ | |||
| 18050 | FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 18051 | }else if( iTermOff==nIncr ){ | |||
| 18052 | int nByte; | |||
| 18053 | iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte)sqlite3Fts5GetVarint32(&pLeaf->p[iOff],(u32*)&(nByte )); | |||
| 18054 | if( (iOff+nByte)>pLeaf->szLeaf ){ | |||
| 18055 | FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 18056 | }else{ | |||
| 18057 | fts5BufferSet(&p->rc, &buf1, nByte, &pLeaf->p[iOff])sqlite3Fts5BufferSet(&p->rc,&buf1,nByte,&pLeaf ->p[iOff]); | |||
| 18058 | } | |||
| 18059 | }else{ | |||
| 18060 | int nKeep, nByte; | |||
| 18061 | iOff += fts5GetVarint32(&pLeaf->p[iOff], nKeep)sqlite3Fts5GetVarint32(&pLeaf->p[iOff],(u32*)&(nKeep )); | |||
| 18062 | iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte)sqlite3Fts5GetVarint32(&pLeaf->p[iOff],(u32*)&(nByte )); | |||
| 18063 | if( nKeep>buf1.n || (iOff+nByte)>pLeaf->szLeaf ){ | |||
| 18064 | FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 18065 | }else{ | |||
| 18066 | buf1.n = nKeep; | |||
| 18067 | fts5BufferAppendBlob(&p->rc, &buf1, nByte, &pLeaf->p[iOff])sqlite3Fts5BufferAppendBlob(&p->rc,&buf1,nByte,& pLeaf->p[iOff]); | |||
| 18068 | } | |||
| 18069 | ||||
| 18070 | if( p->rc==SQLITE_OK0 ){ | |||
| 18071 | res = fts5BufferCompare(&buf1, &buf2); | |||
| 18072 | if( res<=0 ) FTS5_CORRUPT_ROWID(p, iRowid)fts5IndexCorruptRowid(p, iRowid); | |||
| 18073 | } | |||
| 18074 | } | |||
| 18075 | fts5BufferSet(&p->rc, &buf2, buf1.n, buf1.p)sqlite3Fts5BufferSet(&p->rc,&buf2,buf1.n,buf1.p); | |||
| 18076 | } | |||
| 18077 | ||||
| 18078 | fts5BufferFree(&buf1)sqlite3Fts5BufferFree(&buf1); | |||
| 18079 | fts5BufferFree(&buf2)sqlite3Fts5BufferFree(&buf2); | |||
| 18080 | } | |||
| 18081 | ||||
| 18082 | static void fts5IndexIntegrityCheckSegment( | |||
| 18083 | Fts5Index *p, /* FTS5 backend object */ | |||
| 18084 | Fts5StructureSegment *pSeg /* Segment to check internal consistency */ | |||
| 18085 | ){ | |||
| 18086 | Fts5Config *pConfig = p->pConfig; | |||
| 18087 | int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE5); | |||
| 18088 | sqlite3_stmt *pStmt = 0; | |||
| 18089 | int rc2; | |||
| 18090 | int iIdxPrevLeaf = pSeg->pgnoFirst-1; | |||
| 18091 | int iDlidxPrevLeaf = pSeg->pgnoLast; | |||
| 18092 | ||||
| 18093 | if( pSeg->pgnoFirst==0 ) return; | |||
| 18094 | ||||
| 18095 | fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintfsqlite3_api->mprintf( | |||
| 18096 | "SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d " | |||
| 18097 | "ORDER BY 1, 2", | |||
| 18098 | pConfig->zDb, pConfig->zName, pSeg->iSegid | |||
| 18099 | )); | |||
| 18100 | ||||
| 18101 | /* Iterate through the b-tree hierarchy. */ | |||
| 18102 | while( p->rc==SQLITE_OK0 && SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pStmt) ){ | |||
| 18103 | i64 iRow; /* Rowid for this leaf */ | |||
| 18104 | Fts5Data *pLeaf; /* Data for this leaf */ | |||
| 18105 | ||||
| 18106 | const char *zIdxTerm = (const char*)sqlite3_column_blobsqlite3_api->column_blob(pStmt, 1); | |||
| 18107 | int nIdxTerm = sqlite3_column_bytessqlite3_api->column_bytes(pStmt, 1); | |||
| 18108 | int iIdxLeaf = sqlite3_column_intsqlite3_api->column_int(pStmt, 2); | |||
| 18109 | int bIdxDlidx = sqlite3_column_intsqlite3_api->column_int(pStmt, 3); | |||
| 18110 | ||||
| 18111 | /* If the leaf in question has already been trimmed from the segment, | |||
| 18112 | ** ignore this b-tree entry. Otherwise, load it into memory. */ | |||
| 18113 | if( iIdxLeaf<pSeg->pgnoFirst ) continue; | |||
| 18114 | iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, iIdxLeaf)( ((i64)(pSeg->iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(iIdxLeaf)) ); | |||
| 18115 | pLeaf = fts5LeafRead(p, iRow); | |||
| 18116 | if( pLeaf==0 ) break; | |||
| 18117 | ||||
| 18118 | /* Check that the leaf contains at least one term, and that it is equal | |||
| 18119 | ** to or larger than the split-key in zIdxTerm. Also check that if there | |||
| 18120 | ** is also a rowid pointer within the leaf page header, it points to a | |||
| 18121 | ** location before the term. */ | |||
| 18122 | if( pLeaf->nn<=pLeaf->szLeaf ){ | |||
| 18123 | ||||
| 18124 | if( nIdxTerm==0 | |||
| 18125 | && pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE5 | |||
| 18126 | && pLeaf->nn==pLeaf->szLeaf | |||
| 18127 | && pLeaf->nn==4 | |||
| 18128 | ){ | |||
| 18129 | /* special case - the very first page in a segment keeps its %_idx | |||
| 18130 | ** entry even if all the terms are removed from it by secure-delete | |||
| 18131 | ** operations. */ | |||
| 18132 | }else{ | |||
| 18133 | FTS5_CORRUPT_ROWID(p, iRow)fts5IndexCorruptRowid(p, iRow); | |||
| 18134 | } | |||
| 18135 | ||||
| 18136 | }else{ | |||
| 18137 | int iOff; /* Offset of first term on leaf */ | |||
| 18138 | int iRowidOff; /* Offset of first rowid on leaf */ | |||
| 18139 | int nTerm; /* Size of term on leaf in bytes */ | |||
| 18140 | int res; /* Comparison of term and split-key */ | |||
| 18141 | ||||
| 18142 | iOff = fts5LeafFirstTermOff(pLeaf); | |||
| 18143 | iRowidOff = fts5LeafFirstRowidOff(pLeaf)(fts5GetU16((pLeaf)->p)); | |||
| 18144 | if( iRowidOff>=iOff || iOff>=pLeaf->szLeaf ){ | |||
| 18145 | FTS5_CORRUPT_ROWID(p, iRow)fts5IndexCorruptRowid(p, iRow); | |||
| 18146 | }else{ | |||
| 18147 | iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm)sqlite3Fts5GetVarint32(&pLeaf->p[iOff],(u32*)&(nTerm )); | |||
| 18148 | res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm))(((((nTerm) < (nIdxTerm)) ? (nTerm) : (nIdxTerm)))<=0 ? 0 : memcmp((&pLeaf->p[iOff]), (zIdxTerm), ((((nTerm) < (nIdxTerm)) ? (nTerm) : (nIdxTerm))))); | |||
| 18149 | if( res==0 ) res = nTerm - nIdxTerm; | |||
| 18150 | if( res<0 ) FTS5_CORRUPT_ROWID(p, iRow)fts5IndexCorruptRowid(p, iRow); | |||
| 18151 | } | |||
| 18152 | ||||
| 18153 | fts5IntegrityCheckPgidx(p, iRow, pLeaf); | |||
| 18154 | } | |||
| 18155 | fts5DataRelease(pLeaf); | |||
| 18156 | if( p->rc ) break; | |||
| 18157 | ||||
| 18158 | /* Now check that the iter.nEmpty leaves following the current leaf | |||
| 18159 | ** (a) exist and (b) contain no terms. */ | |||
| 18160 | fts5IndexIntegrityCheckEmpty( | |||
| 18161 | p, pSeg, iIdxPrevLeaf+1, iDlidxPrevLeaf+1, iIdxLeaf-1 | |||
| 18162 | ); | |||
| 18163 | if( p->rc ) break; | |||
| 18164 | ||||
| 18165 | /* If there is a doclist-index, check that it looks right. */ | |||
| 18166 | if( bIdxDlidx ){ | |||
| 18167 | Fts5DlidxIter *pDlidx = 0; /* For iterating through doclist index */ | |||
| 18168 | int iPrevLeaf = iIdxLeaf; | |||
| 18169 | int iSegid = pSeg->iSegid; | |||
| 18170 | int iPg = 0; | |||
| 18171 | i64 iKey; | |||
| 18172 | ||||
| 18173 | for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iIdxLeaf); | |||
| 18174 | fts5DlidxIterEof(p, pDlidx)==0; | |||
| 18175 | fts5DlidxIterNext(p, pDlidx) | |||
| 18176 | ){ | |||
| 18177 | ||||
| 18178 | /* Check any rowid-less pages that occur before the current leaf. */ | |||
| 18179 | for(iPg=iPrevLeaf+1; iPg<fts5DlidxIterPgno(pDlidx); iPg++){ | |||
| 18180 | iKey = FTS5_SEGMENT_ROWID(iSegid, iPg)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(iPg)) ); | |||
| 18181 | pLeaf = fts5DataRead(p, iKey); | |||
| 18182 | if( pLeaf ){ | |||
| 18183 | if( fts5LeafFirstRowidOff(pLeaf)(fts5GetU16((pLeaf)->p))!=0 ) FTS5_CORRUPT_ROWID(p, iKey)fts5IndexCorruptRowid(p, iKey); | |||
| 18184 | fts5DataRelease(pLeaf); | |||
| 18185 | } | |||
| 18186 | } | |||
| 18187 | iPrevLeaf = fts5DlidxIterPgno(pDlidx); | |||
| 18188 | ||||
| 18189 | /* Check that the leaf page indicated by the iterator really does | |||
| 18190 | ** contain the rowid suggested by the same. */ | |||
| 18191 | iKey = FTS5_SEGMENT_ROWID(iSegid, iPrevLeaf)( ((i64)(iSegid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(iPrevLeaf)) ); | |||
| 18192 | pLeaf = fts5DataRead(p, iKey); | |||
| 18193 | if( pLeaf ){ | |||
| 18194 | i64 iRowid; | |||
| 18195 | int iRowidOff = fts5LeafFirstRowidOff(pLeaf)(fts5GetU16((pLeaf)->p)); | |||
| 18196 | ASSERT_SZLEAF_OK(pLeaf)((void) (0)); | |||
| 18197 | if( iRowidOff>=pLeaf->szLeaf ){ | |||
| 18198 | FTS5_CORRUPT_ROWID(p, iKey)fts5IndexCorruptRowid(p, iKey); | |||
| 18199 | }else if( bSecureDelete==0 || iRowidOff>0 ){ | |||
| 18200 | i64 iDlRowid = fts5DlidxIterRowid(pDlidx); | |||
| 18201 | fts5GetVarintsqlite3Fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); | |||
| 18202 | if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){ | |||
| 18203 | FTS5_CORRUPT_ROWID(p, iKey)fts5IndexCorruptRowid(p, iKey); | |||
| 18204 | } | |||
| 18205 | } | |||
| 18206 | fts5DataRelease(pLeaf); | |||
| 18207 | } | |||
| 18208 | } | |||
| 18209 | ||||
| 18210 | iDlidxPrevLeaf = iPg; | |||
| 18211 | fts5DlidxIterFree(pDlidx); | |||
| 18212 | fts5TestDlidxReverse(p, iSegid, iIdxLeaf); | |||
| 18213 | }else{ | |||
| 18214 | iDlidxPrevLeaf = pSeg->pgnoLast; | |||
| 18215 | /* TODO: Check there is no doclist index */ | |||
| 18216 | } | |||
| 18217 | ||||
| 18218 | iIdxPrevLeaf = iIdxLeaf; | |||
| 18219 | } | |||
| 18220 | ||||
| 18221 | rc2 = sqlite3_finalizesqlite3_api->finalize(pStmt); | |||
| 18222 | if( p->rc==SQLITE_OK0 ) p->rc = rc2; | |||
| 18223 | ||||
| 18224 | /* Page iter.iLeaf must now be the rightmost leaf-page in the segment */ | |||
| 18225 | #if 0 | |||
| 18226 | if( p->rc==SQLITE_OK0 && iter.iLeaf!=pSeg->pgnoLast ){ | |||
| 18227 | p->rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18228 | } | |||
| 18229 | #endif | |||
| 18230 | } | |||
| 18231 | ||||
| 18232 | ||||
| 18233 | /* | |||
| 18234 | ** Run internal checks to ensure that the FTS index (a) is internally | |||
| 18235 | ** consistent and (b) contains entries for which the XOR of the checksums | |||
| 18236 | ** as calculated by sqlite3Fts5IndexEntryCksum() is cksum. | |||
| 18237 | ** | |||
| 18238 | ** Return SQLITE_CORRUPT if any of the internal checks fail, or if the | |||
| 18239 | ** checksum does not match. Return SQLITE_OK if all checks pass without | |||
| 18240 | ** error, or some other SQLite error code if another error (e.g. OOM) | |||
| 18241 | ** occurs. | |||
| 18242 | */ | |||
| 18243 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){ | |||
| 18244 | int eDetail = p->pConfig->eDetail; | |||
| 18245 | u64 cksum2 = 0; /* Checksum based on contents of indexes */ | |||
| 18246 | Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ | |||
| 18247 | Fts5Iter *pIter; /* Used to iterate through entire index */ | |||
| 18248 | Fts5Structure *pStruct; /* Index structure */ | |||
| 18249 | int iLvl, iSeg; | |||
| 18250 | ||||
| 18251 | #ifdef SQLITE_DEBUG | |||
| 18252 | /* Used by extra internal tests only run if NDEBUG is not defined */ | |||
| 18253 | u64 cksum3 = 0; /* Checksum based on contents of indexes */ | |||
| 18254 | Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */ | |||
| 18255 | int bTestFail = 0; | |||
| 18256 | #endif | |||
| 18257 | const int flags = FTS5INDEX_QUERY_NOOUTPUT0x0020; | |||
| 18258 | ||||
| 18259 | /* Load the FTS index structure */ | |||
| 18260 | pStruct = fts5StructureRead(p); | |||
| 18261 | if( pStruct==0 ){ | |||
| 18262 | assert( p->rc!=SQLITE_OK )((void) (0)); | |||
| 18263 | return fts5IndexReturn(p); | |||
| 18264 | } | |||
| 18265 | ||||
| 18266 | /* Check that the internal nodes of each segment match the leaves */ | |||
| 18267 | for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){ | |||
| 18268 | for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){ | |||
| 18269 | Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; | |||
| 18270 | fts5IndexIntegrityCheckSegment(p, pSeg); | |||
| 18271 | } | |||
| 18272 | } | |||
| 18273 | ||||
| 18274 | /* The cksum argument passed to this function is a checksum calculated | |||
| 18275 | ** based on all expected entries in the FTS index (including prefix index | |||
| 18276 | ** entries). This block checks that a checksum calculated based on the | |||
| 18277 | ** actual contents of FTS index is identical. | |||
| 18278 | ** | |||
| 18279 | ** Two versions of the same checksum are calculated. The first (stack | |||
| 18280 | ** variable cksum2) based on entries extracted from the full-text index | |||
| 18281 | ** while doing a linear scan of each individual index in turn. | |||
| 18282 | ** | |||
| 18283 | ** As each term visited by the linear scans, a separate query for the | |||
| 18284 | ** same term is performed. cksum3 is calculated based on the entries | |||
| 18285 | ** extracted by these queries. | |||
| 18286 | */ | |||
| 18287 | for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, -1, 0, &pIter); | |||
| 18288 | fts5MultiIterEof(p, pIter)==0; | |||
| 18289 | fts5MultiIterNext(p, pIter, 0, 0) | |||
| 18290 | ){ | |||
| 18291 | int n; /* Size of term in bytes */ | |||
| 18292 | i64 iPos = 0; /* Position read from poslist */ | |||
| 18293 | int iOff = 0; /* Offset within poslist */ | |||
| 18294 | i64 iRowid = fts5MultiIterRowid(pIter); | |||
| 18295 | char *z = (char*)fts5MultiIterTerm(pIter, &n); | |||
| 18296 | ||||
| 18297 | /* If this is a new term, query for it. Update cksum3 with the results. */ | |||
| 18298 | fts5TestTerm(p, &term, z, n, cksum2, &cksum3, &bTestFail); | |||
| 18299 | if( p->rc ) break; | |||
| 18300 | ||||
| 18301 | if( eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 18302 | if( 0==fts5MultiIterIsEmpty(p, pIter) ){ | |||
| 18303 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); | |||
| 18304 | } | |||
| 18305 | }else{ | |||
| 18306 | poslist.n = 0; | |||
| 18307 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); | |||
| 18308 | fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0")sqlite3Fts5BufferAppendBlob(&p->rc,&poslist,4,(const u8*)"\0\0\0\0"); | |||
| 18309 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ | |||
| 18310 | int iCol = FTS5_POS2COLUMN(iPos)(int)((iPos >> 32) & 0x7FFFFFFF); | |||
| 18311 | int iTokOff = FTS5_POS2OFFSET(iPos)(int)(iPos & 0x7FFFFFFF); | |||
| 18312 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); | |||
| 18313 | } | |||
| 18314 | } | |||
| 18315 | } | |||
| 18316 | fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3, &bTestFail); | |||
| 18317 | ||||
| 18318 | fts5MultiIterFree(pIter); | |||
| 18319 | if( p->rc==SQLITE_OK0 && bUseCksum && cksum!=cksum2 ){ | |||
| 18320 | p->rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18321 | sqlite3Fts5ConfigErrmsg(p->pConfig, | |||
| 18322 | "fts5: checksum mismatch for table \"%s\"", p->pConfig->zName | |||
| 18323 | ); | |||
| 18324 | } | |||
| 18325 | #ifdef SQLITE_DEBUG | |||
| 18326 | /* In SQLITE_DEBUG builds, expensive extra checks were run as part of | |||
| 18327 | ** the integrity-check above. If no other errors were detected, but one | |||
| 18328 | ** of these tests failed, set the result to SQLITE_CORRUPT_VTAB here. */ | |||
| 18329 | if( p->rc==SQLITE_OK0 && bTestFail ){ | |||
| 18330 | p->rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18331 | } | |||
| 18332 | fts5BufferFree(&term)sqlite3Fts5BufferFree(&term); | |||
| 18333 | #endif | |||
| 18334 | ||||
| 18335 | fts5StructureRelease(pStruct); | |||
| 18336 | fts5BufferFree(&poslist)sqlite3Fts5BufferFree(&poslist); | |||
| 18337 | return fts5IndexReturn(p); | |||
| 18338 | } | |||
| 18339 | ||||
| 18340 | /************************************************************************* | |||
| 18341 | ************************************************************************** | |||
| 18342 | ** Below this point is the implementation of the fts5_decode() scalar | |||
| 18343 | ** function only. | |||
| 18344 | */ | |||
| 18345 | ||||
| 18346 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18347 | /* | |||
| 18348 | ** Decode a segment-data rowid from the %_data table. This function is | |||
| 18349 | ** the opposite of macro FTS5_SEGMENT_ROWID(). | |||
| 18350 | */ | |||
| 18351 | static void fts5DecodeRowid( | |||
| 18352 | i64 iRowid, /* Rowid from %_data table */ | |||
| 18353 | int *pbTombstone, /* OUT: Tombstone hash flag */ | |||
| 18354 | int *piSegid, /* OUT: Segment id */ | |||
| 18355 | int *pbDlidx, /* OUT: Dlidx flag */ | |||
| 18356 | int *piHeight, /* OUT: Height */ | |||
| 18357 | int *piPgno /* OUT: Page number */ | |||
| 18358 | ){ | |||
| 18359 | *piPgno = (int)(iRowid & (((i64)1 << FTS5_DATA_PAGE_B31) - 1)); | |||
| 18360 | iRowid >>= FTS5_DATA_PAGE_B31; | |||
| 18361 | ||||
| 18362 | *piHeight = (int)(iRowid & (((i64)1 << FTS5_DATA_HEIGHT_B5) - 1)); | |||
| 18363 | iRowid >>= FTS5_DATA_HEIGHT_B5; | |||
| 18364 | ||||
| 18365 | *pbDlidx = (int)(iRowid & 0x0001); | |||
| 18366 | iRowid >>= FTS5_DATA_DLI_B1; | |||
| 18367 | ||||
| 18368 | *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B16) - 1)); | |||
| 18369 | iRowid >>= FTS5_DATA_ID_B16; | |||
| 18370 | ||||
| 18371 | *pbTombstone = (int)(iRowid & 0x0001); | |||
| 18372 | } | |||
| 18373 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18374 | ||||
| 18375 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18376 | static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ | |||
| 18377 | int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */ | |||
| 18378 | fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); | |||
| 18379 | ||||
| 18380 | if( iSegid==0 ){ | |||
| 18381 | if( iKey==FTS5_AVERAGES_ROWID1 ){ | |||
| 18382 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} "); | |||
| 18383 | }else{ | |||
| 18384 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{structure}"); | |||
| 18385 | } | |||
| 18386 | } | |||
| 18387 | else{ | |||
| 18388 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%s%ssegid=%d h=%d pgno=%d}", | |||
| 18389 | bDlidx ? "dlidx " : "", | |||
| 18390 | bTomb ? "tombstone " : "", | |||
| 18391 | iSegid, iHeight, iPgno | |||
| 18392 | ); | |||
| 18393 | } | |||
| 18394 | } | |||
| 18395 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18396 | ||||
| 18397 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18398 | static void fts5DebugStructure( | |||
| 18399 | int *pRc, /* IN/OUT: error code */ | |||
| 18400 | Fts5Buffer *pBuf, | |||
| 18401 | Fts5Structure *p | |||
| 18402 | ){ | |||
| 18403 | int iLvl, iSeg; /* Iterate through levels, segments */ | |||
| 18404 | ||||
| 18405 | for(iLvl=0; iLvl<p->nLevel; iLvl++){ | |||
| 18406 | Fts5StructureLevel *pLvl = &p->aLevel[iLvl]; | |||
| 18407 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, | |||
| 18408 | " {lvl=%d nMerge=%d nSeg=%d", iLvl, pLvl->nMerge, pLvl->nSeg | |||
| 18409 | ); | |||
| 18410 | for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){ | |||
| 18411 | Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; | |||
| 18412 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d", | |||
| 18413 | pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast | |||
| 18414 | ); | |||
| 18415 | if( pSeg->iOrigin1>0 ){ | |||
| 18416 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " origin=%lld..%lld", | |||
| 18417 | pSeg->iOrigin1, pSeg->iOrigin2 | |||
| 18418 | ); | |||
| 18419 | } | |||
| 18420 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); | |||
| 18421 | } | |||
| 18422 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); | |||
| 18423 | } | |||
| 18424 | } | |||
| 18425 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18426 | ||||
| 18427 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18428 | /* | |||
| 18429 | ** This is part of the fts5_decode() debugging aid. | |||
| 18430 | ** | |||
| 18431 | ** Arguments pBlob/nBlob contain a serialized Fts5Structure object. This | |||
| 18432 | ** function appends a human-readable representation of the same object | |||
| 18433 | ** to the buffer passed as the second argument. | |||
| 18434 | */ | |||
| 18435 | static void fts5DecodeStructure( | |||
| 18436 | int *pRc, /* IN/OUT: error code */ | |||
| 18437 | Fts5Buffer *pBuf, | |||
| 18438 | const u8 *pBlob, int nBlob | |||
| 18439 | ){ | |||
| 18440 | int rc; /* Return code */ | |||
| 18441 | Fts5Structure *p = 0; /* Decoded structure object */ | |||
| 18442 | ||||
| 18443 | rc = fts5StructureDecode(pBlob, nBlob, 0, &p); | |||
| 18444 | if( rc!=SQLITE_OK0 ){ | |||
| 18445 | *pRc = rc; | |||
| 18446 | return; | |||
| 18447 | } | |||
| 18448 | ||||
| 18449 | fts5DebugStructure(pRc, pBuf, p); | |||
| 18450 | fts5StructureRelease(p); | |||
| 18451 | } | |||
| 18452 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18453 | ||||
| 18454 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18455 | /* | |||
| 18456 | ** This is part of the fts5_decode() debugging aid. | |||
| 18457 | ** | |||
| 18458 | ** Arguments pBlob/nBlob contain an "averages" record. This function | |||
| 18459 | ** appends a human-readable representation of record to the buffer passed | |||
| 18460 | ** as the second argument. | |||
| 18461 | */ | |||
| 18462 | static void fts5DecodeAverages( | |||
| 18463 | int *pRc, /* IN/OUT: error code */ | |||
| 18464 | Fts5Buffer *pBuf, | |||
| 18465 | const u8 *pBlob, int nBlob | |||
| 18466 | ){ | |||
| 18467 | int i = 0; | |||
| 18468 | const char *zSpace = ""; | |||
| 18469 | ||||
| 18470 | while( i<nBlob ){ | |||
| 18471 | u64 iVal; | |||
| 18472 | i += sqlite3Fts5GetVarint(&pBlob[i], &iVal); | |||
| 18473 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "%s%d", zSpace, (int)iVal); | |||
| 18474 | zSpace = " "; | |||
| 18475 | } | |||
| 18476 | } | |||
| 18477 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18478 | ||||
| 18479 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18480 | /* | |||
| 18481 | ** Buffer (a/n) is assumed to contain a list of serialized varints. Read | |||
| 18482 | ** each varint and append its string representation to buffer pBuf. Return | |||
| 18483 | ** after either the input buffer is exhausted or a 0 value is read. | |||
| 18484 | ** | |||
| 18485 | ** The return value is the number of bytes read from the input buffer. | |||
| 18486 | */ | |||
| 18487 | static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ | |||
| 18488 | int iOff = 0; | |||
| 18489 | while( iOff<n ){ | |||
| 18490 | int iVal; | |||
| 18491 | iOff += fts5GetVarint32(&a[iOff], iVal)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(iVal)); | |||
| 18492 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %d", iVal); | |||
| 18493 | } | |||
| 18494 | return iOff; | |||
| 18495 | } | |||
| 18496 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18497 | ||||
| 18498 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18499 | /* | |||
| 18500 | ** The start of buffer (a/n) contains the start of a doclist. The doclist | |||
| 18501 | ** may or may not finish within the buffer. This function appends a text | |||
| 18502 | ** representation of the part of the doclist that is present to buffer | |||
| 18503 | ** pBuf. | |||
| 18504 | ** | |||
| 18505 | ** The return value is the number of bytes read from the input buffer. | |||
| 18506 | */ | |||
| 18507 | static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ | |||
| 18508 | i64 iDocid = 0; | |||
| 18509 | int iOff = 0; | |||
| 18510 | ||||
| 18511 | if( n>0 ){ | |||
| 18512 | iOff = sqlite3Fts5GetVarint(a, (u64*)&iDocid); | |||
| 18513 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid); | |||
| 18514 | } | |||
| 18515 | while( iOff<n ){ | |||
| 18516 | int nPos; | |||
| 18517 | int bDel; | |||
| 18518 | iOff += fts5GetPoslistSize(&a[iOff], &nPos, &bDel); | |||
| 18519 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " nPos=%d%s", nPos, bDel?"*":""); | |||
| 18520 | iOff += fts5DecodePoslist(pRc, pBuf, &a[iOff], MIN(n-iOff, nPos)(((n-iOff) < (nPos)) ? (n-iOff) : (nPos))); | |||
| 18521 | if( iOff<n ){ | |||
| 18522 | i64 iDelta; | |||
| 18523 | iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDelta); | |||
| 18524 | iDocid += iDelta; | |||
| 18525 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid); | |||
| 18526 | } | |||
| 18527 | } | |||
| 18528 | ||||
| 18529 | return iOff; | |||
| 18530 | } | |||
| 18531 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18532 | ||||
| 18533 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18534 | /* | |||
| 18535 | ** This function is part of the fts5_decode() debugging function. It is | |||
| 18536 | ** only ever used with detail=none tables. | |||
| 18537 | ** | |||
| 18538 | ** Buffer (pData/nData) contains a doclist in the format used by detail=none | |||
| 18539 | ** tables. This function appends a human-readable version of that list to | |||
| 18540 | ** buffer pBuf. | |||
| 18541 | ** | |||
| 18542 | ** If *pRc is other than SQLITE_OK when this function is called, it is a | |||
| 18543 | ** no-op. If an OOM or other error occurs within this function, *pRc is | |||
| 18544 | ** set to an SQLite error code before returning. The final state of buffer | |||
| 18545 | ** pBuf is undefined in this case. | |||
| 18546 | */ | |||
| 18547 | static void fts5DecodeRowidList( | |||
| 18548 | int *pRc, /* IN/OUT: Error code */ | |||
| 18549 | Fts5Buffer *pBuf, /* Buffer to append text to */ | |||
| 18550 | const u8 *pData, int nData /* Data to decode list-of-rowids from */ | |||
| 18551 | ){ | |||
| 18552 | int i = 0; | |||
| 18553 | i64 iRowid = 0; | |||
| 18554 | ||||
| 18555 | while( i<nData ){ | |||
| 18556 | const char *zApp = ""; | |||
| 18557 | u64 iVal; | |||
| 18558 | i += sqlite3Fts5GetVarint(&pData[i], &iVal); | |||
| 18559 | iRowid += iVal; | |||
| 18560 | ||||
| 18561 | if( i<nData && pData[i]==0x00 ){ | |||
| 18562 | i++; | |||
| 18563 | if( i<nData && pData[i]==0x00 ){ | |||
| 18564 | i++; | |||
| 18565 | zApp = "+"; | |||
| 18566 | }else{ | |||
| 18567 | zApp = "*"; | |||
| 18568 | } | |||
| 18569 | } | |||
| 18570 | ||||
| 18571 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); | |||
| 18572 | } | |||
| 18573 | } | |||
| 18574 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18575 | ||||
| 18576 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18577 | static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){ | |||
| 18578 | int ii; | |||
| 18579 | fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1)( (u32)((pBuf)->n) + (u32)(pTerm->n*2 + 1) <= (u32)( (pBuf)->nSpace) ? 0 : sqlite3Fts5BufferSize((pRc),(pBuf),( pTerm->n*2 + 1)+(pBuf)->n) ); | |||
| 18580 | if( *pRc==SQLITE_OK0 ){ | |||
| 18581 | for(ii=0; ii<pTerm->n; ii++){ | |||
| 18582 | if( pTerm->p[ii]==0x00 ){ | |||
| 18583 | pBuf->p[pBuf->n++] = '\\'; | |||
| 18584 | pBuf->p[pBuf->n++] = '0'; | |||
| 18585 | }else{ | |||
| 18586 | pBuf->p[pBuf->n++] = pTerm->p[ii]; | |||
| 18587 | } | |||
| 18588 | } | |||
| 18589 | pBuf->p[pBuf->n] = 0x00; | |||
| 18590 | } | |||
| 18591 | } | |||
| 18592 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18593 | ||||
| 18594 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18595 | /* | |||
| 18596 | ** The implementation of user-defined scalar function fts5_decode(). | |||
| 18597 | */ | |||
| 18598 | static void fts5DecodeFunction( | |||
| 18599 | sqlite3_context *pCtx, /* Function call context */ | |||
| 18600 | int nArg, /* Number of args (always 2) */ | |||
| 18601 | sqlite3_value **apVal /* Function arguments */ | |||
| 18602 | ){ | |||
| 18603 | i64 iRowid; /* Rowid for record being decoded */ | |||
| 18604 | int iSegid,iHeight,iPgno,bDlidx;/* Rowid components */ | |||
| 18605 | int bTomb; | |||
| 18606 | const u8 *aBlob; int n; /* Record to decode */ | |||
| 18607 | u8 *a = 0; | |||
| 18608 | Fts5Buffer s; /* Build up text to return here */ | |||
| 18609 | int rc = SQLITE_OK0; /* Return code */ | |||
| 18610 | sqlite3_int64 nSpace = 0; | |||
| 18611 | int eDetailNone = (sqlite3_user_datasqlite3_api->user_data(pCtx)!=0); | |||
| 18612 | ||||
| 18613 | assert( nArg==2 )((void) (0)); | |||
| 18614 | UNUSED_PARAM(nArg)(void)(nArg); | |||
| 18615 | memset(&s, 0, sizeof(Fts5Buffer)); | |||
| 18616 | iRowid = sqlite3_value_int64sqlite3_api->value_int64(apVal[0]); | |||
| 18617 | ||||
| 18618 | /* Make a copy of the second argument (a blob) in aBlob[]. The aBlob[] | |||
| 18619 | ** copy is followed by FTS5_DATA_ZERO_PADDING 0x00 bytes, which prevents | |||
| 18620 | ** buffer overreads even if the record is corrupt. */ | |||
| 18621 | n = sqlite3_value_bytessqlite3_api->value_bytes(apVal[1]); | |||
| 18622 | aBlob = sqlite3_value_blobsqlite3_api->value_blob(apVal[1]); | |||
| 18623 | nSpace = ((i64)n) + FTS5_DATA_ZERO_PADDING8; | |||
| 18624 | a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace); | |||
| 18625 | if( a==0 ) goto decode_out; | |||
| 18626 | if( n>0 ) memcpy(a, aBlob, n); | |||
| 18627 | ||||
| 18628 | fts5DecodeRowid(iRowid, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); | |||
| 18629 | ||||
| 18630 | fts5DebugRowid(&rc, &s, iRowid); | |||
| 18631 | if( bDlidx ){ | |||
| 18632 | Fts5Data dlidx; | |||
| 18633 | Fts5DlidxLvl lvl; | |||
| 18634 | ||||
| 18635 | dlidx.p = a; | |||
| 18636 | dlidx.nn = n; | |||
| 18637 | ||||
| 18638 | memset(&lvl, 0, sizeof(Fts5DlidxLvl)); | |||
| 18639 | lvl.pData = &dlidx; | |||
| 18640 | lvl.iLeafPgno = iPgno; | |||
| 18641 | ||||
| 18642 | for(fts5DlidxLvlNext(&lvl); lvl.bEof==0; fts5DlidxLvlNext(&lvl)){ | |||
| 18643 | sqlite3Fts5BufferAppendPrintf(&rc, &s, | |||
| 18644 | " %d(%lld)", lvl.iLeafPgno, lvl.iRowid | |||
| 18645 | ); | |||
| 18646 | } | |||
| 18647 | }else if( bTomb ){ | |||
| 18648 | u32 nElem = fts5GetU32(&a[4]); | |||
| 18649 | int szKey = (aBlob[0]==4 || aBlob[0]==8) ? aBlob[0] : 8; | |||
| 18650 | int nSlot = (n - 8) / szKey; | |||
| 18651 | int ii; | |||
| 18652 | sqlite3Fts5BufferAppendPrintf(&rc, &s, " nElem=%d", (int)nElem); | |||
| 18653 | if( aBlob[1] ){ | |||
| 18654 | sqlite3Fts5BufferAppendPrintf(&rc, &s, " 0"); | |||
| 18655 | } | |||
| 18656 | for(ii=0; ii<nSlot; ii++){ | |||
| 18657 | u64 iVal = 0; | |||
| 18658 | if( szKey==4 ){ | |||
| 18659 | u32 *aSlot = (u32*)&aBlob[8]; | |||
| 18660 | if( aSlot[ii] ) iVal = fts5GetU32((u8*)&aSlot[ii]); | |||
| 18661 | }else{ | |||
| 18662 | u64 *aSlot = (u64*)&aBlob[8]; | |||
| 18663 | if( aSlot[ii] ) iVal = fts5GetU64((u8*)&aSlot[ii]); | |||
| 18664 | } | |||
| 18665 | if( iVal!=0 ){ | |||
| 18666 | sqlite3Fts5BufferAppendPrintf(&rc, &s, " %lld", (i64)iVal); | |||
| 18667 | } | |||
| 18668 | } | |||
| 18669 | }else if( iSegid==0 ){ | |||
| 18670 | if( iRowid==FTS5_AVERAGES_ROWID1 ){ | |||
| 18671 | fts5DecodeAverages(&rc, &s, a, n); | |||
| 18672 | }else{ | |||
| 18673 | fts5DecodeStructure(&rc, &s, a, n); | |||
| 18674 | } | |||
| 18675 | }else if( eDetailNone ){ | |||
| 18676 | Fts5Buffer term; /* Current term read from page */ | |||
| 18677 | int szLeaf; | |||
| 18678 | int iPgidxOff = szLeaf = fts5GetU16(&a[2]); | |||
| 18679 | int iTermOff; | |||
| 18680 | int nKeep = 0; | |||
| 18681 | int iOff; | |||
| 18682 | ||||
| 18683 | memset(&term, 0, sizeof(Fts5Buffer)); | |||
| 18684 | ||||
| 18685 | /* Decode any entries that occur before the first term. */ | |||
| 18686 | if( szLeaf<n ){ | |||
| 18687 | iPgidxOff += fts5GetVarint32(&a[iPgidxOff], iTermOff)sqlite3Fts5GetVarint32(&a[iPgidxOff],(u32*)&(iTermOff )); | |||
| 18688 | }else{ | |||
| 18689 | iTermOff = szLeaf; | |||
| 18690 | } | |||
| 18691 | fts5DecodeRowidList(&rc, &s, &a[4], iTermOff-4); | |||
| 18692 | ||||
| 18693 | iOff = iTermOff; | |||
| 18694 | while( iOff<szLeaf && rc==SQLITE_OK0 ){ | |||
| 18695 | int nAppend; | |||
| 18696 | ||||
| 18697 | /* Read the term data for the next term*/ | |||
| 18698 | iOff += fts5GetVarint32(&a[iOff], nAppend)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(nAppend)); | |||
| 18699 | term.n = nKeep; | |||
| 18700 | fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff])sqlite3Fts5BufferAppendBlob(&rc,&term,nAppend,&a[ iOff]); | |||
| 18701 | sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); | |||
| 18702 | fts5BufferAppendTerm(&rc, &s, &term); | |||
| 18703 | iOff += nAppend; | |||
| 18704 | ||||
| 18705 | /* Figure out where the doclist for this term ends */ | |||
| 18706 | if( iPgidxOff<n ){ | |||
| 18707 | int nIncr; | |||
| 18708 | iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr)sqlite3Fts5GetVarint32(&a[iPgidxOff],(u32*)&(nIncr)); | |||
| 18709 | iTermOff += nIncr; | |||
| 18710 | }else{ | |||
| 18711 | iTermOff = szLeaf; | |||
| 18712 | } | |||
| 18713 | if( iTermOff>szLeaf ){ | |||
| 18714 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18715 | }else{ | |||
| 18716 | fts5DecodeRowidList(&rc, &s, &a[iOff], iTermOff-iOff); | |||
| 18717 | } | |||
| 18718 | iOff = iTermOff; | |||
| 18719 | if( iOff<szLeaf ){ | |||
| 18720 | iOff += fts5GetVarint32(&a[iOff], nKeep)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(nKeep)); | |||
| 18721 | } | |||
| 18722 | } | |||
| 18723 | ||||
| 18724 | fts5BufferFree(&term)sqlite3Fts5BufferFree(&term); | |||
| 18725 | }else{ | |||
| 18726 | Fts5Buffer term; /* Current term read from page */ | |||
| 18727 | int szLeaf; /* Offset of pgidx in a[] */ | |||
| 18728 | int iPgidxOff; | |||
| 18729 | int iPgidxPrev = 0; /* Previous value read from pgidx */ | |||
| 18730 | int iTermOff = 0; | |||
| 18731 | int iRowidOff = 0; | |||
| 18732 | int iOff; | |||
| 18733 | int nDoclist; | |||
| 18734 | ||||
| 18735 | memset(&term, 0, sizeof(Fts5Buffer)); | |||
| 18736 | ||||
| 18737 | if( n<4 ){ | |||
| 18738 | sqlite3Fts5BufferSet(&rc, &s, 7, (const u8*)"corrupt"); | |||
| 18739 | goto decode_out; | |||
| 18740 | }else{ | |||
| 18741 | iRowidOff = fts5GetU16(&a[0]); | |||
| 18742 | iPgidxOff = szLeaf = fts5GetU16(&a[2]); | |||
| 18743 | if( iPgidxOff<n ){ | |||
| 18744 | fts5GetVarint32(&a[iPgidxOff], iTermOff)sqlite3Fts5GetVarint32(&a[iPgidxOff],(u32*)&(iTermOff )); | |||
| 18745 | }else if( iPgidxOff>n ){ | |||
| 18746 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18747 | goto decode_out; | |||
| 18748 | } | |||
| 18749 | } | |||
| 18750 | ||||
| 18751 | /* Decode the position list tail at the start of the page */ | |||
| 18752 | if( iRowidOff!=0 ){ | |||
| 18753 | iOff = iRowidOff; | |||
| 18754 | }else if( iTermOff!=0 ){ | |||
| 18755 | iOff = iTermOff; | |||
| 18756 | }else{ | |||
| 18757 | iOff = szLeaf; | |||
| 18758 | } | |||
| 18759 | if( iOff>n ){ | |||
| 18760 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18761 | goto decode_out; | |||
| 18762 | } | |||
| 18763 | fts5DecodePoslist(&rc, &s, &a[4], iOff-4); | |||
| 18764 | ||||
| 18765 | /* Decode any more doclist data that appears on the page before the | |||
| 18766 | ** first term. */ | |||
| 18767 | nDoclist = (iTermOff ? iTermOff : szLeaf) - iOff; | |||
| 18768 | if( nDoclist+iOff>n ){ | |||
| 18769 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18770 | goto decode_out; | |||
| 18771 | } | |||
| 18772 | fts5DecodeDoclist(&rc, &s, &a[iOff], nDoclist); | |||
| 18773 | ||||
| 18774 | while( iPgidxOff<n && rc==SQLITE_OK0 ){ | |||
| 18775 | int bFirst = (iPgidxOff==szLeaf); /* True for first term on page */ | |||
| 18776 | int nByte; /* Bytes of data */ | |||
| 18777 | int iEnd; | |||
| 18778 | ||||
| 18779 | iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nByte)sqlite3Fts5GetVarint32(&a[iPgidxOff],(u32*)&(nByte)); | |||
| 18780 | iPgidxPrev += nByte; | |||
| 18781 | iOff = iPgidxPrev; | |||
| 18782 | ||||
| 18783 | if( iPgidxOff<n ){ | |||
| 18784 | fts5GetVarint32(&a[iPgidxOff], nByte)sqlite3Fts5GetVarint32(&a[iPgidxOff],(u32*)&(nByte)); | |||
| 18785 | iEnd = iPgidxPrev + nByte; | |||
| 18786 | }else{ | |||
| 18787 | iEnd = szLeaf; | |||
| 18788 | } | |||
| 18789 | if( iEnd>szLeaf ){ | |||
| 18790 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18791 | break; | |||
| 18792 | } | |||
| 18793 | ||||
| 18794 | if( bFirst==0 ){ | |||
| 18795 | iOff += fts5GetVarint32(&a[iOff], nByte)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(nByte)); | |||
| 18796 | if( nByte>term.n ){ | |||
| 18797 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18798 | break; | |||
| 18799 | } | |||
| 18800 | term.n = nByte; | |||
| 18801 | } | |||
| 18802 | iOff += fts5GetVarint32(&a[iOff], nByte)sqlite3Fts5GetVarint32(&a[iOff],(u32*)&(nByte)); | |||
| 18803 | if( iOff+nByte>n ){ | |||
| 18804 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 18805 | break; | |||
| 18806 | } | |||
| 18807 | fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff])sqlite3Fts5BufferAppendBlob(&rc,&term,nByte,&a[iOff ]); | |||
| 18808 | iOff += nByte; | |||
| 18809 | ||||
| 18810 | sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); | |||
| 18811 | fts5BufferAppendTerm(&rc, &s, &term); | |||
| 18812 | iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff); | |||
| 18813 | } | |||
| 18814 | ||||
| 18815 | fts5BufferFree(&term)sqlite3Fts5BufferFree(&term); | |||
| 18816 | } | |||
| 18817 | ||||
| 18818 | decode_out: | |||
| 18819 | sqlite3_freesqlite3_api->free(a); | |||
| 18820 | if( rc==SQLITE_OK0 ){ | |||
| 18821 | sqlite3_result_textsqlite3_api->result_text(pCtx, (const char*)s.p, s.n, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 18822 | }else{ | |||
| 18823 | sqlite3_result_error_codesqlite3_api->result_error_code(pCtx, rc); | |||
| 18824 | } | |||
| 18825 | fts5BufferFree(&s)sqlite3Fts5BufferFree(&s); | |||
| 18826 | } | |||
| 18827 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18828 | ||||
| 18829 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18830 | /* | |||
| 18831 | ** The implementation of user-defined scalar function fts5_rowid(). | |||
| 18832 | */ | |||
| 18833 | static void fts5RowidFunction( | |||
| 18834 | sqlite3_context *pCtx, /* Function call context */ | |||
| 18835 | int nArg, /* Number of args (always 2) */ | |||
| 18836 | sqlite3_value **apVal /* Function arguments */ | |||
| 18837 | ){ | |||
| 18838 | const char *zArg; | |||
| 18839 | if( nArg==0 ){ | |||
| 18840 | sqlite3_result_errorsqlite3_api->result_error(pCtx, "should be: fts5_rowid(subject, ....)", -1); | |||
| 18841 | }else{ | |||
| 18842 | zArg = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[0]); | |||
| 18843 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(zArg, "segment") ){ | |||
| 18844 | i64 iRowid; | |||
| 18845 | int segid, pgno; | |||
| 18846 | if( nArg!=3 ){ | |||
| 18847 | sqlite3_result_errorsqlite3_api->result_error(pCtx, | |||
| 18848 | "should be: fts5_rowid('segment', segid, pgno))", -1 | |||
| 18849 | ); | |||
| 18850 | }else{ | |||
| 18851 | segid = sqlite3_value_intsqlite3_api->value_int(apVal[1]); | |||
| 18852 | pgno = sqlite3_value_intsqlite3_api->value_int(apVal[2]); | |||
| 18853 | iRowid = FTS5_SEGMENT_ROWID(segid, pgno)( ((i64)(segid) << (31 +5 +1)) + ((i64)(0) << (31 + 5)) + ((i64)(0) << (31)) + ((i64)(pgno)) ); | |||
| 18854 | sqlite3_result_int64sqlite3_api->result_int64(pCtx, iRowid); | |||
| 18855 | } | |||
| 18856 | }else{ | |||
| 18857 | sqlite3_result_errorsqlite3_api->result_error(pCtx, | |||
| 18858 | "first arg to fts5_rowid() must be 'segment'" , -1 | |||
| 18859 | ); | |||
| 18860 | } | |||
| 18861 | } | |||
| 18862 | } | |||
| 18863 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 18864 | ||||
| 18865 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 18866 | ||||
| 18867 | typedef struct Fts5StructVtab Fts5StructVtab; | |||
| 18868 | struct Fts5StructVtab { | |||
| 18869 | sqlite3_vtab base; | |||
| 18870 | }; | |||
| 18871 | ||||
| 18872 | typedef struct Fts5StructVcsr Fts5StructVcsr; | |||
| 18873 | struct Fts5StructVcsr { | |||
| 18874 | sqlite3_vtab_cursor base; | |||
| 18875 | Fts5Structure *pStruct; | |||
| 18876 | int iLevel; | |||
| 18877 | int iSeg; | |||
| 18878 | int iRowid; | |||
| 18879 | }; | |||
| 18880 | ||||
| 18881 | /* | |||
| 18882 | ** Create a new fts5_structure() table-valued function. | |||
| 18883 | */ | |||
| 18884 | static int fts5structConnectMethod( | |||
| 18885 | sqlite3 *db, | |||
| 18886 | void *pAux, | |||
| 18887 | int argc, const char *const*argv, | |||
| 18888 | sqlite3_vtab **ppVtab, | |||
| 18889 | char **pzErr | |||
| 18890 | ){ | |||
| 18891 | Fts5StructVtab *pNew = 0; | |||
| 18892 | int rc = SQLITE_OK0; | |||
| 18893 | ||||
| 18894 | rc = sqlite3_declare_vtabsqlite3_api->declare_vtab(db, | |||
| 18895 | "CREATE TABLE xyz(" | |||
| 18896 | "level, segment, merge, segid, leaf1, leaf2, loc1, loc2, " | |||
| 18897 | "npgtombstone, nentrytombstone, nentry, struct HIDDEN);" | |||
| 18898 | ); | |||
| 18899 | if( rc==SQLITE_OK0 ){ | |||
| 18900 | pNew = sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); | |||
| 18901 | } | |||
| 18902 | ||||
| 18903 | *ppVtab = (sqlite3_vtab*)pNew; | |||
| 18904 | return rc; | |||
| 18905 | } | |||
| 18906 | ||||
| 18907 | /* | |||
| 18908 | ** We must have a single struct=? constraint that will be passed through | |||
| 18909 | ** into the xFilter method. If there is no valid struct=? constraint, | |||
| 18910 | ** then return an SQLITE_CONSTRAINT error. | |||
| 18911 | */ | |||
| 18912 | static int fts5structBestIndexMethod( | |||
| 18913 | sqlite3_vtab *tab, | |||
| 18914 | sqlite3_index_info *pIdxInfo | |||
| 18915 | ){ | |||
| 18916 | int i; | |||
| 18917 | int rc = SQLITE_CONSTRAINT19; | |||
| 18918 | struct sqlite3_index_constraint *p; | |||
| 18919 | pIdxInfo->estimatedCost = (double)100; | |||
| 18920 | pIdxInfo->estimatedRows = 100; | |||
| 18921 | pIdxInfo->idxNum = 0; | |||
| 18922 | for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){ | |||
| 18923 | if( p->usable==0 ) continue; | |||
| 18924 | if( p->op==SQLITE_INDEX_CONSTRAINT_EQ2 && p->iColumn==11 ){ | |||
| 18925 | rc = SQLITE_OK0; | |||
| 18926 | pIdxInfo->aConstraintUsage[i].omit = 1; | |||
| 18927 | pIdxInfo->aConstraintUsage[i].argvIndex = 1; | |||
| 18928 | break; | |||
| 18929 | } | |||
| 18930 | } | |||
| 18931 | return rc; | |||
| 18932 | } | |||
| 18933 | ||||
| 18934 | /* | |||
| 18935 | ** This method is the destructor for bytecodevtab objects. | |||
| 18936 | */ | |||
| 18937 | static int fts5structDisconnectMethod(sqlite3_vtab *pVtab){ | |||
| 18938 | Fts5StructVtab *p = (Fts5StructVtab*)pVtab; | |||
| 18939 | sqlite3_freesqlite3_api->free(p); | |||
| 18940 | return SQLITE_OK0; | |||
| 18941 | } | |||
| 18942 | ||||
| 18943 | /* | |||
| 18944 | ** Constructor for a new bytecodevtab_cursor object. | |||
| 18945 | */ | |||
| 18946 | static int fts5structOpenMethod(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){ | |||
| 18947 | int rc = SQLITE_OK0; | |||
| 18948 | Fts5StructVcsr *pNew = 0; | |||
| 18949 | ||||
| 18950 | pNew = sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); | |||
| 18951 | *ppCsr = (sqlite3_vtab_cursor*)pNew; | |||
| 18952 | ||||
| 18953 | return SQLITE_OK0; | |||
| 18954 | } | |||
| 18955 | ||||
| 18956 | /* | |||
| 18957 | ** Destructor for a bytecodevtab_cursor. | |||
| 18958 | */ | |||
| 18959 | static int fts5structCloseMethod(sqlite3_vtab_cursor *cur){ | |||
| 18960 | Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; | |||
| 18961 | fts5StructureRelease(pCsr->pStruct); | |||
| 18962 | sqlite3_freesqlite3_api->free(pCsr); | |||
| 18963 | return SQLITE_OK0; | |||
| 18964 | } | |||
| 18965 | ||||
| 18966 | ||||
| 18967 | /* | |||
| 18968 | ** Advance a bytecodevtab_cursor to its next row of output. | |||
| 18969 | */ | |||
| 18970 | static int fts5structNextMethod(sqlite3_vtab_cursor *cur){ | |||
| 18971 | Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; | |||
| 18972 | Fts5Structure *p = pCsr->pStruct; | |||
| 18973 | ||||
| 18974 | assert( pCsr->pStruct )((void) (0)); | |||
| 18975 | pCsr->iSeg++; | |||
| 18976 | pCsr->iRowid++; | |||
| 18977 | while( pCsr->iLevel<p->nLevel && pCsr->iSeg>=p->aLevel[pCsr->iLevel].nSeg ){ | |||
| 18978 | pCsr->iLevel++; | |||
| 18979 | pCsr->iSeg = 0; | |||
| 18980 | } | |||
| 18981 | if( pCsr->iLevel>=p->nLevel ){ | |||
| 18982 | fts5StructureRelease(pCsr->pStruct); | |||
| 18983 | pCsr->pStruct = 0; | |||
| 18984 | } | |||
| 18985 | return SQLITE_OK0; | |||
| 18986 | } | |||
| 18987 | ||||
| 18988 | /* | |||
| 18989 | ** Return TRUE if the cursor has been moved off of the last | |||
| 18990 | ** row of output. | |||
| 18991 | */ | |||
| 18992 | static int fts5structEofMethod(sqlite3_vtab_cursor *cur){ | |||
| 18993 | Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; | |||
| 18994 | return pCsr->pStruct==0; | |||
| 18995 | } | |||
| 18996 | ||||
| 18997 | static int fts5structRowidMethod( | |||
| 18998 | sqlite3_vtab_cursor *cur, | |||
| 18999 | sqlite_int64 *piRowid | |||
| 19000 | ){ | |||
| 19001 | Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; | |||
| 19002 | *piRowid = pCsr->iRowid; | |||
| 19003 | return SQLITE_OK0; | |||
| 19004 | } | |||
| 19005 | ||||
| 19006 | /* | |||
| 19007 | ** Return values of columns for the row at which the bytecodevtab_cursor | |||
| 19008 | ** is currently pointing. | |||
| 19009 | */ | |||
| 19010 | static int fts5structColumnMethod( | |||
| 19011 | sqlite3_vtab_cursor *cur, /* The cursor */ | |||
| 19012 | sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ | |||
| 19013 | int i /* Which column to return */ | |||
| 19014 | ){ | |||
| 19015 | Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; | |||
| 19016 | Fts5Structure *p = pCsr->pStruct; | |||
| 19017 | Fts5StructureSegment *pSeg = &p->aLevel[pCsr->iLevel].aSeg[pCsr->iSeg]; | |||
| 19018 | ||||
| 19019 | switch( i ){ | |||
| 19020 | case 0: /* level */ | |||
| 19021 | sqlite3_result_intsqlite3_api->result_int(ctx, pCsr->iLevel); | |||
| 19022 | break; | |||
| 19023 | case 1: /* segment */ | |||
| 19024 | sqlite3_result_intsqlite3_api->result_int(ctx, pCsr->iSeg); | |||
| 19025 | break; | |||
| 19026 | case 2: /* merge */ | |||
| 19027 | sqlite3_result_intsqlite3_api->result_int(ctx, pCsr->iSeg < p->aLevel[pCsr->iLevel].nMerge); | |||
| 19028 | break; | |||
| 19029 | case 3: /* segid */ | |||
| 19030 | sqlite3_result_intsqlite3_api->result_int(ctx, pSeg->iSegid); | |||
| 19031 | break; | |||
| 19032 | case 4: /* leaf1 */ | |||
| 19033 | sqlite3_result_intsqlite3_api->result_int(ctx, pSeg->pgnoFirst); | |||
| 19034 | break; | |||
| 19035 | case 5: /* leaf2 */ | |||
| 19036 | sqlite3_result_intsqlite3_api->result_int(ctx, pSeg->pgnoLast); | |||
| 19037 | break; | |||
| 19038 | case 6: /* origin1 */ | |||
| 19039 | sqlite3_result_int64sqlite3_api->result_int64(ctx, pSeg->iOrigin1); | |||
| 19040 | break; | |||
| 19041 | case 7: /* origin2 */ | |||
| 19042 | sqlite3_result_int64sqlite3_api->result_int64(ctx, pSeg->iOrigin2); | |||
| 19043 | break; | |||
| 19044 | case 8: /* npgtombstone */ | |||
| 19045 | sqlite3_result_intsqlite3_api->result_int(ctx, pSeg->nPgTombstone); | |||
| 19046 | break; | |||
| 19047 | case 9: /* nentrytombstone */ | |||
| 19048 | sqlite3_result_int64sqlite3_api->result_int64(ctx, pSeg->nEntryTombstone); | |||
| 19049 | break; | |||
| 19050 | case 10: /* nentry */ | |||
| 19051 | sqlite3_result_int64sqlite3_api->result_int64(ctx, pSeg->nEntry); | |||
| 19052 | break; | |||
| 19053 | } | |||
| 19054 | return SQLITE_OK0; | |||
| 19055 | } | |||
| 19056 | ||||
| 19057 | /* | |||
| 19058 | ** Initialize a cursor. | |||
| 19059 | ** | |||
| 19060 | ** idxNum==0 means show all subprograms | |||
| 19061 | ** idxNum==1 means show only the main bytecode and omit subprograms. | |||
| 19062 | */ | |||
| 19063 | static int fts5structFilterMethod( | |||
| 19064 | sqlite3_vtab_cursor *pVtabCursor, | |||
| 19065 | int idxNum, const char *idxStr, | |||
| 19066 | int argc, sqlite3_value **argv | |||
| 19067 | ){ | |||
| 19068 | Fts5StructVcsr *pCsr = (Fts5StructVcsr *)pVtabCursor; | |||
| 19069 | int rc = SQLITE_OK0; | |||
| 19070 | ||||
| 19071 | const u8 *aBlob = 0; | |||
| 19072 | int nBlob = 0; | |||
| 19073 | ||||
| 19074 | assert( argc==1 )((void) (0)); | |||
| 19075 | fts5StructureRelease(pCsr->pStruct); | |||
| 19076 | pCsr->pStruct = 0; | |||
| 19077 | ||||
| 19078 | nBlob = sqlite3_value_bytessqlite3_api->value_bytes(argv[0]); | |||
| 19079 | aBlob = (const u8*)sqlite3_value_blobsqlite3_api->value_blob(argv[0]); | |||
| 19080 | rc = fts5StructureDecode(aBlob, nBlob, 0, &pCsr->pStruct); | |||
| 19081 | if( rc==SQLITE_OK0 ){ | |||
| 19082 | pCsr->iLevel = 0; | |||
| 19083 | pCsr->iRowid = 0; | |||
| 19084 | pCsr->iSeg = -1; | |||
| 19085 | rc = fts5structNextMethod(pVtabCursor); | |||
| 19086 | } | |||
| 19087 | ||||
| 19088 | return rc; | |||
| 19089 | } | |||
| 19090 | ||||
| 19091 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ | |||
| 19092 | ||||
| 19093 | /* | |||
| 19094 | ** This is called as part of registering the FTS5 module with database | |||
| 19095 | ** connection db. It registers several user-defined scalar functions useful | |||
| 19096 | ** with FTS5. | |||
| 19097 | ** | |||
| 19098 | ** If successful, SQLITE_OK is returned. If an error occurs, some other | |||
| 19099 | ** SQLite error code is returned instead. | |||
| 19100 | */ | |||
| 19101 | static int sqlite3Fts5IndexInit(sqlite3 *db){ | |||
| 19102 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) | |||
| 19103 | int rc = sqlite3_create_functionsqlite3_api->create_function( | |||
| 19104 | db, "fts5_decode", 2, SQLITE_UTF81, 0, fts5DecodeFunction, 0, 0 | |||
| 19105 | ); | |||
| 19106 | ||||
| 19107 | if( rc==SQLITE_OK0 ){ | |||
| 19108 | rc = sqlite3_create_functionsqlite3_api->create_function( | |||
| 19109 | db, "fts5_decode_none", 2, | |||
| 19110 | SQLITE_UTF81, (void*)db, fts5DecodeFunction, 0, 0 | |||
| 19111 | ); | |||
| 19112 | } | |||
| 19113 | ||||
| 19114 | if( rc==SQLITE_OK0 ){ | |||
| 19115 | rc = sqlite3_create_functionsqlite3_api->create_function( | |||
| 19116 | db, "fts5_rowid", -1, SQLITE_UTF81, 0, fts5RowidFunction, 0, 0 | |||
| 19117 | ); | |||
| 19118 | } | |||
| 19119 | ||||
| 19120 | if( rc==SQLITE_OK0 ){ | |||
| 19121 | static const sqlite3_module fts5structure_module = { | |||
| 19122 | 0, /* iVersion */ | |||
| 19123 | 0, /* xCreate */ | |||
| 19124 | fts5structConnectMethod, /* xConnect */ | |||
| 19125 | fts5structBestIndexMethod, /* xBestIndex */ | |||
| 19126 | fts5structDisconnectMethod, /* xDisconnect */ | |||
| 19127 | 0, /* xDestroy */ | |||
| 19128 | fts5structOpenMethod, /* xOpen */ | |||
| 19129 | fts5structCloseMethod, /* xClose */ | |||
| 19130 | fts5structFilterMethod, /* xFilter */ | |||
| 19131 | fts5structNextMethod, /* xNext */ | |||
| 19132 | fts5structEofMethod, /* xEof */ | |||
| 19133 | fts5structColumnMethod, /* xColumn */ | |||
| 19134 | fts5structRowidMethod, /* xRowid */ | |||
| 19135 | 0, /* xUpdate */ | |||
| 19136 | 0, /* xBegin */ | |||
| 19137 | 0, /* xSync */ | |||
| 19138 | 0, /* xCommit */ | |||
| 19139 | 0, /* xRollback */ | |||
| 19140 | 0, /* xFindFunction */ | |||
| 19141 | 0, /* xRename */ | |||
| 19142 | 0, /* xSavepoint */ | |||
| 19143 | 0, /* xRelease */ | |||
| 19144 | 0, /* xRollbackTo */ | |||
| 19145 | 0, /* xShadowName */ | |||
| 19146 | 0 /* xIntegrity */ | |||
| 19147 | }; | |||
| 19148 | rc = sqlite3_create_modulesqlite3_api->create_module(db, "fts5_structure", &fts5structure_module, 0); | |||
| 19149 | } | |||
| 19150 | return rc; | |||
| 19151 | #else | |||
| 19152 | return SQLITE_OK0; | |||
| 19153 | UNUSED_PARAM(db)(void)(db); | |||
| 19154 | #endif | |||
| 19155 | } | |||
| 19156 | ||||
| 19157 | ||||
| 19158 | static int sqlite3Fts5IndexReset(Fts5Index *p){ | |||
| 19159 | assert( p->pStruct==0 || p->iStructVersion!=0 )((void) (0)); | |||
| 19160 | if( fts5IndexDataVersion(p)!=p->iStructVersion ){ | |||
| 19161 | fts5StructureInvalidate(p); | |||
| 19162 | } | |||
| 19163 | return fts5IndexReturn(p); | |||
| 19164 | } | |||
| 19165 | ||||
| 19166 | #line 1 "fts5_main.c" | |||
| 19167 | /* | |||
| 19168 | ** 2014 Jun 09 | |||
| 19169 | ** | |||
| 19170 | ** The author disclaims copyright to this source code. In place of | |||
| 19171 | ** a legal notice, here is a blessing: | |||
| 19172 | ** | |||
| 19173 | ** May you do good and not evil. | |||
| 19174 | ** May you find forgiveness for yourself and forgive others. | |||
| 19175 | ** May you share freely, never taking more than you give. | |||
| 19176 | ** | |||
| 19177 | ****************************************************************************** | |||
| 19178 | ** | |||
| 19179 | ** This is an SQLite module implementing full-text search. | |||
| 19180 | */ | |||
| 19181 | ||||
| 19182 | ||||
| 19183 | /* #include "fts5Int.h" */ | |||
| 19184 | ||||
| 19185 | /* | |||
| 19186 | ** This variable is set to false when running tests for which the on disk | |||
| 19187 | ** structures should not be corrupt. Otherwise, true. If it is false, extra | |||
| 19188 | ** assert() conditions in the fts5 code are activated - conditions that are | |||
| 19189 | ** only true if it is guaranteed that the fts5 database is not corrupt. | |||
| 19190 | */ | |||
| 19191 | #ifdef SQLITE_DEBUG | |||
| 19192 | int sqlite3_fts5_may_be_corrupt = 1; | |||
| 19193 | #endif | |||
| 19194 | ||||
| 19195 | ||||
| 19196 | typedef struct Fts5Auxdata Fts5Auxdata; | |||
| 19197 | typedef struct Fts5Auxiliary Fts5Auxiliary; | |||
| 19198 | typedef struct Fts5Cursor Fts5Cursor; | |||
| 19199 | typedef struct Fts5FullTable Fts5FullTable; | |||
| 19200 | typedef struct Fts5Sorter Fts5Sorter; | |||
| 19201 | typedef struct Fts5TokenizerModule Fts5TokenizerModule; | |||
| 19202 | ||||
| 19203 | /* | |||
| 19204 | ** NOTES ON TRANSACTIONS: | |||
| 19205 | ** | |||
| 19206 | ** SQLite invokes the following virtual table methods as transactions are | |||
| 19207 | ** opened and closed by the user: | |||
| 19208 | ** | |||
| 19209 | ** xBegin(): Start of a new transaction. | |||
| 19210 | ** xSync(): Initial part of two-phase commit. | |||
| 19211 | ** xCommit(): Final part of two-phase commit. | |||
| 19212 | ** xRollback(): Rollback the transaction. | |||
| 19213 | ** | |||
| 19214 | ** Anything that is required as part of a commit that may fail is performed | |||
| 19215 | ** in the xSync() callback. Current versions of SQLite ignore any errors | |||
| 19216 | ** returned by xCommit(). | |||
| 19217 | ** | |||
| 19218 | ** And as sub-transactions are opened/closed: | |||
| 19219 | ** | |||
| 19220 | ** xSavepoint(int S): Open savepoint S. | |||
| 19221 | ** xRelease(int S): Commit and close savepoint S. | |||
| 19222 | ** xRollbackTo(int S): Rollback to start of savepoint S. | |||
| 19223 | ** | |||
| 19224 | ** During a write-transaction the fts5_index.c module may cache some data | |||
| 19225 | ** in-memory. It is flushed to disk whenever xSync(), xRelease() or | |||
| 19226 | ** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo() | |||
| 19227 | ** is called. | |||
| 19228 | ** | |||
| 19229 | ** Additionally, if SQLITE_DEBUG is defined, an instance of the following | |||
| 19230 | ** structure is used to record the current transaction state. This information | |||
| 19231 | ** is not required, but it is used in the assert() statements executed by | |||
| 19232 | ** function fts5CheckTransactionState() (see below). | |||
| 19233 | */ | |||
| 19234 | struct Fts5TransactionState { | |||
| 19235 | int eState; /* 0==closed, 1==open, 2==synced */ | |||
| 19236 | int iSavepoint; /* Number of open savepoints (0 -> none) */ | |||
| 19237 | }; | |||
| 19238 | ||||
| 19239 | /* | |||
| 19240 | ** A single object of this type is allocated when the FTS5 module is | |||
| 19241 | ** registered with a database handle. It is used to store pointers to | |||
| 19242 | ** all registered FTS5 extensions - tokenizers and auxiliary functions. | |||
| 19243 | */ | |||
| 19244 | struct Fts5Global { | |||
| 19245 | fts5_api api; /* User visible part of object (see fts5.h) */ | |||
| 19246 | sqlite3 *db; /* Associated database connection */ | |||
| 19247 | i64 iNextId; /* Used to allocate unique cursor ids */ | |||
| 19248 | Fts5Auxiliary *pAux; /* First in list of all aux. functions */ | |||
| 19249 | Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ | |||
| 19250 | Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ | |||
| 19251 | Fts5Cursor *pCsr; /* First in list of all open cursors */ | |||
| 19252 | u32 aLocaleHdr[4]; | |||
| 19253 | }; | |||
| 19254 | ||||
| 19255 | /* | |||
| 19256 | ** Size of header on fts5_locale() values. And macro to access a buffer | |||
| 19257 | ** containing a copy of the header from an Fts5Config pointer. | |||
| 19258 | */ | |||
| 19259 | #define FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) | |||
| 19260 | #define FTS5_LOCALE_HDR(pConfig)((const u8*)(pConfig->pGlobal->aLocaleHdr)) ((const u8*)(pConfig->pGlobal->aLocaleHdr)) | |||
| 19261 | ||||
| 19262 | #define FTS5_INSTTOKEN_SUBTYPE73 73 | |||
| 19263 | ||||
| 19264 | /* | |||
| 19265 | ** Each auxiliary function registered with the FTS5 module is represented | |||
| 19266 | ** by an object of the following type. All such objects are stored as part | |||
| 19267 | ** of the Fts5Global.pAux list. | |||
| 19268 | */ | |||
| 19269 | struct Fts5Auxiliary { | |||
| 19270 | Fts5Global *pGlobal; /* Global context for this function */ | |||
| 19271 | char *zFunc; /* Function name (nul-terminated) */ | |||
| 19272 | void *pUserData; /* User-data pointer */ | |||
| 19273 | fts5_extension_function xFunc; /* Callback function */ | |||
| 19274 | void (*xDestroy)(void*); /* Destructor function */ | |||
| 19275 | Fts5Auxiliary *pNext; /* Next registered auxiliary function */ | |||
| 19276 | }; | |||
| 19277 | ||||
| 19278 | /* | |||
| 19279 | ** Each tokenizer module registered with the FTS5 module is represented | |||
| 19280 | ** by an object of the following type. All such objects are stored as part | |||
| 19281 | ** of the Fts5Global.pTok list. | |||
| 19282 | ** | |||
| 19283 | ** bV2Native: | |||
| 19284 | ** True if the tokenizer was registered using xCreateTokenizer_v2(), false | |||
| 19285 | ** for xCreateTokenizer(). If this variable is true, then x2 is populated | |||
| 19286 | ** with the routines as supplied by the caller and x1 contains synthesized | |||
| 19287 | ** wrapper routines. In this case the user-data pointer passed to | |||
| 19288 | ** x1.xCreate should be a pointer to the Fts5TokenizerModule structure, | |||
| 19289 | ** not a copy of pUserData. | |||
| 19290 | ** | |||
| 19291 | ** Of course, if bV2Native is false, then x1 contains the real routines and | |||
| 19292 | ** x2 the synthesized ones. In this case a pointer to the Fts5TokenizerModule | |||
| 19293 | ** object should be passed to x2.xCreate. | |||
| 19294 | ** | |||
| 19295 | ** The synthesized wrapper routines are necessary for xFindTokenizer(_v2) | |||
| 19296 | ** calls. | |||
| 19297 | */ | |||
| 19298 | struct Fts5TokenizerModule { | |||
| 19299 | char *zName; /* Name of tokenizer */ | |||
| 19300 | void *pUserData; /* User pointer passed to xCreate() */ | |||
| 19301 | int bV2Native; /* True if v2 native tokenizer */ | |||
| 19302 | fts5_tokenizer x1; /* Tokenizer functions */ | |||
| 19303 | fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ | |||
| 19304 | void (*xDestroy)(void*); /* Destructor function */ | |||
| 19305 | Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ | |||
| 19306 | }; | |||
| 19307 | ||||
| 19308 | struct Fts5FullTable { | |||
| 19309 | Fts5Table p; /* Public class members from fts5Int.h */ | |||
| 19310 | Fts5Storage *pStorage; /* Document store */ | |||
| 19311 | Fts5Global *pGlobal; /* Global (connection wide) data */ | |||
| 19312 | Fts5Cursor *pSortCsr; /* Sort data from this cursor */ | |||
| 19313 | int iSavepoint; /* Successful xSavepoint()+1 */ | |||
| 19314 | ||||
| 19315 | #ifdef SQLITE_DEBUG | |||
| 19316 | struct Fts5TransactionState ts; | |||
| 19317 | #endif | |||
| 19318 | }; | |||
| 19319 | ||||
| 19320 | struct Fts5MatchPhrase { | |||
| 19321 | Fts5Buffer *pPoslist; /* Pointer to current poslist */ | |||
| 19322 | int nTerm; /* Size of phrase in terms */ | |||
| 19323 | }; | |||
| 19324 | ||||
| 19325 | /* | |||
| 19326 | ** pStmt: | |||
| 19327 | ** SELECT rowid, <fts> FROM <fts> ORDER BY +rank; | |||
| 19328 | ** | |||
| 19329 | ** aIdx[]: | |||
| 19330 | ** There is one entry in the aIdx[] array for each phrase in the query, | |||
| 19331 | ** the value of which is the offset within aPoslist[] following the last | |||
| 19332 | ** byte of the position list for the corresponding phrase. | |||
| 19333 | */ | |||
| 19334 | struct Fts5Sorter { | |||
| 19335 | sqlite3_stmt *pStmt; | |||
| 19336 | i64 iRowid; /* Current rowid */ | |||
| 19337 | const u8 *aPoslist; /* Position lists for current row */ | |||
| 19338 | int nIdx; /* Number of entries in aIdx[] */ | |||
| 19339 | int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */ | |||
| 19340 | }; | |||
| 19341 | ||||
| 19342 | /* Size (int bytes) of an Fts5Sorter object with N indexes */ | |||
| 19343 | #define SZ_FTS5SORTER(N)(__builtin_offsetof(Fts5Sorter, nIdx)+((N+2)/2)*sizeof(i64)) (offsetof(Fts5Sorter,nIdx)__builtin_offsetof(Fts5Sorter, nIdx)+((N+2)/2)*sizeof(i64)) | |||
| 19344 | ||||
| 19345 | /* | |||
| 19346 | ** Virtual-table cursor object. | |||
| 19347 | ** | |||
| 19348 | ** iSpecial: | |||
| 19349 | ** If this is a 'special' query (refer to function fts5SpecialMatch()), | |||
| 19350 | ** then this variable contains the result of the query. | |||
| 19351 | ** | |||
| 19352 | ** iFirstRowid, iLastRowid: | |||
| 19353 | ** These variables are only used for FTS5_PLAN_MATCH cursors. Assuming the | |||
| 19354 | ** cursor iterates in ascending order of rowids, iFirstRowid is the lower | |||
| 19355 | ** limit of rowids to return, and iLastRowid the upper. In other words, the | |||
| 19356 | ** WHERE clause in the user's query might have been: | |||
| 19357 | ** | |||
| 19358 | ** <tbl> MATCH <expr> AND rowid BETWEEN $iFirstRowid AND $iLastRowid | |||
| 19359 | ** | |||
| 19360 | ** If the cursor iterates in descending order of rowid, iFirstRowid | |||
| 19361 | ** is the upper limit (i.e. the "first" rowid visited) and iLastRowid | |||
| 19362 | ** the lower. | |||
| 19363 | */ | |||
| 19364 | struct Fts5Cursor { | |||
| 19365 | sqlite3_vtab_cursor base; /* Base class used by SQLite core */ | |||
| 19366 | Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */ | |||
| 19367 | int *aColumnSize; /* Values for xColumnSize() */ | |||
| 19368 | i64 iCsrId; /* Cursor id */ | |||
| 19369 | ||||
| 19370 | /* Zero from this point onwards on cursor reset */ | |||
| 19371 | int ePlan; /* FTS5_PLAN_XXX value */ | |||
| 19372 | int bDesc; /* True for "ORDER BY rowid DESC" queries */ | |||
| 19373 | i64 iFirstRowid; /* Return no rowids earlier than this */ | |||
| 19374 | i64 iLastRowid; /* Return no rowids later than this */ | |||
| 19375 | sqlite3_stmt *pStmt; /* Statement used to read %_content */ | |||
| 19376 | Fts5Expr *pExpr; /* Expression for MATCH queries */ | |||
| 19377 | Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */ | |||
| 19378 | int csrflags; /* Mask of cursor flags (see below) */ | |||
| 19379 | i64 iSpecial; /* Result of special query */ | |||
| 19380 | ||||
| 19381 | /* "rank" function. Populated on demand from vtab.xColumn(). */ | |||
| 19382 | char *zRank; /* Custom rank function */ | |||
| 19383 | char *zRankArgs; /* Custom rank function args */ | |||
| 19384 | Fts5Auxiliary *pRank; /* Rank callback (or NULL) */ | |||
| 19385 | int nRankArg; /* Number of trailing arguments for rank() */ | |||
| 19386 | sqlite3_value **apRankArg; /* Array of trailing arguments */ | |||
| 19387 | sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */ | |||
| 19388 | ||||
| 19389 | /* Auxiliary data storage */ | |||
| 19390 | Fts5Auxiliary *pAux; /* Currently executing extension function */ | |||
| 19391 | Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ | |||
| 19392 | ||||
| 19393 | /* Cache used by auxiliary API functions xInst() and xInstCount() */ | |||
| 19394 | Fts5PoslistReader *aInstIter; /* One for each phrase */ | |||
| 19395 | int nInstAlloc; /* Size of aInst[] array (entries / 3) */ | |||
| 19396 | int nInstCount; /* Number of phrase instances */ | |||
| 19397 | int *aInst; /* 3 integers per phrase instance */ | |||
| 19398 | }; | |||
| 19399 | ||||
| 19400 | /* | |||
| 19401 | ** Bits that make up the "idxNum" parameter passed indirectly by | |||
| 19402 | ** xBestIndex() to xFilter(). | |||
| 19403 | */ | |||
| 19404 | #define FTS5_BI_MATCH0x0001 0x0001 /* <tbl> MATCH ? */ | |||
| 19405 | #define FTS5_BI_RANK0x0002 0x0002 /* rank MATCH ? */ | |||
| 19406 | #define FTS5_BI_ROWID_EQ0x0004 0x0004 /* rowid == ? */ | |||
| 19407 | #define FTS5_BI_ROWID_LE0x0008 0x0008 /* rowid <= ? */ | |||
| 19408 | #define FTS5_BI_ROWID_GE0x0010 0x0010 /* rowid >= ? */ | |||
| 19409 | ||||
| 19410 | #define FTS5_BI_ORDER_RANK0x0020 0x0020 | |||
| 19411 | #define FTS5_BI_ORDER_ROWID0x0040 0x0040 | |||
| 19412 | #define FTS5_BI_ORDER_DESC0x0080 0x0080 | |||
| 19413 | ||||
| 19414 | /* | |||
| 19415 | ** Values for Fts5Cursor.csrflags | |||
| 19416 | */ | |||
| 19417 | #define FTS5CSR_EOF0x01 0x01 | |||
| 19418 | #define FTS5CSR_REQUIRE_CONTENT0x02 0x02 | |||
| 19419 | #define FTS5CSR_REQUIRE_DOCSIZE0x04 0x04 | |||
| 19420 | #define FTS5CSR_REQUIRE_INST0x08 0x08 | |||
| 19421 | #define FTS5CSR_FREE_ZRANK0x10 0x10 | |||
| 19422 | #define FTS5CSR_REQUIRE_RESEEK0x20 0x20 | |||
| 19423 | #define FTS5CSR_REQUIRE_POSLIST0x40 0x40 | |||
| 19424 | ||||
| 19425 | #define BitFlagAllTest(x,y)(((x) & (y))==(y)) (((x) & (y))==(y)) | |||
| 19426 | #define BitFlagTest(x,y)(((x) & (y))!=0) (((x) & (y))!=0) | |||
| 19427 | ||||
| 19428 | ||||
| 19429 | /* | |||
| 19430 | ** Macros to Set(), Clear() and Test() cursor flags. | |||
| 19431 | */ | |||
| 19432 | #define CsrFlagSet(pCsr, flag)((pCsr)->csrflags |= (flag)) ((pCsr)->csrflags |= (flag)) | |||
| 19433 | #define CsrFlagClear(pCsr, flag)((pCsr)->csrflags &= ~(flag)) ((pCsr)->csrflags &= ~(flag)) | |||
| 19434 | #define CsrFlagTest(pCsr, flag)((pCsr)->csrflags & (flag)) ((pCsr)->csrflags & (flag)) | |||
| 19435 | ||||
| 19436 | struct Fts5Auxdata { | |||
| 19437 | Fts5Auxiliary *pAux; /* Extension to which this belongs */ | |||
| 19438 | void *pPtr; /* Pointer value */ | |||
| 19439 | void(*xDelete)(void*); /* Destructor */ | |||
| 19440 | Fts5Auxdata *pNext; /* Next object in linked list */ | |||
| 19441 | }; | |||
| 19442 | ||||
| 19443 | #ifdef SQLITE_DEBUG | |||
| 19444 | #define FTS5_BEGIN 1 | |||
| 19445 | #define FTS5_SYNC 2 | |||
| 19446 | #define FTS5_COMMIT 3 | |||
| 19447 | #define FTS5_ROLLBACK 4 | |||
| 19448 | #define FTS5_SAVEPOINT 5 | |||
| 19449 | #define FTS5_RELEASE 6 | |||
| 19450 | #define FTS5_ROLLBACKTO 7 | |||
| 19451 | static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ | |||
| 19452 | switch( op ){ | |||
| 19453 | case FTS5_BEGIN: | |||
| 19454 | assert( p->ts.eState==0 )((void) (0)); | |||
| 19455 | p->ts.eState = 1; | |||
| 19456 | p->ts.iSavepoint = -1; | |||
| 19457 | break; | |||
| 19458 | ||||
| 19459 | case FTS5_SYNC: | |||
| 19460 | assert( p->ts.eState==1 || p->ts.eState==2 )((void) (0)); | |||
| 19461 | p->ts.eState = 2; | |||
| 19462 | break; | |||
| 19463 | ||||
| 19464 | case FTS5_COMMIT: | |||
| 19465 | assert( p->ts.eState==2 )((void) (0)); | |||
| 19466 | p->ts.eState = 0; | |||
| 19467 | break; | |||
| 19468 | ||||
| 19469 | case FTS5_ROLLBACK: | |||
| 19470 | assert( p->ts.eState==1 || p->ts.eState==2 || p->ts.eState==0 )((void) (0)); | |||
| 19471 | p->ts.eState = 0; | |||
| 19472 | break; | |||
| 19473 | ||||
| 19474 | case FTS5_SAVEPOINT: | |||
| 19475 | assert( p->ts.eState>=1 )((void) (0)); | |||
| 19476 | assert( iSavepoint>=0 )((void) (0)); | |||
| 19477 | assert( iSavepoint>=p->ts.iSavepoint )((void) (0)); | |||
| 19478 | p->ts.iSavepoint = iSavepoint; | |||
| 19479 | break; | |||
| 19480 | ||||
| 19481 | case FTS5_RELEASE: | |||
| 19482 | assert( p->ts.eState>=1 )((void) (0)); | |||
| 19483 | assert( iSavepoint>=0 )((void) (0)); | |||
| 19484 | assert( iSavepoint<=p->ts.iSavepoint )((void) (0)); | |||
| 19485 | p->ts.iSavepoint = iSavepoint-1; | |||
| 19486 | break; | |||
| 19487 | ||||
| 19488 | case FTS5_ROLLBACKTO: | |||
| 19489 | assert( p->ts.eState>=1 )((void) (0)); | |||
| 19490 | assert( iSavepoint>=-1 )((void) (0)); | |||
| 19491 | /* The following assert() can fail if another vtab strikes an error | |||
| 19492 | ** within an xSavepoint() call then SQLite calls xRollbackTo() - without | |||
| 19493 | ** having called xSavepoint() on this vtab. */ | |||
| 19494 | /* assert( iSavepoint<=p->ts.iSavepoint ); */ | |||
| 19495 | p->ts.iSavepoint = iSavepoint; | |||
| 19496 | break; | |||
| 19497 | } | |||
| 19498 | } | |||
| 19499 | #else | |||
| 19500 | # define fts5CheckTransactionState(x,y,z) | |||
| 19501 | #endif | |||
| 19502 | ||||
| 19503 | /* | |||
| 19504 | ** Return true if pTab is a contentless table. If parameter bIncludeUnindexed | |||
| 19505 | ** is true, this includes contentless tables that store UNINDEXED columns | |||
| 19506 | ** only. | |||
| 19507 | */ | |||
| 19508 | static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){ | |||
| 19509 | int eContent = pTab->p.pConfig->eContent; | |||
| 19510 | return ( | |||
| 19511 | eContent==FTS5_CONTENT_NONE1 | |||
| 19512 | || (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED3) | |||
| 19513 | ); | |||
| 19514 | } | |||
| 19515 | ||||
| 19516 | /* | |||
| 19517 | ** Delete a virtual table handle allocated by fts5InitVtab(). | |||
| 19518 | */ | |||
| 19519 | static void fts5FreeVtab(Fts5FullTable *pTab){ | |||
| 19520 | if( pTab ){ | |||
| 19521 | sqlite3Fts5IndexClose(pTab->p.pIndex); | |||
| 19522 | sqlite3Fts5StorageClose(pTab->pStorage); | |||
| 19523 | sqlite3Fts5ConfigFree(pTab->p.pConfig); | |||
| 19524 | sqlite3_freesqlite3_api->free(pTab); | |||
| 19525 | } | |||
| 19526 | } | |||
| 19527 | ||||
| 19528 | /* | |||
| 19529 | ** The xDisconnect() virtual table method. | |||
| 19530 | */ | |||
| 19531 | static int fts5DisconnectMethod(sqlite3_vtab *pVtab){ | |||
| 19532 | fts5FreeVtab((Fts5FullTable*)pVtab); | |||
| 19533 | return SQLITE_OK0; | |||
| 19534 | } | |||
| 19535 | ||||
| 19536 | /* | |||
| 19537 | ** The xDestroy() virtual table method. | |||
| 19538 | */ | |||
| 19539 | static int fts5DestroyMethod(sqlite3_vtab *pVtab){ | |||
| 19540 | Fts5Table *pTab = (Fts5Table*)pVtab; | |||
| 19541 | int rc = sqlite3Fts5DropAll(pTab->pConfig); | |||
| 19542 | if( rc==SQLITE_OK0 ){ | |||
| 19543 | fts5FreeVtab((Fts5FullTable*)pVtab); | |||
| 19544 | } | |||
| 19545 | return rc; | |||
| 19546 | } | |||
| 19547 | ||||
| 19548 | /* | |||
| 19549 | ** This function is the implementation of both the xConnect and xCreate | |||
| 19550 | ** methods of the FTS3 virtual table. | |||
| 19551 | ** | |||
| 19552 | ** The argv[] array contains the following: | |||
| 19553 | ** | |||
| 19554 | ** argv[0] -> module name ("fts5") | |||
| 19555 | ** argv[1] -> database name | |||
| 19556 | ** argv[2] -> table name | |||
| 19557 | ** argv[...] -> "column name" and other module argument fields. | |||
| 19558 | */ | |||
| 19559 | static int fts5InitVtab( | |||
| 19560 | int bCreate, /* True for xCreate, false for xConnect */ | |||
| 19561 | sqlite3 *db, /* The SQLite database connection */ | |||
| 19562 | void *pAux, /* Hash table containing tokenizers */ | |||
| 19563 | int argc, /* Number of elements in argv array */ | |||
| 19564 | const char * const *argv, /* xCreate/xConnect argument array */ | |||
| 19565 | sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ | |||
| 19566 | char **pzErr /* Write any error message here */ | |||
| 19567 | ){ | |||
| 19568 | Fts5Global *pGlobal = (Fts5Global*)pAux; | |||
| 19569 | const char **azConfig = (const char**)argv; | |||
| 19570 | int rc = SQLITE_OK0; /* Return code */ | |||
| 19571 | Fts5Config *pConfig = 0; /* Results of parsing argc/argv */ | |||
| 19572 | Fts5FullTable *pTab = 0; /* New virtual table object */ | |||
| 19573 | ||||
| 19574 | /* Allocate the new vtab object and parse the configuration */ | |||
| 19575 | pTab = (Fts5FullTable*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5FullTable)); | |||
| 19576 | if( rc==SQLITE_OK0 ){ | |||
| 19577 | rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr); | |||
| 19578 | assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 )((void) (0)); | |||
| 19579 | } | |||
| 19580 | if( rc==SQLITE_OK0 ){ | |||
| 19581 | pConfig->pzErrmsg = pzErr; | |||
| 19582 | pTab->p.pConfig = pConfig; | |||
| 19583 | pTab->pGlobal = pGlobal; | |||
| 19584 | if( bCreate || sqlite3Fts5TokenizerPreload(&pConfig->t) ){ | |||
| 19585 | rc = sqlite3Fts5LoadTokenizer(pConfig); | |||
| 19586 | } | |||
| 19587 | } | |||
| 19588 | ||||
| 19589 | /* Open the index sub-system */ | |||
| 19590 | if( rc==SQLITE_OK0 ){ | |||
| 19591 | rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->p.pIndex, pzErr); | |||
| 19592 | } | |||
| 19593 | ||||
| 19594 | /* Open the storage sub-system */ | |||
| 19595 | if( rc==SQLITE_OK0 ){ | |||
| 19596 | rc = sqlite3Fts5StorageOpen( | |||
| 19597 | pConfig, pTab->p.pIndex, bCreate, &pTab->pStorage, pzErr | |||
| 19598 | ); | |||
| 19599 | } | |||
| 19600 | ||||
| 19601 | /* Call sqlite3_declare_vtab() */ | |||
| 19602 | if( rc==SQLITE_OK0 ){ | |||
| 19603 | rc = sqlite3Fts5ConfigDeclareVtab(pConfig); | |||
| 19604 | } | |||
| 19605 | ||||
| 19606 | /* Load the initial configuration */ | |||
| 19607 | if( rc==SQLITE_OK0 ){ | |||
| 19608 | rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie-1); | |||
| 19609 | } | |||
| 19610 | ||||
| 19611 | if( rc==SQLITE_OK0 && pConfig->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 19612 | rc = sqlite3_vtab_configsqlite3_api->vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT1, (int)1); | |||
| 19613 | } | |||
| 19614 | if( rc==SQLITE_OK0 ){ | |||
| 19615 | rc = sqlite3_vtab_configsqlite3_api->vtab_config(db, SQLITE_VTAB_INNOCUOUS2); | |||
| 19616 | } | |||
| 19617 | ||||
| 19618 | if( pConfig ) pConfig->pzErrmsg = 0; | |||
| 19619 | if( rc!=SQLITE_OK0 ){ | |||
| 19620 | fts5FreeVtab(pTab); | |||
| 19621 | pTab = 0; | |||
| 19622 | }else if( bCreate ){ | |||
| 19623 | fts5CheckTransactionState(pTab, FTS5_BEGIN, 0); | |||
| 19624 | } | |||
| 19625 | *ppVTab = (sqlite3_vtab*)pTab; | |||
| 19626 | return rc; | |||
| 19627 | } | |||
| 19628 | ||||
| 19629 | /* | |||
| 19630 | ** The xConnect() and xCreate() methods for the virtual table. All the | |||
| 19631 | ** work is done in function fts5InitVtab(). | |||
| 19632 | */ | |||
| 19633 | static int fts5ConnectMethod( | |||
| 19634 | sqlite3 *db, /* Database connection */ | |||
| 19635 | void *pAux, /* Pointer to tokenizer hash table */ | |||
| 19636 | int argc, /* Number of elements in argv array */ | |||
| 19637 | const char * const *argv, /* xCreate/xConnect argument array */ | |||
| 19638 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ | |||
| 19639 | char **pzErr /* OUT: sqlite3_malloc'd error message */ | |||
| 19640 | ){ | |||
| 19641 | return fts5InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr); | |||
| 19642 | } | |||
| 19643 | static int fts5CreateMethod( | |||
| 19644 | sqlite3 *db, /* Database connection */ | |||
| 19645 | void *pAux, /* Pointer to tokenizer hash table */ | |||
| 19646 | int argc, /* Number of elements in argv array */ | |||
| 19647 | const char * const *argv, /* xCreate/xConnect argument array */ | |||
| 19648 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ | |||
| 19649 | char **pzErr /* OUT: sqlite3_malloc'd error message */ | |||
| 19650 | ){ | |||
| 19651 | return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); | |||
| 19652 | } | |||
| 19653 | ||||
| 19654 | /* | |||
| 19655 | ** The different query plans. | |||
| 19656 | */ | |||
| 19657 | #define FTS5_PLAN_MATCH1 1 /* (<tbl> MATCH ?) */ | |||
| 19658 | #define FTS5_PLAN_SOURCE2 2 /* A source cursor for SORTED_MATCH */ | |||
| 19659 | #define FTS5_PLAN_SPECIAL3 3 /* An internal query */ | |||
| 19660 | #define FTS5_PLAN_SORTED_MATCH4 4 /* (<tbl> MATCH ? ORDER BY rank) */ | |||
| 19661 | #define FTS5_PLAN_SCAN5 5 /* No usable constraint */ | |||
| 19662 | #define FTS5_PLAN_ROWID6 6 /* (rowid = ?) */ | |||
| 19663 | ||||
| 19664 | /* | |||
| 19665 | ** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this | |||
| 19666 | ** extension is currently being used by a version of SQLite too old to | |||
| 19667 | ** support index-info flags. In that case this function is a no-op. | |||
| 19668 | */ | |||
| 19669 | static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ | |||
| 19670 | #if SQLITE_VERSION_NUMBER3053002>=3008012 | |||
| 19671 | #ifndef SQLITE_CORE | |||
| 19672 | if( sqlite3_libversion_numbersqlite3_api->libversion_number()>=3008012 ) | |||
| 19673 | #endif | |||
| 19674 | { | |||
| 19675 | pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE0x00000001; | |||
| 19676 | } | |||
| 19677 | #endif | |||
| 19678 | } | |||
| 19679 | ||||
| 19680 | static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){ | |||
| 19681 | #if SQLITE_VERSION_NUMBER3053002>=3008002 | |||
| 19682 | #ifndef SQLITE_CORE | |||
| 19683 | if( sqlite3_libversion_numbersqlite3_api->libversion_number()>=3008002 ) | |||
| 19684 | #endif | |||
| 19685 | { | |||
| 19686 | pIdxInfo->estimatedRows = MAX(1, nRow)(((1) > (nRow)) ? (1) : (nRow)); | |||
| 19687 | } | |||
| 19688 | #endif | |||
| 19689 | } | |||
| 19690 | ||||
| 19691 | static int fts5UsePatternMatch( | |||
| 19692 | Fts5Config *pConfig, | |||
| 19693 | struct sqlite3_index_constraint *p | |||
| 19694 | ){ | |||
| 19695 | assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB )((void) (0)); | |||
| 19696 | assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE )((void) (0)); | |||
| 19697 | if( pConfig->t.ePattern==FTS5_PATTERN_GLOB66 && p->op==FTS5_PATTERN_GLOB66 ){ | |||
| 19698 | return 1; | |||
| 19699 | } | |||
| 19700 | if( pConfig->t.ePattern==FTS5_PATTERN_LIKE65 | |||
| 19701 | && (p->op==FTS5_PATTERN_LIKE65 || p->op==FTS5_PATTERN_GLOB66) | |||
| 19702 | ){ | |||
| 19703 | return 1; | |||
| 19704 | } | |||
| 19705 | return 0; | |||
| 19706 | } | |||
| 19707 | ||||
| 19708 | /* | |||
| 19709 | ** Implementation of the xBestIndex method for FTS5 tables. Within the | |||
| 19710 | ** WHERE constraint, it searches for the following: | |||
| 19711 | ** | |||
| 19712 | ** 1. A MATCH constraint against the table column. | |||
| 19713 | ** 2. A MATCH constraint against the "rank" column. | |||
| 19714 | ** 3. A MATCH constraint against some other column. | |||
| 19715 | ** 4. An == constraint against the rowid column. | |||
| 19716 | ** 5. A < or <= constraint against the rowid column. | |||
| 19717 | ** 6. A > or >= constraint against the rowid column. | |||
| 19718 | ** | |||
| 19719 | ** Within the ORDER BY, the following are supported: | |||
| 19720 | ** | |||
| 19721 | ** 5. ORDER BY rank [ASC|DESC] | |||
| 19722 | ** 6. ORDER BY rowid [ASC|DESC] | |||
| 19723 | ** | |||
| 19724 | ** Information for the xFilter call is passed via both the idxNum and | |||
| 19725 | ** idxStr variables. Specifically, idxNum is a bitmask of the following | |||
| 19726 | ** flags used to encode the ORDER BY clause: | |||
| 19727 | ** | |||
| 19728 | ** FTS5_BI_ORDER_RANK | |||
| 19729 | ** FTS5_BI_ORDER_ROWID | |||
| 19730 | ** FTS5_BI_ORDER_DESC | |||
| 19731 | ** | |||
| 19732 | ** idxStr is used to encode data from the WHERE clause. For each argument | |||
| 19733 | ** passed to the xFilter method, the following is appended to idxStr: | |||
| 19734 | ** | |||
| 19735 | ** Match against table column: "m" | |||
| 19736 | ** Match against rank column: "r" | |||
| 19737 | ** Match against other column: "M<column-number>" | |||
| 19738 | ** LIKE against other column: "L<column-number>" | |||
| 19739 | ** GLOB against other column: "G<column-number>" | |||
| 19740 | ** Equality constraint against the rowid: "=" | |||
| 19741 | ** A < or <= against the rowid: "<" | |||
| 19742 | ** A > or >= against the rowid: ">" | |||
| 19743 | ** | |||
| 19744 | ** This function ensures that there is at most one "r" or "=". And that if | |||
| 19745 | ** there exists an "=" then there is no "<" or ">". | |||
| 19746 | ** | |||
| 19747 | ** If an unusable MATCH operator is present in the WHERE clause, then | |||
| 19748 | ** SQLITE_CONSTRAINT is returned. | |||
| 19749 | ** | |||
| 19750 | ** Costs are assigned as follows: | |||
| 19751 | ** | |||
| 19752 | ** a) If a MATCH operator is present, the cost depends on the other | |||
| 19753 | ** constraints also present. As follows: | |||
| 19754 | ** | |||
| 19755 | ** * No other constraints: cost=50000.0 | |||
| 19756 | ** * One rowid range constraint: cost=37500.0 | |||
| 19757 | ** * Both rowid range constraints: cost=30000.0 | |||
| 19758 | ** * An == rowid constraint: cost=25000.0 | |||
| 19759 | ** | |||
| 19760 | ** b) Otherwise, if there is no MATCH: | |||
| 19761 | ** | |||
| 19762 | ** * No other constraints: cost=3000000.0 | |||
| 19763 | ** * One rowid range constraints: cost=2250000.0 | |||
| 19764 | ** * Both rowid range constraint: cost=750000.0 | |||
| 19765 | ** * An == rowid constraint: cost=25.0 | |||
| 19766 | ** | |||
| 19767 | ** Costs are not modified by the ORDER BY clause. | |||
| 19768 | ** | |||
| 19769 | ** The ratios used in case (a) are based on informal results obtained from | |||
| 19770 | ** the tool/fts5cost.tcl script. The "MATCH and ==" combination has the | |||
| 19771 | ** cost set quite high because the query may be a prefix query. Unless | |||
| 19772 | ** there is a prefix index, prefix queries with rowid constraints are much | |||
| 19773 | ** more expensive than non-prefix queries with rowid constraints. | |||
| 19774 | ** | |||
| 19775 | ** The estimated rows returned is set to the cost/40. For simple queries, | |||
| 19776 | ** experimental results show that cost/4 might be about right. But for | |||
| 19777 | ** more complex queries that use multiple terms the number of rows might | |||
| 19778 | ** be far fewer than this. So we compromise and use cost/40. | |||
| 19779 | */ | |||
| 19780 | static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ | |||
| 19781 | Fts5Table *pTab = (Fts5Table*)pVTab; | |||
| 19782 | Fts5Config *pConfig = pTab->pConfig; | |||
| 19783 | const int nCol = pConfig->nCol; | |||
| 19784 | int idxFlags = 0; /* Parameter passed through to xFilter() */ | |||
| 19785 | int i; | |||
| 19786 | ||||
| 19787 | char *idxStr; | |||
| 19788 | int iIdxStr = 0; | |||
| 19789 | int iCons = 0; | |||
| 19790 | ||||
| 19791 | int bSeenEq = 0; | |||
| 19792 | int bSeenGt = 0; | |||
| 19793 | int bSeenLt = 0; | |||
| 19794 | int nSeenMatch = 0; | |||
| 19795 | int bSeenRank = 0; | |||
| 19796 | ||||
| 19797 | ||||
| 19798 | assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH )((void) (0)); | |||
| 19799 | assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH )((void) (0)); | |||
| 19800 | assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH )((void) (0)); | |||
| 19801 | assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH )((void) (0)); | |||
| 19802 | assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH )((void) (0)); | |||
| 19803 | ||||
| 19804 | if( pConfig->bLock ){ | |||
| 19805 | pTab->base.zErrMsg = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 19806 | "recursively defined fts5 content table" | |||
| 19807 | ); | |||
| 19808 | return SQLITE_ERROR1; | |||
| 19809 | } | |||
| 19810 | ||||
| 19811 | idxStr = (char*)sqlite3_malloc64sqlite3_api->malloc64((i64)pInfo->nConstraint * 8 + 1); | |||
| 19812 | if( idxStr==0 ) return SQLITE_NOMEM7; | |||
| 19813 | pInfo->idxStr = idxStr; | |||
| 19814 | pInfo->needToFreeIdxStr = 1; | |||
| 19815 | ||||
| 19816 | for(i=0; i<pInfo->nConstraint; i++){ | |||
| 19817 | struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; | |||
| 19818 | int iCol = p->iColumn; | |||
| 19819 | if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH64 | |||
| 19820 | || (p->op==SQLITE_INDEX_CONSTRAINT_EQ2 && iCol>=nCol) | |||
| 19821 | ){ | |||
| 19822 | /* A MATCH operator or equivalent */ | |||
| 19823 | if( p->usable==0 || iCol<0 ){ | |||
| 19824 | /* As there exists an unusable MATCH constraint this is an | |||
| 19825 | ** unusable plan. Return SQLITE_CONSTRAINT. */ | |||
| 19826 | idxStr[iIdxStr] = 0; | |||
| 19827 | return SQLITE_CONSTRAINT19; | |||
| 19828 | }else{ | |||
| 19829 | if( iCol==nCol+1 ){ | |||
| 19830 | if( bSeenRank ) continue; | |||
| 19831 | idxStr[iIdxStr++] = 'r'; | |||
| 19832 | bSeenRank = 1; | |||
| 19833 | }else{ | |||
| 19834 | nSeenMatch++; | |||
| 19835 | idxStr[iIdxStr++] = 'M'; | |||
| 19836 | sqlite3_snprintfsqlite3_api->xsnprintf(6, &idxStr[iIdxStr], "%d", iCol); | |||
| 19837 | iIdxStr += (int)strlen(&idxStr[iIdxStr]); | |||
| 19838 | assert( idxStr[iIdxStr]=='\0' )((void) (0)); | |||
| 19839 | } | |||
| 19840 | pInfo->aConstraintUsage[i].argvIndex = ++iCons; | |||
| 19841 | pInfo->aConstraintUsage[i].omit = 1; | |||
| 19842 | } | |||
| 19843 | }else if( p->usable ){ | |||
| 19844 | if( iCol>=0 && iCol<nCol && fts5UsePatternMatch(pConfig, p) ){ | |||
| 19845 | assert( p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB )((void) (0)); | |||
| 19846 | idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE65 ? 'L' : 'G'; | |||
| 19847 | sqlite3_snprintfsqlite3_api->xsnprintf(6, &idxStr[iIdxStr], "%d", iCol); | |||
| 19848 | idxStr += strlen(&idxStr[iIdxStr]); | |||
| 19849 | pInfo->aConstraintUsage[i].argvIndex = ++iCons; | |||
| 19850 | assert( idxStr[iIdxStr]=='\0' )((void) (0)); | |||
| 19851 | nSeenMatch++; | |||
| 19852 | }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ2 && iCol<0 ){ | |||
| 19853 | idxStr[iIdxStr++] = '='; | |||
| 19854 | bSeenEq = 1; | |||
| 19855 | pInfo->aConstraintUsage[i].argvIndex = ++iCons; | |||
| 19856 | pInfo->aConstraintUsage[i].omit = 1; | |||
| 19857 | } | |||
| 19858 | } | |||
| 19859 | } | |||
| 19860 | ||||
| 19861 | if( bSeenEq==0 ){ | |||
| 19862 | for(i=0; i<pInfo->nConstraint; i++){ | |||
| 19863 | struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; | |||
| 19864 | if( p->iColumn<0 && p->usable ){ | |||
| 19865 | int op = p->op; | |||
| 19866 | if( op==SQLITE_INDEX_CONSTRAINT_LT16 || op==SQLITE_INDEX_CONSTRAINT_LE8 ){ | |||
| 19867 | if( bSeenLt ) continue; | |||
| 19868 | idxStr[iIdxStr++] = '<'; | |||
| 19869 | pInfo->aConstraintUsage[i].argvIndex = ++iCons; | |||
| 19870 | bSeenLt = 1; | |||
| 19871 | }else | |||
| 19872 | if( op==SQLITE_INDEX_CONSTRAINT_GT4 || op==SQLITE_INDEX_CONSTRAINT_GE32 ){ | |||
| 19873 | if( bSeenGt ) continue; | |||
| 19874 | idxStr[iIdxStr++] = '>'; | |||
| 19875 | pInfo->aConstraintUsage[i].argvIndex = ++iCons; | |||
| 19876 | bSeenGt = 1; | |||
| 19877 | } | |||
| 19878 | } | |||
| 19879 | } | |||
| 19880 | } | |||
| 19881 | idxStr[iIdxStr] = '\0'; | |||
| 19882 | ||||
| 19883 | /* Set idxFlags flags for the ORDER BY clause | |||
| 19884 | ** | |||
| 19885 | ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC". | |||
| 19886 | */ | |||
| 19887 | if( pInfo->nOrderBy==1 ){ | |||
| 19888 | int iSort = pInfo->aOrderBy[0].iColumn; | |||
| 19889 | if( iSort==(pConfig->nCol+1) && nSeenMatch>0 ){ | |||
| 19890 | idxFlags |= FTS5_BI_ORDER_RANK0x0020; | |||
| 19891 | }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){ | |||
| 19892 | idxFlags |= FTS5_BI_ORDER_ROWID0x0040; | |||
| 19893 | } | |||
| 19894 | if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID)(((idxFlags) & (0x0020|0x0040))!=0) ){ | |||
| 19895 | pInfo->orderByConsumed = 1; | |||
| 19896 | if( pInfo->aOrderBy[0].desc ){ | |||
| 19897 | idxFlags |= FTS5_BI_ORDER_DESC0x0080; | |||
| 19898 | } | |||
| 19899 | } | |||
| 19900 | } | |||
| 19901 | ||||
| 19902 | /* Calculate the estimated cost based on the flags set in idxFlags. */ | |||
| 19903 | if( bSeenEq ){ | |||
| 19904 | pInfo->estimatedCost = nSeenMatch ? 25000.0 : 25.0; | |||
| 19905 | fts5SetEstimatedRows(pInfo, 1); | |||
| 19906 | fts5SetUniqueFlag(pInfo); | |||
| 19907 | }else{ | |||
| 19908 | i64 nEstRows; | |||
| 19909 | if( nSeenMatch ){ | |||
| 19910 | if( bSeenLt && bSeenGt ){ | |||
| 19911 | pInfo->estimatedCost = 50000.0; | |||
| 19912 | }else if( bSeenLt || bSeenGt ){ | |||
| 19913 | pInfo->estimatedCost = 37500.0; | |||
| 19914 | }else{ | |||
| 19915 | pInfo->estimatedCost = 50000.0; | |||
| 19916 | } | |||
| 19917 | nEstRows = (i64)(pInfo->estimatedCost / 40.0); | |||
| 19918 | for(i=1; i<nSeenMatch; i++){ | |||
| 19919 | pInfo->estimatedCost *= 2.5; | |||
| 19920 | nEstRows = nEstRows / 2; | |||
| 19921 | } | |||
| 19922 | }else{ | |||
| 19923 | if( bSeenLt && bSeenGt ){ | |||
| 19924 | pInfo->estimatedCost = 750000.0; | |||
| 19925 | }else if( bSeenLt || bSeenGt ){ | |||
| 19926 | pInfo->estimatedCost = 2250000.0; | |||
| 19927 | }else{ | |||
| 19928 | pInfo->estimatedCost = 3000000.0; | |||
| 19929 | } | |||
| 19930 | nEstRows = (i64)(pInfo->estimatedCost / 4.0); | |||
| 19931 | } | |||
| 19932 | fts5SetEstimatedRows(pInfo, nEstRows); | |||
| 19933 | } | |||
| 19934 | ||||
| 19935 | pInfo->idxNum = idxFlags; | |||
| 19936 | return SQLITE_OK0; | |||
| 19937 | } | |||
| 19938 | ||||
| 19939 | static int fts5NewTransaction(Fts5FullTable *pTab){ | |||
| 19940 | Fts5Cursor *pCsr; | |||
| 19941 | for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ | |||
| 19942 | if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK0; | |||
| 19943 | } | |||
| 19944 | return sqlite3Fts5StorageReset(pTab->pStorage); | |||
| 19945 | } | |||
| 19946 | ||||
| 19947 | /* | |||
| 19948 | ** Implementation of xOpen method. | |||
| 19949 | */ | |||
| 19950 | static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ | |||
| 19951 | Fts5FullTable *pTab = (Fts5FullTable*)pVTab; | |||
| 19952 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 19953 | Fts5Cursor *pCsr = 0; /* New cursor object */ | |||
| 19954 | sqlite3_int64 nByte; /* Bytes of space to allocate */ | |||
| 19955 | int rc; /* Return code */ | |||
| 19956 | ||||
| 19957 | rc = fts5NewTransaction(pTab); | |||
| 19958 | if( rc==SQLITE_OK0 ){ | |||
| 19959 | nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int); | |||
| 19960 | pCsr = (Fts5Cursor*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 19961 | if( pCsr ){ | |||
| 19962 | Fts5Global *pGlobal = pTab->pGlobal; | |||
| 19963 | memset(pCsr, 0, (size_t)nByte); | |||
| 19964 | pCsr->aColumnSize = (int*)&pCsr[1]; | |||
| 19965 | pCsr->pNext = pGlobal->pCsr; | |||
| 19966 | pGlobal->pCsr = pCsr; | |||
| 19967 | pCsr->iCsrId = ++pGlobal->iNextId; | |||
| 19968 | }else{ | |||
| 19969 | rc = SQLITE_NOMEM7; | |||
| 19970 | } | |||
| 19971 | } | |||
| 19972 | *ppCsr = (sqlite3_vtab_cursor*)pCsr; | |||
| 19973 | return rc; | |||
| 19974 | } | |||
| 19975 | ||||
| 19976 | static int fts5StmtType(Fts5Cursor *pCsr){ | |||
| 19977 | if( pCsr->ePlan==FTS5_PLAN_SCAN5 ){ | |||
| 19978 | return (pCsr->bDesc) ? FTS5_STMT_SCAN_DESC1 : FTS5_STMT_SCAN_ASC0; | |||
| 19979 | } | |||
| 19980 | return FTS5_STMT_LOOKUP2; | |||
| 19981 | } | |||
| 19982 | ||||
| 19983 | /* | |||
| 19984 | ** This function is called after the cursor passed as the only argument | |||
| 19985 | ** is moved to point at a different row. It clears all cached data | |||
| 19986 | ** specific to the previous row stored by the cursor object. | |||
| 19987 | */ | |||
| 19988 | static void fts5CsrNewrow(Fts5Cursor *pCsr){ | |||
| 19989 | CsrFlagSet(pCsr,((pCsr)->csrflags |= (0x02 | 0x04 | 0x08 | 0x40)) | |||
| 19990 | FTS5CSR_REQUIRE_CONTENT((pCsr)->csrflags |= (0x02 | 0x04 | 0x08 | 0x40)) | |||
| 19991 | | FTS5CSR_REQUIRE_DOCSIZE((pCsr)->csrflags |= (0x02 | 0x04 | 0x08 | 0x40)) | |||
| 19992 | | FTS5CSR_REQUIRE_INST((pCsr)->csrflags |= (0x02 | 0x04 | 0x08 | 0x40)) | |||
| 19993 | | FTS5CSR_REQUIRE_POSLIST((pCsr)->csrflags |= (0x02 | 0x04 | 0x08 | 0x40)) | |||
| 19994 | )((pCsr)->csrflags |= (0x02 | 0x04 | 0x08 | 0x40)); | |||
| 19995 | } | |||
| 19996 | ||||
| 19997 | static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ | |||
| 19998 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 19999 | Fts5Auxdata *pData; | |||
| 20000 | Fts5Auxdata *pNext; | |||
| 20001 | ||||
| 20002 | sqlite3_freesqlite3_api->free(pCsr->aInstIter); | |||
| 20003 | sqlite3_freesqlite3_api->free(pCsr->aInst); | |||
| 20004 | if( pCsr->pStmt ){ | |||
| 20005 | int eStmt = fts5StmtType(pCsr); | |||
| 20006 | sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt); | |||
| 20007 | } | |||
| 20008 | if( pCsr->pSorter ){ | |||
| 20009 | Fts5Sorter *pSorter = pCsr->pSorter; | |||
| 20010 | sqlite3_finalizesqlite3_api->finalize(pSorter->pStmt); | |||
| 20011 | sqlite3_freesqlite3_api->free(pSorter); | |||
| 20012 | } | |||
| 20013 | ||||
| 20014 | if( pCsr->ePlan!=FTS5_PLAN_SOURCE2 ){ | |||
| 20015 | sqlite3Fts5ExprFree(pCsr->pExpr); | |||
| 20016 | } | |||
| 20017 | ||||
| 20018 | for(pData=pCsr->pAuxdata; pData; pData=pNext){ | |||
| 20019 | pNext = pData->pNext; | |||
| 20020 | if( pData->xDelete ) pData->xDelete(pData->pPtr); | |||
| 20021 | sqlite3_freesqlite3_api->free(pData); | |||
| 20022 | } | |||
| 20023 | ||||
| 20024 | sqlite3_finalizesqlite3_api->finalize(pCsr->pRankArgStmt); | |||
| 20025 | sqlite3_freesqlite3_api->free(pCsr->apRankArg); | |||
| 20026 | ||||
| 20027 | if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK)((pCsr)->csrflags & (0x10)) ){ | |||
| 20028 | sqlite3_freesqlite3_api->free(pCsr->zRank); | |||
| 20029 | sqlite3_freesqlite3_api->free(pCsr->zRankArgs); | |||
| 20030 | } | |||
| 20031 | ||||
| 20032 | sqlite3Fts5IndexCloseReader(pTab->p.pIndex); | |||
| 20033 | memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr)); | |||
| 20034 | } | |||
| 20035 | ||||
| 20036 | ||||
| 20037 | /* | |||
| 20038 | ** Close the cursor. For additional information see the documentation | |||
| 20039 | ** on the xClose method of the virtual table interface. | |||
| 20040 | */ | |||
| 20041 | static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ | |||
| 20042 | if( pCursor ){ | |||
| 20043 | Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); | |||
| 20044 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |||
| 20045 | Fts5Cursor **pp; | |||
| 20046 | ||||
| 20047 | fts5FreeCursorComponents(pCsr); | |||
| 20048 | /* Remove the cursor from the Fts5Global.pCsr list */ | |||
| 20049 | for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext); | |||
| 20050 | *pp = pCsr->pNext; | |||
| 20051 | ||||
| 20052 | sqlite3_freesqlite3_api->free(pCsr); | |||
| 20053 | } | |||
| 20054 | return SQLITE_OK0; | |||
| 20055 | } | |||
| 20056 | ||||
| 20057 | static int fts5SorterNext(Fts5Cursor *pCsr){ | |||
| 20058 | Fts5Sorter *pSorter = pCsr->pSorter; | |||
| 20059 | int rc; | |||
| 20060 | ||||
| 20061 | rc = sqlite3_stepsqlite3_api->step(pSorter->pStmt); | |||
| 20062 | if( rc==SQLITE_DONE101 ){ | |||
| 20063 | rc = SQLITE_OK0; | |||
| 20064 | CsrFlagSet(pCsr, FTS5CSR_EOF|FTS5CSR_REQUIRE_CONTENT)((pCsr)->csrflags |= (0x01|0x02)); | |||
| 20065 | }else if( rc==SQLITE_ROW100 ){ | |||
| 20066 | const u8 *a; | |||
| 20067 | const u8 *aBlob; | |||
| 20068 | int nBlob; | |||
| 20069 | int i; | |||
| 20070 | int iOff = 0; | |||
| 20071 | rc = SQLITE_OK0; | |||
| 20072 | ||||
| 20073 | pSorter->iRowid = sqlite3_column_int64sqlite3_api->column_int64(pSorter->pStmt, 0); | |||
| 20074 | nBlob = sqlite3_column_bytessqlite3_api->column_bytes(pSorter->pStmt, 1); | |||
| 20075 | aBlob = a = sqlite3_column_blobsqlite3_api->column_blob(pSorter->pStmt, 1); | |||
| 20076 | ||||
| 20077 | /* nBlob==0 in detail=none mode. */ | |||
| 20078 | if( nBlob>0 ){ | |||
| 20079 | for(i=0; i<(pSorter->nIdx-1); i++){ | |||
| 20080 | int iVal; | |||
| 20081 | a += fts5GetVarint32(a, iVal)sqlite3Fts5GetVarint32(a,(u32*)&(iVal)); | |||
| 20082 | iOff += iVal; | |||
| 20083 | pSorter->aIdx[i] = iOff; | |||
| 20084 | } | |||
| 20085 | pSorter->aIdx[i] = &aBlob[nBlob] - a; | |||
| 20086 | pSorter->aPoslist = a; | |||
| 20087 | } | |||
| 20088 | ||||
| 20089 | fts5CsrNewrow(pCsr); | |||
| 20090 | } | |||
| 20091 | ||||
| 20092 | return rc; | |||
| 20093 | } | |||
| 20094 | ||||
| 20095 | ||||
| 20096 | /* | |||
| 20097 | ** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors | |||
| 20098 | ** open on table pTab. | |||
| 20099 | */ | |||
| 20100 | static void fts5TripCursors(Fts5FullTable *pTab){ | |||
| 20101 | Fts5Cursor *pCsr; | |||
| 20102 | for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ | |||
| 20103 | if( pCsr->ePlan==FTS5_PLAN_MATCH1 | |||
| 20104 | && pCsr->base.pVtab==(sqlite3_vtab*)pTab | |||
| 20105 | ){ | |||
| 20106 | CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK)((pCsr)->csrflags |= (0x20)); | |||
| 20107 | } | |||
| 20108 | } | |||
| 20109 | } | |||
| 20110 | ||||
| 20111 | /* | |||
| 20112 | ** If the REQUIRE_RESEEK flag is set on the cursor passed as the first | |||
| 20113 | ** argument, close and reopen all Fts5IndexIter iterators that the cursor | |||
| 20114 | ** is using. Then attempt to move the cursor to a rowid equal to or laster | |||
| 20115 | ** (in the cursors sort order - ASC or DESC) than the current rowid. | |||
| 20116 | ** | |||
| 20117 | ** If the new rowid is not equal to the old, set output parameter *pbSkip | |||
| 20118 | ** to 1 before returning. Otherwise, leave it unchanged. | |||
| 20119 | ** | |||
| 20120 | ** Return SQLITE_OK if successful or if no reseek was required, or an | |||
| 20121 | ** error code if an error occurred. | |||
| 20122 | */ | |||
| 20123 | static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ | |||
| 20124 | int rc = SQLITE_OK0; | |||
| 20125 | assert( *pbSkip==0 )((void) (0)); | |||
| 20126 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK)((pCsr)->csrflags & (0x20)) ){ | |||
| 20127 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 20128 | int bDesc = pCsr->bDesc; | |||
| 20129 | i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr); | |||
| 20130 | ||||
| 20131 | rc = sqlite3Fts5ExprFirst( | |||
| 20132 | pCsr->pExpr, pTab->p.pIndex, iRowid, pCsr->iLastRowid, bDesc | |||
| 20133 | ); | |||
| 20134 | if( rc==SQLITE_OK0 && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){ | |||
| 20135 | *pbSkip = 1; | |||
| 20136 | } | |||
| 20137 | ||||
| 20138 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK)((pCsr)->csrflags &= ~(0x20)); | |||
| 20139 | fts5CsrNewrow(pCsr); | |||
| 20140 | if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ | |||
| 20141 | CsrFlagSet(pCsr, FTS5CSR_EOF)((pCsr)->csrflags |= (0x01)); | |||
| 20142 | *pbSkip = 1; | |||
| 20143 | } | |||
| 20144 | } | |||
| 20145 | return rc; | |||
| 20146 | } | |||
| 20147 | ||||
| 20148 | ||||
| 20149 | /* | |||
| 20150 | ** Advance the cursor to the next row in the table that matches the | |||
| 20151 | ** search criteria. | |||
| 20152 | ** | |||
| 20153 | ** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned | |||
| 20154 | ** even if we reach end-of-file. The fts5EofMethod() will be called | |||
| 20155 | ** subsequently to determine whether or not an EOF was hit. | |||
| 20156 | */ | |||
| 20157 | static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ | |||
| 20158 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |||
| 20159 | int rc; | |||
| 20160 | ||||
| 20161 | assert( (pCsr->ePlan<3)==((void) (0)) | |||
| 20162 | (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)((void) (0)) | |||
| 20163 | )((void) (0)); | |||
| 20164 | assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) )((void) (0)); | |||
| 20165 | ||||
| 20166 | /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table, | |||
| 20167 | ** clear any token mappings accumulated at the fts5_index.c level. In | |||
| 20168 | ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH, | |||
| 20169 | ** we need to retain the mappings for the entire query. */ | |||
| 20170 | if( pCsr->ePlan==FTS5_PLAN_MATCH1 | |||
| 20171 | && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata | |||
| 20172 | ){ | |||
| 20173 | sqlite3Fts5ExprClearTokens(pCsr->pExpr); | |||
| 20174 | } | |||
| 20175 | ||||
| 20176 | if( pCsr->ePlan<3 ){ | |||
| 20177 | int bSkip = 0; | |||
| 20178 | if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; | |||
| 20179 | rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid); | |||
| 20180 | CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr))((pCsr)->csrflags |= (sqlite3Fts5ExprEof(pCsr->pExpr))); | |||
| 20181 | fts5CsrNewrow(pCsr); | |||
| 20182 | }else{ | |||
| 20183 | switch( pCsr->ePlan ){ | |||
| 20184 | case FTS5_PLAN_SPECIAL3: { | |||
| 20185 | CsrFlagSet(pCsr, FTS5CSR_EOF)((pCsr)->csrflags |= (0x01)); | |||
| 20186 | rc = SQLITE_OK0; | |||
| 20187 | break; | |||
| 20188 | } | |||
| 20189 | ||||
| 20190 | case FTS5_PLAN_SORTED_MATCH4: { | |||
| 20191 | rc = fts5SorterNext(pCsr); | |||
| 20192 | break; | |||
| 20193 | } | |||
| 20194 | ||||
| 20195 | default: { | |||
| 20196 | Fts5Config *pConfig = ((Fts5Table*)pCursor->pVtab)->pConfig; | |||
| 20197 | pConfig->bLock++; | |||
| 20198 | rc = sqlite3_stepsqlite3_api->step(pCsr->pStmt); | |||
| 20199 | pConfig->bLock--; | |||
| 20200 | if( rc!=SQLITE_ROW100 ){ | |||
| 20201 | CsrFlagSet(pCsr, FTS5CSR_EOF)((pCsr)->csrflags |= (0x01)); | |||
| 20202 | rc = sqlite3_resetsqlite3_api->reset(pCsr->pStmt); | |||
| 20203 | if( rc!=SQLITE_OK0 ){ | |||
| 20204 | pCursor->pVtab->zErrMsg = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 20205 | "%s", sqlite3_errmsgsqlite3_api->errmsg(pConfig->db) | |||
| 20206 | ); | |||
| 20207 | } | |||
| 20208 | }else{ | |||
| 20209 | rc = SQLITE_OK0; | |||
| 20210 | CsrFlagSet(pCsr, FTS5CSR_REQUIRE_DOCSIZE)((pCsr)->csrflags |= (0x04)); | |||
| 20211 | } | |||
| 20212 | break; | |||
| 20213 | } | |||
| 20214 | } | |||
| 20215 | } | |||
| 20216 | ||||
| 20217 | return rc; | |||
| 20218 | } | |||
| 20219 | ||||
| 20220 | ||||
| 20221 | static int fts5PrepareStatement( | |||
| 20222 | sqlite3_stmt **ppStmt, | |||
| 20223 | Fts5Config *pConfig, | |||
| 20224 | const char *zFmt, | |||
| 20225 | ... | |||
| 20226 | ){ | |||
| 20227 | sqlite3_stmt *pRet = 0; | |||
| 20228 | int rc; | |||
| 20229 | char *zSql; | |||
| 20230 | va_list ap; | |||
| 20231 | ||||
| 20232 | va_start(ap, zFmt)__builtin_va_start(ap, zFmt); | |||
| 20233 | zSql = sqlite3_vmprintfsqlite3_api->vmprintf(zFmt, ap); | |||
| 20234 | if( zSql==0 ){ | |||
| 20235 | rc = SQLITE_NOMEM7; | |||
| 20236 | }else{ | |||
| 20237 | rc = sqlite3_prepare_v3sqlite3_api->prepare_v3(pConfig->db, zSql, -1, | |||
| 20238 | SQLITE_PREPARE_PERSISTENT0x01, &pRet, 0); | |||
| 20239 | if( rc!=SQLITE_OK0 ){ | |||
| 20240 | sqlite3Fts5ConfigErrmsg(pConfig, "%s", sqlite3_errmsgsqlite3_api->errmsg(pConfig->db)); | |||
| 20241 | } | |||
| 20242 | sqlite3_freesqlite3_api->free(zSql); | |||
| 20243 | } | |||
| 20244 | ||||
| 20245 | va_end(ap)__builtin_va_end(ap); | |||
| 20246 | *ppStmt = pRet; | |||
| 20247 | return rc; | |||
| 20248 | } | |||
| 20249 | ||||
| 20250 | static int fts5CursorFirstSorted( | |||
| 20251 | Fts5FullTable *pTab, | |||
| 20252 | Fts5Cursor *pCsr, | |||
| 20253 | int bDesc | |||
| 20254 | ){ | |||
| 20255 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 20256 | Fts5Sorter *pSorter; | |||
| 20257 | int nPhrase; | |||
| 20258 | sqlite3_int64 nByte; | |||
| 20259 | int rc; | |||
| 20260 | const char *zRank = pCsr->zRank; | |||
| 20261 | const char *zRankArgs = pCsr->zRankArgs; | |||
| 20262 | ||||
| 20263 | nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |||
| 20264 | nByte = SZ_FTS5SORTER(nPhrase)(__builtin_offsetof(Fts5Sorter, nIdx)+((nPhrase+2)/2)*sizeof( i64)); | |||
| 20265 | pSorter = (Fts5Sorter*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 20266 | if( pSorter==0 ) return SQLITE_NOMEM7; | |||
| 20267 | memset(pSorter, 0, (size_t)nByte); | |||
| 20268 | pSorter->nIdx = nPhrase; | |||
| 20269 | ||||
| 20270 | /* TODO: It would be better to have some system for reusing statement | |||
| 20271 | ** handles here, rather than preparing a new one for each query. But that | |||
| 20272 | ** is not possible as SQLite reference counts the virtual table objects. | |||
| 20273 | ** And since the statement required here reads from this very virtual | |||
| 20274 | ** table, saving it creates a circular reference. | |||
| 20275 | ** | |||
| 20276 | ** If SQLite a built-in statement cache, this wouldn't be a problem. */ | |||
| 20277 | rc = fts5PrepareStatement(&pSorter->pStmt, pConfig, | |||
| 20278 | "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(\"%w\"%s%s) %s", | |||
| 20279 | pConfig->zDb, pConfig->zName, zRank, pConfig->zName, | |||
| 20280 | (zRankArgs ? ", " : ""), | |||
| 20281 | (zRankArgs ? zRankArgs : ""), | |||
| 20282 | bDesc ? "DESC" : "ASC" | |||
| 20283 | ); | |||
| 20284 | ||||
| 20285 | pCsr->pSorter = pSorter; | |||
| 20286 | if( rc==SQLITE_OK0 ){ | |||
| 20287 | assert( pTab->pSortCsr==0 )((void) (0)); | |||
| 20288 | pTab->pSortCsr = pCsr; | |||
| 20289 | rc = fts5SorterNext(pCsr); | |||
| 20290 | pTab->pSortCsr = 0; | |||
| 20291 | } | |||
| 20292 | ||||
| 20293 | if( rc!=SQLITE_OK0 ){ | |||
| 20294 | sqlite3_finalizesqlite3_api->finalize(pSorter->pStmt); | |||
| 20295 | sqlite3_freesqlite3_api->free(pSorter); | |||
| 20296 | pCsr->pSorter = 0; | |||
| 20297 | } | |||
| 20298 | ||||
| 20299 | return rc; | |||
| 20300 | } | |||
| 20301 | ||||
| 20302 | static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){ | |||
| 20303 | int rc; | |||
| 20304 | Fts5Expr *pExpr = pCsr->pExpr; | |||
| 20305 | rc = sqlite3Fts5ExprFirst( | |||
| 20306 | pExpr, pTab->p.pIndex, pCsr->iFirstRowid, pCsr->iLastRowid, bDesc | |||
| 20307 | ); | |||
| 20308 | if( sqlite3Fts5ExprEof(pExpr) ){ | |||
| 20309 | CsrFlagSet(pCsr, FTS5CSR_EOF)((pCsr)->csrflags |= (0x01)); | |||
| 20310 | } | |||
| 20311 | fts5CsrNewrow(pCsr); | |||
| 20312 | return rc; | |||
| 20313 | } | |||
| 20314 | ||||
| 20315 | /* | |||
| 20316 | ** Process a "special" query. A special query is identified as one with a | |||
| 20317 | ** MATCH expression that begins with a '*' character. The remainder of | |||
| 20318 | ** the text passed to the MATCH operator are used as the special query | |||
| 20319 | ** parameters. | |||
| 20320 | */ | |||
| 20321 | static int fts5SpecialMatch( | |||
| 20322 | Fts5FullTable *pTab, | |||
| 20323 | Fts5Cursor *pCsr, | |||
| 20324 | const char *zQuery | |||
| 20325 | ){ | |||
| 20326 | int rc = SQLITE_OK0; /* Return code */ | |||
| 20327 | const char *z = zQuery; /* Special query text */ | |||
| 20328 | int n; /* Number of bytes in text at z */ | |||
| 20329 | ||||
| 20330 | while( z[0]==' ' ) z++; | |||
| 20331 | for(n=0; z[n] && z[n]!=' '; n++); | |||
| 20332 | ||||
| 20333 | assert( pTab->p.base.zErrMsg==0 )((void) (0)); | |||
| 20334 | pCsr->ePlan = FTS5_PLAN_SPECIAL3; | |||
| 20335 | ||||
| 20336 | if( n==5 && 0==sqlite3_strnicmpsqlite3_api->strnicmp("reads", z, n) ){ | |||
| 20337 | pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex); | |||
| 20338 | } | |||
| 20339 | else if( n==2 && 0==sqlite3_strnicmpsqlite3_api->strnicmp("id", z, n) ){ | |||
| 20340 | pCsr->iSpecial = pCsr->iCsrId; | |||
| 20341 | } | |||
| 20342 | else{ | |||
| 20343 | /* An unrecognized directive. Return an error message. */ | |||
| 20344 | pTab->p.base.zErrMsg = sqlite3_mprintfsqlite3_api->mprintf("unknown special query: %.*s", n, z); | |||
| 20345 | rc = SQLITE_ERROR1; | |||
| 20346 | } | |||
| 20347 | ||||
| 20348 | return rc; | |||
| 20349 | } | |||
| 20350 | ||||
| 20351 | /* | |||
| 20352 | ** Search for an auxiliary function named zName that can be used with table | |||
| 20353 | ** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary | |||
| 20354 | ** structure. Otherwise, if no such function exists, return NULL. | |||
| 20355 | */ | |||
| 20356 | static Fts5Auxiliary *fts5FindAuxiliary(Fts5FullTable *pTab, const char *zName){ | |||
| 20357 | Fts5Auxiliary *pAux; | |||
| 20358 | ||||
| 20359 | for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){ | |||
| 20360 | if( sqlite3_stricmpsqlite3_api->stricmp(zName, pAux->zFunc)==0 ) return pAux; | |||
| 20361 | } | |||
| 20362 | ||||
| 20363 | /* No function of the specified name was found. Return 0. */ | |||
| 20364 | return 0; | |||
| 20365 | } | |||
| 20366 | ||||
| 20367 | ||||
| 20368 | static int fts5FindRankFunction(Fts5Cursor *pCsr){ | |||
| 20369 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 20370 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 20371 | int rc = SQLITE_OK0; | |||
| 20372 | Fts5Auxiliary *pAux = 0; | |||
| 20373 | const char *zRank = pCsr->zRank; | |||
| 20374 | const char *zRankArgs = pCsr->zRankArgs; | |||
| 20375 | ||||
| 20376 | if( zRankArgs ){ | |||
| 20377 | char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs); | |||
| 20378 | if( zSql ){ | |||
| 20379 | sqlite3_stmt *pStmt = 0; | |||
| 20380 | rc = sqlite3_prepare_v3sqlite3_api->prepare_v3(pConfig->db, zSql, -1, | |||
| 20381 | SQLITE_PREPARE_PERSISTENT0x01, &pStmt, 0); | |||
| 20382 | sqlite3_freesqlite3_api->free(zSql); | |||
| 20383 | assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 )((void) (0)); | |||
| 20384 | if( rc==SQLITE_OK0 ){ | |||
| 20385 | if( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pStmt) ){ | |||
| 20386 | sqlite3_int64 nByte; | |||
| 20387 | pCsr->nRankArg = sqlite3_column_countsqlite3_api->column_count(pStmt); | |||
| 20388 | nByte = sizeof(sqlite3_value*)*pCsr->nRankArg; | |||
| 20389 | pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 20390 | if( rc==SQLITE_OK0 ){ | |||
| 20391 | int i; | |||
| 20392 | for(i=0; i<pCsr->nRankArg; i++){ | |||
| 20393 | pCsr->apRankArg[i] = sqlite3_column_valuesqlite3_api->column_value(pStmt, i); | |||
| 20394 | } | |||
| 20395 | } | |||
| 20396 | pCsr->pRankArgStmt = pStmt; | |||
| 20397 | }else{ | |||
| 20398 | rc = sqlite3_finalizesqlite3_api->finalize(pStmt); | |||
| 20399 | assert( rc!=SQLITE_OK )((void) (0)); | |||
| 20400 | } | |||
| 20401 | } | |||
| 20402 | } | |||
| 20403 | } | |||
| 20404 | ||||
| 20405 | if( rc==SQLITE_OK0 ){ | |||
| 20406 | pAux = fts5FindAuxiliary(pTab, zRank); | |||
| 20407 | if( pAux==0 ){ | |||
| 20408 | assert( pTab->p.base.zErrMsg==0 )((void) (0)); | |||
| 20409 | pTab->p.base.zErrMsg = sqlite3_mprintfsqlite3_api->mprintf("no such function: %s", zRank); | |||
| 20410 | rc = SQLITE_ERROR1; | |||
| 20411 | } | |||
| 20412 | } | |||
| 20413 | ||||
| 20414 | pCsr->pRank = pAux; | |||
| 20415 | return rc; | |||
| 20416 | } | |||
| 20417 | ||||
| 20418 | ||||
| 20419 | static int fts5CursorParseRank( | |||
| 20420 | Fts5Config *pConfig, | |||
| 20421 | Fts5Cursor *pCsr, | |||
| 20422 | sqlite3_value *pRank | |||
| 20423 | ){ | |||
| 20424 | int rc = SQLITE_OK0; | |||
| 20425 | if( pRank ){ | |||
| 20426 | const char *z = (const char*)sqlite3_value_textsqlite3_api->value_text(pRank); | |||
| 20427 | char *zRank = 0; | |||
| 20428 | char *zRankArgs = 0; | |||
| 20429 | ||||
| 20430 | if( z==0 ){ | |||
| 20431 | if( sqlite3_value_typesqlite3_api->value_type(pRank)==SQLITE_NULL5 ) rc = SQLITE_ERROR1; | |||
| 20432 | }else{ | |||
| 20433 | rc = sqlite3Fts5ConfigParseRank(z, &zRank, &zRankArgs); | |||
| 20434 | } | |||
| 20435 | if( rc==SQLITE_OK0 ){ | |||
| 20436 | pCsr->zRank = zRank; | |||
| 20437 | pCsr->zRankArgs = zRankArgs; | |||
| 20438 | CsrFlagSet(pCsr, FTS5CSR_FREE_ZRANK)((pCsr)->csrflags |= (0x10)); | |||
| 20439 | }else if( rc==SQLITE_ERROR1 ){ | |||
| 20440 | pCsr->base.pVtab->zErrMsg = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 20441 | "parse error in rank function: %s", z | |||
| 20442 | ); | |||
| 20443 | } | |||
| 20444 | }else{ | |||
| 20445 | if( pConfig->zRank ){ | |||
| 20446 | pCsr->zRank = (char*)pConfig->zRank; | |||
| 20447 | pCsr->zRankArgs = (char*)pConfig->zRankArgs; | |||
| 20448 | }else{ | |||
| 20449 | pCsr->zRank = (char*)FTS5_DEFAULT_RANK"bm25"; | |||
| 20450 | pCsr->zRankArgs = 0; | |||
| 20451 | } | |||
| 20452 | } | |||
| 20453 | return rc; | |||
| 20454 | } | |||
| 20455 | ||||
| 20456 | static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){ | |||
| 20457 | if( pVal ){ | |||
| 20458 | int eType = sqlite3_value_numeric_typesqlite3_api->value_numeric_type(pVal); | |||
| 20459 | if( eType==SQLITE_INTEGER1 ){ | |||
| 20460 | return sqlite3_value_int64sqlite3_api->value_int64(pVal); | |||
| 20461 | } | |||
| 20462 | } | |||
| 20463 | return iDefault; | |||
| 20464 | } | |||
| 20465 | ||||
| 20466 | /* | |||
| 20467 | ** Set the error message on the virtual table passed as the first argument. | |||
| 20468 | */ | |||
| 20469 | static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ | |||
| 20470 | va_list ap; /* ... printf arguments */ | |||
| 20471 | va_start(ap, zFormat)__builtin_va_start(ap, zFormat); | |||
| 20472 | sqlite3_freesqlite3_api->free(p->p.base.zErrMsg); | |||
| 20473 | p->p.base.zErrMsg = sqlite3_vmprintfsqlite3_api->vmprintf(zFormat, ap); | |||
| 20474 | va_end(ap)__builtin_va_end(ap); | |||
| 20475 | } | |||
| 20476 | ||||
| 20477 | /* | |||
| 20478 | ** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale | |||
| 20479 | ** specified by pLocale/nLocale. The buffer indicated by pLocale must remain | |||
| 20480 | ** valid until after the final call to sqlite3Fts5Tokenize() that will use | |||
| 20481 | ** the locale. | |||
| 20482 | */ | |||
| 20483 | static void sqlite3Fts5SetLocale( | |||
| 20484 | Fts5Config *pConfig, | |||
| 20485 | const char *zLocale, | |||
| 20486 | int nLocale | |||
| 20487 | ){ | |||
| 20488 | Fts5TokenizerConfig *pT = &pConfig->t; | |||
| 20489 | pT->pLocale = zLocale; | |||
| 20490 | pT->nLocale = nLocale; | |||
| 20491 | } | |||
| 20492 | ||||
| 20493 | /* | |||
| 20494 | ** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale(). | |||
| 20495 | */ | |||
| 20496 | static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ | |||
| 20497 | sqlite3Fts5SetLocale(pConfig, 0, 0); | |||
| 20498 | } | |||
| 20499 | ||||
| 20500 | /* | |||
| 20501 | ** Return true if the value passed as the only argument is an | |||
| 20502 | ** fts5_locale() value. | |||
| 20503 | */ | |||
| 20504 | static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){ | |||
| 20505 | int ret = 0; | |||
| 20506 | if( sqlite3_value_typesqlite3_api->value_type(pVal)==SQLITE_BLOB4 ){ | |||
| 20507 | /* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case. | |||
| 20508 | ** If the blob was created using zeroblob(), then sqlite3_value_blob() | |||
| 20509 | ** may call malloc(). If this malloc() fails, then the values returned | |||
| 20510 | ** by both value_blob() and value_bytes() will be 0. If value_bytes() were | |||
| 20511 | ** called first, then the NULL pointer returned by value_blob() might | |||
| 20512 | ** be dereferenced. */ | |||
| 20513 | const u8 *pBlob = sqlite3_value_blobsqlite3_api->value_blob(pVal); | |||
| 20514 | int nBlob = sqlite3_value_bytessqlite3_api->value_bytes(pVal); | |||
| 20515 | if( nBlob>FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) | |||
| 20516 | && 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig)((const u8*)(pConfig->pGlobal->aLocaleHdr)), FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))) | |||
| 20517 | ){ | |||
| 20518 | ret = 1; | |||
| 20519 | } | |||
| 20520 | } | |||
| 20521 | return ret; | |||
| 20522 | } | |||
| 20523 | ||||
| 20524 | /* | |||
| 20525 | ** Value pVal is guaranteed to be an fts5_locale() value, according to | |||
| 20526 | ** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale | |||
| 20527 | ** from the value and returns them separately. | |||
| 20528 | ** | |||
| 20529 | ** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set | |||
| 20530 | ** to point to buffers containing the text and locale, as utf-8, | |||
| 20531 | ** respectively. In this case output parameters (*pnText) and (*pnLoc) are | |||
| 20532 | ** set to the sizes in bytes of these two buffers. | |||
| 20533 | ** | |||
| 20534 | ** Or, if an error occurs, then an SQLite error code is returned. The final | |||
| 20535 | ** value of the four output parameters is undefined in this case. | |||
| 20536 | */ | |||
| 20537 | static int sqlite3Fts5DecodeLocaleValue( | |||
| 20538 | sqlite3_value *pVal, | |||
| 20539 | const char **ppText, | |||
| 20540 | int *pnText, | |||
| 20541 | const char **ppLoc, | |||
| 20542 | int *pnLoc | |||
| 20543 | ){ | |||
| 20544 | const char *p = sqlite3_value_blobsqlite3_api->value_blob(pVal); | |||
| 20545 | int n = sqlite3_value_bytessqlite3_api->value_bytes(pVal); | |||
| 20546 | int nLoc = 0; | |||
| 20547 | ||||
| 20548 | assert( sqlite3_value_type(pVal)==SQLITE_BLOB )((void) (0)); | |||
| 20549 | assert( n>FTS5_LOCALE_HDR_SIZE )((void) (0)); | |||
| 20550 | ||||
| 20551 | for(nLoc=FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )); p[nLoc]; nLoc++){ | |||
| 20552 | if( nLoc==(n-1) ){ | |||
| 20553 | return SQLITE_MISMATCH20; | |||
| 20554 | } | |||
| 20555 | } | |||
| 20556 | *ppLoc = &p[FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))]; | |||
| 20557 | *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )); | |||
| 20558 | ||||
| 20559 | *ppText = &p[nLoc+1]; | |||
| 20560 | *pnText = n - nLoc - 1; | |||
| 20561 | return SQLITE_OK0; | |||
| 20562 | } | |||
| 20563 | ||||
| 20564 | /* | |||
| 20565 | ** Argument pVal is the text of a full-text search expression. It may or | |||
| 20566 | ** may not have been wrapped by fts5_locale(). This function extracts | |||
| 20567 | ** the text of the expression, and sets output variable (*pzText) to | |||
| 20568 | ** point to a nul-terminated buffer containing the expression. | |||
| 20569 | ** | |||
| 20570 | ** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called | |||
| 20571 | ** to set the tokenizer to use the specified locale. | |||
| 20572 | ** | |||
| 20573 | ** If output variable (*pbFreeAndReset) is set to true, then the caller | |||
| 20574 | ** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer | |||
| 20575 | ** locale, and (b) call sqlite3_free() to free (*pzText). | |||
| 20576 | */ | |||
| 20577 | static int fts5ExtractExprText( | |||
| 20578 | Fts5Config *pConfig, /* Fts5 configuration */ | |||
| 20579 | sqlite3_value *pVal, /* Value to extract expression text from */ | |||
| 20580 | char **pzText, /* OUT: nul-terminated buffer of text */ | |||
| 20581 | int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ | |||
| 20582 | ){ | |||
| 20583 | int rc = SQLITE_OK0; | |||
| 20584 | ||||
| 20585 | if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |||
| 20586 | const char *pText = 0; | |||
| 20587 | int nText = 0; | |||
| 20588 | const char *pLoc = 0; | |||
| 20589 | int nLoc = 0; | |||
| 20590 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |||
| 20591 | *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText); | |||
| 20592 | if( rc==SQLITE_OK0 ){ | |||
| 20593 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |||
| 20594 | } | |||
| 20595 | *pbFreeAndReset = 1; | |||
| 20596 | }else{ | |||
| 20597 | *pzText = (char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 20598 | *pbFreeAndReset = 0; | |||
| 20599 | } | |||
| 20600 | ||||
| 20601 | return rc; | |||
| 20602 | } | |||
| 20603 | ||||
| 20604 | ||||
| 20605 | /* | |||
| 20606 | ** This is the xFilter interface for the virtual table. See | |||
| 20607 | ** the virtual table xFilter method documentation for additional | |||
| 20608 | ** information. | |||
| 20609 | ** | |||
| 20610 | ** There are three possible query strategies: | |||
| 20611 | ** | |||
| 20612 | ** 1. Full-text search using a MATCH operator. | |||
| 20613 | ** 2. A by-rowid lookup. | |||
| 20614 | ** 3. A full-table scan. | |||
| 20615 | */ | |||
| 20616 | static int fts5FilterMethod( | |||
| 20617 | sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ | |||
| 20618 | int idxNum, /* Strategy index */ | |||
| 20619 | const char *idxStr, /* Unused */ | |||
| 20620 | int nVal, /* Number of elements in apVal */ | |||
| 20621 | sqlite3_value **apVal /* Arguments for the indexing scheme */ | |||
| 20622 | ){ | |||
| 20623 | Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); | |||
| 20624 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 20625 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |||
| 20626 | int rc = SQLITE_OK0; /* Error code */ | |||
| 20627 | int bDesc; /* True if ORDER BY [rank|rowid] DESC */ | |||
| 20628 | int bOrderByRank; /* True if ORDER BY rank */ | |||
| 20629 | sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */ | |||
| 20630 | sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ | |||
| 20631 | sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ | |||
| 20632 | sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ | |||
| 20633 | int iCol; /* Column on LHS of MATCH operator */ | |||
| 20634 | char **pzErrmsg = pConfig->pzErrmsg; | |||
| 20635 | int bPrefixInsttoken = pConfig->bPrefixInsttoken; | |||
| 20636 | int i; | |||
| 20637 | int iIdxStr = 0; | |||
| 20638 | Fts5Expr *pExpr = 0; | |||
| 20639 | ||||
| 20640 | assert( pConfig->bLock==0 )((void) (0)); | |||
| 20641 | if( pCsr->ePlan ){ | |||
| 20642 | fts5FreeCursorComponents(pCsr); | |||
| 20643 | memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); | |||
| 20644 | } | |||
| 20645 | ||||
| 20646 | assert( pCsr->pStmt==0 )((void) (0)); | |||
| 20647 | assert( pCsr->pExpr==0 )((void) (0)); | |||
| 20648 | assert( pCsr->csrflags==0 )((void) (0)); | |||
| 20649 | assert( pCsr->pRank==0 )((void) (0)); | |||
| 20650 | assert( pCsr->zRank==0 )((void) (0)); | |||
| 20651 | assert( pCsr->zRankArgs==0 )((void) (0)); | |||
| 20652 | assert( pTab->pSortCsr==0 || nVal==0 )((void) (0)); | |||
| 20653 | ||||
| 20654 | assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg )((void) (0)); | |||
| 20655 | pConfig->pzErrmsg = &pTab->p.base.zErrMsg; | |||
| 20656 | ||||
| 20657 | /* Decode the arguments passed through to this function. */ | |||
| 20658 | for(i=0; i<nVal; i++){ | |||
| 20659 | switch( idxStr[iIdxStr++] ){ | |||
| 20660 | case 'r': | |||
| 20661 | pRank = apVal[i]; | |||
| 20662 | break; | |||
| 20663 | case 'M': { | |||
| 20664 | char *zText = 0; | |||
| 20665 | int bFreeAndReset = 0; | |||
| 20666 | int bInternal = 0; | |||
| 20667 | ||||
| 20668 | rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); | |||
| 20669 | if( rc!=SQLITE_OK0 ) goto filter_out; | |||
| 20670 | if( zText==0 ) zText = ""; | |||
| 20671 | if( sqlite3_value_subtypesqlite3_api->value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE73 ){ | |||
| 20672 | pConfig->bPrefixInsttoken = 1; | |||
| 20673 | } | |||
| 20674 | ||||
| 20675 | iCol = 0; | |||
| 20676 | do{ | |||
| 20677 | iCol = iCol*10 + (idxStr[iIdxStr]-'0'); | |||
| 20678 | iIdxStr++; | |||
| 20679 | }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); | |||
| 20680 | ||||
| 20681 | if( zText[0]=='*' ){ | |||
| 20682 | /* The user has issued a query of the form "MATCH '*...'". This | |||
| 20683 | ** indicates that the MATCH expression is not a full text query, | |||
| 20684 | ** but a request for an internal parameter. */ | |||
| 20685 | rc = fts5SpecialMatch(pTab, pCsr, &zText[1]); | |||
| 20686 | bInternal = 1; | |||
| 20687 | }else{ | |||
| 20688 | char **pzErr = &pTab->p.base.zErrMsg; | |||
| 20689 | rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr); | |||
| 20690 | if( rc==SQLITE_OK0 ){ | |||
| 20691 | rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); | |||
| 20692 | pExpr = 0; | |||
| 20693 | } | |||
| 20694 | } | |||
| 20695 | ||||
| 20696 | if( bFreeAndReset ){ | |||
| 20697 | sqlite3_freesqlite3_api->free(zText); | |||
| 20698 | sqlite3Fts5ClearLocale(pConfig); | |||
| 20699 | } | |||
| 20700 | ||||
| 20701 | if( bInternal || rc!=SQLITE_OK0 ) goto filter_out; | |||
| 20702 | ||||
| 20703 | break; | |||
| 20704 | } | |||
| 20705 | case 'L': | |||
| 20706 | case 'G': { | |||
| 20707 | int bGlob = (idxStr[iIdxStr-1]=='G'); | |||
| 20708 | const char *zText = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[i]); | |||
| 20709 | iCol = 0; | |||
| 20710 | do{ | |||
| 20711 | iCol = iCol*10 + (idxStr[iIdxStr]-'0'); | |||
| 20712 | iIdxStr++; | |||
| 20713 | }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); | |||
| 20714 | if( zText ){ | |||
| 20715 | rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr); | |||
| 20716 | } | |||
| 20717 | if( rc==SQLITE_OK0 ){ | |||
| 20718 | rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); | |||
| 20719 | pExpr = 0; | |||
| 20720 | } | |||
| 20721 | if( rc!=SQLITE_OK0 ) goto filter_out; | |||
| 20722 | break; | |||
| 20723 | } | |||
| 20724 | case '=': | |||
| 20725 | pRowidEq = apVal[i]; | |||
| 20726 | break; | |||
| 20727 | case '<': | |||
| 20728 | pRowidLe = apVal[i]; | |||
| 20729 | break; | |||
| 20730 | default: assert( idxStr[iIdxStr-1]=='>' )((void) (0)); | |||
| 20731 | pRowidGe = apVal[i]; | |||
| 20732 | break; | |||
| 20733 | } | |||
| 20734 | } | |||
| 20735 | bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK0x0020) ? 1 : 0); | |||
| 20736 | pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC0x0080) ? 1 : 0); | |||
| 20737 | ||||
| 20738 | /* Set the cursor upper and lower rowid limits. Only some strategies | |||
| 20739 | ** actually use them. This is ok, as the xBestIndex() method leaves the | |||
| 20740 | ** sqlite3_index_constraint.omit flag clear for range constraints | |||
| 20741 | ** on the rowid field. */ | |||
| 20742 | if( pRowidEq ){ | |||
| 20743 | pRowidLe = pRowidGe = pRowidEq; | |||
| 20744 | } | |||
| 20745 | if( bDesc ){ | |||
| 20746 | pCsr->iFirstRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32))); | |||
| 20747 | pCsr->iLastRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64(((i64)-1) - (0xffffffff|(((i64)0x7fffffff)<<32)))); | |||
| 20748 | }else{ | |||
| 20749 | pCsr->iLastRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32))); | |||
| 20750 | pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64(((i64)-1) - (0xffffffff|(((i64)0x7fffffff)<<32)))); | |||
| 20751 | } | |||
| 20752 | ||||
| 20753 | rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); | |||
| 20754 | if( rc!=SQLITE_OK0 ) goto filter_out; | |||
| 20755 | ||||
| 20756 | if( pTab->pSortCsr ){ | |||
| 20757 | /* If pSortCsr is non-NULL, then this call is being made as part of | |||
| 20758 | ** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is | |||
| 20759 | ** set to FTS5_PLAN_SORTED_MATCH). pSortCsr is the cursor that will | |||
| 20760 | ** return results to the user for this query. The current cursor | |||
| 20761 | ** (pCursor) is used to execute the query issued by function | |||
| 20762 | ** fts5CursorFirstSorted() above. */ | |||
| 20763 | assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 )((void) (0)); | |||
| 20764 | assert( nVal==0 && bOrderByRank==0 && bDesc==0 )((void) (0)); | |||
| 20765 | assert( pCsr->iLastRowid==LARGEST_INT64 )((void) (0)); | |||
| 20766 | assert( pCsr->iFirstRowid==SMALLEST_INT64 )((void) (0)); | |||
| 20767 | if( pTab->pSortCsr->bDesc ){ | |||
| 20768 | pCsr->iLastRowid = pTab->pSortCsr->iFirstRowid; | |||
| 20769 | pCsr->iFirstRowid = pTab->pSortCsr->iLastRowid; | |||
| 20770 | }else{ | |||
| 20771 | pCsr->iLastRowid = pTab->pSortCsr->iLastRowid; | |||
| 20772 | pCsr->iFirstRowid = pTab->pSortCsr->iFirstRowid; | |||
| 20773 | } | |||
| 20774 | pCsr->ePlan = FTS5_PLAN_SOURCE2; | |||
| 20775 | pCsr->pExpr = pTab->pSortCsr->pExpr; | |||
| 20776 | rc = fts5CursorFirst(pTab, pCsr, bDesc); | |||
| 20777 | }else if( pCsr->pExpr ){ | |||
| 20778 | assert( rc==SQLITE_OK )((void) (0)); | |||
| 20779 | rc = fts5CursorParseRank(pConfig, pCsr, pRank); | |||
| 20780 | if( rc==SQLITE_OK0 ){ | |||
| 20781 | if( bOrderByRank ){ | |||
| 20782 | pCsr->ePlan = FTS5_PLAN_SORTED_MATCH4; | |||
| 20783 | rc = fts5CursorFirstSorted(pTab, pCsr, bDesc); | |||
| 20784 | }else{ | |||
| 20785 | pCsr->ePlan = FTS5_PLAN_MATCH1; | |||
| 20786 | rc = fts5CursorFirst(pTab, pCsr, bDesc); | |||
| 20787 | } | |||
| 20788 | } | |||
| 20789 | }else if( pConfig->zContent==0 ){ | |||
| 20790 | fts5SetVtabError(pTab,"%s: table does not support scanning",pConfig->zName); | |||
| 20791 | rc = SQLITE_ERROR1; | |||
| 20792 | }else{ | |||
| 20793 | /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup | |||
| 20794 | ** by rowid (ePlan==FTS5_PLAN_ROWID). */ | |||
| 20795 | pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID6 : FTS5_PLAN_SCAN5); | |||
| 20796 | rc = sqlite3Fts5StorageStmt( | |||
| 20797 | pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg | |||
| 20798 | ); | |||
| 20799 | if( rc==SQLITE_OK0 ){ | |||
| 20800 | if( pRowidEq!=0 ){ | |||
| 20801 | assert( pCsr->ePlan==FTS5_PLAN_ROWID )((void) (0)); | |||
| 20802 | sqlite3_bind_valuesqlite3_api->bind_value(pCsr->pStmt, 1, pRowidEq); | |||
| 20803 | }else{ | |||
| 20804 | sqlite3_bind_int64sqlite3_api->bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid); | |||
| 20805 | sqlite3_bind_int64sqlite3_api->bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid); | |||
| 20806 | } | |||
| 20807 | rc = fts5NextMethod(pCursor); | |||
| 20808 | } | |||
| 20809 | } | |||
| 20810 | ||||
| 20811 | filter_out: | |||
| 20812 | sqlite3Fts5ExprFree(pExpr); | |||
| 20813 | pConfig->pzErrmsg = pzErrmsg; | |||
| 20814 | pConfig->bPrefixInsttoken = bPrefixInsttoken; | |||
| 20815 | return rc; | |||
| 20816 | } | |||
| 20817 | ||||
| 20818 | /* | |||
| 20819 | ** This is the xEof method of the virtual table. SQLite calls this | |||
| 20820 | ** routine to find out if it has reached the end of a result set. | |||
| 20821 | */ | |||
| 20822 | static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){ | |||
| 20823 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |||
| 20824 | return (CsrFlagTest(pCsr, FTS5CSR_EOF)((pCsr)->csrflags & (0x01)) ? 1 : 0); | |||
| 20825 | } | |||
| 20826 | ||||
| 20827 | /* | |||
| 20828 | ** Return the rowid that the cursor currently points to. | |||
| 20829 | */ | |||
| 20830 | static i64 fts5CursorRowid(Fts5Cursor *pCsr){ | |||
| 20831 | assert( pCsr->ePlan==FTS5_PLAN_MATCH((void) (0)) | |||
| 20832 | || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH((void) (0)) | |||
| 20833 | || pCsr->ePlan==FTS5_PLAN_SOURCE((void) (0)) | |||
| 20834 | || pCsr->ePlan==FTS5_PLAN_SCAN((void) (0)) | |||
| 20835 | || pCsr->ePlan==FTS5_PLAN_ROWID((void) (0)) | |||
| 20836 | )((void) (0)); | |||
| 20837 | if( pCsr->pSorter ){ | |||
| 20838 | return pCsr->pSorter->iRowid; | |||
| 20839 | }else if( pCsr->ePlan>=FTS5_PLAN_SCAN5 ){ | |||
| 20840 | return sqlite3_column_int64sqlite3_api->column_int64(pCsr->pStmt, 0); | |||
| 20841 | }else{ | |||
| 20842 | return sqlite3Fts5ExprRowid(pCsr->pExpr); | |||
| 20843 | } | |||
| 20844 | } | |||
| 20845 | ||||
| 20846 | /* | |||
| 20847 | ** This is the xRowid method. The SQLite core calls this routine to | |||
| 20848 | ** retrieve the rowid for the current row of the result set. fts5 | |||
| 20849 | ** exposes %_content.rowid as the rowid for the virtual table. The | |||
| 20850 | ** rowid should be written to *pRowid. | |||
| 20851 | */ | |||
| 20852 | static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ | |||
| 20853 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |||
| 20854 | int ePlan = pCsr->ePlan; | |||
| 20855 | ||||
| 20856 | assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 )((void) (0)); | |||
| 20857 | if( ePlan==FTS5_PLAN_SPECIAL3 ){ | |||
| 20858 | *pRowid = 0; | |||
| 20859 | }else{ | |||
| 20860 | *pRowid = fts5CursorRowid(pCsr); | |||
| 20861 | } | |||
| 20862 | ||||
| 20863 | return SQLITE_OK0; | |||
| 20864 | } | |||
| 20865 | ||||
| 20866 | ||||
| 20867 | /* | |||
| 20868 | ** If the cursor requires seeking (bSeekRequired flag is set), seek it. | |||
| 20869 | ** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise. | |||
| 20870 | ** | |||
| 20871 | ** If argument bErrormsg is true and an error occurs, an error message may | |||
| 20872 | ** be left in sqlite3_vtab.zErrMsg. | |||
| 20873 | */ | |||
| 20874 | static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ | |||
| 20875 | int rc = SQLITE_OK0; | |||
| 20876 | ||||
| 20877 | /* If the cursor does not yet have a statement handle, obtain one now. */ | |||
| 20878 | if( pCsr->pStmt==0 ){ | |||
| 20879 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 20880 | int eStmt = fts5StmtType(pCsr); | |||
| 20881 | rc = sqlite3Fts5StorageStmt( | |||
| 20882 | pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->p.base.zErrMsg:0) | |||
| 20883 | ); | |||
| 20884 | assert( rc!=SQLITE_OK || pTab->p.base.zErrMsg==0 )((void) (0)); | |||
| 20885 | assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) )((void) (0)); | |||
| 20886 | } | |||
| 20887 | ||||
| 20888 | if( rc==SQLITE_OK0 && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT)((pCsr)->csrflags & (0x02)) ){ | |||
| 20889 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |||
| 20890 | assert( pCsr->pExpr )((void) (0)); | |||
| 20891 | sqlite3_resetsqlite3_api->reset(pCsr->pStmt); | |||
| 20892 | sqlite3_bind_int64sqlite3_api->bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr)); | |||
| 20893 | pTab->pConfig->bLock++; | |||
| 20894 | rc = sqlite3_stepsqlite3_api->step(pCsr->pStmt); | |||
| 20895 | pTab->pConfig->bLock--; | |||
| 20896 | if( rc==SQLITE_ROW100 ){ | |||
| 20897 | rc = SQLITE_OK0; | |||
| 20898 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT)((pCsr)->csrflags &= ~(0x02)); | |||
| 20899 | }else{ | |||
| 20900 | rc = sqlite3_resetsqlite3_api->reset(pCsr->pStmt); | |||
| 20901 | if( rc==SQLITE_OK0 ){ | |||
| 20902 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 20903 | fts5SetVtabError((Fts5FullTable*)pTab, | |||
| 20904 | "fts5: missing row %lld from content table %s", | |||
| 20905 | fts5CursorRowid(pCsr), | |||
| 20906 | pTab->pConfig->zContent | |||
| 20907 | ); | |||
| 20908 | }else if( pTab->pConfig->pzErrmsg ){ | |||
| 20909 | fts5SetVtabError((Fts5FullTable*)pTab, | |||
| 20910 | "%s", sqlite3_errmsgsqlite3_api->errmsg(pTab->pConfig->db) | |||
| 20911 | ); | |||
| 20912 | } | |||
| 20913 | } | |||
| 20914 | } | |||
| 20915 | return rc; | |||
| 20916 | } | |||
| 20917 | ||||
| 20918 | /* | |||
| 20919 | ** This function is called to handle an FTS INSERT command. In other words, | |||
| 20920 | ** an INSERT statement of the form: | |||
| 20921 | ** | |||
| 20922 | ** INSERT INTO fts(fts) VALUES($pCmd) | |||
| 20923 | ** INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal) | |||
| 20924 | ** | |||
| 20925 | ** Argument pVal is the value assigned to column "fts" by the INSERT | |||
| 20926 | ** statement. This function returns SQLITE_OK if successful, or an SQLite | |||
| 20927 | ** error code if an error occurs. | |||
| 20928 | ** | |||
| 20929 | ** The commands implemented by this function are documented in the "Special | |||
| 20930 | ** INSERT Directives" section of the documentation. It should be updated if | |||
| 20931 | ** more commands are added to this function. | |||
| 20932 | */ | |||
| 20933 | static int fts5SpecialInsert( | |||
| 20934 | Fts5FullTable *pTab, /* Fts5 table object */ | |||
| 20935 | const char *zCmd, /* Text inserted into table-name column */ | |||
| 20936 | sqlite3_value *pVal /* Value inserted into rank column */ | |||
| 20937 | ){ | |||
| 20938 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 20939 | int rc = SQLITE_OK0; | |||
| 20940 | int bError = 0; | |||
| 20941 | int bLoadConfig = 0; | |||
| 20942 | ||||
| 20943 | if( 0==sqlite3_stricmpsqlite3_api->stricmp("delete-all", zCmd) ){ | |||
| 20944 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 20945 | fts5SetVtabError(pTab, | |||
| 20946 | "'delete-all' may only be used with a " | |||
| 20947 | "contentless or external content fts5 table" | |||
| 20948 | ); | |||
| 20949 | rc = SQLITE_ERROR1; | |||
| 20950 | }else{ | |||
| 20951 | rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); | |||
| 20952 | } | |||
| 20953 | bLoadConfig = 1; | |||
| 20954 | }else if( 0==sqlite3_stricmpsqlite3_api->stricmp("rebuild", zCmd) ){ | |||
| 20955 | if( fts5IsContentless(pTab, 1) ){ | |||
| 20956 | fts5SetVtabError(pTab, | |||
| 20957 | "'rebuild' may not be used with a contentless fts5 table" | |||
| 20958 | ); | |||
| 20959 | rc = SQLITE_ERROR1; | |||
| 20960 | }else{ | |||
| 20961 | rc = sqlite3Fts5StorageRebuild(pTab->pStorage); | |||
| 20962 | } | |||
| 20963 | bLoadConfig = 1; | |||
| 20964 | }else if( 0==sqlite3_stricmpsqlite3_api->stricmp("optimize", zCmd) ){ | |||
| 20965 | rc = sqlite3Fts5StorageOptimize(pTab->pStorage); | |||
| 20966 | }else if( 0==sqlite3_stricmpsqlite3_api->stricmp("merge", zCmd) ){ | |||
| 20967 | int nMerge = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 20968 | rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge); | |||
| 20969 | }else if( 0==sqlite3_stricmpsqlite3_api->stricmp("integrity-check", zCmd) ){ | |||
| 20970 | int iArg = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 20971 | rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, iArg); | |||
| 20972 | #ifdef SQLITE_DEBUG | |||
| 20973 | }else if( 0==sqlite3_stricmpsqlite3_api->stricmp("prefix-index", zCmd) ){ | |||
| 20974 | pConfig->bPrefixIndex = sqlite3_value_intsqlite3_api->value_int(pVal); | |||
| 20975 | #endif | |||
| 20976 | }else if( 0==sqlite3_stricmpsqlite3_api->stricmp("flush", zCmd) ){ | |||
| 20977 | rc = sqlite3Fts5FlushToDisk(&pTab->p); | |||
| 20978 | }else{ | |||
| 20979 | rc = sqlite3Fts5FlushToDisk(&pTab->p); | |||
| 20980 | if( rc==SQLITE_OK0 ){ | |||
| 20981 | rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); | |||
| 20982 | } | |||
| 20983 | if( rc==SQLITE_OK0 ){ | |||
| 20984 | rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError); | |||
| 20985 | } | |||
| 20986 | if( rc==SQLITE_OK0 ){ | |||
| 20987 | if( bError ){ | |||
| 20988 | rc = SQLITE_ERROR1; | |||
| 20989 | }else{ | |||
| 20990 | rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0); | |||
| 20991 | } | |||
| 20992 | } | |||
| 20993 | } | |||
| 20994 | ||||
| 20995 | if( rc==SQLITE_OK0 && bLoadConfig ){ | |||
| 20996 | pTab->p.pConfig->iCookie--; | |||
| 20997 | rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); | |||
| 20998 | } | |||
| 20999 | ||||
| 21000 | return rc; | |||
| 21001 | } | |||
| 21002 | ||||
| 21003 | static int fts5SpecialDelete( | |||
| 21004 | Fts5FullTable *pTab, | |||
| 21005 | sqlite3_value **apVal | |||
| 21006 | ){ | |||
| 21007 | int rc = SQLITE_OK0; | |||
| 21008 | int eType1 = sqlite3_value_typesqlite3_api->value_type(apVal[1]); | |||
| 21009 | if( eType1==SQLITE_INTEGER1 ){ | |||
| 21010 | sqlite3_int64 iDel = sqlite3_value_int64sqlite3_api->value_int64(apVal[1]); | |||
| 21011 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2], 0); | |||
| 21012 | } | |||
| 21013 | return rc; | |||
| 21014 | } | |||
| 21015 | ||||
| 21016 | static void fts5StorageInsert( | |||
| 21017 | int *pRc, | |||
| 21018 | Fts5FullTable *pTab, | |||
| 21019 | sqlite3_value **apVal, | |||
| 21020 | i64 *piRowid | |||
| 21021 | ){ | |||
| 21022 | int rc = *pRc; | |||
| 21023 | if( rc==SQLITE_OK0 ){ | |||
| 21024 | rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid); | |||
| 21025 | } | |||
| 21026 | if( rc==SQLITE_OK0 ){ | |||
| 21027 | rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); | |||
| 21028 | } | |||
| 21029 | *pRc = rc; | |||
| 21030 | } | |||
| 21031 | ||||
| 21032 | /* | |||
| 21033 | ** | |||
| 21034 | ** This function is called when the user attempts an UPDATE on a contentless | |||
| 21035 | ** table. Parameter bRowidModified is true if the UPDATE statement modifies | |||
| 21036 | ** the rowid value. Parameter apVal[] contains the new values for each user | |||
| 21037 | ** defined column of the fts5 table. pConfig is the configuration object of the | |||
| 21038 | ** table being updated (guaranteed to be contentless). The contentless_delete=1 | |||
| 21039 | ** and contentless_unindexed=1 options may or may not be set. | |||
| 21040 | ** | |||
| 21041 | ** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite | |||
| 21042 | ** error code if it cannot. In this case an error message is also loaded into | |||
| 21043 | ** pConfig. Output parameter (*pbContent) is set to true if the caller should | |||
| 21044 | ** update the %_content table only - not the FTS index or any other shadow | |||
| 21045 | ** table. This occurs when an UPDATE modifies only UNINDEXED columns of the | |||
| 21046 | ** table. | |||
| 21047 | ** | |||
| 21048 | ** An UPDATE may proceed if: | |||
| 21049 | ** | |||
| 21050 | ** * The only columns modified are UNINDEXED columns, or | |||
| 21051 | ** | |||
| 21052 | ** * The contentless_delete=1 option was specified and all of the indexed | |||
| 21053 | ** columns (not a subset) have been modified. | |||
| 21054 | */ | |||
| 21055 | static int fts5ContentlessUpdate( | |||
| 21056 | Fts5Config *pConfig, | |||
| 21057 | sqlite3_value **apVal, | |||
| 21058 | int bRowidModified, | |||
| 21059 | int *pbContent | |||
| 21060 | ){ | |||
| 21061 | int ii; | |||
| 21062 | int bSeenIndex = 0; /* Have seen modified indexed column */ | |||
| 21063 | int bSeenIndexNC = 0; /* Have seen unmodified indexed column */ | |||
| 21064 | int rc = SQLITE_OK0; | |||
| 21065 | ||||
| 21066 | for(ii=0; ii<pConfig->nCol; ii++){ | |||
| 21067 | if( pConfig->abUnindexed[ii]==0 ){ | |||
| 21068 | if( sqlite3_value_nochangesqlite3_api->value_nochange(apVal[ii]) ){ | |||
| 21069 | bSeenIndexNC++; | |||
| 21070 | }else{ | |||
| 21071 | bSeenIndex++; | |||
| 21072 | } | |||
| 21073 | } | |||
| 21074 | } | |||
| 21075 | ||||
| 21076 | if( bSeenIndex==0 && bRowidModified==0 ){ | |||
| 21077 | *pbContent = 1; | |||
| 21078 | }else{ | |||
| 21079 | if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){ | |||
| 21080 | rc = SQLITE_ERROR1; | |||
| 21081 | sqlite3Fts5ConfigErrmsg(pConfig, | |||
| 21082 | (pConfig->bContentlessDelete ? | |||
| 21083 | "%s a subset of columns on fts5 contentless-delete table: %s" : | |||
| 21084 | "%s contentless fts5 table: %s") | |||
| 21085 | , "cannot UPDATE", pConfig->zName | |||
| 21086 | ); | |||
| 21087 | } | |||
| 21088 | } | |||
| 21089 | ||||
| 21090 | return rc; | |||
| 21091 | } | |||
| 21092 | ||||
| 21093 | /* | |||
| 21094 | ** This function is the implementation of the xUpdate callback used by | |||
| 21095 | ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be | |||
| 21096 | ** inserted, updated or deleted. | |||
| 21097 | ** | |||
| 21098 | ** A delete specifies a single argument - the rowid of the row to remove. | |||
| 21099 | ** | |||
| 21100 | ** Update and insert operations pass: | |||
| 21101 | ** | |||
| 21102 | ** 1. The "old" rowid, or NULL. | |||
| 21103 | ** 2. The "new" rowid. | |||
| 21104 | ** 3. Values for each of the nCol matchable columns. | |||
| 21105 | ** 4. Values for the two hidden columns (<tablename> and "rank"). | |||
| 21106 | */ | |||
| 21107 | static int fts5UpdateMethod( | |||
| 21108 | sqlite3_vtab *pVtab, /* Virtual table handle */ | |||
| 21109 | int nArg, /* Size of argument array */ | |||
| 21110 | sqlite3_value **apVal, /* Array of arguments */ | |||
| 21111 | sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ | |||
| 21112 | ){ | |||
| 21113 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 21114 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 21115 | int eType0; /* value_type() of apVal[0] */ | |||
| 21116 | int rc = SQLITE_OK0; /* Return code */ | |||
| 21117 | ||||
| 21118 | /* A transaction must be open when this is called. */ | |||
| 21119 | assert( pTab->ts.eState==1 || pTab->ts.eState==2 )((void) (0)); | |||
| 21120 | ||||
| 21121 | assert( pVtab->zErrMsg==0 )((void) (0)); | |||
| 21122 | assert( nArg==1 || nArg==(2+pConfig->nCol+2) )((void) (0)); | |||
| 21123 | assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER((void) (0)) | |||
| 21124 | || sqlite3_value_type(apVal[0])==SQLITE_NULL((void) (0)) | |||
| 21125 | )((void) (0)); | |||
| 21126 | assert( pTab->p.pConfig->pzErrmsg==0 )((void) (0)); | |||
| 21127 | if( pConfig->pgsz==0 ){ | |||
| ||||
| 21128 | rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie); | |||
| 21129 | if( rc!=SQLITE_OK0 ) return rc; | |||
| 21130 | } | |||
| 21131 | ||||
| 21132 | pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; | |||
| 21133 | ||||
| 21134 | /* Put any active cursors into REQUIRE_SEEK state. */ | |||
| 21135 | fts5TripCursors(pTab); | |||
| 21136 | ||||
| 21137 | eType0 = sqlite3_value_typesqlite3_api->value_type(apVal[0]); | |||
| 21138 | if( eType0==SQLITE_NULL5 | |||
| 21139 | && sqlite3_value_typesqlite3_api->value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL5 | |||
| 21140 | ){ | |||
| 21141 | /* A "special" INSERT op. These are handled separately. */ | |||
| 21142 | const char *z = (const char*)sqlite3_value_textsqlite3_api->value_text(apVal[2+pConfig->nCol]); | |||
| 21143 | if( pConfig->eContent!=FTS5_CONTENT_NORMAL0 | |||
| 21144 | && 0==sqlite3_stricmpsqlite3_api->stricmp("delete", z) | |||
| 21145 | ){ | |||
| 21146 | if( pConfig->bContentlessDelete ){ | |||
| 21147 | fts5SetVtabError(pTab, | |||
| 21148 | "'delete' may not be used with a contentless_delete=1 table" | |||
| 21149 | ); | |||
| 21150 | rc = SQLITE_ERROR1; | |||
| 21151 | }else{ | |||
| 21152 | rc = fts5SpecialDelete(pTab, apVal); | |||
| 21153 | } | |||
| 21154 | }else{ | |||
| 21155 | rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); | |||
| 21156 | } | |||
| 21157 | }else{ | |||
| 21158 | /* A regular INSERT, UPDATE or DELETE statement. The trick here is that | |||
| 21159 | ** any conflict on the rowid value must be detected before any | |||
| 21160 | ** modifications are made to the database file. There are 4 cases: | |||
| 21161 | ** | |||
| 21162 | ** 1) DELETE | |||
| 21163 | ** 2) UPDATE (rowid not modified) | |||
| 21164 | ** 3) UPDATE (rowid modified) | |||
| 21165 | ** 4) INSERT | |||
| 21166 | ** | |||
| 21167 | ** Cases 3 and 4 may violate the rowid constraint. | |||
| 21168 | */ | |||
| 21169 | int eConflict = SQLITE_ABORT4; | |||
| 21170 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 || pConfig->bContentlessDelete ){ | |||
| 21171 | eConflict = sqlite3_vtab_on_conflictsqlite3_api->vtab_on_conflict(pConfig->db); | |||
| 21172 | } | |||
| 21173 | ||||
| 21174 | assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL )((void) (0)); | |||
| 21175 | assert( nArg!=1 || eType0==SQLITE_INTEGER )((void) (0)); | |||
| 21176 | ||||
| 21177 | /* DELETE */ | |||
| 21178 | if( nArg==1 ){ | |||
| 21179 | /* It is only possible to DELETE from a contentless table if the | |||
| 21180 | ** contentless_delete=1 flag is set. */ | |||
| 21181 | if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){ | |||
| 21182 | fts5SetVtabError(pTab, | |||
| 21183 | "cannot DELETE from contentless fts5 table: %s", pConfig->zName | |||
| 21184 | ); | |||
| 21185 | rc = SQLITE_ERROR1; | |||
| 21186 | }else{ | |||
| 21187 | i64 iDel = sqlite3_value_int64sqlite3_api->value_int64(apVal[0]); /* Rowid to delete */ | |||
| 21188 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); | |||
| 21189 | } | |||
| 21190 | } | |||
| 21191 | ||||
| 21192 | /* INSERT or UPDATE */ | |||
| 21193 | else{ | |||
| 21194 | int eType1 = sqlite3_value_numeric_typesqlite3_api->value_numeric_type(apVal[1]); | |||
| 21195 | ||||
| 21196 | /* It is an error to write an fts5_locale() value to a table without | |||
| 21197 | ** the locale=1 option. */ | |||
| 21198 | if( pConfig->bLocale==0 ){ | |||
| 21199 | int ii; | |||
| 21200 | for(ii=0; ii<pConfig->nCol; ii++){ | |||
| 21201 | sqlite3_value *pVal = apVal[ii+2]; | |||
| 21202 | if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |||
| 21203 | fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); | |||
| 21204 | rc = SQLITE_MISMATCH20; | |||
| 21205 | goto update_out; | |||
| 21206 | } | |||
| 21207 | } | |||
| 21208 | } | |||
| 21209 | ||||
| 21210 | if( eType0!=SQLITE_INTEGER1 ){ | |||
| 21211 | /* An INSERT statement. If the conflict-mode is REPLACE, first remove | |||
| 21212 | ** the current entry (if any). */ | |||
| 21213 | if( eConflict==SQLITE_REPLACE5 && eType1==SQLITE_INTEGER1 ){ | |||
| 21214 | i64 iNew = sqlite3_value_int64sqlite3_api->value_int64(apVal[1]); /* Rowid to delete */ | |||
| 21215 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); | |||
| 21216 | } | |||
| 21217 | fts5StorageInsert(&rc, pTab, apVal, pRowid); | |||
| 21218 | } | |||
| 21219 | ||||
| 21220 | /* UPDATE */ | |||
| 21221 | else{ | |||
| 21222 | Fts5Storage *pStorage = pTab->pStorage; | |||
| 21223 | i64 iOld = sqlite3_value_int64sqlite3_api->value_int64(apVal[0]); /* Old rowid */ | |||
| 21224 | i64 iNew = sqlite3_value_int64sqlite3_api->value_int64(apVal[1]); /* New rowid */ | |||
| 21225 | int bContent = 0; /* Content only update */ | |||
| 21226 | ||||
| 21227 | /* If this is a contentless table (including contentless_unindexed=1 | |||
| 21228 | ** tables), check if the UPDATE may proceed. */ | |||
| 21229 | if( fts5IsContentless(pTab, 1) ){ | |||
| 21230 | rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent); | |||
| 21231 | if( rc!=SQLITE_OK0 ) goto update_out; | |||
| 21232 | } | |||
| 21233 | ||||
| 21234 | if( eType1!=SQLITE_INTEGER1 ){ | |||
| 21235 | rc = SQLITE_MISMATCH20; | |||
| 21236 | }else if( iOld!=iNew ){ | |||
| 21237 | assert( bContent==0 )((void) (0)); | |||
| 21238 | if( eConflict==SQLITE_REPLACE5 ){ | |||
| 21239 | rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); | |||
| 21240 | if( rc==SQLITE_OK0 ){ | |||
| 21241 | rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0); | |||
| 21242 | } | |||
| 21243 | fts5StorageInsert(&rc, pTab, apVal, pRowid); | |||
| 21244 | }else{ | |||
| 21245 | rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); | |||
| 21246 | if( rc==SQLITE_OK0 ){ | |||
| 21247 | rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid); | |||
| 21248 | } | |||
| 21249 | if( rc==SQLITE_OK0 ){ | |||
| 21250 | rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0); | |||
| 21251 | } | |||
| 21252 | if( rc==SQLITE_OK0 ){ | |||
| 21253 | rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid); | |||
| 21254 | } | |||
| 21255 | } | |||
| 21256 | }else if( bContent ){ | |||
| 21257 | /* This occurs when an UPDATE on a contentless table affects *only* | |||
| 21258 | ** UNINDEXED columns. This is a no-op for contentless_unindexed=0 | |||
| 21259 | ** tables, or a write to the %_content table only for =1 tables. */ | |||
| 21260 | assert( fts5IsContentless(pTab, 1) )((void) (0)); | |||
| 21261 | rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); | |||
| 21262 | if( rc==SQLITE_OK0 ){ | |||
| 21263 | rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid); | |||
| 21264 | } | |||
| 21265 | }else{ | |||
| 21266 | rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); | |||
| 21267 | fts5StorageInsert(&rc, pTab, apVal, pRowid); | |||
| 21268 | } | |||
| 21269 | sqlite3Fts5StorageReleaseDeleteRow(pStorage); | |||
| 21270 | } | |||
| 21271 | } | |||
| 21272 | } | |||
| 21273 | ||||
| 21274 | update_out: | |||
| 21275 | sqlite3Fts5IndexCloseReader(pTab->p.pIndex); | |||
| 21276 | pTab->p.pConfig->pzErrmsg = 0; | |||
| 21277 | return rc; | |||
| 21278 | } | |||
| 21279 | ||||
| 21280 | /* | |||
| 21281 | ** Implementation of xSync() method. | |||
| 21282 | */ | |||
| 21283 | static int fts5SyncMethod(sqlite3_vtab *pVtab){ | |||
| 21284 | int rc; | |||
| 21285 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 21286 | fts5CheckTransactionState(pTab, FTS5_SYNC, 0); | |||
| 21287 | pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; | |||
| 21288 | rc = sqlite3Fts5FlushToDisk(&pTab->p); | |||
| 21289 | pTab->p.pConfig->pzErrmsg = 0; | |||
| 21290 | return rc; | |||
| 21291 | } | |||
| 21292 | ||||
| 21293 | /* | |||
| 21294 | ** Implementation of xBegin() method. | |||
| 21295 | */ | |||
| 21296 | static int fts5BeginMethod(sqlite3_vtab *pVtab){ | |||
| 21297 | int rc = fts5NewTransaction((Fts5FullTable*)pVtab); | |||
| 21298 | if( rc==SQLITE_OK0 ){ | |||
| 21299 | fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); | |||
| 21300 | } | |||
| 21301 | return rc; | |||
| 21302 | } | |||
| 21303 | ||||
| 21304 | /* | |||
| 21305 | ** Implementation of xCommit() method. This is a no-op. The contents of | |||
| 21306 | ** the pending-terms hash-table have already been flushed into the database | |||
| 21307 | ** by fts5SyncMethod(). | |||
| 21308 | */ | |||
| 21309 | static int fts5CommitMethod(sqlite3_vtab *pVtab){ | |||
| 21310 | UNUSED_PARAM(pVtab)(void)(pVtab); /* Call below is a no-op for NDEBUG builds */ | |||
| 21311 | fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_COMMIT, 0); | |||
| 21312 | return SQLITE_OK0; | |||
| 21313 | } | |||
| 21314 | ||||
| 21315 | /* | |||
| 21316 | ** Implementation of xRollback(). Discard the contents of the pending-terms | |||
| 21317 | ** hash-table. Any changes made to the database are reverted by SQLite. | |||
| 21318 | */ | |||
| 21319 | static int fts5RollbackMethod(sqlite3_vtab *pVtab){ | |||
| 21320 | int rc; | |||
| 21321 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 21322 | fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); | |||
| 21323 | rc = sqlite3Fts5StorageRollback(pTab->pStorage); | |||
| 21324 | pTab->p.pConfig->pgsz = 0; | |||
| 21325 | return rc; | |||
| 21326 | } | |||
| 21327 | ||||
| 21328 | static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*); | |||
| 21329 | ||||
| 21330 | static void *fts5ApiUserData(Fts5Context *pCtx){ | |||
| 21331 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21332 | return pCsr->pAux->pUserData; | |||
| 21333 | } | |||
| 21334 | ||||
| 21335 | static int fts5ApiColumnCount(Fts5Context *pCtx){ | |||
| 21336 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21337 | return ((Fts5Table*)(pCsr->base.pVtab))->pConfig->nCol; | |||
| 21338 | } | |||
| 21339 | ||||
| 21340 | static int fts5ApiColumnTotalSize( | |||
| 21341 | Fts5Context *pCtx, | |||
| 21342 | int iCol, | |||
| 21343 | sqlite3_int64 *pnToken | |||
| 21344 | ){ | |||
| 21345 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21346 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 21347 | return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken); | |||
| 21348 | } | |||
| 21349 | ||||
| 21350 | static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ | |||
| 21351 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21352 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 21353 | return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); | |||
| 21354 | } | |||
| 21355 | ||||
| 21356 | /* | |||
| 21357 | ** Implementation of xTokenize_v2() API. | |||
| 21358 | */ | |||
| 21359 | static int fts5ApiTokenize_v2( | |||
| 21360 | Fts5Context *pCtx, | |||
| 21361 | const char *pText, int nText, | |||
| 21362 | const char *pLoc, int nLoc, | |||
| 21363 | void *pUserData, | |||
| 21364 | int (*xToken)(void*, int, const char*, int, int, int) | |||
| 21365 | ){ | |||
| 21366 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21367 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |||
| 21368 | int rc = SQLITE_OK0; | |||
| 21369 | ||||
| 21370 | sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc); | |||
| 21371 | rc = sqlite3Fts5Tokenize(pTab->pConfig, | |||
| 21372 | FTS5_TOKENIZE_AUX0x0008, pText, nText, pUserData, xToken | |||
| 21373 | ); | |||
| 21374 | sqlite3Fts5SetLocale(pTab->pConfig, 0, 0); | |||
| 21375 | ||||
| 21376 | return rc; | |||
| 21377 | } | |||
| 21378 | ||||
| 21379 | /* | |||
| 21380 | ** Implementation of xTokenize() API. This is just xTokenize_v2() with NULL/0 | |||
| 21381 | ** passed as the locale. | |||
| 21382 | */ | |||
| 21383 | static int fts5ApiTokenize( | |||
| 21384 | Fts5Context *pCtx, | |||
| 21385 | const char *pText, int nText, | |||
| 21386 | void *pUserData, | |||
| 21387 | int (*xToken)(void*, int, const char*, int, int, int) | |||
| 21388 | ){ | |||
| 21389 | return fts5ApiTokenize_v2(pCtx, pText, nText, 0, 0, pUserData, xToken); | |||
| 21390 | } | |||
| 21391 | ||||
| 21392 | static int fts5ApiPhraseCount(Fts5Context *pCtx){ | |||
| 21393 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21394 | return sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |||
| 21395 | } | |||
| 21396 | ||||
| 21397 | static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ | |||
| 21398 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21399 | return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); | |||
| 21400 | } | |||
| 21401 | ||||
| 21402 | /* | |||
| 21403 | ** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This | |||
| 21404 | ** function extracts the text value of column iCol of the current row. | |||
| 21405 | ** Additionally, if there is an associated locale, it invokes | |||
| 21406 | ** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller | |||
| 21407 | ** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point | |||
| 21408 | ** after this function returns. | |||
| 21409 | ** | |||
| 21410 | ** If successful, (*ppText) is set to point to a buffer containing the text | |||
| 21411 | ** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that | |||
| 21412 | ** buffer in bytes. It is not guaranteed to be nul-terminated. If an error | |||
| 21413 | ** occurs, an SQLite error code is returned. The final values of the two | |||
| 21414 | ** output parameters are undefined in this case. | |||
| 21415 | */ | |||
| 21416 | static int fts5TextFromStmt( | |||
| 21417 | Fts5Config *pConfig, | |||
| 21418 | sqlite3_stmt *pStmt, | |||
| 21419 | int iCol, | |||
| 21420 | const char **ppText, | |||
| 21421 | int *pnText | |||
| 21422 | ){ | |||
| 21423 | sqlite3_value *pVal = sqlite3_column_valuesqlite3_api->column_value(pStmt, iCol+1); | |||
| 21424 | const char *pLoc = 0; | |||
| 21425 | int nLoc = 0; | |||
| 21426 | int rc = SQLITE_OK0; | |||
| 21427 | ||||
| 21428 | if( pConfig->bLocale | |||
| 21429 | && pConfig->eContent==FTS5_CONTENT_EXTERNAL2 | |||
| 21430 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |||
| 21431 | ){ | |||
| 21432 | rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc); | |||
| 21433 | }else{ | |||
| 21434 | *ppText = (const char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 21435 | *pnText = sqlite3_value_bytessqlite3_api->value_bytes(pVal); | |||
| 21436 | if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 21437 | pLoc = (const char*)sqlite3_column_textsqlite3_api->column_text(pStmt, iCol+1+pConfig->nCol); | |||
| 21438 | nLoc = sqlite3_column_bytessqlite3_api->column_bytes(pStmt, iCol+1+pConfig->nCol); | |||
| 21439 | } | |||
| 21440 | } | |||
| 21441 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |||
| 21442 | return rc; | |||
| 21443 | } | |||
| 21444 | ||||
| 21445 | static int fts5ApiColumnText( | |||
| 21446 | Fts5Context *pCtx, | |||
| 21447 | int iCol, | |||
| 21448 | const char **pz, | |||
| 21449 | int *pn | |||
| 21450 | ){ | |||
| 21451 | int rc = SQLITE_OK0; | |||
| 21452 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21453 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |||
| 21454 | ||||
| 21455 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL )((void) (0)); | |||
| 21456 | if( iCol<0 || iCol>=pTab->pConfig->nCol ){ | |||
| 21457 | rc = SQLITE_RANGE25; | |||
| 21458 | }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){ | |||
| 21459 | *pz = 0; | |||
| 21460 | *pn = 0; | |||
| 21461 | }else{ | |||
| 21462 | rc = fts5SeekCursor(pCsr, 0); | |||
| 21463 | if( rc==SQLITE_OK0 ){ | |||
| 21464 | rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn); | |||
| 21465 | sqlite3Fts5ClearLocale(pTab->pConfig); | |||
| 21466 | } | |||
| 21467 | } | |||
| 21468 | return rc; | |||
| 21469 | } | |||
| 21470 | ||||
| 21471 | /* | |||
| 21472 | ** This is called by various API functions - xInst, xPhraseFirst, | |||
| 21473 | ** xPhraseFirstColumn etc. - to obtain the position list for phrase iPhrase | |||
| 21474 | ** of the current row. This function works for both detail=full tables (in | |||
| 21475 | ** which case the position-list was read from the fts index) or for other | |||
| 21476 | ** detail= modes if the row content is available. | |||
| 21477 | */ | |||
| 21478 | static int fts5CsrPoslist( | |||
| 21479 | Fts5Cursor *pCsr, /* Fts5 cursor object */ | |||
| 21480 | int iPhrase, /* Phrase to find position list for */ | |||
| 21481 | const u8 **pa, /* OUT: Pointer to position list buffer */ | |||
| 21482 | int *pn /* OUT: Size of (*pa) in bytes */ | |||
| 21483 | ){ | |||
| 21484 | Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; | |||
| 21485 | int rc = SQLITE_OK0; | |||
| 21486 | int bLive = (pCsr->pSorter==0); | |||
| 21487 | ||||
| 21488 | if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ | |||
| 21489 | rc = SQLITE_RANGE25; | |||
| 21490 | }else if( pConfig->eDetail!=FTS5_DETAIL_FULL0 | |||
| 21491 | && fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) | |||
| 21492 | ){ | |||
| 21493 | *pa = 0; | |||
| 21494 | *pn = 0; | |||
| 21495 | return SQLITE_OK0; | |||
| 21496 | }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST)((pCsr)->csrflags & (0x40)) ){ | |||
| 21497 | if( pConfig->eDetail!=FTS5_DETAIL_FULL0 ){ | |||
| 21498 | Fts5PoslistPopulator *aPopulator; | |||
| 21499 | int i; | |||
| 21500 | ||||
| 21501 | aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); | |||
| 21502 | if( aPopulator==0 ) rc = SQLITE_NOMEM7; | |||
| 21503 | if( rc==SQLITE_OK0 ){ | |||
| 21504 | rc = fts5SeekCursor(pCsr, 0); | |||
| 21505 | } | |||
| 21506 | for(i=0; i<pConfig->nCol && rc==SQLITE_OK0; i++){ | |||
| 21507 | const char *z = 0; | |||
| 21508 | int n = 0; | |||
| 21509 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); | |||
| 21510 | if( rc==SQLITE_OK0 ){ | |||
| 21511 | rc = sqlite3Fts5ExprPopulatePoslists( | |||
| 21512 | pConfig, pCsr->pExpr, aPopulator, i, z, n | |||
| 21513 | ); | |||
| 21514 | } | |||
| 21515 | sqlite3Fts5ClearLocale(pConfig); | |||
| 21516 | } | |||
| 21517 | sqlite3_freesqlite3_api->free(aPopulator); | |||
| 21518 | ||||
| 21519 | if( pCsr->pSorter ){ | |||
| 21520 | sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); | |||
| 21521 | } | |||
| 21522 | } | |||
| 21523 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST)((pCsr)->csrflags &= ~(0x40)); | |||
| 21524 | } | |||
| 21525 | ||||
| 21526 | if( rc==SQLITE_OK0 ){ | |||
| 21527 | if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 21528 | Fts5Sorter *pSorter = pCsr->pSorter; | |||
| 21529 | int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); | |||
| 21530 | *pn = pSorter->aIdx[iPhrase] - i1; | |||
| 21531 | *pa = &pSorter->aPoslist[i1]; | |||
| 21532 | }else{ | |||
| 21533 | *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); | |||
| 21534 | } | |||
| 21535 | }else{ | |||
| 21536 | *pa = 0; | |||
| 21537 | *pn = 0; | |||
| 21538 | } | |||
| 21539 | ||||
| 21540 | return rc; | |||
| 21541 | } | |||
| 21542 | ||||
| 21543 | /* | |||
| 21544 | ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated | |||
| 21545 | ** correctly for the current view. Return SQLITE_OK if successful, or an | |||
| 21546 | ** SQLite error code otherwise. | |||
| 21547 | */ | |||
| 21548 | static int fts5CacheInstArray(Fts5Cursor *pCsr){ | |||
| 21549 | int rc = SQLITE_OK0; | |||
| 21550 | Fts5PoslistReader *aIter; /* One iterator for each phrase */ | |||
| 21551 | int nIter; /* Number of iterators/phrases */ | |||
| 21552 | int nCol = ((Fts5Table*)pCsr->base.pVtab)->pConfig->nCol; | |||
| 21553 | ||||
| 21554 | nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |||
| 21555 | if( pCsr->aInstIter==0 ){ | |||
| 21556 | sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nIter; | |||
| 21557 | pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 21558 | } | |||
| 21559 | aIter = pCsr->aInstIter; | |||
| 21560 | ||||
| 21561 | if( aIter ){ | |||
| 21562 | int nInst = 0; /* Number instances seen so far */ | |||
| 21563 | int i; | |||
| 21564 | ||||
| 21565 | /* Initialize all iterators */ | |||
| 21566 | for(i=0; i<nIter && rc==SQLITE_OK0; i++){ | |||
| 21567 | const u8 *a; | |||
| 21568 | int n; | |||
| 21569 | rc = fts5CsrPoslist(pCsr, i, &a, &n); | |||
| 21570 | if( rc==SQLITE_OK0 ){ | |||
| 21571 | sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); | |||
| 21572 | } | |||
| 21573 | } | |||
| 21574 | ||||
| 21575 | if( rc==SQLITE_OK0 ){ | |||
| 21576 | while( 1 ){ | |||
| 21577 | int *aInst; | |||
| 21578 | int iBest = -1; | |||
| 21579 | for(i=0; i<nIter; i++){ | |||
| 21580 | if( (aIter[i].bEof==0) | |||
| 21581 | && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos) | |||
| 21582 | ){ | |||
| 21583 | iBest = i; | |||
| 21584 | } | |||
| 21585 | } | |||
| 21586 | if( iBest<0 ) break; | |||
| 21587 | ||||
| 21588 | nInst++; | |||
| 21589 | if( nInst>=pCsr->nInstAlloc ){ | |||
| 21590 | int nNewSize = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; | |||
| 21591 | aInst = (int*)sqlite3_realloc64sqlite3_api->realloc64( | |||
| 21592 | pCsr->aInst, nNewSize*sizeof(int)*3 | |||
| 21593 | ); | |||
| 21594 | if( aInst ){ | |||
| 21595 | pCsr->aInst = aInst; | |||
| 21596 | pCsr->nInstAlloc = nNewSize; | |||
| 21597 | }else{ | |||
| 21598 | nInst--; | |||
| 21599 | rc = SQLITE_NOMEM7; | |||
| 21600 | break; | |||
| 21601 | } | |||
| 21602 | } | |||
| 21603 | ||||
| 21604 | aInst = &pCsr->aInst[3 * (nInst-1)]; | |||
| 21605 | aInst[0] = iBest; | |||
| 21606 | aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos)(int)((aIter[iBest].iPos >> 32) & 0x7FFFFFFF); | |||
| 21607 | aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos)(int)(aIter[iBest].iPos & 0x7FFFFFFF); | |||
| 21608 | assert( aInst[1]>=0 )((void) (0)); | |||
| 21609 | if( aInst[1]>=nCol ){ | |||
| 21610 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 21611 | break; | |||
| 21612 | } | |||
| 21613 | sqlite3Fts5PoslistReaderNext(&aIter[iBest]); | |||
| 21614 | } | |||
| 21615 | } | |||
| 21616 | ||||
| 21617 | pCsr->nInstCount = nInst; | |||
| 21618 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST)((pCsr)->csrflags &= ~(0x08)); | |||
| 21619 | } | |||
| 21620 | return rc; | |||
| 21621 | } | |||
| 21622 | ||||
| 21623 | static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){ | |||
| 21624 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21625 | int rc = SQLITE_OK0; | |||
| 21626 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)((pCsr)->csrflags & (0x08))==0 | |||
| 21627 | || SQLITE_OK0==(rc = fts5CacheInstArray(pCsr)) ){ | |||
| 21628 | *pnInst = pCsr->nInstCount; | |||
| 21629 | } | |||
| 21630 | return rc; | |||
| 21631 | } | |||
| 21632 | ||||
| 21633 | static int fts5ApiInst( | |||
| 21634 | Fts5Context *pCtx, | |||
| 21635 | int iIdx, | |||
| 21636 | int *piPhrase, | |||
| 21637 | int *piCol, | |||
| 21638 | int *piOff | |||
| 21639 | ){ | |||
| 21640 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21641 | int rc = SQLITE_OK0; | |||
| 21642 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)((pCsr)->csrflags & (0x08))==0 | |||
| 21643 | || SQLITE_OK0==(rc = fts5CacheInstArray(pCsr)) | |||
| 21644 | ){ | |||
| 21645 | if( iIdx<0 || iIdx>=pCsr->nInstCount ){ | |||
| 21646 | rc = SQLITE_RANGE25; | |||
| 21647 | }else{ | |||
| 21648 | *piPhrase = pCsr->aInst[iIdx*3]; | |||
| 21649 | *piCol = pCsr->aInst[iIdx*3 + 1]; | |||
| 21650 | *piOff = pCsr->aInst[iIdx*3 + 2]; | |||
| 21651 | } | |||
| 21652 | } | |||
| 21653 | return rc; | |||
| 21654 | } | |||
| 21655 | ||||
| 21656 | static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ | |||
| 21657 | return fts5CursorRowid((Fts5Cursor*)pCtx); | |||
| 21658 | } | |||
| 21659 | ||||
| 21660 | static int fts5ColumnSizeCb( | |||
| 21661 | void *pContext, /* Pointer to int */ | |||
| 21662 | int tflags, | |||
| 21663 | const char *pUnused, /* Buffer containing token */ | |||
| 21664 | int nUnused, /* Size of token in bytes */ | |||
| 21665 | int iUnused1, /* Start offset of token */ | |||
| 21666 | int iUnused2 /* End offset of token */ | |||
| 21667 | ){ | |||
| 21668 | int *pCnt = (int*)pContext; | |||
| 21669 | UNUSED_PARAM2(pUnused, nUnused)(void)(pUnused), (void)(nUnused); | |||
| 21670 | UNUSED_PARAM2(iUnused1, iUnused2)(void)(iUnused1), (void)(iUnused2); | |||
| 21671 | if( (tflags & FTS5_TOKEN_COLOCATED0x0001)==0 ){ | |||
| 21672 | (*pCnt)++; | |||
| 21673 | } | |||
| 21674 | return SQLITE_OK0; | |||
| 21675 | } | |||
| 21676 | ||||
| 21677 | static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ | |||
| 21678 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21679 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 21680 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 21681 | int rc = SQLITE_OK0; | |||
| 21682 | ||||
| 21683 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE)((pCsr)->csrflags & (0x04)) ){ | |||
| 21684 | if( pConfig->bColumnsize ){ | |||
| 21685 | i64 iRowid = fts5CursorRowid(pCsr); | |||
| 21686 | rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); | |||
| 21687 | }else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED3 ){ | |||
| 21688 | int i; | |||
| 21689 | for(i=0; i<pConfig->nCol; i++){ | |||
| 21690 | if( pConfig->abUnindexed[i]==0 ){ | |||
| 21691 | pCsr->aColumnSize[i] = -1; | |||
| 21692 | } | |||
| 21693 | } | |||
| 21694 | }else{ | |||
| 21695 | int i; | |||
| 21696 | rc = fts5SeekCursor(pCsr, 0); | |||
| 21697 | for(i=0; rc==SQLITE_OK0 && i<pConfig->nCol; i++){ | |||
| 21698 | if( pConfig->abUnindexed[i]==0 ){ | |||
| 21699 | const char *z = 0; | |||
| 21700 | int n = 0; | |||
| 21701 | pCsr->aColumnSize[i] = 0; | |||
| 21702 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); | |||
| 21703 | if( rc==SQLITE_OK0 ){ | |||
| 21704 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX0x0008, | |||
| 21705 | z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb | |||
| 21706 | ); | |||
| 21707 | } | |||
| 21708 | sqlite3Fts5ClearLocale(pConfig); | |||
| 21709 | } | |||
| 21710 | } | |||
| 21711 | } | |||
| 21712 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE)((pCsr)->csrflags &= ~(0x04)); | |||
| 21713 | } | |||
| 21714 | if( iCol<0 ){ | |||
| 21715 | int i; | |||
| 21716 | *pnToken = 0; | |||
| 21717 | for(i=0; i<pConfig->nCol; i++){ | |||
| 21718 | *pnToken += pCsr->aColumnSize[i]; | |||
| 21719 | } | |||
| 21720 | }else if( iCol<pConfig->nCol ){ | |||
| 21721 | *pnToken = pCsr->aColumnSize[iCol]; | |||
| 21722 | }else{ | |||
| 21723 | *pnToken = 0; | |||
| 21724 | rc = SQLITE_RANGE25; | |||
| 21725 | } | |||
| 21726 | return rc; | |||
| 21727 | } | |||
| 21728 | ||||
| 21729 | /* | |||
| 21730 | ** Implementation of the xSetAuxdata() method. | |||
| 21731 | */ | |||
| 21732 | static int fts5ApiSetAuxdata( | |||
| 21733 | Fts5Context *pCtx, /* Fts5 context */ | |||
| 21734 | void *pPtr, /* Pointer to save as auxdata */ | |||
| 21735 | void(*xDelete)(void*) /* Destructor for pPtr (or NULL) */ | |||
| 21736 | ){ | |||
| 21737 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21738 | Fts5Auxdata *pData; | |||
| 21739 | ||||
| 21740 | /* Search through the cursors list of Fts5Auxdata objects for one that | |||
| 21741 | ** corresponds to the currently executing auxiliary function. */ | |||
| 21742 | for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){ | |||
| 21743 | if( pData->pAux==pCsr->pAux ) break; | |||
| 21744 | } | |||
| 21745 | ||||
| 21746 | if( pData ){ | |||
| 21747 | if( pData->xDelete ){ | |||
| 21748 | pData->xDelete(pData->pPtr); | |||
| 21749 | } | |||
| 21750 | }else{ | |||
| 21751 | int rc = SQLITE_OK0; | |||
| 21752 | pData = (Fts5Auxdata*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Auxdata)); | |||
| 21753 | if( pData==0 ){ | |||
| 21754 | if( xDelete ) xDelete(pPtr); | |||
| 21755 | return rc; | |||
| 21756 | } | |||
| 21757 | pData->pAux = pCsr->pAux; | |||
| 21758 | pData->pNext = pCsr->pAuxdata; | |||
| 21759 | pCsr->pAuxdata = pData; | |||
| 21760 | } | |||
| 21761 | ||||
| 21762 | pData->xDelete = xDelete; | |||
| 21763 | pData->pPtr = pPtr; | |||
| 21764 | return SQLITE_OK0; | |||
| 21765 | } | |||
| 21766 | ||||
| 21767 | static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ | |||
| 21768 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21769 | Fts5Auxdata *pData; | |||
| 21770 | void *pRet = 0; | |||
| 21771 | ||||
| 21772 | for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){ | |||
| 21773 | if( pData->pAux==pCsr->pAux ) break; | |||
| 21774 | } | |||
| 21775 | ||||
| 21776 | if( pData ){ | |||
| 21777 | pRet = pData->pPtr; | |||
| 21778 | if( bClear ){ | |||
| 21779 | pData->pPtr = 0; | |||
| 21780 | pData->xDelete = 0; | |||
| 21781 | } | |||
| 21782 | } | |||
| 21783 | ||||
| 21784 | return pRet; | |||
| 21785 | } | |||
| 21786 | ||||
| 21787 | static void fts5ApiPhraseNext( | |||
| 21788 | Fts5Context *pCtx, | |||
| 21789 | Fts5PhraseIter *pIter, | |||
| 21790 | int *piCol, int *piOff | |||
| 21791 | ){ | |||
| 21792 | if( pIter->a>=pIter->b ){ | |||
| 21793 | *piCol = -1; | |||
| 21794 | *piOff = -1; | |||
| 21795 | }else{ | |||
| 21796 | int iVal; | |||
| 21797 | pIter->a += fts5GetVarint32(pIter->a, iVal)sqlite3Fts5GetVarint32(pIter->a,(u32*)&(iVal)); | |||
| 21798 | if( iVal==1 ){ | |||
| 21799 | /* Avoid returning a (*piCol) value that is too large for the table, | |||
| 21800 | ** even if the position-list is corrupt. The caller might not be | |||
| 21801 | ** expecting it. */ | |||
| 21802 | int nCol = ((Fts5Table*)(((Fts5Cursor*)pCtx)->base.pVtab))->pConfig->nCol; | |||
| 21803 | pIter->a += fts5GetVarint32(pIter->a, iVal)sqlite3Fts5GetVarint32(pIter->a,(u32*)&(iVal)); | |||
| 21804 | *piCol = (iVal>=nCol ? nCol-1 : iVal); | |||
| 21805 | *piOff = 0; | |||
| 21806 | pIter->a += fts5GetVarint32(pIter->a, iVal)sqlite3Fts5GetVarint32(pIter->a,(u32*)&(iVal)); | |||
| 21807 | } | |||
| 21808 | *piOff += (iVal-2); | |||
| 21809 | } | |||
| 21810 | } | |||
| 21811 | ||||
| 21812 | static int fts5ApiPhraseFirst( | |||
| 21813 | Fts5Context *pCtx, | |||
| 21814 | int iPhrase, | |||
| 21815 | Fts5PhraseIter *pIter, | |||
| 21816 | int *piCol, int *piOff | |||
| 21817 | ){ | |||
| 21818 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21819 | int n; | |||
| 21820 | int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); | |||
| 21821 | if( rc==SQLITE_OK0 ){ | |||
| 21822 | assert( pIter->a || n==0 )((void) (0)); | |||
| 21823 | pIter->b = (pIter->a ? &pIter->a[n] : 0); | |||
| 21824 | *piCol = 0; | |||
| 21825 | *piOff = 0; | |||
| 21826 | fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); | |||
| 21827 | } | |||
| 21828 | return rc; | |||
| 21829 | } | |||
| 21830 | ||||
| 21831 | static void fts5ApiPhraseNextColumn( | |||
| 21832 | Fts5Context *pCtx, | |||
| 21833 | Fts5PhraseIter *pIter, | |||
| 21834 | int *piCol | |||
| 21835 | ){ | |||
| 21836 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21837 | Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; | |||
| 21838 | ||||
| 21839 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS2 ){ | |||
| 21840 | if( pIter->a>=pIter->b ){ | |||
| 21841 | *piCol = -1; | |||
| 21842 | }else{ | |||
| 21843 | int iIncr; | |||
| 21844 | pIter->a += fts5GetVarint32(&pIter->a[0], iIncr)sqlite3Fts5GetVarint32(&pIter->a[0],(u32*)&(iIncr) ); | |||
| 21845 | *piCol += (iIncr-2); | |||
| 21846 | } | |||
| 21847 | }else{ | |||
| 21848 | while( 1 ){ | |||
| 21849 | int dummy; | |||
| 21850 | if( pIter->a>=pIter->b ){ | |||
| 21851 | *piCol = -1; | |||
| 21852 | return; | |||
| 21853 | } | |||
| 21854 | if( pIter->a[0]==0x01 ) break; | |||
| 21855 | pIter->a += fts5GetVarint32(pIter->a, dummy)sqlite3Fts5GetVarint32(pIter->a,(u32*)&(dummy)); | |||
| 21856 | } | |||
| 21857 | pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol)sqlite3Fts5GetVarint32(&pIter->a[1],(u32*)&(*piCol )); | |||
| 21858 | } | |||
| 21859 | } | |||
| 21860 | ||||
| 21861 | static int fts5ApiPhraseFirstColumn( | |||
| 21862 | Fts5Context *pCtx, | |||
| 21863 | int iPhrase, | |||
| 21864 | Fts5PhraseIter *pIter, | |||
| 21865 | int *piCol | |||
| 21866 | ){ | |||
| 21867 | int rc = SQLITE_OK0; | |||
| 21868 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21869 | Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; | |||
| 21870 | ||||
| 21871 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS2 ){ | |||
| 21872 | Fts5Sorter *pSorter = pCsr->pSorter; | |||
| 21873 | int n; | |||
| 21874 | if( pSorter ){ | |||
| 21875 | int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); | |||
| 21876 | n = pSorter->aIdx[iPhrase] - i1; | |||
| 21877 | pIter->a = &pSorter->aPoslist[i1]; | |||
| 21878 | }else{ | |||
| 21879 | rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); | |||
| 21880 | } | |||
| 21881 | if( rc==SQLITE_OK0 ){ | |||
| 21882 | assert( pIter->a || n==0 )((void) (0)); | |||
| 21883 | pIter->b = (pIter->a ? &pIter->a[n] : 0); | |||
| 21884 | *piCol = 0; | |||
| 21885 | fts5ApiPhraseNextColumn(pCtx, pIter, piCol); | |||
| 21886 | } | |||
| 21887 | }else{ | |||
| 21888 | int n; | |||
| 21889 | rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); | |||
| 21890 | if( rc==SQLITE_OK0 ){ | |||
| 21891 | assert( pIter->a || n==0 )((void) (0)); | |||
| 21892 | pIter->b = (pIter->a ? &pIter->a[n] : 0); | |||
| 21893 | if( n<=0 ){ | |||
| 21894 | *piCol = -1; | |||
| 21895 | }else if( pIter->a[0]==0x01 ){ | |||
| 21896 | pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol)sqlite3Fts5GetVarint32(&pIter->a[1],(u32*)&(*piCol )); | |||
| 21897 | }else{ | |||
| 21898 | *piCol = 0; | |||
| 21899 | } | |||
| 21900 | } | |||
| 21901 | } | |||
| 21902 | ||||
| 21903 | return rc; | |||
| 21904 | } | |||
| 21905 | ||||
| 21906 | /* | |||
| 21907 | ** xQueryToken() API implemenetation. | |||
| 21908 | */ | |||
| 21909 | static int fts5ApiQueryToken( | |||
| 21910 | Fts5Context* pCtx, | |||
| 21911 | int iPhrase, | |||
| 21912 | int iToken, | |||
| 21913 | const char **ppOut, | |||
| 21914 | int *pnOut | |||
| 21915 | ){ | |||
| 21916 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21917 | return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut); | |||
| 21918 | } | |||
| 21919 | ||||
| 21920 | /* | |||
| 21921 | ** xInstToken() API implemenetation. | |||
| 21922 | */ | |||
| 21923 | static int fts5ApiInstToken( | |||
| 21924 | Fts5Context *pCtx, | |||
| 21925 | int iIdx, | |||
| 21926 | int iToken, | |||
| 21927 | const char **ppOut, int *pnOut | |||
| 21928 | ){ | |||
| 21929 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21930 | int rc = SQLITE_OK0; | |||
| 21931 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)((pCsr)->csrflags & (0x08))==0 | |||
| 21932 | || SQLITE_OK0==(rc = fts5CacheInstArray(pCsr)) | |||
| 21933 | ){ | |||
| 21934 | if( iIdx<0 || iIdx>=pCsr->nInstCount ){ | |||
| 21935 | rc = SQLITE_RANGE25; | |||
| 21936 | }else{ | |||
| 21937 | int iPhrase = pCsr->aInst[iIdx*3]; | |||
| 21938 | int iCol = pCsr->aInst[iIdx*3 + 1]; | |||
| 21939 | int iOff = pCsr->aInst[iIdx*3 + 2]; | |||
| 21940 | i64 iRowid = fts5CursorRowid(pCsr); | |||
| 21941 | rc = sqlite3Fts5ExprInstToken( | |||
| 21942 | pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut | |||
| 21943 | ); | |||
| 21944 | } | |||
| 21945 | } | |||
| 21946 | return rc; | |||
| 21947 | } | |||
| 21948 | ||||
| 21949 | ||||
| 21950 | static int fts5ApiQueryPhrase(Fts5Context*, int, void*, | |||
| 21951 | int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) | |||
| 21952 | ); | |||
| 21953 | ||||
| 21954 | /* | |||
| 21955 | ** The xColumnLocale() API. | |||
| 21956 | */ | |||
| 21957 | static int fts5ApiColumnLocale( | |||
| 21958 | Fts5Context *pCtx, | |||
| 21959 | int iCol, | |||
| 21960 | const char **pzLocale, | |||
| 21961 | int *pnLocale | |||
| 21962 | ){ | |||
| 21963 | int rc = SQLITE_OK0; | |||
| 21964 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 21965 | Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; | |||
| 21966 | ||||
| 21967 | *pzLocale = 0; | |||
| 21968 | *pnLocale = 0; | |||
| 21969 | ||||
| 21970 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL )((void) (0)); | |||
| 21971 | if( iCol<0 || iCol>=pConfig->nCol ){ | |||
| 21972 | rc = SQLITE_RANGE25; | |||
| 21973 | }else if( | |||
| 21974 | pConfig->abUnindexed[iCol]==0 | |||
| 21975 | && 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) | |||
| 21976 | && pConfig->bLocale | |||
| 21977 | ){ | |||
| 21978 | rc = fts5SeekCursor(pCsr, 0); | |||
| 21979 | if( rc==SQLITE_OK0 ){ | |||
| 21980 | const char *zDummy = 0; | |||
| 21981 | int nDummy = 0; | |||
| 21982 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy); | |||
| 21983 | if( rc==SQLITE_OK0 ){ | |||
| 21984 | *pzLocale = pConfig->t.pLocale; | |||
| 21985 | *pnLocale = pConfig->t.nLocale; | |||
| 21986 | } | |||
| 21987 | sqlite3Fts5ClearLocale(pConfig); | |||
| 21988 | } | |||
| 21989 | } | |||
| 21990 | ||||
| 21991 | return rc; | |||
| 21992 | } | |||
| 21993 | ||||
| 21994 | static const Fts5ExtensionApi sFts5Api = { | |||
| 21995 | 4, /* iVersion */ | |||
| 21996 | fts5ApiUserData, | |||
| 21997 | fts5ApiColumnCount, | |||
| 21998 | fts5ApiRowCount, | |||
| 21999 | fts5ApiColumnTotalSize, | |||
| 22000 | fts5ApiTokenize, | |||
| 22001 | fts5ApiPhraseCount, | |||
| 22002 | fts5ApiPhraseSize, | |||
| 22003 | fts5ApiInstCount, | |||
| 22004 | fts5ApiInst, | |||
| 22005 | fts5ApiRowid, | |||
| 22006 | fts5ApiColumnText, | |||
| 22007 | fts5ApiColumnSize, | |||
| 22008 | fts5ApiQueryPhrase, | |||
| 22009 | fts5ApiSetAuxdata, | |||
| 22010 | fts5ApiGetAuxdata, | |||
| 22011 | fts5ApiPhraseFirst, | |||
| 22012 | fts5ApiPhraseNext, | |||
| 22013 | fts5ApiPhraseFirstColumn, | |||
| 22014 | fts5ApiPhraseNextColumn, | |||
| 22015 | fts5ApiQueryToken, | |||
| 22016 | fts5ApiInstToken, | |||
| 22017 | fts5ApiColumnLocale, | |||
| 22018 | fts5ApiTokenize_v2 | |||
| 22019 | }; | |||
| 22020 | ||||
| 22021 | /* | |||
| 22022 | ** Implementation of API function xQueryPhrase(). | |||
| 22023 | */ | |||
| 22024 | static int fts5ApiQueryPhrase( | |||
| 22025 | Fts5Context *pCtx, | |||
| 22026 | int iPhrase, | |||
| 22027 | void *pUserData, | |||
| 22028 | int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*) | |||
| 22029 | ){ | |||
| 22030 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |||
| 22031 | Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); | |||
| 22032 | int rc; | |||
| 22033 | Fts5Cursor *pNew = 0; | |||
| 22034 | ||||
| 22035 | rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew); | |||
| 22036 | if( rc==SQLITE_OK0 ){ | |||
| 22037 | pNew->ePlan = FTS5_PLAN_MATCH1; | |||
| 22038 | pNew->iFirstRowid = SMALLEST_INT64(((i64)-1) - (0xffffffff|(((i64)0x7fffffff)<<32))); | |||
| 22039 | pNew->iLastRowid = LARGEST_INT64(0xffffffff|(((i64)0x7fffffff)<<32)); | |||
| 22040 | pNew->base.pVtab = (sqlite3_vtab*)pTab; | |||
| 22041 | rc = sqlite3Fts5ExprClonePhrase(pCsr->pExpr, iPhrase, &pNew->pExpr); | |||
| 22042 | } | |||
| 22043 | ||||
| 22044 | if( rc==SQLITE_OK0 ){ | |||
| 22045 | for(rc = fts5CursorFirst(pTab, pNew, 0); | |||
| 22046 | rc==SQLITE_OK0 && CsrFlagTest(pNew, FTS5CSR_EOF)((pNew)->csrflags & (0x01))==0; | |||
| 22047 | rc = fts5NextMethod((sqlite3_vtab_cursor*)pNew) | |||
| 22048 | ){ | |||
| 22049 | rc = xCallback(&sFts5Api, (Fts5Context*)pNew, pUserData); | |||
| 22050 | if( rc!=SQLITE_OK0 ){ | |||
| 22051 | if( rc==SQLITE_DONE101 ) rc = SQLITE_OK0; | |||
| 22052 | break; | |||
| 22053 | } | |||
| 22054 | } | |||
| 22055 | } | |||
| 22056 | ||||
| 22057 | fts5CloseMethod((sqlite3_vtab_cursor*)pNew); | |||
| 22058 | return rc; | |||
| 22059 | } | |||
| 22060 | ||||
| 22061 | static void fts5ApiInvoke( | |||
| 22062 | Fts5Auxiliary *pAux, | |||
| 22063 | Fts5Cursor *pCsr, | |||
| 22064 | sqlite3_context *context, | |||
| 22065 | int argc, | |||
| 22066 | sqlite3_value **argv | |||
| 22067 | ){ | |||
| 22068 | assert( pCsr->pAux==0 )((void) (0)); | |||
| 22069 | assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL )((void) (0)); | |||
| 22070 | pCsr->pAux = pAux; | |||
| 22071 | pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); | |||
| 22072 | pCsr->pAux = 0; | |||
| 22073 | } | |||
| 22074 | ||||
| 22075 | static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){ | |||
| 22076 | Fts5Cursor *pCsr; | |||
| 22077 | for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ | |||
| 22078 | if( pCsr->iCsrId==iCsrId ) break; | |||
| 22079 | } | |||
| 22080 | return pCsr; | |||
| 22081 | } | |||
| 22082 | ||||
| 22083 | /* | |||
| 22084 | ** Parameter zFmt is a printf() style formatting string. This function | |||
| 22085 | ** formats it using the trailing arguments and returns the result as | |||
| 22086 | ** an error message to the context passed as the first argument. | |||
| 22087 | */ | |||
| 22088 | static void fts5ResultError(sqlite3_context *pCtx, const char *zFmt, ...){ | |||
| 22089 | char *zErr = 0; | |||
| 22090 | va_list ap; | |||
| 22091 | va_start(ap, zFmt)__builtin_va_start(ap, zFmt); | |||
| 22092 | zErr = sqlite3_vmprintfsqlite3_api->vmprintf(zFmt, ap); | |||
| 22093 | sqlite3_result_errorsqlite3_api->result_error(pCtx, zErr, -1); | |||
| 22094 | sqlite3_freesqlite3_api->free(zErr); | |||
| 22095 | va_end(ap)__builtin_va_end(ap); | |||
| 22096 | } | |||
| 22097 | ||||
| 22098 | static void fts5ApiCallback( | |||
| 22099 | sqlite3_context *context, | |||
| 22100 | int argc, | |||
| 22101 | sqlite3_value **argv | |||
| 22102 | ){ | |||
| 22103 | ||||
| 22104 | Fts5Auxiliary *pAux; | |||
| 22105 | Fts5Cursor *pCsr; | |||
| 22106 | i64 iCsrId; | |||
| 22107 | ||||
| 22108 | assert( argc>=1 )((void) (0)); | |||
| 22109 | pAux = (Fts5Auxiliary*)sqlite3_user_datasqlite3_api->user_data(context); | |||
| 22110 | iCsrId = sqlite3_value_int64sqlite3_api->value_int64(argv[0]); | |||
| 22111 | ||||
| 22112 | pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId); | |||
| 22113 | if( pCsr==0 || (pCsr->ePlan==0 || pCsr->ePlan==FTS5_PLAN_SPECIAL3) ){ | |||
| 22114 | fts5ResultError(context, "no such cursor: %lld", iCsrId); | |||
| 22115 | }else{ | |||
| 22116 | sqlite3_vtab *pTab = pCsr->base.pVtab; | |||
| 22117 | fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]); | |||
| 22118 | sqlite3_freesqlite3_api->free(pTab->zErrMsg); | |||
| 22119 | pTab->zErrMsg = 0; | |||
| 22120 | } | |||
| 22121 | } | |||
| 22122 | ||||
| 22123 | ||||
| 22124 | /* | |||
| 22125 | ** Given cursor id iId, return a pointer to the corresponding Fts5Table | |||
| 22126 | ** object. Or NULL If the cursor id does not exist. | |||
| 22127 | */ | |||
| 22128 | static Fts5Table *sqlite3Fts5TableFromCsrid( | |||
| 22129 | Fts5Global *pGlobal, /* FTS5 global context for db handle */ | |||
| 22130 | i64 iCsrId /* Id of cursor to find */ | |||
| 22131 | ){ | |||
| 22132 | Fts5Cursor *pCsr; | |||
| 22133 | pCsr = fts5CursorFromCsrid(pGlobal, iCsrId); | |||
| 22134 | if( pCsr ){ | |||
| 22135 | return (Fts5Table*)pCsr->base.pVtab; | |||
| 22136 | } | |||
| 22137 | return 0; | |||
| 22138 | } | |||
| 22139 | ||||
| 22140 | /* | |||
| 22141 | ** Return a "position-list blob" corresponding to the current position of | |||
| 22142 | ** cursor pCsr via sqlite3_result_blob(). A position-list blob contains | |||
| 22143 | ** the current position-list for each phrase in the query associated with | |||
| 22144 | ** cursor pCsr. | |||
| 22145 | ** | |||
| 22146 | ** A position-list blob begins with (nPhrase-1) varints, where nPhrase is | |||
| 22147 | ** the number of phrases in the query. Following the varints are the | |||
| 22148 | ** concatenated position lists for each phrase, in order. | |||
| 22149 | ** | |||
| 22150 | ** The first varint (if it exists) contains the size of the position list | |||
| 22151 | ** for phrase 0. The second (same disclaimer) contains the size of position | |||
| 22152 | ** list 1. And so on. There is no size field for the final position list, | |||
| 22153 | ** as it can be derived from the total size of the blob. | |||
| 22154 | */ | |||
| 22155 | static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){ | |||
| 22156 | int i; | |||
| 22157 | int rc = SQLITE_OK0; | |||
| 22158 | int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |||
| 22159 | Fts5Buffer val; | |||
| 22160 | ||||
| 22161 | memset(&val, 0, sizeof(Fts5Buffer)); | |||
| 22162 | switch( ((Fts5Table*)(pCsr->base.pVtab))->pConfig->eDetail ){ | |||
| 22163 | case FTS5_DETAIL_FULL0: | |||
| 22164 | ||||
| 22165 | /* Append the varints */ | |||
| 22166 | for(i=0; i<(nPhrase-1); i++){ | |||
| 22167 | const u8 *dummy; | |||
| 22168 | int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy); | |||
| 22169 | sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); | |||
| 22170 | } | |||
| 22171 | ||||
| 22172 | /* Append the position lists */ | |||
| 22173 | for(i=0; i<nPhrase; i++){ | |||
| 22174 | const u8 *pPoslist; | |||
| 22175 | int nPoslist; | |||
| 22176 | nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist); | |||
| 22177 | sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); | |||
| 22178 | } | |||
| 22179 | break; | |||
| 22180 | ||||
| 22181 | case FTS5_DETAIL_COLUMNS2: | |||
| 22182 | ||||
| 22183 | /* Append the varints */ | |||
| 22184 | for(i=0; rc==SQLITE_OK0 && i<(nPhrase-1); i++){ | |||
| 22185 | const u8 *dummy; | |||
| 22186 | int nByte; | |||
| 22187 | rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &dummy, &nByte); | |||
| 22188 | sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); | |||
| 22189 | } | |||
| 22190 | ||||
| 22191 | /* Append the position lists */ | |||
| 22192 | for(i=0; rc==SQLITE_OK0 && i<nPhrase; i++){ | |||
| 22193 | const u8 *pPoslist; | |||
| 22194 | int nPoslist; | |||
| 22195 | rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &pPoslist, &nPoslist); | |||
| 22196 | sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); | |||
| 22197 | } | |||
| 22198 | break; | |||
| 22199 | ||||
| 22200 | default: | |||
| 22201 | break; | |||
| 22202 | } | |||
| 22203 | ||||
| 22204 | sqlite3_result_blobsqlite3_api->result_blob(pCtx, val.p, val.n, sqlite3_freesqlite3_api->free); | |||
| 22205 | return rc; | |||
| 22206 | } | |||
| 22207 | ||||
| 22208 | /* | |||
| 22209 | ** This is the xColumn method, called by SQLite to request a value from | |||
| 22210 | ** the row that the supplied cursor currently points to. | |||
| 22211 | */ | |||
| 22212 | static int fts5ColumnMethod( | |||
| 22213 | sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ | |||
| 22214 | sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ | |||
| 22215 | int iCol /* Index of column to read value from */ | |||
| 22216 | ){ | |||
| 22217 | Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); | |||
| 22218 | Fts5Config *pConfig = pTab->p.pConfig; | |||
| 22219 | Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |||
| 22220 | int rc = SQLITE_OK0; | |||
| 22221 | ||||
| 22222 | assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 )((void) (0)); | |||
| 22223 | ||||
| 22224 | if( pCsr->ePlan==FTS5_PLAN_SPECIAL3 ){ | |||
| 22225 | if( iCol==pConfig->nCol ){ | |||
| 22226 | sqlite3_result_int64sqlite3_api->result_int64(pCtx, pCsr->iSpecial); | |||
| 22227 | } | |||
| 22228 | }else | |||
| 22229 | ||||
| 22230 | if( iCol==pConfig->nCol ){ | |||
| 22231 | /* User is requesting the value of the special column with the same name | |||
| 22232 | ** as the table. Return the cursor integer id number. This value is only | |||
| 22233 | ** useful in that it may be passed as the first argument to an FTS5 | |||
| 22234 | ** auxiliary function. */ | |||
| 22235 | sqlite3_result_int64sqlite3_api->result_int64(pCtx, pCsr->iCsrId); | |||
| 22236 | }else if( iCol==pConfig->nCol+1 ){ | |||
| 22237 | /* The value of the "rank" column. */ | |||
| 22238 | ||||
| 22239 | if( pCsr->ePlan==FTS5_PLAN_SOURCE2 ){ | |||
| 22240 | fts5PoslistBlob(pCtx, pCsr); | |||
| 22241 | }else if( | |||
| 22242 | pCsr->ePlan==FTS5_PLAN_MATCH1 | |||
| 22243 | || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH4 | |||
| 22244 | ){ | |||
| 22245 | if( pCsr->pRank || SQLITE_OK0==(rc = fts5FindRankFunction(pCsr)) ){ | |||
| 22246 | fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); | |||
| 22247 | } | |||
| 22248 | } | |||
| 22249 | }else{ | |||
| 22250 | if( !sqlite3_vtab_nochangesqlite3_api->vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE1 ){ | |||
| 22251 | pConfig->pzErrmsg = &pTab->p.base.zErrMsg; | |||
| 22252 | rc = fts5SeekCursor(pCsr, 1); | |||
| 22253 | if( rc==SQLITE_OK0 ){ | |||
| 22254 | sqlite3_value *pVal = sqlite3_column_valuesqlite3_api->column_value(pCsr->pStmt, iCol+1); | |||
| 22255 | if( pConfig->bLocale | |||
| 22256 | && pConfig->eContent==FTS5_CONTENT_EXTERNAL2 | |||
| 22257 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |||
| 22258 | ){ | |||
| 22259 | const char *z = 0; | |||
| 22260 | int n = 0; | |||
| 22261 | rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n); | |||
| 22262 | if( rc==SQLITE_OK0 ){ | |||
| 22263 | sqlite3_result_textsqlite3_api->result_text(pCtx, z, n, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 22264 | } | |||
| 22265 | sqlite3Fts5ClearLocale(pConfig); | |||
| 22266 | }else{ | |||
| 22267 | sqlite3_result_valuesqlite3_api->result_value(pCtx, pVal); | |||
| 22268 | } | |||
| 22269 | } | |||
| 22270 | ||||
| 22271 | pConfig->pzErrmsg = 0; | |||
| 22272 | } | |||
| 22273 | } | |||
| 22274 | ||||
| 22275 | return rc; | |||
| 22276 | } | |||
| 22277 | ||||
| 22278 | ||||
| 22279 | /* | |||
| 22280 | ** This routine implements the xFindFunction method for the FTS3 | |||
| 22281 | ** virtual table. | |||
| 22282 | */ | |||
| 22283 | static int fts5FindFunctionMethod( | |||
| 22284 | sqlite3_vtab *pVtab, /* Virtual table handle */ | |||
| 22285 | int nUnused, /* Number of SQL function arguments */ | |||
| 22286 | const char *zName, /* Name of SQL function */ | |||
| 22287 | void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ | |||
| 22288 | void **ppArg /* OUT: User data for *pxFunc */ | |||
| 22289 | ){ | |||
| 22290 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 22291 | Fts5Auxiliary *pAux; | |||
| 22292 | ||||
| 22293 | UNUSED_PARAM(nUnused)(void)(nUnused); | |||
| 22294 | pAux = fts5FindAuxiliary(pTab, zName); | |||
| 22295 | if( pAux ){ | |||
| 22296 | *pxFunc = fts5ApiCallback; | |||
| 22297 | *ppArg = (void*)pAux; | |||
| 22298 | return 1; | |||
| 22299 | } | |||
| 22300 | ||||
| 22301 | /* No function of the specified name was found. Return 0. */ | |||
| 22302 | return 0; | |||
| 22303 | } | |||
| 22304 | ||||
| 22305 | /* | |||
| 22306 | ** Implementation of FTS5 xRename method. Rename an fts5 table. | |||
| 22307 | */ | |||
| 22308 | static int fts5RenameMethod( | |||
| 22309 | sqlite3_vtab *pVtab, /* Virtual table handle */ | |||
| 22310 | const char *zName /* New name of table */ | |||
| 22311 | ){ | |||
| 22312 | int rc; | |||
| 22313 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 22314 | rc = sqlite3Fts5StorageRename(pTab->pStorage, zName); | |||
| 22315 | return rc; | |||
| 22316 | } | |||
| 22317 | ||||
| 22318 | static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ | |||
| 22319 | fts5TripCursors((Fts5FullTable*)pTab); | |||
| 22320 | return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage); | |||
| 22321 | } | |||
| 22322 | ||||
| 22323 | /* | |||
| 22324 | ** The xSavepoint() method. | |||
| 22325 | ** | |||
| 22326 | ** Flush the contents of the pending-terms table to disk. | |||
| 22327 | */ | |||
| 22328 | static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ | |||
| 22329 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 22330 | int rc = SQLITE_OK0; | |||
| 22331 | ||||
| 22332 | fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); | |||
| 22333 | rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); | |||
| 22334 | if( rc==SQLITE_OK0 ){ | |||
| 22335 | pTab->iSavepoint = iSavepoint+1; | |||
| 22336 | } | |||
| 22337 | return rc; | |||
| 22338 | } | |||
| 22339 | ||||
| 22340 | /* | |||
| 22341 | ** The xRelease() method. | |||
| 22342 | ** | |||
| 22343 | ** This is a no-op. | |||
| 22344 | */ | |||
| 22345 | static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ | |||
| 22346 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 22347 | int rc = SQLITE_OK0; | |||
| 22348 | fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); | |||
| 22349 | if( (iSavepoint+1)<pTab->iSavepoint ){ | |||
| 22350 | rc = sqlite3Fts5FlushToDisk(&pTab->p); | |||
| 22351 | if( rc==SQLITE_OK0 ){ | |||
| 22352 | pTab->iSavepoint = iSavepoint; | |||
| 22353 | } | |||
| 22354 | } | |||
| 22355 | return rc; | |||
| 22356 | } | |||
| 22357 | ||||
| 22358 | /* | |||
| 22359 | ** The xRollbackTo() method. | |||
| 22360 | ** | |||
| 22361 | ** Discard the contents of the pending terms table. | |||
| 22362 | */ | |||
| 22363 | static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ | |||
| 22364 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 22365 | int rc = SQLITE_OK0; | |||
| 22366 | fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); | |||
| 22367 | fts5TripCursors(pTab); | |||
| 22368 | if( (iSavepoint+1)<=pTab->iSavepoint ){ | |||
| 22369 | pTab->p.pConfig->pgsz = 0; | |||
| 22370 | rc = sqlite3Fts5StorageRollback(pTab->pStorage); | |||
| 22371 | } | |||
| 22372 | return rc; | |||
| 22373 | } | |||
| 22374 | ||||
| 22375 | /* | |||
| 22376 | ** Register a new auxiliary function with global context pGlobal. | |||
| 22377 | */ | |||
| 22378 | static int fts5CreateAux( | |||
| 22379 | fts5_api *pApi, /* Global context (one per db handle) */ | |||
| 22380 | const char *zName, /* Name of new function */ | |||
| 22381 | void *pUserData, /* User data for aux. function */ | |||
| 22382 | fts5_extension_function xFunc, /* Aux. function implementation */ | |||
| 22383 | void(*xDestroy)(void*) /* Destructor for pUserData */ | |||
| 22384 | ){ | |||
| 22385 | Fts5Global *pGlobal = (Fts5Global*)pApi; | |||
| 22386 | int rc = sqlite3_overload_functionsqlite3_api->overload_function(pGlobal->db, zName, -1); | |||
| 22387 | if( rc==SQLITE_OK0 ){ | |||
| 22388 | Fts5Auxiliary *pAux; | |||
| 22389 | sqlite3_int64 nName; /* Size of zName in bytes, including \0 */ | |||
| 22390 | sqlite3_int64 nByte; /* Bytes of space to allocate */ | |||
| 22391 | ||||
| 22392 | nName = strlen(zName) + 1; | |||
| 22393 | nByte = sizeof(Fts5Auxiliary) + nName; | |||
| 22394 | pAux = (Fts5Auxiliary*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 22395 | if( pAux ){ | |||
| 22396 | memset(pAux, 0, (size_t)nByte); | |||
| 22397 | pAux->zFunc = (char*)&pAux[1]; | |||
| 22398 | memcpy(pAux->zFunc, zName, nName); | |||
| 22399 | pAux->pGlobal = pGlobal; | |||
| 22400 | pAux->pUserData = pUserData; | |||
| 22401 | pAux->xFunc = xFunc; | |||
| 22402 | pAux->xDestroy = xDestroy; | |||
| 22403 | pAux->pNext = pGlobal->pAux; | |||
| 22404 | pGlobal->pAux = pAux; | |||
| 22405 | }else{ | |||
| 22406 | rc = SQLITE_NOMEM7; | |||
| 22407 | } | |||
| 22408 | } | |||
| 22409 | ||||
| 22410 | return rc; | |||
| 22411 | } | |||
| 22412 | ||||
| 22413 | /* | |||
| 22414 | ** This function is used by xCreateTokenizer_v2() and xCreateTokenizer(). | |||
| 22415 | ** It allocates and partially populates a new Fts5TokenizerModule object. | |||
| 22416 | ** The new object is already linked into the Fts5Global context before | |||
| 22417 | ** returning. | |||
| 22418 | ** | |||
| 22419 | ** If successful, SQLITE_OK is returned and a pointer to the new | |||
| 22420 | ** Fts5TokenizerModule object returned via output parameter (*ppNew). All | |||
| 22421 | ** that is required is for the caller to fill in the methods in | |||
| 22422 | ** Fts5TokenizerModule.x1 and x2, and to set Fts5TokenizerModule.bV2Native | |||
| 22423 | ** as appropriate. | |||
| 22424 | ** | |||
| 22425 | ** If an error occurs, an SQLite error code is returned and the final value | |||
| 22426 | ** of (*ppNew) undefined. | |||
| 22427 | */ | |||
| 22428 | static int fts5NewTokenizerModule( | |||
| 22429 | Fts5Global *pGlobal, /* Global context (one per db handle) */ | |||
| 22430 | const char *zName, /* Name of new function */ | |||
| 22431 | void *pUserData, /* User data for aux. function */ | |||
| 22432 | void(*xDestroy)(void*), /* Destructor for pUserData */ | |||
| 22433 | Fts5TokenizerModule **ppNew | |||
| 22434 | ){ | |||
| 22435 | int rc = SQLITE_OK0; | |||
| 22436 | Fts5TokenizerModule *pNew; | |||
| 22437 | sqlite3_int64 nName; /* Size of zName and its \0 terminator */ | |||
| 22438 | sqlite3_int64 nByte; /* Bytes of space to allocate */ | |||
| 22439 | ||||
| 22440 | nName = strlen(zName) + 1; | |||
| 22441 | nByte = sizeof(Fts5TokenizerModule) + nName; | |||
| 22442 | *ppNew = pNew = (Fts5TokenizerModule*)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 22443 | if( pNew ){ | |||
| 22444 | pNew->zName = (char*)&pNew[1]; | |||
| 22445 | memcpy(pNew->zName, zName, nName); | |||
| 22446 | pNew->pUserData = pUserData; | |||
| 22447 | pNew->xDestroy = xDestroy; | |||
| 22448 | pNew->pNext = pGlobal->pTok; | |||
| 22449 | pGlobal->pTok = pNew; | |||
| 22450 | if( pNew->pNext==0 ){ | |||
| 22451 | pGlobal->pDfltTok = pNew; | |||
| 22452 | } | |||
| 22453 | } | |||
| 22454 | ||||
| 22455 | return rc; | |||
| 22456 | } | |||
| 22457 | ||||
| 22458 | /* | |||
| 22459 | ** An instance of this type is used as the Fts5Tokenizer object for | |||
| 22460 | ** wrapper tokenizers - those that provide access to a v1 tokenizer via | |||
| 22461 | ** the fts5_tokenizer_v2 API, and those that provide access to a v2 tokenizer | |||
| 22462 | ** via the fts5_tokenizer API. | |||
| 22463 | */ | |||
| 22464 | typedef struct Fts5VtoVTokenizer Fts5VtoVTokenizer; | |||
| 22465 | struct Fts5VtoVTokenizer { | |||
| 22466 | int bV2Native; /* True if v2 native tokenizer */ | |||
| 22467 | fts5_tokenizer x1; /* Tokenizer functions */ | |||
| 22468 | fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ | |||
| 22469 | Fts5Tokenizer *pReal; | |||
| 22470 | }; | |||
| 22471 | ||||
| 22472 | /* | |||
| 22473 | ** Create a wrapper tokenizer. The context argument pCtx points to the | |||
| 22474 | ** Fts5TokenizerModule object. | |||
| 22475 | */ | |||
| 22476 | static int fts5VtoVCreate( | |||
| 22477 | void *pCtx, | |||
| 22478 | const char **azArg, | |||
| 22479 | int nArg, | |||
| 22480 | Fts5Tokenizer **ppOut | |||
| 22481 | ){ | |||
| 22482 | Fts5TokenizerModule *pMod = (Fts5TokenizerModule*)pCtx; | |||
| 22483 | Fts5VtoVTokenizer *pNew = 0; | |||
| 22484 | int rc = SQLITE_OK0; | |||
| 22485 | ||||
| 22486 | pNew = (Fts5VtoVTokenizer*)sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); | |||
| 22487 | if( rc==SQLITE_OK0 ){ | |||
| 22488 | pNew->x1 = pMod->x1; | |||
| 22489 | pNew->x2 = pMod->x2; | |||
| 22490 | pNew->bV2Native = pMod->bV2Native; | |||
| 22491 | if( pMod->bV2Native ){ | |||
| 22492 | rc = pMod->x2.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); | |||
| 22493 | }else{ | |||
| 22494 | rc = pMod->x1.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); | |||
| 22495 | } | |||
| 22496 | if( rc!=SQLITE_OK0 ){ | |||
| 22497 | sqlite3_freesqlite3_api->free(pNew); | |||
| 22498 | pNew = 0; | |||
| 22499 | } | |||
| 22500 | } | |||
| 22501 | ||||
| 22502 | *ppOut = (Fts5Tokenizer*)pNew; | |||
| 22503 | return rc; | |||
| 22504 | } | |||
| 22505 | ||||
| 22506 | /* | |||
| 22507 | ** Delete an Fts5VtoVTokenizer wrapper tokenizer. | |||
| 22508 | */ | |||
| 22509 | static void fts5VtoVDelete(Fts5Tokenizer *pTok){ | |||
| 22510 | Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; | |||
| 22511 | if( p ){ | |||
| 22512 | if( p->bV2Native ){ | |||
| 22513 | p->x2.xDelete(p->pReal); | |||
| 22514 | }else{ | |||
| 22515 | p->x1.xDelete(p->pReal); | |||
| 22516 | } | |||
| 22517 | sqlite3_freesqlite3_api->free(p); | |||
| 22518 | } | |||
| 22519 | } | |||
| 22520 | ||||
| 22521 | ||||
| 22522 | /* | |||
| 22523 | ** xTokenizer method for a wrapper tokenizer that offers the v1 interface | |||
| 22524 | ** (no support for locales). | |||
| 22525 | */ | |||
| 22526 | static int fts5V1toV2Tokenize( | |||
| 22527 | Fts5Tokenizer *pTok, | |||
| 22528 | void *pCtx, int flags, | |||
| 22529 | const char *pText, int nText, | |||
| 22530 | int (*xToken)(void*, int, const char*, int, int, int) | |||
| 22531 | ){ | |||
| 22532 | Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; | |||
| 22533 | assert( p->bV2Native )((void) (0)); | |||
| 22534 | return p->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken); | |||
| 22535 | } | |||
| 22536 | ||||
| 22537 | /* | |||
| 22538 | ** xTokenizer method for a wrapper tokenizer that offers the v2 interface | |||
| 22539 | ** (with locale support). | |||
| 22540 | */ | |||
| 22541 | static int fts5V2toV1Tokenize( | |||
| 22542 | Fts5Tokenizer *pTok, | |||
| 22543 | void *pCtx, int flags, | |||
| 22544 | const char *pText, int nText, | |||
| 22545 | const char *pLocale, int nLocale, | |||
| 22546 | int (*xToken)(void*, int, const char*, int, int, int) | |||
| 22547 | ){ | |||
| 22548 | Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; | |||
| 22549 | assert( p->bV2Native==0 )((void) (0)); | |||
| 22550 | UNUSED_PARAM2(pLocale,nLocale)(void)(pLocale), (void)(nLocale); | |||
| 22551 | return p->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken); | |||
| 22552 | } | |||
| 22553 | ||||
| 22554 | /* | |||
| 22555 | ** Register a new tokenizer. This is the implementation of the | |||
| 22556 | ** fts5_api.xCreateTokenizer_v2() method. | |||
| 22557 | */ | |||
| 22558 | static int fts5CreateTokenizer_v2( | |||
| 22559 | fts5_api *pApi, /* Global context (one per db handle) */ | |||
| 22560 | const char *zName, /* Name of new function */ | |||
| 22561 | void *pUserData, /* User data for aux. function */ | |||
| 22562 | fts5_tokenizer_v2 *pTokenizer, /* Tokenizer implementation */ | |||
| 22563 | void(*xDestroy)(void*) /* Destructor for pUserData */ | |||
| 22564 | ){ | |||
| 22565 | Fts5Global *pGlobal = (Fts5Global*)pApi; | |||
| 22566 | int rc = SQLITE_OK0; | |||
| 22567 | ||||
| 22568 | if( pTokenizer->iVersion>2 ){ | |||
| 22569 | rc = SQLITE_ERROR1; | |||
| 22570 | }else{ | |||
| 22571 | Fts5TokenizerModule *pNew = 0; | |||
| 22572 | rc = fts5NewTokenizerModule(pGlobal, zName, pUserData, xDestroy, &pNew); | |||
| 22573 | if( pNew ){ | |||
| 22574 | pNew->x2 = *pTokenizer; | |||
| 22575 | pNew->bV2Native = 1; | |||
| 22576 | pNew->x1.xCreate = fts5VtoVCreate; | |||
| 22577 | pNew->x1.xTokenize = fts5V1toV2Tokenize; | |||
| 22578 | pNew->x1.xDelete = fts5VtoVDelete; | |||
| 22579 | } | |||
| 22580 | } | |||
| 22581 | ||||
| 22582 | return rc; | |||
| 22583 | } | |||
| 22584 | ||||
| 22585 | /* | |||
| 22586 | ** The fts5_api.xCreateTokenizer() method. | |||
| 22587 | */ | |||
| 22588 | static int fts5CreateTokenizer( | |||
| 22589 | fts5_api *pApi, /* Global context (one per db handle) */ | |||
| 22590 | const char *zName, /* Name of new function */ | |||
| 22591 | void *pUserData, /* User data for aux. function */ | |||
| 22592 | fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ | |||
| 22593 | void(*xDestroy)(void*) /* Destructor for pUserData */ | |||
| 22594 | ){ | |||
| 22595 | Fts5TokenizerModule *pNew = 0; | |||
| 22596 | int rc = SQLITE_OK0; | |||
| 22597 | ||||
| 22598 | rc = fts5NewTokenizerModule( | |||
| 22599 | (Fts5Global*)pApi, zName, pUserData, xDestroy, &pNew | |||
| 22600 | ); | |||
| 22601 | if( pNew ){ | |||
| 22602 | pNew->x1 = *pTokenizer; | |||
| 22603 | pNew->x2.xCreate = fts5VtoVCreate; | |||
| 22604 | pNew->x2.xTokenize = fts5V2toV1Tokenize; | |||
| 22605 | pNew->x2.xDelete = fts5VtoVDelete; | |||
| 22606 | } | |||
| 22607 | return rc; | |||
| 22608 | } | |||
| 22609 | ||||
| 22610 | /* | |||
| 22611 | ** Search the global context passed as the first argument for a tokenizer | |||
| 22612 | ** module named zName. If found, return a pointer to the Fts5TokenizerModule | |||
| 22613 | ** object. Otherwise, return NULL. | |||
| 22614 | */ | |||
| 22615 | static Fts5TokenizerModule *fts5LocateTokenizer( | |||
| 22616 | Fts5Global *pGlobal, /* Global (one per db handle) object */ | |||
| 22617 | const char *zName /* Name of tokenizer module to find */ | |||
| 22618 | ){ | |||
| 22619 | Fts5TokenizerModule *pMod = 0; | |||
| 22620 | ||||
| 22621 | if( zName==0 ){ | |||
| 22622 | pMod = pGlobal->pDfltTok; | |||
| 22623 | }else{ | |||
| 22624 | for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){ | |||
| 22625 | if( sqlite3_stricmpsqlite3_api->stricmp(zName, pMod->zName)==0 ) break; | |||
| 22626 | } | |||
| 22627 | } | |||
| 22628 | ||||
| 22629 | return pMod; | |||
| 22630 | } | |||
| 22631 | ||||
| 22632 | /* | |||
| 22633 | ** Find a tokenizer. This is the implementation of the | |||
| 22634 | ** fts5_api.xFindTokenizer_v2() method. | |||
| 22635 | */ | |||
| 22636 | static int fts5FindTokenizer_v2( | |||
| 22637 | fts5_api *pApi, /* Global context (one per db handle) */ | |||
| 22638 | const char *zName, /* Name of tokenizer */ | |||
| 22639 | void **ppUserData, | |||
| 22640 | fts5_tokenizer_v2 **ppTokenizer /* Populate this object */ | |||
| 22641 | ){ | |||
| 22642 | int rc = SQLITE_OK0; | |||
| 22643 | Fts5TokenizerModule *pMod; | |||
| 22644 | ||||
| 22645 | pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); | |||
| 22646 | if( pMod ){ | |||
| 22647 | if( pMod->bV2Native ){ | |||
| 22648 | *ppUserData = pMod->pUserData; | |||
| 22649 | }else{ | |||
| 22650 | *ppUserData = (void*)pMod; | |||
| 22651 | } | |||
| 22652 | *ppTokenizer = &pMod->x2; | |||
| 22653 | }else{ | |||
| 22654 | *ppTokenizer = 0; | |||
| 22655 | *ppUserData = 0; | |||
| 22656 | rc = SQLITE_ERROR1; | |||
| 22657 | } | |||
| 22658 | ||||
| 22659 | return rc; | |||
| 22660 | } | |||
| 22661 | ||||
| 22662 | /* | |||
| 22663 | ** Find a tokenizer. This is the implementation of the | |||
| 22664 | ** fts5_api.xFindTokenizer() method. | |||
| 22665 | */ | |||
| 22666 | static int fts5FindTokenizer( | |||
| 22667 | fts5_api *pApi, /* Global context (one per db handle) */ | |||
| 22668 | const char *zName, /* Name of new function */ | |||
| 22669 | void **ppUserData, | |||
| 22670 | fts5_tokenizer *pTokenizer /* Populate this object */ | |||
| 22671 | ){ | |||
| 22672 | int rc = SQLITE_OK0; | |||
| 22673 | Fts5TokenizerModule *pMod; | |||
| 22674 | ||||
| 22675 | pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); | |||
| 22676 | if( pMod ){ | |||
| 22677 | if( pMod->bV2Native==0 ){ | |||
| 22678 | *ppUserData = pMod->pUserData; | |||
| 22679 | }else{ | |||
| 22680 | *ppUserData = (void*)pMod; | |||
| 22681 | } | |||
| 22682 | *pTokenizer = pMod->x1; | |||
| 22683 | }else{ | |||
| 22684 | memset(pTokenizer, 0, sizeof(*pTokenizer)); | |||
| 22685 | *ppUserData = 0; | |||
| 22686 | rc = SQLITE_ERROR1; | |||
| 22687 | } | |||
| 22688 | ||||
| 22689 | return rc; | |||
| 22690 | } | |||
| 22691 | ||||
| 22692 | /* | |||
| 22693 | ** Attempt to instantiate the tokenizer. | |||
| 22694 | */ | |||
| 22695 | static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig){ | |||
| 22696 | const char **azArg = pConfig->t.azArg; | |||
| 22697 | const int nArg = pConfig->t.nArg; | |||
| 22698 | Fts5TokenizerModule *pMod = 0; | |||
| 22699 | int rc = SQLITE_OK0; | |||
| 22700 | ||||
| 22701 | pMod = fts5LocateTokenizer(pConfig->pGlobal, nArg==0 ? 0 : azArg[0]); | |||
| 22702 | if( pMod==0 ){ | |||
| 22703 | assert( nArg>0 )((void) (0)); | |||
| 22704 | rc = SQLITE_ERROR1; | |||
| 22705 | sqlite3Fts5ConfigErrmsg(pConfig, "no such tokenizer: %s", azArg[0]); | |||
| 22706 | }else{ | |||
| 22707 | int (*xCreate)(void*, const char**, int, Fts5Tokenizer**) = 0; | |||
| 22708 | if( pMod->bV2Native ){ | |||
| 22709 | xCreate = pMod->x2.xCreate; | |||
| 22710 | pConfig->t.pApi2 = &pMod->x2; | |||
| 22711 | }else{ | |||
| 22712 | pConfig->t.pApi1 = &pMod->x1; | |||
| 22713 | xCreate = pMod->x1.xCreate; | |||
| 22714 | } | |||
| 22715 | ||||
| 22716 | rc = xCreate(pMod->pUserData, | |||
| 22717 | (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->t.pTok | |||
| 22718 | ); | |||
| 22719 | ||||
| 22720 | if( rc!=SQLITE_OK0 ){ | |||
| 22721 | if( rc!=SQLITE_NOMEM7 ){ | |||
| 22722 | sqlite3Fts5ConfigErrmsg(pConfig, "error in tokenizer constructor"); | |||
| 22723 | } | |||
| 22724 | }else if( pMod->bV2Native==0 ){ | |||
| 22725 | pConfig->t.ePattern = sqlite3Fts5TokenizerPattern( | |||
| 22726 | pMod->x1.xCreate, pConfig->t.pTok | |||
| 22727 | ); | |||
| 22728 | } | |||
| 22729 | } | |||
| 22730 | ||||
| 22731 | if( rc!=SQLITE_OK0 ){ | |||
| 22732 | pConfig->t.pApi1 = 0; | |||
| 22733 | pConfig->t.pApi2 = 0; | |||
| 22734 | pConfig->t.pTok = 0; | |||
| 22735 | } | |||
| 22736 | ||||
| 22737 | return rc; | |||
| 22738 | } | |||
| 22739 | ||||
| 22740 | ||||
| 22741 | /* | |||
| 22742 | ** xDestroy callback passed to sqlite3_create_module(). This is invoked | |||
| 22743 | ** when the db handle is being closed. Free memory associated with | |||
| 22744 | ** tokenizers and aux functions registered with this db handle. | |||
| 22745 | */ | |||
| 22746 | static void fts5ModuleDestroy(void *pCtx){ | |||
| 22747 | Fts5TokenizerModule *pTok, *pNextTok; | |||
| 22748 | Fts5Auxiliary *pAux, *pNextAux; | |||
| 22749 | Fts5Global *pGlobal = (Fts5Global*)pCtx; | |||
| 22750 | ||||
| 22751 | for(pAux=pGlobal->pAux; pAux; pAux=pNextAux){ | |||
| 22752 | pNextAux = pAux->pNext; | |||
| 22753 | if( pAux->xDestroy ) pAux->xDestroy(pAux->pUserData); | |||
| 22754 | sqlite3_freesqlite3_api->free(pAux); | |||
| 22755 | } | |||
| 22756 | ||||
| 22757 | for(pTok=pGlobal->pTok; pTok; pTok=pNextTok){ | |||
| 22758 | pNextTok = pTok->pNext; | |||
| 22759 | if( pTok->xDestroy ) pTok->xDestroy(pTok->pUserData); | |||
| 22760 | sqlite3_freesqlite3_api->free(pTok); | |||
| 22761 | } | |||
| 22762 | ||||
| 22763 | sqlite3_freesqlite3_api->free(pGlobal); | |||
| 22764 | } | |||
| 22765 | ||||
| 22766 | /* | |||
| 22767 | ** Implementation of the fts5() function used by clients to obtain the | |||
| 22768 | ** API pointer. | |||
| 22769 | */ | |||
| 22770 | static void fts5Fts5Func( | |||
| 22771 | sqlite3_context *pCtx, /* Function call context */ | |||
| 22772 | int nArg, /* Number of args */ | |||
| 22773 | sqlite3_value **apArg /* Function arguments */ | |||
| 22774 | ){ | |||
| 22775 | Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_datasqlite3_api->user_data(pCtx); | |||
| 22776 | fts5_api **ppApi; | |||
| 22777 | UNUSED_PARAM(nArg)(void)(nArg); | |||
| 22778 | assert( nArg==1 )((void) (0)); | |||
| 22779 | ppApi = (fts5_api**)sqlite3_value_pointersqlite3_api->value_pointer(apArg[0], "fts5_api_ptr"); | |||
| 22780 | if( ppApi ) *ppApi = &pGlobal->api; | |||
| 22781 | } | |||
| 22782 | ||||
| 22783 | /* | |||
| 22784 | ** Implementation of fts5_source_id() function. | |||
| 22785 | */ | |||
| 22786 | static void fts5SourceIdFunc( | |||
| 22787 | sqlite3_context *pCtx, /* Function call context */ | |||
| 22788 | int nArg, /* Number of args */ | |||
| 22789 | sqlite3_value **apUnused /* Function arguments */ | |||
| 22790 | ){ | |||
| 22791 | assert( nArg==0 )((void) (0)); | |||
| 22792 | UNUSED_PARAM2(nArg, apUnused)(void)(nArg), (void)(apUnused); | |||
| 22793 | sqlite3_result_textsqlite3_api->result_text(pCtx, "fts5: 2026-06-03 19:12:13 d6e03d8c777cfa2d35e3b60d8ec3e0187f3e9f99d8e2ee9cac695fd6fcdf1a24", -1, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 22794 | } | |||
| 22795 | ||||
| 22796 | /* | |||
| 22797 | ** Implementation of fts5_locale(LOCALE, TEXT) function. | |||
| 22798 | ** | |||
| 22799 | ** If parameter LOCALE is NULL, or a zero-length string, then a copy of | |||
| 22800 | ** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as | |||
| 22801 | ** text, and the value returned is a blob consisting of: | |||
| 22802 | ** | |||
| 22803 | ** * The 4 bytes 0x00, 0xE0, 0xB2, 0xEb (FTS5_LOCALE_HEADER). | |||
| 22804 | ** * The LOCALE, as utf-8 text, followed by | |||
| 22805 | ** * 0x00, followed by | |||
| 22806 | ** * The TEXT, as utf-8 text. | |||
| 22807 | ** | |||
| 22808 | ** There is no final nul-terminator following the TEXT value. | |||
| 22809 | */ | |||
| 22810 | static void fts5LocaleFunc( | |||
| 22811 | sqlite3_context *pCtx, /* Function call context */ | |||
| 22812 | int nArg, /* Number of args */ | |||
| 22813 | sqlite3_value **apArg /* Function arguments */ | |||
| 22814 | ){ | |||
| 22815 | const char *zLocale = 0; | |||
| 22816 | i64 nLocale = 0; | |||
| 22817 | const char *zText = 0; | |||
| 22818 | i64 nText = 0; | |||
| 22819 | ||||
| 22820 | assert( nArg==2 )((void) (0)); | |||
| 22821 | UNUSED_PARAM(nArg)(void)(nArg); | |||
| 22822 | ||||
| 22823 | zLocale = (const char*)sqlite3_value_textsqlite3_api->value_text(apArg[0]); | |||
| 22824 | nLocale = sqlite3_value_bytessqlite3_api->value_bytes(apArg[0]); | |||
| 22825 | ||||
| 22826 | zText = (const char*)sqlite3_value_textsqlite3_api->value_text(apArg[1]); | |||
| 22827 | nText = sqlite3_value_bytessqlite3_api->value_bytes(apArg[1]); | |||
| 22828 | ||||
| 22829 | if( zLocale==0 || zLocale[0]=='\0' ){ | |||
| 22830 | sqlite3_result_textsqlite3_api->result_text(pCtx, zText, nText, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 22831 | }else{ | |||
| 22832 | Fts5Global *p = (Fts5Global*)sqlite3_user_datasqlite3_api->user_data(pCtx); | |||
| 22833 | u8 *pBlob = 0; | |||
| 22834 | u8 *pCsr = 0; | |||
| 22835 | i64 nBlob = 0; | |||
| 22836 | ||||
| 22837 | nBlob = FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) + nLocale + 1 + nText; | |||
| 22838 | pBlob = (u8*)sqlite3_malloc64sqlite3_api->malloc64(nBlob); | |||
| 22839 | if( pBlob==0 ){ | |||
| 22840 | sqlite3_result_error_nomemsqlite3_api->result_error_nomem(pCtx); | |||
| 22841 | return; | |||
| 22842 | } | |||
| 22843 | ||||
| 22844 | pCsr = pBlob; | |||
| 22845 | memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))); | |||
| 22846 | pCsr += FTS5_LOCALE_HDR_SIZE((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )); | |||
| 22847 | memcpy(pCsr, zLocale, nLocale); | |||
| 22848 | pCsr += nLocale; | |||
| 22849 | (*pCsr++) = 0x00; | |||
| 22850 | if( zText ) memcpy(pCsr, zText, nText); | |||
| 22851 | assert( &pCsr[nText]==&pBlob[nBlob] )((void) (0)); | |||
| 22852 | ||||
| 22853 | sqlite3_result_blobsqlite3_api->result_blob(pCtx, pBlob, nBlob, sqlite3_freesqlite3_api->free); | |||
| 22854 | } | |||
| 22855 | } | |||
| 22856 | ||||
| 22857 | /* | |||
| 22858 | ** Implementation of fts5_insttoken() function. | |||
| 22859 | */ | |||
| 22860 | static void fts5InsttokenFunc( | |||
| 22861 | sqlite3_context *pCtx, /* Function call context */ | |||
| 22862 | int nArg, /* Number of args */ | |||
| 22863 | sqlite3_value **apArg /* Function arguments */ | |||
| 22864 | ){ | |||
| 22865 | assert( nArg==1 )((void) (0)); | |||
| 22866 | (void)nArg; | |||
| 22867 | sqlite3_result_valuesqlite3_api->result_value(pCtx, apArg[0]); | |||
| 22868 | sqlite3_result_subtypesqlite3_api->result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE73); | |||
| 22869 | } | |||
| 22870 | ||||
| 22871 | /* | |||
| 22872 | ** Return true if zName is the extension on one of the shadow tables used | |||
| 22873 | ** by this module. | |||
| 22874 | */ | |||
| 22875 | static int fts5ShadowName(const char *zName){ | |||
| 22876 | static const char *azName[] = { | |||
| 22877 | "config", "content", "data", "docsize", "idx" | |||
| 22878 | }; | |||
| 22879 | unsigned int i; | |||
| 22880 | for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){ | |||
| 22881 | if( sqlite3_stricmpsqlite3_api->stricmp(zName, azName[i])==0 ) return 1; | |||
| 22882 | } | |||
| 22883 | return 0; | |||
| 22884 | } | |||
| 22885 | ||||
| 22886 | /* | |||
| 22887 | ** Run an integrity check on the FTS5 data structures. Return a string | |||
| 22888 | ** if anything is found amiss. Return a NULL pointer if everything is | |||
| 22889 | ** OK. | |||
| 22890 | */ | |||
| 22891 | static int fts5IntegrityMethod( | |||
| 22892 | sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */ | |||
| 22893 | const char *zSchema, /* Name of schema in which this table lives */ | |||
| 22894 | const char *zTabname, /* Name of the table itself */ | |||
| 22895 | int isQuick, /* True if this is a quick-check */ | |||
| 22896 | char **pzErr /* Write error message here */ | |||
| 22897 | ){ | |||
| 22898 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; | |||
| 22899 | int rc; | |||
| 22900 | ||||
| 22901 | assert( pzErr!=0 && *pzErr==0 )((void) (0)); | |||
| 22902 | UNUSED_PARAM(isQuick)(void)(isQuick); | |||
| 22903 | assert( pTab->p.pConfig->pzErrmsg==0 )((void) (0)); | |||
| 22904 | pTab->p.pConfig->pzErrmsg = pzErr; | |||
| 22905 | rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0); | |||
| 22906 | if( *pzErr==0 && rc!=SQLITE_OK0 ){ | |||
| 22907 | if( (rc&0xff)==SQLITE_CORRUPT11 ){ | |||
| 22908 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("malformed inverted index for FTS5 table %s.%s", | |||
| 22909 | zSchema, zTabname); | |||
| 22910 | rc = (*pzErr) ? SQLITE_OK0 : SQLITE_NOMEM7; | |||
| 22911 | }else{ | |||
| 22912 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("unable to validate the inverted index for" | |||
| 22913 | " FTS5 table %s.%s: %s", | |||
| 22914 | zSchema, zTabname, sqlite3_errstrsqlite3_api->errstr(rc)); | |||
| 22915 | } | |||
| 22916 | }else if( (rc&0xff)==SQLITE_CORRUPT11 ){ | |||
| 22917 | rc = SQLITE_OK0; | |||
| 22918 | } | |||
| 22919 | sqlite3Fts5IndexCloseReader(pTab->p.pIndex); | |||
| 22920 | pTab->p.pConfig->pzErrmsg = 0; | |||
| 22921 | ||||
| 22922 | return rc; | |||
| 22923 | } | |||
| 22924 | ||||
| 22925 | static int fts5Init(sqlite3 *db){ | |||
| 22926 | static const sqlite3_module fts5Mod = { | |||
| 22927 | /* iVersion */ 4, | |||
| 22928 | /* xCreate */ fts5CreateMethod, | |||
| 22929 | /* xConnect */ fts5ConnectMethod, | |||
| 22930 | /* xBestIndex */ fts5BestIndexMethod, | |||
| 22931 | /* xDisconnect */ fts5DisconnectMethod, | |||
| 22932 | /* xDestroy */ fts5DestroyMethod, | |||
| 22933 | /* xOpen */ fts5OpenMethod, | |||
| 22934 | /* xClose */ fts5CloseMethod, | |||
| 22935 | /* xFilter */ fts5FilterMethod, | |||
| 22936 | /* xNext */ fts5NextMethod, | |||
| 22937 | /* xEof */ fts5EofMethod, | |||
| 22938 | /* xColumn */ fts5ColumnMethod, | |||
| 22939 | /* xRowid */ fts5RowidMethod, | |||
| 22940 | /* xUpdate */ fts5UpdateMethod, | |||
| 22941 | /* xBegin */ fts5BeginMethod, | |||
| 22942 | /* xSync */ fts5SyncMethod, | |||
| 22943 | /* xCommit */ fts5CommitMethod, | |||
| 22944 | /* xRollback */ fts5RollbackMethod, | |||
| 22945 | /* xFindFunction */ fts5FindFunctionMethod, | |||
| 22946 | /* xRename */ fts5RenameMethod, | |||
| 22947 | /* xSavepoint */ fts5SavepointMethod, | |||
| 22948 | /* xRelease */ fts5ReleaseMethod, | |||
| 22949 | /* xRollbackTo */ fts5RollbackToMethod, | |||
| 22950 | /* xShadowName */ fts5ShadowName, | |||
| 22951 | /* xIntegrity */ fts5IntegrityMethod | |||
| 22952 | }; | |||
| 22953 | ||||
| 22954 | int rc; | |||
| 22955 | Fts5Global *pGlobal = 0; | |||
| 22956 | ||||
| 22957 | pGlobal = (Fts5Global*)sqlite3_malloc64sqlite3_api->malloc64(sizeof(Fts5Global)); | |||
| 22958 | if( pGlobal==0 ){ | |||
| 22959 | rc = SQLITE_NOMEM7; | |||
| 22960 | }else{ | |||
| 22961 | void *p = (void*)pGlobal; | |||
| 22962 | memset(pGlobal, 0, sizeof(Fts5Global)); | |||
| 22963 | pGlobal->db = db; | |||
| 22964 | pGlobal->api.iVersion = 3; | |||
| 22965 | pGlobal->api.xCreateFunction = fts5CreateAux; | |||
| 22966 | pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; | |||
| 22967 | pGlobal->api.xFindTokenizer = fts5FindTokenizer; | |||
| 22968 | pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; | |||
| 22969 | pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; | |||
| 22970 | ||||
| 22971 | /* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector. | |||
| 22972 | ** The constants below were generated randomly. */ | |||
| 22973 | sqlite3_randomnesssqlite3_api->randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr); | |||
| 22974 | pGlobal->aLocaleHdr[0] ^= 0xF924976D; | |||
| 22975 | pGlobal->aLocaleHdr[1] ^= 0x16596E13; | |||
| 22976 | pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA; | |||
| 22977 | pGlobal->aLocaleHdr[3] ^= 0x9B03A67F; | |||
| 22978 | assert( sizeof(pGlobal->aLocaleHdr)==16 )((void) (0)); | |||
| 22979 | ||||
| 22980 | rc = sqlite3_create_module_v2sqlite3_api->create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); | |||
| 22981 | if( rc==SQLITE_OK0 ) rc = sqlite3Fts5IndexInit(db); | |||
| 22982 | if( rc==SQLITE_OK0 ) rc = sqlite3Fts5ExprInit(pGlobal, db); | |||
| 22983 | if( rc==SQLITE_OK0 ) rc = sqlite3Fts5AuxInit(&pGlobal->api); | |||
| 22984 | if( rc==SQLITE_OK0 ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); | |||
| 22985 | if( rc==SQLITE_OK0 ) rc = sqlite3Fts5VocabInit(pGlobal, db); | |||
| 22986 | if( rc==SQLITE_OK0 ){ | |||
| 22987 | rc = sqlite3_create_functionsqlite3_api->create_function( | |||
| 22988 | db, "fts5", 1, SQLITE_UTF81, p, fts5Fts5Func, 0, 0 | |||
| 22989 | ); | |||
| 22990 | } | |||
| 22991 | if( rc==SQLITE_OK0 ){ | |||
| 22992 | rc = sqlite3_create_functionsqlite3_api->create_function( | |||
| 22993 | db, "fts5_source_id", 0, | |||
| 22994 | SQLITE_UTF81|SQLITE_DETERMINISTIC0x000000800|SQLITE_INNOCUOUS0x000200000, | |||
| 22995 | p, fts5SourceIdFunc, 0, 0 | |||
| 22996 | ); | |||
| 22997 | } | |||
| 22998 | if( rc==SQLITE_OK0 ){ | |||
| 22999 | rc = sqlite3_create_functionsqlite3_api->create_function( | |||
| 23000 | db, "fts5_locale", 2, | |||
| 23001 | SQLITE_UTF81|SQLITE_INNOCUOUS0x000200000|SQLITE_RESULT_SUBTYPE0x001000000|SQLITE_SUBTYPE0x000100000, | |||
| 23002 | p, fts5LocaleFunc, 0, 0 | |||
| 23003 | ); | |||
| 23004 | } | |||
| 23005 | if( rc==SQLITE_OK0 ){ | |||
| 23006 | rc = sqlite3_create_functionsqlite3_api->create_function( | |||
| 23007 | db, "fts5_insttoken", 1, | |||
| 23008 | SQLITE_UTF81|SQLITE_INNOCUOUS0x000200000|SQLITE_RESULT_SUBTYPE0x001000000, | |||
| 23009 | p, fts5InsttokenFunc, 0, 0 | |||
| 23010 | ); | |||
| 23011 | } | |||
| 23012 | } | |||
| 23013 | ||||
| 23014 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file | |||
| 23015 | ** fts5_test_mi.c is compiled and linked into the executable. And call | |||
| 23016 | ** its entry point to enable the matchinfo() demo. */ | |||
| 23017 | #ifdef SQLITE_FTS5_ENABLE_TEST_MI | |||
| 23018 | if( rc==SQLITE_OK0 ){ | |||
| 23019 | extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*); | |||
| 23020 | rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api); | |||
| 23021 | } | |||
| 23022 | #endif | |||
| 23023 | ||||
| 23024 | return rc; | |||
| 23025 | } | |||
| 23026 | ||||
| 23027 | /* | |||
| 23028 | ** The following functions are used to register the module with SQLite. If | |||
| 23029 | ** this module is being built as part of the SQLite core (SQLITE_CORE is | |||
| 23030 | ** defined), then sqlite3_open() will call sqlite3Fts5Init() directly. | |||
| 23031 | ** | |||
| 23032 | ** Or, if this module is being built as a loadable extension, | |||
| 23033 | ** sqlite3Fts5Init() is omitted and the two standard entry points | |||
| 23034 | ** sqlite3_fts_init() and sqlite3_fts5_init() defined instead. | |||
| 23035 | */ | |||
| 23036 | #ifndef SQLITE_CORE | |||
| 23037 | #ifdef _WIN32 | |||
| 23038 | __declspec(dllexport) | |||
| 23039 | #endif | |||
| 23040 | int sqlite3_fts_init( | |||
| 23041 | sqlite3 *db, | |||
| 23042 | char **pzErrMsg, | |||
| 23043 | const sqlite3_api_routines *pApi | |||
| 23044 | ){ | |||
| 23045 | SQLITE_EXTENSION_INIT2(pApi)sqlite3_api=pApi;; | |||
| 23046 | (void)pzErrMsg; /* Unused parameter */ | |||
| 23047 | return fts5Init(db); | |||
| 23048 | } | |||
| 23049 | ||||
| 23050 | #ifdef _WIN32 | |||
| 23051 | __declspec(dllexport) | |||
| 23052 | #endif | |||
| 23053 | int sqlite3_fts5_init( | |||
| 23054 | sqlite3 *db, | |||
| 23055 | char **pzErrMsg, | |||
| 23056 | const sqlite3_api_routines *pApi | |||
| 23057 | ){ | |||
| 23058 | SQLITE_EXTENSION_INIT2(pApi)sqlite3_api=pApi;; | |||
| 23059 | (void)pzErrMsg; /* Unused parameter */ | |||
| 23060 | return fts5Init(db); | |||
| 23061 | } | |||
| 23062 | #else | |||
| 23063 | int sqlite3Fts5Init(sqlite3 *db){ | |||
| 23064 | return fts5Init(db); | |||
| 23065 | } | |||
| 23066 | #endif | |||
| 23067 | ||||
| 23068 | #line 1 "fts5_storage.c" | |||
| 23069 | /* | |||
| 23070 | ** 2014 May 31 | |||
| 23071 | ** | |||
| 23072 | ** The author disclaims copyright to this source code. In place of | |||
| 23073 | ** a legal notice, here is a blessing: | |||
| 23074 | ** | |||
| 23075 | ** May you do good and not evil. | |||
| 23076 | ** May you find forgiveness for yourself and forgive others. | |||
| 23077 | ** May you share freely, never taking more than you give. | |||
| 23078 | ** | |||
| 23079 | ****************************************************************************** | |||
| 23080 | ** | |||
| 23081 | */ | |||
| 23082 | ||||
| 23083 | ||||
| 23084 | ||||
| 23085 | /* #include "fts5Int.h" */ | |||
| 23086 | ||||
| 23087 | /* | |||
| 23088 | ** pSavedRow: | |||
| 23089 | ** SQL statement FTS5_STMT_LOOKUP2 is a copy of FTS5_STMT_LOOKUP, it | |||
| 23090 | ** does a by-rowid lookup to retrieve a single row from the %_content | |||
| 23091 | ** table or equivalent external-content table/view. | |||
| 23092 | ** | |||
| 23093 | ** However, FTS5_STMT_LOOKUP2 is only used when retrieving the original | |||
| 23094 | ** values for a row being UPDATEd. In that case, the SQL statement is | |||
| 23095 | ** not reset and pSavedRow is set to point at it. This is so that the | |||
| 23096 | ** insert operation that follows the delete may access the original | |||
| 23097 | ** row values for any new values for which sqlite3_value_nochange() returns | |||
| 23098 | ** true. i.e. if the user executes: | |||
| 23099 | ** | |||
| 23100 | ** CREATE VIRTUAL TABLE ft USING fts5(a, b, c, locale=1); | |||
| 23101 | ** ... | |||
| 23102 | ** UPDATE fts SET a=?, b=? WHERE rowid=?; | |||
| 23103 | ** | |||
| 23104 | ** then the value passed to the xUpdate() method of this table as the | |||
| 23105 | ** new.c value is an sqlite3_value_nochange() value. So in this case it | |||
| 23106 | ** must be read from the saved row stored in Fts5Storage.pSavedRow. | |||
| 23107 | ** | |||
| 23108 | ** This is necessary - using sqlite3_value_nochange() instead of just having | |||
| 23109 | ** SQLite pass the original value back via xUpdate() - so as not to discard | |||
| 23110 | ** any locale information associated with such values. | |||
| 23111 | ** | |||
| 23112 | */ | |||
| 23113 | struct Fts5Storage { | |||
| 23114 | Fts5Config *pConfig; | |||
| 23115 | Fts5Index *pIndex; | |||
| 23116 | int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ | |||
| 23117 | i64 nTotalRow; /* Total number of rows in FTS table */ | |||
| 23118 | i64 *aTotalSize; /* Total sizes of each column */ | |||
| 23119 | sqlite3_stmt *pSavedRow; | |||
| 23120 | sqlite3_stmt *aStmt[12]; | |||
| 23121 | }; | |||
| 23122 | ||||
| 23123 | ||||
| 23124 | #if FTS5_STMT_SCAN_ASC0!=0 | |||
| 23125 | # error "FTS5_STMT_SCAN_ASC mismatch" | |||
| 23126 | #endif | |||
| 23127 | #if FTS5_STMT_SCAN_DESC1!=1 | |||
| 23128 | # error "FTS5_STMT_SCAN_DESC mismatch" | |||
| 23129 | #endif | |||
| 23130 | #if FTS5_STMT_LOOKUP2!=2 | |||
| 23131 | # error "FTS5_STMT_LOOKUP mismatch" | |||
| 23132 | #endif | |||
| 23133 | ||||
| 23134 | #define FTS5_STMT_LOOKUP23 3 | |||
| 23135 | #define FTS5_STMT_INSERT_CONTENT4 4 | |||
| 23136 | #define FTS5_STMT_REPLACE_CONTENT5 5 | |||
| 23137 | #define FTS5_STMT_DELETE_CONTENT6 6 | |||
| 23138 | #define FTS5_STMT_REPLACE_DOCSIZE7 7 | |||
| 23139 | #define FTS5_STMT_DELETE_DOCSIZE8 8 | |||
| 23140 | #define FTS5_STMT_LOOKUP_DOCSIZE9 9 | |||
| 23141 | #define FTS5_STMT_REPLACE_CONFIG10 10 | |||
| 23142 | #define FTS5_STMT_SCAN11 11 | |||
| 23143 | ||||
| 23144 | /* | |||
| 23145 | ** Prepare the two insert statements - Fts5Storage.pInsertContent and | |||
| 23146 | ** Fts5Storage.pInsertDocsize - if they have not already been prepared. | |||
| 23147 | ** Return SQLITE_OK if successful, or an SQLite error code if an error | |||
| 23148 | ** occurs. | |||
| 23149 | */ | |||
| 23150 | static int fts5StorageGetStmt( | |||
| 23151 | Fts5Storage *p, /* Storage handle */ | |||
| 23152 | int eStmt, /* FTS5_STMT_XXX constant */ | |||
| 23153 | sqlite3_stmt **ppStmt, /* OUT: Prepared statement handle */ | |||
| 23154 | char **pzErrMsg /* OUT: Error message (if any) */ | |||
| 23155 | ){ | |||
| 23156 | int rc = SQLITE_OK0; | |||
| 23157 | ||||
| 23158 | /* If there is no %_docsize table, there should be no requests for | |||
| 23159 | ** statements to operate on it. */ | |||
| 23160 | assert( p->pConfig->bColumnsize || (((void) (0)) | |||
| 23161 | eStmt!=FTS5_STMT_REPLACE_DOCSIZE((void) (0)) | |||
| 23162 | && eStmt!=FTS5_STMT_DELETE_DOCSIZE((void) (0)) | |||
| 23163 | && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE((void) (0)) | |||
| 23164 | ))((void) (0)); | |||
| 23165 | ||||
| 23166 | assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) )((void) (0)); | |||
| 23167 | if( p->aStmt[eStmt]==0 ){ | |||
| 23168 | const char *azStmt[] = { | |||
| 23169 | "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC", | |||
| 23170 | "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC", | |||
| 23171 | "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */ | |||
| 23172 | "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP2 */ | |||
| 23173 | ||||
| 23174 | "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ | |||
| 23175 | "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ | |||
| 23176 | "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */ | |||
| 23177 | "REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */ | |||
| 23178 | "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */ | |||
| 23179 | ||||
| 23180 | "SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ | |||
| 23181 | ||||
| 23182 | "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */ | |||
| 23183 | "SELECT %s FROM %s AS T", /* SCAN */ | |||
| 23184 | }; | |||
| 23185 | Fts5Config *pC = p->pConfig; | |||
| 23186 | char *zSql = 0; | |||
| 23187 | ||||
| 23188 | assert( ArraySize(azStmt)==ArraySize(p->aStmt) )((void) (0)); | |||
| 23189 | ||||
| 23190 | switch( eStmt ){ | |||
| 23191 | case FTS5_STMT_SCAN11: | |||
| 23192 | zSql = sqlite3_mprintfsqlite3_api->mprintf(azStmt[eStmt], | |||
| 23193 | pC->zContentExprlist, pC->zContent | |||
| 23194 | ); | |||
| 23195 | break; | |||
| 23196 | ||||
| 23197 | case FTS5_STMT_SCAN_ASC0: | |||
| 23198 | case FTS5_STMT_SCAN_DESC1: | |||
| 23199 | zSql = sqlite3_mprintfsqlite3_api->mprintf(azStmt[eStmt], pC->zContentExprlist, | |||
| 23200 | pC->zContent, pC->zContentRowid, pC->zContentRowid, | |||
| 23201 | pC->zContentRowid | |||
| 23202 | ); | |||
| 23203 | break; | |||
| 23204 | ||||
| 23205 | case FTS5_STMT_LOOKUP2: | |||
| 23206 | case FTS5_STMT_LOOKUP23: | |||
| 23207 | zSql = sqlite3_mprintfsqlite3_api->mprintf(azStmt[eStmt], | |||
| 23208 | pC->zContentExprlist, pC->zContent, pC->zContentRowid | |||
| 23209 | ); | |||
| 23210 | break; | |||
| 23211 | ||||
| 23212 | case FTS5_STMT_INSERT_CONTENT4: | |||
| 23213 | case FTS5_STMT_REPLACE_CONTENT5: { | |||
| 23214 | char *zBind = 0; | |||
| 23215 | int i; | |||
| 23216 | ||||
| 23217 | assert( pC->eContent==FTS5_CONTENT_NORMAL((void) (0)) | |||
| 23218 | || pC->eContent==FTS5_CONTENT_UNINDEXED((void) (0)) | |||
| 23219 | )((void) (0)); | |||
| 23220 | ||||
| 23221 | /* Add bindings for the "c*" columns - those that store the actual | |||
| 23222 | ** table content. If eContent==NORMAL, then there is one binding | |||
| 23223 | ** for each column. Or, if eContent==UNINDEXED, then there are only | |||
| 23224 | ** bindings for the UNINDEXED columns. */ | |||
| 23225 | for(i=0; rc==SQLITE_OK0 && i<(pC->nCol+1); i++){ | |||
| 23226 | if( !i || pC->eContent==FTS5_CONTENT_NORMAL0 || pC->abUnindexed[i-1] ){ | |||
| 23227 | zBind = sqlite3Fts5Mprintf(&rc, "%z%s?%d", zBind, zBind?",":"",i+1); | |||
| 23228 | } | |||
| 23229 | } | |||
| 23230 | ||||
| 23231 | /* Add bindings for any "l*" columns. Only non-UNINDEXED columns | |||
| 23232 | ** require these. */ | |||
| 23233 | if( pC->bLocale && pC->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 23234 | for(i=0; rc==SQLITE_OK0 && i<pC->nCol; i++){ | |||
| 23235 | if( pC->abUnindexed[i]==0 ){ | |||
| 23236 | zBind = sqlite3Fts5Mprintf(&rc, "%z,?%d", zBind, pC->nCol+i+2); | |||
| 23237 | } | |||
| 23238 | } | |||
| 23239 | } | |||
| 23240 | ||||
| 23241 | zSql = sqlite3Fts5Mprintf(&rc, azStmt[eStmt], pC->zDb, pC->zName,zBind); | |||
| 23242 | sqlite3_freesqlite3_api->free(zBind); | |||
| 23243 | break; | |||
| 23244 | } | |||
| 23245 | ||||
| 23246 | case FTS5_STMT_REPLACE_DOCSIZE7: | |||
| 23247 | zSql = sqlite3_mprintfsqlite3_api->mprintf(azStmt[eStmt], pC->zDb, pC->zName, | |||
| 23248 | (pC->bContentlessDelete ? ",?" : "") | |||
| 23249 | ); | |||
| 23250 | break; | |||
| 23251 | ||||
| 23252 | case FTS5_STMT_LOOKUP_DOCSIZE9: | |||
| 23253 | zSql = sqlite3_mprintfsqlite3_api->mprintf(azStmt[eStmt], | |||
| 23254 | (pC->bContentlessDelete ? ",origin" : ""), | |||
| 23255 | pC->zDb, pC->zName | |||
| 23256 | ); | |||
| 23257 | break; | |||
| 23258 | ||||
| 23259 | default: | |||
| 23260 | zSql = sqlite3_mprintfsqlite3_api->mprintf(azStmt[eStmt], pC->zDb, pC->zName); | |||
| 23261 | break; | |||
| 23262 | } | |||
| 23263 | ||||
| 23264 | if( zSql==0 ){ | |||
| 23265 | rc = SQLITE_NOMEM7; | |||
| 23266 | }else{ | |||
| 23267 | int f = SQLITE_PREPARE_PERSISTENT0x01; | |||
| 23268 | if( eStmt>FTS5_STMT_LOOKUP23 ) f |= SQLITE_PREPARE_NO_VTAB0x04; | |||
| 23269 | p->pConfig->bLock++; | |||
| 23270 | rc = sqlite3_prepare_v3sqlite3_api->prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0); | |||
| 23271 | p->pConfig->bLock--; | |||
| 23272 | sqlite3_freesqlite3_api->free(zSql); | |||
| 23273 | if( rc!=SQLITE_OK0 && pzErrMsg ){ | |||
| 23274 | *pzErrMsg = sqlite3_mprintfsqlite3_api->mprintf("%s", sqlite3_errmsgsqlite3_api->errmsg(pC->db)); | |||
| 23275 | } | |||
| 23276 | if( rc==SQLITE_ERROR1 && eStmt>FTS5_STMT_LOOKUP23 && eStmt<FTS5_STMT_SCAN11 ){ | |||
| 23277 | /* One of the internal tables - not the %_content table - is missing. | |||
| 23278 | ** This counts as a corrupted table. */ | |||
| 23279 | rc = SQLITE_CORRUPT11; | |||
| 23280 | } | |||
| 23281 | } | |||
| 23282 | } | |||
| 23283 | ||||
| 23284 | *ppStmt = p->aStmt[eStmt]; | |||
| 23285 | sqlite3_resetsqlite3_api->reset(*ppStmt); | |||
| 23286 | return rc; | |||
| 23287 | } | |||
| 23288 | ||||
| 23289 | ||||
| 23290 | static int fts5ExecPrintf( | |||
| 23291 | sqlite3 *db, | |||
| 23292 | char **pzErr, | |||
| 23293 | const char *zFormat, | |||
| 23294 | ... | |||
| 23295 | ){ | |||
| 23296 | int rc; | |||
| 23297 | va_list ap; /* ... printf arguments */ | |||
| 23298 | char *zSql; | |||
| 23299 | ||||
| 23300 | va_start(ap, zFormat)__builtin_va_start(ap, zFormat); | |||
| 23301 | zSql = sqlite3_vmprintfsqlite3_api->vmprintf(zFormat, ap); | |||
| 23302 | ||||
| 23303 | if( zSql==0 ){ | |||
| 23304 | rc = SQLITE_NOMEM7; | |||
| 23305 | }else{ | |||
| 23306 | rc = sqlite3_execsqlite3_api->exec(db, zSql, 0, 0, pzErr); | |||
| 23307 | sqlite3_freesqlite3_api->free(zSql); | |||
| 23308 | } | |||
| 23309 | ||||
| 23310 | va_end(ap)__builtin_va_end(ap); | |||
| 23311 | return rc; | |||
| 23312 | } | |||
| 23313 | ||||
| 23314 | /* | |||
| 23315 | ** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error | |||
| 23316 | ** code otherwise. | |||
| 23317 | */ | |||
| 23318 | static int sqlite3Fts5DropAll(Fts5Config *pConfig){ | |||
| 23319 | int rc = fts5ExecPrintf(pConfig->db, 0, | |||
| 23320 | "DROP TABLE IF EXISTS %Q.'%q_data';" | |||
| 23321 | "DROP TABLE IF EXISTS %Q.'%q_idx';" | |||
| 23322 | "DROP TABLE IF EXISTS %Q.'%q_config';", | |||
| 23323 | pConfig->zDb, pConfig->zName, | |||
| 23324 | pConfig->zDb, pConfig->zName, | |||
| 23325 | pConfig->zDb, pConfig->zName | |||
| 23326 | ); | |||
| 23327 | if( rc==SQLITE_OK0 && pConfig->bColumnsize ){ | |||
| 23328 | rc = fts5ExecPrintf(pConfig->db, 0, | |||
| 23329 | "DROP TABLE IF EXISTS %Q.'%q_docsize';", | |||
| 23330 | pConfig->zDb, pConfig->zName | |||
| 23331 | ); | |||
| 23332 | } | |||
| 23333 | if( rc==SQLITE_OK0 && pConfig->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 23334 | rc = fts5ExecPrintf(pConfig->db, 0, | |||
| 23335 | "DROP TABLE IF EXISTS %Q.'%q_content';", | |||
| 23336 | pConfig->zDb, pConfig->zName | |||
| 23337 | ); | |||
| 23338 | } | |||
| 23339 | return rc; | |||
| 23340 | } | |||
| 23341 | ||||
| 23342 | static void fts5StorageRenameOne( | |||
| 23343 | Fts5Config *pConfig, /* Current FTS5 configuration */ | |||
| 23344 | int *pRc, /* IN/OUT: Error code */ | |||
| 23345 | const char *zTail, /* Tail of table name e.g. "data", "config" */ | |||
| 23346 | const char *zName /* New name of FTS5 table */ | |||
| 23347 | ){ | |||
| 23348 | if( *pRc==SQLITE_OK0 ){ | |||
| 23349 | *pRc = fts5ExecPrintf(pConfig->db, 0, | |||
| 23350 | "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';", | |||
| 23351 | pConfig->zDb, pConfig->zName, zTail, zName, zTail | |||
| 23352 | ); | |||
| 23353 | } | |||
| 23354 | } | |||
| 23355 | ||||
| 23356 | static int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){ | |||
| 23357 | Fts5Config *pConfig = pStorage->pConfig; | |||
| 23358 | int rc = sqlite3Fts5StorageSync(pStorage); | |||
| 23359 | ||||
| 23360 | fts5StorageRenameOne(pConfig, &rc, "data", zName); | |||
| 23361 | fts5StorageRenameOne(pConfig, &rc, "idx", zName); | |||
| 23362 | fts5StorageRenameOne(pConfig, &rc, "config", zName); | |||
| 23363 | if( pConfig->bColumnsize ){ | |||
| 23364 | fts5StorageRenameOne(pConfig, &rc, "docsize", zName); | |||
| 23365 | } | |||
| 23366 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 23367 | fts5StorageRenameOne(pConfig, &rc, "content", zName); | |||
| 23368 | } | |||
| 23369 | return rc; | |||
| 23370 | } | |||
| 23371 | ||||
| 23372 | /* | |||
| 23373 | ** Create the shadow table named zPost, with definition zDefn. Return | |||
| 23374 | ** SQLITE_OK if successful, or an SQLite error code otherwise. | |||
| 23375 | */ | |||
| 23376 | static int sqlite3Fts5CreateTable( | |||
| 23377 | Fts5Config *pConfig, /* FTS5 configuration */ | |||
| 23378 | const char *zPost, /* Shadow table to create (e.g. "content") */ | |||
| 23379 | const char *zDefn, /* Columns etc. for shadow table */ | |||
| 23380 | int bWithout, /* True for without rowid */ | |||
| 23381 | char **pzErr /* OUT: Error message */ | |||
| 23382 | ){ | |||
| 23383 | int rc; | |||
| 23384 | char *zErr = 0; | |||
| 23385 | ||||
| 23386 | rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s", | |||
| 23387 | pConfig->zDb, pConfig->zName, zPost, zDefn, | |||
| 23388 | #ifndef SQLITE_FTS5_NO_WITHOUT_ROWID | |||
| 23389 | bWithout?" WITHOUT ROWID": | |||
| 23390 | #endif | |||
| 23391 | "" | |||
| 23392 | ); | |||
| 23393 | if( zErr ){ | |||
| 23394 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 23395 | "fts5: error creating shadow table %q_%s: %s", | |||
| 23396 | pConfig->zName, zPost, zErr | |||
| 23397 | ); | |||
| 23398 | sqlite3_freesqlite3_api->free(zErr); | |||
| 23399 | } | |||
| 23400 | ||||
| 23401 | return rc; | |||
| 23402 | } | |||
| 23403 | ||||
| 23404 | /* | |||
| 23405 | ** Open a new Fts5Index handle. If the bCreate argument is true, create | |||
| 23406 | ** and initialize the underlying tables | |||
| 23407 | ** | |||
| 23408 | ** If successful, set *pp to point to the new object and return SQLITE_OK. | |||
| 23409 | ** Otherwise, set *pp to NULL and return an SQLite error code. | |||
| 23410 | */ | |||
| 23411 | static int sqlite3Fts5StorageOpen( | |||
| 23412 | Fts5Config *pConfig, | |||
| 23413 | Fts5Index *pIndex, | |||
| 23414 | int bCreate, | |||
| 23415 | Fts5Storage **pp, | |||
| 23416 | char **pzErr /* OUT: Error message */ | |||
| 23417 | ){ | |||
| 23418 | int rc = SQLITE_OK0; | |||
| 23419 | Fts5Storage *p; /* New object */ | |||
| 23420 | sqlite3_int64 nByte; /* Bytes of space to allocate */ | |||
| 23421 | ||||
| 23422 | nByte = sizeof(Fts5Storage) /* Fts5Storage object */ | |||
| 23423 | + pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */ | |||
| 23424 | *pp = p = (Fts5Storage*)sqlite3_malloc64sqlite3_api->malloc64(nByte); | |||
| 23425 | if( !p ) return SQLITE_NOMEM7; | |||
| 23426 | ||||
| 23427 | memset(p, 0, (size_t)nByte); | |||
| 23428 | p->aTotalSize = (i64*)&p[1]; | |||
| 23429 | p->pConfig = pConfig; | |||
| 23430 | p->pIndex = pIndex; | |||
| 23431 | ||||
| 23432 | if( bCreate ){ | |||
| 23433 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 | |||
| 23434 | || pConfig->eContent==FTS5_CONTENT_UNINDEXED3 | |||
| 23435 | ){ | |||
| 23436 | int nDefn = 32 + pConfig->nCol*10; | |||
| 23437 | char *zDefn = sqlite3_malloc64sqlite3_api->malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); | |||
| 23438 | if( zDefn==0 ){ | |||
| 23439 | rc = SQLITE_NOMEM7; | |||
| 23440 | }else{ | |||
| 23441 | int i; | |||
| 23442 | int iOff; | |||
| 23443 | sqlite3_snprintfsqlite3_api->xsnprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); | |||
| 23444 | iOff = (int)strlen(zDefn); | |||
| 23445 | for(i=0; i<pConfig->nCol; i++){ | |||
| 23446 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 | |||
| 23447 | || pConfig->abUnindexed[i] | |||
| 23448 | ){ | |||
| 23449 | sqlite3_snprintfsqlite3_api->xsnprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); | |||
| 23450 | iOff += (int)strlen(&zDefn[iOff]); | |||
| 23451 | } | |||
| 23452 | } | |||
| 23453 | if( pConfig->bLocale ){ | |||
| 23454 | for(i=0; i<pConfig->nCol; i++){ | |||
| 23455 | if( pConfig->abUnindexed[i]==0 ){ | |||
| 23456 | sqlite3_snprintfsqlite3_api->xsnprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); | |||
| 23457 | iOff += (int)strlen(&zDefn[iOff]); | |||
| 23458 | } | |||
| 23459 | } | |||
| 23460 | } | |||
| 23461 | rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); | |||
| 23462 | } | |||
| 23463 | sqlite3_freesqlite3_api->free(zDefn); | |||
| 23464 | } | |||
| 23465 | ||||
| 23466 | if( rc==SQLITE_OK0 && pConfig->bColumnsize ){ | |||
| 23467 | const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB"; | |||
| 23468 | if( pConfig->bContentlessDelete ){ | |||
| 23469 | zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER"; | |||
| 23470 | } | |||
| 23471 | rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr); | |||
| 23472 | } | |||
| 23473 | if( rc==SQLITE_OK0 ){ | |||
| 23474 | rc = sqlite3Fts5CreateTable( | |||
| 23475 | pConfig, "config", "k PRIMARY KEY, v", 1, pzErr | |||
| 23476 | ); | |||
| 23477 | } | |||
| 23478 | if( rc==SQLITE_OK0 ){ | |||
| 23479 | rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION4); | |||
| 23480 | } | |||
| 23481 | } | |||
| 23482 | ||||
| 23483 | if( rc ){ | |||
| 23484 | sqlite3Fts5StorageClose(p); | |||
| 23485 | *pp = 0; | |||
| 23486 | } | |||
| 23487 | return rc; | |||
| 23488 | } | |||
| 23489 | ||||
| 23490 | /* | |||
| 23491 | ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen(). | |||
| 23492 | */ | |||
| 23493 | static int sqlite3Fts5StorageClose(Fts5Storage *p){ | |||
| 23494 | int rc = SQLITE_OK0; | |||
| 23495 | if( p ){ | |||
| 23496 | int i; | |||
| 23497 | ||||
| 23498 | /* Finalize all SQL statements */ | |||
| 23499 | for(i=0; i<ArraySize(p->aStmt)((int)(sizeof(p->aStmt) / sizeof(p->aStmt[0]))); i++){ | |||
| 23500 | sqlite3_finalizesqlite3_api->finalize(p->aStmt[i]); | |||
| 23501 | } | |||
| 23502 | ||||
| 23503 | sqlite3_freesqlite3_api->free(p); | |||
| 23504 | } | |||
| 23505 | return rc; | |||
| 23506 | } | |||
| 23507 | ||||
| 23508 | typedef struct Fts5InsertCtx Fts5InsertCtx; | |||
| 23509 | struct Fts5InsertCtx { | |||
| 23510 | Fts5Storage *pStorage; | |||
| 23511 | int iCol; | |||
| 23512 | int szCol; /* Size of column value in tokens */ | |||
| 23513 | }; | |||
| 23514 | ||||
| 23515 | /* | |||
| 23516 | ** Tokenization callback used when inserting tokens into the FTS index. | |||
| 23517 | */ | |||
| 23518 | static int fts5StorageInsertCallback( | |||
| 23519 | void *pContext, /* Pointer to Fts5InsertCtx object */ | |||
| 23520 | int tflags, | |||
| 23521 | const char *pToken, /* Buffer containing token */ | |||
| 23522 | int nToken, /* Size of token in bytes */ | |||
| 23523 | int iUnused1, /* Start offset of token */ | |||
| 23524 | int iUnused2 /* End offset of token */ | |||
| 23525 | ){ | |||
| 23526 | Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext; | |||
| 23527 | Fts5Index *pIdx = pCtx->pStorage->pIndex; | |||
| 23528 | UNUSED_PARAM2(iUnused1, iUnused2)(void)(iUnused1), (void)(iUnused2); | |||
| 23529 | if( nToken>FTS5_MAX_TOKEN_SIZE32768 ) nToken = FTS5_MAX_TOKEN_SIZE32768; | |||
| 23530 | if( (tflags & FTS5_TOKEN_COLOCATED0x0001)==0 || pCtx->szCol==0 ){ | |||
| 23531 | pCtx->szCol++; | |||
| 23532 | } | |||
| 23533 | return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); | |||
| 23534 | } | |||
| 23535 | ||||
| 23536 | /* | |||
| 23537 | ** This function is used as part of an UPDATE statement that modifies the | |||
| 23538 | ** rowid of a row. In that case, this function is called first to set | |||
| 23539 | ** Fts5Storage.pSavedRow to point to a statement that may be used to | |||
| 23540 | ** access the original values of the row being deleted - iDel. | |||
| 23541 | ** | |||
| 23542 | ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. | |||
| 23543 | ** It is not considered an error if row iDel does not exist. In this case | |||
| 23544 | ** pSavedRow is not set and SQLITE_OK returned. | |||
| 23545 | */ | |||
| 23546 | static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){ | |||
| 23547 | int rc = SQLITE_OK0; | |||
| 23548 | sqlite3_stmt *pSeek = 0; | |||
| 23549 | ||||
| 23550 | assert( p->pSavedRow==0 )((void) (0)); | |||
| 23551 | rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP2+1, &pSeek, 0); | |||
| 23552 | if( rc==SQLITE_OK0 ){ | |||
| 23553 | sqlite3_bind_int64sqlite3_api->bind_int64(pSeek, 1, iDel); | |||
| 23554 | if( sqlite3_stepsqlite3_api->step(pSeek)!=SQLITE_ROW100 ){ | |||
| 23555 | rc = sqlite3_resetsqlite3_api->reset(pSeek); | |||
| 23556 | }else{ | |||
| 23557 | p->pSavedRow = pSeek; | |||
| 23558 | } | |||
| 23559 | } | |||
| 23560 | ||||
| 23561 | return rc; | |||
| 23562 | } | |||
| 23563 | ||||
| 23564 | /* | |||
| 23565 | ** If a row with rowid iDel is present in the %_content table, add the | |||
| 23566 | ** delete-markers to the FTS index necessary to delete it. Do not actually | |||
| 23567 | ** remove the %_content row at this time though. | |||
| 23568 | ** | |||
| 23569 | ** If parameter bSaveRow is true, then Fts5Storage.pSavedRow is left | |||
| 23570 | ** pointing to a statement (FTS5_STMT_LOOKUP2) that may be used to access | |||
| 23571 | ** the original values of the row being deleted. This is used by UPDATE | |||
| 23572 | ** statements. | |||
| 23573 | */ | |||
| 23574 | static int fts5StorageDeleteFromIndex( | |||
| 23575 | Fts5Storage *p, | |||
| 23576 | i64 iDel, | |||
| 23577 | sqlite3_value **apVal, | |||
| 23578 | int bSaveRow /* True to set pSavedRow */ | |||
| 23579 | ){ | |||
| 23580 | Fts5Config *pConfig = p->pConfig; | |||
| 23581 | sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ | |||
| 23582 | int rc = SQLITE_OK0; /* Return code */ | |||
| 23583 | int rc2; /* sqlite3_reset() return code */ | |||
| 23584 | int iCol; | |||
| 23585 | Fts5InsertCtx ctx; | |||
| 23586 | ||||
| 23587 | assert( bSaveRow==0 || apVal==0 )((void) (0)); | |||
| 23588 | assert( bSaveRow==0 || bSaveRow==1 )((void) (0)); | |||
| 23589 | assert( FTS5_STMT_LOOKUP2==FTS5_STMT_LOOKUP+1 )((void) (0)); | |||
| 23590 | ||||
| 23591 | if( apVal
| |||
| 23592 | if( p->pSavedRow && bSaveRow ){ | |||
| 23593 | pSeek = p->pSavedRow; | |||
| 23594 | p->pSavedRow = 0; | |||
| 23595 | }else{ | |||
| 23596 | rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP2+bSaveRow, &pSeek, 0); | |||
| 23597 | if( rc!=SQLITE_OK0 ) return rc; | |||
| 23598 | sqlite3_bind_int64sqlite3_api->bind_int64(pSeek, 1, iDel); | |||
| 23599 | if( sqlite3_stepsqlite3_api->step(pSeek)!=SQLITE_ROW100 ){ | |||
| 23600 | return sqlite3_resetsqlite3_api->reset(pSeek); | |||
| 23601 | } | |||
| 23602 | } | |||
| 23603 | } | |||
| 23604 | ||||
| 23605 | ctx.pStorage = p; | |||
| 23606 | ctx.iCol = -1; | |||
| 23607 | for(iCol=1; rc
| |||
| 23608 | if( pConfig->abUnindexed[iCol-1]==0 ){ | |||
| 23609 | sqlite3_value *pVal = 0; | |||
| 23610 | sqlite3_value *pFree = 0; | |||
| 23611 | const char *pText = 0; | |||
| 23612 | int nText = 0; | |||
| 23613 | const char *pLoc = 0; | |||
| 23614 | int nLoc = 0; | |||
| 23615 | ||||
| 23616 | assert( pSeek==0 || apVal==0 )((void) (0)); | |||
| 23617 | assert( pSeek!=0 || apVal!=0 )((void) (0)); | |||
| 23618 | if( pSeek ){ | |||
| 23619 | pVal = sqlite3_column_valuesqlite3_api->column_value(pSeek, iCol); | |||
| 23620 | }else{ | |||
| 23621 | pVal = apVal[iCol-1]; | |||
| ||||
| 23622 | } | |||
| 23623 | ||||
| 23624 | if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |||
| 23625 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |||
| 23626 | }else{ | |||
| 23627 | if( sqlite3_value_typesqlite3_api->value_type(pVal)!=SQLITE_TEXT3 ){ | |||
| 23628 | /* Make a copy of the value to work with. This is because the call | |||
| 23629 | ** to sqlite3_value_text() below forces the type of the value to | |||
| 23630 | ** SQLITE_TEXT, and we may need to use it again later. */ | |||
| 23631 | pFree = pVal = sqlite3_value_dupsqlite3_api->value_dup(pVal); | |||
| 23632 | if( pVal==0 ){ | |||
| 23633 | rc = SQLITE_NOMEM7; | |||
| 23634 | } | |||
| 23635 | } | |||
| 23636 | if( rc==SQLITE_OK0 ){ | |||
| 23637 | pText = (const char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 23638 | nText = sqlite3_value_bytessqlite3_api->value_bytes(pVal); | |||
| 23639 | if( pConfig->bLocale && pSeek ){ | |||
| 23640 | pLoc = (const char*)sqlite3_column_textsqlite3_api->column_text(pSeek, iCol+pConfig->nCol); | |||
| 23641 | nLoc = sqlite3_column_bytessqlite3_api->column_bytes(pSeek, iCol + pConfig->nCol); | |||
| 23642 | } | |||
| 23643 | } | |||
| 23644 | } | |||
| 23645 | ||||
| 23646 | if( rc==SQLITE_OK0 ){ | |||
| 23647 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |||
| 23648 | ctx.szCol = 0; | |||
| 23649 | rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT0x0004, | |||
| 23650 | pText, nText, (void*)&ctx, fts5StorageInsertCallback | |||
| 23651 | ); | |||
| 23652 | p->aTotalSize[iCol-1] -= (i64)ctx.szCol; | |||
| 23653 | if( rc==SQLITE_OK0 && p->aTotalSize[iCol-1]<0 ){ | |||
| 23654 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 23655 | } | |||
| 23656 | sqlite3Fts5ClearLocale(pConfig); | |||
| 23657 | } | |||
| 23658 | sqlite3_value_freesqlite3_api->value_free(pFree); | |||
| 23659 | } | |||
| 23660 | } | |||
| 23661 | if( rc==SQLITE_OK0 && p->nTotalRow<1 ){ | |||
| 23662 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 23663 | }else{ | |||
| 23664 | p->nTotalRow--; | |||
| 23665 | } | |||
| 23666 | ||||
| 23667 | if( rc==SQLITE_OK0 && bSaveRow ){ | |||
| 23668 | assert( p->pSavedRow==0 )((void) (0)); | |||
| 23669 | p->pSavedRow = pSeek; | |||
| 23670 | }else{ | |||
| 23671 | rc2 = sqlite3_resetsqlite3_api->reset(pSeek); | |||
| 23672 | if( rc==SQLITE_OK0 ) rc = rc2; | |||
| 23673 | } | |||
| 23674 | return rc; | |||
| 23675 | } | |||
| 23676 | ||||
| 23677 | /* | |||
| 23678 | ** Reset any saved statement pSavedRow. Zero pSavedRow as well. This | |||
| 23679 | ** should be called by the xUpdate() method of the fts5 table before | |||
| 23680 | ** returning from any operation that may have set Fts5Storage.pSavedRow. | |||
| 23681 | */ | |||
| 23682 | static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){ | |||
| 23683 | assert( pStorage->pSavedRow==0((void) (0)) | |||
| 23684 | || pStorage->pSavedRow==pStorage->aStmt[FTS5_STMT_LOOKUP2]((void) (0)) | |||
| 23685 | )((void) (0)); | |||
| 23686 | sqlite3_resetsqlite3_api->reset(pStorage->pSavedRow); | |||
| 23687 | pStorage->pSavedRow = 0; | |||
| 23688 | } | |||
| 23689 | ||||
| 23690 | /* | |||
| 23691 | ** This function is called to process a DELETE on a contentless_delete=1 | |||
| 23692 | ** table. It adds the tombstone required to delete the entry with rowid | |||
| 23693 | ** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs, | |||
| 23694 | ** an SQLite error code. | |||
| 23695 | */ | |||
| 23696 | static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){ | |||
| 23697 | i64 iOrigin = 0; | |||
| 23698 | sqlite3_stmt *pLookup = 0; | |||
| 23699 | int rc = SQLITE_OK0; | |||
| 23700 | ||||
| 23701 | assert( p->pConfig->bContentlessDelete )((void) (0)); | |||
| 23702 | assert( p->pConfig->eContent==FTS5_CONTENT_NONE((void) (0)) | |||
| 23703 | || p->pConfig->eContent==FTS5_CONTENT_UNINDEXED((void) (0)) | |||
| 23704 | )((void) (0)); | |||
| 23705 | ||||
| 23706 | /* Look up the origin of the document in the %_docsize table. Store | |||
| 23707 | ** this in stack variable iOrigin. */ | |||
| 23708 | rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE9, &pLookup, 0); | |||
| 23709 | if( rc==SQLITE_OK0 ){ | |||
| 23710 | sqlite3_bind_int64sqlite3_api->bind_int64(pLookup, 1, iDel); | |||
| 23711 | if( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pLookup) ){ | |||
| 23712 | iOrigin = sqlite3_column_int64sqlite3_api->column_int64(pLookup, 1); | |||
| 23713 | } | |||
| 23714 | rc = sqlite3_resetsqlite3_api->reset(pLookup); | |||
| 23715 | } | |||
| 23716 | ||||
| 23717 | if( rc==SQLITE_OK0 && iOrigin!=0 ){ | |||
| 23718 | rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel); | |||
| 23719 | } | |||
| 23720 | ||||
| 23721 | return rc; | |||
| 23722 | } | |||
| 23723 | ||||
| 23724 | /* | |||
| 23725 | ** Insert a record into the %_docsize table. Specifically, do: | |||
| 23726 | ** | |||
| 23727 | ** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf); | |||
| 23728 | ** | |||
| 23729 | ** If there is no %_docsize table (as happens if the columnsize=0 option | |||
| 23730 | ** is specified when the FTS5 table is created), this function is a no-op. | |||
| 23731 | */ | |||
| 23732 | static int fts5StorageInsertDocsize( | |||
| 23733 | Fts5Storage *p, /* Storage module to write to */ | |||
| 23734 | i64 iRowid, /* id value */ | |||
| 23735 | Fts5Buffer *pBuf /* sz value */ | |||
| 23736 | ){ | |||
| 23737 | int rc = SQLITE_OK0; | |||
| 23738 | if( p->pConfig->bColumnsize ){ | |||
| 23739 | sqlite3_stmt *pReplace = 0; | |||
| 23740 | rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE7, &pReplace, 0); | |||
| 23741 | if( rc==SQLITE_OK0 ){ | |||
| 23742 | sqlite3_bind_int64sqlite3_api->bind_int64(pReplace, 1, iRowid); | |||
| 23743 | if( p->pConfig->bContentlessDelete ){ | |||
| 23744 | i64 iOrigin = 0; | |||
| 23745 | rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin); | |||
| 23746 | sqlite3_bind_int64sqlite3_api->bind_int64(pReplace, 3, iOrigin); | |||
| 23747 | } | |||
| 23748 | } | |||
| 23749 | if( rc==SQLITE_OK0 ){ | |||
| 23750 | sqlite3_bind_blobsqlite3_api->bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 23751 | sqlite3_stepsqlite3_api->step(pReplace); | |||
| 23752 | rc = sqlite3_resetsqlite3_api->reset(pReplace); | |||
| 23753 | sqlite3_bind_nullsqlite3_api->bind_null(pReplace, 2); | |||
| 23754 | } | |||
| 23755 | } | |||
| 23756 | return rc; | |||
| 23757 | } | |||
| 23758 | ||||
| 23759 | /* | |||
| 23760 | ** Load the contents of the "averages" record from disk into the | |||
| 23761 | ** p->nTotalRow and p->aTotalSize[] variables. If successful, and if | |||
| 23762 | ** argument bCache is true, set the p->bTotalsValid flag to indicate | |||
| 23763 | ** that the contents of aTotalSize[] and nTotalRow are valid until | |||
| 23764 | ** further notice. | |||
| 23765 | ** | |||
| 23766 | ** Return SQLITE_OK if successful, or an SQLite error code if an error | |||
| 23767 | ** occurs. | |||
| 23768 | */ | |||
| 23769 | static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){ | |||
| 23770 | int rc = SQLITE_OK0; | |||
| 23771 | if( p->bTotalsValid==0 ){ | |||
| 23772 | rc = sqlite3Fts5IndexGetAverages(p->pIndex, &p->nTotalRow, p->aTotalSize); | |||
| 23773 | p->bTotalsValid = bCache; | |||
| 23774 | } | |||
| 23775 | return rc; | |||
| 23776 | } | |||
| 23777 | ||||
| 23778 | /* | |||
| 23779 | ** Store the current contents of the p->nTotalRow and p->aTotalSize[] | |||
| 23780 | ** variables in the "averages" record on disk. | |||
| 23781 | ** | |||
| 23782 | ** Return SQLITE_OK if successful, or an SQLite error code if an error | |||
| 23783 | ** occurs. | |||
| 23784 | */ | |||
| 23785 | static int fts5StorageSaveTotals(Fts5Storage *p){ | |||
| 23786 | int nCol = p->pConfig->nCol; | |||
| 23787 | int i; | |||
| 23788 | Fts5Buffer buf; | |||
| 23789 | int rc = SQLITE_OK0; | |||
| 23790 | memset(&buf, 0, sizeof(buf)); | |||
| 23791 | ||||
| 23792 | sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow); | |||
| 23793 | for(i=0; i<nCol; i++){ | |||
| 23794 | sqlite3Fts5BufferAppendVarint(&rc, &buf, p->aTotalSize[i]); | |||
| 23795 | } | |||
| 23796 | if( rc==SQLITE_OK0 ){ | |||
| 23797 | rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n); | |||
| 23798 | } | |||
| 23799 | sqlite3_freesqlite3_api->free(buf.p); | |||
| 23800 | ||||
| 23801 | return rc; | |||
| 23802 | } | |||
| 23803 | ||||
| 23804 | /* | |||
| 23805 | ** Remove a row from the FTS table. | |||
| 23806 | */ | |||
| 23807 | static int sqlite3Fts5StorageDelete( | |||
| 23808 | Fts5Storage *p, /* Storage object */ | |||
| 23809 | i64 iDel, /* Rowid to delete from table */ | |||
| 23810 | sqlite3_value **apVal, /* Optional - values to remove from index */ | |||
| 23811 | int bSaveRow /* If true, set pSavedRow for deleted row */ | |||
| 23812 | ){ | |||
| 23813 | Fts5Config *pConfig = p->pConfig; | |||
| 23814 | int rc; | |||
| 23815 | sqlite3_stmt *pDel = 0; | |||
| 23816 | ||||
| 23817 | assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 )((void) (0)); | |||
| 23818 | rc = fts5StorageLoadTotals(p, 1); | |||
| 23819 | ||||
| 23820 | /* Delete the index records */ | |||
| 23821 | if( rc
| |||
| 23822 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); | |||
| 23823 | } | |||
| 23824 | ||||
| 23825 | if( rc==SQLITE_OK0 ){ | |||
| 23826 | if( p->pConfig->bContentlessDelete ){ | |||
| 23827 | rc = fts5StorageContentlessDelete(p, iDel); | |||
| 23828 | if( rc==SQLITE_OK0 | |||
| 23829 | && bSaveRow | |||
| 23830 | && p->pConfig->eContent==FTS5_CONTENT_UNINDEXED3 | |||
| 23831 | ){ | |||
| 23832 | rc = sqlite3Fts5StorageFindDeleteRow(p, iDel); | |||
| 23833 | } | |||
| 23834 | }else{ | |||
| 23835 | rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); | |||
| 23836 | } | |||
| 23837 | } | |||
| 23838 | ||||
| 23839 | /* Delete the %_docsize record */ | |||
| 23840 | if( rc==SQLITE_OK0 && pConfig->bColumnsize ){ | |||
| 23841 | rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE8, &pDel, 0); | |||
| 23842 | if( rc==SQLITE_OK0 ){ | |||
| 23843 | sqlite3_bind_int64sqlite3_api->bind_int64(pDel, 1, iDel); | |||
| 23844 | sqlite3_stepsqlite3_api->step(pDel); | |||
| 23845 | rc = sqlite3_resetsqlite3_api->reset(pDel); | |||
| 23846 | } | |||
| 23847 | } | |||
| 23848 | ||||
| 23849 | /* Delete the %_content record */ | |||
| 23850 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 | |||
| 23851 | || pConfig->eContent==FTS5_CONTENT_UNINDEXED3 | |||
| 23852 | ){ | |||
| 23853 | if( rc==SQLITE_OK0 ){ | |||
| 23854 | rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT6, &pDel, 0); | |||
| 23855 | } | |||
| 23856 | if( rc==SQLITE_OK0 ){ | |||
| 23857 | sqlite3_bind_int64sqlite3_api->bind_int64(pDel, 1, iDel); | |||
| 23858 | sqlite3_stepsqlite3_api->step(pDel); | |||
| 23859 | rc = sqlite3_resetsqlite3_api->reset(pDel); | |||
| 23860 | } | |||
| 23861 | } | |||
| 23862 | ||||
| 23863 | return rc; | |||
| 23864 | } | |||
| 23865 | ||||
| 23866 | /* | |||
| 23867 | ** Delete all entries in the FTS5 index. | |||
| 23868 | */ | |||
| 23869 | static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ | |||
| 23870 | Fts5Config *pConfig = p->pConfig; | |||
| 23871 | int rc; | |||
| 23872 | ||||
| 23873 | p->bTotalsValid = 0; | |||
| 23874 | ||||
| 23875 | /* Delete the contents of the %_data and %_docsize tables. */ | |||
| 23876 | rc = fts5ExecPrintf(pConfig->db, 0, | |||
| 23877 | "DELETE FROM %Q.'%q_data';" | |||
| 23878 | "DELETE FROM %Q.'%q_idx';", | |||
| 23879 | pConfig->zDb, pConfig->zName, | |||
| 23880 | pConfig->zDb, pConfig->zName | |||
| 23881 | ); | |||
| 23882 | if( rc==SQLITE_OK0 && pConfig->bColumnsize ){ | |||
| 23883 | rc = fts5ExecPrintf(pConfig->db, 0, | |||
| 23884 | "DELETE FROM %Q.'%q_docsize';", pConfig->zDb, pConfig->zName | |||
| 23885 | ); | |||
| 23886 | } | |||
| 23887 | ||||
| 23888 | if( rc==SQLITE_OK0 && pConfig->eContent==FTS5_CONTENT_UNINDEXED3 ){ | |||
| 23889 | rc = fts5ExecPrintf(pConfig->db, 0, | |||
| 23890 | "DELETE FROM %Q.'%q_content';", pConfig->zDb, pConfig->zName | |||
| 23891 | ); | |||
| 23892 | } | |||
| 23893 | ||||
| 23894 | /* Reinitialize the %_data table. This call creates the initial structure | |||
| 23895 | ** and averages records. */ | |||
| 23896 | if( rc==SQLITE_OK0 ){ | |||
| 23897 | rc = sqlite3Fts5IndexReinit(p->pIndex); | |||
| 23898 | } | |||
| 23899 | if( rc==SQLITE_OK0 ){ | |||
| 23900 | rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION4); | |||
| 23901 | } | |||
| 23902 | return rc; | |||
| 23903 | } | |||
| 23904 | ||||
| 23905 | static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ | |||
| 23906 | Fts5Buffer buf = {0,0,0}; | |||
| 23907 | Fts5Config *pConfig = p->pConfig; | |||
| 23908 | sqlite3_stmt *pScan = 0; | |||
| 23909 | Fts5InsertCtx ctx; | |||
| 23910 | int rc, rc2; | |||
| 23911 | ||||
| 23912 | memset(&ctx, 0, sizeof(Fts5InsertCtx)); | |||
| 23913 | ctx.pStorage = p; | |||
| 23914 | rc = sqlite3Fts5StorageDeleteAll(p); | |||
| 23915 | if( rc==SQLITE_OK0 ){ | |||
| 23916 | rc = fts5StorageLoadTotals(p, 1); | |||
| 23917 | } | |||
| 23918 | ||||
| 23919 | if( rc==SQLITE_OK0 ){ | |||
| 23920 | rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN11, &pScan, pConfig->pzErrmsg); | |||
| 23921 | } | |||
| 23922 | ||||
| 23923 | while( rc==SQLITE_OK0 && SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pScan) ){ | |||
| 23924 | i64 iRowid = sqlite3_column_int64sqlite3_api->column_int64(pScan, 0); | |||
| 23925 | ||||
| 23926 | sqlite3Fts5BufferZero(&buf); | |||
| 23927 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); | |||
| 23928 | for(ctx.iCol=0; rc==SQLITE_OK0 && ctx.iCol<pConfig->nCol; ctx.iCol++){ | |||
| 23929 | ctx.szCol = 0; | |||
| 23930 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ | |||
| 23931 | int nText = 0; /* Size of pText in bytes */ | |||
| 23932 | const char *pText = 0; /* Pointer to buffer containing text value */ | |||
| 23933 | int nLoc = 0; /* Size of pLoc in bytes */ | |||
| 23934 | const char *pLoc = 0; /* Pointer to buffer containing text value */ | |||
| 23935 | ||||
| 23936 | sqlite3_value *pVal = sqlite3_column_valuesqlite3_api->column_value(pScan, ctx.iCol+1); | |||
| 23937 | if( pConfig->eContent==FTS5_CONTENT_EXTERNAL2 | |||
| 23938 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |||
| 23939 | ){ | |||
| 23940 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |||
| 23941 | }else{ | |||
| 23942 | pText = (const char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 23943 | nText = sqlite3_value_bytessqlite3_api->value_bytes(pVal); | |||
| 23944 | if( pConfig->bLocale ){ | |||
| 23945 | int iCol = ctx.iCol + 1 + pConfig->nCol; | |||
| 23946 | pLoc = (const char*)sqlite3_column_textsqlite3_api->column_text(pScan, iCol); | |||
| 23947 | nLoc = sqlite3_column_bytessqlite3_api->column_bytes(pScan, iCol); | |||
| 23948 | } | |||
| 23949 | } | |||
| 23950 | ||||
| 23951 | if( rc==SQLITE_OK0 ){ | |||
| 23952 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |||
| 23953 | rc = sqlite3Fts5Tokenize(pConfig, | |||
| 23954 | FTS5_TOKENIZE_DOCUMENT0x0004, | |||
| 23955 | pText, nText, | |||
| 23956 | (void*)&ctx, | |||
| 23957 | fts5StorageInsertCallback | |||
| 23958 | ); | |||
| 23959 | sqlite3Fts5ClearLocale(pConfig); | |||
| 23960 | } | |||
| 23961 | } | |||
| 23962 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); | |||
| 23963 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; | |||
| 23964 | } | |||
| 23965 | p->nTotalRow++; | |||
| 23966 | ||||
| 23967 | if( rc==SQLITE_OK0 ){ | |||
| 23968 | rc = fts5StorageInsertDocsize(p, iRowid, &buf); | |||
| 23969 | } | |||
| 23970 | } | |||
| 23971 | sqlite3_freesqlite3_api->free(buf.p); | |||
| 23972 | rc2 = sqlite3_resetsqlite3_api->reset(pScan); | |||
| 23973 | if( rc==SQLITE_OK0 ) rc = rc2; | |||
| 23974 | ||||
| 23975 | /* Write the averages record */ | |||
| 23976 | if( rc==SQLITE_OK0 ){ | |||
| 23977 | rc = fts5StorageSaveTotals(p); | |||
| 23978 | } | |||
| 23979 | return rc; | |||
| 23980 | } | |||
| 23981 | ||||
| 23982 | static int sqlite3Fts5StorageOptimize(Fts5Storage *p){ | |||
| 23983 | return sqlite3Fts5IndexOptimize(p->pIndex); | |||
| 23984 | } | |||
| 23985 | ||||
| 23986 | static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){ | |||
| 23987 | return sqlite3Fts5IndexMerge(p->pIndex, nMerge); | |||
| 23988 | } | |||
| 23989 | ||||
| 23990 | static int sqlite3Fts5StorageReset(Fts5Storage *p){ | |||
| 23991 | return sqlite3Fts5IndexReset(p->pIndex); | |||
| 23992 | } | |||
| 23993 | ||||
| 23994 | /* | |||
| 23995 | ** Allocate a new rowid. This is used for "external content" tables when | |||
| 23996 | ** a NULL value is inserted into the rowid column. The new rowid is allocated | |||
| 23997 | ** by inserting a dummy row into the %_docsize table. The dummy will be | |||
| 23998 | ** overwritten later. | |||
| 23999 | ** | |||
| 24000 | ** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In | |||
| 24001 | ** this case the user is required to provide a rowid explicitly. | |||
| 24002 | */ | |||
| 24003 | static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ | |||
| 24004 | int rc = SQLITE_MISMATCH20; | |||
| 24005 | if( p->pConfig->bColumnsize ){ | |||
| 24006 | sqlite3_stmt *pReplace = 0; | |||
| 24007 | rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE7, &pReplace, 0); | |||
| 24008 | if( rc==SQLITE_OK0 ){ | |||
| 24009 | sqlite3_bind_nullsqlite3_api->bind_null(pReplace, 1); | |||
| 24010 | sqlite3_bind_nullsqlite3_api->bind_null(pReplace, 2); | |||
| 24011 | sqlite3_stepsqlite3_api->step(pReplace); | |||
| 24012 | rc = sqlite3_resetsqlite3_api->reset(pReplace); | |||
| 24013 | } | |||
| 24014 | if( rc==SQLITE_OK0 ){ | |||
| 24015 | *piRowid = sqlite3_last_insert_rowidsqlite3_api->last_insert_rowid(p->pConfig->db); | |||
| 24016 | } | |||
| 24017 | } | |||
| 24018 | return rc; | |||
| 24019 | } | |||
| 24020 | ||||
| 24021 | /* | |||
| 24022 | ** Insert a new row into the FTS content table. | |||
| 24023 | */ | |||
| 24024 | static int sqlite3Fts5StorageContentInsert( | |||
| 24025 | Fts5Storage *p, | |||
| 24026 | int bReplace, /* True to use REPLACE instead of INSERT */ | |||
| 24027 | sqlite3_value **apVal, | |||
| 24028 | i64 *piRowid | |||
| 24029 | ){ | |||
| 24030 | Fts5Config *pConfig = p->pConfig; | |||
| 24031 | int rc = SQLITE_OK0; | |||
| 24032 | ||||
| 24033 | /* Insert the new row into the %_content table. */ | |||
| 24034 | if( pConfig->eContent!=FTS5_CONTENT_NORMAL0 | |||
| 24035 | && pConfig->eContent!=FTS5_CONTENT_UNINDEXED3 | |||
| 24036 | ){ | |||
| 24037 | if( sqlite3_value_typesqlite3_api->value_type(apVal[1])==SQLITE_INTEGER1 ){ | |||
| 24038 | *piRowid = sqlite3_value_int64sqlite3_api->value_int64(apVal[1]); | |||
| 24039 | }else{ | |||
| 24040 | rc = fts5StorageNewRowid(p, piRowid); | |||
| 24041 | } | |||
| 24042 | }else{ | |||
| 24043 | sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ | |||
| 24044 | int i; /* Counter variable */ | |||
| 24045 | ||||
| 24046 | assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT )((void) (0)); | |||
| 24047 | assert( bReplace==0 || bReplace==1 )((void) (0)); | |||
| 24048 | rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT4+bReplace, &pInsert, 0); | |||
| 24049 | if( pInsert ) sqlite3_clear_bindingssqlite3_api->clear_bindings(pInsert); | |||
| 24050 | ||||
| 24051 | /* Bind the rowid value */ | |||
| 24052 | sqlite3_bind_valuesqlite3_api->bind_value(pInsert, 1, apVal[1]); | |||
| 24053 | ||||
| 24054 | /* Loop through values for user-defined columns. i=2 is the leftmost | |||
| 24055 | ** user-defined column. As is column 1 of pSavedRow. */ | |||
| 24056 | for(i=2; rc==SQLITE_OK0 && i<=pConfig->nCol+1; i++){ | |||
| 24057 | int bUnindexed = pConfig->abUnindexed[i-2]; | |||
| 24058 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 || bUnindexed ){ | |||
| 24059 | sqlite3_value *pVal = apVal[i]; | |||
| 24060 | ||||
| 24061 | if( sqlite3_value_nochangesqlite3_api->value_nochange(pVal) && p->pSavedRow ){ | |||
| 24062 | /* This is an UPDATE statement, and user-defined column (i-2) was not | |||
| 24063 | ** modified. Retrieve the value from Fts5Storage.pSavedRow. */ | |||
| 24064 | pVal = sqlite3_column_valuesqlite3_api->column_value(p->pSavedRow, i-1); | |||
| 24065 | if( pConfig->bLocale && bUnindexed==0 ){ | |||
| 24066 | sqlite3_bind_valuesqlite3_api->bind_value(pInsert, pConfig->nCol + i, | |||
| 24067 | sqlite3_column_valuesqlite3_api->column_value(p->pSavedRow, pConfig->nCol + i - 1) | |||
| 24068 | ); | |||
| 24069 | } | |||
| 24070 | }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |||
| 24071 | const char *pText = 0; | |||
| 24072 | const char *pLoc = 0; | |||
| 24073 | int nText = 0; | |||
| 24074 | int nLoc = 0; | |||
| 24075 | assert( pConfig->bLocale )((void) (0)); | |||
| 24076 | ||||
| 24077 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |||
| 24078 | if( rc==SQLITE_OK0 ){ | |||
| 24079 | sqlite3_bind_textsqlite3_api->bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 24080 | if( bUnindexed==0 ){ | |||
| 24081 | int iLoc = pConfig->nCol + i; | |||
| 24082 | sqlite3_bind_textsqlite3_api->bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT((sqlite3_destructor_type)-1)); | |||
| 24083 | } | |||
| 24084 | } | |||
| 24085 | ||||
| 24086 | continue; | |||
| 24087 | } | |||
| 24088 | ||||
| 24089 | rc = sqlite3_bind_valuesqlite3_api->bind_value(pInsert, i, pVal); | |||
| 24090 | } | |||
| 24091 | } | |||
| 24092 | if( rc==SQLITE_OK0 ){ | |||
| 24093 | sqlite3_stepsqlite3_api->step(pInsert); | |||
| 24094 | rc = sqlite3_resetsqlite3_api->reset(pInsert); | |||
| 24095 | } | |||
| 24096 | *piRowid = sqlite3_last_insert_rowidsqlite3_api->last_insert_rowid(pConfig->db); | |||
| 24097 | } | |||
| 24098 | ||||
| 24099 | return rc; | |||
| 24100 | } | |||
| 24101 | ||||
| 24102 | /* | |||
| 24103 | ** Insert new entries into the FTS index and %_docsize table. | |||
| 24104 | */ | |||
| 24105 | static int sqlite3Fts5StorageIndexInsert( | |||
| 24106 | Fts5Storage *p, | |||
| 24107 | sqlite3_value **apVal, | |||
| 24108 | i64 iRowid | |||
| 24109 | ){ | |||
| 24110 | Fts5Config *pConfig = p->pConfig; | |||
| 24111 | int rc = SQLITE_OK0; /* Return code */ | |||
| 24112 | Fts5InsertCtx ctx; /* Tokenization callback context object */ | |||
| 24113 | Fts5Buffer buf; /* Buffer used to build up %_docsize blob */ | |||
| 24114 | ||||
| 24115 | memset(&buf, 0, sizeof(Fts5Buffer)); | |||
| 24116 | ctx.pStorage = p; | |||
| 24117 | rc = fts5StorageLoadTotals(p, 1); | |||
| 24118 | ||||
| 24119 | if( rc==SQLITE_OK0 ){ | |||
| 24120 | rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); | |||
| 24121 | } | |||
| 24122 | for(ctx.iCol=0; rc==SQLITE_OK0 && ctx.iCol<pConfig->nCol; ctx.iCol++){ | |||
| 24123 | ctx.szCol = 0; | |||
| 24124 | if( pConfig->abUnindexed[ctx.iCol]==0 ){ | |||
| 24125 | int nText = 0; /* Size of pText in bytes */ | |||
| 24126 | const char *pText = 0; /* Pointer to buffer containing text value */ | |||
| 24127 | int nLoc = 0; /* Size of pText in bytes */ | |||
| 24128 | const char *pLoc = 0; /* Pointer to buffer containing text value */ | |||
| 24129 | ||||
| 24130 | sqlite3_value *pVal = apVal[ctx.iCol+2]; | |||
| 24131 | if( p->pSavedRow && sqlite3_value_nochangesqlite3_api->value_nochange(pVal) ){ | |||
| 24132 | pVal = sqlite3_column_valuesqlite3_api->column_value(p->pSavedRow, ctx.iCol+1); | |||
| 24133 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 && pConfig->bLocale ){ | |||
| 24134 | int iCol = ctx.iCol + 1 + pConfig->nCol; | |||
| 24135 | pLoc = (const char*)sqlite3_column_textsqlite3_api->column_text(p->pSavedRow, iCol); | |||
| 24136 | nLoc = sqlite3_column_bytessqlite3_api->column_bytes(p->pSavedRow, iCol); | |||
| 24137 | } | |||
| 24138 | }else{ | |||
| 24139 | pVal = apVal[ctx.iCol+2]; | |||
| 24140 | } | |||
| 24141 | ||||
| 24142 | if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ | |||
| 24143 | rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); | |||
| 24144 | }else{ | |||
| 24145 | pText = (const char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 24146 | nText = sqlite3_value_bytessqlite3_api->value_bytes(pVal); | |||
| 24147 | } | |||
| 24148 | ||||
| 24149 | if( rc==SQLITE_OK0 ){ | |||
| 24150 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |||
| 24151 | rc = sqlite3Fts5Tokenize(pConfig, | |||
| 24152 | FTS5_TOKENIZE_DOCUMENT0x0004, pText, nText, (void*)&ctx, | |||
| 24153 | fts5StorageInsertCallback | |||
| 24154 | ); | |||
| 24155 | sqlite3Fts5ClearLocale(pConfig); | |||
| 24156 | } | |||
| 24157 | } | |||
| 24158 | sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); | |||
| 24159 | p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; | |||
| 24160 | } | |||
| 24161 | p->nTotalRow++; | |||
| 24162 | ||||
| 24163 | /* Write the %_docsize record */ | |||
| 24164 | if( rc==SQLITE_OK0 ){ | |||
| 24165 | rc = fts5StorageInsertDocsize(p, iRowid, &buf); | |||
| 24166 | } | |||
| 24167 | sqlite3_freesqlite3_api->free(buf.p); | |||
| 24168 | ||||
| 24169 | return rc; | |||
| 24170 | } | |||
| 24171 | ||||
| 24172 | static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){ | |||
| 24173 | Fts5Config *pConfig = p->pConfig; | |||
| 24174 | char *zSql; | |||
| 24175 | int rc; | |||
| 24176 | ||||
| 24177 | zSql = sqlite3_mprintfsqlite3_api->mprintf("SELECT count(*) FROM %Q.'%q_%s'", | |||
| 24178 | pConfig->zDb, pConfig->zName, zSuffix | |||
| 24179 | ); | |||
| 24180 | if( zSql==0 ){ | |||
| 24181 | rc = SQLITE_NOMEM7; | |||
| 24182 | }else{ | |||
| 24183 | sqlite3_stmt *pCnt = 0; | |||
| 24184 | rc = sqlite3_prepare_v2sqlite3_api->prepare_v2(pConfig->db, zSql, -1, &pCnt, 0); | |||
| 24185 | if( rc==SQLITE_OK0 ){ | |||
| 24186 | if( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pCnt) ){ | |||
| 24187 | *pnRow = sqlite3_column_int64sqlite3_api->column_int64(pCnt, 0); | |||
| 24188 | } | |||
| 24189 | rc = sqlite3_finalizesqlite3_api->finalize(pCnt); | |||
| 24190 | } | |||
| 24191 | } | |||
| 24192 | ||||
| 24193 | sqlite3_freesqlite3_api->free(zSql); | |||
| 24194 | return rc; | |||
| 24195 | } | |||
| 24196 | ||||
| 24197 | /* | |||
| 24198 | ** Context object used by sqlite3Fts5StorageIntegrity(). | |||
| 24199 | */ | |||
| 24200 | typedef struct Fts5IntegrityCtx Fts5IntegrityCtx; | |||
| 24201 | struct Fts5IntegrityCtx { | |||
| 24202 | i64 iRowid; | |||
| 24203 | int iCol; | |||
| 24204 | int szCol; | |||
| 24205 | u64 cksum; | |||
| 24206 | Fts5Termset *pTermset; | |||
| 24207 | Fts5Config *pConfig; | |||
| 24208 | }; | |||
| 24209 | ||||
| 24210 | ||||
| 24211 | /* | |||
| 24212 | ** Tokenization callback used by integrity check. | |||
| 24213 | */ | |||
| 24214 | static int fts5StorageIntegrityCallback( | |||
| 24215 | void *pContext, /* Pointer to Fts5IntegrityCtx object */ | |||
| 24216 | int tflags, | |||
| 24217 | const char *pToken, /* Buffer containing token */ | |||
| 24218 | int nToken, /* Size of token in bytes */ | |||
| 24219 | int iUnused1, /* Start offset of token */ | |||
| 24220 | int iUnused2 /* End offset of token */ | |||
| 24221 | ){ | |||
| 24222 | Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext; | |||
| 24223 | Fts5Termset *pTermset = pCtx->pTermset; | |||
| 24224 | int bPresent; | |||
| 24225 | int ii; | |||
| 24226 | int rc = SQLITE_OK0; | |||
| 24227 | int iPos; | |||
| 24228 | int iCol; | |||
| 24229 | ||||
| 24230 | UNUSED_PARAM2(iUnused1, iUnused2)(void)(iUnused1), (void)(iUnused2); | |||
| 24231 | if( nToken>FTS5_MAX_TOKEN_SIZE32768 ) nToken = FTS5_MAX_TOKEN_SIZE32768; | |||
| 24232 | ||||
| 24233 | if( (tflags & FTS5_TOKEN_COLOCATED0x0001)==0 || pCtx->szCol==0 ){ | |||
| 24234 | pCtx->szCol++; | |||
| 24235 | } | |||
| 24236 | ||||
| 24237 | switch( pCtx->pConfig->eDetail ){ | |||
| 24238 | case FTS5_DETAIL_FULL0: | |||
| 24239 | iPos = pCtx->szCol-1; | |||
| 24240 | iCol = pCtx->iCol; | |||
| 24241 | break; | |||
| 24242 | ||||
| 24243 | case FTS5_DETAIL_COLUMNS2: | |||
| 24244 | iPos = pCtx->iCol; | |||
| 24245 | iCol = 0; | |||
| 24246 | break; | |||
| 24247 | ||||
| 24248 | default: | |||
| 24249 | assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE )((void) (0)); | |||
| 24250 | iPos = 0; | |||
| 24251 | iCol = 0; | |||
| 24252 | break; | |||
| 24253 | } | |||
| 24254 | ||||
| 24255 | rc = sqlite3Fts5TermsetAdd(pTermset, 0, pToken, nToken, &bPresent); | |||
| 24256 | if( rc==SQLITE_OK0 && bPresent==0 ){ | |||
| 24257 | pCtx->cksum ^= sqlite3Fts5IndexEntryCksum( | |||
| 24258 | pCtx->iRowid, iCol, iPos, 0, pToken, nToken | |||
| 24259 | ); | |||
| 24260 | } | |||
| 24261 | ||||
| 24262 | for(ii=0; rc==SQLITE_OK0 && ii<pCtx->pConfig->nPrefix; ii++){ | |||
| 24263 | const int nChar = pCtx->pConfig->aPrefix[ii]; | |||
| 24264 | int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar); | |||
| 24265 | if( nByte ){ | |||
| 24266 | rc = sqlite3Fts5TermsetAdd(pTermset, ii+1, pToken, nByte, &bPresent); | |||
| 24267 | if( bPresent==0 ){ | |||
| 24268 | pCtx->cksum ^= sqlite3Fts5IndexEntryCksum( | |||
| 24269 | pCtx->iRowid, iCol, iPos, ii+1, pToken, nByte | |||
| 24270 | ); | |||
| 24271 | } | |||
| 24272 | } | |||
| 24273 | } | |||
| 24274 | ||||
| 24275 | return rc; | |||
| 24276 | } | |||
| 24277 | ||||
| 24278 | /* | |||
| 24279 | ** Check that the contents of the FTS index match that of the %_content | |||
| 24280 | ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return | |||
| 24281 | ** some other SQLite error code if an error occurs while attempting to | |||
| 24282 | ** determine this. | |||
| 24283 | */ | |||
| 24284 | static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){ | |||
| 24285 | Fts5Config *pConfig = p->pConfig; | |||
| 24286 | int rc = SQLITE_OK0; /* Return code */ | |||
| 24287 | int *aColSize; /* Array of size pConfig->nCol */ | |||
| 24288 | i64 *aTotalSize; /* Array of size pConfig->nCol */ | |||
| 24289 | Fts5IntegrityCtx ctx; | |||
| 24290 | sqlite3_stmt *pScan; | |||
| 24291 | int bUseCksum; | |||
| 24292 | ||||
| 24293 | memset(&ctx, 0, sizeof(Fts5IntegrityCtx)); | |||
| 24294 | ctx.pConfig = p->pConfig; | |||
| 24295 | aTotalSize = (i64*)sqlite3_malloc64sqlite3_api->malloc64(pConfig->nCol*(sizeof(int)+sizeof(i64))); | |||
| 24296 | if( !aTotalSize ) return SQLITE_NOMEM7; | |||
| 24297 | aColSize = (int*)&aTotalSize[pConfig->nCol]; | |||
| 24298 | memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol); | |||
| 24299 | ||||
| 24300 | bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL0 | |||
| 24301 | || (pConfig->eContent==FTS5_CONTENT_EXTERNAL2 && iArg) | |||
| 24302 | ); | |||
| 24303 | if( bUseCksum ){ | |||
| 24304 | /* Generate the expected index checksum based on the contents of the | |||
| 24305 | ** %_content table. This block stores the checksum in ctx.cksum. */ | |||
| 24306 | rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN11, &pScan, 0); | |||
| 24307 | if( rc==SQLITE_OK0 ){ | |||
| 24308 | int rc2; | |||
| 24309 | while( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pScan) ){ | |||
| 24310 | int i; | |||
| 24311 | ctx.iRowid = sqlite3_column_int64sqlite3_api->column_int64(pScan, 0); | |||
| 24312 | ctx.szCol = 0; | |||
| 24313 | if( pConfig->bColumnsize ){ | |||
| 24314 | rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); | |||
| 24315 | } | |||
| 24316 | if( rc==SQLITE_OK0 && pConfig->eDetail==FTS5_DETAIL_NONE1 ){ | |||
| 24317 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); | |||
| 24318 | } | |||
| 24319 | for(i=0; rc==SQLITE_OK0 && i<pConfig->nCol; i++){ | |||
| 24320 | if( pConfig->abUnindexed[i]==0 ){ | |||
| 24321 | const char *pText = 0; | |||
| 24322 | int nText = 0; | |||
| 24323 | const char *pLoc = 0; | |||
| 24324 | int nLoc = 0; | |||
| 24325 | sqlite3_value *pVal = sqlite3_column_valuesqlite3_api->column_value(pScan, i+1); | |||
| 24326 | ||||
| 24327 | if( pConfig->eContent==FTS5_CONTENT_EXTERNAL2 | |||
| 24328 | && sqlite3Fts5IsLocaleValue(pConfig, pVal) | |||
| 24329 | ){ | |||
| 24330 | rc = sqlite3Fts5DecodeLocaleValue( | |||
| 24331 | pVal, &pText, &nText, &pLoc, &nLoc | |||
| 24332 | ); | |||
| 24333 | }else{ | |||
| 24334 | if( pConfig->eContent==FTS5_CONTENT_NORMAL0 && pConfig->bLocale ){ | |||
| 24335 | int iCol = i + 1 + pConfig->nCol; | |||
| 24336 | pLoc = (const char*)sqlite3_column_textsqlite3_api->column_text(pScan, iCol); | |||
| 24337 | nLoc = sqlite3_column_bytessqlite3_api->column_bytes(pScan, iCol); | |||
| 24338 | } | |||
| 24339 | pText = (const char*)sqlite3_value_textsqlite3_api->value_text(pVal); | |||
| 24340 | nText = sqlite3_value_bytessqlite3_api->value_bytes(pVal); | |||
| 24341 | } | |||
| 24342 | ||||
| 24343 | ctx.iCol = i; | |||
| 24344 | ctx.szCol = 0; | |||
| 24345 | ||||
| 24346 | if( rc==SQLITE_OK0 && pConfig->eDetail==FTS5_DETAIL_COLUMNS2 ){ | |||
| 24347 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); | |||
| 24348 | } | |||
| 24349 | ||||
| 24350 | if( rc==SQLITE_OK0 ){ | |||
| 24351 | sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); | |||
| 24352 | rc = sqlite3Fts5Tokenize(pConfig, | |||
| 24353 | FTS5_TOKENIZE_DOCUMENT0x0004, | |||
| 24354 | pText, nText, | |||
| 24355 | (void*)&ctx, | |||
| 24356 | fts5StorageIntegrityCallback | |||
| 24357 | ); | |||
| 24358 | sqlite3Fts5ClearLocale(pConfig); | |||
| 24359 | } | |||
| 24360 | ||||
| 24361 | /* If this is not a columnsize=0 database, check that the number | |||
| 24362 | ** of tokens in the value matches the aColSize[] value read from | |||
| 24363 | ** the %_docsize table. */ | |||
| 24364 | if( rc==SQLITE_OK0 | |||
| 24365 | && pConfig->bColumnsize | |||
| 24366 | && ctx.szCol!=aColSize[i] | |||
| 24367 | ){ | |||
| 24368 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 24369 | } | |||
| 24370 | aTotalSize[i] += ctx.szCol; | |||
| 24371 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS2 ){ | |||
| 24372 | sqlite3Fts5TermsetFree(ctx.pTermset); | |||
| 24373 | ctx.pTermset = 0; | |||
| 24374 | } | |||
| 24375 | } | |||
| 24376 | } | |||
| 24377 | sqlite3Fts5TermsetFree(ctx.pTermset); | |||
| 24378 | ctx.pTermset = 0; | |||
| 24379 | ||||
| 24380 | if( rc!=SQLITE_OK0 ) break; | |||
| 24381 | } | |||
| 24382 | rc2 = sqlite3_resetsqlite3_api->reset(pScan); | |||
| 24383 | if( rc==SQLITE_OK0 ) rc = rc2; | |||
| 24384 | } | |||
| 24385 | ||||
| 24386 | /* Test that the "totals" (sometimes called "averages") record looks Ok */ | |||
| 24387 | if( rc==SQLITE_OK0 ){ | |||
| 24388 | int i; | |||
| 24389 | rc = fts5StorageLoadTotals(p, 0); | |||
| 24390 | for(i=0; rc==SQLITE_OK0 && i<pConfig->nCol; i++){ | |||
| 24391 | if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 24392 | } | |||
| 24393 | } | |||
| 24394 | ||||
| 24395 | /* Check that the %_docsize and %_content tables contain the expected | |||
| 24396 | ** number of rows. */ | |||
| 24397 | if( rc==SQLITE_OK0 && pConfig->eContent==FTS5_CONTENT_NORMAL0 ){ | |||
| 24398 | i64 nRow = 0; | |||
| 24399 | rc = fts5StorageCount(p, "content", &nRow); | |||
| 24400 | if( rc==SQLITE_OK0 && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 24401 | } | |||
| 24402 | if( rc==SQLITE_OK0 && pConfig->bColumnsize ){ | |||
| 24403 | i64 nRow = 0; | |||
| 24404 | rc = fts5StorageCount(p, "docsize", &nRow); | |||
| 24405 | if( rc==SQLITE_OK0 && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 24406 | } | |||
| 24407 | } | |||
| 24408 | ||||
| 24409 | /* Pass the expected checksum down to the FTS index module. It will | |||
| 24410 | ** verify, amongst other things, that it matches the checksum generated by | |||
| 24411 | ** inspecting the index itself. */ | |||
| 24412 | if( rc==SQLITE_OK0 ){ | |||
| 24413 | rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum, bUseCksum); | |||
| 24414 | } | |||
| 24415 | ||||
| 24416 | sqlite3_freesqlite3_api->free(aTotalSize); | |||
| 24417 | return rc; | |||
| 24418 | } | |||
| 24419 | ||||
| 24420 | /* | |||
| 24421 | ** Obtain an SQLite statement handle that may be used to read data from the | |||
| 24422 | ** %_content table. | |||
| 24423 | */ | |||
| 24424 | static int sqlite3Fts5StorageStmt( | |||
| 24425 | Fts5Storage *p, | |||
| 24426 | int eStmt, | |||
| 24427 | sqlite3_stmt **pp, | |||
| 24428 | char **pzErrMsg | |||
| 24429 | ){ | |||
| 24430 | int rc; | |||
| 24431 | assert( eStmt==FTS5_STMT_SCAN_ASC((void) (0)) | |||
| 24432 | || eStmt==FTS5_STMT_SCAN_DESC((void) (0)) | |||
| 24433 | || eStmt==FTS5_STMT_LOOKUP((void) (0)) | |||
| 24434 | )((void) (0)); | |||
| 24435 | rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg); | |||
| 24436 | if( rc==SQLITE_OK0 ){ | |||
| 24437 | assert( p->aStmt[eStmt]==*pp )((void) (0)); | |||
| 24438 | p->aStmt[eStmt] = 0; | |||
| 24439 | } | |||
| 24440 | return rc; | |||
| 24441 | } | |||
| 24442 | ||||
| 24443 | /* | |||
| 24444 | ** Release an SQLite statement handle obtained via an earlier call to | |||
| 24445 | ** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function | |||
| 24446 | ** must match that passed to the sqlite3Fts5StorageStmt() call. | |||
| 24447 | */ | |||
| 24448 | static void sqlite3Fts5StorageStmtRelease( | |||
| 24449 | Fts5Storage *p, | |||
| 24450 | int eStmt, | |||
| 24451 | sqlite3_stmt *pStmt | |||
| 24452 | ){ | |||
| 24453 | assert( eStmt==FTS5_STMT_SCAN_ASC((void) (0)) | |||
| 24454 | || eStmt==FTS5_STMT_SCAN_DESC((void) (0)) | |||
| 24455 | || eStmt==FTS5_STMT_LOOKUP((void) (0)) | |||
| 24456 | )((void) (0)); | |||
| 24457 | if( p->aStmt[eStmt]==0 ){ | |||
| 24458 | sqlite3_resetsqlite3_api->reset(pStmt); | |||
| 24459 | p->aStmt[eStmt] = pStmt; | |||
| 24460 | }else{ | |||
| 24461 | sqlite3_finalizesqlite3_api->finalize(pStmt); | |||
| 24462 | } | |||
| 24463 | } | |||
| 24464 | ||||
| 24465 | static int fts5StorageDecodeSizeArray( | |||
| 24466 | int *aCol, int nCol, /* Array to populate */ | |||
| 24467 | const u8 *aBlob, int nBlob /* Record to read varints from */ | |||
| 24468 | ){ | |||
| 24469 | int i; | |||
| 24470 | int iOff = 0; | |||
| 24471 | for(i=0; i<nCol; i++){ | |||
| 24472 | if( iOff>=nBlob ) return 1; | |||
| 24473 | iOff += fts5GetVarint32(&aBlob[iOff], aCol[i])sqlite3Fts5GetVarint32(&aBlob[iOff],(u32*)&(aCol[i])); | |||
| 24474 | } | |||
| 24475 | return (iOff!=nBlob); | |||
| 24476 | } | |||
| 24477 | ||||
| 24478 | /* | |||
| 24479 | ** Argument aCol points to an array of integers containing one entry for | |||
| 24480 | ** each table column. This function reads the %_docsize record for the | |||
| 24481 | ** specified rowid and populates aCol[] with the results. | |||
| 24482 | ** | |||
| 24483 | ** An SQLite error code is returned if an error occurs, or SQLITE_OK | |||
| 24484 | ** otherwise. | |||
| 24485 | */ | |||
| 24486 | static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ | |||
| 24487 | int nCol = p->pConfig->nCol; /* Number of user columns in table */ | |||
| 24488 | sqlite3_stmt *pLookup = 0; /* Statement to query %_docsize */ | |||
| 24489 | int rc; /* Return Code */ | |||
| 24490 | ||||
| 24491 | assert( p->pConfig->bColumnsize )((void) (0)); | |||
| 24492 | rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE9, &pLookup, 0); | |||
| 24493 | if( pLookup ){ | |||
| 24494 | int bCorrupt = 1; | |||
| 24495 | assert( rc==SQLITE_OK )((void) (0)); | |||
| 24496 | sqlite3_bind_int64sqlite3_api->bind_int64(pLookup, 1, iRowid); | |||
| 24497 | if( SQLITE_ROW100==sqlite3_stepsqlite3_api->step(pLookup) ){ | |||
| 24498 | const u8 *aBlob = sqlite3_column_blobsqlite3_api->column_blob(pLookup, 0); | |||
| 24499 | int nBlob = sqlite3_column_bytessqlite3_api->column_bytes(pLookup, 0); | |||
| 24500 | if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){ | |||
| 24501 | bCorrupt = 0; | |||
| 24502 | } | |||
| 24503 | } | |||
| 24504 | rc = sqlite3_resetsqlite3_api->reset(pLookup); | |||
| 24505 | if( bCorrupt && rc==SQLITE_OK0 ){ | |||
| 24506 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 24507 | } | |||
| 24508 | }else{ | |||
| 24509 | assert( rc!=SQLITE_OK )((void) (0)); | |||
| 24510 | } | |||
| 24511 | ||||
| 24512 | return rc; | |||
| 24513 | } | |||
| 24514 | ||||
| 24515 | static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){ | |||
| 24516 | int rc = fts5StorageLoadTotals(p, 0); | |||
| 24517 | if( rc==SQLITE_OK0 ){ | |||
| 24518 | *pnToken = 0; | |||
| 24519 | if( iCol<0 ){ | |||
| 24520 | int i; | |||
| 24521 | for(i=0; i<p->pConfig->nCol; i++){ | |||
| 24522 | *pnToken += p->aTotalSize[i]; | |||
| 24523 | } | |||
| 24524 | }else if( iCol<p->pConfig->nCol ){ | |||
| 24525 | *pnToken = p->aTotalSize[iCol]; | |||
| 24526 | }else{ | |||
| 24527 | rc = SQLITE_RANGE25; | |||
| 24528 | } | |||
| 24529 | } | |||
| 24530 | return rc; | |||
| 24531 | } | |||
| 24532 | ||||
| 24533 | static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){ | |||
| 24534 | int rc = fts5StorageLoadTotals(p, 0); | |||
| 24535 | if( rc==SQLITE_OK0 ){ | |||
| 24536 | /* nTotalRow being zero does not necessarily indicate a corrupt | |||
| 24537 | ** database - it might be that the FTS5 table really does contain zero | |||
| 24538 | ** rows. However this function is only called from the xRowCount() API, | |||
| 24539 | ** and there is no way for that API to be invoked if the table contains | |||
| 24540 | ** no rows. Hence the FTS5_CORRUPT return. */ | |||
| 24541 | *pnRow = p->nTotalRow; | |||
| 24542 | if( p->nTotalRow<=0 ) rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 24543 | } | |||
| 24544 | return rc; | |||
| 24545 | } | |||
| 24546 | ||||
| 24547 | /* | |||
| 24548 | ** Flush any data currently held in-memory to disk. | |||
| 24549 | */ | |||
| 24550 | static int sqlite3Fts5StorageSync(Fts5Storage *p){ | |||
| 24551 | int rc = SQLITE_OK0; | |||
| 24552 | i64 iLastRowid = sqlite3_last_insert_rowidsqlite3_api->last_insert_rowid(p->pConfig->db); | |||
| 24553 | if( p->bTotalsValid ){ | |||
| 24554 | rc = fts5StorageSaveTotals(p); | |||
| 24555 | if( rc==SQLITE_OK0 ){ | |||
| 24556 | p->bTotalsValid = 0; | |||
| 24557 | } | |||
| 24558 | } | |||
| 24559 | if( rc==SQLITE_OK0 ){ | |||
| 24560 | rc = sqlite3Fts5IndexSync(p->pIndex); | |||
| 24561 | } | |||
| 24562 | sqlite3_set_last_insert_rowidsqlite3_api->set_last_insert_rowid(p->pConfig->db, iLastRowid); | |||
| 24563 | return rc; | |||
| 24564 | } | |||
| 24565 | ||||
| 24566 | static int sqlite3Fts5StorageRollback(Fts5Storage *p){ | |||
| 24567 | p->bTotalsValid = 0; | |||
| 24568 | return sqlite3Fts5IndexRollback(p->pIndex); | |||
| 24569 | } | |||
| 24570 | ||||
| 24571 | static int sqlite3Fts5StorageConfigValue( | |||
| 24572 | Fts5Storage *p, | |||
| 24573 | const char *z, | |||
| 24574 | sqlite3_value *pVal, | |||
| 24575 | int iVal | |||
| 24576 | ){ | |||
| 24577 | sqlite3_stmt *pReplace = 0; | |||
| 24578 | int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG10, &pReplace, 0); | |||
| 24579 | if( rc==SQLITE_OK0 ){ | |||
| 24580 | sqlite3_bind_textsqlite3_api->bind_text(pReplace, 1, z, -1, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 24581 | if( pVal ){ | |||
| 24582 | sqlite3_bind_valuesqlite3_api->bind_value(pReplace, 2, pVal); | |||
| 24583 | }else{ | |||
| 24584 | sqlite3_bind_intsqlite3_api->bind_int(pReplace, 2, iVal); | |||
| 24585 | } | |||
| 24586 | sqlite3_stepsqlite3_api->step(pReplace); | |||
| 24587 | rc = sqlite3_resetsqlite3_api->reset(pReplace); | |||
| 24588 | sqlite3_bind_nullsqlite3_api->bind_null(pReplace, 1); | |||
| 24589 | } | |||
| 24590 | if( rc==SQLITE_OK0 && pVal ){ | |||
| 24591 | int iNew = p->pConfig->iCookie + 1; | |||
| 24592 | rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew); | |||
| 24593 | if( rc==SQLITE_OK0 ){ | |||
| 24594 | p->pConfig->iCookie = iNew; | |||
| 24595 | } | |||
| 24596 | } | |||
| 24597 | return rc; | |||
| 24598 | } | |||
| 24599 | ||||
| 24600 | #line 1 "fts5_tokenize.c" | |||
| 24601 | /* | |||
| 24602 | ** 2014 May 31 | |||
| 24603 | ** | |||
| 24604 | ** The author disclaims copyright to this source code. In place of | |||
| 24605 | ** a legal notice, here is a blessing: | |||
| 24606 | ** | |||
| 24607 | ** May you do good and not evil. | |||
| 24608 | ** May you find forgiveness for yourself and forgive others. | |||
| 24609 | ** May you share freely, never taking more than you give. | |||
| 24610 | ** | |||
| 24611 | ****************************************************************************** | |||
| 24612 | */ | |||
| 24613 | ||||
| 24614 | ||||
| 24615 | /* #include "fts5Int.h" */ | |||
| 24616 | ||||
| 24617 | /************************************************************************** | |||
| 24618 | ** Start of ascii tokenizer implementation. | |||
| 24619 | */ | |||
| 24620 | ||||
| 24621 | /* | |||
| 24622 | ** For tokenizers with no "unicode" modifier, the set of token characters | |||
| 24623 | ** is the same as the set of ASCII range alphanumeric characters. | |||
| 24624 | */ | |||
| 24625 | static unsigned char aAsciiTokenChar[128] = { | |||
| 24626 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00..0x0F */ | |||
| 24627 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10..0x1F */ | |||
| 24628 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20..0x2F */ | |||
| 24629 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30..0x3F */ | |||
| 24630 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40..0x4F */ | |||
| 24631 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50..0x5F */ | |||
| 24632 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60..0x6F */ | |||
| 24633 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x70..0x7F */ | |||
| 24634 | }; | |||
| 24635 | ||||
| 24636 | typedef struct AsciiTokenizer AsciiTokenizer; | |||
| 24637 | struct AsciiTokenizer { | |||
| 24638 | unsigned char aTokenChar[128]; | |||
| 24639 | }; | |||
| 24640 | ||||
| 24641 | static void fts5AsciiAddExceptions( | |||
| 24642 | AsciiTokenizer *p, | |||
| 24643 | const char *zArg, | |||
| 24644 | int bTokenChars | |||
| 24645 | ){ | |||
| 24646 | int i; | |||
| 24647 | for(i=0; zArg[i]; i++){ | |||
| 24648 | if( (zArg[i] & 0x80)==0 ){ | |||
| 24649 | p->aTokenChar[(int)zArg[i]] = (unsigned char)bTokenChars; | |||
| 24650 | } | |||
| 24651 | } | |||
| 24652 | } | |||
| 24653 | ||||
| 24654 | /* | |||
| 24655 | ** Delete a "ascii" tokenizer. | |||
| 24656 | */ | |||
| 24657 | static void fts5AsciiDelete(Fts5Tokenizer *p){ | |||
| 24658 | sqlite3_freesqlite3_api->free(p); | |||
| 24659 | } | |||
| 24660 | ||||
| 24661 | /* | |||
| 24662 | ** Create an "ascii" tokenizer. | |||
| 24663 | */ | |||
| 24664 | static int fts5AsciiCreate( | |||
| 24665 | void *pUnused, | |||
| 24666 | const char **azArg, int nArg, | |||
| 24667 | Fts5Tokenizer **ppOut | |||
| 24668 | ){ | |||
| 24669 | int rc = SQLITE_OK0; | |||
| 24670 | AsciiTokenizer *p = 0; | |||
| 24671 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 24672 | if( nArg%2 ){ | |||
| 24673 | rc = SQLITE_ERROR1; | |||
| 24674 | }else{ | |||
| 24675 | p = sqlite3_malloc64sqlite3_api->malloc64(sizeof(AsciiTokenizer)); | |||
| 24676 | if( p==0 ){ | |||
| 24677 | rc = SQLITE_NOMEM7; | |||
| 24678 | }else{ | |||
| 24679 | int i; | |||
| 24680 | memset(p, 0, sizeof(AsciiTokenizer)); | |||
| 24681 | memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar)); | |||
| 24682 | for(i=0; rc==SQLITE_OK0 && i<nArg; i+=2){ | |||
| 24683 | const char *zArg = azArg[i+1]; | |||
| 24684 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "tokenchars") ){ | |||
| 24685 | fts5AsciiAddExceptions(p, zArg, 1); | |||
| 24686 | }else | |||
| 24687 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "separators") ){ | |||
| 24688 | fts5AsciiAddExceptions(p, zArg, 0); | |||
| 24689 | }else{ | |||
| 24690 | rc = SQLITE_ERROR1; | |||
| 24691 | } | |||
| 24692 | } | |||
| 24693 | if( rc!=SQLITE_OK0 ){ | |||
| 24694 | fts5AsciiDelete((Fts5Tokenizer*)p); | |||
| 24695 | p = 0; | |||
| 24696 | } | |||
| 24697 | } | |||
| 24698 | } | |||
| 24699 | ||||
| 24700 | *ppOut = (Fts5Tokenizer*)p; | |||
| 24701 | return rc; | |||
| 24702 | } | |||
| 24703 | ||||
| 24704 | ||||
| 24705 | static void asciiFold(char *aOut, const char *aIn, int nByte){ | |||
| 24706 | int i; | |||
| 24707 | for(i=0; i<nByte; i++){ | |||
| 24708 | char c = aIn[i]; | |||
| 24709 | if( c>='A' && c<='Z' ) c += 32; | |||
| 24710 | aOut[i] = c; | |||
| 24711 | } | |||
| 24712 | } | |||
| 24713 | ||||
| 24714 | /* | |||
| 24715 | ** Tokenize some text using the ascii tokenizer. | |||
| 24716 | */ | |||
| 24717 | static int fts5AsciiTokenize( | |||
| 24718 | Fts5Tokenizer *pTokenizer, | |||
| 24719 | void *pCtx, | |||
| 24720 | int iUnused, | |||
| 24721 | const char *pText, int nText, | |||
| 24722 | int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) | |||
| 24723 | ){ | |||
| 24724 | AsciiTokenizer *p = (AsciiTokenizer*)pTokenizer; | |||
| 24725 | int rc = SQLITE_OK0; | |||
| 24726 | int ie; | |||
| 24727 | int is = 0; | |||
| 24728 | ||||
| 24729 | char aFold[64]; | |||
| 24730 | int nFold = sizeof(aFold); | |||
| 24731 | char *pFold = aFold; | |||
| 24732 | unsigned char *a = p->aTokenChar; | |||
| 24733 | ||||
| 24734 | UNUSED_PARAM(iUnused)(void)(iUnused); | |||
| 24735 | ||||
| 24736 | while( is<nText && rc==SQLITE_OK0 ){ | |||
| 24737 | int nByte; | |||
| 24738 | ||||
| 24739 | /* Skip any leading divider characters. */ | |||
| 24740 | while( is<nText && ((pText[is]&0x80)==0 && a[(int)pText[is]]==0) ){ | |||
| 24741 | is++; | |||
| 24742 | } | |||
| 24743 | if( is==nText ) break; | |||
| 24744 | ||||
| 24745 | /* Count the token characters */ | |||
| 24746 | ie = is+1; | |||
| 24747 | while( ie<nText && ((pText[ie]&0x80) || a[(int)pText[ie]] ) ){ | |||
| 24748 | ie++; | |||
| 24749 | } | |||
| 24750 | ||||
| 24751 | /* Fold to lower case */ | |||
| 24752 | nByte = ie-is; | |||
| 24753 | if( nByte>nFold ){ | |||
| 24754 | if( pFold!=aFold ) sqlite3_freesqlite3_api->free(pFold); | |||
| 24755 | pFold = sqlite3_malloc64sqlite3_api->malloc64((sqlite3_int64)nByte*2); | |||
| 24756 | if( pFold==0 ){ | |||
| 24757 | rc = SQLITE_NOMEM7; | |||
| 24758 | break; | |||
| 24759 | } | |||
| 24760 | nFold = nByte*2; | |||
| 24761 | } | |||
| 24762 | asciiFold(pFold, &pText[is], nByte); | |||
| 24763 | ||||
| 24764 | /* Invoke the token callback */ | |||
| 24765 | rc = xToken(pCtx, 0, pFold, nByte, is, ie); | |||
| 24766 | is = ie+1; | |||
| 24767 | } | |||
| 24768 | ||||
| 24769 | if( pFold!=aFold ) sqlite3_freesqlite3_api->free(pFold); | |||
| 24770 | if( rc==SQLITE_DONE101 ) rc = SQLITE_OK0; | |||
| 24771 | return rc; | |||
| 24772 | } | |||
| 24773 | ||||
| 24774 | /************************************************************************** | |||
| 24775 | ** Start of unicode61 tokenizer implementation. | |||
| 24776 | */ | |||
| 24777 | ||||
| 24778 | ||||
| 24779 | /* | |||
| 24780 | ** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied | |||
| 24781 | ** from the sqlite3 source file utf.c. If this file is compiled as part | |||
| 24782 | ** of the amalgamation, they are not required. | |||
| 24783 | */ | |||
| 24784 | #ifndef SQLITE_AMALGAMATION | |||
| 24785 | ||||
| 24786 | static const unsigned char sqlite3Utf8Trans1[] = { | |||
| 24787 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | |||
| 24788 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, | |||
| 24789 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, | |||
| 24790 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, | |||
| 24791 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | |||
| 24792 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, | |||
| 24793 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | |||
| 24794 | 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, | |||
| 24795 | }; | |||
| 24796 | ||||
| 24797 | #define READ_UTF8(zIn, zTerm, c)c = *(zIn++); if( c>=0xc0 ){ c = sqlite3Utf8Trans1[c-0xc0] ; while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ c = (c<<6) + (0x3f & *(zIn++)); } if( c<0x80 || ( c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } } \ | |||
| 24798 | c = *(zIn++); \ | |||
| 24799 | if( c>=0xc0 ){ \ | |||
| 24800 | c = sqlite3Utf8Trans1[c-0xc0]; \ | |||
| 24801 | while( zIn<zTerm && (*zIn & 0xc0)==0x80 ){ \ | |||
| 24802 | c = (c<<6) + (0x3f & *(zIn++)); \ | |||
| 24803 | } \ | |||
| 24804 | if( c<0x80 \ | |||
| 24805 | || (c&0xFFFFF800)==0xD800 \ | |||
| 24806 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ | |||
| 24807 | } | |||
| 24808 | ||||
| 24809 | ||||
| 24810 | #define WRITE_UTF8(zOut, c){ if( c<0x00080 ){ *zOut++ = (unsigned char)(c&0xFF); } else if( c<0x00800 ){ *zOut++ = 0xC0 + (unsigned char)((c >>6)&0x1F); *zOut++ = 0x80 + (unsigned char)(c & 0x3F); } else if( c<0x10000 ){ *zOut++ = 0xE0 + (unsigned char)((c>>12)&0x0F); *zOut++ = 0x80 + (unsigned char )((c>>6) & 0x3F); *zOut++ = 0x80 + (unsigned char)( c & 0x3F); }else{ *zOut++ = 0xF0 + (unsigned char)((c>> 18) & 0x07); *zOut++ = 0x80 + (unsigned char)((c>>12 ) & 0x3F); *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); *zOut++ = 0x80 + (unsigned char)(c & 0x3F); } } { \ | |||
| 24811 | if( c<0x00080 ){ \ | |||
| 24812 | *zOut++ = (unsigned char)(c&0xFF); \ | |||
| 24813 | } \ | |||
| 24814 | else if( c<0x00800 ){ \ | |||
| 24815 | *zOut++ = 0xC0 + (unsigned char)((c>>6)&0x1F); \ | |||
| 24816 | *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ | |||
| 24817 | } \ | |||
| 24818 | else if( c<0x10000 ){ \ | |||
| 24819 | *zOut++ = 0xE0 + (unsigned char)((c>>12)&0x0F); \ | |||
| 24820 | *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \ | |||
| 24821 | *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ | |||
| 24822 | }else{ \ | |||
| 24823 | *zOut++ = 0xF0 + (unsigned char)((c>>18) & 0x07); \ | |||
| 24824 | *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \ | |||
| 24825 | *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \ | |||
| 24826 | *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ | |||
| 24827 | } \ | |||
| 24828 | } | |||
| 24829 | ||||
| 24830 | #endif /* ifndef SQLITE_AMALGAMATION */ | |||
| 24831 | ||||
| 24832 | #define FTS5_SKIP_UTF8(zIn){ if( ((unsigned char)(*(zIn++)))>=0xc0 ){ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } } } { \ | |||
| 24833 | if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \ | |||
| 24834 | while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \ | |||
| 24835 | } \ | |||
| 24836 | } | |||
| 24837 | ||||
| 24838 | typedef struct Unicode61Tokenizer Unicode61Tokenizer; | |||
| 24839 | struct Unicode61Tokenizer { | |||
| 24840 | unsigned char aTokenChar[128]; /* ASCII range token characters */ | |||
| 24841 | char *aFold; /* Buffer to fold text into */ | |||
| 24842 | int nFold; /* Size of aFold[] in bytes */ | |||
| 24843 | int eRemoveDiacritic; /* True if remove_diacritics=1 is set */ | |||
| 24844 | int nException; | |||
| 24845 | int *aiException; | |||
| 24846 | ||||
| 24847 | unsigned char aCategory[32]; /* True for token char categories */ | |||
| 24848 | }; | |||
| 24849 | ||||
| 24850 | /* Values for eRemoveDiacritic (must match internals of fts5_unicode2.c) */ | |||
| 24851 | #define FTS5_REMOVE_DIACRITICS_NONE0 0 | |||
| 24852 | #define FTS5_REMOVE_DIACRITICS_SIMPLE1 1 | |||
| 24853 | #define FTS5_REMOVE_DIACRITICS_COMPLEX2 2 | |||
| 24854 | ||||
| 24855 | static int fts5UnicodeAddExceptions( | |||
| 24856 | Unicode61Tokenizer *p, /* Tokenizer object */ | |||
| 24857 | const char *z, /* Characters to treat as exceptions */ | |||
| 24858 | int bTokenChars /* 1 for 'tokenchars', 0 for 'separators' */ | |||
| 24859 | ){ | |||
| 24860 | int rc = SQLITE_OK0; | |||
| 24861 | int n = (int)strlen(z); | |||
| 24862 | int *aNew; | |||
| 24863 | ||||
| 24864 | if( n>0 ){ | |||
| 24865 | aNew = (int*)sqlite3_realloc64sqlite3_api->realloc64(p->aiException, | |||
| 24866 | (n+p->nException)*sizeof(int)); | |||
| 24867 | if( aNew ){ | |||
| 24868 | int nNew = p->nException; | |||
| 24869 | const unsigned char *zCsr = (const unsigned char*)z; | |||
| 24870 | const unsigned char *zTerm = (const unsigned char*)&z[n]; | |||
| 24871 | while( zCsr<zTerm ){ | |||
| 24872 | u32 iCode; | |||
| 24873 | int bToken; | |||
| 24874 | READ_UTF8(zCsr, zTerm, iCode)iCode = *(zCsr++); if( iCode>=0xc0 ){ iCode = sqlite3Utf8Trans1 [iCode-0xc0]; while( zCsr<zTerm && (*zCsr & 0xc0 )==0x80 ){ iCode = (iCode<<6) + (0x3f & *(zCsr++)); } if( iCode<0x80 || (iCode&0xFFFFF800)==0xD800 || (iCode &0xFFFFFFFE)==0xFFFE ){ iCode = 0xFFFD; } }; | |||
| 24875 | if( iCode<128 ){ | |||
| 24876 | p->aTokenChar[iCode] = (unsigned char)bTokenChars; | |||
| 24877 | }else{ | |||
| 24878 | bToken = p->aCategory[sqlite3Fts5UnicodeCategory(iCode)]; | |||
| 24879 | assert( (bToken==0 || bToken==1) )((void) (0)); | |||
| 24880 | assert( (bTokenChars==0 || bTokenChars==1) )((void) (0)); | |||
| 24881 | if( bToken!=bTokenChars && sqlite3Fts5UnicodeIsdiacritic(iCode)==0 ){ | |||
| 24882 | int i; | |||
| 24883 | for(i=0; i<nNew; i++){ | |||
| 24884 | if( (u32)aNew[i]>iCode ) break; | |||
| 24885 | } | |||
| 24886 | memmove(&aNew[i+1], &aNew[i], (nNew-i)*sizeof(int)); | |||
| 24887 | aNew[i] = iCode; | |||
| 24888 | nNew++; | |||
| 24889 | } | |||
| 24890 | } | |||
| 24891 | } | |||
| 24892 | p->aiException = aNew; | |||
| 24893 | p->nException = nNew; | |||
| 24894 | }else{ | |||
| 24895 | rc = SQLITE_NOMEM7; | |||
| 24896 | } | |||
| 24897 | } | |||
| 24898 | ||||
| 24899 | return rc; | |||
| 24900 | } | |||
| 24901 | ||||
| 24902 | /* | |||
| 24903 | ** Return true if the p->aiException[] array contains the value iCode. | |||
| 24904 | */ | |||
| 24905 | static int fts5UnicodeIsException(Unicode61Tokenizer *p, int iCode){ | |||
| 24906 | if( p->nException>0 ){ | |||
| 24907 | int *a = p->aiException; | |||
| 24908 | int iLo = 0; | |||
| 24909 | int iHi = p->nException-1; | |||
| 24910 | ||||
| 24911 | while( iHi>=iLo ){ | |||
| 24912 | int iTest = (iHi + iLo) / 2; | |||
| 24913 | if( iCode==a[iTest] ){ | |||
| 24914 | return 1; | |||
| 24915 | }else if( iCode>a[iTest] ){ | |||
| 24916 | iLo = iTest+1; | |||
| 24917 | }else{ | |||
| 24918 | iHi = iTest-1; | |||
| 24919 | } | |||
| 24920 | } | |||
| 24921 | } | |||
| 24922 | ||||
| 24923 | return 0; | |||
| 24924 | } | |||
| 24925 | ||||
| 24926 | /* | |||
| 24927 | ** Delete a "unicode61" tokenizer. | |||
| 24928 | */ | |||
| 24929 | static void fts5UnicodeDelete(Fts5Tokenizer *pTok){ | |||
| 24930 | if( pTok ){ | |||
| 24931 | Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTok; | |||
| 24932 | sqlite3_freesqlite3_api->free(p->aiException); | |||
| 24933 | sqlite3_freesqlite3_api->free(p->aFold); | |||
| 24934 | sqlite3_freesqlite3_api->free(p); | |||
| 24935 | } | |||
| 24936 | return; | |||
| 24937 | } | |||
| 24938 | ||||
| 24939 | static int unicodeSetCategories(Unicode61Tokenizer *p, const char *zCat){ | |||
| 24940 | const char *z = zCat; | |||
| 24941 | ||||
| 24942 | while( *z ){ | |||
| 24943 | while( *z==' ' || *z=='\t' ) z++; | |||
| 24944 | if( *z && sqlite3Fts5UnicodeCatParse(z, p->aCategory) ){ | |||
| 24945 | return SQLITE_ERROR1; | |||
| 24946 | } | |||
| 24947 | while( *z!=' ' && *z!='\t' && *z!='\0' ) z++; | |||
| 24948 | } | |||
| 24949 | ||||
| 24950 | sqlite3Fts5UnicodeAscii(p->aCategory, p->aTokenChar); | |||
| 24951 | return SQLITE_OK0; | |||
| 24952 | } | |||
| 24953 | ||||
| 24954 | /* | |||
| 24955 | ** Create a "unicode61" tokenizer. | |||
| 24956 | */ | |||
| 24957 | static int fts5UnicodeCreate( | |||
| 24958 | void *pUnused, | |||
| 24959 | const char **azArg, int nArg, | |||
| 24960 | Fts5Tokenizer **ppOut | |||
| 24961 | ){ | |||
| 24962 | int rc = SQLITE_OK0; /* Return code */ | |||
| 24963 | Unicode61Tokenizer *p = 0; /* New tokenizer object */ | |||
| 24964 | ||||
| 24965 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 24966 | ||||
| 24967 | if( nArg%2 ){ | |||
| 24968 | rc = SQLITE_ERROR1; | |||
| 24969 | }else{ | |||
| 24970 | p = (Unicode61Tokenizer*)sqlite3_malloc64sqlite3_api->malloc64(sizeof(Unicode61Tokenizer)); | |||
| 24971 | if( p ){ | |||
| 24972 | const char *zCat = "L* N* Co"; | |||
| 24973 | int i; | |||
| 24974 | memset(p, 0, sizeof(Unicode61Tokenizer)); | |||
| 24975 | ||||
| 24976 | p->eRemoveDiacritic = FTS5_REMOVE_DIACRITICS_SIMPLE1; | |||
| 24977 | p->nFold = 64; | |||
| 24978 | p->aFold = sqlite3_malloc64sqlite3_api->malloc64(p->nFold * sizeof(char)); | |||
| 24979 | if( p->aFold==0 ){ | |||
| 24980 | rc = SQLITE_NOMEM7; | |||
| 24981 | } | |||
| 24982 | ||||
| 24983 | /* Search for a "categories" argument */ | |||
| 24984 | for(i=0; rc==SQLITE_OK0 && i<nArg; i+=2){ | |||
| 24985 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "categories") ){ | |||
| 24986 | zCat = azArg[i+1]; | |||
| 24987 | } | |||
| 24988 | } | |||
| 24989 | if( rc==SQLITE_OK0 ){ | |||
| 24990 | rc = unicodeSetCategories(p, zCat); | |||
| 24991 | } | |||
| 24992 | ||||
| 24993 | for(i=0; rc==SQLITE_OK0 && i<nArg; i+=2){ | |||
| 24994 | const char *zArg = azArg[i+1]; | |||
| 24995 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "remove_diacritics") ){ | |||
| 24996 | if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ | |||
| 24997 | rc = SQLITE_ERROR1; | |||
| 24998 | }else{ | |||
| 24999 | p->eRemoveDiacritic = (zArg[0] - '0'); | |||
| 25000 | assert( p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_NONE((void) (0)) | |||
| 25001 | || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_SIMPLE((void) (0)) | |||
| 25002 | || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_COMPLEX((void) (0)) | |||
| 25003 | )((void) (0)); | |||
| 25004 | } | |||
| 25005 | }else | |||
| 25006 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "tokenchars") ){ | |||
| 25007 | rc = fts5UnicodeAddExceptions(p, zArg, 1); | |||
| 25008 | }else | |||
| 25009 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "separators") ){ | |||
| 25010 | rc = fts5UnicodeAddExceptions(p, zArg, 0); | |||
| 25011 | }else | |||
| 25012 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "categories") ){ | |||
| 25013 | /* no-op */ | |||
| 25014 | }else{ | |||
| 25015 | rc = SQLITE_ERROR1; | |||
| 25016 | } | |||
| 25017 | } | |||
| 25018 | }else{ | |||
| 25019 | rc = SQLITE_NOMEM7; | |||
| 25020 | } | |||
| 25021 | if( rc!=SQLITE_OK0 ){ | |||
| 25022 | fts5UnicodeDelete((Fts5Tokenizer*)p); | |||
| 25023 | p = 0; | |||
| 25024 | } | |||
| 25025 | *ppOut = (Fts5Tokenizer*)p; | |||
| 25026 | } | |||
| 25027 | return rc; | |||
| 25028 | } | |||
| 25029 | ||||
| 25030 | /* | |||
| 25031 | ** Return true if, for the purposes of tokenizing with the tokenizer | |||
| 25032 | ** passed as the first argument, codepoint iCode is considered a token | |||
| 25033 | ** character (not a separator). | |||
| 25034 | */ | |||
| 25035 | static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){ | |||
| 25036 | return ( | |||
| 25037 | p->aCategory[sqlite3Fts5UnicodeCategory((u32)iCode)] | |||
| 25038 | ^ fts5UnicodeIsException(p, iCode) | |||
| 25039 | ); | |||
| 25040 | } | |||
| 25041 | ||||
| 25042 | static int fts5UnicodeTokenize( | |||
| 25043 | Fts5Tokenizer *pTokenizer, | |||
| 25044 | void *pCtx, | |||
| 25045 | int iUnused, | |||
| 25046 | const char *pText, int nText, | |||
| 25047 | int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) | |||
| 25048 | ){ | |||
| 25049 | Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTokenizer; | |||
| 25050 | int rc = SQLITE_OK0; | |||
| 25051 | unsigned char *a = p->aTokenChar; | |||
| 25052 | ||||
| 25053 | unsigned char *zTerm = (unsigned char*)&pText[nText]; | |||
| 25054 | unsigned char *zCsr = (unsigned char *)pText; | |||
| 25055 | ||||
| 25056 | /* Output buffer */ | |||
| 25057 | char *aFold = p->aFold; | |||
| 25058 | int nFold = p->nFold; | |||
| 25059 | const char *pEnd = &aFold[nFold-6]; | |||
| 25060 | ||||
| 25061 | UNUSED_PARAM(iUnused)(void)(iUnused); | |||
| 25062 | ||||
| 25063 | /* Each iteration of this loop gobbles up a contiguous run of separators, | |||
| 25064 | ** then the next token. */ | |||
| 25065 | while( rc==SQLITE_OK0 ){ | |||
| 25066 | u32 iCode; /* non-ASCII codepoint read from input */ | |||
| 25067 | char *zOut = aFold; | |||
| 25068 | int is; | |||
| 25069 | int ie; | |||
| 25070 | ||||
| 25071 | /* Skip any separator characters. */ | |||
| 25072 | while( 1 ){ | |||
| 25073 | if( zCsr>=zTerm ) goto tokenize_done; | |||
| 25074 | if( *zCsr & 0x80 ) { | |||
| 25075 | /* A character outside of the ascii range. Skip past it if it is | |||
| 25076 | ** a separator character. Or break out of the loop if it is not. */ | |||
| 25077 | is = zCsr - (unsigned char*)pText; | |||
| 25078 | READ_UTF8(zCsr, zTerm, iCode)iCode = *(zCsr++); if( iCode>=0xc0 ){ iCode = sqlite3Utf8Trans1 [iCode-0xc0]; while( zCsr<zTerm && (*zCsr & 0xc0 )==0x80 ){ iCode = (iCode<<6) + (0x3f & *(zCsr++)); } if( iCode<0x80 || (iCode&0xFFFFF800)==0xD800 || (iCode &0xFFFFFFFE)==0xFFFE ){ iCode = 0xFFFD; } }; | |||
| 25079 | if( fts5UnicodeIsAlnum(p, iCode) ){ | |||
| 25080 | goto non_ascii_tokenchar; | |||
| 25081 | } | |||
| 25082 | }else{ | |||
| 25083 | if( a[*zCsr] ){ | |||
| 25084 | is = zCsr - (unsigned char*)pText; | |||
| 25085 | goto ascii_tokenchar; | |||
| 25086 | } | |||
| 25087 | zCsr++; | |||
| 25088 | } | |||
| 25089 | } | |||
| 25090 | ||||
| 25091 | /* Run through the tokenchars. Fold them into the output buffer along | |||
| 25092 | ** the way. */ | |||
| 25093 | while( zCsr<zTerm ){ | |||
| 25094 | ||||
| 25095 | /* Grow the output buffer so that there is sufficient space to fit the | |||
| 25096 | ** largest possible utf-8 character. */ | |||
| 25097 | if( zOut>pEnd ){ | |||
| 25098 | aFold = sqlite3_malloc64sqlite3_api->malloc64((sqlite3_int64)nFold*2); | |||
| 25099 | if( aFold==0 ){ | |||
| 25100 | rc = SQLITE_NOMEM7; | |||
| 25101 | goto tokenize_done; | |||
| 25102 | } | |||
| 25103 | zOut = &aFold[zOut - p->aFold]; | |||
| 25104 | memcpy(aFold, p->aFold, nFold); | |||
| 25105 | sqlite3_freesqlite3_api->free(p->aFold); | |||
| 25106 | p->aFold = aFold; | |||
| 25107 | p->nFold = nFold = nFold*2; | |||
| 25108 | pEnd = &aFold[nFold-6]; | |||
| 25109 | } | |||
| 25110 | ||||
| 25111 | if( *zCsr & 0x80 ){ | |||
| 25112 | /* An non-ascii-range character. Fold it into the output buffer if | |||
| 25113 | ** it is a token character, or break out of the loop if it is not. */ | |||
| 25114 | READ_UTF8(zCsr, zTerm, iCode)iCode = *(zCsr++); if( iCode>=0xc0 ){ iCode = sqlite3Utf8Trans1 [iCode-0xc0]; while( zCsr<zTerm && (*zCsr & 0xc0 )==0x80 ){ iCode = (iCode<<6) + (0x3f & *(zCsr++)); } if( iCode<0x80 || (iCode&0xFFFFF800)==0xD800 || (iCode &0xFFFFFFFE)==0xFFFE ){ iCode = 0xFFFD; } }; | |||
| 25115 | if( fts5UnicodeIsAlnum(p,iCode)||sqlite3Fts5UnicodeIsdiacritic(iCode) ){ | |||
| 25116 | non_ascii_tokenchar: | |||
| 25117 | iCode = sqlite3Fts5UnicodeFold(iCode, p->eRemoveDiacritic); | |||
| 25118 | if( iCode ) WRITE_UTF8(zOut, iCode){ if( iCode<0x00080 ){ *zOut++ = (unsigned char)(iCode& 0xFF); } else if( iCode<0x00800 ){ *zOut++ = 0xC0 + (unsigned char)((iCode>>6)&0x1F); *zOut++ = 0x80 + (unsigned char)(iCode & 0x3F); } else if( iCode<0x10000 ){ *zOut ++ = 0xE0 + (unsigned char)((iCode>>12)&0x0F); *zOut ++ = 0x80 + (unsigned char)((iCode>>6) & 0x3F); *zOut ++ = 0x80 + (unsigned char)(iCode & 0x3F); }else{ *zOut++ = 0xF0 + (unsigned char)((iCode>>18) & 0x07); *zOut ++ = 0x80 + (unsigned char)((iCode>>12) & 0x3F); *zOut ++ = 0x80 + (unsigned char)((iCode>>6) & 0x3F); *zOut ++ = 0x80 + (unsigned char)(iCode & 0x3F); } }; | |||
| 25119 | }else{ | |||
| 25120 | break; | |||
| 25121 | } | |||
| 25122 | }else if( a[*zCsr]==0 ){ | |||
| 25123 | /* An ascii-range separator character. End of token. */ | |||
| 25124 | break; | |||
| 25125 | }else{ | |||
| 25126 | ascii_tokenchar: | |||
| 25127 | if( *zCsr>='A' && *zCsr<='Z' ){ | |||
| 25128 | *zOut++ = *zCsr + 32; | |||
| 25129 | }else{ | |||
| 25130 | *zOut++ = *zCsr; | |||
| 25131 | } | |||
| 25132 | zCsr++; | |||
| 25133 | } | |||
| 25134 | ie = zCsr - (unsigned char*)pText; | |||
| 25135 | } | |||
| 25136 | ||||
| 25137 | /* Invoke the token callback */ | |||
| 25138 | rc = xToken(pCtx, 0, aFold, zOut-aFold, is, ie); | |||
| 25139 | } | |||
| 25140 | ||||
| 25141 | tokenize_done: | |||
| 25142 | if( rc==SQLITE_DONE101 ) rc = SQLITE_OK0; | |||
| 25143 | return rc; | |||
| 25144 | } | |||
| 25145 | ||||
| 25146 | /************************************************************************** | |||
| 25147 | ** Start of porter stemmer implementation. | |||
| 25148 | */ | |||
| 25149 | ||||
| 25150 | /* Any tokens larger than this (in bytes) are passed through without | |||
| 25151 | ** stemming. */ | |||
| 25152 | #define FTS5_PORTER_MAX_TOKEN64 64 | |||
| 25153 | ||||
| 25154 | typedef struct PorterTokenizer PorterTokenizer; | |||
| 25155 | struct PorterTokenizer { | |||
| 25156 | fts5_tokenizer_v2 tokenizer_v2; /* Parent tokenizer module */ | |||
| 25157 | Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */ | |||
| 25158 | char aBuf[FTS5_PORTER_MAX_TOKEN64 + 64]; | |||
| 25159 | }; | |||
| 25160 | ||||
| 25161 | /* | |||
| 25162 | ** Delete a "porter" tokenizer. | |||
| 25163 | */ | |||
| 25164 | static void fts5PorterDelete(Fts5Tokenizer *pTok){ | |||
| 25165 | if( pTok ){ | |||
| 25166 | PorterTokenizer *p = (PorterTokenizer*)pTok; | |||
| 25167 | if( p->pTokenizer ){ | |||
| 25168 | p->tokenizer_v2.xDelete(p->pTokenizer); | |||
| 25169 | } | |||
| 25170 | sqlite3_freesqlite3_api->free(p); | |||
| 25171 | } | |||
| 25172 | } | |||
| 25173 | ||||
| 25174 | /* | |||
| 25175 | ** Create a "porter" tokenizer. | |||
| 25176 | */ | |||
| 25177 | static int fts5PorterCreate( | |||
| 25178 | void *pCtx, | |||
| 25179 | const char **azArg, int nArg, | |||
| 25180 | Fts5Tokenizer **ppOut | |||
| 25181 | ){ | |||
| 25182 | fts5_api *pApi = (fts5_api*)pCtx; | |||
| 25183 | int rc = SQLITE_OK0; | |||
| 25184 | PorterTokenizer *pRet; | |||
| 25185 | void *pUserdata = 0; | |||
| 25186 | const char *zBase = "unicode61"; | |||
| 25187 | fts5_tokenizer_v2 *pV2 = 0; | |||
| 25188 | ||||
| 25189 | while( nArg>0 ){ | |||
| 25190 | if( sqlite3_stricmpsqlite3_api->stricmp(azArg[0],"porter")==0 ){ | |||
| 25191 | nArg--; | |||
| 25192 | azArg++; | |||
| 25193 | }else{ | |||
| 25194 | zBase = azArg[0]; | |||
| 25195 | break; | |||
| 25196 | } | |||
| 25197 | } | |||
| 25198 | ||||
| 25199 | pRet = (PorterTokenizer*)sqlite3_malloc64sqlite3_api->malloc64(sizeof(PorterTokenizer)); | |||
| 25200 | if( pRet ){ | |||
| 25201 | memset(pRet, 0, sizeof(PorterTokenizer)); | |||
| 25202 | rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2); | |||
| 25203 | }else{ | |||
| 25204 | rc = SQLITE_NOMEM7; | |||
| 25205 | } | |||
| 25206 | if( rc==SQLITE_OK0 ){ | |||
| 25207 | int nArg2 = (nArg>0 ? nArg-1 : 0); | |||
| 25208 | const char **az2 = (nArg2 ? &azArg[1] : 0); | |||
| 25209 | memcpy(&pRet->tokenizer_v2, pV2, sizeof(fts5_tokenizer_v2)); | |||
| 25210 | rc = pRet->tokenizer_v2.xCreate(pUserdata, az2, nArg2, &pRet->pTokenizer); | |||
| 25211 | } | |||
| 25212 | ||||
| 25213 | if( rc!=SQLITE_OK0 ){ | |||
| 25214 | fts5PorterDelete((Fts5Tokenizer*)pRet); | |||
| 25215 | pRet = 0; | |||
| 25216 | } | |||
| 25217 | *ppOut = (Fts5Tokenizer*)pRet; | |||
| 25218 | return rc; | |||
| 25219 | } | |||
| 25220 | ||||
| 25221 | typedef struct PorterContext PorterContext; | |||
| 25222 | struct PorterContext { | |||
| 25223 | void *pCtx; | |||
| 25224 | int (*xToken)(void*, int, const char*, int, int, int); | |||
| 25225 | char *aBuf; | |||
| 25226 | }; | |||
| 25227 | ||||
| 25228 | typedef struct PorterRule PorterRule; | |||
| 25229 | struct PorterRule { | |||
| 25230 | const char *zSuffix; | |||
| 25231 | int nSuffix; | |||
| 25232 | int (*xCond)(char *zStem, int nStem); | |||
| 25233 | const char *zOutput; | |||
| 25234 | int nOutput; | |||
| 25235 | }; | |||
| 25236 | ||||
| 25237 | #if 0 | |||
| 25238 | static int fts5PorterApply(char *aBuf, int *pnBuf, PorterRule *aRule){ | |||
| 25239 | int ret = -1; | |||
| 25240 | int nBuf = *pnBuf; | |||
| 25241 | PorterRule *p; | |||
| 25242 | ||||
| 25243 | for(p=aRule; p->zSuffix; p++){ | |||
| 25244 | assert( strlen(p->zSuffix)==p->nSuffix )((void) (0)); | |||
| 25245 | assert( strlen(p->zOutput)==p->nOutput )((void) (0)); | |||
| 25246 | if( nBuf<p->nSuffix ) continue; | |||
| 25247 | if( 0==memcmp(&aBuf[nBuf - p->nSuffix], p->zSuffix, p->nSuffix) ) break; | |||
| 25248 | } | |||
| 25249 | ||||
| 25250 | if( p->zSuffix ){ | |||
| 25251 | int nStem = nBuf - p->nSuffix; | |||
| 25252 | if( p->xCond==0 || p->xCond(aBuf, nStem) ){ | |||
| 25253 | memcpy(&aBuf[nStem], p->zOutput, p->nOutput); | |||
| 25254 | *pnBuf = nStem + p->nOutput; | |||
| 25255 | ret = p - aRule; | |||
| 25256 | } | |||
| 25257 | } | |||
| 25258 | ||||
| 25259 | return ret; | |||
| 25260 | } | |||
| 25261 | #endif | |||
| 25262 | ||||
| 25263 | static int fts5PorterIsVowel(char c, int bYIsVowel){ | |||
| 25264 | return ( | |||
| 25265 | c=='a' || c=='e' || c=='i' || c=='o' || c=='u' || (bYIsVowel && c=='y') | |||
| 25266 | ); | |||
| 25267 | } | |||
| 25268 | ||||
| 25269 | static int fts5PorterGobbleVC(char *zStem, int nStem, int bPrevCons){ | |||
| 25270 | int i; | |||
| 25271 | int bCons = bPrevCons; | |||
| 25272 | ||||
| 25273 | /* Scan for a vowel */ | |||
| 25274 | for(i=0; i<nStem; i++){ | |||
| 25275 | if( 0==(bCons = !fts5PorterIsVowel(zStem[i], bCons)) ) break; | |||
| 25276 | } | |||
| 25277 | ||||
| 25278 | /* Scan for a consonent */ | |||
| 25279 | for(i++; i<nStem; i++){ | |||
| 25280 | if( (bCons = !fts5PorterIsVowel(zStem[i], bCons)) ) return i+1; | |||
| 25281 | } | |||
| 25282 | return 0; | |||
| 25283 | } | |||
| 25284 | ||||
| 25285 | /* porter rule condition: (m > 0) */ | |||
| 25286 | static int fts5Porter_MGt0(char *zStem, int nStem){ | |||
| 25287 | return !!fts5PorterGobbleVC(zStem, nStem, 0); | |||
| 25288 | } | |||
| 25289 | ||||
| 25290 | /* porter rule condition: (m > 1) */ | |||
| 25291 | static int fts5Porter_MGt1(char *zStem, int nStem){ | |||
| 25292 | int n; | |||
| 25293 | n = fts5PorterGobbleVC(zStem, nStem, 0); | |||
| 25294 | if( n && fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){ | |||
| 25295 | return 1; | |||
| 25296 | } | |||
| 25297 | return 0; | |||
| 25298 | } | |||
| 25299 | ||||
| 25300 | /* porter rule condition: (m = 1) */ | |||
| 25301 | static int fts5Porter_MEq1(char *zStem, int nStem){ | |||
| 25302 | int n; | |||
| 25303 | n = fts5PorterGobbleVC(zStem, nStem, 0); | |||
| 25304 | if( n && 0==fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){ | |||
| 25305 | return 1; | |||
| 25306 | } | |||
| 25307 | return 0; | |||
| 25308 | } | |||
| 25309 | ||||
| 25310 | /* porter rule condition: (*o) */ | |||
| 25311 | static int fts5Porter_Ostar(char *zStem, int nStem){ | |||
| 25312 | if( zStem[nStem-1]=='w' || zStem[nStem-1]=='x' || zStem[nStem-1]=='y' ){ | |||
| 25313 | return 0; | |||
| 25314 | }else{ | |||
| 25315 | int i; | |||
| 25316 | int mask = 0; | |||
| 25317 | int bCons = 0; | |||
| 25318 | for(i=0; i<nStem; i++){ | |||
| 25319 | bCons = !fts5PorterIsVowel(zStem[i], bCons); | |||
| 25320 | assert( bCons==0 || bCons==1 )((void) (0)); | |||
| 25321 | mask = (mask << 1) + bCons; | |||
| 25322 | } | |||
| 25323 | return ((mask & 0x0007)==0x0005); | |||
| 25324 | } | |||
| 25325 | } | |||
| 25326 | ||||
| 25327 | /* porter rule condition: (m > 1 and (*S or *T)) */ | |||
| 25328 | static int fts5Porter_MGt1_and_S_or_T(char *zStem, int nStem){ | |||
| 25329 | assert( nStem>0 )((void) (0)); | |||
| 25330 | return (zStem[nStem-1]=='s' || zStem[nStem-1]=='t') | |||
| 25331 | && fts5Porter_MGt1(zStem, nStem); | |||
| 25332 | } | |||
| 25333 | ||||
| 25334 | /* porter rule condition: (*v*) */ | |||
| 25335 | static int fts5Porter_Vowel(char *zStem, int nStem){ | |||
| 25336 | int i; | |||
| 25337 | for(i=0; i<nStem; i++){ | |||
| 25338 | if( fts5PorterIsVowel(zStem[i], i>0) ){ | |||
| 25339 | return 1; | |||
| 25340 | } | |||
| 25341 | } | |||
| 25342 | return 0; | |||
| 25343 | } | |||
| 25344 | ||||
| 25345 | ||||
| 25346 | /************************************************************************** | |||
| 25347 | *************************************************************************** | |||
| 25348 | ** GENERATED CODE STARTS HERE (mkportersteps.tcl) | |||
| 25349 | */ | |||
| 25350 | ||||
| 25351 | static int fts5PorterStep4(char *aBuf, int *pnBuf){ | |||
| 25352 | int ret = 0; | |||
| 25353 | int nBuf = *pnBuf; | |||
| 25354 | switch( aBuf[nBuf-2] ){ | |||
| 25355 | ||||
| 25356 | case 'a': | |||
| 25357 | if( nBuf>2 && 0==memcmp("al", &aBuf[nBuf-2], 2) ){ | |||
| 25358 | if( fts5Porter_MGt1(aBuf, nBuf-2) ){ | |||
| 25359 | *pnBuf = nBuf - 2; | |||
| 25360 | } | |||
| 25361 | } | |||
| 25362 | break; | |||
| 25363 | ||||
| 25364 | case 'c': | |||
| 25365 | if( nBuf>4 && 0==memcmp("ance", &aBuf[nBuf-4], 4) ){ | |||
| 25366 | if( fts5Porter_MGt1(aBuf, nBuf-4) ){ | |||
| 25367 | *pnBuf = nBuf - 4; | |||
| 25368 | } | |||
| 25369 | }else if( nBuf>4 && 0==memcmp("ence", &aBuf[nBuf-4], 4) ){ | |||
| 25370 | if( fts5Porter_MGt1(aBuf, nBuf-4) ){ | |||
| 25371 | *pnBuf = nBuf - 4; | |||
| 25372 | } | |||
| 25373 | } | |||
| 25374 | break; | |||
| 25375 | ||||
| 25376 | case 'e': | |||
| 25377 | if( nBuf>2 && 0==memcmp("er", &aBuf[nBuf-2], 2) ){ | |||
| 25378 | if( fts5Porter_MGt1(aBuf, nBuf-2) ){ | |||
| 25379 | *pnBuf = nBuf - 2; | |||
| 25380 | } | |||
| 25381 | } | |||
| 25382 | break; | |||
| 25383 | ||||
| 25384 | case 'i': | |||
| 25385 | if( nBuf>2 && 0==memcmp("ic", &aBuf[nBuf-2], 2) ){ | |||
| 25386 | if( fts5Porter_MGt1(aBuf, nBuf-2) ){ | |||
| 25387 | *pnBuf = nBuf - 2; | |||
| 25388 | } | |||
| 25389 | } | |||
| 25390 | break; | |||
| 25391 | ||||
| 25392 | case 'l': | |||
| 25393 | if( nBuf>4 && 0==memcmp("able", &aBuf[nBuf-4], 4) ){ | |||
| 25394 | if( fts5Porter_MGt1(aBuf, nBuf-4) ){ | |||
| 25395 | *pnBuf = nBuf - 4; | |||
| 25396 | } | |||
| 25397 | }else if( nBuf>4 && 0==memcmp("ible", &aBuf[nBuf-4], 4) ){ | |||
| 25398 | if( fts5Porter_MGt1(aBuf, nBuf-4) ){ | |||
| 25399 | *pnBuf = nBuf - 4; | |||
| 25400 | } | |||
| 25401 | } | |||
| 25402 | break; | |||
| 25403 | ||||
| 25404 | case 'n': | |||
| 25405 | if( nBuf>3 && 0==memcmp("ant", &aBuf[nBuf-3], 3) ){ | |||
| 25406 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25407 | *pnBuf = nBuf - 3; | |||
| 25408 | } | |||
| 25409 | }else if( nBuf>5 && 0==memcmp("ement", &aBuf[nBuf-5], 5) ){ | |||
| 25410 | if( fts5Porter_MGt1(aBuf, nBuf-5) ){ | |||
| 25411 | *pnBuf = nBuf - 5; | |||
| 25412 | } | |||
| 25413 | }else if( nBuf>4 && 0==memcmp("ment", &aBuf[nBuf-4], 4) ){ | |||
| 25414 | if( fts5Porter_MGt1(aBuf, nBuf-4) ){ | |||
| 25415 | *pnBuf = nBuf - 4; | |||
| 25416 | } | |||
| 25417 | }else if( nBuf>3 && 0==memcmp("ent", &aBuf[nBuf-3], 3) ){ | |||
| 25418 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25419 | *pnBuf = nBuf - 3; | |||
| 25420 | } | |||
| 25421 | } | |||
| 25422 | break; | |||
| 25423 | ||||
| 25424 | case 'o': | |||
| 25425 | if( nBuf>3 && 0==memcmp("ion", &aBuf[nBuf-3], 3) ){ | |||
| 25426 | if( fts5Porter_MGt1_and_S_or_T(aBuf, nBuf-3) ){ | |||
| 25427 | *pnBuf = nBuf - 3; | |||
| 25428 | } | |||
| 25429 | }else if( nBuf>2 && 0==memcmp("ou", &aBuf[nBuf-2], 2) ){ | |||
| 25430 | if( fts5Porter_MGt1(aBuf, nBuf-2) ){ | |||
| 25431 | *pnBuf = nBuf - 2; | |||
| 25432 | } | |||
| 25433 | } | |||
| 25434 | break; | |||
| 25435 | ||||
| 25436 | case 's': | |||
| 25437 | if( nBuf>3 && 0==memcmp("ism", &aBuf[nBuf-3], 3) ){ | |||
| 25438 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25439 | *pnBuf = nBuf - 3; | |||
| 25440 | } | |||
| 25441 | } | |||
| 25442 | break; | |||
| 25443 | ||||
| 25444 | case 't': | |||
| 25445 | if( nBuf>3 && 0==memcmp("ate", &aBuf[nBuf-3], 3) ){ | |||
| 25446 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25447 | *pnBuf = nBuf - 3; | |||
| 25448 | } | |||
| 25449 | }else if( nBuf>3 && 0==memcmp("iti", &aBuf[nBuf-3], 3) ){ | |||
| 25450 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25451 | *pnBuf = nBuf - 3; | |||
| 25452 | } | |||
| 25453 | } | |||
| 25454 | break; | |||
| 25455 | ||||
| 25456 | case 'u': | |||
| 25457 | if( nBuf>3 && 0==memcmp("ous", &aBuf[nBuf-3], 3) ){ | |||
| 25458 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25459 | *pnBuf = nBuf - 3; | |||
| 25460 | } | |||
| 25461 | } | |||
| 25462 | break; | |||
| 25463 | ||||
| 25464 | case 'v': | |||
| 25465 | if( nBuf>3 && 0==memcmp("ive", &aBuf[nBuf-3], 3) ){ | |||
| 25466 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25467 | *pnBuf = nBuf - 3; | |||
| 25468 | } | |||
| 25469 | } | |||
| 25470 | break; | |||
| 25471 | ||||
| 25472 | case 'z': | |||
| 25473 | if( nBuf>3 && 0==memcmp("ize", &aBuf[nBuf-3], 3) ){ | |||
| 25474 | if( fts5Porter_MGt1(aBuf, nBuf-3) ){ | |||
| 25475 | *pnBuf = nBuf - 3; | |||
| 25476 | } | |||
| 25477 | } | |||
| 25478 | break; | |||
| 25479 | ||||
| 25480 | } | |||
| 25481 | return ret; | |||
| 25482 | } | |||
| 25483 | ||||
| 25484 | ||||
| 25485 | static int fts5PorterStep1B2(char *aBuf, int *pnBuf){ | |||
| 25486 | int ret = 0; | |||
| 25487 | int nBuf = *pnBuf; | |||
| 25488 | switch( aBuf[nBuf-2] ){ | |||
| 25489 | ||||
| 25490 | case 'a': | |||
| 25491 | if( nBuf>2 && 0==memcmp("at", &aBuf[nBuf-2], 2) ){ | |||
| 25492 | memcpy(&aBuf[nBuf-2], "ate", 3); | |||
| 25493 | *pnBuf = nBuf - 2 + 3; | |||
| 25494 | ret = 1; | |||
| 25495 | } | |||
| 25496 | break; | |||
| 25497 | ||||
| 25498 | case 'b': | |||
| 25499 | if( nBuf>2 && 0==memcmp("bl", &aBuf[nBuf-2], 2) ){ | |||
| 25500 | memcpy(&aBuf[nBuf-2], "ble", 3); | |||
| 25501 | *pnBuf = nBuf - 2 + 3; | |||
| 25502 | ret = 1; | |||
| 25503 | } | |||
| 25504 | break; | |||
| 25505 | ||||
| 25506 | case 'i': | |||
| 25507 | if( nBuf>2 && 0==memcmp("iz", &aBuf[nBuf-2], 2) ){ | |||
| 25508 | memcpy(&aBuf[nBuf-2], "ize", 3); | |||
| 25509 | *pnBuf = nBuf - 2 + 3; | |||
| 25510 | ret = 1; | |||
| 25511 | } | |||
| 25512 | break; | |||
| 25513 | ||||
| 25514 | } | |||
| 25515 | return ret; | |||
| 25516 | } | |||
| 25517 | ||||
| 25518 | ||||
| 25519 | static int fts5PorterStep2(char *aBuf, int *pnBuf){ | |||
| 25520 | int ret = 0; | |||
| 25521 | int nBuf = *pnBuf; | |||
| 25522 | switch( aBuf[nBuf-2] ){ | |||
| 25523 | ||||
| 25524 | case 'a': | |||
| 25525 | if( nBuf>7 && 0==memcmp("ational", &aBuf[nBuf-7], 7) ){ | |||
| 25526 | if( fts5Porter_MGt0(aBuf, nBuf-7) ){ | |||
| 25527 | memcpy(&aBuf[nBuf-7], "ate", 3); | |||
| 25528 | *pnBuf = nBuf - 7 + 3; | |||
| 25529 | } | |||
| 25530 | }else if( nBuf>6 && 0==memcmp("tional", &aBuf[nBuf-6], 6) ){ | |||
| 25531 | if( fts5Porter_MGt0(aBuf, nBuf-6) ){ | |||
| 25532 | memcpy(&aBuf[nBuf-6], "tion", 4); | |||
| 25533 | *pnBuf = nBuf - 6 + 4; | |||
| 25534 | } | |||
| 25535 | } | |||
| 25536 | break; | |||
| 25537 | ||||
| 25538 | case 'c': | |||
| 25539 | if( nBuf>4 && 0==memcmp("enci", &aBuf[nBuf-4], 4) ){ | |||
| 25540 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25541 | memcpy(&aBuf[nBuf-4], "ence", 4); | |||
| 25542 | *pnBuf = nBuf - 4 + 4; | |||
| 25543 | } | |||
| 25544 | }else if( nBuf>4 && 0==memcmp("anci", &aBuf[nBuf-4], 4) ){ | |||
| 25545 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25546 | memcpy(&aBuf[nBuf-4], "ance", 4); | |||
| 25547 | *pnBuf = nBuf - 4 + 4; | |||
| 25548 | } | |||
| 25549 | } | |||
| 25550 | break; | |||
| 25551 | ||||
| 25552 | case 'e': | |||
| 25553 | if( nBuf>4 && 0==memcmp("izer", &aBuf[nBuf-4], 4) ){ | |||
| 25554 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25555 | memcpy(&aBuf[nBuf-4], "ize", 3); | |||
| 25556 | *pnBuf = nBuf - 4 + 3; | |||
| 25557 | } | |||
| 25558 | } | |||
| 25559 | break; | |||
| 25560 | ||||
| 25561 | case 'g': | |||
| 25562 | if( nBuf>4 && 0==memcmp("logi", &aBuf[nBuf-4], 4) ){ | |||
| 25563 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25564 | memcpy(&aBuf[nBuf-4], "log", 3); | |||
| 25565 | *pnBuf = nBuf - 4 + 3; | |||
| 25566 | } | |||
| 25567 | } | |||
| 25568 | break; | |||
| 25569 | ||||
| 25570 | case 'l': | |||
| 25571 | if( nBuf>3 && 0==memcmp("bli", &aBuf[nBuf-3], 3) ){ | |||
| 25572 | if( fts5Porter_MGt0(aBuf, nBuf-3) ){ | |||
| 25573 | memcpy(&aBuf[nBuf-3], "ble", 3); | |||
| 25574 | *pnBuf = nBuf - 3 + 3; | |||
| 25575 | } | |||
| 25576 | }else if( nBuf>4 && 0==memcmp("alli", &aBuf[nBuf-4], 4) ){ | |||
| 25577 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25578 | memcpy(&aBuf[nBuf-4], "al", 2); | |||
| 25579 | *pnBuf = nBuf - 4 + 2; | |||
| 25580 | } | |||
| 25581 | }else if( nBuf>5 && 0==memcmp("entli", &aBuf[nBuf-5], 5) ){ | |||
| 25582 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25583 | memcpy(&aBuf[nBuf-5], "ent", 3); | |||
| 25584 | *pnBuf = nBuf - 5 + 3; | |||
| 25585 | } | |||
| 25586 | }else if( nBuf>3 && 0==memcmp("eli", &aBuf[nBuf-3], 3) ){ | |||
| 25587 | if( fts5Porter_MGt0(aBuf, nBuf-3) ){ | |||
| 25588 | memcpy(&aBuf[nBuf-3], "e", 1); | |||
| 25589 | *pnBuf = nBuf - 3 + 1; | |||
| 25590 | } | |||
| 25591 | }else if( nBuf>5 && 0==memcmp("ousli", &aBuf[nBuf-5], 5) ){ | |||
| 25592 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25593 | memcpy(&aBuf[nBuf-5], "ous", 3); | |||
| 25594 | *pnBuf = nBuf - 5 + 3; | |||
| 25595 | } | |||
| 25596 | } | |||
| 25597 | break; | |||
| 25598 | ||||
| 25599 | case 'o': | |||
| 25600 | if( nBuf>7 && 0==memcmp("ization", &aBuf[nBuf-7], 7) ){ | |||
| 25601 | if( fts5Porter_MGt0(aBuf, nBuf-7) ){ | |||
| 25602 | memcpy(&aBuf[nBuf-7], "ize", 3); | |||
| 25603 | *pnBuf = nBuf - 7 + 3; | |||
| 25604 | } | |||
| 25605 | }else if( nBuf>5 && 0==memcmp("ation", &aBuf[nBuf-5], 5) ){ | |||
| 25606 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25607 | memcpy(&aBuf[nBuf-5], "ate", 3); | |||
| 25608 | *pnBuf = nBuf - 5 + 3; | |||
| 25609 | } | |||
| 25610 | }else if( nBuf>4 && 0==memcmp("ator", &aBuf[nBuf-4], 4) ){ | |||
| 25611 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25612 | memcpy(&aBuf[nBuf-4], "ate", 3); | |||
| 25613 | *pnBuf = nBuf - 4 + 3; | |||
| 25614 | } | |||
| 25615 | } | |||
| 25616 | break; | |||
| 25617 | ||||
| 25618 | case 's': | |||
| 25619 | if( nBuf>5 && 0==memcmp("alism", &aBuf[nBuf-5], 5) ){ | |||
| 25620 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25621 | memcpy(&aBuf[nBuf-5], "al", 2); | |||
| 25622 | *pnBuf = nBuf - 5 + 2; | |||
| 25623 | } | |||
| 25624 | }else if( nBuf>7 && 0==memcmp("iveness", &aBuf[nBuf-7], 7) ){ | |||
| 25625 | if( fts5Porter_MGt0(aBuf, nBuf-7) ){ | |||
| 25626 | memcpy(&aBuf[nBuf-7], "ive", 3); | |||
| 25627 | *pnBuf = nBuf - 7 + 3; | |||
| 25628 | } | |||
| 25629 | }else if( nBuf>7 && 0==memcmp("fulness", &aBuf[nBuf-7], 7) ){ | |||
| 25630 | if( fts5Porter_MGt0(aBuf, nBuf-7) ){ | |||
| 25631 | memcpy(&aBuf[nBuf-7], "ful", 3); | |||
| 25632 | *pnBuf = nBuf - 7 + 3; | |||
| 25633 | } | |||
| 25634 | }else if( nBuf>7 && 0==memcmp("ousness", &aBuf[nBuf-7], 7) ){ | |||
| 25635 | if( fts5Porter_MGt0(aBuf, nBuf-7) ){ | |||
| 25636 | memcpy(&aBuf[nBuf-7], "ous", 3); | |||
| 25637 | *pnBuf = nBuf - 7 + 3; | |||
| 25638 | } | |||
| 25639 | } | |||
| 25640 | break; | |||
| 25641 | ||||
| 25642 | case 't': | |||
| 25643 | if( nBuf>5 && 0==memcmp("aliti", &aBuf[nBuf-5], 5) ){ | |||
| 25644 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25645 | memcpy(&aBuf[nBuf-5], "al", 2); | |||
| 25646 | *pnBuf = nBuf - 5 + 2; | |||
| 25647 | } | |||
| 25648 | }else if( nBuf>5 && 0==memcmp("iviti", &aBuf[nBuf-5], 5) ){ | |||
| 25649 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25650 | memcpy(&aBuf[nBuf-5], "ive", 3); | |||
| 25651 | *pnBuf = nBuf - 5 + 3; | |||
| 25652 | } | |||
| 25653 | }else if( nBuf>6 && 0==memcmp("biliti", &aBuf[nBuf-6], 6) ){ | |||
| 25654 | if( fts5Porter_MGt0(aBuf, nBuf-6) ){ | |||
| 25655 | memcpy(&aBuf[nBuf-6], "ble", 3); | |||
| 25656 | *pnBuf = nBuf - 6 + 3; | |||
| 25657 | } | |||
| 25658 | } | |||
| 25659 | break; | |||
| 25660 | ||||
| 25661 | } | |||
| 25662 | return ret; | |||
| 25663 | } | |||
| 25664 | ||||
| 25665 | ||||
| 25666 | static int fts5PorterStep3(char *aBuf, int *pnBuf){ | |||
| 25667 | int ret = 0; | |||
| 25668 | int nBuf = *pnBuf; | |||
| 25669 | switch( aBuf[nBuf-2] ){ | |||
| 25670 | ||||
| 25671 | case 'a': | |||
| 25672 | if( nBuf>4 && 0==memcmp("ical", &aBuf[nBuf-4], 4) ){ | |||
| 25673 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25674 | memcpy(&aBuf[nBuf-4], "ic", 2); | |||
| 25675 | *pnBuf = nBuf - 4 + 2; | |||
| 25676 | } | |||
| 25677 | } | |||
| 25678 | break; | |||
| 25679 | ||||
| 25680 | case 's': | |||
| 25681 | if( nBuf>4 && 0==memcmp("ness", &aBuf[nBuf-4], 4) ){ | |||
| 25682 | if( fts5Porter_MGt0(aBuf, nBuf-4) ){ | |||
| 25683 | *pnBuf = nBuf - 4; | |||
| 25684 | } | |||
| 25685 | } | |||
| 25686 | break; | |||
| 25687 | ||||
| 25688 | case 't': | |||
| 25689 | if( nBuf>5 && 0==memcmp("icate", &aBuf[nBuf-5], 5) ){ | |||
| 25690 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25691 | memcpy(&aBuf[nBuf-5], "ic", 2); | |||
| 25692 | *pnBuf = nBuf - 5 + 2; | |||
| 25693 | } | |||
| 25694 | }else if( nBuf>5 && 0==memcmp("iciti", &aBuf[nBuf-5], 5) ){ | |||
| 25695 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25696 | memcpy(&aBuf[nBuf-5], "ic", 2); | |||
| 25697 | *pnBuf = nBuf - 5 + 2; | |||
| 25698 | } | |||
| 25699 | } | |||
| 25700 | break; | |||
| 25701 | ||||
| 25702 | case 'u': | |||
| 25703 | if( nBuf>3 && 0==memcmp("ful", &aBuf[nBuf-3], 3) ){ | |||
| 25704 | if( fts5Porter_MGt0(aBuf, nBuf-3) ){ | |||
| 25705 | *pnBuf = nBuf - 3; | |||
| 25706 | } | |||
| 25707 | } | |||
| 25708 | break; | |||
| 25709 | ||||
| 25710 | case 'v': | |||
| 25711 | if( nBuf>5 && 0==memcmp("ative", &aBuf[nBuf-5], 5) ){ | |||
| 25712 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25713 | *pnBuf = nBuf - 5; | |||
| 25714 | } | |||
| 25715 | } | |||
| 25716 | break; | |||
| 25717 | ||||
| 25718 | case 'z': | |||
| 25719 | if( nBuf>5 && 0==memcmp("alize", &aBuf[nBuf-5], 5) ){ | |||
| 25720 | if( fts5Porter_MGt0(aBuf, nBuf-5) ){ | |||
| 25721 | memcpy(&aBuf[nBuf-5], "al", 2); | |||
| 25722 | *pnBuf = nBuf - 5 + 2; | |||
| 25723 | } | |||
| 25724 | } | |||
| 25725 | break; | |||
| 25726 | ||||
| 25727 | } | |||
| 25728 | return ret; | |||
| 25729 | } | |||
| 25730 | ||||
| 25731 | ||||
| 25732 | static int fts5PorterStep1B(char *aBuf, int *pnBuf){ | |||
| 25733 | int ret = 0; | |||
| 25734 | int nBuf = *pnBuf; | |||
| 25735 | switch( aBuf[nBuf-2] ){ | |||
| 25736 | ||||
| 25737 | case 'e': | |||
| 25738 | if( nBuf>3 && 0==memcmp("eed", &aBuf[nBuf-3], 3) ){ | |||
| 25739 | if( fts5Porter_MGt0(aBuf, nBuf-3) ){ | |||
| 25740 | memcpy(&aBuf[nBuf-3], "ee", 2); | |||
| 25741 | *pnBuf = nBuf - 3 + 2; | |||
| 25742 | } | |||
| 25743 | }else if( nBuf>2 && 0==memcmp("ed", &aBuf[nBuf-2], 2) ){ | |||
| 25744 | if( fts5Porter_Vowel(aBuf, nBuf-2) ){ | |||
| 25745 | *pnBuf = nBuf - 2; | |||
| 25746 | ret = 1; | |||
| 25747 | } | |||
| 25748 | } | |||
| 25749 | break; | |||
| 25750 | ||||
| 25751 | case 'n': | |||
| 25752 | if( nBuf>3 && 0==memcmp("ing", &aBuf[nBuf-3], 3) ){ | |||
| 25753 | if( fts5Porter_Vowel(aBuf, nBuf-3) ){ | |||
| 25754 | *pnBuf = nBuf - 3; | |||
| 25755 | ret = 1; | |||
| 25756 | } | |||
| 25757 | } | |||
| 25758 | break; | |||
| 25759 | ||||
| 25760 | } | |||
| 25761 | return ret; | |||
| 25762 | } | |||
| 25763 | ||||
| 25764 | /* | |||
| 25765 | ** GENERATED CODE ENDS HERE (mkportersteps.tcl) | |||
| 25766 | *************************************************************************** | |||
| 25767 | **************************************************************************/ | |||
| 25768 | ||||
| 25769 | static void fts5PorterStep1A(char *aBuf, int *pnBuf){ | |||
| 25770 | int nBuf = *pnBuf; | |||
| 25771 | if( aBuf[nBuf-1]=='s' ){ | |||
| 25772 | if( aBuf[nBuf-2]=='e' ){ | |||
| 25773 | if( (nBuf>4 && aBuf[nBuf-4]=='s' && aBuf[nBuf-3]=='s') | |||
| 25774 | || (nBuf>3 && aBuf[nBuf-3]=='i' ) | |||
| 25775 | ){ | |||
| 25776 | *pnBuf = nBuf-2; | |||
| 25777 | }else{ | |||
| 25778 | *pnBuf = nBuf-1; | |||
| 25779 | } | |||
| 25780 | } | |||
| 25781 | else if( aBuf[nBuf-2]!='s' ){ | |||
| 25782 | *pnBuf = nBuf-1; | |||
| 25783 | } | |||
| 25784 | } | |||
| 25785 | } | |||
| 25786 | ||||
| 25787 | static int fts5PorterCb( | |||
| 25788 | void *pCtx, | |||
| 25789 | int tflags, | |||
| 25790 | const char *pToken, | |||
| 25791 | int nToken, | |||
| 25792 | int iStart, | |||
| 25793 | int iEnd | |||
| 25794 | ){ | |||
| 25795 | PorterContext *p = (PorterContext*)pCtx; | |||
| 25796 | ||||
| 25797 | char *aBuf; | |||
| 25798 | int nBuf; | |||
| 25799 | ||||
| 25800 | if( nToken>FTS5_PORTER_MAX_TOKEN64 || nToken<3 ) goto pass_through; | |||
| 25801 | aBuf = p->aBuf; | |||
| 25802 | nBuf = nToken; | |||
| 25803 | memcpy(aBuf, pToken, nBuf); | |||
| 25804 | ||||
| 25805 | /* Step 1. */ | |||
| 25806 | fts5PorterStep1A(aBuf, &nBuf); | |||
| 25807 | if( fts5PorterStep1B(aBuf, &nBuf) ){ | |||
| 25808 | if( fts5PorterStep1B2(aBuf, &nBuf)==0 ){ | |||
| 25809 | char c = aBuf[nBuf-1]; | |||
| 25810 | if( fts5PorterIsVowel(c, 0)==0 | |||
| 25811 | && c!='l' && c!='s' && c!='z' && c==aBuf[nBuf-2] | |||
| 25812 | ){ | |||
| 25813 | nBuf--; | |||
| 25814 | }else if( fts5Porter_MEq1(aBuf, nBuf) && fts5Porter_Ostar(aBuf, nBuf) ){ | |||
| 25815 | aBuf[nBuf++] = 'e'; | |||
| 25816 | } | |||
| 25817 | } | |||
| 25818 | } | |||
| 25819 | ||||
| 25820 | /* Step 1C. */ | |||
| 25821 | if( aBuf[nBuf-1]=='y' && fts5Porter_Vowel(aBuf, nBuf-1) ){ | |||
| 25822 | aBuf[nBuf-1] = 'i'; | |||
| 25823 | } | |||
| 25824 | ||||
| 25825 | /* Steps 2 through 4. */ | |||
| 25826 | fts5PorterStep2(aBuf, &nBuf); | |||
| 25827 | fts5PorterStep3(aBuf, &nBuf); | |||
| 25828 | fts5PorterStep4(aBuf, &nBuf); | |||
| 25829 | ||||
| 25830 | /* Step 5a. */ | |||
| 25831 | assert( nBuf>0 )((void) (0)); | |||
| 25832 | if( aBuf[nBuf-1]=='e' ){ | |||
| 25833 | if( fts5Porter_MGt1(aBuf, nBuf-1) | |||
| 25834 | || (fts5Porter_MEq1(aBuf, nBuf-1) && !fts5Porter_Ostar(aBuf, nBuf-1)) | |||
| 25835 | ){ | |||
| 25836 | nBuf--; | |||
| 25837 | } | |||
| 25838 | } | |||
| 25839 | ||||
| 25840 | /* Step 5b. */ | |||
| 25841 | if( nBuf>1 && aBuf[nBuf-1]=='l' | |||
| 25842 | && aBuf[nBuf-2]=='l' && fts5Porter_MGt1(aBuf, nBuf-1) | |||
| 25843 | ){ | |||
| 25844 | nBuf--; | |||
| 25845 | } | |||
| 25846 | ||||
| 25847 | return p->xToken(p->pCtx, tflags, aBuf, nBuf, iStart, iEnd); | |||
| 25848 | ||||
| 25849 | pass_through: | |||
| 25850 | return p->xToken(p->pCtx, tflags, pToken, nToken, iStart, iEnd); | |||
| 25851 | } | |||
| 25852 | ||||
| 25853 | /* | |||
| 25854 | ** Tokenize using the porter tokenizer. | |||
| 25855 | */ | |||
| 25856 | static int fts5PorterTokenize( | |||
| 25857 | Fts5Tokenizer *pTokenizer, | |||
| 25858 | void *pCtx, | |||
| 25859 | int flags, | |||
| 25860 | const char *pText, int nText, | |||
| 25861 | const char *pLoc, int nLoc, | |||
| 25862 | int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) | |||
| 25863 | ){ | |||
| 25864 | PorterTokenizer *p = (PorterTokenizer*)pTokenizer; | |||
| 25865 | PorterContext sCtx; | |||
| 25866 | sCtx.xToken = xToken; | |||
| 25867 | sCtx.pCtx = pCtx; | |||
| 25868 | sCtx.aBuf = p->aBuf; | |||
| 25869 | return p->tokenizer_v2.xTokenize( | |||
| 25870 | p->pTokenizer, (void*)&sCtx, flags, pText, nText, pLoc, nLoc, fts5PorterCb | |||
| 25871 | ); | |||
| 25872 | } | |||
| 25873 | ||||
| 25874 | /************************************************************************** | |||
| 25875 | ** Start of trigram implementation. | |||
| 25876 | */ | |||
| 25877 | typedef struct TrigramTokenizer TrigramTokenizer; | |||
| 25878 | struct TrigramTokenizer { | |||
| 25879 | int bFold; /* True to fold to lower-case */ | |||
| 25880 | int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */ | |||
| 25881 | }; | |||
| 25882 | ||||
| 25883 | /* | |||
| 25884 | ** Free a trigram tokenizer. | |||
| 25885 | */ | |||
| 25886 | static void fts5TriDelete(Fts5Tokenizer *p){ | |||
| 25887 | sqlite3_freesqlite3_api->free(p); | |||
| 25888 | } | |||
| 25889 | ||||
| 25890 | /* | |||
| 25891 | ** Allocate a trigram tokenizer. | |||
| 25892 | */ | |||
| 25893 | static int fts5TriCreate( | |||
| 25894 | void *pUnused, | |||
| 25895 | const char **azArg, | |||
| 25896 | int nArg, | |||
| 25897 | Fts5Tokenizer **ppOut | |||
| 25898 | ){ | |||
| 25899 | int rc = SQLITE_OK0; | |||
| 25900 | TrigramTokenizer *pNew = 0; | |||
| 25901 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 25902 | if( nArg%2 ){ | |||
| 25903 | rc = SQLITE_ERROR1; | |||
| 25904 | }else{ | |||
| 25905 | int i; | |||
| 25906 | pNew = (TrigramTokenizer*)sqlite3_malloc64sqlite3_api->malloc64(sizeof(*pNew)); | |||
| 25907 | if( pNew==0 ){ | |||
| 25908 | rc = SQLITE_NOMEM7; | |||
| 25909 | }else{ | |||
| 25910 | pNew->bFold = 1; | |||
| 25911 | pNew->iFoldParam = 0; | |||
| 25912 | ||||
| 25913 | for(i=0; rc==SQLITE_OK0 && i<nArg; i+=2){ | |||
| 25914 | const char *zArg = azArg[i+1]; | |||
| 25915 | if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "case_sensitive") ){ | |||
| 25916 | if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){ | |||
| 25917 | rc = SQLITE_ERROR1; | |||
| 25918 | }else{ | |||
| 25919 | pNew->bFold = (zArg[0]=='0'); | |||
| 25920 | } | |||
| 25921 | }else if( 0==sqlite3_stricmpsqlite3_api->stricmp(azArg[i], "remove_diacritics") ){ | |||
| 25922 | if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ | |||
| 25923 | rc = SQLITE_ERROR1; | |||
| 25924 | }else{ | |||
| 25925 | pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; | |||
| 25926 | } | |||
| 25927 | }else{ | |||
| 25928 | rc = SQLITE_ERROR1; | |||
| 25929 | } | |||
| 25930 | } | |||
| 25931 | ||||
| 25932 | if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ | |||
| 25933 | rc = SQLITE_ERROR1; | |||
| 25934 | } | |||
| 25935 | ||||
| 25936 | if( rc!=SQLITE_OK0 ){ | |||
| 25937 | fts5TriDelete((Fts5Tokenizer*)pNew); | |||
| 25938 | pNew = 0; | |||
| 25939 | } | |||
| 25940 | } | |||
| 25941 | } | |||
| 25942 | *ppOut = (Fts5Tokenizer*)pNew; | |||
| 25943 | return rc; | |||
| 25944 | } | |||
| 25945 | ||||
| 25946 | /* | |||
| 25947 | ** Trigram tokenizer tokenize routine. | |||
| 25948 | */ | |||
| 25949 | static int fts5TriTokenize( | |||
| 25950 | Fts5Tokenizer *pTok, | |||
| 25951 | void *pCtx, | |||
| 25952 | int unusedFlags, | |||
| 25953 | const char *pText, int nText, | |||
| 25954 | int (*xToken)(void*, int, const char*, int, int, int) | |||
| 25955 | ){ | |||
| 25956 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; | |||
| 25957 | int rc = SQLITE_OK0; | |||
| 25958 | char aBuf[32]; | |||
| 25959 | char *zOut = aBuf; | |||
| 25960 | int ii; | |||
| 25961 | const unsigned char *zIn = (const unsigned char*)pText; | |||
| 25962 | const unsigned char *zEof = (zIn ? &zIn[nText] : 0); | |||
| 25963 | u32 iCode = 0; | |||
| 25964 | int aStart[3]; /* Input offset of each character in aBuf[] */ | |||
| 25965 | ||||
| 25966 | UNUSED_PARAM(unusedFlags)(void)(unusedFlags); | |||
| 25967 | ||||
| 25968 | /* Populate aBuf[] with the characters for the first trigram. */ | |||
| 25969 | for(ii=0; ii<3; ii++){ | |||
| 25970 | do { | |||
| 25971 | aStart[ii] = zIn - (const unsigned char*)pText; | |||
| 25972 | if( zIn>=zEof ) return SQLITE_OK0; | |||
| 25973 | READ_UTF8(zIn, zEof, iCode)iCode = *(zIn++); if( iCode>=0xc0 ){ iCode = sqlite3Utf8Trans1 [iCode-0xc0]; while( zIn<zEof && (*zIn & 0xc0) ==0x80 ){ iCode = (iCode<<6) + (0x3f & *(zIn++)); } if( iCode<0x80 || (iCode&0xFFFFF800)==0xD800 || (iCode &0xFFFFFFFE)==0xFFFE ){ iCode = 0xFFFD; } }; | |||
| 25974 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); | |||
| 25975 | }while( iCode==0 ); | |||
| 25976 | WRITE_UTF8(zOut, iCode){ if( iCode<0x00080 ){ *zOut++ = (unsigned char)(iCode& 0xFF); } else if( iCode<0x00800 ){ *zOut++ = 0xC0 + (unsigned char)((iCode>>6)&0x1F); *zOut++ = 0x80 + (unsigned char)(iCode & 0x3F); } else if( iCode<0x10000 ){ *zOut ++ = 0xE0 + (unsigned char)((iCode>>12)&0x0F); *zOut ++ = 0x80 + (unsigned char)((iCode>>6) & 0x3F); *zOut ++ = 0x80 + (unsigned char)(iCode & 0x3F); }else{ *zOut++ = 0xF0 + (unsigned char)((iCode>>18) & 0x07); *zOut ++ = 0x80 + (unsigned char)((iCode>>12) & 0x3F); *zOut ++ = 0x80 + (unsigned char)((iCode>>6) & 0x3F); *zOut ++ = 0x80 + (unsigned char)(iCode & 0x3F); } }; | |||
| 25977 | } | |||
| 25978 | ||||
| 25979 | /* At the start of each iteration of this loop: | |||
| 25980 | ** | |||
| 25981 | ** aBuf: Contains 3 characters. The 3 characters of the next trigram. | |||
| 25982 | ** zOut: Points to the byte following the last character in aBuf. | |||
| 25983 | ** aStart[3]: Contains the byte offset in the input text corresponding | |||
| 25984 | ** to the start of each of the three characters in the buffer. | |||
| 25985 | */ | |||
| 25986 | assert( zIn<=zEof )((void) (0)); | |||
| 25987 | while( 1 ){ | |||
| 25988 | int iNext; /* Start of character following current tri */ | |||
| 25989 | const char *z1; | |||
| 25990 | ||||
| 25991 | /* Read characters from the input up until the first non-diacritic */ | |||
| 25992 | do { | |||
| 25993 | iNext = zIn - (const unsigned char*)pText; | |||
| 25994 | if( zIn>=zEof ){ | |||
| 25995 | iCode = 0; | |||
| 25996 | break; | |||
| 25997 | } | |||
| 25998 | READ_UTF8(zIn, zEof, iCode)iCode = *(zIn++); if( iCode>=0xc0 ){ iCode = sqlite3Utf8Trans1 [iCode-0xc0]; while( zIn<zEof && (*zIn & 0xc0) ==0x80 ){ iCode = (iCode<<6) + (0x3f & *(zIn++)); } if( iCode<0x80 || (iCode&0xFFFFF800)==0xD800 || (iCode &0xFFFFFFFE)==0xFFFE ){ iCode = 0xFFFD; } }; | |||
| 25999 | if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); | |||
| 26000 | }while( iCode==0 ); | |||
| 26001 | ||||
| 26002 | /* Pass the current trigram back to fts5 */ | |||
| 26003 | rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); | |||
| 26004 | if( iCode==0 || rc!=SQLITE_OK0 ) break; | |||
| 26005 | ||||
| 26006 | /* Remove the first character from buffer aBuf[]. Append the character | |||
| 26007 | ** with codepoint iCode. */ | |||
| 26008 | z1 = aBuf; | |||
| 26009 | FTS5_SKIP_UTF8(z1){ if( ((unsigned char)(*(z1++)))>=0xc0 ){ while( (((unsigned char)*z1) & 0xc0)==0x80 ){ z1++; } } }; | |||
| 26010 | memmove(aBuf, z1, zOut - z1); | |||
| 26011 | zOut -= (z1 - aBuf); | |||
| 26012 | WRITE_UTF8(zOut, iCode){ if( iCode<0x00080 ){ *zOut++ = (unsigned char)(iCode& 0xFF); } else if( iCode<0x00800 ){ *zOut++ = 0xC0 + (unsigned char)((iCode>>6)&0x1F); *zOut++ = 0x80 + (unsigned char)(iCode & 0x3F); } else if( iCode<0x10000 ){ *zOut ++ = 0xE0 + (unsigned char)((iCode>>12)&0x0F); *zOut ++ = 0x80 + (unsigned char)((iCode>>6) & 0x3F); *zOut ++ = 0x80 + (unsigned char)(iCode & 0x3F); }else{ *zOut++ = 0xF0 + (unsigned char)((iCode>>18) & 0x07); *zOut ++ = 0x80 + (unsigned char)((iCode>>12) & 0x3F); *zOut ++ = 0x80 + (unsigned char)((iCode>>6) & 0x3F); *zOut ++ = 0x80 + (unsigned char)(iCode & 0x3F); } }; | |||
| 26013 | ||||
| 26014 | /* Update the aStart[] array */ | |||
| 26015 | aStart[0] = aStart[1]; | |||
| 26016 | aStart[1] = aStart[2]; | |||
| 26017 | aStart[2] = iNext; | |||
| 26018 | } | |||
| 26019 | ||||
| 26020 | return rc; | |||
| 26021 | } | |||
| 26022 | ||||
| 26023 | /* | |||
| 26024 | ** Argument xCreate is a pointer to a constructor function for a tokenizer. | |||
| 26025 | ** pTok is a tokenizer previously created using the same method. This function | |||
| 26026 | ** returns one of FTS5_PATTERN_NONE, FTS5_PATTERN_LIKE or FTS5_PATTERN_GLOB | |||
| 26027 | ** indicating the style of pattern matching that the tokenizer can support. | |||
| 26028 | ** In practice, this is: | |||
| 26029 | ** | |||
| 26030 | ** "trigram" tokenizer, case_sensitive=1 - FTS5_PATTERN_GLOB | |||
| 26031 | ** "trigram" tokenizer, case_sensitive=0 (the default) - FTS5_PATTERN_LIKE | |||
| 26032 | ** all other tokenizers - FTS5_PATTERN_NONE | |||
| 26033 | */ | |||
| 26034 | static int sqlite3Fts5TokenizerPattern( | |||
| 26035 | int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), | |||
| 26036 | Fts5Tokenizer *pTok | |||
| 26037 | ){ | |||
| 26038 | if( xCreate==fts5TriCreate ){ | |||
| 26039 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; | |||
| 26040 | if( p->iFoldParam==0 ){ | |||
| 26041 | return p->bFold ? FTS5_PATTERN_LIKE65 : FTS5_PATTERN_GLOB66; | |||
| 26042 | } | |||
| 26043 | } | |||
| 26044 | return FTS5_PATTERN_NONE0; | |||
| 26045 | } | |||
| 26046 | ||||
| 26047 | /* | |||
| 26048 | ** Return true if the tokenizer described by p->azArg[] is the trigram | |||
| 26049 | ** tokenizer. This tokenizer needs to be loaded before xBestIndex is | |||
| 26050 | ** called for the first time in order to correctly handle LIKE/GLOB. | |||
| 26051 | */ | |||
| 26052 | static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig *p){ | |||
| 26053 | return (p->nArg>=1 && 0==sqlite3_stricmpsqlite3_api->stricmp(p->azArg[0], "trigram")); | |||
| 26054 | } | |||
| 26055 | ||||
| 26056 | ||||
| 26057 | /* | |||
| 26058 | ** Register all built-in tokenizers with FTS5. | |||
| 26059 | */ | |||
| 26060 | static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ | |||
| 26061 | struct BuiltinTokenizer { | |||
| 26062 | const char *zName; | |||
| 26063 | fts5_tokenizer x; | |||
| 26064 | } aBuiltin[] = { | |||
| 26065 | { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, | |||
| 26066 | { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, | |||
| 26067 | { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}}, | |||
| 26068 | }; | |||
| 26069 | ||||
| 26070 | int rc = SQLITE_OK0; /* Return code */ | |||
| 26071 | int i; /* To iterate through builtin functions */ | |||
| 26072 | ||||
| 26073 | for(i=0; rc==SQLITE_OK0 && i<ArraySize(aBuiltin)((int)(sizeof(aBuiltin) / sizeof(aBuiltin[0]))); i++){ | |||
| 26074 | rc = pApi->xCreateTokenizer(pApi, | |||
| 26075 | aBuiltin[i].zName, | |||
| 26076 | (void*)pApi, | |||
| 26077 | &aBuiltin[i].x, | |||
| 26078 | 0 | |||
| 26079 | ); | |||
| 26080 | } | |||
| 26081 | if( rc==SQLITE_OK0 ){ | |||
| 26082 | fts5_tokenizer_v2 sPorter = { | |||
| 26083 | 2, | |||
| 26084 | fts5PorterCreate, | |||
| 26085 | fts5PorterDelete, | |||
| 26086 | fts5PorterTokenize | |||
| 26087 | }; | |||
| 26088 | rc = pApi->xCreateTokenizer_v2(pApi, | |||
| 26089 | "porter", | |||
| 26090 | (void*)pApi, | |||
| 26091 | &sPorter, | |||
| 26092 | 0 | |||
| 26093 | ); | |||
| 26094 | } | |||
| 26095 | return rc; | |||
| 26096 | } | |||
| 26097 | ||||
| 26098 | #line 1 "fts5_unicode2.c" | |||
| 26099 | /* | |||
| 26100 | ** 2012-05-25 | |||
| 26101 | ** | |||
| 26102 | ** The author disclaims copyright to this source code. In place of | |||
| 26103 | ** a legal notice, here is a blessing: | |||
| 26104 | ** | |||
| 26105 | ** May you do good and not evil. | |||
| 26106 | ** May you find forgiveness for yourself and forgive others. | |||
| 26107 | ** May you share freely, never taking more than you give. | |||
| 26108 | ** | |||
| 26109 | ****************************************************************************** | |||
| 26110 | */ | |||
| 26111 | ||||
| 26112 | /* | |||
| 26113 | ** DO NOT EDIT THIS MACHINE GENERATED FILE. | |||
| 26114 | */ | |||
| 26115 | ||||
| 26116 | ||||
| 26117 | #include <assert.h> | |||
| 26118 | ||||
| 26119 | ||||
| 26120 | ||||
| 26121 | /* | |||
| 26122 | ** If the argument is a codepoint corresponding to a lowercase letter | |||
| 26123 | ** in the ASCII range with a diacritic added, return the codepoint | |||
| 26124 | ** of the ASCII letter only. For example, if passed 235 - "LATIN | |||
| 26125 | ** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER | |||
| 26126 | ** E"). The resuls of passing a codepoint that corresponds to an | |||
| 26127 | ** uppercase letter are undefined. | |||
| 26128 | */ | |||
| 26129 | static int fts5_remove_diacritic(int c, int bComplex){ | |||
| 26130 | unsigned short aDia[] = { | |||
| 26131 | 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, | |||
| 26132 | 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, | |||
| 26133 | 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, | |||
| 26134 | 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, | |||
| 26135 | 3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896, | |||
| 26136 | 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106, | |||
| 26137 | 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344, | |||
| 26138 | 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198, | |||
| 26139 | 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468, | |||
| 26140 | 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704, | |||
| 26141 | 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914, | |||
| 26142 | 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218, | |||
| 26143 | 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554, | |||
| 26144 | 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766, | |||
| 26145 | 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118, | |||
| 26146 | 63182, 63242, 63274, 63310, 63368, 63390, | |||
| 26147 | }; | |||
| 26148 | #define HIBIT((unsigned char)0x80) ((unsigned char)0x80) | |||
| 26149 | unsigned char aChar[] = { | |||
| 26150 | '\0', 'a', 'c', 'e', 'i', 'n', | |||
| 26151 | 'o', 'u', 'y', 'y', 'a', 'c', | |||
| 26152 | 'd', 'e', 'e', 'g', 'h', 'i', | |||
| 26153 | 'j', 'k', 'l', 'n', 'o', 'r', | |||
| 26154 | 's', 't', 'u', 'u', 'w', 'y', | |||
| 26155 | 'z', 'o', 'u', 'a', 'i', 'o', | |||
| 26156 | 'u', 'u'|HIBIT((unsigned char)0x80), 'a'|HIBIT((unsigned char)0x80), 'g', 'k', 'o', | |||
| 26157 | 'o'|HIBIT((unsigned char)0x80), 'j', 'g', 'n', 'a'|HIBIT((unsigned char)0x80), 'a', | |||
| 26158 | 'e', 'i', 'o', 'r', 'u', 's', | |||
| 26159 | 't', 'h', 'a', 'e', 'o'|HIBIT((unsigned char)0x80), 'o', | |||
| 26160 | 'o'|HIBIT((unsigned char)0x80), 'y', '\0', '\0', '\0', '\0', | |||
| 26161 | '\0', '\0', '\0', '\0', 'a', 'b', | |||
| 26162 | 'c'|HIBIT((unsigned char)0x80), 'd', 'd', 'e'|HIBIT((unsigned char)0x80), 'e', 'e'|HIBIT((unsigned char)0x80), | |||
| 26163 | 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT((unsigned char)0x80), | |||
| 26164 | 'k', 'l', 'l'|HIBIT((unsigned char)0x80), 'l', 'm', 'n', | |||
| 26165 | 'o'|HIBIT((unsigned char)0x80), 'p', 'r', 'r'|HIBIT((unsigned char)0x80), 'r', 's', | |||
| 26166 | 's'|HIBIT((unsigned char)0x80), 't', 'u', 'u'|HIBIT((unsigned char)0x80), 'v', 'w', | |||
| 26167 | 'w', 'x', 'y', 'z', 'h', 't', | |||
| 26168 | 'w', 'y', 'a', 'a'|HIBIT((unsigned char)0x80), 'a'|HIBIT((unsigned char)0x80), 'a'|HIBIT((unsigned char)0x80), | |||
| 26169 | 'e', 'e'|HIBIT((unsigned char)0x80), 'e'|HIBIT((unsigned char)0x80), 'i', 'o', 'o'|HIBIT((unsigned char)0x80), | |||
| 26170 | 'o'|HIBIT((unsigned char)0x80), 'o'|HIBIT((unsigned char)0x80), 'u', 'u'|HIBIT((unsigned char)0x80), 'u'|HIBIT((unsigned char)0x80), 'y', | |||
| 26171 | }; | |||
| 26172 | ||||
| 26173 | unsigned int key = (((unsigned int)c)<<3) | 0x00000007; | |||
| 26174 | int iRes = 0; | |||
| 26175 | int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1; | |||
| 26176 | int iLo = 0; | |||
| 26177 | while( iHi>=iLo ){ | |||
| 26178 | int iTest = (iHi + iLo) / 2; | |||
| 26179 | if( key >= aDia[iTest] ){ | |||
| 26180 | iRes = iTest; | |||
| 26181 | iLo = iTest+1; | |||
| 26182 | }else{ | |||
| 26183 | iHi = iTest-1; | |||
| 26184 | } | |||
| 26185 | } | |||
| 26186 | assert( key>=aDia[iRes] )((void) (0)); | |||
| 26187 | if( bComplex==0 && (aChar[iRes] & 0x80) ) return c; | |||
| 26188 | return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F); | |||
| 26189 | } | |||
| 26190 | ||||
| 26191 | ||||
| 26192 | /* | |||
| 26193 | ** Return true if the argument interpreted as a unicode codepoint | |||
| 26194 | ** is a diacritical modifier character. | |||
| 26195 | */ | |||
| 26196 | static int sqlite3Fts5UnicodeIsdiacritic(int c){ | |||
| 26197 | unsigned int mask0 = 0x08029FDF; | |||
| 26198 | unsigned int mask1 = 0x000361F8; | |||
| 26199 | if( c<768 || c>817 ) return 0; | |||
| 26200 | return (c < 768+32) ? | |||
| 26201 | (mask0 & ((unsigned int)1 << (c-768))) : | |||
| 26202 | (mask1 & ((unsigned int)1 << (c-768-32))); | |||
| 26203 | } | |||
| 26204 | ||||
| 26205 | ||||
| 26206 | /* | |||
| 26207 | ** Interpret the argument as a unicode codepoint. If the codepoint | |||
| 26208 | ** is an upper case character that has a lower case equivalent, | |||
| 26209 | ** return the codepoint corresponding to the lower case version. | |||
| 26210 | ** Otherwise, return a copy of the argument. | |||
| 26211 | ** | |||
| 26212 | ** The results are undefined if the value passed to this function | |||
| 26213 | ** is less than zero. | |||
| 26214 | */ | |||
| 26215 | static int sqlite3Fts5UnicodeFold(int c, int eRemoveDiacritic){ | |||
| 26216 | /* Each entry in the following array defines a rule for folding a range | |||
| 26217 | ** of codepoints to lower case. The rule applies to a range of nRange | |||
| 26218 | ** codepoints starting at codepoint iCode. | |||
| 26219 | ** | |||
| 26220 | ** If the least significant bit in flags is clear, then the rule applies | |||
| 26221 | ** to all nRange codepoints (i.e. all nRange codepoints are upper case and | |||
| 26222 | ** need to be folded). Or, if it is set, then the rule only applies to | |||
| 26223 | ** every second codepoint in the range, starting with codepoint C. | |||
| 26224 | ** | |||
| 26225 | ** The 7 most significant bits in flags are an index into the aiOff[] | |||
| 26226 | ** array. If a specific codepoint C does require folding, then its lower | |||
| 26227 | ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF). | |||
| 26228 | ** | |||
| 26229 | ** The contents of this array are generated by parsing the CaseFolding.txt | |||
| 26230 | ** file distributed as part of the "Unicode Character Database". See | |||
| 26231 | ** http://www.unicode.org for details. | |||
| 26232 | */ | |||
| 26233 | static const struct TableEntry { | |||
| 26234 | unsigned short iCode; | |||
| 26235 | unsigned char flags; | |||
| 26236 | unsigned char nRange; | |||
| 26237 | } aEntry[] = { | |||
| 26238 | {65, 14, 26}, {181, 64, 1}, {192, 14, 23}, | |||
| 26239 | {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, | |||
| 26240 | {313, 1, 16}, {330, 1, 46}, {376, 116, 1}, | |||
| 26241 | {377, 1, 6}, {383, 104, 1}, {385, 50, 1}, | |||
| 26242 | {386, 1, 4}, {390, 44, 1}, {391, 0, 1}, | |||
| 26243 | {393, 42, 2}, {395, 0, 1}, {398, 32, 1}, | |||
| 26244 | {399, 38, 1}, {400, 40, 1}, {401, 0, 1}, | |||
| 26245 | {403, 42, 1}, {404, 46, 1}, {406, 52, 1}, | |||
| 26246 | {407, 48, 1}, {408, 0, 1}, {412, 52, 1}, | |||
| 26247 | {413, 54, 1}, {415, 56, 1}, {416, 1, 6}, | |||
| 26248 | {422, 60, 1}, {423, 0, 1}, {425, 60, 1}, | |||
| 26249 | {428, 0, 1}, {430, 60, 1}, {431, 0, 1}, | |||
| 26250 | {433, 58, 2}, {435, 1, 4}, {439, 62, 1}, | |||
| 26251 | {440, 0, 1}, {444, 0, 1}, {452, 2, 1}, | |||
| 26252 | {453, 0, 1}, {455, 2, 1}, {456, 0, 1}, | |||
| 26253 | {458, 2, 1}, {459, 1, 18}, {478, 1, 18}, | |||
| 26254 | {497, 2, 1}, {498, 1, 4}, {502, 122, 1}, | |||
| 26255 | {503, 134, 1}, {504, 1, 40}, {544, 110, 1}, | |||
| 26256 | {546, 1, 18}, {570, 70, 1}, {571, 0, 1}, | |||
| 26257 | {573, 108, 1}, {574, 68, 1}, {577, 0, 1}, | |||
| 26258 | {579, 106, 1}, {580, 28, 1}, {581, 30, 1}, | |||
| 26259 | {582, 1, 10}, {837, 36, 1}, {880, 1, 4}, | |||
| 26260 | {886, 0, 1}, {902, 18, 1}, {904, 16, 3}, | |||
| 26261 | {908, 26, 1}, {910, 24, 2}, {913, 14, 17}, | |||
| 26262 | {931, 14, 9}, {962, 0, 1}, {975, 4, 1}, | |||
| 26263 | {976, 140, 1}, {977, 142, 1}, {981, 146, 1}, | |||
| 26264 | {982, 144, 1}, {984, 1, 24}, {1008, 136, 1}, | |||
| 26265 | {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1}, | |||
| 26266 | {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1}, | |||
| 26267 | {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32}, | |||
| 26268 | {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1}, | |||
| 26269 | {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38}, | |||
| 26270 | {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1}, | |||
| 26271 | {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1}, | |||
| 26272 | {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6}, | |||
| 26273 | {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6}, | |||
| 26274 | {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8}, | |||
| 26275 | {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2}, | |||
| 26276 | {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1}, | |||
| 26277 | {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2}, | |||
| 26278 | {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2}, | |||
| 26279 | {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2}, | |||
| 26280 | {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1}, | |||
| 26281 | {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16}, | |||
| 26282 | {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47}, | |||
| 26283 | {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1}, | |||
| 26284 | {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1}, | |||
| 26285 | {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1}, | |||
| 26286 | {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2}, | |||
| 26287 | {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1}, | |||
| 26288 | {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14}, | |||
| 26289 | {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1}, | |||
| 26290 | {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1}, | |||
| 26291 | {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1}, | |||
| 26292 | {65313, 14, 26}, | |||
| 26293 | }; | |||
| 26294 | static const unsigned short aiOff[] = { | |||
| 26295 | 1, 2, 8, 15, 16, 26, 28, 32, | |||
| 26296 | 37, 38, 40, 48, 63, 64, 69, 71, | |||
| 26297 | 79, 80, 116, 202, 203, 205, 206, 207, | |||
| 26298 | 209, 210, 211, 213, 214, 217, 218, 219, | |||
| 26299 | 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721, | |||
| 26300 | 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274, | |||
| 26301 | 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406, | |||
| 26302 | 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, | |||
| 26303 | 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, | |||
| 26304 | 65514, 65521, 65527, 65528, 65529, | |||
| 26305 | }; | |||
| 26306 | ||||
| 26307 | int ret = c; | |||
| 26308 | ||||
| 26309 | assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 )((void) (0)); | |||
| 26310 | ||||
| 26311 | if( c<128 ){ | |||
| 26312 | if( c>='A' && c<='Z' ) ret = c + ('a' - 'A'); | |||
| 26313 | }else if( c<65536 ){ | |||
| 26314 | const struct TableEntry *p; | |||
| 26315 | int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; | |||
| 26316 | int iLo = 0; | |||
| 26317 | int iRes = -1; | |||
| 26318 | ||||
| 26319 | assert( c>aEntry[0].iCode )((void) (0)); | |||
| 26320 | while( iHi>=iLo ){ | |||
| 26321 | int iTest = (iHi + iLo) / 2; | |||
| 26322 | int cmp = (c - aEntry[iTest].iCode); | |||
| 26323 | if( cmp>=0 ){ | |||
| 26324 | iRes = iTest; | |||
| 26325 | iLo = iTest+1; | |||
| 26326 | }else{ | |||
| 26327 | iHi = iTest-1; | |||
| 26328 | } | |||
| 26329 | } | |||
| 26330 | ||||
| 26331 | assert( iRes>=0 && c>=aEntry[iRes].iCode )((void) (0)); | |||
| 26332 | p = &aEntry[iRes]; | |||
| 26333 | if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){ | |||
| 26334 | ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF; | |||
| 26335 | assert( ret>0 )((void) (0)); | |||
| 26336 | } | |||
| 26337 | ||||
| 26338 | if( eRemoveDiacritic ){ | |||
| 26339 | ret = fts5_remove_diacritic(ret, eRemoveDiacritic==2); | |||
| 26340 | } | |||
| 26341 | } | |||
| 26342 | ||||
| 26343 | else if( c>=66560 && c<66600 ){ | |||
| 26344 | ret = c + 40; | |||
| 26345 | } | |||
| 26346 | ||||
| 26347 | return ret; | |||
| 26348 | } | |||
| 26349 | ||||
| 26350 | ||||
| 26351 | static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){ | |||
| 26352 | aArray[0] = 1; | |||
| 26353 | switch( zCat[0] ){ | |||
| 26354 | case 'C': | |||
| 26355 | switch( zCat[1] ){ | |||
| 26356 | case 'c': aArray[1] = 1; break; | |||
| 26357 | case 'f': aArray[2] = 1; break; | |||
| 26358 | case 'n': aArray[3] = 1; break; | |||
| 26359 | case 's': aArray[4] = 1; break; | |||
| 26360 | case 'o': aArray[31] = 1; break; | |||
| 26361 | case '*': | |||
| 26362 | aArray[1] = 1; | |||
| 26363 | aArray[2] = 1; | |||
| 26364 | aArray[3] = 1; | |||
| 26365 | aArray[4] = 1; | |||
| 26366 | aArray[31] = 1; | |||
| 26367 | break; | |||
| 26368 | default: return 1; } | |||
| 26369 | break; | |||
| 26370 | ||||
| 26371 | case 'L': | |||
| 26372 | switch( zCat[1] ){ | |||
| 26373 | case 'l': aArray[5] = 1; break; | |||
| 26374 | case 'm': aArray[6] = 1; break; | |||
| 26375 | case 'o': aArray[7] = 1; break; | |||
| 26376 | case 't': aArray[8] = 1; break; | |||
| 26377 | case 'u': aArray[9] = 1; break; | |||
| 26378 | case 'C': aArray[30] = 1; break; | |||
| 26379 | case '*': | |||
| 26380 | aArray[5] = 1; | |||
| 26381 | aArray[6] = 1; | |||
| 26382 | aArray[7] = 1; | |||
| 26383 | aArray[8] = 1; | |||
| 26384 | aArray[9] = 1; | |||
| 26385 | aArray[30] = 1; | |||
| 26386 | break; | |||
| 26387 | default: return 1; } | |||
| 26388 | break; | |||
| 26389 | ||||
| 26390 | case 'M': | |||
| 26391 | switch( zCat[1] ){ | |||
| 26392 | case 'c': aArray[10] = 1; break; | |||
| 26393 | case 'e': aArray[11] = 1; break; | |||
| 26394 | case 'n': aArray[12] = 1; break; | |||
| 26395 | case '*': | |||
| 26396 | aArray[10] = 1; | |||
| 26397 | aArray[11] = 1; | |||
| 26398 | aArray[12] = 1; | |||
| 26399 | break; | |||
| 26400 | default: return 1; } | |||
| 26401 | break; | |||
| 26402 | ||||
| 26403 | case 'N': | |||
| 26404 | switch( zCat[1] ){ | |||
| 26405 | case 'd': aArray[13] = 1; break; | |||
| 26406 | case 'l': aArray[14] = 1; break; | |||
| 26407 | case 'o': aArray[15] = 1; break; | |||
| 26408 | case '*': | |||
| 26409 | aArray[13] = 1; | |||
| 26410 | aArray[14] = 1; | |||
| 26411 | aArray[15] = 1; | |||
| 26412 | break; | |||
| 26413 | default: return 1; } | |||
| 26414 | break; | |||
| 26415 | ||||
| 26416 | case 'P': | |||
| 26417 | switch( zCat[1] ){ | |||
| 26418 | case 'c': aArray[16] = 1; break; | |||
| 26419 | case 'd': aArray[17] = 1; break; | |||
| 26420 | case 'e': aArray[18] = 1; break; | |||
| 26421 | case 'f': aArray[19] = 1; break; | |||
| 26422 | case 'i': aArray[20] = 1; break; | |||
| 26423 | case 'o': aArray[21] = 1; break; | |||
| 26424 | case 's': aArray[22] = 1; break; | |||
| 26425 | case '*': | |||
| 26426 | aArray[16] = 1; | |||
| 26427 | aArray[17] = 1; | |||
| 26428 | aArray[18] = 1; | |||
| 26429 | aArray[19] = 1; | |||
| 26430 | aArray[20] = 1; | |||
| 26431 | aArray[21] = 1; | |||
| 26432 | aArray[22] = 1; | |||
| 26433 | break; | |||
| 26434 | default: return 1; } | |||
| 26435 | break; | |||
| 26436 | ||||
| 26437 | case 'S': | |||
| 26438 | switch( zCat[1] ){ | |||
| 26439 | case 'c': aArray[23] = 1; break; | |||
| 26440 | case 'k': aArray[24] = 1; break; | |||
| 26441 | case 'm': aArray[25] = 1; break; | |||
| 26442 | case 'o': aArray[26] = 1; break; | |||
| 26443 | case '*': | |||
| 26444 | aArray[23] = 1; | |||
| 26445 | aArray[24] = 1; | |||
| 26446 | aArray[25] = 1; | |||
| 26447 | aArray[26] = 1; | |||
| 26448 | break; | |||
| 26449 | default: return 1; } | |||
| 26450 | break; | |||
| 26451 | ||||
| 26452 | case 'Z': | |||
| 26453 | switch( zCat[1] ){ | |||
| 26454 | case 'l': aArray[27] = 1; break; | |||
| 26455 | case 'p': aArray[28] = 1; break; | |||
| 26456 | case 's': aArray[29] = 1; break; | |||
| 26457 | case '*': | |||
| 26458 | aArray[27] = 1; | |||
| 26459 | aArray[28] = 1; | |||
| 26460 | aArray[29] = 1; | |||
| 26461 | break; | |||
| 26462 | default: return 1; } | |||
| 26463 | break; | |||
| 26464 | ||||
| 26465 | ||||
| 26466 | default: | |||
| 26467 | return 1; | |||
| 26468 | } | |||
| 26469 | return 0; | |||
| 26470 | } | |||
| 26471 | ||||
| 26472 | static u16 aFts5UnicodeBlock[] = { | |||
| 26473 | 0, 1471, 1753, 1760, 1760, 1760, 1760, 1760, 1760, 1760, | |||
| 26474 | 1760, 1760, 1760, 1760, 1760, 1763, 1765, | |||
| 26475 | }; | |||
| 26476 | static u16 aFts5UnicodeMap[] = { | |||
| 26477 | 0, 32, 33, 36, 37, 40, 41, 42, 43, 44, | |||
| 26478 | 45, 46, 48, 58, 60, 63, 65, 91, 92, 93, | |||
| 26479 | 94, 95, 96, 97, 123, 124, 125, 126, 127, 160, | |||
| 26480 | 161, 162, 166, 167, 168, 169, 170, 171, 172, 173, | |||
| 26481 | 174, 175, 176, 177, 178, 180, 181, 182, 184, 185, | |||
| 26482 | 186, 187, 188, 191, 192, 215, 216, 223, 247, 248, | |||
| 26483 | 256, 312, 313, 329, 330, 377, 383, 385, 387, 388, | |||
| 26484 | 391, 394, 396, 398, 402, 403, 405, 406, 409, 412, | |||
| 26485 | 414, 415, 417, 418, 423, 427, 428, 431, 434, 436, | |||
| 26486 | 437, 440, 442, 443, 444, 446, 448, 452, 453, 454, | |||
| 26487 | 455, 456, 457, 458, 459, 460, 461, 477, 478, 496, | |||
| 26488 | 497, 498, 499, 500, 503, 505, 506, 564, 570, 572, | |||
| 26489 | 573, 575, 577, 580, 583, 584, 592, 660, 661, 688, | |||
| 26490 | 706, 710, 722, 736, 741, 748, 749, 750, 751, 768, | |||
| 26491 | 880, 884, 885, 886, 890, 891, 894, 900, 902, 903, | |||
| 26492 | 904, 908, 910, 912, 913, 931, 940, 975, 977, 978, | |||
| 26493 | 981, 984, 1008, 1012, 1014, 1015, 1018, 1020, 1021, 1072, | |||
| 26494 | 1120, 1154, 1155, 1160, 1162, 1217, 1231, 1232, 1329, 1369, | |||
| 26495 | 1370, 1377, 1417, 1418, 1423, 1425, 1470, 1471, 1472, 1473, | |||
| 26496 | 1475, 1476, 1478, 1479, 1488, 1520, 1523, 1536, 1542, 1545, | |||
| 26497 | 1547, 1548, 1550, 1552, 1563, 1566, 1568, 1600, 1601, 1611, | |||
| 26498 | 1632, 1642, 1646, 1648, 1649, 1748, 1749, 1750, 1757, 1758, | |||
| 26499 | 1759, 1765, 1767, 1769, 1770, 1774, 1776, 1786, 1789, 1791, | |||
| 26500 | 1792, 1807, 1808, 1809, 1810, 1840, 1869, 1958, 1969, 1984, | |||
| 26501 | 1994, 2027, 2036, 2038, 2039, 2042, 2048, 2070, 2074, 2075, | |||
| 26502 | 2084, 2085, 2088, 2089, 2096, 2112, 2137, 2142, 2208, 2210, | |||
| 26503 | 2276, 2304, 2307, 2308, 2362, 2363, 2364, 2365, 2366, 2369, | |||
| 26504 | 2377, 2381, 2382, 2384, 2385, 2392, 2402, 2404, 2406, 2416, | |||
| 26505 | 2417, 2418, 2425, 2433, 2434, 2437, 2447, 2451, 2474, 2482, | |||
| 26506 | 2486, 2492, 2493, 2494, 2497, 2503, 2507, 2509, 2510, 2519, | |||
| 26507 | 2524, 2527, 2530, 2534, 2544, 2546, 2548, 2554, 2555, 2561, | |||
| 26508 | 2563, 2565, 2575, 2579, 2602, 2610, 2613, 2616, 2620, 2622, | |||
| 26509 | 2625, 2631, 2635, 2641, 2649, 2654, 2662, 2672, 2674, 2677, | |||
| 26510 | 2689, 2691, 2693, 2703, 2707, 2730, 2738, 2741, 2748, 2749, | |||
| 26511 | 2750, 2753, 2759, 2761, 2763, 2765, 2768, 2784, 2786, 2790, | |||
| 26512 | 2800, 2801, 2817, 2818, 2821, 2831, 2835, 2858, 2866, 2869, | |||
| 26513 | 2876, 2877, 2878, 2879, 2880, 2881, 2887, 2891, 2893, 2902, | |||
| 26514 | 2903, 2908, 2911, 2914, 2918, 2928, 2929, 2930, 2946, 2947, | |||
| 26515 | 2949, 2958, 2962, 2969, 2972, 2974, 2979, 2984, 2990, 3006, | |||
| 26516 | 3008, 3009, 3014, 3018, 3021, 3024, 3031, 3046, 3056, 3059, | |||
| 26517 | 3065, 3066, 3073, 3077, 3086, 3090, 3114, 3125, 3133, 3134, | |||
| 26518 | 3137, 3142, 3146, 3157, 3160, 3168, 3170, 3174, 3192, 3199, | |||
| 26519 | 3202, 3205, 3214, 3218, 3242, 3253, 3260, 3261, 3262, 3263, | |||
| 26520 | 3264, 3270, 3271, 3274, 3276, 3285, 3294, 3296, 3298, 3302, | |||
| 26521 | 3313, 3330, 3333, 3342, 3346, 3389, 3390, 3393, 3398, 3402, | |||
| 26522 | 3405, 3406, 3415, 3424, 3426, 3430, 3440, 3449, 3450, 3458, | |||
| 26523 | 3461, 3482, 3507, 3517, 3520, 3530, 3535, 3538, 3542, 3544, | |||
| 26524 | 3570, 3572, 3585, 3633, 3634, 3636, 3647, 3648, 3654, 3655, | |||
| 26525 | 3663, 3664, 3674, 3713, 3716, 3719, 3722, 3725, 3732, 3737, | |||
| 26526 | 3745, 3749, 3751, 3754, 3757, 3761, 3762, 3764, 3771, 3773, | |||
| 26527 | 3776, 3782, 3784, 3792, 3804, 3840, 3841, 3844, 3859, 3860, | |||
| 26528 | 3861, 3864, 3866, 3872, 3882, 3892, 3893, 3894, 3895, 3896, | |||
| 26529 | 3897, 3898, 3899, 3900, 3901, 3902, 3904, 3913, 3953, 3967, | |||
| 26530 | 3968, 3973, 3974, 3976, 3981, 3993, 4030, 4038, 4039, 4046, | |||
| 26531 | 4048, 4053, 4057, 4096, 4139, 4141, 4145, 4146, 4152, 4153, | |||
| 26532 | 4155, 4157, 4159, 4160, 4170, 4176, 4182, 4184, 4186, 4190, | |||
| 26533 | 4193, 4194, 4197, 4199, 4206, 4209, 4213, 4226, 4227, 4229, | |||
| 26534 | 4231, 4237, 4238, 4239, 4240, 4250, 4253, 4254, 4256, 4295, | |||
| 26535 | 4301, 4304, 4347, 4348, 4349, 4682, 4688, 4696, 4698, 4704, | |||
| 26536 | 4746, 4752, 4786, 4792, 4800, 4802, 4808, 4824, 4882, 4888, | |||
| 26537 | 4957, 4960, 4969, 4992, 5008, 5024, 5120, 5121, 5741, 5743, | |||
| 26538 | 5760, 5761, 5787, 5788, 5792, 5867, 5870, 5888, 5902, 5906, | |||
| 26539 | 5920, 5938, 5941, 5952, 5970, 5984, 5998, 6002, 6016, 6068, | |||
| 26540 | 6070, 6071, 6078, 6086, 6087, 6089, 6100, 6103, 6104, 6107, | |||
| 26541 | 6108, 6109, 6112, 6128, 6144, 6150, 6151, 6155, 6158, 6160, | |||
| 26542 | 6176, 6211, 6212, 6272, 6313, 6314, 6320, 6400, 6432, 6435, | |||
| 26543 | 6439, 6441, 6448, 6450, 6451, 6457, 6464, 6468, 6470, 6480, | |||
| 26544 | 6512, 6528, 6576, 6593, 6600, 6608, 6618, 6622, 6656, 6679, | |||
| 26545 | 6681, 6686, 6688, 6741, 6742, 6743, 6744, 6752, 6753, 6754, | |||
| 26546 | 6755, 6757, 6765, 6771, 6783, 6784, 6800, 6816, 6823, 6824, | |||
| 26547 | 6912, 6916, 6917, 6964, 6965, 6966, 6971, 6972, 6973, 6978, | |||
| 26548 | 6979, 6981, 6992, 7002, 7009, 7019, 7028, 7040, 7042, 7043, | |||
| 26549 | 7073, 7074, 7078, 7080, 7082, 7083, 7084, 7086, 7088, 7098, | |||
| 26550 | 7142, 7143, 7144, 7146, 7149, 7150, 7151, 7154, 7164, 7168, | |||
| 26551 | 7204, 7212, 7220, 7222, 7227, 7232, 7245, 7248, 7258, 7288, | |||
| 26552 | 7294, 7360, 7376, 7379, 7380, 7393, 7394, 7401, 7405, 7406, | |||
| 26553 | 7410, 7412, 7413, 7424, 7468, 7531, 7544, 7545, 7579, 7616, | |||
| 26554 | 7676, 7680, 7830, 7838, 7936, 7944, 7952, 7960, 7968, 7976, | |||
| 26555 | 7984, 7992, 8000, 8008, 8016, 8025, 8027, 8029, 8031, 8033, | |||
| 26556 | 8040, 8048, 8064, 8072, 8080, 8088, 8096, 8104, 8112, 8118, | |||
| 26557 | 8120, 8124, 8125, 8126, 8127, 8130, 8134, 8136, 8140, 8141, | |||
| 26558 | 8144, 8150, 8152, 8157, 8160, 8168, 8173, 8178, 8182, 8184, | |||
| 26559 | 8188, 8189, 8192, 8203, 8208, 8214, 8216, 8217, 8218, 8219, | |||
| 26560 | 8221, 8222, 8223, 8224, 8232, 8233, 8234, 8239, 8240, 8249, | |||
| 26561 | 8250, 8251, 8255, 8257, 8260, 8261, 8262, 8263, 8274, 8275, | |||
| 26562 | 8276, 8277, 8287, 8288, 8298, 8304, 8305, 8308, 8314, 8317, | |||
| 26563 | 8318, 8319, 8320, 8330, 8333, 8334, 8336, 8352, 8400, 8413, | |||
| 26564 | 8417, 8418, 8421, 8448, 8450, 8451, 8455, 8456, 8458, 8459, | |||
| 26565 | 8462, 8464, 8467, 8468, 8469, 8470, 8472, 8473, 8478, 8484, | |||
| 26566 | 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8496, 8500, | |||
| 26567 | 8501, 8505, 8506, 8508, 8510, 8512, 8517, 8519, 8522, 8523, | |||
| 26568 | 8524, 8526, 8527, 8528, 8544, 8579, 8581, 8585, 8592, 8597, | |||
| 26569 | 8602, 8604, 8608, 8609, 8611, 8612, 8614, 8615, 8622, 8623, | |||
| 26570 | 8654, 8656, 8658, 8659, 8660, 8661, 8692, 8960, 8968, 8972, | |||
| 26571 | 8992, 8994, 9001, 9002, 9003, 9084, 9085, 9115, 9140, 9180, | |||
| 26572 | 9186, 9216, 9280, 9312, 9372, 9450, 9472, 9655, 9656, 9665, | |||
| 26573 | 9666, 9720, 9728, 9839, 9840, 9985, 10088, 10089, 10090, 10091, | |||
| 26574 | 10092, 10093, 10094, 10095, 10096, 10097, 10098, 10099, 10100, 10101, | |||
| 26575 | 10102, 10132, 10176, 10181, 10182, 10183, 10214, 10215, 10216, 10217, | |||
| 26576 | 10218, 10219, 10220, 10221, 10222, 10223, 10224, 10240, 10496, 10627, | |||
| 26577 | 10628, 10629, 10630, 10631, 10632, 10633, 10634, 10635, 10636, 10637, | |||
| 26578 | 10638, 10639, 10640, 10641, 10642, 10643, 10644, 10645, 10646, 10647, | |||
| 26579 | 10648, 10649, 10712, 10713, 10714, 10715, 10716, 10748, 10749, 10750, | |||
| 26580 | 11008, 11056, 11077, 11079, 11088, 11264, 11312, 11360, 11363, 11365, | |||
| 26581 | 11367, 11374, 11377, 11378, 11380, 11381, 11383, 11388, 11390, 11393, | |||
| 26582 | 11394, 11492, 11493, 11499, 11503, 11506, 11513, 11517, 11518, 11520, | |||
| 26583 | 11559, 11565, 11568, 11631, 11632, 11647, 11648, 11680, 11688, 11696, | |||
| 26584 | 11704, 11712, 11720, 11728, 11736, 11744, 11776, 11778, 11779, 11780, | |||
| 26585 | 11781, 11782, 11785, 11786, 11787, 11788, 11789, 11790, 11799, 11800, | |||
| 26586 | 11802, 11803, 11804, 11805, 11806, 11808, 11809, 11810, 11811, 11812, | |||
| 26587 | 11813, 11814, 11815, 11816, 11817, 11818, 11823, 11824, 11834, 11904, | |||
| 26588 | 11931, 12032, 12272, 12288, 12289, 12292, 12293, 12294, 12295, 12296, | |||
| 26589 | 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 12306, | |||
| 26590 | 12308, 12309, 12310, 12311, 12312, 12313, 12314, 12315, 12316, 12317, | |||
| 26591 | 12318, 12320, 12321, 12330, 12334, 12336, 12337, 12342, 12344, 12347, | |||
| 26592 | 12348, 12349, 12350, 12353, 12441, 12443, 12445, 12447, 12448, 12449, | |||
| 26593 | 12539, 12540, 12543, 12549, 12593, 12688, 12690, 12694, 12704, 12736, | |||
| 26594 | 12784, 12800, 12832, 12842, 12872, 12880, 12881, 12896, 12928, 12938, | |||
| 26595 | 12977, 12992, 13056, 13312, 19893, 19904, 19968, 40908, 40960, 40981, | |||
| 26596 | 40982, 42128, 42192, 42232, 42238, 42240, 42508, 42509, 42512, 42528, | |||
| 26597 | 42538, 42560, 42606, 42607, 42608, 42611, 42612, 42622, 42623, 42624, | |||
| 26598 | 42655, 42656, 42726, 42736, 42738, 42752, 42775, 42784, 42786, 42800, | |||
| 26599 | 42802, 42864, 42865, 42873, 42878, 42888, 42889, 42891, 42896, 42912, | |||
| 26600 | 43000, 43002, 43003, 43010, 43011, 43014, 43015, 43019, 43020, 43043, | |||
| 26601 | 43045, 43047, 43048, 43056, 43062, 43064, 43065, 43072, 43124, 43136, | |||
| 26602 | 43138, 43188, 43204, 43214, 43216, 43232, 43250, 43256, 43259, 43264, | |||
| 26603 | 43274, 43302, 43310, 43312, 43335, 43346, 43359, 43360, 43392, 43395, | |||
| 26604 | 43396, 43443, 43444, 43446, 43450, 43452, 43453, 43457, 43471, 43472, | |||
| 26605 | 43486, 43520, 43561, 43567, 43569, 43571, 43573, 43584, 43587, 43588, | |||
| 26606 | 43596, 43597, 43600, 43612, 43616, 43632, 43633, 43639, 43642, 43643, | |||
| 26607 | 43648, 43696, 43697, 43698, 43701, 43703, 43705, 43710, 43712, 43713, | |||
| 26608 | 43714, 43739, 43741, 43742, 43744, 43755, 43756, 43758, 43760, 43762, | |||
| 26609 | 43763, 43765, 43766, 43777, 43785, 43793, 43808, 43816, 43968, 44003, | |||
| 26610 | 44005, 44006, 44008, 44009, 44011, 44012, 44013, 44016, 44032, 55203, | |||
| 26611 | 55216, 55243, 55296, 56191, 56319, 57343, 57344, 63743, 63744, 64112, | |||
| 26612 | 64256, 64275, 64285, 64286, 64287, 64297, 64298, 64312, 64318, 64320, | |||
| 26613 | 64323, 64326, 64434, 64467, 64830, 64831, 64848, 64914, 65008, 65020, | |||
| 26614 | 65021, 65024, 65040, 65047, 65048, 65049, 65056, 65072, 65073, 65075, | |||
| 26615 | 65077, 65078, 65079, 65080, 65081, 65082, 65083, 65084, 65085, 65086, | |||
| 26616 | 65087, 65088, 65089, 65090, 65091, 65092, 65093, 65095, 65096, 65097, | |||
| 26617 | 65101, 65104, 65108, 65112, 65113, 65114, 65115, 65116, 65117, 65118, | |||
| 26618 | 65119, 65122, 65123, 65124, 65128, 65129, 65130, 65136, 65142, 65279, | |||
| 26619 | 65281, 65284, 65285, 65288, 65289, 65290, 65291, 65292, 65293, 65294, | |||
| 26620 | 65296, 65306, 65308, 65311, 65313, 65339, 65340, 65341, 65342, 65343, | |||
| 26621 | 65344, 65345, 65371, 65372, 65373, 65374, 65375, 65376, 65377, 65378, | |||
| 26622 | 65379, 65380, 65382, 65392, 65393, 65438, 65440, 65474, 65482, 65490, | |||
| 26623 | 65498, 65504, 65506, 65507, 65508, 65509, 65512, 65513, 65517, 65529, | |||
| 26624 | 65532, 0, 13, 40, 60, 63, 80, 128, 256, 263, | |||
| 26625 | 311, 320, 373, 377, 394, 400, 464, 509, 640, 672, | |||
| 26626 | 768, 800, 816, 833, 834, 842, 896, 927, 928, 968, | |||
| 26627 | 976, 977, 1024, 1064, 1104, 1184, 2048, 2056, 2058, 2103, | |||
| 26628 | 2108, 2111, 2135, 2136, 2304, 2326, 2335, 2336, 2367, 2432, | |||
| 26629 | 2494, 2560, 2561, 2565, 2572, 2576, 2581, 2585, 2616, 2623, | |||
| 26630 | 2624, 2640, 2656, 2685, 2687, 2816, 2873, 2880, 2904, 2912, | |||
| 26631 | 2936, 3072, 3680, 4096, 4097, 4098, 4099, 4152, 4167, 4178, | |||
| 26632 | 4198, 4224, 4226, 4227, 4272, 4275, 4279, 4281, 4283, 4285, | |||
| 26633 | 4286, 4304, 4336, 4352, 4355, 4391, 4396, 4397, 4406, 4416, | |||
| 26634 | 4480, 4482, 4483, 4531, 4534, 4543, 4545, 4549, 4560, 5760, | |||
| 26635 | 5803, 5804, 5805, 5806, 5808, 5814, 5815, 5824, 8192, 9216, | |||
| 26636 | 9328, 12288, 26624, 28416, 28496, 28497, 28559, 28563, 45056, 53248, | |||
| 26637 | 53504, 53545, 53605, 53607, 53610, 53613, 53619, 53627, 53635, 53637, | |||
| 26638 | 53644, 53674, 53678, 53760, 53826, 53829, 54016, 54112, 54272, 54298, | |||
| 26639 | 54324, 54350, 54358, 54376, 54402, 54428, 54430, 54434, 54437, 54441, | |||
| 26640 | 54446, 54454, 54459, 54461, 54469, 54480, 54506, 54532, 54535, 54541, | |||
| 26641 | 54550, 54558, 54584, 54587, 54592, 54598, 54602, 54610, 54636, 54662, | |||
| 26642 | 54688, 54714, 54740, 54766, 54792, 54818, 54844, 54870, 54896, 54922, | |||
| 26643 | 54952, 54977, 54978, 55003, 55004, 55010, 55035, 55036, 55061, 55062, | |||
| 26644 | 55068, 55093, 55094, 55119, 55120, 55126, 55151, 55152, 55177, 55178, | |||
| 26645 | 55184, 55209, 55210, 55235, 55236, 55242, 55246, 60928, 60933, 60961, | |||
| 26646 | 60964, 60967, 60969, 60980, 60985, 60987, 60994, 60999, 61001, 61003, | |||
| 26647 | 61005, 61009, 61012, 61015, 61017, 61019, 61021, 61023, 61025, 61028, | |||
| 26648 | 61031, 61036, 61044, 61049, 61054, 61056, 61067, 61089, 61093, 61099, | |||
| 26649 | 61168, 61440, 61488, 61600, 61617, 61633, 61649, 61696, 61712, 61744, | |||
| 26650 | 61808, 61926, 61968, 62016, 62032, 62208, 62256, 62263, 62336, 62368, | |||
| 26651 | 62406, 62432, 62464, 62528, 62530, 62713, 62720, 62784, 62800, 62971, | |||
| 26652 | 63045, 63104, 63232, 0, 42710, 42752, 46900, 46912, 47133, 63488, | |||
| 26653 | 1, 32, 256, 0, 65533, | |||
| 26654 | }; | |||
| 26655 | static u16 aFts5UnicodeData[] = { | |||
| 26656 | 1025, 61, 117, 55, 117, 54, 50, 53, 57, 53, | |||
| 26657 | 49, 85, 333, 85, 121, 85, 841, 54, 53, 50, | |||
| 26658 | 56, 48, 56, 837, 54, 57, 50, 57, 1057, 61, | |||
| 26659 | 53, 151, 58, 53, 56, 58, 39, 52, 57, 34, | |||
| 26660 | 58, 56, 58, 57, 79, 56, 37, 85, 56, 47, | |||
| 26661 | 39, 51, 111, 53, 745, 57, 233, 773, 57, 261, | |||
| 26662 | 1822, 37, 542, 37, 1534, 222, 69, 73, 37, 126, | |||
| 26663 | 126, 73, 69, 137, 37, 73, 37, 105, 101, 73, | |||
| 26664 | 37, 73, 37, 190, 158, 37, 126, 126, 73, 37, | |||
| 26665 | 126, 94, 37, 39, 94, 69, 135, 41, 40, 37, | |||
| 26666 | 41, 40, 37, 41, 40, 37, 542, 37, 606, 37, | |||
| 26667 | 41, 40, 37, 126, 73, 37, 1886, 197, 73, 37, | |||
| 26668 | 73, 69, 126, 105, 37, 286, 2181, 39, 869, 582, | |||
| 26669 | 152, 390, 472, 166, 248, 38, 56, 38, 568, 3596, | |||
| 26670 | 158, 38, 56, 94, 38, 101, 53, 88, 41, 53, | |||
| 26671 | 105, 41, 73, 37, 553, 297, 1125, 94, 37, 105, | |||
| 26672 | 101, 798, 133, 94, 57, 126, 94, 37, 1641, 1541, | |||
| 26673 | 1118, 58, 172, 75, 1790, 478, 37, 2846, 1225, 38, | |||
| 26674 | 213, 1253, 53, 49, 55, 1452, 49, 44, 53, 76, | |||
| 26675 | 53, 76, 53, 44, 871, 103, 85, 162, 121, 85, | |||
| 26676 | 55, 85, 90, 364, 53, 85, 1031, 38, 327, 684, | |||
| 26677 | 333, 149, 71, 44, 3175, 53, 39, 236, 34, 58, | |||
| 26678 | 204, 70, 76, 58, 140, 71, 333, 103, 90, 39, | |||
| 26679 | 469, 34, 39, 44, 967, 876, 2855, 364, 39, 333, | |||
| 26680 | 1063, 300, 70, 58, 117, 38, 711, 140, 38, 300, | |||
| 26681 | 38, 108, 38, 172, 501, 807, 108, 53, 39, 359, | |||
| 26682 | 876, 108, 42, 1735, 44, 42, 44, 39, 106, 268, | |||
| 26683 | 138, 44, 74, 39, 236, 327, 76, 85, 333, 53, | |||
| 26684 | 38, 199, 231, 44, 74, 263, 71, 711, 231, 39, | |||
| 26685 | 135, 44, 39, 106, 140, 74, 74, 44, 39, 42, | |||
| 26686 | 71, 103, 76, 333, 71, 87, 207, 58, 55, 76, | |||
| 26687 | 42, 199, 71, 711, 231, 71, 71, 71, 44, 106, | |||
| 26688 | 76, 76, 108, 44, 135, 39, 333, 76, 103, 44, | |||
| 26689 | 76, 42, 295, 103, 711, 231, 71, 167, 44, 39, | |||
| 26690 | 106, 172, 76, 42, 74, 44, 39, 71, 76, 333, | |||
| 26691 | 53, 55, 44, 74, 263, 71, 711, 231, 71, 167, | |||
| 26692 | 44, 39, 42, 44, 42, 140, 74, 74, 44, 44, | |||
| 26693 | 42, 71, 103, 76, 333, 58, 39, 207, 44, 39, | |||
| 26694 | 199, 103, 135, 71, 39, 71, 71, 103, 391, 74, | |||
| 26695 | 44, 74, 106, 106, 44, 39, 42, 333, 111, 218, | |||
| 26696 | 55, 58, 106, 263, 103, 743, 327, 167, 39, 108, | |||
| 26697 | 138, 108, 140, 76, 71, 71, 76, 333, 239, 58, | |||
| 26698 | 74, 263, 103, 743, 327, 167, 44, 39, 42, 44, | |||
| 26699 | 170, 44, 74, 74, 76, 74, 39, 71, 76, 333, | |||
| 26700 | 71, 74, 263, 103, 1319, 39, 106, 140, 106, 106, | |||
| 26701 | 44, 39, 42, 71, 76, 333, 207, 58, 199, 74, | |||
| 26702 | 583, 775, 295, 39, 231, 44, 106, 108, 44, 266, | |||
| 26703 | 74, 53, 1543, 44, 71, 236, 55, 199, 38, 268, | |||
| 26704 | 53, 333, 85, 71, 39, 71, 39, 39, 135, 231, | |||
| 26705 | 103, 39, 39, 71, 135, 44, 71, 204, 76, 39, | |||
| 26706 | 167, 38, 204, 333, 135, 39, 122, 501, 58, 53, | |||
| 26707 | 122, 76, 218, 333, 335, 58, 44, 58, 44, 58, | |||
| 26708 | 44, 54, 50, 54, 50, 74, 263, 1159, 460, 42, | |||
| 26709 | 172, 53, 76, 167, 364, 1164, 282, 44, 218, 90, | |||
| 26710 | 181, 154, 85, 1383, 74, 140, 42, 204, 42, 76, | |||
| 26711 | 74, 76, 39, 333, 213, 199, 74, 76, 135, 108, | |||
| 26712 | 39, 106, 71, 234, 103, 140, 423, 44, 74, 76, | |||
| 26713 | 202, 44, 39, 42, 333, 106, 44, 90, 1225, 41, | |||
| 26714 | 41, 1383, 53, 38, 10631, 135, 231, 39, 135, 1319, | |||
| 26715 | 135, 1063, 135, 231, 39, 135, 487, 1831, 135, 2151, | |||
| 26716 | 108, 309, 655, 519, 346, 2727, 49, 19847, 85, 551, | |||
| 26717 | 61, 839, 54, 50, 2407, 117, 110, 423, 135, 108, | |||
| 26718 | 583, 108, 85, 583, 76, 423, 103, 76, 1671, 76, | |||
| 26719 | 42, 236, 266, 44, 74, 364, 117, 38, 117, 55, | |||
| 26720 | 39, 44, 333, 335, 213, 49, 149, 108, 61, 333, | |||
| 26721 | 1127, 38, 1671, 1319, 44, 39, 2247, 935, 108, 138, | |||
| 26722 | 76, 106, 74, 44, 202, 108, 58, 85, 333, 967, | |||
| 26723 | 167, 1415, 554, 231, 74, 333, 47, 1114, 743, 76, | |||
| 26724 | 106, 85, 1703, 42, 44, 42, 236, 44, 42, 44, | |||
| 26725 | 74, 268, 202, 332, 44, 333, 333, 245, 38, 213, | |||
| 26726 | 140, 42, 1511, 44, 42, 172, 42, 44, 170, 44, | |||
| 26727 | 74, 231, 333, 245, 346, 300, 314, 76, 42, 967, | |||
| 26728 | 42, 140, 74, 76, 42, 44, 74, 71, 333, 1415, | |||
| 26729 | 44, 42, 76, 106, 44, 42, 108, 74, 149, 1159, | |||
| 26730 | 266, 268, 74, 76, 181, 333, 103, 333, 967, 198, | |||
| 26731 | 85, 277, 108, 53, 428, 42, 236, 135, 44, 135, | |||
| 26732 | 74, 44, 71, 1413, 2022, 421, 38, 1093, 1190, 1260, | |||
| 26733 | 140, 4830, 261, 3166, 261, 265, 197, 201, 261, 265, | |||
| 26734 | 261, 265, 197, 201, 261, 41, 41, 41, 94, 229, | |||
| 26735 | 265, 453, 261, 264, 261, 264, 261, 264, 165, 69, | |||
| 26736 | 137, 40, 56, 37, 120, 101, 69, 137, 40, 120, | |||
| 26737 | 133, 69, 137, 120, 261, 169, 120, 101, 69, 137, | |||
| 26738 | 40, 88, 381, 162, 209, 85, 52, 51, 54, 84, | |||
| 26739 | 51, 54, 52, 277, 59, 60, 162, 61, 309, 52, | |||
| 26740 | 51, 149, 80, 117, 57, 54, 50, 373, 57, 53, | |||
| 26741 | 48, 341, 61, 162, 194, 47, 38, 207, 121, 54, | |||
| 26742 | 50, 38, 335, 121, 54, 50, 422, 855, 428, 139, | |||
| 26743 | 44, 107, 396, 90, 41, 154, 41, 90, 37, 105, | |||
| 26744 | 69, 105, 37, 58, 41, 90, 57, 169, 218, 41, | |||
| 26745 | 58, 41, 58, 41, 58, 137, 58, 37, 137, 37, | |||
| 26746 | 135, 37, 90, 69, 73, 185, 94, 101, 58, 57, | |||
| 26747 | 90, 37, 58, 527, 1134, 94, 142, 47, 185, 186, | |||
| 26748 | 89, 154, 57, 90, 57, 90, 57, 250, 57, 1018, | |||
| 26749 | 89, 90, 57, 58, 57, 1018, 8601, 282, 153, 666, | |||
| 26750 | 89, 250, 54, 50, 2618, 57, 986, 825, 1306, 217, | |||
| 26751 | 602, 1274, 378, 1935, 2522, 719, 5882, 57, 314, 57, | |||
| 26752 | 1754, 281, 3578, 57, 4634, 3322, 54, 50, 54, 50, | |||
| 26753 | 54, 50, 54, 50, 54, 50, 54, 50, 54, 50, | |||
| 26754 | 975, 1434, 185, 54, 50, 1017, 54, 50, 54, 50, | |||
| 26755 | 54, 50, 54, 50, 54, 50, 537, 8218, 4217, 54, | |||
| 26756 | 50, 54, 50, 54, 50, 54, 50, 54, 50, 54, | |||
| 26757 | 50, 54, 50, 54, 50, 54, 50, 54, 50, 54, | |||
| 26758 | 50, 2041, 54, 50, 54, 50, 1049, 54, 50, 8281, | |||
| 26759 | 1562, 697, 90, 217, 346, 1513, 1509, 126, 73, 69, | |||
| 26760 | 254, 105, 37, 94, 37, 94, 165, 70, 105, 37, | |||
| 26761 | 3166, 37, 218, 158, 108, 94, 149, 47, 85, 1221, | |||
| 26762 | 37, 37, 1799, 38, 53, 44, 743, 231, 231, 231, | |||
| 26763 | 231, 231, 231, 231, 231, 1036, 85, 52, 51, 52, | |||
| 26764 | 51, 117, 52, 51, 53, 52, 51, 309, 49, 85, | |||
| 26765 | 49, 53, 52, 51, 85, 52, 51, 54, 50, 54, | |||
| 26766 | 50, 54, 50, 54, 50, 181, 38, 341, 81, 858, | |||
| 26767 | 2874, 6874, 410, 61, 117, 58, 38, 39, 46, 54, | |||
| 26768 | 50, 54, 50, 54, 50, 54, 50, 54, 50, 90, | |||
| 26769 | 54, 50, 54, 50, 54, 50, 54, 50, 49, 54, | |||
| 26770 | 82, 58, 302, 140, 74, 49, 166, 90, 110, 38, | |||
| 26771 | 39, 53, 90, 2759, 76, 88, 70, 39, 49, 2887, | |||
| 26772 | 53, 102, 39, 1319, 3015, 90, 143, 346, 871, 1178, | |||
| 26773 | 519, 1018, 335, 986, 271, 58, 495, 1050, 335, 1274, | |||
| 26774 | 495, 2042, 8218, 39, 39, 2074, 39, 39, 679, 38, | |||
| 26775 | 36583, 1786, 1287, 198, 85, 8583, 38, 117, 519, 333, | |||
| 26776 | 71, 1502, 39, 44, 107, 53, 332, 53, 38, 798, | |||
| 26777 | 44, 2247, 334, 76, 213, 760, 294, 88, 478, 69, | |||
| 26778 | 2014, 38, 261, 190, 350, 38, 88, 158, 158, 382, | |||
| 26779 | 70, 37, 231, 44, 103, 44, 135, 44, 743, 74, | |||
| 26780 | 76, 42, 154, 207, 90, 55, 58, 1671, 149, 74, | |||
| 26781 | 1607, 522, 44, 85, 333, 588, 199, 117, 39, 333, | |||
| 26782 | 903, 268, 85, 743, 364, 74, 53, 935, 108, 42, | |||
| 26783 | 1511, 44, 74, 140, 74, 44, 138, 437, 38, 333, | |||
| 26784 | 85, 1319, 204, 74, 76, 74, 76, 103, 44, 263, | |||
| 26785 | 44, 42, 333, 149, 519, 38, 199, 122, 39, 42, | |||
| 26786 | 1543, 44, 39, 108, 71, 76, 167, 76, 39, 44, | |||
| 26787 | 39, 71, 38, 85, 359, 42, 76, 74, 85, 39, | |||
| 26788 | 70, 42, 44, 199, 199, 199, 231, 231, 1127, 74, | |||
| 26789 | 44, 74, 44, 74, 53, 42, 44, 333, 39, 39, | |||
| 26790 | 743, 1575, 36, 68, 68, 36, 63, 63, 11719, 3399, | |||
| 26791 | 229, 165, 39, 44, 327, 57, 423, 167, 39, 71, | |||
| 26792 | 71, 3463, 536, 11623, 54, 50, 2055, 1735, 391, 55, | |||
| 26793 | 58, 524, 245, 54, 50, 53, 236, 53, 81, 80, | |||
| 26794 | 54, 50, 54, 50, 54, 50, 54, 50, 54, 50, | |||
| 26795 | 54, 50, 54, 50, 54, 50, 85, 54, 50, 149, | |||
| 26796 | 112, 117, 149, 49, 54, 50, 54, 50, 54, 50, | |||
| 26797 | 117, 57, 49, 121, 53, 55, 85, 167, 4327, 34, | |||
| 26798 | 117, 55, 117, 54, 50, 53, 57, 53, 49, 85, | |||
| 26799 | 333, 85, 121, 85, 841, 54, 53, 50, 56, 48, | |||
| 26800 | 56, 837, 54, 57, 50, 57, 54, 50, 53, 54, | |||
| 26801 | 50, 85, 327, 38, 1447, 70, 999, 199, 199, 199, | |||
| 26802 | 103, 87, 57, 56, 58, 87, 58, 153, 90, 98, | |||
| 26803 | 90, 391, 839, 615, 71, 487, 455, 3943, 117, 1455, | |||
| 26804 | 314, 1710, 143, 570, 47, 410, 1466, 44, 935, 1575, | |||
| 26805 | 999, 143, 551, 46, 263, 46, 967, 53, 1159, 263, | |||
| 26806 | 53, 174, 1289, 1285, 2503, 333, 199, 39, 1415, 71, | |||
| 26807 | 39, 743, 53, 271, 711, 207, 53, 839, 53, 1799, | |||
| 26808 | 71, 39, 108, 76, 140, 135, 103, 871, 108, 44, | |||
| 26809 | 271, 309, 935, 79, 53, 1735, 245, 711, 271, 615, | |||
| 26810 | 271, 2343, 1007, 42, 44, 42, 1703, 492, 245, 655, | |||
| 26811 | 333, 76, 42, 1447, 106, 140, 74, 76, 85, 34, | |||
| 26812 | 149, 807, 333, 108, 1159, 172, 42, 268, 333, 149, | |||
| 26813 | 76, 42, 1543, 106, 300, 74, 135, 149, 333, 1383, | |||
| 26814 | 44, 42, 44, 74, 204, 42, 44, 333, 28135, 3182, | |||
| 26815 | 149, 34279, 18215, 2215, 39, 1482, 140, 422, 71, 7898, | |||
| 26816 | 1274, 1946, 74, 108, 122, 202, 258, 268, 90, 236, | |||
| 26817 | 986, 140, 1562, 2138, 108, 58, 2810, 591, 841, 837, | |||
| 26818 | 841, 229, 581, 841, 837, 41, 73, 41, 73, 137, | |||
| 26819 | 265, 133, 37, 229, 357, 841, 837, 73, 137, 265, | |||
| 26820 | 233, 837, 73, 137, 169, 41, 233, 837, 841, 837, | |||
| 26821 | 841, 837, 841, 837, 841, 837, 841, 837, 841, 901, | |||
| 26822 | 809, 57, 805, 57, 197, 809, 57, 805, 57, 197, | |||
| 26823 | 809, 57, 805, 57, 197, 809, 57, 805, 57, 197, | |||
| 26824 | 809, 57, 805, 57, 197, 94, 1613, 135, 871, 71, | |||
| 26825 | 39, 39, 327, 135, 39, 39, 39, 39, 39, 39, | |||
| 26826 | 103, 71, 39, 39, 39, 39, 39, 39, 71, 39, | |||
| 26827 | 135, 231, 135, 135, 39, 327, 551, 103, 167, 551, | |||
| 26828 | 89, 1434, 3226, 506, 474, 506, 506, 367, 1018, 1946, | |||
| 26829 | 1402, 954, 1402, 314, 90, 1082, 218, 2266, 666, 1210, | |||
| 26830 | 186, 570, 2042, 58, 5850, 154, 2010, 154, 794, 2266, | |||
| 26831 | 378, 2266, 3738, 39, 39, 39, 39, 39, 39, 17351, | |||
| 26832 | 34, 3074, 7692, 63, 63, | |||
| 26833 | }; | |||
| 26834 | ||||
| 26835 | static int sqlite3Fts5UnicodeCategory(u32 iCode) { | |||
| 26836 | int iRes = -1; | |||
| 26837 | int iHi; | |||
| 26838 | int iLo; | |||
| 26839 | int ret; | |||
| 26840 | u16 iKey; | |||
| 26841 | ||||
| 26842 | if( iCode>=(1<<20) ){ | |||
| 26843 | return 0; | |||
| 26844 | } | |||
| 26845 | iLo = aFts5UnicodeBlock[(iCode>>16)]; | |||
| 26846 | iHi = aFts5UnicodeBlock[1+(iCode>>16)]; | |||
| 26847 | iKey = (iCode & 0xFFFF); | |||
| 26848 | while( iHi>iLo ){ | |||
| 26849 | int iTest = (iHi + iLo) / 2; | |||
| 26850 | assert( iTest>=iLo && iTest<iHi )((void) (0)); | |||
| 26851 | if( iKey>=aFts5UnicodeMap[iTest] ){ | |||
| 26852 | iRes = iTest; | |||
| 26853 | iLo = iTest+1; | |||
| 26854 | }else{ | |||
| 26855 | iHi = iTest; | |||
| 26856 | } | |||
| 26857 | } | |||
| 26858 | ||||
| 26859 | if( iRes<0 ) return 0; | |||
| 26860 | if( iKey>=(aFts5UnicodeMap[iRes]+(aFts5UnicodeData[iRes]>>5)) ) return 0; | |||
| 26861 | ret = aFts5UnicodeData[iRes] & 0x1F; | |||
| 26862 | if( ret!=30 ) return ret; | |||
| 26863 | return ((iKey - aFts5UnicodeMap[iRes]) & 0x01) ? 5 : 9; | |||
| 26864 | } | |||
| 26865 | ||||
| 26866 | static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){ | |||
| 26867 | int i = 0; | |||
| 26868 | int iTbl = 0; | |||
| 26869 | while( i<128 ){ | |||
| 26870 | int bToken = aArray[ aFts5UnicodeData[iTbl] & 0x1F ]; | |||
| 26871 | int n = (aFts5UnicodeData[iTbl] >> 5) + i; | |||
| 26872 | for(; i<128 && i<n; i++){ | |||
| 26873 | aAscii[i] = (u8)bToken; | |||
| 26874 | } | |||
| 26875 | iTbl++; | |||
| 26876 | } | |||
| 26877 | aAscii[0] = 0; /* 0x00 is never a token character */ | |||
| 26878 | } | |||
| 26879 | ||||
| 26880 | #line 1 "fts5_varint.c" | |||
| 26881 | /* | |||
| 26882 | ** 2015 May 30 | |||
| 26883 | ** | |||
| 26884 | ** The author disclaims copyright to this source code. In place of | |||
| 26885 | ** a legal notice, here is a blessing: | |||
| 26886 | ** | |||
| 26887 | ** May you do good and not evil. | |||
| 26888 | ** May you find forgiveness for yourself and forgive others. | |||
| 26889 | ** May you share freely, never taking more than you give. | |||
| 26890 | ** | |||
| 26891 | ****************************************************************************** | |||
| 26892 | ** | |||
| 26893 | ** Routines for varint serialization and deserialization. | |||
| 26894 | */ | |||
| 26895 | ||||
| 26896 | ||||
| 26897 | /* #include "fts5Int.h" */ | |||
| 26898 | ||||
| 26899 | /* | |||
| 26900 | ** This is a copy of the sqlite3GetVarint32() routine from the SQLite core. | |||
| 26901 | ** Except, this version does handle the single byte case that the core | |||
| 26902 | ** version depends on being handled before its function is called. | |||
| 26903 | */ | |||
| 26904 | static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v){ | |||
| 26905 | u32 a,b; | |||
| 26906 | ||||
| 26907 | /* The 1-byte case. Overwhelmingly the most common. */ | |||
| 26908 | a = *p; | |||
| 26909 | /* a: p0 (unmasked) */ | |||
| 26910 | if (!(a&0x80)) | |||
| 26911 | { | |||
| 26912 | /* Values between 0 and 127 */ | |||
| 26913 | *v = a; | |||
| 26914 | return 1; | |||
| 26915 | } | |||
| 26916 | ||||
| 26917 | /* The 2-byte case */ | |||
| 26918 | p++; | |||
| 26919 | b = *p; | |||
| 26920 | /* b: p1 (unmasked) */ | |||
| 26921 | if (!(b&0x80)) | |||
| 26922 | { | |||
| 26923 | /* Values between 128 and 16383 */ | |||
| 26924 | a &= 0x7f; | |||
| 26925 | a = a<<7; | |||
| 26926 | *v = a | b; | |||
| 26927 | return 2; | |||
| 26928 | } | |||
| 26929 | ||||
| 26930 | /* The 3-byte case */ | |||
| 26931 | p++; | |||
| 26932 | a = a<<14; | |||
| 26933 | a |= *p; | |||
| 26934 | /* a: p0<<14 | p2 (unmasked) */ | |||
| 26935 | if (!(a&0x80)) | |||
| 26936 | { | |||
| 26937 | /* Values between 16384 and 2097151 */ | |||
| 26938 | a &= (0x7f<<14)|(0x7f); | |||
| 26939 | b &= 0x7f; | |||
| 26940 | b = b<<7; | |||
| 26941 | *v = a | b; | |||
| 26942 | return 3; | |||
| 26943 | } | |||
| 26944 | ||||
| 26945 | /* A 32-bit varint is used to store size information in btrees. | |||
| 26946 | ** Objects are rarely larger than 2MiB limit of a 3-byte varint. | |||
| 26947 | ** A 3-byte varint is sufficient, for example, to record the size | |||
| 26948 | ** of a 1048569-byte BLOB or string. | |||
| 26949 | ** | |||
| 26950 | ** We only unroll the first 1-, 2-, and 3- byte cases. The very | |||
| 26951 | ** rare larger cases can be handled by the slower 64-bit varint | |||
| 26952 | ** routine. | |||
| 26953 | */ | |||
| 26954 | { | |||
| 26955 | u64 v64; | |||
| 26956 | u8 n; | |||
| 26957 | p -= 2; | |||
| 26958 | n = sqlite3Fts5GetVarint(p, &v64); | |||
| 26959 | *v = ((u32)v64) & 0x7FFFFFFF; | |||
| 26960 | assert( n>3 && n<=9 )((void) (0)); | |||
| 26961 | return n; | |||
| 26962 | } | |||
| 26963 | } | |||
| 26964 | ||||
| 26965 | ||||
| 26966 | /* | |||
| 26967 | ** Bitmasks used by sqlite3GetVarint(). These precomputed constants | |||
| 26968 | ** are defined here rather than simply putting the constant expressions | |||
| 26969 | ** inline in order to work around bugs in the RVT compiler. | |||
| 26970 | ** | |||
| 26971 | ** SLOT_2_0 A mask for (0x7f<<14) | 0x7f | |||
| 26972 | ** | |||
| 26973 | ** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0 | |||
| 26974 | */ | |||
| 26975 | #define SLOT_2_00x001fc07f 0x001fc07f | |||
| 26976 | #define SLOT_4_2_00xf01fc07f 0xf01fc07f | |||
| 26977 | ||||
| 26978 | /* | |||
| 26979 | ** Read a 64-bit variable-length integer from memory starting at p[0]. | |||
| 26980 | ** Return the number of bytes read. The value is stored in *v. | |||
| 26981 | */ | |||
| 26982 | static u8 sqlite3Fts5GetVarint(const unsigned char *p, u64 *v){ | |||
| 26983 | u32 a,b,s; | |||
| 26984 | ||||
| 26985 | a = *p; | |||
| 26986 | /* a: p0 (unmasked) */ | |||
| 26987 | if (!(a&0x80)) | |||
| 26988 | { | |||
| 26989 | *v = a; | |||
| 26990 | return 1; | |||
| 26991 | } | |||
| 26992 | ||||
| 26993 | p++; | |||
| 26994 | b = *p; | |||
| 26995 | /* b: p1 (unmasked) */ | |||
| 26996 | if (!(b&0x80)) | |||
| 26997 | { | |||
| 26998 | a &= 0x7f; | |||
| 26999 | a = a<<7; | |||
| 27000 | a |= b; | |||
| 27001 | *v = a; | |||
| 27002 | return 2; | |||
| 27003 | } | |||
| 27004 | ||||
| 27005 | /* Verify that constants are precomputed correctly */ | |||
| 27006 | assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) )((void) (0)); | |||
| 27007 | assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) )((void) (0)); | |||
| 27008 | ||||
| 27009 | p++; | |||
| 27010 | a = a<<14; | |||
| 27011 | a |= *p; | |||
| 27012 | /* a: p0<<14 | p2 (unmasked) */ | |||
| 27013 | if (!(a&0x80)) | |||
| 27014 | { | |||
| 27015 | a &= SLOT_2_00x001fc07f; | |||
| 27016 | b &= 0x7f; | |||
| 27017 | b = b<<7; | |||
| 27018 | a |= b; | |||
| 27019 | *v = a; | |||
| 27020 | return 3; | |||
| 27021 | } | |||
| 27022 | ||||
| 27023 | /* CSE1 from below */ | |||
| 27024 | a &= SLOT_2_00x001fc07f; | |||
| 27025 | p++; | |||
| 27026 | b = b<<14; | |||
| 27027 | b |= *p; | |||
| 27028 | /* b: p1<<14 | p3 (unmasked) */ | |||
| 27029 | if (!(b&0x80)) | |||
| 27030 | { | |||
| 27031 | b &= SLOT_2_00x001fc07f; | |||
| 27032 | /* moved CSE1 up */ | |||
| 27033 | /* a &= (0x7f<<14)|(0x7f); */ | |||
| 27034 | a = a<<7; | |||
| 27035 | a |= b; | |||
| 27036 | *v = a; | |||
| 27037 | return 4; | |||
| 27038 | } | |||
| 27039 | ||||
| 27040 | /* a: p0<<14 | p2 (masked) */ | |||
| 27041 | /* b: p1<<14 | p3 (unmasked) */ | |||
| 27042 | /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ | |||
| 27043 | /* moved CSE1 up */ | |||
| 27044 | /* a &= (0x7f<<14)|(0x7f); */ | |||
| 27045 | b &= SLOT_2_00x001fc07f; | |||
| 27046 | s = a; | |||
| 27047 | /* s: p0<<14 | p2 (masked) */ | |||
| 27048 | ||||
| 27049 | p++; | |||
| 27050 | a = a<<14; | |||
| 27051 | a |= *p; | |||
| 27052 | /* a: p0<<28 | p2<<14 | p4 (unmasked) */ | |||
| 27053 | if (!(a&0x80)) | |||
| 27054 | { | |||
| 27055 | /* we can skip these cause they were (effectively) done above in calc'ing s */ | |||
| 27056 | /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ | |||
| 27057 | /* b &= (0x7f<<14)|(0x7f); */ | |||
| 27058 | b = b<<7; | |||
| 27059 | a |= b; | |||
| 27060 | s = s>>18; | |||
| 27061 | *v = ((u64)s)<<32 | a; | |||
| 27062 | return 5; | |||
| 27063 | } | |||
| 27064 | ||||
| 27065 | /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ | |||
| 27066 | s = s<<7; | |||
| 27067 | s |= b; | |||
| 27068 | /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ | |||
| 27069 | ||||
| 27070 | p++; | |||
| 27071 | b = b<<14; | |||
| 27072 | b |= *p; | |||
| 27073 | /* b: p1<<28 | p3<<14 | p5 (unmasked) */ | |||
| 27074 | if (!(b&0x80)) | |||
| 27075 | { | |||
| 27076 | /* we can skip this cause it was (effectively) done above in calc'ing s */ | |||
| 27077 | /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ | |||
| 27078 | a &= SLOT_2_00x001fc07f; | |||
| 27079 | a = a<<7; | |||
| 27080 | a |= b; | |||
| 27081 | s = s>>18; | |||
| 27082 | *v = ((u64)s)<<32 | a; | |||
| 27083 | return 6; | |||
| 27084 | } | |||
| 27085 | ||||
| 27086 | p++; | |||
| 27087 | a = a<<14; | |||
| 27088 | a |= *p; | |||
| 27089 | /* a: p2<<28 | p4<<14 | p6 (unmasked) */ | |||
| 27090 | if (!(a&0x80)) | |||
| 27091 | { | |||
| 27092 | a &= SLOT_4_2_00xf01fc07f; | |||
| 27093 | b &= SLOT_2_00x001fc07f; | |||
| 27094 | b = b<<7; | |||
| 27095 | a |= b; | |||
| 27096 | s = s>>11; | |||
| 27097 | *v = ((u64)s)<<32 | a; | |||
| 27098 | return 7; | |||
| 27099 | } | |||
| 27100 | ||||
| 27101 | /* CSE2 from below */ | |||
| 27102 | a &= SLOT_2_00x001fc07f; | |||
| 27103 | p++; | |||
| 27104 | b = b<<14; | |||
| 27105 | b |= *p; | |||
| 27106 | /* b: p3<<28 | p5<<14 | p7 (unmasked) */ | |||
| 27107 | if (!(b&0x80)) | |||
| 27108 | { | |||
| 27109 | b &= SLOT_4_2_00xf01fc07f; | |||
| 27110 | /* moved CSE2 up */ | |||
| 27111 | /* a &= (0x7f<<14)|(0x7f); */ | |||
| 27112 | a = a<<7; | |||
| 27113 | a |= b; | |||
| 27114 | s = s>>4; | |||
| 27115 | *v = ((u64)s)<<32 | a; | |||
| 27116 | return 8; | |||
| 27117 | } | |||
| 27118 | ||||
| 27119 | p++; | |||
| 27120 | a = a<<15; | |||
| 27121 | a |= *p; | |||
| 27122 | /* a: p4<<29 | p6<<15 | p8 (unmasked) */ | |||
| 27123 | ||||
| 27124 | /* moved CSE2 up */ | |||
| 27125 | /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */ | |||
| 27126 | b &= SLOT_2_00x001fc07f; | |||
| 27127 | b = b<<8; | |||
| 27128 | a |= b; | |||
| 27129 | ||||
| 27130 | s = s<<4; | |||
| 27131 | b = p[-4]; | |||
| 27132 | b &= 0x7f; | |||
| 27133 | b = b>>3; | |||
| 27134 | s |= b; | |||
| 27135 | ||||
| 27136 | *v = ((u64)s)<<32 | a; | |||
| 27137 | ||||
| 27138 | return 9; | |||
| 27139 | } | |||
| 27140 | ||||
| 27141 | /* | |||
| 27142 | ** The variable-length integer encoding is as follows: | |||
| 27143 | ** | |||
| 27144 | ** KEY: | |||
| 27145 | ** A = 0xxxxxxx 7 bits of data and one flag bit | |||
| 27146 | ** B = 1xxxxxxx 7 bits of data and one flag bit | |||
| 27147 | ** C = xxxxxxxx 8 bits of data | |||
| 27148 | ** | |||
| 27149 | ** 7 bits - A | |||
| 27150 | ** 14 bits - BA | |||
| 27151 | ** 21 bits - BBA | |||
| 27152 | ** 28 bits - BBBA | |||
| 27153 | ** 35 bits - BBBBA | |||
| 27154 | ** 42 bits - BBBBBA | |||
| 27155 | ** 49 bits - BBBBBBA | |||
| 27156 | ** 56 bits - BBBBBBBA | |||
| 27157 | ** 64 bits - BBBBBBBBC | |||
| 27158 | */ | |||
| 27159 | ||||
| 27160 | #ifdef SQLITE_NOINLINE | |||
| 27161 | # define FTS5_NOINLINE SQLITE_NOINLINE | |||
| 27162 | #else | |||
| 27163 | # define FTS5_NOINLINE | |||
| 27164 | #endif | |||
| 27165 | ||||
| 27166 | /* | |||
| 27167 | ** Write a 64-bit variable-length integer to memory starting at p[0]. | |||
| 27168 | ** The length of data write will be between 1 and 9 bytes. The number | |||
| 27169 | ** of bytes written is returned. | |||
| 27170 | ** | |||
| 27171 | ** A variable-length integer consists of the lower 7 bits of each byte | |||
| 27172 | ** for all bytes that have the 8th bit set and one byte with the 8th | |||
| 27173 | ** bit clear. Except, if we get to the 9th byte, it stores the full | |||
| 27174 | ** 8 bits and is the last byte. | |||
| 27175 | */ | |||
| 27176 | static int FTS5_NOINLINE fts5PutVarint64(unsigned char *p, u64 v){ | |||
| 27177 | int i, j, n; | |||
| 27178 | u8 buf[10]; | |||
| 27179 | if( v & (((u64)0xff000000)<<32) ){ | |||
| 27180 | p[8] = (u8)v; | |||
| 27181 | v >>= 8; | |||
| 27182 | for(i=7; i>=0; i--){ | |||
| 27183 | p[i] = (u8)((v & 0x7f) | 0x80); | |||
| 27184 | v >>= 7; | |||
| 27185 | } | |||
| 27186 | return 9; | |||
| 27187 | } | |||
| 27188 | n = 0; | |||
| 27189 | do{ | |||
| 27190 | buf[n++] = (u8)((v & 0x7f) | 0x80); | |||
| 27191 | v >>= 7; | |||
| 27192 | }while( v!=0 ); | |||
| 27193 | buf[0] &= 0x7f; | |||
| 27194 | assert( n<=9 )((void) (0)); | |||
| 27195 | for(i=0, j=n-1; j>=0; j--, i++){ | |||
| 27196 | p[i] = buf[j]; | |||
| 27197 | } | |||
| 27198 | return n; | |||
| 27199 | } | |||
| 27200 | ||||
| 27201 | static int sqlite3Fts5PutVarint(unsigned char *p, u64 v){ | |||
| 27202 | if( v<=0x7f ){ | |||
| 27203 | p[0] = v&0x7f; | |||
| 27204 | return 1; | |||
| 27205 | } | |||
| 27206 | if( v<=0x3fff ){ | |||
| 27207 | p[0] = ((v>>7)&0x7f)|0x80; | |||
| 27208 | p[1] = v&0x7f; | |||
| 27209 | return 2; | |||
| 27210 | } | |||
| 27211 | return fts5PutVarint64(p,v); | |||
| 27212 | } | |||
| 27213 | ||||
| 27214 | ||||
| 27215 | static int sqlite3Fts5GetVarintLen(u32 iVal){ | |||
| 27216 | #if 0 | |||
| 27217 | if( iVal<(1 << 7 ) ) return 1; | |||
| 27218 | #endif | |||
| 27219 | assert( iVal>=(1 << 7) )((void) (0)); | |||
| 27220 | if( iVal<(1 << 14) ) return 2; | |||
| 27221 | if( iVal<(1 << 21) ) return 3; | |||
| 27222 | if( iVal<(1 << 28) ) return 4; | |||
| 27223 | return 5; | |||
| 27224 | } | |||
| 27225 | ||||
| 27226 | #line 1 "fts5_vocab.c" | |||
| 27227 | /* | |||
| 27228 | ** 2015 May 08 | |||
| 27229 | ** | |||
| 27230 | ** The author disclaims copyright to this source code. In place of | |||
| 27231 | ** a legal notice, here is a blessing: | |||
| 27232 | ** | |||
| 27233 | ** May you do good and not evil. | |||
| 27234 | ** May you find forgiveness for yourself and forgive others. | |||
| 27235 | ** May you share freely, never taking more than you give. | |||
| 27236 | ** | |||
| 27237 | ****************************************************************************** | |||
| 27238 | ** | |||
| 27239 | ** This is an SQLite virtual table module implementing direct access to an | |||
| 27240 | ** existing FTS5 index. The module may create several different types of | |||
| 27241 | ** tables: | |||
| 27242 | ** | |||
| 27243 | ** col: | |||
| 27244 | ** CREATE TABLE vocab(term, col, doc, cnt, PRIMARY KEY(term, col)); | |||
| 27245 | ** | |||
| 27246 | ** One row for each term/column combination. The value of $doc is set to | |||
| 27247 | ** the number of fts5 rows that contain at least one instance of term | |||
| 27248 | ** $term within column $col. Field $cnt is set to the total number of | |||
| 27249 | ** instances of term $term in column $col (in any row of the fts5 table). | |||
| 27250 | ** | |||
| 27251 | ** row: | |||
| 27252 | ** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term)); | |||
| 27253 | ** | |||
| 27254 | ** One row for each term in the database. The value of $doc is set to | |||
| 27255 | ** the number of fts5 rows that contain at least one instance of term | |||
| 27256 | ** $term. Field $cnt is set to the total number of instances of term | |||
| 27257 | ** $term in the database. | |||
| 27258 | ** | |||
| 27259 | ** instance: | |||
| 27260 | ** CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>)); | |||
| 27261 | ** | |||
| 27262 | ** One row for each term instance in the database. | |||
| 27263 | */ | |||
| 27264 | ||||
| 27265 | ||||
| 27266 | /* #include "fts5Int.h" */ | |||
| 27267 | ||||
| 27268 | ||||
| 27269 | typedef struct Fts5VocabTable Fts5VocabTable; | |||
| 27270 | typedef struct Fts5VocabCursor Fts5VocabCursor; | |||
| 27271 | ||||
| 27272 | struct Fts5VocabTable { | |||
| 27273 | sqlite3_vtab base; | |||
| 27274 | char *zFts5Tbl; /* Name of fts5 table */ | |||
| 27275 | char *zFts5Db; /* Db containing fts5 table */ | |||
| 27276 | sqlite3 *db; /* Database handle */ | |||
| 27277 | Fts5Global *pGlobal; /* FTS5 global object for this database */ | |||
| 27278 | int eType; /* FTS5_VOCAB_COL, ROW or INSTANCE */ | |||
| 27279 | unsigned bBusy; /* True if busy */ | |||
| 27280 | }; | |||
| 27281 | ||||
| 27282 | struct Fts5VocabCursor { | |||
| 27283 | sqlite3_vtab_cursor base; | |||
| 27284 | sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */ | |||
| 27285 | Fts5Table *pFts5; /* Associated FTS5 table */ | |||
| 27286 | ||||
| 27287 | int bEof; /* True if this cursor is at EOF */ | |||
| 27288 | Fts5IndexIter *pIter; /* Term/rowid iterator object */ | |||
| 27289 | void *pStruct; /* From sqlite3Fts5StructureRef() */ | |||
| 27290 | ||||
| 27291 | int nLeTerm; /* Size of zLeTerm in bytes */ | |||
| 27292 | char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ | |||
| 27293 | int colUsed; /* Copy of sqlite3_index_info.colUsed */ | |||
| 27294 | ||||
| 27295 | /* These are used by 'col' tables only */ | |||
| 27296 | int iCol; | |||
| 27297 | i64 *aCnt; | |||
| 27298 | i64 *aDoc; | |||
| 27299 | ||||
| 27300 | /* Output values used by all tables. */ | |||
| 27301 | i64 rowid; /* This table's current rowid value */ | |||
| 27302 | Fts5Buffer term; /* Current value of 'term' column */ | |||
| 27303 | ||||
| 27304 | /* Output values Used by 'instance' tables only */ | |||
| 27305 | i64 iInstPos; | |||
| 27306 | int iInstOff; | |||
| 27307 | }; | |||
| 27308 | ||||
| 27309 | #define FTS5_VOCAB_COL0 0 | |||
| 27310 | #define FTS5_VOCAB_ROW1 1 | |||
| 27311 | #define FTS5_VOCAB_INSTANCE2 2 | |||
| 27312 | ||||
| 27313 | #define FTS5_VOCAB_COL_SCHEMA"term, col, doc, cnt" "term, col, doc, cnt" | |||
| 27314 | #define FTS5_VOCAB_ROW_SCHEMA"term, doc, cnt" "term, doc, cnt" | |||
| 27315 | #define FTS5_VOCAB_INST_SCHEMA"term, doc, col, offset" "term, doc, col, offset" | |||
| 27316 | ||||
| 27317 | /* | |||
| 27318 | ** Bits for the mask used as the idxNum value by xBestIndex/xFilter. | |||
| 27319 | */ | |||
| 27320 | #define FTS5_VOCAB_TERM_EQ0x0100 0x0100 | |||
| 27321 | #define FTS5_VOCAB_TERM_GE0x0200 0x0200 | |||
| 27322 | #define FTS5_VOCAB_TERM_LE0x0400 0x0400 | |||
| 27323 | ||||
| 27324 | #define FTS5_VOCAB_COLUSED_MASK0xFF 0xFF | |||
| 27325 | ||||
| 27326 | ||||
| 27327 | /* | |||
| 27328 | ** Translate a string containing an fts5vocab table type to an | |||
| 27329 | ** FTS5_VOCAB_XXX constant. If successful, set *peType to the output | |||
| 27330 | ** value and return SQLITE_OK. Otherwise, set *pzErr to an error message | |||
| 27331 | ** and return SQLITE_ERROR. | |||
| 27332 | */ | |||
| 27333 | static int fts5VocabTableType(const char *zType, char **pzErr, int *peType){ | |||
| 27334 | int rc = SQLITE_OK0; | |||
| 27335 | char *zCopy = sqlite3Fts5Strndup(&rc, zType, -1); | |||
| 27336 | if( rc==SQLITE_OK0 ){ | |||
| 27337 | sqlite3Fts5Dequote(zCopy); | |||
| 27338 | if( sqlite3_stricmpsqlite3_api->stricmp(zCopy, "col")==0 ){ | |||
| 27339 | *peType = FTS5_VOCAB_COL0; | |||
| 27340 | }else | |||
| 27341 | ||||
| 27342 | if( sqlite3_stricmpsqlite3_api->stricmp(zCopy, "row")==0 ){ | |||
| 27343 | *peType = FTS5_VOCAB_ROW1; | |||
| 27344 | }else | |||
| 27345 | if( sqlite3_stricmpsqlite3_api->stricmp(zCopy, "instance")==0 ){ | |||
| 27346 | *peType = FTS5_VOCAB_INSTANCE2; | |||
| 27347 | }else | |||
| 27348 | { | |||
| 27349 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("fts5vocab: unknown table type: %Q", zCopy); | |||
| 27350 | rc = SQLITE_ERROR1; | |||
| 27351 | } | |||
| 27352 | sqlite3_freesqlite3_api->free(zCopy); | |||
| 27353 | } | |||
| 27354 | ||||
| 27355 | return rc; | |||
| 27356 | } | |||
| 27357 | ||||
| 27358 | ||||
| 27359 | /* | |||
| 27360 | ** The xDisconnect() virtual table method. | |||
| 27361 | */ | |||
| 27362 | static int fts5VocabDisconnectMethod(sqlite3_vtab *pVtab){ | |||
| 27363 | Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab; | |||
| 27364 | sqlite3_freesqlite3_api->free(pTab); | |||
| 27365 | return SQLITE_OK0; | |||
| 27366 | } | |||
| 27367 | ||||
| 27368 | /* | |||
| 27369 | ** The xDestroy() virtual table method. | |||
| 27370 | */ | |||
| 27371 | static int fts5VocabDestroyMethod(sqlite3_vtab *pVtab){ | |||
| 27372 | Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab; | |||
| 27373 | sqlite3_freesqlite3_api->free(pTab); | |||
| 27374 | return SQLITE_OK0; | |||
| 27375 | } | |||
| 27376 | ||||
| 27377 | /* | |||
| 27378 | ** This function is the implementation of both the xConnect and xCreate | |||
| 27379 | ** methods of the FTS3 virtual table. | |||
| 27380 | ** | |||
| 27381 | ** The argv[] array contains the following: | |||
| 27382 | ** | |||
| 27383 | ** argv[0] -> module name ("fts5vocab") | |||
| 27384 | ** argv[1] -> database name | |||
| 27385 | ** argv[2] -> table name | |||
| 27386 | ** | |||
| 27387 | ** then: | |||
| 27388 | ** | |||
| 27389 | ** argv[3] -> name of fts5 table | |||
| 27390 | ** argv[4] -> type of fts5vocab table | |||
| 27391 | ** | |||
| 27392 | ** or, for tables in the TEMP schema only. | |||
| 27393 | ** | |||
| 27394 | ** argv[3] -> name of fts5 tables database | |||
| 27395 | ** argv[4] -> name of fts5 table | |||
| 27396 | ** argv[5] -> type of fts5vocab table | |||
| 27397 | */ | |||
| 27398 | static int fts5VocabInitVtab( | |||
| 27399 | sqlite3 *db, /* The SQLite database connection */ | |||
| 27400 | void *pAux, /* Pointer to Fts5Global object */ | |||
| 27401 | int argc, /* Number of elements in argv array */ | |||
| 27402 | const char * const *argv, /* xCreate/xConnect argument array */ | |||
| 27403 | sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ | |||
| 27404 | char **pzErr /* Write any error message here */ | |||
| 27405 | ){ | |||
| 27406 | const char *azSchema[] = { | |||
| 27407 | "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA"term, col, doc, cnt" ")", | |||
| 27408 | "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA"term, doc, cnt" ")", | |||
| 27409 | "CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA"term, doc, col, offset" ")" | |||
| 27410 | }; | |||
| 27411 | ||||
| 27412 | Fts5VocabTable *pRet = 0; | |||
| 27413 | int rc = SQLITE_OK0; /* Return code */ | |||
| 27414 | int bDb; | |||
| 27415 | ||||
| 27416 | bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0); | |||
| 27417 | ||||
| 27418 | if( argc!=5 && bDb==0 ){ | |||
| 27419 | *pzErr = sqlite3_mprintfsqlite3_api->mprintf("wrong number of vtable arguments"); | |||
| 27420 | rc = SQLITE_ERROR1; | |||
| 27421 | }else{ | |||
| 27422 | i64 nByte; /* Bytes of space to allocate */ | |||
| 27423 | const char *zDb = bDb ? argv[3] : argv[1]; | |||
| 27424 | const char *zTab = bDb ? argv[4] : argv[3]; | |||
| 27425 | const char *zType = bDb ? argv[5] : argv[4]; | |||
| 27426 | i64 nDb = strlen(zDb)+1; | |||
| 27427 | i64 nTab = strlen(zTab)+1; | |||
| 27428 | int eType = 0; | |||
| 27429 | ||||
| 27430 | rc = fts5VocabTableType(zType, pzErr, &eType); | |||
| 27431 | if( rc==SQLITE_OK0 ){ | |||
| 27432 | assert( eType>=0 && eType<ArraySize(azSchema) )((void) (0)); | |||
| 27433 | rc = sqlite3_declare_vtabsqlite3_api->declare_vtab(db, azSchema[eType]); | |||
| 27434 | } | |||
| 27435 | ||||
| 27436 | nByte = sizeof(Fts5VocabTable) + nDb + nTab; | |||
| 27437 | pRet = sqlite3Fts5MallocZero(&rc, nByte); | |||
| 27438 | if( pRet ){ | |||
| 27439 | pRet->pGlobal = (Fts5Global*)pAux; | |||
| 27440 | pRet->eType = eType; | |||
| 27441 | pRet->db = db; | |||
| 27442 | pRet->zFts5Tbl = (char*)&pRet[1]; | |||
| 27443 | pRet->zFts5Db = &pRet->zFts5Tbl[nTab]; | |||
| 27444 | memcpy(pRet->zFts5Tbl, zTab, nTab); | |||
| 27445 | memcpy(pRet->zFts5Db, zDb, nDb); | |||
| 27446 | sqlite3Fts5Dequote(pRet->zFts5Tbl); | |||
| 27447 | sqlite3Fts5Dequote(pRet->zFts5Db); | |||
| 27448 | } | |||
| 27449 | } | |||
| 27450 | ||||
| 27451 | *ppVTab = (sqlite3_vtab*)pRet; | |||
| 27452 | return rc; | |||
| 27453 | } | |||
| 27454 | ||||
| 27455 | ||||
| 27456 | /* | |||
| 27457 | ** The xConnect() and xCreate() methods for the virtual table. All the | |||
| 27458 | ** work is done in function fts5VocabInitVtab(). | |||
| 27459 | */ | |||
| 27460 | static int fts5VocabConnectMethod( | |||
| 27461 | sqlite3 *db, /* Database connection */ | |||
| 27462 | void *pAux, /* Pointer to tokenizer hash table */ | |||
| 27463 | int argc, /* Number of elements in argv array */ | |||
| 27464 | const char * const *argv, /* xCreate/xConnect argument array */ | |||
| 27465 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ | |||
| 27466 | char **pzErr /* OUT: sqlite3_malloc'd error message */ | |||
| 27467 | ){ | |||
| 27468 | return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr); | |||
| 27469 | } | |||
| 27470 | static int fts5VocabCreateMethod( | |||
| 27471 | sqlite3 *db, /* Database connection */ | |||
| 27472 | void *pAux, /* Pointer to tokenizer hash table */ | |||
| 27473 | int argc, /* Number of elements in argv array */ | |||
| 27474 | const char * const *argv, /* xCreate/xConnect argument array */ | |||
| 27475 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ | |||
| 27476 | char **pzErr /* OUT: sqlite3_malloc'd error message */ | |||
| 27477 | ){ | |||
| 27478 | return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr); | |||
| 27479 | } | |||
| 27480 | ||||
| 27481 | /* | |||
| 27482 | ** Implementation of the xBestIndex method. | |||
| 27483 | ** | |||
| 27484 | ** Only constraints of the form: | |||
| 27485 | ** | |||
| 27486 | ** term <= ? | |||
| 27487 | ** term == ? | |||
| 27488 | ** term >= ? | |||
| 27489 | ** | |||
| 27490 | ** are interpreted. Less-than and less-than-or-equal are treated | |||
| 27491 | ** identically, as are greater-than and greater-than-or-equal. | |||
| 27492 | */ | |||
| 27493 | static int fts5VocabBestIndexMethod( | |||
| 27494 | sqlite3_vtab *pUnused, | |||
| 27495 | sqlite3_index_info *pInfo | |||
| 27496 | ){ | |||
| 27497 | int i; | |||
| 27498 | int iTermEq = -1; | |||
| 27499 | int iTermGe = -1; | |||
| 27500 | int iTermLe = -1; | |||
| 27501 | int idxNum = (int)pInfo->colUsed; | |||
| 27502 | int nArg = 0; | |||
| 27503 | ||||
| 27504 | UNUSED_PARAM(pUnused)(void)(pUnused); | |||
| 27505 | ||||
| 27506 | assert( (pInfo->colUsed & FTS5_VOCAB_COLUSED_MASK)==pInfo->colUsed )((void) (0)); | |||
| 27507 | ||||
| 27508 | for(i=0; i<pInfo->nConstraint; i++){ | |||
| 27509 | struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; | |||
| 27510 | if( p->usable==0 ) continue; | |||
| 27511 | if( p->iColumn==0 ){ /* term column */ | |||
| 27512 | if( p->op==SQLITE_INDEX_CONSTRAINT_EQ2 ) iTermEq = i; | |||
| 27513 | if( p->op==SQLITE_INDEX_CONSTRAINT_LE8 ) iTermLe = i; | |||
| 27514 | if( p->op==SQLITE_INDEX_CONSTRAINT_LT16 ) iTermLe = i; | |||
| 27515 | if( p->op==SQLITE_INDEX_CONSTRAINT_GE32 ) iTermGe = i; | |||
| 27516 | if( p->op==SQLITE_INDEX_CONSTRAINT_GT4 ) iTermGe = i; | |||
| 27517 | } | |||
| 27518 | } | |||
| 27519 | ||||
| 27520 | if( iTermEq>=0 ){ | |||
| 27521 | idxNum |= FTS5_VOCAB_TERM_EQ0x0100; | |||
| 27522 | pInfo->aConstraintUsage[iTermEq].argvIndex = ++nArg; | |||
| 27523 | pInfo->estimatedCost = 100; | |||
| 27524 | }else{ | |||
| 27525 | pInfo->estimatedCost = 1000000; | |||
| 27526 | if( iTermGe>=0 ){ | |||
| 27527 | idxNum |= FTS5_VOCAB_TERM_GE0x0200; | |||
| 27528 | pInfo->aConstraintUsage[iTermGe].argvIndex = ++nArg; | |||
| 27529 | pInfo->estimatedCost = pInfo->estimatedCost / 2; | |||
| 27530 | } | |||
| 27531 | if( iTermLe>=0 ){ | |||
| 27532 | idxNum |= FTS5_VOCAB_TERM_LE0x0400; | |||
| 27533 | pInfo->aConstraintUsage[iTermLe].argvIndex = ++nArg; | |||
| 27534 | pInfo->estimatedCost = pInfo->estimatedCost / 2; | |||
| 27535 | } | |||
| 27536 | } | |||
| 27537 | ||||
| 27538 | /* This virtual table always delivers results in ascending order of | |||
| 27539 | ** the "term" column (column 0). So if the user has requested this | |||
| 27540 | ** specifically - "ORDER BY term" or "ORDER BY term ASC" - set the | |||
| 27541 | ** sqlite3_index_info.orderByConsumed flag to tell the core the results | |||
| 27542 | ** are already in sorted order. */ | |||
| 27543 | if( pInfo->nOrderBy==1 | |||
| 27544 | && pInfo->aOrderBy[0].iColumn==0 | |||
| 27545 | && pInfo->aOrderBy[0].desc==0 | |||
| 27546 | ){ | |||
| 27547 | pInfo->orderByConsumed = 1; | |||
| 27548 | } | |||
| 27549 | ||||
| 27550 | pInfo->idxNum = idxNum; | |||
| 27551 | return SQLITE_OK0; | |||
| 27552 | } | |||
| 27553 | ||||
| 27554 | /* | |||
| 27555 | ** Implementation of xOpen method. | |||
| 27556 | */ | |||
| 27557 | static int fts5VocabOpenMethod( | |||
| 27558 | sqlite3_vtab *pVTab, | |||
| 27559 | sqlite3_vtab_cursor **ppCsr | |||
| 27560 | ){ | |||
| 27561 | Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab; | |||
| 27562 | Fts5Table *pFts5 = 0; | |||
| 27563 | Fts5VocabCursor *pCsr = 0; | |||
| 27564 | int rc = SQLITE_OK0; | |||
| 27565 | sqlite3_stmt *pStmt = 0; | |||
| 27566 | char *zSql = 0; | |||
| 27567 | ||||
| 27568 | if( pTab->bBusy ){ | |||
| 27569 | pVTab->zErrMsg = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 27570 | "recursive definition for %s.%s", pTab->zFts5Db, pTab->zFts5Tbl | |||
| 27571 | ); | |||
| 27572 | return SQLITE_ERROR1; | |||
| 27573 | } | |||
| 27574 | zSql = sqlite3Fts5Mprintf(&rc, | |||
| 27575 | "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'", | |||
| 27576 | pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl | |||
| 27577 | ); | |||
| 27578 | if( zSql ){ | |||
| 27579 | rc = sqlite3_prepare_v2sqlite3_api->prepare_v2(pTab->db, zSql, -1, &pStmt, 0); | |||
| 27580 | } | |||
| 27581 | sqlite3_freesqlite3_api->free(zSql); | |||
| 27582 | assert( rc==SQLITE_OK || pStmt==0 )((void) (0)); | |||
| 27583 | if( rc==SQLITE_ERROR1 ) rc = SQLITE_OK0; | |||
| 27584 | ||||
| 27585 | pTab->bBusy = 1; | |||
| 27586 | if( pStmt && sqlite3_stepsqlite3_api->step(pStmt)==SQLITE_ROW100 ){ | |||
| 27587 | i64 iId = sqlite3_column_int64sqlite3_api->column_int64(pStmt, 0); | |||
| 27588 | pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId); | |||
| 27589 | } | |||
| 27590 | pTab->bBusy = 0; | |||
| 27591 | ||||
| 27592 | if( rc==SQLITE_OK0 ){ | |||
| 27593 | if( pFts5==0 ){ | |||
| 27594 | rc = sqlite3_finalizesqlite3_api->finalize(pStmt); | |||
| 27595 | pStmt = 0; | |||
| 27596 | if( rc==SQLITE_OK0 ){ | |||
| 27597 | pVTab->zErrMsg = sqlite3_mprintfsqlite3_api->mprintf( | |||
| 27598 | "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl | |||
| 27599 | ); | |||
| 27600 | rc = SQLITE_ERROR1; | |||
| 27601 | } | |||
| 27602 | }else{ | |||
| 27603 | rc = sqlite3Fts5FlushToDisk(pFts5); | |||
| 27604 | } | |||
| 27605 | } | |||
| 27606 | ||||
| 27607 | if( rc==SQLITE_OK0 ){ | |||
| 27608 | i64 nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); | |||
| 27609 | pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte); | |||
| 27610 | } | |||
| 27611 | ||||
| 27612 | if( pCsr ){ | |||
| 27613 | pCsr->pFts5 = pFts5; | |||
| 27614 | pCsr->pStmt = pStmt; | |||
| 27615 | pCsr->aCnt = (i64*)&pCsr[1]; | |||
| 27616 | pCsr->aDoc = &pCsr->aCnt[pFts5->pConfig->nCol]; | |||
| 27617 | }else{ | |||
| 27618 | sqlite3_finalizesqlite3_api->finalize(pStmt); | |||
| 27619 | } | |||
| 27620 | ||||
| 27621 | *ppCsr = (sqlite3_vtab_cursor*)pCsr; | |||
| 27622 | return rc; | |||
| 27623 | } | |||
| 27624 | ||||
| 27625 | /* | |||
| 27626 | ** Restore cursor pCsr to the state it was in immediately after being | |||
| 27627 | ** created by the xOpen() method. | |||
| 27628 | */ | |||
| 27629 | static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ | |||
| 27630 | int nCol = pCsr->pFts5->pConfig->nCol; | |||
| 27631 | pCsr->rowid = 0; | |||
| 27632 | sqlite3Fts5IterClose(pCsr->pIter); | |||
| 27633 | sqlite3Fts5StructureRelease(pCsr->pStruct); | |||
| 27634 | pCsr->pStruct = 0; | |||
| 27635 | pCsr->pIter = 0; | |||
| 27636 | sqlite3_freesqlite3_api->free(pCsr->zLeTerm); | |||
| 27637 | pCsr->nLeTerm = -1; | |||
| 27638 | pCsr->zLeTerm = 0; | |||
| 27639 | pCsr->bEof = 0; | |||
| 27640 | pCsr->iCol = 0; | |||
| 27641 | pCsr->iInstPos = 0; | |||
| 27642 | pCsr->iInstOff = 0; | |||
| 27643 | pCsr->colUsed = 0; | |||
| 27644 | memset(pCsr->aCnt, 0, sizeof(i64)*nCol); | |||
| 27645 | memset(pCsr->aDoc, 0, sizeof(i64)*nCol); | |||
| 27646 | } | |||
| 27647 | ||||
| 27648 | /* | |||
| 27649 | ** Close the cursor. For additional information see the documentation | |||
| 27650 | ** on the xClose method of the virtual table interface. | |||
| 27651 | */ | |||
| 27652 | static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){ | |||
| 27653 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; | |||
| 27654 | fts5VocabResetCursor(pCsr); | |||
| 27655 | sqlite3Fts5BufferFree(&pCsr->term); | |||
| 27656 | sqlite3_finalizesqlite3_api->finalize(pCsr->pStmt); | |||
| 27657 | sqlite3_freesqlite3_api->free(pCsr); | |||
| 27658 | return SQLITE_OK0; | |||
| 27659 | } | |||
| 27660 | ||||
| 27661 | static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){ | |||
| 27662 | int rc = SQLITE_OK0; | |||
| 27663 | ||||
| 27664 | if( sqlite3Fts5IterEof(pCsr->pIter)((pCsr->pIter)->bEof) ){ | |||
| 27665 | pCsr->bEof = 1; | |||
| 27666 | }else{ | |||
| 27667 | const char *zTerm; | |||
| 27668 | int nTerm; | |||
| 27669 | zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); | |||
| 27670 | if( pCsr->nLeTerm>=0 ){ | |||
| 27671 | int nCmp = MIN(nTerm, pCsr->nLeTerm)(((nTerm) < (pCsr->nLeTerm)) ? (nTerm) : (pCsr->nLeTerm )); | |||
| 27672 | int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); | |||
| 27673 | if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){ | |||
| 27674 | pCsr->bEof = 1; | |||
| 27675 | } | |||
| 27676 | } | |||
| 27677 | ||||
| 27678 | sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); | |||
| 27679 | } | |||
| 27680 | return rc; | |||
| 27681 | } | |||
| 27682 | ||||
| 27683 | static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ | |||
| 27684 | int eDetail = pCsr->pFts5->pConfig->eDetail; | |||
| 27685 | int rc = SQLITE_OK0; | |||
| 27686 | Fts5IndexIter *pIter = pCsr->pIter; | |||
| 27687 | i64 *pp = &pCsr->iInstPos; | |||
| 27688 | int *po = &pCsr->iInstOff; | |||
| 27689 | ||||
| 27690 | assert( sqlite3Fts5IterEof(pIter)==0 )((void) (0)); | |||
| 27691 | assert( pCsr->bEof==0 )((void) (0)); | |||
| 27692 | while( eDetail==FTS5_DETAIL_NONE1 | |||
| 27693 | || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp) | |||
| 27694 | ){ | |||
| 27695 | pCsr->iInstPos = 0; | |||
| 27696 | pCsr->iInstOff = 0; | |||
| 27697 | ||||
| 27698 | rc = sqlite3Fts5IterNextScan(pCsr->pIter); | |||
| 27699 | if( rc==SQLITE_OK0 ){ | |||
| 27700 | rc = fts5VocabInstanceNewTerm(pCsr); | |||
| 27701 | if( pCsr->bEof || eDetail==FTS5_DETAIL_NONE1 ) break; | |||
| 27702 | } | |||
| 27703 | if( rc ){ | |||
| 27704 | pCsr->bEof = 1; | |||
| 27705 | break; | |||
| 27706 | } | |||
| 27707 | } | |||
| 27708 | ||||
| 27709 | return rc; | |||
| 27710 | } | |||
| 27711 | ||||
| 27712 | /* | |||
| 27713 | ** Advance the cursor to the next row in the table. | |||
| 27714 | */ | |||
| 27715 | static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ | |||
| 27716 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; | |||
| 27717 | Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; | |||
| 27718 | int nCol = pCsr->pFts5->pConfig->nCol; | |||
| 27719 | int rc; | |||
| 27720 | ||||
| 27721 | rc = sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct); | |||
| 27722 | if( rc!=SQLITE_OK0 ) return rc; | |||
| 27723 | pCsr->rowid++; | |||
| 27724 | ||||
| 27725 | if( pTab->eType==FTS5_VOCAB_INSTANCE2 ){ | |||
| 27726 | return fts5VocabInstanceNext(pCsr); | |||
| 27727 | } | |||
| 27728 | ||||
| 27729 | if( pTab->eType==FTS5_VOCAB_COL0 ){ | |||
| 27730 | for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ | |||
| 27731 | if( pCsr->aDoc[pCsr->iCol] ) break; | |||
| 27732 | } | |||
| 27733 | } | |||
| 27734 | ||||
| 27735 | if( pTab->eType!=FTS5_VOCAB_COL0 || pCsr->iCol>=nCol ){ | |||
| 27736 | if( sqlite3Fts5IterEof(pCsr->pIter)((pCsr->pIter)->bEof) ){ | |||
| 27737 | pCsr->bEof = 1; | |||
| 27738 | }else{ | |||
| 27739 | const char *zTerm; | |||
| 27740 | int nTerm; | |||
| 27741 | ||||
| 27742 | zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); | |||
| 27743 | assert( nTerm>=0 )((void) (0)); | |||
| 27744 | if( pCsr->nLeTerm>=0 ){ | |||
| 27745 | int nCmp = MIN(nTerm, pCsr->nLeTerm)(((nTerm) < (pCsr->nLeTerm)) ? (nTerm) : (pCsr->nLeTerm )); | |||
| 27746 | int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); | |||
| 27747 | if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){ | |||
| 27748 | pCsr->bEof = 1; | |||
| 27749 | return SQLITE_OK0; | |||
| 27750 | } | |||
| 27751 | } | |||
| 27752 | ||||
| 27753 | sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); | |||
| 27754 | memset(pCsr->aCnt, 0, nCol * sizeof(i64)); | |||
| 27755 | memset(pCsr->aDoc, 0, nCol * sizeof(i64)); | |||
| 27756 | pCsr->iCol = 0; | |||
| 27757 | ||||
| 27758 | assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW )((void) (0)); | |||
| 27759 | while( rc==SQLITE_OK0 ){ | |||
| 27760 | int eDetail = pCsr->pFts5->pConfig->eDetail; | |||
| 27761 | const u8 *pPos; int nPos; /* Position list */ | |||
| 27762 | i64 iPos = 0; /* 64-bit position read from poslist */ | |||
| 27763 | int iOff = 0; /* Current offset within position list */ | |||
| 27764 | ||||
| 27765 | pPos = pCsr->pIter->pData; | |||
| 27766 | nPos = pCsr->pIter->nData; | |||
| 27767 | ||||
| 27768 | switch( pTab->eType ){ | |||
| 27769 | case FTS5_VOCAB_ROW1: | |||
| 27770 | /* Do not bother counting the number of instances if the "cnt" | |||
| 27771 | ** column is not being read (according to colUsed). */ | |||
| 27772 | if( eDetail==FTS5_DETAIL_FULL0 && (pCsr->colUsed & 0x04) ){ | |||
| 27773 | while( iPos<nPos ){ | |||
| 27774 | u32 ii; | |||
| 27775 | fts5FastGetVarint32(pPos, iPos, ii){ ii = (pPos)[iPos++]; if( ii & 0x80 ){ iPos--; iPos += sqlite3Fts5GetVarint32 (&(pPos)[iPos],(u32*)&(ii)); } }; | |||
| 27776 | if( ii==1 ){ | |||
| 27777 | /* New column in the position list */ | |||
| 27778 | fts5FastGetVarint32(pPos, iPos, ii){ ii = (pPos)[iPos++]; if( ii & 0x80 ){ iPos--; iPos += sqlite3Fts5GetVarint32 (&(pPos)[iPos],(u32*)&(ii)); } }; | |||
| 27779 | }else{ | |||
| 27780 | /* An instance - increment pCsr->aCnt[] */ | |||
| 27781 | pCsr->aCnt[0]++; | |||
| 27782 | } | |||
| 27783 | } | |||
| 27784 | } | |||
| 27785 | pCsr->aDoc[0]++; | |||
| 27786 | break; | |||
| 27787 | ||||
| 27788 | case FTS5_VOCAB_COL0: | |||
| 27789 | if( eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 27790 | int iCol = -1; | |||
| 27791 | while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ | |||
| 27792 | int ii = FTS5_POS2COLUMN(iPos)(int)((iPos >> 32) & 0x7FFFFFFF); | |||
| 27793 | if( iCol!=ii ){ | |||
| 27794 | if( ii>=nCol ){ | |||
| 27795 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 27796 | break; | |||
| 27797 | } | |||
| 27798 | pCsr->aDoc[ii]++; | |||
| 27799 | iCol = ii; | |||
| 27800 | } | |||
| 27801 | pCsr->aCnt[ii]++; | |||
| 27802 | } | |||
| 27803 | }else if( eDetail==FTS5_DETAIL_COLUMNS2 ){ | |||
| 27804 | while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){ | |||
| 27805 | assert_nc( iPos>=0 && iPos<nCol )((void) (0)); | |||
| 27806 | if( iPos>=nCol ){ | |||
| 27807 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 27808 | break; | |||
| 27809 | } | |||
| 27810 | pCsr->aDoc[iPos]++; | |||
| 27811 | } | |||
| 27812 | }else{ | |||
| 27813 | assert( eDetail==FTS5_DETAIL_NONE )((void) (0)); | |||
| 27814 | pCsr->aDoc[0]++; | |||
| 27815 | } | |||
| 27816 | break; | |||
| 27817 | ||||
| 27818 | default: | |||
| 27819 | assert( pTab->eType==FTS5_VOCAB_INSTANCE )((void) (0)); | |||
| 27820 | break; | |||
| 27821 | } | |||
| 27822 | ||||
| 27823 | if( rc==SQLITE_OK0 ){ | |||
| 27824 | rc = sqlite3Fts5IterNextScan(pCsr->pIter); | |||
| 27825 | } | |||
| 27826 | if( pTab->eType==FTS5_VOCAB_INSTANCE2 ) break; | |||
| 27827 | ||||
| 27828 | if( rc==SQLITE_OK0 ){ | |||
| 27829 | zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); | |||
| 27830 | if( nTerm!=pCsr->term.n | |||
| 27831 | || (nTerm>0 && memcmp(zTerm, pCsr->term.p, nTerm)) | |||
| 27832 | ){ | |||
| 27833 | break; | |||
| 27834 | } | |||
| 27835 | if( sqlite3Fts5IterEof(pCsr->pIter)((pCsr->pIter)->bEof) ) break; | |||
| 27836 | } | |||
| 27837 | } | |||
| 27838 | } | |||
| 27839 | } | |||
| 27840 | ||||
| 27841 | if( rc==SQLITE_OK0 && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL0 ){ | |||
| 27842 | for(/* noop */; pCsr->iCol<nCol && pCsr->aDoc[pCsr->iCol]==0; pCsr->iCol++); | |||
| 27843 | if( pCsr->iCol==nCol ){ | |||
| 27844 | rc = FTS5_CORRUPT(11 | (1<<8)); | |||
| 27845 | } | |||
| 27846 | } | |||
| 27847 | return rc; | |||
| 27848 | } | |||
| 27849 | ||||
| 27850 | /* | |||
| 27851 | ** This is the xFilter implementation for the virtual table. | |||
| 27852 | */ | |||
| 27853 | static int fts5VocabFilterMethod( | |||
| 27854 | sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ | |||
| 27855 | int idxNum, /* Strategy index */ | |||
| 27856 | const char *zUnused, /* Unused */ | |||
| 27857 | int nUnused, /* Number of elements in apVal */ | |||
| 27858 | sqlite3_value **apVal /* Arguments for the indexing scheme */ | |||
| 27859 | ){ | |||
| 27860 | Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; | |||
| 27861 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; | |||
| 27862 | int eType = pTab->eType; | |||
| 27863 | int rc = SQLITE_OK0; | |||
| 27864 | ||||
| 27865 | int iVal = 0; | |||
| 27866 | int f = FTS5INDEX_QUERY_SCAN0x0008; | |||
| 27867 | const char *zTerm = 0; | |||
| 27868 | int nTerm = 0; | |||
| 27869 | ||||
| 27870 | sqlite3_value *pEq = 0; | |||
| 27871 | sqlite3_value *pGe = 0; | |||
| 27872 | sqlite3_value *pLe = 0; | |||
| 27873 | ||||
| 27874 | UNUSED_PARAM2(zUnused, nUnused)(void)(zUnused), (void)(nUnused); | |||
| 27875 | ||||
| 27876 | fts5VocabResetCursor(pCsr); | |||
| 27877 | if( idxNum & FTS5_VOCAB_TERM_EQ0x0100 ) pEq = apVal[iVal++]; | |||
| 27878 | if( idxNum & FTS5_VOCAB_TERM_GE0x0200 ) pGe = apVal[iVal++]; | |||
| 27879 | if( idxNum & FTS5_VOCAB_TERM_LE0x0400 ) pLe = apVal[iVal++]; | |||
| 27880 | pCsr->colUsed = (idxNum & FTS5_VOCAB_COLUSED_MASK0xFF); | |||
| 27881 | ||||
| 27882 | if( pEq ){ | |||
| 27883 | zTerm = (const char *)sqlite3_value_textsqlite3_api->value_text(pEq); | |||
| 27884 | nTerm = sqlite3_value_bytessqlite3_api->value_bytes(pEq); | |||
| 27885 | f = FTS5INDEX_QUERY_NOTOKENDATA0x0080; | |||
| 27886 | }else{ | |||
| 27887 | if( pGe ){ | |||
| 27888 | zTerm = (const char *)sqlite3_value_textsqlite3_api->value_text(pGe); | |||
| 27889 | nTerm = sqlite3_value_bytessqlite3_api->value_bytes(pGe); | |||
| 27890 | } | |||
| 27891 | if( pLe ){ | |||
| 27892 | const char *zCopy = (const char *)sqlite3_value_textsqlite3_api->value_text(pLe); | |||
| 27893 | if( zCopy==0 ) zCopy = ""; | |||
| 27894 | pCsr->nLeTerm = sqlite3_value_bytessqlite3_api->value_bytes(pLe); | |||
| 27895 | pCsr->zLeTerm = sqlite3_malloc64sqlite3_api->malloc64((i64)pCsr->nLeTerm+1); | |||
| 27896 | if( pCsr->zLeTerm==0 ){ | |||
| 27897 | rc = SQLITE_NOMEM7; | |||
| 27898 | }else{ | |||
| 27899 | memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1); | |||
| 27900 | } | |||
| 27901 | } | |||
| 27902 | } | |||
| 27903 | ||||
| 27904 | if( rc==SQLITE_OK0 ){ | |||
| 27905 | Fts5Index *pIndex = pCsr->pFts5->pIndex; | |||
| 27906 | rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); | |||
| 27907 | if( rc==SQLITE_OK0 ){ | |||
| 27908 | pCsr->pStruct = sqlite3Fts5StructureRef(pIndex); | |||
| 27909 | } | |||
| 27910 | } | |||
| 27911 | if( rc==SQLITE_OK0 && eType==FTS5_VOCAB_INSTANCE2 ){ | |||
| 27912 | rc = fts5VocabInstanceNewTerm(pCsr); | |||
| 27913 | } | |||
| 27914 | if( rc==SQLITE_OK0 && !pCsr->bEof | |||
| 27915 | && (eType!=FTS5_VOCAB_INSTANCE2 | |||
| 27916 | || pCsr->pFts5->pConfig->eDetail!=FTS5_DETAIL_NONE1) | |||
| 27917 | ){ | |||
| 27918 | rc = fts5VocabNextMethod(pCursor); | |||
| 27919 | } | |||
| 27920 | ||||
| 27921 | return rc; | |||
| 27922 | } | |||
| 27923 | ||||
| 27924 | /* | |||
| 27925 | ** This is the xEof method of the virtual table. SQLite calls this | |||
| 27926 | ** routine to find out if it has reached the end of a result set. | |||
| 27927 | */ | |||
| 27928 | static int fts5VocabEofMethod(sqlite3_vtab_cursor *pCursor){ | |||
| 27929 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; | |||
| 27930 | return pCsr->bEof; | |||
| 27931 | } | |||
| 27932 | ||||
| 27933 | static int fts5VocabColumnMethod( | |||
| 27934 | sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ | |||
| 27935 | sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ | |||
| 27936 | int iCol /* Index of column to read value from */ | |||
| 27937 | ){ | |||
| 27938 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; | |||
| 27939 | int eDetail = pCsr->pFts5->pConfig->eDetail; | |||
| 27940 | int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType; | |||
| 27941 | i64 iVal = 0; | |||
| 27942 | ||||
| 27943 | if( iCol==0 ){ | |||
| 27944 | sqlite3_result_textsqlite3_api->result_text( | |||
| 27945 | pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT((sqlite3_destructor_type)-1) | |||
| 27946 | ); | |||
| 27947 | }else if( eType==FTS5_VOCAB_COL0 ){ | |||
| 27948 | assert( iCol==1 || iCol==2 || iCol==3 )((void) (0)); | |||
| 27949 | if( iCol==1 ){ | |||
| 27950 | if( eDetail!=FTS5_DETAIL_NONE1 ){ | |||
| 27951 | const char *z = pCsr->pFts5->pConfig->azCol[pCsr->iCol]; | |||
| 27952 | sqlite3_result_textsqlite3_api->result_text(pCtx, z, -1, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 27953 | } | |||
| 27954 | }else if( iCol==2 ){ | |||
| 27955 | iVal = pCsr->aDoc[pCsr->iCol]; | |||
| 27956 | }else{ | |||
| 27957 | iVal = pCsr->aCnt[pCsr->iCol]; | |||
| 27958 | } | |||
| 27959 | }else if( eType==FTS5_VOCAB_ROW1 ){ | |||
| 27960 | assert( iCol==1 || iCol==2 )((void) (0)); | |||
| 27961 | if( iCol==1 ){ | |||
| 27962 | iVal = pCsr->aDoc[0]; | |||
| 27963 | }else{ | |||
| 27964 | iVal = pCsr->aCnt[0]; | |||
| 27965 | } | |||
| 27966 | }else{ | |||
| 27967 | assert( eType==FTS5_VOCAB_INSTANCE )((void) (0)); | |||
| 27968 | switch( iCol ){ | |||
| 27969 | case 1: | |||
| 27970 | sqlite3_result_int64sqlite3_api->result_int64(pCtx, pCsr->pIter->iRowid); | |||
| 27971 | break; | |||
| 27972 | case 2: { | |||
| 27973 | int ii = -1; | |||
| 27974 | if( eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 27975 | ii = FTS5_POS2COLUMN(pCsr->iInstPos)(int)((pCsr->iInstPos >> 32) & 0x7FFFFFFF); | |||
| 27976 | }else if( eDetail==FTS5_DETAIL_COLUMNS2 ){ | |||
| 27977 | ii = (int)pCsr->iInstPos; | |||
| 27978 | } | |||
| 27979 | if( ii>=0 && ii<pCsr->pFts5->pConfig->nCol ){ | |||
| 27980 | const char *z = pCsr->pFts5->pConfig->azCol[ii]; | |||
| 27981 | sqlite3_result_textsqlite3_api->result_text(pCtx, z, -1, SQLITE_STATIC((sqlite3_destructor_type)0)); | |||
| 27982 | } | |||
| 27983 | break; | |||
| 27984 | } | |||
| 27985 | default: { | |||
| 27986 | assert( iCol==3 )((void) (0)); | |||
| 27987 | if( eDetail==FTS5_DETAIL_FULL0 ){ | |||
| 27988 | int ii = FTS5_POS2OFFSET(pCsr->iInstPos)(int)(pCsr->iInstPos & 0x7FFFFFFF); | |||
| 27989 | sqlite3_result_intsqlite3_api->result_int(pCtx, ii); | |||
| 27990 | } | |||
| 27991 | break; | |||
| 27992 | } | |||
| 27993 | } | |||
| 27994 | } | |||
| 27995 | ||||
| 27996 | if( iVal>0 ) sqlite3_result_int64sqlite3_api->result_int64(pCtx, iVal); | |||
| 27997 | return SQLITE_OK0; | |||
| 27998 | } | |||
| 27999 | ||||
| 28000 | /* | |||
| 28001 | ** This is the xRowid method. The SQLite core calls this routine to | |||
| 28002 | ** retrieve the rowid for the current row of the result set. The | |||
| 28003 | ** rowid should be written to *pRowid. | |||
| 28004 | */ | |||
| 28005 | static int fts5VocabRowidMethod( | |||
| 28006 | sqlite3_vtab_cursor *pCursor, | |||
| 28007 | sqlite_int64 *pRowid | |||
| 28008 | ){ | |||
| 28009 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; | |||
| 28010 | *pRowid = pCsr->rowid; | |||
| 28011 | return SQLITE_OK0; | |||
| 28012 | } | |||
| 28013 | ||||
| 28014 | static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ | |||
| 28015 | static const sqlite3_module fts5Vocab = { | |||
| 28016 | /* iVersion */ 2, | |||
| 28017 | /* xCreate */ fts5VocabCreateMethod, | |||
| 28018 | /* xConnect */ fts5VocabConnectMethod, | |||
| 28019 | /* xBestIndex */ fts5VocabBestIndexMethod, | |||
| 28020 | /* xDisconnect */ fts5VocabDisconnectMethod, | |||
| 28021 | /* xDestroy */ fts5VocabDestroyMethod, | |||
| 28022 | /* xOpen */ fts5VocabOpenMethod, | |||
| 28023 | /* xClose */ fts5VocabCloseMethod, | |||
| 28024 | /* xFilter */ fts5VocabFilterMethod, | |||
| 28025 | /* xNext */ fts5VocabNextMethod, | |||
| 28026 | /* xEof */ fts5VocabEofMethod, | |||
| 28027 | /* xColumn */ fts5VocabColumnMethod, | |||
| 28028 | /* xRowid */ fts5VocabRowidMethod, | |||
| 28029 | /* xUpdate */ 0, | |||
| 28030 | /* xBegin */ 0, | |||
| 28031 | /* xSync */ 0, | |||
| 28032 | /* xCommit */ 0, | |||
| 28033 | /* xRollback */ 0, | |||
| 28034 | /* xFindFunction */ 0, | |||
| 28035 | /* xRename */ 0, | |||
| 28036 | /* xSavepoint */ 0, | |||
| 28037 | /* xRelease */ 0, | |||
| 28038 | /* xRollbackTo */ 0, | |||
| 28039 | /* xShadowName */ 0, | |||
| 28040 | /* xIntegrity */ 0 | |||
| 28041 | }; | |||
| 28042 | void *p = (void*)pGlobal; | |||
| 28043 | ||||
| 28044 | return sqlite3_create_module_v2sqlite3_api->create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); | |||
| 28045 | } | |||
| 28046 | ||||
| 28047 | ||||
| 28048 | /* Here ends the fts5.c composite file. */ | |||
| 28049 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ |