mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
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.
This commit is contained in:
parent
5fcb58baca
commit
e5db11c558
@ -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,
|
|||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* best_penalty[j] is the best penalty we have seen so far for column j,
|
||||||
|
* or -1 when we haven't yet examined column j. Array entries to the
|
||||||
|
* right of the first -1 are undefined.
|
||||||
|
*/
|
||||||
|
best_penalty[0] = -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop over tuples on page.
|
* Loop over tuples on page.
|
||||||
*
|
|
||||||
* We'll exit early if we find an index key that can accommodate the new key with no
|
|
||||||
* penalty on any column. sum_grow is used to track this condition. Normally, it is the
|
|
||||||
* sum of the penalties we've seen for this column so far, which is not a very useful
|
|
||||||
* 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))
|
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 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (which == InvalidOffsetNumber)
|
return result;
|
||||||
which = FirstOffsetNumber;
|
|
||||||
|
|
||||||
return which;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user