1 /*-------------------------------------------------------------------------
4 * the postgres vacuum cleaner
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.15 1997/01/22 01:42:16 momjian Exp $
12 *-------------------------------------------------------------------------
16 #include <sys/types.h>
23 #include <utils/portal.h>
24 #include <access/genam.h>
25 #include <access/heapam.h>
26 #include <access/xact.h>
27 #include <storage/bufmgr.h>
28 #include <access/transam.h>
29 #include <catalog/pg_index.h>
30 #include <catalog/index.h>
31 #include <catalog/catname.h>
32 #include <catalog/catalog.h>
33 #include <catalog/pg_class.h>
34 #include <catalog/pg_proc.h>
35 #include <storage/smgr.h>
36 #include <storage/lmgr.h>
37 #include <utils/inval.h>
38 #include <utils/mcxt.h>
39 #include <utils/syscache.h>
40 #include <commands/vacuum.h>
41 #include <storage/bufpage.h>
42 #include "storage/shmem.h"
44 # include <rusagestub.h>
45 #else /* NEED_RUSAGE */
46 # include <sys/time.h>
47 # include <sys/resource.h>
48 #endif /* NEED_RUSAGE */
50 bool VacuumRunning = false;
51 static int MESSLEV; /* message level */
55 FuncIndexInfo *finfoP;
60 /* non-export function prototypes */
61 static void _vc_init(void);
62 static void _vc_shutdown(void);
63 static void _vc_vacuum(NameData *VacRelP);
64 static VRelList _vc_getrels(Portal p, NameData *VacRelP);
65 static void _vc_vacone (VRelList curvrl);
66 static void _vc_scanheap (VRelList curvrl, Relation onerel, VPageList Vvpl, VPageList Fvpl);
67 static void _vc_rpfheap (VRelList curvrl, Relation onerel, VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel);
68 static void _vc_vacheap (VRelList curvrl, Relation onerel, VPageList vpl);
69 static void _vc_vacpage (Page page, VPageDescr vpd, Relation archrel);
70 static void _vc_vaconeind (VPageList vpl, Relation indrel, int nhtups);
71 static void _vc_updstats(Oid relid, int npages, int ntuples, bool hasindex);
72 static void _vc_setpagelock(Relation rel, BlockNumber blkno);
73 static VPageDescr _vc_tidreapped (ItemPointer itemptr, VPageList curvrl);
74 static void _vc_reappage (VPageList vpl, VPageDescr vpc);
75 static void _vc_vpinsert (VPageList vpl, VPageDescr vpnew);
76 static void _vc_free(Portal p, VRelList vrl);
77 static void _vc_getindices (Oid relid, int *nindices, Relation **Irel);
78 static void _vc_clsindices (int nindices, Relation *Irel);
79 static Relation _vc_getarchrel(Relation heaprel);
80 static void _vc_archive(Relation archrel, HeapTuple htup);
81 static bool _vc_isarchrel(char *rname);
82 static void _vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc);
83 static char * _vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *));
84 static int _vc_cmp_blk (char *left, char *right);
85 static int _vc_cmp_offno (char *left, char *right);
86 static bool _vc_enough_space (VPageDescr vpd, Size len);
90 vacuum(char *vacrel, bool verbose)
99 /* vacrel gets de-allocated on transaction commit */
101 /* initialize vacuum cleaner */
104 /* vacuum the database */
107 strcpy(VacRel.data,vacrel);
118 * _vc_init(), _vc_shutdown() -- start up and shut down the vacuum cleaner.
120 * We run exactly one vacuum cleaner at a time. We use the file system
121 * to guarantee an exclusive lock on vacuuming, since a single vacuum
122 * cleaner instantiation crosses transaction boundaries, and we'd lose
123 * postgres-style locks at the end of every transaction.
125 * The strangeness with committing and starting transactions in the
126 * init and shutdown routines is due to the fact that the vacuum cleaner
127 * is invoked via a sql command, and so is already executing inside
128 * a transaction. We need to leave ourselves in a predictable state
129 * on entry and exit to the vacuum cleaner. We commit the transaction
130 * started in PostgresMain() inside _vc_init(), and start one in
131 * _vc_shutdown() to match the commit waiting for us back in
139 if ((fd = open("pg_vlock", O_CREAT|O_EXCL, 0600)) < 0)
140 elog(WARN, "can't create lock file -- another vacuum cleaner running?");
145 * By here, exclusive open on the lock file succeeded. If we abort
146 * for any reason during vacuuming, we need to remove the lock file.
147 * This global variable is checked in the transaction manager on xact
148 * abort, and the routine vc_abort() is called if necessary.
151 VacuumRunning = true;
153 /* matches the StartTransaction in PostgresMain() */
154 CommitTransactionCommand();
160 /* on entry, not in a transaction */
161 if (unlink("pg_vlock") < 0)
162 elog(WARN, "vacuum: can't destroy lock file!");
164 /* okay, we're done */
165 VacuumRunning = false;
167 /* matches the CommitTransaction in PostgresMain() */
168 StartTransactionCommand();
174 /* on abort, remove the vacuum cleaner lock file */
175 (void) unlink("pg_vlock");
177 VacuumRunning = false;
181 * _vc_vacuum() -- vacuum the database.
183 * This routine builds a list of relations to vacuum, and then calls
184 * code that vacuums them one at a time. We are careful to vacuum each
185 * relation in a separate transaction in order to avoid holding too many
189 _vc_vacuum(NameData *VacRelP)
196 * Create a portal for safe memory across transctions. We need to
197 * palloc the name space for it because our hash function expects
198 * the name to be on a longword boundary. CreatePortal copies the
199 * name to safe storage for us.
202 pname = (char *) palloc(strlen(VACPNAME) + 1);
203 strcpy(pname, VACPNAME);
204 p = CreatePortal(pname);
207 /* get list of relations */
208 vrl = _vc_getrels(p, VacRelP);
210 /* vacuum each heap relation */
211 for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
220 _vc_getrels(Portal p, NameData *VacRelP)
224 HeapScanDesc pgcscan;
227 PortalVariableMemory portalmem;
238 StartTransactionCommand();
241 ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname,
242 NameEqualRegProcedure,
243 PointerGetDatum(VacRelP->data));
245 ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind,
246 CharacterEqualRegProcedure, CharGetDatum('r'));
249 portalmem = PortalGetVariableMemory(p);
250 vrl = cur = (VRelList) NULL;
252 pgclass = heap_openr(RelationRelationName);
253 pgcdesc = RelationGetTupleDescriptor(pgclass);
255 pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey);
257 while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &buf))) {
262 * We have to be careful not to vacuum the archive (since it
263 * already contains vacuumed tuples), and not to vacuum
264 * relations on write-once storage managers like the Sony
265 * jukebox at Berkeley.
268 d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname,
272 /* skip archive relations */
273 if (_vc_isarchrel(rname)) {
278 /* don't vacuum large objects for now - something breaks when we do */
279 if ( (strlen(rname) > 4) && rname[0] == 'X' &&
280 rname[1] == 'i' && rname[2] == 'n' &&
281 (rname[3] == 'v' || rname[3] == 'x'))
283 elog (NOTICE, "Rel %.*s: can't vacuum LargeObjects now",
289 d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relsmgr,
291 smgrno = DatumGetInt16(d);
293 /* skip write-once storage managers */
294 if (smgriswo(smgrno)) {
299 d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relkind,
302 rkind = DatumGetChar(d);
304 /* skip system relations */
307 elog(NOTICE, "Vacuum: can not process index and certain system tables" );
311 /* get a relation list entry for this guy */
312 old = MemoryContextSwitchTo((MemoryContext)portalmem);
313 if (vrl == (VRelList) NULL) {
314 vrl = cur = (VRelList) palloc(sizeof(VRelListData));
316 cur->vrl_next = (VRelList) palloc(sizeof(VRelListData));
319 (void) MemoryContextSwitchTo(old);
321 cur->vrl_relid = pgctup->t_oid;
322 cur->vrl_attlist = (VAttList) NULL;
323 cur->vrl_npages = cur->vrl_ntups = 0;
324 cur->vrl_hasindex = false;
325 cur->vrl_next = (VRelList) NULL;
327 /* wei hates it if you forget to do this */
331 elog(NOTICE, "Vacuum: table not found" );
335 heap_endscan(pgcscan);
337 CommitTransactionCommand();
343 * _vc_vacone() -- vacuum one heap relation
345 * This routine vacuums a single heap, cleans out its indices, and
346 * updates its statistics npages and ntuples statistics.
348 * Doing one heap at a time incurs extra overhead, since we need to
349 * check that the heap exists again just before we vacuum it. The
350 * reason that we do this is so that vacuuming can be spread across
351 * many small transactions. Otherwise, two-phase locking would require
352 * us to lock the entire database during one pass of the vacuum cleaner.
355 _vc_vacone (VRelList curvrl)
361 HeapScanDesc pgcscan;
364 VPageListData Vvpl; /* List of pages to vacuum and/or clean indices */
365 VPageListData Fvpl; /* List of pages with space enough for re-using */
371 StartTransactionCommand();
373 ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber,
374 ObjectIdEqualRegProcedure,
375 ObjectIdGetDatum(curvrl->vrl_relid));
377 pgclass = heap_openr(RelationRelationName);
378 pgcdesc = RelationGetTupleDescriptor(pgclass);
379 pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey);
382 * Race condition -- if the pg_class tuple has gone away since the
383 * last time we saw it, we don't need to vacuum it.
386 if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &pgcbuf))) {
387 heap_endscan(pgcscan);
389 CommitTransactionCommand();
393 /* now open the class and vacuum it */
394 onerel = heap_open(curvrl->vrl_relid);
396 /* we require the relation to be locked until the indices are cleaned */
397 RelationSetLockForWrite(onerel);
400 Vvpl.vpl_npages = Fvpl.vpl_npages = 0;
401 _vc_scanheap(curvrl, onerel, &Vvpl, &Fvpl);
403 /* Now open/count indices */
404 Irel = (Relation *) NULL;
405 if ( Vvpl.vpl_npages > 0 )
406 /* Open all indices of this relation */
407 _vc_getindices(curvrl->vrl_relid, &nindices, &Irel);
409 /* Count indices only */
410 _vc_getindices(curvrl->vrl_relid, &nindices, NULL);
413 curvrl->vrl_hasindex = true;
415 curvrl->vrl_hasindex = false;
417 /* Clean index' relation(s) */
418 if ( Irel != (Relation*) NULL )
420 for (i = 0; i < nindices; i++)
421 _vc_vaconeind (&Vvpl, Irel[i], curvrl->vrl_ntups);
424 if ( Fvpl.vpl_npages > 0 ) /* Try to shrink heap */
425 _vc_rpfheap (curvrl, onerel, &Vvpl, &Fvpl, nindices, Irel);
426 else if ( Vvpl.vpl_npages > 0 ) /* Clean pages from Vvpl list */
428 if ( Irel != (Relation*) NULL )
429 _vc_clsindices (nindices, Irel);
430 _vc_vacheap (curvrl, onerel, &Vvpl);
433 /* ok - free Vvpl list of reapped pages */
434 if ( Vvpl.vpl_npages > 0 )
436 vpp = Vvpl.vpl_pgdesc;
437 for (i = 0; i < Vvpl.vpl_npages; i++, vpp++)
439 pfree (Vvpl.vpl_pgdesc);
440 if ( Fvpl.vpl_npages > 0 )
441 pfree (Fvpl.vpl_pgdesc);
444 /* all done with this class */
446 heap_endscan(pgcscan);
449 /* update statistics in pg_class */
450 _vc_updstats(curvrl->vrl_relid, curvrl->vrl_npages, curvrl->vrl_ntups,
451 curvrl->vrl_hasindex);
453 CommitTransactionCommand();
457 * _vc_scanheap() -- scan an open heap relation
459 * This routine sets commit times, constructs Vvpl list of
460 * empty/uninitialized pages and pages with dead tuples and
461 * ~LP_USED line pointers, constructs Fvpl list of pages
462 * appropriate for purposes of shrinking and maintains statistics
463 * on the number of live tuples in a heap.
466 _vc_scanheap (VRelList curvrl, Relation onerel,
467 VPageList Vvpl, VPageList Fvpl)
474 Page page, tempPage = NULL;
475 OffsetNumber offnum, maxoff;
476 bool pgchanged, tupgone, dobufrel, notup;
477 AbsoluteTime purgetime, expiretime;
478 RelativeTime preservetime;
481 uint32 nvac, ntups, nunused, ncrash, nempg, nnepg, nchpg, nemend;
483 Size min_tlen = MAXTUPLEN;
486 struct rusage ru0, ru1;
488 getrusage(RUSAGE_SELF, &ru0);
490 nvac = ntups = nunused = ncrash = nempg = nnepg = nchpg = nemend = 0;
493 relname = (RelationGetRelationName(onerel))->data;
495 nblocks = RelationGetNumberOfBlocks(onerel);
497 /* calculate the purge time: tuples that expired before this time
498 will be archived or deleted */
499 purgetime = GetCurrentTransactionStartTime();
500 expiretime = (AbsoluteTime)onerel->rd_rel->relexpires;
501 preservetime = (RelativeTime)onerel->rd_rel->relpreserved;
503 if (RelativeTimeIsValid(preservetime) && (preservetime)) {
504 purgetime -= preservetime;
505 if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime) &&
506 expiretime > purgetime)
507 purgetime = expiretime;
510 else if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime))
511 purgetime = expiretime;
513 vpc = (VPageDescr) palloc (sizeof(VPageDescrData) + MaxOffsetNumber*sizeof(OffsetNumber));
516 for (blkno = 0; blkno < nblocks; blkno++) {
517 buf = ReadBuffer(onerel, blkno);
518 page = BufferGetPage(buf);
519 vpc->vpd_blkno = blkno;
522 if (PageIsNew(page)) {
523 elog (NOTICE, "Rel %.*s: Uninitialized page %u - fixing",
524 NAMEDATALEN, relname, blkno);
525 PageInit (page, BufferGetPageSize (buf), 0);
526 vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower;
527 frsize += (vpc->vpd_free - sizeof (ItemIdData));
530 _vc_reappage (Vvpl, vpc);
535 if (PageIsEmpty(page)) {
536 vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower;
537 frsize += (vpc->vpd_free - sizeof (ItemIdData));
540 _vc_reappage (Vvpl, vpc);
547 maxoff = PageGetMaxOffsetNumber(page);
548 for (offnum = FirstOffsetNumber;
550 offnum = OffsetNumberNext(offnum)) {
551 itemid = PageGetItemId(page, offnum);
554 * Collect un-used items too - it's possible to have
555 * indices pointing here after crash.
557 if (!ItemIdIsUsed(itemid)) {
558 vpc->vpd_voff[vpc->vpd_noff++] = offnum;
563 htup = (HeapTuple) PageGetItem(page, itemid);
566 if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) &&
567 TransactionIdIsValid((TransactionId)htup->t_xmin)) {
569 if (TransactionIdDidAbort(htup->t_xmin)) {
571 } else if (TransactionIdDidCommit(htup->t_xmin)) {
572 htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin);
574 } else if ( !TransactionIdIsInProgress (htup->t_xmin) ) {
576 * Not Aborted, Not Committed, Not in Progress -
577 * so it from crashed process. - vadim 11/26/96
583 elog (MESSLEV, "Rel %.*s: InsertTransactionInProgress %u for TID %u/%u",
584 NAMEDATALEN, relname, htup->t_xmin, blkno, offnum);
588 if (TransactionIdIsValid((TransactionId)htup->t_xmax)) {
589 if (TransactionIdDidAbort(htup->t_xmax)) {
590 StoreInvalidTransactionId(&(htup->t_xmax));
592 } else if (TransactionIdDidCommit(htup->t_xmax)) {
593 if (!AbsoluteTimeIsBackwardCompatiblyReal(htup->t_tmax)) {
595 htup->t_tmax = TransactionIdGetCommitTime(htup->t_xmax);
600 * Reap the dead tuple if its expiration time is
604 if (htup->t_tmax < purgetime) {
611 * Is it possible at all ? - vadim 11/26/96
613 if ( !TransactionIdIsValid((TransactionId)htup->t_xmin) )
615 elog (NOTICE, "TID %u/%u: INSERT_TRANSACTION_ID IS INVALID. \
616 DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.",
617 TransactionIdIsValid((TransactionId)htup->t_xmax),
622 * It's possibly! But from where it comes ?
623 * And should we fix it ? - vadim 11/28/96
625 itemptr = &(htup->t_ctid);
626 if ( !ItemPointerIsValid (itemptr) ||
627 BlockIdGetBlockNumber(&(itemptr->ip_blkid)) != blkno )
629 elog (NOTICE, "ITEM POINTER IS INVALID: %u/%u FOR %u/%u. TUPGONE %d.",
630 BlockIdGetBlockNumber(&(itemptr->ip_blkid)),
631 itemptr->ip_posid, blkno, offnum, tupgone);
637 if ( htup->t_len != itemid->lp_len )
639 elog (NOTICE, "PAGEHEADER' LEN %u IS NOT THE SAME AS HTUP' %u FOR %u/%u.TUPGONE %d.",
640 itemid->lp_len, htup->t_len, blkno, offnum, tupgone);
642 if ( !OidIsValid(htup->t_oid) )
644 elog (NOTICE, "OID IS INVALID FOR %u/%u.TUPGONE %d.",
645 blkno, offnum, tupgone);
651 if ( tempPage == (Page) NULL )
655 pageSize = PageGetPageSize(page);
656 tempPage = (Page) palloc(pageSize);
657 memmove (tempPage, page, pageSize);
660 lpp = &(((PageHeader) tempPage)->pd_linp[offnum - 1]);
663 lpp->lp_flags &= ~LP_USED;
665 vpc->vpd_voff[vpc->vpd_noff++] = offnum;
671 if ( htup->t_len < min_tlen )
672 min_tlen = htup->t_len;
673 if ( htup->t_len > max_tlen )
674 max_tlen = htup->t_len;
685 if ( tempPage != (Page) NULL )
686 { /* Some tuples are gone */
687 PageRepairFragmentation(tempPage);
688 vpc->vpd_free = ((PageHeader)tempPage)->pd_upper - ((PageHeader)tempPage)->pd_lower;
689 frsize += vpc->vpd_free;
690 _vc_reappage (Vvpl, vpc);
692 tempPage = (Page) NULL;
694 else if ( vpc->vpd_noff > 0 )
695 { /* there are only ~LP_USED line pointers */
696 vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower;
697 frsize += vpc->vpd_free;
698 _vc_reappage (Vvpl, vpc);
710 /* save stats in the rel list for use later */
711 curvrl->vrl_ntups = ntups;
712 curvrl->vrl_npages = nblocks;
714 min_tlen = max_tlen = 0;
715 curvrl->vrl_min_tlen = min_tlen;
716 curvrl->vrl_max_tlen = max_tlen;
718 Vvpl->vpl_nemend = nemend;
719 Fvpl->vpl_nemend = nemend;
722 * Try to make Fvpl keeping in mind that we can't use free space
723 * of "empty" end-pages and last page if it reapped.
725 if ( Vvpl->vpl_npages - nemend > 0 )
727 int nusf; /* blocks usefull for re-using */
729 nusf = Vvpl->vpl_npages - nemend;
730 if ( (Vvpl->vpl_pgdesc[nusf-1])->vpd_blkno == nblocks - nemend - 1 )
733 for (i = 0; i < nusf; i++)
735 vp = Vvpl->vpl_pgdesc[i];
736 if ( _vc_enough_space (vp, min_tlen) )
738 _vc_vpinsert (Fvpl, vp);
739 frsusf += vp->vpd_free;
744 getrusage(RUSAGE_SELF, &ru1);
746 elog (MESSLEV, "Rel %.*s: Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \
747 Tup %u: Vac %u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; Re-using: Free/Avail. Space %u/%u; EndEmpty/Avail. Pages %u/%u. Elapsed %u/%u sec.",
748 NAMEDATALEN, relname,
749 nblocks, nchpg, Vvpl->vpl_npages, nempg, nnepg,
750 ntups, nvac, ncrash, nunused, min_tlen, max_tlen,
751 frsize, frsusf, nemend, Fvpl->vpl_npages,
752 ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec,
753 ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
759 * _vc_rpfheap() -- try to repaire relation' fragmentation
761 * This routine marks dead tuples as unused and tries re-use dead space
762 * by moving tuples (and inserting indices if needed). It constructs
763 * Nvpl list of free-ed pages (moved tuples) and clean indices
764 * for them after committing (in hack-manner - without losing locks
765 * and freeing memory!) current transaction. It truncates relation
766 * if some end-blocks are gone away.
769 _vc_rpfheap (VRelList curvrl, Relation onerel,
770 VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel)
774 AbsoluteTime myCTM = 0;
777 Page page, ToPage = NULL;
778 OffsetNumber offnum = 0, maxoff = 0, newoff, moff;
779 ItemId itemid, newitemid;
780 HeapTuple htup, newtup;
781 TupleDesc tupdesc = NULL;
782 Datum *idatum = NULL;
784 InsertIndexResult iresult;
786 VPageDescr ToVpd = NULL, Fvplast, Vvplast, vpc, *vpp;
788 IndDesc *Idesc, *idcur;
789 int Fblklast, Vblklast, i;
791 int nmoved, Fnpages, Vnpages;
793 bool isempty, dowrite;
795 struct rusage ru0, ru1;
797 getrusage(RUSAGE_SELF, &ru0);
799 myXID = GetCurrentTransactionId();
800 myCID = GetCurrentCommandId();
802 if ( Irel != (Relation*) NULL ) /* preparation for index' inserts */
804 _vc_mkindesc (onerel, nindices, Irel, &Idesc);
805 tupdesc = RelationGetTupleDescriptor(onerel);
806 idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof (*idatum));
807 inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof (*inulls));
810 /* if the relation has an archive, open it */
811 if (onerel->rd_rel->relarch != 'n')
813 archrel = _vc_getarchrel(onerel);
814 /* Archive tuples from "empty" end-pages */
815 for ( vpp = Vvpl->vpl_pgdesc + Vvpl->vpl_npages - 1,
816 i = Vvpl->vpl_nemend; i > 0; i--, vpp-- )
818 if ( (*vpp)->vpd_noff > 0 )
820 buf = ReadBuffer(onerel, (*vpp)->vpd_blkno);
821 page = BufferGetPage(buf);
822 Assert ( !PageIsEmpty(page) );
823 _vc_vacpage (page, *vpp, archrel);
829 archrel = (Relation) NULL;
832 Fnpages = Fvpl->vpl_npages;
833 Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1];
834 Fblklast = Fvplast->vpd_blkno;
835 Assert ( Vvpl->vpl_npages > Vvpl->vpl_nemend );
836 Vnpages = Vvpl->vpl_npages - Vvpl->vpl_nemend;
837 Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1];
838 Vblklast = Vvplast->vpd_blkno;
839 Assert ( Vblklast >= Fblklast );
840 ToBuf = InvalidBuffer;
843 vpc = (VPageDescr) palloc (sizeof(VPageDescrData) + MaxOffsetNumber*sizeof(OffsetNumber));
844 vpc->vpd_nusd = vpc->vpd_noff = 0;
846 nblocks = curvrl->vrl_npages;
847 for (blkno = nblocks - Vvpl->vpl_nemend - 1; ; blkno--)
849 /* if it's reapped page and it was used by me - quit */
850 if ( blkno == Fblklast && Fvplast->vpd_nusd > 0 )
853 buf = ReadBuffer(onerel, blkno);
854 page = BufferGetPage(buf);
858 isempty = PageIsEmpty(page);
861 if ( blkno == Vblklast ) /* it's reapped page */
863 if ( Vvplast->vpd_noff > 0 ) /* there are dead tuples */
864 { /* on this page - clean */
865 Assert ( ! isempty );
866 _vc_vacpage (page, Vvplast, archrel);
871 Assert ( --Vnpages > 0 );
872 /* get prev reapped page from Vvpl */
873 Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1];
874 Vblklast = Vvplast->vpd_blkno;
875 if ( blkno == Fblklast ) /* this page in Fvpl too */
877 Assert ( --Fnpages > 0 );
878 Assert ( Fvplast->vpd_nusd == 0 );
879 /* get prev reapped page from Fvpl */
880 Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1];
881 Fblklast = Fvplast->vpd_blkno;
883 Assert ( Fblklast <= Vblklast );
892 Assert ( ! isempty );
895 vpc->vpd_blkno = blkno;
896 maxoff = PageGetMaxOffsetNumber(page);
897 for (offnum = FirstOffsetNumber;
899 offnum = OffsetNumberNext(offnum))
901 itemid = PageGetItemId(page, offnum);
903 if (!ItemIdIsUsed(itemid))
906 htup = (HeapTuple) PageGetItem(page, itemid);
909 /* try to find new page for this tuple */
910 if ( ToBuf == InvalidBuffer ||
911 ! _vc_enough_space (ToVpd, tlen) )
913 if ( ToBuf != InvalidBuffer )
916 ToBuf = InvalidBuffer;
918 * If no one tuple can't be added to this page -
919 * remove page from Fvpl. - vadim 11/27/96
921 if ( !_vc_enough_space (ToVpd, curvrl->vrl_min_tlen) )
923 if ( ToVpd != Fvplast )
925 Assert ( Fnpages > ToVpI + 1 );
926 memmove (Fvpl->vpl_pgdesc + ToVpI,
927 Fvpl->vpl_pgdesc + ToVpI + 1,
928 sizeof (VPageDescr*) * (Fnpages - ToVpI - 1));
930 Assert ( Fnpages >= 1 );
934 /* get prev reapped page from Fvpl */
935 Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1];
936 Fblklast = Fvplast->vpd_blkno;
939 for (i=0; i < Fnpages; i++)
941 if ( _vc_enough_space (Fvpl->vpl_pgdesc[i], tlen) )
945 break; /* can't move item anywhere */
947 ToVpd = Fvpl->vpl_pgdesc[ToVpI];
948 ToBuf = ReadBuffer(onerel, ToVpd->vpd_blkno);
949 ToPage = BufferGetPage(ToBuf);
950 /* if this page was not used before - clean it */
951 if ( ! PageIsEmpty(ToPage) && ToVpd->vpd_nusd == 0 )
952 _vc_vacpage (ToPage, ToVpd, archrel);
956 newtup = (HeapTuple) palloc (tlen);
957 memmove((char *) newtup, (char *) htup, tlen);
959 /* store transaction information */
960 TransactionIdStore(myXID, &(newtup->t_xmin));
961 newtup->t_cmin = myCID;
962 StoreInvalidTransactionId(&(newtup->t_xmax));
963 newtup->t_tmin = INVALID_ABSTIME;
964 newtup->t_tmax = CURRENT_ABSTIME;
965 ItemPointerSetInvalid(&newtup->t_chain);
967 /* add tuple to the page */
968 newoff = PageAddItem (ToPage, (Item)newtup, tlen,
969 InvalidOffsetNumber, LP_USED);
970 if ( newoff == InvalidOffsetNumber )
973 failed to add item with len = %u to page %u (free space %u, nusd %u, noff %u)",
974 tlen, ToVpd->vpd_blkno, ToVpd->vpd_free,
975 ToVpd->vpd_nusd, ToVpd->vpd_noff);
977 newitemid = PageGetItemId(ToPage, newoff);
979 newtup = (HeapTuple) PageGetItem(ToPage, newitemid);
980 ItemPointerSet(&(newtup->t_ctid), ToVpd->vpd_blkno, newoff);
982 /* now logically delete end-tuple */
983 TransactionIdStore(myXID, &(htup->t_xmax));
984 htup->t_cmax = myCID;
985 memmove ((char*)&(htup->t_chain), (char*)&(newtup->t_ctid), sizeof (newtup->t_ctid));
989 ToVpd->vpd_free = ((PageHeader)ToPage)->pd_upper - ((PageHeader)ToPage)->pd_lower;
990 vpc->vpd_voff[vpc->vpd_noff++] = offnum;
992 /* insert index' tuples if needed */
993 if ( Irel != (Relation*) NULL )
995 for (i = 0, idcur = Idesc; i < nindices; i++, idcur++)
999 (AttrNumber *)&(idcur->tform->indkey[0]),
1006 iresult = index_insert (
1012 if (iresult) pfree(iresult);
1016 } /* walk along page */
1018 if ( vpc->vpd_noff > 0 ) /* some tuples were moved */
1020 _vc_reappage (&Nvpl, vpc);
1028 if ( offnum <= maxoff )
1029 break; /* some item(s) left */
1031 } /* walk along relation */
1033 blkno++; /* new number of blocks */
1035 if ( ToBuf != InvalidBuffer )
1037 Assert (nmoved > 0);
1044 * We have to commit our tuple' movings before we'll truncate
1045 * relation, but we shouldn't lose our locks. And so - quick hack:
1046 * flush buffers and record status of current transaction
1047 * as committed, and continue. - vadim 11/13/96
1049 FlushBufferPool(!TransactionFlushEnabled());
1050 TransactionIdCommit(myXID);
1051 FlushBufferPool(!TransactionFlushEnabled());
1052 myCTM = TransactionIdGetCommitTime(myXID);
1056 * Clean uncleaned reapped pages from Vvpl list
1057 * and set commit' times for inserted tuples
1060 for (i = 0, vpp = Vvpl->vpl_pgdesc; i < Vnpages; i++, vpp++)
1062 Assert ( (*vpp)->vpd_blkno < blkno );
1063 buf = ReadBuffer(onerel, (*vpp)->vpd_blkno);
1064 page = BufferGetPage(buf);
1065 if ( (*vpp)->vpd_nusd == 0 ) /* this page was not used */
1067 /* noff == 0 in empty pages only - such pages should be re-used */
1068 Assert ( (*vpp)->vpd_noff > 0 );
1069 _vc_vacpage (page, *vpp, archrel);
1071 else /* this page was used */
1074 moff = PageGetMaxOffsetNumber(page);
1075 for (newoff = FirstOffsetNumber;
1077 newoff = OffsetNumberNext(newoff))
1079 itemid = PageGetItemId(page, newoff);
1080 if (!ItemIdIsUsed(itemid))
1082 htup = (HeapTuple) PageGetItem(page, itemid);
1083 if ( TransactionIdEquals((TransactionId)htup->t_xmin, myXID) )
1085 htup->t_tmin = myCTM;
1089 Assert ( (*vpp)->vpd_nusd == ntups );
1094 Assert ( nmoved == nchkmvd );
1096 getrusage(RUSAGE_SELF, &ru1);
1098 elog (MESSLEV, "Rel %.*s: Pages: %u --> %u; Tuple(s) moved: %u. \
1099 Elapsed %u/%u sec.",
1100 NAMEDATALEN, (RelationGetRelationName(onerel))->data,
1101 nblocks, blkno, nmoved,
1102 ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec,
1103 ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
1105 if ( Nvpl.vpl_npages > 0 )
1107 /* vacuum indices again if needed */
1108 if ( Irel != (Relation*) NULL )
1110 VPageDescr *vpleft, *vpright, vpsave;
1112 /* re-sort Nvpl.vpl_pgdesc */
1113 for (vpleft = Nvpl.vpl_pgdesc,
1114 vpright = Nvpl.vpl_pgdesc + Nvpl.vpl_npages - 1;
1115 vpleft < vpright; vpleft++, vpright--)
1117 vpsave = *vpleft; *vpleft = *vpright; *vpright = vpsave;
1119 for (i = 0; i < nindices; i++)
1120 _vc_vaconeind (&Nvpl, Irel[i], curvrl->vrl_ntups);
1124 * clean moved tuples from last page in Nvpl list
1125 * if some tuples left there
1127 if ( vpc->vpd_noff > 0 && offnum <= maxoff )
1129 Assert (vpc->vpd_blkno == blkno - 1);
1130 buf = ReadBuffer(onerel, vpc->vpd_blkno);
1131 page = BufferGetPage (buf);
1134 for (offnum = FirstOffsetNumber;
1136 offnum = OffsetNumberNext(offnum))
1138 itemid = PageGetItemId(page, offnum);
1139 if (!ItemIdIsUsed(itemid))
1141 htup = (HeapTuple) PageGetItem(page, itemid);
1142 Assert ( TransactionIdEquals((TransactionId)htup->t_xmax, myXID) );
1143 itemid->lp_flags &= ~LP_USED;
1146 Assert ( vpc->vpd_noff == ntups );
1147 PageRepairFragmentation(page);
1151 /* now - free new list of reapped pages */
1152 vpp = Nvpl.vpl_pgdesc;
1153 for (i = 0; i < Nvpl.vpl_npages; i++, vpp++)
1155 pfree (Nvpl.vpl_pgdesc);
1158 /* truncate relation */
1159 if ( blkno < nblocks )
1161 blkno = smgrtruncate (onerel->rd_rel->relsmgr, onerel, blkno);
1162 Assert ( blkno >= 0 );
1163 curvrl->vrl_npages = blkno; /* set new number of blocks */
1166 if ( archrel != (Relation) NULL )
1167 heap_close(archrel);
1169 if ( Irel != (Relation*) NULL ) /* pfree index' allocations */
1174 _vc_clsindices (nindices, Irel);
1182 * _vc_vacheap() -- free dead tuples
1184 * This routine marks dead tuples as unused and truncates relation
1185 * if there are "empty" end-blocks.
1188 _vc_vacheap (VRelList curvrl, Relation onerel, VPageList Vvpl)
1197 nblocks = Vvpl->vpl_npages;
1198 /* if the relation has an archive, open it */
1199 if (onerel->rd_rel->relarch != 'n')
1200 archrel = _vc_getarchrel(onerel);
1203 archrel = (Relation) NULL;
1204 nblocks -= Vvpl->vpl_nemend; /* nothing to do with them */
1207 for (i = 0, vpp = Vvpl->vpl_pgdesc; i < nblocks; i++, vpp++)
1209 if ( (*vpp)->vpd_noff > 0 )
1211 buf = ReadBuffer(onerel, (*vpp)->vpd_blkno);
1212 page = BufferGetPage (buf);
1213 _vc_vacpage (page, *vpp, archrel);
1218 /* truncate relation if there are some empty end-pages */
1219 if ( Vvpl->vpl_nemend > 0 )
1221 Assert ( curvrl->vrl_npages >= Vvpl->vpl_nemend );
1222 nblocks = curvrl->vrl_npages - Vvpl->vpl_nemend;
1223 elog (MESSLEV, "Rel %.*s: Pages: %u --> %u.",
1224 NAMEDATALEN, (RelationGetRelationName(onerel))->data,
1225 curvrl->vrl_npages, nblocks);
1228 * we have to flush "empty" end-pages (if changed, but who knows it)
1231 FlushBufferPool(!TransactionFlushEnabled());
1233 nblocks = smgrtruncate (onerel->rd_rel->relsmgr, onerel, nblocks);
1234 Assert ( nblocks >= 0 );
1235 curvrl->vrl_npages = nblocks; /* set new number of blocks */
1238 if ( archrel != (Relation) NULL )
1239 heap_close(archrel);
1244 * _vc_vacpage() -- free (and archive if needed) dead tuples on a page
1245 * and repaire its fragmentation.
1248 _vc_vacpage (Page page, VPageDescr vpd, Relation archrel)
1254 Assert ( vpd->vpd_nusd == 0 );
1255 for (i=0; i < vpd->vpd_noff; i++)
1257 itemid = &(((PageHeader) page)->pd_linp[vpd->vpd_voff[i] - 1]);
1258 if ( archrel != (Relation) NULL && ItemIdIsUsed(itemid) )
1260 htup = (HeapTuple) PageGetItem (page, itemid);
1261 _vc_archive (archrel, htup);
1263 itemid->lp_flags &= ~LP_USED;
1265 PageRepairFragmentation(page);
1270 * _vc_vaconeind() -- vacuum one index relation.
1272 * Vpl is the VPageList of the heap we're currently vacuuming.
1273 * It's locked. Indrel is an index relation on the vacuumed heap.
1274 * We don't set locks on the index relation here, since the indexed
1275 * access methods support locking at different granularities.
1276 * We let them handle it.
1278 * Finally, we arrange to update the index relation's statistics in
1282 _vc_vaconeind(VPageList vpl, Relation indrel, int nhtups)
1284 RetrieveIndexResult res;
1285 IndexScanDesc iscan;
1286 ItemPointer heapptr;
1291 struct rusage ru0, ru1;
1293 getrusage(RUSAGE_SELF, &ru0);
1295 /* walk through the entire index */
1296 iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
1300 while ((res = index_getnext(iscan, ForwardScanDirection))
1301 != (RetrieveIndexResult) NULL) {
1302 heapptr = &res->heap_iptr;
1304 if ( (vp = _vc_tidreapped (heapptr, vpl)) != (VPageDescr) NULL)
1307 elog(DEBUG, "<%x,%x> -> <%x,%x>",
1308 ItemPointerGetBlockNumber(&(res->index_iptr)),
1309 ItemPointerGetOffsetNumber(&(res->index_iptr)),
1310 ItemPointerGetBlockNumber(&(res->heap_iptr)),
1311 ItemPointerGetOffsetNumber(&(res->heap_iptr)));
1313 if ( vp->vpd_noff == 0 )
1314 { /* this is EmptyPage !!! */
1315 elog (NOTICE, "Ind %.*s: pointer to EmptyPage (blk %u off %u) - fixing",
1316 NAMEDATALEN, indrel->rd_rel->relname.data,
1317 vp->vpd_blkno, ItemPointerGetOffsetNumber(heapptr));
1320 index_delete(indrel, &res->index_iptr);
1329 index_endscan(iscan);
1331 /* now update statistics in pg_class */
1332 nipages = RelationGetNumberOfBlocks(indrel);
1333 _vc_updstats(indrel->rd_id, nipages, nitups, false);
1335 getrusage(RUSAGE_SELF, &ru1);
1337 elog (MESSLEV, "Ind %.*s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.",
1338 NAMEDATALEN, indrel->rd_rel->relname.data, nipages, nitups, nvac,
1339 ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec,
1340 ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
1342 if ( nitups != nhtups )
1343 elog (NOTICE, "NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)",
1346 } /* _vc_vaconeind */
1349 * _vc_tidreapped() -- is a particular tid reapped?
1351 * vpl->VPageDescr_array is sorted in right order.
1354 _vc_tidreapped(ItemPointer itemptr, VPageList vpl)
1356 OffsetNumber ioffno;
1358 VPageDescr vp, *vpp;
1361 vpd.vpd_blkno = ItemPointerGetBlockNumber(itemptr);
1362 ioffno = ItemPointerGetOffsetNumber(itemptr);
1365 vpp = (VPageDescr*) _vc_find_eq ((char*)(vpl->vpl_pgdesc),
1366 vpl->vpl_npages, sizeof (VPageDescr), (char*)&vp,
1369 if ( vpp == (VPageDescr*) NULL )
1370 return ((VPageDescr)NULL);
1373 /* ok - we are on true page */
1375 if ( vp->vpd_noff == 0 ) { /* this is EmptyPage !!! */
1379 voff = (OffsetNumber*) _vc_find_eq ((char*)(vp->vpd_voff),
1380 vp->vpd_noff, sizeof (OffsetNumber), (char*)&ioffno,
1383 if ( voff == (OffsetNumber*) NULL )
1384 return ((VPageDescr)NULL);
1388 } /* _vc_tidreapped */
1391 * _vc_updstats() -- update pg_class statistics for one relation
1393 * This routine works for both index and heap relation entries in
1394 * pg_class. We violate no-overwrite semantics here by storing new
1395 * values for ntuples, npages, and hasindex directly in the pg_class
1396 * tuple that's already on the page. The reason for this is that if
1397 * we updated these tuples in the usual way, then every tuple in pg_class
1398 * would be replaced every day. This would make planning and executing
1399 * historical queries very expensive.
1402 _vc_updstats(Oid relid, int npages, int ntuples, bool hasindex)
1408 Form_pg_class pgcform;
1412 * update number of tuples and number of pages in pg_class
1414 ScanKeyEntryInitialize(&skey, 0x0, ObjectIdAttributeNumber,
1415 ObjectIdEqualRegProcedure,
1416 ObjectIdGetDatum(relid));
1418 rd = heap_openr(RelationRelationName);
1419 sdesc = heap_beginscan(rd, false, NowTimeQual, 1, &skey);
1421 if (!HeapTupleIsValid(tup = heap_getnext(sdesc, 0, &buf)))
1422 elog(WARN, "pg_class entry for relid %d vanished during vacuuming",
1425 /* overwrite the existing statistics in the tuple */
1426 _vc_setpagelock(rd, BufferGetBlockNumber(buf));
1427 pgcform = (Form_pg_class) GETSTRUCT(tup);
1428 pgcform->reltuples = ntuples;
1429 pgcform->relpages = npages;
1430 pgcform->relhasindex = hasindex;
1432 /* XXX -- after write, should invalidate relcache in other backends */
1433 WriteNoReleaseBuffer(buf); /* heap_endscan release scan' buffers ? */
1435 /* invalidating system relations confuses the function cache
1436 of pg_operator and pg_opclass */
1437 if ( !IsSystemRelationName(pgcform->relname.data))
1438 RelationInvalidateHeapTuple(rd, tup);
1440 /* that's all, folks */
1441 heap_endscan(sdesc);
1446 static void _vc_setpagelock(Relation rel, BlockNumber blkno)
1448 ItemPointerData itm;
1450 ItemPointerSet(&itm, blkno, 1);
1452 RelationSetLockForWritePage(rel, &itm);
1457 * _vc_reappage() -- save a page on the array of reapped pages.
1459 * As a side effect of the way that the vacuuming loop for a given
1460 * relation works, higher pages come after lower pages in the array
1461 * (and highest tid on a page is last).
1464 _vc_reappage(VPageList vpl, VPageDescr vpc)
1468 /* allocate a VPageDescrData entry */
1469 newvpd = (VPageDescr) palloc(sizeof(VPageDescrData) + vpc->vpd_noff*sizeof(OffsetNumber));
1472 if ( vpc->vpd_noff > 0 )
1473 memmove (newvpd->vpd_voff, vpc->vpd_voff, vpc->vpd_noff*sizeof(OffsetNumber));
1474 newvpd->vpd_blkno = vpc->vpd_blkno;
1475 newvpd->vpd_free = vpc->vpd_free;
1476 newvpd->vpd_nusd = vpc->vpd_nusd;
1477 newvpd->vpd_noff = vpc->vpd_noff;
1479 /* insert this page into vpl list */
1480 _vc_vpinsert (vpl, newvpd);
1482 } /* _vc_reappage */
1485 _vc_vpinsert (VPageList vpl, VPageDescr vpnew)
1488 /* allocate a VPageDescr entry if needed */
1489 if ( vpl->vpl_npages == 0 )
1490 vpl->vpl_pgdesc = (VPageDescr*) palloc(100*sizeof(VPageDescr));
1491 else if ( vpl->vpl_npages % 100 == 0 )
1492 vpl->vpl_pgdesc = (VPageDescr*) repalloc(vpl->vpl_pgdesc, (vpl->vpl_npages+100)*sizeof(VPageDescr));
1493 vpl->vpl_pgdesc[vpl->vpl_npages] = vpnew;
1494 (vpl->vpl_npages)++;
1499 _vc_free(Portal p, VRelList vrl)
1502 VAttList p_val, val;
1504 PortalVariableMemory pmem;
1506 pmem = PortalGetVariableMemory(p);
1507 old = MemoryContextSwitchTo((MemoryContext)pmem);
1509 while (vrl != (VRelList) NULL) {
1511 /* free attribute list */
1512 val = vrl->vrl_attlist;
1513 while (val != (VAttList) NULL) {
1515 val = val->val_next;
1519 /* free rel list entry */
1521 vrl = vrl->vrl_next;
1525 (void) MemoryContextSwitchTo(old);
1529 * _vc_getarchrel() -- open the archive relation for a heap relation
1531 * The archive relation is named 'a,XXXXX' for the heap relation
1532 * whose relid is XXXXX.
1535 #define ARCHIVE_PREFIX "a,"
1538 _vc_getarchrel(Relation heaprel)
1543 archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */
1544 sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id);
1546 archrel = heap_openr(archrelname);
1553 * _vc_archive() -- write a tuple to an archive relation
1555 * In the future, this will invoke the archived accessd method. For
1556 * now, archive relations are on mag disk.
1559 _vc_archive(Relation archrel, HeapTuple htup)
1561 doinsert(archrel, htup);
1565 _vc_isarchrel(char *rname)
1567 if (strncmp(ARCHIVE_PREFIX, rname,strlen(ARCHIVE_PREFIX)) == 0)
1574 _vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *))
1577 int last = nelem - 1;
1578 int celm = nelem / 2;
1579 bool last_move, first_move;
1581 last_move = first_move = true;
1584 if ( first_move == true )
1586 res = compar (bot, elm);
1593 if ( last_move == true )
1595 res = compar (elm, bot + last*size);
1599 return (bot + last*size);
1602 res = compar (elm, bot + celm*size);
1604 return (bot + celm*size);
1618 last = last - celm - 1;
1619 bot = bot + (celm+1)*size;
1620 celm = (last + 1) / 2;
1627 _vc_cmp_blk (char *left, char *right)
1629 BlockNumber lblk, rblk;
1631 lblk = (*((VPageDescr*)left))->vpd_blkno;
1632 rblk = (*((VPageDescr*)right))->vpd_blkno;
1643 _vc_cmp_offno (char *left, char *right)
1646 if ( *(OffsetNumber*)left < *(OffsetNumber*)right )
1648 if ( *(OffsetNumber*)left == *(OffsetNumber*)right )
1652 } /* _vc_cmp_offno */
1656 _vc_getindices (Oid relid, int *nindices, Relation **Irel)
1662 HeapScanDesc pgiscan;
1671 ioid = (Oid *) palloc(10*sizeof(Oid));
1673 /* prepare a heap scan on the pg_index relation */
1674 pgindex = heap_openr(IndexRelationName);
1675 pgidesc = RelationGetTupleDescriptor(pgindex);
1677 ScanKeyEntryInitialize(&pgikey, 0x0, Anum_pg_index_indrelid,
1678 ObjectIdEqualRegProcedure,
1679 ObjectIdGetDatum(relid));
1681 pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey);
1683 while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, NULL))) {
1684 d = (Datum) heap_getattr(pgitup, InvalidBuffer, Anum_pg_index_indexrelid,
1688 ioid = (Oid *) repalloc(ioid, (i+10)*sizeof(Oid));
1689 ioid[i-1] = DatumGetObjectId(d);
1692 heap_endscan(pgiscan);
1693 heap_close(pgindex);
1695 if ( i == 0 ) { /* No one index found */
1700 if ( Irel != (Relation **) NULL )
1701 *Irel = (Relation *) palloc(i * sizeof(Relation));
1703 for (k = 0; i > 0; )
1705 irel = index_open(ioid[--i]);
1706 if ( irel != (Relation) NULL )
1708 if ( Irel != (Relation **) NULL )
1715 elog (NOTICE, "CAN't OPEN INDEX %u - SKIP IT", ioid[i]);
1720 if ( Irel != (Relation **) NULL && *nindices == 0 )
1723 *Irel = (Relation *) NULL;
1726 } /* _vc_getindices */
1730 _vc_clsindices (int nindices, Relation *Irel)
1733 if ( Irel == (Relation*) NULL )
1736 while (nindices--) {
1737 index_close (Irel[nindices]);
1741 } /* _vc_clsindices */
1745 _vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc)
1748 HeapTuple pgIndexTup;
1749 AttrNumber *attnumP;
1753 *Idesc = (IndDesc *) palloc (nindices * sizeof (IndDesc));
1755 for (i = 0, idcur = *Idesc; i < nindices; i++, idcur++) {
1757 SearchSysCacheTuple(INDEXRELID,
1758 ObjectIdGetDatum(Irel[i]->rd_id),
1761 idcur->tform = (IndexTupleForm)GETSTRUCT(pgIndexTup);
1762 for (attnumP = &(idcur->tform->indkey[0]), natts = 0;
1763 *attnumP != InvalidAttrNumber && natts != INDEX_MAX_KEYS;
1764 attnumP++, natts++);
1765 if (idcur->tform->indproc != InvalidOid) {
1766 idcur->finfoP = &(idcur->finfo);
1767 FIgetnArgs(idcur->finfoP) = natts;
1769 FIgetProcOid(idcur->finfoP) = idcur->tform->indproc;
1770 *(FIgetname(idcur->finfoP)) = '\0';
1772 idcur->finfoP = (FuncIndexInfo *) NULL;
1774 idcur->natts = natts;
1777 } /* _vc_mkindesc */
1781 _vc_enough_space (VPageDescr vpd, Size len)
1784 len = DOUBLEALIGN(len);
1786 if ( len > vpd->vpd_free )
1789 if ( vpd->vpd_nusd < vpd->vpd_noff ) /* there are free itemid(s) */
1790 return (true); /* and len <= free_space */
1792 /* ok. noff_usd >= noff_free and so we'll have to allocate new itemid */
1793 if ( len <= vpd->vpd_free - sizeof (ItemIdData) )
1798 } /* _vc_enough_space */