/ Check-in [c7271fbd]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge recent changes from trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rowvalue
Files: files | file ages | folders
SHA1:c7271fbde1aebb15daaedb7f1fa75fe410fd46f6
User & Date: drh 2016-09-02 23:56:32
Context
2016-09-03
01:46
Performance optimizations. check-in: f1d06c49 user: drh tags: rowvalue
2016-09-02
23:56
Merge recent changes from trunk. check-in: c7271fbd user: drh tags: rowvalue
21:34
Add a test case for the OOM handled by the previous commit. check-in: 9bdf7ca1 user: dan tags: trunk
2016-08-27
14:13
Merge updates from trunk. check-in: 082fd5f8 user: drh tags: rowvalue
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5_aux.c.

   241    241     if( rc!=SQLITE_OK ){
   242    242       sqlite3_result_error_code(pCtx, rc);
   243    243     }
   244    244   }
   245    245   /*
   246    246   ** End of highlight() implementation.
   247    247   **************************************************************************/
          248  +
          249  +/*
          250  +** Context object passed to the fts5SentenceFinderCb() function.
          251  +*/
          252  +typedef struct Fts5SFinder Fts5SFinder;
          253  +struct Fts5SFinder {
          254  +  int iPos;                       /* Current token position */
          255  +  int nFirstAlloc;                /* Allocated size of aFirst[] */
          256  +  int nFirst;                     /* Number of entries in aFirst[] */
          257  +  int *aFirst;                    /* Array of first token in each sentence */
          258  +  const char *zDoc;               /* Document being tokenized */
          259  +};
          260  +
          261  +/*
          262  +** Add an entry to the Fts5SFinder.aFirst[] array. Grow the array if
          263  +** necessary. Return SQLITE_OK if successful, or SQLITE_NOMEM if an
          264  +** error occurs.
          265  +*/
          266  +static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){
          267  +  if( p->nFirstAlloc==p->nFirst ){
          268  +    int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64;
          269  +    int *aNew;
          270  +
          271  +    aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int));
          272  +    if( aNew==0 ) return SQLITE_NOMEM;
          273  +    p->aFirst = aNew;
          274  +    p->nFirstAlloc = nNew;
          275  +  }
          276  +  p->aFirst[p->nFirst++] = iAdd;
          277  +  return SQLITE_OK;
          278  +}
          279  +
          280  +/*
          281  +** This function is an xTokenize() callback used by the auxiliary snippet()
          282  +** function. Its job is to identify tokens that are the first in a sentence.
          283  +** For each such token, an entry is added to the SFinder.aFirst[] array.
          284  +*/
          285  +static int fts5SentenceFinderCb(
          286  +  void *pContext,                 /* Pointer to HighlightContext object */
          287  +  int tflags,                     /* Mask of FTS5_TOKEN_* flags */
          288  +  const char *pToken,             /* Buffer containing token */
          289  +  int nToken,                     /* Size of token in bytes */
          290  +  int iStartOff,                  /* Start offset of token */
          291  +  int iEndOff                     /* End offset of token */
          292  +){
          293  +  int rc = SQLITE_OK;
          294  +
          295  +  if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
          296  +    Fts5SFinder *p = (Fts5SFinder*)pContext;
          297  +    if( p->iPos>0 ){
          298  +      int i;
          299  +      char c = 0;
          300  +      for(i=iStartOff-1; i>=0; i--){
          301  +        c = p->zDoc[i];
          302  +        if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) break;
          303  +      }
          304  +      if( i!=iStartOff-1 && (c=='.' || c==':') ){
          305  +        rc = fts5SentenceFinderAdd(p, p->iPos);
          306  +      }
          307  +    }else{
          308  +      rc = fts5SentenceFinderAdd(p, 0);
          309  +    }
          310  +    p->iPos++;
          311  +  }
          312  +  return rc;
          313  +}
          314  +
          315  +static int fts5SnippetScore(
          316  +  const Fts5ExtensionApi *pApi,   /* API offered by current FTS version */
          317  +  Fts5Context *pFts,              /* First arg to pass to pApi functions */
          318  +  int nDocsize,                   /* Size of column in tokens */
          319  +  unsigned char *aSeen,           /* Array with one element per query phrase */
          320  +  int iCol,                       /* Column to score */
          321  +  int iPos,                       /* Starting offset to score */
          322  +  int nToken,                     /* Max tokens per snippet */
          323  +  int *pnScore,                   /* OUT: Score */
          324  +  int *piPos                      /* OUT: Adjusted offset */
          325  +){
          326  +  int rc;
          327  +  int i;
          328  +  int ip = 0;
          329  +  int ic = 0;
          330  +  int iOff = 0;
          331  +  int iFirst = -1;
          332  +  int nInst;
          333  +  int nScore = 0;
          334  +  int iLast = 0;
          335  +
          336  +  rc = pApi->xInstCount(pFts, &nInst);
          337  +  for(i=0; i<nInst && rc==SQLITE_OK; i++){
          338  +    rc = pApi->xInst(pFts, i, &ip, &ic, &iOff);
          339  +    if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){
          340  +      nScore += (aSeen[ip] ? 1 : 1000);
          341  +      aSeen[ip] = 1;
          342  +      if( iFirst<0 ) iFirst = iOff;
          343  +      iLast = iOff + pApi->xPhraseSize(pFts, ip);
          344  +    }
          345  +  }
          346  +
          347  +  *pnScore = nScore;
          348  +  if( piPos ){
          349  +    int iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
          350  +    if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken;
          351  +    if( iAdj<0 ) iAdj = 0;
          352  +    *piPos = iAdj;
          353  +  }
          354  +
          355  +  return rc;
          356  +}
   248    357   
   249    358   /*
   250    359   ** Implementation of snippet() function.
   251    360   */
   252    361   static void fts5SnippetFunction(
   253    362     const Fts5ExtensionApi *pApi,   /* API offered by current FTS version */
   254    363     Fts5Context *pFts,              /* First arg to pass to pApi functions */
................................................................................
   263    372     int nToken;                     /* 5th argument to snippet() */
   264    373     int nInst = 0;                  /* Number of instance matches this row */
   265    374     int i;                          /* Used to iterate through instances */
   266    375     int nPhrase;                    /* Number of phrases in query */
   267    376     unsigned char *aSeen;           /* Array of "seen instance" flags */
   268    377     int iBestCol;                   /* Column containing best snippet */
   269    378     int iBestStart = 0;             /* First token of best snippet */
   270         -  int iBestLast;                  /* Last token of best snippet */
   271    379     int nBestScore = 0;             /* Score of best snippet */
   272    380     int nColSize = 0;               /* Total size of iBestCol in tokens */
          381  +  Fts5SFinder sFinder;            /* Used to find the beginnings of sentences */
          382  +  int nCol;
   273    383   
   274    384     if( nVal!=5 ){
   275    385       const char *zErr = "wrong number of arguments to function snippet()";
   276    386       sqlite3_result_error(pCtx, zErr, -1);
   277    387       return;
   278    388     }
   279    389   
          390  +  nCol = pApi->xColumnCount(pFts);
   280    391     memset(&ctx, 0, sizeof(HighlightContext));
   281    392     iCol = sqlite3_value_int(apVal[0]);
   282    393     ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
   283    394     ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
   284    395     zEllips = (const char*)sqlite3_value_text(apVal[3]);
   285    396     nToken = sqlite3_value_int(apVal[4]);
   286         -  iBestLast = nToken-1;
   287    397   
   288    398     iBestCol = (iCol>=0 ? iCol : 0);
   289    399     nPhrase = pApi->xPhraseCount(pFts);
   290    400     aSeen = sqlite3_malloc(nPhrase);
   291    401     if( aSeen==0 ){
   292    402       rc = SQLITE_NOMEM;
   293    403     }
   294         -
   295    404     if( rc==SQLITE_OK ){
   296    405       rc = pApi->xInstCount(pFts, &nInst);
   297    406     }
   298         -  for(i=0; rc==SQLITE_OK && i<nInst; i++){
   299         -    int ip, iSnippetCol, iStart;
   300         -    memset(aSeen, 0, nPhrase);
   301         -    rc = pApi->xInst(pFts, i, &ip, &iSnippetCol, &iStart);
   302         -    if( rc==SQLITE_OK && (iCol<0 || iSnippetCol==iCol) ){
   303         -      int nScore = 1000;
   304         -      int iLast = iStart - 1 + pApi->xPhraseSize(pFts, ip);
   305         -      int j;
   306         -      aSeen[ip] = 1;
   307         -
   308         -      for(j=i+1; rc==SQLITE_OK && j<nInst; j++){
   309         -        int ic; int io; int iFinal;
   310         -        rc = pApi->xInst(pFts, j, &ip, &ic, &io);
   311         -        iFinal = io + pApi->xPhraseSize(pFts, ip) - 1;
   312         -        if( rc==SQLITE_OK && ic==iSnippetCol && iLast<iStart+nToken ){
   313         -          nScore += aSeen[ip] ? 1000 : 1;
   314         -          aSeen[ip] = 1;
   315         -          if( iFinal>iLast ) iLast = iFinal;
          407  +
          408  +  memset(&sFinder, 0, sizeof(Fts5SFinder));
          409  +  for(i=0; i<nCol; i++){
          410  +    if( iCol<0 || iCol==i ){
          411  +      int nDoc;
          412  +      int nDocsize;
          413  +      int ii;
          414  +      sFinder.iPos = 0;
          415  +      sFinder.nFirst = 0;
          416  +      rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc);
          417  +      if( rc!=SQLITE_OK ) break;
          418  +      rc = pApi->xTokenize(pFts, 
          419  +          sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb
          420  +      );
          421  +      if( rc!=SQLITE_OK ) break;
          422  +      rc = pApi->xColumnSize(pFts, i, &nDocsize);
          423  +      if( rc!=SQLITE_OK ) break;
          424  +
          425  +      for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){
          426  +        int ip, ic, io;
          427  +        int iAdj;
          428  +        int nScore;
          429  +        int jj;
          430  +
          431  +        rc = pApi->xInst(pFts, ii, &ip, &ic, &io);
          432  +        if( ic!=i || rc!=SQLITE_OK ) continue;
          433  +        memset(aSeen, 0, nPhrase);
          434  +        rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
          435  +            io, nToken, &nScore, &iAdj
          436  +        );
          437  +        if( rc==SQLITE_OK && nScore>nBestScore ){
          438  +          nBestScore = nScore;
          439  +          iBestCol = i;
          440  +          iBestStart = iAdj;
          441  +          nColSize = nDocsize;
   316    442           }
   317         -      }
   318    443   
   319         -      if( rc==SQLITE_OK && nScore>nBestScore ){
   320         -        iBestCol = iSnippetCol;
   321         -        iBestStart = iStart;
   322         -        iBestLast = iLast;
   323         -        nBestScore = nScore;
          444  +        if( rc==SQLITE_OK && sFinder.nFirst && nDocsize>nToken ){
          445  +          for(jj=0; jj<(sFinder.nFirst-1); jj++){
          446  +            if( sFinder.aFirst[jj+1]>io ) break;
          447  +          }
          448  +
          449  +          if( sFinder.aFirst[jj]<io ){
          450  +            int nScore;
          451  +            memset(aSeen, 0, nPhrase);
          452  +            rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i, 
          453  +              sFinder.aFirst[jj], nToken, &nScore, 0
          454  +            );
          455  +
          456  +            nScore += (sFinder.aFirst[jj]==0 ? 120 : 100);
          457  +            if( rc==SQLITE_OK && nScore>nBestScore ){
          458  +              nBestScore = nScore;
          459  +              iBestCol = i;
          460  +              iBestStart = sFinder.aFirst[jj];
          461  +              nColSize = nDocsize;
          462  +            }
          463  +          }
          464  +        }
   324    465         }
   325    466       }
   326    467     }
   327    468   
   328    469     if( rc==SQLITE_OK ){
   329         -    rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
          470  +    rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
   330    471     }
   331         -  if( rc==SQLITE_OK ){
   332         -    rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
          472  +  if( rc==SQLITE_OK && nColSize==0 ){
          473  +    rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
   333    474     }
   334    475     if( ctx.zIn ){
   335    476       if( rc==SQLITE_OK ){
   336    477         rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
   337    478       }
   338    479   
   339         -    if( (iBestStart+nToken-1)>iBestLast ){
   340         -      iBestStart -= (iBestStart+nToken-1-iBestLast) / 2;
   341         -    }
   342         -    if( iBestStart+nToken>nColSize ){
   343         -      iBestStart = nColSize - nToken;
   344         -    }
   345         -    if( iBestStart<0 ) iBestStart = 0;
   346         -
   347    480       ctx.iRangeStart = iBestStart;
   348    481       ctx.iRangeEnd = iBestStart + nToken - 1;
   349    482   
   350    483       if( iBestStart>0 ){
   351    484         fts5HighlightAppend(&rc, &ctx, zEllips, -1);
   352    485       }
   353    486   
................................................................................
   361    494         rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
   362    495       }
   363    496       if( ctx.iRangeEnd>=(nColSize-1) ){
   364    497         fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
   365    498       }else{
   366    499         fts5HighlightAppend(&rc, &ctx, zEllips, -1);
   367    500       }
   368         -
   369         -    if( rc==SQLITE_OK ){
   370         -      sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
   371         -    }else{
   372         -      sqlite3_result_error_code(pCtx, rc);
   373         -    }
   374         -    sqlite3_free(ctx.zOut);
          501  +  }
          502  +  if( rc==SQLITE_OK ){
          503  +    sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
          504  +  }else{
          505  +    sqlite3_result_error_code(pCtx, rc);
   375    506     }
          507  +  sqlite3_free(ctx.zOut);
   376    508     sqlite3_free(aSeen);
          509  +  sqlite3_free(sFinder.aFirst);
   377    510   }
   378    511   
   379    512   /************************************************************************/
   380    513   
   381    514   /*
   382    515   ** The first time the bm25() function is called for a query, an instance
   383    516   ** of the following structure is allocated and populated.

Changes to ext/fts5/test/fts5af.test.

    68     68     1.6 {o o o o o X o} {o o o o o [X] o}
    69     69     1.7 {o o o o o o X} {o o o o o o [X]}
    70     70   
    71     71     2.1 {X o o o o o o o} {[X] o o o o o o...}
    72     72     2.2 {o X o o o o o o} {o [X] o o o o o...}
    73     73     2.3 {o o X o o o o o} {o o [X] o o o o...}
    74     74     2.4 {o o o X o o o o} {o o o [X] o o o...}
    75         -  2.5 {o o o o X o o o} {...o o o [X] o o o}
    76         -  2.6 {o o o o o X o o} {...o o o o [X] o o}
    77         -  2.7 {o o o o o o X o} {...o o o o o [X] o}
           75  +  2.5 {o o o o X o o o} {o o o o [X] o o...}
           76  +  2.6 {o o o o o X o o} {o o o o o [X] o...}
           77  +  2.7 {o o o o o o X o} {o o o o o o [X]...}
    78     78     2.8 {o o o o o o o X} {...o o o o o o [X]}
           79  +
           80  +  2.9  {o o o o o o o X o}       {...o o o o o [X] o}
           81  +  2.10 {o o o o o o o X o o}     {...o o o o [X] o o}
           82  +  2.11 {o o o o o o o X o o o}   {...o o o [X] o o o}
           83  +  2.12 {o o o o o o o X o o o o} {...o o o [X] o o o...}
           84  +
    79     85   
    80     86     3.1 {X o o o o o o o o} {[X] o o o o o o...}
    81     87     3.2 {o X o o o o o o o} {o [X] o o o o o...}
    82     88     3.3 {o o X o o o o o o} {o o [X] o o o o...}
    83     89     3.4 {o o o X o o o o o} {o o o [X] o o o...}
    84         -  3.5 {o o o o X o o o o} {...o o o [X] o o o...}
    85         -  3.6 {o o o o o X o o o} {...o o o [X] o o o}
    86         -  3.7 {o o o o o o X o o} {...o o o o [X] o o}
    87         -  3.8 {o o o o o o o X o} {...o o o o o [X] o}
    88         -  3.9 {o o o o o o o o X} {...o o o o o o [X]}
           90  +
           91  +  3.5 {o o o o o o o X o o o o} {...o o o [X] o o o...}
           92  +  3.6 {o o o o o o o o X o o o} {...o o o [X] o o o}
           93  +  3.7 {o o o o o o o o o X o o} {...o o o o [X] o o}
           94  +  3.8 {o o o o o o o o o o X o} {...o o o o o [X] o}
           95  +  3.9 {o o o o o o o o o o o X} {...o o o o o o [X]}
    89     96   
    90     97     4.1 {X o o o o o X o o} {[X] o o o o o [X]...}
    91         -  4.2 {o X o o o o o X o} {...[X] o o o o o [X]...}
    92         -  4.3 {o o X o o o o o X} {...[X] o o o o o [X]}
           98  +  4.2 {o o o o o o o X o o o o o X o} {...[X] o o o o o [X]...}
           99  +  4.3 {o o o o o o o o X o o o o o X} {...[X] o o o o o [X]}
    93    100   
    94    101     5.1 {X o o o o X o o o} {[X] o o o o [X] o...}
    95         -  5.2 {o X o o o o X o o} {...[X] o o o o [X] o...}
    96         -  5.3 {o o X o o o o X o} {...[X] o o o o [X] o}
    97         -  5.4 {o o o X o o o o X} {...o [X] o o o o [X]}
          102  +  5.2 {o o o o o o o X o o o o X o o} {...[X] o o o o [X] o...}
          103  +  5.3 {o o o o o o o o X o o o o X o} {...[X] o o o o [X] o}
          104  +  5.4 {o o o o o o o o o X o o o o X} {...o [X] o o o o [X]}
    98    105   
    99    106     6.1 {X o o o X o o o} {[X] o o o [X] o o...}
   100    107     6.2 {o X o o o X o o o} {o [X] o o o [X] o...}
   101         -  6.3 {o o X o o o X o o} {...o [X] o o o [X] o...}
   102         -  6.4 {o o o X o o o X o} {...o [X] o o o [X] o}
   103         -  6.5 {o o o o X o o o X} {...o o [X] o o o [X]}
          108  +  6.3 {o o o o o o o X o o o X o o} {...o [X] o o o [X] o...}
          109  +  6.4 {o o o o o o o o X o o o X o} {...o [X] o o o [X] o}
          110  +  6.5 {o o o o o o o o o X o o o X} {...o o [X] o o o [X]}
   104    111   
   105    112     7.1 {X o o X o o o o o} {[X] o o [X] o o o...}
   106    113     7.2 {o X o o X o o o o} {o [X] o o [X] o o...}
   107         -  7.3 {o o X o o X o o o} {...o [X] o o [X] o o...}
   108         -  7.4 {o o o X o o X o o} {...o [X] o o [X] o o}
   109         -  7.5 {o o o o X o o X o} {...o o [X] o o [X] o}
   110         -  7.6 {o o o o o X o o X} {...o o o [X] o o [X]}
          114  +  7.3 {o o o o o o o X o o X o o o} {...o [X] o o [X] o o...}
          115  +  7.4 {o o o o o o o o X o o X o o} {...o [X] o o [X] o o}
          116  +  7.5 {o o o o o o o o o X o o X o} {...o o [X] o o [X] o}
          117  +  7.6 {o o o o o o o o o o X o o X} {...o o o [X] o o [X]}
   111    118   
   112         -  8.1 {o o o o X o o o o o o o o o o o o o o o o o o o o o X X X o o o}
          119  +  8.1 {o o o o o o o o o X o o o o o o o o o o o o o o o o X X X o o o}
   113    120         {...o o [X] [X] [X] o o...}
          121  +  8.2 {o o o o o o o. o o X o o o o o o o o o o o o o o o o X X X o o o} 
          122  +      {...o o [X] o o o o...}
          123  +  8.3 {o o o o X o o o o o o o o o o o o o o o o o o o o o X X X o o o} 
          124  +      {o o o o [X] o o...}
   114    125   } {
   115    126     do_snippet_test 1.$tn $doc X $res
   116    127   }
   117    128   
   118    129   if {[detail_is_full]} {
   119    130     foreach {tn doc res} {
   120    131       1.1 {X Y o o o o o} {[X Y] o o o o o}
................................................................................
   123    134       1.4 {o o o X Y o o} {o o o [X Y] o o}
   124    135       1.5 {o o o o X Y o} {o o o o [X Y] o}
   125    136       1.6 {o o o o o X Y} {o o o o o [X Y]}
   126    137   
   127    138       2.1 {X Y o o o o o o} {[X Y] o o o o o...}
   128    139       2.2 {o X Y o o o o o} {o [X Y] o o o o...}
   129    140       2.3 {o o X Y o o o o} {o o [X Y] o o o...}
   130         -    2.4 {o o o X Y o o o} {...o o [X Y] o o o}
   131         -    2.5 {o o o o X Y o o} {...o o o [X Y] o o}
   132         -    2.6 {o o o o o X Y o} {...o o o o [X Y] o}
   133         -    2.7 {o o o o o o X Y} {...o o o o o [X Y]}
          141  +    2.4 {o o o o o o o X Y o o o} {...o o [X Y] o o o}
          142  +    2.5 {o o o o o o o o X Y o o} {...o o o [X Y] o o}
          143  +    2.6 {o o o o o o o o o X Y o} {...o o o o [X Y] o}
          144  +    2.7 {o o o o o o o o o o X Y} {...o o o o o [X Y]}
   134    145   
   135    146       3.1 {X Y o o o o o o o} {[X Y] o o o o o...}
   136    147       3.2 {o X Y o o o o o o} {o [X Y] o o o o...}
   137    148       3.3 {o o X Y o o o o o} {o o [X Y] o o o...}
   138         -    3.4 {o o o X Y o o o o} {...o o [X Y] o o o...}
   139         -    3.5 {o o o o X Y o o o} {...o o [X Y] o o o}
   140         -    3.6 {o o o o o X Y o o} {...o o o [X Y] o o}
   141         -    3.7 {o o o o o o X Y o} {...o o o o [X Y] o}
   142         -    3.8 {o o o o o o o X Y} {...o o o o o [X Y]}
          149  +    3.4 {o o o o o o o X Y o o o o} {...o o [X Y] o o o...}
          150  +    3.5 {o o o o o o o o X Y o o o} {...o o [X Y] o o o}
          151  +    3.6 {o o o o o o o o o X Y o o} {...o o o [X Y] o o}
          152  +    3.7 {o o o o o o o o o o X Y o} {...o o o o [X Y] o}
          153  +    3.8 {o o o o o o o o o o o X Y} {...o o o o o [X Y]}
   143    154     } {
   144    155       do_snippet_test 2.$tn $doc "X + Y" $res
   145    156     }
   146    157   }
   147    158   
          159  +do_execsql_test 4.0 {
          160  +  CREATE VIRTUAL TABLE x1 USING fts5(a, b);
          161  +  INSERT INTO x1 VALUES('xyz', '1 2 3 4 5 6 7 8 9 10 11 12 13');
          162  +  SELECT snippet(x1, 1, '[', ']', '...', 5) FROM x1('xyz');
          163  +} {
          164  +  {1 2 3 4 5...}
          165  +}
          166  +
          167  +do_execsql_test 5.0 {
          168  +  CREATE VIRTUAL TABLE p1 USING fts5(a, b);
          169  +  INSERT INTO p1 VALUES(
          170  +    'x a a a a a a a a a a',
          171  +    'a a a a a a a a a a a a a a a a a a a x'
          172  +  );
          173  +}
          174  +do_execsql_test 5.1 {
          175  +  SELECT snippet(p1, 0, '[', ']', '...', 6) FROM p1('x');
          176  +} {{[x] a a a a a...}}
          177  +
   148    178   } ;# foreach_detail_mode 
   149    179   
   150    180   finish_test
   151    181   

Changes to ext/fts5/test/fts5unicode2.test.

   156    156        the maximum x value.
   157    157     }
   158    158     3 "ROW" {
   159    159        ...returns the value of y on the same [row] that contains 
   160    160        the maximum x value.
   161    161     }
   162    162     4 "rollback" {
   163         -     ...[ROLLBACK]. Instead, the pending statement
   164         -     will return SQLITE_ABORT upon next access after the [ROLLBACK].
          163  +     Pending statements no longer block [ROLLBACK]. Instead, the pending
          164  +     statement will return SQLITE_ABORT upon...
   165    165     }
   166    166     5 "rOllback" {
   167         -     ...[ROLLBACK]. Instead, the pending statement
   168         -     will return SQLITE_ABORT upon next access after the [ROLLBACK].
          167  +     Pending statements no longer block [ROLLBACK]. Instead, the pending
          168  +     statement will return SQLITE_ABORT upon...
   169    169     }
   170    170     6 "lang*" {
   171    171        Added support for the FTS4 [languageid] option.
   172    172     }
   173    173   } {
   174    174     do_test 2.$tn {
   175    175       set q [mapdoc $query]

Changes to ext/rbu/rbudiff.test.

   136    136       );
   137    137       DELETE FROM t2;
   138    138       INSERT INTO t2 VALUES(1,
   139    139           X'0000000000000000111111111111111122222222222222223333333FFF333333'
   140    140       );
   141    141     }
   142    142   
          143  +  4 {
          144  +    CREATE TABLE x1(a, b, c, PRIMARY KEY(a, b, c));
          145  +    INSERT INTO x1 VALUES('u', 'v', NULL);
          146  +    INSERT INTO x1 VALUES('x', 'y', 'z');
          147  +    INSERT INTO x1 VALUES('a', NULL, 'b');
          148  +  } {
          149  +    INSERT INTO x1 VALUES('a', 'b', 'c');
          150  +  }
          151  +
   143    152   } {
   144    153     catch { db close }
   145    154   
   146    155     forcedelete test.db test.db2
   147    156     sqlite3 db test.db
   148    157     db eval "$init"
   149    158     sqlite3 db test.db2
................................................................................
   275    284         db2 eval { INSERT INTO t1(t1) VALUES('integrity-check') }
   276    285       } {}
   277    286   
   278    287       db close
   279    288       db2 close
   280    289     }
   281    290   }
          291  +
   282    292   
   283    293   finish_test
   284    294   

Changes to ext/rbu/sqlite3rbu.h.

   100    100   ** Instead of a regular table, the RBU database may also contain virtual
   101    101   ** tables or view named using the data_<target> naming scheme. 
   102    102   **
   103    103   ** Instead of the plain data_<target> naming scheme, RBU database tables 
   104    104   ** may also be named data<integer>_<target>, where <integer> is any sequence
   105    105   ** of zero or more numeric characters (0-9). This can be significant because
   106    106   ** tables within the RBU database are always processed in order sorted by 
   107         -** name. By judicious selection of the the <integer> portion of the names
          107  +** name. By judicious selection of the <integer> portion of the names
   108    108   ** of the RBU tables the user can therefore control the order in which they
   109    109   ** are processed. This can be useful, for example, to ensure that "external
   110    110   ** content" FTS4 tables are updated before their underlying content tables.
   111    111   **
   112    112   ** If the target database table is a virtual table or a table that has no
   113    113   ** PRIMARY KEY declaration, the data_% table must also contain a column 
   114    114   ** named "rbu_rowid". This column is mapped to the tables implicit primary 

Changes to ext/rtree/rtree.c.

  1538   1538     memset(pCsr, 0, sizeof(RtreeCursor));
  1539   1539     pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
  1540   1540   
  1541   1541     pCsr->iStrategy = idxNum;
  1542   1542     if( idxNum==1 ){
  1543   1543       /* Special case - lookup by rowid. */
  1544   1544       RtreeNode *pLeaf;        /* Leaf on which the required cell resides */
  1545         -    RtreeSearchPoint *p;     /* Search point for the the leaf */
         1545  +    RtreeSearchPoint *p;     /* Search point for the leaf */
  1546   1546       i64 iRowid = sqlite3_value_int64(argv[0]);
  1547   1547       i64 iNode = 0;
  1548   1548       rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
  1549   1549       if( rc==SQLITE_OK && pLeaf!=0 ){
  1550   1550         p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
  1551   1551         assert( p!=0 );  /* Always returns pCsr->sPoint */
  1552   1552         pCsr->aNode[0] = pLeaf;

Changes to ext/session/session_common.tcl.

    71     71   
    72     72   proc do_common_sql {sql} {
    73     73     execsql $sql db
    74     74     execsql $sql db2
    75     75   }
    76     76   
    77     77   proc changeset_from_sql {sql {dbname main}} {
           78  +  if {$dbname == "main"} {
           79  +    return [sql_exec_changeset db $sql]
           80  +  }
    78     81     set rc [catch {
    79     82       sqlite3session S db $dbname
    80     83       db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
    81     84         S attach $name
    82     85       }
    83     86       db eval $sql
    84     87       S changeset

Changes to ext/session/sqlite3session.h.

   723    723     void *pB,                       /* Pointer to buffer containing changeset B */
   724    724     int *pnOut,                     /* OUT: Number of bytes in output changeset */
   725    725     void **ppOut                    /* OUT: Buffer containing output changeset */
   726    726   );
   727    727   
   728    728   
   729    729   /*
   730         -** Changegroup handle.
          730  +** CAPI3REF: Changegroup Handle
   731    731   */
   732    732   typedef struct sqlite3_changegroup sqlite3_changegroup;
   733    733   
   734    734   /*
   735         -** CAPI3REF: Combine two or more changesets into a single changeset.
          735  +** CAPI3REF: Create A New Changegroup Object
   736    736   **
   737    737   ** An sqlite3_changegroup object is used to combine two or more changesets
   738    738   ** (or patchsets) into a single changeset (or patchset). A single changegroup
   739    739   ** object may combine changesets or patchsets, but not both. The output is
   740    740   ** always in the same format as the input.
   741    741   **
   742    742   ** If successful, this function returns SQLITE_OK and populates (*pp) with
................................................................................
   765    765   ** As well as the regular sqlite3changegroup_add() and 
   766    766   ** sqlite3changegroup_output() functions, also available are the streaming
   767    767   ** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm().
   768    768   */
   769    769   int sqlite3changegroup_new(sqlite3_changegroup **pp);
   770    770   
   771    771   /*
          772  +** CAPI3REF: Add A Changeset To A Changegroup
          773  +**
   772    774   ** Add all changes within the changeset (or patchset) in buffer pData (size
   773    775   ** nData bytes) to the changegroup. 
   774    776   **
   775    777   ** If the buffer contains a patchset, then all prior calls to this function
   776    778   ** on the same changegroup object must also have specified patchsets. Or, if
   777    779   ** the buffer contains a changeset, so must have the earlier calls to this
   778    780   ** function. Otherwise, SQLITE_ERROR is returned and no changes are added
................................................................................
   840    842   ** final contents of the changegroup is undefined.
   841    843   **
   842    844   ** If no error occurs, SQLITE_OK is returned.
   843    845   */
   844    846   int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
   845    847   
   846    848   /*
          849  +** CAPI3REF: Obtain A Composite Changeset From A Changegroup
          850  +**
   847    851   ** Obtain a buffer containing a changeset (or patchset) representing the
   848    852   ** current contents of the changegroup. If the inputs to the changegroup
   849    853   ** were themselves changesets, the output is a changeset. Or, if the
   850    854   ** inputs were patchsets, the output is also a patchset.
   851    855   **
   852    856   ** As with the output of the sqlite3session_changeset() and
   853    857   ** sqlite3session_patchset() functions, all changes related to a single
................................................................................
   868    872   int sqlite3changegroup_output(
   869    873     sqlite3_changegroup*,
   870    874     int *pnData,                    /* OUT: Size of output buffer in bytes */
   871    875     void **ppData                   /* OUT: Pointer to output buffer */
   872    876   );
   873    877   
   874    878   /*
   875         -** Delete a changegroup object.
          879  +** CAPI3REF: Delete A Changegroup Object
   876    880   */
   877    881   void sqlite3changegroup_delete(sqlite3_changegroup*);
   878    882   
   879    883   /*
   880    884   ** CAPI3REF: Apply A Changeset To A Database
   881    885   **
   882    886   ** Apply a changeset to a database. This function attempts to update the

Changes to ext/session/test_session.c.

    24     24   typedef struct TestStreamInput TestStreamInput;
    25     25   struct TestStreamInput {
    26     26     int nStream;                    /* Maximum chunk size */
    27     27     unsigned char *aData;           /* Pointer to buffer containing data */
    28     28     int nData;                      /* Size of buffer aData in bytes */
    29     29     int iData;                      /* Bytes of data already read by sessions */
    30     30   };
           31  +
           32  +/*
           33  +** Extract an sqlite3* db handle from the object passed as the second
           34  +** argument. If successful, set *pDb to point to the db handle and return
           35  +** TCL_OK. Otherwise, return TCL_ERROR.
           36  +*/
           37  +static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
           38  +  Tcl_CmdInfo info;
           39  +  if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
           40  +    Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
           41  +    return TCL_ERROR;
           42  +  }
           43  +
           44  +  *pDb = *(sqlite3 **)info.objClientData;
           45  +  return TCL_OK;
           46  +}
           47  +
           48  +/*************************************************************************
           49  +** The following code is copied byte-for-byte from the sessions module
           50  +** documentation.  It is used by some of the sessions modules tests to
           51  +** ensure that the example in the documentation does actually work.
           52  +*/ 
           53  +/*
           54  +** Argument zSql points to a buffer containing an SQL script to execute 
           55  +** against the database handle passed as the first argument. As well as
           56  +** executing the SQL script, this function collects a changeset recording
           57  +** all changes made to the "main" database file. Assuming no error occurs,
           58  +** output variables (*ppChangeset) and (*pnChangeset) are set to point
           59  +** to a buffer containing the changeset and the size of the changeset in
           60  +** bytes before returning SQLITE_OK. In this case it is the responsibility
           61  +** of the caller to eventually free the changeset blob by passing it to
           62  +** the sqlite3_free function.
           63  +**
           64  +** Or, if an error does occur, return an SQLite error code. The final
           65  +** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
           66  +*/
           67  +int sql_exec_changeset(
           68  +  sqlite3 *db,                  /* Database handle */
           69  +  const char *zSql,             /* SQL script to execute */
           70  +  int *pnChangeset,             /* OUT: Size of changeset blob in bytes */
           71  +  void **ppChangeset            /* OUT: Pointer to changeset blob */
           72  +){
           73  +  sqlite3_session *pSession = 0;
           74  +  int rc;
           75  +
           76  +  /* Create a new session object */
           77  +  rc = sqlite3session_create(db, "main", &pSession);
           78  +
           79  +  /* Configure the session object to record changes to all tables */
           80  +  if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
           81  +
           82  +  /* Execute the SQL script */
           83  +  if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
           84  +
           85  +  /* Collect the changeset */
           86  +  if( rc==SQLITE_OK ){
           87  +    rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
           88  +  }
           89  +
           90  +  /* Delete the session object */
           91  +  sqlite3session_delete(pSession);
           92  +
           93  +  return rc;
           94  +}
           95  +/************************************************************************/
           96  +
           97  +/*
           98  +** Tclcmd: sql_exec_changeset DB SQL
           99  +*/
          100  +static int SQLITE_TCLAPI test_sql_exec_changeset(
          101  +  void * clientData,
          102  +  Tcl_Interp *interp,
          103  +  int objc,
          104  +  Tcl_Obj *CONST objv[]
          105  +){
          106  +  const char *zSql;
          107  +  sqlite3 *db;
          108  +  void *pChangeset;
          109  +  int nChangeset;
          110  +  int rc;
          111  +
          112  +  if( objc!=3 ){
          113  +    Tcl_WrongNumArgs(interp, 1, objv, "DB SQL");
          114  +    return TCL_ERROR;
          115  +  }
          116  +  if( dbHandleFromObj(interp, objv[1], &db) ) return TCL_ERROR;
          117  +  zSql = (const char*)Tcl_GetString(objv[2]);
          118  +
          119  +  rc = sql_exec_changeset(db, zSql, &nChangeset, &pChangeset);
          120  +  if( rc!=SQLITE_OK ){
          121  +    Tcl_ResetResult(interp);
          122  +    Tcl_AppendResult(interp, "error in sql_exec_changeset()", 0);
          123  +    return TCL_ERROR;
          124  +  }
          125  +
          126  +  Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChangeset, nChangeset));
          127  +  sqlite3_free(pChangeset);
          128  +  return TCL_OK;
          129  +}
          130  +
          131  +
    31    132   
    32    133   #define SESSION_STREAM_TCL_VAR "sqlite3session_streams"
    33    134   
    34    135   /*
    35    136   ** Attempt to find the global variable zVar within interpreter interp
    36    137   ** and extract an integer value from it. Return this value.
    37    138   **
................................................................................
   915   1016       return test_session_error(interp, rc, 0);
   916   1017     }
   917   1018   
   918   1019     return TCL_OK;
   919   1020   }
   920   1021   
   921   1022   int TestSession_Init(Tcl_Interp *interp){
   922         -  Tcl_CreateObjCommand(interp, "sqlite3session", test_sqlite3session, 0, 0);
   923         -  Tcl_CreateObjCommand(
   924         -      interp, "sqlite3session_foreach", test_sqlite3session_foreach, 0, 0
   925         -  );
   926         -  Tcl_CreateObjCommand(
   927         -      interp, "sqlite3changeset_invert", test_sqlite3changeset_invert, 0, 0
   928         -  );
   929         -  Tcl_CreateObjCommand(
   930         -      interp, "sqlite3changeset_concat", test_sqlite3changeset_concat, 0, 0
   931         -  );
   932         -  Tcl_CreateObjCommand(
   933         -      interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0
   934         -  );
   935         -  Tcl_CreateObjCommand(
   936         -      interp, "sqlite3changeset_apply_replace_all", 
   937         -      test_sqlite3changeset_apply_replace_all, 0, 0
   938         -  );
         1023  +  struct Cmd {
         1024  +    const char *zCmd;
         1025  +    Tcl_ObjCmdProc *xProc;
         1026  +  } aCmd[] = {
         1027  +    { "sqlite3session", test_sqlite3session },
         1028  +    { "sqlite3session_foreach", test_sqlite3session_foreach },
         1029  +    { "sqlite3changeset_invert", test_sqlite3changeset_invert },
         1030  +    { "sqlite3changeset_concat", test_sqlite3changeset_concat },
         1031  +    { "sqlite3changeset_apply", test_sqlite3changeset_apply },
         1032  +    { "sqlite3changeset_apply_replace_all", 
         1033  +      test_sqlite3changeset_apply_replace_all },
         1034  +    { "sql_exec_changeset", test_sql_exec_changeset },
         1035  +  };
         1036  +  int i;
         1037  +
         1038  +  for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
         1039  +    struct Cmd *p = &aCmd[i];
         1040  +    Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
         1041  +  }
         1042  +
   939   1043     return TCL_OK;
   940   1044   }
   941   1045   
   942   1046   #endif /* SQLITE_TEST && SQLITE_SESSION && SQLITE_PREUPDATE_HOOK */

