]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge latest trunk changes with this branch. Add tests for columnsize=0.
authordan <dan@noemail.net>
Tue, 23 Jun 2015 15:06:13 +0000 (15:06 +0000)
committerdan <dan@noemail.net>
Tue, 23 Jun 2015 15:06:13 +0000 (15:06 +0000)
FossilOrigin-Name: ef44c71a22518727030dd90c0139af8973b05841

1  2 
Makefile.in
ext/fts5/fts5_storage.c
ext/fts5/test/fts5columnsize.test
ext/fts5/test/fts5fault4.test
main.mk
manifest
manifest.uuid
src/main.c
test/permutations.test
tool/mksqlite3c.tcl

diff --cc Makefile.in
Simple merge
index 588f98147db9474d891138c9fbdbb292c06ffb41,0000000000000000000000000000000000000000..60c64017160433fc246c6224ae0cfe49e4cace22
mode 100644,000000..100644
--- /dev/null
@@@ -1,1093 -1,0 +1,1093 @@@
-   }
-   if( rc==SQLITE_OK ){
-     sqlite3_bind_int64(pDel, 1, iDel);
-     sqlite3_step(pDel);
-     rc = sqlite3_reset(pDel);
 +/*
 +** 2014 May 31
 +**
 +** The author disclaims copyright to this source code.  In place of
 +** a legal notice, here is a blessing:
 +**
 +**    May you do good and not evil.
 +**    May you find forgiveness for yourself and forgive others.
 +**    May you share freely, never taking more than you give.
 +**
 +******************************************************************************
 +**
 +*/
 +
 +#ifdef SQLITE_ENABLE_FTS5
 +
 +
 +#include "fts5Int.h"
 +
 +struct Fts5Storage {
 +  Fts5Config *pConfig;
 +  Fts5Index *pIndex;
 +  int bTotalsValid;               /* True if nTotalRow/aTotalSize[] are valid */
 +  i64 nTotalRow;                  /* Total number of rows in FTS table */
 +  i64 *aTotalSize;                /* Total sizes of each column */ 
 +  sqlite3_stmt *aStmt[11];
 +};
 +
 +
 +#if FTS5_STMT_SCAN_ASC!=0 
 +# error "FTS5_STMT_SCAN_ASC mismatch" 
 +#endif
 +#if FTS5_STMT_SCAN_DESC!=1 
 +# error "FTS5_STMT_SCAN_DESC mismatch" 
 +#endif
 +#if FTS5_STMT_LOOKUP!=2
 +# error "FTS5_STMT_LOOKUP mismatch" 
 +#endif
 +
 +#define FTS5_STMT_INSERT_CONTENT  3
 +#define FTS5_STMT_REPLACE_CONTENT 4
 +#define FTS5_STMT_DELETE_CONTENT  5
 +#define FTS5_STMT_REPLACE_DOCSIZE  6
 +#define FTS5_STMT_DELETE_DOCSIZE  7
 +#define FTS5_STMT_LOOKUP_DOCSIZE  8
 +#define FTS5_STMT_REPLACE_CONFIG 9
 +#define FTS5_STMT_SCAN 10
 +
 +/*
 +** Prepare the two insert statements - Fts5Storage.pInsertContent and
 +** Fts5Storage.pInsertDocsize - if they have not already been prepared.
 +** Return SQLITE_OK if successful, or an SQLite error code if an error
 +** occurs.
 +*/
 +static int fts5StorageGetStmt(
 +  Fts5Storage *p,                 /* Storage handle */
 +  int eStmt,                      /* FTS5_STMT_XXX constant */
 +  sqlite3_stmt **ppStmt,          /* OUT: Prepared statement handle */
 +  char **pzErrMsg                 /* OUT: Error message (if any) */
 +){
 +  int rc = SQLITE_OK;
 +
 +  /* If there is no %_docsize table, there should be no requests for 
 +  ** statements to operate on it.  */
 +  assert( p->pConfig->bColumnsize || (
 +        eStmt!=FTS5_STMT_REPLACE_DOCSIZE 
 +     && eStmt!=FTS5_STMT_DELETE_DOCSIZE 
 +     && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE 
 +  ));
 +
 +  assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
 +  if( p->aStmt[eStmt]==0 ){
 +    const char *azStmt[] = {
 +      "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC",
 +      "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC",
 +      "SELECT %s FROM %s T WHERE T.%Q=?",               /* LOOKUP  */
 +
 +      "INSERT INTO %Q.'%q_content' VALUES(%s)",         /* INSERT_CONTENT  */
 +      "REPLACE INTO %Q.'%q_content' VALUES(%s)",        /* REPLACE_CONTENT */
 +      "DELETE FROM %Q.'%q_content' WHERE id=?",         /* DELETE_CONTENT  */
 +      "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)",       /* REPLACE_DOCSIZE  */
 +      "DELETE FROM %Q.'%q_docsize' WHERE id=?",         /* DELETE_DOCSIZE  */
 +
 +      "SELECT sz FROM %Q.'%q_docsize' WHERE id=?",      /* LOOKUP_DOCSIZE  */
 +
 +      "REPLACE INTO %Q.'%q_config' VALUES(?,?)",        /* REPLACE_CONFIG */
 +      "SELECT %s FROM %s AS T",                         /* SCAN */
 +    };
 +    Fts5Config *pC = p->pConfig;
 +    char *zSql = 0;
 +
 +    switch( eStmt ){
 +      case FTS5_STMT_SCAN:
 +        zSql = sqlite3_mprintf(azStmt[eStmt], 
 +            pC->zContentExprlist, pC->zContent
 +        );
 +        break;
 +
 +      case FTS5_STMT_SCAN_ASC:
 +      case FTS5_STMT_SCAN_DESC:
 +        zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, 
 +            pC->zContent, pC->zContentRowid, pC->zContentRowid,
 +            pC->zContentRowid
 +        );
 +        break;
 +
 +      case FTS5_STMT_LOOKUP:
 +        zSql = sqlite3_mprintf(azStmt[eStmt], 
 +            pC->zContentExprlist, pC->zContent, pC->zContentRowid
 +        );
 +        break;
 +
 +      case FTS5_STMT_INSERT_CONTENT: 
 +      case FTS5_STMT_REPLACE_CONTENT: {
 +        int nCol = pC->nCol + 1;
 +        char *zBind;
 +        int i;
 +
 +        zBind = sqlite3_malloc(1 + nCol*2);
 +        if( zBind ){
 +          for(i=0; i<nCol; i++){
 +            zBind[i*2] = '?';
 +            zBind[i*2 + 1] = ',';
 +          }
 +          zBind[i*2-1] = '\0';
 +          zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind);
 +          sqlite3_free(zBind);
 +        }
 +        break;
 +      }
 +
 +      default:
 +        zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
 +        break;
 +    }
 +
 +    if( zSql==0 ){
 +      rc = SQLITE_NOMEM;
 +    }else{
 +      rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0);
 +      sqlite3_free(zSql);
 +      if( rc!=SQLITE_OK && pzErrMsg ){
 +        *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
 +      }
 +    }
 +  }
 +
 +  *ppStmt = p->aStmt[eStmt];
 +  return rc;
 +}
 +
 +
 +static int fts5ExecPrintf(
 +  sqlite3 *db,
 +  char **pzErr,
 +  const char *zFormat,
 +  ...
 +){
 +  int rc;
 +  va_list ap;                     /* ... printf arguments */
 +  va_start(ap, zFormat);
 +  char *zSql = sqlite3_vmprintf(zFormat, ap);
 +
 +  if( zSql==0 ){
 +    rc = SQLITE_NOMEM;
 +  }else{
 +    rc = sqlite3_exec(db, zSql, 0, 0, pzErr);
 +    sqlite3_free(zSql);
 +  }
 +
 +  va_end(ap);
 +  return rc;
 +}
 +
 +/*
 +** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
 +** code otherwise.
 +*/
 +int sqlite3Fts5DropAll(Fts5Config *pConfig){
 +  int rc = fts5ExecPrintf(pConfig->db, 0, 
 +      "DROP TABLE IF EXISTS %Q.'%q_data';"
 +      "DROP TABLE IF EXISTS %Q.'%q_config';",
 +      pConfig->zDb, pConfig->zName,
 +      pConfig->zDb, pConfig->zName
 +  );
 +  if( rc==SQLITE_OK && pConfig->bColumnsize ){
 +    rc = fts5ExecPrintf(pConfig->db, 0, 
 +        "DROP TABLE IF EXISTS %Q.'%q_docsize';",
 +        pConfig->zDb, pConfig->zName
 +    );
 +  }
 +  if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
 +    rc = fts5ExecPrintf(pConfig->db, 0, 
 +        "DROP TABLE IF EXISTS %Q.'%q_content';",
 +        pConfig->zDb, pConfig->zName
 +    );
 +  }
 +  return rc;
 +}
 +
 +static void fts5StorageRenameOne(
 +  Fts5Config *pConfig,            /* Current FTS5 configuration */
 +  int *pRc,                       /* IN/OUT: Error code */
 +  const char *zTail,              /* Tail of table name e.g. "data", "config" */
 +  const char *zName               /* New name of FTS5 table */
 +){
 +  if( *pRc==SQLITE_OK ){
 +    *pRc = fts5ExecPrintf(pConfig->db, 0, 
 +        "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
 +        pConfig->zDb, pConfig->zName, zTail, zName, zTail
 +    );
 +  }
 +}
 +
 +int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){
 +  Fts5Config *pConfig = pStorage->pConfig;
 +  int rc = sqlite3Fts5StorageSync(pStorage, 1);
 +
 +  fts5StorageRenameOne(pConfig, &rc, "data", zName);
 +  fts5StorageRenameOne(pConfig, &rc, "config", zName);
 +  if( pConfig->bColumnsize ){
 +    fts5StorageRenameOne(pConfig, &rc, "docsize", zName);
 +  }
 +  if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
 +    fts5StorageRenameOne(pConfig, &rc, "content", zName);
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Create the shadow table named zPost, with definition zDefn. Return
 +** SQLITE_OK if successful, or an SQLite error code otherwise.
 +*/
 +int sqlite3Fts5CreateTable(
 +  Fts5Config *pConfig,            /* FTS5 configuration */
 +  const char *zPost,              /* Shadow table to create (e.g. "content") */
 +  const char *zDefn,              /* Columns etc. for shadow table */
 +  int bWithout,                   /* True for without rowid */
 +  char **pzErr                    /* OUT: Error message */
 +){
 +  int rc;
 +  char *zErr = 0;
 +
 +  rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s",
 +      pConfig->zDb, pConfig->zName, zPost, zDefn, bWithout?" WITHOUT ROWID":""
 +  );
 +  if( zErr ){
 +    *pzErr = sqlite3_mprintf(
 +        "fts5: error creating shadow table %q_%s: %s", 
 +        pConfig->zName, zPost, zErr
 +    );
 +    sqlite3_free(zErr);
 +  }
 +
 +  return rc;
 +}
 +
 +/*
 +** Open a new Fts5Index handle. If the bCreate argument is true, create
 +** and initialize the underlying tables 
 +**
 +** If successful, set *pp to point to the new object and return SQLITE_OK.
 +** Otherwise, set *pp to NULL and return an SQLite error code.
 +*/
 +int sqlite3Fts5StorageOpen(
 +  Fts5Config *pConfig, 
 +  Fts5Index *pIndex, 
 +  int bCreate, 
 +  Fts5Storage **pp,
 +  char **pzErr                    /* OUT: Error message */
 +){
 +  int rc = SQLITE_OK;
 +  Fts5Storage *p;                 /* New object */
 +  int nByte;                      /* Bytes of space to allocate */
 +
 +  nByte = sizeof(Fts5Storage)               /* Fts5Storage object */
 +        + pConfig->nCol * sizeof(i64);      /* Fts5Storage.aTotalSize[] */
 +  *pp = p = (Fts5Storage*)sqlite3_malloc(nByte);
 +  if( !p ) return SQLITE_NOMEM;
 +
 +  memset(p, 0, nByte);
 +  p->aTotalSize = (i64*)&p[1];
 +  p->pConfig = pConfig;
 +  p->pIndex = pIndex;
 +
 +  if( bCreate ){
 +    if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
 +      int nDefn = 32 + pConfig->nCol*10;
 +      char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
 +      if( zDefn==0 ){
 +        rc = SQLITE_NOMEM;
 +      }else{
 +        int i;
 +        int iOff;
 +        sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY");
 +        iOff = strlen(zDefn);
 +        for(i=0; i<pConfig->nCol; i++){
 +          sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
 +          iOff += strlen(&zDefn[iOff]);
 +        }
 +        rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
 +      }
 +      sqlite3_free(zDefn);
 +    }
 +
 +    if( rc==SQLITE_OK && pConfig->bColumnsize ){
 +      rc = sqlite3Fts5CreateTable(
 +          pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
 +      );
 +    }
 +    if( rc==SQLITE_OK ){
 +      rc = sqlite3Fts5CreateTable(
 +          pConfig, "config", "k PRIMARY KEY, v", 1, pzErr
 +      );
 +    }
 +    if( rc==SQLITE_OK ){
 +      rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
 +    }
 +  }
 +
 +  if( rc ){
 +    sqlite3Fts5StorageClose(p);
 +    *pp = 0;
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
 +*/
 +int sqlite3Fts5StorageClose(Fts5Storage *p){
 +  int rc = SQLITE_OK;
 +  if( p ){
 +    int i;
 +
 +    /* Finalize all SQL statements */
 +    for(i=0; i<ArraySize(p->aStmt); i++){
 +      sqlite3_finalize(p->aStmt[i]);
 +    }
 +
 +    sqlite3_free(p);
 +  }
 +  return rc;
 +}
 +
 +typedef struct Fts5InsertCtx Fts5InsertCtx;
 +struct Fts5InsertCtx {
 +  Fts5Storage *pStorage;
 +  int iCol;
 +  int szCol;                      /* Size of column value in tokens */
 +};
 +
 +/*
 +** Tokenization callback used when inserting tokens into the FTS index.
 +*/
 +static int fts5StorageInsertCallback(
 +  void *pContext,                 /* Pointer to Fts5InsertCtx object */
 +  const char *pToken,             /* Buffer containing token */
 +  int nToken,                     /* Size of token in bytes */
 +  int iStart,                     /* Start offset of token */
 +  int iEnd                        /* End offset of token */
 +){
 +  Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext;
 +  Fts5Index *pIdx = pCtx->pStorage->pIndex;
 +  int iPos = pCtx->szCol++;
 +  return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, iPos, pToken, nToken);
 +}
 +
 +/*
 +** If a row with rowid iDel is present in the %_content table, add the
 +** delete-markers to the FTS index necessary to delete it. Do not actually
 +** remove the %_content row at this time though.
 +*/
 +static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){
 +  Fts5Config *pConfig = p->pConfig;
 +  sqlite3_stmt *pSeek;            /* SELECT to read row iDel from %_data */
 +  int rc;                         /* Return code */
 +
 +  rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
 +  if( rc==SQLITE_OK ){
 +    int rc2;
 +    sqlite3_bind_int64(pSeek, 1, iDel);
 +    if( sqlite3_step(pSeek)==SQLITE_ROW ){
 +      int iCol;
 +      Fts5InsertCtx ctx;
 +      ctx.pStorage = p;
 +      ctx.iCol = -1;
 +      rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
 +      for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
 +        if( pConfig->abUnindexed[iCol-1] ) continue;
 +        ctx.szCol = 0;
 +        rc = sqlite3Fts5Tokenize(pConfig, 
 +            (const char*)sqlite3_column_text(pSeek, iCol),
 +            sqlite3_column_bytes(pSeek, iCol),
 +            (void*)&ctx,
 +            fts5StorageInsertCallback
 +        );
 +        p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
 +      }
 +      p->nTotalRow--;
 +    }
 +    rc2 = sqlite3_reset(pSeek);
 +    if( rc==SQLITE_OK ) rc = rc2;
 +  }
 +
 +  return rc;
 +}
 +
 +
 +/*
 +** Insert a record into the %_docsize table. Specifically, do:
 +**
 +**   INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
 +**
 +** If there is no %_docsize table (as happens if the columnsize=0 option
 +** is specified when the FTS5 table is created), this function is a no-op.
 +*/
 +static int fts5StorageInsertDocsize(
 +  Fts5Storage *p,                 /* Storage module to write to */
 +  i64 iRowid,                     /* id value */
 +  Fts5Buffer *pBuf                /* sz value */
 +){
 +  int rc = SQLITE_OK;
 +  if( p->pConfig->bColumnsize ){
 +    sqlite3_stmt *pReplace = 0;
 +    rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
 +    if( rc==SQLITE_OK ){
 +      sqlite3_bind_int64(pReplace, 1, iRowid);
 +      sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
 +      sqlite3_step(pReplace);
 +      rc = sqlite3_reset(pReplace);
 +    }
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Load the contents of the "averages" record from disk into the 
 +** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
 +** argument bCache is true, set the p->bTotalsValid flag to indicate
 +** that the contents of aTotalSize[] and nTotalRow are valid until
 +** further notice.
 +**
 +** Return SQLITE_OK if successful, or an SQLite error code if an error
 +** occurs.
 +*/
 +static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){
 +  int rc = SQLITE_OK;
 +  if( p->bTotalsValid==0 ){
 +    int nCol = p->pConfig->nCol;
 +    Fts5Buffer buf;
 +    memset(&buf, 0, sizeof(buf));
 +
 +    memset(p->aTotalSize, 0, sizeof(i64) * nCol);
 +    p->nTotalRow = 0;
 +    rc = sqlite3Fts5IndexGetAverages(p->pIndex, &buf);
 +    if( rc==SQLITE_OK && buf.n ){
 +      int i = 0;
 +      int iCol;
 +      i += fts5GetVarint(&buf.p[i], (u64*)&p->nTotalRow);
 +      for(iCol=0; i<buf.n && iCol<nCol; iCol++){
 +        i += fts5GetVarint(&buf.p[i], (u64*)&p->aTotalSize[iCol]);
 +      }
 +    }
 +    sqlite3_free(buf.p);
 +    p->bTotalsValid = bCache;
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Store the current contents of the p->nTotalRow and p->aTotalSize[] 
 +** variables in the "averages" record on disk.
 +**
 +** Return SQLITE_OK if successful, or an SQLite error code if an error
 +** occurs.
 +*/
 +static int fts5StorageSaveTotals(Fts5Storage *p){
 +  int nCol = p->pConfig->nCol;
 +  int i;
 +  Fts5Buffer buf;
 +  int rc = SQLITE_OK;
 +  memset(&buf, 0, sizeof(buf));
 +
 +  sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow);
 +  for(i=0; i<nCol; i++){
 +    sqlite3Fts5BufferAppendVarint(&rc, &buf, p->aTotalSize[i]);
 +  }
 +  if( rc==SQLITE_OK ){
 +    rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n);
 +  }
 +  sqlite3_free(buf.p);
 +
 +  return rc;
 +}
 +
 +/*
 +** Remove a row from the FTS table.
 +*/
 +int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){
 +  Fts5Config *pConfig = p->pConfig;
 +  int rc;
 +  sqlite3_stmt *pDel;
 +
 +  rc = fts5StorageLoadTotals(p, 1);
 +
 +  /* Delete the index records */
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageDeleteFromIndex(p, iDel);
 +  }
 +
 +  /* Delete the %_docsize record */
 +  if( rc==SQLITE_OK && pConfig->bColumnsize ){
 +    rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
++    if( rc==SQLITE_OK ){
++      sqlite3_bind_int64(pDel, 1, iDel);
++      sqlite3_step(pDel);
++      rc = sqlite3_reset(pDel);
++    }
 +  }
 +
 +  /* Delete the %_content record */
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0);
 +  }
 +  if( rc==SQLITE_OK ){
 +    sqlite3_bind_int64(pDel, 1, iDel);
 +    sqlite3_step(pDel);
 +    rc = sqlite3_reset(pDel);
 +  }
 +
 +  /* Write the averages record */
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageSaveTotals(p);
 +  }
 +
 +  return rc;
 +}
 +
 +int sqlite3Fts5StorageSpecialDelete(
 +  Fts5Storage *p, 
 +  i64 iDel, 
 +  sqlite3_value **apVal
 +){
 +  Fts5Config *pConfig = p->pConfig;
 +  int rc;
 +  sqlite3_stmt *pDel;
 +
 +  assert( pConfig->eContent!=FTS5_CONTENT_NORMAL );
 +  rc = fts5StorageLoadTotals(p, 1);
 +
 +  /* Delete the index records */
 +  if( rc==SQLITE_OK ){
 +    int iCol;
 +    Fts5InsertCtx ctx;
 +    ctx.pStorage = p;
 +    ctx.iCol = -1;
 +
 +    rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
 +    for(iCol=0; rc==SQLITE_OK && iCol<pConfig->nCol; iCol++){
 +      if( pConfig->abUnindexed[iCol] ) continue;
 +      ctx.szCol = 0;
 +      rc = sqlite3Fts5Tokenize(pConfig, 
 +        (const char*)sqlite3_value_text(apVal[iCol]),
 +        sqlite3_value_bytes(apVal[iCol]),
 +        (void*)&ctx,
 +        fts5StorageInsertCallback
 +      );
 +      p->aTotalSize[iCol] -= (i64)ctx.szCol;
 +    }
 +    p->nTotalRow--;
 +  }
 +
 +  /* Delete the %_docsize record */
 +  if( pConfig->bColumnsize ){
 +    if( rc==SQLITE_OK ){
 +      rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
 +    }
 +    if( rc==SQLITE_OK ){
 +      sqlite3_bind_int64(pDel, 1, iDel);
 +      sqlite3_step(pDel);
 +      rc = sqlite3_reset(pDel);
 +    }
 +  }
 +
 +  /* Write the averages record */
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageSaveTotals(p);
 +  }
 +
 +  return rc;
 +}
 +
 +/*
 +** Delete all entries in the FTS5 index.
 +*/
 +int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
 +  Fts5Config *pConfig = p->pConfig;
 +  int rc;
 +
 +  /* Delete the contents of the %_data and %_docsize tables. */
 +  rc = fts5ExecPrintf(pConfig->db, 0,
 +      "DELETE FROM %Q.'%q_data';",
 +      pConfig->zDb, pConfig->zName
 +  );
 +  if( rc==SQLITE_OK && pConfig->bColumnsize ){
 +    rc = fts5ExecPrintf(pConfig->db, 0,
 +        "DELETE FROM %Q.'%q_docsize';",
 +        pConfig->zDb, pConfig->zName
 +    );
 +  }
 +
 +  /* Reinitialize the %_data table. This call creates the initial structure
 +  ** and averages records.  */
 +  if( rc==SQLITE_OK ){
 +    rc = sqlite3Fts5IndexReinit(p->pIndex);
 +  }
 +  if( rc==SQLITE_OK ){
 +    rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
 +  }
 +  return rc;
 +}
 +
 +int sqlite3Fts5StorageRebuild(Fts5Storage *p){
 +  Fts5Buffer buf = {0,0,0};
 +  Fts5Config *pConfig = p->pConfig;
 +  sqlite3_stmt *pScan = 0;
 +  Fts5InsertCtx ctx;
 +  int rc;
 +
 +  memset(&ctx, 0, sizeof(Fts5InsertCtx));
 +  ctx.pStorage = p;
 +  rc = sqlite3Fts5StorageDeleteAll(p);
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageLoadTotals(p, 1);
 +  }
 +
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
 +  }
 +
 +  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
 +    i64 iRowid = sqlite3_column_int64(pScan, 0);
 +
 +    sqlite3Fts5BufferZero(&buf);
 +    rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iRowid);
 +    for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
 +      ctx.szCol = 0;
 +      if( pConfig->abUnindexed[ctx.iCol]==0 ){
 +        rc = sqlite3Fts5Tokenize(pConfig, 
 +            (const char*)sqlite3_column_text(pScan, ctx.iCol+1),
 +            sqlite3_column_bytes(pScan, ctx.iCol+1),
 +            (void*)&ctx,
 +            fts5StorageInsertCallback
 +        );
 +      }
 +      sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
 +      p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
 +    }
 +    p->nTotalRow++;
 +
 +    if( rc==SQLITE_OK ){
 +      rc = fts5StorageInsertDocsize(p, iRowid, &buf);
 +    }
 +  }
 +  sqlite3_free(buf.p);
 +
 +  /* Write the averages record */
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageSaveTotals(p);
 +  }
 +  return rc;
 +}
 +
 +int sqlite3Fts5StorageOptimize(Fts5Storage *p){
 +  return sqlite3Fts5IndexOptimize(p->pIndex);
 +}
 +
 +int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){
 +  return sqlite3Fts5IndexMerge(p->pIndex, nMerge);
 +}
 +
 +/*
 +** Allocate a new rowid. This is used for "external content" tables when
 +** a NULL value is inserted into the rowid column. The new rowid is allocated
 +** by inserting a dummy row into the %_docsize table. The dummy will be
 +** overwritten later.
 +**
 +** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
 +** this case the user is required to provide a rowid explicitly.
 +*/
 +static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
 +  int rc = SQLITE_MISMATCH;
 +  if( p->pConfig->bColumnsize ){
 +    sqlite3_stmt *pReplace = 0;
 +    rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
 +    if( rc==SQLITE_OK ){
 +      sqlite3_bind_null(pReplace, 1);
 +      sqlite3_bind_null(pReplace, 2);
 +      sqlite3_step(pReplace);
 +      rc = sqlite3_reset(pReplace);
 +    }
 +    if( rc==SQLITE_OK ){
 +      *piRowid = sqlite3_last_insert_rowid(p->pConfig->db);
 +    }
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Insert a new row into the FTS table.
 +*/
 +int sqlite3Fts5StorageInsert(
 +  Fts5Storage *p,                 /* Storage module to write to */
 +  sqlite3_value **apVal,          /* Array of values passed to xUpdate() */
 +  int eConflict,                  /* on conflict clause */
 +  i64 *piRowid                    /* OUT: rowid of new record */
 +){
 +  Fts5Config *pConfig = p->pConfig;
 +  int rc = SQLITE_OK;             /* Return code */
 +  sqlite3_stmt *pInsert;          /* Statement used to write %_content table */
 +  int eStmt = 0;                  /* Type of statement used on %_content */
 +  int i;                          /* Counter variable */
 +  Fts5InsertCtx ctx;              /* Tokenization callback context object */
 +  Fts5Buffer buf;                 /* Buffer used to build up %_docsize blob */
 +
 +  memset(&buf, 0, sizeof(Fts5Buffer));
 +  rc = fts5StorageLoadTotals(p, 1);
 +
 +  /* Insert the new row into the %_content table. */
 +  if( rc==SQLITE_OK ){
 +    if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
 +      if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
 +        *piRowid = sqlite3_value_int64(apVal[1]);
 +      }else{
 +        rc = fts5StorageNewRowid(p, piRowid);
 +      }
 +    }else{
 +      if( eConflict==SQLITE_REPLACE ){
 +        eStmt = FTS5_STMT_REPLACE_CONTENT;
 +        rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1]));
 +      }else{
 +        eStmt = FTS5_STMT_INSERT_CONTENT;
 +      }
 +      if( rc==SQLITE_OK ){
 +        rc = fts5StorageGetStmt(p, eStmt, &pInsert, 0);
 +      }
 +      for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
 +        rc = sqlite3_bind_value(pInsert, i, apVal[i]);
 +      }
 +      if( rc==SQLITE_OK ){
 +        sqlite3_step(pInsert);
 +        rc = sqlite3_reset(pInsert);
 +      }
 +      *piRowid = sqlite3_last_insert_rowid(pConfig->db);
 +    }
 +  }
 +
 +  /* Add new entries to the FTS index */
 +  if( rc==SQLITE_OK ){
 +    rc = sqlite3Fts5IndexBeginWrite(p->pIndex, *piRowid);
 +    ctx.pStorage = p;
 +  }
 +  for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
 +    ctx.szCol = 0;
 +    if( pConfig->abUnindexed[ctx.iCol]==0 ){
 +      rc = sqlite3Fts5Tokenize(pConfig, 
 +          (const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
 +          sqlite3_value_bytes(apVal[ctx.iCol+2]),
 +          (void*)&ctx,
 +          fts5StorageInsertCallback
 +      );
 +    }
 +    sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
 +    p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
 +  }
 +  p->nTotalRow++;
 +
 +  /* Write the %_docsize record */
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageInsertDocsize(p, *piRowid, &buf);
 +  }
 +  sqlite3_free(buf.p);
 +
 +  /* Write the averages record */
 +  if( rc==SQLITE_OK ){
 +    rc = fts5StorageSaveTotals(p);
 +  }
 +
 +  return rc;
 +}
 +
 +static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){
 +  Fts5Config *pConfig = p->pConfig;
 +  char *zSql;
 +  int rc;
 +
 +  zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'", 
 +      pConfig->zDb, pConfig->zName, zSuffix
 +  );
 +  if( zSql==0 ){
 +    rc = SQLITE_NOMEM;
 +  }else{
 +    sqlite3_stmt *pCnt = 0;
 +    rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pCnt, 0);
 +    if( rc==SQLITE_OK ){
 +      if( SQLITE_ROW==sqlite3_step(pCnt) ){
 +        *pnRow = sqlite3_column_int64(pCnt, 0);
 +      }
 +      rc = sqlite3_finalize(pCnt);
 +    }
 +  }
 +
 +  sqlite3_free(zSql);
 +  return rc;
 +}
 +
 +/*
 +** Context object used by sqlite3Fts5StorageIntegrity().
 +*/
 +typedef struct Fts5IntegrityCtx Fts5IntegrityCtx;
 +struct Fts5IntegrityCtx {
 +  i64 iRowid;
 +  int iCol;
 +  int szCol;
 +  u64 cksum;
 +  Fts5Config *pConfig;
 +};
 +
 +/*
 +** Tokenization callback used by integrity check.
 +*/
 +static int fts5StorageIntegrityCallback(
 +  void *pContext,                 /* Pointer to Fts5InsertCtx object */
 +  const char *pToken,             /* Buffer containing token */
 +  int nToken,                     /* Size of token in bytes */
 +  int iStart,                     /* Start offset of token */
 +  int iEnd                        /* End offset of token */
 +){
 +  Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext;
 +  int iPos = pCtx->szCol++;
 +  pCtx->cksum ^= sqlite3Fts5IndexCksum(
 +      pCtx->pConfig, pCtx->iRowid, pCtx->iCol, iPos, pToken, nToken
 +  );
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Check that the contents of the FTS index match that of the %_content
 +** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
 +** some other SQLite error code if an error occurs while attempting to
 +** determine this.
 +*/
 +int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
 +  Fts5Config *pConfig = p->pConfig;
 +  int rc;                         /* Return code */
 +  int *aColSize;                  /* Array of size pConfig->nCol */
 +  i64 *aTotalSize;                /* Array of size pConfig->nCol */
 +  Fts5IntegrityCtx ctx;
 +  sqlite3_stmt *pScan;
 +
 +  memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
 +  ctx.pConfig = p->pConfig;
 +  aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64)));
 +  if( !aTotalSize ) return SQLITE_NOMEM;
 +  aColSize = (int*)&aTotalSize[pConfig->nCol];
 +  memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
 +
 +  /* Generate the expected index checksum based on the contents of the
 +  ** %_content table. This block stores the checksum in ctx.cksum. */
 +  rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
 +  if( rc==SQLITE_OK ){
 +    int rc2;
 +    while( SQLITE_ROW==sqlite3_step(pScan) ){
 +      int i;
 +      ctx.iRowid = sqlite3_column_int64(pScan, 0);
 +      ctx.szCol = 0;
 +      rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
 +      for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
 +        if( pConfig->abUnindexed[i] ) continue;
 +        ctx.iCol = i;
 +        ctx.szCol = 0;
 +        rc = sqlite3Fts5Tokenize(
 +            pConfig, 
 +            (const char*)sqlite3_column_text(pScan, i+1),
 +            sqlite3_column_bytes(pScan, i+1),
 +            (void*)&ctx,
 +            fts5StorageIntegrityCallback
 +        );
 +        if( ctx.szCol!=aColSize[i] ) rc = FTS5_CORRUPT;
 +        aTotalSize[i] += ctx.szCol;
 +      }
 +      if( rc!=SQLITE_OK ) break;
 +    }
 +    rc2 = sqlite3_reset(pScan);
 +    if( rc==SQLITE_OK ) rc = rc2;
 +  }
 +
 +  /* Test that the "totals" (sometimes called "averages") record looks Ok */
 +  if( rc==SQLITE_OK ){
 +    int i;
 +    rc = fts5StorageLoadTotals(p, 0);
 +    for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
 +      if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
 +    }
 +  }
 +
 +  /* Check that the %_docsize and %_content tables contain the expected
 +  ** number of rows.  */
 +  if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
 +    i64 nRow;
 +    rc = fts5StorageCount(p, "content", &nRow);
 +    if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
 +  }
 +  if( rc==SQLITE_OK ){
 +    i64 nRow;
 +    rc = fts5StorageCount(p, "docsize", &nRow);
 +    if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
 +  }
 +
 +  /* Pass the expected checksum down to the FTS index module. It will
 +  ** verify, amongst other things, that it matches the checksum generated by
 +  ** inspecting the index itself.  */
 +  if( rc==SQLITE_OK ){
 +    rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
 +  }
 +
 +  sqlite3_free(aTotalSize);
 +  return rc;
 +}
 +
 +/*
 +** Obtain an SQLite statement handle that may be used to read data from the
 +** %_content table.
 +*/
 +int sqlite3Fts5StorageStmt(
 +  Fts5Storage *p, 
 +  int eStmt, 
 +  sqlite3_stmt **pp, 
 +  char **pzErrMsg
 +){
 +  int rc;
 +  assert( eStmt==FTS5_STMT_SCAN_ASC 
 +       || eStmt==FTS5_STMT_SCAN_DESC
 +       || eStmt==FTS5_STMT_LOOKUP
 +  );
 +  rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg);
 +  if( rc==SQLITE_OK ){
 +    assert( p->aStmt[eStmt]==*pp );
 +    p->aStmt[eStmt] = 0;
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Release an SQLite statement handle obtained via an earlier call to
 +** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
 +** must match that passed to the sqlite3Fts5StorageStmt() call.
 +*/
 +void sqlite3Fts5StorageStmtRelease(
 +  Fts5Storage *p, 
 +  int eStmt, 
 +  sqlite3_stmt *pStmt
 +){
 +  assert( eStmt==FTS5_STMT_SCAN_ASC
 +       || eStmt==FTS5_STMT_SCAN_DESC
 +       || eStmt==FTS5_STMT_LOOKUP
 +  );
 +  if( p->aStmt[eStmt]==0 ){
 +    sqlite3_reset(pStmt);
 +    p->aStmt[eStmt] = pStmt;
 +  }else{
 +    sqlite3_finalize(pStmt);
 +  }
 +}
 +
 +static int fts5StorageDecodeSizeArray(
 +  int *aCol, int nCol,            /* Array to populate */
 +  const u8 *aBlob, int nBlob      /* Record to read varints from */
 +){
 +  int i;
 +  int iOff = 0;
 +  for(i=0; i<nCol; i++){
 +    if( iOff>=nBlob ) return 1;
 +    iOff += fts5GetVarint32(&aBlob[iOff], aCol[i]);
 +  }
 +  return (iOff!=nBlob);
 +}
 +
 +/*
 +** Argument aCol points to an array of integers containing one entry for
 +** each table column. This function reads the %_docsize record for the
 +** specified rowid and populates aCol[] with the results.
 +**
 +** An SQLite error code is returned if an error occurs, or SQLITE_OK
 +** otherwise.
 +*/
 +int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
 +  int nCol = p->pConfig->nCol;
 +  sqlite3_stmt *pLookup = 0;
 +  int rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
 +  if( rc==SQLITE_OK ){
 +    int bCorrupt = 1;
 +    sqlite3_bind_int64(pLookup, 1, iRowid);
 +    if( SQLITE_ROW==sqlite3_step(pLookup) ){
 +      const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
 +      int nBlob = sqlite3_column_bytes(pLookup, 0);
 +      if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){
 +        bCorrupt = 0;
 +      }
 +    }
 +    rc = sqlite3_reset(pLookup);
 +    if( bCorrupt && rc==SQLITE_OK ){
 +      rc = FTS5_CORRUPT;
 +    }
 +  }
 +
 +  return rc;
 +}
 +
 +int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){
 +  int rc = fts5StorageLoadTotals(p, 0);
 +  if( rc==SQLITE_OK ){
 +    *pnToken = 0;
 +    if( iCol<0 ){
 +      int i;
 +      for(i=0; i<p->pConfig->nCol; i++){
 +        *pnToken += p->aTotalSize[i];
 +      }
 +    }else if( iCol<p->pConfig->nCol ){
 +      *pnToken = p->aTotalSize[iCol];
 +    }else{
 +      rc = SQLITE_RANGE;
 +    }
 +  }
 +  return rc;
 +}
 +
 +int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){
 +  int rc = fts5StorageLoadTotals(p, 0);
 +  if( rc==SQLITE_OK ){
 +    *pnRow = p->nTotalRow;
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Flush any data currently held in-memory to disk.
 +*/
 +int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit){
 +  if( bCommit && p->bTotalsValid ){
 +    int rc = fts5StorageSaveTotals(p);
 +    p->bTotalsValid = 0;
 +    if( rc!=SQLITE_OK ) return rc;
 +  }
 +  return sqlite3Fts5IndexSync(p->pIndex, bCommit);
 +}
 +
 +int sqlite3Fts5StorageRollback(Fts5Storage *p){
 +  p->bTotalsValid = 0;
 +  return sqlite3Fts5IndexRollback(p->pIndex);
 +}
 +
 +int sqlite3Fts5StorageConfigValue(
 +  Fts5Storage *p, 
 +  const char *z,
 +  sqlite3_value *pVal,
 +  int iVal
 +){
 +  sqlite3_stmt *pReplace = 0;
 +  int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0);
 +  if( rc==SQLITE_OK ){
 +    sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_STATIC);
 +    if( pVal ){
 +      sqlite3_bind_value(pReplace, 2, pVal);
 +    }else{
 +      sqlite3_bind_int(pReplace, 2, iVal);
 +    }
 +    sqlite3_step(pReplace);
 +    rc = sqlite3_reset(pReplace);
 +  }
 +  if( rc==SQLITE_OK && pVal ){
 +    int iNew = p->pConfig->iCookie + 1;
 +    rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew);
 +    if( rc==SQLITE_OK ){
 +      p->pConfig->iCookie = iNew;
 +    }
 +  }
 +  return rc;
 +}
 +
 +
 +#endif /* SQLITE_ENABLE_FTS5 */
