| | @@ -1171,11 +1171,11 @@ |
| 1171 | 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | 1173 | */ |
| 1174 | 1174 | #define SQLITE_VERSION "3.34.0" |
| 1175 | 1175 | #define SQLITE_VERSION_NUMBER 3034000 |
| 1176 | | -#define SQLITE_SOURCE_ID "2020-09-30 18:06:51 4a43430fd23f88352c33b29c4c105b72f6dc821f94bf362040c41a1648c402e5" |
| 1176 | +#define SQLITE_SOURCE_ID "2020-10-12 18:09:16 7e17c2f4b7dc9b563d0b4da949bb134dc7c4fc9c86ce03891432a884ca6409d5" |
| 1177 | 1177 | |
| 1178 | 1178 | /* |
| 1179 | 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | 1181 | ** |
| | @@ -88945,11 +88945,12 @@ |
| 88945 | 88945 | ** |
| 88946 | 88946 | ** Begin a transaction on database P1 if a transaction is not already |
| 88947 | 88947 | ** active. |
| 88948 | 88948 | ** If P2 is non-zero, then a write-transaction is started, or if a |
| 88949 | 88949 | ** read-transaction is already active, it is upgraded to a write-transaction. |
| 88950 | | -** If P2 is zero, then a read-transaction is started. |
| 88950 | +** If P2 is zero, then a read-transaction is started. If P2 is 2 or more |
| 88951 | +** then an exclusive transaction is started. |
| 88951 | 88952 | ** |
| 88952 | 88953 | ** P1 is the index of the database file on which the transaction is |
| 88953 | 88954 | ** started. Index 0 is the main database file and index 1 is the |
| 88954 | 88955 | ** file used for temporary tables. Indices of 2 or more are used for |
| 88955 | 88956 | ** attached databases. |
| | @@ -88979,10 +88980,11 @@ |
| 88979 | 88980 | Btree *pBt; |
| 88980 | 88981 | int iMeta = 0; |
| 88981 | 88982 | |
| 88982 | 88983 | assert( p->bIsReader ); |
| 88983 | 88984 | assert( p->readOnly==0 || pOp->p2==0 ); |
| 88985 | + assert( pOp->p2>=0 && pOp->p2<=2 ); |
| 88984 | 88986 | assert( pOp->p1>=0 && pOp->p1<db->nDb ); |
| 88985 | 88987 | assert( DbMaskTest(p->btreeMask, pOp->p1) ); |
| 88986 | 88988 | if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){ |
| 88987 | 88989 | rc = SQLITE_READONLY; |
| 88988 | 88990 | goto abort_due_to_error; |
| | @@ -89842,22 +89844,22 @@ |
| 89842 | 89844 | } |
| 89843 | 89845 | break; |
| 89844 | 89846 | } |
| 89845 | 89847 | |
| 89846 | 89848 | |
| 89847 | | -/* Opcode: SeekScan P1 * * * * |
| 89849 | +/* Opcode: SeekScan P1 P2 * * * |
| 89848 | 89850 | ** Synopsis: Scan-ahead up to P1 rows |
| 89849 | 89851 | ** |
| 89850 | 89852 | ** This opcode is a prefix opcode to OP_SeekGE. In other words, this |
| 89851 | | -** opcode must be immediately followed by OP_SeekGE. Furthermore, the |
| 89852 | | -** OP_SeekGE must be followed by OP_IdxGT. These constraints are |
| 89853 | +** opcode must be immediately followed by OP_SeekGE. This constraint is |
| 89853 | 89854 | ** checked by assert() statements. |
| 89854 | 89855 | ** |
| 89855 | 89856 | ** This opcode uses the P1 through P4 operands of the subsequent |
| 89856 | 89857 | ** OP_SeekGE. In the text that follows, the operands of the subsequent |
| 89857 | 89858 | ** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only |
| 89858 | | -** the P1 operand of this opcode is used, and it is denoted as This.P1. |
| 89859 | +** the P1 and P2 operands of this opcode are also used, and are called |
| 89860 | +** This.P1 and This.P2. |
| 89859 | 89861 | ** |
| 89860 | 89862 | ** This opcode helps to optimize IN operators on a multi-column index |
| 89861 | 89863 | ** where the IN operator is on the later terms of the index by avoiding |
| 89862 | 89864 | ** unnecessary seeks on the btree, substituting steps to the next row |
| 89863 | 89865 | ** of the b-tree instead. A correct answer is obtained if this opcode |
| | @@ -89871,39 +89873,43 @@ |
| 89871 | 89873 | ** then this opcode is a no-op and control passes through into the OP_SeekGE. |
| 89872 | 89874 | ** |
| 89873 | 89875 | ** If the SeekGE.P1 cursor is pointing to a valid row, then that row |
| 89874 | 89876 | ** might be the target row, or it might be near and slightly before the |
| 89875 | 89877 | ** target row. This opcode attempts to position the cursor on the target |
| 89876 | | -** row by, perhaps stepping by invoking sqlite3BtreeStep() on the cursor |
| 89878 | +** row by, perhaps by invoking sqlite3BtreeStep() on the cursor |
| 89877 | 89879 | ** between 0 and This.P1 times. |
| 89878 | 89880 | ** |
| 89879 | 89881 | ** There are three possible outcomes from this opcode:<ol> |
| 89880 | 89882 | ** |
| 89881 | 89883 | ** <li> If after This.P1 steps, the cursor is still point to a place that |
| 89882 | 89884 | ** is earlier in the btree than the target row, |
| 89883 | 89885 | ** then fall through into the subsquence OP_SeekGE opcode. |
| 89884 | 89886 | ** |
| 89885 | 89887 | ** <li> If the cursor is successfully moved to the target row by 0 or more |
| 89886 | | -** sqlite3BtreeNext() calls, then jump to the first instruction after the |
| 89887 | | -** OP_IdxGT opcode - or in other words, skip the next two opcodes. |
| 89888 | +** sqlite3BtreeNext() calls, then jump to This.P2, which will land just |
| 89889 | +** past the OP_IdxGT opcode that follows the OP_SeekGE. |
| 89888 | 89890 | ** |
| 89889 | 89891 | ** <li> If the cursor ends up past the target row (indicating the the target |
| 89890 | 89892 | ** row does not exist in the btree) then jump to SeekOP.P2. |
| 89891 | 89893 | ** </ol> |
| 89892 | 89894 | */ |
| 89893 | 89895 | case OP_SeekScan: { |
| 89894 | 89896 | VdbeCursor *pC; |
| 89895 | 89897 | int res; |
| 89896 | | - int n; |
| 89898 | + int nStep; |
| 89897 | 89899 | UnpackedRecord r; |
| 89898 | 89900 | |
| 89899 | 89901 | assert( pOp[1].opcode==OP_SeekGE ); |
| 89900 | | - assert( pOp[2].opcode==OP_IdxGT ); |
| 89901 | | - assert( pOp[1].p1==pOp[2].p1 ); |
| 89902 | | - assert( pOp[1].p2==pOp[2].p2 ); |
| 89903 | | - assert( pOp[1].p3==pOp[2].p3 ); |
| 89904 | | - assert( pOp[1].p4.i==pOp[2].p4.i ); |
| 89902 | + |
| 89903 | + /* pOp->p2 points to the first instruction past the OP_IdxGT that |
| 89904 | + ** follows the OP_SeekGE. */ |
| 89905 | + assert( pOp->p2>=(int)(pOp-aOp)+2 ); |
| 89906 | + assert( aOp[pOp->p2-1].opcode==OP_IdxGT ); |
| 89907 | + assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); |
| 89908 | + assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); |
| 89909 | + assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); |
| 89910 | + |
| 89905 | 89911 | assert( pOp->p1>0 ); |
| 89906 | 89912 | pC = p->apCsr[pOp[1].p1]; |
| 89907 | 89913 | assert( pC!=0 ); |
| 89908 | 89914 | assert( pC->eCurType==CURTYPE_BTREE ); |
| 89909 | 89915 | assert( !pC->isTable ); |
| | @@ -89913,12 +89919,12 @@ |
| 89913 | 89919 | printf("... cursor not valid - fall through\n"); |
| 89914 | 89920 | } |
| 89915 | 89921 | #endif |
| 89916 | 89922 | break; |
| 89917 | 89923 | } |
| 89918 | | - n = pOp->p1; |
| 89919 | | - assert( n>=1 ); |
| 89924 | + nStep = pOp->p1; |
| 89925 | + assert( nStep>=1 ); |
| 89920 | 89926 | r.pKeyInfo = pC->pKeyInfo; |
| 89921 | 89927 | r.nField = (u16)pOp[1].p4.i; |
| 89922 | 89928 | r.default_rc = 0; |
| 89923 | 89929 | r.aMem = &aMem[pOp[1].p3]; |
| 89924 | 89930 | #ifdef SQLITE_DEBUG |
| | @@ -89936,37 +89942,37 @@ |
| 89936 | 89942 | if( rc ) goto abort_due_to_error; |
| 89937 | 89943 | if( res>0 ){ |
| 89938 | 89944 | seekscan_search_fail: |
| 89939 | 89945 | #ifdef SQLITE_DEBUG |
| 89940 | 89946 | if( db->flags&SQLITE_VdbeTrace ){ |
| 89941 | | - printf("... %d steps and then skip\n", pOp->p1 - n); |
| 89947 | + printf("... %d steps and then skip\n", pOp->p1 - nStep); |
| 89942 | 89948 | } |
| 89943 | 89949 | #endif |
| 89944 | 89950 | VdbeBranchTaken(1,3); |
| 89945 | 89951 | pOp++; |
| 89946 | 89952 | goto jump_to_p2; |
| 89947 | 89953 | } |
| 89948 | 89954 | if( res==0 ){ |
| 89949 | 89955 | #ifdef SQLITE_DEBUG |
| 89950 | 89956 | if( db->flags&SQLITE_VdbeTrace ){ |
| 89951 | | - printf("... %d steps and then success\n", pOp->p1 - n); |
| 89957 | + printf("... %d steps and then success\n", pOp->p1 - nStep); |
| 89952 | 89958 | } |
| 89953 | 89959 | #endif |
| 89954 | 89960 | VdbeBranchTaken(2,3); |
| 89955 | | - pOp += 2; |
| 89961 | + goto jump_to_p2; |
| 89956 | 89962 | break; |
| 89957 | 89963 | } |
| 89958 | | - if( n<=0 ){ |
| 89964 | + if( nStep<=0 ){ |
| 89959 | 89965 | #ifdef SQLITE_DEBUG |
| 89960 | 89966 | if( db->flags&SQLITE_VdbeTrace ){ |
| 89961 | 89967 | printf("... fall through after %d steps\n", pOp->p1); |
| 89962 | 89968 | } |
| 89963 | 89969 | #endif |
| 89964 | 89970 | VdbeBranchTaken(0,3); |
| 89965 | 89971 | break; |
| 89966 | 89972 | } |
| 89967 | | - n--; |
| 89973 | + nStep--; |
| 89968 | 89974 | rc = sqlite3BtreeNext(pC->uc.pCursor, 0); |
| 89969 | 89975 | if( rc ){ |
| 89970 | 89976 | if( rc==SQLITE_DONE ){ |
| 89971 | 89977 | rc = SQLITE_OK; |
| 89972 | 89978 | goto seekscan_search_fail; |
| | @@ -100066,11 +100072,13 @@ |
| 100066 | 100072 | ** SELECT * FROM t1 WHERE (select a from t1); |
| 100067 | 100073 | */ |
| 100068 | 100074 | SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ |
| 100069 | 100075 | int op; |
| 100070 | 100076 | while( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){ |
| 100071 | | - assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); |
| 100077 | + assert( pExpr->op==TK_COLLATE |
| 100078 | + || pExpr->op==TK_IF_NULL_ROW |
| 100079 | + || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) ); |
| 100072 | 100080 | pExpr = pExpr->pLeft; |
| 100073 | 100081 | assert( pExpr!=0 ); |
| 100074 | 100082 | } |
| 100075 | 100083 | op = pExpr->op; |
| 100076 | 100084 | if( op==TK_SELECT ){ |
| | @@ -112360,11 +112368,11 @@ |
| 112360 | 112368 | Table *p; |
| 112361 | 112369 | int i; |
| 112362 | 112370 | char *zColl; /* Dequoted name of collation sequence */ |
| 112363 | 112371 | sqlite3 *db; |
| 112364 | 112372 | |
| 112365 | | - if( (p = pParse->pNewTable)==0 ) return; |
| 112373 | + if( (p = pParse->pNewTable)==0 || IN_RENAME_OBJECT ) return; |
| 112366 | 112374 | i = p->nCol-1; |
| 112367 | 112375 | db = pParse->db; |
| 112368 | 112376 | zColl = sqlite3NameFromToken(db, pToken); |
| 112369 | 112377 | if( !zColl ) return; |
| 112370 | 112378 | |
| | @@ -115361,11 +115369,20 @@ |
| 115361 | 115369 | } |
| 115362 | 115370 | v = sqlite3GetVdbe(pParse); |
| 115363 | 115371 | if( !v ) return; |
| 115364 | 115372 | if( type!=TK_DEFERRED ){ |
| 115365 | 115373 | for(i=0; i<db->nDb; i++){ |
| 115366 | | - sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); |
| 115374 | + int eTxnType; |
| 115375 | + Btree *pBt = db->aDb[i].pBt; |
| 115376 | + if( pBt && sqlite3BtreeIsReadonly(pBt) ){ |
| 115377 | + eTxnType = 0; /* Read txn */ |
| 115378 | + }else if( type==TK_EXCLUSIVE ){ |
| 115379 | + eTxnType = 2; /* Exclusive txn */ |
| 115380 | + }else{ |
| 115381 | + eTxnType = 1; /* Write txn */ |
| 115382 | + } |
| 115383 | + sqlite3VdbeAddOp2(v, OP_Transaction, i, eTxnType); |
| 115367 | 115384 | sqlite3VdbeUsesBtree(v, i); |
| 115368 | 115385 | } |
| 115369 | 115386 | } |
| 115370 | 115387 | sqlite3VdbeAddOp0(v, OP_AutoCommit); |
| 115371 | 115388 | } |
| | @@ -117348,14 +117365,10 @@ |
| 117348 | 117365 | ** opcode if it is present */ |
| 117349 | 117366 | sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); |
| 117350 | 117367 | } |
| 117351 | 117368 | if( regOut ){ |
| 117352 | 117369 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); |
| 117353 | | - if( pIdx->pTable->pSelect ){ |
| 117354 | | - const char *zAff = sqlite3IndexAffinityStr(pParse->db, pIdx); |
| 117355 | | - sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); |
| 117356 | | - } |
| 117357 | 117370 | } |
| 117358 | 117371 | sqlite3ReleaseTempRange(pParse, regBase, nCol); |
| 117359 | 117372 | return regBase; |
| 117360 | 117373 | } |
| 117361 | 117374 | |
| | @@ -138158,11 +138171,11 @@ |
| 138158 | 138171 | pGrp = sqlite3ExprListAppend(pParse, pGrp, sqlite3ExprDup(db, pNew, 0)); |
| 138159 | 138172 | } |
| 138160 | 138173 | #endif |
| 138161 | 138174 | pList = sqlite3ExprListAppend(pParse, pList, pNew); |
| 138162 | 138175 | } |
| 138163 | | - eDest = SRT_Upfrom; |
| 138176 | + eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom; |
| 138164 | 138177 | }else if( pTab->pSelect ){ |
| 138165 | 138178 | for(i=0; i<pTab->nCol; i++){ |
| 138166 | 138179 | pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); |
| 138167 | 138180 | } |
| 138168 | 138181 | eDest = SRT_Table; |
| | @@ -139111,16 +139124,30 @@ |
| 139111 | 139124 | ephemTab = pParse->nTab++; |
| 139112 | 139125 | addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg); |
| 139113 | 139126 | regArg = pParse->nMem + 1; |
| 139114 | 139127 | pParse->nMem += nArg; |
| 139115 | 139128 | if( pSrc->nSrc>1 ){ |
| 139129 | + Index *pPk = 0; |
| 139116 | 139130 | Expr *pRow; |
| 139117 | 139131 | ExprList *pList; |
| 139118 | | - if( pRowid ){ |
| 139119 | | - pRow = sqlite3ExprDup(db, pRowid, 0); |
| 139132 | + if( HasRowid(pTab) ){ |
| 139133 | + if( pRowid ){ |
| 139134 | + pRow = sqlite3ExprDup(db, pRowid, 0); |
| 139135 | + }else{ |
| 139136 | + pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0); |
| 139137 | + } |
| 139120 | 139138 | }else{ |
| 139121 | | - pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0); |
| 139139 | + i16 iPk; /* PRIMARY KEY column */ |
| 139140 | + pPk = sqlite3PrimaryKeyIndex(pTab); |
| 139141 | + assert( pPk!=0 ); |
| 139142 | + assert( pPk->nKeyCol==1 ); |
| 139143 | + iPk = pPk->aiColumn[0]; |
| 139144 | + if( aXRef[iPk]>=0 ){ |
| 139145 | + pRow = sqlite3ExprDup(db, pChanges->a[aXRef[iPk]].pExpr, 0); |
| 139146 | + }else{ |
| 139147 | + pRow = exprRowColumn(pParse, iPk); |
| 139148 | + } |
| 139122 | 139149 | } |
| 139123 | 139150 | pList = sqlite3ExprListAppend(pParse, 0, pRow); |
| 139124 | 139151 | |
| 139125 | 139152 | for(i=0; i<pTab->nCol; i++){ |
| 139126 | 139153 | if( aXRef[i]>=0 ){ |
| | @@ -139130,11 +139157,11 @@ |
| 139130 | 139157 | }else{ |
| 139131 | 139158 | pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); |
| 139132 | 139159 | } |
| 139133 | 139160 | } |
| 139134 | 139161 | |
| 139135 | | - updateFromSelect(pParse, ephemTab, 0, pList, pSrc, pWhere, 0, 0); |
| 139162 | + updateFromSelect(pParse, ephemTab, pPk, pList, pSrc, pWhere, 0, 0); |
| 139136 | 139163 | sqlite3ExprListDelete(db, pList); |
| 139137 | 139164 | eOnePass = ONEPASS_OFF; |
| 139138 | 139165 | }else{ |
| 139139 | 139166 | regRec = ++pParse->nMem; |
| 139140 | 139167 | regRowid = ++pParse->nMem; |
| | @@ -142457,11 +142484,16 @@ |
| 142457 | 142484 | pIn->eEndLoopOp = OP_Noop; |
| 142458 | 142485 | } |
| 142459 | 142486 | pIn++; |
| 142460 | 142487 | } |
| 142461 | 142488 | } |
| 142462 | | - if( iEq>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 ){ |
| 142489 | + testcase( iEq>0 |
| 142490 | + && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 |
| 142491 | + && (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ); |
| 142492 | + if( iEq>0 |
| 142493 | + && (pLoop->wsFlags & (WHERE_IN_SEEKSCAN|WHERE_VIRTUALTABLE))==0 |
| 142494 | + ){ |
| 142463 | 142495 | sqlite3VdbeAddOp3(v, OP_SeekHit, pLevel->iIdxCur, 0, iEq); |
| 142464 | 142496 | } |
| 142465 | 142497 | }else{ |
| 142466 | 142498 | pLevel->u.in.nIn = 0; |
| 142467 | 142499 | } |
| | @@ -143507,10 +143539,11 @@ |
| 143507 | 143539 | char *zEndAff = 0; /* Affinity for end of range constraint */ |
| 143508 | 143540 | u8 bSeekPastNull = 0; /* True to seek past initial nulls */ |
| 143509 | 143541 | u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */ |
| 143510 | 143542 | int omitTable; /* True if we use the index only */ |
| 143511 | 143543 | int regBignull = 0; /* big-null flag register */ |
| 143544 | + int addrSeekScan = 0; /* Opcode of the OP_SeekScan, if any */ |
| 143512 | 143545 | |
| 143513 | 143546 | pIdx = pLoop->u.btree.pIndex; |
| 143514 | 143547 | iIdxCur = pLevel->iIdxCur; |
| 143515 | 143548 | assert( nEq>=pLoop->nSkip ); |
| 143516 | 143549 | |
| | @@ -143652,22 +143685,22 @@ |
| 143652 | 143685 | VdbeComment((v, "NULL-scan pass ctr")); |
| 143653 | 143686 | } |
| 143654 | 143687 | |
| 143655 | 143688 | op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; |
| 143656 | 143689 | assert( op!=0 ); |
| 143657 | | - if( (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){ |
| 143658 | | - assert( op==OP_SeekGE ); |
| 143690 | + if( (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 && op==OP_SeekGE ){ |
| 143659 | 143691 | assert( regBignull==0 ); |
| 143660 | 143692 | /* TUNING: The OP_SeekScan opcode seeks to reduce the number |
| 143661 | 143693 | ** of expensive seek operations by replacing a single seek with |
| 143662 | 143694 | ** 1 or more step operations. The question is, how many steps |
| 143663 | 143695 | ** should we try before giving up and going with a seek. The cost |
| 143664 | 143696 | ** of a seek is proportional to the logarithm of the of the number |
| 143665 | 143697 | ** of entries in the tree, so basing the number of steps to try |
| 143666 | 143698 | ** on the estimated number of rows in the btree seems like a good |
| 143667 | 143699 | ** guess. */ |
| 143668 | | - sqlite3VdbeAddOp1(v, OP_SeekScan, (pIdx->aiRowLogEst[0]+9)/10); |
| 143700 | + addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan, |
| 143701 | + (pIdx->aiRowLogEst[0]+9)/10); |
| 143669 | 143702 | VdbeCoverage(v); |
| 143670 | 143703 | } |
| 143671 | 143704 | sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); |
| 143672 | 143705 | VdbeCoverage(v); |
| 143673 | 143706 | VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind ); |
| | @@ -143748,10 +143781,11 @@ |
| 143748 | 143781 | sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); |
| 143749 | 143782 | testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT ); |
| 143750 | 143783 | testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE ); |
| 143751 | 143784 | testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT ); |
| 143752 | 143785 | testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); |
| 143786 | + if( addrSeekScan ) sqlite3VdbeJumpHere(v, addrSeekScan); |
| 143753 | 143787 | } |
| 143754 | 143788 | if( regBignull ){ |
| 143755 | 143789 | /* During a NULL-scan, check to see if we have reached the end of |
| 143756 | 143790 | ** the NULLs */ |
| 143757 | 143791 | assert( bSeekPastNull==!bStopAtNull ); |
| | @@ -148484,11 +148518,11 @@ |
| 148484 | 148518 | } |
| 148485 | 148519 | }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ |
| 148486 | 148520 | /* "x IN (value, value, ...)" */ |
| 148487 | 148521 | nIn = sqlite3LogEst(pExpr->x.pList->nExpr); |
| 148488 | 148522 | } |
| 148489 | | - if( pProbe->hasStat1 ){ |
| 148523 | + if( pProbe->hasStat1 && rLogSize>=10 ){ |
| 148490 | 148524 | LogEst M, logK, safetyMargin; |
| 148491 | 148525 | /* Let: |
| 148492 | 148526 | ** N = the total number of rows in the table |
| 148493 | 148527 | ** K = the number of entries on the RHS of the IN operator |
| 148494 | 148528 | ** M = the number of rows in the table that match terms to the |
| | @@ -148503,11 +148537,12 @@ |
| 148503 | 148537 | ** |
| 148504 | 148538 | ** Our estimates for M, K, and N might be inaccurate, so we build in |
| 148505 | 148539 | ** a safety margin of 2 (LogEst: 10) that favors using the IN operator |
| 148506 | 148540 | ** with the index, as using an index has better worst-case behavior. |
| 148507 | 148541 | ** If we do not have real sqlite_stat1 data, always prefer to use |
| 148508 | | - ** the index. |
| 148542 | + ** the index. Do not bother with this optimization on very small |
| 148543 | + ** tables (less than 2 rows) as it is pointless in that case. |
| 148509 | 148544 | */ |
| 148510 | 148545 | M = pProbe->aiRowLogEst[saved_nEq]; |
| 148511 | 148546 | logK = estLog(nIn); |
| 148512 | 148547 | safetyMargin = 10; /* TUNING: extra weight for indexed IN */ |
| 148513 | 148548 | if( M + logK + safetyMargin < nIn + rLogSize ){ |
| | @@ -209277,10 +209312,11 @@ |
| 209277 | 209312 | int eDetail; /* FTS5_DETAIL_XXX value */ |
| 209278 | 209313 | char *zContentExprlist; |
| 209279 | 209314 | Fts5Tokenizer *pTok; |
| 209280 | 209315 | fts5_tokenizer *pTokApi; |
| 209281 | 209316 | int bLock; /* True when table is preparing statement */ |
| 209317 | + int ePattern; /* FTS_PATTERN_XXX constant */ |
| 209282 | 209318 | |
| 209283 | 209319 | /* Values loaded from the %_config table */ |
| 209284 | 209320 | int iCookie; /* Incremented when %_config is modified */ |
| 209285 | 209321 | int pgsz; /* Approximate page size used in %_data */ |
| 209286 | 209322 | int nAutomerge; /* 'automerge' setting */ |
| | @@ -209297,21 +209333,23 @@ |
| 209297 | 209333 | int bPrefixIndex; /* True to use prefix-indexes */ |
| 209298 | 209334 | #endif |
| 209299 | 209335 | }; |
| 209300 | 209336 | |
| 209301 | 209337 | /* Current expected value of %_config table 'version' field */ |
| 209302 | | -#define FTS5_CURRENT_VERSION 4 |
| 209338 | +#define FTS5_CURRENT_VERSION 4 |
| 209303 | 209339 | |
| 209304 | 209340 | #define FTS5_CONTENT_NORMAL 0 |
| 209305 | 209341 | #define FTS5_CONTENT_NONE 1 |
| 209306 | 209342 | #define FTS5_CONTENT_EXTERNAL 2 |
| 209307 | 209343 | |
| 209308 | | -#define FTS5_DETAIL_FULL 0 |
| 209309 | | -#define FTS5_DETAIL_NONE 1 |
| 209310 | | -#define FTS5_DETAIL_COLUMNS 2 |
| 209344 | +#define FTS5_DETAIL_FULL 0 |
| 209345 | +#define FTS5_DETAIL_NONE 1 |
| 209346 | +#define FTS5_DETAIL_COLUMNS 2 |
| 209311 | 209347 | |
| 209312 | | - |
| 209348 | +#define FTS5_PATTERN_NONE 0 |
| 209349 | +#define FTS5_PATTERN_LIKE 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */ |
| 209350 | +#define FTS5_PATTERN_GLOB 66 /* matches SQLITE_INDEX_CONSTRAINT_GLOB */ |
| 209313 | 209351 | |
| 209314 | 209352 | static int sqlite3Fts5ConfigParse( |
| 209315 | 209353 | Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** |
| 209316 | 209354 | ); |
| 209317 | 209355 | static void sqlite3Fts5ConfigFree(Fts5Config*); |
| | @@ -209647,12 +209685,11 @@ |
| 209647 | 209685 | |
| 209648 | 209686 | static int sqlite3Fts5GetTokenizer( |
| 209649 | 209687 | Fts5Global*, |
| 209650 | 209688 | const char **azArg, |
| 209651 | 209689 | int nArg, |
| 209652 | | - Fts5Tokenizer**, |
| 209653 | | - fts5_tokenizer**, |
| 209690 | + Fts5Config*, |
| 209654 | 209691 | char **pzErr |
| 209655 | 209692 | ); |
| 209656 | 209693 | |
| 209657 | 209694 | static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); |
| 209658 | 209695 | |
| | @@ -209777,14 +209814,22 @@ |
| 209777 | 209814 | }; |
| 209778 | 209815 | |
| 209779 | 209816 | /* Parse a MATCH expression. */ |
| 209780 | 209817 | static int sqlite3Fts5ExprNew( |
| 209781 | 209818 | Fts5Config *pConfig, |
| 209819 | + int bPhraseToAnd, |
| 209782 | 209820 | int iCol, /* Column on LHS of MATCH operator */ |
| 209783 | 209821 | const char *zExpr, |
| 209784 | 209822 | Fts5Expr **ppNew, |
| 209785 | 209823 | char **pzErr |
| 209824 | +); |
| 209825 | +static int sqlite3Fts5ExprPattern( |
| 209826 | + Fts5Config *pConfig, |
| 209827 | + int bGlob, |
| 209828 | + int iCol, |
| 209829 | + const char *zText, |
| 209830 | + Fts5Expr **pp |
| 209786 | 209831 | ); |
| 209787 | 209832 | |
| 209788 | 209833 | /* |
| 209789 | 209834 | ** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc); |
| 209790 | 209835 | ** rc==SQLITE_OK && 0==sqlite3Fts5ExprEof(pExpr); |
| | @@ -209890,10 +209935,14 @@ |
| 209890 | 209935 | /************************************************************************** |
| 209891 | 209936 | ** Interface to code in fts5_tokenizer.c. |
| 209892 | 209937 | */ |
| 209893 | 209938 | |
| 209894 | 209939 | static int sqlite3Fts5TokenizerInit(fts5_api*); |
| 209940 | +static int sqlite3Fts5TokenizerPattern( |
| 209941 | + int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), |
| 209942 | + Fts5Tokenizer *pTok |
| 209943 | +); |
| 209895 | 209944 | /* |
| 209896 | 209945 | ** End of interface to code in fts5_tokenizer.c. |
| 209897 | 209946 | **************************************************************************/ |
| 209898 | 209947 | |
| 209899 | 209948 | /************************************************************************** |
| | @@ -212869,11 +212918,11 @@ |
| 212869 | 212918 | if( p==0 ){ |
| 212870 | 212919 | *pzErr = sqlite3_mprintf("parse error in tokenize directive"); |
| 212871 | 212920 | rc = SQLITE_ERROR; |
| 212872 | 212921 | }else{ |
| 212873 | 212922 | rc = sqlite3Fts5GetTokenizer(pGlobal, |
| 212874 | | - (const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi, |
| 212923 | + (const char**)azArg, (int)nArg, pConfig, |
| 212875 | 212924 | pzErr |
| 212876 | 212925 | ); |
| 212877 | 212926 | } |
| 212878 | 212927 | } |
| 212879 | 212928 | } |
| | @@ -212941,13 +212990,11 @@ |
| 212941 | 212990 | ** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error |
| 212942 | 212991 | ** code if an error occurs. |
| 212943 | 212992 | */ |
| 212944 | 212993 | static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){ |
| 212945 | 212994 | assert( pConfig->pTok==0 && pConfig->pTokApi==0 ); |
| 212946 | | - return sqlite3Fts5GetTokenizer( |
| 212947 | | - pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0 |
| 212948 | | - ); |
| 212995 | + return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0); |
| 212949 | 212996 | } |
| 212950 | 212997 | |
| 212951 | 212998 | /* |
| 212952 | 212999 | ** Gobble up the first bareword or quoted word from the input buffer zIn. |
| 212953 | 213000 | ** Return a pointer to the character immediately following the last in |
| | @@ -213635,10 +213682,11 @@ |
| 213635 | 213682 | char *zErr; |
| 213636 | 213683 | int rc; |
| 213637 | 213684 | int nPhrase; /* Size of apPhrase array */ |
| 213638 | 213685 | Fts5ExprPhrase **apPhrase; /* Array of all phrases */ |
| 213639 | 213686 | Fts5ExprNode *pExpr; /* Result of a successful parse */ |
| 213687 | + int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ |
| 213640 | 213688 | }; |
| 213641 | 213689 | |
| 213642 | 213690 | static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ |
| 213643 | 213691 | va_list ap; |
| 213644 | 213692 | va_start(ap, zFmt); |
| | @@ -213723,10 +213771,11 @@ |
| 213723 | 213771 | static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc64((sqlite3_int64)t);} |
| 213724 | 213772 | static void fts5ParseFree(void *p){ sqlite3_free(p); } |
| 213725 | 213773 | |
| 213726 | 213774 | static int sqlite3Fts5ExprNew( |
| 213727 | 213775 | Fts5Config *pConfig, /* FTS5 Configuration */ |
| 213776 | + int bPhraseToAnd, |
| 213728 | 213777 | int iCol, |
| 213729 | 213778 | const char *zExpr, /* Expression text */ |
| 213730 | 213779 | Fts5Expr **ppNew, |
| 213731 | 213780 | char **pzErr |
| 213732 | 213781 | ){ |
| | @@ -213738,10 +213787,11 @@ |
| 213738 | 213787 | Fts5Expr *pNew; |
| 213739 | 213788 | |
| 213740 | 213789 | *ppNew = 0; |
| 213741 | 213790 | *pzErr = 0; |
| 213742 | 213791 | memset(&sParse, 0, sizeof(sParse)); |
| 213792 | + sParse.bPhraseToAnd = bPhraseToAnd; |
| 213743 | 213793 | pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc); |
| 213744 | 213794 | if( pEngine==0 ){ return SQLITE_NOMEM; } |
| 213745 | 213795 | sParse.pConfig = pConfig; |
| 213746 | 213796 | |
| 213747 | 213797 | do { |
| | @@ -213780,10 +213830,11 @@ |
| 213780 | 213830 | } |
| 213781 | 213831 | pNew->pIndex = 0; |
| 213782 | 213832 | pNew->pConfig = pConfig; |
| 213783 | 213833 | pNew->apExprPhrase = sParse.apPhrase; |
| 213784 | 213834 | pNew->nPhrase = sParse.nPhrase; |
| 213835 | + pNew->bDesc = 0; |
| 213785 | 213836 | sParse.apPhrase = 0; |
| 213786 | 213837 | } |
| 213787 | 213838 | }else{ |
| 213788 | 213839 | sqlite3Fts5ParseNodeFree(sParse.pExpr); |
| 213789 | 213840 | } |
| | @@ -213790,10 +213841,85 @@ |
| 213790 | 213841 | |
| 213791 | 213842 | sqlite3_free(sParse.apPhrase); |
| 213792 | 213843 | *pzErr = sParse.zErr; |
| 213793 | 213844 | return sParse.rc; |
| 213794 | 213845 | } |
| 213846 | + |
| 213847 | +/* |
| 213848 | +** This function is only called when using the special 'trigram' tokenizer. |
| 213849 | +** Argument zText contains the text of a LIKE or GLOB pattern matched |
| 213850 | +** against column iCol. This function creates and compiles an FTS5 MATCH |
| 213851 | +** expression that will match a superset of the rows matched by the LIKE or |
| 213852 | +** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error |
| 213853 | +** code. |
| 213854 | +*/ |
| 213855 | +static int sqlite3Fts5ExprPattern( |
| 213856 | + Fts5Config *pConfig, int bGlob, int iCol, const char *zText, Fts5Expr **pp |
| 213857 | +){ |
| 213858 | + i64 nText = strlen(zText); |
| 213859 | + char *zExpr = (char*)sqlite3_malloc64(nText*4 + 1); |
| 213860 | + int rc = SQLITE_OK; |
| 213861 | + |
| 213862 | + if( zExpr==0 ){ |
| 213863 | + rc = SQLITE_NOMEM; |
| 213864 | + }else{ |
| 213865 | + char aSpec[3]; |
| 213866 | + int iOut = 0; |
| 213867 | + int i = 0; |
| 213868 | + int iFirst = 0; |
| 213869 | + |
| 213870 | + if( bGlob==0 ){ |
| 213871 | + aSpec[0] = '_'; |
| 213872 | + aSpec[1] = '%'; |
| 213873 | + aSpec[2] = 0; |
| 213874 | + }else{ |
| 213875 | + aSpec[0] = '*'; |
| 213876 | + aSpec[1] = '?'; |
| 213877 | + aSpec[2] = '['; |
| 213878 | + } |
| 213879 | + |
| 213880 | + while( i<=nText ){ |
| 213881 | + if( i==nText |
| 213882 | + || zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2] |
| 213883 | + ){ |
| 213884 | + if( i-iFirst>=3 ){ |
| 213885 | + int jj; |
| 213886 | + zExpr[iOut++] = '"'; |
| 213887 | + for(jj=iFirst; jj<i; jj++){ |
| 213888 | + zExpr[iOut++] = zText[jj]; |
| 213889 | + if( zText[jj]=='"' ) zExpr[iOut++] = '"'; |
| 213890 | + } |
| 213891 | + zExpr[iOut++] = '"'; |
| 213892 | + zExpr[iOut++] = ' '; |
| 213893 | + } |
| 213894 | + if( zText[i]==aSpec[2] ){ |
| 213895 | + i += 2; |
| 213896 | + if( zText[i-1]=='^' ) i++; |
| 213897 | + while( i<nText && zText[i]!=']' ) i++; |
| 213898 | + } |
| 213899 | + iFirst = i+1; |
| 213900 | + } |
| 213901 | + i++; |
| 213902 | + } |
| 213903 | + if( iOut>0 ){ |
| 213904 | + int bAnd = 0; |
| 213905 | + if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ |
| 213906 | + bAnd = 1; |
| 213907 | + if( pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 213908 | + iCol = pConfig->nCol; |
| 213909 | + } |
| 213910 | + } |
| 213911 | + zExpr[iOut] = '\0'; |
| 213912 | + rc = sqlite3Fts5ExprNew(pConfig, bAnd, iCol, zExpr, pp,pConfig->pzErrmsg); |
| 213913 | + }else{ |
| 213914 | + *pp = 0; |
| 213915 | + } |
| 213916 | + sqlite3_free(zExpr); |
| 213917 | + } |
| 213918 | + |
| 213919 | + return rc; |
| 213920 | +} |
| 213795 | 213921 | |
| 213796 | 213922 | /* |
| 213797 | 213923 | ** Free the expression node object passed as the only argument. |
| 213798 | 213924 | */ |
| 213799 | 213925 | static void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){ |
| | @@ -215167,10 +215293,24 @@ |
| 215167 | 215293 | |
| 215168 | 215294 | static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){ |
| 215169 | 215295 | assert( pParse->pExpr==0 ); |
| 215170 | 215296 | pParse->pExpr = p; |
| 215171 | 215297 | } |
| 215298 | + |
| 215299 | +static int parseGrowPhraseArray(Fts5Parse *pParse){ |
| 215300 | + if( (pParse->nPhrase % 8)==0 ){ |
| 215301 | + sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); |
| 215302 | + Fts5ExprPhrase **apNew; |
| 215303 | + apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); |
| 215304 | + if( apNew==0 ){ |
| 215305 | + pParse->rc = SQLITE_NOMEM; |
| 215306 | + return SQLITE_NOMEM; |
| 215307 | + } |
| 215308 | + pParse->apPhrase = apNew; |
| 215309 | + } |
| 215310 | + return SQLITE_OK; |
| 215311 | +} |
| 215172 | 215312 | |
| 215173 | 215313 | /* |
| 215174 | 215314 | ** This function is called by the parser to process a string token. The |
| 215175 | 215315 | ** string may or may not be quoted. In any case it is tokenized and a |
| 215176 | 215316 | ** phrase object consisting of all tokens returned. |
| | @@ -215203,20 +215343,13 @@ |
| 215203 | 215343 | fts5ExprPhraseFree(sCtx.pPhrase); |
| 215204 | 215344 | sCtx.pPhrase = 0; |
| 215205 | 215345 | }else{ |
| 215206 | 215346 | |
| 215207 | 215347 | if( pAppend==0 ){ |
| 215208 | | - if( (pParse->nPhrase % 8)==0 ){ |
| 215209 | | - sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); |
| 215210 | | - Fts5ExprPhrase **apNew; |
| 215211 | | - apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); |
| 215212 | | - if( apNew==0 ){ |
| 215213 | | - pParse->rc = SQLITE_NOMEM; |
| 215214 | | - fts5ExprPhraseFree(sCtx.pPhrase); |
| 215215 | | - return 0; |
| 215216 | | - } |
| 215217 | | - pParse->apPhrase = apNew; |
| 215348 | + if( parseGrowPhraseArray(pParse) ){ |
| 215349 | + fts5ExprPhraseFree(sCtx.pPhrase); |
| 215350 | + return 0; |
| 215218 | 215351 | } |
| 215219 | 215352 | pParse->nPhrase++; |
| 215220 | 215353 | } |
| 215221 | 215354 | |
| 215222 | 215355 | if( sCtx.pPhrase==0 ){ |
| | @@ -215618,10 +215751,71 @@ |
| 215618 | 215751 | sqlite3_free(pSub); |
| 215619 | 215752 | }else{ |
| 215620 | 215753 | p->apChild[p->nChild++] = pSub; |
| 215621 | 215754 | } |
| 215622 | 215755 | } |
| 215756 | + |
| 215757 | +/* |
| 215758 | +** This function is used when parsing LIKE or GLOB patterns against |
| 215759 | +** trigram indexes that specify either detail=column or detail=none. |
| 215760 | +** It converts a phrase: |
| 215761 | +** |
| 215762 | +** abc + def + ghi |
| 215763 | +** |
| 215764 | +** into an AND tree: |
| 215765 | +** |
| 215766 | +** abc AND def AND ghi |
| 215767 | +*/ |
| 215768 | +static Fts5ExprNode *fts5ParsePhraseToAnd( |
| 215769 | + Fts5Parse *pParse, |
| 215770 | + Fts5ExprNearset *pNear |
| 215771 | +){ |
| 215772 | + int nTerm = pNear->apPhrase[0]->nTerm; |
| 215773 | + int ii; |
| 215774 | + int nByte; |
| 215775 | + Fts5ExprNode *pRet; |
| 215776 | + |
| 215777 | + assert( pNear->nPhrase==1 ); |
| 215778 | + assert( pParse->bPhraseToAnd ); |
| 215779 | + |
| 215780 | + nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); |
| 215781 | + pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 215782 | + if( pRet ){ |
| 215783 | + pRet->eType = FTS5_AND; |
| 215784 | + pRet->nChild = nTerm; |
| 215785 | + fts5ExprAssignXNext(pRet); |
| 215786 | + pParse->nPhrase--; |
| 215787 | + for(ii=0; ii<nTerm; ii++){ |
| 215788 | + Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 215789 | + &pParse->rc, sizeof(Fts5ExprPhrase) |
| 215790 | + ); |
| 215791 | + if( pPhrase ){ |
| 215792 | + if( parseGrowPhraseArray(pParse) ){ |
| 215793 | + fts5ExprPhraseFree(pPhrase); |
| 215794 | + }else{ |
| 215795 | + pParse->apPhrase[pParse->nPhrase++] = pPhrase; |
| 215796 | + pPhrase->nTerm = 1; |
| 215797 | + pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup( |
| 215798 | + &pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1 |
| 215799 | + ); |
| 215800 | + pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING, |
| 215801 | + 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) |
| 215802 | + ); |
| 215803 | + } |
| 215804 | + } |
| 215805 | + } |
| 215806 | + |
| 215807 | + if( pParse->rc ){ |
| 215808 | + sqlite3Fts5ParseNodeFree(pRet); |
| 215809 | + pRet = 0; |
| 215810 | + }else{ |
| 215811 | + sqlite3Fts5ParseNearsetFree(pNear); |
| 215812 | + } |
| 215813 | + } |
| 215814 | + |
| 215815 | + return pRet; |
| 215816 | +} |
| 215623 | 215817 | |
| 215624 | 215818 | /* |
| 215625 | 215819 | ** Allocate and return a new expression object. If anything goes wrong (i.e. |
| 215626 | 215820 | ** OOM error), leave an error code in pParse and return NULL. |
| 215627 | 215821 | */ |
| | @@ -215643,55 +215837,62 @@ |
| 215643 | 215837 | ); |
| 215644 | 215838 | if( eType==FTS5_STRING && pNear==0 ) return 0; |
| 215645 | 215839 | if( eType!=FTS5_STRING && pLeft==0 ) return pRight; |
| 215646 | 215840 | if( eType!=FTS5_STRING && pRight==0 ) return pLeft; |
| 215647 | 215841 | |
| 215648 | | - if( eType==FTS5_NOT ){ |
| 215649 | | - nChild = 2; |
| 215650 | | - }else if( eType==FTS5_AND || eType==FTS5_OR ){ |
| 215651 | | - nChild = 2; |
| 215652 | | - if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 215653 | | - if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 215654 | | - } |
| 215655 | | - |
| 215656 | | - nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); |
| 215657 | | - pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 215658 | | - |
| 215659 | | - if( pRet ){ |
| 215660 | | - pRet->eType = eType; |
| 215661 | | - pRet->pNear = pNear; |
| 215662 | | - fts5ExprAssignXNext(pRet); |
| 215663 | | - if( eType==FTS5_STRING ){ |
| 215664 | | - int iPhrase; |
| 215665 | | - for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){ |
| 215666 | | - pNear->apPhrase[iPhrase]->pNode = pRet; |
| 215667 | | - if( pNear->apPhrase[iPhrase]->nTerm==0 ){ |
| 215668 | | - pRet->xNext = 0; |
| 215669 | | - pRet->eType = FTS5_EOF; |
| 215670 | | - } |
| 215671 | | - } |
| 215672 | | - |
| 215673 | | - if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ |
| 215674 | | - Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; |
| 215675 | | - if( pNear->nPhrase!=1 |
| 215676 | | - || pPhrase->nTerm>1 |
| 215677 | | - || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) |
| 215678 | | - ){ |
| 215679 | | - assert( pParse->rc==SQLITE_OK ); |
| 215680 | | - pParse->rc = SQLITE_ERROR; |
| 215681 | | - assert( pParse->zErr==0 ); |
| 215682 | | - pParse->zErr = sqlite3_mprintf( |
| 215683 | | - "fts5: %s queries are not supported (detail!=full)", |
| 215684 | | - pNear->nPhrase==1 ? "phrase": "NEAR" |
| 215685 | | - ); |
| 215686 | | - sqlite3_free(pRet); |
| 215687 | | - pRet = 0; |
| 215688 | | - } |
| 215689 | | - } |
| 215690 | | - }else{ |
| 215691 | | - fts5ExprAddChildren(pRet, pLeft); |
| 215692 | | - fts5ExprAddChildren(pRet, pRight); |
| 215842 | + if( eType==FTS5_STRING |
| 215843 | + && pParse->bPhraseToAnd |
| 215844 | + && pNear->apPhrase[0]->nTerm>1 |
| 215845 | + ){ |
| 215846 | + pRet = fts5ParsePhraseToAnd(pParse, pNear); |
| 215847 | + }else{ |
| 215848 | + if( eType==FTS5_NOT ){ |
| 215849 | + nChild = 2; |
| 215850 | + }else if( eType==FTS5_AND || eType==FTS5_OR ){ |
| 215851 | + nChild = 2; |
| 215852 | + if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 215853 | + if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 215854 | + } |
| 215855 | + |
| 215856 | + nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); |
| 215857 | + pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 215858 | + |
| 215859 | + if( pRet ){ |
| 215860 | + pRet->eType = eType; |
| 215861 | + pRet->pNear = pNear; |
| 215862 | + fts5ExprAssignXNext(pRet); |
| 215863 | + if( eType==FTS5_STRING ){ |
| 215864 | + int iPhrase; |
| 215865 | + for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){ |
| 215866 | + pNear->apPhrase[iPhrase]->pNode = pRet; |
| 215867 | + if( pNear->apPhrase[iPhrase]->nTerm==0 ){ |
| 215868 | + pRet->xNext = 0; |
| 215869 | + pRet->eType = FTS5_EOF; |
| 215870 | + } |
| 215871 | + } |
| 215872 | + |
| 215873 | + if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ |
| 215874 | + Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; |
| 215875 | + if( pNear->nPhrase!=1 |
| 215876 | + || pPhrase->nTerm>1 |
| 215877 | + || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) |
| 215878 | + ){ |
| 215879 | + assert( pParse->rc==SQLITE_OK ); |
| 215880 | + pParse->rc = SQLITE_ERROR; |
| 215881 | + assert( pParse->zErr==0 ); |
| 215882 | + pParse->zErr = sqlite3_mprintf( |
| 215883 | + "fts5: %s queries are not supported (detail!=full)", |
| 215884 | + pNear->nPhrase==1 ? "phrase": "NEAR" |
| 215885 | + ); |
| 215886 | + sqlite3_free(pRet); |
| 215887 | + pRet = 0; |
| 215888 | + } |
| 215889 | + } |
| 215890 | + }else{ |
| 215891 | + fts5ExprAddChildren(pRet, pLeft); |
| 215892 | + fts5ExprAddChildren(pRet, pRight); |
| 215893 | + } |
| 215693 | 215894 | } |
| 215694 | 215895 | } |
| 215695 | 215896 | } |
| 215696 | 215897 | |
| 215697 | 215898 | if( pRet==0 ){ |
| | @@ -216041,11 +216242,11 @@ |
| 216041 | 216242 | zExpr = (const char*)sqlite3_value_text(apVal[0]); |
| 216042 | 216243 | if( zExpr==0 ) zExpr = ""; |
| 216043 | 216244 | |
| 216044 | 216245 | rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr); |
| 216045 | 216246 | if( rc==SQLITE_OK ){ |
| 216046 | | - rc = sqlite3Fts5ExprNew(pConfig, pConfig->nCol, zExpr, &pExpr, &zErr); |
| 216247 | + rc = sqlite3Fts5ExprNew(pConfig, 0, pConfig->nCol, zExpr, &pExpr, &zErr); |
| 216047 | 216248 | } |
| 216048 | 216249 | if( rc==SQLITE_OK ){ |
| 216049 | 216250 | char *zText; |
| 216050 | 216251 | if( pExpr->pRoot->xNext==0 ){ |
| 216051 | 216252 | zText = sqlite3_mprintf(""); |
| | @@ -216746,12 +216947,13 @@ |
| 216746 | 216947 | pPtr = (u8*)p; |
| 216747 | 216948 | |
| 216748 | 216949 | /* If this is a new rowid, append the 4-byte size field for the previous |
| 216749 | 216950 | ** entry, and the new rowid for this entry. */ |
| 216750 | 216951 | if( iRowid!=p->iRowid ){ |
| 216952 | + u64 iDiff = (u64)iRowid - (u64)p->iRowid; |
| 216751 | 216953 | fts5HashAddPoslistSize(pHash, p, 0); |
| 216752 | | - p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); |
| 216954 | + p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iDiff); |
| 216753 | 216955 | p->iRowid = iRowid; |
| 216754 | 216956 | bNew = 1; |
| 216755 | 216957 | p->iSzPoslist = p->nData; |
| 216756 | 216958 | if( pHash->eDetail!=FTS5_DETAIL_NONE ){ |
| 216757 | 216959 | p->nData += 1; |
| | @@ -218722,11 +218924,11 @@ |
| 218722 | 218924 | n = pIter->iEndofDoclist; |
| 218723 | 218925 | } |
| 218724 | 218926 | |
| 218725 | 218927 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 218726 | 218928 | while( 1 ){ |
| 218727 | | - i64 iDelta = 0; |
| 218929 | + u64 iDelta = 0; |
| 218728 | 218930 | |
| 218729 | 218931 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 218730 | 218932 | /* todo */ |
| 218731 | 218933 | if( i<n && a[i]==0 ){ |
| 218732 | 218934 | i++; |
| | @@ -218737,11 +218939,11 @@ |
| 218737 | 218939 | int bDummy; |
| 218738 | 218940 | i += fts5GetPoslistSize(&a[i], &nPos, &bDummy); |
| 218739 | 218941 | i += nPos; |
| 218740 | 218942 | } |
| 218741 | 218943 | if( i>=n ) break; |
| 218742 | | - i += fts5GetVarint(&a[i], (u64*)&iDelta); |
| 218944 | + i += fts5GetVarint(&a[i], &iDelta); |
| 218743 | 218945 | pIter->iRowid += iDelta; |
| 218744 | 218946 | |
| 218745 | 218947 | /* If necessary, grow the pIter->aRowidOffset[] array. */ |
| 218746 | 218948 | if( iRowidOffset>=pIter->nRowidOffset ){ |
| 218747 | 218949 | int nNew = pIter->nRowidOffset + 8; |
| | @@ -218836,20 +219038,20 @@ |
| 218836 | 219038 | UNUSED_PARAM(pbUnused); |
| 218837 | 219039 | |
| 218838 | 219040 | if( pIter->iRowidOffset>0 ){ |
| 218839 | 219041 | u8 *a = pIter->pLeaf->p; |
| 218840 | 219042 | int iOff; |
| 218841 | | - i64 iDelta; |
| 219043 | + u64 iDelta; |
| 218842 | 219044 | |
| 218843 | 219045 | pIter->iRowidOffset--; |
| 218844 | 219046 | pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset]; |
| 218845 | 219047 | fts5SegIterLoadNPos(p, pIter); |
| 218846 | 219048 | iOff = pIter->iLeafOffset; |
| 218847 | 219049 | if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){ |
| 218848 | 219050 | iOff += pIter->nPos; |
| 218849 | 219051 | } |
| 218850 | | - fts5GetVarint(&a[iOff], (u64*)&iDelta); |
| 219052 | + fts5GetVarint(&a[iOff], &iDelta); |
| 218851 | 219053 | pIter->iRowid -= iDelta; |
| 218852 | 219054 | }else{ |
| 218853 | 219055 | fts5SegIterReverseNewPage(p, pIter); |
| 218854 | 219056 | } |
| 218855 | 219057 | } |
| | @@ -224064,10 +224266,27 @@ |
| 224064 | 224266 | { |
| 224065 | 224267 | pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE; |
| 224066 | 224268 | } |
| 224067 | 224269 | #endif |
| 224068 | 224270 | } |
| 224271 | + |
| 224272 | +static int fts5UsePatternMatch( |
| 224273 | + Fts5Config *pConfig, |
| 224274 | + struct sqlite3_index_constraint *p |
| 224275 | +){ |
| 224276 | + assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB ); |
| 224277 | + assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE ); |
| 224278 | + if( pConfig->ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){ |
| 224279 | + return 1; |
| 224280 | + } |
| 224281 | + if( pConfig->ePattern==FTS5_PATTERN_LIKE |
| 224282 | + && (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB) |
| 224283 | + ){ |
| 224284 | + return 1; |
| 224285 | + } |
| 224286 | + return 0; |
| 224287 | +} |
| 224069 | 224288 | |
| 224070 | 224289 | /* |
| 224071 | 224290 | ** Implementation of the xBestIndex method for FTS5 tables. Within the |
| 224072 | 224291 | ** WHERE constraint, it searches for the following: |
| 224073 | 224292 | ** |
| | @@ -224094,11 +224313,13 @@ |
| 224094 | 224313 | ** idxStr is used to encode data from the WHERE clause. For each argument |
| 224095 | 224314 | ** passed to the xFilter method, the following is appended to idxStr: |
| 224096 | 224315 | ** |
| 224097 | 224316 | ** Match against table column: "m" |
| 224098 | 224317 | ** Match against rank column: "r" |
| 224099 | | -** Match against other column: "<column-number>" |
| 224318 | +** Match against other column: "M<column-number>" |
| 224319 | +** LIKE against other column: "L<column-number>" |
| 224320 | +** GLOB against other column: "G<column-number>" |
| 224100 | 224321 | ** Equality constraint against the rowid: "=" |
| 224101 | 224322 | ** A < or <= against the rowid: "<" |
| 224102 | 224323 | ** A > or >= against the rowid: ">" |
| 224103 | 224324 | ** |
| 224104 | 224325 | ** This function ensures that there is at most one "r" or "=". And that if |
| | @@ -224155,11 +224376,11 @@ |
| 224155 | 224376 | "recursively defined fts5 content table" |
| 224156 | 224377 | ); |
| 224157 | 224378 | return SQLITE_ERROR; |
| 224158 | 224379 | } |
| 224159 | 224380 | |
| 224160 | | - idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1); |
| 224381 | + idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1); |
| 224161 | 224382 | if( idxStr==0 ) return SQLITE_NOMEM; |
| 224162 | 224383 | pInfo->idxStr = idxStr; |
| 224163 | 224384 | pInfo->needToFreeIdxStr = 1; |
| 224164 | 224385 | |
| 224165 | 224386 | for(i=0; i<pInfo->nConstraint; i++){ |
| | @@ -224179,29 +224400,33 @@ |
| 224179 | 224400 | }else{ |
| 224180 | 224401 | if( iCol==nCol+1 ){ |
| 224181 | 224402 | if( bSeenRank ) continue; |
| 224182 | 224403 | idxStr[iIdxStr++] = 'r'; |
| 224183 | 224404 | bSeenRank = 1; |
| 224184 | | - }else{ |
| 224405 | + }else if( iCol>=0 ){ |
| 224185 | 224406 | bSeenMatch = 1; |
| 224186 | | - idxStr[iIdxStr++] = 'm'; |
| 224187 | | - if( iCol<nCol ){ |
| 224188 | | - sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol); |
| 224189 | | - idxStr += strlen(&idxStr[iIdxStr]); |
| 224190 | | - assert( idxStr[iIdxStr]=='\0' ); |
| 224191 | | - } |
| 224407 | + idxStr[iIdxStr++] = 'M'; |
| 224408 | + sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol); |
| 224409 | + idxStr += strlen(&idxStr[iIdxStr]); |
| 224410 | + assert( idxStr[iIdxStr]=='\0' ); |
| 224192 | 224411 | } |
| 224193 | 224412 | pInfo->aConstraintUsage[i].argvIndex = ++iCons; |
| 224194 | 224413 | pInfo->aConstraintUsage[i].omit = 1; |
| 224195 | 224414 | } |
| 224196 | | - } |
| 224197 | | - else if( p->usable && bSeenEq==0 |
| 224198 | | - && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 |
| 224199 | | - ){ |
| 224200 | | - idxStr[iIdxStr++] = '='; |
| 224201 | | - bSeenEq = 1; |
| 224202 | | - pInfo->aConstraintUsage[i].argvIndex = ++iCons; |
| 224415 | + }else if( p->usable ){ |
| 224416 | + if( iCol>=0 && iCol<nCol && fts5UsePatternMatch(pConfig, p) ){ |
| 224417 | + assert( p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB ); |
| 224418 | + idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G'; |
| 224419 | + sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol); |
| 224420 | + idxStr += strlen(&idxStr[iIdxStr]); |
| 224421 | + pInfo->aConstraintUsage[i].argvIndex = ++iCons; |
| 224422 | + assert( idxStr[iIdxStr]=='\0' ); |
| 224423 | + }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){ |
| 224424 | + idxStr[iIdxStr++] = '='; |
| 224425 | + bSeenEq = 1; |
| 224426 | + pInfo->aConstraintUsage[i].argvIndex = ++iCons; |
| 224427 | + } |
| 224203 | 224428 | } |
| 224204 | 224429 | } |
| 224205 | 224430 | |
| 224206 | 224431 | if( bSeenEq==0 ){ |
| 224207 | 224432 | for(i=0; i<pInfo->nConstraint; i++){ |
| | @@ -224830,41 +225055,55 @@ |
| 224830 | 225055 | for(i=0; i<nVal; i++){ |
| 224831 | 225056 | switch( idxStr[iIdxStr++] ){ |
| 224832 | 225057 | case 'r': |
| 224833 | 225058 | pRank = apVal[i]; |
| 224834 | 225059 | break; |
| 224835 | | - case 'm': { |
| 225060 | + case 'M': { |
| 224836 | 225061 | const char *zText = (const char*)sqlite3_value_text(apVal[i]); |
| 224837 | 225062 | if( zText==0 ) zText = ""; |
| 224838 | | - |
| 224839 | | - if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){ |
| 224840 | | - iCol = 0; |
| 224841 | | - do{ |
| 224842 | | - iCol = iCol*10 + (idxStr[iIdxStr]-'0'); |
| 224843 | | - iIdxStr++; |
| 224844 | | - }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); |
| 224845 | | - }else{ |
| 224846 | | - iCol = pConfig->nCol; |
| 224847 | | - } |
| 225063 | + iCol = 0; |
| 225064 | + do{ |
| 225065 | + iCol = iCol*10 + (idxStr[iIdxStr]-'0'); |
| 225066 | + iIdxStr++; |
| 225067 | + }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); |
| 224848 | 225068 | |
| 224849 | 225069 | if( zText[0]=='*' ){ |
| 224850 | 225070 | /* The user has issued a query of the form "MATCH '*...'". This |
| 224851 | 225071 | ** indicates that the MATCH expression is not a full text query, |
| 224852 | 225072 | ** but a request for an internal parameter. */ |
| 224853 | 225073 | rc = fts5SpecialMatch(pTab, pCsr, &zText[1]); |
| 224854 | 225074 | goto filter_out; |
| 224855 | 225075 | }else{ |
| 224856 | 225076 | char **pzErr = &pTab->p.base.zErrMsg; |
| 224857 | | - rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr); |
| 225077 | + rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr); |
| 224858 | 225078 | if( rc==SQLITE_OK ){ |
| 224859 | 225079 | rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); |
| 224860 | 225080 | pExpr = 0; |
| 224861 | 225081 | } |
| 224862 | 225082 | if( rc!=SQLITE_OK ) goto filter_out; |
| 224863 | 225083 | } |
| 224864 | 225084 | |
| 224865 | 225085 | break; |
| 225086 | + } |
| 225087 | + case 'L': |
| 225088 | + case 'G': { |
| 225089 | + int bGlob = (idxStr[iIdxStr-1]=='G'); |
| 225090 | + const char *zText = (const char*)sqlite3_value_text(apVal[i]); |
| 225091 | + iCol = 0; |
| 225092 | + do{ |
| 225093 | + iCol = iCol*10 + (idxStr[iIdxStr]-'0'); |
| 225094 | + iIdxStr++; |
| 225095 | + }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); |
| 225096 | + if( zText ){ |
| 225097 | + rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr); |
| 225098 | + } |
| 225099 | + if( rc==SQLITE_OK ){ |
| 225100 | + rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); |
| 225101 | + pExpr = 0; |
| 225102 | + } |
| 225103 | + if( rc!=SQLITE_OK ) goto filter_out; |
| 225104 | + break; |
| 224866 | 225105 | } |
| 224867 | 225106 | case '=': |
| 224868 | 225107 | pRowidEq = apVal[i]; |
| 224869 | 225108 | break; |
| 224870 | 225109 | case '<': |
| | @@ -226273,12 +226512,11 @@ |
| 226273 | 226512 | |
| 226274 | 226513 | static int sqlite3Fts5GetTokenizer( |
| 226275 | 226514 | Fts5Global *pGlobal, |
| 226276 | 226515 | const char **azArg, |
| 226277 | 226516 | int nArg, |
| 226278 | | - Fts5Tokenizer **ppTok, |
| 226279 | | - fts5_tokenizer **ppTokApi, |
| 226517 | + Fts5Config *pConfig, |
| 226280 | 226518 | char **pzErr |
| 226281 | 226519 | ){ |
| 226282 | 226520 | Fts5TokenizerModule *pMod; |
| 226283 | 226521 | int rc = SQLITE_OK; |
| 226284 | 226522 | |
| | @@ -226286,20 +226524,26 @@ |
| 226286 | 226524 | if( pMod==0 ){ |
| 226287 | 226525 | assert( nArg>0 ); |
| 226288 | 226526 | rc = SQLITE_ERROR; |
| 226289 | 226527 | *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]); |
| 226290 | 226528 | }else{ |
| 226291 | | - rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok); |
| 226292 | | - *ppTokApi = &pMod->x; |
| 226293 | | - if( rc!=SQLITE_OK && pzErr ){ |
| 226294 | | - *pzErr = sqlite3_mprintf("error in tokenizer constructor"); |
| 226529 | + rc = pMod->x.xCreate( |
| 226530 | + pMod->pUserData, &azArg[1], (nArg?nArg-1:0), &pConfig->pTok |
| 226531 | + ); |
| 226532 | + pConfig->pTokApi = &pMod->x; |
| 226533 | + if( rc!=SQLITE_OK ){ |
| 226534 | + if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor"); |
| 226535 | + }else{ |
| 226536 | + pConfig->ePattern = sqlite3Fts5TokenizerPattern( |
| 226537 | + pMod->x.xCreate, pConfig->pTok |
| 226538 | + ); |
| 226295 | 226539 | } |
| 226296 | 226540 | } |
| 226297 | 226541 | |
| 226298 | 226542 | if( rc!=SQLITE_OK ){ |
| 226299 | | - *ppTokApi = 0; |
| 226300 | | - *ppTok = 0; |
| 226543 | + pConfig->pTokApi = 0; |
| 226544 | + pConfig->pTok = 0; |
| 226301 | 226545 | } |
| 226302 | 226546 | |
| 226303 | 226547 | return rc; |
| 226304 | 226548 | } |
| 226305 | 226549 | |
| | @@ -226344,11 +226588,11 @@ |
| 226344 | 226588 | int nArg, /* Number of args */ |
| 226345 | 226589 | sqlite3_value **apUnused /* Function arguments */ |
| 226346 | 226590 | ){ |
| 226347 | 226591 | assert( nArg==0 ); |
| 226348 | 226592 | UNUSED_PARAM2(nArg, apUnused); |
| 226349 | | - sqlite3_result_text(pCtx, "fts5: 2020-09-30 18:06:51 4a43430fd23f88352c33b29c4c105b72f6dc821f94bf362040c41a1648c402e5", -1, SQLITE_TRANSIENT); |
| 226593 | + sqlite3_result_text(pCtx, "fts5: 2020-10-12 18:09:16 7e17c2f4b7dc9b563d0b4da949bb134dc7c4fc9c86ce03891432a884ca6409d5", -1, SQLITE_TRANSIENT); |
| 226350 | 226594 | } |
| 226351 | 226595 | |
| 226352 | 226596 | /* |
| 226353 | 226597 | ** Return true if zName is the extension on one of the shadow tables used |
| 226354 | 226598 | ** by this module. |
| | @@ -228897,10 +229141,135 @@ |
| 228897 | 229141 | sCtx.aBuf = p->aBuf; |
| 228898 | 229142 | return p->tokenizer.xTokenize( |
| 228899 | 229143 | p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb |
| 228900 | 229144 | ); |
| 228901 | 229145 | } |
| 229146 | + |
| 229147 | +/************************************************************************** |
| 229148 | +** Start of trigram implementation. |
| 229149 | +*/ |
| 229150 | +typedef struct TrigramTokenizer TrigramTokenizer; |
| 229151 | +struct TrigramTokenizer { |
| 229152 | + int bFold; /* True to fold to lower-case */ |
| 229153 | +}; |
| 229154 | + |
| 229155 | +/* |
| 229156 | +** Free a trigram tokenizer. |
| 229157 | +*/ |
| 229158 | +static void fts5TriDelete(Fts5Tokenizer *p){ |
| 229159 | + sqlite3_free(p); |
| 229160 | +} |
| 229161 | + |
| 229162 | +/* |
| 229163 | +** Allocate a trigram tokenizer. |
| 229164 | +*/ |
| 229165 | +static int fts5TriCreate( |
| 229166 | + void *pCtx, |
| 229167 | + const char **azArg, |
| 229168 | + int nArg, |
| 229169 | + Fts5Tokenizer **ppOut |
| 229170 | +){ |
| 229171 | + int rc = SQLITE_OK; |
| 229172 | + TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); |
| 229173 | + if( pNew==0 ){ |
| 229174 | + rc = SQLITE_NOMEM; |
| 229175 | + }else{ |
| 229176 | + int i; |
| 229177 | + pNew->bFold = 1; |
| 229178 | + for(i=0; rc==SQLITE_OK && i<nArg; i+=2){ |
| 229179 | + const char *zArg = azArg[i+1]; |
| 229180 | + if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){ |
| 229181 | + if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){ |
| 229182 | + rc = SQLITE_ERROR; |
| 229183 | + }else{ |
| 229184 | + pNew->bFold = (zArg[0]=='0'); |
| 229185 | + } |
| 229186 | + }else{ |
| 229187 | + rc = SQLITE_ERROR; |
| 229188 | + } |
| 229189 | + } |
| 229190 | + if( rc!=SQLITE_OK ){ |
| 229191 | + fts5TriDelete((Fts5Tokenizer*)pNew); |
| 229192 | + pNew = 0; |
| 229193 | + } |
| 229194 | + } |
| 229195 | + *ppOut = (Fts5Tokenizer*)pNew; |
| 229196 | + return rc; |
| 229197 | +} |
| 229198 | + |
| 229199 | +/* |
| 229200 | +** Trigram tokenizer tokenize routine. |
| 229201 | +*/ |
| 229202 | +static int fts5TriTokenize( |
| 229203 | + Fts5Tokenizer *pTok, |
| 229204 | + void *pCtx, |
| 229205 | + int flags, |
| 229206 | + const char *pText, int nText, |
| 229207 | + int (*xToken)(void*, int, const char*, int, int, int) |
| 229208 | +){ |
| 229209 | + TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229210 | + int rc = SQLITE_OK; |
| 229211 | + char aBuf[32]; |
| 229212 | + const unsigned char *zIn = (const unsigned char*)pText; |
| 229213 | + const unsigned char *zEof = &zIn[nText]; |
| 229214 | + u32 iCode; |
| 229215 | + |
| 229216 | + while( 1 ){ |
| 229217 | + char *zOut = aBuf; |
| 229218 | + int iStart = zIn - (const unsigned char*)pText; |
| 229219 | + const unsigned char *zNext; |
| 229220 | + |
| 229221 | + READ_UTF8(zIn, zEof, iCode); |
| 229222 | + if( iCode==0 ) break; |
| 229223 | + zNext = zIn; |
| 229224 | + if( zIn<zEof ){ |
| 229225 | + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); |
| 229226 | + WRITE_UTF8(zOut, iCode); |
| 229227 | + READ_UTF8(zIn, zEof, iCode); |
| 229228 | + if( iCode==0 ) break; |
| 229229 | + }else{ |
| 229230 | + break; |
| 229231 | + } |
| 229232 | + if( zIn<zEof ){ |
| 229233 | + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); |
| 229234 | + WRITE_UTF8(zOut, iCode); |
| 229235 | + READ_UTF8(zIn, zEof, iCode); |
| 229236 | + if( iCode==0 ) break; |
| 229237 | + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); |
| 229238 | + WRITE_UTF8(zOut, iCode); |
| 229239 | + }else{ |
| 229240 | + break; |
| 229241 | + } |
| 229242 | + rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf); |
| 229243 | + if( rc!=SQLITE_OK ) break; |
| 229244 | + zIn = zNext; |
| 229245 | + } |
| 229246 | + |
| 229247 | + return rc; |
| 229248 | +} |
| 229249 | + |
| 229250 | +/* |
| 229251 | +** Argument xCreate is a pointer to a constructor function for a tokenizer. |
| 229252 | +** pTok is a tokenizer previously created using the same method. This function |
| 229253 | +** returns one of FTS5_PATTERN_NONE, FTS5_PATTERN_LIKE or FTS5_PATTERN_GLOB |
| 229254 | +** indicating the style of pattern matching that the tokenizer can support. |
| 229255 | +** In practice, this is: |
| 229256 | +** |
| 229257 | +** "trigram" tokenizer, case_sensitive=1 - FTS5_PATTERN_GLOB |
| 229258 | +** "trigram" tokenizer, case_sensitive=0 (the default) - FTS5_PATTERN_LIKE |
| 229259 | +** all other tokenizers - FTS5_PATTERN_NONE |
| 229260 | +*/ |
| 229261 | +static int sqlite3Fts5TokenizerPattern( |
| 229262 | + int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), |
| 229263 | + Fts5Tokenizer *pTok |
| 229264 | +){ |
| 229265 | + if( xCreate==fts5TriCreate ){ |
| 229266 | + TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229267 | + return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; |
| 229268 | + } |
| 229269 | + return FTS5_PATTERN_NONE; |
| 229270 | +} |
| 228902 | 229271 | |
| 228903 | 229272 | /* |
| 228904 | 229273 | ** Register all built-in tokenizers with FTS5. |
| 228905 | 229274 | */ |
| 228906 | 229275 | static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ |
| | @@ -228909,10 +229278,11 @@ |
| 228909 | 229278 | fts5_tokenizer x; |
| 228910 | 229279 | } aBuiltin[] = { |
| 228911 | 229280 | { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, |
| 228912 | 229281 | { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, |
| 228913 | 229282 | { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }}, |
| 229283 | + { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}}, |
| 228914 | 229284 | }; |
| 228915 | 229285 | |
| 228916 | 229286 | int rc = SQLITE_OK; /* Return code */ |
| 228917 | 229287 | int i; /* To iterate through builtin functions */ |
| 228918 | 229288 | |
| | @@ -231140,12 +231510,12 @@ |
| 231140 | 231510 | } |
| 231141 | 231511 | #endif /* SQLITE_CORE */ |
| 231142 | 231512 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 231143 | 231513 | |
| 231144 | 231514 | /************** End of stmt.c ************************************************/ |
| 231145 | | -#if __LINE__!=231145 |
| 231515 | +#if __LINE__!=231515 |
| 231146 | 231516 | #undef SQLITE_SOURCE_ID |
| 231147 | | -#define SQLITE_SOURCE_ID "2020-09-30 18:06:51 4a43430fd23f88352c33b29c4c105b72f6dc821f94bf362040c41a1648c4alt2" |
| 231517 | +#define SQLITE_SOURCE_ID "2020-10-12 18:09:16 7e17c2f4b7dc9b563d0b4da949bb134dc7c4fc9c86ce03891432a884ca64alt2" |
| 231148 | 231518 | #endif |
| 231149 | 231519 | /* Return the source-id for this library */ |
| 231150 | 231520 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 231151 | 231521 | /************************** End of sqlite3.c ******************************/ |
| 231152 | 231522 | |