From: dan Date: Tue, 4 Oct 2011 11:22:59 +0000 (+0000) Subject: Add experimental 'content' option to FTS4. X-Git-Tag: version-3.7.9~18^2~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d8e454e19ac21660a134c7167c0e5787111c7504;p=thirdparty%2Fsqlite.git Add experimental 'content' option to FTS4. FossilOrigin-Name: 1d27ea741f61c624e18bdc6a3b1c2d8574a64ddc --- diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 29e071a50f..c188945b28 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -468,6 +468,7 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ 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); @@ -507,16 +508,19 @@ static void fts3DbExec( ** 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. @@ -578,23 +582,27 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){ 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 && inColumn; 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 && inColumn; 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);", @@ -745,8 +753,8 @@ static char *fts3QuoteId(char const *zInput){ } /* -** 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 FROM %_content AS x ... ** @@ -757,7 +765,7 @@ static char *fts3QuoteId(char const *zInput){ ** 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. @@ -773,16 +781,28 @@ static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){ 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; inColumn; 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; inColumn; i++){ - fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); + fts3Appendf(pRc, &zRet, "rowid"); + for(i=0; inColumn; 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; } @@ -906,6 +926,91 @@ static int fts3PrefixParameter( 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 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; @@ -1052,6 +1158,12 @@ static int fts3InitVtab( } bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); break; + + case 5: /* CONTENT */ + sqlite3_free(zUncompress); + zContent = zVal; + zVal = 0; + break; } } sqlite3_free(zVal); @@ -1064,6 +1176,27 @@ static int fts3InitVtab( 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 ){ @@ -1108,6 +1241,8 @@ static int fts3InitVtab( p->bHasDocsize = (isFts4 && bNoDocsize==0); p->bHasStat = isFts4; p->bDescIdx = bDescIdx; + p->zContentTbl = zContent; + zContent = 0; TESTONLY( p->inTransaction = -1 ); TESTONLY( p->mxSavepoint = -1 ); @@ -1169,6 +1304,7 @@ fts3_init_out: sqlite3_free(aIndex); sqlite3_free(zCompress); sqlite3_free(zUncompress); + sqlite3_free(zContent); sqlite3_free((void *)aCol); if( rc!=SQLITE_OK ){ if( p ){ @@ -1334,11 +1470,16 @@ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ }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 ){ @@ -2713,12 +2854,13 @@ static int fts3FilterMethod( ** 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); @@ -2781,7 +2923,7 @@ static int fts3ColumnMethod( 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)); } } @@ -3079,10 +3221,13 @@ static int fts3RenameMethod( 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';", diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index ed8043adf6..b4f26fc7a5 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -184,6 +184,7 @@ struct Fts3Table { 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. @@ -245,6 +246,7 @@ struct Fts3Cursor { 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 */ diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c index b569eb131b..13d0ca3551 100644 --- a/ext/fts3/fts3_snippet.c +++ b/ext/fts3/fts3_snippet.c @@ -1409,7 +1409,7 @@ void sqlite3Fts3Offsets( if( !pTerm ){ /* All offsets for this column have been gathered. */ - break; + rc = SQLITE_DONE; }else{ assert( iCurrent<=iMinPos ); if( 0==(0xFE&*pTerm->pList) ){ @@ -1426,7 +1426,7 @@ void sqlite3Fts3Offsets( "%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; } } diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 36f2249e12..46a1ead2d8 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -256,7 +256,7 @@ static int fts3SqlStmt( /* 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)", @@ -298,7 +298,7 @@ static int fts3SqlStmt( 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); } @@ -780,6 +780,18 @@ static int fts3InsertData( 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: ** @@ -830,14 +842,16 @@ static int fts3InsertData( ** 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 ){ @@ -2125,12 +2139,18 @@ static void fts3SegWriterFree(SegmentWriter *pWriter){ 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; } @@ -2787,9 +2807,9 @@ static void fts3DecodeIntArray( ** 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 */ @@ -2911,6 +2931,84 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){ return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc; } +/* +** This function is called when the user executes the following statement: +** +** INSERT INTO () 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 && iColnColumn; 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: ** @@ -2928,6 +3026,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ 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]); @@ -3099,14 +3199,18 @@ static int fts3DeleteByRowid( /* 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); } @@ -3167,7 +3271,7 @@ int sqlite3Fts3UpdateMethod( ** 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 ){ @@ -3219,7 +3323,9 @@ int sqlite3Fts3UpdateMethod( 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); diff --git a/manifest b/manifest index 916f02c341..6ca44ed83d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -62,22 +62,22 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 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 @@ -486,6 +486,7 @@ F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2 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 @@ -620,7 +621,7 @@ F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806 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 @@ -965,7 +966,10 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5 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 diff --git a/manifest.uuid b/manifest.uuid index c4bc7f0c61..be6a47c58b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eb5da5e1dbe9c198095036827318fb381441cbd0 \ No newline at end of file +1d27ea741f61c624e18bdc6a3b1c2d8574a64ddc \ No newline at end of file diff --git a/test/fts4content.test b/test/fts4content.test new file mode 100644 index 0000000000..6ac5a6806a --- /dev/null +++ b/test/fts4content.test @@ -0,0 +1,366 @@ +# 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 diff --git a/test/permutations.test b/test/permutations.test index c911e6f062..9ae881ba23 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -180,10 +180,9 @@ test_suite "fts3" -prefix "" -description { 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 }