From be3da24134f53a0c8f10291611af758dc0ced611 Mon Sep 17 00:00:00 2001 From: drh Date: Sun, 29 Dec 2019 00:52:41 +0000 Subject: [PATCH] Add the OP_FinishSeek opcode which completes an OP_DeferredSeek if the seek has not already completed. Also add the sqlite3WhereUsesDeferredSeek() interface to the query planner. The UPDATE implementation adds an OP_FinishSeek before running the final OP_Insert if one is needed. Ticket [ec8abb025e78f40c] and also an assertion fault reported by Yongheng. FossilOrigin-Name: 21ef6e99331210b80fa7c71b4f02e8f768a748d01aef884368af2f6b51a067e0 --- manifest | 32 ++++++++++++++++---------------- manifest.uuid | 2 +- src/btree.c | 1 - src/sqliteInt.h | 1 + src/update.c | 25 +++++++++++-------------- src/vdbe.c | 19 +++++++++++++++++++ src/vdbeInt.h | 1 + src/vdbeaux.c | 4 ++-- src/where.c | 10 +++++++++- src/whereInt.h | 1 + src/wherecode.c | 1 + test/update.test | 17 +++++++++++++++++ 12 files changed, 79 insertions(+), 35 deletions(-) diff --git a/manifest b/manifest index 5dde30505a..2c91266268 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sattempt\sto\sflatten\scompound\ssub-queries\sin\sa\sFROM\sclause\sinto\sthe\sparent\sif\sany\scomponent\sof\sthe\ssub-query\suses\sa\swindow\sfunction. -D 2019-12-28T18:25:51.300 +C Add\sthe\sOP_FinishSeek\sopcode\swhich\scompletes\san\sOP_DeferredSeek\sif\sthe\sseek\nhas\snot\salready\scompleted.\s\sAlso\sadd\sthe\ssqlite3WhereUsesDeferredSeek()\ninterface\sto\sthe\squery\splanner.\s\sThe\sUPDATE\simplementation\sadds\san\nOP_FinishSeek\sbefore\srunning\sthe\sfinal\sOP_Insert\sif\sone\sis\sneeded.\nTicket\s[ec8abb025e78f40c]\sand\salso\san\sassertion\sfault\sreported\sby\sYongheng. +D 2019-12-29T00:52:41.688 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -469,7 +469,7 @@ F src/auth.c a3d5bfdba83d25abed1013a8c7a5f204e2e29b0c25242a56bc02bb0c07bf1e06 F src/backup.c f70077d40c08b7787bfe934e4d1da8030cb0cc57d46b345fba2294b7d1be23ab F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33 F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 -F src/btree.c 695bcbc62177cf70faaf6b34f89c22415e5581a13c57d67c862057636d3f09b9 +F src/btree.c e03085a75c1d6cd1db7f458a6e09d7111a4ebe4b7490aafc833ec714158ffdb1 F src/btree.h f27a33c49280209a93385e218306c4ee5f46ba8d7649d2f81a7166b282232484 F src/btreeInt.h 91806f01fd1145a9a86ba3042f25c38d8faf6002701bf5e780742cf88bcff437 F src/build.c 5aa8776d926954f13ddc37144c765ff8d80e49b74432c0f5a10f29433f0d69a8 @@ -533,7 +533,7 @@ F src/shell.c.in 4a3a9e1c11847b1904f2b01d087af1c052f660902755abab457cab1756817de F src/sqlite.h.in 2a23e8161775253d9cf383c2c6aa559005dc787d350dcb0be67a6c4cc3bd1d19 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 72af51aa4e912e14cd495fb6e7fac65f0940db80ed950d90911aff292cc47ce2 -F src/sqliteInt.h 3e235c7a406630bff9fe7183d9af5d4ce21a9b7acb77bf1b82a0caa130a5f687 +F src/sqliteInt.h dcd5b4b29596cb5969586790ae45f75fe8deaf5fde1a90d572ad12b2f970b318 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -595,16 +595,16 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 7b17f6e2f20f6cbcb0b215025a86b7457c38451fc7622f705e553d7a488c572d F src/treeview.c 7a8097cff1584acd0a228817103513bf1d1bf5ba91ff142b99c83e406c0968f3 F src/trigger.c 681ccdb910a87243940d63f99b26190d9c5d2534c2ded3c0825b7c0e315a342e -F src/update.c aed4261a7854ff3031c0d361b04988c8dce5b845a3fb3999fedc51140a836e78 +F src/update.c fae1082d3e28a13a4d4ed7ac35eaeb2f746d736882e46dd653671ab64396d86b F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78 F src/utf.c 2f0fac345c7660d5c5bd3df9e9d8d33d4c27f366bcfb09e07443064d751a0507 F src/util.c 2c92bc706bbdb1c45a25180291e7e05a56e297aa5dd7b2bcd2b1c47e8bb05b17 F src/vacuum.c 82dcec9e7b1afa980288718ad11bc499651c722d7b9f32933c4d694d91cb6ebf -F src/vdbe.c 4d91d635d7aff34e09fade185911c7980fc42f93cc7b18f5387fad5a1166f08c +F src/vdbe.c 7235823bf9ec3ea298cc5c5e832c026d1cb4838c7507cdf28bf97c829353876a F src/vdbe.h 3f068f00b23aebf392df142312ab5874588371c6d83e60d953f6d6b6453491c5 -F src/vdbeInt.h 1ccaf470287e2d153b16cf7b0274d436db2c2f74cdf14afd1a0ff4cb51548ae6 +F src/vdbeInt.h e02ccac0334f7c71c952210657e6e18de1917605887c7bc6167a80a17f62da18 F src/vdbeapi.c 1252d80c548711e47a6d84dae88ed4e95d3fbb4e7bd0eaa1347299af7efddf02 -F src/vdbeaux.c e041d20b5000abbd651f7d470c66eaa13e868f58a13c1a940b4d426c6d0d43b5 +F src/vdbeaux.c 0a9716e47012ef018038c2e1dab9f701a6fb4429bb3ee1d4d0f49497519ace74 F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1 F src/vdbemem.c fa083086758379b52f8771d69424b273c7bd0f94413802404ee32cd5cc7cd870 F src/vdbesort.c a3be032cc3fee0e3af31773af4a7a6f931b7230a34f53282ccf1d9a2a72343be @@ -614,9 +614,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 15a2845769f51ba132f9cf0b2c7a6887a91fc8437892dbcce9fcdc68b66d60a1 F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a F src/walker.c a137468bf36c92e64d2275caa80c83902e3a0fc59273591b96c6416d3253d05d -F src/where.c 7bb294fdada405412dfd76d351286e0dec3dda07a8e19b0f904b8e8fc0ced41f -F src/whereInt.h de1b77e416ad5a7cc60a71fc1317484f8d83ca1e52b805dc8c978785a92eeded -F src/wherecode.c 3c9b185cdb6950fa1043d324d3e8ef14b07cef4f5cfb475611b6f43480494fb2 +F src/where.c 602e5093556bbd9090c0a9bd834fec0717e3a4b0377a021d38091a6d554a1177 +F src/whereInt.h a727b32260e12707a8c1bc29d7f7e9b6dc1a44551a45093d5968fbe570ff0c56 +F src/wherecode.c a987d22b42e09b06f3a49596e0953b1cd28e568cc656681776edc0026cfac0cc F src/whereexpr.c 4b34be1434183e7bb8a05d4bf42bd53ea53021b0b060936fbd12062b4ff6b396 F src/window.c 87795bb8293179cb8a92529264d49bd66b5bcad5e91cfc17dd8d3e66a2a77f88 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 @@ -1605,7 +1605,7 @@ F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97 F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981 -F test/update.test 6fdd76fdf3194fb10ee184b3b2999c61d10d2a0b825dbcaa2d08ff394f3e26e3 +F test/update.test 40aa53edd597a4cf16bca3890f429a91999ad7885117918ec8fc9d5d8b41d104 F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3 F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18 F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09 @@ -1853,7 +1853,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 82be135dee7ccfde5f8a67f3621b7ced449dce89bae9cadf025154a4de848c11 -R 689fc9af6e2b279bc6d7fd22df0aa0f5 -U dan -Z 819583b2c969ec1f752f2ef5cdcbf2f5 +P eeb76f621de2f930a548db0fbb9fe25b4479b73581826b8dfa2e63cd1f1ab783 +R af3bb40e1df9ee06fcab6108c3a14e15 +U drh +Z da1bc4514591aa0f5f1f69cf011c69ab diff --git a/manifest.uuid b/manifest.uuid index d61ccdd153..f5441cc065 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eeb76f621de2f930a548db0fbb9fe25b4479b73581826b8dfa2e63cd1f1ab783 \ No newline at end of file +21ef6e99331210b80fa7c71b4f02e8f768a748d01aef884368af2f6b51a067e0 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 52c2ceaffb..eb80816bbb 100644 --- a/src/btree.c +++ b/src/btree.c @@ -8658,7 +8658,6 @@ int sqlite3BtreeInsert( if( flags & BTREE_SAVEPOSITION ){ assert( pCur->curFlags & BTCF_ValidNKey ); assert( pX->nKey==pCur->info.nKey ); - assert( pCur->info.nSize!=0 ); assert( loc==0 ); } #endif diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c1ed684ffc..84f8e1e5c5 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4115,6 +4115,7 @@ int sqlite3WhereOkOnePass(WhereInfo*, int*); #define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */ #define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */ #define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */ +int sqlite3WhereUsesDeferredSeek(WhereInfo*); void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); diff --git a/src/update.c b/src/update.c index 52ae8a0305..8ff92daea9 100644 --- a/src/update.c +++ b/src/update.c @@ -191,6 +191,7 @@ void sqlite3Update( int iPk = 0; /* First of nPk cells holding PRIMARY KEY value */ i16 nPk = 0; /* Number of components of the PRIMARY KEY */ int bReplace = 0; /* True if REPLACE conflict resolution might happen */ + int bFinishSeek = 1; /* The OP_FinishSeek opcode is needed */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ @@ -524,6 +525,7 @@ void sqlite3Update( pWInfo = 0; eOnePass = ONEPASS_SINGLE; sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL); + bFinishSeek = 0; }else{ /* Begin the database scan. ** @@ -550,6 +552,7 @@ void sqlite3Update( ** strategy that uses an index for which one or more columns are being ** updated. */ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + bFinishSeek = sqlite3WhereUsesDeferredSeek(pWInfo); if( eOnePass!=ONEPASS_SINGLE ){ sqlite3MultiWrite(pParse); if( eOnePass==ONEPASS_MULTI ){ @@ -713,6 +716,7 @@ void sqlite3Update( testcase( i==31 ); testcase( i==32 ); sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k); + bFinishSeek = 0; }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, k); } @@ -800,21 +804,14 @@ void sqlite3Update( /* Delete the index entries associated with the current record. */ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1); -#ifndef SQLITE_OMIT_GENERATED_COLUMNS - /* If pTab contains one or more virtual columns, then it is possible - ** (though unlikely) that no OP_Column opcodes have been run against - ** the table since the OP_SeekDeferred, meaning that there has not been - ** a seek against the cursor yet. The OP_Delete opcode and OP_Insert - ** opcodes that follow will be needing this seek, so code a bogus - ** OP_Column just to make sure the seek has been done. - ** See ticket ec8abb025e78f40c 2019-12-26 - */ - if( eOnePass!=ONEPASS_OFF && (pTab->tabFlags & TF_HasVirtual)!=0 ){ - int r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, iDataCur, 0, r1); - sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); + /* We must run the OP_FinishSeek opcode to resolve a prior + ** OP_DeferredSeek if there is any possibility that there have been + ** no OP_Column opcodes since the OP_DeferredSeek was issued. But + ** we want to avoid the OP_FinishSeek if possible, as running it + ** costs CPU cycles. */ + if( bFinishSeek ){ + sqlite3VdbeAddOp1(v, OP_FinishSeek, iDataCur); } -#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ /* If changing the rowid value, or if there are foreign key constraints ** to process, delete the old record. Otherwise, add a noop OP_Delete diff --git a/src/vdbe.c b/src/vdbe.c index 23c8b18ac8..0aeaa072cf 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -4827,6 +4827,7 @@ case OP_Insert: { pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->deferredMoveto==0 ); assert( pC->uc.pCursor!=0 ); assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable ); assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC ); @@ -5700,6 +5701,24 @@ case OP_IdxRowid: { /* out2 */ break; } +/* Opcode: FinishSeek P1 * * * * +** +** If cursor P1 was previously moved via OP_DeferredSeek, complete that +** seek operation now, without further delay. If the cursor seek has +** already occurred, this instruction is a no-op. +*/ +case OP_FinishSeek: { + VdbeCursor *pC; /* The P1 index cursor */ + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + if( pC->deferredMoveto ){ + rc = sqlite3VdbeFinishMoveto(pC); + if( rc ) goto abort_due_to_error; + } + break; +} + /* Opcode: IdxGE P1 P2 P3 P4 P5 ** Synopsis: key=r[P3@P4] ** diff --git a/src/vdbeInt.h b/src/vdbeInt.h index cc410510ae..138377def1 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -483,6 +483,7 @@ struct PreUpdate { void sqlite3VdbeError(Vdbe*, const char *, ...); void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); +int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); int sqlite3VdbeCursorMoveto(VdbeCursor**, int*); int sqlite3VdbeCursorRestore(VdbeCursor*); u32 sqlite3VdbeSerialTypeLen(u32); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index ac52303d57..9d04c160a2 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -3414,7 +3414,7 @@ void sqlite3VdbeDelete(Vdbe *p){ ** carried out. Seek the cursor now. If an error occurs, return ** the appropriate error code. */ -static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){ +int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor *p){ int res, rc; #ifdef SQLITE_TEST extern int sqlite3_search_count; @@ -3486,7 +3486,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){ *piCol = iMap - 1; return SQLITE_OK; } - return handleDeferredMoveto(p); + return sqlite3VdbeFinishMoveto(p); } if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ return handleMovedCursor(p); diff --git a/src/where.c b/src/where.c index 244bc57a47..7c05a58e49 100644 --- a/src/where.c +++ b/src/where.c @@ -120,7 +120,7 @@ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){ /* ** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to -** operate directly on the rowis returned by a WHERE clause. Return +** operate directly on the rowids returned by a WHERE clause. Return ** ONEPASS_SINGLE (1) if the statement can operation directly because only ** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass ** optimization can be used on multiple @@ -147,6 +147,14 @@ int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){ return pWInfo->eOnePass; } +/* +** Return TRUE if the WHERE loop uses the OP_DeferredSeek opcode to move +** the data cursor to the row selected by the index cursor. +*/ +int sqlite3WhereUsesDeferredSeek(WhereInfo *pWInfo){ + return pWInfo->bDeferredSeek; +} + /* ** Move the content of pSrc into pDest */ diff --git a/src/whereInt.h b/src/whereInt.h index 8d333c032d..f500e01d48 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -459,6 +459,7 @@ struct WhereInfo { i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */ u8 sorted; /* True if really sorted (not just grouped) */ u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */ + u8 bDeferredSeek; /* Uses OP_DeferredSeek */ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ u8 eDistinct; /* One of the WHERE_DISTINCT_* values */ u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */ diff --git a/src/wherecode.c b/src/wherecode.c index 9c51b5669c..96c2971a5f 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1045,6 +1045,7 @@ static void codeDeferredSeek( assert( iIdxCur>0 ); assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 ); + pWInfo->bDeferredSeek = 1; sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) diff --git a/test/update.test b/test/update.test index 2f36bdf2f7..b49ed6d292 100644 --- a/test/update.test +++ b/test/update.test @@ -684,4 +684,21 @@ do_execsql_test update-18.20 { SELECT * FROM t0; } {0 0} +# 2019-12-28 assertion fault reported by Yongheng +# Similar to ticket ec8abb025e78f40c +# An UPDATE was reaching the OP_Delete after running OP_DeferredSeek +# without ever hitting an OP_Column. The enhanced solution is to +# fix OP_Delete so that it can do the seek itself. +# +reset_db +do_execsql_test update-19.10 { + CREATE TABLE t1( + a TEXT, + b INTEGER PRIMARY KEY UNIQUE + ); + INSERT INTO t1 VALUES(1,2); + UPDATE t1 SET a = quote(b) WHERE b>=2; + SELECT * FROM t1; +} {2 2} + finish_test -- 2.47.2