Commit e5db11c5 authored by Tom Lane's avatar Tom Lane

Improve coding of gistchoose and gistRelocateBuildBuffersOnSplit.

This is mostly cosmetic, but it does eliminate a speculative portability
issue.  The previous coding ignored the fact that sum_grow could easily
overflow (in fact, it could be summing multiple IEEE float infinities).
On a platform where that didn't guarantee to produce a positive result,
the code would misbehave.  In any case, it was less than readable.
parent 5fcb58ba
...@@ -625,18 +625,17 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate, ...@@ -625,18 +625,17 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
} }
/* /*
* Loop through all index tuples on the buffer on the page being split, * Loop through all index tuples in the buffer of the page being split,
* moving them to buffers on the new pages. We try to move each tuple * moving them to buffers for the new pages. We try to move each tuple to
* the page that will result in the lowest penalty for the leading column * the page that will result in the lowest penalty for the leading column
* or, in the case of a tie, the lowest penalty for the earliest column * or, in the case of a tie, the lowest penalty for the earliest column
* that is not tied. * that is not tied.
* *
* The guts of this loop are very similar to gistchoose(). * The page searching logic is very similar to gistchoose().
*/ */
while (gistPopItupFromNodeBuffer(gfbb, &oldBuf, &itup)) while (gistPopItupFromNodeBuffer(gfbb, &oldBuf, &itup))
{ {
float sum_grow, float best_penalty[INDEX_MAX_KEYS];
which_grow[INDEX_MAX_KEYS];
int i, int i,
which; which;
IndexTuple newtup; IndexTuple newtup;
...@@ -645,71 +644,88 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate, ...@@ -645,71 +644,88 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
gistDeCompressAtt(giststate, r, gistDeCompressAtt(giststate, r,
itup, NULL, (OffsetNumber) 0, entry, isnull); itup, NULL, (OffsetNumber) 0, entry, isnull);
which = -1; /* default to using first page (shouldn't matter) */
*which_grow = -1.0f; which = 0;
/* /*
* Loop over possible target pages. We'll exit early if we find an index key that * best_penalty[j] is the best penalty we have seen so far for column
* can accommodate the new key with no penalty on any column. sum_grow is used to * j, or -1 when we haven't yet examined column j. Array entries to
* track this condition. It doesn't need to be exactly accurate, just >0 whenever * the right of the first -1 are undefined.
* we want the loop to continue and equal to 0 when we want it to terminate.
*/ */
sum_grow = 1.0f; best_penalty[0] = -1;
for (i = 0; i < splitPagesCount && sum_grow; i++) /*
* Loop over possible target pages, looking for one to move this tuple
* to.
*/
for (i = 0; i < splitPagesCount; i++)
{ {
int j;
RelocationBufferInfo *splitPageInfo = &relocationBuffersInfos[i]; RelocationBufferInfo *splitPageInfo = &relocationBuffersInfos[i];
bool zero_penalty;
int j;
sum_grow = 0.0f; zero_penalty = true;
/* Loop over index attributes. */ /* Loop over index attributes. */
for (j = 0; j < r->rd_att->natts; j++) for (j = 0; j < r->rd_att->natts; j++)
{ {
float usize; float usize;
/* Compute penalty for this column. */
usize = gistpenalty(giststate, j, usize = gistpenalty(giststate, j,
&splitPageInfo->entry[j], &splitPageInfo->entry[j],
splitPageInfo->isnull[j], splitPageInfo->isnull[j],
&entry[j], isnull[j]); &entry[j], isnull[j]);
if (usize > 0)
zero_penalty = false;
if (which_grow[j] < 0 || usize < which_grow[j]) if (best_penalty[j] < 0 || usize < best_penalty[j])
{ {
/* /*
* We get here in two cases. First, we may have just discovered that the * New best penalty for column. Tentatively select this
* current tuple is the best one we've seen so far; that is, for the first * page as the target, and record the best penalty. Then
* column for which the penalty is not equal to the best tuple seen so far, * reset the next column's penalty to "unknown" (and
* this one has a lower penalty than the previously-seen one. But, when * indirectly, the same for all the ones to its right).
* a new best tuple is found, we must record the best penalty value for * This will force us to adopt this page's penalty values
* all the remaining columns. We'll end up here for each remaining index * as the best for all the remaining columns during
* column in that case, too. * subsequent loop iterations.
*/ */
which = i; which = i;
which_grow[j] = usize; best_penalty[j] = usize;
if (j < r->rd_att->natts - 1) if (j < r->rd_att->natts - 1)
which_grow[j + 1] = -1; best_penalty[j + 1] = -1;
sum_grow += which_grow[j];
} }
else if (which_grow[j] == usize) else if (best_penalty[j] == usize)
{ {
/* /*
* The current tuple is exactly as good for this column as the best tuple * The current page is exactly as good for this column as
* seen so far. The next iteration of this loop will compare the next * the best page seen so far. The next iteration of this
* column. * loop will compare the next column.
*/ */
sum_grow += usize;
} }
else else
{ {
/* /*
* The current tuple is worse for this column than the best tuple seen so * The current page is worse for this column than the best
* far. Skip the remaining columns and move on to the next tuple, if any. * page seen so far. Skip the remaining columns and move
* on to the next page, if any.
*/ */
sum_grow = 1; zero_penalty = false; /* so outer loop won't exit */
break; break;
} }
} }
/*
* If we find a page with zero penalty for all columns, there's no
* need to examine remaining pages; just break out of the loop and
* return it.
*/
if (zero_penalty)
break;
} }
/* OK, "which" is the page index to push the tuple to */
targetBufferInfo = &relocationBuffersInfos[which]; targetBufferInfo = &relocationBuffersInfos[which];
/* Push item to selected node buffer */ /* Push item to selected node buffer */
......
...@@ -363,113 +363,120 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis ...@@ -363,113 +363,120 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
} }
/* /*
* Search a page for the entry with lowest penalty. * Search an upper index page for the entry with lowest penalty for insertion
* of the new index key contained in "it".
* *
* The index may have multiple columns, and there's a penalty value for each column. * Returns the index of the page entry to insert into.
* The penalty associated with a column which appears earlier in the index definition is
* strictly more important than the penalty of column which appears later in the index
* definition.
*/ */
OffsetNumber OffsetNumber
gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
GISTSTATE *giststate) GISTSTATE *giststate)
{ {
OffsetNumber result;
OffsetNumber maxoff; OffsetNumber maxoff;
OffsetNumber i; OffsetNumber i;
OffsetNumber which; float best_penalty[INDEX_MAX_KEYS];
float sum_grow,
which_grow[INDEX_MAX_KEYS];
GISTENTRY entry, GISTENTRY entry,
identry[INDEX_MAX_KEYS]; identry[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS];
maxoff = PageGetMaxOffsetNumber(p); Assert(!GistPageIsLeaf(p));
*which_grow = -1.0;
which = InvalidOffsetNumber;
sum_grow = 1;
gistDeCompressAtt(giststate, r, gistDeCompressAtt(giststate, r,
it, NULL, (OffsetNumber) 0, it, NULL, (OffsetNumber) 0,
identry, isnull); identry, isnull);
Assert(maxoff >= FirstOffsetNumber); /* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
Assert(!GistPageIsLeaf(p)); result = FirstOffsetNumber;
/* /*
* Loop over tuples on page. * The index may have multiple columns, and there's a penalty value for
* each column. The penalty associated with a column that appears earlier
* in the index definition is strictly more important than the penalty of
* a column that appears later in the index definition.
* *
* We'll exit early if we find an index key that can accommodate the new key with no * best_penalty[j] is the best penalty we have seen so far for column j,
* penalty on any column. sum_grow is used to track this condition. Normally, it is the * or -1 when we haven't yet examined column j. Array entries to the
* sum of the penalties we've seen for this column so far, which is not a very useful * right of the first -1 are undefined.
* quantity in general because the penalties for each column are only considered
* independently, but all we really care about is whether or not it's greater than zero.
* Since penalties can't be negative, the sum of the penalties will be greater than
* zero if and only if at least one penalty was greater than zero. To make things just
* a bit more complicated, we arbitrarily set sum_grow to 1.0 whenever we want to force
* the at least one more iteration of this outer loop. Any non-zero value would serve
* just as well.
*/ */
for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i)) best_penalty[0] = -1;
/*
* Loop over tuples on page.
*/
maxoff = PageGetMaxOffsetNumber(p);
Assert(maxoff >= FirstOffsetNumber);
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{ {
int j;
IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i)); IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
bool zero_penalty;
int j;
sum_grow = 0; zero_penalty = true;
/* Loop over indexed attribtues. */ /* Loop over index attributes. */
for (j = 0; j < r->rd_att->natts; j++) for (j = 0; j < r->rd_att->natts; j++)
{ {
Datum datum; Datum datum;
float usize; float usize;
bool IsNull; bool IsNull;
/* Compute penalty for this column. */
datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull); datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
gistdentryinit(giststate, j, &entry, datum, r, p, i, gistdentryinit(giststate, j, &entry, datum, r, p, i,
FALSE, IsNull); FALSE, IsNull);
usize = gistpenalty(giststate, j, &entry, IsNull, usize = gistpenalty(giststate, j, &entry, IsNull,
&identry[j], isnull[j]); &identry[j], isnull[j]);
if (usize > 0)
zero_penalty = false;
if (which_grow[j] < 0 || usize < which_grow[j]) if (best_penalty[j] < 0 || usize < best_penalty[j])
{ {
/* /*
* We get here in two cases. First, we may have just discovered that the * New best penalty for column. Tentatively select this tuple
* current tuple is the best one we've seen so far; that is, for the first * as the target, and record the best penalty. Then reset the
* column for which the penalty is not equal to the best tuple seen so far, * next column's penalty to "unknown" (and indirectly, the
* this one has a lower penalty than the previously-seen one. But, when * same for all the ones to its right). This will force us to
* a new best tuple is found, we must record the best penalty value for * adopt this tuple's penalty values as the best for all the
* all the remaining columns. We'll end up here for each remaining index * remaining columns during subsequent loop iterations.
* column in that case, too.
*/ */
which = i; result = i;
which_grow[j] = usize; best_penalty[j] = usize;
if (j < r->rd_att->natts - 1) if (j < r->rd_att->natts - 1)
which_grow[j + 1] = -1; best_penalty[j + 1] = -1;
sum_grow += which_grow[j];
} }
else if (which_grow[j] == usize) else if (best_penalty[j] == usize)
{ {
/* /*
* The current tuple is exactly as good for this column as the best tuple * The current tuple is exactly as good for this column as the
* seen so far. The next iteration of this loop will compare the next * best tuple seen so far. The next iteration of this loop
* column. * will compare the next column.
*/ */
sum_grow += usize;
} }
else else
{ {
/* /*
* The current tuple is worse for this column than the best tuple seen so * The current tuple is worse for this column than the best
* far. Skip the remaining columns and move on to the next tuple, if any. * tuple seen so far. Skip the remaining columns and move on
* to the next tuple, if any.
*/ */
sum_grow = 1; zero_penalty = false; /* so outer loop won't exit */
break; break;
} }
} }
}
if (which == InvalidOffsetNumber) /*
which = FirstOffsetNumber; * If we find a tuple with zero penalty for all columns, there's no
* need to examine remaining tuples; just break out of the loop and
* return it.
*/
if (zero_penalty)
break;
}
return which; return result;
} }
/* /*
......
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