From 299b187d76563b09636bfc8cde7f2c5f0443ccd6 Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Mon, 22 Nov 2004 10:02:10 +0000 Subject: [PATCH] Back out changes allowing writes to tables that have open cursors. (CVS 2133) FossilOrigin-Name: 91acd87e52509a8f78894d0f4b625b54376cac21 --- manifest | 22 +-- manifest.uuid | 2 +- src/btree.c | 370 +++++++----------------------------------------- src/delete.c | 107 +++++++++----- src/select.c | 4 +- src/sqliteInt.h | 5 +- src/update.c | 4 +- src/where.c | 9 +- 8 files changed, 148 insertions(+), 375 deletions(-) diff --git a/manifest b/manifest index 9ce0f1c7e9..6e2e1927e8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Modify\stest\ssuite\sto\swork\swhen\sSQLITE_OMIT_VIEW\sis\sdefined.\s(CVS\s2132) -D 2004-11-22T08:43:32 +C Back\sout\schanges\sallowing\swrites\sto\stables\sthat\shave\sopen\scursors.\s(CVS\s2133) +D 2004-11-22T10:02:10 F Makefile.in 8291610f5839939a5fbff4dbbf85adb0fe1ac37f F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -29,11 +29,11 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea -F src/btree.c 614ac85f810a636d375da58d0e069896eb467b1a +F src/btree.c edcd4abafe0db11081e4e2db854b17649bec27d9 F src/btree.h 861e40b759a195ba63819740e484390012cf81ab F src/build.c c7dd57fdbb330d65df241277291cad8e58687a46 F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f -F src/delete.c be9d039b819f4a5d0fdfaeceace139ba189ef819 +F src/delete.c cf185995e20a61c0fecc2a9a9a3b19bd18bd05b3 F src/expr.c 511c27a8858ca12614f495c9c90f5d12db11e6c2 F src/func.c b668e5ad043176049454c95a6a780367a0e8f6bb F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5 @@ -58,10 +58,10 @@ F src/parse.y 0a4bdfd7b65d9761b41a862d09a17c90c1f526f7 F src/pragma.c 0b43b8cac4870bfa041bf2ca29d7ce47b76362d6 F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 -F src/select.c cf4b7952d6d214931c52636ee726f19ee2a275c5 +F src/select.c 7b17db766e669fc85837af50cc110d0988d31ee9 F src/shell.c e8f4f486cbf6e60d81173146ac8a6522c930fa51 F src/sqlite.h.in 6d0e82c24ef3f84a10b468119f3943a5dfc806c7 -F src/sqliteInt.h 8f3b4d0e90c294e0e22b20a9f7f32f2523b8894c +F src/sqliteInt.h d16d54eeefb24b4b1d0e9fa80593bf110a293aab F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9 F src/tclsqlite.c 7f1a1a678140e6901c8954590ca2aabe50b48f71 F src/test1.c 21b1cc9358678da579d7aad8f16a40735a837078 @@ -71,7 +71,7 @@ F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5 F src/tokenize.c 2ad3d1ae1a0a70746db0b31a0a74f58050a3c39a F src/trigger.c 0c91b56182560263733e4b035acdb939bd1cf0e2 -F src/update.c 395a2b270dfcbc96c20e40c9cb42b0533768ce30 +F src/update.c 24a605a4250fe4a0b173bedd7132d1892ad27090 F src/utf.c e45ce11be6922408cd381561721f6cca7d3b992a F src/util.c 4a8db4e97a3cfda12ad8dda3e77dd2d00ad1de5e F src/vacuum.c d061dd908a9e809c54e40e24a551b1d64abd3d16 @@ -81,7 +81,7 @@ F src/vdbeInt.h 6017100adff362b8dfa37a69e3f1431f084bfa5b F src/vdbeapi.c 74be7f96c0a1ac275661f8b32276ac521d9ce37c F src/vdbeaux.c dc06bbb8511d07f8d45ed2ea760f35f0736a690c F src/vdbemem.c 5876c8abf4374fef671f4fd8dc333ef3fc95a2f0 -F src/where.c 4d28167e450255372b45abf1bc8cd5f0e9264d7b +F src/where.c f8a9e0bca6cb0a6fc4c189ed9fa771e75ad68bc8 F test/all.test 929bfa932b55e75c96fe2203f7650ba451c1862c F test/alter.test 2b4478c4906e4d1504b1a121b4ffbc8d11043b53 F test/attach.test bb0ff048b0a65ca2bd5f186eee05d2ec084f3972 @@ -261,7 +261,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c -P 9df837c03939cdcb31856ac17b2425a6dd92d7b2 -R 58a8d6a23d66b3c503089009ebe0b762 +P 711e8d7695dfc74b3f1ee00591dcdda2cd7fc7d5 +R 435be4c4df18ad02a5220daa06c920af U danielk1977 -Z 2746b3ff0377ed15d41d60a317d903c0 +Z 690d6154f35eb3573c00acd3211a6213 diff --git a/manifest.uuid b/manifest.uuid index d4dd8085f8..0500a7a6a7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -711e8d7695dfc74b3f1ee00591dcdda2cd7fc7d5 \ No newline at end of file +91acd87e52509a8f78894d0f4b625b54376cac21 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index ba2bda3ab4..d73166f7f4 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** 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 @@ -344,16 +344,6 @@ struct CellInfo { ** 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 */ @@ -367,7 +357,6 @@ struct BtCursor { 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. */ }; /* @@ -2125,7 +2114,6 @@ int sqlite3BtreeCursor( pBt->pCursor = pCur; pCur->isValid = 0; pCur->status = SQLITE_OK; - pCur->delShift = 0; *ppCur = pCur; return SQLITE_OK; @@ -2224,7 +2212,7 @@ static void getCellInfo(BtCursor *pCur){ ** 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); @@ -2241,7 +2229,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ ** 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{ @@ -2352,7 +2340,7 @@ static int getPayload( ** 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 ); @@ -2371,7 +2359,7 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ ** 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 ); @@ -2559,7 +2547,6 @@ static int moveToRoot(BtCursor *pCur){ rc = moveToChild(pCur, subpage); } pCur->isValid = pCur->pPage->nCell>0; - pCur->delShift = 0; return rc; } @@ -2797,15 +2784,6 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ assert( pPage->isInit ); assert( pCur->idxnCell ); - /* 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 ){ @@ -2856,15 +2834,6 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ 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 ); @@ -3656,7 +3625,6 @@ static int balance_nonroot(MemPage *pPage){ 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. @@ -3905,6 +3873,16 @@ static int balance_nonroot(MemPage *pPage){ zeroPage(pNew, pageFlags); } + /* Free any old pages that were not reused as new pages. + */ + while( i=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 && ipgno ){ - 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( iCell0 && !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; iaData[0]); for(i=0; inCell; i++){ apCell[i] = findCell(pChild,i); @@ -4190,26 +4078,12 @@ static int balance_shallower(MemPage *pPage){ 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; @@ -4218,15 +4092,6 @@ static int balance_shallower(MemPage *pPage){ 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; @@ -4257,7 +4122,6 @@ static int balance_deeper(MemPage *pPage){ 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 ); @@ -4284,21 +4148,6 @@ static int balance_deeper(MemPage *pPage){ 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; @@ -4331,8 +4180,24 @@ static int balance(MemPage *pPage){ ** 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; } @@ -4357,7 +4222,6 @@ int sqlite3BtreeInsert( Btree *pBt = pCur->pBt; unsigned char *oldCell; unsigned char *newCell = 0; - BtCursor *pCur2; if( pCur->status ){ return pCur->status; /* A rollback destroyed this cursor */ @@ -4409,30 +4273,13 @@ int sqlite3BtreeInsert( 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; @@ -4448,8 +4295,6 @@ int sqlite3BtreeDelete(BtCursor *pCur){ 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 ){ @@ -4472,50 +4317,11 @@ int sqlite3BtreeDelete(BtCursor *pCur){ 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); } @@ -4527,120 +4333,48 @@ int sqlite3BtreeDelete(BtCursor *pCur){ ** 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; } diff --git a/src/delete.c b/src/delete.c index 43ee228424..4887d69848 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** 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" @@ -64,6 +64,7 @@ void sqlite3OpenTableForReading( sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); } + /* ** Process a DELETE FROM statement. */ @@ -75,7 +76,7 @@ void sqlite3DeleteFrom( 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 */ @@ -149,15 +150,10 @@ void sqlite3DeleteFrom( 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; } @@ -231,6 +227,22 @@ void sqlite3DeleteFrom( } } + /* 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 ){ @@ -238,49 +250,80 @@ void sqlite3DeleteFrom( 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); - } - sqlite3VdbeAddOp(v, OP_Recno, iCur, 0); - sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==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); - /* Code the AFTER triggers. */ + /* 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); + } + + /* 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); } } diff --git a/src/select.c b/src/select.c index 00ffa40607..412ee8eb6f 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.214 2004/11/16 15:50:20 danielk1977 Exp $ +** $Id: select.c,v 1.215 2004/11/22 10:02:11 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -2523,7 +2523,7 @@ int sqlite3Select( /* Begin the database scan */ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, - pGroupBy ? 0 : &pOrderBy, -1); + pGroupBy ? 0 : &pOrderBy); if( pWInfo==0 ) goto select_end; /* Use the standard inner loop if we are not dealing with diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 39ae7ae615..9be16bc956 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.341 2004/11/20 19:18:01 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.342 2004/11/22 10:02:11 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1314,10 +1314,9 @@ void sqlite3SelectUnbind(Select*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, int); void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*); -void sqlite3OpenTable(Vdbe*, int iCur, Table*, int); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); -WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**, int); +WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**); void sqlite3WhereEnd(WhereInfo*); void sqlite3ExprCode(Parse*, Expr*); int sqlite3ExprCodeExprList(Parse*, ExprList*); diff --git a/src/update.c b/src/update.c index b56574cb6c..501ca6c3eb 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.95 2004/11/16 15:50:20 danielk1977 Exp $ +** $Id: update.c,v 1.96 2004/11/22 10:02:11 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -235,7 +235,7 @@ void sqlite3Update( /* Begin the database scan */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0, -1); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0); if( pWInfo==0 ) goto update_cleanup; /* Remember the index of every item to be updated. diff --git a/src/where.c b/src/where.c index f1a9a0c0b2..c3f8082fc2 100644 --- a/src/where.c +++ b/src/where.c @@ -12,7 +12,7 @@ ** This module contains C code that generates VDBE code used to process ** the WHERE clause of SQL statements. ** -** $Id: where.c,v 1.117 2004/11/16 15:50:20 danielk1977 Exp $ +** $Id: where.c,v 1.118 2004/11/22 10:02:20 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -481,8 +481,7 @@ WhereInfo *sqlite3WhereBegin( SrcList *pTabList, /* A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ int pushKey, /* If TRUE, leave the table key on the stack */ - ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */ - int iTabCur /* Cursor for pTabList->aSrc[0] */ + ExprList **ppOrderBy /* An ORDER BY clause, or NULL */ ){ int i; /* Loop counter */ WhereInfo *pWInfo; /* Will become the return value of this function */ @@ -778,9 +777,7 @@ WhereInfo *sqlite3WhereBegin( pTab = pTabList->a[i].pTab; if( pTab->isTransient || pTab->pSelect ) continue; - if( i>0 || iTabCur<0 ){ - sqlite3OpenTableForReading(v, pTabList->a[i].iCursor, pTab); - } + sqlite3OpenTableForReading(v, pTabList->a[i].iCursor, pTab); sqlite3CodeVerifySchema(pParse, pTab->iDb); if( (pIx = pWInfo->a[i].pIdx)!=0 ){ sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);