diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index e2c3139d8534237bd9d13f8452f8942f1b74fc36..39c05e24bd8193056af42341e3b4646d0412cb69 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ <!-- Documentation of the system catalogs, directed toward PostgreSQL developers - $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.21 2001/08/21 16:35:58 tgl Exp $ + $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.22 2001/08/26 16:55:58 tgl Exp $ --> <chapter id="catalogs"> @@ -840,11 +840,34 @@ <entry><type>oid</type></entry> <entry></entry> <entry> - Last oid in existence after the database was created; useful + Last system OID in the database; useful particularly to <application>pg_dump</application> </entry> </row> + <row> + <entry>datvacuumxid</entry> + <entry><type>xid</type></entry> + <entry></entry> + <entry> + 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. + </entry> + </row> + + <row> + <entry>datfrozenxid</entry> + <entry><type>xid</type></entry> + <entry></entry> + <entry> + All tuples inserted by transaction IDs before this one have been + relabeled with a permanent (<quote>frozen</>) transaction ID in this + database. This is useful to check whether a database must be vacuumed + soon to avoid transaction ID wraparound problems. + </entry> + </row> + <row> <entry>datpath</entry> <entry><type>text</type></entry> diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index e128c1891d0f097cadee4d51da2859ce196d2495..20477ac191c269038f39352cdd2f296b2b0deb9f 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_database.sgml,v 1.17 2000/11/15 19:43:39 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_database.sgml,v 1.18 2001/08/26 16:55:59 tgl Exp $ Postgres documentation --> @@ -284,6 +284,20 @@ comment from Olly; response from Thomas... simply by setting the flag false). The <literal>template0</literal> database is normally marked this way to prevent modification of it. </para> + + <para> + After preparing a template database, or making any changes to one, + it is a good idea to perform + <command>VACUUM FREEZE</> or <command>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 <quote>frozen</> and will not be subject to transaction + ID wraparound problems. This is particularly important for a database + that will have <literal>datallowconn</literal> set to false, since it + will be impossible to do routine maintenance <command>VACUUM</>s on + such a database. + See the Administrator's Guide for more information. + </para> </refsect2> </refsect1> diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index e8374725b34bd3cc8e98d224ca092a866bd6f5ec..62e47a7086dc2f9e0a2b18a36317b94d9e07c7da 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.17 2001/07/10 22:09:28 tgl Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.18 2001/08/26 16:55:59 tgl Exp $ Postgres documentation --> @@ -20,11 +20,11 @@ Postgres documentation </refnamediv> <refsynopsisdiv> <refsynopsisdivinfo> - <date>2001-07-10</date> + <date>2001-08-26</date> </refsynopsisdivinfo> <synopsis> -VACUUM [ FULL ] [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> ] -VACUUM [ FULL ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ] +VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> ] +VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ] </synopsis> <refsect2 id="R2-SQL-VACUUM-1"> @@ -46,6 +46,14 @@ VACUUM [ FULL ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</repl </para> </listitem> </varlistentry> + <varlistentry> + <term>FREEZE</term> + <listitem> + <para> + Selects aggressive <quote>freezing</quote> of tuples. + </para> + </listitem> + </varlistentry> <varlistentry> <term>VERBOSE</term> <listitem> @@ -169,21 +177,38 @@ NOTICE: Index <replaceable class="PARAMETER">index</replaceable>: Pages 28; </para> <para> - Plain <command>VACUUM</command> simply reclaims space and makes it + <command>VACUUM ANALYZE</command> performs a <command>VACUUM</command> + and then an <command>ANALYZE</command> for each selected table. This + is a handy combination form for routine maintenance scripts. See + <xref linkend="sql-analyze" endterm="sql-analyze-title"> + for more details about its processing. + </para> + + <para> + Plain <command>VACUUM</command> (without <literal>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. <command>VACUUM FULL</command> 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. </para> <para> - <command>VACUUM ANALYZE</command> performs a <command>VACUUM</command> - and then an <command>ANALYZE</command> for each selected table. This - is a handy combination form for routine maintenance scripts. See - <xref linkend="sql-analyze" endterm="sql-analyze-title"> - for more details about its processing. + <command>FREEZE</command> is a special-purpose option that + causes tuples to be marked <quote>frozen</quote> 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 <quote>frozen</> and will not be + subject to transaction ID wraparound problems, no matter how long the + database is left un-vacuumed. + <command>FREEZE</command> 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 <command>VACUUM</> operations. + See <xref linkend="sql-createdatabase" endterm="sql-createdatabase-title"> + for details. </para> <refsect2 id="R2-SQL-VACUUM-3"> diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c index 07581e4444a875e08f562e9f89bdd49fff966135..a403838bd792ec086f1a517db5bea8b3604728f7 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 3364ed66337b9b709defd09c31b2d45f1cb8a066..2a73c045b76ad069ac8940fc4cf56f1fcff85b78 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 689fc33ceaff5d65edec367f860e1a29af5ee372..babe7c9a48bfcad032e480ca8a3f0446dfa2fb8b 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 <limits.h> + #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 3d51423af426b0e920bf42e8ec4dc87d9928d710..25c8d7d2d4d3873c27be82feeeae4f731720c2d6 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 f11b3d9f31921909ccfcf59416e8359bbb6bda20..9debf84cab4385692936b221f7e68342eba3856a 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 b20525e77d949ea63f0ad23549c7d400f5b53c34..f7f765d9d7a0b70284d13eb6ad25cf664fc18e7b 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 9a9d90fa2cf1e5b8bd95e0dab2dcc017c19b5bb7..fe72ff96020f3d0e1c5c94e32d0007d3dab6aa34 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 <unistd.h> +#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 bdde6114133b14b4857de5b721699ba2219a6e1f..f525ecaf36c609bfafad2d1b767bd27d0259b517 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 26da8d479370eb68696652447a5032a873d691d2..cf9ad2e983c5967e3984ff11b4b277806547e115 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 c2cd2109fe97f9025a7642a989bfaae24aebdd58..9dce1f61b46111754d4b03cd5b4dc06b10578a08 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 84ef63e6295e51517a9a6b23fe26e292be65dfc2..8d5e9abb07d9ff6844ddcc4c383168e7e630ceb5 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 <boolean> opt_binary, opt_using, opt_instead, opt_cursor %type <boolean> opt_with_copy, index_opt_unique, opt_verbose, opt_full -%type <boolean> analyze_keyword +%type <boolean> opt_freeze, analyze_keyword %type <ival> 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 5c1427da03ec5c645c4c715d6146ab6f78d3b7d3..bff257a545522c64c4507a2c0179b8120a745f95 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 985577c4a6c571c32985bf6f3b51432328fa67b5..1d43b1ead4476274032b5b2b64e8ab411ef2acd5 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 5f5699c4458e9e5179c72c9805f76bbe72ff9970..f0536d761186ef318f3e8d4c836ad32e81fe6ce1 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 bbb014391a489498d19a6998737aadc79c7d409b..11799e7049074fc440d307e555816a23ed21f217 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 <<EOF -VACUUM FULL ANALYZE; +VACUUM FULL FREEZE; EOF if [ "$?" -ne 0 ]; then exit_nicely diff --git a/src/include/access/transam.h b/src/include/access/transam.h index f0d213361ac73a8689c6fd7149262e02a14ab1a8..5ce62d289f980790596749374aca69ab133a63c4 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.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: 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 $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,7 @@ #define BootstrapTransactionId ((TransactionId) 1) #define FrozenTransactionId ((TransactionId) 2) #define FirstNormalTransactionId ((TransactionId) 3) +#define MaxTransactionId ((TransactionId) 0xFFFFFFFF) /* ---------------- * transaction ID manipulation macros @@ -38,11 +39,7 @@ */ #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 */ @@ -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 a9c7b674a95a22e48882bceb2995912b15ac1a11..55c043511778e8edaa455808baa430c2fbb05f28 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 58d43a652931c6fd76dfacb96299123152ceb38a..0b67a37ad22aedd311ee827cd62f35f67f5b0a28 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 eb1ba77235e87365d54ab4d173e17151ebed7add..826d2b8ea849517029f29797398d269ec21572de 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 4f2708e3bd08f045126bc72beb4a609de5176e16..2d0103a87b8397aa8ca117887018e548c3d94c1c 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 ffc83da3b4cfc7ccd08dbcc9c896660d0b1ca82b..f3e0fbd99b7ba0072d3ac393e180849d5117e42a 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 e2a48dec681fea3550cfa7ed0687ec576a77668d..c8919ff683314111ef9f3bce33c051d868787cc8 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 0d362bb1801ef58a960f5968b44c9f71bc304877..5d1541ef3ba233f9f9f3467f8786b0a15ba6da46 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 a961cb5af4f85781a0eae36e5659acc4435c2897..315ad113ee766c5a1e53a5575f084e1aa2a47f5a 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 11aad6095738d8d3f2f151a00d79e4e318eb9a05..2e1ac7bfb155bcfa824e94877c3184cd487fcc5b 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 bff437f540bd7d9211b230a5135c90bbac8793e9..cc238e7457bd0a4f13987da69bb62587a8af9b3c 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);