diff --git a/manifest b/manifest index d6048a60fe..ee7ea0da28 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplification\sand\ssize\sreduction\sto\sthe\sprintf\slogic.\s\sRemove\sthe\sbFlags\nparameter\sfrom\ssqlite3VXPrintf()\sand\ssqlite3XPrintf().\s\sUse\ssqlite3XPrintf()\ninstead\sof\ssqlite3_snprintf()\sfor\srendering\sP4\svalues\sin\sEXPLAIN\soutput. -D 2016-01-30T12:50:25.204 +C Add\sthe\sBTREE_FORDELETE\sand\sBTREE_AUXDELETE\sflags\sto\sthe\sb-tree\slayer\sinterface\nand\suse\sthem.\s\sAdd\sassert()\sstatement\sto\sverify\sthat\sthey\sare\scorrect. +D 2016-01-30T13:32:30.291 F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 72b7858f02017611c3ac1ddc965251017fed0845 @@ -290,8 +290,8 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73 -F src/btree.c 5e3435a31afbe9d839c5ee2630b8390712847415 -F src/btree.h 526137361963e746949ab966a910c7f455ac6b04 +F src/btree.c e3929afb9bfbaa6b39685b5e5a207036ac862a75 +F src/btree.h 368ceeb4bd9312dc8df2ffd64b4b7dbcf4db5f8e F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5 F src/build.c 0510844c48d80732aead74b5727403b493dd1cd5 F src/callback.c 29ae4faba226c7ebb9aee93016b5ce8a8f071261 @@ -299,7 +299,7 @@ F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f F src/ctime.c 60e135af364d777a9ab41c97e5e89cd224da6198 F src/date.c 997651e3ee6c2818fbf7fcdb7156cef9eb3ece20 F src/dbstat.c b2ec6793eef97aebb4d171d490a4ffdfa9f2475c -F src/delete.c 00af9f08a15ddc5cba5962d3d3e5bf2d67b2e7da +F src/delete.c 33ed87dc0746b1f8ce186f62b608bf40801af9c0 F src/expr.c d10c1cdef5810cdbf73adc9f9b383684230b360a F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c c66d3e5b35d4d95b5c1e2ee6c12f5df13a7f9ad6 @@ -308,7 +308,7 @@ F src/global.c bd5a0af3f30b0c01be6db756c626cd3c33a3d260 F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5 F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c a00e6d8a843dc22e2c136df04e6300c4528d9b9f +F src/insert.c 410f52b9ef4603dc0aebb169b7cb6b3c60eda07e F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b @@ -352,7 +352,7 @@ F src/shell.c dcd7a83645ef2a58ee9c6d0ea4714d877d7835c4 F src/sqlite.h.in 214476a62012e578f42133a9a3b4f97a9aa421a3 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d -F src/sqliteInt.h 5b552cc0eee50d519e2e020f16abbc68947b0e59 +F src/sqliteInt.h 2f80b9b1506a8d602b2a99f3f0bfae22df3e7d70 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e @@ -412,9 +412,9 @@ F src/update.c 17332f9fe818cbc0444c36a811800af8498af4c3 F src/utf.c 32d7f82aa921322f3e1c956f4b58f019ebd2c6b3 F src/util.c 72d40df0a52d3f30b462a15f0e094fcbade6dc82 F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701 -F src/vdbe.c 0686ef5ee103935548d1aa2c0c28e52f1ae6e5a8 +F src/vdbe.c d9d2406d8a3baca537d2b05a2354afaa25e1ca84 F src/vdbe.h 7a733ea8aac1b77305a67698e784fa3484ee3337 -F src/vdbeInt.h 171fdc5f6af4eeb0ff0559dbf0a71244d726a670 +F src/vdbeInt.h 716df83ac45b70ff000b515040ea606d0cb5d0f3 F src/vdbeapi.c ffae8f5af4570fbd548504e815e9fb7227f0822e F src/vdbeaux.c 221631e40111b5efa96ea557c6e2e7f62fd32b2a F src/vdbeblob.c 37c3d11a753e403698c69e17383d282e1ae73e75 @@ -642,7 +642,7 @@ F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48 F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13 F test/fkey8.test 8f08203458321e6c19a263829de4cfc936274ab0 F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749 -F test/fordelete.test ba12ec1d27cc34a4c23db4446029126d773f3849 +F test/fordelete.test 57ed9b953eeace09dd2eac3251b40bf9d6990aec F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7 @@ -1422,7 +1422,8 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 9de3d7123007636aa97da1c70bc34344b0391078 -R 821c09620afbfbde9b9d6d9359867747 +P 0bdb41c45aa1cc8e5c136aaa6605d54b401483bd 46080b0474363ca6594d2e5c249a280e2b4fc67a +R 54437ffc6525e4a6d93d7714d1fd6e2f +T +closed 46080b0474363ca6594d2e5c249a280e2b4fc67a U drh -Z e57134af984dbdd8b3338eb2a295687a +Z 640a4b96ac8cc992fda6dcd328c886fd diff --git a/manifest.uuid b/manifest.uuid index 0454e6289a..32d73b4945 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0bdb41c45aa1cc8e5c136aaa6605d54b401483bd \ No newline at end of file +85c467041c9378cae3038756da815e9117ee8c7d \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 481418a3dc..b08367cbd7 100644 --- a/src/btree.c +++ b/src/btree.c @@ -4049,13 +4049,13 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ ** on the database already. If a write-cursor is requested, then ** the caller is assumed to have an open write transaction. ** -** If wrFlag==0, then the cursor can only be used for reading. -** If wrFlag==1, then the cursor can be used for reading or for -** writing if other conditions for writing are also met. These -** are the conditions that must be met in order for writing to -** be allowed: +** If the BTREE_WRCSR bit of wrFlag is clear, then the cursor can only +** be used for reading. If the BTREE_WRCSR bit is set, then the cursor +** can be used for reading or for writing if other conditions for writing +** are also met. These are the conditions that must be met in order +** for writing to be allowed: ** -** 1: The cursor must have been opened with wrFlag==1 +** 1: The cursor must have been opened with wrFlag containing BTREE_WRCSR ** ** 2: Other database connections that share the same pager cache ** but which are not in the READ_UNCOMMITTED state may not have @@ -4067,6 +4067,16 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ ** ** 4: There must be an active transaction. ** +** The BTREE_FORDELETE bit of wrFlag may optionally be set if BTREE_WRCSR +** is set. If FORDELETE is set, that is a hint to the implementation that +** this cursor will only be used to seek to and delete entries of an index +** as part of a larger DELETE statement. The FORDELETE hint is not used by +** this implementation. But in a hypothetical alternative storage engine +** in which index entries are automatically deleted when corresponding table +** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE +** operations on this cursor can be no-ops and all READ operations can +** return a null row (2-bytes: 0x01 0x00). +** ** No checking is done to make sure that page iTable really is the ** root page of a b-tree. If it is not, then the cursor acquired ** will not work correctly. @@ -8082,13 +8092,21 @@ end_insert: /* ** Delete the entry that the cursor is pointing to. ** -** If the second parameter is zero, then the cursor is left pointing at an -** arbitrary location after the delete. If it is non-zero, then the cursor -** is left in a state such that the next call to BtreeNext() or BtreePrev() -** moves it to the same row as it would if the call to BtreeDelete() had -** been omitted. +** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then +** the cursor is left pointing at an arbitrary location after the delete. +** But if that bit is set, then the cursor is left in a state such that +** the next call to BtreeNext() or BtreePrev() moves it to the same row +** as it would have been on if the call to BtreeDelete() had been omitted. +** +** The BTREE_AUXDELETE bit of flags indicates that is one of several deletes +** associated with a single table entry and its indexes. Only one of those +** deletes is considered the "primary" delete. The primary delete occurs +** on a cursor that is not a BTREE_FORDELETE cursor. All but one delete +** operation on non-FORDELETE cursors is tagged with the AUXDELETE flag. +** The BTREE_AUXDELETE bit is a hint that is not used by this implementation, +** but which might be used by alternative storage engines. */ -int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ +int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; int rc; /* Return code */ @@ -8098,6 +8116,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ int iCellDepth; /* Depth of node containing pCell */ u16 szCell; /* Size of the cell being deleted */ int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ + u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */ assert( cursorOwnsBtShared(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); @@ -8107,6 +8126,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ assert( !hasReadConflicts(p, pCur->pgnoRoot) ); assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); assert( pCur->eState==CURSOR_VALID ); + assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); iCellDepth = pCur->iPage; iCellIdx = pCur->aiIdx[iCellDepth]; diff --git a/src/btree.h b/src/btree.h index 37a9915eda..30522e99e1 100644 --- a/src/btree.h +++ b/src/btree.h @@ -245,7 +245,12 @@ int sqlite3BtreeMovetoUnpacked( ); int sqlite3BtreeCursorHasMoved(BtCursor*); int sqlite3BtreeCursorRestore(BtCursor*, int*); -int sqlite3BtreeDelete(BtCursor*, int); +int sqlite3BtreeDelete(BtCursor*, u8 flags); + +/* Allowed flags for the 2nd argument to sqlite3BtreeDelete() */ +#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ +#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ + int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, const void *pData, int nData, int nZero, int bias, int seekResult); diff --git a/src/delete.c b/src/delete.c index ed273bde81..5b5a5e3cb1 100644 --- a/src/delete.c +++ b/src/delete.c @@ -479,13 +479,12 @@ void sqlite3DeleteFrom( */ if( !isView ){ int iAddrOnce = 0; - u8 p5 = (eOnePass==ONEPASS_OFF ? 0 : OPFLAG_FORDELETE); if( eOnePass==ONEPASS_MULTI ){ iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v); } testcase( IsVirtual(pTab) ); - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, p5, iTabCur, - aToOpen, &iDataCur, &iIdxCur); + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE, + iTabCur, aToOpen, &iDataCur, &iIdxCur); assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur ); assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 ); if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce); @@ -718,15 +717,20 @@ void sqlite3GenerateRowDelete( ** a view (in which case the only effect of the DELETE statement is to ** fire the INSTEAD OF triggers). */ if( pTab->pSelect==0 ){ + u8 p5 = 0; sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); if( count ){ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } + if( eMode!=ONEPASS_OFF ){ + sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE); + } if( iIdxNoSeek>=0 ){ sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek); } - sqlite3VdbeChangeP5(v, eMode==ONEPASS_MULTI); + if( eMode==ONEPASS_MULTI ) p5 |= OPFLAG_SAVEPOSITION; + sqlite3VdbeChangeP5(v, p5); } /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to diff --git a/src/insert.c b/src/insert.c index 8082bcb459..cbe933c221 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1647,7 +1647,7 @@ int sqlite3OpenTableAndIndices( Parse *pParse, /* Parsing context */ Table *pTab, /* Table to be opened */ int op, /* OP_OpenRead or OP_OpenWrite */ - u8 p5, /* P5 value for OP_Open* instructions */ + u8 p5, /* P5 value for OP_Open* opcodes (except on WITHOUT ROWID) */ int iBase, /* Use this for the table cursor, if there is one */ u8 *aToOpen, /* If not NULL: boolean for each table and index */ int *piDataCur, /* Write the database source cursor number here */ @@ -1682,15 +1682,16 @@ int sqlite3OpenTableAndIndices( for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); - if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) && piDataCur ){ - *piDataCur = iIdxCur; - } if( aToOpen==0 || aToOpen[i+1] ){ sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - sqlite3VdbeChangeP5(v, p5); VdbeComment((v, "%s", pIdx->zName)); } + if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ + if( piDataCur ) *piDataCur = iIdxCur; + }else{ + sqlite3VdbeChangeP5(v, p5); + } } if( iBase>pParse->nTab ) pParse->nTab = iBase; return i; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index b851399c59..dbe1595af9 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2847,7 +2847,8 @@ struct AuthContext { /* ** Bitfield flags for P5 value in various opcodes. */ -#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ +#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */ + /* Also used in P2 (not P5) of OP_Delete */ #define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */ #define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ @@ -2860,6 +2861,8 @@ struct AuthContext { #define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */ #define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */ #define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ +#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */ +#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */ /* * Each trigger present in the database schema is stored as an instance of diff --git a/src/vdbe.c b/src/vdbe.c index b7c0258ce6..5ad8b11a46 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -551,6 +551,9 @@ int sqlite3VdbeExec( Op *pOp = aOp; /* Current operation */ #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) Op *pOrigOp; /* Value of pOp at the top of the loop */ +#endif +#ifdef SQLITE_DEBUG + int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ #endif int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ @@ -3397,6 +3400,9 @@ case OP_OpenWrite: pCur->nullRow = 1; pCur->isOrdered = 1; pCur->pgnoRoot = p2; +#ifdef SQLITE_DEBUG + pCur->wrFlag = wrFlag; +#endif rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor); pCur->pKeyInfo = pKeyInfo; /* Set the VdbeCursor.isTable variable. Previous versions of @@ -4356,14 +4362,22 @@ case OP_InsertInt: { ** ** Delete the record at which the P1 cursor is currently pointing. ** -** If the P5 parameter is non-zero, the cursor will be left pointing at -** either the next or the previous record in the table. If it is left -** pointing at the next record, then the next Next instruction will be a -** no-op. As a result, in this case it is OK to delete a record from within a -** Next loop. If P5 is zero, then the cursor is left in an undefined state. +** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then +** the cursor will be left pointing at either the next or the previous +** record in the table. If it is left pointing at the next record, then +** the next Next instruction will be a no-op. As a result, in this case +** it is ok to delete a record from within a Next loop. If +** OPFLAG_SAVEPOSITION bit of P5 is clear, then the cursor will be +** left in an undefined state. ** -** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is -** incremented (otherwise not). +** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this +** delete one of several associated with deleting a table row and all its +** associated index entries. Exactly one of those deletes is the "primary" +** delete. The others are all on OPFLAG_FORDELETE cursors or else are +** marked with the AUXDELETE flag. +** +** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row +** change count is incremented (otherwise not). ** ** P1 must not be pseudo-table. It has to be a real table with ** multiple rows. @@ -4399,7 +4413,26 @@ case OP_Delete: { assert( pC->movetoTarget==iKey ); } #endif - + + /* Only flags that can be set are SAVEPOISTION and AUXDELETE */ + assert( (pOp->p5 & ~(OPFLAG_SAVEPOSITION|OPFLAG_AUXDELETE))==0 ); + assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION ); + assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE ); + +#ifdef SQLITE_DEBUG + if( p->pFrame==0 ){ + if( pC->isEphemeral==0 + && (pOp->p5 & OPFLAG_AUXDELETE)==0 + && (pC->wrFlag & OPFLAG_FORDELETE)==0 + ){ + nExtraDelete++; + } + if( pOp->p2 & OPFLAG_NCHANGE ){ + nExtraDelete--; + } + } +#endif + rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); pC->cacheStatus = CACHE_STALE; @@ -4944,12 +4977,9 @@ case OP_IdxDelete: { r.nField = (u16)pOp->p3; r.default_rc = 0; r.aMem = &aMem[pOp->p2]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ideferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; @@ -6768,6 +6798,9 @@ vdbe_return: testcase( nVmStep>0 ); p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; sqlite3VdbeLeave(p); + assert( rc!=SQLITE_OK || nExtraDelete==0 + || sqlite3_strlike("DELETE%",p->zSql,0)!=0 + ); return rc; /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 85c1a54512..497beb87d5 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -83,6 +83,7 @@ struct VdbeCursor { u8 isTable; /* True for rowid tables. False for indexes */ #ifdef SQLITE_DEBUG u8 seekOp; /* Most recent seek operation on this cursor */ + u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ diff --git a/test/fordelete.test b/test/fordelete.test index 1e860e8867..bc38a4c5e3 100644 --- a/test/fordelete.test +++ b/test/fordelete.test @@ -30,17 +30,49 @@ proc analyze_delete_program {sql} { } { set T($rootpage) $name } - - # Calculate the results. - set res [list] + + # For each OpenWrite instruction generated for the proposed DELETE + # statement, add the following array entries: + # + # $M() -> + # $O() -> "*" | "" + # + # The O() entry is set to "*" if the BTREE_FORDELETE flag is specified, + # or "" otherwise. + # db eval "EXPLAIN $sql" R { - if {$R(opcode) == "OpenWrite"} { - set obj $T($R(p2)) - if {"0x$R(p5)" & 0x08} { append obj *} - lappend res $obj + if {$R(opcode)=="OpenWrite"} { + set root $R(p2) + set csr $R(p1) + if {[info exists T($root)]} { set M($csr) $T($root) } + + set obj $T($root) + set O($obj) "" + if {"0x$R(p5)" & 0x08} { + set O($obj) * + } else { + set O($obj) "" + } } } + db eval "EXPLAIN $sql" R { + if {$R(opcode) == "Delete"} { + set csr $R(p1) + if {[info exists M($csr)]} { + set idxdelete [expr {("0x$R(p5)" & 0x04) ? 1 : 0}] + if {$idxdelete} { + append O($M($csr)) "+" + } + } + } + } + + set res [list] + foreach {k v} [array get O] { + lappend res "${k}${v}" + } + lsort $res } @@ -53,10 +85,10 @@ do_execsql_test 1.0 { } foreach {tn sql res} { - 1 { DELETE FROM t1 WHERE a=?} { sqlite_autoindex_t1_1 t1* } - 2 { DELETE FROM t1 WHERE a=? AND b=? } { sqlite_autoindex_t1_1 t1 } - 3 { DELETE FROM t1 WHERE a>? } { sqlite_autoindex_t1_1 t1* } - 4 { DELETE FROM t1 WHERE rowid=? } { sqlite_autoindex_t1_1* t1 } + 1 { DELETE FROM t1 WHERE a=?} { sqlite_autoindex_t1_1 t1*+ } + 2 { DELETE FROM t1 WHERE a=? AND b=? } { sqlite_autoindex_t1_1 t1+ } + 3 { DELETE FROM t1 WHERE a>? } { sqlite_autoindex_t1_1 t1*+ } + 4 { DELETE FROM t1 WHERE rowid=? } { sqlite_autoindex_t1_1* t1 } } { do_adp_test 1.$tn $sql $res } @@ -68,8 +100,8 @@ do_execsql_test 2.0 { CREATE INDEX t2c ON t2(c); } foreach {tn sql res} { - 1 { DELETE FROM t2 WHERE a=?} { t2* t2a t2b* t2c* } - 2 { DELETE FROM t2 WHERE a=? AND +b=?} { t2 t2a t2b* t2c* } + 1 { DELETE FROM t2 WHERE a=?} { t2*+ t2a t2b* t2c* } + 2 { DELETE FROM t2 WHERE a=? AND +b=?} { t2+ t2a t2b* t2c* } 3 { DELETE FROM t2 WHERE a=? OR b=?} { t2 t2a* t2b* t2c* } 4 { DELETE FROM t2 WHERE +a=? } { t2 t2a* t2b* t2c* } 5 { DELETE FROM t2 WHERE rowid=? } { t2 t2a* t2b* t2c* } @@ -126,5 +158,53 @@ do_test 3.2 { } } {6 {} {} {}} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 4.0 { + CREATE TABLE log(x); + CREATE TABLE p1(one PRIMARY KEY, two); + + CREATE TRIGGER tr_bd BEFORE DELETE ON p1 BEGIN + INSERT INTO log VALUES('delete'); + END; + INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C'); + DELETE FROM p1 WHERE one = 'a'; +} + +reset_db +do_execsql_test 4.1 { + BEGIN TRANSACTION; + CREATE TABLE tbl(a PRIMARY KEY, b, c); + CREATE TABLE log(a, b, c); + INSERT INTO "tbl" VALUES(1,2,3); + CREATE TRIGGER the_trigger BEFORE DELETE ON tbl BEGIN + INSERT INTO log VALUES(1, 2,3); + END; + COMMIT; + DELETE FROM tbl WHERE a=1; +} + +reset_db +do_execsql_test 5.1 { + PRAGMA foreign_keys = 1; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE TABLE t2( + c INTEGER PRIMARY KEY, + d INTEGER DEFAULT 1 REFERENCES t1 ON DELETE SET DEFAULT + ); +} {} +do_execsql_test 5.2 { + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t2 VALUES(1, 2); + SELECT * FROM t2; +} {1 2} +do_execsql_test 5.3 { + DELETE FROM t1 WHERE a = 2; +} {} + + finish_test