Changes to src/backup.c.

   192    192       p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
   193    193       p->pDestDb = pDestDb;
   194    194       p->pSrcDb = pSrcDb;
   195    195       p->iNext = 1;
   196    196       p->isAttached = 0;
   197    197   
   198    198       if( 0==p->pSrc || 0==p->pDest 
   199         -     || setDestPgsz(p)==SQLITE_NOMEM 
   200    199        || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK 
   201    200        ){
   202    201         /* One (or both) of the named databases did not exist or an OOM
   203    202         ** error was hit. Or there is a transaction open on the destination
   204    203         ** database. The error has already been written into the pDestDb 
   205    204         ** handle. All that is left to do here is free the sqlite3_backup 
   206    205         ** structure.  */
................................................................................
   380    379       */
   381    380       if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
   382    381         rc = SQLITE_BUSY;
   383    382       }else{
   384    383         rc = SQLITE_OK;
   385    384       }
   386    385   
   387         -    /* Lock the destination database, if it is not locked already. */
   388         -    if( SQLITE_OK==rc && p->bDestLocked==0
   389         -     && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) 
   390         -    ){
   391         -      p->bDestLocked = 1;
   392         -      sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
   393         -    }
   394         -
   395    386       /* If there is no open read-transaction on the source database, open
   396    387       ** one now. If a transaction is opened here, then it will be closed
   397    388       ** before this function exits.
   398    389       */
   399    390       if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){
   400    391         rc = sqlite3BtreeBeginTrans(p->pSrc, 0);
   401    392         bCloseTrans = 1;
   402    393       }
          394  +
          395  +    /* If the destination database has not yet been locked (i.e. if this
          396  +    ** is the first call to backup_step() for the current backup operation),
          397  +    ** try to set its page size to the same as the source database. This
          398  +    ** is especially important on ZipVFS systems, as in that case it is
          399  +    ** not possible to create a database file that uses one page size by
          400  +    ** writing to it with another.  */
          401  +    if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){
          402  +      rc = SQLITE_NOMEM;
          403  +    }
          404  +
          405  +    /* Lock the destination database, if it is not locked already. */
          406  +    if( SQLITE_OK==rc && p->bDestLocked==0
          407  +     && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) 
          408  +    ){
          409  +      p->bDestLocked = 1;
          410  +      sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
          411  +    }
   403    412   
   404    413       /* Do not allow backup if the destination database is in WAL mode
   405    414       ** and the page sizes are different between source and destination */
   406    415       pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
   407    416       pgszDest = sqlite3BtreeGetPageSize(p->pDest);
   408    417       destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest));
   409    418       if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){

Changes to src/main.c.

  2951   2951   
  2952   2952     /* Register all built-in functions, but do not attempt to read the
  2953   2953     ** database schema yet. This is delayed until the first time the database
  2954   2954     ** is accessed.
  2955   2955     */
  2956   2956     sqlite3Error(db, SQLITE_OK);
  2957   2957     sqlite3RegisterPerConnectionBuiltinFunctions(db);
         2958  +  rc = sqlite3_errcode(db);
         2959  +
         2960  +#ifdef SQLITE_ENABLE_FTS5
         2961  +  /* Register any built-in FTS5 module before loading the automatic
         2962  +  ** extensions. This allows automatic extensions to register FTS5 
         2963  +  ** tokenizers and auxiliary functions.  */
         2964  +  if( !db->mallocFailed && rc==SQLITE_OK ){
         2965  +    rc = sqlite3Fts5Init(db);
         2966  +  }
         2967  +#endif
  2958   2968   
  2959   2969     /* Load automatic extensions - extensions that have been registered
  2960   2970     ** using the sqlite3_automatic_extension() API.
  2961   2971     */
  2962         -  rc = sqlite3_errcode(db);
  2963   2972     if( rc==SQLITE_OK ){
  2964   2973       sqlite3AutoLoadExtensions(db);
  2965   2974       rc = sqlite3_errcode(db);
  2966   2975       if( rc!=SQLITE_OK ){
  2967   2976         goto opendb_out;
  2968   2977       }
  2969   2978     }
................................................................................
  2982   2991     }
  2983   2992   #endif
  2984   2993   
  2985   2994   #ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
  2986   2995     if( !db->mallocFailed && rc==SQLITE_OK ){
  2987   2996       rc = sqlite3Fts3Init(db);
  2988   2997     }
  2989         -#endif
  2990         -
  2991         -#ifdef SQLITE_ENABLE_FTS5
  2992         -  if( !db->mallocFailed && rc==SQLITE_OK ){
  2993         -    rc = sqlite3Fts5Init(db);
  2994         -  }
  2995   2998   #endif
  2996   2999   
  2997   3000   #ifdef SQLITE_ENABLE_ICU
  2998   3001     if( !db->mallocFailed && rc==SQLITE_OK ){
  2999   3002       rc = sqlite3IcuInit(db);
  3000   3003     }
  3001   3004   #endif

