Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add missing comments and fix compiler warnings in new FTS3/4 code. Other minor fixes too. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | experimental |
Files: | files | file ages | folders |
SHA1: |
1c9c70fec3c88319f7b2efe5316694a6 |
User & Date: | dan 2010-10-22 16:44:39.000 |
Context
2010-10-22
| ||
19:03 | Add new test file fts3defer2.test. (check-in: 5a4d5bfcae user: dan tags: experimental) | |
16:44 | Add missing comments and fix compiler warnings in new FTS3/4 code. Other minor fixes too. (check-in: 1c9c70fec3 user: dan tags: experimental) | |
2010-10-21
| ||
15:49 | Merge trunk changes into experimental branch. (check-in: fd1e5cade0 user: dan tags: experimental) | |
Changes
Changes to ext/fts3/fts3.c.
︙ | ︙ | |||
966 967 968 969 970 971 972 | return rc; } }else{ return SQLITE_OK; } } | | > > > > > > > > > > > > > > < | < | < > > > | | | | | | > | 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 | return rc; } }else{ return SQLITE_OK; } } /* ** This function is used to process a single interior node when searching ** a b-tree for a term or term prefix. The node data is passed to this ** function via the zNode/nNode parameters. The term to search for is ** passed in zTerm/nTerm. ** ** If piFirst is not NULL, then this function sets *piFirst to the blockid ** of the child node that heads the sub-tree that may contain the term. ** ** If piLast is not NULL, then *piLast is set to the right-most child node ** that heads a sub-tree that may contain a term for which zTerm/nTerm is ** a prefix. ** ** If an OOM error occurs, SQLITE_NOMEM is returned. Otherwise, SQLITE_OK. */ static int fts3ScanInteriorNode( Fts3Table *p, /* Virtual table handle */ const char *zTerm, /* Term to select leaves for */ int nTerm, /* Size of term zTerm in bytes */ const char *zNode, /* Buffer containing segment interior node */ int nNode, /* Size of buffer at zNode */ sqlite3_int64 *piFirst, /* OUT: Selected child node */ sqlite3_int64 *piLast /* OUT: Selected child node */ ){ int rc = SQLITE_OK; /* Return code */ const char *zCsr = zNode; /* Cursor to iterate through node */ const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ char *zBuffer = 0; /* Buffer to load terms into */ int nAlloc = 0; /* Size of allocated buffer */ int isFirstTerm = 1; /* True when processing first term on page */ sqlite3_int64 iChild; /* Block id of child node to descend to */ /* Skip over the 'height' varint that occurs at the start of every ** interior node. Then load the blockid of the left-child of the b-tree ** node into variable iChild. */ zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); while( zCsr<zEnd && (piFirst || piLast) ){ int cmp; /* memcmp() result */ int nSuffix; /* Size of term suffix */ int nPrefix = 0; /* Size of term prefix */ int nBuffer; /* Total term size */ /* Load the next term on the node into zBuffer. Use realloc() to expand ** the size of zBuffer if required. */ if( !isFirstTerm ){ zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix); } isFirstTerm = 0; zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix); if( nPrefix+nSuffix>nAlloc ){ char *zNew; |
︙ | ︙ | |||
1048 1049 1050 1051 1052 1053 1054 | sqlite3_free(zBuffer); return rc; } /* | | | | | | | > > > > > | > | | | < > > | | > > > | 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 | sqlite3_free(zBuffer); return rc; } /* ** The buffer pointed to by argument zNode (size nNode bytes) contains an ** interior node of a b-tree segment. The zTerm buffer (size nTerm bytes) ** contains a term. This function searches the sub-tree headed by the zNode ** node for the range of leaf nodes that may contain the specified term ** or terms for which the specified term is a prefix. ** ** If piLeaf is not NULL, then *piLeaf is set to the blockid of the ** left-most leaf node in the tree that may contain the specified term. ** If piLeaf2 is not NULL, then *piLeaf2 is set to the blockid of the ** right-most leaf node that may contain a term for which the specified ** term is a prefix. ** ** It is possible that the range of returned leaf nodes does not contain ** the specified term or any terms for which it is a prefix. However, if the ** segment does contain any such terms, they are stored within the identified ** range. Because this function only inspects interior segment nodes (and ** never loads leaf nodes into memory), it is not possible to be sure. ** ** If an error occurs, an error code other than SQLITE_OK is returned. */ static int fts3SelectLeaf( Fts3Table *p, /* Virtual table handle */ const char *zTerm, /* Term to select leaves for */ int nTerm, /* Size of term zTerm in bytes */ const char *zNode, /* Buffer containing segment interior node */ int nNode, /* Size of buffer at zNode */ sqlite3_int64 *piLeaf, /* Selected leaf node */ sqlite3_int64 *piLeaf2 /* Selected leaf node */ ){ int rc; /* Return code */ int iHeight; /* Height of this node in tree */ assert( piLeaf || piLeaf2 ); sqlite3Fts3GetVarint32(zNode, &iHeight); rc = fts3ScanInteriorNode(p, zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); if( rc==SQLITE_OK && iHeight>1 ){ char *zBlob = 0; /* Blob read from %_segments table */ int nBlob; /* Size of zBlob in bytes */ if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob); if( rc==SQLITE_OK ){ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); } sqlite3_free(zBlob); piLeaf = 0; zBlob = 0; } rc = sqlite3Fts3ReadBlock(p, piLeaf ? *piLeaf : *piLeaf2, &zBlob, &nBlob); if( rc==SQLITE_OK ){ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2); } sqlite3_free(zBlob); } return rc; } /* ** This function is used to create delta-encoded serialized lists of FTS3 |
︙ | ︙ | |||
1942 1943 1944 1945 1946 1947 1948 | if( sqlite3_column_int64(pStmt, 1)==0 ){ /* The entire segment is stored on the root node (which must be a ** leaf). Do not bother inspecting any data in this case, just ** create a Fts3SegReader to scan the single leaf. */ rc = sqlite3Fts3SegReaderNew(p, iAge, 0, 0, 0, zRoot, nRoot, &pNew); }else{ | < | | < < < < < < < | < | < < < < < < > | 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 | if( sqlite3_column_int64(pStmt, 1)==0 ){ /* The entire segment is stored on the root node (which must be a ** leaf). Do not bother inspecting any data in this case, just ** create a Fts3SegReader to scan the single leaf. */ rc = sqlite3Fts3SegReaderNew(p, iAge, 0, 0, 0, zRoot, nRoot, &pNew); }else{ sqlite3_int64 i1; /* First leaf that may contain zTerm */ sqlite3_int64 i2; /* Final leaf that may contain zTerm */ rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1, (isPrefix?&i2:0)); if( isPrefix==0 ) i2 = i1; if( rc==SQLITE_OK ){ rc = sqlite3Fts3SegReaderNew(p, iAge, i1, i2, 0, 0, 0, &pNew); } } assert( (pNew==0)==(rc!=SQLITE_OK) ); /* If a new Fts3SegReader was allocated, add it to the array. */ if( rc==SQLITE_OK ){ rc = fts3SegReaderArrayAdd(&pArray, pNew); } if( rc==SQLITE_OK ){ rc = sqlite3Fts3SegReaderCost(pCsr, pNew, &pArray->nCost); } iAge++; } if( rc==SQLITE_DONE ){ rc = sqlite3_reset(pStmt); }else{ sqlite3_reset(pStmt); } |
︙ | ︙ | |||
2105 2106 2107 2108 2109 2110 2111 | } } } } return rc; } | > > > > > > > | > > > < | 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 | } } } } return rc; } /* ** This function removes the position information from a doclist. When ** called, buffer aList (size *pnList bytes) contains a doclist that includes ** position information. This function removes the position information so ** that aList contains only docids, and adjusts *pnList to reflect the new ** (possibly reduced) size of the doclist. */ static void fts3DoclistStripPositions( char *aList, /* IN/OUT: Buffer containing doclist */ int *pnList /* IN/OUT: Size of doclist in bytes */ ){ if( aList ){ char *aEnd = &aList[*pnList]; /* Pointer to one byte after EOF */ char *p = aList; /* Input cursor */ char *pOut = aList; /* Output cursor */ while( p<aEnd ){ sqlite3_int64 delta; p += sqlite3Fts3GetVarint(p, &delta); fts3PoslistCopy(0, &p); pOut += sqlite3Fts3PutVarint(pOut, delta); } |
︙ | ︙ | |||
2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 | *pnOut = nOut; }else{ sqlite3_free(pOut); } return rc; } static int fts3NearMerge( int mergetype, /* MERGE_POS_NEAR or MERGE_NEAR */ int nNear, /* Parameter to NEAR operator */ int nTokenLeft, /* Number of tokens in LHS phrase arg */ char *aLeft, /* Doclist for LHS (incl. positions) */ int nLeft, /* Size of LHS doclist in bytes */ int nTokenRight, /* As nTokenLeft */ char *aRight, /* As aLeft */ int nRight, /* As nRight */ char **paOut, /* OUT: Results of merge (malloced) */ int *pnOut /* OUT: Sized of output buffer */ ){ | > > > > > > > > | | > > > > > > > > > > | > > > > > | | | 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 | *pnOut = nOut; }else{ sqlite3_free(pOut); } return rc; } /* ** This function merges two doclists according to the requirements of a ** NEAR operator. ** ** Both input doclists must include position information. The output doclist ** includes position information if the first argument to this function ** is MERGE_POS_NEAR, or does not if it is MERGE_NEAR. */ static int fts3NearMerge( int mergetype, /* MERGE_POS_NEAR or MERGE_NEAR */ int nNear, /* Parameter to NEAR operator */ int nTokenLeft, /* Number of tokens in LHS phrase arg */ char *aLeft, /* Doclist for LHS (incl. positions) */ int nLeft, /* Size of LHS doclist in bytes */ int nTokenRight, /* As nTokenLeft */ char *aRight, /* As aLeft */ int nRight, /* As nRight */ char **paOut, /* OUT: Results of merge (malloced) */ int *pnOut /* OUT: Sized of output buffer */ ){ char *aOut; /* Buffer to write output doclist to */ int rc; /* Return code */ assert( mergetype==MERGE_POS_NEAR || MERGE_NEAR ); aOut = sqlite3_malloc(nLeft+nRight+1); if( aOut==0 ){ rc = SQLITE_NOMEM; }else{ rc = fts3DoclistMerge(mergetype, nNear+nTokenRight, nNear+nTokenLeft, aOut, pnOut, aLeft, nLeft, aRight, nRight, 0 ); if( rc!=SQLITE_OK ){ sqlite3_free(aOut); aOut = 0; } } *paOut = aOut; return rc; } /* ** This function is used as part of the processing for the snippet() and ** offsets() functions. ** ** Both pLeft and pRight are expression nodes of type FTSQUERY_PHRASE. Both ** have their respective doclists (including position information) loaded ** in Fts3Expr.aDoclist/nDoclist. This function removes all entries from ** each doclist that are not within nNear tokens of a corresponding entry ** in the other doclist. */ int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){ int rc; /* Return code */ assert( pLeft->eType==FTSQUERY_PHRASE ); assert( pRight->eType==FTSQUERY_PHRASE ); assert( pLeft->isLoaded && pRight->isLoaded ); if( pLeft->aDoclist==0 || pRight->aDoclist==0 ){ sqlite3_free(pLeft->aDoclist); sqlite3_free(pRight->aDoclist); pRight->aDoclist = 0; pLeft->aDoclist = 0; rc = SQLITE_OK; }else{ char *aOut; /* Buffer in which to assemble new doclist */ int nOut; /* Size of buffer aOut in bytes */ rc = fts3NearMerge(MERGE_POS_NEAR, nNear, pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist, pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist, &aOut, &nOut ); if( rc!=SQLITE_OK ) return rc; |
︙ | ︙ | |||
2338 2339 2340 2341 2342 2343 2344 | sqlite3_free(pLeft->aDoclist); pLeft->aDoclist = aOut; pLeft->nDoclist = nOut; } return rc; } | < < < < < < < > | | < < < < < < < < < < | < < < < < < < < < < < < < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 | sqlite3_free(pLeft->aDoclist); pLeft->aDoclist = aOut; pLeft->nDoclist = nOut; } return rc; } /* ** Allocate an Fts3SegReaderArray for each token in the expression pExpr. ** The allocated objects are stored in the Fts3PhraseToken.pArray member ** variables of each token structure. */ static int fts3ExprAllocateSegReaders( Fts3Cursor *pCsr, /* FTS3 table */ Fts3Expr *pExpr, /* Expression to create seg-readers for */ int *pnExpr /* OUT: Number of AND'd expressions */ ){ int rc = SQLITE_OK; /* Return code */ if( pCsr->doDeferred ) return SQLITE_OK; if( pnExpr && pExpr->eType!=FTSQUERY_AND ){ (*pnExpr)++; pnExpr = 0; } if( pExpr->eType==FTSQUERY_PHRASE ){ Fts3Phrase *pPhrase = pExpr->pPhrase; int ii; for(ii=0; rc==SQLITE_OK && ii<pPhrase->nToken; ii++){ Fts3PhraseToken *pTok = &pPhrase->aToken[ii]; if( pTok->pArray==0 ){ rc = fts3TermSegReaderArray( pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray ); } } }else{ rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pLeft, pnExpr); if( rc==SQLITE_OK ){ rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pRight, pnExpr); } } return rc; } /* ** Free the Fts3SegReaderArray objects associated with each token in the ** expression pExpr. In other words, this function frees the resources ** allocated by fts3ExprAllocateSegReaders(). */ static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){ if( pExpr ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase ){ int kk; for(kk=0; kk<pPhrase->nToken; kk++){ fts3SegReaderArrayFree(pPhrase->aToken[kk].pArray); pPhrase->aToken[kk].pArray = 0; } } fts3ExprFreeSegReaders(pExpr->pLeft); fts3ExprFreeSegReaders(pExpr->pRight); } } /* ** Return the sum of the costs of all tokens in the expression pExpr. This ** function must be called after Fts3SegReaderArrays have been allocated ** for all tokens using fts3ExprAllocateSegReaders(). */ int fts3ExprCost(Fts3Expr *pExpr){ int nCost; /* Return value */ if( pExpr->eType==FTSQUERY_PHRASE ){ Fts3Phrase *pPhrase = pExpr->pPhrase; int ii; nCost = 0; for(ii=0; ii<pPhrase->nToken; ii++){ nCost += pPhrase->aToken[ii].pArray->nCost; } }else{ nCost = fts3ExprCost(pExpr->pLeft) + fts3ExprCost(pExpr->pRight); } return nCost; } /* ** The following is a helper function (and type) for fts3EvalExpr(). It ** must be called after Fts3SegReaders have been allocated for every token ** in the expression. See the context it is called from in fts3EvalExpr() ** for further explanation. */ typedef struct ExprAndCost ExprAndCost; struct ExprAndCost { Fts3Expr *pExpr; int nCost; }; static void fts3ExprAssignCosts( Fts3Expr *pExpr, /* Expression to create seg-readers for */ ExprAndCost **ppExprCost /* OUT: Write to *ppExprCost */ ){ if( pExpr->eType==FTSQUERY_AND ){ fts3ExprAssignCosts(pExpr->pLeft, ppExprCost); fts3ExprAssignCosts(pExpr->pRight, ppExprCost); }else{ (*ppExprCost)->pExpr = pExpr; (*ppExprCost)->nCost = fts3ExprCost(pExpr);; (*ppExprCost)++; } } /* ** Evaluate the full-text expression pExpr against FTS3 table pTab. Store ** the resulting doclist in *paOut and *pnOut. This routine mallocs for ** the space needed to store the output. The caller is responsible for ** freeing the space when it has finished. ** ** This function is called in two distinct contexts: ** ** * From within the virtual table xFilter() method. In this case, the ** output doclist contains entries for all rows in the table, based on ** data read from the full-text index. ** ** In this case, if the query expression contains one or more tokens that ** are very common, then the returned doclist may contain a superset of ** the documents that actually match the expression. ** ** * From within the virtual table xNext() method. This call is only made ** if the call from within xFilter() found that there were very common ** tokens in the query expression and did return a superset of the ** matching documents. In this case the returned doclist contains only ** entries that correspond to the current row of the table. Instead of ** reading the data for each token from the full-text index, the data is ** already available in-memory in the Fts3PhraseToken.pDeferred structures. ** See fts3EvalDeferred() for how it gets there. ** ** In the first case above, Fts3Cursor.doDeferred==0. In the second (if it is ** required) Fts3Cursor.doDeferred==1. ** ** If the SQLite invokes the snippet(), offsets() or matchinfo() function ** as part of a SELECT on an FTS3 table, this function is called on each ** individual phrase expression in the query. If there were very common tokens ** found in the xFilter() call, then this function is called once for phrase ** for each row visited, and the returned doclist contains entries for the ** current row only. Otherwise, if there were no very common tokens, then this ** function is called once only for each phrase in the query and the returned ** doclist contains entries for all rows of the table. ** ** Fts3Cursor.doDeferred==1 when this function is called on phrases as a ** result of a snippet(), offsets() or matchinfo() invocation. */ static int fts3EvalExpr( Fts3Cursor *p, /* Virtual table cursor handle */ Fts3Expr *pExpr, /* Parsed fts3 expression */ char **paOut, /* OUT: Pointer to malloc'd result buffer */ int *pnOut, /* OUT: Size of buffer at *paOut */ int isReqPos /* Require positions in output buffer */ |
︙ | ︙ | |||
2592 2593 2594 2595 2596 2597 2598 2599 2600 | } } return rc; } /* ** */ | > > > > > > > > > > > > > > | > > > | 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 | } } return rc; } /* ** This function is called from within xNext() for each row visited by ** an FTS3 query. If evaluating the FTS3 query expression within xFilter() ** was able to determine the exact set of matching rows, this function sets ** *pbRes to true and returns SQLITE_IO immediately. ** ** Otherwise, if evaluating the query expression within xFilter() returned a ** superset of the matching documents instead of an exact set (this happens ** when the query includes very common tokens and it is deemed too expensive to ** load their doclists from disk), this function tests if the current row ** really does match the FTS3 query. ** ** If an error occurs, an SQLite error code is returned. Otherwise, SQLITE_OK ** is returned and *pbRes is set to true if the current row matches the ** FTS3 query (and should be included in the results returned to SQLite), or ** false otherwise. */ static int fts3EvalDeferred( Fts3Cursor *pCsr, /* FTS3 cursor pointing at row to test */ int *pbRes /* OUT: Set to true if row is a match */ ){ int rc = SQLITE_OK; if( pCsr->pDeferred==0 ){ *pbRes = 1; }else{ rc = fts3CursorSeek(0, pCsr); if( rc==SQLITE_OK ){ sqlite3Fts3FreeDeferredDoclists(pCsr); |
︙ | ︙ | |||
2673 2674 2675 2676 2677 2678 2679 | ** in the %_content table. ** ** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The ** column on the left-hand side of the MATCH operator is column ** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand ** side of the MATCH operator. */ | < < < < < | 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 | ** in the %_content table. ** ** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The ** column on the left-hand side of the MATCH operator is column ** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand ** side of the MATCH operator. */ static int fts3FilterMethod( sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ int idxNum, /* Strategy index */ const char *idxStr, /* Unused */ int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ |
︙ | ︙ | |||
2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 | ** rowid should be written to *pRowid. */ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; if( pCsr->aDoclist ){ *pRowid = pCsr->iPrevId; }else{ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); } return SQLITE_OK; } /* ** This is the xColumn method, called by SQLite to request a value from | > > > > > | 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 | ** rowid should be written to *pRowid. */ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; if( pCsr->aDoclist ){ *pRowid = pCsr->iPrevId; }else{ /* This branch runs if the query is implemented using a full-table scan ** (not using the full-text index). In this case grab the rowid from the ** SELECT statement. */ assert( pCsr->isRequireSeek==0 ); *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); } return SQLITE_OK; } /* ** This is the xColumn method, called by SQLite to request a value from |
︙ | ︙ | |||
3206 3207 3208 3209 3210 3211 3212 | static void hashDestroy(void *p){ Fts3Hash *pHash = (Fts3Hash *)p; sqlite3Fts3HashClear(pHash); sqlite3_free(pHash); } /* | | | | | < | > > | 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 | static void hashDestroy(void *p){ Fts3Hash *pHash = (Fts3Hash *)p; sqlite3Fts3HashClear(pHash); sqlite3_free(pHash); } /* ** The fts3 built-in tokenizers - "simple", "porter" and "icu"- are ** implemented in files fts3_tokenizer1.c, fts3_porter.c and fts3_icu.c ** respectively. The following three forward declarations are for functions ** declared in these files used to retrieve the respective implementations. ** ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed ** to by the argument to point to the "simple" tokenizer implementation. ** And so on. */ void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); #ifdef SQLITE_ENABLE_ICU void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); #endif /* ** Initialise the fts3 extension. If this extension is built as part ** of the sqlite library, then this function is called directly by ** SQLite. If fts3 is built as a dynamically loadable extension, this ** function is called by the sqlite3_extension_init() entry point. */ |
︙ | ︙ |
Changes to ext/fts3/fts3Int.h.
︙ | ︙ | |||
118 119 120 121 122 123 124 | int nColumn; /* number of named columns in virtual table */ char **azColumn; /* column names. malloced */ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ | | < < > > | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | int nColumn; /* number of named columns in virtual table */ char **azColumn; /* column names. malloced */ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ sqlite3_stmt *aStmt[24]; int nNodeSize; /* Soft limit for node size */ u8 bHasContent; /* True if %_content table exists */ u8 bHasDocsize; /* True if %_docsize table exists */ int nPgsz; /* Page size for host database */ char *zSegmentsTbl; /* Name of %_segments table */ sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ /* The following hash table is used to buffer pending index updates during ** transactions. Variable nPendingData estimates the memory size of the ** pending data, including hash table overhead, but not malloc overhead. ** When nPendingData exceeds nMaxPendingData, the buffer is flushed ** automatically. Variable iPrevDocid is the docid of the most recently |
︙ | ︙ | |||
159 160 161 162 163 164 165 | Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ | < | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ int doDeferred; int nRowAvg; /* Average size of database rows, in pages */ }; /* ** The Fts3Cursor.eSearch member is always set to one of the following. ** Actualy, Fts3Cursor.eSearch can be greater than or equal to |
︙ | ︙ | |||
189 190 191 192 193 194 195 | /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. ** For a sequence of tokens contained in double-quotes (i.e. "one two three") ** nToken will be the number of tokens in the string. */ | < | | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. ** For a sequence of tokens contained in double-quotes (i.e. "one two three") ** nToken will be the number of tokens in the string. */ struct Fts3PhraseToken { char *z; /* Text of the token */ int n; /* Number of bytes in buffer z */ int isPrefix; /* True if token ends with a "*" character */ Fts3SegReaderArray *pArray; /* Segment-reader for this token */ Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ }; struct Fts3Phrase { int nToken; /* Number of tokens in the phrase */ int iColumn; /* Index of column this phrase must match */ int isNot; /* Phrase prefixed by unary not (-) operator */ Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ |
︙ | ︙ | |||
254 255 256 257 258 259 260 | #define FTSQUERY_NEAR 1 #define FTSQUERY_NOT 2 #define FTSQUERY_AND 3 #define FTSQUERY_OR 4 #define FTSQUERY_PHRASE 5 | < < < < < | > | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | #define FTSQUERY_NEAR 1 #define FTSQUERY_NOT 2 #define FTSQUERY_AND 3 #define FTSQUERY_OR 4 #define FTSQUERY_PHRASE 5 /* fts3_write.c */ int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); int sqlite3Fts3PendingTermsFlush(Fts3Table *); void sqlite3Fts3PendingTermsClear(Fts3Table *); int sqlite3Fts3Optimize(Fts3Table *); int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); void sqlite3Fts3SegReaderFree(Fts3Table *, Fts3SegReader *); int sqlite3Fts3SegReaderIterate( Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, int (*)(Fts3Table *, void *, char *, int, char *, int), void * ); int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *); int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); int sqlite3Fts3MatchinfoDocsizeLocal(Fts3Cursor*, u32*); int sqlite3Fts3MatchinfoDocsizeGlobal(Fts3Cursor*, u32*); int sqlite3Fts3ReadLock(Fts3Table *); int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*); void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *); int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int); int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *); void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *); char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *); |
︙ | ︙ |
Changes to ext/fts3/fts3_write.c.
︙ | ︙ | |||
74 75 76 77 78 79 80 | struct Fts3SegReader { int iIdx; /* Index within level, or 0x7FFFFFFF for PT */ sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */ sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */ sqlite3_int64 iEndBlock; /* Rowid of final block in segment (or 0) */ sqlite3_int64 iCurrentBlock; /* Current leaf block (or 0) */ | < < > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | struct Fts3SegReader { int iIdx; /* Index within level, or 0x7FFFFFFF for PT */ sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */ sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */ sqlite3_int64 iEndBlock; /* Rowid of final block in segment (or 0) */ sqlite3_int64 iCurrentBlock; /* Current leaf block (or 0) */ char *aNode; /* Pointer to node data (or NULL) */ int nNode; /* Size of buffer at aNode (or 0) */ Fts3HashElem **ppNextElem; /* Variables set by fts3SegReaderNext(). These may be read directly ** by the caller. They are valid from the time SegmentReaderNew() returns ** until SegmentReaderNext() returns something other than SQLITE_OK ** (i.e. SQLITE_DONE). */ int nTerm; /* Number of bytes in current term */ char *zTerm; /* Pointer to current term */ int nTermAlloc; /* Allocated size of zTerm buffer */ char *aDoclist; /* Pointer to doclist of current entry */ int nDoclist; /* Size of doclist in current entry */ /* The following variables are used to iterate through the current doclist */ char *pOffsetList; sqlite3_int64 iDocid; }; |
︙ | ︙ | |||
166 167 168 169 170 171 172 | #define SQL_SELECT_LEVEL 12 #define SQL_SELECT_ALL_LEVEL 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_COUNT_MAX 15 #define SQL_DELETE_SEGDIR_BY_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 | < | | | | | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | #define SQL_SELECT_LEVEL 12 #define SQL_SELECT_ALL_LEVEL 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_COUNT_MAX 15 #define SQL_DELETE_SEGDIR_BY_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 #define SQL_DELETE_DOCSIZE 19 #define SQL_REPLACE_DOCSIZE 20 #define SQL_SELECT_DOCSIZE 21 #define SQL_SELECT_DOCTOTAL 22 #define SQL_REPLACE_DOCTOTAL 23 /* ** This function is used to obtain an SQLite prepared statement handle ** for the statement identified by the second argument. If successful, ** *pp is set to the requested statement handle and SQLITE_OK returned. ** Otherwise, an SQLite error code is returned and *pp is set to 0. ** |
︙ | ︙ | |||
216 217 218 219 220 221 222 | /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", /* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", /* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", | < | | | | | | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", /* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", /* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", /* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?", /* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?", /* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0", /* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)", }; int rc = SQLITE_OK; sqlite3_stmt *pStmt; assert( SizeofArray(azSql)==SizeofArray(p->aStmt) ); assert( eStmt<SizeofArray(azSql) && eStmt>=0 ); |
︙ | ︙ | |||
296 297 298 299 300 301 302 | sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); } *pRC = rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); } *pRC = rc; } /* ** This function ensures that the caller has obtained a shared-cache ** table-lock on the %_content table. This is required before reading ** data from the fts3 table. If this lock is not acquired first, then ** the caller may end up holding read-locks on the %_segments and %_segdir ** tables, but no read-lock on the %_content table. If this happens ** a second connection will be able to write to the fts3 table, but |
︙ | ︙ | |||
591 592 593 594 595 596 597 598 599 600 601 602 603 604 | int rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ) return rc; } p->iPrevDocid = iDocid; return SQLITE_OK; } void sqlite3Fts3PendingTermsClear(Fts3Table *p){ Fts3HashElem *pElem; for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ sqlite3_free(fts3HashData(pElem)); } fts3HashClear(&p->pendingTerms); p->nPendingData = 0; | > > > | 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 | int rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ) return rc; } p->iPrevDocid = iDocid; return SQLITE_OK; } /* ** Discard the contents of the pending-terms hash table. */ void sqlite3Fts3PendingTermsClear(Fts3Table *p){ Fts3HashElem *pElem; for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ sqlite3_free(fts3HashData(pElem)); } fts3HashClear(&p->pendingTerms); p->nPendingData = 0; |
︙ | ︙ | |||
803 804 805 806 807 808 809 810 | return rc; } /* ** The %_segments table is declared as follows: ** ** CREATE TABLE %_segments(blockid INTEGER PRIMARY KEY, block BLOB) */ | > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > > | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 | return rc; } /* ** The %_segments table is declared as follows: ** ** CREATE TABLE %_segments(blockid INTEGER PRIMARY KEY, block BLOB) ** ** This function reads data from a single row of the %_segments table. The ** specific row is identified by the iBlockid parameter. If paBlob is not ** NULL, then a buffer is allocated using sqlite3_malloc() and populated ** with the contents of the blob stored in the "block" column of the ** identified table row is. Whether or not paBlob is NULL, *pnBlob is set ** to the size of the blob in bytes before returning. ** ** If an error occurs, or the table does not contain the specified row, ** an SQLite error code is returned. Otherwise, SQLITE_OK is returned. If ** paBlob is non-NULL, then it is the responsibility of the caller to ** eventually free the returned buffer. ** ** This function may leave an open sqlite3_blob* handle in the ** Fts3Table.pSegments variable. This handle is reused by subsequent calls ** to this function. The handle may be closed by calling the ** sqlite3Fts3SegmentsClose() function. Reusing a blob handle is a handy ** performance improvement, but the blob handle should always be closed ** before control is returned to the user (to prevent a lock being held ** on the database file for longer than necessary). Thus, any virtual table ** method (xFilter etc.) that may directly or indirectly call this function ** must call sqlite3Fts3SegmentsClose() before returning. */ int sqlite3Fts3ReadBlock( Fts3Table *p, /* FTS3 table handle */ sqlite3_int64 iBlockid, /* Access the row with blockid=$iBlockid */ char **paBlob, /* OUT: Blob data in malloc'd buffer */ int *pnBlob /* OUT: Size of blob data */ ){ int rc; /* Return code */ /* pnBlob must be non-NULL. paBlob may be NULL or non-NULL. */ assert( pnBlob); if( p->pSegments ){ rc = sqlite3_blob_reopen(p->pSegments, iBlockid); }else{ if( 0==p->zSegmentsTbl ){ p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName); if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM; } rc = sqlite3_blob_open( p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments ); } if( rc==SQLITE_OK ){ int nByte = sqlite3_blob_bytes(p->pSegments); if( paBlob ){ char *aByte = sqlite3_malloc(nByte); |
︙ | ︙ | |||
845 846 847 848 849 850 851 852 853 854 855 | } *pnBlob = nByte; } return rc; } void sqlite3Fts3SegmentsClose(Fts3Table *p){ sqlite3_blob_close(p->pSegments); p->pSegments = 0; } | > > > > < < | | 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 | } *pnBlob = nByte; } return rc; } /* ** Close the blob handle at p->pSegments, if it is open. See comments above ** the sqlite3Fts3ReadBlock() function for details. */ void sqlite3Fts3SegmentsClose(Fts3Table *p){ sqlite3_blob_close(p->pSegments); p->pSegments = 0; } /* ** Move the iterator passed as the first argument to the next term in the ** segment. If successful, SQLITE_OK is returned. If there is no next term, ** SQLITE_DONE. Otherwise, an SQLite error code. */ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ char *pNext; /* Cursor variable */ int nPrefix; /* Number of bytes in term prefix */ int nSuffix; /* Number of bytes in term suffix */ if( !pReader->aDoclist ){ pNext = pReader->aNode; }else{ pNext = &pReader->aDoclist[pReader->nDoclist]; } if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){ int rc; /* Return code from Fts3ReadBlock() */ if( fts3SegReaderIsPending(pReader) ){ Fts3HashElem *pElem = *(pReader->ppNextElem); if( pElem==0 ){ pReader->aNode = 0; }else{ PendingList *pList = (PendingList *)fts3HashData(pElem); |
︙ | ︙ | |||
894 895 896 897 898 899 900 901 902 903 904 | if( !fts3SegReaderIsRootOnly(pReader) ){ sqlite3_free(pReader->aNode); } pReader->aNode = 0; /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf ** blocks have already been traversed. */ if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){ return SQLITE_OK; } | > | < | < < | 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 | if( !fts3SegReaderIsRootOnly(pReader) ){ sqlite3_free(pReader->aNode); } pReader->aNode = 0; /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf ** blocks have already been traversed. */ assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock ); if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){ return SQLITE_OK; } rc = sqlite3Fts3ReadBlock( p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode ); if( rc!=SQLITE_OK ) return rc; pNext = pReader->aNode; } pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); if( nPrefix+nSuffix>pReader->nTermAlloc ){ |
︙ | ︙ | |||
1007 1008 1009 1010 1011 1012 1013 | Fts3Cursor *pCsr, /* FTS3 cursor handle */ Fts3SegReader *pReader, /* Segment-reader handle */ int *pnCost /* IN/OUT: Number of bytes read */ ){ Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; int rc = SQLITE_OK; /* Return code */ int nCost = 0; /* Cost in bytes to return */ | < | 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 | Fts3Cursor *pCsr, /* FTS3 cursor handle */ Fts3SegReader *pReader, /* Segment-reader handle */ int *pnCost /* IN/OUT: Number of bytes read */ ){ Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; int rc = SQLITE_OK; /* Return code */ int nCost = 0; /* Cost in bytes to return */ int pgsz = p->nPgsz; /* Database page size */ /* If this seg-reader is reading the pending-terms table, or if all data ** for the segment is stored on the root page of the b-tree, then the cost ** is zero. In this case all required data is already in main memory. */ if( p->bHasDocsize |
︙ | ︙ | |||
1061 1062 1063 1064 1065 1066 1067 | } /* Assume that a blob flows over onto overflow pages if it is larger ** than (pgsz-35) bytes in size (the file-format documentation ** confirms this). */ for(iBlock=pReader->iStartBlock; iBlock<=pReader->iLeafEndBlock; iBlock++){ | | | 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 | } /* Assume that a blob flows over onto overflow pages if it is larger ** than (pgsz-35) bytes in size (the file-format documentation ** confirms this). */ for(iBlock=pReader->iStartBlock; iBlock<=pReader->iLeafEndBlock; iBlock++){ rc = sqlite3Fts3ReadBlock(p, iBlock, 0, &nBlob); if( rc!=SQLITE_OK ) break; if( (nBlob+35)>pgsz ){ int nOvfl = (nBlob + 34)/pgsz; nCost += ((nOvfl + pCsr->nRowAvg - 1)/pCsr->nRowAvg); } } } |
︙ | ︙ | |||
1130 1131 1132 1133 1134 1135 1136 | pReader->nNode = nRoot; memcpy(pReader->aNode, zRoot, nRoot); }else{ pReader->iCurrentBlock = iStartLeaf-1; } rc = fts3SegReaderNext(p, pReader); | < | 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 | pReader->nNode = nRoot; memcpy(pReader->aNode, zRoot, nRoot); }else{ pReader->iCurrentBlock = iStartLeaf-1; } rc = fts3SegReaderNext(p, pReader); if( rc==SQLITE_OK ){ *ppReader = pReader; }else{ sqlite3Fts3SegReaderFree(p, pReader); } return rc; } |
︙ | ︙ | |||
1467 1468 1469 1470 1471 1472 1473 | } /* ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger ** (according to memcmp) than the previous term. */ static int fts3NodeAddTerm( | | | 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 | } /* ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger ** (according to memcmp) than the previous term. */ static int fts3NodeAddTerm( Fts3Table *p, /* Virtual table handle */ SegmentNode **ppTree, /* IN/OUT: SegmentNode handle */ int isCopyTerm, /* True if zTerm/nTerm is transient */ const char *zTerm, /* Pointer to buffer containing term */ int nTerm /* Size of term in bytes */ ){ SegmentNode *pTree = *ppTree; int rc; |
︙ | ︙ | |||
2240 2241 2242 2243 2244 2245 2246 | ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ int i; /* Iterator variable */ int rc; /* Return code */ int iIdx; /* Index of new segment */ | | | 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 | ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ int i; /* Iterator variable */ int rc; /* Return code */ int iIdx; /* Index of new segment */ int iNewLevel = 0; /* Level to create new segment at */ sqlite3_stmt *pStmt = 0; SegmentWriter *pWriter = 0; int nSegment = 0; /* Number of segments being merged */ Fts3SegReader **apSegment = 0; /* Array of Segment iterators */ Fts3SegReader *pPending = 0; /* Iterator for pending-terms */ Fts3SegFilter filter; /* Segment term filter condition */ |
︙ | ︙ | |||
2718 2719 2720 2721 2722 2723 2724 | sqlite3_tokenizer *pT = p->pTokenizer; sqlite3_tokenizer_module const *pModule = pT->pModule; assert( pCsr->isRequireSeek==0 ); iDocid = sqlite3_column_int64(pCsr->pStmt, 0); for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){ | | | 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 | sqlite3_tokenizer *pT = p->pTokenizer; sqlite3_tokenizer_module const *pModule = pT->pModule; assert( pCsr->isRequireSeek==0 ); iDocid = sqlite3_column_int64(pCsr->pStmt, 0); for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){ const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1); sqlite3_tokenizer_cursor *pTC = 0; rc = pModule->xOpen(pT, zText, -1, &pTC); while( rc==SQLITE_OK ){ char const *zToken; /* Buffer containing token */ int nToken; /* Number of bytes in token */ int iDum1, iDum2; /* Dummy variables */ |
︙ | ︙ | |||
2808 2809 2810 2811 2812 2813 2814 | aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*p->nColumn*2 ); if( aSzIns==0 ) return SQLITE_NOMEM; aSzDel = &aSzIns[p->nColumn]; memset(aSzIns, 0, sizeof(aSzIns[0])*p->nColumn*2); /* If this is a DELETE or UPDATE operation, remove the old record. */ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ | | | 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 | aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*p->nColumn*2 ); if( aSzIns==0 ) return SQLITE_NOMEM; aSzDel = &aSzIns[p->nColumn]; memset(aSzIns, 0, sizeof(aSzIns[0])*p->nColumn*2); /* If this is a DELETE or UPDATE operation, remove the old record. */ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ int isEmpty = 0; rc = fts3IsEmpty(p, apVal, &isEmpty); if( rc==SQLITE_OK ){ if( isEmpty ){ /* Deleting this row means the whole table is empty. In this case ** delete the contents of all three tables and throw away any ** data in the pendingTerms hash table. */ |
︙ | ︙ |
Changes to test/fts3cov.test.
1 2 3 4 5 6 7 8 | # 2009 December 03 # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 2009 December 03 # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The tests in this file are structural coverage tests for FTS3. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If this build does not include FTS3, skip the tests in this file. # |
︙ | ︙ | |||
93 94 95 96 97 98 99 | do_test fts3cov-2.2 { set root [db one {SELECT root FROM t1_segdir}] read_fts3varint [string range $root 1 end] left_child execsql { DELETE FROM t1_segments WHERE blockid = $left_child } } {} do_error_test fts3cov-2.3 { SELECT * FROM t1 WHERE t1 MATCH 'c*' | | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | do_test fts3cov-2.2 { set root [db one {SELECT root FROM t1_segdir}] read_fts3varint [string range $root 1 end] left_child execsql { DELETE FROM t1_segments WHERE blockid = $left_child } } {} do_error_test fts3cov-2.3 { SELECT * FROM t1 WHERE t1 MATCH 'c*' } {SQL logic error or missing database} # Test the "replaced with NULL" case: do_test fts3cov-2.4 { execsql { INSERT INTO t1_segments VALUES($left_child, NULL) } } {} do_error_test fts3cov-2.5 { SELECT * FROM t1 WHERE t1 MATCH 'cloud' } {SQL logic error or missing database} #-------------------------------------------------------------------------- # The following tests are to test the effects of OOM errors while storing # terms in the pending-hash table. Specifically, while creating doclist # blobs to store in the table. More specifically, to test OOM errors while # appending column numbers to doclists. For example, if a doclist consists # of: |
︙ | ︙ |