8000 Back-patch recent fixes for gistchoose and gistRelocateBuildBuffersOn… · sureandrew/postgres@3d27e4d · GitHub
[go: up one dir, main page]

Skip to content

Commit 3d27e4d

Browse files
committed
Back-patch recent fixes for gistchoose and gistRelocateBuildBuffersOnSplit.
This back-ports commits c8ba697 and e5db11c, which fix one definite and one speculative bug in gistchoose, and make the code a lot more intelligible as well. In 9.2 only, this also affects the largely-copied-and-pasted logic in gistRelocateBuildBuffersOnSplit. The impact of the bugs was that the functions might make poor decisions as to which index tree branch to push a new entry down into, resulting in GiST index bloat and poor performance. The fixes rectify these decisions for future insertions, but a REINDEX would be needed to clean up any existing index bloat. Alexander Korotkov, Robert Haas, Tom Lane
1 parent 588fb3f commit 3d27e4d

File tree

1 file changed

+73
-25
lines changed

1 file changed

+73
-25
lines changed

src/backend/access/gist/gistutil.c

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -370,36 +370,55 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
370370
}
371371

372372
/*
373-
* find entry with lowest penalty
373+
* Search an upper index page for the entry with lowest penalty for insertion
374+
* of the new index key contained in "it".
375+
*
376+
* Returns the index of the page entry to insert into.
374377
*/
375378
OffsetNumber
376379
gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
377380
GISTSTATE *giststate)
378381
{
382+
OffsetNumber result;
379383
OffsetNumber maxoff;
380384
OffsetNumber i;
381-
OffsetNumber which;
382-
float sum_grow,
383-
which_grow[INDEX_MAX_KEYS];
385+
float best_penalty[INDEX_MAX_KEYS];
384386
GISTENTRY entry,
385387
identry[INDEX_MAX_KEYS];
386388
bool isnull[INDEX_MAX_KEYS];
387389

388-
maxoff = PageGetMaxOffsetNumber(p);
389-
*which_grow = -1.0;
390-
which = InvalidOffsetNumber;
391-
sum_grow = 1;
390+
Assert(!GistPageIsLeaf(p));
391+
392392
gistDeCompressAtt(giststate, r,
393393
it, NULL, (OffsetNumber) 0,
394394
identry, isnull);
395395

396+
/* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
397+
result = FirstOffsetNumber;
398+
399+
/*
400+
* The index may have multiple columns, and there's a penalty value for
401+
* each column. The penalty associated with a column that appears earlier
402+
* in the index definition is strictly more important than the penalty of
403+
* a column that appears later in the index definition.
404+
*
405+
* best_penalty[j] is the best penalty we have seen so far for column j,
406+
* or -1 when we haven't yet examined column j. Array entries to the
407+
* right of the first -1 are undefined.
408+
*/
409+
best_penalty[0] = -1;
410+
411+
/*
412+
* Loop over tuples on page.
413+
*/
414+
maxoff = PageGetMaxOffsetNumber(p);
396415
Assert(maxoff >= FirstOffsetNumber);
397-
Assert(!GistPageIsLeaf(p));
398416

399-
for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i))
417+
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
400418
{
401-
int j;
402419
IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
420+
bool zero_penalty;
421+
int j;
403422

404423
if (!GistPageIsLeaf(p) && GistTupleIsInvalid(itup))
405424
{
@@ -409,41 +428,70 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
409428
continue;
410429
}
411430

412-
sum_grow = 0;
431+
zero_penalty = true;
432+
433+
/* Loop over index attributes. */
413434
for (j = 0; j < r->rd_att->natts; j++)
414435
{
415436
Datum datum;
416437
float usize;
417438
bool IsNull;
418439

440+
/* Compute penalty for this column. */
419441
datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
420442
gistdentryinit(giststate, j, &entry, datum, r, p, i,
421443
FALSE, IsNull);
422444
usize = gistpenalty(giststate, j, &entry, IsNull,
423445
&identry[j], isnull[j]);
446+
if (usize > 0)
447+
zero_penalty = false;
424448

425-
if (which_grow[j] < 0 || usize < which_grow[j])
449+
if (best_penalty[j] < 0 || usize < best_penalty[j])
450+
{
451+
/*
452+
* New best penalty for column. Tentatively select this tuple
453+
* as the target, and record the best penalty. Then reset the
454+
* next column's penalty to "unknown" (and indirectly, the
455+
* same for all the ones to its right). This will force us to
456+
* adopt this tuple's penalty values as the best for all the
457+
* remaining columns during subsequent loop iterations.
458+
*/
459+
result = i;
460+
best_penalty[j] = usize;
461+
462+
if (j < r->rd_att->natts - 1)
463+
best_penalty[j + 1] = -1;
464+
}
465+
else if (best_penalty[j] == usize)
426466
{
427-
which = i;
428-
which_grow[j] = usize;
429-
if (j < r->rd_att->natts - 1 && i == FirstOffsetNumber)
430-
which_grow[j + 1] = -1;
431-
sum_grow += which_grow[j];
467+
/*
468+
* The current tuple is exactly as good for this column as the
469+
* best tuple seen so far. The next iteration of this loop
470+
* will compare the next column.
471+
*/
432472
}
433-
else if (which_grow[j] == usize)
434-
sum_grow += usize;
435473
else
436474
{
437-
sum_grow = 1;
475+
/*
476+
* The current tuple is worse for this column than the best
477+
* tuple seen so far. Skip the remaining columns and move on
478+
* to the next tuple, if any.
479+
*/
480+
zero_penalty = false; /* so outer loop won't exit */
438481
break;
439482
}
440483
}
441-
}
442484

443-
if (which == InvalidOffsetNumber)
444-
which = FirstOffsetNumber;
485+
/*
486+
* If we find a tuple with zero penalty for all columns, there's no
487+
* need to examine remaining tuples; just break out of the loop and
488+
* return it.
489+
*/
490+
if (zero_penalty)
491+
break;
492+
}
445493

446-
return which;
494+
return result;
447495
}
448496

449497
/*

0 commit comments

Comments
 (0)
0