Changes to src/pager.c.

  6652   6652   ** then savepoint iSavepoint is also destroyed.
  6653   6653   **
  6654   6654   ** This function may return SQLITE_NOMEM if a memory allocation fails,
  6655   6655   ** or an IO error code if an IO error occurs while rolling back a 
  6656   6656   ** savepoint. If no errors occur, SQLITE_OK is returned.
  6657   6657   */ 
  6658   6658   int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
  6659         -  int rc = pPager->errCode;       /* Return code */
         6659  +  int rc = pPager->errCode;
         6660  +  
         6661  +#ifdef SQLITE_ENABLE_ZIPVFS
         6662  +  if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK;
         6663  +#endif
  6660   6664   
  6661   6665     assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
  6662   6666     assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
  6663   6667   
  6664   6668     if( rc==SQLITE_OK && iSavepoint<pPager->nSavepoint ){
  6665   6669       int ii;            /* Iterator variable */
  6666   6670       int nNew;          /* Number of remaining savepoints after this op. */
................................................................................
  6693   6697       ** the database file, so the playback operation can be skipped.
  6694   6698       */
  6695   6699       else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){
  6696   6700         PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1];
  6697   6701         rc = pagerPlaybackSavepoint(pPager, pSavepoint);
  6698   6702         assert(rc!=SQLITE_DONE);
  6699   6703       }
         6704  +    
         6705  +#ifdef SQLITE_ENABLE_ZIPVFS
         6706  +    /* If the cache has been modified but the savepoint cannot be rolled 
         6707  +    ** back journal_mode=off, put the pager in the error state. This way,
         6708  +    ** if the VFS used by this pager includes ZipVFS, the entire transaction
         6709  +    ** can be rolled back at the ZipVFS level.  */
         6710  +    else if( 
         6711  +        pPager->journalMode==PAGER_JOURNALMODE_OFF 
         6712  +     && pPager->eState>=PAGER_WRITER_CACHEMOD
         6713  +    ){
         6714  +      pPager->errCode = SQLITE_ABORT;
         6715  +      pPager->eState = PAGER_ERROR;
         6716  +    }
         6717  +#endif
  6700   6718     }
  6701   6719   
  6702   6720     return rc;
  6703   6721   }
  6704   6722   
  6705   6723   /*
  6706   6724   ** Return the full pathname of the database file.

Changes to src/treeview.c.

   116    116       }
   117    117       sqlite3TreeViewPop(pView);
   118    118     }
   119    119   }
   120    120   
   121    121   
   122    122   /*
   123         -** Generate a human-readable description of a the Select object.
          123  +** Generate a human-readable description of a Select object.
   124    124   */
   125    125   void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
   126    126     int n = 0;
   127    127     int cnt = 0;
   128    128     pView = sqlite3TreeViewPush(pView, moreToFollow);
   129    129     if( p->pWith ){
   130    130       sqlite3TreeViewWith(pView, p->pWith, 1);

Changes to src/vtab.c.

   668    668     sqlite3VtabLock(pVTab);
   669    669   }
   670    670   
   671    671   /*
   672    672   ** This function is invoked by the vdbe to call the xCreate method
   673    673   ** of the virtual table named zTab in database iDb. 
   674    674   **
   675         -** If an error occurs, *pzErr is set to point an an English language
          675  +** If an error occurs, *pzErr is set to point to an English language
   676    676   ** description of the error and an SQLITE_XXX error code is returned.
   677    677   ** In this case the caller must call sqlite3DbFree(db, ) on *pzErr.
   678    678   */
   679    679   int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
   680    680     int rc = SQLITE_OK;
   681    681     Table *pTab;
   682    682     Module *pMod;

