documentation to come, but the code is all here. initdb forced.
oid
- Last oid in existence after the database was created; useful
+ Last system OID in the database; useful
+ |
+ datvacuumxid
+ xid
+
+
+ All tuples inserted or deleted by transaction IDs before this one
+ have been marked as known committed or known aborted in this database.
+ This is used to determine when commit-log space can be recycled.
+
+
+
+ |
+ datfrozenxid
+ xid
+
+
+ All tuples inserted by transaction IDs before this one have been
+ relabeled with a permanent (frozen>) transaction ID in this
+ database. This is useful to check whether a database must be vacuumed
+ soon to avoid transaction ID wraparound problems.
+
+
+
|
datpath
text
simply by setting the flag false). The template0
database is normally marked this way to prevent modification of it.
+
+ After preparing a template database, or making any changes to one,
+ it is a good idea to perform
+ VACUUM FREEZE> or VACUUM FULL FREEZE> in that
+ database. If this is done when there are no other open transactions
+ in the same database, then it is guaranteed that all tuples in the
+ database are frozen> and will not be subject to transaction
+ ID wraparound problems. This is particularly important for a database
+ that will have datallowconn set to false, since it
+ will be impossible to do routine maintenance VACUUM>s on
+ such a database.
+ See the Administrator's Guide for more information.
+
- 2001-07-10
+ 2001-08-26
-VACUUM [ FULL ] [ VERBOSE ] [ table ]
-VACUUM [ FULL ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
+VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ table ]
+VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
+
+ FREEZE
+
+ Selects aggressive freezing
of tuples.
+
+
+
VERBOSE
- Plain VACUUM simply reclaims space and makes it
+ VACUUM ANALYZE performs a VACUUM
+ and then an ANALYZE for each selected table. This
+ is a handy combination form for routine maintenance scripts. See
+
+ for more details about its processing.
+
+
+ Plain VACUUM (without FULL>) simply reclaims
+ space and makes it
available for re-use. This form of the command can operate in parallel
with normal reading and writing of the table. VACUUM
FULL does more extensive processing, including moving of tuples
across blocks to try to compact the table to the minimum number of disk
- blocks. This is much slower and requires an exclusive lock on each table
- while it is being processed.
+ blocks. This form is much slower and requires an exclusive lock on each
+ table while it is being processed.
- VACUUM ANALYZE performs a VACUUM
- and then an ANALYZE for each selected table. This
- is a handy combination form for routine maintenance scripts. See
-
- for more details about its processing.
+ FREEZE is a special-purpose option that
+ causes tuples to be marked frozen
as soon as possible,
+ rather than waiting until they are quite old. If this is done when there
+ are no other open transactions in the same database, then it is guaranteed
+ that all tuples in the database are frozen> and will not be
+ subject to transaction ID wraparound problems, no matter how long the
+ database is left un-vacuumed.
+ FREEZE is not recommended for routine use. Its only
+ intended usage is in connection with preparation of user-defined template
+ databases, or other databases that are completely read-only and will not
+ receive routine maintenance VACUUM> operations.
+ See
+ for details.
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/access/transam/clog.c,v 1.2 2001/08/25 23:24:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/clog.c,v 1.3 2001/08/26 16:55:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
int pageno;
- /* No work except at first XID of a page */
- if (TransactionIdToPgIndex(newestXact) != 0)
+ /*
+ * No work except at first XID of a page. But beware: just after
+ * wraparound, the first XID of page zero is FirstNormalTransactionId.
+ */
+ if (TransactionIdToPgIndex(newestXact) != 0 &&
+ !TransactionIdEquals(newestXact, FirstNormalTransactionId))
return;
pageno = TransactionIdToPage(newestXact);
S_LOCK(&(ClogCtl->control_lck));
restart:;
+ /*
+ * While we are holding the lock, make an important safety check:
+ * the planned cutoff point must be <= the current CLOG endpoint page.
+ * Otherwise we have already wrapped around, and proceeding with the
+ * truncation would risk removing the current CLOG segment.
+ */
+ if (CLOGPagePrecedes(ClogCtl->latest_page_number, cutoffPage))
+ {
+ S_UNLOCK(&(ClogCtl->control_lck));
+ elog(LOG, "unable to truncate commit log: apparent wraparound");
+ return;
+ }
for (slotno = 0; slotno < NUM_CLOG_BUFFERS; slotno++)
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.47 2001/08/25 18:52:41 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.48 2001/08/26 16:55:59 tgl Exp $
*
* NOTES
* This file contains the high level access-method interface to the
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED);
}
+
+
+/*
+ * TransactionIdPrecedes --- is id1 logically < id2?
+ */
+bool
+TransactionIdPrecedes(TransactionId id1, TransactionId id2)
+{
+ /*
+ * If either ID is a permanent XID then we can just do unsigned
+ * comparison. If both are normal, do a modulo-2^31 comparison.
+ */
+ int32 diff;
+
+ if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
+ return (id1 < id2);
+
+ diff = (int32) (id1 - id2);
+ return (diff < 0);
+}
+
+/*
+ * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
+ */
+bool
+TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
+{
+ int32 diff;
+
+ if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
+ return (id1 <= id2);
+
+ diff = (int32) (id1 - id2);
+ return (diff <= 0);
+}
+
+/*
+ * TransactionIdFollows --- is id1 logically > id2?
+ */
+bool
+TransactionIdFollows(TransactionId id1, TransactionId id2)
+{
+ int32 diff;
+
+ if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
+ return (id1 > id2);
+
+ diff = (int32) (id1 - id2);
+ return (diff > 0);
+}
+
+/*
+ * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
+ */
+bool
+TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
+{
+ int32 diff;
+
+ if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
+ return (id1 >= id2);
+
+ diff = (int32) (id1 - id2);
+ return (diff >= 0);
+}
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: xid.c,v 1.32 2001/08/23 23:06:37 tgl Exp $
- *
- * OLD COMMENTS
- * XXX WARNING
- * Much of this file will change when we change our representation
- * of transaction ids -cim 3/23/90
- *
- * It is time to make the switch from 5 byte to 4 byte transaction ids
- * This file was totally reworked. -mer 5/22/92
+ * $Id: xid.c,v 1.33 2001/08/26 16:55:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include
+
#include "access/xact.h"
+
#define PG_GETARG_TRANSACTIONID(n) DatumGetTransactionId(PG_GETARG_DATUM(n))
#define PG_RETURN_TRANSACTIONID(x) return TransactionIdGetDatum(x)
Datum
xidin(PG_FUNCTION_ARGS)
{
- char *representation = PG_GETARG_CSTRING(0);
+ char *str = PG_GETARG_CSTRING(0);
- PG_RETURN_TRANSACTIONID((TransactionId) atol(representation));
+ PG_RETURN_TRANSACTIONID((TransactionId) strtoul(str, NULL, 0));
}
Datum
{
TransactionId transactionId = PG_GETARG_TRANSACTIONID(0);
/* maximum 32 bit unsigned integer representation takes 10 chars */
- char *representation = palloc(11);
+ char *str = palloc(11);
- snprintf(representation, 11, "%lu", (unsigned long) transactionId);
+ snprintf(str, 11, "%lu", (unsigned long) transactionId);
- PG_RETURN_CSTRING(representation);
+ PG_RETURN_CSTRING(str);
}
-/* ----------------------------------------------------------------
- * xideq
- * ----------------------------------------------------------------
- */
-
/*
- * xideq - returns 1, iff xid1 == xid2
- * 0 else;
+ * xideq - are two xids equal?
*/
Datum
xideq(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(TransactionIdEquals(xid1, xid2));
}
+
+/*
+ * xid_age - compute age of an XID (relative to current xact)
+ */
+Datum
+xid_age(PG_FUNCTION_ARGS)
+{
+ TransactionId xid = PG_GETARG_TRANSACTIONID(0);
+ TransactionId now = GetCurrentTransactionId();
+
+ /* Permanent XIDs are always infinitely old */
+ if (! TransactionIdIsNormal(xid))
+ PG_RETURN_INT32(INT_MAX);
+
+ PG_RETURN_INT32((int32) (now - xid));
+}
#
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/catalog/Attic/genbki.sh,v 1.22 2001/08/24 14:07:48 petere Exp $
+# $Header: /cvsroot/pgsql/src/backend/catalog/Attic/genbki.sh,v 1.23 2001/08/26 16:55:59 tgl Exp $
#
# NOTES
# non-essential whitespace is removed from the generated file.
touch ${OUTPUT_PREFIX}.description.$$
# ----------------
-# strip comments and trash from .h before we generate
-# the .bki file...
+# Strip comments and other trash from .h
+#
+# Put multi-line start/end comments on a separate line
+#
+# Rename datatypes that have different names in .h files than in SQL
+#
+# Substitute values of configuration constants
# ----------------
-# also, change Oid to oid. -- AY 8/94.
-# also, change NameData to name. -- jolly 8/21/95.
-# put multi-line start/end comments on a separate line
#
cat $INFILES | \
sed -e 's;/\*.*\*/;;g' \
sed -e "s/;[ ]*$//g" \
-e "s/^[ ]*//" \
-e "s/[ ]Oid/ oid/g" \
- -e "s/[ ]NameData/ name/g" \
-e "s/^Oid/oid/g" \
+ -e "s/(Oid/(oid/g" \
+ -e "s/[ ]NameData/ name/g" \
-e "s/^NameData/name/g" \
-e "s/(NameData/(name/g" \
- -e "s/(Oid/(oid/g" \
+ -e "s/[ ]TransactionId/ xid/g" \
+ -e "s/^TransactionId/xid/g" \
+ -e "s/(TransactionId/(xid/g" \
-e "s/NAMEDATALEN/$NAMEDATALEN/g" \
-e "s/DEFAULT_ATTSTATTARGET/$DEFAULTATTSTATTARGET/g" \
-e "s/INDEX_MAX_KEYS\*2/$INDEXMAXKEYS2/g" \
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.162 2001/08/22 18:24:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.163 2001/08/26 16:55:59 tgl Exp $
*
*
* INTERFACE ROUTINES
TupleTableSlot *slot;
ExprContext *econtext;
Snapshot snapshot;
- TransactionId XmaxRecent;
+ TransactionId OldestXmin;
/*
* sanity checks
if (IsBootstrapProcessingMode())
{
snapshot = SnapshotNow;
- XmaxRecent = InvalidTransactionId;
+ OldestXmin = InvalidTransactionId;
}
else
{
snapshot = SnapshotAny;
- GetXmaxRecent(&XmaxRecent);
+ OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared);
}
scan = heap_beginscan(heapRelation, /* relation */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
sv_infomask = heapTuple->t_data->t_infomask;
- switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, XmaxRecent))
+ switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin))
{
case HEAPTUPLE_DEAD:
indexIt = false;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.78 2001/08/10 18:57:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.79 2001/08/26 16:55:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* non-export function prototypes */
static bool get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
- int *encodingP, bool *dbIsTemplateP,
- Oid *dbLastSysOidP, char *dbpath);
+ int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
+ TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
+ char *dbpath);
static bool get_user_info(Oid use_sysid, bool *use_super, bool *use_createdb);
static char *resolve_alt_dbpath(const char *dbpath, Oid dboid);
static bool remove_dbdirs(const char *real_loc, const char *altloc);
int src_encoding;
bool src_istemplate;
Oid src_lastsysoid;
+ TransactionId src_vacuumxid;
+ TransactionId src_frozenxid;
char src_dbpath[MAXPGPATH];
Relation pg_database_rel;
HeapTuple tuple;
* idea, so accept possibility of race to create. We will check again
* after we grab the exclusive lock.
*/
- if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL))
+ if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
elog(ERROR, "CREATE DATABASE: database \"%s\" already exists", dbname);
/*
dbtemplate = "template1"; /* Default template database name */
if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding,
- &src_istemplate, &src_lastsysoid, src_dbpath))
+ &src_istemplate, &src_lastsysoid,
+ &src_vacuumxid, &src_frozenxid,
+ src_dbpath))
elog(ERROR, "CREATE DATABASE: template \"%s\" does not exist",
dbtemplate);
pg_database_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
/* Check to see if someone else created same DB name meanwhile. */
- if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL))
+ if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
{
+ /* Don't hold lock while doing recursive remove */
+ heap_close(pg_database_rel, AccessExclusiveLock);
remove_dbdirs(nominal_loc, alt_loc);
elog(ERROR, "CREATE DATABASE: database \"%s\" already exists", dbname);
}
new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false);
new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
+ new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid);
+ new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
/* no nulls here, GetRawDatabaseInfo doesn't like them */
new_record[Anum_pg_database_datpath - 1] =
DirectFunctionCall1(textin, CStringGetDatum(dbpath ? dbpath : ""));
pgdbrel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
if (!get_db_info(dbname, &db_id, &db_owner, NULL,
- &db_istemplate, NULL, dbpath))
+ &db_istemplate, NULL, NULL, NULL, dbpath))
elog(ERROR, "DROP DATABASE: database \"%s\" does not exist", dbname);
if (!use_super && GetUserId() != db_owner)
static bool
get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
- int *encodingP, bool *dbIsTemplateP,
- Oid *dbLastSysOidP, char *dbpath)
+ int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
+ TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
+ char *dbpath)
{
Relation relation;
ScanKeyData scanKey;
HeapScanDesc scan;
HeapTuple tuple;
+ bool gottuple;
AssertArg(name);
F_NAMEEQ, NameGetDatum(name));
scan = heap_beginscan(relation, 0, SnapshotNow, 1, &scanKey);
- if (!HeapScanIsValid(scan))
- elog(ERROR, "Cannot begin scan of %s", DatabaseRelationName);
tuple = heap_getnext(scan, 0);
- if (HeapTupleIsValid(tuple))
+ gottuple = HeapTupleIsValid(tuple);
+ if (gottuple)
{
Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
text *tmptext;
/* oid of the database */
if (dbIdP)
*dbIdP = tuple->t_data->t_oid;
- /* uid of the owner */
+ /* sysid of the owner */
if (ownerIdP)
*ownerIdP = dbform->datdba;
/* multibyte encoding */
/* last system OID used in database */
if (dbLastSysOidP)
*dbLastSysOidP = dbform->datlastsysoid;
+ /* limit of vacuumed XIDs */
+ if (dbVacuumXidP)
+ *dbVacuumXidP = dbform->datvacuumxid;
+ /* limit of frozen XIDs */
+ if (dbFrozenXidP)
+ *dbFrozenXidP = dbform->datfrozenxid;
/* database path (as registered in pg_database) */
if (dbpath)
{
heap_endscan(scan);
heap_close(relation, AccessShareLock);
- return HeapTupleIsValid(tuple);
+ return gottuple;
}
static bool
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.207 2001/08/10 18:57:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.208 2001/08/26 16:55:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include
+#include "access/clog.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
+#include "catalog/pg_database.h"
#include "catalog/pg_index.h"
#include "commands/vacuum.h"
#include "executor/executor.h"
static int MESSAGE_LEVEL; /* message level */
-static TransactionId XmaxRecent;
+static TransactionId OldestXmin;
+static TransactionId FreezeLimit;
+
+static TransactionId initialOldestXmin;
+static TransactionId initialFreezeLimit;
/* non-export function prototypes */
-static void vacuum_init(void);
-static void vacuum_shutdown(void);
+static void vacuum_init(VacuumStmt *vacstmt);
+static void vacuum_shutdown(VacuumStmt *vacstmt);
static VRelList getrels(Name VacRelP, const char *stmttype);
+static void vac_update_dbstats(Oid dbid,
+ TransactionId vacuumXID,
+ TransactionId frozenXID);
+static void vac_truncate_clog(TransactionId vacuumXID,
+ TransactionId frozenXID);
static void vacuum_rel(Oid relid, VacuumStmt *vacstmt);
-static void full_vacuum_rel(Relation onerel);
+static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
static void scan_heap(VRelStats *vacrelstats, Relation onerel,
VacPageList vacuum_pages, VacPageList fraged_pages);
static void repair_frag(VRelStats *vacrelstats, Relation onerel,
/*
* Start up the vacuum cleaner.
*/
- vacuum_init();
+ vacuum_init(vacstmt);
/*
* Process each selected relation. We are careful to process
analyze_rel(cur->vrl_relid, vacstmt);
}
- /*
- * If we did a complete vacuum, then flush the init file that relcache.c
- * uses to save startup time. The next backend startup will rebuild the
- * init file with up-to-date information from pg_class. This lets the
- * optimizer see the stats that we've collected for certain critical
- * system indexes. See relcache.c for more details.
- *
- * Ignore any failure to unlink the file, since it might not be there if
- * no backend has been started since the last vacuum.
- */
- if (vacstmt->vacrel == NULL)
- unlink(RELCACHE_INIT_FILENAME);
-
/* clean up */
- vacuum_shutdown();
+ vacuum_shutdown(vacstmt);
}
/*
* PostgresMain().
*/
static void
-vacuum_init(void)
+vacuum_init(VacuumStmt *vacstmt)
{
+ if (vacstmt->vacuum && vacstmt->vacrel == NULL)
+ {
+ /*
+ * Compute the initially applicable OldestXmin and FreezeLimit XIDs,
+ * so that we can record these values at the end of the VACUUM.
+ * Note that individual tables may well be processed with newer values,
+ * but we can guarantee that no (non-shared) relations are processed
+ * with older ones.
+ *
+ * It is okay to record non-shared values in pg_database, even though
+ * we may vacuum shared relations with older cutoffs, because only
+ * the minimum of the values present in pg_database matters. We
+ * can be sure that shared relations have at some time been vacuumed
+ * with cutoffs no worse than the global minimum; for, if there is
+ * a backend in some other DB with xmin = OLDXMIN that's determining
+ * the cutoff with which we vacuum shared relations, it is not possible
+ * for that database to have a cutoff newer than OLDXMIN recorded in
+ * pg_database.
+ */
+ vacuum_set_xid_limits(vacstmt, false,
+ &initialOldestXmin, &initialFreezeLimit);
+ }
+
/* matches the StartTransaction in PostgresMain() */
CommitTransactionCommand();
}
static void
-vacuum_shutdown(void)
+vacuum_shutdown(VacuumStmt *vacstmt)
{
/* on entry, we are not in a transaction */
/* matches the CommitTransaction in PostgresMain() */
StartTransactionCommand();
+ /*
+ * If we did a database-wide VACUUM, update the database's pg_database
+ * row with info about the transaction IDs used, and try to truncate
+ * pg_clog.
+ */
+ if (vacstmt->vacuum && vacstmt->vacrel == NULL)
+ {
+ vac_update_dbstats(MyDatabaseId,
+ initialOldestXmin, initialFreezeLimit);
+ vac_truncate_clog(initialOldestXmin, initialFreezeLimit);
+ }
+
+ /*
+ * If we did a complete vacuum or analyze, then flush the init file that
+ * relcache.c uses to save startup time. The next backend startup will
+ * rebuild the init file with up-to-date information from pg_class.
+ * This lets the optimizer see the stats that we've collected for certain
+ * critical system indexes. See relcache.c for more details.
+ *
+ * Ignore any failure to unlink the file, since it might not be there if
+ * no backend has been started since the last vacuum.
+ */
+ if (vacstmt->vacrel == NULL)
+ unlink(RELCACHE_INIT_FILENAME);
+
/*
* Clean up working storage --- note we must do this after
* StartTransactionCommand, else we might be trying to delete the
return vrl;
}
+/*
+ * vacuum_set_xid_limits() -- compute oldest-Xmin and freeze cutoff points
+ */
+void
+vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
+ TransactionId *oldestXmin,
+ TransactionId *freezeLimit)
+{
+ TransactionId limit;
+
+ *oldestXmin = GetOldestXmin(sharedRel);
+
+ Assert(TransactionIdIsNormal(*oldestXmin));
+
+ if (vacstmt->freeze)
+ {
+ /* FREEZE option: use oldest Xmin as freeze cutoff too */
+ limit = *oldestXmin;
+ }
+ else
+ {
+ /*
+ * Normal case: freeze cutoff is well in the past, to wit, about
+ * halfway to the wrap horizon
+ */
+ limit = GetCurrentTransactionId() - (MaxTransactionId >> 2);
+ }
+
+ /*
+ * Be careful not to generate a "permanent" XID
+ */
+ if (!TransactionIdIsNormal(limit))
+ limit = FirstNormalTransactionId;
+
+ /*
+ * Ensure sane relationship of limits
+ */
+ if (TransactionIdFollows(limit, *oldestXmin))
+ {
+ elog(NOTICE, "oldest Xmin is far in the past --- close open transactions soon to avoid wraparound problems");
+ limit = *oldestXmin;
+ }
+
+ *freezeLimit = limit;
+}
+
/*
* vac_update_relstats() -- update statistics for one relation
}
+/*
+ * vac_update_dbstats() -- update statistics for one database
+ *
+ * Update the whole-database statistics that are kept in its pg_database
+ * row.
+ *
+ * We violate no-overwrite semantics here by storing new values for the
+ * statistics columns directly into the tuple that's already on the page.
+ * As with vac_update_relstats, this avoids leaving dead tuples behind
+ * after a VACUUM; which is good since GetRawDatabaseInfo
+ * can get confused by finding dead tuples in pg_database.
+ *
+ * This routine is shared by full and lazy VACUUM. Note that it is only
+ * applied after a database-wide VACUUM operation.
+ */
+static void
+vac_update_dbstats(Oid dbid,
+ TransactionId vacuumXID,
+ TransactionId frozenXID)
+{
+ Relation relation;
+ ScanKeyData entry[1];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Form_pg_database dbform;
+
+ relation = heap_openr(DatabaseRelationName, RowExclusiveLock);
+
+ /* Must use a heap scan, since there's no syscache for pg_database */
+ ScanKeyEntryInitialize(&entry[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(dbid));
+
+ scan = heap_beginscan(relation, 0, SnapshotNow, 1, entry);
+
+ tuple = heap_getnext(scan, 0);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "database %u does not exist", dbid);
+
+ dbform = (Form_pg_database) GETSTRUCT(tuple);
+
+ /* overwrite the existing statistics in the tuple */
+ dbform->datvacuumxid = vacuumXID;
+ dbform->datfrozenxid = frozenXID;
+
+ /* invalidate the tuple in the cache and write the buffer */
+ RelationInvalidateHeapTuple(relation, tuple);
+ WriteNoReleaseBuffer(scan->rs_cbuf);
+
+ heap_endscan(scan);
+
+ heap_close(relation, RowExclusiveLock);
+}
+
+
+/*
+ * vac_truncate_clog() -- attempt to truncate the commit log
+ *
+ * Scan pg_database to determine the system-wide oldest datvacuumxid,
+ * and use it to truncate the transaction commit log (pg_clog).
+ * Also generate a warning if the system-wide oldest datfrozenxid
+ * seems to be in danger of wrapping around.
+ *
+ * The passed XIDs are simply the ones I just wrote into my pg_database
+ * entry. They're used to initialize the "min" calculations.
+ *
+ * This routine is shared by full and lazy VACUUM. Note that it is only
+ * applied after a database-wide VACUUM operation.
+ */
+static void
+vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
+{
+ Relation relation;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ int32 age;
+
+ relation = heap_openr(DatabaseRelationName, AccessShareLock);
+
+ scan = heap_beginscan(relation, 0, SnapshotNow, 0, NULL);
+
+ while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+ {
+ Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
+
+ /* Ignore non-connectable databases (eg, template0) */
+ /* It's assumed that these have been frozen correctly */
+ if (!dbform->datallowconn)
+ continue;
+
+ if (TransactionIdIsNormal(dbform->datvacuumxid) &&
+ TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
+ vacuumXID = dbform->datvacuumxid;
+ if (TransactionIdIsNormal(dbform->datfrozenxid) &&
+ TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
+ frozenXID = dbform->datfrozenxid;
+ }
+
+ heap_endscan(scan);
+
+ heap_close(relation, AccessShareLock);
+
+ /* Truncate CLOG to the oldest vacuumxid */
+ TruncateCLOG(vacuumXID);
+
+ /* Give warning about impending wraparound problems */
+ age = (int32) (GetCurrentTransactionId() - frozenXID);
+ if (age > (int32) ((MaxTransactionId >> 3) * 3))
+ elog(NOTICE, "Some databases have not been vacuumed in %d transactions."
+ "\n\tBetter vacuum them within %d transactions,"
+ "\n\tor you may have a wraparound failure.",
+ age, (int32) (MaxTransactionId >> 1) - age);
+}
+
+
/****************************************************************************
* *
* Code common to both flavors of VACUUM *
* Do the actual work --- either FULL or "lazy" vacuum
*/
if (vacstmt->full)
- full_vacuum_rel(onerel);
+ full_vacuum_rel(onerel, vacstmt);
else
lazy_vacuum_rel(onerel, vacstmt);
* and locked the relation.
*/
static void
-full_vacuum_rel(Relation onerel)
+full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
{
VacPageListData vacuum_pages; /* List of pages to vacuum and/or
* clean indexes */
IsSystemRelationName(RelationGetRelationName(onerel)))
reindex = true;
- GetXmaxRecent(&XmaxRecent);
+ vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,
+ &OldestXmin, &FreezeLimit);
/*
* Set up statistics-gathering machinery.
tupgone = false;
sv_infomask = tuple.t_data->t_infomask;
- switch (HeapTupleSatisfiesVacuum(tuple.t_data, XmaxRecent))
+ switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
{
case HEAPTUPLE_DEAD:
tupgone = true; /* we can delete the tuple */
break;
case HEAPTUPLE_LIVE:
+ /*
+ * Tuple is good. Consider whether to replace its xmin
+ * value with FrozenTransactionId.
+ */
+ if (TransactionIdIsNormal(tuple.t_data->t_xmin) &&
+ TransactionIdPrecedes(tuple.t_data->t_xmin,
+ FreezeLimit))
+ {
+ tuple.t_data->t_xmin = FrozenTransactionId;
+ tuple.t_data->t_infomask &= ~HEAP_XMIN_INVALID;
+ tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
+ pgchanged = true;
+ }
break;
case HEAPTUPLE_RECENTLY_DEAD:
/*
* tuples to another places.
*/
if ((tuple.t_data->t_infomask & HEAP_UPDATED &&
- !TransactionIdPrecedes(tuple.t_data->t_xmin, XmaxRecent)) ||
+ !TransactionIdPrecedes(tuple.t_data->t_xmin, OldestXmin)) ||
(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID) &&
!(ItemPointerEquals(&(tuple.t_self),
&(tuple.t_data->t_ctid)))))
/*
* This means that in the middle of chain there
- * was tuple updated by older (than XmaxRecent)
+ * was tuple updated by older (than OldestXmin)
* xaction and this tuple is already deleted by
* me. Actually, upper part of chain should be
* removed and seems that this should be handled
/* All done ? */
if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||
- TransactionIdPrecedes(tp.t_data->t_xmin, XmaxRecent))
+ TransactionIdPrecedes(tp.t_data->t_xmin, OldestXmin))
break;
/* Well, try to find tuple with old row version */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.4 2001/08/10 18:57:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.5 2001/08/26 16:55:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static int MESSAGE_LEVEL; /* message level */
-static TransactionId XmaxRecent;
+static TransactionId OldestXmin;
+static TransactionId FreezeLimit;
/* non-export function prototypes */
else
MESSAGE_LEVEL = DEBUG;
- GetXmaxRecent(&XmaxRecent);
+ vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,
+ &OldestXmin, &FreezeLimit);
vacrelstats = (LVRelStats *) palloc(sizeof(LVRelStats));
MemSet(vacrelstats, 0, sizeof(LVRelStats));
tupgone = false;
sv_infomask = tuple.t_data->t_infomask;
- switch (HeapTupleSatisfiesVacuum(tuple.t_data, XmaxRecent))
+ switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
{
case HEAPTUPLE_DEAD:
tupgone = true; /* we can delete the tuple */
break;
case HEAPTUPLE_LIVE:
+ /*
+ * Tuple is good. Consider whether to replace its xmin
+ * value with FrozenTransactionId.
+ */
+ if (TransactionIdIsNormal(tuple.t_data->t_xmin) &&
+ TransactionIdPrecedes(tuple.t_data->t_xmin,
+ FreezeLimit))
+ {
+ tuple.t_data->t_xmin = FrozenTransactionId;
+ tuple.t_data->t_infomask &= ~HEAP_XMIN_INVALID;
+ tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
+ pgchanged = true;
+ }
break;
case HEAPTUPLE_RECENTLY_DEAD:
/*
tupgone = false;
sv_infomask = tuple.t_data->t_infomask;
- switch (HeapTupleSatisfiesVacuum(tuple.t_data, XmaxRecent))
+ switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
{
case HEAPTUPLE_DEAD:
tupgone = true; /* we can delete the tuple */
break;
case HEAPTUPLE_LIVE:
+ /* Shouldn't be necessary to re-freeze anything */
break;
case HEAPTUPLE_RECENTLY_DEAD:
/*
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.154 2001/08/21 16:36:02 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.155 2001/08/26 16:55:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
newnode->vacuum = from->vacuum;
newnode->full = from->full;
newnode->analyze = from->analyze;
+ newnode->freeze = from->freeze;
newnode->verbose = from->verbose;
if (from->vacrel)
newnode->vacrel = pstrdup(from->vacrel);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.102 2001/08/21 16:36:02 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.103 2001/08/26 16:55:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return false;
if (a->analyze != b->analyze)
return false;
+ if (a->freeze != b->freeze)
+ return false;
if (a->verbose != b->verbose)
return false;
if (!equalstr(a->vacrel, b->vacrel))
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.248 2001/08/25 18:52:41 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.249 2001/08/26 16:55:59 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
%type opt_binary, opt_using, opt_instead, opt_cursor
%type opt_with_copy, index_opt_unique, opt_verbose, opt_full
-%type analyze_keyword
+%type opt_freeze, analyze_keyword
%type copy_dirn, direction, reindex_type, drop_type,
opt_column, event, comment_type, comment_cl,
CACHE, CHECKPOINT, CLUSTER, COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE,
DATABASE, DELIMITERS, DO,
EACH, ENCODING, EXCLUSIVE, EXPLAIN,
- FORCE, FORWARD, FUNCTION, HANDLER,
+ FORCE, FORWARD, FREEZE, FUNCTION, HANDLER,
ILIKE, INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
LANCOMPILER, LIMIT, LISTEN, LOAD, LOCATION, LOCK_P,
MAXVALUE, MINVALUE, MODE, MOVE,
*
*****************************************************************************/
-VacuumStmt: VACUUM opt_full opt_verbose
+VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
{
VacuumStmt *n = makeNode(VacuumStmt);
n->vacuum = true;
n->analyze = false;
n->full = $2;
- n->verbose = $3;
+ n->freeze = $3;
+ n->verbose = $4;
n->vacrel = NULL;
n->va_cols = NIL;
$$ = (Node *)n;
}
- | VACUUM opt_full opt_verbose relation_name
+ | VACUUM opt_full opt_freeze opt_verbose relation_name
{
VacuumStmt *n = makeNode(VacuumStmt);
n->vacuum = true;
n->analyze = false;
n->full = $2;
- n->verbose = $3;
- n->vacrel = $4;
+ n->freeze = $3;
+ n->verbose = $4;
+ n->vacrel = $5;
n->va_cols = NIL;
$$ = (Node *)n;
}
- | VACUUM opt_full opt_verbose AnalyzeStmt
+ | VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt
{
- VacuumStmt *n = (VacuumStmt *) $4;
+ VacuumStmt *n = (VacuumStmt *) $5;
n->vacuum = true;
n->full = $2;
- n->verbose |= $3;
+ n->freeze = $3;
+ n->verbose |= $4;
$$ = (Node *)n;
}
;
n->vacuum = false;
n->analyze = true;
n->full = false;
+ n->freeze = false;
n->verbose = $2;
n->vacrel = NULL;
n->va_cols = NIL;
n->vacuum = false;
n->analyze = true;
n->full = false;
+ n->freeze = false;
n->verbose = $2;
n->vacrel = $3;
n->va_cols = $4;
| /*EMPTY*/ { $$ = FALSE; }
;
+opt_freeze: FREEZE { $$ = TRUE; }
+ | /*EMPTY*/ { $$ = FALSE; }
+ ;
+
opt_name_list: '(' name_list ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
| DROP { $$ = "drop"; }
| EACH { $$ = "each"; }
| ENCODING { $$ = "encoding"; }
+ | ENCRYPTED { $$ = "encrypted"; }
| ESCAPE { $$ = "escape"; }
| EXCLUSIVE { $$ = "exclusive"; }
| EXECUTE { $$ = "execute"; }
| TRUNCATE { $$ = "truncate"; }
| TRUSTED { $$ = "trusted"; }
| TYPE_P { $$ = "type"; }
+ | UNENCRYPTED { $$ = "unencrypted"; }
| UNLISTEN { $$ = "unlisten"; }
| UNTIL { $$ = "until"; }
| UPDATE { $$ = "update"; }
| DISTINCT { $$ = "distinct"; }
| DO { $$ = "do"; }
| ELSE { $$ = "else"; }
- | ENCRYPTED { $$ = "encrypted"; }
| END_TRANS { $$ = "end"; }
| EXCEPT { $$ = "except"; }
| EXISTS { $$ = "exists"; }
| FLOAT { $$ = "float"; }
| FOR { $$ = "for"; }
| FOREIGN { $$ = "foreign"; }
+ | FREEZE { $$ = "freeze"; }
| FROM { $$ = "from"; }
| FULL { $$ = "full"; }
| GLOBAL { $$ = "global"; }
| TRANSACTION { $$ = "transaction"; }
| TRIM { $$ = "trim"; }
| TRUE_P { $$ = "true"; }
- | UNENCRYPTED { $$ = "unencrypted"; }
| UNION { $$ = "union"; }
| UNIQUE { $$ = "unique"; }
| UNKNOWN { $$ = "unknown"; }
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.96 2001/08/16 20:38:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.97 2001/08/26 16:56:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{"force", FORCE},
{"foreign", FOREIGN},
{"forward", FORWARD},
+ {"freeze", FREEZE},
{"from", FROM},
{"full", FULL},
{"function", FUNCTION},
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.39 2001/08/25 18:52:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.40 2001/08/26 16:56:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "storage/sinval.h"
#include "storage/sinvaladt.h"
#include "utils/tqual.h"
+#include "miscadmin.h"
+
SPINLOCK SInvalLock = (SPINLOCK) NULL;
}
/*
- * GetXmaxRecent -- returns oldest transaction that was running
- * when all current transaction were started.
- * It's used by vacuum to decide what deleted
- * tuples must be preserved in a table.
+ * GetOldestXmin -- returns oldest transaction that was running
+ * when any current transaction was started.
+ *
+ * If allDbs is TRUE then all backends are considered; if allDbs is FALSE
+ * then only backends running in my own database are considered.
*
- * Note: we include all currently running xids in the set of considered xids.
+ * This is used by VACUUM to decide which deleted tuples must be preserved
+ * in a table. allDbs = TRUE is needed for shared relations, but allDbs =
+ * FALSE is sufficient for non-shared relations, since only backends in my
+ * own database could ever see the tuples in them.
+ *
+ * Note: we include the currently running xids in the set of considered xids.
* This ensures that if a just-started xact has not yet set its snapshot,
* when it does set the snapshot it cannot set xmin less than what we compute.
*/
-void
-GetXmaxRecent(TransactionId *XmaxRecent)
+TransactionId
+GetOldestXmin(bool allDbs)
{
SISeg *segP = shmInvalBuffer;
ProcState *stateP = segP->procState;
if (pOffset != INVALID_OFFSET)
{
PROC *proc = (PROC *) MAKE_PTR(pOffset);
- /* Fetch xid just once - see GetNewTransactionId */
- TransactionId xid = proc->xid;
- if (TransactionIdIsNormal(xid))
+ if (allDbs || proc->databaseId == MyDatabaseId)
{
- if (TransactionIdPrecedes(xid, result))
- result = xid;
- xid = proc->xmin;
+ /* Fetch xid just once - see GetNewTransactionId */
+ TransactionId xid = proc->xid;
+
if (TransactionIdIsNormal(xid))
+ {
if (TransactionIdPrecedes(xid, result))
result = xid;
+ xid = proc->xmin;
+ if (TransactionIdIsNormal(xid))
+ if (TransactionIdPrecedes(xid, result))
+ result = xid;
+ }
}
}
}
SpinRelease(SInvalLock);
- *XmaxRecent = result;
+ return result;
}
/*----------
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.41 2001/08/25 18:52:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.42 2001/08/26 16:56:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* HeapTupleSatisfiesVacuum - determine tuple status for VACUUM and related
* operations
*
- * XmaxRecent is a cutoff XID (obtained from GetXmaxRecent()). Tuples
- * deleted by XIDs >= XmaxRecent are deemed "recently dead"; they might
+ * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
+ * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
* still be visible to some open transaction, so we can't remove them,
* even if we see that the deleting transaction has committed.
*
* change in t_infomask and scheduling a disk write if so.
*/
HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId XmaxRecent)
+HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
{
/*
* Has inserting transaction committed?
return HEAPTUPLE_DEAD;
}
- if (!TransactionIdPrecedes(tuple->t_xmax, XmaxRecent))
+ if (!TransactionIdPrecedes(tuple->t_xmax, OldestXmin))
{
/* deleting xact is too recent, tuple could still be visible */
return HEAPTUPLE_RECENTLY_DEAD;
# Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
-# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.134 2001/08/25 18:52:42 tgl Exp $
+# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.135 2001/08/26 16:56:00 tgl Exp $
#
#-------------------------------------------------------------------------
$ECHO_N "vacuuming database template1... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <
-VACUUM FULL ANALYZE;
+VACUUM FULL FREEZE;
EOF
if [ "$?" -ne 0 ]; then
exit_nicely
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: transam.h,v 1.39 2001/08/25 18:52:42 tgl Exp $
+ * $Id: transam.h,v 1.40 2001/08/26 16:56:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define BootstrapTransactionId ((TransactionId) 1)
#define FrozenTransactionId ((TransactionId) 2)
#define FirstNormalTransactionId ((TransactionId) 3)
+#define MaxTransactionId ((TransactionId) 0xFFFFFFFF)
/* ----------------
* transaction ID manipulation macros
*/
#define TransactionIdIsValid(xid) ((xid) != InvalidTransactionId)
#define TransactionIdIsNormal(xid) ((xid) >= FirstNormalTransactionId)
-#define TransactionIdEquals(id1, id2) ((id1) == (id2))
-#define TransactionIdPrecedes(id1, id2) ((id1) < (id2))
-#define TransactionIdPrecedesOrEquals(id1, id2) ((id1) <= (id2))
-#define TransactionIdFollows(id1, id2) ((id1) > (id2))
-#define TransactionIdFollowsOrEquals(id1, id2) ((id1) >= (id2))
+#define TransactionIdEquals(id1, id2) ((id1) == (id2))
#define TransactionIdStore(xid, dest) (*(dest) = (xid))
#define StoreInvalidTransactionId(dest) (*(dest) = InvalidTransactionId)
/* advance a transaction ID variable, handling wraparound correctly */
extern bool TransactionIdDidAbort(TransactionId transactionId);
extern void TransactionIdCommit(TransactionId transactionId);
extern void TransactionIdAbort(TransactionId transactionId);
+extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2);
+extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
+extern bool TransactionIdFollows(TransactionId id1, TransactionId id2);
+extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2);
/* in transam/varsup.c */
extern TransactionId GetNewTransactionId(void);
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: xact.h,v 1.35 2001/08/25 18:52:42 tgl Exp $
+ * $Id: xact.h,v 1.36 2001/08/26 16:56:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum xidin(PG_FUNCTION_ARGS);
extern Datum xidout(PG_FUNCTION_ARGS);
extern Datum xideq(PG_FUNCTION_ARGS);
+extern Datum xid_age(PG_FUNCTION_ARGS);
#endif /* XACT_H */
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: catversion.h,v 1.92 2001/08/25 18:52:42 tgl Exp $
+ * $Id: catversion.h,v 1.93 2001/08/26 16:56:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200108241
+#define CATALOG_VERSION_NO 200108251
#endif
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_attribute.h,v 1.75 2001/08/25 18:52:42 tgl Exp $
+ * $Id: pg_attribute.h,v 1.76 2001/08/26 16:56:00 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
DATA(insert ( 1262 datistemplate 16 0 1 4 0 -1 -1 t p f c f f));
DATA(insert ( 1262 datallowconn 16 0 1 5 0 -1 -1 t p f c f f));
DATA(insert ( 1262 datlastsysoid 26 0 4 6 0 -1 -1 t p f i f f));
+DATA(insert ( 1262 datvacuumxid 28 0 4 7 0 -1 -1 t p f i f f));
+DATA(insert ( 1262 datfrozenxid 28 0 4 8 0 -1 -1 t p f i f f));
/* do not mark datpath as toastable; GetRawDatabaseInfo won't cope */
-DATA(insert ( 1262 datpath 25 0 -1 7 0 -1 -1 f p f i f f));
+DATA(insert ( 1262 datpath 25 0 -1 9 0 -1 -1 f p f i f f));
DATA(insert ( 1262 ctid 27 0 6 -1 0 -1 -1 f p f i f f));
DATA(insert ( 1262 oid 26 0 4 -2 0 -1 -1 t p f i f f));
DATA(insert ( 1262 xmin 28 0 4 -3 0 -1 -1 t p f i f f));
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_class.h,v 1.53 2001/08/25 18:52:43 tgl Exp $
+ * $Id: pg_class.h,v 1.54 2001/08/26 16:56:01 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
DESCR("");
DATA(insert OID = 1261 ( pg_group 87 PGUID 0 1261 0 0 0 0 f t r 3 0 0 0 0 0 f f f f _null_ ));
DESCR("");
-DATA(insert OID = 1262 ( pg_database 88 PGUID 0 1262 0 0 0 0 f t r 7 0 0 0 0 0 t f f f _null_ ));
+DATA(insert OID = 1262 ( pg_database 88 PGUID 0 1262 0 0 0 0 f t r 9 0 0 0 0 0 t f f f _null_ ));
DESCR("");
DATA(insert OID = 376 ( pg_xactlock 0 PGUID 0 0 0 0 0 0 f t s 1 0 0 0 0 0 f f f f _null_ ));
DESCR("");
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_database.h,v 1.17 2001/03/22 04:00:38 momjian Exp $
+ * $Id: pg_database.h,v 1.18 2001/08/26 16:56:02 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
*/
CATALOG(pg_database) BOOTSTRAP
{
- NameData datname;
- int4 datdba;
- int4 encoding;
- bool datistemplate; /* allowed as template for CREATE
- * DATABASE? */
+ NameData datname; /* database name */
+ int4 datdba; /* sysid of owner */
+ int4 encoding; /* multibyte encoding, if any */
+ bool datistemplate; /* allowed as CREATE DATABASE template? */
bool datallowconn; /* new connections allowed? */
- Oid datlastsysoid;
+ Oid datlastsysoid; /* highest OID to consider a system OID */
+ TransactionId datvacuumxid; /* all XIDs before this are vacuumed */
+ TransactionId datfrozenxid; /* all XIDs before this are frozen */
text datpath; /* VARIABLE LENGTH FIELD */
} FormData_pg_database;
* compiler constants for pg_database
* ----------------
*/
-#define Natts_pg_database 7
+#define Natts_pg_database 9
#define Anum_pg_database_datname 1
#define Anum_pg_database_datdba 2
#define Anum_pg_database_encoding 3
#define Anum_pg_database_datistemplate 4
#define Anum_pg_database_datallowconn 5
#define Anum_pg_database_datlastsysoid 6
-#define Anum_pg_database_datpath 7
+#define Anum_pg_database_datvacuumxid 7
+#define Anum_pg_database_datfrozenxid 8
+#define Anum_pg_database_datpath 9
-DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 "" ));
+DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 "" ));
DESCR("Default template database");
#define TemplateDbOid 1
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_proc.h,v 1.206 2001/08/16 20:38:54 tgl Exp $
+ * $Id: pg_proc.h,v 1.207 2001/08/26 16:56:02 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
DESCR("convert abstime to date");
DATA(insert OID = 1180 ( abstime PGUID 12 f t f t 1 f 702 "1184" 100 0 0 100 timestamp_abstime - ));
DESCR("convert timestamp to abstime");
+DATA(insert OID = 1181 ( age PGUID 12 f t f t 1 f 23 "28" 100 0 0 100 xid_age - ));
+DESCR("age of a transaction ID, in transactions before current transaction");
DATA(insert OID = 1188 ( timestamp_mi PGUID 12 f t f t 2 f 1186 "1184 1184" 100 0 0 100 timestamp_mi - ));
DESCR("subtract");
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: vacuum.h,v 1.39 2001/07/18 00:46:25 tgl Exp $
+ * $Id: vacuum.h,v 1.40 2001/08/26 16:56:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
BlockNumber num_pages,
double num_tuples,
bool hasindex);
+extern void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
+ TransactionId *oldestXmin,
+ TransactionId *freezeLimit);
extern bool vac_is_partial_index(Relation indrel);
extern void vac_init_rusage(VacRUsage *ru0);
extern const char *vac_show_rusage(VacRUsage *ru0);
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.142 2001/08/21 16:36:06 tgl Exp $
+ * $Id: parsenodes.h,v 1.143 2001/08/26 16:56:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool vacuum; /* do VACUUM step */
bool full; /* do FULL (non-concurrent) vacuum */
bool analyze; /* do ANALYZE step */
+ bool freeze; /* early-freeze option */
bool verbose; /* print progress info */
char *vacrel; /* name of single table to process, or NULL */
List *va_cols; /* list of column names, or NIL for all */
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: sinval.h,v 1.20 2001/07/06 21:04:26 tgl Exp $
+ * $Id: sinval.h,v 1.21 2001/08/26 16:56:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
extern bool TransactionIdIsInProgress(TransactionId xid);
-extern void GetXmaxRecent(TransactionId *XmaxRecent);
+extern TransactionId GetOldestXmin(bool allDbs);
extern int CountActiveBackends(void);
/* Use "struct proc", not PROC, to avoid including proc.h here */
extern struct proc *BackendIdGetProc(BackendId procId);
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: tqual.h,v 1.33 2001/08/23 23:06:38 tgl Exp $
+ * $Id: tqual.h,v 1.34 2001/08/26 16:56:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Snapshot snapshot);
extern int HeapTupleSatisfiesUpdate(HeapTuple tuple);
extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTupleHeader tuple,
- TransactionId XmaxRecent);
+ TransactionId OldestXmin);
extern Snapshot GetSnapshotData(bool serializable);
extern void SetQuerySnapshot(void);