** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.222 2004/11/22 05:26:27 danielk1977 Exp $
+** $Id: btree.c,v 1.223 2004/11/22 10:02:10 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
** A cursor is a pointer to a particular entry in the BTree.
** The entry is identified by its MemPage and the index in
** MemPage.aCell[] of the entry.
-**
-** Normally, the BtCursor.delShift variable is 0. If non-zero, this
-** indicates that the entry to which the cursor logically points
-** was deleted (by a BtreeDelete() call). If this is the case, the
-** BtreeKeySize() and BtreeDataSize() calls both return 0.
-
-** If BtCursor.delShift is +1, then do not move the cursor for a
-** BtreeNext() operation (it was already advanced when the entry the
-** cursor logically points to was deleted). If BtCursor.delShift is
-** -1, then ignore the next BtreePrevious() call.
*/
struct BtCursor {
Btree *pBt; /* The Btree to which this cursor belongs */
u8 wrFlag; /* True if writable */
u8 isValid; /* TRUE if points to a valid entry */
u8 status; /* Set to SQLITE_ABORT if cursors is invalidated */
- int delShift; /* See above. */
};
/*
pBt->pCursor = pCur;
pCur->isValid = 0;
pCur->status = SQLITE_OK;
- pCur->delShift = 0;
*ppCur = pCur;
return SQLITE_OK;
** itself, not the number of bytes in the key.
*/
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
- if( !pCur->isValid || pCur->delShift ){
+ if( !pCur->isValid ){
*pSize = 0;
}else{
getCellInfo(pCur);
** the database is empty) then *pSize is set to 0.
*/
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
- if( !pCur->isValid || pCur->delShift ){
+ if( !pCur->isValid ){
/* Not pointing at a valid entry - set *pSize to 0. */
*pSize = 0;
}else{
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- if( !pCur->isValid || pCur->delShift ){
+ if( !pCur->isValid ){
return pCur->status;
}
assert( pCur->pPage!=0 );
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- if( !pCur->isValid || pCur->delShift ){
+ if( !pCur->isValid ){
return pCur->status ? pCur->status : SQLITE_INTERNAL;
}
assert( pCur->pPage!=0 );
rc = moveToChild(pCur, subpage);
}
pCur->isValid = pCur->pPage->nCell>0;
- pCur->delShift = 0;
return rc;
}
assert( pPage->isInit );
assert( pCur->idx<pPage->nCell );
- /* If BtCursor.delShift is 1, the cursor has already been advanced. */
- if( pCur->delShift==1 ){
- *pRes = 0;
- pCur->delShift = 0;
- return SQLITE_OK;
- }else{
- pCur->delShift = 0;
- }
-
pCur->idx++;
pCur->info.nSize = 0;
if( pCur->idx>=pPage->nCell ){
return SQLITE_OK;
}
- /* If BtCursor.delShift is -1, the cursor has already been advanced. */
- if( pCur->delShift==-1 ){
- *pRes = 0;
- pCur->delShift = 0;
- return SQLITE_OK;
- }else{
- pCur->delShift = 0;
- }
-
pPage = pCur->pPage;
assert( pPage->isInit );
assert( pCur->idx>=0 );
int *szCell; /* Local size of all cells in apCell[] */
u8 *aCopy[NB]; /* Space for holding data of apCopy[] */
u8 *aSpace; /* Space to hold copies of dividers cells */
- BtCursor *pCur;
/*
** Find the parent page.
zeroPage(pNew, pageFlags);
}
+ /* Free any old pages that were not reused as new pages.
+ */
+ while( i<nOld ){
+ rc = freePage(apOld[i]);
+ if( rc ) goto balance_cleanup;
+ releasePage(apOld[i]);
+ apOld[i] = 0;
+ i++;
+ }
+
/*
** Put the new pages in accending order. This helps to
** keep entries in the disk file in order so that a scan
nNew>=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0,
nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0));
- /* If there are other cursors that refer to one of the pages involved
- ** in the balancing, then adjust these cursors so that they still
- ** point to the same cells.
- */
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- int nCellCnt = 0;
- int iCell = -1;
- Pgno pgno = pCur->pPage->pgno;
-
- /* If the cursor is not valid, do not do anything with it. */
- if( !pCur->isValid ) continue;
-
- /* If the cursor pointed to one of the cells moved around during the
- ** balancing, then set variable iCell to the index of the cell in apCell.
- ** This is used by the block below to figure out where the cell was
- ** moved to, and adjust the cursor appropriately.
- **
- ** If the cursor points to the parent page, but the cell was not involved
- ** in the balance, then declare the cache of the cell-parse invalid, as a
- ** defragmentation may of occured during the balance. Also, if the index
- ** of the cell is greater than that of the divider cells, then it may
- ** need to be adjusted (in case there are now more or less divider cells
- ** than there were before the balancing).
- */
- for(i=0; iCell<0 && i<nOld; i++){
- if( pgno==apCopy[i]->pgno ){
- iCell = nCellCnt + pCur->idx;
- break;
- }
- nCellCnt += (apCopy[i]->nCell + apCopy[i]->nOverflow) + (leafData?0:1);
- }
- if( pgno==pParent->pgno ){
- assert( !leafData );
- assert( iCell==-1 );
- if( pCur->idx>=nxDiv && pCur->idx<(nxDiv+nOld-1) ){
- for(i=0; i<=(pCur->idx-nxDiv); i++){
- iCell += (apCopy[i]->nCell + apCopy[i]->nOverflow + 1);
- }
- }
- if( pCur->idx>=(nxDiv+nOld-1) ){
- TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
- pCur, pgno, pCur->idx, pgno, pCur->idx+(nNew-nOld)));
- pCur->idx += (nNew-nOld);
- }
- pCur->info.nSize = 0;
- }
-
- /* If iCell is greater than or equal to zero, then pCur points at a
- ** cell that was moved around during the balance. Figure out where
- ** the cell was moved to and adjust pCur to match.
- */
- if( iCell>=0 ){
- int idxNew;
- Pgno pgnoNew;
- int x = 0;
-
- assert( iCell<nCell );
- while( cntNew[x]<=iCell ) x++;
- if( x>0 && !leafData && cntNew[x-1]==iCell ){
- /* The cell that pCur points to is a divider cell in pParent. */
- pgnoNew = pParent->pgno;
- idxNew = nxDiv + x-1;
- }else{
- /* The cell that pCur points to is on page apNew[x]. */
- idxNew = iCell-(x>0?cntNew[x-1]:0)-((leafData||x==0)?0:1);
- pgnoNew = apNew[x]->pgno;
- }
-
- TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
- pCur, pgno, pCur->idx, pgnoNew, idxNew));
-
- pCur->idx = idxNew;
- releasePage(pCur->pPage);
- rc = getPage(pBt, pgnoNew, &pCur->pPage);
- assert( rc==SQLITE_OK );
- assert( pCur->pPage->isInit );
- pCur->info.nSize = 0;
- }
- }
-
- /* Free any old pages that were not reused as new pages.
- */
- for(i=nNew; i<nOld; i++){
- rc = freePage(apOld[i]);
- if( rc ) goto balance_cleanup;
- releasePage(apOld[i]);
- apOld[i] = 0;
- }
-
/*
** Evenly distribute the data in apCell[] across the new pages.
** Insert divider cells into pParent as necessary.
/* The child information will fit on the root page, so do the
** copy */
int i;
- BtCursor *pCur;
zeroPage(pPage, pChild->aData[0]);
for(i=0; i<pChild->nCell; i++){
apCell[i] = findCell(pChild,i);
assemblePage(pPage, pChild->nCell, apCell, szCell);
freePage(pChild);
TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno));
- /* If there were cursors pointing at this page, point them at the
- ** new page instead. Decrement the reference count for the old
- ** page and increment it for the new one.
- */
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->pPage==pChild ){
- TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
- pCur, pPage->pgno, pCur->idx, pPage->pgno, pCur->idx));
- releasePage(pCur->pPage);
- rc = getPage(pBt, 1, &pCur->pPage);
- assert( rc==SQLITE_OK );
- }
- }
}else{
/* The child has more information that will fit on the root.
** The tree is already balanced. Do nothing. */
TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno));
}
}else{
- BtCursor *pCur;
memcpy(pPage->aData, pChild->aData, pPage->pBt->usableSize);
pPage->isInit = 0;
pPage->pParent = 0;
freePage(pChild);
TRACE(("BALANCE: transfer child %d into root %d\n",
pChild->pgno, pPage->pgno));
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->pPage==pChild ){
- TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
- pCur, pChild->pgno, pCur->idx, pPage->pgno, pCur->idx));
- releasePage(pCur->pPage);
- rc = getPage(pBt, pPage->pgno, &pCur->pPage);
- assert( rc==SQLITE_OK );
- }
- }
}
rc = reparentChildPages(pPage);
if( rc!=SQLITE_OK ) goto end_shallow_balance;
u8 *cdata; /* Content of the child page */
int hdr; /* Offset to page header in parent */
int brk; /* Offset to content of first cell in parent */
- BtCursor *pCur;
assert( pPage->pParent==0 );
assert( pPage->nOverflow>0 );
zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
-
- /* If there were cursors pointing at this page, point them at the new
- ** page instead. Decrement the reference count for the old page and
- ** increment it for the new one.
- */
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->pPage==pPage ){
- TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n",
- pCur, pPage->pgno, pCur->idx, pChild->pgno, pCur->idx));
- releasePage(pCur->pPage);
- rc = getPage(pBt, pChild->pgno, &pCur->pPage);
- assert( rc==SQLITE_OK );
- }
- }
-
rc = balance_nonroot(pChild);
releasePage(pChild);
return rc;
** wrFlag==0 then this routine returns SQLITE_LOCKED. If all
** cursors that point to pgnoRoot were opened with wrFlag==1
** then this routine returns SQLITE_OK.
+**
+** In addition to checking for read-locks (where a read-lock
+** means a cursor opened with wrFlag==0) this routine also moves
+** all cursors other than pExclude so that they are pointing to the
+** first Cell on root page. This is necessary because an insert
+** or delete might change the number of cells on a page or delete
+** a page entirely and we do not want to leave any cursors
+** pointing to non-existant pages or cells.
*/
static int checkReadLocks(Btree *pBt, Pgno pgnoRoot, BtCursor *pExclude){
+ BtCursor *p;
+ for(p=pBt->pCursor; p; p=p->pNext){
+ if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
+ if( p->wrFlag==0 ) return SQLITE_LOCKED;
+ if( p->pPage->pgno!=p->pgnoRoot ){
+ moveToRoot(p);
+ }
+ }
return SQLITE_OK;
}
Btree *pBt = pCur->pBt;
unsigned char *oldCell;
unsigned char *newCell = 0;
- BtCursor *pCur2;
if( pCur->status ){
return pCur->status; /* A rollback destroyed this cursor */
assert( pPage->leaf );
}
rc = insertCell(pPage, pCur->idx, newCell, szNew, 0);
- pCur->isValid = 1;
-
- /* If there are other cursors pointing at this page with a BtCursor.idx
- ** field greater than or equal to 'i', then the cell they refer to
- ** has been modified or moved within the page. Fix the cursor.
- */
- for(pCur2=pPage->pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
- if( pCur2->pPage==pPage ){
- if( pCur2->idx>=pCur->idx && pCur!=pCur2 && loc!=0 ){
- /* The cell pointed to by pCur2 was shifted one to the right on it's
- ** page by this Insert().
- */
- TRACE(("INSERT: Cursor %p migrates from %d,%d to %d,%d\n",
- pCur2, pPage->pgno, pCur2->idx, pPage->pgno, pCur2->idx+1));
- pCur2->idx++;
- }
- pCur2->info.nSize = 0;
- }
- }
-
if( rc!=SQLITE_OK ) goto end_insert;
rc = balance(pPage);
/* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
/* fflush(stdout); */
+ if( rc==SQLITE_OK ){
+ moveToRoot(pCur);
+ }
end_insert:
sqliteFree(newCell);
return rc;
int rc;
Pgno pgnoChild = 0;
Btree *pBt = pCur->pBt;
- int idx; /* Index of the cell to delete */
- BtCursor *pCur2; /* Iterator variable for the pBt.pCursor link-list */
assert( pPage->isInit );
if( pCur->status ){
rc = sqlite3pager_write(pPage->aData);
if( rc ) return rc;
- /* Set index to the index in pPage that contains the cell to delete. Also
- ** increment the reference count for pPage. This allows us to move the
- ** cursor pCur before the delete takes place.
- */
- idx = pCur->idx;
- rc = getPage(pBt, pPage->pgno, &pPage);
- if( rc ) return rc;
- assert( pPage==pCur->pPage );
-
- /* If there are any cursors that point to the cell being deleted,
- ** move them to the next or previous entry in the table. It is preferable
- ** to move the cursor to the 'next' location, rather than the 'previous'
- ** one, as most table scans are done in the forward direction (also, code
- ** below depends on this). If neither entry exists, declare the cursor
- ** invalid.
- */
- for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
- if( pCur2->pPage==pPage && pCur2->idx==idx && pCur2->isValid ){
- int res;
- pCur2->delShift = 0;
- rc = sqlite3BtreeNext(pCur2, &res);
- if( rc ) goto delete_out;
- if( res ){
- /* If the next tree entry cannot be found, then the cursor must
- ** already point to the last table entry. So point it to the
- ** second last by calling BtreeLast(), BtreePrevious().
- */
- rc = sqlite3BtreeLast(pCur2, &res);
- if( rc ) goto delete_out;
- assert( res==0 );
- rc = sqlite3BtreePrevious(pCur2, &res);
- if( rc ) goto delete_out;
- pCur2->delShift = -1;
- }else{
- pCur2->delShift = 1;
- }
- }
- }
-
/* Locate the cell within it's page and leave pCell pointing to the
** data. The clearCell() call frees any overflow pages associated with the
** cell. The cell itself is still intact.
*/
- pCell = findCell(pPage, idx);
+ pCell = findCell(pPage, pCur->idx);
if( !pPage->leaf ){
pgnoChild = get4byte(pCell);
}
** do something we will leave a hole on an internal page.
** We have to fill the hole by moving in a cell from a leaf. The
** next Cell after the one to be deleted is guaranteed to exist and
- ** to be a leaf so we can use it. Conveniantly, pCur now points
- ** at this cell (because it was advanced above).
+ ** to be a leaf so we can use it.
*/
BtCursor leafCur;
unsigned char *pNext;
int szNext;
+ int notUsed;
unsigned char *tempCell;
assert( !pPage->leafData );
-
- /* Make a copy of *pCur in leafCur. leafCur now points to the cell
- ** that will be moved into the space left by the cell being deleted.
- */
- assert( pCur->delShift==1 );
- assert( pCur->isValid );
getTempCursor(pCur, &leafCur);
+ rc = sqlite3BtreeNext(&leafCur, ¬Used);
if( rc!=SQLITE_OK ){
if( rc!=SQLITE_NOMEM ){
rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */
}
- goto delete_out;
+ return rc;
}
rc = sqlite3pager_write(leafCur.pPage->aData);
- if( rc ) goto delete_out;
- TRACE(("DELETE: table=%d delete internal from %d,%d replace "
- "from leaf %d,%d\n", pCur->pgnoRoot, pPage->pgno, idx,
- leafCur.pPage->pgno, leafCur.idx));
-
- /* Drop the cell from the internal page. Make a copy of the cell from
- ** the leaf page into memory obtained from malloc(). Insert it into
- ** the internal page, at the position vacated by the delete. There
- ** are now two copies of the leaf-cell in the tree.
- */
- dropCell(pPage, idx, cellSizePtr(pPage, pCell));
+ if( rc ) return rc;
+ TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
+ pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
+ dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
pNext = findCell(leafCur.pPage, leafCur.idx);
szNext = cellSizePtr(leafCur.pPage, pNext);
assert( MX_CELL_SIZE(pBt)>=szNext+4 );
tempCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
- if( tempCell==0 ){
- rc = SQLITE_NOMEM;
- goto delete_out;
- }
- rc = insertCell(pPage, idx, pNext-4, szNext+4, tempCell);
- if( rc!=SQLITE_OK ) goto delete_out;
- put4byte(findOverflowCell(pPage, idx), pgnoChild);
- pPage->idxShift = 0;
-
- /* If there are any cursors that point to the leaf-cell, move them
- ** so that they point at internal cell. This is easiest done by
- ** calling BtreePrevious().
- **
- ** Also, any cursors that point to the internal page have their
- ** cached parses invalidated, as the insertCell() above may have
- ** caused a defragmation.
- */
- for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
- if( pCur2->pPage==leafCur.pPage && pCur2->idx==leafCur.idx ){
- int res;
- int delShiftSave = pCur2->delShift;
- assert( leafCur.idx==0 );
- pCur2->delShift = 0;
- rc = sqlite3BtreePrevious(pCur2, &res);
- if( rc ) goto delete_out;
- assert( res==0 );
- assert( pCur2->pPage==pPage );
- assert( pCur2->idx==idx );
- pCur2->delShift = delShiftSave;
- }
- if( pCur2->pPage==pPage ){
- pCur2->info.nSize = 0;
- }
- }
-
- /* Balance the internal page. Free the memory allocated for the
- ** copy of the leaf cell. Then delete the cell from the leaf page.
- */
+ if( tempCell==0 ) return SQLITE_NOMEM;
+ rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell);
+ if( rc!=SQLITE_OK ) return rc;
+ put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild);
rc = balance(pPage);
sqliteFree(tempCell);
- if( rc ) goto delete_out;
+ if( rc ) return rc;
dropCell(leafCur.pPage, leafCur.idx, szNext);
-
- for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
- if( pCur2->pPage==leafCur.pPage && pCur2->idx>leafCur.idx ){
- TRACE(("DELETE: Cursor %p migrates from %d,%d to %d,%d\n", pCur2,
- leafCur.pPage->pgno,pCur2->idx,leafCur.pPage->pgno, pCur2->idx-1));
- pCur2->idx--;
- pCur2->info.nSize = 0;
- }
- }
-
rc = balance(leafCur.pPage);
releaseTempCursor(&leafCur);
}else{
- TRACE(("DELETE: table=%d delete %d from leaf %d\n",
- pCur->pgnoRoot, idx, pPage->pgno));
- dropCell(pPage, idx, cellSizePtr(pPage, pCell));
-
- /* If there were cursors pointing to cells on pPage with index values
- ** greater than idx, decrement the index values now.
- */
- for(pCur2=pBt->pCursor; pCur2; pCur2 = pCur2->pNext){
- assert( !pCur2->isValid || pCur2->pPage!=pPage || pCur2->idx!=idx );
- if( pCur2->pPage==pPage && pCur2->idx>idx ){
- TRACE(("DELETE: Cursor %p migrates from %d,%d to %d,%d\n",
- pCur2, pPage->pgno, pCur2->idx, pPage->pgno, pCur2->idx-1));
- pCur2->idx--;
- pCur2->info.nSize = 0;
- }
- }
-
+ TRACE(("DELETE: table=%d delete from leaf %d\n",
+ pCur->pgnoRoot, pPage->pgno));
+ dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
rc = balance(pPage);
}
-
-delete_out:
- releasePage(pPage);
+ moveToRoot(pCur);
return rc;
}
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
-** $Id: delete.c,v 1.89 2004/11/16 15:50:20 danielk1977 Exp $
+** $Id: delete.c,v 1.90 2004/11/22 10:02:10 danielk1977 Exp $
*/
#include "sqliteInt.h"
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
}
+
/*
** Process a DELETE FROM statement.
*/
Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */
const char *zDb; /* Name of database holding pTab */
- int addr = 0; /* A couple addresses of generated code */
+ int end, addr = 0; /* A couple addresses of generated code */
int i; /* Loop counter */
WhereInfo *pWInfo; /* Information about the WHERE clause */
Index *pIdx; /* For looping over indices of the table */
oldIdx = pParse->nTab++;
}
- /* Resolve the column names in all the expressions. Allocate cursors
- ** for the table and indices first, in case an expression needs to use
- ** a cursor (e.g. an IN() expression).
+ /* Resolve the column names in all the expressions.
*/
assert( pTabList->nSrc==1 );
iCur = pTabList->a[0].iCursor = pParse->nTab++;
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- pParse->nTab++;
- }
if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pWhere, 0, 0) ){
goto delete_from_cleanup;
}
}
}
+ /* Begin the database scan
+ */
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0);
+ if( pWInfo==0 ) goto delete_from_cleanup;
+
+ /* Remember the key of every item to be deleted.
+ */
+ sqlite3VdbeAddOp(v, OP_ListWrite, 0, 0);
+ if( db->flags & SQLITE_CountRows ){
+ sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
+ }
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+
/* Open the pseudo-table used to store OLD if there are triggers.
*/
if( row_triggers_exist ){
sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
}
- /* Open cursors for the table and indices we are deleting from. */
- if( !isView ){
- sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
- }
-
- /* Begin the database scan
+ /* Delete every item whose key was written to the list during the
+ ** database scan. We have to delete items after the scan is complete
+ ** because deleting an item can change the scan order.
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, isView?0:iCur);
- if( pWInfo==0 ) goto delete_from_cleanup;
- addr = pWInfo->iContinue;
+ sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0);
+ end = sqlite3VdbeMakeLabel(v);
- /* If row-triggers exist, copy the record being deleted into the
- ** oldIdx psuedo-table. Then invoke the BEFORE triggers.
+ /* This is the beginning of the delete loop when there are
+ ** row triggers.
*/
if( row_triggers_exist ){
+ addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ if( !isView ){
+ sqlite3OpenTableForReading(v, iCur, pTab);
+ }
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
+ if( !isView ){
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
+
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
addr);
}
- /* Delete the row. Increment the callback value if the count-rows flag
- ** is set.
- */
- if( db->flags & SQLITE_CountRows ){
- sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
+ if( !isView ){
+ /* Open cursors for the table we are deleting from and all its
+ ** indices. If there are row triggers, this happens inside the
+ ** OP_ListRead loop because the cursor have to all be closed
+ ** before the trigger fires. If there are no row triggers, the
+ ** cursors are opened only once on the outside the loop.
+ */
+ sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
+
+ /* This is the beginning of the delete loop when there are no
+ ** row triggers */
+ if( !row_triggers_exist ){
+ addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
+ }
+
+ /* Delete the row */
+ sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0);
}
- sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
- sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0);
- /* Code the AFTER triggers. */
+ /* If there are row triggers, close all cursors then invoke
+ ** the AFTER triggers
+ */
if( row_triggers_exist ){
+ if( !isView ){
+ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
+ }
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
addr);
}
- /* End the database scan loop and close indices. */
- sqlite3WhereEnd(pWInfo);
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
+ /* End of the delete loop */
+ sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
+ sqlite3VdbeResolveLabel(v, end);
+ sqlite3VdbeAddOp(v, OP_ListReset, 0, 0);
+
+ /* Close the cursors after the loop if there are no row triggers */
+ if( !row_triggers_exist ){
+ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
+ }
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
}
}