Changes to test/backup_malloc.test.

    79     79     if {$rc && ($errcode == "SQLITE_NOMEM" || $errcode == "SQLITE_IOERR_NOMEM")} {
    80     80       error "out of memory"
    81     81     }
    82     82   } -cleanup {
    83     83     catch { B finish }
    84     84     db2 close
    85     85   }
           86  +
           87  +reset_db
           88  +do_execsql_test 3.0 {
           89  +  PRAGMA page_size = 16384;
           90  +  BEGIN;
           91  +  CREATE TABLE t1(a, b);
           92  +  INSERT INTO t1 VALUES(1, 2);
           93  +  COMMIT;
           94  +}
           95  +
           96  +do_faultsim_test 3 -faults oom* -prep {
           97  +  catch { db close }
           98  +
           99  +  forcedelete test2.db
          100  +  sqlite3 db2 test2.db
          101  +  sqlite3 db test.db
          102  +  sqlite3_backup B db2 main db main
          103  +} -body {
          104  +
          105  +  set rc [B step 50]
          106  +  if {$rc == "SQLITE_NOMEM" || $rc == "SQLITE_IOERR_NOMEM"} {
          107  +    error "out of memory"
          108  +  }
          109  +
          110  +} -test {
          111  +  faultsim_test_result {0 {}} 
          112  +  faultsim_integrity_check
          113  +  
          114  +  # Finalize the backup.
          115  +  catch { B finish }
          116  +}
    86    117   
    87    118   finish_test

