diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/commands/vacuum.c | |
download | postgresql-PG95-1_01.tar.gz postgresql-PG95-1_01.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/commands/vacuum.c')
-rw-r--r-- | src/backend/commands/vacuum.c | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c new file mode 100644 index 00000000000..7e1514cd2a3 --- /dev/null +++ b/src/backend/commands/vacuum.c @@ -0,0 +1,853 @@ +/*------------------------------------------------------------------------- + * + * vacuum.c-- + * the postgres vacuum cleaner + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <sys/file.h> + +#include "postgres.h" +#include "utils/portal.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "access/xact.h" +#include "storage/bufmgr.h" +#include "access/transam.h" +#include "utils/tqual.h" +#include "access/htup.h" + +#include "catalog/pg_index.h" +#include "catalog/catname.h" +#include "catalog/pg_class.h" +#include "catalog/pg_proc.h" + +#include "storage/fd.h" /* for O_ */ +#include "storage/itemid.h" +#include "storage/bufmgr.h" +#include "storage/bufpage.h" +#include "storage/smgr.h" + +#include "utils/elog.h" +#include "utils/mcxt.h" +#include "utils/palloc.h" + +#include "commands/vacuum.h" + +bool VacuumRunning = false; + +/* non-export function prototypes */ +static void _vc_init(char *vacrel); +static void _vc_shutdown(char *vacrel); +static void _vc_vacuum(char *vacrel); +static VRelList _vc_getrels(Portal p, char *vacrel); +static void _vc_vacone(Portal p, VRelList curvrl); +static void _vc_vacheap(Portal p, VRelList curvrl, Relation onerel); +static void _vc_vacindices(VRelList curvrl, Relation onerel); +static void _vc_vaconeind(VRelList curvrl, Relation indrel); +static void _vc_updstats(Oid relid, int npages, int ntuples, bool hasindex); +static void _vc_setpagelock(Relation rel, BlockNumber blkno); +static bool _vc_ontidlist(ItemPointer itemptr, VTidList tidlist); +static void _vc_reaptid(Portal p, VRelList curvrl, BlockNumber blkno, + OffsetNumber offnum); +static void _vc_free(Portal p, VRelList vrl); +static Relation _vc_getarchrel(Relation heaprel); +static void _vc_archive(Relation archrel, HeapTuple htup); +static bool _vc_isarchrel(char *rname); + +void +vacuum(char *vacrel) +{ + /* initialize vacuum cleaner */ + _vc_init(vacrel); + + /* vacuum the database */ + _vc_vacuum(vacrel); + + /* clean up */ + _vc_shutdown(vacrel); +} + +/* + * _vc_init(), _vc_shutdown() -- start up and shut down the vacuum cleaner. + * + * We run exactly one vacuum cleaner at a time. We use the file system + * to guarantee an exclusive lock on vacuuming, since a single vacuum + * cleaner instantiation crosses transaction boundaries, and we'd lose + * postgres-style locks at the end of every transaction. + * + * The strangeness with committing and starting transactions in the + * init and shutdown routines is due to the fact that the vacuum cleaner + * is invoked via a sql command, and so is already executing inside + * a transaction. We need to leave ourselves in a predictable state + * on entry and exit to the vacuum cleaner. We commit the transaction + * started in PostgresMain() inside _vc_init(), and start one in + * _vc_shutdown() to match the commit waiting for us back in + * PostgresMain(). + */ +static void +_vc_init(char *vacrel) +{ + int fd; + + if ((fd = open("pg_vlock", O_CREAT|O_EXCL, 0600)) < 0) + elog(WARN, "can't create lock file -- another vacuum cleaner running?"); + + close(fd); + + /* + * By here, exclusive open on the lock file succeeded. If we abort + * for any reason during vacuuming, we need to remove the lock file. + * This global variable is checked in the transaction manager on xact + * abort, and the routine vc_abort() is called if necessary. + */ + + VacuumRunning = true; + + /* matches the StartTransaction in PostgresMain() */ + CommitTransactionCommand(); +} + +static void +_vc_shutdown(char *vacrel) +{ + /* on entry, not in a transaction */ + if (unlink("pg_vlock") < 0) + elog(WARN, "vacuum: can't destroy lock file!"); + + /* okay, we're done */ + VacuumRunning = false; + + /* matches the CommitTransaction in PostgresMain() */ + StartTransactionCommand(); +} + +void +vc_abort() +{ + /* on abort, remove the vacuum cleaner lock file */ + (void) unlink("pg_vlock"); + + VacuumRunning = false; +} + +/* + * _vc_vacuum() -- vacuum the database. + * + * This routine builds a list of relations to vacuum, and then calls + * code that vacuums them one at a time. We are careful to vacuum each + * relation in a separate transaction in order to avoid holding too many + * locks at one time. + */ +static void +_vc_vacuum(char *vacrel) +{ + VRelList vrl, cur; + char *pname; + Portal p; + + /* + * Create a portal for safe memory across transctions. We need to + * palloc the name space for it because our hash function expects + * the name to be on a longword boundary. CreatePortal copies the + * name to safe storage for us. + */ + + pname = (char *) palloc(strlen(VACPNAME) + 1); + strcpy(pname, VACPNAME); + p = CreatePortal(pname); + pfree(pname); + + /* get list of relations */ + vrl = _vc_getrels(p, vacrel); + + /* vacuum each heap relation */ + for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) + _vc_vacone(p, cur); + + _vc_free(p, vrl); + + PortalDestroy(&p); +} + +static VRelList +_vc_getrels(Portal p, char *vacrel) +{ + Relation pgclass; + TupleDesc pgcdesc; + HeapScanDesc pgcscan; + HeapTuple pgctup; + Buffer buf; + PortalVariableMemory portalmem; + MemoryContext old; + VRelList vrl, cur; + Datum d; + char *rname; + int16 smgrno; + bool n; + ScanKeyData pgckey; + + StartTransactionCommand(); + + if (vacrel) { + ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname, + NameEqualRegProcedure, + PointerGetDatum(vacrel)); + } else { + ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind, + CharacterEqualRegProcedure, CharGetDatum('r')); + } + + portalmem = PortalGetVariableMemory(p); + vrl = (VRelList) NULL; + + pgclass = heap_openr(RelationRelationName); + pgcdesc = RelationGetTupleDescriptor(pgclass); + + pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey); + + while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &buf))) { + + /* + * We have to be careful not to vacuum the archive (since it + * already contains vacuumed tuples), and not to vacuum + * relations on write-once storage managers like the Sony + * jukebox at Berkeley. + */ + + d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname, + pgcdesc, &n); + rname = (char*)d; + + /* skip archive relations */ + if (_vc_isarchrel(rname)) { + ReleaseBuffer(buf); + continue; + } + + d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relsmgr, + pgcdesc, &n); + smgrno = DatumGetInt16(d); + + /* skip write-once storage managers */ + if (smgriswo(smgrno)) { + ReleaseBuffer(buf); + continue; + } + + /* get a relation list entry for this guy */ + old = MemoryContextSwitchTo((MemoryContext)portalmem); + if (vrl == (VRelList) NULL) { + vrl = cur = (VRelList) palloc(sizeof(VRelListData)); + } else { + cur->vrl_next = (VRelList) palloc(sizeof(VRelListData)); + cur = cur->vrl_next; + } + (void) MemoryContextSwitchTo(old); + + cur->vrl_relid = pgctup->t_oid; + cur->vrl_attlist = (VAttList) NULL; + cur->vrl_tidlist = (VTidList) NULL; + cur->vrl_npages = cur->vrl_ntups = 0; + cur->vrl_hasindex = false; + cur->vrl_next = (VRelList) NULL; + + /* wei hates it if you forget to do this */ + ReleaseBuffer(buf); + } + + heap_close(pgclass); + heap_endscan(pgcscan); + + CommitTransactionCommand(); + + return (vrl); +} + +/* + * _vc_vacone() -- vacuum one heap relation + * + * This routine vacuums a single heap, cleans out its indices, and + * updates its statistics npages and ntuples statistics. + * + * Doing one heap at a time incurs extra overhead, since we need to + * check that the heap exists again just before we vacuum it. The + * reason that we do this is so that vacuuming can be spread across + * many small transactions. Otherwise, two-phase locking would require + * us to lock the entire database during one pass of the vacuum cleaner. + */ +static void +_vc_vacone(Portal p, VRelList curvrl) +{ + Relation pgclass; + TupleDesc pgcdesc; + HeapTuple pgctup; + Buffer pgcbuf; + HeapScanDesc pgcscan; + Relation onerel; + ScanKeyData pgckey; + + StartTransactionCommand(); + + ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(curvrl->vrl_relid)); + + pgclass = heap_openr(RelationRelationName); + pgcdesc = RelationGetTupleDescriptor(pgclass); + pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey); + + /* + * Race condition -- if the pg_class tuple has gone away since the + * last time we saw it, we don't need to vacuum it. + */ + + if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &pgcbuf))) { + heap_endscan(pgcscan); + heap_close(pgclass); + CommitTransactionCommand(); + return; + } + + /* now open the class and vacuum it */ + onerel = heap_open(curvrl->vrl_relid); + + /* we require the relation to be locked until the indices are cleaned */ + RelationSetLockForWrite(onerel); + + /* vacuum it */ + _vc_vacheap(p, curvrl, onerel); + + /* if we vacuumed any heap tuples, vacuum the indices too */ + if (curvrl->vrl_tidlist != (VTidList) NULL) + _vc_vacindices(curvrl, onerel); + else + curvrl->vrl_hasindex = onerel->rd_rel->relhasindex; + + /* all done with this class */ + heap_close(onerel); + heap_endscan(pgcscan); + heap_close(pgclass); + + /* update statistics in pg_class */ + _vc_updstats(curvrl->vrl_relid, curvrl->vrl_npages, curvrl->vrl_ntups, + curvrl->vrl_hasindex); + + CommitTransactionCommand(); +} + +/* + * _vc_vacheap() -- vacuum an open heap relation + * + * This routine sets commit times, vacuums dead tuples, cleans up + * wasted space on the page, and maintains statistics on the number + * of live tuples in a heap. In addition, it records the tids of + * all tuples removed from the heap for any reason. These tids are + * used in a scan of indices on the relation to get rid of dead + * index tuples. + */ +static void +_vc_vacheap(Portal p, VRelList curvrl, Relation onerel) +{ + int nblocks, blkno; + ItemId itemid; + HeapTuple htup; + Buffer buf; + Page page; + OffsetNumber offnum, maxoff; + Relation archrel; + bool isarchived; + int nvac; + int ntups; + bool pgchanged, tupgone; + AbsoluteTime purgetime, expiretime; + RelativeTime preservetime; + + nvac = 0; + ntups = 0; + nblocks = RelationGetNumberOfBlocks(onerel); + + { + char *relname; + relname = (RelationGetRelationName(onerel))->data; + + if ( (strlen(relname) > 4) && + relname[0] == 'X' && + relname[1] == 'i' && + relname[2] == 'n' && + (relname[3] == 'v' || relname[3] == 'x')) + return; + } + + + /* if the relation has an archive, open it */ + if (onerel->rd_rel->relarch != 'n') { + isarchived = true; + archrel = _vc_getarchrel(onerel); + } else + isarchived = false; + + /* don't vacuum large objects for now. + something breaks when we do*/ + { + char *relname; + relname = (RelationGetRelationName(onerel))->data; + + if ( (strlen(relname) > 4) && + relname[0] == 'X' && + relname[1] == 'i' && + relname[2] == 'n' && + (relname[3] == 'v' || relname[3] == 'x')) + return; + } + + /* calculate the purge time: tuples that expired before this time + will be archived or deleted */ + purgetime = GetCurrentTransactionStartTime(); + expiretime = (AbsoluteTime)onerel->rd_rel->relexpires; + preservetime = (RelativeTime)onerel->rd_rel->relpreserved; + + if (RelativeTimeIsValid(preservetime) && (preservetime)) { + purgetime -= preservetime; + if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime) && + expiretime > purgetime) + purgetime = expiretime; + } + + else if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime)) + purgetime = expiretime; + + for (blkno = 0; blkno < nblocks; blkno++) { + buf = ReadBuffer(onerel, blkno); + page = BufferGetPage(buf); + + if (PageIsEmpty(page)) { + ReleaseBuffer(buf); + continue; + } + + pgchanged = false; + maxoff = PageGetMaxOffsetNumber(page); + for (offnum = FirstOffsetNumber; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) { + itemid = PageGetItemId(page, offnum); + + if (!ItemIdIsUsed(itemid)) + continue; + + htup = (HeapTuple) PageGetItem(page, itemid); + tupgone = false; + + if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) && + TransactionIdIsValid((TransactionId)htup->t_xmin)) { + + if (TransactionIdDidAbort(htup->t_xmin)) { + _vc_reaptid(p, curvrl, blkno, offnum); + pgchanged = true; + tupgone = true; + } else if (TransactionIdDidCommit(htup->t_xmin)) { + htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin); + pgchanged = true; + } + } + + if (TransactionIdIsValid((TransactionId)htup->t_xmax)) { + if (TransactionIdDidAbort(htup->t_xmax)) { + StoreInvalidTransactionId(&(htup->t_xmax)); + pgchanged = true; + } else if (TransactionIdDidCommit(htup->t_xmax)) { + if (!AbsoluteTimeIsBackwardCompatiblyReal(htup->t_tmax)) { + + htup->t_tmax = TransactionIdGetCommitTime(htup->t_xmax); + pgchanged = true; + } + + /* + * Reap the dead tuple if its expiration time is + * before purgetime. + */ + + if (!tupgone && htup->t_tmax < purgetime) { + _vc_reaptid(p, curvrl, blkno, offnum); + tupgone = true; + pgchanged = true; + } + } + } + + if (tupgone) { + ItemId lpp = &(((PageHeader) page)->pd_linp[offnum - 1]); + + /* write the tuple to the archive, if necessary */ + if (isarchived) + _vc_archive(archrel, htup); + + /* mark it unused */ + lpp->lp_flags &= ~LP_USED; + + ++nvac; + } else { + ntups++; + } + } + + if (pgchanged) { + PageRepairFragmentation(page); + WriteBuffer(buf); + } else { + ReleaseBuffer(buf); + } + } + + if (isarchived) + heap_close(archrel); + + /* save stats in the rel list for use later */ + curvrl->vrl_ntups = ntups; + curvrl->vrl_npages = nblocks; +} + +/* + * _vc_vacindices() -- vacuum all the indices for a particular heap relation. + * + * On entry, curvrl points at the relation currently being vacuumed. + * We already have a write lock on the relation, so we don't need to + * worry about anyone building an index on it while we're doing the + * vacuuming. The tid list for curvrl is sorted in reverse tid order: + * that is, tids on higher page numbers are before those on lower page + * numbers, and tids high on the page are before those low on the page. + * We use this ordering to cut down the search cost when we look at an + * index entry. + * + * We're executing inside the transaction that vacuumed the heap. + */ +static void +_vc_vacindices(VRelList curvrl, Relation onerel) +{ + Relation pgindex; + TupleDesc pgidesc; + HeapTuple pgitup; + HeapScanDesc pgiscan; + Buffer buf; + Relation indrel; + Oid indoid; + Datum d; + bool n; + int nindices; + ScanKeyData pgikey; + + /* see if we can dodge doing any work at all */ + if (!(onerel->rd_rel->relhasindex)) + return; + + nindices = 0; + + /* prepare a heap scan on the pg_index relation */ + pgindex = heap_openr(IndexRelationName); + pgidesc = RelationGetTupleDescriptor(pgindex); + + ScanKeyEntryInitialize(&pgikey, 0x0, Anum_pg_index_indrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(curvrl->vrl_relid)); + + pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey); + + /* vacuum all the indices */ + while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, &buf))) { + d = (Datum) heap_getattr(pgitup, buf, Anum_pg_index_indexrelid, + pgidesc, &n); + indoid = DatumGetObjectId(d); + indrel = index_open(indoid); + _vc_vaconeind(curvrl, indrel); + heap_close(indrel); + nindices++; + } + + heap_endscan(pgiscan); + heap_close(pgindex); + + if (nindices > 0) + curvrl->vrl_hasindex = true; + else + curvrl->vrl_hasindex = false; +} + +/* + * _vc_vaconeind() -- vacuum one index relation. + * + * Curvrl is the VRelList entry for the heap we're currently vacuuming. + * It's locked. The vrl_tidlist entry in curvrl is the list of deleted + * heap tids, sorted in reverse (page, offset) order. Onerel is an + * index relation on the vacuumed heap. We don't set locks on the index + * relation here, since the indexed access methods support locking at + * different granularities. We let them handle it. + * + * Finally, we arrange to update the index relation's statistics in + * pg_class. + */ +static void +_vc_vaconeind(VRelList curvrl, Relation indrel) +{ + RetrieveIndexResult res; + IndexScanDesc iscan; + ItemPointer heapptr; + int nvac; + int nitups; + int nipages; + + /* walk through the entire index */ + iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); + nvac = 0; + nitups = 0; + + while ((res = index_getnext(iscan, ForwardScanDirection)) + != (RetrieveIndexResult) NULL) { + heapptr = &res->heap_iptr; + + if (_vc_ontidlist(heapptr, curvrl->vrl_tidlist)) { +#if 0 + elog(DEBUG, "<%x,%x> -> <%x,%x>", + ItemPointerGetBlockNumber(&(res->index_iptr)), + ItemPointerGetOffsetNumber(&(res->index_iptr)), + ItemPointerGetBlockNumber(&(res->heap_iptr)), + ItemPointerGetOffsetNumber(&(res->heap_iptr))); +#endif + ++nvac; + index_delete(indrel, &res->index_iptr); + } else { + nitups++; + } + + /* be tidy */ + pfree(res); + } + + index_endscan(iscan); + + /* now update statistics in pg_class */ + nipages = RelationGetNumberOfBlocks(indrel); + _vc_updstats(indrel->rd_id, nipages, nitups, false); +} + +/* + * _vc_updstats() -- update pg_class statistics for one relation + * + * This routine works for both index and heap relation entries in + * pg_class. We violate no-overwrite semantics here by storing new + * values for ntuples, npages, and hasindex directly in the pg_class + * tuple that's already on the page. The reason for this is that if + * we updated these tuples in the usual way, then every tuple in pg_class + * would be replaced every day. This would make planning and executing + * historical queries very expensive. + */ +static void +_vc_updstats(Oid relid, int npages, int ntuples, bool hasindex) +{ + Relation rd; + HeapScanDesc sdesc; + HeapTuple tup; + Buffer buf; + Form_pg_class pgcform; + ScanKeyData skey; + + /* + * update number of tuples and number of pages in pg_class + */ + ScanKeyEntryInitialize(&skey, 0x0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + + rd = heap_openr(RelationRelationName); + sdesc = heap_beginscan(rd, false, NowTimeQual, 1, &skey); + + if (!HeapTupleIsValid(tup = heap_getnext(sdesc, 0, &buf))) + elog(WARN, "pg_class entry for relid %d vanished during vacuuming", + relid); + + /* overwrite the existing statistics in the tuple */ + _vc_setpagelock(rd, BufferGetBlockNumber(buf)); + pgcform = (Form_pg_class) GETSTRUCT(tup); + pgcform->reltuples = ntuples; + pgcform->relpages = npages; + pgcform->relhasindex = hasindex; + + /* XXX -- after write, should invalidate relcache in other backends */ + WriteNoReleaseBuffer(buf); + + /* that's all, folks */ + heap_endscan(sdesc); + heap_close(rd); + +} + +static void _vc_setpagelock(Relation rel, BlockNumber blkno) +{ + ItemPointerData itm; + + ItemPointerSet(&itm, blkno, 1); + + RelationSetLockForWritePage(rel, &itm); +} + +/* + * _vc_ontidlist() -- is a particular tid on the supplied tid list? + * + * Tidlist is sorted in reverse (page, offset) order. + */ +static bool +_vc_ontidlist(ItemPointer itemptr, VTidList tidlist) +{ + BlockNumber ibkno; + OffsetNumber ioffno; + ItemPointer check; + BlockNumber ckbkno; + OffsetNumber ckoffno; + + ibkno = ItemPointerGetBlockNumber(itemptr); + ioffno = ItemPointerGetOffsetNumber(itemptr); + + while (tidlist != (VTidList) NULL) { + check = &(tidlist->vtl_tid); + ckbkno = ItemPointerGetBlockNumber(check); + ckoffno = ItemPointerGetOffsetNumber(check); + + /* see if we've looked far enough down the list */ + if ((ckbkno < ibkno) || (ckbkno == ibkno && ckoffno < ioffno)) + return (false); + + /* see if we have a match */ + if (ckbkno == ibkno && ckoffno == ioffno) + return (true); + + /* check next */ + tidlist = tidlist->vtl_next; + } + + /* ran off the end of the list without finding a match */ + return (false); +} + +/* + * _vc_reaptid() -- save a tid on the list of reaped tids for the current + * entry on the vacuum relation list. + * + * As a side effect of the way that the vacuuming loop for a given + * relation works, the tids of vacuumed tuples wind up in reverse + * order in the list -- highest tid on a page is first, and higher + * pages come before lower pages. This is important later when we + * vacuum the indices, as it gives us a way of stopping the search + * for a tid if we notice we've passed the page it would be on. + */ +static void +_vc_reaptid(Portal p, + VRelList curvrl, + BlockNumber blkno, + OffsetNumber offnum) +{ + PortalVariableMemory pmem; + MemoryContext old; + VTidList newvtl; + + /* allocate a VTidListData entry in the portal memory context */ + pmem = PortalGetVariableMemory(p); + old = MemoryContextSwitchTo((MemoryContext) pmem); + newvtl = (VTidList) palloc(sizeof(VTidListData)); + MemoryContextSwitchTo(old); + + /* fill it in */ + ItemPointerSet(&(newvtl->vtl_tid), blkno, offnum); + newvtl->vtl_next = curvrl->vrl_tidlist; + curvrl->vrl_tidlist = newvtl; +} + +static void +_vc_free(Portal p, VRelList vrl) +{ + VRelList p_vrl; + VAttList p_val, val; + VTidList p_vtl, vtl; + MemoryContext old; + PortalVariableMemory pmem; + + pmem = PortalGetVariableMemory(p); + old = MemoryContextSwitchTo((MemoryContext)pmem); + + while (vrl != (VRelList) NULL) { + + /* free attribute list */ + val = vrl->vrl_attlist; + while (val != (VAttList) NULL) { + p_val = val; + val = val->val_next; + pfree(p_val); + } + + /* free tid list */ + vtl = vrl->vrl_tidlist; + while (vtl != (VTidList) NULL) { + p_vtl = vtl; + vtl = vtl->vtl_next; + pfree(p_vtl); + } + + /* free rel list entry */ + p_vrl = vrl; + vrl = vrl->vrl_next; + pfree(p_vrl); + } + + (void) MemoryContextSwitchTo(old); +} + +/* + * _vc_getarchrel() -- open the archive relation for a heap relation + * + * The archive relation is named 'a,XXXXX' for the heap relation + * whose relid is XXXXX. + */ + +#define ARCHIVE_PREFIX "a," + +static Relation +_vc_getarchrel(Relation heaprel) +{ + Relation archrel; + char *archrelname; + + archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */ + sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id); + + archrel = heap_openr(archrelname); + + pfree(archrelname); + return (archrel); +} + +/* + * _vc_archive() -- write a tuple to an archive relation + * + * In the future, this will invoke the archived accessd method. For + * now, archive relations are on mag disk. + */ +static void +_vc_archive(Relation archrel, HeapTuple htup) +{ + doinsert(archrel, htup); +} + +static bool +_vc_isarchrel(char *rname) +{ + if (strncmp(ARCHIVE_PREFIX, rname,strlen(ARCHIVE_PREFIX)) == 0) + return (true); + + return (false); +} |