From be3ab76bfb35dc767caf520dacd1bac63369ac84 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 7 Aug 2023 16:15:56 +0000 Subject: [PATCH] Changes so that sqlite3_vtab_nochange() works with "UPDATE ... FROM...". Use this to allow UPDATE on a contentless fts5 table if new values are supplied for all indexed columns. FossilOrigin-Name: 16cd2161e312cf97129011fc829079db8f762b822b2f4fabf7ff6742c071302f --- ext/fts5/fts5_main.c | 8 +++- ext/fts5/test/fts5contentless.test | 4 +- ext/fts5/test/fts5contentless5.test | 59 +++++++++++++++++++++++++++++ manifest | 26 +++++++------ manifest.uuid | 2 +- src/expr.c | 3 ++ src/select.c | 10 +++++ src/update.c | 4 +- 8 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 ext/fts5/test/fts5contentless5.test diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index d1c79d3604..c34a5a332b 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -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; } diff --git a/ext/fts5/test/fts5contentless.test b/ext/fts5/test/fts5contentless.test index 41a78e9c8f..f75ccb44c2 100644 --- a/ext/fts5/test/fts5contentless.test +++ b/ext/fts5/test/fts5contentless.test @@ -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'); diff --git a/ext/fts5/test/fts5contentless5.test b/ext/fts5/test/fts5contentless5.test new file mode 100644 index 0000000000..1541b0c68d --- /dev/null +++ b/ext/fts5/test/fts5contentless5.test @@ -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 + diff --git a/manifest b/manifest index d8d3391f11..d159c83a4e 100644 --- a/manifest +++ b/manifest @@ -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 Changes\sso\sthat\ssqlite3_vtab_nochange()\sworks\swith\s"UPDATE\s...\sFROM...".\sUse\sthis\sto\sallow\sUPDATE\son\sa\scontentless\sfts5\stable\sif\snew\svalues\sare\ssupplied\sfor\sall\sindexed\scolumns. +D 2023-08-07T16:15:56.046 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,11 @@ 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 +R cb2871ec0eb1a937adec492d1c792b96 +T *branch * fts5-contentless-delete +T *sym-fts5-contentless-delete * +T -sym-trunk * +U dan +Z b30ed14be16970913bfed643626fe629 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 86338d2727..ea2017dc46 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2ca064d8eb37252e16b0fec9924e9ba9289d96a737346431c6ba9cb1c161e5de \ No newline at end of file +16cd2161e312cf97129011fc829079db8f762b822b2f4fabf7ff6742c071302f \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 0c41f6684a..d96f362856 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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; } diff --git a/src/select.c b/src/select.c index 41e42b6808..321badd07f 100644 --- a/src/select.c +++ b/src/select.c @@ -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 diff --git a/src/update.c b/src/update.c index 2101aa8284..3b3c2f838b 100644 --- a/src/update.c +++ b/src/update.c @@ -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); } }