diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 541d6c539d..536c47fc78 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -2767,11 +2767,13 @@ static int fts3NearMerge( ** each doclist that are not within nNear tokens of a corresponding entry ** in the other doclist. */ -int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){ +int sqlite3Fts3ExprNearTrim(Fts3Expr *pELeft, Fts3Expr *pERight, int nNear){ int rc; /* Return code */ + Fts3Phrase *pLeft = pELeft->pPhrase; + Fts3Phrase *pRight = pERight->pPhrase; - assert( pLeft->eType==FTSQUERY_PHRASE ); - assert( pRight->eType==FTSQUERY_PHRASE ); + assert( pELeft->eType==FTSQUERY_PHRASE && pLeft ); + assert( pERight->eType==FTSQUERY_PHRASE && pRight ); assert( pLeft->isLoaded && pRight->isLoaded ); if( pLeft->aDoclist==0 || pRight->aDoclist==0 ){ @@ -2785,8 +2787,8 @@ int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){ int nOut; /* Size of buffer aOut in bytes */ rc = fts3NearMerge(MERGE_POS_NEAR, nNear, - pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist, - pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist, + pLeft->nToken, pLeft->aDoclist, pLeft->nDoclist, + pRight->nToken, pRight->aDoclist, pRight->nDoclist, &aOut, &nOut ); if( rc!=SQLITE_OK ) return rc; @@ -2795,8 +2797,8 @@ int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){ pRight->nDoclist = nOut; rc = fts3NearMerge(MERGE_POS_NEAR, nNear, - pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist, - pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist, + pRight->nToken, pRight->aDoclist, pRight->nDoclist, + pLeft->nToken, pLeft->aDoclist, pLeft->nDoclist, &aOut, &nOut ); sqlite3_free(pLeft->aDoclist); @@ -3459,12 +3461,19 @@ static int fts3RollbackMethod(sqlite3_vtab *pVtab){ */ int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *pCsr, Fts3Expr *pExpr){ int rc; - assert( pExpr->eType==FTSQUERY_PHRASE && pExpr->pPhrase ); + Fts3Phrase *pPhrase = pExpr->pPhrase; + assert( pExpr->eType==FTSQUERY_PHRASE && pPhrase ); assert( pCsr->eEvalmode==FTS3_EVAL_NEXT ); - rc = fts3EvalExpr(pCsr, pExpr, &pExpr->aDoclist, &pExpr->nDoclist, 1); + rc = fts3EvalExpr(pCsr, pExpr, &pPhrase->aDoclist, &pPhrase->nDoclist, 1); return rc; } +/* +** TODO: This is something to do with matchinfo(). Similar to +** sqlite3ExprLoadDoclists() but slightly different. +** +** UPDATE: Only used when there are deferred tokens. +*/ int sqlite3Fts3ExprLoadFtDoclist( Fts3Cursor *pCsr, Fts3Expr *pExpr, @@ -3510,44 +3519,46 @@ char *sqlite3Fts3FindPositions( sqlite3_int64 iDocid, /* Docid associated with requested pos-list */ int iCol /* Column of requested pos-list */ ){ - assert( pExpr->isLoaded ); - if( pExpr->aDoclist ){ - char *pEnd = &pExpr->aDoclist[pExpr->nDoclist]; + Fts3Phrase *pPhrase = pExpr->pPhrase; + assert( pPhrase->isLoaded ); + + if( pPhrase->aDoclist ){ + char *pEnd = &pPhrase->aDoclist[pPhrase->nDoclist]; char *pCsr; - if( pExpr->pCurrent==0 ){ + if( pPhrase->pCurrent==0 ){ if( pCursor->desc==0 ){ - pExpr->pCurrent = pExpr->aDoclist; - pExpr->iCurrent = 0; - fts3GetDeltaVarint(&pExpr->pCurrent, &pExpr->iCurrent); + pPhrase->pCurrent = pPhrase->aDoclist; + pPhrase->iCurrent = 0; + fts3GetDeltaVarint(&pPhrase->pCurrent, &pPhrase->iCurrent); }else{ - pCsr = pExpr->aDoclist; + pCsr = pPhrase->aDoclist; while( pCsriCurrent); + fts3GetDeltaVarint(&pCsr, &pPhrase->iCurrent); fts3PoslistCopy(0, &pCsr); } - fts3ReversePoslist(pExpr->aDoclist, &pCsr); - pExpr->pCurrent = pCsr; + fts3ReversePoslist(pPhrase->aDoclist, &pCsr); + pPhrase->pCurrent = pCsr; } } - pCsr = pExpr->pCurrent; + pCsr = pPhrase->pCurrent; assert( pCsr ); while( (pCursor->desc==0 && pCsrdesc && pCsr>pExpr->aDoclist) + || (pCursor->desc && pCsr>pPhrase->aDoclist) ){ - if( pCursor->desc==0 && pExpr->iCurrentdesc==0 && pPhrase->iCurrentiCurrent); + fts3GetDeltaVarint(&pCsr, &pPhrase->iCurrent); } - pExpr->pCurrent = pCsr; - }else if( pCursor->desc && pExpr->iCurrent>iDocid ){ - fts3GetReverseDeltaVarint(&pCsr, pExpr->aDoclist, &pExpr->iCurrent); - fts3ReversePoslist(pExpr->aDoclist, &pCsr); - pExpr->pCurrent = pCsr; + pPhrase->pCurrent = pCsr; + }else if( pCursor->desc && pPhrase->iCurrent>iDocid ){ + fts3GetReverseDeltaVarint(&pCsr, pPhrase->aDoclist, &pPhrase->iCurrent); + fts3ReversePoslist(pPhrase->aDoclist, &pCsr); + pPhrase->pCurrent = pCsr; }else{ - if( pExpr->iCurrent==iDocid ){ + if( pPhrase->iCurrent==iDocid ){ int iThis = 0; if( iCol<0 ){ /* If iCol is negative, return a pointer to the start of the diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 00e1c37d2b..42be10773a 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -268,17 +268,16 @@ struct Fts3Cursor { ** sequence. A single token is the base case and the most common case. ** For a sequence of tokens contained in double-quotes (i.e. "one two three") ** nToken will be the number of tokens in the string. -** -** The nDocMatch and nMatch variables contain data that may be used by the -** matchinfo() function. They are populated when the full-text index is -** queried for hits on the phrase. If one or more tokens in the phrase -** are deferred, the nDocMatch and nMatch variables are populated based -** on the assumption that the */ struct Fts3PhraseToken { char *z; /* Text of the token */ int n; /* Number of bytes in buffer z */ int isPrefix; /* True if token ends with a "*" character */ + + /* Variables above this point are populated when the expression is + ** parsed (by code in fts3_expr.c). Below this point the variables are + ** used when evaluating the expression. */ + int bFulltext; /* True if full-text index was used */ Fts3SegReaderCursor *pSegcsr; /* Segment-reader for this token */ Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ @@ -288,18 +287,24 @@ struct Fts3Phrase { /* Variables populated by fts3_expr.c when parsing a MATCH expression */ int nToken; /* Number of tokens in the phrase */ int iColumn; /* Index of column this phrase must match */ - int isNot; /* Phrase prefixed by unary not (-) operator */ + + int isLoaded; /* True if aDoclist/nDoclist are initialized. */ + char *aDoclist; /* Buffer containing doclist */ + int nDoclist; /* Size of aDoclist in bytes */ + sqlite3_int64 iCurrent; + char *pCurrent; + Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ }; /* ** A tree of these objects forms the RHS of a MATCH operator. ** -** If Fts3Expr.eType is either FTSQUERY_NEAR or FTSQUERY_PHRASE and isLoaded -** is true, then aDoclist points to a malloced buffer, size nDoclist bytes, -** containing the results of the NEAR or phrase query in FTS3 doclist -** format. As usual, the initial "Length" field found in doclists stored -** on disk is omitted from this buffer. +** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist +** points to a malloced buffer, size nDoclist bytes, containing the results +** of this phrase query in FTS3 doclist format. As usual, the initial +** "Length" field found in doclists stored on disk is omitted from this +** buffer. ** ** Variable pCurrent always points to the start of a docid field within ** aDoclist. Since the doclist is usually scanned in docid order, this can @@ -312,13 +317,6 @@ struct Fts3Expr { Fts3Expr *pLeft; /* Left operand */ Fts3Expr *pRight; /* Right operand */ Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */ - - int isLoaded; /* True if aDoclist/nDoclist are initialized. */ - char *aDoclist; /* Buffer containing doclist */ - int nDoclist; /* Size of aDoclist in bytes */ - - sqlite3_int64 iCurrent; - char *pCurrent; }; /* diff --git a/ext/fts3/fts3_expr.c b/ext/fts3/fts3_expr.c index 43f6d84a84..095841d142 100644 --- a/ext/fts3/fts3_expr.c +++ b/ext/fts3/fts3_expr.c @@ -81,12 +81,21 @@ int sqlite3_fts3_enable_parentheses = 0; #include #include +/* +** isNot: +** This variable is used by function getNextNode(). When getNextNode() is +** called, it sets ParseContext.isNot to true if the 'next node' is a +** FTSQUERY_PHRASE with a unary "-" attached to it. i.e. "mysql" in the +** FTS3 query "sqlite -mysql". Otherwise, ParseContext.isNot is set to +** zero. +*/ typedef struct ParseContext ParseContext; struct ParseContext { sqlite3_tokenizer *pTokenizer; /* Tokenizer module */ const char **azCol; /* Array of column names for fts3 table */ int nCol; /* Number of entries in azCol[] */ int iDefaultCol; /* Default column to query */ + int isNot; /* True if getNextNode() sees a unary - */ sqlite3_context *pCtx; /* Write error message here */ int nNest; /* Number of nested brackets */ }; @@ -172,7 +181,7 @@ static int getNextToken( iEnd++; } if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){ - pRet->pPhrase->isNot = 1; + pParse->isNot = 1; } } nConsumed = iEnd; @@ -224,36 +233,55 @@ static int getNextString( char *zTemp = 0; int nTemp = 0; + const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); + int nToken = 0; + + /* The final Fts3Expr data structure, including the Fts3Phrase, + ** Fts3PhraseToken structures token buffers are all stored as a single + ** allocation so that the expression can be freed with a single call to + ** sqlite3_free(). Setting this up requires a two pass approach. + ** + ** The first pass, in the block below, uses a tokenizer cursor to iterate + ** through the tokens in the expression. This pass uses fts3ReallocOrFree() + ** to assemble data in two dynamic buffers: + ** + ** Buffer p: Points to the Fts3Expr structure, followed by the Fts3Phrase + ** structure, followed by the array of Fts3PhraseToken + ** structures. This pass only populates the Fts3PhraseToken array. + ** + ** Buffer zTemp: Contains copies of all tokens. + ** + ** The second pass, in the block that begins "if( rc==SQLITE_DONE )" below, + ** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase + ** structures. + */ rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor); if( rc==SQLITE_OK ){ int ii; pCursor->pTokenizer = pTokenizer; for(ii=0; rc==SQLITE_OK; ii++){ - const char *zToken; - int nToken, iBegin, iEnd, iPos; - rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); + const char *zByte; + int nByte, iBegin, iEnd, iPos; + rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); if( rc==SQLITE_OK ){ - int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); - p = fts3ReallocOrFree(p, nByte+ii*sizeof(Fts3PhraseToken)); - zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken); - if( !p || !zTemp ){ - goto no_mem; - } - if( ii==0 ){ - memset(p, 0, nByte); - p->pPhrase = (Fts3Phrase *)&p[1]; - } - p->pPhrase = (Fts3Phrase *)&p[1]; - memset(&p->pPhrase->aToken[ii], 0, sizeof(Fts3PhraseToken)); - p->pPhrase->nToken = ii+1; - p->pPhrase->aToken[ii].n = nToken; - memcpy(&zTemp[nTemp], zToken, nToken); - nTemp += nToken; - if( iEndpPhrase->aToken[ii].isPrefix = 1; - }else{ - p->pPhrase->aToken[ii].isPrefix = 0; - } + Fts3PhraseToken *pToken; + + p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); + if( !p ) goto no_mem; + + zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); + if( !zTemp ) goto no_mem; + + assert( nToken==ii ); + pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; + memset(pToken, 0, sizeof(Fts3PhraseToken)); + + memcpy(&zTemp[nTemp], zByte, nByte); + nTemp += nByte; + + pToken->n = nByte; + pToken->isPrefix = (iEndpPhrase->nToken-1):0) * sizeof(Fts3PhraseToken); - p = fts3ReallocOrFree(p, nByte + nTemp); - if( !p ){ - goto no_mem; - } - if( zTemp ){ - zNew = &(((char *)p)[nByte]); - memcpy(zNew, zTemp, nTemp); - }else{ - memset(p, 0, nByte+nTemp); - } - p->pPhrase = (Fts3Phrase *)&p[1]; - for(jj=0; jjpPhrase->nToken; jj++){ - p->pPhrase->aToken[jj].z = &zNew[nNew]; - nNew += p->pPhrase->aToken[jj].n; - } - sqlite3_free(zTemp); + char *zBuf = 0; + + p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); + if( !p ) goto no_mem; + memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); p->eType = FTSQUERY_PHRASE; + p->pPhrase = (Fts3Phrase *)&p[1]; p->pPhrase->iColumn = pParse->iDefaultCol; + p->pPhrase->nToken = nToken; + + zBuf = (char *)&p->pPhrase->aToken[nToken]; + memcpy(zBuf, zTemp, nTemp); + sqlite3_free(zTemp); + + for(jj=0; jjpPhrase->nToken; jj++){ + p->pPhrase->aToken[jj].z = zBuf; + zBuf += p->pPhrase->aToken[jj].n; + } rc = SQLITE_OK; } @@ -341,6 +365,8 @@ static int getNextNode( const char *zInput = z; int nInput = n; + pParse->isNot = 0; + /* Skip over any whitespace before checking for a keyword, an open or ** close bracket, or a quoted string. */ @@ -559,7 +585,7 @@ static int fts3ExprParse( int isPhrase; if( !sqlite3_fts3_enable_parentheses - && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot + && p->eType==FTSQUERY_PHRASE && pParse->isNot ){ /* Create an implicit NOT operator. */ Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); @@ -577,7 +603,6 @@ static int fts3ExprParse( p = pPrev; }else{ int eType = p->eType; - assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot ); isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); /* The isRequirePhrase variable is set to true if a phrase or @@ -740,9 +765,10 @@ int sqlite3Fts3ExprParse( */ void sqlite3Fts3ExprFree(Fts3Expr *p){ if( p ){ + assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 ); sqlite3Fts3ExprFree(p->pLeft); sqlite3Fts3ExprFree(p->pRight); - sqlite3_free(p->aDoclist); + if( p->pPhrase ) sqlite3_free(p->pPhrase->aDoclist); sqlite3_free(p); } } @@ -800,7 +826,7 @@ static char *exprToString(Fts3Expr *pExpr, char *zBuf){ Fts3Phrase *pPhrase = pExpr->pPhrase; int i; zBuf = sqlite3_mprintf( - "%zPHRASE %d %d", zBuf, pPhrase->iColumn, pPhrase->isNot); + "%zPHRASE %d 0", zBuf, pPhrase->iColumn); for(i=0; zBuf && inToken; i++){ zBuf = sqlite3_mprintf("%z %.*s%s", zBuf, pPhrase->aToken[i].n, pPhrase->aToken[i].z, diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c index 8bf9ed2d5a..b31a1cf359 100644 --- a/ext/fts3/fts3_snippet.c +++ b/ext/fts3/fts3_snippet.c @@ -228,16 +228,17 @@ static int fts3ExprNearTrim(Fts3Expr *pExpr){ */ static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ int rc = SQLITE_OK; + Fts3Phrase *pPhrase = pExpr->pPhrase; LoadDoclistCtx *p = (LoadDoclistCtx *)ctx; UNUSED_PARAMETER(iPhrase); p->nPhrase++; - p->nToken += pExpr->pPhrase->nToken; + p->nToken += pPhrase->nToken; - if( pExpr->isLoaded==0 ){ + if( pPhrase->isLoaded==0 ){ rc = sqlite3Fts3ExprLoadDoclist(p->pCsr, pExpr); - pExpr->isLoaded = 1; + pPhrase->isLoaded = 1; if( rc==SQLITE_OK ){ rc = fts3ExprNearTrim(pExpr); } @@ -826,16 +827,15 @@ static int fts3ExprGlobalHitsCb( ){ MatchInfo *p = (MatchInfo *)pCtx; Fts3Cursor *pCsr = p->pCursor; + Fts3Phrase *pPhrase = pExpr->pPhrase; char *pIter; char *pEnd; char *pFree = 0; u32 *aOut = &p->aMatchinfo[3*iPhrase*p->nCol]; - assert( pExpr->isLoaded ); - assert( pExpr->eType==FTSQUERY_PHRASE ); + assert( pPhrase->isLoaded ); if( pCsr->pDeferred ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; int ii; for(ii=0; iinToken; ii++){ if( pPhrase->aToken[ii].bFulltext ) break; @@ -855,8 +855,8 @@ static int fts3ExprGlobalHitsCb( return SQLITE_OK; } }else{ - pIter = pExpr->aDoclist; - pEnd = &pExpr->aDoclist[pExpr->nDoclist]; + pIter = pPhrase->aDoclist; + pEnd = &pPhrase->aDoclist[pPhrase->nDoclist]; } /* Fill in the global hit count matrix row for this phrase. */ @@ -885,7 +885,7 @@ static int fts3ExprLocalHitsCb( for(i=0; inCol; i++) p->aMatchinfo[iStart+i*3] = 0; - if( pExpr->aDoclist ){ + if( pExpr->pPhrase->aDoclist ){ char *pCsr; pCsr = sqlite3Fts3FindPositions(p->pCursor, pExpr, p->pCursor->iPrevId, -1); diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 3d4e022298..f664dec8cf 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -2642,15 +2642,17 @@ char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *pDeferred, int *pnByte){ */ static void fts3DeferredDoclistClear(Fts3Expr *pExpr){ if( pExpr ){ + Fts3Phrase *pPhrase = pExpr->pPhrase; fts3DeferredDoclistClear(pExpr->pLeft); fts3DeferredDoclistClear(pExpr->pRight); - if( pExpr->isLoaded ){ - sqlite3_free(pExpr->aDoclist); - pExpr->isLoaded = 0; - pExpr->aDoclist = 0; - pExpr->nDoclist = 0; - pExpr->pCurrent = 0; - pExpr->iCurrent = 0; + if( pPhrase ){ + assert( pExpr->eType==FTSQUERY_PHRASE ); + sqlite3_free(pPhrase->aDoclist); + pPhrase->isLoaded = 0; + pPhrase->aDoclist = 0; + pPhrase->nDoclist = 0; + pPhrase->pCurrent = 0; + pPhrase->iCurrent = 0; } } } diff --git a/manifest b/manifest index 7cd9a1f2c5..950ee87f2e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C If\sa\sprefix\sindex\sof\ssize\sN\sis\snot\spresent,\suse\sa\sprefix\sindex\sof\ssize\sN+1\salong\swith\sthe\sterms\sindex\sfor\squeries\sfor\sprefixes\sof\slength\sN. -D 2011-05-25T19:17:32.713 +C Minor\schanges\smade\swhile\splanning\sa\slarger\schange. +D 2011-05-28T15:57:40.694 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 11dcc00a8d0e5202def00e81732784fb0cc4fe1d F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -61,21 +61,21 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c b44e0abc7aaef8d5489533b3f0556b28097378f9 +F ext/fts3/fts3.c eb59cdd89e9309ab9b2dca196a7c52f9e8927319 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h deeeac21d17da06683a79b40ae119b93cf86f90a +F ext/fts3/fts3Int.h 832f4d421f03a9d364186e779ee51994df500c62 F ext/fts3/fts3_aux.c 2817a1ec9ffbad673cb1a1527ad807811bc7645b -F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93 +F ext/fts3/fts3_expr.c 25e30cf24198333f2ed545af905b168e88f56903 F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295 F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2 -F ext/fts3/fts3_snippet.c 92b40397b28422c35c4127492d7ac6da34d1966a +F ext/fts3/fts3_snippet.c 6ee626017ddcf7d72ca4f587724e3506434fc0d7 F ext/fts3/fts3_term.c 0ade1812c0e97f394b58299810dfd5d2fb7ba501 F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3 F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d -F ext/fts3/fts3_write.c beaa93bcbe1664fcada75b70893f9b221acf2777 +F ext/fts3/fts3_write.c ed58c53fbcbc2ea79258e734159a5951ffeb1bd4 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9 @@ -489,7 +489,7 @@ F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test dd7001ac86d09c154a7dff064f4739c60e2b312c F test/fuzzer1.test 3105b5a89a6cb0d475f0877debec942fe4143462 -F test/hook.test f04c3412463f8ec117c1c704c74ca0f627ce733a +F test/hook.test f2277c309e4ee8067d95d6b9b315568e9d5329b2 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75 @@ -939,7 +939,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P f0f0a03db214b68a37069f64c27ae8520220c900 -R 95d28d5ab6425c93022acec043aee69b +P cc83991caae7c7d647432d5711b6cd80228c3002 +R eef52aec1faead4921ebaf39c1605d2b U dan -Z 2f1dd28b688335ffa6210dbb33afadf5 +Z 01d8f7aea2ce8ebb35c05ccc519b614f diff --git a/manifest.uuid b/manifest.uuid index 3079457335..6768a6838c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cc83991caae7c7d647432d5711b6cd80228c3002 \ No newline at end of file +84097a4c759b1d65890af885f137d3cb16eef584 \ No newline at end of file diff --git a/test/hook.test b/test/hook.test index 6496d41e13..c4dfb2e4f9 100644 --- a/test/hook.test +++ b/test/hook.test @@ -274,6 +274,34 @@ ifcapable compound&&attach { set ::update_hook } [list] } + +do_test hook-4.4 { + execsql { + CREATE TABLE t4(a UNIQUE, b); + INSERT INTO t4 VALUES(1, 'a'); + INSERT INTO t4 VALUES(2, 'b'); + } + set ::update_hook [list] + execsql { + REPLACE INTO t4 VALUES(1, 'c'); + } + set ::update_hook +} [list INSERT main t4 3 ] +do_execsql_test hook-4.4.1 { + SELECT * FROM t4 ORDER BY a; +} {1 c 2 b} +do_test hook-4.4.2 { + set ::update_hook [list] + execsql { + PRAGMA recursive_triggers = on; + REPLACE INTO t4 VALUES(1, 'd'); + } + set ::update_hook +} [list INSERT main t4 4 ] +do_execsql_test hook-4.4.3 { + SELECT * FROM t4 ORDER BY a; +} {1 d 2 b} + db update_hook {} # #----------------------------------------------------------------------------