From bc7d37a525c02f4a0e983854c4222e9d063eeae2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 26 Aug 2001 16:56:03 +0000 Subject: [PATCH] Transaction IDs wrap around, per my proposal of 13-Aug-01. More documentation to come, but the code is all here. initdb forced. --- doc/src/sgml/catalogs.sgml | 27 ++- doc/src/sgml/ref/create_database.sgml | 16 +- doc/src/sgml/ref/vacuum.sgml | 49 +++-- src/backend/access/transam/clog.c | 22 +- src/backend/access/transam/transam.c | 67 +++++- src/backend/access/transam/xid.c | 47 +++-- src/backend/catalog/genbki.sh | 21 +- src/backend/catalog/index.c | 10 +- src/backend/commands/dbcommands.c | 44 ++-- src/backend/commands/vacuum.c | 280 +++++++++++++++++++++++--- src/backend/commands/vacuumlazy.c | 26 ++- src/backend/nodes/copyfuncs.c | 3 +- src/backend/nodes/equalfuncs.c | 4 +- src/backend/parser/gram.y | 36 ++-- src/backend/parser/keywords.c | 3 +- src/backend/storage/ipc/sinval.c | 42 ++-- src/backend/utils/time/tqual.c | 10 +- src/bin/initdb/initdb.sh | 4 +- src/include/access/transam.h | 13 +- src/include/access/xact.h | 3 +- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_attribute.h | 6 +- src/include/catalog/pg_class.h | 4 +- src/include/catalog/pg_database.h | 23 ++- src/include/catalog/pg_proc.h | 4 +- src/include/commands/vacuum.h | 5 +- src/include/nodes/parsenodes.h | 3 +- src/include/storage/sinval.h | 4 +- src/include/utils/tqual.h | 4 +- 29 files changed, 615 insertions(+), 169 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index e2c3139d85..39c05e24bd 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ @@ -840,11 +840,34 @@ oid - Last oid in existence after the database was created; useful + Last system OID in the database; useful particularly to pg_dump + + 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 diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index e128c1891d..20477ac191 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -1,5 +1,5 @@ @@ -284,6 +284,20 @@ comment from Olly; response from Thomas... 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 VACUUMs on + such a database. + See the Administrator's Guide for more information. + diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index e8374725b3..62e47a7086 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -1,5 +1,5 @@ @@ -20,11 +20,11 @@ Postgres documentation - 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 [, ...] ) ] ] @@ -46,6 +46,14 @@ VACUUM [ FULL ] [ VERBOSE ] ANALYZE [ table + + FREEZE + + + Selects aggressive freezing of tuples. + + + VERBOSE @@ -169,21 +177,38 @@ NOTICE: Index index: Pages 28; - 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. diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c index 07581e4444..a403838bd7 100644 --- a/src/backend/access/transam/clog.c +++ b/src/backend/access/transam/clog.c @@ -13,7 +13,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -762,8 +762,12 @@ ExtendCLOG(TransactionId newestXact) { 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); @@ -818,6 +822,18 @@ TruncateCLOG(TransactionId oldestXact) 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++) { diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c index 3364ed6633..2a73c045b7 100644 --- a/src/backend/access/transam/transam.c +++ b/src/backend/access/transam/transam.c @@ -8,7 +8,7 @@ * * * 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 @@ -236,3 +236,68 @@ TransactionIdAbort(TransactionId transactionId) 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); +} diff --git a/src/backend/access/transam/xid.c b/src/backend/access/transam/xid.c index 689fc33cea..babe7c9a48 100644 --- a/src/backend/access/transam/xid.c +++ b/src/backend/access/transam/xid.c @@ -6,23 +6,18 @@ * 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) @@ -30,9 +25,9 @@ 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 @@ -40,21 +35,15 @@ xidout(PG_FUNCTION_ARGS) { 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) @@ -64,3 +53,19 @@ 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)); +} diff --git a/src/backend/catalog/genbki.sh b/src/backend/catalog/genbki.sh index 3d51423af4..25c8d7d2d4 100644 --- a/src/backend/catalog/genbki.sh +++ b/src/backend/catalog/genbki.sh @@ -10,7 +10,7 @@ # # # 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. @@ -155,12 +155,14 @@ INDEXMAXKEYS4=`expr $INDEXMAXKEYS '*' 4` || exit 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' \ @@ -173,11 +175,14 @@ 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" \ diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index f11b3d9f31..9debf84cab 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * 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 @@ -1692,7 +1692,7 @@ IndexBuildHeapScan(Relation heapRelation, TupleTableSlot *slot; ExprContext *econtext; Snapshot snapshot; - TransactionId XmaxRecent; + TransactionId OldestXmin; /* * sanity checks @@ -1731,12 +1731,12 @@ IndexBuildHeapScan(Relation heapRelation, if (IsBootstrapProcessingMode()) { snapshot = SnapshotNow; - XmaxRecent = InvalidTransactionId; + OldestXmin = InvalidTransactionId; } else { snapshot = SnapshotAny; - GetXmaxRecent(&XmaxRecent); + OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared); } scan = heap_beginscan(heapRelation, /* relation */ @@ -1769,7 +1769,7 @@ IndexBuildHeapScan(Relation heapRelation, 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; diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index b20525e77d..f7f765d9d7 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -9,7 +9,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -39,8 +39,9 @@ /* 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); @@ -65,6 +66,8 @@ createdb(const char *dbname, const char *dbpath, 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; @@ -91,7 +94,7 @@ createdb(const char *dbname, const char *dbpath, * 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); /* @@ -101,7 +104,9 @@ createdb(const char *dbname, const char *dbpath, 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); @@ -208,8 +213,10 @@ createdb(const char *dbname, const char *dbpath, 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); } @@ -227,6 +234,8 @@ createdb(const char *dbname, const char *dbpath, 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 : "")); @@ -307,7 +316,7 @@ dropdb(const char *dbname) 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) @@ -397,13 +406,15 @@ dropdb(const char *dbname) 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); @@ -414,12 +425,11 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, 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; @@ -428,7 +438,7 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, /* 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 */ @@ -440,6 +450,12 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, /* 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) { @@ -462,7 +478,7 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, heap_endscan(scan); heap_close(relation, AccessShareLock); - return HeapTupleIsValid(tuple); + return gottuple; } static bool diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 9a9d90fa2c..fe72ff9602 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -21,11 +21,13 @@ #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" @@ -108,15 +110,24 @@ static MemoryContext vac_context = NULL; 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, @@ -213,7 +224,7 @@ vacuum(VacuumStmt *vacstmt) /* * Start up the vacuum cleaner. */ - vacuum_init(); + vacuum_init(vacstmt); /* * Process each selected relation. We are careful to process @@ -230,21 +241,8 @@ vacuum(VacuumStmt *vacstmt) 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); } /* @@ -268,20 +266,68 @@ vacuum(VacuumStmt *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 @@ -382,6 +428,52 @@ getrels(Name VacRelP, const char *stmttype) 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 @@ -449,6 +541,122 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, } +/* + * 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 * @@ -550,7 +758,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt) * 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); @@ -597,7 +805,7 @@ vacuum_rel(Oid relid, VacuumStmt *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 */ @@ -613,7 +821,8 @@ full_vacuum_rel(Relation onerel) IsSystemRelationName(RelationGetRelationName(onerel))) reindex = true; - GetXmaxRecent(&XmaxRecent); + vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared, + &OldestXmin, &FreezeLimit); /* * Set up statistics-gathering machinery. @@ -845,12 +1054,25 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, 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: /* @@ -1312,7 +1534,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, * 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))))) @@ -1362,7 +1584,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, /* * 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 @@ -1430,7 +1652,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, /* 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 */ diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index bdde611413..f525ecaf36 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -31,7 +31,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -94,7 +94,8 @@ typedef struct LVRelStats static int MESSAGE_LEVEL; /* message level */ -static TransactionId XmaxRecent; +static TransactionId OldestXmin; +static TransactionId FreezeLimit; /* non-export function prototypes */ @@ -143,7 +144,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) 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)); @@ -307,12 +309,25 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, 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: /* @@ -783,12 +798,13 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) 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: /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 26da8d4793..cf9ad2e983 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -2246,6 +2246,7 @@ _copyVacuumStmt(VacuumStmt *from) 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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c2cd2109fe..9dce1f61b4 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -20,7 +20,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -1116,6 +1116,8 @@ _equalVacuumStmt(VacuumStmt *a, VacuumStmt *b) 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)) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 84ef63e629..8d5e9abb07 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * 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 @@ -215,7 +215,7 @@ static void doNegateFloat(Value *v); %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, @@ -346,7 +346,7 @@ static void doNegateFloat(Value *v); 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, @@ -3082,34 +3082,37 @@ ClusterStmt: CLUSTER index_name ON relation_name * *****************************************************************************/ -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; } ; @@ -3120,6 +3123,7 @@ AnalyzeStmt: analyze_keyword opt_verbose n->vacuum = false; n->analyze = true; n->full = false; + n->freeze = false; n->verbose = $2; n->vacrel = NULL; n->va_cols = NIL; @@ -3131,6 +3135,7 @@ AnalyzeStmt: analyze_keyword opt_verbose n->vacuum = false; n->analyze = true; n->full = false; + n->freeze = false; n->verbose = $2; n->vacrel = $3; n->va_cols = $4; @@ -3150,6 +3155,10 @@ opt_full: FULL { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; +opt_freeze: FREEZE { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } + ; + opt_name_list: '(' name_list ')' { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; @@ -5615,6 +5624,7 @@ TokenId: ABSOLUTE { $$ = "absolute"; } | DROP { $$ = "drop"; } | EACH { $$ = "each"; } | ENCODING { $$ = "encoding"; } + | ENCRYPTED { $$ = "encrypted"; } | ESCAPE { $$ = "escape"; } | EXCLUSIVE { $$ = "exclusive"; } | EXECUTE { $$ = "execute"; } @@ -5693,6 +5703,7 @@ TokenId: ABSOLUTE { $$ = "absolute"; } | TRUNCATE { $$ = "truncate"; } | TRUSTED { $$ = "trusted"; } | TYPE_P { $$ = "type"; } + | UNENCRYPTED { $$ = "unencrypted"; } | UNLISTEN { $$ = "unlisten"; } | UNTIL { $$ = "until"; } | UPDATE { $$ = "update"; } @@ -5753,7 +5764,6 @@ ColLabel: ColId { $$ = $1; } | DISTINCT { $$ = "distinct"; } | DO { $$ = "do"; } | ELSE { $$ = "else"; } - | ENCRYPTED { $$ = "encrypted"; } | END_TRANS { $$ = "end"; } | EXCEPT { $$ = "except"; } | EXISTS { $$ = "exists"; } @@ -5763,6 +5773,7 @@ ColLabel: ColId { $$ = $1; } | FLOAT { $$ = "float"; } | FOR { $$ = "for"; } | FOREIGN { $$ = "foreign"; } + | FREEZE { $$ = "freeze"; } | FROM { $$ = "from"; } | FULL { $$ = "full"; } | GLOBAL { $$ = "global"; } @@ -5825,7 +5836,6 @@ ColLabel: ColId { $$ = $1; } | TRANSACTION { $$ = "transaction"; } | TRIM { $$ = "trim"; } | TRUE_P { $$ = "true"; } - | UNENCRYPTED { $$ = "unencrypted"; } | UNION { $$ = "union"; } | UNIQUE { $$ = "unique"; } | UNKNOWN { $$ = "unknown"; } diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 5c1427da03..bff257a545 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -118,6 +118,7 @@ static ScanKeyword ScanKeywords[] = { {"force", FORCE}, {"foreign", FOREIGN}, {"forward", FORWARD}, + {"freeze", FREEZE}, {"from", FROM}, {"full", FULL}, {"function", FUNCTION}, diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c index 985577c4a6..1d43b1ead4 100644 --- a/src/backend/storage/ipc/sinval.c +++ b/src/backend/storage/ipc/sinval.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,8 @@ #include "storage/sinval.h" #include "storage/sinvaladt.h" #include "utils/tqual.h" +#include "miscadmin.h" + SPINLOCK SInvalLock = (SPINLOCK) NULL; @@ -210,17 +212,23 @@ TransactionIdIsInProgress(TransactionId xid) } /* - * 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; @@ -238,24 +246,28 @@ GetXmaxRecent(TransactionId *XmaxRecent) 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; } /*---------- diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 5f5699c445..f0536d7611 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -592,8 +592,8 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot) * 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. * @@ -603,7 +603,7 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot) * 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? @@ -712,7 +712,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId XmaxRecent) 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; diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index bbb014391a..11799e7049 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # 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 $ # #------------------------------------------------------------------------- @@ -841,7 +841,7 @@ echo "ok" $ECHO_N "vacuuming database template1... "$ECHO_C "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <= 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 */ @@ -105,6 +102,10 @@ extern bool TransactionIdDidCommit(TransactionId transactionId); 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); diff --git a/src/include/access/xact.h b/src/include/access/xact.h index a9c7b674a9..55c0435117 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -130,5 +130,6 @@ extern void xact_desc(char *buf, uint8 xl_info, char *rec); 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 */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 58d43a6529..0b67a37ad2 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200108241 +#define CATALOG_VERSION_NO 200108251 #endif diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index eb1ba77235..826d2b8ea8 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -8,7 +8,7 @@ * 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 @@ -276,8 +276,10 @@ DATA(insert ( 1262 encoding 23 0 4 3 0 -1 -1 t p f i f f)); 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)); diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 4f2708e3bd..2d0103a87b 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -8,7 +8,7 @@ * 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 @@ -142,7 +142,7 @@ DATA(insert OID = 1260 ( pg_shadow 86 PGUID 0 1260 0 0 0 0 f t r 8 0 0 0 0 0 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(""); diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h index ffc83da3b4..f3e0fbd99b 100644 --- a/src/include/catalog/pg_database.h +++ b/src/include/catalog/pg_database.h @@ -8,7 +8,7 @@ * 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 @@ -33,13 +33,14 @@ */ 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; @@ -54,16 +55,18 @@ typedef FormData_pg_database *Form_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 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2a48dec68..c8919ff683 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * 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 @@ -1450,6 +1450,8 @@ DATA(insert OID = 1179 ( date PGUID 12 f t f t 1 f 1082 "702" 100 0 0 100 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"); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 0d362bb180..5d1541ef3b 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -44,6 +44,9 @@ extern void vac_update_relstats(Oid relid, 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); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index a961cb5af4..315ad113ee 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -694,6 +694,7 @@ typedef struct VacuumStmt 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 */ diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h index 11aad60957..2e1ac7bfb1 100644 --- a/src/include/storage/sinval.h +++ b/src/include/storage/sinval.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -76,7 +76,7 @@ extern void ReceiveSharedInvalidMessages( 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); diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h index bff437f540..cc238e7457 100644 --- a/src/include/utils/tqual.h +++ b/src/include/utils/tqual.h @@ -8,7 +8,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -104,7 +104,7 @@ extern bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, 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); -- 2.40.0