internal static RC incrVacuumStep(BtShared pBt, Pgno nFin, Pgno iLastPg) { Pgno nFreeList; // Number of pages still on the free-list Debug.Assert(MutexEx.Held(pBt.Mutex)); Debug.Assert(iLastPg > nFin); if (!PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg != PENDING_BYTE_PAGE(pBt)) { PTRMAP eType = 0; Pgno iPtrPage = 0; nFreeList = ConvertEx.Get4(pBt.Page1.Data, 36); if (nFreeList == 0) { return(RC.DONE); } var rc = pBt.ptrmapGet(iLastPg, ref eType, ref iPtrPage); if (rc != RC.OK) { return(rc); } if (eType == PTRMAP.ROOTPAGE) { return(SysEx.SQLITE_CORRUPT_BKPT()); } if (eType == PTRMAP.FREEPAGE) { if (nFin == 0) { // Remove the page from the files free-list. This is not required if nFin is non-zero. In that case, the free-list will be // truncated to zero after this function returns, so it doesn't matter if it still contains some garbage entries. Pgno iFreePg = 0; var pFreePg = new MemPage(); rc = pBt.allocateBtreePage(ref pFreePg, ref iFreePg, iLastPg, 1); if (rc != RC.OK) { return(rc); } Debug.Assert(iFreePg == iLastPg); pFreePg.releasePage(); } } else { Pgno iFreePg = 0; // Index of free page to move pLastPg to var pLastPg = new MemPage(); rc = pBt.btreeGetPage(iLastPg, ref pLastPg, 0); if (rc != RC.OK) { return(rc); } // If nFin is zero, this loop runs exactly once and page pLastPg is swapped with the first free page pulled off the free list. // On the other hand, if nFin is greater than zero, then keep looping until a free-page located within the first nFin pages of the file is found. do { var pFreePg = new MemPage(); rc = pBt.allocateBtreePage(ref pFreePg, ref iFreePg, 0, 0); if (rc != RC.OK) { pLastPg.releasePage(); return(rc); } pFreePg.releasePage(); } while (nFin != 0 && iFreePg > nFin); Debug.Assert(iFreePg < iLastPg); rc = Pager.Write(pLastPg.DbPage); if (rc == RC.OK) { rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, (nFin != 0) ? 1 : 0); } pLastPg.releasePage(); if (rc != RC.OK) { return(rc); } } } if (nFin == 0) { iLastPg--; while (iLastPg == PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg)) { if (PTRMAP_ISPAGE(pBt, iLastPg)) { var pPg = new MemPage(); var rc = pBt.btreeGetPage(iLastPg, ref pPg, 0); if (rc != RC.OK) { return(rc); } rc = Pager.Write(pPg.DbPage); pPg.releasePage(); if (rc != RC.OK) { return(rc); } } iLastPg--; } pBt.Pager.TruncateImage(iLastPg); pBt.Pages = iLastPg; } return(RC.OK); }
internal static RC incrVacuumStep(BtShared pBt, Pgno nFin, Pgno iLastPg) { Pgno nFreeList; // Number of pages still on the free-list Debug.Assert(MutexEx.Held(pBt.Mutex)); Debug.Assert(iLastPg > nFin); if (!PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg != PENDING_BYTE_PAGE(pBt)) { PTRMAP eType = 0; Pgno iPtrPage = 0; nFreeList = ConvertEx.Get4(pBt.Page1.Data, 36); if (nFreeList == 0) return RC.DONE; var rc = pBt.ptrmapGet( iLastPg, ref eType, ref iPtrPage); if (rc != RC.OK) return rc; if (eType == PTRMAP.ROOTPAGE) return SysEx.SQLITE_CORRUPT_BKPT(); if (eType == PTRMAP.FREEPAGE) { if (nFin == 0) { // Remove the page from the files free-list. This is not required if nFin is non-zero. In that case, the free-list will be // truncated to zero after this function returns, so it doesn't matter if it still contains some garbage entries. Pgno iFreePg = 0; var pFreePg = new MemPage(); rc = pBt.allocateBtreePage( ref pFreePg, ref iFreePg, iLastPg, 1); if (rc != RC.OK) return rc; Debug.Assert(iFreePg == iLastPg); pFreePg.releasePage(); } } else { Pgno iFreePg = 0; // Index of free page to move pLastPg to var pLastPg = new MemPage(); rc = pBt.btreeGetPage( iLastPg, ref pLastPg, 0); if (rc != RC.OK) return rc; // If nFin is zero, this loop runs exactly once and page pLastPg is swapped with the first free page pulled off the free list. // On the other hand, if nFin is greater than zero, then keep looping until a free-page located within the first nFin pages of the file is found. do { var pFreePg = new MemPage(); rc = pBt.allocateBtreePage(ref pFreePg, ref iFreePg, 0, 0); if (rc != RC.OK) { pLastPg.releasePage(); return rc; } pFreePg.releasePage(); } while (nFin != 0 && iFreePg > nFin); Debug.Assert(iFreePg < iLastPg); rc = Pager.Write(pLastPg.DbPage); if (rc == RC.OK) rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, (nFin != 0) ? 1 : 0); pLastPg.releasePage(); if (rc != RC.OK) return rc; } } if (nFin == 0) { iLastPg--; while (iLastPg == PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg)) { if (PTRMAP_ISPAGE(pBt, iLastPg)) { var pPg = new MemPage(); var rc = pBt.btreeGetPage(iLastPg, ref pPg, 0); if (rc != RC.OK) return rc; rc = Pager.Write(pPg.DbPage); pPg.releasePage(); if (rc != RC.OK) return rc; } iLastPg--; } pBt.Pager.TruncateImage(iLastPg); pBt.Pages = iLastPg; } return RC.OK; }
internal static RC relocatePage(BtShared pBt, MemPage pDbPage, PTRMAP eType, Pgno iPtrPage, Pgno iFreePage, int isCommit) { var pPtrPage = new MemPage(); // The page that contains a pointer to pDbPage var iDbPage = pDbPage.ID; var pPager = pBt.Pager; Debug.Assert(eType == PTRMAP.OVERFLOW2 || eType == PTRMAP.OVERFLOW1 || eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE); Debug.Assert(MutexEx.Held(pBt.Mutex)); Debug.Assert(pDbPage.Shared == pBt); // Move page iDbPage from its current location to page number iFreePage Btree.TRACE("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", iDbPage, iFreePage, iPtrPage, eType); var rc = pPager.sqlite3PagerMovepage(pDbPage.DbPage, iFreePage, isCommit); if (rc != RC.OK) { return(rc); } pDbPage.ID = iFreePage; // If pDbPage was a btree-page, then it may have child pages and/or cells that point to overflow pages. The pointer map entries for all these // pages need to be changed. // If pDbPage is an overflow page, then the first 4 bytes may store a pointer to a subsequent overflow page. If this is the case, then // the pointer map needs to be updated for the subsequent overflow page. if (eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE) { rc = pDbPage.setChildPtrmaps(); if (rc != RC.OK) { return(rc); } } else { var nextOvfl = (Pgno)ConvertEx.Get4(pDbPage.Data); if (nextOvfl != 0) { pBt.ptrmapPut(nextOvfl, PTRMAP.OVERFLOW2, iFreePage, ref rc); if (rc != RC.OK) { return(rc); } } } // Fix the database pointer on page iPtrPage that pointed at iDbPage so that it points at iFreePage. Also fix the pointer map entry for iPtrPage. if (eType != PTRMAP.ROOTPAGE) { rc = pBt.btreeGetPage(iPtrPage, ref pPtrPage, 0); if (rc != RC.OK) { return(rc); } rc = Pager.Write(pPtrPage.DbPage); if (rc != RC.OK) { pPtrPage.releasePage(); return(rc); } rc = pPtrPage.modifyPagePointer(iDbPage, iFreePage, eType); pPtrPage.releasePage(); if (rc == RC.OK) { pBt.ptrmapPut(iFreePage, eType, iPtrPage, ref rc); } } return(rc); }
internal static RC relocatePage(BtShared pBt, MemPage pDbPage, PTRMAP eType, Pgno iPtrPage, Pgno iFreePage, int isCommit) { var pPtrPage = new MemPage(); // The page that contains a pointer to pDbPage var iDbPage = pDbPage.ID; var pPager = pBt.Pager; Debug.Assert(eType == PTRMAP.OVERFLOW2 || eType == PTRMAP.OVERFLOW1 || eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE); Debug.Assert(MutexEx.Held(pBt.Mutex)); Debug.Assert(pDbPage.Shared == pBt); // Move page iDbPage from its current location to page number iFreePage Btree.TRACE("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", iDbPage, iFreePage, iPtrPage, eType); var rc = pPager.sqlite3PagerMovepage(pDbPage.DbPage, iFreePage, isCommit); if (rc != RC.OK) return rc; pDbPage.ID = iFreePage; // If pDbPage was a btree-page, then it may have child pages and/or cells that point to overflow pages. The pointer map entries for all these // pages need to be changed. // If pDbPage is an overflow page, then the first 4 bytes may store a pointer to a subsequent overflow page. If this is the case, then // the pointer map needs to be updated for the subsequent overflow page. if (eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE) { rc = pDbPage.setChildPtrmaps(); if (rc != RC.OK) return rc; } else { var nextOvfl = (Pgno)ConvertEx.Get4(pDbPage.Data); if (nextOvfl != 0) { pBt.ptrmapPut(nextOvfl, PTRMAP.OVERFLOW2, iFreePage, ref rc); if (rc != RC.OK) return rc; } } // Fix the database pointer on page iPtrPage that pointed at iDbPage so that it points at iFreePage. Also fix the pointer map entry for iPtrPage. if (eType != PTRMAP.ROOTPAGE) { rc = pBt.btreeGetPage(iPtrPage, ref pPtrPage, 0); if (rc != RC.OK) return rc; rc = Pager.Write(pPtrPage.DbPage); if (rc != RC.OK) { pPtrPage.releasePage(); return rc; } rc = pPtrPage.modifyPagePointer(iDbPage, iFreePage, eType); pPtrPage.releasePage(); if (rc == RC.OK) pBt.ptrmapPut(iFreePage, eType, iPtrPage, ref rc); } return rc; }