From: Robert Haas Date: Fri, 11 Apr 2014 17:09:01 +0000 (-0400) Subject: improve FreePageManager X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=5025cef95acbff3d64eec5853de22671f845b999;p=users%2Frhaas%2Fpostgres.git improve FreePageManager - fix a bug when the btree root is created from the last free page - when the btree root is a leaf and is surrounded on either side by freespace, collapse it to a singleton range --- diff --git a/src/backend/utils/mmgr/freepage.c b/src/backend/utils/mmgr/freepage.c index a572fefb8c..f830149892 100644 --- a/src/backend/utils/mmgr/freepage.c +++ b/src/backend/utils/mmgr/freepage.c @@ -507,34 +507,68 @@ FreePageBtreeCleanup(FreePageManager *fpm) { FreePageBtree *root = relptr_access(base, fpm->btree_root); - /* Can't do anything if the root has multiple keys. */ - if (root->hdr.nused > 1) - break; - - /* Root should never be empty. */ - Assert(root->hdr.nused == 1); + /* If the root contains only one key, reduce depth by one. */ + if (root->hdr.nused == 1) + { + /* Shrink depth of tree by one. */ + Assert(fpm->btree_depth > 0); + --fpm->btree_depth; + if (root->hdr.magic == FREE_PAGE_LEAF_MAGIC) + { + /* If root is a leaf, convert only entry to singleton range. */ + relptr_store(base, fpm->btree_root, (FreePageBtree *) NULL); + fpm->singleton_first_page = root->u.leaf_key[0].first_page; + fpm->singleton_npages = root->u.leaf_key[0].npages; + } + else + { + FreePageBtree *newroot; - /* Shrink depth of tree by one. */ - Assert(fpm->btree_depth > 0); - --fpm->btree_depth; - if (root->hdr.magic == FREE_PAGE_LEAF_MAGIC) + /* If root is an internal page, make only child the root. */ + Assert(root->hdr.magic == FREE_PAGE_INTERNAL_MAGIC); + relptr_copy(fpm->btree_root, root->u.internal_key[0].child); + newroot = relptr_access(base, fpm->btree_root); + relptr_store(base, newroot->hdr.parent, (FreePageBtree *) NULL); + } + FreePageBtreeRecycle(fpm, fpm_pointer_to_page(base, root)); + } + else if (root->hdr.nused == 2 && + root->hdr.magic == FREE_PAGE_LEAF_MAGIC) { - /* If root is a leaf, convert only entry to singleton range. */ - relptr_store(base, fpm->btree_root, (FreePageBtree *) NULL); - fpm->singleton_first_page = root->u.leaf_key[0].first_page; - fpm->singleton_npages = root->u.leaf_key[0].npages; + Size end_of_first; + Size start_of_second; + + end_of_first = root->u.leaf_key[0].first_page + + root->u.leaf_key[0].npages; + start_of_second = root->u.leaf_key[1].first_page; + + if (end_of_first + 1 == start_of_second) + { + Size root_page = fpm_pointer_to_page(base, root); + + if (end_of_first == root_page) + { + FreePagePopSpanLeader(fpm, root->u.leaf_key[0].first_page); + FreePagePopSpanLeader(fpm, root->u.leaf_key[1].first_page); + fpm->singleton_first_page = root->u.leaf_key[0].first_page; + fpm->singleton_npages = root->u.leaf_key[0].npages + + root->u.leaf_key[1].npages + 1; + fpm->btree_depth = 0; + relptr_store(base, fpm->btree_root, + (FreePageBtree *) NULL); + FreePagePushSpanLeader(fpm, fpm->singleton_first_page, + fpm->singleton_npages); + } + } + + /* Whether it worked or not, it's time to stop. */ + break; } else { - FreePageBtree *newroot; - - /* If root is an internal page, make only child the root. */ - Assert(root->hdr.magic == FREE_PAGE_INTERNAL_MAGIC); - relptr_copy(fpm->btree_root, root->u.internal_key[0].child); - newroot = relptr_access(base, fpm->btree_root); - relptr_store(base, newroot->hdr.parent, (FreePageBtree *) NULL); + /* Nothing more to do. Stop. */ + break; } - FreePageBtreeRecycle(fpm, fpm_pointer_to_page(base, root)); } /* @@ -1401,6 +1435,19 @@ FreePageManagerPutInternal(FreePageManager *fpm, Size first_page, Size npages, fpm->singleton_npages = 0; fpm->btree_depth = 1; + /* + * Corner case: it may be that the btree root took the very last + * free page. In that case, the sole btree entry covers a zero + * page run, which is invalid. Overwrite it with the entry we're + * trying to insert and get out. + */ + if (root->u.leaf_key[0].npages == 0) + { + root->u.leaf_key[0].first_page = first_page; + root->u.leaf_key[0].npages = npages; + return npages; + } + /* Fall through to insert the new key. */ } }