Enhance sqlite3_vtab_nochange() so that it works with "UPDATE ... FROM ..." statements. Use this to allow some updates on fts5 contentless-delete tables.

FossilOrigin-Name: 27ff86e4d8d251dbbcc9f0682d3d7b040518cbeee891cfe253661d1fdbec4e4f
This commit is contained in:
dan 2023-08-07 17:09:25 +00:00
commit 786a9dcf5d
8 changed files with 98 additions and 16 deletions

View File

@ -1689,7 +1689,7 @@ static int fts5UpdateMethod(
** VIRTUAL TABLE statement contained "contentless_delete=1". */
if( eType0==SQLITE_INTEGER
&& pConfig->eContent==FTS5_CONTENT_NONE
&& (nArg>1 || pConfig->bContentlessDelete==0)
&& pConfig->bContentlessDelete==0
){
pTab->p.base.zErrMsg = sqlite3_mprintf(
"cannot %s contentless fts5 table: %s",
@ -2544,6 +2544,12 @@ static int fts5ColumnMethod(
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
pConfig->pzErrmsg = 0;
}else if( pConfig->bContentlessDelete && sqlite3_vtab_nochange(pCtx) ){
char *zErr = sqlite3_mprintf("cannot UPDATE a subset of "
"columns on fts5 contentless-delete table: %s", pConfig->zName
);
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_free(zErr);
}
return rc;
}

View File

@ -188,11 +188,11 @@ do_execsql_test 5.2 {
do_catchsql_test 5.3 {
UPDATE ft SET x='four six' WHERE rowid=3
} {1 {cannot UPDATE contentless fts5 table: ft}}
} {0 {}}
do_execsql_test 5.4 {
SELECT rowid FROM ft('one');
} {1 3 4 5}
} {1 4 5}
do_execsql_test 5.5 {
REPLACE INTO ft(rowid, x) VALUES(3, 'four six');

View File

@ -0,0 +1,59 @@
# 2023 August 7
#
# 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 contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless5
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, content='', contentless_delete=1);
INSERT INTO t1 VALUES('A', 'B', 'C');
INSERT INTO t1 VALUES('D', 'E', 'F');
INSERT INTO t1 VALUES('G', 'H', 'I');
}
do_execsql_test 1.01 {
CREATE TABLE t2(x, y);
INSERT INTO t2 VALUES('x', 'y');
}
# explain_i "UPDATE t1 SET a='a' WHERE t1.rowid=1"
breakpoint
explain_i "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1 AND b IS NULL"
#breakpoint
#explain_i "UPDATE t1 SET a='a' WHERE b IS NULL AND rowid=?"
foreach {tn up err} {
1 "UPDATE t1 SET a='a', b='b', c='c' WHERE rowid=1" 0
2 "UPDATE t1 SET a='a', b='b' WHERE rowid=1" 1
3 "UPDATE t1 SET b='b', c='c' WHERE rowid=1" 1
4 "UPDATE t1 SET a='a', c='c' WHERE rowid=1" 1
5 "UPDATE t1 SET a='a', c='c' WHERE t1.rowid=1 AND b IS NULL" 1
6 "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1" 1
7 "UPDATE t1 SET a='a', b='b', c='c' FROM t2 WHERE t1.rowid=1" 0
} {
set res(0) {0 {}}
set res(1) {1 {cannot UPDATE a subset of columns on fts5 contentless-delete table: t1}}
do_catchsql_test 1.$tn $up $res($err)
}
finish_test

View File

