sqlite3_free(p->zSegmentsTbl);
sqlite3_free(p->zReadExprlist);
sqlite3_free(p->zWriteExprlist);
+ sqlite3_free(p->zContentTbl);
/* Invoke the tokenizer destructor to free the tokenizer. */
p->pTokenizer->pModule->xDestroy(p->pTokenizer);
** The xDestroy() virtual table method.
*/
static int fts3DestroyMethod(sqlite3_vtab *pVtab){
- int rc = SQLITE_OK; /* Return code */
Fts3Table *p = (Fts3Table *)pVtab;
- sqlite3 *db = p->db;
+ int rc = SQLITE_OK; /* Return code */
+ const char *zDb = p->zDb; /* Name of database (e.g. "main", "temp") */
+ sqlite3 *db = p->db; /* Database handle */
/* Drop the shadow tables */
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", p->zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", p->zDb,p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", p->zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", p->zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", p->zDb, p->zName);
+ if( p->zContentTbl==0 ){
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName);
+ }
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName);
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName);
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName);
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName);
/* If everything has worked, invoke fts3DisconnectMethod() to free the
** memory associated with the Fts3Table structure and return SQLITE_OK.
static int fts3CreateTables(Fts3Table *p){
int rc = SQLITE_OK; /* Return code */
int i; /* Iterator variable */
- char *zContentCols; /* Columns of %_content table */
sqlite3 *db = p->db; /* The database connection */
- /* Create a list of user columns for the content table */
- zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
- for(i=0; zContentCols && i<p->nColumn; i++){
- char *z = p->azColumn[i];
- zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
+ if( p->zContentTbl==0 ){
+ char *zContentCols; /* Columns of %_content table */
+
+ /* Create a list of user columns for the content table */
+ zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
+ for(i=0; zContentCols && i<p->nColumn; i++){
+ char *z = p->azColumn[i];
+ zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
+ }
+ if( zContentCols==0 ) rc = SQLITE_NOMEM;
+
+ /* Create the content table */
+ fts3DbExec(&rc, db,
+ "CREATE TABLE %Q.'%q_content'(%s)",
+ p->zDb, p->zName, zContentCols
+ );
+ sqlite3_free(zContentCols);
}
- if( zContentCols==0 ) rc = SQLITE_NOMEM;
- /* Create the content table */
- fts3DbExec(&rc, db,
- "CREATE TABLE %Q.'%q_content'(%s)",
- p->zDb, p->zName, zContentCols
- );
- sqlite3_free(zContentCols);
/* Create other tables */
fts3DbExec(&rc, db,
"CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);",
}
/*
-** Return a list of comma separated SQL expressions that could be used
-** in a SELECT statement such as the following:
+** Return a list of comma separated SQL expressions and a FROM clause that
+** could be used in a SELECT statement such as the following:
**
** SELECT <list of expressions> FROM %_content AS x ...
**
** table has the three user-defined columns "a", "b", and "c", the following
** string is returned:
**
-** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')"
+** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c') FROM %_content AS x"
**
** The pointer returned points to a buffer allocated by sqlite3_malloc(). It
** is the responsibility of the caller to eventually free it.
char *zFunction;
int i;
- if( !zFunc ){
- zFunction = "";
+ if( p->zContentTbl==0 ){
+ if( !zFunc ){
+ zFunction = "";
+ }else{
+ zFree = zFunction = fts3QuoteId(zFunc);
+ }
+ fts3Appendf(pRc, &zRet, "docid");
+ for(i=0; i<p->nColumn; i++){
+ fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
+ }
+ sqlite3_free(zFree);
}else{
- zFree = zFunction = fts3QuoteId(zFunc);
- }
- fts3Appendf(pRc, &zRet, "docid");
- for(i=0; i<p->nColumn; i++){
- fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
+ fts3Appendf(pRc, &zRet, "rowid");
+ for(i=0; i<p->nColumn; i++){
+ fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]);
+ }
}
- sqlite3_free(zFree);
+ fts3Appendf(pRc, &zRet, "FROM '%q'.'%q%s' AS x",
+ p->zDb,
+ (p->zContentTbl ? p->zContentTbl : p->zName),
+ (p->zContentTbl ? "" : "_content")
+ );
return zRet;
}
return SQLITE_OK;
}
+/*
+** This function is called when initializing an FTS4 table that uses the
+** content=xxx option. It determines the number of and names of the columns
+** of the new FTS4 table.
+**
+** The third argument passed to this function is the value passed to the
+** config=xxx option (i.e. "xxx"). This function queries the database for
+** a table of that name. If found, the output variables are populated
+** as follows:
+**
+** *pnCol: Set to the number of columns table xxx has,
+**
+** *pnStr: Set to the total amount of space required to store a copy
+** of each columns name, including the nul-terminator.
+**
+** *pazCol: Set to point to an array of *pnCol strings. Each string is
+** the name of the corresponding column in table xxx. The array
+** and its contents are allocated using a single allocation. It
+** is the responsibility of the caller to free this allocation
+** by eventually passing the *pazCol value to sqlite3_free().
+**
+** If the table cannot be found, an error code is returned and the output
+** variables are undefined. Or, if an OOM is encountered, SQLITE_NOMEM is
+** returned (and the output variables are undefined).
+*/
+static int fts3ContentColumns(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of db (i.e. "main", "temp" etc.) */
+ const char *zTbl, /* Name of content table */
+ const char ***pazCol, /* OUT: Malloc'd array of column names */
+ int *pnCol, /* OUT: Size of array *pazCol */
+ int *pnStr /* OUT: Bytes of string content */
+){
+ int rc = SQLITE_OK; /* Return code */
+ char *zSql; /* "SELECT *" statement on zTbl */
+ sqlite3_stmt *pStmt = 0; /* Compiled version of zSql */
+
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zTbl);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ }
+ sqlite3_free(zSql);
+
+ if( rc==SQLITE_OK ){
+ const char **azCol; /* Output array */
+ int nStr = 0; /* Size of all column names (incl. 0x00) */
+ int nCol; /* Number of table columns */
+ int i; /* Used to iterate through columns */
+
+ /* Loop through the returned columns. Set nStr to the number of bytes of
+ ** space required to store a copy of each column name, including the
+ ** nul-terminator byte. */
+ nCol = sqlite3_column_count(pStmt);
+ for(i=0; i<nCol; i++){
+ const char *zCol = sqlite3_column_name(pStmt, i);
+ nStr += strlen(zCol) + 1;
+ }
+
+ /* Allocate and populate the array to return. */
+ azCol = (const char **)sqlite3_malloc(sizeof(char *) * nCol + nStr);
+ if( azCol==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ char *p = (char *)&azCol[nCol];
+ for(i=0; i<nCol; i++){
+ const char *zCol = sqlite3_column_name(pStmt, i);
+ int n = strlen(zCol)+1;
+ memcpy(p, zCol, n);
+ azCol[i] = p;
+ p += n;
+ }
+ }
+ sqlite3_finalize(pStmt);
+
+ /* Set the output variables. */
+ *pnCol = nCol;
+ *pnStr = nStr;
+ *pazCol = azCol;
+ }
+
+ return rc;
+}
+
/*
** This function is the implementation of both the xConnect and xCreate
** methods of the FTS3 virtual table.
char *zPrefix = 0; /* Prefix parameter value (or NULL) */
char *zCompress = 0; /* compress=? parameter (or NULL) */
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
+ char *zContent = 0; /* content=? parameter (or NULL) */
assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
struct Fts4Option {
const char *zOpt;
int nOpt;
- char **pzVar;
} aFts4Opt[] = {
- { "matchinfo", 9, 0 }, /* 0 -> MATCHINFO */
- { "prefix", 6, 0 }, /* 1 -> PREFIX */
- { "compress", 8, 0 }, /* 2 -> COMPRESS */
- { "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */
- { "order", 5, 0 } /* 4 -> ORDER */
+ { "matchinfo", 9 }, /* 0 -> MATCHINFO */
+ { "prefix", 6 }, /* 1 -> PREFIX */
+ { "compress", 8 }, /* 2 -> COMPRESS */
+ { "uncompress", 10 }, /* 3 -> UNCOMPRESS */
+ { "order", 5 }, /* 4 -> ORDER */
+ { "content", 7 } /* 5 -> CONTENT */
};
int iOpt;
}
bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
break;
+
+ case 5: /* CONTENT */
+ sqlite3_free(zUncompress);
+ zContent = zVal;
+ zVal = 0;
+ break;
}
}
sqlite3_free(zVal);
aCol[nCol++] = z;
}
}
+
+ /* If a content=xxx option was specified, the following:
+ **
+ ** 1. Ignore any compress= and uncompress= options.
+ **
+ ** 2. Ignore any column names that were specified as part of the
+ ** the CREATE VIRTUAL TABLE statement.
+ **
+ ** 3. Determine the actual column names to use for the FTS table
+ ** based on the columns of the content= table.
+ */
+ if( rc==SQLITE_OK && zContent ){
+ sqlite3_free(aCol);
+ sqlite3_free(zCompress);
+ sqlite3_free(zUncompress);
+ zCompress = 0;
+ zUncompress = 0;
+ aCol = 0;
+ rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString);
+ assert( rc!=SQLITE_OK || nCol>0 );
+ }
if( rc!=SQLITE_OK ) goto fts3_init_out;
if( nCol==0 ){
p->bHasDocsize = (isFts4 && bNoDocsize==0);
p->bHasStat = isFts4;
p->bDescIdx = bDescIdx;
+ p->zContentTbl = zContent;
+ zContent = 0;
TESTONLY( p->inTransaction = -1 );
TESTONLY( p->mxSavepoint = -1 );
sqlite3_free(aIndex);
sqlite3_free(zCompress);
sqlite3_free(zUncompress);
+ sqlite3_free(zContent);
sqlite3_free((void *)aCol);
if( rc!=SQLITE_OK ){
if( p ){
}else{
int rc = sqlite3_reset(pCsr->pStmt);
if( rc==SQLITE_OK ){
- /* If no row was found and no error has occured, then the %_content
- ** table is missing a row that is present in the full-text index.
- ** The data structures are corrupt.
- */
- rc = SQLITE_CORRUPT_VTAB;
+ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
+ if( p->zContentTbl==0 ){
+ /* If no row was found and no error has occured, then the %_content
+ ** table is missing a row that is present in the full-text index.
+ ** The data structures are corrupt.
+ */
+ rc = SQLITE_CORRUPT_VTAB;
+ }else{
+ return SQLITE_OK;
+ }
}
pCsr->isEof = 1;
if( pContext ){
** row by docid.
*/
if( idxNum==FTS3_FULLSCAN_SEARCH ){
- const char *zSort = (pCsr->bDesc ? "DESC" : "ASC");
- const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s";
- zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort);
+ const char *zTmpl = "SELECT %s ORDER BY rowid %s";
+ zSql = sqlite3_mprintf(zTmpl,
+ p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
+ );
}else{
- const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?";
- zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName);
+ const char *zTmpl = "SELECT %s WHERE rowid = ?";
+ zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist);
}
if( !zSql ) return SQLITE_NOMEM;
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
}else{
rc = fts3CursorSeek(0, pCsr);
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){
sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
}
return rc;
}
- fts3DbExec(&rc, db,
- "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
- p->zDb, p->zName, zName
- );
+ if( p->zContentTbl==0 ){
+ fts3DbExec(&rc, db,
+ "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
+ p->zDb, p->zName, zName
+ );
+ }
+
if( p->bHasDocsize ){
fts3DbExec(&rc, db,
"ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';",
int nColumn; /* number of named columns in virtual table */
char **azColumn; /* column names. malloced */
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
+ char *zContentTbl; /* content=xxx option, or NULL */
/* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call.
i16 eSearch; /* Search strategy (see below) */
u8 isEof; /* True if at End Of Results */
u8 isRequireSeek; /* True if must seek pStmt to %_content row */
+ u8 isNullRow; /* True for a row of NULLs */
sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
Fts3Expr *pExpr; /* Parsed MATCH query string */
int nPhrase; /* Number of matchable phrases in query */
if( !pTerm ){
/* All offsets for this column have been gathered. */
- break;
+ rc = SQLITE_DONE;
}else{
assert( iCurrent<=iMinPos );
if( 0==(0xFE&*pTerm->pList) ){
"%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
);
rc = fts3StringAppend(&res, aBuffer, -1);
- }else if( rc==SQLITE_DONE ){
+ }else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){
rc = SQLITE_CORRUPT_VTAB;
}
}
/* 4 */ "DELETE FROM %Q.'%q_segdir'",
/* 5 */ "DELETE FROM %Q.'%q_docsize'",
/* 6 */ "DELETE FROM %Q.'%q_stat'",
-/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?",
+/* 7 */ "SELECT %s WHERE rowid=?",
/* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1",
/* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
if( eStmt==SQL_CONTENT_INSERT ){
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
- zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName);
+ zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
}else{
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
}
int rc; /* Return code */
sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */
+ if( p->zContentTbl ){
+ sqlite3_value *pRowid = apVal[p->nColumn+3];
+ if( sqlite3_value_type(pRowid)==SQLITE_NULL ){
+ pRowid = apVal[1];
+ }
+ if( sqlite3_value_type(pRowid)!=SQLITE_INTEGER ){
+ return SQLITE_CONSTRAINT;
+ }
+ *piDocid = sqlite3_value_int64(pRowid);
+ return SQLITE_OK;
+ }
+
/* Locate the statement handle used to insert data into the %_content
** table. The SQL for this statement is:
**
** Remove all data from the FTS3 table. Clear the hash table containing
** pending terms.
*/
-static int fts3DeleteAll(Fts3Table *p){
+static int fts3DeleteAll(Fts3Table *p, int bContent){
int rc = SQLITE_OK; /* Return code */
/* Discard the contents of the pending-terms hash table. */
sqlite3Fts3PendingTermsClear(p);
- /* Delete everything from the %_content, %_segments and %_segdir tables. */
- fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
+ /* Delete everything from the shadow tables. Except, leave %_content as
+ ** is if bContent is false. */
+ assert( p->zContentTbl==0 || bContent==0 );
+ if( bContent ) fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0);
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
if( p->bHasDocsize ){
static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
sqlite3_stmt *pStmt;
int rc;
- rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
- if( rc==SQLITE_OK ){
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
- *pisEmpty = sqlite3_column_int(pStmt, 0);
+ if( p->zContentTbl ){
+ /* If using the content=xxx option, assume the table is never empty */
+ *pisEmpty = 0;
+ rc = SQLITE_OK;
+ }else{
+ rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ *pisEmpty = sqlite3_column_int(pStmt, 0);
+ }
+ rc = sqlite3_reset(pStmt);
}
- rc = sqlite3_reset(pStmt);
}
return rc;
}
** a blob of varints.
*/
static void fts3InsertDocsize(
- int *pRC, /* Result code */
- Fts3Table *p, /* Table into which to insert */
- u32 *aSz /* Sizes of each column */
+ int *pRC, /* Result code */
+ Fts3Table *p, /* Table into which to insert */
+ u32 *aSz /* Sizes of each column, in tokens */
){
char *pBlob; /* The BLOB encoding of the document size */
int nBlob; /* Number of bytes in the BLOB */
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
}
+/*
+** This function is called when the user executes the following statement:
+**
+** INSERT INTO <tbl>(<tbl>) VALUES('rebuild');
+**
+** The entire FTS index is discarded and rebuilt. If the table is one
+** created using the content=xxx option, then the new index is based on
+** the current contents of the xxx table. Otherwise, it is rebuilt based
+** on the contents of the %_content table.
+*/
+static int fts3DoRebuild(Fts3Table *p){
+ int rc; /* Return Code */
+
+ rc = fts3DeleteAll(p, 0);
+ if( rc==SQLITE_OK ){
+ u32 *aSz = 0;
+ u32 *aSzIns;
+ u32 *aSzDel;
+ sqlite3_stmt *pStmt = 0;
+ int nEntry = 0;
+
+ /* Compose and prepare an SQL statement to loop through the content table */
+ char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ }
+
+ if( rc==SQLITE_OK ){
+ int nByte = sizeof(u32) * (p->nColumn+1)*3;
+ aSz = (u32 *)sqlite3_malloc(nByte);
+ if( aSz==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(aSz, 0, nByte);
+ aSzIns = &aSz[p->nColumn+1];
+ aSzDel = &aSzIns[p->nColumn+1];
+ }
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ int iCol;
+ rc = fts3PendingTermsDocid(p, sqlite3_column_int64(pStmt, 0));
+ aSz[p->nColumn] = 0;
+ for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
+ const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
+ rc = fts3PendingTermsAdd(p, z, iCol, &aSz[iCol]);
+ aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
+ }
+ if( p->bHasDocsize ){
+ fts3InsertDocsize(&rc, p, aSz);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+ }else{
+ nEntry++;
+ for(iCol=0; iCol<=p->nColumn; iCol++){
+ aSzIns[iCol] += aSz[iCol];
+ }
+ }
+ }
+ if( p->bHasStat ){
+ fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nEntry);
+ }
+ sqlite3_free(aSz);
+
+ if( pStmt ){
+ assert( rc==SQLITE_OK );
+ rc = sqlite3_finalize(pStmt);
+ }
+ }
+
+ return rc;
+}
+
/*
** Handle a 'special' INSERT of the form:
**
return SQLITE_NOMEM;
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
rc = fts3DoOptimize(p, 0);
+ }else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){
+ rc = fts3DoRebuild(p);
#ifdef SQLITE_TEST
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
p->nNodeSize = atoi(&zVal[9]);
/* Deleting this row means the whole table is empty. In this case
** delete the contents of all three tables and throw away any
** data in the pendingTerms hash table. */
- rc = fts3DeleteAll(p);
+ rc = fts3DeleteAll(p, 1);
*pnDoc = *pnDoc - 1;
}else{
sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
rc = fts3PendingTermsDocid(p, iRemove);
fts3DeleteTerms(&rc, p, pRowid, aSzDel);
- fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
- if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
+ if( p->zContentTbl==0 ){
+ fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
+ if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
+ }else{
+ *pnDoc = *pnDoc - 1;
+ }
if( p->bHasDocsize ){
fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
}
** detect the conflict and return SQLITE_CONSTRAINT before beginning to
** modify the database file.
*/
- if( nArg>1 ){
+ if( nArg>1 && p->zContentTbl==0 ){
/* Find the value object that holds the new rowid value. */
sqlite3_value *pNewRowid = apVal[3+p->nColumn];
if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
if( nArg>1 && rc==SQLITE_OK ){
if( bInsertDone==0 ){
rc = fts3InsertData(p, apVal, pRowid);
- if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT_VTAB;
+ if( rc==SQLITE_CONSTRAINT && p->zContentTbl==0 ){
+ rc = SQLITE_CORRUPT_VTAB;
+ }
}
if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){
rc = fts3PendingTermsDocid(p, *pRowid);
-C Update\sMSVC\smakefile\sto\sallow\stargets\sto\sbe\sbuilt\swith\ssupport\sfor\sICU.
-D 2011-10-02T05:23:16.470
+C Add\sexperimental\s'content'\soption\sto\sFTS4.
+D 2011-10-04T11:22:59.677
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts3/fts3.c 195e4da669741c1f097434ec48c0ba5739193af9
+F ext/fts3/fts3.c 626fcf477e531b470f06aae3d427abcda2d5d31c
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
-F ext/fts3/fts3Int.h 30063fdd0bc433b5db1532e3a363cb0f2f7e8eb3
+F ext/fts3/fts3Int.h a335d671d42ca07528a93c5b830e48e0205b26bc
F ext/fts3/fts3_aux.c 0ebfa7b86cf8ff6a0861605fcc63b83ec1b70691
F ext/fts3/fts3_expr.c 23791de01b3a5d313d76e02befd2601d4096bc2b
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
F ext/fts3/fts3_icu.c 6c8f395cdf9e1e3afa7fadb7e523dbbf381c6dfa
F ext/fts3/fts3_porter.c 8d946908f4812c005d3d33fcbe78418b1f4eb70c
-F ext/fts3/fts3_snippet.c 58b2ba2b934c1e2a2f6ac857d7f3c7e1a14b4532
+F ext/fts3/fts3_snippet.c 8838a1de5f7df3a559596870caaa4a9895248998
F ext/fts3/fts3_term.c a5457992723455a58804cb75c8cbd8978db5c2ef
F ext/fts3/fts3_test.c 24fa13f330db011500acb95590da9eee24951894
F ext/fts3/fts3_tokenizer.c 9ff7ec66ae3c5c0340fa081958e64f395c71a106
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
F ext/fts3/fts3_tokenizer1.c 0dde8f307b8045565cf63797ba9acfaff1c50c68
-F ext/fts3/fts3_write.c 194829c8fd024a448fc899e5ff02a8ed06595529
+F ext/fts3/fts3_write.c 9e14eb54310345b441d7a8c83bc3ae474230bea6
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2
F test/fts3sort.test 9a5176c9317bb545ec5f144d62e6fedb4da6c66e
F test/fts4aa.test 6e7f90420b837b2c685f3bcbe84c868492d40a68
+F test/fts4content.test fbafb7160eef1560b6a254b1ad5631acccd388a1
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
-F test/permutations.test ad17319066a90e2db71823c3ff104795ffc71b31
+F test/permutations.test 7b1c4cb40af3712e42364c82390ace0df207b5e0
F test/pragma.test c8108e01da04f16e67e5754e610bc62c1b993f6c
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings.sh b7fdb2cc525f5ef4fa43c80e771636dd3690f9d2
-P 9ddfe1e41300413bc9af7e5ce0ec9d1daf9136b1
-R 03863d4dd01ba5995289085115b33375
-U mistachkin
-Z 7dfdbd4e337132033acb3bc17ce78d60
+P eb5da5e1dbe9c198095036827318fb381441cbd0
+R 7991a3071fb34636c7b5ed4b010ad95a
+T *branch * fts4-content
+T *sym-fts4-content *
+T -sym-trunk *
+U dan
+Z 48ff718a3615a321d5bc0764cb014ed4
-eb5da5e1dbe9c198095036827318fb381441cbd0
\ No newline at end of file
+1d27ea741f61c624e18bdc6a3b1c2d8574a64ddc
\ No newline at end of file
--- /dev/null
+# 2011 October 03
+#
+# 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 implements regression tests for SQLite library. The
+# focus of this script is testing the content=xxx FTS4 option.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix fts4content
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+do_execsql_test 1.1 {
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES('w x', 'x y', 'y z');
+ CREATE VIRTUAL TABLE ft1 USING fts4(content=t1);
+}
+
+do_execsql_test 1.2 {
+ PRAGMA table_info(ft1);
+} {
+ 0 a {} 0 {} 0
+ 1 b {} 0 {} 0
+ 2 c {} 0 {} 0
+}
+
+do_execsql_test 1.3 { SELECT *, rowid FROM ft1 } {{w x} {x y} {y z} 1}
+do_execsql_test 1.4 { SELECT a, c FROM ft1 WHERE rowid=1 } {{w x} {y z}}
+
+do_execsql_test 1.5 { INSERT INTO ft1(ft1) VALUES('rebuild') } {}
+do_execsql_test 1.6 { SELECT rowid FROM ft1 WHERE ft1 MATCH 'x' } {1}
+do_execsql_test 1.7 { SELECT rowid FROM ft1 WHERE ft1 MATCH 'a' } {}
+
+#-------------------------------------------------------------------------
+# The following block of tests - 2.* - test that a content=xxx FTS table
+# can be queried. Also tested are cases where rows identified in the FTS
+# are missing from the content table, and cases where the index is
+# inconsistent with the content table.
+#
+do_execsql_test 2.0 {
+ CREATE TABLE t2(x);
+ INSERT INTO t2 VALUES('O S W W F U C R Q I C N P Z Y Y E Y Y E'); -- 1
+ INSERT INTO t2 VALUES('Y X U V L B E H Y J C Y A I A P V F V K'); -- 2
+ INSERT INTO t2 VALUES('P W I N J H I I N I F B K D U Q B Z S F'); -- 3
+ INSERT INTO t2 VALUES('N R O R H J R H G M D I U U B O M P A U'); -- 4
+ INSERT INTO t2 VALUES('Y O V O G T P N G T N F I V B U M J M G'); -- 5
+ INSERT INTO t2 VALUES('J O B N K N E C H Z R K J O U G M K L S'); -- 6
+ INSERT INTO t2 VALUES('S Z S R I Q U A P W R X H K C Z U L S P'); -- 7
+ INSERT INTO t2 VALUES('J C H N R C K R V N M O F Z M Z A I H W'); -- 8
+ INSERT INTO t2 VALUES('O Y G I S J U U W O D Z F J K N R P R L'); -- 9
+ INSERT INTO t2 VALUES('B G L K U R U P V X Z I H V R W C Q A S'); -- 10
+ INSERT INTO t2 VALUES('T F T J F F Y V F W N X K Q A Y L X W G'); -- 11
+ INSERT INTO t2 VALUES('C J U H B Q X L C M M Y E G V F W V Z C'); -- 12
+ INSERT INTO t2 VALUES('B W L T F S G X D P H N G M R I O A X I'); -- 13
+ INSERT INTO t2 VALUES('N G Y O K Q K Z N M H U J E D H U W R K'); -- 14
+ INSERT INTO t2 VALUES('U D T R U Y F J D S J X E H Q G V A S Z'); -- 15
+ INSERT INTO t2 VALUES('M I W P J S H R J D Q I C G P C T P H R'); -- 16
+ INSERT INTO t2 VALUES('J M N I S L X Q C A B F C B Y D H V R J'); -- 17
+ INSERT INTO t2 VALUES('F V Z W J Q L P X Y E W B U Q N H X K T'); -- 18
+ INSERT INTO t2 VALUES('R F S R Y O F Q E I E G H C B H R X Y N'); -- 19
+ INSERT INTO t2 VALUES('U Q Q Q T E P D M F X P J G H X C Q D L'); -- 20
+}
+
+do_execsql_test 2.1 {
+ CREATE VIRTUAL TABLE ft2 USING fts4(content=t2);
+ INSERT INTO ft2(ft2) VALUES('rebuild');
+
+ -- Modify the backing table a bit: Row 17 is missing and the contents
+ -- of row 20 do not match the FTS index contents.
+ DELETE FROM t2 WHERE rowid = 17;
+ UPDATE t2 SET x = 'a b c d e f g h i j' WHERE rowid = 20;
+}
+
+foreach {tn match rowidlist} {
+ 1 {S} {1 3 6 7 9 10 13 15 16 17 19}
+ 2 {"S R"} {7 19}
+ 3 {"N K N"} {6}
+ 4 {"Q Q"} {20}
+ 5 {"B Y D"} {17}
+} {
+ do_execsql_test 2.2.1.$tn {
+ SELECT rowid FROM ft2 WHERE ft2 MATCH $match
+ } $rowidlist
+
+ do_execsql_test 2.2.2.$tn {
+ SELECT docid FROM ft2 WHERE ft2 MATCH $match
+ } $rowidlist
+}
+
+foreach {tn match result} {
+ 1 {"N K N"} {{J O B N K N E C H Z R K J O U G M K L S}}
+ 2 {"Q Q"} {{a b c d e f g h i j}}
+ 3 {"B Y D"} {{}}
+} {
+ do_execsql_test 2.3.$tn {
+ SELECT * FROM ft2 WHERE ft2 MATCH $match
+ } $result
+}
+
+foreach {tn match result} {
+ 1 {"N K N"} {{..O B [N] [K] [N] E..}}
+ 2 {"B Y D"} {{}}
+ 3 {"Q Q"} {{a [b] [c] [d] e f..}}
+} {
+ do_execsql_test 2.4.$tn {
+ SELECT snippet(ft2, '[', ']', '..', -1, 6) FROM ft2 WHERE ft2 MATCH $match
+ } $result
+}
+
+foreach {tn match result} {
+ 1 {"N K N"} {{0 0 6 1 0 1 8 1 0 2 10 1}}
+ 2 {"B Y D"} {{}}
+ 3 {"Q Q"} {{0 0 2 1 0 0 4 1 0 1 4 1 0 1 6 1}}
+ 4 {"Q D L"} {{}}
+} {
+ do_execsql_test 2.5.$tn {
+ SELECT offsets(ft2) FROM ft2 WHERE ft2 MATCH $match
+ } $result
+}
+
+#-------------------------------------------------------------------------
+# The following block of tests - 3.* - test that the FTS index can be
+# modified by writing to the table. But that this has no effect on the
+# content table.
+#
+
+do_execsql_test 3.1 {
+ CREATE TABLE t3(x, y);
+ CREATE VIRTUAL TABLE ft3 USING fts4(content=t3);
+}
+
+do_catchsql_test 3.1.1 {
+ INSERT INTO ft3 VALUES('a b c', 'd e f');
+} {1 {constraint failed}}
+do_execsql_test 3.1.2 {
+ INSERT INTO ft3(docid, x, y) VALUES(21, 'a b c', 'd e f');
+ SELECT rowid FROM ft3 WHERE ft3 MATCH '"a b c"';
+} {21}
+do_execsql_test 3.1.3 { SELECT * FROM t3 } {}
+
+# This DELETE does not work, since there is no row in [t3] to base the
+# DELETE on. So the SELECT on [ft3] still returns rowid 21.
+do_execsql_test 3.1.4 {
+ DELETE FROM ft3;
+ SELECT rowid FROM ft3 WHERE ft3 MATCH '"a b c"';
+} {21}
+
+# If the row is added to [t3] before the DELETE on [ft3], it works.
+do_execsql_test 3.1.5 {
+ INSERT INTO t3(rowid, x, y) VALUES(21, 'a b c', 'd e f');
+ DELETE FROM ft3;
+ SELECT rowid FROM ft3 WHERE ft3 MATCH '"a b c"';
+} {}
+do_execsql_test 3.1.6 { SELECT rowid FROM t3 } {21}
+
+do_execsql_test 3.2.1 {
+ INSERT INTO ft3(rowid, x, y) VALUES(0, 'R T M S M', 'A F O K H');
+ INSERT INTO ft3(rowid, x, y) VALUES(1, 'C Z J O X', 'U S Q D K');
+ INSERT INTO ft3(rowid, x, y) VALUES(2, 'N G H P O', 'N O P O C');
+ INSERT INTO ft3(rowid, x, y) VALUES(3, 'V H S D R', 'K N G E C');
+ INSERT INTO ft3(rowid, x, y) VALUES(4, 'J T R V U', 'U X S L C');
+ INSERT INTO ft3(rowid, x, y) VALUES(5, 'N A Y N G', 'X D G P Y');
+ INSERT INTO ft3(rowid, x, y) VALUES(6, 'I Q I S P', 'D R O Q B');
+ INSERT INTO ft3(rowid, x, y) VALUES(7, 'T K T Z J', 'B W D G O');
+ INSERT INTO ft3(rowid, x, y) VALUES(8, 'Y K F X T', 'D F G V G');
+ INSERT INTO ft3(rowid, x, y) VALUES(9, 'E L E T L', 'P W N F Z');
+ INSERT INTO ft3(rowid, x, y) VALUES(10, 'O G J G X', 'G J F E P');
+ INSERT INTO ft3(rowid, x, y) VALUES(11, 'O L N N Z', 'K E Z F D');
+ INSERT INTO ft3(rowid, x, y) VALUES(12, 'R Z M R J', 'X G I M Z');
+ INSERT INTO ft3(rowid, x, y) VALUES(13, 'L X N N X', 'R R N S T');
+ INSERT INTO ft3(rowid, x, y) VALUES(14, 'F L B J H', 'K W F L C');
+ INSERT INTO ft3(rowid, x, y) VALUES(15, 'P E B M V', 'E A A B U');
+ INSERT INTO ft3(rowid, x, y) VALUES(16, 'V E C F P', 'L U T V K');
+ INSERT INTO ft3(rowid, x, y) VALUES(17, 'T N O Z N', 'T P Q X N');
+ INSERT INTO ft3(rowid, x, y) VALUES(18, 'V W U W R', 'H O A A V');
+ INSERT INTO ft3(rowid, x, y) VALUES(19, 'A H N L F', 'I G H B O');
+}
+
+foreach {tn match rowidlist} {
+ 1 "N A" {5 19}
+ 2 "x:O" {1 2 10 11 17}
+ 3 "y:O" {0 2 6 7 18 19}
+} {
+ set res [list]
+ foreach rowid $rowidlist { lappend res $rowid {} {} }
+
+ do_execsql_test 3.2.2.$tn {
+ SELECT rowid, * FROM ft3 WHERE ft3 MATCH $match
+ } $res
+ do_execsql_test 3.2.3.$tn {
+ SELECT docid, * FROM ft3 WHERE ft3 MATCH $match
+ } $res
+}
+
+do_execsql_test 3.3.1 {
+ INSERT INTO t3(rowid, x, y) VALUES(0, 'R T M S M', 'A F O K H');
+ INSERT INTO t3(rowid, x, y) VALUES(1, 'C Z J O X', 'U S Q D K');
+ INSERT INTO t3(rowid, x, y) VALUES(2, 'N G H P O', 'N O P O C');
+ INSERT INTO t3(rowid, x, y) VALUES(3, 'V H S D R', 'K N G E C');
+ INSERT INTO t3(rowid, x, y) VALUES(4, 'J T R V U', 'U X S L C');
+ INSERT INTO t3(rowid, x, y) VALUES(5, 'N A Y N G', 'X D G P Y');
+ UPDATE ft3 SET x = y, y = x;
+ DELETE FROM t3;
+}
+
+foreach {tn match rowidlist} {
+ 1 "N A" {5 19}
+ 2 "x:O" {0 2 10 11 17}
+ 3 "y:O" {1 2 6 7 18 19}
+} {
+ set res [list]
+ foreach rowid $rowidlist { lappend res $rowid {} {} }
+
+ do_execsql_test 3.3.2.$tn {
+ SELECT rowid, * FROM ft3 WHERE ft3 MATCH $match
+ } $res
+ do_execsql_test 3.3.3.$tn {
+ SELECT docid, * FROM ft3 WHERE ft3 MATCH $match
+ } $res
+}
+
+do_execsql_test 3.3.1 {
+ INSERT INTO t3(rowid, x, y) VALUES(15, 'P E B M V', 'E A A B U');
+ INSERT INTO t3(rowid, x, y) VALUES(16, 'V E C F P', 'L U T V K');
+ INSERT INTO t3(rowid, x, y) VALUES(17, 'T N O Z N', 'T P Q X N');
+ INSERT INTO t3(rowid, x, y) VALUES(18, 'V W U W R', 'H O A A V');
+ INSERT INTO t3(rowid, x, y) VALUES(19, 'A H N L F', 'I G H B O');
+ DELETE FROM ft3;
+}
+
+foreach {tn match rowidlist} {
+ 1 "N A" {5}
+ 2 "x:O" {0 2 10 11}
+ 3 "y:O" {1 2 6 7}
+} {
+ set res [list]
+ foreach rowid $rowidlist { lappend res $rowid {} {} }
+
+ do_execsql_test 3.3.2.$tn {
+ SELECT rowid, * FROM ft3 WHERE ft3 MATCH $match
+ } $res
+ do_execsql_test 3.3.3.$tn {
+ SELECT docid, * FROM ft3 WHERE ft3 MATCH $match
+ } $res
+}
+
+
+#-------------------------------------------------------------------------
+# Test cases 4.* test the 'rebuild' command. On content=xxx and regular
+# FTS tables.
+#
+do_execsql_test 4.0 {
+ CREATE TABLE t4(x);
+ CREATE VIRTUAL TABLE ft4 USING fts4(content=t4);
+ CREATE VIRTUAL TABLE ft4x USING fts4(x);
+}
+
+do_execsql_test 4.1.1 {
+ INSERT INTO ft4x(ft4x) VALUES('rebuild');
+ INSERT INTO ft4(ft4) VALUES('rebuild');
+} {}
+do_execsql_test 4.1.2 {
+ SELECT id, quote(value) FROM ft4_stat
+} {0 X'000000'}
+do_execsql_test 4.1.3 {
+ SELECT id, quote(value) FROM ft4x_stat
+} {0 X'000000'}
+
+do_execsql_test 4.2.1 {
+ INSERT INTO ft4x VALUES('M G M F T');
+ INSERT INTO ft4x VALUES('Z Q C A U');
+ INSERT INTO ft4x VALUES('N L L V');
+ INSERT INTO ft4x VALUES('T F D X D');
+ INSERT INTO ft4x VALUES('Z H I S D');
+
+ SELECT id, quote(value) FROM ft4x_stat
+} {0 X'05182B'}
+
+do_execsql_test 4.2.2 {
+ INSERT INTO ft4(rowid, x) SELECT rowid, * FROM ft4x;
+ SELECT id, quote(value) FROM ft4_stat
+} {0 X'05182B'}
+
+do_execsql_test 4.2.3 {
+ SELECT docid, quote(size) FROM ft4_docsize
+} {1 X'05' 2 X'05' 3 X'04' 4 X'05' 5 X'05'}
+
+do_execsql_test 4.2.4 {
+ INSERT INTO ft4x(ft4x) VALUES('rebuild');
+ SELECT id, quote(value) FROM ft4x_stat;
+ SELECT docid, quote(size) FROM ft4x_docsize
+} {0 X'05182B' 1 X'05' 2 X'05' 3 X'04' 4 X'05' 5 X'05'}
+
+do_execsql_test 4.2.5 {
+ INSERT INTO ft4(ft4) VALUES('rebuild');
+ SELECT id, quote(value) FROM ft4_stat;
+ SELECT docid, quote(size) FROM ft4_docsize
+} {0 X'000000'}
+
+do_execsql_test 4.2.6 {
+ INSERT INTO t4(rowid, x) SELECT rowid, x FROM ft4x;
+ INSERT INTO ft4(ft4) VALUES('rebuild');
+ SELECT id, quote(value) FROM ft4_stat;
+ SELECT docid, quote(size) FROM ft4_docsize
+} {0 X'05182B' 1 X'05' 2 X'05' 3 X'04' 4 X'05' 5 X'05'}
+
+
+#-------------------------------------------------------------------------
+# Test cases 5.* test that the following commands do not create/move or
+# delete a %_content table when used with a content=xxx FTS table.
+#
+do_execsql_test 5.1.1 {
+ CREATE TABLE t5(a, b, c, d);
+ CREATE VIRTUAL TABLE ft5 USING fts4(content=t5);
+ SELECT name FROM sqlite_master WHERE name LIKE '%t5%';
+} {
+ t5 ft5 ft5_segments ft5_segdir
+ sqlite_autoindex_ft5_segdir_1 ft5_docsize ft5_stat
+}
+do_execsql_test 5.1.2 {
+ ALTER TABLE ft5 RENAME TO ft6;
+ SELECT name FROM sqlite_master WHERE name LIKE '%t5%';
+} {
+ t5
+}
+do_execsql_test 5.1.3 {
+ SELECT name FROM sqlite_master WHERE name LIKE '%t6%';
+} {
+ ft6 ft6_segments ft6_segdir
+ sqlite_autoindex_ft6_segdir_1 ft6_docsize ft6_stat
+}
+do_execsql_test 5.1.4 {
+ INSERT INTO t5 VALUES('a', 'b', 'c', 'd');
+ INSERT INTO ft6(ft6) VALUES('rebuild');
+ SELECT rowid FROM ft6 WHERE ft6 MATCH 'b';
+} {1}
+do_execsql_test 5.1.5 {
+ DROP TABLE ft6;
+ SELECT * FROM t5;
+} {a b c d}
+do_execsql_test 5.1.6 {
+ SELECT name FROM sqlite_master WHERE name LIKE '%t6%';
+} {
+}
+do_execsql_test 5.1.7 {
+ CREATE VIRTUAL TABLE ft5 USING fts4(content=t5);
+ CREATE TABLE t5_content(a, b);
+ DROP TABLE ft5;
+ SELECT name FROM sqlite_master WHERE name LIKE '%t5%';
+} {
+ t5 t5_content
+}
+
+finish_test
fts3defer.test fts3defer2.test fts3e.test fts3expr.test fts3expr2.test
fts3near.test fts3query.test fts3shared.test fts3snippet.test
fts3sort.test
-
fts3fault.test fts3malloc.test fts3matchinfo.test
-
fts3aux1.test fts3comp1.test fts3auto.test
+ fts4aa.test fts4content.test
}