Commit 6f519ad0 authored by Bruce Momjian's avatar Bruce Momjian

btree source code cleanups:

I refactored findsplitloc and checksplitloc so that the division of
labor is more clear IMO. I pushed all the space calculation inside the
loop to checksplitloc.

I also fixed the off by 4 in free space calculation caused by
PageGetFreeSpace subtracting sizeof(ItemIdData), even though it was
harmless, because it was distracting and I felt it might come back to
bite us in the future if we change the page layout or alignments.
There's now a new function PageGetExactFreeSpace that doesn't do the
subtraction.

findsplitloc now tries the "just the new item to right page" split as
well. If people don't like the refactoring, I can write a patch to just
add that.

Heikki Linnakangas
parent 526b1d69
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.151 2007/02/08 13:52:55 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.152 2007/02/21 20:02:17 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -29,6 +29,10 @@ typedef struct ...@@ -29,6 +29,10 @@ typedef struct
int fillfactor; /* needed when splitting rightmost page */ int fillfactor; /* needed when splitting rightmost page */
bool is_leaf; /* T if splitting a leaf page */ bool is_leaf; /* T if splitting a leaf page */
bool is_rightmost; /* T if splitting a rightmost page */ bool is_rightmost; /* T if splitting a rightmost page */
OffsetNumber newitemoff; /* where the new item is to be inserted */
int leftspace; /* space available for items on left page */
int rightspace; /* space available for items on right page */
int olddataitemstotal; /* space taken by old items */
bool have_split; /* found a valid split? */ bool have_split; /* found a valid split? */
...@@ -57,9 +61,9 @@ static OffsetNumber _bt_findsplitloc(Relation rel, Page page, ...@@ -57,9 +61,9 @@ static OffsetNumber _bt_findsplitloc(Relation rel, Page page,
OffsetNumber newitemoff, OffsetNumber newitemoff,
Size newitemsz, Size newitemsz,
bool *newitemonleft); bool *newitemonleft);
static void _bt_checksplitloc(FindSplitData *state, OffsetNumber firstright, static void _bt_checksplitloc(FindSplitData *state,
int leftfree, int rightfree, OffsetNumber firstoldonright, bool newitemonleft,
bool newitemonleft, Size firstrightitemsz); int dataitemstoleft, Size firstoldonrightsz);
static void _bt_pgaddtup(Relation rel, Page page, static void _bt_pgaddtup(Relation rel, Page page,
Size itemsize, IndexTuple itup, Size itemsize, IndexTuple itup,
OffsetNumber itup_off, const char *where); OffsetNumber itup_off, const char *where);
...@@ -1101,13 +1105,31 @@ _bt_findsplitloc(Relation rel, ...@@ -1101,13 +1105,31 @@ _bt_findsplitloc(Relation rel,
int leftspace, int leftspace,
rightspace, rightspace,
goodenough, goodenough,
dataitemtotal, olddataitemstotal,
dataitemstoleft; olddataitemstoleft;
bool goodenoughfound;
opaque = (BTPageOpaque) PageGetSpecialPointer(page); opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* Passed-in newitemsz is MAXALIGNED but does not include line pointer */ /* Passed-in newitemsz is MAXALIGNED but does not include line pointer */
newitemsz += sizeof(ItemIdData); newitemsz += sizeof(ItemIdData);
/* Total free space available on a btree page, after fixed overhead */
leftspace = rightspace =
PageGetPageSize(page) - SizeOfPageHeaderData -
MAXALIGN(sizeof(BTPageOpaqueData));
/* The right page will have the same high key as the old page */
if (!P_RIGHTMOST(opaque))
{
itemid = PageGetItemId(page, P_HIKEY);
rightspace -= (int) (MAXALIGN(ItemIdGetLength(itemid)) +
sizeof(ItemIdData));
}
/* Count up total space in data items without actually scanning 'em */
olddataitemstotal = rightspace - (int) PageGetExactFreeSpace(page);
state.newitemsz = newitemsz; state.newitemsz = newitemsz;
state.is_leaf = P_ISLEAF(opaque); state.is_leaf = P_ISLEAF(opaque);
state.is_rightmost = P_RIGHTMOST(opaque); state.is_rightmost = P_RIGHTMOST(opaque);
...@@ -1120,11 +1142,10 @@ _bt_findsplitloc(Relation rel, ...@@ -1120,11 +1142,10 @@ _bt_findsplitloc(Relation rel,
state.newitemonleft = false; /* these just to keep compiler quiet */ state.newitemonleft = false; /* these just to keep compiler quiet */
state.firstright = 0; state.firstright = 0;
state.best_delta = 0; state.best_delta = 0;
state.leftspace = leftspace;
/* Total free space available on a btree page, after fixed overhead */ state.rightspace = rightspace;
leftspace = rightspace = state.olddataitemstotal = olddataitemstotal;
PageGetPageSize(page) - SizeOfPageHeaderData - state.newitemoff = newitemoff;
MAXALIGN(sizeof(BTPageOpaqueData));
/* /*
* Finding the best possible split would require checking all the possible * Finding the best possible split would require checking all the possible
...@@ -1137,74 +1158,60 @@ _bt_findsplitloc(Relation rel, ...@@ -1137,74 +1158,60 @@ _bt_findsplitloc(Relation rel,
*/ */
goodenough = leftspace / 16; goodenough = leftspace / 16;
/* The right page will have the same high key as the old page */
if (!P_RIGHTMOST(opaque))
{
itemid = PageGetItemId(page, P_HIKEY);
rightspace -= (int) (MAXALIGN(ItemIdGetLength(itemid)) +
sizeof(ItemIdData));
}
/* Count up total space in data items without actually scanning 'em */
dataitemtotal = rightspace - (int) PageGetFreeSpace(page);
/* /*
* Scan through the data items and calculate space usage for a split at * Scan through the data items and calculate space usage for a split at
* each possible position. * each possible position.
*/ */
dataitemstoleft = 0; olddataitemstoleft = 0;
goodenoughfound = false;
maxoff = PageGetMaxOffsetNumber(page); maxoff = PageGetMaxOffsetNumber(page);
for (offnum = P_FIRSTDATAKEY(opaque); for (offnum = P_FIRSTDATAKEY(opaque);
offnum <= maxoff; offnum <= maxoff;
offnum = OffsetNumberNext(offnum)) offnum = OffsetNumberNext(offnum))
{ {
Size itemsz; Size itemsz;
int leftfree,
rightfree;
itemid = PageGetItemId(page, offnum); itemid = PageGetItemId(page, offnum);
itemsz = MAXALIGN(ItemIdGetLength(itemid)) + sizeof(ItemIdData); itemsz = MAXALIGN(ItemIdGetLength(itemid)) + sizeof(ItemIdData);
/*
* We have to allow for the current item becoming the high key of the
* left page; therefore it counts against left space as well as right
* space.
*/
leftfree = leftspace - dataitemstoleft - (int) itemsz;
rightfree = rightspace - (dataitemtotal - dataitemstoleft);
/* /*
* Will the new item go to left or right of split? * Will the new item go to left or right of split?
*/ */
if (offnum > newitemoff) if (offnum > newitemoff)
_bt_checksplitloc(&state, offnum, leftfree, rightfree, _bt_checksplitloc(&state, offnum, true,
true, itemsz); olddataitemstoleft, itemsz);
else if (offnum < newitemoff) else if (offnum < newitemoff)
_bt_checksplitloc(&state, offnum, leftfree, rightfree, _bt_checksplitloc(&state, offnum, false,
false, itemsz); olddataitemstoleft, itemsz);
else else
{ {
/* need to try it both ways! */ /* need to try it both ways! */
_bt_checksplitloc(&state, offnum, leftfree, rightfree, _bt_checksplitloc(&state, offnum, true,
true, itemsz); olddataitemstoleft, itemsz);
/*
* Here we are contemplating newitem as first on right. In this _bt_checksplitloc(&state, offnum, false,
* case it, not the current item, will become the high key of the olddataitemstoleft, itemsz);
* left page, and so we have to correct the allowance made above.
*/
leftfree += (int) itemsz - (int) newitemsz;
_bt_checksplitloc(&state, offnum, leftfree, rightfree,
false, newitemsz);
} }
/* Abort scan once we find a good-enough choice */ /* Abort scan once we find a good-enough choice */
if (state.have_split && state.best_delta <= goodenough) if (state.have_split && state.best_delta <= goodenough)
{
goodenoughfound = true;
break; break;
}
dataitemstoleft += itemsz; olddataitemstoleft += itemsz;
} }
/* If the new item goes as the last item, check for splitting so that
* all the old items go to the left page and the new item goes to the
* right page.
*/
if (newitemoff > maxoff && !goodenoughfound)
_bt_checksplitloc(&state, newitemoff, false, olddataitemstotal, 0);
/* /*
* I believe it is not possible to fail to find a feasible split, but just * I believe it is not possible to fail to find a feasible split, but just
* in case ... * in case ...
...@@ -1220,15 +1227,50 @@ _bt_findsplitloc(Relation rel, ...@@ -1220,15 +1227,50 @@ _bt_findsplitloc(Relation rel,
/* /*
* Subroutine to analyze a particular possible split choice (ie, firstright * Subroutine to analyze a particular possible split choice (ie, firstright
* and newitemonleft settings), and record the best split so far in *state. * and newitemonleft settings), and record the best split so far in *state.
*
* firstoldonright is the offset of the first item on the original page
* that goes to the right page, and firstoldonrightsz is the size of that
* tuple. firstoldonright can be > max offset, which means that all the old
* items go to the left page and only the new item goes to the right page.
* In that case, firstoldonrightsz is not used.
*
* olddataitemstoleft is the total size of all old items to the left of
* firstoldonright.
*/ */
static void static void
_bt_checksplitloc(FindSplitData *state, OffsetNumber firstright, _bt_checksplitloc(FindSplitData *state,
int leftfree, int rightfree, OffsetNumber firstoldonright,
bool newitemonleft, Size firstrightitemsz) bool newitemonleft,
int olddataitemstoleft,
Size firstoldonrightsz)
{ {
int leftfree,
rightfree;
Size firstrightitemsz;
bool newitemisfirstonright;
/* Is the new item going to be the first item on the right page? */
newitemisfirstonright = (firstoldonright == state->newitemoff
&& !newitemonleft);
if(newitemisfirstonright)
firstrightitemsz = state->newitemsz;
else
firstrightitemsz = firstoldonrightsz;
/* Account for all the old tuples */
leftfree = state->leftspace - olddataitemstoleft;
rightfree = state->rightspace -
(state->olddataitemstotal - olddataitemstoleft);
/* /*
* Account for the new item on whichever side it is to be put. * The first item on the right page becomes the high key of the
* left page; therefore it counts against left space as well as right
* space.
*/ */
leftfree -= firstrightitemsz;
/* account for the new item */
if (newitemonleft) if (newitemonleft)
leftfree -= (int) state->newitemsz; leftfree -= (int) state->newitemsz;
else else
...@@ -1270,7 +1312,7 @@ _bt_checksplitloc(FindSplitData *state, OffsetNumber firstright, ...@@ -1270,7 +1312,7 @@ _bt_checksplitloc(FindSplitData *state, OffsetNumber firstright,
{ {
state->have_split = true; state->have_split = true;
state->newitemonleft = newitemonleft; state->newitemonleft = newitemonleft;
state->firstright = firstright; state->firstright = firstoldonright;
state->best_delta = delta; state->best_delta = delta;
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.70 2007/01/05 22:19:38 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.71 2007/02/21 20:02:17 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -418,7 +418,8 @@ PageRepairFragmentation(Page page, OffsetNumber *unused) ...@@ -418,7 +418,8 @@ PageRepairFragmentation(Page page, OffsetNumber *unused)
/* /*
* PageGetFreeSpace * PageGetFreeSpace
* Returns the size of the free (allocatable) space on a page. * Returns the size of the free (allocatable) space on a page,
* deducted by the space needed for a new line pointer.
*/ */
Size Size
PageGetFreeSpace(Page page) PageGetFreeSpace(Page page)
...@@ -434,7 +435,26 @@ PageGetFreeSpace(Page page) ...@@ -434,7 +435,26 @@ PageGetFreeSpace(Page page)
if (space < (int) sizeof(ItemIdData)) if (space < (int) sizeof(ItemIdData))
return 0; return 0;
space -= sizeof(ItemIdData); /* XXX not always appropriate */ space -= sizeof(ItemIdData);
return (Size) space;
}
/*
* PageGetExactFreeSpace
* Returns the size of the free (allocatable) space on a page.
*/
Size
PageGetExactFreeSpace(Page page)
{
int space;
/*
* Use signed arithmetic here so that we behave sensibly if pd_lower >
* pd_upper.
*/
space = (int) ((PageHeader) page)->pd_upper -
(int) ((PageHeader) page)->pd_lower;
return (Size) space; return (Size) space;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.70 2007/02/09 03:35:34 tgl Exp $ * $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.71 2007/02/21 20:02:17 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -322,6 +322,7 @@ extern Page PageGetTempPage(Page page, Size specialSize); ...@@ -322,6 +322,7 @@ extern Page PageGetTempPage(Page page, Size specialSize);
extern void PageRestoreTempPage(Page tempPage, Page oldPage); extern void PageRestoreTempPage(Page tempPage, Page oldPage);
extern int PageRepairFragmentation(Page page, OffsetNumber *unused); extern int PageRepairFragmentation(Page page, OffsetNumber *unused);
extern Size PageGetFreeSpace(Page page); extern Size PageGetFreeSpace(Page page);
extern Size PageGetExactFreeSpace(Page page);
extern void PageIndexTupleDelete(Page page, OffsetNumber offset); extern void PageIndexTupleDelete(Page page, OffsetNumber offset);
extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems); extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment