improve FreePageManager
authorRobert Haas <rhaas@postgresql.org>
Fri, 11 Apr 2014 17:09:01 +0000 (13:09 -0400)
committerRobert Haas <rhaas@postgresql.org>
Fri, 11 Apr 2014 17:09:01 +0000 (13:09 -0400)
- 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

src/backend/utils/mmgr/freepage.c

index a572fefb8cf1e8ea229c1a8a6eaef8a42efb6e47..f830149892719b58e9ca66736a95e50324d2fc4d 100644 (file)
@@ -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. */
                }
        }