mirror of
https://github.com/sqlite/sqlite.git
synced 2025-12-24 14:17:58 +03:00
Allow UPDATEs of unindexed columns in fts5 contentless_unindexed=1 tables. Testing to come.
FossilOrigin-Name: cd36d66c88d7282eb0a3ccde5713253f72f5843e451b2693b71adfdae28b41fb
This commit is contained in:
@@ -726,7 +726,7 @@ int sqlite3Fts5DropAll(Fts5Config*);
|
||||
int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **);
|
||||
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int);
|
||||
int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
|
||||
int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*);
|
||||
int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
|
||||
|
||||
int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg);
|
||||
|
||||
@@ -1801,7 +1801,7 @@ static void fts5StorageInsert(
|
||||
){
|
||||
int rc = *pRc;
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid);
|
||||
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid);
|
||||
@@ -1809,6 +1809,45 @@ static void fts5StorageInsert(
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
static int fts5ContentlessUpdateOk(
|
||||
Fts5Config *pConfig,
|
||||
sqlite3_value **apVal,
|
||||
i64 iOld,
|
||||
i64 iNew,
|
||||
int *pbContent
|
||||
){
|
||||
int ii;
|
||||
int bSeenIndex = 0;
|
||||
int bSeenIndexNC = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
for(ii=0; ii<pConfig->nCol; ii++){
|
||||
if( pConfig->abUnindexed[ii]==0 ){
|
||||
if( sqlite3_value_nochange(apVal[ii]) ){
|
||||
bSeenIndexNC++;
|
||||
}else{
|
||||
bSeenIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( bSeenIndex==0 && iOld==iNew ){
|
||||
*pbContent = 1;
|
||||
}else{
|
||||
if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3Fts5ConfigErrmsg(pConfig,
|
||||
(pConfig->bContentlessDelete ?
|
||||
"%s a subset of columns on fts5 contentless-delete table: %s" :
|
||||
"%s contentless fts5 table: %s")
|
||||
, "cannot UPDATE", pConfig->zName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1895,25 +1934,35 @@ static int fts5UpdateMethod(
|
||||
assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
|
||||
assert( nArg!=1 || eType0==SQLITE_INTEGER );
|
||||
|
||||
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
|
||||
** This is not suported. Except - they are both supported if the CREATE
|
||||
** VIRTUAL TABLE statement contained "contentless_delete=1". */
|
||||
if( eType0==SQLITE_INTEGER
|
||||
&& fts5IsContentless(pTab, 1)
|
||||
&& pConfig->bContentlessDelete==0
|
||||
){
|
||||
pTab->p.base.zErrMsg = sqlite3_mprintf(
|
||||
"cannot %s contentless fts5 table: %s",
|
||||
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
/*
|
||||
** Extra rule for contentless tables:
|
||||
**
|
||||
** DELETE:
|
||||
** It is only possible to DELETE if the contentless_delete=1 flag
|
||||
** is set.
|
||||
**
|
||||
** UPDATE:
|
||||
** A "content-only" UPDATE is one that only affects UNINDEXED
|
||||
** columns. And that does not modify the rowid value.
|
||||
**
|
||||
** If the contentless_delete flag is clear, then content-only UPDATEs
|
||||
** are the only ones supported. Or, if contentless_delete=1 is set,
|
||||
** then updates that modify all indexed columns are also supported.
|
||||
** If all indexed columns are updated, then rowid updates are allowed.
|
||||
*/
|
||||
|
||||
/* DELETE */
|
||||
else if( nArg==1 ){
|
||||
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0);
|
||||
bUpdateOrDelete = 1;
|
||||
if( nArg==1 ){
|
||||
if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){
|
||||
fts5SetVtabError(pTab,
|
||||
"cannot DELETE from contentless fts5 table: %s", pConfig->zName
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0);
|
||||
bUpdateOrDelete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* INSERT or UPDATE */
|
||||
@@ -1947,35 +1996,49 @@ static int fts5UpdateMethod(
|
||||
|
||||
/* UPDATE */
|
||||
else{
|
||||
Fts5Storage *pStorage = pTab->pStorage;
|
||||
i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
|
||||
i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
|
||||
int bContent = 0; /* Content only update */
|
||||
|
||||
if( fts5IsContentless(pTab, 1) ){
|
||||
rc = fts5ContentlessUpdateOk(pConfig,&apVal[2],iOld,iNew,&bContent);
|
||||
if( rc!=SQLITE_OK ) goto update_out;
|
||||
}
|
||||
|
||||
if( eType1!=SQLITE_INTEGER ){
|
||||
rc = SQLITE_MISMATCH;
|
||||
}else if( iOld!=iNew ){
|
||||
assert( bContent==0 );
|
||||
if( eConflict==SQLITE_REPLACE ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1);
|
||||
rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0);
|
||||
rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0);
|
||||
}
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageFindDeleteRow(pTab->pStorage, iOld);
|
||||
rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage,apVal,pRowid);
|
||||
rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1);
|
||||
rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid);
|
||||
rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal,*pRowid);
|
||||
}
|
||||
}
|
||||
}else if( bContent ){
|
||||
rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid);
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1);
|
||||
rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}
|
||||
bUpdateOrDelete = 1;
|
||||
sqlite3Fts5StorageReleaseDeleteRow(pTab->pStorage);
|
||||
sqlite3Fts5StorageReleaseDeleteRow(pStorage);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2968,18 +3031,7 @@ static int fts5ColumnMethod(
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* A column created by the user containing values. */
|
||||
int bNochange = sqlite3_vtab_nochange(pCtx);
|
||||
|
||||
if( bNochange ){
|
||||
if( pConfig->bContentlessDelete
|
||||
&& (pConfig->eContent==FTS5_CONTENT_NONE || !pConfig->abUnindexed[iCol])
|
||||
){
|
||||
fts5ResultError(pCtx, "cannot UPDATE a subset of "
|
||||
"columns on fts5 contentless-delete table: %s", pConfig->zName
|
||||
);
|
||||
}
|
||||
}else if( pConfig->eContent!=FTS5_CONTENT_NONE ){
|
||||
if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){
|
||||
pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
|
||||
rc = fts5SeekCursor(pCsr, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
@@ -141,7 +141,8 @@ static int fts5StorageGetStmt(
|
||||
);
|
||||
break;
|
||||
|
||||
case FTS5_STMT_INSERT_CONTENT: {
|
||||
case FTS5_STMT_INSERT_CONTENT:
|
||||
case FTS5_STMT_REPLACE_CONTENT: {
|
||||
char *zBind = 0;
|
||||
int i;
|
||||
|
||||
@@ -930,6 +931,7 @@ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
|
||||
*/
|
||||
int sqlite3Fts5StorageContentInsert(
|
||||
Fts5Storage *p,
|
||||
int bReplace, /* True to use REPLACE instead of INSERT */
|
||||
sqlite3_value **apVal,
|
||||
i64 *piRowid
|
||||
){
|
||||
@@ -948,7 +950,10 @@ int sqlite3Fts5StorageContentInsert(
|
||||
}else{
|
||||
sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */
|
||||
int i; /* Counter variable */
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0);
|
||||
|
||||
assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT );
|
||||
assert( bReplace==0 || bReplace==1 );
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT+bReplace, &pInsert, 0);
|
||||
if( pInsert ) sqlite3_clear_bindings(pInsert);
|
||||
|
||||
/* Bind the rowid value */
|
||||
|
||||
@@ -255,5 +255,43 @@ do_execsql_test 5.6 {
|
||||
{{} two {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that it is possible to UPDATE a contentless_unindexed=1 table
|
||||
# if the only columns being modified are UNINDEXED.
|
||||
#
|
||||
# If the contentless_unindexed=1 table is also contentless_delete=1, then
|
||||
# it is also possible to update indexed columns - but only if *all* indexed
|
||||
# columns are updated.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(a, b UNINDEXED, c UNINDEXED, d,
|
||||
contentless_unindexed=1, content=''
|
||||
);
|
||||
|
||||
INSERT INTO ft1(rowid, a, b, c, d) VALUES
|
||||
(100, 'x y', 'b1', 'c1', 'a b'),
|
||||
(200, 'c d', 'b2', 'c2', 'a b'),
|
||||
(300, 'e f', 'b3', 'c3', 'a b');
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
UPDATE ft1 SET b='b1.1', c='c1.1' WHERE rowid=100;
|
||||
}
|
||||
do_execsql_test 6.2 {
|
||||
UPDATE ft1 SET b='b2.1' WHERE rowid=200;
|
||||
}
|
||||
do_execsql_test 6.3 {
|
||||
UPDATE ft1 SET c='c3.1' WHERE rowid=300;
|
||||
}
|
||||
|
||||
do_execsql_test 6.4 {
|
||||
SELECT rowid, a, b, c, d FROM ft1
|
||||
} {
|
||||
100 {} b1.1 c1.1 {}
|
||||
200 {} b2.1 c2 {}
|
||||
300 {} b3 c3.1 {}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
18
manifest
18
manifest
@@ -1,5 +1,5 @@
|
||||
C Prevent\sregular\sDELETE\sand\sUPDATE\sstatements\sfrom\srunning\sagainst\scontentless_unindexed=1\stables\sthat\sare\snot\salso\scontentless_delete=1.
|
||||
D 2024-09-25T12:03:08.340
|
||||
C Allow\sUPDATEs\sof\sunindexed\scolumns\sin\sfts5\scontentless_unindexed=1\stables.\sTesting\sto\scome.
|
||||
D 2024-09-27T10:57:41.777
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -93,15 +93,15 @@ F ext/fts3/unicode/mkunicode.tcl 63db9624ccf70d4887836c320eda93ab552f21008f3be7e
|
||||
F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb
|
||||
F ext/fts5/extract_api_docs.tcl 009cf59c77afa86d137b0cca3e3b1a5efbe2264faa2df233f9a7aa8563926d15
|
||||
F ext/fts5/fts5.h efaaac0df3d3bc740383044c144b582f47921aafa21d7b10eb98f42c24c740b0
|
||||
F ext/fts5/fts5Int.h 927772e795bc897a210630296531c6a397b247f0b6f65ef9e9dc8e03baa77d1d
|
||||
F ext/fts5/fts5Int.h bf0d3efa144f36e00f9b5206626aec2f436f58186a0835092394f2202e9828e3
|
||||
F ext/fts5/fts5_aux.c 65a0468dd177d6093aa9ae1622e6d86b0136b8d267c62c0ad6493ad1e9a3d759
|
||||
F ext/fts5/fts5_buffer.c 0eec58bff585f1a44ea9147eae5da2447292080ea435957f7488c70673cb6f09
|
||||
F ext/fts5/fts5_config.c a6633d88596758941c625b526075b85d3d9fd1089d8d9eab5db6e8a71fd347ad
|
||||
F ext/fts5/fts5_expr.c 9a56f53700d1860f0ee2f373c2b9074eaf2a7aa0637d0e27a6476de26a3fee33
|
||||
F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1
|
||||
F ext/fts5/fts5_index.c 571483823193f09439356741669aa8c81da838ae6f5e1bfa7517f7ee2fb3addd
|
||||
F ext/fts5/fts5_main.c 0d2013bd03285f4bbcea0f4594f2cd33b68337da709da79cd46379be189d11da
|
||||
F ext/fts5/fts5_storage.c 1fbaf212042bdb363d74a48d3293b2c453a46880e86656df3ee19ade63472681
|
||||
F ext/fts5/fts5_main.c 853d44c804b6d842f0197002a03ecf482b721834b0bf3d2f1ab3a21e4fd13df5
|
||||
F ext/fts5/fts5_storage.c 3b5d743e97502263961e706aedd1000c394ee9df7942ba6671ce4bc41fcf9993
|
||||
F ext/fts5/fts5_tcl.c 4db9258a7882c5eac0da4433042132aaf15b87dd1e1636c7a6ca203abd2c8bfe
|
||||
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
|
||||
F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b
|
||||
@@ -246,7 +246,7 @@ F ext/fts5/test/fts5unicode2.test 3bbd30152f9f760bf13886e5b1e5ec23ff62f56758ddda
|
||||
F ext/fts5/test/fts5unicode3.test f4891a3dac3b49c3d7c0fdb29566e9eb0ecff35263370c89f9661b1952b20818
|
||||
F ext/fts5/test/fts5unicode4.test 728c8f0caafb05567f524ad313d9f8b780fa45987b8a8df04eff87923c74b4d0
|
||||
F ext/fts5/test/fts5unindexed.test 168838d2c385e131120bbf5b516d2432a5fabc4caa2259c932e1d49ae209a4ae
|
||||
F ext/fts5/test/fts5unindexed2.test 54a924b8acc6270350898e09d56c1b942b6e3cae789b9e5f31ec7b9a3dc7953e
|
||||
F ext/fts5/test/fts5unindexed2.test 516236eceaac05ace322290a0d3705b4c4ffe4760d8eb9d014d9d27d56dfcc02
|
||||
F ext/fts5/test/fts5update.test b8affd796e45c94a4d19ad5c26606ea06065a0f162a9562d9f005b5a80ccf0bc
|
||||
F ext/fts5/test/fts5version.test c22d163c17e60a99f022cbc52de5a48bb7f84deaa00fe15e9bc4c3aa1996204e
|
||||
F ext/fts5/test/fts5vocab.test 2a2bdb60d0998fa3124d541b6d30b019504918dc43a6584645b63a24be72f992
|
||||
@@ -2213,8 +2213,8 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P c51dc2a5e75baacbd905cf314e7b1a58a81993ff05ca656739e028d7db25d5b2
|
||||
R 0573b4e17b15d974d21966942a753c69
|
||||
P 21539e9d0d57fdc762affbce9220d1bb1ca009d9dc751b4ccfe63eecbbe2f575
|
||||
R de377aee83cef1249306ac8b9c4a8126
|
||||
U dan
|
||||
Z 19e56c0d30986f9b0323b85fbd6aa264
|
||||
Z cfbbba283553b2d6b64e253eb6c401a0
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
@@ -1 +1 @@
|
||||
21539e9d0d57fdc762affbce9220d1bb1ca009d9dc751b4ccfe63eecbbe2f575
|
||||
cd36d66c88d7282eb0a3ccde5713253f72f5843e451b2693b71adfdae28b41fb
|
||||
|
||||
Reference in New Issue
Block a user