Changes to tool/sqldiff.c.

  1175   1175     strPrintf(pSql, "SELECT ");
  1176   1176     strPrintfArray(pSql, ", ", "%s", azCol, -1);
  1177   1177     strPrintf(pSql, ", 0, ");       /* Set ota_control to 0 for an insert */
  1178   1178     strPrintfArray(pSql, ", ", "NULL", azCol, -1);
  1179   1179     strPrintf(pSql, " FROM aux.%Q AS n WHERE NOT EXISTS (\n", zTab);
  1180   1180     strPrintf(pSql, "    SELECT 1 FROM ", zTab);
  1181   1181     strPrintf(pSql, " main.%Q AS o WHERE ", zTab);
  1182         -  strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
  1183         -  strPrintf(pSql, "\n)");
         1182  +  strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
         1183  +  strPrintf(pSql, "\n) AND ");
         1184  +  strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
  1184   1185   
  1185   1186     /* Deleted rows: */
  1186   1187     strPrintf(pSql, "\nUNION ALL\nSELECT ");
  1187   1188     strPrintfArray(pSql, ", ", "%s", azCol, nPK);
  1188   1189     if( azCol[nPK] ){
  1189   1190       strPrintf(pSql, ", ");
  1190   1191       strPrintfArray(pSql, ", ", "NULL", &azCol[nPK], -1);
  1191   1192     }
  1192   1193     strPrintf(pSql, ", 1, ");       /* Set ota_control to 1 for a delete */
  1193   1194     strPrintfArray(pSql, ", ", "NULL", azCol, -1);
  1194   1195     strPrintf(pSql, " FROM main.%Q AS n WHERE NOT EXISTS (\n", zTab);
  1195   1196     strPrintf(pSql, "    SELECT 1 FROM ", zTab);
  1196   1197     strPrintf(pSql, " aux.%Q AS o WHERE ", zTab);
  1197         -  strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
  1198         -  strPrintf(pSql, "\n) ");
         1198  +  strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
         1199  +  strPrintf(pSql, "\n) AND ");
         1200  +  strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
  1199   1201   
  1200   1202     /* Updated rows. If all table columns are part of the primary key, there 
  1201   1203     ** can be no updates. In this case this part of the compound SELECT can
  1202   1204     ** be omitted altogether. */
  1203   1205     if( azCol[nPK] ){
  1204   1206       strPrintf(pSql, "\nUNION ALL\nSELECT ");
  1205   1207       strPrintfArray(pSql, ", ", "n.%s", azCol, nPK);
................................................................................
  1222   1224       strPrintfArray(pSql, ", ", "NULL", azCol, nPK);
  1223   1225       strPrintf(pSql, ",\n");
  1224   1226       strPrintfArray(pSql, " ,\n", 
  1225   1227           "    CASE WHEN n.%s IS o.%s THEN NULL ELSE o.%s END", &azCol[nPK], -1
  1226   1228       );
  1227   1229   
  1228   1230       strPrintf(pSql, "\nFROM main.%Q AS o, aux.%Q AS n\nWHERE ", zTab, zTab);
  1229         -    strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
         1231  +    strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
  1230   1232       strPrintf(pSql, " AND ota_control LIKE '%%x%%'");
  1231   1233     }
  1232   1234   
  1233   1235     /* Now add an ORDER BY clause to sort everything by PK. */
  1234   1236     strPrintf(pSql, "\nORDER BY ");
  1235   1237     for(i=1; i<=nPK; i++) strPrintf(pSql, "%s%d", ((i>1)?", ":""), i);
  1236   1238   }