Пример #1
0
        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);
        }