From 38a7382cf6b802f806b4649fccb803b287c9faac Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 4 Feb 2021 11:14:37 +0000 Subject: [PATCH] Work toward handling interleaved RETURNING statements. Trunk does not handle that case correctly. This branch is a partial implementation of ideas that might, however. FossilOrigin-Name: e1eb62131913fcc4aa58a3f7e634ed2bbecf32763d936067d36ce1c2d2142a75 --- manifest | 29 +++++++++++++------------ manifest.uuid | 2 +- src/select.c | 11 +++++++--- src/sqliteInt.h | 4 +++- src/trigger.c | 2 +- src/vdbe.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ src/vdbeInt.h | 1 + src/vdbeapi.c | 3 +++ src/vdbeaux.c | 2 +- 9 files changed, 90 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index 512110c1e4..7d728446bd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Corrections\sto\sthe\s'filepath_normalize'\stest\ssuite\shelper\sprocedure. -D 2021-02-03T19:38:40.978 +C Work\stoward\shandling\sinterleaved\sRETURNING\sstatements.\s\sTrunk\sdoes\snot\nhandle\sthat\scase\scorrectly.\s\sThis\sbranch\sis\sa\spartial\simplementation\sof\nideas\sthat\smight,\showever. +D 2021-02-04T11:14:37.812 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -541,12 +541,12 @@ F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c f6761473ea4b51190fc52f8f2121498b78717266e106e7bff12849ea2d52165f F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c 738cb746189f721f59972993c13085fa2975c4cbfd04ba26445f3b42c81237dc +F src/select.c 14d7315897b9654badf01635781ae15113ee88a4e6684d3a60a63242ea3cadd5 F src/shell.c.in 9ebc74e4f05cfbd0f4a36060fdaeff1da4e9af4458358722bc08c5a1ab9a0879 F src/sqlite.h.in 8855a19f37ade8dad189a9e48233a2ebe1b46faf469c7eb0906a654e252dcc57 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h 0fda3b2c05b1559135aa2c4ecb8e75bd2085ba4433310bbb5427d97c2d81315d +F src/sqliteInt.h 9462cda2ddf4f2953c72d87ee041f74f5f395b44b1b943bcad3f8a4a245b478f F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -607,17 +607,17 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c c64c49d7c2ec4490c2fef1f24350167ba16b03b0c6cee58ad1a1d70a4325d4e9 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda -F src/trigger.c 0a242d65dd9b9822d4e990653eb4ece3557dcda01374934aa3cc1f9718d8dee3 +F src/trigger.c 79d6f7329fc6a8fdad9aa6d4da9703f412ef7977592ea7819dfcb0bac2500f59 F src/update.c 0f5a61f0787199983530a33f6fffe4f52742f35fcdf6ccfad1078b1a8bc17723 F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 41c7a72da1df47864faa378a1c720b38adb288c6838cb6be5594511b6287a048 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 -F src/vdbe.c b3c9a3f9d546d4848a2a8c88a3c40875cd6ba3d248d9b99d01b1eeb38b29c171 +F src/vdbe.c 88ae06e39cbab0d3433b5512bef6921d845460b1ab49ae2040332cec6fd4b024 F src/vdbe.h a71bf43572d3de57923d1928ac01ae8d355cd67e94462ba4f7462265cedbef9a -F src/vdbeInt.h 3df118924e1711f1bbc8e30c46260d0ab6c3b029b32dd411f789111f76434f3c -F src/vdbeapi.c 4a43e303ec3354c785f453e881521969378e85628278ab74ba4a9df790c0d93b -F src/vdbeaux.c 34994221fbde303e362cf11c7d97fe9b115055cfdf716260ca0140811de94aab +F src/vdbeInt.h b257dd6c6363c5611e37a390b0c479dd20c74dea7f888ebaba2f65f6bfa6fe18 +F src/vdbeapi.c 8e29edeb515656e65345945c8ec17b0dd37abd3bf96d708e49afff95dd1b8856 +F src/vdbeaux.c be48e68e8994af49fff588a1f167a704f6cba61b707156da154440b60d35313e F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1 F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a @@ -1899,7 +1899,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6b29e549bb34933bfd0758e31085e65dcc0f75446c478fc775d96cf01c22cf43 -R 85ac248a64299c59878f72d774654f84 -U mistachkin -Z cd094eb04790b0c848908f94f1895107 +P 32f4d04470bf953b08eea285543f16e03de13d5448c1ebccbba1578ca3b5363e +R 324119af37eb27d16c017f1cb32c8d09 +T *branch * interleaved-returning +T *sym-interleaved-returning * +T -sym-trunk * +U drh +Z dd3a22aecc43ba661a828fb24a9b1b1c diff --git a/manifest.uuid b/manifest.uuid index a3ab0cdd0d..517b29d526 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -32f4d04470bf953b08eea285543f16e03de13d5448c1ebccbba1578ca3b5363e \ No newline at end of file +e1eb62131913fcc4aa58a3f7e634ed2bbecf32763d936067d36ce1c2d2142a75 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 7c3019890c..be5fa74964 100644 --- a/src/select.c +++ b/src/select.c @@ -1210,10 +1210,15 @@ static void selectInnerLoop( if( pSort ){ pushOntoSorter(pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); - }else if( eDest==SRT_Coroutine ){ - sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); + }else if( eDest==SRT_Output ){ + if( pDest->iSDParm>=0 ){ + sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol); + }else{ + sqlite3VdbeAddOp2(v, OP_Returning, regResult, nResultCol); + sqlite3VdbeAddOp0(v, OP_CkNesting); + } }else{ - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol); + sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); } break; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3cadb7f53a..a32f103156 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3201,7 +3201,9 @@ struct Select { ** the side-effects of functions. ** ** SRT_Output Generate a row of output (using the OP_ResultRow -** opcode) for each row in the result set. +** opcode) for each row in the result set. If iSDParm +** is negative, then enforce strict nesting order +** on concurrent statements. ** ** SRT_Mem Only valid if the result is a single column. ** Store the first column of the first result row diff --git a/src/trigger.c b/src/trigger.c index ef0a90b24f..f393bc7e12 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -943,7 +943,7 @@ static int codeTriggerProgram( Select *pNew; pSelect->pEList = sqlite3ExpandReturning(pParse, pList, pParse->pTriggerTab); - sqlite3SelectDestInit(&sDest, SRT_Output, 0); + sqlite3SelectDestInit(&sDest, SRT_Output, -1); pNew = sqlite3SelectDup(db, pSelect, 0); if( pNew ){ sqlite3Select(pParse, pNew, &sDest); diff --git a/src/vdbe.c b/src/vdbe.c index 97cd696de3..3cb3d9faf4 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1445,6 +1445,60 @@ case OP_IntCopy: { /* out2 */ break; } +/* Opcode: Returning P1 P2 * * * +** Synopsis: output=r[P1@P2] +** +** The registers P1 through P1+P2-1 contain a single row of +** results. This opcode causes the sqlite3_step() call to terminate +** with an SQLITE_ROW return code and it sets up the sqlite3_stmt +** structure to provide access to the r(P1)..r(P1+P2-1) values as +** the result row. +** +** This opcode is like OP_ResultRow with the addition that it enforces +** nesting order for concurrent prepared statements. This opcode +** is always followed immediately by an OP_CkNesting opcode which does +** the actual nesting test. DML statement that have a RETURNING clause +** use this opcode, rather than OP_ResultRow, to return their results, +** and thus prevent interleaved DML statements. +*/ +case OP_Returning: { + assert( pOp[1].opcode==OP_CkNesting ); + if( p->pPrev ){ + p->pPrev->pNext = p->pNext; + if( p->pNext ) p->pNext->pPrev = p->pPrev; + p->pPrev = 0; + p->pNext = db->pVdbe; + db->pVdbe = p; + } + p->bUsesReturning = 1; + goto result_row_opcode_body; +} + + +/* Opcode: CkNested * * * * * +** +** This opcode always follows an OP_Returning opcode. Thus, it is +** the first opcode to run on the next sqlite3_step() call after +** receiving the results of a RETURNING clause in a DML statement. +** +** This opcode checks to verify that the DML statements are properly +** nested. If an error occurs, it returns SQLITE_BUSY_NESTING. +*/ +case OP_CkNesting: { + Vdbe *pX; + for(pX=db->pVdbe; pX!=p && ALWAYS(pX!=0); pX=pX->pNext){ + if( pX->bUsesReturning + && pX->iVdbeMagic==VDBE_MAGIC_RUN + && pX->pc>0 + ){ + rc = SQLITE_BUSY; + goto vdbe_return; + } + } + break; +} + + /* Opcode: ChngCntRow P1 P2 * * * ** Synopsis: output=r[P1] ** @@ -1477,6 +1531,8 @@ case OP_ChngCntRow: { case OP_ResultRow: { Mem *pMem; int i; + + result_row_opcode_body: assert( p->nResColumn==pOp->p2 ); assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 4f8a2edf37..51514f7473 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -427,6 +427,7 @@ struct Vdbe { bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ + bft bUsesReturning:1; /* True for DML statements with RETURNING */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index ba3bdf6a5f..6cecfe5648 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -634,6 +634,9 @@ static int sqlite3Step(Vdbe *p){ ** previous sqlite3_step() returned something other than a SQLITE_LOCKED ** or SQLITE_BUSY error. */ + if( p->iVDbeMagic==VDBE_MAGIC_ABORT ){ + return SQLITE_ABORT; + } #ifdef SQLITE_OMIT_AUTORESET if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ sqlite3_reset((sqlite3_stmt*)p); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 1c77b2ce94..80aaf01aa3 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -2954,7 +2954,7 @@ static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){ assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); assert( db->nStatement>0 ); - assert( p->iStatement==(db->nStatement+db->nSavepoint) ); + /* assert( p->iStatement==(db->nStatement+db->nSavepoint) ); */ for(i=0; inDb; i++){ int rc2 = SQLITE_OK; -- 2.39.5