00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "postgres.h"
00017
00018 #include "access/genam.h"
00019 #include "access/spgist_private.h"
00020 #include "access/transam.h"
00021 #include "catalog/storage_xlog.h"
00022 #include "commands/vacuum.h"
00023 #include "miscadmin.h"
00024 #include "storage/bufmgr.h"
00025 #include "storage/indexfsm.h"
00026 #include "storage/lmgr.h"
00027 #include "utils/snapmgr.h"
00028
00029
00030
00031 typedef struct spgVacPendingItem
00032 {
00033 ItemPointerData tid;
00034 bool done;
00035 struct spgVacPendingItem *next;
00036 } spgVacPendingItem;
00037
00038
00039 typedef struct spgBulkDeleteState
00040 {
00041
00042 IndexVacuumInfo *info;
00043 IndexBulkDeleteResult *stats;
00044 IndexBulkDeleteCallback callback;
00045 void *callback_state;
00046
00047
00048 SpGistState spgstate;
00049 spgVacPendingItem *pendingList;
00050 TransactionId myXmin;
00051 BlockNumber lastFilledBlock;
00052 } spgBulkDeleteState;
00053
00054
00055
00056
00057
00058
00059
00060
00061 static void
00062 spgAddPendingTID(spgBulkDeleteState *bds, ItemPointer tid)
00063 {
00064 spgVacPendingItem *pitem;
00065 spgVacPendingItem **listLink;
00066
00067
00068 listLink = &bds->pendingList;
00069 while (*listLink != NULL)
00070 {
00071 pitem = *listLink;
00072 if (ItemPointerEquals(tid, &pitem->tid))
00073 return;
00074 listLink = &pitem->next;
00075 }
00076
00077 pitem = (spgVacPendingItem *) palloc(sizeof(spgVacPendingItem));
00078 pitem->tid = *tid;
00079 pitem->done = false;
00080 pitem->next = NULL;
00081 *listLink = pitem;
00082 }
00083
00084
00085
00086
00087 static void
00088 spgClearPendingList(spgBulkDeleteState *bds)
00089 {
00090 spgVacPendingItem *pitem;
00091 spgVacPendingItem *nitem;
00092
00093 for (pitem = bds->pendingList; pitem != NULL; pitem = nitem)
00094 {
00095 nitem = pitem->next;
00096
00097 Assert(pitem->done);
00098 pfree(pitem);
00099 }
00100 bds->pendingList = NULL;
00101 }
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123 static void
00124 vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer,
00125 bool forPending)
00126 {
00127 Page page = BufferGetPage(buffer);
00128 spgxlogVacuumLeaf xlrec;
00129 XLogRecData rdata[8];
00130 OffsetNumber toDead[MaxIndexTuplesPerPage];
00131 OffsetNumber toPlaceholder[MaxIndexTuplesPerPage];
00132 OffsetNumber moveSrc[MaxIndexTuplesPerPage];
00133 OffsetNumber moveDest[MaxIndexTuplesPerPage];
00134 OffsetNumber chainSrc[MaxIndexTuplesPerPage];
00135 OffsetNumber chainDest[MaxIndexTuplesPerPage];
00136 OffsetNumber predecessor[MaxIndexTuplesPerPage + 1];
00137 bool deletable[MaxIndexTuplesPerPage + 1];
00138 int nDeletable;
00139 OffsetNumber i,
00140 max = PageGetMaxOffsetNumber(page);
00141
00142 memset(predecessor, 0, sizeof(predecessor));
00143 memset(deletable, 0, sizeof(deletable));
00144 nDeletable = 0;
00145
00146
00147 for (i = FirstOffsetNumber; i <= max; i++)
00148 {
00149 SpGistLeafTuple lt;
00150
00151 lt = (SpGistLeafTuple) PageGetItem(page,
00152 PageGetItemId(page, i));
00153 if (lt->tupstate == SPGIST_LIVE)
00154 {
00155 Assert(ItemPointerIsValid(<->heapPtr));
00156
00157 if (bds->callback(<->heapPtr, bds->callback_state))
00158 {
00159 bds->stats->tuples_removed += 1;
00160 deletable[i] = true;
00161 nDeletable++;
00162 }
00163 else
00164 {
00165 if (!forPending)
00166 bds->stats->num_index_tuples += 1;
00167 }
00168
00169
00170 if (lt->nextOffset != InvalidOffsetNumber)
00171 {
00172
00173 if (lt->nextOffset < FirstOffsetNumber ||
00174 lt->nextOffset > max ||
00175 predecessor[lt->nextOffset] != InvalidOffsetNumber)
00176 elog(ERROR, "inconsistent tuple chain links in page %u of index \"%s\"",
00177 BufferGetBlockNumber(buffer),
00178 RelationGetRelationName(index));
00179 predecessor[lt->nextOffset] = i;
00180 }
00181 }
00182 else if (lt->tupstate == SPGIST_REDIRECT)
00183 {
00184 SpGistDeadTuple dt = (SpGistDeadTuple) lt;
00185
00186 Assert(dt->nextOffset == InvalidOffsetNumber);
00187 Assert(ItemPointerIsValid(&dt->pointer));
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198 if (TransactionIdFollowsOrEquals(dt->xid, bds->myXmin))
00199 spgAddPendingTID(bds, &dt->pointer);
00200 }
00201 else
00202 {
00203 Assert(lt->nextOffset == InvalidOffsetNumber);
00204 }
00205 }
00206
00207 if (nDeletable == 0)
00208 return;
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231 xlrec.nDead = xlrec.nPlaceholder = xlrec.nMove = xlrec.nChain = 0;
00232
00233 for (i = FirstOffsetNumber; i <= max; i++)
00234 {
00235 SpGistLeafTuple head;
00236 bool interveningDeletable;
00237 OffsetNumber prevLive;
00238 OffsetNumber j;
00239
00240 head = (SpGistLeafTuple) PageGetItem(page,
00241 PageGetItemId(page, i));
00242 if (head->tupstate != SPGIST_LIVE)
00243 continue;
00244 if (predecessor[i] != 0)
00245 continue;
00246
00247
00248 interveningDeletable = false;
00249 prevLive = deletable[i] ? InvalidOffsetNumber : i;
00250
00251
00252 j = head->nextOffset;
00253 while (j != InvalidOffsetNumber)
00254 {
00255 SpGistLeafTuple lt;
00256
00257 lt = (SpGistLeafTuple) PageGetItem(page,
00258 PageGetItemId(page, j));
00259 if (lt->tupstate != SPGIST_LIVE)
00260 {
00261
00262 elog(ERROR, "unexpected SPGiST tuple state: %d",
00263 lt->tupstate);
00264 }
00265
00266 if (deletable[j])
00267 {
00268
00269 toPlaceholder[xlrec.nPlaceholder] = j;
00270 xlrec.nPlaceholder++;
00271
00272 interveningDeletable = true;
00273 }
00274 else if (prevLive == InvalidOffsetNumber)
00275 {
00276
00277
00278
00279
00280 moveSrc[xlrec.nMove] = j;
00281 moveDest[xlrec.nMove] = i;
00282 xlrec.nMove++;
00283
00284 prevLive = i;
00285 interveningDeletable = false;
00286 }
00287 else
00288 {
00289
00290
00291
00292
00293 if (interveningDeletable)
00294 {
00295 chainSrc[xlrec.nChain] = prevLive;
00296 chainDest[xlrec.nChain] = j;
00297 xlrec.nChain++;
00298 }
00299 prevLive = j;
00300 interveningDeletable = false;
00301 }
00302
00303 j = lt->nextOffset;
00304 }
00305
00306 if (prevLive == InvalidOffsetNumber)
00307 {
00308
00309 toDead[xlrec.nDead] = i;
00310 xlrec.nDead++;
00311 }
00312 else if (interveningDeletable)
00313 {
00314
00315 chainSrc[xlrec.nChain] = prevLive;
00316 chainDest[xlrec.nChain] = InvalidOffsetNumber;
00317 xlrec.nChain++;
00318 }
00319 }
00320
00321
00322 if (nDeletable != xlrec.nDead + xlrec.nPlaceholder + xlrec.nMove)
00323 elog(ERROR, "inconsistent counts of deletable tuples");
00324
00325
00326 xlrec.node = index->rd_node;
00327 xlrec.blkno = BufferGetBlockNumber(buffer);
00328 STORE_STATE(&bds->spgstate, xlrec.stateSrc);
00329
00330 ACCEPT_RDATA_DATA(&xlrec, sizeof(xlrec), 0);
00331
00332 ACCEPT_RDATA_DATA(toDead, sizeof(OffsetNumber) * xlrec.nDead, 1);
00333 ACCEPT_RDATA_DATA(toPlaceholder, sizeof(OffsetNumber) * xlrec.nPlaceholder, 2);
00334 ACCEPT_RDATA_DATA(moveSrc, sizeof(OffsetNumber) * xlrec.nMove, 3);
00335 ACCEPT_RDATA_DATA(moveDest, sizeof(OffsetNumber) * xlrec.nMove, 4);
00336 ACCEPT_RDATA_DATA(chainSrc, sizeof(OffsetNumber) * xlrec.nChain, 5);
00337 ACCEPT_RDATA_DATA(chainDest, sizeof(OffsetNumber) * xlrec.nChain, 6);
00338 ACCEPT_RDATA_BUFFER(buffer, 7);
00339
00340
00341 START_CRIT_SECTION();
00342
00343 spgPageIndexMultiDelete(&bds->spgstate, page,
00344 toDead, xlrec.nDead,
00345 SPGIST_DEAD, SPGIST_DEAD,
00346 InvalidBlockNumber, InvalidOffsetNumber);
00347
00348 spgPageIndexMultiDelete(&bds->spgstate, page,
00349 toPlaceholder, xlrec.nPlaceholder,
00350 SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
00351 InvalidBlockNumber, InvalidOffsetNumber);
00352
00353
00354
00355
00356
00357
00358
00359
00360 for (i = 0; i < xlrec.nMove; i++)
00361 {
00362 ItemId idSrc = PageGetItemId(page, moveSrc[i]);
00363 ItemId idDest = PageGetItemId(page, moveDest[i]);
00364 ItemIdData tmp;
00365
00366 tmp = *idSrc;
00367 *idSrc = *idDest;
00368 *idDest = tmp;
00369 }
00370
00371 spgPageIndexMultiDelete(&bds->spgstate, page,
00372 moveSrc, xlrec.nMove,
00373 SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
00374 InvalidBlockNumber, InvalidOffsetNumber);
00375
00376 for (i = 0; i < xlrec.nChain; i++)
00377 {
00378 SpGistLeafTuple lt;
00379
00380 lt = (SpGistLeafTuple) PageGetItem(page,
00381 PageGetItemId(page, chainSrc[i]));
00382 Assert(lt->tupstate == SPGIST_LIVE);
00383 lt->nextOffset = chainDest[i];
00384 }
00385
00386 MarkBufferDirty(buffer);
00387
00388 if (RelationNeedsWAL(index))
00389 {
00390 XLogRecPtr recptr;
00391
00392 recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_LEAF, rdata);
00393
00394 PageSetLSN(page, recptr);
00395 }
00396
00397 END_CRIT_SECTION();
00398 }
00399
00400
00401
00402
00403
00404
00405 static void
00406 vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
00407 {
00408 Page page = BufferGetPage(buffer);
00409 spgxlogVacuumRoot xlrec;
00410 XLogRecData rdata[3];
00411 OffsetNumber toDelete[MaxIndexTuplesPerPage];
00412 OffsetNumber i,
00413 max = PageGetMaxOffsetNumber(page);
00414
00415 xlrec.blkno = BufferGetBlockNumber(buffer);
00416 xlrec.nDelete = 0;
00417
00418
00419 for (i = FirstOffsetNumber; i <= max; i++)
00420 {
00421 SpGistLeafTuple lt;
00422
00423 lt = (SpGistLeafTuple) PageGetItem(page,
00424 PageGetItemId(page, i));
00425 if (lt->tupstate == SPGIST_LIVE)
00426 {
00427 Assert(ItemPointerIsValid(<->heapPtr));
00428
00429 if (bds->callback(<->heapPtr, bds->callback_state))
00430 {
00431 bds->stats->tuples_removed += 1;
00432 toDelete[xlrec.nDelete] = i;
00433 xlrec.nDelete++;
00434 }
00435 else
00436 {
00437 bds->stats->num_index_tuples += 1;
00438 }
00439 }
00440 else
00441 {
00442
00443 elog(ERROR, "unexpected SPGiST tuple state: %d",
00444 lt->tupstate);
00445 }
00446 }
00447
00448 if (xlrec.nDelete == 0)
00449 return;
00450
00451
00452 xlrec.node = index->rd_node;
00453 STORE_STATE(&bds->spgstate, xlrec.stateSrc);
00454
00455 ACCEPT_RDATA_DATA(&xlrec, sizeof(xlrec), 0);
00456
00457 ACCEPT_RDATA_DATA(toDelete, sizeof(OffsetNumber) * xlrec.nDelete, 1);
00458 ACCEPT_RDATA_BUFFER(buffer, 2);
00459
00460
00461 START_CRIT_SECTION();
00462
00463
00464 PageIndexMultiDelete(page, toDelete, xlrec.nDelete);
00465
00466 MarkBufferDirty(buffer);
00467
00468 if (RelationNeedsWAL(index))
00469 {
00470 XLogRecPtr recptr;
00471
00472 recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_ROOT, rdata);
00473
00474 PageSetLSN(page, recptr);
00475 }
00476
00477 END_CRIT_SECTION();
00478 }
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489 static void
00490 vacuumRedirectAndPlaceholder(Relation index, Buffer buffer)
00491 {
00492 Page page = BufferGetPage(buffer);
00493 SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
00494 OffsetNumber i,
00495 max = PageGetMaxOffsetNumber(page),
00496 firstPlaceholder = InvalidOffsetNumber;
00497 bool hasNonPlaceholder = false;
00498 bool hasUpdate = false;
00499 OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPage];
00500 OffsetNumber itemnos[MaxIndexTuplesPerPage];
00501 spgxlogVacuumRedirect xlrec;
00502 XLogRecData rdata[3];
00503
00504 xlrec.node = index->rd_node;
00505 xlrec.blkno = BufferGetBlockNumber(buffer);
00506 xlrec.nToPlaceholder = 0;
00507 xlrec.newestRedirectXid = InvalidTransactionId;
00508
00509 START_CRIT_SECTION();
00510
00511
00512
00513
00514
00515 for (i = max;
00516 i >= FirstOffsetNumber &&
00517 (opaque->nRedirection > 0 || !hasNonPlaceholder);
00518 i--)
00519 {
00520 SpGistDeadTuple dt;
00521
00522 dt = (SpGistDeadTuple) PageGetItem(page, PageGetItemId(page, i));
00523
00524 if (dt->tupstate == SPGIST_REDIRECT &&
00525 TransactionIdPrecedes(dt->xid, RecentGlobalXmin))
00526 {
00527 dt->tupstate = SPGIST_PLACEHOLDER;
00528 Assert(opaque->nRedirection > 0);
00529 opaque->nRedirection--;
00530 opaque->nPlaceholder++;
00531
00532
00533 if (!TransactionIdIsValid(xlrec.newestRedirectXid) ||
00534 TransactionIdPrecedes(xlrec.newestRedirectXid, dt->xid))
00535 xlrec.newestRedirectXid = dt->xid;
00536
00537 ItemPointerSetInvalid(&dt->pointer);
00538
00539 itemToPlaceholder[xlrec.nToPlaceholder] = i;
00540 xlrec.nToPlaceholder++;
00541
00542 hasUpdate = true;
00543 }
00544
00545 if (dt->tupstate == SPGIST_PLACEHOLDER)
00546 {
00547 if (!hasNonPlaceholder)
00548 firstPlaceholder = i;
00549 }
00550 else
00551 {
00552 hasNonPlaceholder = true;
00553 }
00554 }
00555
00556
00557
00558
00559
00560
00561 if (firstPlaceholder != InvalidOffsetNumber)
00562 {
00563
00564
00565
00566 for (i = firstPlaceholder; i <= max; i++)
00567 itemnos[i - firstPlaceholder] = i;
00568
00569 i = max - firstPlaceholder + 1;
00570 Assert(opaque->nPlaceholder >= i);
00571 opaque->nPlaceholder -= i;
00572
00573
00574 PageIndexMultiDelete(page, itemnos, i);
00575
00576 hasUpdate = true;
00577 }
00578
00579 xlrec.firstPlaceholder = firstPlaceholder;
00580
00581 if (hasUpdate)
00582 MarkBufferDirty(buffer);
00583
00584 if (hasUpdate && RelationNeedsWAL(index))
00585 {
00586 XLogRecPtr recptr;
00587
00588 ACCEPT_RDATA_DATA(&xlrec, sizeof(xlrec), 0);
00589 ACCEPT_RDATA_DATA(itemToPlaceholder, sizeof(OffsetNumber) * xlrec.nToPlaceholder, 1);
00590 ACCEPT_RDATA_BUFFER(buffer, 2);
00591
00592 recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_REDIRECT, rdata);
00593
00594 PageSetLSN(page, recptr);
00595 }
00596
00597 END_CRIT_SECTION();
00598 }
00599
00600
00601
00602
00603 static void
00604 spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno)
00605 {
00606 Relation index = bds->info->index;
00607 Buffer buffer;
00608 Page page;
00609
00610
00611 vacuum_delay_point();
00612
00613 buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
00614 RBM_NORMAL, bds->info->strategy);
00615 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
00616 page = (Page) BufferGetPage(buffer);
00617
00618 if (PageIsNew(page))
00619 {
00620
00621
00622
00623
00624 SpGistInitBuffer(buffer, 0);
00625 SpGistPageSetDeleted(page);
00626
00627 MarkBufferDirty(buffer);
00628 }
00629 else if (SpGistPageIsDeleted(page))
00630 {
00631
00632 }
00633 else if (SpGistPageIsLeaf(page))
00634 {
00635 if (SpGistBlockIsRoot(blkno))
00636 {
00637 vacuumLeafRoot(bds, index, buffer);
00638
00639 }
00640 else
00641 {
00642 vacuumLeafPage(bds, index, buffer, false);
00643 vacuumRedirectAndPlaceholder(index, buffer);
00644 }
00645 }
00646 else
00647 {
00648
00649 vacuumRedirectAndPlaceholder(index, buffer);
00650 }
00651
00652
00653
00654
00655
00656
00657
00658 if (!SpGistBlockIsRoot(blkno))
00659 {
00660
00661 if (PageIsEmpty(page) && !SpGistPageIsDeleted(page))
00662 {
00663 SpGistPageSetDeleted(page);
00664
00665 MarkBufferDirty(buffer);
00666 }
00667
00668 if (SpGistPageIsDeleted(page))
00669 {
00670 RecordFreeIndexPage(index, blkno);
00671 bds->stats->pages_deleted++;
00672 }
00673 else
00674 bds->lastFilledBlock = blkno;
00675 }
00676
00677 SpGistSetLastUsedPage(index, buffer);
00678
00679 UnlockReleaseBuffer(buffer);
00680 }
00681
00682
00683
00684
00685 static void
00686 spgprocesspending(spgBulkDeleteState *bds)
00687 {
00688 Relation index = bds->info->index;
00689 spgVacPendingItem *pitem;
00690 spgVacPendingItem *nitem;
00691 BlockNumber blkno;
00692 Buffer buffer;
00693 Page page;
00694
00695 for (pitem = bds->pendingList; pitem != NULL; pitem = pitem->next)
00696 {
00697 if (pitem->done)
00698 continue;
00699
00700
00701 vacuum_delay_point();
00702
00703
00704 blkno = ItemPointerGetBlockNumber(&pitem->tid);
00705 buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
00706 RBM_NORMAL, bds->info->strategy);
00707 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
00708 page = (Page) BufferGetPage(buffer);
00709
00710 if (PageIsNew(page) || SpGistPageIsDeleted(page))
00711 {
00712
00713 }
00714 else if (SpGistPageIsLeaf(page))
00715 {
00716 if (SpGistBlockIsRoot(blkno))
00717 {
00718
00719 elog(ERROR, "redirection leads to root page of index \"%s\"",
00720 RelationGetRelationName(index));
00721 }
00722
00723
00724 vacuumLeafPage(bds, index, buffer, true);
00725
00726 vacuumRedirectAndPlaceholder(index, buffer);
00727
00728 SpGistSetLastUsedPage(index, buffer);
00729
00730
00731
00732
00733
00734 pitem->done = true;
00735 for (nitem = pitem->next; nitem != NULL; nitem = nitem->next)
00736 {
00737 if (ItemPointerGetBlockNumber(&nitem->tid) == blkno)
00738 nitem->done = true;
00739 }
00740 }
00741 else
00742 {
00743
00744
00745
00746
00747
00748
00749
00750 for (nitem = pitem; nitem != NULL; nitem = nitem->next)
00751 {
00752 if (nitem->done)
00753 continue;
00754 if (ItemPointerGetBlockNumber(&nitem->tid) == blkno)
00755 {
00756 OffsetNumber offset;
00757 SpGistInnerTuple innerTuple;
00758
00759 offset = ItemPointerGetOffsetNumber(&nitem->tid);
00760 innerTuple = (SpGistInnerTuple) PageGetItem(page,
00761 PageGetItemId(page, offset));
00762 if (innerTuple->tupstate == SPGIST_LIVE)
00763 {
00764 SpGistNodeTuple node;
00765 int i;
00766
00767 SGITITERATE(innerTuple, i, node)
00768 {
00769 if (ItemPointerIsValid(&node->t_tid))
00770 spgAddPendingTID(bds, &node->t_tid);
00771 }
00772 }
00773 else if (innerTuple->tupstate == SPGIST_REDIRECT)
00774 {
00775
00776 spgAddPendingTID(bds,
00777 &((SpGistDeadTuple) innerTuple)->pointer);
00778 }
00779 else
00780 elog(ERROR, "unexpected SPGiST tuple state: %d",
00781 innerTuple->tupstate);
00782
00783 nitem->done = true;
00784 }
00785 }
00786 }
00787
00788 UnlockReleaseBuffer(buffer);
00789 }
00790
00791 spgClearPendingList(bds);
00792 }
00793
00794
00795
00796
00797 static void
00798 spgvacuumscan(spgBulkDeleteState *bds)
00799 {
00800 Relation index = bds->info->index;
00801 bool needLock;
00802 BlockNumber num_pages,
00803 blkno;
00804
00805
00806 initSpGistState(&bds->spgstate, index);
00807 bds->pendingList = NULL;
00808 bds->myXmin = GetActiveSnapshot()->xmin;
00809 bds->lastFilledBlock = SPGIST_LAST_FIXED_BLKNO;
00810
00811
00812
00813
00814
00815 bds->stats->estimated_count = false;
00816 bds->stats->num_index_tuples = 0;
00817 bds->stats->pages_deleted = 0;
00818
00819
00820 needLock = !RELATION_IS_LOCAL(index);
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830 blkno = SPGIST_METAPAGE_BLKNO + 1;
00831 for (;;)
00832 {
00833
00834 if (needLock)
00835 LockRelationForExtension(index, ExclusiveLock);
00836 num_pages = RelationGetNumberOfBlocks(index);
00837 if (needLock)
00838 UnlockRelationForExtension(index, ExclusiveLock);
00839
00840
00841 if (blkno >= num_pages)
00842 break;
00843
00844 for (; blkno < num_pages; blkno++)
00845 {
00846 spgvacuumpage(bds, blkno);
00847
00848 if (bds->pendingList != NULL)
00849 spgprocesspending(bds);
00850 }
00851 }
00852
00853
00854 SpGistUpdateMetaPage(index);
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869 #ifdef NOT_USED
00870 if (num_pages > bds->lastFilledBlock + 1)
00871 {
00872 BlockNumber lastBlock = num_pages - 1;
00873
00874 num_pages = bds->lastFilledBlock + 1;
00875 RelationTruncate(index, num_pages);
00876 bds->stats->pages_removed += lastBlock - bds->lastFilledBlock;
00877 bds->stats->pages_deleted -= lastBlock - bds->lastFilledBlock;
00878 }
00879 #endif
00880
00881
00882 bds->stats->num_pages = num_pages;
00883 bds->stats->pages_free = bds->stats->pages_deleted;
00884 }
00885
00886
00887
00888
00889
00890
00891
00892
00893 Datum
00894 spgbulkdelete(PG_FUNCTION_ARGS)
00895 {
00896 IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
00897 IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
00898 IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
00899 void *callback_state = (void *) PG_GETARG_POINTER(3);
00900 spgBulkDeleteState bds;
00901
00902
00903 if (stats == NULL)
00904 stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
00905 bds.info = info;
00906 bds.stats = stats;
00907 bds.callback = callback;
00908 bds.callback_state = callback_state;
00909
00910 spgvacuumscan(&bds);
00911
00912 PG_RETURN_POINTER(stats);
00913 }
00914
00915
00916 static bool
00917 dummy_callback(ItemPointer itemptr, void *state)
00918 {
00919 return false;
00920 }
00921
00922
00923
00924
00925
00926
00927 Datum
00928 spgvacuumcleanup(PG_FUNCTION_ARGS)
00929 {
00930 IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
00931 IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
00932 Relation index = info->index;
00933 spgBulkDeleteState bds;
00934
00935
00936 if (info->analyze_only)
00937 PG_RETURN_POINTER(stats);
00938
00939
00940
00941
00942
00943
00944
00945 if (stats == NULL)
00946 {
00947 stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
00948 bds.info = info;
00949 bds.stats = stats;
00950 bds.callback = dummy_callback;
00951 bds.callback_state = NULL;
00952
00953 spgvacuumscan(&bds);
00954 }
00955
00956
00957 IndexFreeSpaceMapVacuum(index);
00958
00959
00960
00961
00962
00963
00964
00965 if (!info->estimated_count)
00966 {
00967 if (stats->num_index_tuples > info->num_heap_tuples)
00968 stats->num_index_tuples = info->num_heap_tuples;
00969 }
00970
00971 PG_RETURN_POINTER(stats);
00972 }