return rc;
}
+static int fts5SpecialDelete(
+ Fts5Table *pTab,
+ sqlite3_value **apVal,
+ sqlite3_int64 *piRowid
+){
+ int rc = SQLITE_OK;
+ int eType1 = sqlite3_value_type(apVal[1]);
+ if( eType1==SQLITE_INTEGER ){
+ sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]);
+ rc = sqlite3Fts5StorageSpecialDelete(pTab->pStorage, iDel, &apVal[2]);
+ }
+ return rc;
+}
+
/*
** This function is the implementation of the xUpdate callback used by
** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
*/
assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) );
- if( nArg>1 && SQLITE_NULL!=sqlite3_value_type(apVal[2 + pConfig->nCol]) ){
- return fts5SpecialInsert(pTab,
- apVal[2 + pConfig->nCol], apVal[2 + pConfig->nCol + 1]
- );
+ if( nArg>1 ){
+ sqlite3_value *pCmd = sqlite3_value_type(apVal[2 + pConfig->nCol]);
+ if( SQLITE_NULL!=sqlite3_value_type(pCmd) ){
+ const char *z = sqlite3_value_text(pCmd);
+ if( pConfig->bExternalContent && sqlite3_stricmp("delete", z) ){
+ return fts5SpecialDelete(pTab, apVal, pRowid);
+ }else{
+ return fts5SpecialInsert(pTab, pCmd, apVal[2 + pConfig->nCol + 1]);
+ }
+ }
}
eType0 = sqlite3_value_type(apVal[0]);
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
- if( eType0==SQLITE_INTEGER ){
+ assert( pVtab->zErrMsg==0 );
+
+ if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel);
}
char **azCol; /* Column names */
int nPrefix; /* Number of prefix indexes */
int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
+ int bExternalContent; /* Content is external */
+ char *zContent; /* "content=" option value (or NULL) */
+ char *zContentRowid; /* "content_rowid=" option value (or NULL) */
Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi;
int sqlite3Fts5StorageConfigValue(Fts5Storage *p, const char*, sqlite3_value*);
+int sqlite3Fts5StorageSpecialDelete(Fts5Storage *p, i64 iDel, sqlite3_value**);
+
/*
** End of interface to code in fts5_storage.c.
**************************************************************************/
return z;
}
+/*
+** Duplicate the string passed as the only argument into a buffer allocated
+** by sqlite3_malloc().
+**
+** Return 0 if an OOM error is encountered.
+*/
+static char *fts5Strdup(int *pRc, const char *z){
+ char *pRet = 0;
+ if( *pRc==SQLITE_OK ){
+ pRet = sqlite3_mprintf("%s", z);
+ if( pRet==0 ) *pRc = SQLITE_NOMEM;
+ }
+ return pRet;
+}
+
+/*
+** Argument z points to a nul-terminated string containing an SQL identifier.
+** This function returns a copy of the identifier enclosed in backtick
+** quotes.
+*/
+static char *fts5EscapeName(int *pRc, const char *z){
+ char *pRet = 0;
+ if( *pRc==SQLITE_OK ){
+ int n = strlen(z);
+ pRet = (char*)sqlite3_malloc(2 * 2*n + 1);
+ if( pRet==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ int i;
+ char *p = pRet;
+ for(i=0; i<n; i++){
+ if( z[i]=='`' ) *p++ = '`';
+ *p++ = z[i];
+ }
+ *p++ = '`';
+ *p++ = '\0';
+ }
+ }
+ return pRet;
+}
+
/*
** Parse the "special" CREATE VIRTUAL TABLE directive and update
** configuration object pConfig as appropriate.
return rc;
}
- *pzErr = sqlite3_mprintf("unrecognized directive: \"%s\"", zCmd);
- return SQLITE_ERROR;
-}
+ if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){
+ int rc = SQLITE_OK;
+ if( pConfig->zContent ){
+ *pzErr = sqlite3_mprintf("multiple content=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->zContent = sqlite3_mprintf("%Q.%Q", pConfig->zDb, zArg);
+ pConfig->bExternalContent = 1;
+ if( pConfig->zContent==0 ) rc = SQLITE_NOMEM;
+ }
+ return rc;
+ }
-/*
-** Duplicate the string passed as the only argument into a buffer allocated
-** by sqlite3_malloc().
-**
-** Return 0 if an OOM error is encountered.
-*/
-static char *fts5Strdup(int *pRc, const char *z){
- char *pRet = 0;
- if( *pRc==SQLITE_OK ){
- pRet = sqlite3_mprintf("%s", z);
- if( pRet==0 ) *pRc = SQLITE_NOMEM;
+ if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
+ int rc = SQLITE_OK;
+ if( pConfig->zContentRowid ){
+ *pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->zContentRowid = fts5EscapeName(&rc, zArg);
+ }
+ return rc;
}
- return pRet;
+
+ *pzErr = sqlite3_mprintf("unrecognized directive: \"%s\"", zCmd);
+ return SQLITE_ERROR;
}
/*
rc = fts5ConfigDefaultTokenizer(pGlobal, pRet);
}
+ /* If no zContent option was specified, fill in the default values. */
+ if( rc==SQLITE_OK && pRet->zContent==0 ){
+ pRet->zContent = sqlite3_mprintf("%Q.'%q_content'", pRet->zDb, pRet->zName);
+ if( pRet->zContent==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_free(pRet->zContentRowid);
+ pRet->zContentRowid = 0;
+ }
+ }
+ if( rc==SQLITE_OK && pRet->zContentRowid==0 ){
+ pRet->zContentRowid = fts5Strdup(&rc, "rowid");
+ }
+
if( rc!=SQLITE_OK ){
sqlite3Fts5ConfigFree(pRet);
*ppOut = 0;
sqlite3_free(pConfig->aPrefix);
sqlite3_free(pConfig->zRank);
sqlite3_free(pConfig->zRankArgs);
+ sqlite3_free(pConfig->zContent);
+ sqlite3_free(pConfig->zContentRowid);
sqlite3_free(pConfig);
}
}
** It is the responsibility of the caller to eventually free the returned
** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned.
*/
-static char *fts5Strndup(const char *pIn, int nIn){
- char *zRet = (char*)sqlite3_malloc(nIn+1);
- if( zRet ){
- memcpy(zRet, pIn, nIn);
- zRet[nIn] = '\0';
+static char *fts5Strndup(int *pRc, const char *pIn, int nIn){
+ char *zRet = 0;
+ if( *pRc==SQLITE_OK ){
+ zRet = (char*)sqlite3_malloc(nIn+1);
+ if( zRet ){
+ memcpy(zRet, pIn, nIn);
+ zRet[nIn] = '\0';
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
}
return zRet;
}
static int fts5ParseStringFromToken(Fts5Token *pToken, char **pz){
- *pz = fts5Strndup(pToken->p, pToken->n);
- if( *pz==0 ) return SQLITE_NOMEM;
- return SQLITE_OK;
+ int rc = SQLITE_OK;
+ *pz = fts5Strndup(&rc, pToken->p, pToken->n);
+ return rc;
}
/*
int iEnd, /* End offset of token */
int iPos /* Position offset of token */
){
+ int rc = SQLITE_OK;
const int SZALLOC = 8;
TokenCtx *pCtx = (TokenCtx*)pContext;
Fts5ExprPhrase *pPhrase = pCtx->pPhrase;
pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
memset(pTerm, 0, sizeof(Fts5ExprTerm));
- pTerm->zTerm = fts5Strndup(pToken, nToken);
+ pTerm->zTerm = fts5Strndup(&rc, pToken, nToken);
- return pTerm->zTerm ? SQLITE_OK : SQLITE_NOMEM;
+ return rc;
}
assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
if( p->aStmt[eStmt]==0 ){
const char *azStmt[] = {
- "SELECT * FROM %Q.'%q_content' ORDER BY id ASC", /* SCAN_ASC */
- "SELECT * FROM %Q.'%q_content' ORDER BY id DESC", /* SCAN_DESC */
- "SELECT * FROM %Q.'%q_content' WHERE rowid=?", /* LOOKUP */
+ "SELECT * FROM %s ORDER BY id ASC", /* SCAN_ASC */
+ "SELECT * FROM %s ORDER BY id DESC", /* SCAN_DESC */
+ "SELECT * FROM %s WHERE %s=?", /* LOOKUP */
"INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
"REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
"REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
};
- Fts5Config *pConfig = p->pConfig;
+ Fts5Config *pC = p->pConfig;
char *zSql = 0;
- if( eStmt==FTS5_STMT_INSERT_CONTENT || eStmt==FTS5_STMT_REPLACE_CONTENT ){
- int nCol = pConfig->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] = ',';
+ switch( eStmt ){
+ case FTS5_STMT_SCAN_ASC:
+ case FTS5_STMT_SCAN_DESC:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContent);
+ break;
+
+ case FTS5_STMT_LOOKUP:
+ zSql = sqlite3_mprintf(azStmt[eStmt], 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);
}
- zBind[i*2-1] = '\0';
- zSql = sqlite3_mprintf(azStmt[eStmt],pConfig->zDb,pConfig->zName,zBind);
- sqlite3_free(zBind);
+ break;
}
- }else{
- zSql = sqlite3_mprintf(azStmt[eStmt], pConfig->zDb, pConfig->zName);
+
+ default:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
+ break;
}
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
- rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p->aStmt[eStmt], 0);
+ rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0);
sqlite3_free(zSql);
}
}
p->pIndex = pIndex;
if( bCreate ){
- int i;
- char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
- if( zDefn==0 ){
- rc = SQLITE_NOMEM;
- }else{
- int iOff = sprintf(zDefn, "id INTEGER PRIMARY KEY");
- for(i=0; i<pConfig->nCol; i++){
- iOff += sprintf(&zDefn[iOff], ", c%d", i);
+ if( pConfig->bExternalContent==0 ){
+ char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
+ if( zDefn==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ int iOff = sprintf(zDefn, "id INTEGER PRIMARY KEY");
+ for(i=0; i<pConfig->nCol; i++){
+ iOff += sprintf(&zDefn[iOff], ", c%d", i);
+ }
+ rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
}
- rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
+ sqlite3_free(zDefn);
}
- sqlite3_free(zDefn);
+
if( rc==SQLITE_OK ){
rc = sqlite3Fts5CreateTable(
pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
return rc;
}
+int sqlite3Fts5StorageSpecialDelete(
+ Fts5Storage *p,
+ i64 iDel,
+ sqlite3_value **apVal
+){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+ sqlite3_stmt *pDel;
+
+ assert( p->pConfig->bExternalContent );
+ 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++){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ (const char*)sqlite3_value_text(apVal[iCol]),
+ sqlite3_value_bytes(apVal[iCol]),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
+ }
+ p->nTotalRow--;
+ }
+
+ /* Delete the %_docsize record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel);
+ }
+ 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;
+
+}
+
+/*
+** 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.
+*/
+static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
+ sqlite3_stmt *pReplace = 0;
+ int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace);
+ 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.
*/
rc = fts5StorageLoadTotals(p, 1);
/* Insert the new row into the %_content table. */
- if( rc==SQLITE_OK ){
- if( eConflict==SQLITE_REPLACE ){
- eStmt = FTS5_STMT_REPLACE_CONTENT;
+ if( rc==SQLITE_OK && pConfig->bExternalContent==0 ){
+ if( pConfig->bExternalContent ){
if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
- rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1]));
+ *piRowid = sqlite3_value_int64(apVal[1]);
+ }else{
+ rc = fts5StorageNewRowid(p, piRowid);
}
}else{
- eStmt = FTS5_STMT_INSERT_CONTENT;
+ if( eConflict==SQLITE_REPLACE ){
+ eStmt = FTS5_STMT_REPLACE_CONTENT;
+ if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
+ rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1]));
+ }
+ }else{
+ eStmt = FTS5_STMT_INSERT_CONTENT;
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, eStmt, &pInsert);
+ }
+ 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);
}
}
- if( rc==SQLITE_OK ){
- rc = fts5StorageGetStmt(p, eStmt, &pInsert);
- }
- 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 ){
-C Allow\sthe\srank\scolumn\sto\sbe\sremapped\son\sa\sper-query\sbasis\sby\sincluding\sa\sterm\ssimilar\sto\s"rank\smatch\s'bm25(10,2)'"\sin\sa\swhere\sclause.
-D 2015-01-02T14:55:22.175
+C Add\ssupport\sfor\sexternal\scontent\stables\sto\sfts5.
+D 2015-01-03T20:44:58.134
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 7cd23e4fc91004a6bd081623e1bc6932e44828c0
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
F ext/fts3/unicode/mkunicode.tcl 4199cb887040ee3c3cd59a5171ddb0566904586e
F ext/fts5/extract_api_docs.tcl 6320db4a1d0722a4e2069e661381ad75e9889786
-F ext/fts5/fts5.c a80283dca24506f1c748fffbba8d87ae4d348b50
+F ext/fts5/fts5.c 16177d7f81af1852cf7f477b5ae119215ad6044a
F ext/fts5/fts5.h 4f9d2c477c0ee1907164642471329a82cb6b203b
-F ext/fts5/fts5Int.h b5d7970b851d2b4f1745cd2d5c95216c9847aef2
+F ext/fts5/fts5Int.h 8062dc2363c863dc8a5b2e5651cb8c966bd6c4cb
F ext/fts5/fts5_aux.c 445e54031ff94174673f4f5aac6c064df20a2a6b
F ext/fts5/fts5_buffer.c 1bc5c762bb2e9b4a40b2e8a820a31b809e72eec1
-F ext/fts5/fts5_config.c 74a860e10c5583831f04d0088c4a49a3c6eca43d
-F ext/fts5/fts5_expr.c 27d3d2deebae277c34ae2bb3d501dd879c442ba5
+F ext/fts5/fts5_config.c 16d647c7bfe50d4e823267188e12e2d001d655e0
+F ext/fts5/fts5_expr.c 317093f00a2ccdaaee0a5290f9f228c600189c41
F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279
F ext/fts5/fts5_index.c 4a8e8535b4303400ddb5f6fb08152da0d88ebf6f
-F ext/fts5/fts5_storage.c 13794781977c9a624eb8bd7b9509de241e405853
+F ext/fts5/fts5_storage.c b95fcca70f94656854e7afcfbb9896455f6b034d
F ext/fts5/fts5_tcl.c 664e710e2bbeed505cb91848772ca7538623a67f
F ext/fts5/fts5_tokenize.c 5a0ad46408d09bcda2bf0addb5af42fdb75ebabb
F ext/fts5/fts5_unicode2.c 9c7dd640d1f014bf5c3ee029759adfbb4d7e95a9
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 4b3651677e7132c4c45605bc1f216fc08ef31198
-R 691df06fdaf9c3542bb10cf702e4a0f8
+P 1cd15a1759004d5d321056905dbb6acff20dc7d9
+R d21eb6bee3b06e51f22d32d1e0bd7016
U dan
-Z 12262406c5f3f18d2ab88add956e21a6
+Z e88e77f44b464406d3184a89736eaa7d
-1cd15a1759004d5d321056905dbb6acff20dc7d9
\ No newline at end of file
+17ef5b59f789e9fa35c4f053246d819987fd06f8
\ No newline at end of file