internal RC freePage2(MemPage pMemPage, Pgno iPage) { MemPage pTrunk = null; // Free-list trunk page var pPage1 = this.Page1; // Local reference to page 1 Debug.Assert(MutexEx.Held(this.Mutex)); Debug.Assert(iPage > 1); Debug.Assert(pMemPage == null || pMemPage.ID == iPage); MemPage pPage; // Page being freed. May be NULL. if (pMemPage != null) { pPage = pMemPage; Pager.AddPageRef(pPage.DbPage); } else { pPage = btreePageLookup(iPage); } // Increment the free page count on pPage1 var rc = Pager.Write(pPage1.DbPage); if (rc != RC.OK) { goto freepage_out; } var nFree = (int)ConvertEx.Get4(pPage1.Data, 36); // Initial number of pages on free-list ConvertEx.Put4(pPage1.Data, 36, nFree + 1); if (this.SecureDelete) { // If the secure_delete option is enabled, then always fully overwrite deleted information with zeros. if ((pPage == null && ((rc = btreeGetPage(iPage, ref pPage, 0)) != RC.OK)) || ((rc = Pager.Write(pPage.DbPage)) != RC.OK)) { goto freepage_out; } Array.Clear(pPage.Data, 0, (int)pPage.Shared.PageSize); } // If the database supports auto-vacuum, write an entry in the pointer-map to indicate that the page is free. #if !SQLITE_OMIT_AUTOVACUUM if (this.AutoVacuum) #else if (false) #endif { ptrmapPut(iPage, PTRMAP.FREEPAGE, 0, ref rc); if (rc != RC.OK) { goto freepage_out; } } // Now manipulate the actual database free-list structure. There are two possibilities. If the free-list is currently empty, or if the first // trunk page in the free-list is full, then this page will become a new free-list trunk page. Otherwise, it will become a leaf of the // first trunk page in the current free-list. This block tests if it is possible to add the page as a new free-list leaf. Pgno iTrunk = 0; // Page number of free-list trunk page if (nFree != 0) { uint nLeaf; // Initial number of leaf cells on trunk page iTrunk = (Pgno)ConvertEx.Get4(pPage1.Data, 32); // Page number of free-list trunk page rc = btreeGetPage(iTrunk, ref pTrunk, 0); if (rc != RC.OK) { goto freepage_out; } nLeaf = ConvertEx.Get4(pTrunk.Data, 4); Debug.Assert(this.UsableSize > 32); if (nLeaf > (uint)this.UsableSize / 4 - 2) { rc = SysEx.SQLITE_CORRUPT_BKPT(); goto freepage_out; } if (nLeaf < (uint)this.UsableSize / 4 - 8) { // In this case there is room on the trunk page to insert the page being freed as a new leaf. // Note: that the trunk page is not really full until it contains usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have // coded. But due to a coding error in versions of SQLite prior to 3.6.0, databases with freelist trunk pages holding more than // usableSize/4 - 8 entries will be reported as corrupt. In order to maintain backwards compatibility with older versions of SQLite, // we will continue to restrict the number of entries to usableSize/4 - 8 for now. At some point in the future (once everyone has upgraded // to 3.6.0 or later) we should consider fixing the conditional above to read "usableSize/4-2" instead of "usableSize/4-8". rc = Pager.Write(pTrunk.DbPage); if (rc == RC.OK) { ConvertEx.Put4(pTrunk.Data, 4, nLeaf + 1); ConvertEx.Put4(pTrunk.Data, (uint)(8 + nLeaf * 4), iPage); if (pPage != null && !this.SecureDelete) { Pager.DontWrite(pPage.DbPage); } rc = btreeSetHasContent(iPage); } Btree.TRACE("FREE-PAGE: %d leaf on trunk page %d\n", iPage, pTrunk.ID); goto freepage_out; } } // If control flows to this point, then it was not possible to add the the page being freed as a leaf page of the first trunk in the free-list. // Possibly because the free-list is empty, or possibly because the first trunk in the free-list is full. Either way, the page being freed // will become the new first trunk page in the free-list. if (pPage == null && (rc = btreeGetPage(iPage, ref pPage, 0)) != RC.OK) { goto freepage_out; } rc = Pager.Write(pPage.DbPage); if (rc != RC.OK) { goto freepage_out; } ConvertEx.Put4L(pPage.Data, iTrunk); ConvertEx.Put4(pPage.Data, 4, 0); ConvertEx.Put4(pPage1.Data, 32, iPage); Btree.TRACE("FREE-PAGE: %d new trunk page replacing %d\n", pPage.ID, iTrunk); freepage_out: if (pPage != null) { pPage.HasInit = false; } pPage.releasePage(); pTrunk.releasePage(); return(rc); }