@ -1,5 +1,5 @@
C Rename\sfts5_api\spContext\sparameters\sto\spUserData,\sper\s/chat\sdiscussion.\sThis\sis\sa\scosmetic\schange\smade\sto\sreduce\sconfusion\sbetween\sthose\sparameters\sand\sthe\stwo\sother\scontext-type\sparameters\sin\sthat\sAPI.
D 2023-08-07T09:44:00.281
C Enhance\ssqlite3_vtab_nochange()\sso\sthat\sit\sworks\swith\s"UPDATE\s...\sFROM\s..."\sstatements.\sUse\sthis\sto\sallow\ssome\supdates\son\sfts5\scontentless-delete\stables.
D 2023-08-07T17:09:25.480
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -93,7 +93,7 @@ F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d
F ext/fts5/fts5_expr.c 2473c13542f463cae4b938c498d6193c90d38ea1a2a4f9849c0479736e50d24d
F ext/fts5/fts5_hash.c 65e7707bc8774706574346d18c20218facf87de3599b995963c3e6d6809f203d
F ext/fts5/fts5_index.c 93b4cd116b76b6adf224cd3d213f1e06cfe10ae0eb21d6372b1c53b8f0c382a3
F ext/fts5/fts5_main.c 2f87ee44fdb21539c264541149f07f70e065d58f37420063e5ddef80ba0f5ede
F ext/fts5/fts5_main.c 7070031993ba5b5d89b13206ec4ef624895f2f7c0ec72725913d301e4d382445
F ext/fts5/fts5_storage.c 3c9b41fce41b6410f2e8f82eb035c6a29b2560483f773e6dc98cf3cb2e4ddbb5
F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
@ -132,10 +132,11 @@ F ext/fts5/test/fts5config.test 60094712debc59286c59aef0e6cf511c37d866802776a825
F ext/fts5/test/fts5conflict.test 655925678e630d3cdf145d18725a558971806416f453ac8410ca8c04d934238d
F ext/fts5/test/fts5connect.test 08030168fc96fc278fa81f28654fb7e90566f33aff269c073e19b3ae9126b2f4
F ext/fts5/test/fts5content.test 213506436fb2c87567b8e31f6d43ab30aab99354cec74ed679f22aad0cdbf283
F ext/fts5/test/fts5contentless.test 9a42a86822670792ba632f5c57459addeb774d93b29d5e6ddae08faa64c2b6d9
F ext/fts5/test/fts5contentless.test eb31159d62811b3a32fb1cfb36be20f9d9db75637c7fe6aa7f5c533db2d00576
F ext/fts5/test/fts5contentless2.test 12c778d134a121b8bad000fbf3ae900d53226fee840ce36fe941b92737f1fda7
F ext/fts5/test/fts5contentless3.test 487dce16b6677f68b44d7cbd158b9b7275d25e2c14d713f9188d9645bb699286
F ext/fts5/test/fts5contentless4.test 0f43ededc2874f65d7da99b641a82239854d98d3fa43db729f284b723f23b69f
F ext/fts5/test/fts5contentless5.test 216962d51376b62e4ff45e8db00aa3e1ab548cb36b7fd450633e73b2d678f88e
F ext/fts5/test/fts5corrupt.test 77ae6f41a7eba10620efb921cf7dbe218b0ef232b04519deb43581cb17a57ebe
F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec994e16ab132952e7805f
F ext/fts5/test/fts5corrupt3.test 7da9895dafa404efd20728f66ff4b94399788bdc042c36fe2689801bba2ccd78
@ -593,7 +594,7 @@ F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94
F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500
F src/expr.c ef4a81822da6f767696bd7f4b9983328af061158958138540142285a5b1181b7
F src/expr.c 1affe0cc049683ef0ef3545d9b6901508556b0ef7e2892a344c3df6d7288d79d
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36
F src/func.c cc1da67fd643a43cfe691784158ec656d8ec6d13bb17e67018b01b38b3e4f5ab
@ -643,7 +644,7 @@ F src/printf.c e3ba080e2f409f9bfcc8d34724e6fc160e9c718dc92d0548f6b71b8b6f860ce2
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c abf48be202d35c4f450325b61992e98ac4aa81ed1e29709069432877d3b555d3
F src/select.c 5f545a2c8702d4d3430bbb188cfec47d6c122d899061ef00cbe56af14591c574
F src/shell.c.in 694aaf751f00610381533d4a31c83d142cfc83ef91ef65e2aa6912ace7c39b40
F src/sqlite.h.in 7b07a33d2af82ee974aa91e6294abce0282b2f4c5934b291d2fff961810dd867
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
@ -708,7 +709,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 0fb405f9adf3f757c26bfc1ae6d58ac5dccbb918917ba9e5ef0e6673a06563d3
F src/treeview.c 1d52fbc4e97161e65858d36e3424ea6e3fc045dd8a679c82b4b9593dc30de3bd
F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0
F src/update.c d5b755580a86d235b12faf10de81e60ad97c8117f8c3063d92c772df94455d44
F src/update.c 0bb9171afaa4d0b100ad946873bccda7aef90ffe083ef5c63668fce08c4df9da
F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c b3532a95ad56db67b3acd3955e688e4cb80ebec6fd1f459a8eb51cceedd6de69
@ -2049,8 +2050,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 8ede50082e7bcf0226a3b42a590f188b5a139bbf081c9eed7ff8f6f2a6a274a0
R 7643277fee9d7d8c9b719b0a83935d9f
U stephan
Z 84e0aac57037c35f1739965aedd50f66
P 2ca064d8eb37252e16b0fec9924e9ba9289d96a737346431c6ba9cb1c161e5de 16cd2161e312cf97129011fc829079db8f762b822b2f4fabf7ff6742c071302f
R cb2871ec0eb1a937adec492d1c792b96
T +closed 16cd2161e312cf97129011fc829079db8f762b822b2f4fabf7ff6742c071302f
U dan
Z 636193e34abfece337b10699da0a61ae
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
2ca064d8eb37252e16b0fec9924e9ba9289d96a737346431c6ba9cb1c161e5de
27ff86e4d8d251dbbcc9f0682d3d7b040518cbeee891cfe253661d1fdbec4e4f

