| | @@ -16,11 +16,11 @@ |
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | | -** 81202d2ab5963fdcf20555b6d0b31cc955ac with changes in files: |
| 21 | +** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files: |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| | @@ -465,11 +465,11 @@ |
| 465 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | 467 | */ |
| 468 | 468 | #define SQLITE_VERSION "3.48.0" |
| 469 | 469 | #define SQLITE_VERSION_NUMBER 3048000 |
| 470 | | -#define SQLITE_SOURCE_ID "2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8" |
| 470 | +#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" |
| 471 | 471 | |
| 472 | 472 | /* |
| 473 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | 475 | ** |
| | @@ -4521,15 +4521,26 @@ |
| 4521 | 4521 | ** |
| 4522 | 4522 | ** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt> |
| 4523 | 4523 | ** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler |
| 4524 | 4524 | ** to return an error (error code SQLITE_ERROR) if the statement uses |
| 4525 | 4525 | ** any virtual tables. |
| 4526 | +** |
| 4527 | +** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt> |
| 4528 | +** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler |
| 4529 | +** errors from being sent to the error log defined by |
| 4530 | +** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test |
| 4531 | +** compiles to see if some SQL syntax is well-formed, without generating |
| 4532 | +** messages on the global error log when it is not. If the test compile |
| 4533 | +** fails, the sqlite3_prepare_v3() call returns the same error indications |
| 4534 | +** with or without this flag; it just omits the call to [sqlite3_log()] that |
| 4535 | +** logs the error. |
| 4526 | 4536 | ** </dl> |
| 4527 | 4537 | */ |
| 4528 | 4538 | #define SQLITE_PREPARE_PERSISTENT 0x01 |
| 4529 | 4539 | #define SQLITE_PREPARE_NORMALIZE 0x02 |
| 4530 | 4540 | #define SQLITE_PREPARE_NO_VTAB 0x04 |
| 4541 | +#define SQLITE_PREPARE_DONT_LOG 0x10 |
| 4531 | 4542 | |
| 4532 | 4543 | /* |
| 4533 | 4544 | ** CAPI3REF: Compiling An SQL Statement |
| 4534 | 4545 | ** KEYWORDS: {SQL statement compiler} |
| 4535 | 4546 | ** METHOD: sqlite3 |
| | @@ -13467,17 +13478,32 @@ |
| 13467 | 13478 | ** This is used to access token iToken of phrase hit iIdx within the |
| 13468 | 13479 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 13469 | 13480 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 13470 | 13481 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 13471 | 13482 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 13472 | | -** bytes. This API is not available if the specified token matches a |
| 13473 | | -** prefix query term. In that case both output variables are always set |
| 13474 | | -** to 0. |
| 13483 | +** bytes. |
| 13475 | 13484 | ** |
| 13476 | 13485 | ** The output text is not a copy of the document text that was tokenized. |
| 13477 | 13486 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 13478 | 13487 | ** includes any embedded 0x00 and trailing data. |
| 13488 | +** |
| 13489 | +** This API may be slow in some cases if the token identified by parameters |
| 13490 | +** iIdx and iToken matched a prefix token in the query. In most cases, the |
| 13491 | +** first call to this API for each prefix token in the query is forced |
| 13492 | +** to scan the portion of the full-text index that matches the prefix |
| 13493 | +** token to collect the extra data required by this API. If the prefix |
| 13494 | +** token matches a large number of token instances in the document set, |
| 13495 | +** this may be a performance problem. |
| 13496 | +** |
| 13497 | +** If the user knows in advance that a query may use this API for a |
| 13498 | +** prefix token, FTS5 may be configured to collect all required data as part |
| 13499 | +** of the initial querying of the full-text index, avoiding the second scan |
| 13500 | +** entirely. This also causes prefix queries that do not use this API to |
| 13501 | +** run more slowly and use more memory. FTS5 may be configured in this way |
| 13502 | +** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] |
| 13503 | +** option, or on a per-query basis using the |
| 13504 | +** [fts5_insttoken | fts5_insttoken()] user function. |
| 13479 | 13505 | ** |
| 13480 | 13506 | ** This API can be quite slow if used with an FTS5 table created with the |
| 13481 | 13507 | ** "detail=none" or "detail=column" option. |
| 13482 | 13508 | ** |
| 13483 | 13509 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| | @@ -17049,11 +17075,11 @@ |
| 17049 | 17075 | |
| 17050 | 17076 | /* |
| 17051 | 17077 | ** Additional non-public SQLITE_PREPARE_* flags |
| 17052 | 17078 | */ |
| 17053 | 17079 | #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ |
| 17054 | | -#define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */ |
| 17080 | +#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ |
| 17055 | 17081 | |
| 17056 | 17082 | /* |
| 17057 | 17083 | ** Prototypes for the VDBE interface. See comments on the implementation |
| 17058 | 17084 | ** for a description of what each of these routines does. |
| 17059 | 17085 | */ |
| | @@ -32279,10 +32305,11 @@ |
| 32279 | 32305 | && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) |
| 32280 | 32306 | ){ |
| 32281 | 32307 | pExpr = pExpr->pLeft; |
| 32282 | 32308 | } |
| 32283 | 32309 | if( pExpr==0 ) return; |
| 32310 | + if( ExprHasProperty(pExpr, EP_FromDDL) ) return; |
| 32284 | 32311 | db->errByteOffset = pExpr->w.iOfst; |
| 32285 | 32312 | } |
| 32286 | 32313 | |
| 32287 | 32314 | /* |
| 32288 | 32315 | ** Enlarge the memory allocation on a StrAccum object so that it is |
| | @@ -33008,11 +33035,11 @@ |
| 33008 | 33035 | } |
| 33009 | 33036 | if( pItem->fg.isCte ){ |
| 33010 | 33037 | sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); |
| 33011 | 33038 | } |
| 33012 | 33039 | if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ |
| 33013 | | - sqlite3_str_appendf(&x, " ON"); |
| 33040 | + sqlite3_str_appendf(&x, " isOn"); |
| 33014 | 33041 | } |
| 33015 | 33042 | if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc"); |
| 33016 | 33043 | if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated"); |
| 33017 | 33044 | if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized"); |
| 33018 | 33045 | if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); |
| | @@ -34092,10 +34119,14 @@ |
| 34092 | 34119 | ** |
| 34093 | 34120 | ** This routines are given external linkage so that they will always be |
| 34094 | 34121 | ** accessible to the debugging, and to avoid warnings about unused |
| 34095 | 34122 | ** functions. But these routines only exist in debugging builds, so they |
| 34096 | 34123 | ** do not contaminate the interface. |
| 34124 | +** |
| 34125 | +** See Also: |
| 34126 | +** |
| 34127 | +** sqlite3ShowWhereTerm() in where.c |
| 34097 | 34128 | */ |
| 34098 | 34129 | SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); } |
| 34099 | 34130 | SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);} |
| 34100 | 34131 | SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); } |
| 34101 | 34132 | SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); } |
| | @@ -35668,12 +35699,12 @@ |
| 35668 | 35699 | int esign = 1; /* sign of exponent */ |
| 35669 | 35700 | int e = 0; /* exponent */ |
| 35670 | 35701 | int eValid = 1; /* True exponent is either not used or is well-formed */ |
| 35671 | 35702 | int nDigit = 0; /* Number of digits processed */ |
| 35672 | 35703 | int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ |
| 35704 | + u64 s2; /* round-tripped significand */ |
| 35673 | 35705 | double rr[2]; |
| 35674 | | - u64 s2; |
| 35675 | 35706 | |
| 35676 | 35707 | assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); |
| 35677 | 35708 | *pResult = 0.0; /* Default return value, in case of an error */ |
| 35678 | 35709 | if( length==0 ) return 0; |
| 35679 | 35710 | |
| | @@ -35772,25 +35803,36 @@ |
| 35772 | 35803 | |
| 35773 | 35804 | /* adjust exponent by d, and update sign */ |
| 35774 | 35805 | e = (e*esign) + d; |
| 35775 | 35806 | |
| 35776 | 35807 | /* Try to adjust the exponent to make it smaller */ |
| 35777 | | - while( e>0 && s<(LARGEST_UINT64/10) ){ |
| 35808 | + while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){ |
| 35778 | 35809 | s *= 10; |
| 35779 | 35810 | e--; |
| 35780 | 35811 | } |
| 35781 | 35812 | while( e<0 && (s%10)==0 ){ |
| 35782 | 35813 | s /= 10; |
| 35783 | 35814 | e++; |
| 35784 | 35815 | } |
| 35785 | 35816 | |
| 35786 | 35817 | rr[0] = (double)s; |
| 35787 | | - s2 = (u64)rr[0]; |
| 35788 | | -#if defined(_MSC_VER) && _MSC_VER<1700 |
| 35789 | | - if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); } |
| 35818 | + assert( sizeof(s2)==sizeof(rr[0]) ); |
| 35819 | +#ifdef SQLITE_DEBUG |
| 35820 | + rr[1] = 18446744073709549568.0; |
| 35821 | + memcpy(&s2, &rr[1], sizeof(s2)); |
| 35822 | + assert( s2==0x43efffffffffffffLL ); |
| 35790 | 35823 | #endif |
| 35791 | | - rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); |
| 35824 | + /* Largest double that can be safely converted to u64 |
| 35825 | + ** vvvvvvvvvvvvvvvvvvvvvv */ |
| 35826 | + if( rr[0]<=18446744073709549568.0 ){ |
| 35827 | + s2 = (u64)rr[0]; |
| 35828 | + rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); |
| 35829 | + }else{ |
| 35830 | + rr[1] = 0.0; |
| 35831 | + } |
| 35832 | + assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */ |
| 35833 | + |
| 35792 | 35834 | if( e>0 ){ |
| 35793 | 35835 | while( e>=100 ){ |
| 35794 | 35836 | e -= 100; |
| 35795 | 35837 | dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); |
| 35796 | 35838 | } |
| | @@ -51771,11 +51813,11 @@ |
| 51771 | 51813 | */ |
| 51772 | 51814 | char *zTmpname = 0; /* For temporary filename, if necessary. */ |
| 51773 | 51815 | |
| 51774 | 51816 | int rc = SQLITE_OK; /* Function Return Code */ |
| 51775 | 51817 | #if !defined(NDEBUG) || SQLITE_OS_WINCE |
| 51776 | | - int eType = flags&0xFFFFFF00; /* Type of file to open */ |
| 51818 | + int eType = flags&0x0FFF00; /* Type of file to open */ |
| 51777 | 51819 | #endif |
| 51778 | 51820 | |
| 51779 | 51821 | int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); |
| 51780 | 51822 | int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); |
| 51781 | 51823 | int isCreate = (flags & SQLITE_OPEN_CREATE); |
| | @@ -112021,11 +112063,11 @@ |
| 112021 | 112063 | ** |
| 112022 | 112064 | ** (4) If pSrc is the right operand of a LEFT JOIN, then... |
| 112023 | 112065 | ** (4a) pExpr must come from an ON clause.. |
| 112024 | 112066 | ** (4b) and specifically the ON clause associated with the LEFT JOIN. |
| 112025 | 112067 | ** |
| 112026 | | -** (5) If pSrc is not the right operand of a LEFT JOIN or the left |
| 112068 | +** (5) If pSrc is the right operand of a LEFT JOIN or the left |
| 112027 | 112069 | ** operand of a RIGHT JOIN, then pExpr must be from the WHERE |
| 112028 | 112070 | ** clause, not an ON clause. |
| 112029 | 112071 | ** |
| 112030 | 112072 | ** (6) Either: |
| 112031 | 112073 | ** |
| | @@ -115555,35 +115597,41 @@ |
| 115555 | 115597 | ** |
| 115556 | 115598 | ** Additionally, if pExpr is a simple SQL value and the value is the |
| 115557 | 115599 | ** same as that currently bound to variable pVar, non-zero is returned. |
| 115558 | 115600 | ** Otherwise, if the values are not the same or if pExpr is not a simple |
| 115559 | 115601 | ** SQL value, zero is returned. |
| 115602 | +** |
| 115603 | +** If the SQLITE_EnableQPSG flag is set on the database connection, then |
| 115604 | +** this routine always returns false. |
| 115560 | 115605 | */ |
| 115561 | | -static int exprCompareVariable( |
| 115606 | +static SQLITE_NOINLINE int exprCompareVariable( |
| 115562 | 115607 | const Parse *pParse, |
| 115563 | 115608 | const Expr *pVar, |
| 115564 | 115609 | const Expr *pExpr |
| 115565 | 115610 | ){ |
| 115566 | | - int res = 0; |
| 115611 | + int res = 2; |
| 115567 | 115612 | int iVar; |
| 115568 | 115613 | sqlite3_value *pL, *pR = 0; |
| 115569 | 115614 | |
| 115615 | + if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){ |
| 115616 | + return 0; |
| 115617 | + } |
| 115618 | + if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2; |
| 115570 | 115619 | sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR); |
| 115571 | 115620 | if( pR ){ |
| 115572 | 115621 | iVar = pVar->iColumn; |
| 115573 | 115622 | sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); |
| 115574 | 115623 | pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB); |
| 115575 | 115624 | if( pL ){ |
| 115576 | 115625 | if( sqlite3_value_type(pL)==SQLITE_TEXT ){ |
| 115577 | 115626 | sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */ |
| 115578 | 115627 | } |
| 115579 | | - res = 0==sqlite3MemCompare(pL, pR, 0); |
| 115628 | + res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0; |
| 115580 | 115629 | } |
| 115581 | 115630 | sqlite3ValueFree(pR); |
| 115582 | 115631 | sqlite3ValueFree(pL); |
| 115583 | 115632 | } |
| 115584 | | - |
| 115585 | 115633 | return res; |
| 115586 | 115634 | } |
| 115587 | 115635 | |
| 115588 | 115636 | /* |
| 115589 | 115637 | ** Do a deep comparison of two expression trees. Return 0 if the two |
| | @@ -115605,16 +115653,14 @@ |
| 115605 | 115653 | ** can be sure the expressions are the same. In the places where |
| 115606 | 115654 | ** this routine is used, it does not hurt to get an extra 2 - that |
| 115607 | 115655 | ** just might result in some slightly slower code. But returning |
| 115608 | 115656 | ** an incorrect 0 or 1 could lead to a malfunction. |
| 115609 | 115657 | ** |
| 115610 | | -** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in |
| 115611 | | -** pParse->pReprepare can be matched against literals in pB. The |
| 115612 | | -** pParse->pVdbe->expmask bitmask is updated for each variable referenced. |
| 115613 | | -** If pParse is NULL (the normal case) then any TK_VARIABLE term in |
| 115614 | | -** Argument pParse should normally be NULL. If it is not NULL and pA or |
| 115615 | | -** pB causes a return value of 2. |
| 115658 | +** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE |
| 115659 | +** terms in pA with bindings in pParse->pReprepare can be matched against |
| 115660 | +** literals in pB. The pParse->pVdbe->expmask bitmask is updated for |
| 115661 | +** each variable referenced. |
| 115616 | 115662 | */ |
| 115617 | 115663 | SQLITE_PRIVATE int sqlite3ExprCompare( |
| 115618 | 115664 | const Parse *pParse, |
| 115619 | 115665 | const Expr *pA, |
| 115620 | 115666 | const Expr *pB, |
| | @@ -115622,12 +115668,12 @@ |
| 115622 | 115668 | ){ |
| 115623 | 115669 | u32 combinedFlags; |
| 115624 | 115670 | if( pA==0 || pB==0 ){ |
| 115625 | 115671 | return pB==pA ? 0 : 2; |
| 115626 | 115672 | } |
| 115627 | | - if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){ |
| 115628 | | - return 0; |
| 115673 | + if( pParse && pA->op==TK_VARIABLE ){ |
| 115674 | + return exprCompareVariable(pParse, pA, pB); |
| 115629 | 115675 | } |
| 115630 | 115676 | combinedFlags = pA->flags | pB->flags; |
| 115631 | 115677 | if( combinedFlags & EP_IntValue ){ |
| 115632 | 115678 | if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){ |
| 115633 | 115679 | return 0; |
| | @@ -115817,23 +115863,75 @@ |
| 115817 | 115863 | return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); |
| 115818 | 115864 | } |
| 115819 | 115865 | } |
| 115820 | 115866 | return 0; |
| 115821 | 115867 | } |
| 115868 | + |
| 115869 | +/* |
| 115870 | +** Return true if the boolean value of the expression is always either |
| 115871 | +** FALSE or NULL. |
| 115872 | +*/ |
| 115873 | +static int sqlite3ExprIsNotTrue(Expr *pExpr){ |
| 115874 | + int v; |
| 115875 | + if( pExpr->op==TK_NULL ) return 1; |
| 115876 | + if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1; |
| 115877 | + v = 1; |
| 115878 | + if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1; |
| 115879 | + return 0; |
| 115880 | +} |
| 115881 | + |
| 115882 | +/* |
| 115883 | +** Return true if the expression is one of the following: |
| 115884 | +** |
| 115885 | +** CASE WHEN x THEN y END |
| 115886 | +** CASE WHEN x THEN y ELSE NULL END |
| 115887 | +** CASE WHEN x THEN y ELSE false END |
| 115888 | +** iif(x,y) |
| 115889 | +** iif(x,y,NULL) |
| 115890 | +** iif(x,y,false) |
| 115891 | +*/ |
| 115892 | +static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){ |
| 115893 | + ExprList *pList; |
| 115894 | + if( pExpr->op==TK_FUNCTION ){ |
| 115895 | + const char *z = pExpr->u.zToken; |
| 115896 | + FuncDef *pDef; |
| 115897 | + if( (z[0]!='i' && z[0]!='I') ) return 0; |
| 115898 | + if( pExpr->x.pList==0 ) return 0; |
| 115899 | + pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0); |
| 115900 | +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 115901 | + if( pDef==0 ) return 0; |
| 115902 | +#else |
| 115903 | + if( NEVER(pDef==0) ) return 0; |
| 115904 | +#endif |
| 115905 | + if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0; |
| 115906 | + if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0; |
| 115907 | + }else if( pExpr->op==TK_CASE ){ |
| 115908 | + if( pExpr->pLeft!=0 ) return 0; |
| 115909 | + }else{ |
| 115910 | + return 0; |
| 115911 | + } |
| 115912 | + pList = pExpr->x.pList; |
| 115913 | + assert( pList!=0 ); |
| 115914 | + if( pList->nExpr==2 ) return 1; |
| 115915 | + if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1; |
| 115916 | + return 0; |
| 115917 | +} |
| 115822 | 115918 | |
| 115823 | 115919 | /* |
| 115824 | 115920 | ** Return true if we can prove the pE2 will always be true if pE1 is |
| 115825 | 115921 | ** true. Return false if we cannot complete the proof or if pE2 might |
| 115826 | 115922 | ** be false. Examples: |
| 115827 | 115923 | ** |
| 115828 | | -** pE1: x==5 pE2: x==5 Result: true |
| 115829 | | -** pE1: x>0 pE2: x==5 Result: false |
| 115830 | | -** pE1: x=21 pE2: x=21 OR y=43 Result: true |
| 115831 | | -** pE1: x!=123 pE2: x IS NOT NULL Result: true |
| 115832 | | -** pE1: x!=?1 pE2: x IS NOT NULL Result: true |
| 115833 | | -** pE1: x IS NULL pE2: x IS NOT NULL Result: false |
| 115834 | | -** pE1: x IS ?2 pE2: x IS NOT NULL Result: false |
| 115924 | +** pE1: x==5 pE2: x==5 Result: true |
| 115925 | +** pE1: x>0 pE2: x==5 Result: false |
| 115926 | +** pE1: x=21 pE2: x=21 OR y=43 Result: true |
| 115927 | +** pE1: x!=123 pE2: x IS NOT NULL Result: true |
| 115928 | +** pE1: x!=?1 pE2: x IS NOT NULL Result: true |
| 115929 | +** pE1: x IS NULL pE2: x IS NOT NULL Result: false |
| 115930 | +** pE1: x IS ?2 pE2: x IS NOT NULL Result: false |
| 115931 | +** pE1: iif(x,y) pE2: x Result: true |
| 115932 | +** PE1: iif(x,y,0) pE2: x Result: true |
| 115835 | 115933 | ** |
| 115836 | 115934 | ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has |
| 115837 | 115935 | ** Expr.iTable<0 then assume a table number given by iTab. |
| 115838 | 115936 | ** |
| 115839 | 115937 | ** If pParse is not NULL, then the values of bound variables in pE1 are |
| | @@ -115863,10 +115961,13 @@ |
| 115863 | 115961 | if( pE2->op==TK_NOTNULL |
| 115864 | 115962 | && exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0) |
| 115865 | 115963 | ){ |
| 115866 | 115964 | return 1; |
| 115867 | 115965 | } |
| 115966 | + if( sqlite3ExprIsIIF(pParse->db, pE1) ){ |
| 115967 | + return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab); |
| 115968 | + } |
| 115868 | 115969 | return 0; |
| 115869 | 115970 | } |
| 115870 | 115971 | |
| 115871 | 115972 | /* This is a helper function to impliesNotNullRow(). In this routine, |
| 115872 | 115973 | ** set pWalker->eCode to one only if *both* of the input expressions |
| | @@ -132082,11 +132183,14 @@ |
| 132082 | 132183 | MFUNCTION(degrees, 1, radToDeg, math1Func ), |
| 132083 | 132184 | MFUNCTION(pi, 0, 0, piFunc ), |
| 132084 | 132185 | #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ |
| 132085 | 132186 | FUNCTION(sign, 1, 0, 0, signFunc ), |
| 132086 | 132187 | INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), |
| 132188 | + INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ), |
| 132087 | 132189 | INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), |
| 132190 | + INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ), |
| 132191 | + INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ), |
| 132088 | 132192 | }; |
| 132089 | 132193 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 132090 | 132194 | sqlite3AlterFunctions(); |
| 132091 | 132195 | #endif |
| 132092 | 132196 | sqlite3WindowFunctions(); |
| | @@ -140730,11 +140834,12 @@ |
| 140730 | 140834 | pTab = sqliteHashData(k); |
| 140731 | 140835 | if( pTab->nCol==0 ){ |
| 140732 | 140836 | char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); |
| 140733 | 140837 | if( zSql ){ |
| 140734 | 140838 | sqlite3_stmt *pDummy = 0; |
| 140735 | | - (void)sqlite3_prepare(db, zSql, -1, &pDummy, 0); |
| 140839 | + (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG, |
| 140840 | + &pDummy, 0); |
| 140736 | 140841 | (void)sqlite3_finalize(pDummy); |
| 140737 | 140842 | sqlite3DbFree(db, zSql); |
| 140738 | 140843 | } |
| 140739 | 140844 | if( db->mallocFailed ){ |
| 140740 | 140845 | sqlite3ErrorMsg(db->pParse, "out of memory"); |
| | @@ -147529,36 +147634,36 @@ |
| 147529 | 147634 | return pExpr; |
| 147530 | 147635 | } |
| 147531 | 147636 | if( pSubst->isOuterJoin ){ |
| 147532 | 147637 | ExprSetProperty(pNew, EP_CanBeNull); |
| 147533 | 147638 | } |
| 147639 | + if( pNew->op==TK_TRUEFALSE ){ |
| 147640 | + pNew->u.iValue = sqlite3ExprTruthValue(pNew); |
| 147641 | + pNew->op = TK_INTEGER; |
| 147642 | + ExprSetProperty(pNew, EP_IntValue); |
| 147643 | + } |
| 147644 | + |
| 147645 | + /* Ensure that the expression now has an implicit collation sequence, |
| 147646 | + ** just as it did when it was a column of a view or sub-query. */ |
| 147647 | + { |
| 147648 | + CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew); |
| 147649 | + CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, |
| 147650 | + pSubst->pCList->a[iColumn].pExpr |
| 147651 | + ); |
| 147652 | + if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){ |
| 147653 | + pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew, |
| 147654 | + (pColl ? pColl->zName : "BINARY") |
| 147655 | + ); |
| 147656 | + } |
| 147657 | + } |
| 147658 | + ExprClearProperty(pNew, EP_Collate); |
| 147534 | 147659 | if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ |
| 147535 | 147660 | sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, |
| 147536 | 147661 | pExpr->flags & (EP_OuterON|EP_InnerON)); |
| 147537 | 147662 | } |
| 147538 | 147663 | sqlite3ExprDelete(db, pExpr); |
| 147539 | 147664 | pExpr = pNew; |
| 147540 | | - if( pExpr->op==TK_TRUEFALSE ){ |
| 147541 | | - pExpr->u.iValue = sqlite3ExprTruthValue(pExpr); |
| 147542 | | - pExpr->op = TK_INTEGER; |
| 147543 | | - ExprSetProperty(pExpr, EP_IntValue); |
| 147544 | | - } |
| 147545 | | - |
| 147546 | | - /* Ensure that the expression now has an implicit collation sequence, |
| 147547 | | - ** just as it did when it was a column of a view or sub-query. */ |
| 147548 | | - { |
| 147549 | | - CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr); |
| 147550 | | - CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, |
| 147551 | | - pSubst->pCList->a[iColumn].pExpr |
| 147552 | | - ); |
| 147553 | | - if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){ |
| 147554 | | - pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr, |
| 147555 | | - (pColl ? pColl->zName : "BINARY") |
| 147556 | | - ); |
| 147557 | | - } |
| 147558 | | - } |
| 147559 | | - ExprClearProperty(pExpr, EP_Collate); |
| 147560 | 147665 | } |
| 147561 | 147666 | } |
| 147562 | 147667 | }else{ |
| 147563 | 147668 | if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){ |
| 147564 | 147669 | pExpr->iTable = pSubst->iNewTable; |
| | @@ -148291,20 +148396,20 @@ |
| 148291 | 148396 | } |
| 148292 | 148397 | |
| 148293 | 148398 | /* Transfer the FROM clause terms from the subquery into the |
| 148294 | 148399 | ** outer query. |
| 148295 | 148400 | */ |
| 148401 | + iNewParent = pSubSrc->a[0].iCursor; |
| 148296 | 148402 | for(i=0; i<nSubSrc; i++){ |
| 148297 | 148403 | SrcItem *pItem = &pSrc->a[i+iFrom]; |
| 148298 | 148404 | assert( pItem->fg.isTabFunc==0 ); |
| 148299 | 148405 | assert( pItem->fg.isSubquery |
| 148300 | 148406 | || pItem->fg.fixedSchema |
| 148301 | 148407 | || pItem->u4.zDatabase==0 ); |
| 148302 | 148408 | if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); |
| 148303 | 148409 | *pItem = pSubSrc->a[i]; |
| 148304 | 148410 | pItem->fg.jointype |= ltorj; |
| 148305 | | - iNewParent = pSubSrc->a[i].iCursor; |
| 148306 | 148411 | memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); |
| 148307 | 148412 | } |
| 148308 | 148413 | pSrc->a[iFrom].fg.jointype &= JT_LTORJ; |
| 148309 | 148414 | pSrc->a[iFrom].fg.jointype |= jointype | ltorj; |
| 148310 | 148415 | |
| | @@ -148340,10 +148445,11 @@ |
| 148340 | 148445 | pSub->pOrderBy = 0; |
| 148341 | 148446 | } |
| 148342 | 148447 | pWhere = pSub->pWhere; |
| 148343 | 148448 | pSub->pWhere = 0; |
| 148344 | 148449 | if( isOuterJoin>0 ){ |
| 148450 | + assert( pSubSrc->nSrc==1 ); |
| 148345 | 148451 | sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); |
| 148346 | 148452 | } |
| 148347 | 148453 | if( pWhere ){ |
| 148348 | 148454 | if( pParent->pWhere ){ |
| 148349 | 148455 | pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere); |
| | @@ -151439,11 +151545,11 @@ |
| 151439 | 151545 | sqlite3TreeViewSelect(0, p, 0); |
| 151440 | 151546 | } |
| 151441 | 151547 | #endif |
| 151442 | 151548 | assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); |
| 151443 | 151549 | }else{ |
| 151444 | | - TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); |
| 151550 | + TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n")); |
| 151445 | 151551 | } |
| 151446 | 151552 | |
| 151447 | 151553 | /* Convert unused result columns of the subquery into simple NULL |
| 151448 | 151554 | ** expressions, to avoid unneeded searching and computation. |
| 151449 | 151555 | ** tag-select-0440 |
| | @@ -158930,10 +159036,11 @@ |
| 158930 | 159036 | if( pOrigLhs ){ |
| 158931 | 159037 | sqlite3ExprListDelete(db, pOrigLhs); |
| 158932 | 159038 | pNew->pLeft->x.pList = pLhs; |
| 158933 | 159039 | } |
| 158934 | 159040 | pSelect->pEList = pRhs; |
| 159041 | + pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */ |
| 158935 | 159042 | if( pLhs && pLhs->nExpr==1 ){ |
| 158936 | 159043 | /* Take care here not to generate a TK_VECTOR containing only a |
| 158937 | 159044 | ** single value. Since the parser never creates such a vector, some |
| 158938 | 159045 | ** of the subroutines do not handle this case. */ |
| 158939 | 159046 | Expr *p = pLhs->a[0].pExpr; |
| | @@ -164002,11 +164109,11 @@ |
| 164002 | 164109 | || pTerm->pExpr->w.iJoin != pSrc->iCursor |
| 164003 | 164110 | ){ |
| 164004 | 164111 | return 0; |
| 164005 | 164112 | } |
| 164006 | 164113 | if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 |
| 164007 | | - && ExprHasProperty(pTerm->pExpr, EP_InnerON) |
| 164114 | + && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON)) |
| 164008 | 164115 | ){ |
| 164009 | 164116 | return 0; |
| 164010 | 164117 | } |
| 164011 | 164118 | return 1; |
| 164012 | 164119 | } |
| | @@ -165495,11 +165602,11 @@ |
| 165495 | 165602 | return rc; |
| 165496 | 165603 | } |
| 165497 | 165604 | #endif /* SQLITE_ENABLE_STAT4 */ |
| 165498 | 165605 | |
| 165499 | 165606 | |
| 165500 | | -#ifdef WHERETRACE_ENABLED |
| 165607 | +#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG) |
| 165501 | 165608 | /* |
| 165502 | 165609 | ** Print the content of a WhereTerm object |
| 165503 | 165610 | */ |
| 165504 | 165611 | SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ |
| 165505 | 165612 | if( pTerm==0 ){ |
| | @@ -165538,10 +165645,13 @@ |
| 165538 | 165645 | sqlite3DebugPrintf(" iParent=%d", pTerm->iParent); |
| 165539 | 165646 | } |
| 165540 | 165647 | sqlite3DebugPrintf("\n"); |
| 165541 | 165648 | sqlite3TreeViewExpr(0, pTerm->pExpr, 0); |
| 165542 | 165649 | } |
| 165650 | +} |
| 165651 | +SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){ |
| 165652 | + sqlite3WhereTermPrint(pTerm, 0); |
| 165543 | 165653 | } |
| 165544 | 165654 | #endif |
| 165545 | 165655 | |
| 165546 | 165656 | #ifdef WHERETRACE_ENABLED |
| 165547 | 165657 | /* |
| | @@ -166724,11 +166834,10 @@ |
| 166724 | 166834 | pParse = pWC->pWInfo->pParse; |
| 166725 | 166835 | while( pWhere->op==TK_AND ){ |
| 166726 | 166836 | if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; |
| 166727 | 166837 | pWhere = pWhere->pRight; |
| 166728 | 166838 | } |
| 166729 | | - if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; |
| 166730 | 166839 | for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 166731 | 166840 | Expr *pExpr; |
| 166732 | 166841 | pExpr = pTerm->pExpr; |
| 166733 | 166842 | if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) |
| 166734 | 166843 | && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) |
| | @@ -169385,11 +169494,11 @@ |
| 169385 | 169494 | break; |
| 169386 | 169495 | } |
| 169387 | 169496 | } |
| 169388 | 169497 | if( hasRightJoin |
| 169389 | 169498 | && ExprHasProperty(pTerm->pExpr, EP_InnerON) |
| 169390 | | - && pTerm->pExpr->w.iJoin==pItem->iCursor |
| 169499 | + && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor) |
| 169391 | 169500 | ){ |
| 169392 | 169501 | break; /* restriction (5) */ |
| 169393 | 169502 | } |
| 169394 | 169503 | } |
| 169395 | 169504 | if( pTerm<pEnd ) continue; |
| | @@ -173846,10 +173955,17 @@ |
| 173846 | 173955 | ** Then the "b" IdList records the list "a,b,c". |
| 173847 | 173956 | */ |
| 173848 | 173957 | struct TrigEvent { int a; IdList * b; }; |
| 173849 | 173958 | |
| 173850 | 173959 | struct FrameBound { int eType; Expr *pExpr; }; |
| 173960 | + |
| 173961 | +/* |
| 173962 | +** Generate a syntax error |
| 173963 | +*/ |
| 173964 | +static void parserSyntaxError(Parse *pParse, Token *p){ |
| 173965 | + sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p); |
| 173966 | +} |
| 173851 | 173967 | |
| 173852 | 173968 | /* |
| 173853 | 173969 | ** Disable lookaside memory allocation for objects that might be |
| 173854 | 173970 | ** shared across database connections. |
| 173855 | 173971 | */ |
| | @@ -178214,11 +178330,11 @@ |
| 178214 | 178330 | ** that look like this: #1 #2 ... These terms refer to registers |
| 178215 | 178331 | ** in the virtual machine. #N is the N-th register. */ |
| 178216 | 178332 | Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ |
| 178217 | 178333 | assert( t.n>=2 ); |
| 178218 | 178334 | if( pParse->nested==0 ){ |
| 178219 | | - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); |
| 178335 | + parserSyntaxError(pParse, &t); |
| 178220 | 178336 | yymsp[0].minor.yy454 = 0; |
| 178221 | 178337 | }else{ |
| 178222 | 178338 | yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); |
| 178223 | 178339 | if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); |
| 178224 | 178340 | } |
| | @@ -179062,11 +179178,11 @@ |
| 179062 | 179178 | #define TOKEN yyminor |
| 179063 | 179179 | /************ Begin %syntax_error code ****************************************/ |
| 179064 | 179180 | |
| 179065 | 179181 | UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ |
| 179066 | 179182 | if( TOKEN.z[0] ){ |
| 179067 | | - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); |
| 179183 | + parserSyntaxError(pParse, &TOKEN); |
| 179068 | 179184 | }else{ |
| 179069 | 179185 | sqlite3ErrorMsg(pParse, "incomplete input"); |
| 179070 | 179186 | } |
| 179071 | 179187 | /************ End %syntax_error code ******************************************/ |
| 179072 | 179188 | sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| | @@ -180553,11 +180669,13 @@ |
| 180553 | 180669 | } |
| 180554 | 180670 | if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ |
| 180555 | 180671 | if( pParse->zErrMsg==0 ){ |
| 180556 | 180672 | pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); |
| 180557 | 180673 | } |
| 180558 | | - sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); |
| 180674 | + if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){ |
| 180675 | + sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); |
| 180676 | + } |
| 180559 | 180677 | nErr++; |
| 180560 | 180678 | } |
| 180561 | 180679 | pParse->zTail = zSql; |
| 180562 | 180680 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 180563 | 180681 | sqlite3_free(pParse->apVtabLock); |
| | @@ -185391,10 +185509,11 @@ |
| 185391 | 185509 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185392 | 185510 | sqlite3ShowWindow(0); |
| 185393 | 185511 | sqlite3ShowWinFunc(0); |
| 185394 | 185512 | #endif |
| 185395 | 185513 | sqlite3ShowSelect(0); |
| 185514 | + sqlite3ShowWhereTerm(0); |
| 185396 | 185515 | } |
| 185397 | 185516 | #endif |
| 185398 | 185517 | break; |
| 185399 | 185518 | } |
| 185400 | 185519 | |
| | @@ -194537,14 +194656,15 @@ |
| 194537 | 194656 | rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); |
| 194538 | 194657 | if( rc==SQLITE_OK ){ |
| 194539 | 194658 | Fts3PhraseToken *pToken; |
| 194540 | 194659 | |
| 194541 | 194660 | p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); |
| 194542 | | - if( !p ) goto no_mem; |
| 194543 | | - |
| 194544 | 194661 | zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); |
| 194545 | | - if( !zTemp ) goto no_mem; |
| 194662 | + if( !zTemp || !p ){ |
| 194663 | + rc = SQLITE_NOMEM; |
| 194664 | + goto getnextstring_out; |
| 194665 | + } |
| 194546 | 194666 | |
| 194547 | 194667 | assert( nToken==ii ); |
| 194548 | 194668 | pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; |
| 194549 | 194669 | memset(pToken, 0, sizeof(Fts3PhraseToken)); |
| 194550 | 194670 | |
| | @@ -194555,53 +194675,51 @@ |
| 194555 | 194675 | pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*'); |
| 194556 | 194676 | pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^'); |
| 194557 | 194677 | nToken = ii+1; |
| 194558 | 194678 | } |
| 194559 | 194679 | } |
| 194560 | | - |
| 194561 | | - pModule->xClose(pCursor); |
| 194562 | | - pCursor = 0; |
| 194563 | 194680 | } |
| 194564 | 194681 | |
| 194565 | 194682 | if( rc==SQLITE_DONE ){ |
| 194566 | 194683 | int jj; |
| 194567 | 194684 | char *zBuf = 0; |
| 194568 | 194685 | |
| 194569 | 194686 | p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); |
| 194570 | | - if( !p ) goto no_mem; |
| 194687 | + if( !p ){ |
| 194688 | + rc = SQLITE_NOMEM; |
| 194689 | + goto getnextstring_out; |
| 194690 | + } |
| 194571 | 194691 | memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); |
| 194572 | 194692 | p->eType = FTSQUERY_PHRASE; |
| 194573 | 194693 | p->pPhrase = (Fts3Phrase *)&p[1]; |
| 194574 | 194694 | p->pPhrase->iColumn = pParse->iDefaultCol; |
| 194575 | 194695 | p->pPhrase->nToken = nToken; |
| 194576 | 194696 | |
| 194577 | 194697 | zBuf = (char *)&p->pPhrase->aToken[nToken]; |
| 194698 | + assert( nTemp==0 || zTemp ); |
| 194578 | 194699 | if( zTemp ){ |
| 194579 | 194700 | memcpy(zBuf, zTemp, nTemp); |
| 194580 | | - sqlite3_free(zTemp); |
| 194581 | | - }else{ |
| 194582 | | - assert( nTemp==0 ); |
| 194583 | 194701 | } |
| 194584 | 194702 | |
| 194585 | 194703 | for(jj=0; jj<p->pPhrase->nToken; jj++){ |
| 194586 | 194704 | p->pPhrase->aToken[jj].z = zBuf; |
| 194587 | 194705 | zBuf += p->pPhrase->aToken[jj].n; |
| 194588 | 194706 | } |
| 194589 | 194707 | rc = SQLITE_OK; |
| 194590 | 194708 | } |
| 194591 | 194709 | |
| 194592 | | - *ppExpr = p; |
| 194593 | | - return rc; |
| 194594 | | -no_mem: |
| 194595 | | - |
| 194710 | + getnextstring_out: |
| 194596 | 194711 | if( pCursor ){ |
| 194597 | 194712 | pModule->xClose(pCursor); |
| 194598 | 194713 | } |
| 194599 | 194714 | sqlite3_free(zTemp); |
| 194600 | | - sqlite3_free(p); |
| 194601 | | - *ppExpr = 0; |
| 194602 | | - return SQLITE_NOMEM; |
| 194715 | + if( rc!=SQLITE_OK ){ |
| 194716 | + sqlite3_free(p); |
| 194717 | + p = 0; |
| 194718 | + } |
| 194719 | + *ppExpr = p; |
| 194720 | + return rc; |
| 194603 | 194721 | } |
| 194604 | 194722 | |
| 194605 | 194723 | /* |
| 194606 | 194724 | ** The output variable *ppExpr is populated with an allocated Fts3Expr |
| 194607 | 194725 | ** structure, or set to 0 if the end of the input buffer is reached. |
| | @@ -226191,10 +226309,12 @@ |
| 226191 | 226309 | if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){ |
| 226192 | 226310 | unsigned char *aPage = sqlite3PagerGetData(pDbPage); |
| 226193 | 226311 | memcpy(aPage, pData, szPage); |
| 226194 | 226312 | pTab->pgnoTrunc = 0; |
| 226195 | 226313 | } |
| 226314 | + }else{ |
| 226315 | + pTab->pgnoTrunc = 0; |
| 226196 | 226316 | } |
| 226197 | 226317 | sqlite3PagerUnref(pDbPage); |
| 226198 | 226318 | return rc; |
| 226199 | 226319 | |
| 226200 | 226320 | update_fail: |
| | @@ -233154,17 +233274,32 @@ |
| 233154 | 233274 | ** This is used to access token iToken of phrase hit iIdx within the |
| 233155 | 233275 | ** current row. If iIdx is less than zero or greater than or equal to the |
| 233156 | 233276 | ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, |
| 233157 | 233277 | ** output variable (*ppToken) is set to point to a buffer containing the |
| 233158 | 233278 | ** matching document token, and (*pnToken) to the size of that buffer in |
| 233159 | | -** bytes. This API is not available if the specified token matches a |
| 233160 | | -** prefix query term. In that case both output variables are always set |
| 233161 | | -** to 0. |
| 233279 | +** bytes. |
| 233162 | 233280 | ** |
| 233163 | 233281 | ** The output text is not a copy of the document text that was tokenized. |
| 233164 | 233282 | ** It is the output of the tokenizer module. For tokendata=1 tables, this |
| 233165 | 233283 | ** includes any embedded 0x00 and trailing data. |
| 233284 | +** |
| 233285 | +** This API may be slow in some cases if the token identified by parameters |
| 233286 | +** iIdx and iToken matched a prefix token in the query. In most cases, the |
| 233287 | +** first call to this API for each prefix token in the query is forced |
| 233288 | +** to scan the portion of the full-text index that matches the prefix |
| 233289 | +** token to collect the extra data required by this API. If the prefix |
| 233290 | +** token matches a large number of token instances in the document set, |
| 233291 | +** this may be a performance problem. |
| 233292 | +** |
| 233293 | +** If the user knows in advance that a query may use this API for a |
| 233294 | +** prefix token, FTS5 may be configured to collect all required data as part |
| 233295 | +** of the initial querying of the full-text index, avoiding the second scan |
| 233296 | +** entirely. This also causes prefix queries that do not use this API to |
| 233297 | +** run more slowly and use more memory. FTS5 may be configured in this way |
| 233298 | +** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] |
| 233299 | +** option, or on a per-query basis using the |
| 233300 | +** [fts5_insttoken | fts5_insttoken()] user function. |
| 233166 | 233301 | ** |
| 233167 | 233302 | ** This API can be quite slow if used with an FTS5 table created with the |
| 233168 | 233303 | ** "detail=none" or "detail=column" option. |
| 233169 | 233304 | ** |
| 233170 | 233305 | ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) |
| | @@ -233843,11 +233978,12 @@ |
| 233843 | 233978 | int nUsermerge; /* 'usermerge' setting */ |
| 233844 | 233979 | int nHashSize; /* Bytes of memory for in-memory hash */ |
| 233845 | 233980 | char *zRank; /* Name of rank function */ |
| 233846 | 233981 | char *zRankArgs; /* Arguments to rank function */ |
| 233847 | 233982 | int bSecureDelete; /* 'secure-delete' */ |
| 233848 | | - int nDeleteMerge; /* 'deletemerge' */ |
| 233983 | + int nDeleteMerge; /* 'deletemerge' */ |
| 233984 | + int bPrefixInsttoken; /* 'prefix-insttoken' */ |
| 233849 | 233985 | |
| 233850 | 233986 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ |
| 233851 | 233987 | char **pzErrmsg; |
| 233852 | 233988 | |
| 233853 | 233989 | #ifdef SQLITE_DEBUG |
| | @@ -234100,11 +234236,18 @@ |
| 234100 | 234236 | static int sqlite3Fts5StructureTest(Fts5Index*, void*); |
| 234101 | 234237 | |
| 234102 | 234238 | /* |
| 234103 | 234239 | ** Used by xInstToken(): |
| 234104 | 234240 | */ |
| 234105 | | -static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); |
| 234241 | +static int sqlite3Fts5IterToken( |
| 234242 | + Fts5IndexIter *pIndexIter, |
| 234243 | + const char *pToken, int nToken, |
| 234244 | + i64 iRowid, |
| 234245 | + int iCol, |
| 234246 | + int iOff, |
| 234247 | + const char **ppOut, int *pnOut |
| 234248 | +); |
| 234106 | 234249 | |
| 234107 | 234250 | /* |
| 234108 | 234251 | ** Insert or remove data to or from the index. Each time a document is |
| 234109 | 234252 | ** added to or removed from the index, this function is called one or more |
| 234110 | 234253 | ** times. |
| | @@ -238314,10 +238457,23 @@ |
| 238314 | 238457 | if( bVal<0 ){ |
| 238315 | 238458 | *pbBadkey = 1; |
| 238316 | 238459 | }else{ |
| 238317 | 238460 | pConfig->bSecureDelete = (bVal ? 1 : 0); |
| 238318 | 238461 | } |
| 238462 | + } |
| 238463 | + |
| 238464 | + else if( 0==sqlite3_stricmp(zKey, "insttoken") ){ |
| 238465 | + int bVal = -1; |
| 238466 | + if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ |
| 238467 | + bVal = sqlite3_value_int(pVal); |
| 238468 | + } |
| 238469 | + if( bVal<0 ){ |
| 238470 | + *pbBadkey = 1; |
| 238471 | + }else{ |
| 238472 | + pConfig->bPrefixInsttoken = (bVal ? 1 : 0); |
| 238473 | + } |
| 238474 | + |
| 238319 | 238475 | }else{ |
| 238320 | 238476 | *pbBadkey = 1; |
| 238321 | 238477 | } |
| 238322 | 238478 | return rc; |
| 238323 | 238479 | } |
| | @@ -241449,11 +241605,11 @@ |
| 241449 | 241605 | && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 |
| 241450 | 241606 | ){ |
| 241451 | 241607 | int rc = sqlite3Fts5PoslistWriterAppend( |
| 241452 | 241608 | &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff |
| 241453 | 241609 | ); |
| 241454 | | - if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){ |
| 241610 | + if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){ |
| 241455 | 241611 | int iCol = p->iOff>>32; |
| 241456 | 241612 | int iTokOff = p->iOff & 0x7FFFFFFF; |
| 241457 | 241613 | rc = sqlite3Fts5IndexIterWriteTokendata( |
| 241458 | 241614 | pT->pIter, pToken, nToken, iRowid, iCol, iTokOff |
| 241459 | 241615 | ); |
| | @@ -241642,19 +241798,18 @@ |
| 241642 | 241798 | pPhrase = pExpr->apExprPhrase[iPhrase]; |
| 241643 | 241799 | if( iToken<0 || iToken>=pPhrase->nTerm ){ |
| 241644 | 241800 | return SQLITE_RANGE; |
| 241645 | 241801 | } |
| 241646 | 241802 | pTerm = &pPhrase->aTerm[iToken]; |
| 241647 | | - if( pTerm->bPrefix==0 ){ |
| 241648 | | - if( pExpr->pConfig->bTokendata ){ |
| 241649 | | - rc = sqlite3Fts5IterToken( |
| 241650 | | - pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut |
| 241651 | | - ); |
| 241652 | | - }else{ |
| 241653 | | - *ppOut = pTerm->pTerm; |
| 241654 | | - *pnOut = pTerm->nFullTerm; |
| 241655 | | - } |
| 241803 | + if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){ |
| 241804 | + rc = sqlite3Fts5IterToken( |
| 241805 | + pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm, |
| 241806 | + iRowid, iCol, iOff+iToken, ppOut, pnOut |
| 241807 | + ); |
| 241808 | + }else{ |
| 241809 | + *ppOut = pTerm->pTerm; |
| 241810 | + *pnOut = pTerm->nFullTerm; |
| 241656 | 241811 | } |
| 241657 | 241812 | return rc; |
| 241658 | 241813 | } |
| 241659 | 241814 | |
| 241660 | 241815 | /* |
| | @@ -248465,10 +248620,387 @@ |
| 248465 | 248620 | fts5BufferFree(&tmp); |
| 248466 | 248621 | memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); |
| 248467 | 248622 | *p1 = out; |
| 248468 | 248623 | } |
| 248469 | 248624 | |
| 248625 | + |
| 248626 | +/* |
| 248627 | +** Iterate through a range of entries in the FTS index, invoking the xVisit |
| 248628 | +** callback for each of them. |
| 248629 | +** |
| 248630 | +** Parameter pToken points to an nToken buffer containing an FTS index term |
| 248631 | +** (i.e. a document term with the preceding 1 byte index identifier - |
| 248632 | +** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits |
| 248633 | +** all entries for terms that have pToken/nToken as a prefix. If bPrefix |
| 248634 | +** is false, then only entries with pToken/nToken as the entire key are |
| 248635 | +** visited. |
| 248636 | +** |
| 248637 | +** If the current table is a tokendata=1 table, then if bPrefix is true then |
| 248638 | +** each index term is treated separately. However, if bPrefix is false, then |
| 248639 | +** all index terms corresponding to pToken/nToken are collapsed into a single |
| 248640 | +** term before the callback is invoked. |
| 248641 | +** |
| 248642 | +** The callback invoked for each entry visited is specified by paramter xVisit. |
| 248643 | +** Each time it is invoked, it is passed a pointer to the Fts5Index object, |
| 248644 | +** a copy of the 7th paramter to this function (pCtx) and a pointer to the |
| 248645 | +** iterator that indicates the current entry. If the current entry is the |
| 248646 | +** first with a new term (i.e. different from that of the previous entry, |
| 248647 | +** including the very first term), then the final two parameters are passed |
| 248648 | +** a pointer to the term and its size in bytes, respectively. If the current |
| 248649 | +** entry is not the first associated with its term, these two parameters |
| 248650 | +** are passed 0. |
| 248651 | +** |
| 248652 | +** If parameter pColset is not NULL, then it is used to filter entries before |
| 248653 | +** the callback is invoked. |
| 248654 | +*/ |
| 248655 | +static int fts5VisitEntries( |
| 248656 | + Fts5Index *p, /* Fts5 index object */ |
| 248657 | + Fts5Colset *pColset, /* Columns filter to apply, or NULL */ |
| 248658 | + u8 *pToken, /* Buffer containing token */ |
| 248659 | + int nToken, /* Size of buffer pToken in bytes */ |
| 248660 | + int bPrefix, /* True for a prefix scan */ |
| 248661 | + void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int), |
| 248662 | + void *pCtx /* Passed as second argument to xVisit() */ |
| 248663 | +){ |
| 248664 | + const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0) |
| 248665 | + | FTS5INDEX_QUERY_SKIPEMPTY |
| 248666 | + | FTS5INDEX_QUERY_NOOUTPUT; |
| 248667 | + Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ |
| 248668 | + int bNewTerm = 1; |
| 248669 | + Fts5Structure *pStruct = fts5StructureRead(p); |
| 248670 | + |
| 248671 | + fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); |
| 248672 | + fts5IterSetOutputCb(&p->rc, p1); |
| 248673 | + for( /* no-op */ ; |
| 248674 | + fts5MultiIterEof(p, p1)==0; |
| 248675 | + fts5MultiIterNext2(p, p1, &bNewTerm) |
| 248676 | + ){ |
| 248677 | + Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; |
| 248678 | + int nNew = 0; |
| 248679 | + const u8 *pNew = 0; |
| 248680 | + |
| 248681 | + p1->xSetOutputs(p1, pSeg); |
| 248682 | + if( p->rc ) break; |
| 248683 | + |
| 248684 | + if( bNewTerm ){ |
| 248685 | + nNew = pSeg->term.n; |
| 248686 | + pNew = pSeg->term.p; |
| 248687 | + if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break; |
| 248688 | + } |
| 248689 | + |
| 248690 | + xVisit(p, pCtx, p1, pNew, nNew); |
| 248691 | + } |
| 248692 | + fts5MultiIterFree(p1); |
| 248693 | + |
| 248694 | + fts5StructureRelease(pStruct); |
| 248695 | + return p->rc; |
| 248696 | +} |
| 248697 | + |
| 248698 | + |
| 248699 | +/* |
| 248700 | +** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an |
| 248701 | +** array of these for each row it visits (so all iRowid fields are the same). |
| 248702 | +** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an |
| 248703 | +** array of these for the entire query (in which case iRowid fields may take |
| 248704 | +** a variety of values). |
| 248705 | +** |
| 248706 | +** Each instance in the array indicates the iterator (and therefore term) |
| 248707 | +** associated with position iPos of rowid iRowid. This is used by the |
| 248708 | +** xInstToken() API. |
| 248709 | +** |
| 248710 | +** iRowid: |
| 248711 | +** Rowid for the current entry. |
| 248712 | +** |
| 248713 | +** iPos: |
| 248714 | +** Position of current entry within row. In the usual ((iCol<<32)+iOff) |
| 248715 | +** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()). |
| 248716 | +** |
| 248717 | +** iIter: |
| 248718 | +** If the Fts5TokenDataIter iterator that the entry is part of is |
| 248719 | +** actually an iterator (i.e. with nIter>0, not just a container for |
| 248720 | +** Fts5TokenDataMap structures), then this variable is an index into |
| 248721 | +** the apIter[] array. The corresponding term is that which the iterator |
| 248722 | +** at apIter[iIter] currently points to. |
| 248723 | +** |
| 248724 | +** Or, if the Fts5TokenDataIter iterator is just a container object |
| 248725 | +** (nIter==0), then iIter is an index into the term.p[] buffer where |
| 248726 | +** the term is stored. |
| 248727 | +** |
| 248728 | +** nByte: |
| 248729 | +** In the case where iIter is an index into term.p[], this variable |
| 248730 | +** is the size of the term in bytes. If iIter is an index into apIter[], |
| 248731 | +** this variable is unused. |
| 248732 | +*/ |
| 248733 | +struct Fts5TokenDataMap { |
| 248734 | + i64 iRowid; /* Row this token is located in */ |
| 248735 | + i64 iPos; /* Position of token */ |
| 248736 | + int iIter; /* Iterator token was read from */ |
| 248737 | + int nByte; /* Length of token in bytes (or 0) */ |
| 248738 | +}; |
| 248739 | + |
| 248740 | +/* |
| 248741 | +** An object used to supplement Fts5Iter for tokendata=1 iterators. |
| 248742 | +** |
| 248743 | +** This object serves two purposes. The first is as a container for an array |
| 248744 | +** of Fts5TokenDataMap structures, which are used to find the token required |
| 248745 | +** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and |
| 248746 | +** aMap[] variables. |
| 248747 | +*/ |
| 248748 | +struct Fts5TokenDataIter { |
| 248749 | + int nMapAlloc; /* Allocated size of aMap[] in entries */ |
| 248750 | + int nMap; /* Number of valid entries in aMap[] */ |
| 248751 | + Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */ |
| 248752 | + |
| 248753 | + /* The following are used for prefix-queries only. */ |
| 248754 | + Fts5Buffer terms; |
| 248755 | + |
| 248756 | + /* The following are used for other full-token tokendata queries only. */ |
| 248757 | + int nIter; |
| 248758 | + int nIterAlloc; |
| 248759 | + Fts5PoslistReader *aPoslistReader; |
| 248760 | + int *aPoslistToIter; |
| 248761 | + Fts5Iter *apIter[1]; |
| 248762 | +}; |
| 248763 | + |
| 248764 | +/* |
| 248765 | +** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 248766 | +** merges the two arrays together and writes the result to output array |
| 248767 | +** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| 248768 | +** |
| 248769 | +** Duplicate entries are copied into the output. So the size of the output |
| 248770 | +** array is always (n1+n2) entries. |
| 248771 | +*/ |
| 248772 | +static void fts5TokendataMerge( |
| 248773 | + Fts5TokenDataMap *a1, int n1, /* Input array 1 */ |
| 248774 | + Fts5TokenDataMap *a2, int n2, /* Input array 2 */ |
| 248775 | + Fts5TokenDataMap *aOut /* Output array */ |
| 248776 | +){ |
| 248777 | + int i1 = 0; |
| 248778 | + int i2 = 0; |
| 248779 | + |
| 248780 | + assert( n1>=0 && n2>=0 ); |
| 248781 | + while( i1<n1 || i2<n2 ){ |
| 248782 | + Fts5TokenDataMap *pOut = &aOut[i1+i2]; |
| 248783 | + if( i2>=n2 || (i1<n1 && ( |
| 248784 | + a1[i1].iRowid<a2[i2].iRowid |
| 248785 | + || (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos) |
| 248786 | + ))){ |
| 248787 | + memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap)); |
| 248788 | + i1++; |
| 248789 | + }else{ |
| 248790 | + memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap)); |
| 248791 | + i2++; |
| 248792 | + } |
| 248793 | + } |
| 248794 | +} |
| 248795 | + |
| 248796 | + |
| 248797 | +/* |
| 248798 | +** Append a mapping to the token-map belonging to object pT. |
| 248799 | +*/ |
| 248800 | +static void fts5TokendataIterAppendMap( |
| 248801 | + Fts5Index *p, |
| 248802 | + Fts5TokenDataIter *pT, |
| 248803 | + int iIter, |
| 248804 | + int nByte, |
| 248805 | + i64 iRowid, |
| 248806 | + i64 iPos |
| 248807 | +){ |
| 248808 | + if( p->rc==SQLITE_OK ){ |
| 248809 | + if( pT->nMap==pT->nMapAlloc ){ |
| 248810 | + int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; |
| 248811 | + int nAlloc = nNew * sizeof(Fts5TokenDataMap); |
| 248812 | + Fts5TokenDataMap *aNew; |
| 248813 | + |
| 248814 | + aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc); |
| 248815 | + if( aNew==0 ){ |
| 248816 | + p->rc = SQLITE_NOMEM; |
| 248817 | + return; |
| 248818 | + } |
| 248819 | + |
| 248820 | + pT->aMap = aNew; |
| 248821 | + pT->nMapAlloc = nNew; |
| 248822 | + } |
| 248823 | + |
| 248824 | + pT->aMap[pT->nMap].iRowid = iRowid; |
| 248825 | + pT->aMap[pT->nMap].iPos = iPos; |
| 248826 | + pT->aMap[pT->nMap].iIter = iIter; |
| 248827 | + pT->aMap[pT->nMap].nByte = nByte; |
| 248828 | + pT->nMap++; |
| 248829 | + } |
| 248830 | +} |
| 248831 | + |
| 248832 | +/* |
| 248833 | +** Sort the contents of the pT->aMap[] array. |
| 248834 | +** |
| 248835 | +** The sorting algorithm requries a malloc(). If this fails, an error code |
| 248836 | +** is left in Fts5Index.rc before returning. |
| 248837 | +*/ |
| 248838 | +static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 248839 | + Fts5TokenDataMap *aTmp = 0; |
| 248840 | + int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| 248841 | + |
| 248842 | + aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 248843 | + if( aTmp ){ |
| 248844 | + Fts5TokenDataMap *a1 = pT->aMap; |
| 248845 | + Fts5TokenDataMap *a2 = aTmp; |
| 248846 | + i64 nHalf; |
| 248847 | + |
| 248848 | + for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){ |
| 248849 | + int i1; |
| 248850 | + for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){ |
| 248851 | + int n1 = MIN(nHalf, pT->nMap-i1); |
| 248852 | + int n2 = MIN(nHalf, pT->nMap-i1-n1); |
| 248853 | + fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]); |
| 248854 | + } |
| 248855 | + SWAPVAL(Fts5TokenDataMap*, a1, a2); |
| 248856 | + } |
| 248857 | + |
| 248858 | + if( a1!=pT->aMap ){ |
| 248859 | + memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap)); |
| 248860 | + } |
| 248861 | + sqlite3_free(aTmp); |
| 248862 | + |
| 248863 | +#ifdef SQLITE_DEBUG |
| 248864 | + { |
| 248865 | + int ii; |
| 248866 | + for(ii=1; ii<pT->nMap; ii++){ |
| 248867 | + Fts5TokenDataMap *p1 = &pT->aMap[ii-1]; |
| 248868 | + Fts5TokenDataMap *p2 = &pT->aMap[ii]; |
| 248869 | + assert( p1->iRowid<p2->iRowid |
| 248870 | + || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos) |
| 248871 | + ); |
| 248872 | + } |
| 248873 | + } |
| 248874 | +#endif |
| 248875 | + } |
| 248876 | +} |
| 248877 | + |
| 248878 | +/* |
| 248879 | +** Delete an Fts5TokenDataIter structure and its contents. |
| 248880 | +*/ |
| 248881 | +static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ |
| 248882 | + if( pSet ){ |
| 248883 | + int ii; |
| 248884 | + for(ii=0; ii<pSet->nIter; ii++){ |
| 248885 | + fts5MultiIterFree(pSet->apIter[ii]); |
| 248886 | + } |
| 248887 | + fts5BufferFree(&pSet->terms); |
| 248888 | + sqlite3_free(pSet->aPoslistReader); |
| 248889 | + sqlite3_free(pSet->aMap); |
| 248890 | + sqlite3_free(pSet); |
| 248891 | + } |
| 248892 | +} |
| 248893 | + |
| 248894 | + |
| 248895 | +/* |
| 248896 | +** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata() |
| 248897 | +** to pass data to prefixIterSetupTokendataCb(). |
| 248898 | +*/ |
| 248899 | +typedef struct TokendataSetupCtx TokendataSetupCtx; |
| 248900 | +struct TokendataSetupCtx { |
| 248901 | + Fts5TokenDataIter *pT; /* Object being populated with mappings */ |
| 248902 | + int iTermOff; /* Offset of current term in terms.p[] */ |
| 248903 | + int nTermByte; /* Size of current term in bytes */ |
| 248904 | +}; |
| 248905 | + |
| 248906 | +/* |
| 248907 | +** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This |
| 248908 | +** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each |
| 248909 | +** position in the current position-list. It doesn't matter that some of |
| 248910 | +** these may be out of order - they will be sorted later. |
| 248911 | +*/ |
| 248912 | +static void prefixIterSetupTokendataCb( |
| 248913 | + Fts5Index *p, |
| 248914 | + void *pCtx, |
| 248915 | + Fts5Iter *p1, |
| 248916 | + const u8 *pNew, |
| 248917 | + int nNew |
| 248918 | +){ |
| 248919 | + TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx; |
| 248920 | + int iPosOff = 0; |
| 248921 | + i64 iPos = 0; |
| 248922 | + |
| 248923 | + if( pNew ){ |
| 248924 | + pSetup->nTermByte = nNew-1; |
| 248925 | + pSetup->iTermOff = pSetup->pT->terms.n; |
| 248926 | + fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1); |
| 248927 | + } |
| 248928 | + |
| 248929 | + while( 0==sqlite3Fts5PoslistNext64( |
| 248930 | + p1->base.pData, p1->base.nData, &iPosOff, &iPos |
| 248931 | + ) ){ |
| 248932 | + fts5TokendataIterAppendMap(p, |
| 248933 | + pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos |
| 248934 | + ); |
| 248935 | + } |
| 248936 | +} |
| 248937 | + |
| 248938 | + |
| 248939 | +/* |
| 248940 | +** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries(). |
| 248941 | +*/ |
| 248942 | +typedef struct PrefixSetupCtx PrefixSetupCtx; |
| 248943 | +struct PrefixSetupCtx { |
| 248944 | + void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); |
| 248945 | + void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); |
| 248946 | + i64 iLastRowid; |
| 248947 | + int nMerge; |
| 248948 | + Fts5Buffer *aBuf; |
| 248949 | + int nBuf; |
| 248950 | + Fts5Buffer doclist; |
| 248951 | + TokendataSetupCtx *pTokendata; |
| 248952 | +}; |
| 248953 | + |
| 248954 | +/* |
| 248955 | +** fts5VisitEntries() callback used by fts5SetupPrefixIter() |
| 248956 | +*/ |
| 248957 | +static void prefixIterSetupCb( |
| 248958 | + Fts5Index *p, |
| 248959 | + void *pCtx, |
| 248960 | + Fts5Iter *p1, |
| 248961 | + const u8 *pNew, |
| 248962 | + int nNew |
| 248963 | +){ |
| 248964 | + PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx; |
| 248965 | + const int nMerge = pSetup->nMerge; |
| 248966 | + |
| 248967 | + if( p1->base.nData>0 ){ |
| 248968 | + if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){ |
| 248969 | + int i; |
| 248970 | + for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){ |
| 248971 | + int i1 = i*nMerge; |
| 248972 | + int iStore; |
| 248973 | + assert( i1+nMerge<=pSetup->nBuf ); |
| 248974 | + for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248975 | + if( pSetup->aBuf[iStore].n==0 ){ |
| 248976 | + fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]); |
| 248977 | + fts5BufferZero(&pSetup->doclist); |
| 248978 | + break; |
| 248979 | + } |
| 248980 | + } |
| 248981 | + if( iStore==i1+nMerge ){ |
| 248982 | + pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]); |
| 248983 | + for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248984 | + fts5BufferZero(&pSetup->aBuf[iStore]); |
| 248985 | + } |
| 248986 | + } |
| 248987 | + } |
| 248988 | + pSetup->iLastRowid = 0; |
| 248989 | + } |
| 248990 | + |
| 248991 | + pSetup->xAppend( |
| 248992 | + p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist |
| 248993 | + ); |
| 248994 | + pSetup->iLastRowid = p1->base.iRowid; |
| 248995 | + } |
| 248996 | + |
| 248997 | + if( pSetup->pTokendata ){ |
| 248998 | + prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew); |
| 248999 | + } |
| 249000 | +} |
| 249001 | + |
| 248470 | 249002 | static void fts5SetupPrefixIter( |
| 248471 | 249003 | Fts5Index *p, /* Index to read from */ |
| 248472 | 249004 | int bDesc, /* True for "ORDER BY rowid DESC" */ |
| 248473 | 249005 | int iIdx, /* Index to scan for data */ |
| 248474 | 249006 | u8 *pToken, /* Buffer containing prefix to match */ |
| | @@ -248475,137 +249007,89 @@ |
| 248475 | 249007 | int nToken, /* Size of buffer pToken in bytes */ |
| 248476 | 249008 | Fts5Colset *pColset, /* Restrict matches to these columns */ |
| 248477 | 249009 | Fts5Iter **ppIter /* OUT: New iterator */ |
| 248478 | 249010 | ){ |
| 248479 | 249011 | Fts5Structure *pStruct; |
| 248480 | | - Fts5Buffer *aBuf; |
| 248481 | | - int nBuf = 32; |
| 248482 | | - int nMerge = 1; |
| 249012 | + PrefixSetupCtx s; |
| 249013 | + TokendataSetupCtx s2; |
| 248483 | 249014 | |
| 248484 | | - void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); |
| 248485 | | - void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); |
| 249015 | + memset(&s, 0, sizeof(s)); |
| 249016 | + memset(&s2, 0, sizeof(s2)); |
| 249017 | + |
| 249018 | + s.nMerge = 1; |
| 249019 | + s.iLastRowid = 0; |
| 249020 | + s.nBuf = 32; |
| 249021 | + if( iIdx==0 |
| 249022 | + && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 249023 | + && p->pConfig->bPrefixInsttoken |
| 249024 | + ){ |
| 249025 | + s.pTokendata = &s2; |
| 249026 | + s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT)); |
| 249027 | + } |
| 249028 | + |
| 248486 | 249029 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 248487 | | - xMerge = fts5MergeRowidLists; |
| 248488 | | - xAppend = fts5AppendRowid; |
| 249030 | + s.xMerge = fts5MergeRowidLists; |
| 249031 | + s.xAppend = fts5AppendRowid; |
| 248489 | 249032 | }else{ |
| 248490 | | - nMerge = FTS5_MERGE_NLIST-1; |
| 248491 | | - nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ |
| 248492 | | - xMerge = fts5MergePrefixLists; |
| 248493 | | - xAppend = fts5AppendPoslist; |
| 249033 | + s.nMerge = FTS5_MERGE_NLIST-1; |
| 249034 | + s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ |
| 249035 | + s.xMerge = fts5MergePrefixLists; |
| 249036 | + s.xAppend = fts5AppendPoslist; |
| 248494 | 249037 | } |
| 248495 | 249038 | |
| 248496 | | - aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); |
| 249039 | + s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf); |
| 248497 | 249040 | pStruct = fts5StructureRead(p); |
| 248498 | | - assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); |
| 249041 | + assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) ); |
| 248499 | 249042 | |
| 248500 | 249043 | if( p->rc==SQLITE_OK ){ |
| 248501 | | - const int flags = FTS5INDEX_QUERY_SCAN |
| 248502 | | - | FTS5INDEX_QUERY_SKIPEMPTY |
| 248503 | | - | FTS5INDEX_QUERY_NOOUTPUT; |
| 249044 | + void *pCtx = (void*)&s; |
| 248504 | 249045 | int i; |
| 248505 | | - i64 iLastRowid = 0; |
| 248506 | | - Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ |
| 248507 | 249046 | Fts5Data *pData; |
| 248508 | | - Fts5Buffer doclist; |
| 248509 | | - int bNewTerm = 1; |
| 248510 | | - |
| 248511 | | - memset(&doclist, 0, sizeof(doclist)); |
| 248512 | 249047 | |
| 248513 | 249048 | /* If iIdx is non-zero, then it is the number of a prefix-index for |
| 248514 | 249049 | ** prefixes 1 character longer than the prefix being queried for. That |
| 248515 | 249050 | ** index contains all the doclists required, except for the one |
| 248516 | 249051 | ** corresponding to the prefix itself. That one is extracted from the |
| 248517 | 249052 | ** main term index here. */ |
| 248518 | 249053 | if( iIdx!=0 ){ |
| 248519 | | - int dummy = 0; |
| 248520 | | - const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; |
| 248521 | 249054 | pToken[0] = FTS5_MAIN_PREFIX; |
| 248522 | | - fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1); |
| 248523 | | - fts5IterSetOutputCb(&p->rc, p1); |
| 248524 | | - for(; |
| 248525 | | - fts5MultiIterEof(p, p1)==0; |
| 248526 | | - fts5MultiIterNext2(p, p1, &dummy) |
| 248527 | | - ){ |
| 248528 | | - Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; |
| 248529 | | - p1->xSetOutputs(p1, pSeg); |
| 248530 | | - if( p1->base.nData ){ |
| 248531 | | - xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); |
| 248532 | | - iLastRowid = p1->base.iRowid; |
| 248533 | | - } |
| 248534 | | - } |
| 248535 | | - fts5MultiIterFree(p1); |
| 249055 | + fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx); |
| 248536 | 249056 | } |
| 248537 | 249057 | |
| 248538 | 249058 | pToken[0] = FTS5_MAIN_PREFIX + iIdx; |
| 248539 | | - fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); |
| 248540 | | - fts5IterSetOutputCb(&p->rc, p1); |
| 248541 | | - |
| 248542 | | - for( /* no-op */ ; |
| 248543 | | - fts5MultiIterEof(p, p1)==0; |
| 248544 | | - fts5MultiIterNext2(p, p1, &bNewTerm) |
| 248545 | | - ){ |
| 248546 | | - Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; |
| 248547 | | - int nTerm = pSeg->term.n; |
| 248548 | | - const u8 *pTerm = pSeg->term.p; |
| 248549 | | - p1->xSetOutputs(p1, pSeg); |
| 248550 | | - |
| 248551 | | - assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); |
| 248552 | | - if( bNewTerm ){ |
| 248553 | | - if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break; |
| 248554 | | - } |
| 248555 | | - |
| 248556 | | - if( p1->base.nData==0 ) continue; |
| 248557 | | - if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ |
| 248558 | | - for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ |
| 248559 | | - int i1 = i*nMerge; |
| 248560 | | - int iStore; |
| 248561 | | - assert( i1+nMerge<=nBuf ); |
| 248562 | | - for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248563 | | - if( aBuf[iStore].n==0 ){ |
| 248564 | | - fts5BufferSwap(&doclist, &aBuf[iStore]); |
| 248565 | | - fts5BufferZero(&doclist); |
| 248566 | | - break; |
| 248567 | | - } |
| 248568 | | - } |
| 248569 | | - if( iStore==i1+nMerge ){ |
| 248570 | | - xMerge(p, &doclist, nMerge, &aBuf[i1]); |
| 248571 | | - for(iStore=i1; iStore<i1+nMerge; iStore++){ |
| 248572 | | - fts5BufferZero(&aBuf[iStore]); |
| 248573 | | - } |
| 248574 | | - } |
| 248575 | | - } |
| 248576 | | - iLastRowid = 0; |
| 248577 | | - } |
| 248578 | | - |
| 248579 | | - xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); |
| 248580 | | - iLastRowid = p1->base.iRowid; |
| 248581 | | - } |
| 248582 | | - |
| 248583 | | - assert( (nBuf%nMerge)==0 ); |
| 248584 | | - for(i=0; i<nBuf; i+=nMerge){ |
| 249059 | + fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx); |
| 249060 | + |
| 249061 | + assert( (s.nBuf%s.nMerge)==0 ); |
| 249062 | + for(i=0; i<s.nBuf; i+=s.nMerge){ |
| 248585 | 249063 | int iFree; |
| 248586 | 249064 | if( p->rc==SQLITE_OK ){ |
| 248587 | | - xMerge(p, &doclist, nMerge, &aBuf[i]); |
| 249065 | + s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]); |
| 248588 | 249066 | } |
| 248589 | | - for(iFree=i; iFree<i+nMerge; iFree++){ |
| 248590 | | - fts5BufferFree(&aBuf[iFree]); |
| 249067 | + for(iFree=i; iFree<i+s.nMerge; iFree++){ |
| 249068 | + fts5BufferFree(&s.aBuf[iFree]); |
| 248591 | 249069 | } |
| 248592 | 249070 | } |
| 248593 | | - fts5MultiIterFree(p1); |
| 248594 | 249071 | |
| 248595 | | - pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING); |
| 249072 | + pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING); |
| 248596 | 249073 | if( pData ){ |
| 248597 | 249074 | pData->p = (u8*)&pData[1]; |
| 248598 | | - pData->nn = pData->szLeaf = doclist.n; |
| 248599 | | - if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); |
| 249075 | + pData->nn = pData->szLeaf = s.doclist.n; |
| 249076 | + if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n); |
| 248600 | 249077 | fts5MultiIterNew2(p, pData, bDesc, ppIter); |
| 248601 | 249078 | } |
| 248602 | | - fts5BufferFree(&doclist); |
| 249079 | + |
| 249080 | + if( p->rc==SQLITE_OK && s.pTokendata ){ |
| 249081 | + fts5TokendataIterSortMap(p, s2.pT); |
| 249082 | + (*ppIter)->pTokenDataIter = s2.pT; |
| 249083 | + s2.pT = 0; |
| 249084 | + } |
| 248603 | 249085 | } |
| 248604 | 249086 | |
| 249087 | + fts5TokendataIterDelete(s2.pT); |
| 249088 | + fts5BufferFree(&s.doclist); |
| 248605 | 249089 | fts5StructureRelease(pStruct); |
| 248606 | | - sqlite3_free(aBuf); |
| 249090 | + sqlite3_free(s.aBuf); |
| 248607 | 249091 | } |
| 248608 | 249092 | |
| 248609 | 249093 | |
| 248610 | 249094 | /* |
| 248611 | 249095 | ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain |
| | @@ -248855,42 +249339,10 @@ |
| 248855 | 249339 | static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ |
| 248856 | 249340 | fts5DataRelease(pSeg->pLeaf); |
| 248857 | 249341 | pSeg->pLeaf = 0; |
| 248858 | 249342 | } |
| 248859 | 249343 | |
| 248860 | | -/* |
| 248861 | | -** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an |
| 248862 | | -** array of these for each row it visits. Or, for an iterator used by an |
| 248863 | | -** "ORDER BY rank" query, it accumulates an array of these for the entire |
| 248864 | | -** query. |
| 248865 | | -** |
| 248866 | | -** Each instance in the array indicates the iterator (and therefore term) |
| 248867 | | -** associated with position iPos of rowid iRowid. This is used by the |
| 248868 | | -** xInstToken() API. |
| 248869 | | -*/ |
| 248870 | | -struct Fts5TokenDataMap { |
| 248871 | | - i64 iRowid; /* Row this token is located in */ |
| 248872 | | - i64 iPos; /* Position of token */ |
| 248873 | | - int iIter; /* Iterator token was read from */ |
| 248874 | | -}; |
| 248875 | | - |
| 248876 | | -/* |
| 248877 | | -** An object used to supplement Fts5Iter for tokendata=1 iterators. |
| 248878 | | -*/ |
| 248879 | | -struct Fts5TokenDataIter { |
| 248880 | | - int nIter; |
| 248881 | | - int nIterAlloc; |
| 248882 | | - |
| 248883 | | - int nMap; |
| 248884 | | - int nMapAlloc; |
| 248885 | | - Fts5TokenDataMap *aMap; |
| 248886 | | - |
| 248887 | | - Fts5PoslistReader *aPoslistReader; |
| 248888 | | - int *aPoslistToIter; |
| 248889 | | - Fts5Iter *apIter[1]; |
| 248890 | | -}; |
| 248891 | | - |
| 248892 | 249344 | /* |
| 248893 | 249345 | ** This function appends iterator pAppend to Fts5TokenDataIter pIn and |
| 248894 | 249346 | ** returns the result. |
| 248895 | 249347 | */ |
| 248896 | 249348 | static Fts5TokenDataIter *fts5AppendTokendataIter( |
| | @@ -248923,58 +249375,10 @@ |
| 248923 | 249375 | assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); |
| 248924 | 249376 | |
| 248925 | 249377 | return pRet; |
| 248926 | 249378 | } |
| 248927 | 249379 | |
| 248928 | | -/* |
| 248929 | | -** Delete an Fts5TokenDataIter structure and its contents. |
| 248930 | | -*/ |
| 248931 | | -static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ |
| 248932 | | - if( pSet ){ |
| 248933 | | - int ii; |
| 248934 | | - for(ii=0; ii<pSet->nIter; ii++){ |
| 248935 | | - fts5MultiIterFree(pSet->apIter[ii]); |
| 248936 | | - } |
| 248937 | | - sqlite3_free(pSet->aPoslistReader); |
| 248938 | | - sqlite3_free(pSet->aMap); |
| 248939 | | - sqlite3_free(pSet); |
| 248940 | | - } |
| 248941 | | -} |
| 248942 | | - |
| 248943 | | -/* |
| 248944 | | -** Append a mapping to the token-map belonging to object pT. |
| 248945 | | -*/ |
| 248946 | | -static void fts5TokendataIterAppendMap( |
| 248947 | | - Fts5Index *p, |
| 248948 | | - Fts5TokenDataIter *pT, |
| 248949 | | - int iIter, |
| 248950 | | - i64 iRowid, |
| 248951 | | - i64 iPos |
| 248952 | | -){ |
| 248953 | | - if( p->rc==SQLITE_OK ){ |
| 248954 | | - if( pT->nMap==pT->nMapAlloc ){ |
| 248955 | | - int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; |
| 248956 | | - int nByte = nNew * sizeof(Fts5TokenDataMap); |
| 248957 | | - Fts5TokenDataMap *aNew; |
| 248958 | | - |
| 248959 | | - aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte); |
| 248960 | | - if( aNew==0 ){ |
| 248961 | | - p->rc = SQLITE_NOMEM; |
| 248962 | | - return; |
| 248963 | | - } |
| 248964 | | - |
| 248965 | | - pT->aMap = aNew; |
| 248966 | | - pT->nMapAlloc = nNew; |
| 248967 | | - } |
| 248968 | | - |
| 248969 | | - pT->aMap[pT->nMap].iRowid = iRowid; |
| 248970 | | - pT->aMap[pT->nMap].iPos = iPos; |
| 248971 | | - pT->aMap[pT->nMap].iIter = iIter; |
| 248972 | | - pT->nMap++; |
| 248973 | | - } |
| 248974 | | -} |
| 248975 | | - |
| 248976 | 249380 | /* |
| 248977 | 249381 | ** The iterator passed as the only argument must be a tokendata=1 iterator |
| 248978 | 249382 | ** (pIter->pTokenDataIter!=0). This function sets the iterator output |
| 248979 | 249383 | ** variables (pIter->base.*) according to the contents of the current |
| 248980 | 249384 | ** row. |
| | @@ -249011,11 +249415,11 @@ |
| 249011 | 249415 | int eDetail = pIter->pIndex->pConfig->eDetail; |
| 249012 | 249416 | pIter->base.bEof = 0; |
| 249013 | 249417 | pIter->base.iRowid = iRowid; |
| 249014 | 249418 | |
| 249015 | 249419 | if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ |
| 249016 | | - fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1); |
| 249420 | + fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1); |
| 249017 | 249421 | }else |
| 249018 | 249422 | if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ |
| 249019 | 249423 | int nReader = 0; |
| 249020 | 249424 | int nByte = 0; |
| 249021 | 249425 | i64 iPrev = 0; |
| | @@ -249264,10 +249668,11 @@ |
| 249264 | 249668 | |
| 249265 | 249669 | if( p->rc==SQLITE_OK ){ |
| 249266 | 249670 | pRet = fts5MultiIterAlloc(p, 0); |
| 249267 | 249671 | } |
| 249268 | 249672 | if( pRet ){ |
| 249673 | + pRet->nSeg = 0; |
| 249269 | 249674 | pRet->pTokenDataIter = pSet; |
| 249270 | 249675 | if( pSet ){ |
| 249271 | 249676 | fts5IterSetOutputsTokendata(pRet); |
| 249272 | 249677 | }else{ |
| 249273 | 249678 | pRet->base.bEof = 1; |
| | @@ -249278,11 +249683,10 @@ |
| 249278 | 249683 | |
| 249279 | 249684 | fts5StructureRelease(pStruct); |
| 249280 | 249685 | fts5BufferFree(&bSeek); |
| 249281 | 249686 | return pRet; |
| 249282 | 249687 | } |
| 249283 | | - |
| 249284 | 249688 | |
| 249285 | 249689 | /* |
| 249286 | 249690 | ** Open a new iterator to iterate though all rowid that match the |
| 249287 | 249691 | ** specified token or token prefix. |
| 249288 | 249692 | */ |
| | @@ -249304,10 +249708,15 @@ |
| 249304 | 249708 | int iIdx = 0; /* Index to search */ |
| 249305 | 249709 | int iPrefixIdx = 0; /* +1 prefix index */ |
| 249306 | 249710 | int bTokendata = pConfig->bTokendata; |
| 249307 | 249711 | if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); |
| 249308 | 249712 | |
| 249713 | + /* The NOTOKENDATA flag is set when each token in a tokendata=1 table |
| 249714 | + ** should be treated individually, instead of merging all those with |
| 249715 | + ** a common prefix into a single entry. This is used, for example, by |
| 249716 | + ** queries performed as part of an integrity-check, or by the fts5vocab |
| 249717 | + ** module. */ |
| 249309 | 249718 | if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ |
| 249310 | 249719 | bTokendata = 0; |
| 249311 | 249720 | } |
| 249312 | 249721 | |
| 249313 | 249722 | /* Figure out which index to search and set iIdx accordingly. If this |
| | @@ -249334,11 +249743,11 @@ |
| 249334 | 249743 | if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; |
| 249335 | 249744 | } |
| 249336 | 249745 | } |
| 249337 | 249746 | |
| 249338 | 249747 | if( bTokendata && iIdx==0 ){ |
| 249339 | | - buf.p[0] = '0'; |
| 249748 | + buf.p[0] = FTS5_MAIN_PREFIX; |
| 249340 | 249749 | pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); |
| 249341 | 249750 | }else if( iIdx<=pConfig->nPrefix ){ |
| 249342 | 249751 | /* Straight index lookup */ |
| 249343 | 249752 | Fts5Structure *pStruct = fts5StructureRead(p); |
| 249344 | 249753 | buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); |
| | @@ -249347,11 +249756,11 @@ |
| 249347 | 249756 | pColset, buf.p, nToken+1, -1, 0, &pRet |
| 249348 | 249757 | ); |
| 249349 | 249758 | fts5StructureRelease(pStruct); |
| 249350 | 249759 | } |
| 249351 | 249760 | }else{ |
| 249352 | | - /* Scan multiple terms in the main index */ |
| 249761 | + /* Scan multiple terms in the main index for a prefix query. */ |
| 249353 | 249762 | int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; |
| 249354 | 249763 | fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); |
| 249355 | 249764 | if( pRet==0 ){ |
| 249356 | 249765 | assert( p->rc!=SQLITE_OK ); |
| 249357 | 249766 | }else{ |
| | @@ -249383,11 +249792,12 @@ |
| 249383 | 249792 | ** Move to the next matching rowid. |
| 249384 | 249793 | */ |
| 249385 | 249794 | static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ |
| 249386 | 249795 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249387 | 249796 | assert( pIter->pIndex->rc==SQLITE_OK ); |
| 249388 | | - if( pIter->pTokenDataIter ){ |
| 249797 | + if( pIter->nSeg==0 ){ |
| 249798 | + assert( pIter->pTokenDataIter ); |
| 249389 | 249799 | fts5TokendataIterNext(pIter, 0, 0); |
| 249390 | 249800 | }else{ |
| 249391 | 249801 | fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); |
| 249392 | 249802 | } |
| 249393 | 249803 | return fts5IndexReturn(pIter->pIndex); |
| | @@ -249420,11 +249830,12 @@ |
| 249420 | 249830 | ** definition of "at or after" depends on whether this iterator iterates |
| 249421 | 249831 | ** in ascending or descending rowid order. |
| 249422 | 249832 | */ |
| 249423 | 249833 | static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ |
| 249424 | 249834 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249425 | | - if( pIter->pTokenDataIter ){ |
| 249835 | + if( pIter->nSeg==0 ){ |
| 249836 | + assert( pIter->pTokenDataIter ); |
| 249426 | 249837 | fts5TokendataIterNext(pIter, 1, iMatch); |
| 249427 | 249838 | }else{ |
| 249428 | 249839 | fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); |
| 249429 | 249840 | } |
| 249430 | 249841 | return fts5IndexReturn(pIter->pIndex); |
| | @@ -249438,32 +249849,87 @@ |
| 249438 | 249849 | const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); |
| 249439 | 249850 | assert_nc( z || n<=1 ); |
| 249440 | 249851 | *pn = n-1; |
| 249441 | 249852 | return (z ? &z[1] : 0); |
| 249442 | 249853 | } |
| 249854 | + |
| 249855 | +/* |
| 249856 | +** pIter is a prefix query. This function populates pIter->pTokenDataIter |
| 249857 | +** with an Fts5TokenDataIter object containing mappings for all rows |
| 249858 | +** matched by the query. |
| 249859 | +*/ |
| 249860 | +static int fts5SetupPrefixIterTokendata( |
| 249861 | + Fts5Iter *pIter, |
| 249862 | + const char *pToken, /* Token prefix to search for */ |
| 249863 | + int nToken /* Size of pToken in bytes */ |
| 249864 | +){ |
| 249865 | + Fts5Index *p = pIter->pIndex; |
| 249866 | + Fts5Buffer token = {0, 0, 0}; |
| 249867 | + TokendataSetupCtx ctx; |
| 249868 | + |
| 249869 | + memset(&ctx, 0, sizeof(ctx)); |
| 249870 | + |
| 249871 | + fts5BufferGrow(&p->rc, &token, nToken+1); |
| 249872 | + ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); |
| 249873 | + |
| 249874 | + if( p->rc==SQLITE_OK ){ |
| 249875 | + |
| 249876 | + /* Fill in the token prefix to search for */ |
| 249877 | + token.p[0] = FTS5_MAIN_PREFIX; |
| 249878 | + memcpy(&token.p[1], pToken, nToken); |
| 249879 | + token.n = nToken+1; |
| 249880 | + |
| 249881 | + fts5VisitEntries( |
| 249882 | + p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx |
| 249883 | + ); |
| 249884 | + |
| 249885 | + fts5TokendataIterSortMap(p, ctx.pT); |
| 249886 | + } |
| 249887 | + |
| 249888 | + if( p->rc==SQLITE_OK ){ |
| 249889 | + pIter->pTokenDataIter = ctx.pT; |
| 249890 | + }else{ |
| 249891 | + fts5TokendataIterDelete(ctx.pT); |
| 249892 | + } |
| 249893 | + fts5BufferFree(&token); |
| 249894 | + |
| 249895 | + return fts5IndexReturn(p); |
| 249896 | +} |
| 249443 | 249897 | |
| 249444 | 249898 | /* |
| 249445 | 249899 | ** This is used by xInstToken() to access the token at offset iOff, column |
| 249446 | 249900 | ** iCol of row iRowid. The token is returned via output variables *ppOut |
| 249447 | 249901 | ** and *pnOut. The iterator passed as the first argument must be a tokendata=1 |
| 249448 | 249902 | ** iterator (pIter->pTokenDataIter!=0). |
| 249903 | +** |
| 249904 | +** pToken/nToken: |
| 249449 | 249905 | */ |
| 249450 | 249906 | static int sqlite3Fts5IterToken( |
| 249451 | 249907 | Fts5IndexIter *pIndexIter, |
| 249908 | + const char *pToken, int nToken, |
| 249452 | 249909 | i64 iRowid, |
| 249453 | 249910 | int iCol, |
| 249454 | 249911 | int iOff, |
| 249455 | 249912 | const char **ppOut, int *pnOut |
| 249456 | 249913 | ){ |
| 249457 | 249914 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249458 | 249915 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249459 | | - Fts5TokenDataMap *aMap = pT->aMap; |
| 249460 | 249916 | i64 iPos = (((i64)iCol)<<32) + iOff; |
| 249461 | | - |
| 249917 | + Fts5TokenDataMap *aMap = 0; |
| 249462 | 249918 | int i1 = 0; |
| 249463 | | - int i2 = pT->nMap; |
| 249919 | + int i2 = 0; |
| 249464 | 249920 | int iTest = 0; |
| 249921 | + |
| 249922 | + assert( pT || (pToken && pIter->nSeg>0) ); |
| 249923 | + if( pT==0 ){ |
| 249924 | + int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken); |
| 249925 | + if( rc!=SQLITE_OK ) return rc; |
| 249926 | + pT = pIter->pTokenDataIter; |
| 249927 | + } |
| 249928 | + |
| 249929 | + i2 = pT->nMap; |
| 249930 | + aMap = pT->aMap; |
| 249465 | 249931 | |
| 249466 | 249932 | while( i2>i1 ){ |
| 249467 | 249933 | iTest = (i1 + i2) / 2; |
| 249468 | 249934 | |
| 249469 | 249935 | if( aMap[iTest].iRowid<iRowid ){ |
| | @@ -249483,13 +249949,19 @@ |
| 249483 | 249949 | } |
| 249484 | 249950 | } |
| 249485 | 249951 | } |
| 249486 | 249952 | |
| 249487 | 249953 | if( i2>i1 ){ |
| 249488 | | - Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; |
| 249489 | | - *ppOut = (const char*)pMap->aSeg[0].term.p+1; |
| 249490 | | - *pnOut = pMap->aSeg[0].term.n-1; |
| 249954 | + if( pIter->nSeg==0 ){ |
| 249955 | + Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; |
| 249956 | + *ppOut = (const char*)pMap->aSeg[0].term.p+1; |
| 249957 | + *pnOut = pMap->aSeg[0].term.n-1; |
| 249958 | + }else{ |
| 249959 | + Fts5TokenDataMap *p = &aMap[iTest]; |
| 249960 | + *ppOut = (const char*)&pT->terms.p[p->iIter]; |
| 249961 | + *pnOut = aMap[iTest].nByte; |
| 249962 | + } |
| 249491 | 249963 | } |
| 249492 | 249964 | |
| 249493 | 249965 | return SQLITE_OK; |
| 249494 | 249966 | } |
| 249495 | 249967 | |
| | @@ -249497,11 +249969,13 @@ |
| 249497 | 249969 | ** Clear any existing entries from the token-map associated with the |
| 249498 | 249970 | ** iterator passed as the only argument. |
| 249499 | 249971 | */ |
| 249500 | 249972 | static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ |
| 249501 | 249973 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249502 | | - if( pIter && pIter->pTokenDataIter ){ |
| 249974 | + if( pIter && pIter->pTokenDataIter |
| 249975 | + && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL) |
| 249976 | + ){ |
| 249503 | 249977 | pIter->pTokenDataIter->nMap = 0; |
| 249504 | 249978 | } |
| 249505 | 249979 | } |
| 249506 | 249980 | |
| 249507 | 249981 | /* |
| | @@ -249517,21 +249991,33 @@ |
| 249517 | 249991 | i64 iRowid, int iCol, int iOff |
| 249518 | 249992 | ){ |
| 249519 | 249993 | Fts5Iter *pIter = (Fts5Iter*)pIndexIter; |
| 249520 | 249994 | Fts5TokenDataIter *pT = pIter->pTokenDataIter; |
| 249521 | 249995 | Fts5Index *p = pIter->pIndex; |
| 249522 | | - int ii; |
| 249996 | + i64 iPos = (((i64)iCol)<<32) + iOff; |
| 249523 | 249997 | |
| 249524 | 249998 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 249525 | | - assert( pIter->pTokenDataIter ); |
| 249526 | | - |
| 249527 | | - for(ii=0; ii<pT->nIter; ii++){ |
| 249528 | | - Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; |
| 249529 | | - if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; |
| 249530 | | - } |
| 249531 | | - if( ii<pT->nIter ){ |
| 249532 | | - fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); |
| 249999 | + assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 250000 | + if( pIter->nSeg>0 ){ |
| 250001 | + /* This is a prefix term iterator. */ |
| 250002 | + if( pT==0 ){ |
| 250003 | + pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT)); |
| 250004 | + pIter->pTokenDataIter = pT; |
| 250005 | + } |
| 250006 | + if( pT ){ |
| 250007 | + fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 250008 | + fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| 250009 | + } |
| 250010 | + }else{ |
| 250011 | + int ii; |
| 250012 | + for(ii=0; ii<pT->nIter; ii++){ |
| 250013 | + Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; |
| 250014 | + if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; |
| 250015 | + } |
| 250016 | + if( ii<pT->nIter ){ |
| 250017 | + fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos); |
| 250018 | + } |
| 249533 | 250019 | } |
| 249534 | 250020 | return fts5IndexReturn(p); |
| 249535 | 250021 | } |
| 249536 | 250022 | |
| 249537 | 250023 | /* |
| | @@ -251432,10 +251918,11 @@ |
| 251432 | 251918 | ** containing a copy of the header from an Fts5Config pointer. |
| 251433 | 251919 | */ |
| 251434 | 251920 | #define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) |
| 251435 | 251921 | #define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr)) |
| 251436 | 251922 | |
| 251923 | +#define FTS5_INSTTOKEN_SUBTYPE 73 |
| 251437 | 251924 | |
| 251438 | 251925 | /* |
| 251439 | 251926 | ** Each auxiliary function registered with the FTS5 module is represented |
| 251440 | 251927 | ** by an object of the following type. All such objects are stored as part |
| 251441 | 251928 | ** of the Fts5Global.pAux list. |
| | @@ -252757,10 +253244,11 @@ |
| 252757 | 253244 | sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ |
| 252758 | 253245 | sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ |
| 252759 | 253246 | sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ |
| 252760 | 253247 | int iCol; /* Column on LHS of MATCH operator */ |
| 252761 | 253248 | char **pzErrmsg = pConfig->pzErrmsg; |
| 253249 | + int bPrefixInsttoken = pConfig->bPrefixInsttoken; |
| 252762 | 253250 | int i; |
| 252763 | 253251 | int iIdxStr = 0; |
| 252764 | 253252 | Fts5Expr *pExpr = 0; |
| 252765 | 253253 | |
| 252766 | 253254 | assert( pConfig->bLock==0 ); |
| | @@ -252792,10 +253280,13 @@ |
| 252792 | 253280 | int bInternal = 0; |
| 252793 | 253281 | |
| 252794 | 253282 | rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); |
| 252795 | 253283 | if( rc!=SQLITE_OK ) goto filter_out; |
| 252796 | 253284 | if( zText==0 ) zText = ""; |
| 253285 | + if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){ |
| 253286 | + pConfig->bPrefixInsttoken = 1; |
| 253287 | + } |
| 252797 | 253288 | |
| 252798 | 253289 | iCol = 0; |
| 252799 | 253290 | do{ |
| 252800 | 253291 | iCol = iCol*10 + (idxStr[iIdxStr]-'0'); |
| 252801 | 253292 | iIdxStr++; |
| | @@ -252932,10 +253423,11 @@ |
| 252932 | 253423 | } |
| 252933 | 253424 | |
| 252934 | 253425 | filter_out: |
| 252935 | 253426 | sqlite3Fts5ExprFree(pExpr); |
| 252936 | 253427 | pConfig->pzErrmsg = pzErrmsg; |
| 253428 | + pConfig->bPrefixInsttoken = bPrefixInsttoken; |
| 252937 | 253429 | return rc; |
| 252938 | 253430 | } |
| 252939 | 253431 | |
| 252940 | 253432 | /* |
| 252941 | 253433 | ** This is the xEof method of the virtual table. SQLite calls this |
| | @@ -254927,11 +255419,11 @@ |
| 254927 | 255419 | int nArg, /* Number of args */ |
| 254928 | 255420 | sqlite3_value **apUnused /* Function arguments */ |
| 254929 | 255421 | ){ |
| 254930 | 255422 | assert( nArg==0 ); |
| 254931 | 255423 | UNUSED_PARAM2(nArg, apUnused); |
| 254932 | | - sqlite3_result_text(pCtx, "fts5: 2024-11-14 19:34:28 81202d2ab5963fdcf20555b6d0b31cc955ac27f1cd87656faea5c0611c9a2ee8", -1, SQLITE_TRANSIENT); |
| 255424 | + sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT); |
| 254933 | 255425 | } |
| 254934 | 255426 | |
| 254935 | 255427 | /* |
| 254936 | 255428 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 254937 | 255429 | ** |
| | @@ -254990,10 +255482,24 @@ |
| 254990 | 255482 | assert( &pCsr[nText]==&pBlob[nBlob] ); |
| 254991 | 255483 | |
| 254992 | 255484 | sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); |
| 254993 | 255485 | } |
| 254994 | 255486 | } |
| 255487 | + |
| 255488 | +/* |
| 255489 | +** Implementation of fts5_insttoken() function. |
| 255490 | +*/ |
| 255491 | +static void fts5InsttokenFunc( |
| 255492 | + sqlite3_context *pCtx, /* Function call context */ |
| 255493 | + int nArg, /* Number of args */ |
| 255494 | + sqlite3_value **apArg /* Function arguments */ |
| 255495 | +){ |
| 255496 | + assert( nArg==1 ); |
| 255497 | + (void)nArg; |
| 255498 | + sqlite3_result_value(pCtx, apArg[0]); |
| 255499 | + sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE); |
| 255500 | +} |
| 254995 | 255501 | |
| 254996 | 255502 | /* |
| 254997 | 255503 | ** Return true if zName is the extension on one of the shadow tables used |
| 254998 | 255504 | ** by this module. |
| 254999 | 255505 | */ |
| | @@ -255120,13 +255626,20 @@ |
| 255120 | 255626 | ); |
| 255121 | 255627 | } |
| 255122 | 255628 | if( rc==SQLITE_OK ){ |
| 255123 | 255629 | rc = sqlite3_create_function( |
| 255124 | 255630 | db, "fts5_locale", 2, |
| 255125 | | - SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, |
| 255631 | + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE, |
| 255126 | 255632 | p, fts5LocaleFunc, 0, 0 |
| 255127 | 255633 | ); |
| 255634 | + } |
| 255635 | + if( rc==SQLITE_OK ){ |
| 255636 | + rc = sqlite3_create_function( |
| 255637 | + db, "fts5_insttoken", 1, |
| 255638 | + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, |
| 255639 | + p, fts5InsttokenFunc, 0, 0 |
| 255640 | + ); |
| 255128 | 255641 | } |
| 255129 | 255642 | } |
| 255130 | 255643 | |
| 255131 | 255644 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
| 255132 | 255645 | ** fts5_test_mi.c is compiled and linked into the executable. And call |
| | @@ -258048,11 +258561,11 @@ |
| 258048 | 258561 | int rc = SQLITE_OK; |
| 258049 | 258562 | char aBuf[32]; |
| 258050 | 258563 | char *zOut = aBuf; |
| 258051 | 258564 | int ii; |
| 258052 | 258565 | const unsigned char *zIn = (const unsigned char*)pText; |
| 258053 | | - const unsigned char *zEof = &zIn[nText]; |
| 258566 | + const unsigned char *zEof = (zIn ? &zIn[nText] : 0); |
| 258054 | 258567 | u32 iCode = 0; |
| 258055 | 258568 | int aStart[3]; /* Input offset of each character in aBuf[] */ |
| 258056 | 258569 | |
| 258057 | 258570 | UNUSED_PARAM(unusedFlags); |
| 258058 | 258571 | |
| 258059 | 258572 | |