Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch btree-fordelete-flag Excluding Merge-Ins
This is equivalent to a diff from a3d7b8ac to 46080b04
2016-01-30
| ||
13:32 | Add the BTREE_FORDELETE and BTREE_AUXDELETE flags to the b-tree layer interface and use them. Add assert() statement to verify that they are correct. (check-in: 85c46704 user: drh tags: trunk) | |
2016-01-29
| ||
19:04 | Improvements to FORDELETE processing, including bug fixes and adding assert() statements to help verify correct operation. (Closed-Leaf check-in: 46080b04 user: drh tags: btree-fordelete-flag) | |
18:48 | Remove a stray "exit" in a test script. (Closed-Leaf check-in: 10c88bf9 user: drh tags: fordelete-assert) | |
2016-01-27
| ||
07:28 | Support building for Windows 10 desktop using 'MinCore.lib'. (check-in: 4bf89891 user: mistachkin tags: trunk) | |
2016-01-26
| ||
23:32 | Merge all recent trunk enhancements. (check-in: 9a71d56d user: drh tags: btree-fordelete-flag) | |
20:19 | Performance improvements for fts5, particularly detail=col mode. (check-in: a3d7b8ac user: dan tags: trunk) | |
20:08 | Further minor performance improvements and code-size reductions related to fts5 column filters on detail=col tables. (Leaf check-in: b4ac61ae user: dan tags: fts5-perf) | |
14:48 | Fix issues on unix with opening database files via symlinks that are not in the current working directory. And with nested symlinks. (check-in: 4003db4a user: dan tags: trunk) | |
Changes to src/btree.c.
︙ | ︙ | |||
4045 4046 4047 4048 4049 4050 4051 | /* ** Create a new cursor for the BTree whose root is on the page ** iTable. If a read-only cursor is requested, it is assumed that ** the caller already has at least a read-only transaction open ** on the database already. If a write-cursor is requested, then ** the caller is assumed to have an open write transaction. ** | | | | | | | > > > > > > > > > > | 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 | /* ** Create a new cursor for the BTree whose root is on the page ** iTable. If a read-only cursor is requested, it is assumed that ** the caller already has at least a read-only transaction open ** on the database already. If a write-cursor is requested, then ** the caller is assumed to have an open write transaction. ** ** 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 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 ** cursors open with wrFlag==0 on the same table. Otherwise ** the changes made by this write cursor would be visible to ** the read cursors in the other database connection. ** ** 3: The database must be writable (not on read-only media) ** ** 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. ** ** It is assumed that the sqlite3BtreeCursorZero() has been called ** on pCur to initialize the memory space prior to invoking this routine. |
︙ | ︙ | |||
8078 8079 8080 8081 8082 8083 8084 | end_insert: return rc; } /* ** Delete the entry that the cursor is pointing to. ** | | | > | | | > > > > > > > | > > | 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 | end_insert: return rc; } /* ** Delete the entry that the cursor is pointing to. ** ** 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, u8 flags){ Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; int rc; /* Return code */ MemPage *pPage; /* Page to delete cell from */ unsigned char *pCell; /* Pointer to cell to delete */ int iCellIdx; /* Index of cell to delete */ 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 ); assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( pCur->curFlags & BTCF_WriteFlag ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); assert( pCur->eState==CURSOR_VALID ); assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); iCellDepth = pCur->iPage; iCellIdx = pCur->aiIdx[iCellDepth]; pPage = pCur->apPage[iCellDepth]; pCell = findCell(pPage, iCellIdx); /* If the page containing the entry to delete is not a leaf page, move |
︙ | ︙ |
Changes to src/btree.h.
︙ | ︙ | |||
241 242 243 244 245 246 247 | UnpackedRecord *pUnKey, i64 intKey, int bias, int *pRes ); int sqlite3BtreeCursorHasMoved(BtCursor*); int sqlite3BtreeCursorRestore(BtCursor*, int*); | | > > > > > | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | UnpackedRecord *pUnKey, i64 intKey, int bias, int *pRes ); int sqlite3BtreeCursorHasMoved(BtCursor*); int sqlite3BtreeCursorRestore(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); int sqlite3BtreeFirst(BtCursor*, int *pRes); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int *pRes); int sqlite3BtreeEof(BtCursor*); |
︙ | ︙ |
Changes to src/delete.c.
︙ | ︙ | |||
475 476 477 478 479 480 481 | /* Unless this is a view, open cursors for the table we are ** deleting from and all its indices. If this is a view, then the ** only effect this statement has is to fire the INSTEAD OF ** triggers. */ if( !isView ){ int iAddrOnce = 0; | < | | | 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 | /* Unless this is a view, open cursors for the table we are ** deleting from and all its indices. If this is a view, then the ** only effect this statement has is to fire the INSTEAD OF ** triggers. */ if( !isView ){ int iAddrOnce = 0; if( eOnePass==ONEPASS_MULTI ){ iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v); } testcase( IsVirtual(pTab) ); 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); } /* Set up a loop over the rowids/primary-keys that were found in the ** where-clause loop above. |
︙ | ︙ | |||
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0); } /* Delete the index and table entries. Skip this step if pTab is really ** a view (in which case the only effect of the DELETE statement is to ** fire the INSTEAD OF triggers). */ if( pTab->pSelect==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( iIdxNoSeek>=0 ){ sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek); } | > > > > > | | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 | sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0); } /* Delete the index and table entries. Skip this step if pTab is really ** 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); } if( eMode==ONEPASS_MULTI ) p5 |= OPFLAG_SAVEPOSITION; sqlite3VdbeChangeP5(v, p5); } /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just deleted. */ sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0); |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
1643 1644 1645 1646 1647 1648 1649 | ** If pTab is a virtual table, then this routine is a no-op and the ** *piDataCur and *piIdxCur values are left uninitialized. */ int sqlite3OpenTableAndIndices( Parse *pParse, /* Parsing context */ Table *pTab, /* Table to be opened */ int op, /* OP_OpenRead or OP_OpenWrite */ | | | 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 | ** If pTab is a virtual table, then this routine is a no-op and the ** *piDataCur and *piIdxCur values are left uninitialized. */ 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* 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 */ int *piIdxCur /* Write the first index cursor number here */ ){ int i; int iDb; |
︙ | ︙ | |||
1678 1679 1680 1681 1682 1683 1684 | }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); } if( piIdxCur ) *piIdxCur = iBase; for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); | < < < < > > > > > | 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 | }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); } if( piIdxCur ) *piIdxCur = iBase; for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); if( aToOpen==0 || aToOpen[i+1] ){ sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); 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; } |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
2843 2844 2845 2846 2847 2848 2849 | const char *zAuthContext; /* Put saved Parse.zAuthContext here */ Parse *pParse; /* The Parse structure */ }; /* ** Bitfield flags for P5 value in various opcodes. */ | | > > > | 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 | const char *zAuthContext; /* Put saved Parse.zAuthContext here */ Parse *pParse; /* The Parse structure */ }; /* ** Bitfield flags for P5 value in various opcodes. */ #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 */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */ #define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ #define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ #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 * struct Trigger. * * Pointers to instances of struct Trigger are stored in two ways. * 1. In the "trigHash" hash table (part of the sqlite3* that represents the |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
548 549 550 551 552 553 554 555 556 557 558 559 560 561 | Vdbe *p /* The VDBE */ ){ Op *aOp = p->aOp; /* Copy of p->aOp */ Op *pOp = aOp; /* Current operation */ #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) Op *pOrigOp; /* Value of pOp at the top of the loop */ #endif int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ int iCompare = 0; /* Result of last OP_Compare operation */ unsigned nVmStep = 0; /* Number of virtual machine steps */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK | > > > | 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | Vdbe *p /* The VDBE */ ){ Op *aOp = p->aOp; /* Copy of p->aOp */ 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 */ u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ int iCompare = 0; /* Result of last OP_Compare operation */ unsigned nVmStep = 0; /* Number of virtual machine steps */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
︙ | ︙ | |||
3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 | assert( nField>=0 ); testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; pCur->isOrdered = 1; pCur->pgnoRoot = p2; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor); pCur->pKeyInfo = pKeyInfo; /* Set the VdbeCursor.isTable variable. Previous versions of ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has ** since moved into the btree layer. */ pCur->isTable = pOp->p4type!=P4_KEYINFO; | > > > | 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 | assert( nField>=0 ); testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); if( pCur==0 ) goto no_mem; 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 ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has ** since moved into the btree layer. */ pCur->isTable = pOp->p4type!=P4_KEYINFO; |
︙ | ︙ | |||
4337 4338 4339 4340 4341 4342 4343 | break; } /* Opcode: Delete P1 P2 * P4 P5 ** ** Delete the record at which the P1 cursor is currently pointing. ** | > | | | | > | > > > > > > | | | 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 | break; } /* Opcode: Delete P1 P2 * P4 P5 ** ** Delete the record at which the P1 cursor is currently pointing. ** ** 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_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. ** ** If P4 is not NULL, then it is the name of the table that P1 is ** pointing to. The update hook will be invoked, if it exists. ** If P4 is not NULL then the P1 cursor must have been positioned |
︙ | ︙ | |||
4380 4381 4382 4383 4384 4385 4386 | ** is being deleted */ if( pOp->p4.z && pC->isTable && pOp->p5==0 ){ i64 iKey = 0; sqlite3BtreeKeySize(pC->uc.pCursor, &iKey); assert( pC->movetoTarget==iKey ); } #endif | | > > > > > > > > > > > > > > > > > > > | 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 | ** is being deleted */ if( pOp->p4.z && pC->isTable && pOp->p5==0 ){ i64 iKey = 0; sqlite3BtreeKeySize(pC->uc.pCursor, &iKey); 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; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && hasUpdateCallback ){ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, db->aDb[pC->iDb].zName, pOp->p4.z, pC->movetoTarget); |
︙ | ︙ | |||
4925 4926 4927 4928 4929 4930 4931 | pCrsr = pC->uc.pCursor; assert( pCrsr!=0 ); assert( pOp->p5==0 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; r.default_rc = 0; r.aMem = &aMem[pOp->p2]; | < < < | | 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 | pCrsr = pC->uc.pCursor; assert( pCrsr!=0 ); assert( pOp->p5==0 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; r.default_rc = 0; r.aMem = &aMem[pOp->p2]; rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); if( rc==SQLITE_OK && res==0 ){ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); } assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; break; } /* Opcode: IdxRowid P1 P2 * * * |
︙ | ︙ | |||
6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 | ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: db->lastRowid = lastRowid; testcase( nVmStep>0 ); p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; sqlite3VdbeLeave(p); return rc; /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH ** is encountered. */ too_big: sqlite3VdbeError(p, "string or blob too big"); | > > > | 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 | ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: db->lastRowid = lastRowid; 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 ** is encountered. */ too_big: sqlite3VdbeError(p, "string or blob too big"); |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | u8 eCurType; /* One of the CURTYPE_* values above */ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ u8 nullRow; /* True if pointing to a row with no data */ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ u8 isTable; /* True for rowid tables. False for indexes */ #ifdef SQLITE_DEBUG u8 seekOp; /* Most recent seek operation on this cursor */ #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ Pgno pgnoRoot; /* Root page of the open btree cursor */ i16 nField; /* Number of fields in the header */ u16 nHdrParsed; /* Number of header fields parsed so far */ | > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | u8 eCurType; /* One of the CURTYPE_* values above */ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ u8 nullRow; /* True if pointing to a row with no data */ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ 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 */ Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ Pgno pgnoRoot; /* Root page of the open btree cursor */ i16 nField; /* Number of fields in the header */ u16 nHdrParsed; /* Number of header fields parsed so far */ |
︙ | ︙ |
Changes to test/fordelete.test.
︙ | ︙ | |||
26 27 28 29 30 31 32 | proc analyze_delete_program {sql} { # Build a map from root page to table/index name. db eval { SELECT name, rootpage FROM sqlite_master } { set T($rootpage) $name } | | | > > > > > > | > | > > > > | > | > > > > > > > > > > > > > | | | > > > > > > > | | | | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | proc analyze_delete_program {sql} { # Build a map from root page to table/index name. db eval { SELECT name, rootpage FROM sqlite_master } { set T($rootpage) $name } # For each OpenWrite instruction generated for the proposed DELETE # statement, add the following array entries: # # $M(<cursor number>) -> <object name> # $O(<object name>) -> "*" | "" # # 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 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 } proc do_adp_test {tn sql res} { uplevel [list do_test $tn [list analyze_delete_program $sql] [list {*}$res]] } do_execsql_test 1.0 { CREATE TABLE t1(a PRIMARY KEY, b); } 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 } } { do_adp_test 1.$tn $sql $res } do_execsql_test 2.0 { CREATE TABLE t2(a, b, c); CREATE INDEX t2a ON t2(a); CREATE INDEX t2b ON t2(b); 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* } 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* } } { do_adp_test 2.$tn $sql $res } |
︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 | btree_close_cursor $csr db eval { COMMIT } db eval { SELECT * FROM x2; } } {6 {} {} {}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | btree_close_cursor $csr db eval { COMMIT } db eval { SELECT * FROM x2; } } {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 |