mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
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
This commit is contained in:
@ -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
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
{
|
{
|
||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
* 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);
|
|||||||
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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user