index 4dead06f629fd85cf94c8c4cd7f1b92ba49f885e,0000000000000000000000000000000000000000..3e725366dab4df2bf9a23c0e69752555a37136fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,119 @@@
 +# 2015 Jun 10
 +#
 +# The author disclaims copyright to this source code.  In place of
 +# a legal notice, here is a blessing:
 +#
 +#    May you do good and not evil.
 +#    May you find forgiveness for yourself and forgive others.
 +#    May you share freely, never taking more than you give.
 +#
 +#***********************************************************************
 +#
 +# Tests focusing on fts5 tables with the columnsize=0 option.
 +#
 +
 +source [file join [file dirname [info script]] fts5_common.tcl]
 +set testprefix fts5columnsize
 +
 +#-------------------------------------------------------------------------
 +# Check that the option can be parsed and that the %_docsize table is
 +# only created if it is set to true.
 +#
 +foreach {tn outcome stmt} {
 +  1 0 { CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize=0) }
 +  2 1 { CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize=1) }
 +  3 0 { CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize='0') }
 +  4 1 { CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize='1') }
 +  5 2 { CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize='') }
 +  6 2 { CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize=2) }
 +  7 1 { CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize=0, columnsize=1) }
 +  8 1 { CREATE VIRTUAL TABLE t1 USING fts5(x) }
 +} {
 +  execsql { 
 +    DROP TABLE IF EXISTS t1;
 +  }
 +  if {$outcome==2} {
 +    do_catchsql_test 1.$tn.1 $stmt {1 {malformed columnsize=... directive}}
 +  } else {
 +    do_execsql_test 1.$tn.2 $stmt
 +    do_execsql_test 1.$tn.3 {
 +      SELECT count(*) FROM sqlite_master WHERE name = 't1_docsize'
 +    } $outcome
 +  }
 +}
 +
 +#-------------------------------------------------------------------------
 +# Run tests on a table with no %_content or %_docsize backing store.
 +#
 +do_execsql_test 2.0 {
 +  CREATE VIRTUAL TABLE t2 USING fts5(x, columnsize=0, content='');
 +}
 +do_catchsql_test 2.1 {
 +  INSERT INTO t2 VALUES('a b c d e f');
 +} {1 {datatype mismatch}}
 +do_execsql_test 2.2 {
 +  INSERT INTO t2(rowid, x) VALUES(1, 'c d e f');
 +  INSERT INTO t2(rowid, x) VALUES(2, 'c d e f g h');
 +  INSERT INTO t2(rowid, x) VALUES(3, 'a b c d e f g h');
 +} {}
 +do_execsql_test 2.3 {
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'b'; SELECT '::';
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'e'; SELECT '::';
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'h'; 
 +} {3 :: 1 2 3 :: 2 3}
 +do_execsql_test 2.4 {
 +  INSERT INTO t2(t2, rowid, x) VALUES('delete', 2, 'c d e f g h');
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'b'; SELECT '::';
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'e'; SELECT '::';
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'h'; 
 +} {3 :: 1 3 :: 3}
 +do_execsql_test 2.5 {
 +  INSERT INTO t2(t2) VALUES('delete-all');
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'b'; SELECT '::';
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'e'; SELECT '::';
 +  SELECT rowid FROM t2 WHERE t2 MATCH 'h'; 
 +} {:: ::}
 +do_execsql_test 2.6 {
 +  INSERT INTO t2(rowid, x) VALUES(1, 'o t t f');
 +  INSERT INTO t2(rowid, x) VALUES(2, 'f s s e');
 +  INSERT INTO t2(rowid, x) VALUES(3, 'n t e t');
 +}
 +
 +do_catchsql_test 2.7.1 {
 +  SELECT rowid FROM t2
 +} {1 {t2: table does not support scanning}}
 +do_catchsql_test 2.7.2 {
 +  SELECT rowid FROM t2 WHERE rowid=2
 +} {1 {t2: table does not support scanning}}
 +do_catchsql_test 2.7.3 {
 +  SELECT rowid FROM t2 WHERE rowid BETWEEN 1 AND 3
 +} {1 {t2: table does not support scanning}}
 +
 +do_execsql_test 2.X {
 +  DROP TABLE t2
 +}
 +
 +#-------------------------------------------------------------------------
 +# Test the xColumnSize() API
 +#
 +fts5_aux_test_functions db
 +
 +do_execsql_test 3.0 {
 +  CREATE VIRTUAL TABLE t3 USING fts5(x, y UNINDEXED, z, columnsize=0);
 +  INSERT INTO t3 VALUES('a a', 'b b b', 'c');
 +  INSERT INTO t3 VALUES('x a x', 'b b b y', '');
 +}
 +do_execsql_test 3.1 {
 +  SELECT rowid, fts5_test_columnsize(t3) FROM t3 WHERE t3 MATCH 'a'
 +} {
 +  1 {2 0 1} 2 {3 0 0}
 +}
++do_execsql_test 3.1 {
++  INSERT INTO t3 VALUES(NULL, NULL, 'a a a a');
++  DELETE FROM t3 WHERE rowid = 1;
++  SELECT rowid, fts5_test_columnsize(t3) FROM t3 WHERE t3 MATCH 'a'
++} {
++  2 {3 0 0} 3 {0 0 4}
++}
 +
 +finish_test
index f224df40cddb5847e319673b2a4d574c7948c2e1,0000000000000000000000000000000000000000..a392b238eeb2b13de47a7182937578867344c44b
mode 100644,000000..100644
--- /dev/null
@@@ -1,403 -1,0 +1,419 @@@
 +# 2014 June 17
 +#
 +# The author disclaims copyright to this source code.  In place of
 +# a legal notice, here is a blessing:
 +#
 +#    May you do good and not evil.
 +#    May you find forgiveness for yourself and forgive others.
 +#    May you share freely, never taking more than you give.
 +#
 +#*************************************************************************
 +#
 +# This file is focused on OOM errors.
 +#
 +
 +source [file join [file dirname [info script]] fts5_common.tcl]
 +source $testdir/malloc_common.tcl
 +set testprefix fts5fault4
 +
 +# If SQLITE_ENABLE_FTS3 is defined, omit this file.
 +ifcapable !fts5 {
 +  finish_test
 +  return
 +}
 +
 +#-------------------------------------------------------------------------
 +# An OOM while dropping an fts5 table.
 +#
 +db func rnddoc fts5_rnddoc 
 +do_test 1.0 {
 +  execsql { CREATE VIRTUAL TABLE xx USING fts5(x) }
 +} {}
 +faultsim_save_and_close
 +
 +do_faultsim_test 1 -faults oom-* -prep {
 +  faultsim_restore_and_reopen
 +  execsql { SELECT * FROM xx }
 +} -body {
 +  execsql { DROP TABLE xx }
 +} -test {
 +  faultsim_test_result [list 0 {}]
 +}
 +
 +#-------------------------------------------------------------------------
 +# An OOM within an "ORDER BY rank" query.
 +#
 +db func rnddoc fts5_rnddoc 
 +do_execsql_test 2.0 {
 +  CREATE VIRTUAL TABLE xx USING fts5(x);
 +  INSERT INTO xx VALUES ('abc ' || rnddoc(10));
 +  INSERT INTO xx VALUES ('abc abc' || rnddoc(9));
 +  INSERT INTO xx VALUES ('abc abc abc' || rnddoc(8));
 +} {}
 +faultsim_save_and_close
 +
 +do_faultsim_test 2 -faults oom-* -prep {
 +  faultsim_restore_and_reopen
 +  execsql { SELECT * FROM xx }
 +} -body {
 +  execsql { SELECT rowid FROM xx WHERE xx MATCH 'abc' ORDER BY rank }
 +} -test {
 +  faultsim_test_result [list 0 {3 2 1}]
 +}
 +
 +#-------------------------------------------------------------------------
 +# An OOM while "reseeking" an FTS cursor.
 +#
 +do_execsql_test 3.0 {
 +  CREATE VIRTUAL TABLE jj USING fts5(j);
 +  INSERT INTO jj(rowid, j) VALUES(101, 'm t w t f s s');
 +  INSERT INTO jj(rowid, j) VALUES(202, 't w t f s');
 +  INSERT INTO jj(rowid, j) VALUES(303, 'w t f');
 +  INSERT INTO jj(rowid, j) VALUES(404, 't');
 +}
 +faultsim_save_and_close
 +
 +do_faultsim_test 3 -faults oom-* -prep {
 +  faultsim_restore_and_reopen
 +  execsql { SELECT * FROM jj }
 +} -body {
 +  set res [list]
 +  db eval { SELECT rowid FROM jj WHERE jj MATCH 't' } {
 +    lappend res $rowid
 +    if {$rowid==303} {
 +      execsql { DELETE FROM jj WHERE rowid=404 }
 +    }
 +  }
 +  set res
 +} -test {
 +  faultsim_test_result [list 0 {101 202 303}]
 +}
 +
 +#-------------------------------------------------------------------------
 +# An OOM within a special "*reads" query.
 +#
 +reset_db
 +db func rnddoc fts5_rnddoc
 +do_execsql_test 4.0 {
 +  CREATE VIRTUAL TABLE x1 USING fts5(x);
 +  INSERT INTO x1(x1, rank) VALUES('pgsz', 32);
 +
 +  WITH ii(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<10 )
 +  INSERT INTO x1 SELECT rnddoc(5) FROM ii;
 +}
 +
 +set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}]
 +
 +do_faultsim_test 4 -faults oom-* -body {
 +  db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'}
 +} -test {
 +  faultsim_test_result {0 {0 {} 3}}
 +}
 +
 +#-------------------------------------------------------------------------
 +# An OOM within a query that uses a custom rank function.
 +#
 +reset_db
 +do_execsql_test 5.0 {
 +  PRAGMA encoding='utf16';
 +  CREATE VIRTUAL TABLE x2 USING fts5(x);
 +  INSERT INTO x2(rowid, x) VALUES(10, 'a b c'); -- 3
 +  INSERT INTO x2(rowid, x) VALUES(20, 'a b c'); -- 6
 +  INSERT INTO x2(rowid, x) VALUES(30, 'a b c'); -- 2
 +  INSERT INTO x2(rowid, x) VALUES(40, 'a b c'); -- 5
 +  INSERT INTO x2(rowid, x) VALUES(50, 'a b c'); -- 1
 +}
 +
 +proc rowidmod {cmd mod} { 
 +  set row [$cmd xRowid]
 +  expr {$row % $mod}
 +}
 +sqlite3_fts5_create_function db rowidmod rowidmod
 +
 +do_faultsim_test 5.1 -faults oom-* -body {
 +  db eval {
 +    SELECT rowid || '-' || rank FROM x2 WHERE x2 MATCH 'b' AND 
 +    rank MATCH "rowidmod('7')" ORDER BY rank
 +  }
 +} -test {
 +  faultsim_test_result {0 {50-1 30-2 10-3 40-5 20-6}}
 +}
 +
 +proc rowidprefix {cmd prefix} { 
 +  set row [$cmd xRowid]
 +  set {} "${row}-${prefix}"
 +}
 +sqlite3_fts5_create_function db rowidprefix rowidprefix
 +
 +set str [string repeat abcdefghijklmnopqrstuvwxyz 10]
 +do_faultsim_test 5.2 -faults oom-* -body {
 +  db eval "
 +    SELECT rank, x FROM x2 WHERE x2 MATCH 'b' AND 
 +    rank MATCH 'rowidprefix(''$::str'')'
 +    LIMIT 1
 +  "
 +} -test {
 +  faultsim_test_result "0 {10-$::str {a b c}}"
 +}
 +
 +
 +#-------------------------------------------------------------------------
 +# OOM errors within auxiliary functions.
 +#
 +reset_db
 +do_execsql_test 6.0 {
 +  CREATE VIRTUAL TABLE x3 USING fts5(xxx);
 +  INSERT INTO x3 VALUES('a b c d c b a');
 +  INSERT INTO x3 VALUES('a a a a a a a');
 +  INSERT INTO x3 VALUES('a a a a a a a');
 +}
 +
 +do_faultsim_test 6.1 -faults oom-t* -body {
 +  db eval { SELECT highlight(x3, 0, '*', '*') FROM x3 WHERE x3 MATCH 'c' }
 +} -test {
 +  faultsim_test_result {0 {{a b *c* d *c* b a}}}
 +}
 +
 +proc firstinst {cmd} { 
 +  foreach {p c o} [$cmd xInst 0] {}
 +  expr $c*100 + $o
 +}
 +sqlite3_fts5_create_function db firstinst firstinst
 +
 +do_faultsim_test 6.2 -faults oom-t* -body {
 +  db eval { SELECT firstinst(x3) FROM x3 WHERE x3 MATCH 'c' }
 +} -test {
 +  faultsim_test_result {0 2} {1 SQLITE_NOMEM}
 +}
 +
 +proc previc {cmd} {
 +  set res [$cmd xGetAuxdataInt 0]
 +  $cmd xSetAuxdataInt [$cmd xInstCount]
 +  return $res
 +}
 +sqlite3_fts5_create_function db previc  previc
 +
 +do_faultsim_test 6.2 -faults oom-t* -body {
 +  db eval { SELECT previc(x3) FROM x3 WHERE x3 MATCH 'a' }
 +} -test {
 +  faultsim_test_result {0 {0 2 7}} {1 SQLITE_NOMEM}
 +}
 +
 +#-------------------------------------------------------------------------
 +# OOM error when querying for a phrase with many tokens.
 +#
 +reset_db
 +do_execsql_test 7.0 {
 +  CREATE VIRTUAL TABLE tt USING fts5(x, y);
 +  INSERT INTO tt VALUES('f b g b c b', 'f a d c c b');  -- 1
 +  INSERT INTO tt VALUES('d a e f e d', 'f b b d e e');  -- 2
 +  INSERT INTO tt VALUES('f b g a d c', 'e f c f a d');  -- 3
 +  INSERT INTO tt VALUES('f f c d g f', 'f a e b g b');  -- 4
 +  INSERT INTO tt VALUES('a g b d a g', 'e g a e a c');  -- 5
 +  INSERT INTO tt VALUES('c d b d e f', 'f g e g e e');  -- 6
 +  INSERT INTO tt VALUES('e g f f b c', 'f c e f g f');  -- 7
 +  INSERT INTO tt VALUES('e g c f c e', 'f e e a f g');  -- 8
 +  INSERT INTO tt VALUES('e a e b e e', 'd c c f f f');  -- 9
 +  INSERT INTO tt VALUES('f a g g c c', 'e g d g c e');  -- 10
 +  INSERT INTO tt VALUES('c d b a e f', 'f g e h e e');  -- 11
 +
 +  CREATE VIRTUAL TABLE tt2 USING fts5(o);
 +  INSERT INTO tt2(rowid, o) SELECT rowid, x||' '||y FROM tt;
 +  INSERT INTO tt2(rowid, o) VALUES(12, 'a b c d e f g h i j k l');
 +}
 +
 +do_faultsim_test 7.2 -faults oom-* -body {
 +  db eval { SELECT rowid FROM tt WHERE tt MATCH 'f+g+e+g+e+e' }
 +} -test {
 +  faultsim_test_result {0 6} {1 SQLITE_NOMEM}
 +}
 +
 +do_faultsim_test 7.3 -faults oom-* -body {
 +  db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d e f)' }
 +} -test {
 +  faultsim_test_result {0 11} {1 SQLITE_NOMEM}
 +}
 +
 +do_faultsim_test 7.4 -faults oom-t* -body {
 +  db eval { SELECT rowid FROM tt2 WHERE tt2 MATCH '"g c f c e f e e a f"' }
 +} -test {
 +  faultsim_test_result {0 8} {1 SQLITE_NOMEM}
 +}
 +
 +do_faultsim_test 7.5 -faults oom-* -body {
 +  db eval {SELECT rowid FROM tt2 WHERE tt2 MATCH 'NEAR(a b c d e f g h i j k)'}
 +} -test {
 +  faultsim_test_result {0 12} {1 SQLITE_NOMEM}
 +}
 +
 +do_faultsim_test 7.6 -faults oom-* -body {
 +  db eval {SELECT rowid FROM tt WHERE tt MATCH 'y: "c c"'}
 +} -test {
 +  faultsim_test_result {0 {1 9}} {1 SQLITE_NOMEM}
 +}
 +
 +#-------------------------------------------------------------------------
 +#
 +reset_db
 +do_execsql_test 8.0 {
 +  CREATE VIRTUAL TABLE tt USING fts5(x);
 +  INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
 +  BEGIN;
 +    INSERT INTO tt(rowid, x) VALUES(1, 'a b c d x x');
 +    WITH ii(i) AS (SELECT 2 UNION ALL SELECT i+1 FROM ii WHERE i<99)
 +      INSERT INTO tt(rowid, x) SELECT i, 'a b c x x d' FROM ii;
 +    INSERT INTO tt(rowid, x) VALUES(100, 'a b c d x x');
 +  COMMIT;
 +}
 +
 +do_faultsim_test 8.1 -faults oom-t* -body {
 +  db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d, 2)' }
 +} -test {
 +  faultsim_test_result {0 {1 100}} {1 SQLITE_NOMEM}
 +}
 +
 +do_faultsim_test 8.2 -faults oom-t* -body {
 +  db eval { SELECT count(*) FROM tt WHERE tt MATCH 'a OR d' }
 +} -test {
 +  faultsim_test_result {0 100} {1 SQLITE_NOMEM}
 +}
 +
 +
 +#-------------------------------------------------------------------------
 +# Fault in NOT query.
 +#
 +reset_db
 +do_execsql_test 9.0 {
 +  CREATE VIRTUAL TABLE tt USING fts5(x);
 +  INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
 +  BEGIN;
 +    WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<200)
 +      INSERT INTO tt(rowid, x) 
 +      SELECT i, CASE WHEN (i%50)==0 THEN 'a a a a a a' ELSE 'a x a x a x' END 
 +      FROM ii;
 +  COMMIT;
 +}
 +
 +do_faultsim_test 9.1 -faults oom-* -body {
 +  db eval { SELECT rowid FROM tt WHERE tt MATCH 'a NOT x' }
 +} -test {
 +  faultsim_test_result {0 {50 100 150 200}} {1 SQLITE_NOMEM}
 +}
 +
 +#-------------------------------------------------------------------------
 +# OOM in fts5_expr() SQL function.
 +#
 +do_faultsim_test 10.1 -faults oom-t* -body {
 +  db one { SELECT fts5_expr('a AND b NEAR(a b)') }
 +} -test {
 +  faultsim_test_result {0 {"a" AND "b" AND NEAR("a" "b", 10)}} 
 +}
 +
 +do_faultsim_test 10.2 -faults oom-t* -body {
 +  db one { SELECT fts5_expr_tcl('x:"a b c" AND b NEAR(a b)', 'ns', 'x') }
 +} -test {
 +  set res {AND [ns -col 0 -- {a b c}] [ns -- {b}] [ns -near 10 -- {a} {b}]}
 +  faultsim_test_result [list 0 $res]
 +}
 +
 +do_faultsim_test 10.3 -faults oom-t* -body {
 +  db one { SELECT fts5_expr('x:a', 'x') }
 +} -test {
 +  faultsim_test_result {0 {x : "a"}}
 +}
 +
 +#-------------------------------------------------------------------------
 +# OOM while configuring 'rank' option.
 +#
 +reset_db
 +do_execsql_test 11.0 {
 +  CREATE VIRTUAL TABLE ft USING fts5(x);
 +}
 +do_faultsim_test 11.1 -faults oom-t* -body {
 +  db eval { INSERT INTO ft(ft, rank) VALUES('rank', 'bm25(10.0, 5.0)') }
 +} -test {
 +  faultsim_test_result {0 {}} {1 {disk I/O error}}
 +}
 +
 +#-------------------------------------------------------------------------
 +# OOM while creating an fts5vocab table.
 +#
 +reset_db
 +do_execsql_test 12.0 {
 +  CREATE VIRTUAL TABLE ft USING fts5(x);
 +}
 +faultsim_save_and_close
 +do_faultsim_test 12.1 -faults oom-t* -prep {
 +  faultsim_restore_and_reopen
 +  db eval { SELECT * FROM sqlite_master }
 +} -body {
 +  db eval { CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row') }
 +} -test {
 +  faultsim_test_result {0 {}} 
 +}
 +
 +#-------------------------------------------------------------------------
 +# OOM while querying an fts5vocab table.
 +#
 +reset_db
 +do_execsql_test 13.0 {
 +  CREATE VIRTUAL TABLE ft USING fts5(x);
 +  INSERT INTO ft VALUES('a b');
 +  CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row');
 +}
 +faultsim_save_and_close
 +do_faultsim_test 13.1 -faults oom-t* -prep {
 +  faultsim_restore_and_reopen
 +  db eval { SELECT * FROM vv }
 +} -body {
 +  db eval { SELECT * FROM vv }
 +} -test {
 +  faultsim_test_result {0 {a 1 1 b 1 1}} 
 +}
 +
 +#-------------------------------------------------------------------------
 +# OOM in multi-column token query.
 +#
 +reset_db
 +do_execsql_test 13.0 {
 +  CREATE VIRTUAL TABLE ft USING fts5(x, y, z);
 +  INSERT INTO ft(ft, rank) VALUES('pgsz', 32);
 +  INSERT INTO ft VALUES(
 +      'x x x x x x x x x x x x x x x x',
 +      'y y y y y y y y y y y y y y y y',
 +      'z z z z z z z z x x x x x x x x'
 +  );
 +  INSERT INTO ft SELECT * FROM ft;
 +  INSERT INTO ft SELECT * FROM ft;
 +  INSERT INTO ft SELECT * FROM ft;
 +  INSERT INTO ft SELECT * FROM ft;
 +}
 +faultsim_save_and_close
 +do_faultsim_test 13.1 -faults oom-t* -prep {
 +  faultsim_restore_and_reopen
 +  db eval { SELECT * FROM ft }
 +} -body {
 +  db eval { SELECT rowid FROM ft WHERE ft MATCH '{x z}: x' }
 +} -test {
 +  faultsim_test_result {0 {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}}
 +}
 +
++#-------------------------------------------------------------------------
++# OOM in an "ALTER TABLE RENAME TO"
++#
++reset_db
++do_execsql_test 14.0 {
++  CREATE VIRTUAL TABLE "tbl one" USING fts5(x, y, z);
++}
++faultsim_save_and_close
++do_faultsim_test 14.1 -faults oom-t* -prep {
++  faultsim_restore_and_reopen
++  db eval { SELECT * FROM "tbl one" }
++} -body {
++  db eval { ALTER TABLE "tbl one" RENAME TO "tbl two" }
++} -test {
++  faultsim_test_result {0 {}}
++}
 +
 +finish_test
 +
diff --cc main.mk
index ecbcd05fc532f83da18d9c1b6af36b901669ab28,3131386f3a07eedca5698026fbd3c485077d8ffb..77357d00037726a5c2ea3ac52f0bccbc75a49af0
+++ b/main.mk
@@@ -67,25 -66,12 +67,26 @@@ LIBOBJ+= vdbe.o parse.o 
           notify.o opcodes.o os.o os_unix.o os_win.o \
           pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
           random.o resolve.o rowset.o rtree.o select.o sqlite3ota.o status.o \
-          table.o threads.o tokenize.o trigger.o \
+          table.o threads.o tokenize.o treeview.o trigger.o \
           update.o userauth.o util.o vacuum.o \
           vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
-        vdbetrace.o wal.o walker.o where.o utf.o vtab.o
+        vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \
+          utf.o vtab.o
  
 +LIBOBJ += fts5.o
 +LIBOBJ += fts5_aux.o
 +LIBOBJ += fts5_buffer.o
 +LIBOBJ += fts5_config.o
 +LIBOBJ += fts5_expr.o
 +LIBOBJ += fts5_hash.o
 +LIBOBJ += fts5_index.o
 +LIBOBJ += fts5_storage.o
 +LIBOBJ += fts5_tokenize.o
 +LIBOBJ += fts5_unicode2.o
 +LIBOBJ += fts5_varint.o
 +LIBOBJ += fts5_vocab.o
 +LIBOBJ += fts5parse.o
 +
  
  
  # All of the source code files.
diff --cc manifest
index 517ec49ea916c7afda6f2001375c2150c55cee75,609f6bf0274321186f14e53493a0422b62b0f314..61510aa714b4803352deb322f77d9f265fa352e2
+++ b/manifest
@@@ -1,9 -1,9 +1,9 @@@
- C Fix\sthe\sfts5\sxRename()\smethod.
- D 2015-06-10T10:45:34.820
 -C Test\sthat\sthe\sleft\sand\sright\ssides\sof\sa\scompound\sSELECT\soperator\shave\sthe\ssame\s\snumber\sof\sexpressions\sin\sthe\sexpanded\sexpression\slist\sbefore\sbeginning\sto\sgenerate\scode.
 -D 2015-06-23T12:19:55.597
++C Merge\slatest\strunk\schanges\swith\sthis\sbranch.\sAdd\stests\sfor\scolumnsize=0.
++D 2015-06-23T15:06:13.029
  F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
- F Makefile.in d272f8755b464f20e02dd7799bfe16794c9574c4
 -F Makefile.in 1063c58075b7400d93326b0eb332b48a54f53025
++F Makefile.in 6fa5a3c6f1f558bb443429e33806e2e494823e44
  F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
- F Makefile.msc d37d2c2323df3acae6e24c71a478889421c17264
+ F Makefile.msc b7db9ccbbad1c495b98e5326a06cac03aa206127
  F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
  F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
  F VERSION ce0ae95abd7121c534f6917c1c8f2b70d9acd4db
@@@ -102,85 -102,9 +102,85 @@@ F ext/fts3/mkfts3amal.tcl 252ecb7fe6467
  F ext/fts3/tool/fts3view.c 8e53d0190a7b3443764bbd32ad47be2bd852026d
  F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
  F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
 -F ext/fts3/unicode/mkunicode.tcl a2567f9d6ad6779879a2e394c120ad8718557e65
 +F ext/fts3/unicode/mkunicode.tcl ed0534dd51efce39878bce33944c6073d37a1e20
 +F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
 +F ext/fts5/extract_api_docs.tcl 55a6d648d516f35d9a1e580ac00de27154e1904a
 +F ext/fts5/fts5.c 4ce5d0990c61a41155fc014b0066ae6d25a388d3
 +F ext/fts5/fts5.h 81d1a92fc2b4bd477af7e4e0b38b456f3e199fba
 +F ext/fts5/fts5Int.h 21eb91e02ad119e1d92ff100f366a976e12190de
 +F ext/fts5/fts5_aux.c d53f00f31ad615ca4f139dd8751f9041afa00971
 +F ext/fts5/fts5_buffer.c be0dc80a9406151b350be27c7ec2956722578771
 +F ext/fts5/fts5_config.c 6ae691e36f90185896f4db0a819ae2394f880ca1
 +F ext/fts5/fts5_expr.c 549bda1f7edcf10365fbfbc002bdea1be3c287bb
 +F ext/fts5/fts5_hash.c c1cfdb2cae0fad00b06fae38a40eaf9261563ccc
 +F ext/fts5/fts5_index.c 7cea402924cd3d8cd5943a7f9514c9153696571b
- F ext/fts5/fts5_storage.c 7e77d1b2da424283d1d58a77e9a98067dc96f2c7
++F ext/fts5/fts5_storage.c b2fa301fce865d582d367a5e1bb438fe60c03cb5
 +F ext/fts5/fts5_tcl.c 7ea165878e4ae3598e89acd470a0ee1b5a00e33c
 +F ext/fts5/fts5_tokenize.c 97251d68d7a6a9415bde1203f9382864dfc1f989
 +F ext/fts5/fts5_unicode2.c da3cf712f05cd8347c8c5bc00964cc0361c88da9
 +F ext/fts5/fts5_varint.c 366452037bf9a000c351374b489badc1b3541796
 +F ext/fts5/fts5_vocab.c 1f8543b2c1ae4427f127a911bc8e60873fcd7bf9
 +F ext/fts5/fts5parse.y 833db1101b78c0c47686ab1b84918e38c36e9452
 +F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
 +F ext/fts5/test/fts5_common.tcl 0b465b1127adcd1c8131f3454ab4264a6964674c
 +F ext/fts5/test/fts5aa.test 0be21c89fd66b588db355a6398911fd875bdcc6c
 +F ext/fts5/test/fts5ab.test 6fe3a56731d15978afbb74ae51b355fc9310f2ad
 +F ext/fts5/test/fts5ac.test 0990ae7497ebaea2ab5f7fd5caedd93a71a905fc
 +F ext/fts5/test/fts5ad.test 312f3c8ed9592533499c5b94d2059ae6382913a0
 +F ext/fts5/test/fts5ae.test ddc558e3e3b52db0101f7541b2e3849b77052c92
 +F ext/fts5/test/fts5af.test c2501ec2b61d6b179c305f5d2b8782ab3d4f832a
 +F ext/fts5/test/fts5ag.test ec3e119b728196620a31507ef503c455a7a73505
 +F ext/fts5/test/fts5ah.test b9e78fa986a7bd564ebadfb244de02c84d7ac3ae
 +F ext/fts5/test/fts5ai.test f20e53bbf0c55bc596f1fd47f2740dae028b8f37
 +F ext/fts5/test/fts5aj.test 05b569f5c16ea3098fb1984eec5cf50dbdaae5d8
 +F ext/fts5/test/fts5ak.test 7b8c5df96df599293f920b7e5521ebc79f647592
 +F ext/fts5/test/fts5al.test fc60ebeac9d8e366e71309d4c31fa72199d711d7
 +F ext/fts5/test/fts5alter.test 3342e7fd58556d2a7e5299a7d9dec62e358028ed
 +F ext/fts5/test/fts5auto.test caa5bcf917db11944655a2a9bd38c67c520376ca
 +F ext/fts5/test/fts5aux.test e5631607bbc05ac1c38cf7d691000509aca71ef3
 +F ext/fts5/test/fts5auxdata.test c69b86092bf1a157172de5f9169731af3403179b
 +F ext/fts5/test/fts5bigpl.test b1cfd00561350ab04994ba7dd9d48468e5e0ec3b
- F ext/fts5/test/fts5columnsize.test c7333cf079022c1ad25d04538b8f279fad4c2f8d
++F ext/fts5/test/fts5columnsize.test bd07a42a80a6805e84afa7daf54ecd4563f752d0
 +F ext/fts5/test/fts5config.test c9cc535f3b36cde1e5a32bf579f3f5962a9e82b2
 +F ext/fts5/test/fts5content.test e46904decd896e38c848ad4f38fa4e80251a028b
 +F ext/fts5/test/fts5corrupt.test 35bfdbbb3cdcea46ae7385f6432e9b5c574e70a1
 +F ext/fts5/test/fts5corrupt2.test c231f532162de381fa83ec477b51cd8633fd9da7
 +F ext/fts5/test/fts5corrupt3.test da4e2adb2308d8587c2eff31b5aa47447b8a2edb
 +F ext/fts5/test/fts5dlidx.test 070531bd45685e545e3e6021deb543f730a4011b
 +F ext/fts5/test/fts5doclist.test 635b80ac785627841a59c583bac702b55d49fdc5
 +F ext/fts5/test/fts5ea.test 451bb37310ee6df8ef72e4354fda5621b3b51448
 +F ext/fts5/test/fts5eb.test 728a1f23f263548f5c29b29dfb851b5f2dbe723e
 +F ext/fts5/test/fts5fault1.test b42d3296be8a75f557cf2cbce0d8b483fc9db45b
 +F ext/fts5/test/fts5fault2.test 28c36c843bb39ae855ba79827417ecc37f114341
 +F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3
- F ext/fts5/test/fts5fault4.test 8671f534136aa1c80a102e8fd25b4921885e6667
++F ext/fts5/test/fts5fault4.test 762991d526ee67c2b374351a17248097ea38bee7
 +F ext/fts5/test/fts5fault5.test 54da9fd4c3434a1d4f6abdcb6469299d91cf5875
 +F ext/fts5/test/fts5fault6.test 234dc6355f8d3f8b5be2763f30699d770247c215
 +F ext/fts5/test/fts5full.test 0924bdca5416a242103239ace79c6f5aa34bab8d
 +F ext/fts5/test/fts5hash.test bdba7b591d503005d5a81871ba00a359daa1e969
 +F ext/fts5/test/fts5integrity.test b45f633381a85dc000e41d68c96ab510985ca35e
 +F ext/fts5/test/fts5merge.test 8077454f2975a63f35761f4b8a718b3a808b7c9c
 +F ext/fts5/test/fts5near.test d2e3343e62d438f2efd96ebcd83a0d30a16ea6dc
 +F ext/fts5/test/fts5optimize.test 0028c90a7817d3e576d1148fc8dff17d89054e54
 +F ext/fts5/test/fts5plan.test 7f38179220c9385f88e1470aae6cba134a308b40
 +F ext/fts5/test/fts5porter.test 50322599823cb8080a99f0ec0c39f7d0c12bcb5e
 +F ext/fts5/test/fts5porter2.test c534385e88e685b354c2b2020acc0c4920042c8e
 +F ext/fts5/test/fts5prefix.test 7eba86fc270b110ba2b83ba286a1fd4b3b17955e
 +F ext/fts5/test/fts5rank.test f59a6b20ec8e08cb130d833dcece59cf9cd92890
 +F ext/fts5/test/fts5rebuild.test 77c6613aa048f38b4a12ddfacb2e6e1342e1b066
 +F ext/fts5/test/fts5restart.test cd58a5fb552ac10db549482698e503f82693bcd0
 +F ext/fts5/test/fts5rowid.test ca9d91ccb3a4590fc561b2d7a884361bb21e8df5
 +F ext/fts5/test/fts5tokenizer.test 668747fcb41de6fc7daebc478920b705164fccc1
 +F ext/fts5/test/fts5unicode.test 79b3e34eb29ce4929628aa514a40cb467fdabe4d
 +F ext/fts5/test/fts5unicode2.test ad38982b03dc9213445facb16e99f668a74cc4ba
 +F ext/fts5/test/fts5unicode3.test 273f9086ad33935566bbc0d0c94d0d9687ef686b
 +F ext/fts5/test/fts5unindexed.test f388605341a476b6ab622b4c267cd168f59a5944
 +F ext/fts5/test/fts5version.test dc34a735af6625a1a7a4a916a38d122071343887
 +F ext/fts5/test/fts5vocab.test 389e5fe4928eae5fddcf26bcc5a6890b0791aa75
 +F ext/fts5/tool/loadfts5.tcl 7ef3e62131f0434a78e4f5c5b056b09d221710a8
 +F ext/fts5/tool/showfts5.tcl 921f33b30c3189deefd2b2cc81f951638544aaf1
  F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
- F ext/icu/icu.c d415ccf984defeb9df2c0e1afcfaa2f6dc05eacb
+ F ext/icu/icu.c b2732aef0b076e4276d9b39b5a33cec7a05e1413
  F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
  F ext/misc/amatch.c 27b9b601fb1453084e18a3432ea0240d7af8decb
  F ext/misc/closure.c 636024302cde41b2bf0c542f81c40c624cfb7012
@@@ -247,7 -171,7 +247,7 @@@ F ext/userauth/userauth.c 5fa3bdb492f48
  F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
  F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
  F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
- F main.mk dc2b1bf580712422d5a8bb774d8d940940563fb8
 -F main.mk 68f86c21505d6b66765a13c193f00a53dde6a212
++F main.mk 080c85fad8bf6532b7aeb782452b78e4440de346
  F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
  F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32
  F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@@@ -290,8 -214,8 +290,8 @@@ F src/insert.c b5f8b35a1b7924020e48cade
  F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
  F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
  F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
- F src/loadext.c 29255bbe1cfb2ce9bbff2526a5ecfddcb49b9271
- F src/main.c 33562894d96cb65f4926cd5317725427cdb22770
+ F src/loadext.c e722f4b832f923744788365df5fb8515c0bc8a47
 -F src/main.c c0061a4f8ba86f957534be93b7026dab324f12c2
++F src/main.c fb40edfcda10062e4d1adab7f41635002e9b7e9d
  F src/malloc.c 908c780fdddd472163c2d1b1820ae4081f01ad20
  F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
  F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
@@@ -388,16 -313,16 +389,16 @@@ F src/update.c 487747b328b7216bb7f6af06
  F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
  F src/util.c a6431c92803b975b7322724a7b433e538d243539
  F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
- F src/vdbe.c 0a6a1df5c31415a0e974e74e7bd412616889453d
- F src/vdbe.h 7e538ecf47dccb307ea2d087c3ddc2dd8d70e79d
- F src/vdbeInt.h f0ccddac48583d5f762dc554a9f79e85ea8807e0
+ F src/vdbe.c c9b8985dfc5df9bd512342ea2e56af4be30cb31a
+ F src/vdbe.h 90048aea1910f9df93e6044592bd4a466dc9c5e7
+ F src/vdbeInt.h 20295e482121d13437f69985f77db211cdc8bac1
  F src/vdbeapi.c 6a0d7757987018ff6b1b81bc5293219cd26bb299
- F src/vdbeaux.c 46f9bc4b32866082eb87a36b461e487a0bbdbe8e
+ F src/vdbeaux.c 4c82d6f686f72ea7d266d26d528a171b728626f7
  F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
- F src/vdbemem.c 67b302dc6df64b4d6785881c5d22bd4f9b17739d
+ F src/vdbemem.c 4e947cd322bb531e3f7f6f58f0f536d182b38ef8
  F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
  F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
 -F src/vtab.c c535e80259ebe616467181a83a4263555b97c694
 +F src/vtab.c 082b35a25a26e3d36f365ca8cd73c1922532f05e
  F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
  F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113
  F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
@@@ -897,7 -825,7 +901,7 @@@ F test/pagesize.test 5769fc62d8c890a83a
  F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
  F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
  F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
- F test/permutations.test a54a4c5e66dc158cb2c05579bbb4f7d1a4fdb6c1
 -F test/permutations.test 6b0f339a4d5f00041555a986dde8fbe8f54c25bc
++F test/permutations.test 6a88fd9ca15b804e9c20990773262ca67494058f
  F test/pragma.test be7195f0aa72bdb8a512133e9640ac40f15b57a2
  F test/pragma2.test f624a496a95ee878e81e59961eade66d5c00c028
  F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
@@@ -1324,7 -1251,7 +1328,7 @@@ F tool/mkopts.tcl 66ac10d240cc6e86abd37
  F tool/mkpragmatab.tcl 40c287d3f929ece67da6e9e7c49885789960accf
  F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
  F tool/mksqlite3c-noext.tcl 69bae8ce4aa52d2ff82d4a8a856bf283ec035b2e
- F tool/mksqlite3c.tcl ccee8fe53dabbeb00d55fe0a4a24005f69eccec9
 -F tool/mksqlite3c.tcl d79e450048b0bbf04d53677e01c249a012981182
++F tool/mksqlite3c.tcl b601b174d783094edd926d913a8f545709e89f8a
  F tool/mksqlite3h.tcl 44730d586c9031638cdd2eb443b801c0d2dbd9f8
  F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
  F tool/mkvsix.tcl 3b58b9398f91c7dbf18d49eb87cefeee9efdbce1
@@@ -1357,9 -1284,9 +1361,9 @@@ F tool/varint.c 5d94cb5003db9dbbcbcc5df
  F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c
  F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
  F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
- F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
+ F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
  F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
- P aa12f9d9b79c2f523fd6b00e47bcb66dba09ce0c
- R 92d332093f5da6f2c10bbd12aba6b7b5
 -P b97f9cf73e503c7285ba3a801e1f932f222d96b2
 -R 5d4925e11483f968b1575f6656e62b8a
++P 0f7fd51325875fbf0f1eaca3bbbd170ef99c4208 4df852ce26c95d5d23c83dbe9c59d2c3435acddf
++R 3ac475f744e56c1ad80818d65ed40944
  U dan
- Z 4c307463292fc937d885d935d5d6ac74
 -Z 6c0c66a18bcc0b19ae618b4e0a65b57c
++Z b74a37a10556c5365b257dc3f6c234f4
diff --cc manifest.uuid
index 052ab5492f578a27899b81b0754fbfdbda1800ed,3a395d46f997c333582c690ac972088e2e812c68..92aa404f552979954aa799d1f4b7e4ed7a7b43b9
@@@ -1,1 -1,1 +1,1 @@@
- 0f7fd51325875fbf0f1eaca3bbbd170ef99c4208
 -4df852ce26c95d5d23c83dbe9c59d2c3435acddf
++ef44c71a22518727030dd90c0139af8973b05841
diff --cc src/main.c
Simple merge
Simple merge
Simple merge