diff -rc -X diff-ignore 11fixclass/doc/src/sgml/catalogs.sgml 12ntrelminxid/doc/src/sgml/catalogs.sgml
*** 11fixclass/doc/src/sgml/catalogs.sgml 2006-06-09 18:52:50.000000000 -0400
--- 12ntrelminxid/doc/src/sgml/catalogs.sgml 2006-06-10 19:38:04.000000000 -0400
***************
*** 1619,1624 ****
--- 1619,1648 ----
+ 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[]
***************
*** 2048,2068 ****
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.
--- 2072,2098 ----
xid
! The transaction ID that was used as cleaning point as of the last vacuum
! operation. All rows inserted or deleted by transaction IDs before 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>.
! 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 useful 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>.
diff -rc -X diff-ignore 11fixclass/src/backend/access/heap/heapam.c 12ntrelminxid/src/backend/access/heap/heapam.c
*** 11fixclass/src/backend/access/heap/heapam.c 2006-06-10 19:17:47.000000000 -0400
--- 12ntrelminxid/src/backend/access/heap/heapam.c 2006-06-11 02:41:15.000000000 -0400
***************
*** 45,61 ****
--- 45,74 ----
#include "access/valid.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
+ #include "catalog/heap.h"
#include "catalog/namespace.h"
+ #include "catalog/pg_ntclass.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/procarray.h"
#include "utils/inval.h"
#include "utils/relcache.h"
+ #include "utils/syscache.h"
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
ItemPointerData from, Buffer newbuf, HeapTuple newtup, bool move);
+ /*
+ * There are some situations in which we want to acquire strong locks on
+ * relations, but we know there is no need to unfreeze them; for example,
+ * during VACUUM. Any such caller sets this variable to true, which turns
+ * heap_unfreeze into a no-op.
+ *
+ * It's advisable to do this in a PG_TRY block so that it won't "forget" to
+ * reset the variable in case of error.
+ */
+ bool disable_heap_unfreeze = false;
/* ----------------------------------------------------------------
* heap support routines
***************
*** 2669,2674 ****
--- 2682,2772 ----
return HeapTupleMayBeUpdated;
}
+ /*
+ * heap_unfreeze
+ * Mark a table as no longer frozen in pg_ntclass.
+ *
+ * This routine updates the pg_ntclass.relminxid and relvacuumxid columns so
+ * that they no longer appear as frozen.
+ */
+ void
+ heap_unfreeze(Relation rel)
+ {
+ Form_pg_ntclass ntclassForm;
+ HeapTuple tuple;
+ bool dirty = false;
+
+ /* early exit if somebody decided to disable us */
+ if (disable_heap_unfreeze)
+ return;
+
+ /* Exit early in bootstrap mode */
+ if (IsBootstrapProcessingMode())
+ return;
+
+ /*
+ * We process only normal heaps, toast tables, and non-transactional
+ * heaps. Other kinds of heaps don't store Xids in them and thus they
+ * don't need unfreezing.
+ */
+ if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ rel->rd_rel->relkind != RELKIND_INDEX &&
+ rel->rd_rel->relkind != RELKIND_NON_TRANSACTIONAL)
+ return;
+
+ /*
+ * Under normal conditions, we should have a snapshot already, but in some
+ * cases there may not be one. Getting a snapshot guarantees we will have
+ * a valid RecentXmin to use.
+ */
+ if (!TransactionIdIsValid(RecentXmin))
+ ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
+ Assert(TransactionIdIsValid(RecentXmin));
+
+ PG_TRY();
+ {
+ Relation pg_ntclass;
+
+ /* Disable ourselves so that we don't recurse on unfreezing pg_ntclass */
+ disable_heap_unfreeze = true;
+
+ pg_ntclass = heap_open(NtClassRelationId, RowExclusiveLock);
+
+ /* Fetch a copy to scribble on */
+ tuple = get_ntclass_entry(pg_ntclass, rel);
+
+ ntclassForm = (Form_pg_ntclass) GETSTRUCT(tuple);
+
+ /* Apply all necessary updates. */
+ if (TransactionIdEquals(ntclassForm->relminxid, FrozenTransactionId))
+ {
+ ntclassForm->relminxid = RecentXmin;
+ dirty = true;
+ }
+ if (TransactionIdEquals(ntclassForm->relvacuumxid, FrozenTransactionId))
+ {
+ ntclassForm->relvacuumxid = RecentXmin;
+ dirty = true;
+ }
+ /* Update tuple if necessary. */
+ if (dirty)
+ update_ntclass_entry(pg_ntclass, tuple);
+
+ /* All done, cleanup and return */
+ heap_freetuple(tuple);
+ heap_close(pg_ntclass, RowExclusiveLock);
+
+ /* reenable heap unfreezing */
+ disable_heap_unfreeze = false;
+ }
+ PG_CATCH();
+ {
+ /* make sure to reenable heap unfreezing in case of error */
+ disable_heap_unfreeze = false;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ }
/*
* heap_inplace_update - update a tuple "in place" (ie, overwrite it)
diff -rc -X diff-ignore 11fixclass/src/backend/access/transam/varsup.c 12ntrelminxid/src/backend/access/transam/varsup.c
*** 11fixclass/src/backend/access/transam/varsup.c 2006-03-08 16:23:24.000000000 -0300
--- 12ntrelminxid/src/backend/access/transam/varsup.c 2006-06-10 19:38:04.000000000 -0400
***************
*** 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;
diff -rc -X diff-ignore 11fixclass/src/backend/catalog/heap.c 12ntrelminxid/src/backend/catalog/heap.c
*** 11fixclass/src/backend/catalog/heap.c 2006-06-11 02:46:03.000000000 -0400
--- 12ntrelminxid/src/backend/catalog/heap.c 2006-06-11 01:03:39.000000000 -0400
***************
*** 637,648 ****
switch (relkind)
{
case RELKIND_RELATION:
- case RELKIND_INDEX:
case RELKIND_TOASTVALUE:
case RELKIND_NON_TRANSACTIONAL:
/* The relation is real, but as yet empty */
new_rel_nttup->relpages = 0;
new_rel_nttup->reltuples = 0;
break;
default:
elog(ERROR, "can't create pg_ntclass entries for relkind '%c'",
--- 637,665 ----
switch (relkind)
{
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_NON_TRANSACTIONAL:
/* The relation is real, but as yet empty */
new_rel_nttup->relpages = 0;
new_rel_nttup->reltuples = 0;
+ /*
+ * Real tables have Xids stored in them, so initialize our
+ * known value to the minimum Xid that could put tuples in the
+ * new table.
+ */
+ new_rel_nttup->relminxid = RecentXmin;
+ new_rel_nttup->relvacuumxid = RecentXmin;
+ break;
+ case RELKIND_INDEX:
+ /* The relation is real, but as yet empty */
+ new_rel_nttup->relpages = 0;
+ new_rel_nttup->reltuples = 0;
+ /*
+ * Index relations don't have Xids stored in them, so set the
+ * transaction Id values to InvalidTransactionId.
+ */
+ new_rel_nttup->relminxid = InvalidTransactionId;
+ new_rel_nttup->relvacuumxid = InvalidTransactionId;
break;
default:
elog(ERROR, "can't create pg_ntclass entries for relkind '%c'",
diff -rc -X diff-ignore 11fixclass/src/backend/commands/analyze.c 12ntrelminxid/src/backend/commands/analyze.c
*** 11fixclass/src/backend/commands/analyze.c 2006-06-10 19:17:47.000000000 -0400
--- 12ntrelminxid/src/backend/commands/analyze.c 2006-06-11 01:26:00.000000000 -0400
***************
*** 420,426 ****
/*
* If we are running a standalone ANALYZE, update pages/tuples stats in
! * pg_class. We know the accurate page count from the smgr, but only an
* approximate number of tuples; therefore, if we are part of VACUUM
* ANALYZE do *not* overwrite the accurate count already inserted by
* VACUUM. The same consideration applies to indexes.
--- 420,426 ----
/*
* If we are running a standalone ANALYZE, update pages/tuples stats in
! * pg_ntclass. We know the accurate page count from the smgr, but only an
* approximate number of tuples; therefore, if we are part of VACUUM
* ANALYZE do *not* overwrite the accurate count already inserted by
* VACUUM. The same consideration applies to indexes.
***************
*** 429,436 ****
{
vac_update_relstats(onerel,
RelationGetNumberOfBlocks(onerel),
! totalrows,
! hasindex);
for (ind = 0; ind < nindexes; ind++)
{
AnlIndexData *thisdata = &indexdata[ind];
--- 429,437 ----
{
vac_update_relstats(onerel,
RelationGetNumberOfBlocks(onerel),
! totalrows, hasindex,
! InvalidTransactionId, InvalidTransactionId);
!
for (ind = 0; ind < nindexes; ind++)
{
AnlIndexData *thisdata = &indexdata[ind];
***************
*** 439,446 ****
totalindexrows = ceil(thisdata->tupleFract * totalrows);
vac_update_relstats(Irel[ind],
RelationGetNumberOfBlocks(Irel[ind]),
! totalindexrows,
! false);
}
/* report results to the stats collector, too */
--- 440,447 ----
totalindexrows = ceil(thisdata->tupleFract * totalrows);
vac_update_relstats(Irel[ind],
RelationGetNumberOfBlocks(Irel[ind]),
! totalindexrows, false,
! InvalidTransactionId, InvalidTransactionId);
}
/* report results to the stats collector, too */
diff -rc -X diff-ignore 11fixclass/src/backend/commands/dbcommands.c 12ntrelminxid/src/backend/commands/dbcommands.c
*** 11fixclass/src/backend/commands/dbcommands.c 2006-05-04 21:36:10.000000000 -0400
--- 12ntrelminxid/src/backend/commands/dbcommands.c 2006-06-10 19:38:04.000000000 -0400
***************
*** 46,51 ****
--- 46,52 ----
#include "utils/flatfiles.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
+ #include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
***************
*** 55,61 ****
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 ----
Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
! TransactionId *dbVacuumXidP, TransactionId *dbMinXidP,
Oid *dbTablespace);
static bool have_createdb_privilege(void);
static void remove_dbtablespaces(Oid db_id);
***************
*** 76,82 ****
bool src_allowconn;
Oid src_lastsysoid;
TransactionId src_vacuumxid;
! TransactionId src_frozenxid;
Oid src_deftablespace;
volatile Oid dst_deftablespace;
Relation pg_database_rel;
--- 77,83 ----
bool src_allowconn;
Oid src_lastsysoid;
TransactionId src_vacuumxid;
! TransactionId src_minxid;
Oid src_deftablespace;
volatile Oid dst_deftablespace;
Relation pg_database_rel;
***************
*** 228,234 ****
if (!get_db_info(dbtemplate, ShareLock,
&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",
--- 229,235 ----
if (!get_db_info(dbtemplate, ShareLock,
&src_dboid, &src_owner, &src_encoding,
&src_istemplate, &src_allowconn, &src_lastsysoid,
! &src_vacuumxid, &src_minxid, &src_deftablespace))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("template database \"%s\" does not exist",
***************
*** 327,340 ****
}
/*
! * 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();
/*
* Check for db name conflict. This is just to give a more friendly
--- 328,342 ----
}
/*
! * Normally we mark the new database with the same datminxid as the source.
! * However, if the source is fully frozen, we must not mark the new
! * database as frozen because of the new pg_database tuple, which will be
! * marked with our transaction ID.
! */
! if (TransactionIdEquals(src_minxid, FrozenTransactionId))
! src_minxid = GetCurrentTransactionId();
! if (TransactionIdEquals(src_vacuumxid, FrozenTransactionId))
! src_vacuumxid = GetCurrentTransactionId();
/*
* Check for db name conflict. This is just to give a more friendly
***************
*** 367,373 ****
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);
/*
--- 369,375 ----
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_datminxid - 1] = TransactionIdGetDatum(src_minxid);
new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
/*
***************
*** 1050,1055 ****
--- 1052,1119 ----
*/
}
+ /*
+ * UnfreezeDatabase
+ * Unfreezes a database
+ *
+ * Unfreezing a database is to set datminxid and datvacuumxid to a current
+ * normal Xid. Currently, we do this anytime somebody connects to a database
+ * that is currently marked as frozen (datminxid = FrozenTransactionId).
+ *
+ * NB --- this is called early during backend initialization.
+ */
+ void
+ UnfreezeDatabase(Oid dbid, TransactionId unfreezeXid)
+ {
+ Relation dbRel;
+ HeapTuple tuple;
+ Form_pg_database dbForm;
+ bool dirty = false;
+
+ PG_TRY();
+ {
+ disable_heap_unfreeze = true;
+
+ dbRel = heap_open(DatabaseRelationId, RowExclusiveLock);
+
+ /* fetch a copy of the tuple to scribble on */
+ tuple = SearchSysCacheCopy(DATABASEOID,
+ dbid,
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR,
+ "cache lookup failed for database %u", dbid);
+
+ dbForm = (Form_pg_database) GETSTRUCT(tuple);
+
+ /* make sure no one did it while we weren't looking */
+ if (TransactionIdEquals(dbForm->datminxid, FrozenTransactionId))
+ {
+ dbForm->datminxid = unfreezeXid;
+ dirty = true;
+ }
+ if (TransactionIdEquals(dbForm->datvacuumxid, FrozenTransactionId))
+ {
+ dbForm->datvacuumxid = unfreezeXid;
+ dirty = true;
+ }
+
+ if (dirty)
+ heap_inplace_update(dbRel, tuple);
+
+ heap_close(dbRel, RowExclusiveLock);
+ }
+ PG_CATCH();
+ {
+ disable_heap_unfreeze = false;
+ PG_RE_THROW();
+ };
+ PG_END_TRY();
+
+ /* reenable heap unfreezing */
+ disable_heap_unfreeze = false;
+ }
/*
* Helper functions
***************
*** 1066,1072 ****
Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
! TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
Oid *dbTablespace)
{
bool result = false;
--- 1130,1136 ----
Oid *dbIdP, Oid *ownerIdP,
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
Oid *dbLastSysOidP,
! TransactionId *dbVacuumXidP, TransactionId *dbMinXidP,
Oid *dbTablespace)
{
bool result = false;
***************
*** 1155,1163 ****
/* 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;
--- 1219,1227 ----
/* limit of vacuumed XIDs */
if (dbVacuumXidP)
*dbVacuumXidP = dbform->datvacuumxid;
! /* limit of min XIDs */
! if (dbMinXidP)
! *dbMinXidP = dbform->datminxid;
/* default tablespace for this database */
if (dbTablespace)
*dbTablespace = dbform->dattablespace;
diff -rc -X diff-ignore 11fixclass/src/backend/commands/vacuum.c 12ntrelminxid/src/backend/commands/vacuum.c
*** 11fixclass/src/backend/commands/vacuum.c 2006-06-10 19:17:47.000000000 -0400
--- 12ntrelminxid/src/backend/commands/vacuum.c 2006-06-11 02:37:38.000000000 -0400
***************
*** 129,134 ****
--- 129,135 ----
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;
***************
*** 196,220 ****
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, bool full);
! 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, bool allow_toast);
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,
--- 197,216 ----
static int elevel = -1;
/* non-export function prototypes */
static List *get_rel_oids(List *relids, const RangeVar *vacrel,
const char *stmttype, bool full);
! static void vac_update_dbminxid(Oid dbid);
! static void vac_truncate_clog(void);
! static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool allow_toast);
static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
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,
***************
*** 270,277 ****
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,
--- 266,271 ----
***************
*** 355,386 ****
relations = get_rel_oids(relids, vacstmt->relation, stmttype,
vacstmt->full);
- 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.
*
--- 349,354 ----
***************
*** 441,446 ****
--- 409,424 ----
VacuumCostBalance = 0;
/*
+ * During vacuum, we are going to lock the relation with a writer's
+ * lock, but if it's already frozen, we won't do any writing on it, so
+ * disable the unfreezing for the duration of this VACUUM operation.
+ * However, if this a FULL vacuum, we may mark some tuples with our
+ * Xid even if the table is frozen, so skip this step.
+ */
+ if (!vacstmt->full)
+ disable_heap_unfreeze = true;
+
+ /*
* Loop to process each selected relation.
*/
foreach(cur, relations)
***************
*** 448,457 ****
Oid relid = lfirst_oid(cur);
if (vacstmt->vacuum)
! {
! if (!vacuum_rel(relid, vacstmt, false))
! all_rels = false; /* forget about updating dbstats */
! }
if (vacstmt->analyze)
{
MemoryContext old_context = NULL;
--- 426,433 ----
Oid relid = lfirst_oid(cur);
if (vacstmt->vacuum)
! vacuum_rel(relid, vacstmt, false);
!
if (vacstmt->analyze)
{
MemoryContext old_context = NULL;
***************
*** 496,507 ****
--- 472,487 ----
{
/* Make sure cost accounting is turned off after error */
VacuumCostActive = false;
+ /* reenable heap unfreezing too */
+ disable_heap_unfreeze = false;
PG_RE_THROW();
}
PG_END_TRY();
/* Turn off vacuum cost accounting */
VacuumCostActive = false;
+ /* reenable heap unfreezing too */
+ disable_heap_unfreeze = false;
/*
* Finish up processing.
***************
*** 534,550 ****
if (all_rels)
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);
! }
}
/*
--- 514,524 ----
if (all_rels)
PrintFreeSpaceMapStatistics(elevel);
! /* Update pg_database.datminxid and datvacuumxid */
! vac_update_dbminxid(MyDatabaseId);
!
! /* Try to truncate pg_clog. */
! vac_truncate_clog();
}
/*
***************
*** 701,707 ****
*/
void
vac_update_relstats(Relation rel, BlockNumber num_pages, double num_tuples,
! bool hasindex)
{
Relation rd;
HeapTuple ctup;
--- 675,682 ----
*/
void
vac_update_relstats(Relation rel, BlockNumber num_pages, double num_tuples,
! bool hasindex, TransactionId minxid,
! TransactionId vacuumxid)
{
Relation rd;
HeapTuple ctup;
***************
*** 709,832 ****
Form_pg_class pgcform;
bool dirty;
! /* Process pg_ntclass first */
! rd = heap_open(NtClassRelationId, RowExclusiveLock);
! /* Fetch a copy of the pg_ntclass tuple to scribble on */
! ctup = get_ntclass_entry(rd, rel);
! pgntform = (Form_pg_ntclass) GETSTRUCT(ctup);
! /* Apply required updates, if any, to copied tuple */
! dirty = false;
! if (pgntform->relpages != (int32) num_pages)
! {
! pgntform->relpages = (int32) num_pages;
! dirty = true;
! }
! if (pgntform->reltuples != (float4) num_tuples)
! {
! pgntform->reltuples = (float4) num_tuples;
! dirty = true;
! }
! /* If anything changed, write out the tuple */
! if (dirty)
! update_ntclass_entry(rd, ctup);
! heap_freetuple(ctup);
! heap_close(rd, RowExclusiveLock);
! /* Now go for the pg_class tuple */
! rd = heap_open(RelationRelationId, RowExclusiveLock);
! /* Fetch a copy of the tuple to scribble on */
! ctup = SearchSysCacheCopy(RELOID,
! ObjectIdGetDatum(RelationGetRelid(rel)),
! 0, 0, 0);
! if (!HeapTupleIsValid(ctup))
! elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
! RelationGetRelid(rel));
! pgcform = (Form_pg_class) GETSTRUCT(ctup);
! /* Apply required updates, if any, to copied tuple */
! dirty = false;
! if (pgcform->relhasindex != hasindex)
! {
! pgcform->relhasindex = hasindex;
! dirty = true;
}
! /*
! * If we have discovered that there are no indexes, then there's no
! * primary key either. This could be done more thoroughly...
! */
! if (!hasindex)
{
! if (pgcform->relhaspkey)
! {
! pgcform->relhaspkey = false;
! dirty = true;
! }
}
! /*
! * If anything changed, write out the tuple
! */
! if (dirty)
! heap_inplace_update(rd, ctup);
!
! heap_close(rd, RowExclusiveLock);
}
-
/*
! * 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 transaction semantics here by overwriting the database's
! * existing pg_database tuple with the new values. This is reasonably
! * safe since the new values are correct whether or not this transaction
* commits. 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;
HeapTuple tuple;
Form_pg_database dbform;
! relation = heap_open(DatabaseRelationId, RowExclusiveLock);
! /* Fetch a copy of the tuple to scribble on */
! tuple = SearchSysCacheCopy(DATABASEOID,
! ObjectIdGetDatum(dbid),
! 0, 0, 0);
! if (!HeapTupleIsValid(tuple))
! elog(ERROR, "could not find tuple for database %u", dbid);
! dbform = (Form_pg_database) GETSTRUCT(tuple);
!
! /* overwrite the existing statistics in the tuple */
! dbform->datvacuumxid = vacuumXID;
! dbform->datfrozenxid = frozenXID;
! heap_inplace_update(relation, tuple);
! 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
--- 684,944 ----
Form_pg_class pgcform;
bool dirty;
! PG_TRY();
! {
! /* Don't unfreeze pg_ntclass nor pg_class by doing this */
! disable_heap_unfreeze = true;
! /* Process pg_ntclass first */
! rd = heap_open(NtClassRelationId, RowExclusiveLock);
! /* Fetch a copy of the pg_ntclass tuple to scribble on */
! ctup = get_ntclass_entry(rd, rel);
! pgntform = (Form_pg_ntclass) GETSTRUCT(ctup);
! /* Apply required updates, if any, to copied tuple */
! dirty = false;
! if (pgntform->relpages != (int32) num_pages)
! {
! pgntform->relpages = (int32) num_pages;
! dirty = true;
! }
! if (pgntform->reltuples != (float4) num_tuples)
! {
! pgntform->reltuples = (float4) num_tuples;
! dirty = true;
! }
! if (pgntform->relminxid != minxid)
! {
! pgntform->relminxid = minxid;
! dirty = true;
! }
! /*
! * If relminxid is Frozen (i.e., the table is truly frozen), it's more
! * useful to mark it as having vacuumxid frozen as well. This means
! * tht this table does not impose any particular limit to pg_clog
! * truncation.
! *
! * It seems a bit of a hack to be doing this here, but it would be even
! * uglier to have all the callers do it.
! */
! if (TransactionIdEquals(minxid, FrozenTransactionId))
! vacuumxid = FrozenTransactionId;
! if (pgntform->relvacuumxid != vacuumxid)
! {
! pgntform->relvacuumxid = vacuumxid;
! dirty = true;
! }
! /* If anything changed, write out the tuple */
! if (dirty)
! update_ntclass_entry(rd, ctup);
! heap_freetuple(ctup);
!
! heap_close(rd, RowExclusiveLock);
!
! /* Now go for the pg_class tuple */
! rd = heap_open(RelationRelationId, RowExclusiveLock);
!
! /* Fetch a copy of the tuple to scribble on */
! ctup = SearchSysCacheCopy(RELOID,
! ObjectIdGetDatum(RelationGetRelid(rel)),
! 0, 0, 0);
! if (!HeapTupleIsValid(ctup))
! elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
! RelationGetRelid(rel));
! pgcform = (Form_pg_class) GETSTRUCT(ctup);
! /* Apply required updates, if any, to copied tuple */
! dirty = false;
! if (pgcform->relhasindex != hasindex)
! {
! pgcform->relhasindex = hasindex;
! dirty = true;
! }
! /*
! * If we have discovered that there are no indexes, then there's no
! * primary key either. This could be done more thoroughly...
! */
! if (!hasindex)
! {
! if (pgcform->relhaspkey)
! {
! pgcform->relhaspkey = false;
! dirty = true;
! }
! }
! /*
! * If anything changed, write out the tuple
! */
! if (dirty)
! heap_inplace_update(rd, ctup);
! heap_close(rd, RowExclusiveLock);
! heap_freetuple(ctup);
}
! PG_CATCH();
{
! disable_heap_unfreeze = false;
! PG_RE_THROW();
}
+ PG_END_TRY();
! disable_heap_unfreeze = false;
}
/*
! * vac_update_dbminxid() -- update the minimum Xid present in one database
*
! * Update pg_database's datminxid and datvacuumxid, and the flat-file copy
! * of it. datminxid is updated to the minimum of all relminxid found in
! * pg_ntclass.
*
* We violate transaction semantics here by overwriting the database's
! * existing pg_database tuple with the new value. This is reasonably
! * safe since the new value is correct whether or not this transaction
* commits. 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_dbminxid(Oid dbid)
{
HeapTuple tuple;
Form_pg_database dbform;
+ Relation relation;
+ SysScanDesc scan;
+ HeapTuple ntclassTup;
+ TransactionId newMinXid = InvalidTransactionId;
+ TransactionId newVacXid = InvalidTransactionId;
+ bool dirty = false;
! PG_TRY();
! {
! /*
! * Disable heap unfreezing of pg_database, since we are going
! * to update the tuple in-place and won't be writing our Xid on it.
! */
! disable_heap_unfreeze = true;
! /* We must seqscan pg_ntclass to find the minimum Xid */
! relation = heap_open(NtClassRelationId, AccessShareLock);
! scan = systable_beginscan(relation, InvalidOid, false,
! SnapshotNow, 0, NULL);
! while ((ntclassTup = systable_getnext(scan)) != NULL)
! {
! Form_pg_ntclass ntclassForm;
! ntclassForm = (Form_pg_ntclass) GETSTRUCT(ntclassTup);
!
! /*
! * Compute the minimum relminxid in all the tables in the database.
! * We consider only normal Xids --- this means in particular we
! * avoid setting the minimum to FrozenTransactionId here. If all
! * tables turn out to be frozen, we will exit the loop with the
! * value set to InvalidTransactionId. We cannot allow newMinXid to
! * be set to FrozenTransactionId --- that messes us up because of
! * the semantics of TransactionIdPrecedes.
! *
! * Other values we are ignoring here are InvalidTransactionId
! * (which is set for some bootstrap tables) and
! * BootstrapTransactionId. This doesn't cause any problems because
! * for all practical purposes they behave exactly like
! * FrozenTransactionId.
! */
! if (TransactionIdIsValid(ntclassForm->relminxid) &&
! TransactionIdIsNormal(ntclassForm->relminxid) &&
! (!TransactionIdIsValid(newMinXid) ||
! TransactionIdPrecedes(ntclassForm->relminxid, newMinXid)))
! newMinXid = ntclassForm->relminxid;
!
! /* ditto, for relvacuumxid */
! if (TransactionIdIsValid(ntclassForm->relvacuumxid) &&
! TransactionIdIsNormal(ntclassForm->relvacuumxid) &&
! (!TransactionIdIsValid(newVacXid) ||
! TransactionIdPrecedes(ntclassForm->relvacuumxid, newVacXid)))
! newVacXid = ntclassForm->relvacuumxid;
! }
!
! /* we're done with pg_ntclass */
! systable_endscan(scan);
! heap_close(relation, AccessShareLock);
+ /*
+ * If we got InvalidTransactionId, then all tables must be frozen. As
+ * a special protection, we only allow a database to be wholly marked
+ * as "frozen" if this is a standalone backend. Otherwise, some other
+ * backend may be modifying a table behind our back; we can't safely
+ * assume that the database is truly frozen. So if we detect that all
+ * tables are frozen but we're running on a regular backend, fall back
+ * to storing RecentXmin in datminxid (the minimum Xid which could be
+ * unfreezing a table simultaneously.)
+ */
+ if (!TransactionIdIsValid(newMinXid))
+ newMinXid = IsPostmasterEnvironment ? RecentXmin :
+ FrozenTransactionId;
+
+ /*
+ * In datvacuumxid, if we got InvalidTransactionId we use
+ * FrozenTransactionId in pg_database. This case is not valid as
+ * truncation point for pg_clog, but it's handled specially in
+ * vac_truncate_clog() because it's useful as a permanent sign
+ * that this database doesn't have a true lower limit on truncation.
+ */
+ if (!TransactionIdIsValid(newVacXid))
+ newVacXid = FrozenTransactionId;
+
+ /* Now fetch the pg_database tuple we need to update. */
+ relation = heap_open(DatabaseRelationId, RowExclusiveLock);
+
+ /* Fetch a copy of the tuple to scribble on */
+ tuple = SearchSysCacheCopy(DATABASEOID,
+ ObjectIdGetDatum(dbid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for database %u", dbid);
+
+ dbform = (Form_pg_database) GETSTRUCT(tuple);
+
+ if (TransactionIdPrecedes(dbform->datminxid, newMinXid) ||
+ TransactionIdEquals(newMinXid, FrozenTransactionId))
+ {
+ dbform->datminxid = newMinXid;
+ dirty = true;
+ }
+ if (TransactionIdPrecedes(dbform->datvacuumxid, newVacXid) ||
+ TransactionIdEquals(newVacXid, FrozenTransactionId))
+ {
+ dbform->datvacuumxid = newVacXid;
+ dirty = true;
+ }
+
+ if (dirty)
+ heap_inplace_update(relation, tuple);
+
+ heap_freetuple(tuple);
+
+ heap_close(relation, RowExclusiveLock);
+ }
+ PG_CATCH();
+ {
+ /* Make sure to reenable heap unfreezing in case of error */
+ disable_heap_unfreeze = false;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ /* reenable heap unfreezing */
+ disable_heap_unfreeze = false;
+ }
/*
* vac_truncate_clog() -- attempt to truncate the commit log
***************
*** 841,868 ****
* 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;
HeapScanDesc scan;
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.
*/
--- 953,988 ----
* will generate more-annoying warnings, and ultimately refuse to issue
* any more new XIDs.
*
* 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(void)
{
TransactionId myXID = GetCurrentTransactionId();
+ TransactionId minXID;
+ TransactionId vacuumXID;
Relation relation;
HeapScanDesc scan;
HeapTuple tuple;
int32 age;
NameData oldest_datname;
bool vacuumAlreadyWrapped = false;
! bool minAlreadyWrapped = false;
! /*
! * Initialize the minimum values to a recent value.
! */
! minXID = vacuumXID = RecentXmin;
namestrcpy(&oldest_datname, get_database_name(MyDatabaseId));
/*
+ * Note we don't initialize the oldest database name here. This is because
+ * the name will only be used if myXID - minXID is some positive quantity,
+ * and if that happens, we will also initialize the name in the loop below.
+ */
+
+ /*
* Note: the "already wrapped" cases should now be impossible due to the
* defenses in GetNewTransactionId, but we keep them anyway.
*/
***************
*** 874,884 ****
{
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))
{
if (TransactionIdPrecedes(myXID, dbform->datvacuumxid))
--- 994,1009 ----
{
Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
! /*
! * Note we ignore FrozenTransactionId here for both values. If all
! * databases turn out to be frozen, the values will end up as the
! * current XID, which is the correct truncation point for pg_clog and
! * also the correct value for the varsup.c limit.
! *
! * Also note that the all-databases-are-frozen case is pretty rare.
! * It can only happen if the user VACUUM FREEZEs all databases using
! * standalone backends.
! */
if (TransactionIdIsNormal(dbform->datvacuumxid))
{
if (TransactionIdPrecedes(myXID, dbform->datvacuumxid))
***************
*** 886,898 ****
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);
}
}
--- 1011,1023 ----
else if (TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
vacuumXID = dbform->datvacuumxid;
}
! if (TransactionIdIsNormal(dbform->datminxid))
{
! if (TransactionIdPrecedes(myXID, dbform->datminxid))
! minAlreadyWrapped = true;
! else if (TransactionIdPrecedes(dbform->datminxid, minXID))
{
! minXID = dbform->datminxid;
namecpy(&oldest_datname, &dbform->datname);
}
}
***************
*** 921,927 ****
* 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"),
--- 1046,1052 ----
* Do not update varsup.c if we seem to have suffered wraparound already;
* the computed XID might be bogus.
*/
! if (minAlreadyWrapped)
{
ereport(WARNING,
(errmsg("some databases have not been vacuumed in over 1 billion transactions"),
***************
*** 930,939 ****
}
/* 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",
--- 1055,1064 ----
}
/* 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",
***************
*** 955,965 ****
/*
* 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
--- 1080,1085 ----
***************
*** 968,981 ****
*
* At entry and exit, we are not inside a transaction.
*/
! static bool
vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool allow_toast)
{
LOCKMODE lmode;
Relation onerel;
LockRelId onerelid;
Oid toast_relid;
- bool result;
/* Begin a transaction for vacuuming this relation */
StartTransactionCommand();
--- 1088,1100 ----
*
* At entry and exit, we are not inside a transaction.
*/
! static void
vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool allow_toast)
{
LOCKMODE lmode;
Relation onerel;
LockRelId onerelid;
Oid toast_relid;
/* Begin a transaction for vacuuming this relation */
StartTransactionCommand();
***************
*** 1004,1021 ****
{
StrategyHintVacuum(false);
CommitTransactionCommand();
! return true; /* okay 'cause no data there */
}
/*
* Determine the type of lock we want --- hard exclusive lock for a FULL
! * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
! * way, we can be sure that no other backend is vacuuming the same table.
*/
! lmode = vacstmt->full ? AccessExclusiveLock : ShareUpdateExclusiveLock;
/*
! * Open the class, get an appropriate lock on it, and check permissions.
*
* We allow the user to vacuum a table if he is superuser, the table
* owner, or the database owner (but in the latter case, only if it's not
--- 1123,1164 ----
{
StrategyHintVacuum(false);
CommitTransactionCommand();
! return;
}
/*
* Determine the type of lock we want --- hard exclusive lock for a FULL
! * vacuum, ExclusiveLock for VACUUM FREEZE, but just
! * ShareUpdateExclusiveLock for concurrent vacuum. Either way, we can be
! * sure that no other backend is vacuuming the same table.
! */
! lmode = vacstmt->full ? AccessExclusiveLock :
! vacstmt->freeze ? ExclusiveLock : ShareUpdateExclusiveLock;
!
! /*
! * Open the class and get an appropriate lock on it.
*/
! PG_TRY();
! {
! /*
! * Note that we can skip unfreezing the table at this moment, because
! * we will update the correct relminxid later, if needed. Furthermore,
! * doing this allows us to skip vacuuming the table at all if it has
! * been frozen and not modified since.
! */
! disable_heap_unfreeze = true;
! onerel = relation_open(relid, lmode);
! }
! PG_CATCH();
! {
! disable_heap_unfreeze = false;
! PG_RE_THROW();
! }
! PG_END_TRY();
! disable_heap_unfreeze = false;
/*
! * Check permissions on the table.
*
* We allow the user to vacuum a table if he is superuser, the table
* owner, or the database owner (but in the latter case, only if it's not
***************
*** 1024,1031 ****
* Note we choose to treat permissions failure as a WARNING and keep
* trying to vacuum the rest of the DB --- is this appropriate?
*/
- onerel = relation_open(relid, lmode);
-
if (!(pg_class_ownercheck(RelationGetRelid(onerel), GetUserId()) ||
(pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared)))
{
--- 1167,1172 ----
***************
*** 1035,1041 ****
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return false;
}
/*
--- 1176,1182 ----
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return;
}
/*
***************
*** 1053,1059 ****
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return false;
}
/*
--- 1194,1200 ----
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return;
}
/*
***************
*** 1069,1075 ****
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return false;
}
/*
--- 1210,1216 ----
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return;
}
/*
***************
*** 1084,1090 ****
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return true; /* assume no long-lived data in temp tables */
}
/*
--- 1225,1231 ----
relation_close(onerel, lmode);
StrategyHintVacuum(false);
CommitTransactionCommand();
! return; /* assume no long-lived data in temp tables */
}
/*
***************
*** 1113,1120 ****
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);
--- 1254,1259 ----
***************
*** 1132,1148 ****
* totally unimportant for toast relations.
*/
if (toast_relid != InvalidOid)
! {
! if (!vacuum_rel(toast_relid, vacstmt, true))
! result = false; /* failed to vacuum the TOAST table? */
! }
/*
* Now release the session-level lock on the master table.
*/
UnlockRelationForSession(&onerelid, lmode);
-
- return result;
}
--- 1271,1282 ----
* totally unimportant for toast relations.
*/
if (toast_relid != InvalidOid)
! vacuum_rel(toast_relid, vacstmt, true);
/*
* Now release the session-level lock on the master table.
*/
UnlockRelationForSession(&onerelid, lmode);
}
***************
*** 1174,1179 ****
--- 1308,1315 ----
int nindexes,
i;
VRelStats *vacrelstats;
+ TransactionId FreezeLimit,
+ OldestXmin;
vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,
&OldestXmin, &FreezeLimit);
***************
*** 1186,1194 ****
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);
--- 1322,1342 ----
vacrelstats->rel_tuples = 0;
vacrelstats->hasindex = false;
+ /*
+ * Set initial minimum Xid, which will be updated if a smaller Xid is found
+ * in the relation by scan_heap.
+ *
+ * We use RecentXmin here (the minimum Xid that belongs to a transaction
+ * that is still open according to our snapshot), because it is the
+ * earliest transaction that could insert new tuples in the table after our
+ * VACUUM is done.
+ */
+ 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);
***************
*** 1216,1222 ****
{
/* Try to shrink heap */
repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages,
! nindexes, Irel);
vac_close_indexes(nindexes, Irel, NoLock);
}
else
--- 1364,1370 ----
{
/* Try to shrink heap */
repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages,
! nindexes, Irel, OldestXmin);
vac_close_indexes(nindexes, Irel, NoLock);
}
else
***************
*** 1234,1240 ****
/* update statistics in pg_class */
vac_update_relstats(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,
--- 1382,1389 ----
/* update statistics in pg_class */
vac_update_relstats(onerel, vacrelstats->rel_pages,
! vacrelstats->rel_tuples, vacrelstats->hasindex,
! vacrelstats->minxid, OldestXmin);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
***************
*** 1248,1263 ****
* 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,
--- 1397,1413 ----
* 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,
***************
*** 1371,1376 ****
--- 1521,1527 ----
{
ItemId itemid = PageGetItemId(page, offnum);
bool tupgone = false;
+ HeapTupleData tuple;
/*
* Collect un-used items too - it's possible to have indexes
***************
*** 1512,1517 ****
--- 1663,1691 ----
min_tlen = tuple.t_len;
if (tuple.t_len > max_tlen)
max_tlen = tuple.t_len;
+
+ /*
+ * Checks for pg_class.relminxid: determine the earliest
+ * Xid in any tuple of any table.
+ */
+ if (TransactionIdIsNormal(HeapTupleHeaderGetXmin(tuple.t_data)) &&
+ TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
+ vacrelstats->minxid))
+ vacrelstats->minxid = HeapTupleHeaderGetXmin(tuple.t_data);
+
+ /*
+ * If XMAX is not marked INVALID, we assume it's valid without
+ * making any check on it --- it must be recently obsoleted or
+ * still running, else HeapTupleSatisfiesVacuum would have
+ * deemed it removable.
+ */
+ if (!(tuple.t_data->t_infomask | HEAP_XMAX_INVALID))
+ {
+ if (TransactionIdIsNormal(HeapTupleHeaderGetXmax(tuple.t_data)) &&
+ TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple.t_data),
+ vacrelstats->minxid))
+ vacrelstats->minxid = HeapTupleHeaderGetXmax(tuple.t_data);
+ }
}
} /* scan along page */
***************
*** 1651,1657 ****
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;
--- 1825,1831 ----
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;
***************
*** 2994,3000 ****
/* now update statistics in pg_class */
vac_update_relstats(indrel,
stats->num_pages, stats->num_index_tuples,
! false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
--- 3168,3174 ----
/* now update statistics in pg_class */
vac_update_relstats(indrel,
stats->num_pages, stats->num_index_tuples,
! false, InvalidTransactionId, InvalidTransactionId);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
***************
*** 3063,3069 ****
/* now update statistics in pg_class */
vac_update_relstats(indrel,
stats->num_pages, stats->num_index_tuples,
! false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
--- 3237,3243 ----
/* now update statistics in pg_class */
vac_update_relstats(indrel,
stats->num_pages, stats->num_index_tuples,
! false, InvalidTransactionId, InvalidTransactionId);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
diff -rc -X diff-ignore 11fixclass/src/backend/commands/vacuumlazy.c 12ntrelminxid/src/backend/commands/vacuumlazy.c
*** 11fixclass/src/backend/commands/vacuumlazy.c 2006-06-10 19:17:47.000000000 -0400
--- 12ntrelminxid/src/backend/commands/vacuumlazy.c 2006-06-11 02:41:44.000000000 -0400
***************
*** 42,47 ****
--- 42,50 ----
#include "access/genam.h"
#include "access/heapam.h"
#include "access/xlog.h"
+ #include "catalog/catalog.h"
+ #include "catalog/heap.h"
+ #include "catalog/pg_ntclass.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "pgstat.h"
***************
*** 72,77 ****
--- 75,81 ----
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 */
***************
*** 88,100 ****
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_vacuum_index(Relation indrel,
IndexBulkDeleteResult **stats,
--- 92,102 ----
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_vacuum_index(Relation indrel,
IndexBulkDeleteResult **stats,
***************
*** 104,112 ****
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);
--- 106,115 ----
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);
***************
*** 122,128 ****
* 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.
--- 125,132 ----
* 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.
***************
*** 135,146 ****
--- 139,177 ----
int nindexes;
bool hasindex;
BlockNumber possibly_freeable;
+ TransactionId OldestXmin,
+ FreezeLimit;
if (vacstmt->verbose)
elevel = INFO;
else
elevel = DEBUG2;
+ /*
+ * We can skip vacuuming a frozen table, since we know nobody has touched
+ * it since the last VACUUM.
+ */
+ {
+ HeapTuple ctup;
+ Form_pg_ntclass ntForm;
+ Relation ntRel;
+ bool canskip = false;
+
+ ntRel = heap_open(NtClassRelationId, AccessShareLock);
+ ctup = get_ntclass_entry(ntRel, onerel);
+
+ ntForm = (Form_pg_ntclass) GETSTRUCT(ctup);
+
+ if (TransactionIdEquals(ntForm->relminxid, FrozenTransactionId))
+ canskip = true;
+
+ heap_freetuple(ctup);
+ heap_close(ntRel, AccessShareLock);
+
+ if (canskip)
+ return;
+ }
+
vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,
&OldestXmin, &FreezeLimit);
***************
*** 150,161 ****
/* 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);
--- 181,214 ----
/* XXX should we scale it up or down? Adjust vacuum.c too, if so */
vacrelstats->threshold = GetAvgFSMRequestSize(&onerel->rd_node);
+ /*
+ * Set initial minimum Xid, which will be updated if a smaller Xid is found
+ * in the relation by lazy_scan_heap.
+ *
+ * In VACUUM FREEZE, we use FrozenTransactionId here. This is safe
+ * because we acquired ExclusiveLock above, so no one can be inserting
+ * newer tuples in pages earlier to those we have scanned. If there's any
+ * tuple whose Xid we can't change, the lower bound will be raised.
+ *
+ * In the non-FREEZE case, we use RecentXmin here (the minimum Xid that
+ * belongs to a transaction that is still open according to our snapshot),
+ * because it is the earliest transaction that could concurrently insert
+ * new tuples in the table.
+ *
+ * The FREEZE case doesn't have an equivalent in VACUUM FULL because FULL
+ * in combination with FREEZE is verboten.
+ */
+ if (vacstmt->freeze)
+ vacrelstats->minxid = FrozenTransactionId;
+ else
+ 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);
***************
*** 169,175 ****
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);
--- 222,228 ----
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);
***************
*** 177,184 ****
/* Update statistics in pg_class */
vac_update_relstats(onerel,
vacrelstats->rel_pages,
! vacrelstats->rel_tuples,
! hasindex);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
--- 230,237 ----
/* Update statistics in pg_class */
vac_update_relstats(onerel,
vacrelstats->rel_pages,
! vacrelstats->rel_tuples, hasindex,
! vacrelstats->minxid, OldestXmin);
/* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
***************
*** 193,202 ****
* 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;
--- 246,259 ----
* 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 stores in vacrelstats.minxid the minimum Xid found anywhere on
+ * the table, for later recording it in pg_ntclass.relminxid.
*/
static void
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
! Relation *Irel, int nindexes, TransactionId FreezeLimit,
! TransactionId OldestXmin)
{
BlockNumber nblocks,
blkno;
***************
*** 408,413 ****
--- 465,484 ----
{
num_tuples += 1;
hastup = true;
+
+ /*
+ * If the tuple is alive, we consider it for the "minxid"
+ * calculations.
+ */
+ 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 */
***************
*** 668,676 ****
/* now update statistics in pg_class */
vac_update_relstats(indrel,
! stats->num_pages,
! stats->num_index_tuples,
! false);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
--- 739,746 ----
/* now update statistics in pg_class */
vac_update_relstats(indrel,
! stats->num_pages, stats->num_index_tuples,
! false, InvalidTransactionId, InvalidTransactionId);
ereport(elevel,
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
***************
*** 691,697 ****
* 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;
--- 761,768 ----
* 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;
***************
*** 732,738 ****
* 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)
{
--- 803,809 ----
* 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)
{
***************
*** 787,793 ****
* Returns number of nondeletable pages (last nonempty page + 1).
*/
static BlockNumber
! count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
{
BlockNumber blkno;
HeapTupleData tuple;
--- 858,865 ----
* Returns number of nondeletable pages (last nonempty page + 1).
*/
static BlockNumber
! count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats,
! TransactionId OldestXmin)
{
BlockNumber blkno;
HeapTupleData tuple;
diff -rc -X diff-ignore 11fixclass/src/backend/libpq/hba.c 12ntrelminxid/src/backend/libpq/hba.c
*** 11fixclass/src/backend/libpq/hba.c 2006-03-08 16:23:25.000000000 -0300
--- 12ntrelminxid/src/backend/libpq/hba.c 2006-06-10 19:38:04.000000000 -0400
***************
*** 1005,1011 ****
* 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
--- 1005,1011 ----
* 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
***************
*** 1013,1019 ****
*/
bool
read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
! Oid *dbtablespace, TransactionId *dbfrozenxid,
TransactionId *dbvacuumxid)
{
char buf[MAX_TOKEN];
--- 1013,1019 ----
*/
bool
read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
! Oid *dbtablespace, TransactionId *dbminxid,
TransactionId *dbvacuumxid)
{
char buf[MAX_TOKEN];
***************
*** 1036,1042 ****
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");
--- 1036,1042 ----
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");
diff -rc -X diff-ignore 11fixclass/src/backend/postmaster/autovacuum.c 12ntrelminxid/src/backend/postmaster/autovacuum.c
*** 11fixclass/src/backend/postmaster/autovacuum.c 2006-06-10 19:17:47.000000000 -0400
--- 12ntrelminxid/src/backend/postmaster/autovacuum.c 2006-06-10 19:38:04.000000000 -0400
***************
*** 80,86 ****
{
Oid oid;
char *name;
! TransactionId frozenxid;
TransactionId vacuumxid;
PgStat_StatDBEntry *entry;
int32 age;
--- 80,86 ----
{
Oid oid;
char *name;
! TransactionId minxid;
TransactionId vacuumxid;
PgStat_StatDBEntry *entry;
int32 age;
***************
*** 350,356 ****
{
autovac_dbase *tmp = lfirst(cell);
bool this_whole_db;
! int32 freeze_age,
vacuum_age;
/*
--- 350,356 ----
{
autovac_dbase *tmp = lfirst(cell);
bool this_whole_db;
! int32 true_age,
vacuum_age;
/*
***************
*** 363,371 ****
* 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));
--- 363,371 ----
* 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));
***************
*** 456,462 ****
FILE *db_file;
Oid db_id;
Oid db_tablespace;
! TransactionId db_frozenxid;
TransactionId db_vacuumxid;
filename = database_getflatfilename();
--- 456,462 ----
FILE *db_file;
Oid db_id;
Oid db_tablespace;
! TransactionId db_minxid;
TransactionId db_vacuumxid;
filename = database_getflatfilename();
***************
*** 467,473 ****
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;
--- 467,473 ----
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;
***************
*** 476,482 ****
db->oid = db_id;
db->name = pstrdup(thisname);
! db->frozenxid = db_frozenxid;
db->vacuumxid = db_vacuumxid;
/* these get set later: */
db->entry = NULL;
--- 476,482 ----
db->oid = db_id;
db->name = pstrdup(thisname);
! db->minxid = db_minxid;
db->vacuumxid = db_vacuumxid;
/* these get set later: */
db->entry = NULL;
diff -rc -X diff-ignore 11fixclass/src/backend/storage/lmgr/lmgr.c 12ntrelminxid/src/backend/storage/lmgr/lmgr.c
*** 11fixclass/src/backend/storage/lmgr/lmgr.c 2006-05-04 21:36:11.000000000 -0400
--- 12ntrelminxid/src/backend/storage/lmgr/lmgr.c 2006-06-10 22:42:34.000000000 -0400
***************
*** 15,20 ****
--- 15,21 ----
#include "postgres.h"
+ #include "access/heapam.h"
#include "access/subtrans.h"
#include "access/transam.h"
#include "access/xact.h"
***************
*** 53,58 ****
--- 54,66 ----
LOCKTAG tag;
LockAcquireResult res;
+ /*
+ * If somebody tries to lock a relation for more than a simple SELECT,
+ * unfreeze it.
+ */
+ if (lockmode > AccessShareLock)
+ heap_unfreeze(relation);
+
SET_LOCKTAG_RELATION(tag,
relation->rd_lockInfo.lockRelId.dbId,
relation->rd_lockInfo.lockRelId.relId);
***************
*** 89,94 ****
--- 97,109 ----
LOCKTAG tag;
LockAcquireResult res;
+ /*
+ * If somebody tries to lock a relation for more than a simple SELECT,
+ * unfreeze it.
+ */
+ if (lockmode > AccessShareLock)
+ heap_unfreeze(relation);
+
SET_LOCKTAG_RELATION(tag,
relation->rd_lockInfo.lockRelId.dbId,
relation->rd_lockInfo.lockRelId.relId);
diff -rc -X diff-ignore 11fixclass/src/backend/utils/init/flatfiles.c 12ntrelminxid/src/backend/utils/init/flatfiles.c
*** 11fixclass/src/backend/utils/init/flatfiles.c 2006-05-04 21:36:12.000000000 -0400
--- 12ntrelminxid/src/backend/utils/init/flatfiles.c 2006-06-10 19:38:04.000000000 -0400
***************
*** 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,234 ----
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);
--- 244,257 ----
}
/*
! * The file format is: "dbname" oid tablespace minxid 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, 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);
}
--- 272,281 ----
tempname, filename)));
/*
! * Set the transaction ID wrap limit using the oldest datminxid
*/
! if (oldest_datminxid != InvalidTransactionId)
! SetTransactionIdLimit(oldest_datminxid, &oldest_datname);
}
diff -rc -X diff-ignore 11fixclass/src/backend/utils/init/postinit.c 12ntrelminxid/src/backend/utils/init/postinit.c
*** 11fixclass/src/backend/utils/init/postinit.c 2006-05-04 21:36:12.000000000 -0400
--- 12ntrelminxid/src/backend/utils/init/postinit.c 2006-06-10 19:38:04.000000000 -0400
***************
*** 24,29 ****
--- 24,30 ----
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "catalog/pg_tablespace.h"
+ #include "commands/dbcommands.h"
#include "libpq/hba.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
***************
*** 193,198 ****
--- 194,210 ----
PGC_BACKEND, PGC_S_DEFAULT);
/*
+ * If the database is marked as frozen, unfreeze it to make sure we won't
+ * leave non-vacuumed tuples hidden behind a frozen pg_database entry.
+ *
+ * This is more paranoid than it needs to be -- if we had a way of
+ * declaring a session as being guaranteed-read-only, we could skip doing
+ * this for such sessions. In the meantime, be safe.
+ */
+ if (TransactionIdEquals(dbform->datminxid, FrozenTransactionId))
+ UnfreezeDatabase(MyDatabaseId, GetCurrentTransactionId());
+
+ /*
* Lastly, set up any database-specific configuration variables.
*/
if (IsUnderPostmaster)
diff -rc -X diff-ignore 11fixclass/src/backend/utils/mmgr/aset.c 12ntrelminxid/src/backend/utils/mmgr/aset.c
*** 11fixclass/src/backend/utils/mmgr/aset.c 2006-03-08 16:23:32.000000000 -0300
--- 12ntrelminxid/src/backend/utils/mmgr/aset.c 2006-06-11 02:50:35.000000000 -0400
***************
*** 11,17 ****
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
! * $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.66 2006-03-05 15:58:49 momjian Exp $
*
* NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set
--- 11,17 ----
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
! * $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.66 2006/03/05 15:58:49 momjian Exp $
*
* NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set
diff -rc -X diff-ignore 11fixclass/src/bin/initdb/initdb.c 12ntrelminxid/src/bin/initdb/initdb.c
*** 11fixclass/src/bin/initdb/initdb.c 2006-06-11 17:31:15.000000000 -0400
--- 12ntrelminxid/src/bin/initdb/initdb.c 2006-06-11 17:56:25.000000000 -0400
***************
*** 184,189 ****
--- 184,190 ----
static void setup_schema(void);
static void vacuum_db(void);
static void make_template0(void);
+ static void freeze_template0(void);
static void make_postgres(void);
static void trapsig(int signum);
static void check_ok(void);
***************
*** 2014,2019 ****
--- 2015,2054 ----
}
/*
+ * freeze template0
+ *
+ * Note that this routine connects to template0, not template1 like all the
+ * rest.
+ */
+ static void
+ freeze_template0(void)
+ {
+ PG_CMD_DECL;
+ char **line;
+ static char *template_freeze[] = {
+ "VACUUM FREEZE;\n",
+ NULL
+ };
+
+ fputs(_("freezing template0 ... "), stdout);
+ fflush(stdout);
+
+ snprintf(cmd, sizeof(cmd),
+ "\"%s\" %s template0 >%s",
+ backend_exec, backend_options,
+ DEVNULL);
+
+ PG_CMD_OPEN;
+
+ for (line = template_freeze; *line; line++)
+ PG_CMD_PUTS(*line);
+
+ PG_CMD_CLOSE;
+
+ check_ok();
+ }
+
+ /*
* copy template1 to postgres
*/
static void
***************
*** 2953,2958 ****
--- 2988,2995 ----
make_template0();
+ freeze_template0();
+
make_postgres();
if (authwarning != NULL)
Sólo en 12ntrelminxid/src/bin/initdb: .#initdb.c.1.116
diff -rc -X diff-ignore 11fixclass/src/include/access/heapam.h 12ntrelminxid/src/include/access/heapam.h
*** 11fixclass/src/include/access/heapam.h 2006-05-13 19:12:34.000000000 -0400
--- 12ntrelminxid/src/include/access/heapam.h 2006-06-10 19:38:04.000000000 -0400
***************
*** 122,127 ****
--- 122,129 ----
/* heapam.c */
+ extern bool disable_heap_unfreeze;
+
typedef enum
{
LockTupleShared,
***************
*** 168,173 ****
--- 170,176 ----
Buffer *buffer, ItemPointer ctid,
TransactionId *update_xmax, CommandId cid,
LockTupleMode mode, bool nowait);
+ extern void heap_unfreeze(Relation rel);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
diff -rc -X diff-ignore 11fixclass/src/include/access/transam.h 12ntrelminxid/src/include/access/transam.h
*** 11fixclass/src/include/access/transam.h 2006-03-08 16:23:46.000000000 -0300
--- 12ntrelminxid/src/include/access/transam.h 2006-06-10 19:38:04.000000000 -0400
***************
*** 123,129 ****
/* 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);
--- 123,129 ----
/* 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);
diff -rc -X diff-ignore 11fixclass/src/include/catalog/pg_attribute.h 12ntrelminxid/src/include/catalog/pg_attribute.h
*** 11fixclass/src/include/catalog/pg_attribute.h 2006-06-09 18:47:58.000000000 -0400
--- 12ntrelminxid/src/include/catalog/pg_attribute.h 2006-06-10 22:45:18.000000000 -0400
***************
*** 442,452 ****
* ----------------
*/
#define Schema_pg_ntclass \
! { 1004, {"relpages"}, 23, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
! { 1004, {"reltuples"}, 700, -1, 4, 2, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }
DATA(insert ( 1004 relpages 23 -1 4 1 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1004 reltuples 700 -1 4 2 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1004 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
/* no OIDs in pg_ntclass */
DATA(insert ( 1004 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
--- 442,456 ----
* ----------------
*/
#define Schema_pg_ntclass \
! { 1004, {"relpages"}, 23, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
! { 1004, {"reltuples"}, 700, -1, 4, 2, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
! { 1004, {"relminxid"}, 28, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
! { 1004, {"relvacuumxid"}, 28, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }
DATA(insert ( 1004 relpages 23 -1 4 1 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1004 reltuples 700 -1 4 2 0 -1 -1 f p i t f f t 0));
+ DATA(insert ( 1004 relminxid 28 -1 4 3 0 -1 -1 t p i t f f t 0));
+ DATA(insert ( 1004 relvacuumxid 28 -1 4 4 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1004 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
/* no OIDs in pg_ntclass */
DATA(insert ( 1004 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
diff -rc -X diff-ignore 11fixclass/src/include/catalog/pg_class.h 12ntrelminxid/src/include/catalog/pg_class.h
*** 11fixclass/src/include/catalog/pg_class.h 2006-06-10 18:10:25.000000000 -0400
--- 12ntrelminxid/src/include/catalog/pg_class.h 2006-06-11 01:42:29.000000000 -0400
***************
*** 149,155 ****
DESCR("");
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 "(0,4)" f f r 24 0 0 0 0 0 t f f f _null_ ));
DESCR("");
! DATA(insert OID = 1004 ( pg_ntclass PGNSP 86 PGUID 0 1004 0 0 0 "(0,5)" f f n 2 0 0 0 0 0 f f f f _null_ ));
DESCR("");
#define RELKIND_INDEX 'i' /* secondary index */
--- 149,155 ----
DESCR("");
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 "(0,4)" f f r 24 0 0 0 0 0 t f f f _null_ ));
DESCR("");
! DATA(insert OID = 1004 ( pg_ntclass PGNSP 86 PGUID 0 1004 0 0 0 "(0,5)" f f n 4 0 0 0 0 0 f f f f _null_ ));
DESCR("");
#define RELKIND_INDEX 'i' /* secondary index */
diff -rc -X diff-ignore 11fixclass/src/include/catalog/pg_database.h 12ntrelminxid/src/include/catalog/pg_database.h
*** 11fixclass/src/include/catalog/pg_database.h 2006-03-08 16:23:46.000000000 -0300
--- 12ntrelminxid/src/include/catalog/pg_database.h 2006-06-10 22:39:48.000000000 -0400
***************
*** 43,49 ****
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) */
--- 43,49 ----
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 datminxid; /* minimum XID present anywhere in the DB */
Oid dattablespace; /* default table space for this DB */
text datconfig[1]; /* database-specific GUC (VAR LENGTH) */
aclitem datacl[1]; /* access permissions (VAR LENGTH) */
***************
*** 69,75 ****
#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,75 ----
#define Anum_pg_database_datconnlimit 6
#define Anum_pg_database_datlastsysoid 7
#define Anum_pg_database_datvacuumxid 8
! #define Anum_pg_database_datminxid 9
#define Anum_pg_database_dattablespace 10
#define Anum_pg_database_datconfig 11
#define Anum_pg_database_datacl 12
diff -rc -X diff-ignore 11fixclass/src/include/catalog/pg_ntclass.h 12ntrelminxid/src/include/catalog/pg_ntclass.h
*** 11fixclass/src/include/catalog/pg_ntclass.h 2006-06-10 22:21:01.000000000 -0400
--- 12ntrelminxid/src/include/catalog/pg_ntclass.h 2006-06-11 01:33:58.000000000 -0400
***************
*** 38,48 ****
{
int4 relpages; /* # of blocks (not always up-to-date) */
float4 reltuples; /* # of tuples (not always up-to-date) */
} FormData_pg_ntclass;
/* Size of pg_ntclass tuples */
#define NTCLASS_TUPLE_SIZE \
! (offsetof(FormData_pg_ntclass,reltuples) + sizeof(float4))
/* ----------------
* Form_pg_ntclass corresponds to a pointer to a tuple with
* the format of pg_ntclass relation.
--- 38,50 ----
{
int4 relpages; /* # of blocks (not always up-to-date) */
float4 reltuples; /* # of tuples (not always up-to-date) */
+ TransactionId relminxid; /* minimum Xid present in table */
+ TransactionId relvacuumxid; /* cutoff point used at last VACUUM */
} FormData_pg_ntclass;
/* Size of pg_ntclass tuples */
#define NTCLASS_TUPLE_SIZE \
! (offsetof(FormData_pg_ntclass,relvacuumxid) + sizeof(TransactionId))
/* ----------------
* Form_pg_ntclass corresponds to a pointer to a tuple with
* the format of pg_ntclass relation.
***************
*** 54,62 ****
* compiler constants for pg_ntclass
* ----------------
*/
! #define Natts_pg_ntclass 2
#define Anum_pg_ntclass_relpages 1
#define Anum_pg_ntclass_reltuples 2
/* ----------------
* initial contents of pg_ntclass
--- 56,66 ----
* compiler constants for pg_ntclass
* ----------------
*/
! #define Natts_pg_ntclass 4
#define Anum_pg_ntclass_relpages 1
#define Anum_pg_ntclass_reltuples 2
+ #define Anum_pg_ntclass_relminxid 3
+ #define Anum_pg_ntclass_relvacuumxid 4
/* ----------------
* initial contents of pg_ntclass
***************
*** 66,75 ****
* ----------------
*/
! DATA(insert ( 0 0 ));
! DATA(insert ( 0 0 ));
! DATA(insert ( 0 0 ));
! DATA(insert ( 0 0 ));
! DATA(insert ( 0 0 ));
#endif /* PG_NTCLASS_H */
--- 70,79 ----
* ----------------
*/
! DATA(insert ( 0 0 0 0 ));
! DATA(insert ( 0 0 0 0 ));
! DATA(insert ( 0 0 0 0 ));
! DATA(insert ( 0 0 0 0 ));
! DATA(insert ( 0 0 0 0 ));
#endif /* PG_NTCLASS_H */
diff -rc -X diff-ignore 11fixclass/src/include/commands/dbcommands.h 12ntrelminxid/src/include/commands/dbcommands.h
*** 11fixclass/src/include/commands/dbcommands.h 2006-05-04 21:36:12.000000000 -0400
--- 12ntrelminxid/src/include/commands/dbcommands.h 2006-06-10 19:38:04.000000000 -0400
***************
*** 58,63 ****
--- 58,64 ----
extern void AlterDatabase(AlterDatabaseStmt *stmt);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId);
+ extern void UnfreezeDatabase(Oid dbid, TransactionId unfreezeXid);
extern Oid get_database_oid(const char *dbname);
extern char *get_database_name(Oid dbid);
diff -rc -X diff-ignore 11fixclass/src/include/commands/vacuum.h 12ntrelminxid/src/include/commands/vacuum.h
*** 11fixclass/src/include/commands/vacuum.h 2006-06-04 22:36:52.000000000 -0400
--- 12ntrelminxid/src/include/commands/vacuum.h 2006-06-10 20:22:17.000000000 -0400
***************
*** 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(Relation rel,
! BlockNumber num_pages,
! double num_tuples,
! bool hasindex);
extern void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
TransactionId *oldestXmin,
TransactionId *freezeLimit);
--- 114,122 ----
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(Relation rel, BlockNumber num_pages,
! double num_tuples, bool hasindex,
! TransactionId minxid, TransactionId vacuumxid);
extern void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel,
TransactionId *oldestXmin,
TransactionId *freezeLimit);
diff -rc -X diff-ignore 11fixclass/src/include/libpq/hba.h 12ntrelminxid/src/include/libpq/hba.h
*** 11fixclass/src/include/libpq/hba.h 2006-03-08 16:23:47.000000000 -0300
--- 12ntrelminxid/src/include/libpq/hba.h 2006-06-10 19:38:04.000000000 -0400
***************
*** 40,46 ****
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 */
--- 40,46 ----
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 */