Index: doc/src/sgml/catalogs.sgml
===================================================================
RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/catalogs.sgml,v
retrieving revision 2.115
diff -c -r2.115 catalogs.sgml
*** doc/src/sgml/catalogs.sgml 4 Nov 2005 23:13:59 -0000 2.115
--- doc/src/sgml/catalogs.sgml 14 Nov 2005 23:25:57 -0000
***************
*** 1613,1618 ****
--- 1613,1642 ----
+ relminxid
+ xid
+
+
+ The minimum transaction ID present in all rows in this table. This
+ value is used to determine the database-global
+ pg_database>.datminxid> value.
+
+
+
+
+ relvacuumxid
+ xid
+
+
+ The transaction ID that was used as cleaning point as of the last vacuum
+ operation. All rows inserted, updated or deleted in this table by
+ transactions whose IDs are below this one have been marked as known good
+ or deleted. This is used to determine the database-global
+ pg_database>.datvacuumxid> value.
+
+
+
+
relacl
aclitem[]
***************
*** 1980,2004 ****
! datvacuumxid
xid
! All rows 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 rows 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 wrap-around problems.
--- 2004,2034 ----
! datminxid
xid
! The minimum transaction ID present in all tables in this database.
! All rows inserted by transaction IDs before this one have been
! relabeled with a permanent (frozen>) transaction ID in this
! database. This is used to check whether a database must be
! vacuumed soon to avoid transaction ID wrap-around problems.
! If InvalidTransactionId, then the minimum is unknown and can be
! determined by scanning pg_class>.relminxid>.
! datvacuumxid
xid
! The transaction ID that was used as cleaning point as of the last vacuum
! operation. All rows inserted, updated or deleted by transactions whose
! IDs are below this one have been marked as known good or deleted. This
! is used to determine when commit-log space can be recycled.
! If InvalidTransactionId, then the minimum is unknown and can be
! determined by scanning pg_class>.relvacuumxid>.
Index: src/backend/access/transam/varsup.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/varsup.c,v
retrieving revision 1.68
diff -c -r1.68 varsup.c
*** src/backend/access/transam/varsup.c 29 Oct 2005 00:31:50 -0000 1.68
--- src/backend/access/transam/varsup.c 7 Nov 2005 12:06:20 -0000
***************
*** 168,178 ****
/*
* Determine the last safe XID to allocate given the currently oldest
! * datfrozenxid (ie, the oldest XID that might exist in any database
* of our cluster).
*/
void
! SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
Name oldest_datname)
{
TransactionId xidWarnLimit;
--- 168,178 ----
/*
* Determine the last safe XID to allocate given the currently oldest
! * datminxid (ie, the oldest XID that might exist in any database
* of our cluster).
*/
void
! SetTransactionIdLimit(TransactionId oldest_datminxid,
Name oldest_datname)
{
TransactionId xidWarnLimit;
***************
*** 180,195 ****
TransactionId xidWrapLimit;
TransactionId curXid;
! Assert(TransactionIdIsValid(oldest_datfrozenxid));
/*
* The place where we actually get into deep trouble is halfway around
! * from the oldest potentially-existing XID. (This calculation is
! * probably off by one or two counts, because the special XIDs reduce the
! * size of the loop a little bit. But we throw in plenty of slop below,
! * so it doesn't matter.)
*/
! xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
if (xidWrapLimit < FirstNormalTransactionId)
xidWrapLimit += FirstNormalTransactionId;
--- 180,195 ----
TransactionId xidWrapLimit;
TransactionId curXid;
! Assert(TransactionIdIsValid(oldest_datminxid));
/*
* The place where we actually get into deep trouble is halfway around
! * from the oldest existing XID. (This calculation is probably off by one
! * or two counts, because the special XIDs reduce the size of the loop a
! * little bit. But we throw in plenty of slop below, so it doesn't
! * matter.)
*/
! xidWrapLimit = oldest_datminxid + (MaxTransactionId >> 1);
if (xidWrapLimit < FirstNormalTransactionId)
xidWrapLimit += FirstNormalTransactionId;
Index: src/backend/catalog/heap.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/heap.c,v
retrieving revision 1.292
diff -c -r1.292 heap.c
*** src/backend/catalog/heap.c 18 Oct 2005 01:06:23 -0000 1.292
--- src/backend/catalog/heap.c 14 Nov 2005 23:09:48 -0000
***************
*** 38,43 ****
--- 38,44 ----
#include "catalog/indexing.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_constraint.h"
+ #include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_statistic.h"
***************
*** 576,591 ****
--- 577,601 ----
/* The relation is real, but as yet empty */
new_rel_reltup->relpages = 0;
new_rel_reltup->reltuples = 0;
+ /* Use the minimum Xid that could put tuples in the table */
+ new_rel_reltup->relminxid = RecentXmin;
+ new_rel_reltup->relvacuumxid = RecentXmin;
break;
case RELKIND_SEQUENCE:
/* Sequences always have a known size */
new_rel_reltup->relpages = 1;
new_rel_reltup->reltuples = 1;
+ /* Sequences will never have Xids */
+ new_rel_reltup->relminxid = InvalidTransactionId;
+ new_rel_reltup->relvacuumxid = InvalidTransactionId;
break;
default:
/* Views, etc, have no disk storage */
new_rel_reltup->relpages = 0;
new_rel_reltup->reltuples = 0;
+ /* Views, etc, won't have Xids either */
+ new_rel_reltup->relminxid = InvalidTransactionId;
+ new_rel_reltup->relvacuumxid = InvalidTransactionId;
break;
}
***************
*** 1125,1130 ****
--- 1135,1189 ----
}
/*
+ * Invalidate (set to invalid) the datminxid and/or datvacuumxid of a database,
+ * when we drop the table that has the minimum Xids.
+ */
+ static void
+ InvalidateDbMinxid(TransactionId relminxid, TransactionId relvacuumxid)
+ {
+ Oid dbid = MyDatabaseId;
+ Relation dbrel;
+ HeapTuple tuple;
+ HeapScanDesc scan;
+ Form_pg_database dbform;
+ ScanKeyData entry[1];
+
+ dbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
+
+ /* Must use a heap scan, since there's no syscache for pg_database */
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(dbid));
+
+ scan = heap_beginscan(dbrel, SnapshotNow, 1, entry);
+
+ tuple = heap_getnext(scan, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for database %u", dbid);
+
+ /* Ensure no one does this at the same time */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
+
+ dbform = (Form_pg_database) GETSTRUCT(tuple);
+
+ /* Note we don't actually care if dbform->datminxid is already Invalid */
+ if (TransactionIdEquals(dbform->datminxid, relminxid))
+ dbform->datminxid = InvalidTransactionId;
+
+ /* ditto */
+ if (TransactionIdEquals(dbform->datvacuumxid, relvacuumxid))
+ dbform->datvacuumxid = InvalidTransactionId;
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ heap_endscan(scan);
+
+ heap_close(dbrel, RowExclusiveLock);
+ }
+
+ /*
* heap_drop_with_catalog - removes specified relation from catalogs
*
* Note that this routine is not responsible for dropping objects that are
***************
*** 1153,1158 ****
--- 1212,1221 ----
smgrscheduleunlink(rel->rd_smgr, rel->rd_istemp);
}
+ /* Invalidate pg_database.datminxid, if appropiate */
+ if ((rel->rd_rel->relkind == RELKIND_RELATION) && (!rel->rd_istemp))
+ InvalidateDbMinxid(rel->rd_rel->relminxid, rel->rd_rel->relvacuumxid);
+
/*
* Close relcache entry, but *keep* AccessExclusiveLock on the relation
* until transaction commit. This ensures no one else will try to do
Index: src/backend/commands/analyze.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/analyze.c,v
retrieving revision 1.89
diff -c -r1.89 analyze.c
*** src/backend/commands/analyze.c 15 Oct 2005 02:49:15 -0000 1.89
--- src/backend/commands/analyze.c 14 Nov 2005 21:17:38 -0000
***************
*** 419,426 ****
{
vac_update_relstats(RelationGetRelid(onerel),
RelationGetNumberOfBlocks(onerel),
! totalrows,
! hasindex);
for (ind = 0; ind < nindexes; ind++)
{
AnlIndexData *thisdata = &indexdata[ind];
--- 419,429 ----
{
vac_update_relstats(RelationGetRelid(onerel),
RelationGetNumberOfBlocks(onerel),
! totalrows, hasindex, false,
! InvalidTransactionId,
! InvalidTransactionId,
! NULL, NULL);
!
for (ind = 0; ind < nindexes; ind++)
{
AnlIndexData *thisdata = &indexdata[ind];
***************
*** 430,436 ****
vac_update_relstats(RelationGetRelid(Irel[ind]),
RelationGetNumberOfBlocks(Irel[ind]),
totalindexrows,
! false);
}
/* report results to the stats collector, too */
--- 433,440 ----
vac_update_relstats(RelationGetRelid(Irel[ind]),
RelationGetNumberOfBlocks(Irel[ind]),
totalindexrows,
! false, false, InvalidTransactionId,
! InvalidTransactionId, NULL, NULL);
}
/* report results to the stats collector, too */
Index: src/backend/commands/dbcommands.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/dbcommands.c,v
retrieving revision 1.173
diff -c -r1.173 dbcommands.c
*** src/backend/commands/dbcommands.c 15 Oct 2005 02:49:15 -0000 1.173
--- src/backend/commands/dbcommands.c 7 Nov 2005 12:10:03 -0000
***************
*** 56,62 ****
static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
! TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
Oid *dbTablespace);
static bool have_createdb_privilege(void);
static void remove_dbtablespaces(Oid db_id);
--- 56,62 ----
static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
! TransactionId *dbMinXidP,
Oid *dbTablespace);
static bool have_createdb_privilege(void);
static void remove_dbtablespaces(Oid db_id);
***************
*** 76,83 ****
bool src_istemplate;
bool src_allowconn;
Oid src_lastsysoid;
! TransactionId src_vacuumxid;
! TransactionId src_frozenxid;
Oid src_deftablespace;
volatile Oid dst_deftablespace;
volatile Relation pg_database_rel;
--- 76,82 ----
bool src_istemplate;
bool src_allowconn;
Oid src_lastsysoid;
! TransactionId src_minxid;
Oid src_deftablespace;
volatile Oid dst_deftablespace;
volatile Relation pg_database_rel;
***************
*** 224,230 ****
* grab the exclusive lock.
*/
if (get_db_info(dbname, NULL, NULL, NULL,
! NULL, NULL, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_DATABASE),
errmsg("database \"%s\" already exists", dbname)));
--- 223,229 ----
* grab the exclusive lock.
*/
if (get_db_info(dbname, NULL, NULL, NULL,
! NULL, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_DATABASE),
errmsg("database \"%s\" already exists", dbname)));
***************
*** 237,243 ****
if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding,
&src_istemplate, &src_allowconn, &src_lastsysoid,
! &src_vacuumxid, &src_frozenxid, &src_deftablespace))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("template database \"%s\" does not exist", dbtemplate)));
--- 236,242 ----
if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding,
&src_istemplate, &src_allowconn, &src_lastsysoid,
! &src_minxid, &src_deftablespace))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("template database \"%s\" does not exist", dbtemplate)));
***************
*** 336,349 ****
}
/*
! * Normally we mark the new database with the same datvacuumxid and
! * datfrozenxid as the source. However, if the source is not allowing
! * connections then we assume it is fully frozen, and we can set the
! * current transaction ID as the xid limits. This avoids immediately
! * starting to generate warnings after cloning template0.
*/
if (!src_allowconn)
! src_vacuumxid = src_frozenxid = GetCurrentTransactionId();
/*
* Preassign OID for pg_database tuple, so that we can compute db path.
--- 335,348 ----
}
/*
! * Normally we mark the new database with the same datminxid as the source.
! * However, if the source is not allowing connections then we assume it is
! * fully frozen, and we can set the current transaction ID as the xid
! * limit. This avoids immediately starting to generate warnings after
! * cloning template0.
*/
if (!src_allowconn)
! src_minxid = GetCurrentTransactionId();
/*
* Preassign OID for pg_database tuple, so that we can compute db path.
***************
*** 441,447 ****
/* Check to see if someone else created same DB name meanwhile. */
if (get_db_info(dbname, NULL, NULL, NULL,
! NULL, NULL, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_DATABASE),
errmsg("database \"%s\" already exists", dbname)));
--- 440,446 ----
/* Check to see if someone else created same DB name meanwhile. */
if (get_db_info(dbname, NULL, NULL, NULL,
! NULL, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_DATABASE),
errmsg("database \"%s\" already exists", dbname)));
***************
*** 463,470 ****
new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
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);
new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
/*
--- 462,468 ----
new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
! new_record[Anum_pg_database_datminxid - 1] = TransactionIdGetDatum(src_minxid);
new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
/*
***************
*** 584,590 ****
pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock);
if (!get_db_info(dbname, &db_id, NULL, NULL,
! &db_istemplate, NULL, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist", dbname)));
--- 582,588 ----
pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock);
if (!get_db_info(dbname, &db_id, NULL, NULL,
! &db_istemplate, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist", dbname)));
***************
*** 1082,1089 ****
static bool
get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
! Oid *dbLastSysOidP,
! TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
Oid *dbTablespace)
{
Relation relation;
--- 1080,1086 ----
static bool
get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
! Oid *dbLastSysOidP, TransactionId *dbMinXidP,
Oid *dbTablespace)
{
Relation relation;
***************
*** 1130,1141 ****
/* 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;
/* default tablespace for this database */
if (dbTablespace)
*dbTablespace = dbform->dattablespace;
--- 1127,1135 ----
/* last system OID used in database */
if (dbLastSysOidP)
*dbLastSysOidP = dbform->datlastsysoid;
! /* limit of min XIDs */
! if (dbMinXidP)
! *dbMinXidP = dbform->datminxid;
/* default tablespace for this database */
if (dbTablespace)
*dbTablespace = dbform->dattablespace;
Index: src/backend/commands/vacuum.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuum.c,v
retrieving revision 1.317
diff -c -r1.317 vacuum.c
*** src/backend/commands/vacuum.c 15 Oct 2005 02:49:16 -0000 1.317
--- src/backend/commands/vacuum.c 14 Nov 2005 21:53:16 -0000
***************
*** 125,130 ****
--- 125,131 ----
Size min_tlen;
Size max_tlen;
bool hasindex;
+ TransactionId minxid; /* Minimum Xid present anywhere on table */
/* vtlinks array for tuple chain following - sorted by new_tid */
int num_vtlinks;
VTupleLink vtlinks;
***************
*** 192,216 ****
static int elevel = -1;
- static TransactionId OldestXmin;
- static TransactionId FreezeLimit;
-
/* non-export function prototypes */
static List *get_rel_oids(List *relids, const RangeVar *vacrel,
! const char *stmttype);
! static void vac_update_dbstats(Oid dbid,
! TransactionId vacuumXID,
! TransactionId frozenXID);
! static void vac_truncate_clog(TransactionId vacuumXID,
! TransactionId frozenXID);
! static bool vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind);
! 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,
VacPageList vacuum_pages, VacPageList fraged_pages,
! int nindexes, Relation *Irel);
static void move_chain_tuple(Relation rel,
Buffer old_buf, Page old_page, HeapTuple old_tup,
Buffer dst_buf, Page dst_page, VacPage dst_vacpage,
--- 193,216 ----
static int elevel = -1;
/* non-export function prototypes */
static List *get_rel_oids(List *relids, const RangeVar *vacrel,
! const char *stmttype);
! static void vac_update_dbxids(Oid dbid, TransactionId prevminXid,
! TransactionId prevvacXid);
! static void vac_truncate_clog(TransactionId minXID);
! static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind,
! TransactionId *prevmin, TransactionId *prevvac);
! static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
! TransactionId *relminxid, TransactionId *relvacuumxid);
!
static void scan_heap(VRelStats *vacrelstats, Relation onerel,
! VacPageList vacuum_pages, VacPageList fraged_pages,
! TransactionId FreezeLimit, TransactionId OldestXmin);
static void repair_frag(VRelStats *vacrelstats, Relation onerel,
VacPageList vacuum_pages, VacPageList fraged_pages,
! int nindexes, Relation *Irel, TransactionId OldestXmin);
static void move_chain_tuple(Relation rel,
Buffer old_buf, Page old_page, HeapTuple old_tup,
Buffer dst_buf, Page dst_page, VacPage dst_vacpage,
***************
*** 267,279 ****
vacuum(VacuumStmt *vacstmt, List *relids)
{
const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
- TransactionId initialOldestXmin = InvalidTransactionId;
- TransactionId initialFreezeLimit = InvalidTransactionId;
volatile MemoryContext anl_context = NULL;
volatile bool all_rels,
in_outer_xact,
use_own_xacts;
List *relations;
if (vacstmt->verbose)
elevel = INFO;
--- 267,281 ----
vacuum(VacuumStmt *vacstmt, List *relids)
{
const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
volatile MemoryContext anl_context = NULL;
volatile bool all_rels,
in_outer_xact,
use_own_xacts;
List *relations;
+ TransactionId prevmin,
+ prevvac;
+ TransactionId currmin = InvalidTransactionId,
+ currvac = InvalidTransactionId;
if (vacstmt->verbose)
elevel = INFO;
***************
*** 350,381 ****
*/
relations = get_rel_oids(relids, vacstmt->relation, stmttype);
- if (vacstmt->vacuum && all_rels)
- {
- /*
- * It's a database-wide VACUUM.
- *
- * 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);
- }
-
/*
* Decide whether we need to start/commit our own transactions.
*
--- 352,357 ----
***************
*** 444,451 ****
if (vacstmt->vacuum)
{
! if (!vacuum_rel(relid, vacstmt, RELKIND_RELATION))
! all_rels = false; /* forget about updating dbstats */
}
if (vacstmt->analyze)
{
--- 420,437 ----
if (vacstmt->vacuum)
{
! vacuum_rel(relid, vacstmt, RELKIND_RELATION, &prevmin,
! &prevvac);
!
! if (!TransactionIdIsValid(currmin) ||
! (TransactionIdIsNormal(prevmin) &&
! TransactionIdPrecedes(prevmin, currmin)))
! currmin = prevmin;
!
! if (!TransactionIdIsValid(currvac) ||
! (TransactionIdIsNormal(prevvac) &&
! TransactionIdPrecedes(prevvac, currvac)))
! currvac = prevvac;
}
if (vacstmt->analyze)
{
***************
*** 522,536 ****
PrintFreeSpaceMapStatistics(elevel);
/*
! * If we completed a database-wide VACUUM without skipping any
! * relations, update the database's pg_database row with info about
! * the transaction IDs used, and try to truncate pg_clog.
*/
! if (all_rels)
{
! vac_update_dbstats(MyDatabaseId,
! initialOldestXmin, initialFreezeLimit);
! vac_truncate_clog(initialOldestXmin, initialFreezeLimit);
}
}
--- 508,526 ----
PrintFreeSpaceMapStatistics(elevel);
/*
! * Skip these steps if the current minxid is InvalidTransactionId.
! * It shouldn't happen on normal operation, but it happens during
! * initdb.
*/
! if (TransactionIdIsValid(currmin))
{
! Assert(TransactionIdIsValid(currvac));
!
! /* Update pg_database.datminxid, if necessary. */
! vac_update_dbxids(MyDatabaseId, currmin, currvac);
!
! /* Try to truncate pg_clog. */
! vac_truncate_clog(currvac);
}
}
***************
*** 663,668 ****
--- 653,662 ----
* doing ANALYZE, but we always update these stats. This routine works
* for both index and heap relation entries in pg_class.
*
+ * relminxid and relvacuumxid are output parameters, and correspond
+ * to the values in pg_class that existed prior to the execution of
+ * this vacuum.
+ *
* We violate no-overwrite semantics here by storing new values for the
* statistics columns directly into the pg_class tuple that's already on
* the page. The reason for this is that if we updated these tuples in
***************
*** 676,682 ****
*/
void
vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
! bool hasindex)
{
Relation rd;
HeapTupleData rtup;
--- 670,678 ----
*/
void
vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
! bool hasindex, bool update_xids, TransactionId minxid,
! TransactionId vacuumxid, TransactionId *relminxid,
! TransactionId *relvacuumxid)
{
Relation rd;
HeapTupleData rtup;
***************
*** 708,713 ****
--- 704,721 ----
/* overwrite the existing statistics in the tuple */
pgcform = (Form_pg_class) GETSTRUCT(&rtup);
+
+ /* Set relminxid/relvacuumxid only if asked to */
+ if (update_xids)
+ {
+ AssertArg(relminxid != NULL && relvacuumxid != NULL);
+
+ *relminxid = pgcform->relminxid;
+ pgcform->relminxid = minxid;
+ *relvacuumxid = pgcform->relvacuumxid;
+ pgcform->relvacuumxid = vacuumxid;
+ }
+
pgcform->relpages = (int32) num_pages;
pgcform->reltuples = (float4) num_tuples;
pgcform->relhasindex = hasindex;
***************
*** 737,767 ****
/*
! * vac_update_dbstats() -- update statistics for one database
*
! * Update the whole-database statistics that are kept in its pg_database
! * row, and the flat-file copy of pg_database.
*
! * 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.
*
! * 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_open(DatabaseRelationId, RowExclusiveLock);
/* Must use a heap scan, since there's no syscache for pg_database */
ScanKeyInit(&entry[0],
--- 745,792 ----
/*
! * vac_update_dbxids() -- update the minimum Xid present in one database
*
! * Update pg_database's datminxid and datvacuumxid and the flat-file copy
! * of pg_database.
*
! * The prevminXid argument (resp. prevvacXid) is the minimum of all the
! * relminxids (resp. relvacuumxid) that the vacuumed table(s) had before
! * this vacuum operation.
! *
! * The Xids are updated to the minimum of all relminxid/relvacuumxid found
! * in pg_class, if this prevminXid/prevvacXid is found to be equal to the
! * current minimum -- that is, one of the processed tables was the one
! * holding the minimum back.
! *
! * Note that it's possible for the data in pg_database to be
! * InvalidTransactionId -- for example, if a table is dropped. This is
! * to cope with the possibility that the dropped table was the one with
! * the minimum. In this case, we need to search the minimum
! * inconditionally.
! *
! * We violate no-overwrite semantics here by storing a new value for the
! * statistic column directly into the tuple that's already on the page.
* As with vac_update_relstats, this avoids leaving dead tuples behind
* after a VACUUM.
*
! * This routine is shared by full and lazy VACUUM.
*/
static void
! vac_update_dbxids(Oid dbid, TransactionId prevminXid, TransactionId prevvacXid)
{
! Relation dbrel;
! ScanKeyData entry[1];
HeapScanDesc scan;
HeapTuple tuple;
Form_pg_database dbform;
+ TransactionId prevmin,
+ prevvac;
+
+ Assert(TransactionIdIsValid(prevminXid));
+ Assert(TransactionIdIsValid(prevvacXid));
! dbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
/* Must use a heap scan, since there's no syscache for pg_database */
ScanKeyInit(&entry[0],
***************
*** 769,826 ****
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(dbid));
! scan = heap_beginscan(relation, SnapshotNow, 1, entry);
tuple = heap_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for database %u", dbid);
! /* ensure no one else does this at the same time */
! LockBuffer(scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
dbform = (Form_pg_database) GETSTRUCT(tuple);
! /* overwrite the existing statistics in the tuple */
! dbform->datvacuumxid = vacuumXID;
! dbform->datfrozenxid = frozenXID;
! LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
! /* invalidate the tuple in the cache and write the buffer */
! CacheInvalidateHeapTuple(relation, tuple);
! WriteNoReleaseBuffer(scan->rs_cbuf);
! heap_endscan(scan);
! heap_close(relation, RowExclusiveLock);
! /* Mark the flat-file copy of pg_database for update at commit */
! database_file_update_needed();
! }
/*
* 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 update the XID wrap limit point maintained by varsup.c.
*
! * We also generate a warning if the system-wide oldest datfrozenxid
* seems to be in danger of wrapping around. This is a long-in-advance
* warning; if we start getting uncomfortably close, GetNewTransactionId
* will generate more-annoying warnings, and ultimately refuse to issue
* any more new XIDs.
*
! * 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)
{
TransactionId myXID = GetCurrentTransactionId();
Relation relation;
--- 794,903 ----
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(dbid));
! scan = heap_beginscan(dbrel, SnapshotNow, 1, entry);
tuple = heap_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for database %u", dbid);
! /* Ensure no one does this at the same time */
! LockSharedObject(DatabaseRelationId, dbid, 0, AccessExclusiveLock);
dbform = (Form_pg_database) GETSTRUCT(tuple);
+ prevmin = dbform->datminxid;
+ prevvac = dbform->datvacuumxid;
! /*
! * If the table we just vacuumed was holding one of the minima back, or any
! * of them was unset, update pg_database.
! */
! if (!TransactionIdIsValid(prevmin) ||
! !TransactionIdIsValid(prevvac) ||
! TransactionIdEquals(prevmin, prevminXid) ||
! TransactionIdEquals(prevvac, prevvacXid))
! {
! Relation classRel;
! SysScanDesc classScan;
! HeapTuple classTup;
! TransactionId newMinXid = InvalidTransactionId,
! newVacXid = InvalidTransactionId;
! /* scan pg_class searching for the minimum relminxid */
! classRel = heap_open(RelationRelationId, AccessShareLock);
! classScan = systable_beginscan(classRel, InvalidOid, false,
! SnapshotNow, 0, NULL);
! while ((classTup = systable_getnext(classScan)) != NULL)
! {
! Form_pg_class classForm;
! classForm = (Form_pg_class) GETSTRUCT(classTup);
! /* Only consider normal tables */
! if (classForm->relkind != RELKIND_RELATION)
! continue;
!
! if (!TransactionIdIsValid(newMinXid) ||
! (TransactionIdIsNormal(classForm->relminxid) &&
! TransactionIdPrecedes(classForm->relminxid, newMinXid)))
! newMinXid = classForm->relminxid;
!
! if (!TransactionIdIsValid(newVacXid) ||
! (TransactionIdIsNormal(classForm->relvacuumxid) &&
! TransactionIdPrecedes(classForm->relvacuumxid, newVacXid)))
! newVacXid = classForm->relvacuumxid;
! }
!
! systable_endscan(classScan);
! heap_close(classRel, AccessShareLock);
!
! Assert(TransactionIdIsValid(newMinXid));
! Assert(TransactionIdIsValid(newVacXid));
!
! LockBuffer(scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
!
! dbform->datminxid = newMinXid;
! dbform->datvacuumxid = newVacXid;
!
! /* invalidate the tuple in the cache and write the buffer */
! CacheInvalidateHeapTuple(dbrel, tuple);
! WriteNoReleaseBuffer(scan->rs_cbuf);
!
! LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
!
! /* Mark the flat-file copy of pg_database for update at commit */
! database_file_update_needed();
! }
+ UnlockSharedObject(DatabaseRelationId, dbid, 0, AccessExclusiveLock);
+
+ heap_endscan(scan);
+
+ heap_close(dbrel, RowExclusiveLock);
+ }
/*
* vac_truncate_clog() -- attempt to truncate the commit log
*
! * Truncate the transaction commit log (pg_clog) using the minimum
! * Xid found on pg_database. Also update the XID wrap limit point
! * maintained by varsup.c.
*
! * We also generate a warning if the system-wide oldest datminxid
* seems to be in danger of wrapping around. This is a long-in-advance
* warning; if we start getting uncomfortably close, GetNewTransactionId
* will generate more-annoying warnings, and ultimately refuse to issue
* any more new XIDs.
*
! * The passed XID is simply the one I just wrote into my pg_database
! * entry. It's used to initialize the "min" calculation.
*
! * This routine is shared by full and lazy VACUUM.
*/
static void
! vac_truncate_clog(TransactionId minXID)
{
TransactionId myXID = GetCurrentTransactionId();
Relation relation;
***************
*** 828,842 ****
HeapTuple tuple;
int32 age;
NameData oldest_datname;
! bool vacuumAlreadyWrapped = false;
! bool frozenAlreadyWrapped = false;
! /* init oldest_datname to sync with my frozenXID */
namestrcpy(&oldest_datname, get_database_name(MyDatabaseId));
/*
! * Note: the "already wrapped" cases should now be impossible due to the
! * defenses in GetNewTransactionId, but we keep them anyway.
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
--- 905,920 ----
HeapTuple tuple;
int32 age;
NameData oldest_datname;
! bool alreadyWrapped = false;
!
! Assert(TransactionIdIsValid(minXID));
! /* init oldest_datname to sync with my minXID */
namestrcpy(&oldest_datname, get_database_name(MyDatabaseId));
/*
! * Note: the "already wrapped" case should now be impossible due to the
! * defenses in GetNewTransactionId, but we keep it anyway.
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
***************
*** 851,870 ****
if (!dbform->datallowconn)
continue;
! if (TransactionIdIsNormal(dbform->datvacuumxid))
{
! if (TransactionIdPrecedes(myXID, dbform->datvacuumxid))
! vacuumAlreadyWrapped = true;
! else if (TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
! vacuumXID = dbform->datvacuumxid;
! }
! if (TransactionIdIsNormal(dbform->datfrozenxid))
! {
! if (TransactionIdPrecedes(myXID, dbform->datfrozenxid))
! frozenAlreadyWrapped = true;
! else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
{
! frozenXID = dbform->datfrozenxid;
namecpy(&oldest_datname, &dbform->datname);
}
}
--- 929,941 ----
if (!dbform->datallowconn)
continue;
! if (TransactionIdIsNormal(dbform->datminxid))
{
! if (TransactionIdPrecedes(myXID, dbform->datminxid))
! alreadyWrapped = true;
! else if (TransactionIdPrecedes(dbform->datminxid, minXID))
{
! minXID = dbform->datminxid;
namecpy(&oldest_datname, &dbform->datname);
}
}
***************
*** 875,899 ****
heap_close(relation, AccessShareLock);
/*
! * Do not truncate CLOG if we seem to have suffered wraparound already;
! * the computed minimum XID might be bogus.
! */
! if (vacuumAlreadyWrapped)
! {
! ereport(WARNING,
! (errmsg("some databases have not been vacuumed in over 2 billion transactions"),
! errdetail("You may have already suffered transaction-wraparound data loss.")));
! return;
! }
!
! /* Truncate CLOG to the oldest vacuumxid */
! TruncateCLOG(vacuumXID);
!
! /*
! * Do not update varsup.c if we seem to have suffered wraparound already;
! * the computed XID might be bogus.
*/
! if (frozenAlreadyWrapped)
{
ereport(WARNING,
(errmsg("some databases have not been vacuumed in over 1 billion transactions"),
--- 946,955 ----
heap_close(relation, AccessShareLock);
/*
! * Do not truncate CLOG or update varsup.c if we seem to have suffered
! * wraparound already; the computed XID might be bogus.
*/
! if (alreadyWrapped)
{
ereport(WARNING,
(errmsg("some databases have not been vacuumed in over 1 billion transactions"),
***************
*** 901,911 ****
return;
}
/* Update the wrap limit for GetNewTransactionId */
! SetTransactionIdLimit(frozenXID, &oldest_datname);
/* Give warning about impending wraparound problems */
! age = (int32) (myXID - frozenXID);
if (age > (int32) ((MaxTransactionId >> 3) * 3))
ereport(WARNING,
(errmsg("database \"%s\" must be vacuumed within %u transactions",
--- 957,970 ----
return;
}
+ /* Truncate CLOG to the oldest minxid */
+ TruncateCLOG(minXID);
+
/* Update the wrap limit for GetNewTransactionId */
! SetTransactionIdLimit(minXID, &oldest_datname);
/* Give warning about impending wraparound problems */
! age = (int32) (myXID - minXID);
if (age > (int32) ((MaxTransactionId >> 3) * 3))
ereport(WARNING,
(errmsg("database \"%s\" must be vacuumed within %u transactions",
***************
*** 927,953 ****
/*
* vacuum_rel() -- vacuum one heap relation
*
- * Returns TRUE if we actually processed the relation (or can ignore it
- * for some reason), FALSE if we failed to process it due to permissions
- * or other reasons. (A FALSE result really means that some data
- * may have been left unvacuumed, so we can't update XID stats.)
- *
* Doing one heap at a time incurs extra overhead, since we need to
* check that the heap exists again just before we vacuum it. The
* reason that we do this is so that vacuuming can be spread across
* many small transactions. Otherwise, two-phase locking would require
* us to lock the entire database during one pass of the vacuum cleaner.
*
* At entry and exit, we are not inside a transaction.
*/
! static bool
! vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
{
LOCKMODE lmode;
Relation onerel;
LockRelId onerelid;
Oid toast_relid;
- bool result;
/* Begin a transaction for vacuuming this relation */
StartTransactionCommand();
--- 986,1010 ----
/*
* vacuum_rel() -- vacuum one heap relation
*
* Doing one heap at a time incurs extra overhead, since we need to
* check that the heap exists again just before we vacuum it. The
* reason that we do this is so that vacuuming can be spread across
* many small transactions. Otherwise, two-phase locking would require
* us to lock the entire database during one pass of the vacuum cleaner.
*
+ * prevmin and prevvac are output parameters, and correspond to the values
+ * in pg_class that existed prior to the execution of this vacuum.
+ *
* At entry and exit, we are not inside a transaction.
*/
! static void
! vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind,
! TransactionId *prevmin, TransactionId *prevvac)
{
LOCKMODE lmode;
Relation onerel;
LockRelId onerelid;
Oid toast_relid;
/* Begin a transaction for vacuuming this relation */
StartTransactionCommand();
***************
*** 976,982 ****
{
StrategyHintVacuum(false);
CommitTransactionCommand();
! return true; /* okay 'cause no data there */
}
/*
--- 1033,1039 ----
{
StrategyHintVacuum(false);
CommitTransactionCommand();
! return;
}
/*
***************
*** 1008,1014 ****
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return false;
}
/*
--- 1065,1071 ----
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return;
}
/*
***************
*** 1023,1029 ****
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return false;
}
/*
--- 1080,1086 ----
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return;
}
/*
***************
*** 1038,1044 ****
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return true; /* assume no long-lived data in temp tables */
}
/*
--- 1095,1101 ----
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return; /* assume no long-lived data in temp tables */
}
/*
***************
*** 1063,1073 ****
* Do the actual work --- either FULL or "lazy" vacuum
*/
if (vacstmt->full)
! full_vacuum_rel(onerel, vacstmt);
else
! lazy_vacuum_rel(onerel, vacstmt);
!
! result = true; /* did the vacuum */
/* all done with this class, but hold lock until commit */
relation_close(onerel, NoLock);
--- 1120,1128 ----
* Do the actual work --- either FULL or "lazy" vacuum
*/
if (vacstmt->full)
! full_vacuum_rel(onerel, vacstmt, prevmin, prevvac);
else
! lazy_vacuum_rel(onerel, vacstmt, prevmin, prevvac);
/* all done with this class, but hold lock until commit */
relation_close(onerel, NoLock);
***************
*** 1081,1102 ****
/*
* If the relation has a secondary toast rel, vacuum that too while we
* still hold the session lock on the master table. Note however that
! * "analyze" will not get done on the toast table. This is good, because
! * the toaster always uses hardcoded index access and statistics are
! * totally unimportant for toast relations.
*/
if (toast_relid != InvalidOid)
! {
! if (!vacuum_rel(toast_relid, vacstmt, RELKIND_TOASTVALUE))
! result = false; /* failed to vacuum the TOAST table? */
! }
/*
* Now release the session-level lock on the master table.
*/
UnlockRelationForSession(&onerelid, lmode);
-
- return result;
}
--- 1136,1154 ----
/*
* If the relation has a secondary toast rel, vacuum that too while we
* still hold the session lock on the master table. Note however that
! * "analyze" will not get done on the toast table. This is good,
! * because the toaster always uses hardcoded index access and
! * statistics are totally unimportant for toast relations.
! *
! * Note we ignore the TOAST table for the "minxid" calculations.
*/
if (toast_relid != InvalidOid)
! vacuum_rel(toast_relid, vacstmt, RELKIND_TOASTVALUE, NULL, NULL);
/*
* Now release the session-level lock on the master table.
*/
UnlockRelationForSession(&onerelid, lmode);
}
***************
*** 1116,1124 ****
*
* At entry, we have already established a transaction and opened
* and locked the relation.
*/
static void
! full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
{
VacPageListData vacuum_pages; /* List of pages to vacuum and/or
* clean indexes */
--- 1168,1182 ----
*
* At entry, we have already established a transaction and opened
* and locked the relation.
+ *
+ * relminxid and relvacuumxid are output parameters, and correspond
+ * to the values in pg_class that existed prior to the execution of
+ * this vacuum.
+ *
*/
static void
! full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, TransactionId *relminxid,
! TransactionId *relvacuumxid)
{
VacPageListData vacuum_pages; /* List of pages to vacuum and/or
* clean indexes */
***************
*** 1128,1133 ****
--- 1186,1193 ----
int nindexes,
i;
VRelStats *vacrelstats;
+ TransactionId FreezeLimit,
+ OldestXmin;
vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,
&OldestXmin, &FreezeLimit);
***************
*** 1140,1148 ****
vacrelstats->rel_tuples = 0;
vacrelstats->hasindex = false;
/* scan the heap */
vacuum_pages.num_pages = fraged_pages.num_pages = 0;
! scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages);
/* Now open all indexes of the relation */
vac_open_indexes(onerel, AccessExclusiveLock, &nindexes, &Irel);
--- 1200,1215 ----
vacrelstats->rel_tuples = 0;
vacrelstats->hasindex = false;
+ /*
+ * Set initial minimum Xid, which will be updated if a smaller Xid is found
+ * in the relation.
+ */
+ vacrelstats->minxid = RecentXmin;
+
/* scan the heap */
vacuum_pages.num_pages = fraged_pages.num_pages = 0;
! scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages, FreezeLimit,
! OldestXmin);
/* Now open all indexes of the relation */
vac_open_indexes(onerel, AccessExclusiveLock, &nindexes, &Irel);
***************
*** 1170,1176 ****
{
/* Try to shrink heap */
repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages,
! nindexes, Irel);
vac_close_indexes(nindexes, Irel, NoLock);
}
else
--- 1237,1243 ----
{
/* Try to shrink heap */
repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages,
! nindexes, Irel, OldestXmin);
vac_close_indexes(nindexes, Irel, NoLock);
}
else
***************
*** 1186,1194 ****
/* update shared free space map with final free space info */
vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages);
! /* update statistics in pg_class */
vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
! vacrelstats->rel_tuples, vacrelstats->hasindex);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
--- 1253,1267 ----
/* update shared free space map with final free space info */
vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages);
! /*
! * Update statistics in pg_class. Note we only want the Xids
! * if this is a plain relation (i.e. not a TOAST table)
! */
vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
! vacrelstats->rel_tuples, vacrelstats->hasindex,
! (onerel->rd_rel->relkind == RELKIND_RELATION),
! vacrelstats->minxid, OldestXmin, relminxid,
! relvacuumxid);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
***************
*** 1202,1217 ****
* This routine sets commit status bits, constructs vacuum_pages (list
* of pages we need to compact free space on and/or clean indexes of
* deleted tuples), constructs fraged_pages (list of pages with free
! * space that tuples could be moved into), and calculates statistics
! * on the number of live tuples in the heap.
*/
static void
scan_heap(VRelStats *vacrelstats, Relation onerel,
! VacPageList vacuum_pages, VacPageList fraged_pages)
{
BlockNumber nblocks,
blkno;
- HeapTupleData tuple;
char *relname;
VacPage vacpage;
BlockNumber empty_pages,
--- 1275,1291 ----
* This routine sets commit status bits, constructs vacuum_pages (list
* of pages we need to compact free space on and/or clean indexes of
* deleted tuples), constructs fraged_pages (list of pages with free
! * space that tuples could be moved into), calculates statistics on the
! * number of live tuples in the heap, and figures out the minimum normal
! * Xid present anywhere on the table.
*/
static void
scan_heap(VRelStats *vacrelstats, Relation onerel,
! VacPageList vacuum_pages, VacPageList fraged_pages,
! TransactionId FreezeLimit, TransactionId OldestXmin)
{
BlockNumber nblocks,
blkno;
char *relname;
VacPage vacpage;
BlockNumber empty_pages,
***************
*** 1326,1331 ****
--- 1400,1406 ----
{
ItemId itemid = PageGetItemId(page, offnum);
bool tupgone = false;
+ HeapTupleData tuple;
/*
* Collect un-used items too - it's possible to have indexes
***************
*** 1468,1473 ****
--- 1543,1560 ----
min_tlen = tuple.t_len;
if (tuple.t_len > max_tlen)
max_tlen = tuple.t_len;
+
+ /* Checks for pg_class.relminxid */
+ if (TransactionIdIsNormal(HeapTupleHeaderGetXmin(tuple.t_data)) &&
+ TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
+ vacrelstats->minxid))
+ vacrelstats->minxid = HeapTupleHeaderGetXmin(tuple.t_data);
+
+ if (TransactionIdIsNormal(HeapTupleHeaderGetXmax(tuple.t_data)) &&
+ TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple.t_data),
+ vacrelstats->minxid))
+ vacrelstats->minxid = HeapTupleHeaderGetXmax(tuple.t_data);
+
}
} /* scan along page */
***************
*** 1609,1615 ****
static void
repair_frag(VRelStats *vacrelstats, Relation onerel,
VacPageList vacuum_pages, VacPageList fraged_pages,
! int nindexes, Relation *Irel)
{
TransactionId myXID = GetCurrentTransactionId();
Buffer dst_buffer = InvalidBuffer;
--- 1696,1702 ----
static void
repair_frag(VRelStats *vacrelstats, Relation onerel,
VacPageList vacuum_pages, VacPageList fraged_pages,
! int nindexes, Relation *Irel, TransactionId OldestXmin)
{
TransactionId myXID = GetCurrentTransactionId();
Buffer dst_buffer = InvalidBuffer;
***************
*** 2962,2968 ****
/* now update statistics in pg_class */
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages, stats->num_index_tuples,
! false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
--- 3049,3056 ----
/* now update statistics in pg_class */
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages, stats->num_index_tuples,
! false, false, InvalidTransactionId,
! InvalidTransactionId, NULL, NULL);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
***************
*** 3029,3035 ****
/* now update statistics in pg_class */
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages, stats->num_index_tuples,
! false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
--- 3117,3124 ----
/* now update statistics in pg_class */
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages, stats->num_index_tuples,
! false, false, InvalidTransactionId,
! InvalidTransactionId, NULL, NULL);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
Index: src/backend/commands/vacuumlazy.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuumlazy.c,v
retrieving revision 1.61
diff -c -r1.61 vacuumlazy.c
*** src/backend/commands/vacuumlazy.c 15 Oct 2005 02:49:16 -0000 1.61
--- src/backend/commands/vacuumlazy.c 14 Nov 2005 21:35:48 -0000
***************
*** 71,76 ****
--- 71,77 ----
double tuples_deleted;
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
Size threshold; /* minimum interesting free space */
+ TransactionId minxid; /* minimum Xid present anywhere in table */
/* List of TIDs of tuples we intend to delete */
/* NB: this list is ordered by TID address */
int num_dead_tuples; /* current # of entries */
***************
*** 87,99 ****
static int elevel = -1;
- static TransactionId OldestXmin;
- static TransactionId FreezeLimit;
-
/* non-export function prototypes */
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
! Relation *Irel, int nindexes);
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
static void lazy_scan_index(Relation indrel, LVRelStats *vacrelstats);
static void lazy_vacuum_index(Relation indrel,
--- 88,98 ----
static int elevel = -1;
/* non-export function prototypes */
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
! Relation *Irel, int nindexes, TransactionId FreezeLimit,
! TransactionId OldestXmin);
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
static void lazy_scan_index(Relation indrel, LVRelStats *vacrelstats);
static void lazy_vacuum_index(Relation indrel,
***************
*** 102,110 ****
LVRelStats *vacrelstats);
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
int tupindex, LVRelStats *vacrelstats);
! static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats);
static BlockNumber count_nondeletable_pages(Relation onerel,
! LVRelStats *vacrelstats);
static void lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks);
static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
ItemPointer itemptr);
--- 101,110 ----
LVRelStats *vacrelstats);
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
int tupindex, LVRelStats *vacrelstats);
! static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
! TransactionId OldestXmin);
static BlockNumber count_nondeletable_pages(Relation onerel,
! LVRelStats *vacrelstats, TransactionId OldestXmin);
static void lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks);
static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
ItemPointer itemptr);
***************
*** 121,139 ****
* lazy_vacuum_rel() -- perform LAZY VACUUM for one heap relation
*
* This routine vacuums a single heap, cleans out its indexes, and
! * updates its num_pages and num_tuples statistics.
*
* At entry, we have already established a transaction and opened
* and locked the relation.
*/
void
! lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
{
LVRelStats *vacrelstats;
Relation *Irel;
int nindexes;
bool hasindex;
BlockNumber possibly_freeable;
if (vacstmt->verbose)
elevel = INFO;
--- 121,147 ----
* lazy_vacuum_rel() -- perform LAZY VACUUM for one heap relation
*
* This routine vacuums a single heap, cleans out its indexes, and
! * updates its relpages and reltuples statistics, as well as the
! * relminxid and relvacuumxid information.
*
* At entry, we have already established a transaction and opened
* and locked the relation.
+ *
+ * relminxid and relvacuumxid are output parameters, and correspond
+ * to the values in pg_class that existed prior to the execution of
+ * this vacuum.
*/
void
! lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, TransactionId *relminxid,
! TransactionId *relvacuumxid)
{
LVRelStats *vacrelstats;
Relation *Irel;
int nindexes;
bool hasindex;
BlockNumber possibly_freeable;
+ TransactionId OldestXmin,
+ FreezeLimit;
if (vacstmt->verbose)
elevel = INFO;
***************
*** 149,160 ****
/* XXX should we scale it up or down? Adjust vacuum.c too, if so */
vacrelstats->threshold = GetAvgFSMRequestSize(&onerel->rd_node);
/* Open all indexes of the relation */
vac_open_indexes(onerel, ShareUpdateExclusiveLock, &nindexes, &Irel);
hasindex = (nindexes > 0);
/* Do the vacuuming */
! lazy_scan_heap(onerel, vacrelstats, Irel, nindexes);
/* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock);
--- 157,171 ----
/* XXX should we scale it up or down? Adjust vacuum.c too, if so */
vacrelstats->threshold = GetAvgFSMRequestSize(&onerel->rd_node);
+ /* Set initial minimum Xid in table */
+ vacrelstats->minxid = RecentXmin;
+
/* Open all indexes of the relation */
vac_open_indexes(onerel, ShareUpdateExclusiveLock, &nindexes, &Irel);
hasindex = (nindexes > 0);
/* Do the vacuuming */
! lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, FreezeLimit, OldestXmin);
/* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock);
***************
*** 168,183 ****
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
if (possibly_freeable >= REL_TRUNCATE_MINIMUM ||
possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION)
! lazy_truncate_heap(onerel, vacrelstats);
/* Update shared free space map with final free space info */
lazy_update_fsm(onerel, vacrelstats);
/* Update statistics in pg_class */
! vac_update_relstats(RelationGetRelid(onerel),
! vacrelstats->rel_pages,
! vacrelstats->rel_tuples,
! hasindex);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
--- 179,195 ----
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
if (possibly_freeable >= REL_TRUNCATE_MINIMUM ||
possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION)
! lazy_truncate_heap(onerel, vacrelstats, OldestXmin);
/* Update shared free space map with final free space info */
lazy_update_fsm(onerel, vacrelstats);
/* Update statistics in pg_class */
! vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
! vacrelstats->rel_tuples, hasindex,
! (onerel->rd_rel->relkind == RELKIND_RELATION),
! vacrelstats->minxid, OldestXmin, relminxid,
! relvacuumxid);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
***************
*** 192,201 ****
* and pages with free space, and calculates statistics on the number
* of live tuples in the heap. When done, or when we run low on space
* for dead-tuple TIDs, invoke vacuuming of indexes and heap.
*/
static void
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
! Relation *Irel, int nindexes)
{
BlockNumber nblocks,
blkno;
--- 204,217 ----
* and pages with free space, and calculates statistics on the number
* of live tuples in the heap. When done, or when we run low on space
* for dead-tuple TIDs, invoke vacuuming of indexes and heap.
+ *
+ * It also updates the minimum Xid found anywhere on the table, for
+ * pg_class.relminxid.
*/
static void
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
! Relation *Irel, int nindexes, TransactionId FreezeLimit,
! TransactionId OldestXmin)
{
BlockNumber nblocks,
blkno;
***************
*** 420,425 ****
--- 436,452 ----
{
num_tuples += 1;
hastup = true;
+
+ /* Checks for pg_class.relminxid */
+ if (TransactionIdIsNormal(HeapTupleHeaderGetXmin(tuple.t_data)) &&
+ TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
+ vacrelstats->minxid))
+ vacrelstats->minxid = HeapTupleHeaderGetXmin(tuple.t_data);
+
+ if (TransactionIdIsNormal(HeapTupleHeaderGetXmax(tuple.t_data)) &&
+ TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple.t_data),
+ vacrelstats->minxid))
+ vacrelstats->minxid = HeapTupleHeaderGetXmax(tuple.t_data);
}
} /* scan along page */
***************
*** 644,650 ****
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages,
stats->num_index_tuples,
! false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
--- 671,678 ----
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages,
stats->num_index_tuples,
! false, false, InvalidTransactionId,
! InvalidTransactionId, NULL, NULL);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
***************
*** 720,726 ****
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages,
stats->num_index_tuples,
! false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
--- 748,755 ----
vac_update_relstats(RelationGetRelid(indrel),
stats->num_pages,
stats->num_index_tuples,
! false, false, InvalidTransactionId,
! InvalidTransactionId, NULL, NULL);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
***************
*** 741,747 ****
* lazy_truncate_heap - try to truncate off any empty pages at the end
*/
static void
! lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
{
BlockNumber old_rel_pages = vacrelstats->rel_pages;
BlockNumber new_rel_pages;
--- 770,777 ----
* lazy_truncate_heap - try to truncate off any empty pages at the end
*/
static void
! lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
! TransactionId OldestXmin)
{
BlockNumber old_rel_pages = vacrelstats->rel_pages;
BlockNumber new_rel_pages;
***************
*** 782,788 ****
* because other backends could have added tuples to these pages whilst we
* were vacuuming.
*/
! new_rel_pages = count_nondeletable_pages(onerel, vacrelstats);
if (new_rel_pages >= old_rel_pages)
{
--- 812,818 ----
* because other backends could have added tuples to these pages whilst we
* were vacuuming.
*/
! new_rel_pages = count_nondeletable_pages(onerel, vacrelstats, OldestXmin);
if (new_rel_pages >= old_rel_pages)
{
***************
*** 837,843 ****
* Returns number of nondeletable pages (last nonempty page + 1).
*/
static BlockNumber
! count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
{
BlockNumber blkno;
HeapTupleData tuple;
--- 867,874 ----
* Returns number of nondeletable pages (last nonempty page + 1).
*/
static BlockNumber
! count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats,
! TransactionId OldestXmin)
{
BlockNumber blkno;
HeapTupleData tuple;
Index: src/backend/libpq/hba.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.149
diff -c -r1.149 hba.c
*** src/backend/libpq/hba.c 17 Oct 2005 16:24:19 -0000 1.149
--- src/backend/libpq/hba.c 14 Nov 2005 23:41:13 -0000
***************
*** 1001,1007 ****
* dbname: gets database name (must be of size NAMEDATALEN bytes)
* dboid: gets database OID
* dbtablespace: gets database's default tablespace's OID
! * dbfrozenxid: gets database's frozen XID
* dbvacuumxid: gets database's vacuum XID
*
* This is not much related to the other functions in hba.c, but we put it
--- 1001,1007 ----
* dbname: gets database name (must be of size NAMEDATALEN bytes)
* dboid: gets database OID
* dbtablespace: gets database's default tablespace's OID
! * dbminxid: gets database's minimum XID
* dbvacuumxid: gets database's vacuum XID
*
* This is not much related to the other functions in hba.c, but we put it
***************
*** 1009,1015 ****
*/
bool
read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
! Oid *dbtablespace, TransactionId *dbfrozenxid,
TransactionId *dbvacuumxid)
{
char buf[MAX_TOKEN];
--- 1009,1015 ----
*/
bool
read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
! Oid *dbtablespace, TransactionId *dbminxid,
TransactionId *dbvacuumxid)
{
char buf[MAX_TOKEN];
***************
*** 1032,1038 ****
next_token(fp, buf, sizeof(buf));
if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file");
! *dbfrozenxid = atoxid(buf);
next_token(fp, buf, sizeof(buf));
if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file");
--- 1032,1038 ----
next_token(fp, buf, sizeof(buf));
if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file");
! *dbminxid = atoxid(buf);
next_token(fp, buf, sizeof(buf));
if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file");
Index: src/backend/postmaster/autovacuum.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/autovacuum.c,v
retrieving revision 1.5
diff -c -r1.5 autovacuum.c
*** src/backend/postmaster/autovacuum.c 15 Oct 2005 02:49:23 -0000 1.5
--- src/backend/postmaster/autovacuum.c 15 Nov 2005 01:10:42 -0000
***************
*** 76,82 ****
{
Oid oid;
char *name;
! TransactionId frozenxid;
TransactionId vacuumxid;
PgStat_StatDBEntry *entry;
int32 age;
--- 76,82 ----
{
Oid oid;
char *name;
! TransactionId minxid;
TransactionId vacuumxid;
PgStat_StatDBEntry *entry;
int32 age;
***************
*** 326,332 ****
{
autovac_dbase *tmp = lfirst(cell);
bool this_whole_db;
! int32 freeze_age,
vacuum_age;
/*
--- 326,332 ----
{
autovac_dbase *tmp = lfirst(cell);
bool this_whole_db;
! int32 true_age,
vacuum_age;
/*
***************
*** 339,347 ****
* Unlike vacuum.c, we also look at vacuumxid. This is so that pg_clog
* can be kept trimmed to a reasonable size.
*/
! freeze_age = (int32) (nextXid - tmp->frozenxid);
vacuum_age = (int32) (nextXid - tmp->vacuumxid);
! tmp->age = Max(freeze_age, vacuum_age);
this_whole_db = (tmp->age >
(int32) ((MaxTransactionId >> 3) * 3 - 100000));
--- 339,347 ----
* Unlike vacuum.c, we also look at vacuumxid. This is so that pg_clog
* can be kept trimmed to a reasonable size.
*/
! true_age = (int32) (nextXid - tmp->minxid);
vacuum_age = (int32) (nextXid - tmp->vacuumxid);
! tmp->age = Max(true_age, vacuum_age);
this_whole_db = (tmp->age >
(int32) ((MaxTransactionId >> 3) * 3 - 100000));
***************
*** 441,447 ****
FILE *db_file;
Oid db_id;
Oid db_tablespace;
! TransactionId db_frozenxid;
TransactionId db_vacuumxid;
filename = database_getflatfilename();
--- 441,447 ----
FILE *db_file;
Oid db_id;
Oid db_tablespace;
! TransactionId db_minxid;
TransactionId db_vacuumxid;
filename = database_getflatfilename();
***************
*** 452,458 ****
errmsg("could not open file \"%s\": %m", filename)));
while (read_pg_database_line(db_file, thisname, &db_id,
! &db_tablespace, &db_frozenxid,
&db_vacuumxid))
{
autovac_dbase *db;
--- 452,458 ----
errmsg("could not open file \"%s\": %m", filename)));
while (read_pg_database_line(db_file, thisname, &db_id,
! &db_tablespace, &db_minxid,
&db_vacuumxid))
{
autovac_dbase *db;
***************
*** 461,467 ****
db->oid = db_id;
db->name = pstrdup(thisname);
! db->frozenxid = db_frozenxid;
db->vacuumxid = db_vacuumxid;
/* these get set later: */
db->entry = NULL;
--- 461,467 ----
db->oid = db_id;
db->name = pstrdup(thisname);
! db->minxid = db_minxid;
db->vacuumxid = db_vacuumxid;
/* these get set later: */
db->entry = NULL;
Index: src/backend/utils/init/flatfiles.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/flatfiles.c,v
retrieving revision 1.15
diff -c -r1.15 flatfiles.c
*** src/backend/utils/init/flatfiles.c 15 Oct 2005 02:49:33 -0000 1.15
--- src/backend/utils/init/flatfiles.c 15 Nov 2005 01:12:30 -0000
***************
*** 163,169 ****
/*
* write_database_file: update the flat database file
*
! * A side effect is to determine the oldest database's datfrozenxid
* so we can set or update the XID wrap limit.
*/
static void
--- 163,169 ----
/*
* write_database_file: update the flat database file
*
! * A side effect is to determine the oldest database's datminxid
* so we can set or update the XID wrap limit.
*/
static void
***************
*** 177,183 ****
HeapScanDesc scan;
HeapTuple tuple;
NameData oldest_datname;
! TransactionId oldest_datfrozenxid = InvalidTransactionId;
/*
* Create a temporary filename to be renamed later. This prevents the
--- 177,183 ----
HeapScanDesc scan;
HeapTuple tuple;
NameData oldest_datname;
! TransactionId oldest_datminxid = InvalidTransactionId;
/*
* Create a temporary filename to be renamed later. This prevents the
***************
*** 208,234 ****
char *datname;
Oid datoid;
Oid dattablespace;
! TransactionId datfrozenxid,
! datvacuumxid;
datname = NameStr(dbform->datname);
datoid = HeapTupleGetOid(tuple);
dattablespace = dbform->dattablespace;
! datfrozenxid = dbform->datfrozenxid;
datvacuumxid = dbform->datvacuumxid;
/*
! * Identify the oldest datfrozenxid, ignoring databases that are not
! * connectable (we assume they are safely frozen). This must match
* the logic in vac_truncate_clog() in vacuum.c.
*/
if (dbform->datallowconn &&
! TransactionIdIsNormal(datfrozenxid))
{
! if (oldest_datfrozenxid == InvalidTransactionId ||
! TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid))
{
! oldest_datfrozenxid = datfrozenxid;
namestrcpy(&oldest_datname, datname);
}
}
--- 208,235 ----
char *datname;
Oid datoid;
Oid dattablespace;
! TransactionId datminxid,
! datvacuumxid;
!
datname = NameStr(dbform->datname);
datoid = HeapTupleGetOid(tuple);
dattablespace = dbform->dattablespace;
! datminxid = dbform->datminxid;
datvacuumxid = dbform->datvacuumxid;
/*
! * Identify the oldest datminxid, ignoring databases that are not
! * connectable (we assume they are safely frozen). This must match
* the logic in vac_truncate_clog() in vacuum.c.
*/
if (dbform->datallowconn &&
! TransactionIdIsNormal(datminxid))
{
! if (oldest_datminxid == InvalidTransactionId ||
! TransactionIdPrecedes(datminxid, oldest_datminxid))
{
! oldest_datminxid = datminxid;
namestrcpy(&oldest_datname, datname);
}
}
***************
*** 244,257 ****
}
/*
! * The file format is: "dbname" oid tablespace frozenxid vacuumxid
*
! * The xids are not needed for backend startup, but are of use to
* autovacuum, and might also be helpful for forensic purposes.
*/
fputs_quote(datname, fp);
fprintf(fp, " %u %u %u %u\n",
! datoid, dattablespace, datfrozenxid, datvacuumxid);
}
heap_endscan(scan);
--- 245,258 ----
}
/*
! * The file format is: "dbname" oid tablespace minxid vacuumxid
*
! * The minxid is not needed for backend startup, but is of use to
* autovacuum, and might also be helpful for forensic purposes.
*/
fputs_quote(datname, fp);
fprintf(fp, " %u %u %u %u\n",
! datoid, dattablespace, datminxid, datvacuumxid);
}
heap_endscan(scan);
***************
*** 272,281 ****
tempname, filename)));
/*
! * Set the transaction ID wrap limit using the oldest datfrozenxid
*/
! if (oldest_datfrozenxid != InvalidTransactionId)
! SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname);
}
--- 273,282 ----
tempname, filename)));
/*
! * Set the transaction ID wrap limit using the oldest datminxid
*/
! if (oldest_datminxid != InvalidTransactionId)
! SetTransactionIdLimit(oldest_datminxid, &oldest_datname);
}
Index: src/include/access/transam.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/transam.h,v
retrieving revision 1.56
diff -c -r1.56 transam.h
*** src/include/access/transam.h 15 Oct 2005 02:49:42 -0000 1.56
--- src/include/access/transam.h 7 Nov 2005 12:35:01 -0000
***************
*** 123,130 ****
/* in transam/varsup.c */
extern TransactionId GetNewTransactionId(bool isSubXact);
extern TransactionId ReadNewTransactionId(void);
! extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
! Name oldest_datname);
extern Oid GetNewObjectId(void);
#endif /* TRAMSAM_H */
--- 123,130 ----
/* in transam/varsup.c */
extern TransactionId GetNewTransactionId(bool isSubXact);
extern TransactionId ReadNewTransactionId(void);
! extern void SetTransactionIdLimit(TransactionId oldest_datminxid,
! Name oldest_datname);
extern Oid GetNewObjectId(void);
#endif /* TRAMSAM_H */
Index: src/include/catalog/pg_attribute.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_attribute.h,v
retrieving revision 1.119
diff -c -r1.119 pg_attribute.h
*** src/include/catalog/pg_attribute.h 15 Oct 2005 02:49:42 -0000 1.119
--- src/include/catalog/pg_attribute.h 14 Nov 2005 19:56:02 -0000
***************
*** 404,410 ****
{ 1259, {"relhaspkey"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1259, {"relhasrules"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1259, {"relhassubclass"},16, -1, 1, 24, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
! { 1259, {"relacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
--- 404,412 ----
{ 1259, {"relhaspkey"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1259, {"relhasrules"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
{ 1259, {"relhassubclass"},16, -1, 1, 24, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
! { 1259, {"relminxid"}, 28, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
! { 1259, {"relvacuumxid"}, 28, -1, 4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
! { 1259, {"relacl"}, 1034, -1, -1, 27, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
***************
*** 430,436 ****
DATA(insert ( 1259 relhaspkey 16 -1 1 22 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relhasrules 16 -1 1 23 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relhassubclass 16 -1 1 24 0 -1 -1 t p c t f f t 0));
! DATA(insert ( 1259 relacl 1034 -1 -1 25 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
--- 432,440 ----
DATA(insert ( 1259 relhaspkey 16 -1 1 22 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relhasrules 16 -1 1 23 0 -1 -1 t p c t f f t 0));
DATA(insert ( 1259 relhassubclass 16 -1 1 24 0 -1 -1 t p c t f f t 0));
! DATA(insert ( 1259 relminxid 28 -1 4 25 0 -1 -1 t p i t f f t 0));
! DATA(insert ( 1259 relvacuumxid 28 -1 4 26 0 -1 -1 t p i t f f t 0));
! DATA(insert ( 1259 relacl 1034 -1 -1 27 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
Index: src/include/catalog/pg_class.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_class.h,v
retrieving revision 1.90
diff -c -r1.90 pg_class.h
*** src/include/catalog/pg_class.h 15 Oct 2005 02:49:42 -0000 1.90
--- src/include/catalog/pg_class.h 14 Nov 2005 20:46:10 -0000
***************
*** 74,79 ****
--- 74,81 ----
bool relhaspkey; /* has PRIMARY KEY index */
bool relhasrules; /* has associated rules */
bool relhassubclass; /* has derived classes */
+ TransactionId relminxid; /* minimum Xid present in table */
+ TransactionId relvacuumxid; /* OldestXmin of latest vacuum */
/*
* relacl may or may not be present, see note above!
***************
*** 83,89 ****
/* Size of fixed part of pg_class tuples, not counting relacl or padding */
#define CLASS_TUPLE_SIZE \
! (offsetof(FormData_pg_class,relhassubclass) + sizeof(bool))
/* ----------------
* Form_pg_class corresponds to a pointer to a tuple with
--- 85,91 ----
/* Size of fixed part of pg_class tuples, not counting relacl or padding */
#define CLASS_TUPLE_SIZE \
! (offsetof(FormData_pg_class,relvacuumxid) + sizeof(TransactionId))
/* ----------------
* Form_pg_class corresponds to a pointer to a tuple with
***************
*** 103,110 ****
* relacl field. This is a kluge.
* ----------------
*/
! #define Natts_pg_class_fixed 24
! #define Natts_pg_class 25
#define Anum_pg_class_relname 1
#define Anum_pg_class_relnamespace 2
#define Anum_pg_class_reltype 3
--- 105,112 ----
* relacl field. This is a kluge.
* ----------------
*/
! #define Natts_pg_class_fixed 26
! #define Natts_pg_class 27
#define Anum_pg_class_relname 1
#define Anum_pg_class_relnamespace 2
#define Anum_pg_class_reltype 3
***************
*** 129,135 ****
#define Anum_pg_class_relhaspkey 22
#define Anum_pg_class_relhasrules 23
#define Anum_pg_class_relhassubclass 24
! #define Anum_pg_class_relacl 25
/* ----------------
* initial contents of pg_class
--- 131,139 ----
#define Anum_pg_class_relhaspkey 22
#define Anum_pg_class_relhasrules 23
#define Anum_pg_class_relhassubclass 24
! #define Anum_pg_class_relminxid 25
! #define Anum_pg_class_relvacuumxid 26
! #define Anum_pg_class_relacl 27
/* ----------------
* initial contents of pg_class
***************
*** 139,151 ****
* ----------------
*/
! DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ ));
DESCR("");
! DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f _null_ ));
DESCR("");
! DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 0 0 0 t f f f _null_ ));
DESCR("");
! DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 25 0 0 0 0 0 t f f f _null_ ));
DESCR("");
#define RELKIND_INDEX 'i' /* secondary index */
--- 143,155 ----
* ----------------
*/
! DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f 0 0 _null_ ));
DESCR("");
! DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f 0 0 _null_ ));
DESCR("");
! DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0 0 0 0 t f f f 0 0 _null_ ));
DESCR("");
! DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 27 0 0 0 0 0 t f f f 0 0 _null_ ));
DESCR("");
#define RELKIND_INDEX 'i' /* secondary index */
Index: src/include/catalog/pg_database.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_database.h,v
retrieving revision 1.38
diff -c -r1.38 pg_database.h
*** src/include/catalog/pg_database.h 15 Oct 2005 02:49:42 -0000 1.38
--- src/include/catalog/pg_database.h 14 Nov 2005 21:42:41 -0000
***************
*** 42,49 ****
bool datallowconn; /* new connections allowed? */
int4 datconnlimit; /* max connections allowed (-1=no limit) */
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 */
Oid dattablespace; /* default table space for this DB */
text datconfig[1]; /* database-specific GUC (VAR LENGTH) */
aclitem datacl[1]; /* access permissions (VAR LENGTH) */
--- 42,50 ----
bool datallowconn; /* new connections allowed? */
int4 datconnlimit; /* max connections allowed (-1=no limit) */
Oid datlastsysoid; /* highest OID to consider a system OID */
! TransactionId datminxid; /* no table contains an Xid below this one */
! TransactionId datvacuumxid; /* all Xids in all tables before this have
! * been marked known-committed or -aborted */
Oid dattablespace; /* default table space for this DB */
text datconfig[1]; /* database-specific GUC (VAR LENGTH) */
aclitem datacl[1]; /* access permissions (VAR LENGTH) */
***************
*** 68,75 ****
#define Anum_pg_database_datallowconn 5
#define Anum_pg_database_datconnlimit 6
#define Anum_pg_database_datlastsysoid 7
! #define Anum_pg_database_datvacuumxid 8
! #define Anum_pg_database_datfrozenxid 9
#define Anum_pg_database_dattablespace 10
#define Anum_pg_database_datconfig 11
#define Anum_pg_database_datacl 12
--- 69,76 ----
#define Anum_pg_database_datallowconn 5
#define Anum_pg_database_datconnlimit 6
#define Anum_pg_database_datlastsysoid 7
! #define Anum_pg_database_datminxid 8
! #define Anum_pg_database_datvacuumxid 9
#define Anum_pg_database_dattablespace 10
#define Anum_pg_database_datconfig 11
#define Anum_pg_database_datacl 12
Index: src/include/commands/vacuum.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/commands/vacuum.h,v
retrieving revision 1.62
diff -c -r1.62 vacuum.h
*** src/include/commands/vacuum.h 15 Oct 2005 02:49:44 -0000 1.62
--- src/include/commands/vacuum.h 14 Nov 2005 21:17:05 -0000
***************
*** 114,123 ****
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
int *nindexes, Relation **Irel);
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
! 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);
--- 114,123 ----
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
int *nindexes, Relation **Irel);
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
! extern void vac_update_relstats(Oid relid, BlockNumber num_pages,
! double num_tuples, bool hasindex, bool update_xids,
! TransactionId minxid, TransactionId vacuumxid,
! TransactionId *relminxid, TransactionId *relvacuumxid);
extern void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
TransactionId *oldestXmin,
TransactionId *freezeLimit);
***************
*** 125,131 ****
extern void vacuum_delay_point(void);
/* in commands/vacuumlazy.c */
! extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
/* in commands/analyze.c */
extern void analyze_rel(Oid relid, VacuumStmt *vacstmt);
--- 125,132 ----
extern void vacuum_delay_point(void);
/* in commands/vacuumlazy.c */
! extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
! TransactionId *relminxid, TransactionId *relvacuumxid);
/* in commands/analyze.c */
extern void analyze_rel(Oid relid, VacuumStmt *vacstmt);
Index: src/include/libpq/hba.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/libpq/hba.h,v
retrieving revision 1.41
diff -c -r1.41 hba.h
*** src/include/libpq/hba.h 15 Oct 2005 02:49:44 -0000 1.41
--- src/include/libpq/hba.h 15 Nov 2005 01:04:19 -0000
***************
*** 36,43 ****
extern void load_role(void);
extern int hba_getauthmethod(hbaPort *port);
extern int authident(hbaPort *port);
! extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
! Oid *dbtablespace, TransactionId *dbfrozenxid,
! TransactionId *dbvacuumxid);
#endif /* HBA_H */
--- 36,43 ----
extern void load_role(void);
extern int hba_getauthmethod(hbaPort *port);
extern int authident(hbaPort *port);
! extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
! Oid *dbtablespace, TransactionId *dbminxid,
! TransactionId *dbvacuumxid);
#endif /* HBA_H */