View File

@ -3972,10 +3972,13 @@ int sqlite3ExprCodeGetColumn(
u8 p5 /* P5 value for OP_Column + FLAGS */
){
assert( pParse->pVdbe!=0 );
assert( (p5 & (OPFLAG_NOCHNG|OPFLAG_TYPEOFARG|OPFLAG_LENGTHARG))==p5 );
assert( IsVirtual(pTab) || (p5 & OPFLAG_NOCHNG)==0 );
sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pTab, iTable, iColumn, iReg);
if( p5 ){
VdbeOp *pOp = sqlite3VdbeGetLastOp(pParse->pVdbe);
if( pOp->opcode==OP_Column ) pOp->p5 = p5;
if( pOp->opcode==OP_VColumn ) pOp->p5 = (p5 & OPFLAG_NOCHNG);
}
return iReg;
}

View File

@ -1298,6 +1298,16 @@ static void selectInnerLoop(
testcase( eDest==SRT_Fifo );
testcase( eDest==SRT_DistFifo );
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG)
/* A destination of SRT_Table and a non-zero iSDParm2 parameter means
** that this is an "UPDATE ... FROM" on a virtual table or view. In this
** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC.
** This does not affect operation in any way - it just allows MakeRecord
** to process OPFLAG_NOCHANGE values without an assert() failing. */
if( eDest==SRT_Table && pDest->iSDParm2 ){
sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
}
#endif
#ifndef SQLITE_OMIT_CTE
if( eDest==SRT_DistFifo ){
/* If the destination is DistFifo, then cursor (iParm+1) is open

View File

@ -1259,7 +1259,9 @@ static void updateVirtualTable(
sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0)
);
}else{
pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i));
Expr *pRow = exprRowColumn(pParse, i);
if( pRow ) pRow->op2 = OPFLAG_NOCHNG;
pList = sqlite3ExprListAppend(pParse, pList, pRow);
}
}