Index: src/backend/access/transam/varsup.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/varsup.c,v retrieving revision 1.66 diff -c -r1.66 varsup.c *** src/backend/access/transam/varsup.c 22 Aug 2005 16:59:47 -0000 1.66 --- src/backend/access/transam/varsup.c 20 Sep 2005 20:31:22 -0000 *************** *** 171,181 **** /* * 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; --- 171,181 ---- /* * 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; *************** *** 183,198 **** 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; --- 183,198 ---- 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.290 diff -c -r1.290 heap.c *** src/backend/catalog/heap.c 26 Aug 2005 03:07:12 -0000 1.290 --- src/backend/catalog/heap.c 24 Sep 2005 00:43:44 -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,598 ---- /* 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; 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; break; default: /* Views, etc, have no disk storage */ new_rel_reltup->relpages = 0; new_rel_reltup->reltuples = 0; + /* Neither will views nor anything else */ + new_rel_reltup->relminxid = InvalidTransactionId; break; } *************** *** 1128,1133 **** --- 1135,1186 ---- } /* + * Invalidate (set to invalid) the datminxid of a database, when we + * drop the table that has the minimum pg_class.relminxid. + */ + static void + InvalidateDbMinxid(TransactionId relminxid) + { + Oid dbid = MyDatabaseId; + Relation dbrel; + HeapTuple tuple; + HeapScanDesc scan; + Form_pg_database dbform; + ScanKeyData entry[1]; + + if (!TransactionIdIsValid(relminxid)) + return; + + 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 */ + LockSharedObject(DatabaseRelationId, dbid, 0, AccessExclusiveLock); + + dbform = (Form_pg_database) GETSTRUCT(tuple); + if (TransactionIdEquals(dbform->datminxid, relminxid)) + dbform->datminxid = InvalidTransactionId; + + UnlockSharedObject(DatabaseRelationId, dbid, 0, AccessExclusiveLock); + + 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 *************** *** 1156,1161 **** --- 1209,1219 ---- 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); + /* * Close relcache entry, but *keep* AccessExclusiveLock on the * relation until transaction commit. This ensures no one else will Index: src/backend/commands/analyze.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/analyze.c,v retrieving revision 1.88 diff -c -r1.88 analyze.c *** src/backend/commands/analyze.c 29 Jul 2005 19:30:03 -0000 1.88 --- src/backend/commands/analyze.c 15 Sep 2005 21:50:29 -0000 *************** *** 424,430 **** vac_update_relstats(RelationGetRelid(onerel), RelationGetNumberOfBlocks(onerel), totalrows, ! hasindex); for (ind = 0; ind < nindexes; ind++) { AnlIndexData *thisdata = &indexdata[ind]; --- 424,431 ---- vac_update_relstats(RelationGetRelid(onerel), RelationGetNumberOfBlocks(onerel), totalrows, ! hasindex, ! InvalidTransactionId); for (ind = 0; ind < nindexes; ind++) { AnlIndexData *thisdata = &indexdata[ind]; *************** *** 434,440 **** vac_update_relstats(RelationGetRelid(Irel[ind]), RelationGetNumberOfBlocks(Irel[ind]), totalindexrows, ! false); } /* report results to the stats collector, too */ --- 435,441 ---- vac_update_relstats(RelationGetRelid(Irel[ind]), RelationGetNumberOfBlocks(Irel[ind]), totalindexrows, ! false, InvalidTransactionId); } /* 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.171 diff -c -r1.171 dbcommands.c *** src/backend/commands/dbcommands.c 22 Aug 2005 17:38:20 -0000 1.171 --- src/backend/commands/dbcommands.c 20 Sep 2005 21:05:01 -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 **** * after we 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 ---- * after we 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 --- 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 *************** *** 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); /* *************** *** 583,589 **** 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))); --- 581,587 ---- 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))); *************** *** 1084,1091 **** 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; --- 1082,1088 ---- 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; *************** *** 1132,1143 **** /* 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; --- 1129,1137 ---- /* 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.314 diff -c -r1.314 vacuum.c *** src/backend/commands/vacuum.c 2 Sep 2005 19:02:19 -0000 1.314 --- src/backend/commands/vacuum.c 24 Sep 2005 01:46:44 -0000 *************** *** 124,129 **** --- 124,130 ---- 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; *************** *** 191,215 **** 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, --- 192,211 ---- 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_dbminxid(Oid dbid, TransactionId prevminXID); ! static void vac_truncate_clog(TransactionId minXID); ! static TransactionId vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind); ! static TransactionId 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, *************** *** 266,278 **** 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; --- 262,274 ---- 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; + TransactionId currmin = InvalidTransactionId; if (vacstmt->verbose) elevel = INFO; *************** *** 351,382 **** */ 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. * --- 347,352 ---- *************** *** 446,453 **** if (vacstmt->vacuum) { ! if (!vacuum_rel(relid, vacstmt, RELKIND_RELATION)) ! all_rels = false; /* forget about updating dbstats */ } if (vacstmt->analyze) { --- 416,427 ---- if (vacstmt->vacuum) { ! prevmin = vacuum_rel(relid, vacstmt, RELKIND_RELATION); ! ! if (!TransactionIdIsValid(currmin) || ! (TransactionIdIsNormal(prevmin) && ! TransactionIdPrecedes(prevmin, currmin))) ! currmin = prevmin; } if (vacstmt->analyze) { *************** *** 524,538 **** 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); } } --- 498,514 ---- 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)) { ! /* Update pg_database.datminxid, if necessary. */ ! vac_update_dbminxid(MyDatabaseId, currmin); ! ! /* Try to truncate pg_clog. */ ! vac_truncate_clog(currmin); } } *************** *** 613,620 **** */ void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel, ! TransactionId *oldestXmin, ! TransactionId *freezeLimit) { TransactionId limit; --- 589,596 ---- */ void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel, ! TransactionId *oldestXmin, ! TransactionId *freezeLimit) { TransactionId limit; *************** *** 656,662 **** *freezeLimit = limit; } - /* * vac_update_relstats() -- update statistics for one relation * --- 632,637 ---- *************** *** 665,670 **** --- 640,648 ---- * doing ANALYZE, but we always update these stats. This routine works * for both index and heap relation entries in pg_class. * + * The return value is the relminxid previous to the change, in the + * case of a plain relation, or InvalidTransactionId otherwise. + * * 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,690 **** * This routine is shared by full VACUUM, lazy VACUUM, and stand-alone * ANALYZE. */ ! void vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, ! bool hasindex) { Relation rd; HeapTupleData rtup; HeapTuple ctup; Form_pg_class pgcform; Buffer buffer; /* * update number of tuples and number of pages in pg_class --- 654,669 ---- * This routine is shared by full VACUUM, lazy VACUUM, and stand-alone * ANALYZE. */ ! TransactionId vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, ! bool hasindex, TransactionId minxid) { Relation rd; HeapTupleData rtup; HeapTuple ctup; Form_pg_class pgcform; Buffer buffer; + TransactionId prevmin; /* * update number of tuples and number of pages in pg_class *************** *** 710,715 **** --- 689,704 ---- /* overwrite the existing statistics in the tuple */ pgcform = (Form_pg_class) GETSTRUCT(&rtup); + + /* Set relminxid only to regular tables */ + if (pgcform->relkind == RELKIND_RELATION) + { + prevmin = pgcform->relminxid; + pgcform->relminxid = minxid; + } + else + prevmin = pgcform->relminxid = InvalidTransactionId; + pgcform->relpages = (int32) num_pages; pgcform->reltuples = (float4) num_tuples; pgcform->relhasindex = hasindex; *************** *** 735,769 **** WriteBuffer(buffer); 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 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], --- 724,771 ---- WriteBuffer(buffer); heap_close(rd, RowExclusiveLock); + + return prevmin; } /* ! * vac_update_dbminxid() -- update the minimum Xid present in one database * ! * Update pg_database.datminxid, and the flat-file copy of pg_database. ! * The prevminXID argument is the minimum of all the relminxids that ! * the vacuumed table(s) had before this vacuum operation. The ! * datminxid is updated to the minimum of all relminxids found in ! * pg_class, if this prevminXID is found to be equal to the current ! * datminxid -- that is, one of the processed tables was the one ! * holding the datminxid back. * ! * Note that it's possible for pg_database.datminxid 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 ! * minimum relminxid. 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_dbminxid(Oid dbid, TransactionId prevminXID) { ! Relation dbrel; ! ScanKeyData entry[1]; HeapScanDesc scan; HeapTuple tuple; Form_pg_database dbform; + TransactionId prevmin; + + Assert(TransactionIdIsValid(prevminXID)); ! dbrel = heap_open(DatabaseRelationId, RowExclusiveLock); /* Must use a heap scan, since there's no syscache for pg_database */ ScanKeyInit(&entry[0], *************** *** 771,828 **** 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; --- 773,868 ---- 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; ! /* ! * If the table we just vacuumed was holding the minimum back, try to ! * update datminxid. ! */ ! if (!TransactionIdIsValid(prevmin) || ! TransactionIdEquals(prevmin, prevminXID)) ! { ! Relation classRel; ! SysScanDesc classScan; ! TransactionId newMinXID = InvalidTransactionId; ! HeapTuple classTup; ! /* 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; ! } ! ! systable_endscan(classScan); + heap_close(classRel, AccessShareLock); + + Assert(TransactionIdIsValid(newMinXID)); + + dbform->datminxid = newMinXID; + + /* invalidate the tuple in the cache and write the buffer */ + CacheInvalidateHeapTuple(dbrel, tuple); + WriteNoReleaseBuffer(scan->rs_cbuf); + + /* 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; *************** *** 830,844 **** 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); --- 870,885 ---- 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); *************** *** 853,872 **** 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); } } --- 894,906 ---- 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); } } *************** *** 876,901 **** 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"), --- 910,923 ---- heap_close(relation, AccessShareLock); ! /* Truncate CLOG to the oldest minxid */ ! TruncateCLOG(minXID); /* * Do not 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"), *************** *** 904,913 **** } /* 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", --- 926,935 ---- } /* 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", *************** *** 929,955 **** /* * 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(); --- 951,974 ---- /* * 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. * + * The return value is the pg_class.relminxid previous to the vacuum. + * * At entry and exit, we are not inside a transaction. */ ! static TransactionId vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) { LOCKMODE lmode; Relation onerel; LockRelId onerelid; Oid toast_relid; ! TransactionId prevmin; /* Begin a transaction for vacuuming this relation */ StartTransactionCommand(); *************** *** 978,984 **** { StrategyHintVacuum(false); CommitTransactionCommand(); ! return true; /* okay 'cause no data there */ } /* --- 997,1003 ---- { StrategyHintVacuum(false); CommitTransactionCommand(); ! return InvalidTransactionId; } /* *************** *** 1012,1018 **** relation_close(onerel, lmode); StrategyHintVacuum(false); CommitTransactionCommand(); ! return false; } /* --- 1031,1037 ---- relation_close(onerel, lmode); StrategyHintVacuum(false); CommitTransactionCommand(); ! return InvalidTransactionId; } /* *************** *** 1027,1033 **** relation_close(onerel, lmode); StrategyHintVacuum(false); CommitTransactionCommand(); ! return false; } /* --- 1046,1052 ---- relation_close(onerel, lmode); StrategyHintVacuum(false); CommitTransactionCommand(); ! return InvalidTransactionId; } /* *************** *** 1042,1049 **** relation_close(onerel, lmode); StrategyHintVacuum(false); CommitTransactionCommand(); ! return true; /* assume no long-lived data in temp ! * tables */ } /* --- 1061,1068 ---- relation_close(onerel, lmode); StrategyHintVacuum(false); CommitTransactionCommand(); ! return InvalidTransactionId; /* assume no long-lived data in temp ! * tables */ } /* *************** *** 1068,1078 **** * 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); --- 1087,1095 ---- * Do the actual work --- either FULL or "lazy" vacuum */ if (vacstmt->full) ! prevmin = full_vacuum_rel(onerel, vacstmt); else ! prevmin = lazy_vacuum_rel(onerel, vacstmt); /* all done with this class, but hold lock until commit */ relation_close(onerel, NoLock); *************** *** 1089,1099 **** * "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? */ } /* --- 1106,1117 ---- * "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); } /* *************** *** 1101,1107 **** */ UnlockRelationForSession(&onerelid, lmode); ! return result; } --- 1119,1125 ---- */ UnlockRelationForSession(&onerelid, lmode); ! return prevmin; } *************** *** 1121,1128 **** * * 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 --- 1139,1148 ---- * * At entry, we have already established a transaction and opened * and locked the relation. + * + * The return value is the pg_class.relminxid previous to the vacuum. */ ! static TransactionId full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) { VacPageListData vacuum_pages; /* List of pages to vacuum and/or *************** *** 1133,1138 **** --- 1153,1161 ---- int nindexes, i; VRelStats *vacrelstats; + TransactionId FreezeLimit, + OldestXmin, + prevmin; vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared, &OldestXmin, &FreezeLimit); *************** *** 1145,1153 **** 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); --- 1168,1183 ---- 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); *************** *** 1175,1181 **** { /* Try to shrink heap */ repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages, ! nindexes, Irel); vac_close_indexes(nindexes, Irel, NoLock); } else --- 1205,1211 ---- { /* Try to shrink heap */ repair_frag(vacrelstats, onerel, &vacuum_pages, &fraged_pages, ! nindexes, Irel, OldestXmin); vac_close_indexes(nindexes, Irel, NoLock); } else *************** *** 1192,1203 **** 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, vacstmt->analyze, vacrelstats->rel_tuples); } --- 1222,1237 ---- vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages); /* update statistics in pg_class */ ! prevmin = vac_update_relstats(RelationGetRelid(onerel), ! vacrelstats->rel_pages, ! vacrelstats->rel_tuples, ! vacrelstats->hasindex, vacrelstats->minxid); /* report results to the stats collector, too */ pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared, vacstmt->analyze, vacrelstats->rel_tuples); + + return prevmin; } *************** *** 1207,1222 **** * 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, --- 1241,1257 ---- * 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, *************** *** 1325,1330 **** --- 1360,1366 ---- { ItemId itemid = PageGetItemId(page, offnum); bool tupgone = false; + HeapTupleData tuple; /* * Collect un-used items too - it's possible to have indexes *************** *** 1468,1473 **** --- 1504,1521 ---- 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; --- 1657,1663 ---- 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; *************** *** 2967,2973 **** /* 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", --- 3015,3021 ---- /* now update statistics in pg_class */ vac_update_relstats(RelationGetRelid(indrel), stats->num_pages, stats->num_index_tuples, ! false, InvalidTransactionId); ereport(elevel, (errmsg("index \"%s\" now contains %.0f row versions in %u pages", *************** *** 3034,3040 **** /* 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", --- 3082,3088 ---- /* now update statistics in pg_class */ vac_update_relstats(RelationGetRelid(indrel), stats->num_pages, stats->num_index_tuples, ! false, InvalidTransactionId); 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.58 diff -c -r1.58 vacuumlazy.c *** src/backend/commands/vacuumlazy.c 2 Sep 2005 19:02:20 -0000 1.58 --- src/backend/commands/vacuumlazy.c 22 Sep 2005 14:30:45 -0000 *************** *** 70,75 **** --- 70,76 ---- 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 */ *************** *** 86,98 **** 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, --- 87,97 ---- 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, *************** *** 101,109 **** 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); --- 100,109 ---- 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); *************** *** 124,131 **** * * At entry, we have already established a transaction and opened * and locked the relation. */ ! void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) { LVRelStats *vacrelstats; --- 124,133 ---- * * At entry, we have already established a transaction and opened * and locked the relation. + * + * The return value is the pg_class.relminxid previous to the vacuum. */ ! TransactionId lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) { LVRelStats *vacrelstats; *************** *** 133,138 **** --- 135,143 ---- int nindexes; bool hasindex; BlockNumber possibly_freeable; + TransactionId prevmin, + OldestXmin, + FreezeLimit; if (vacstmt->verbose) elevel = INFO; *************** *** 148,159 **** /* 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); --- 153,167 ---- /* 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); *************** *** 167,186 **** 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, vacstmt->analyze, vacrelstats->rel_tuples); } --- 175,196 ---- 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 */ ! prevmin = vac_update_relstats(RelationGetRelid(onerel), ! vacrelstats->rel_pages, ! vacrelstats->rel_tuples, hasindex, ! vacrelstats->minxid); /* report results to the stats collector, too */ pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared, vacstmt->analyze, vacrelstats->rel_tuples); + + return prevmin; } *************** *** 191,200 **** * 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; --- 201,214 ---- * 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 **** --- 434,450 ---- { 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", --- 669,675 ---- vac_update_relstats(RelationGetRelid(indrel), stats->num_pages, stats->num_index_tuples, ! false, InvalidTransactionId); 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", --- 745,751 ---- vac_update_relstats(RelationGetRelid(indrel), stats->num_pages, stats->num_index_tuples, ! false, InvalidTransactionId); 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; --- 766,773 ---- * 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; *************** *** 783,789 **** * optional, 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) { --- 809,815 ---- * optional, 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) { *************** *** 838,844 **** * Returns number of nondeletable pages (last nonempty page + 1). */ static BlockNumber ! count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) { BlockNumber blkno; HeapTupleData tuple; --- 864,871 ---- * 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.147 diff -c -r1.147 hba.c *** src/backend/libpq/hba.c 11 Aug 2005 21:11:44 -0000 1.147 --- src/backend/libpq/hba.c 20 Sep 2005 20:42:35 -0000 *************** *** 1000,1015 **** * 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 * here because it uses the next_token() infrastructure. */ bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid, ! Oid *dbtablespace, TransactionId *dbfrozenxid, ! TransactionId *dbvacuumxid) { char buf[MAX_TOKEN]; --- 1000,1013 ---- * 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 * * This is not much related to the other functions in hba.c, but we put it * here because it uses the next_token() infrastructure. */ bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid, ! Oid *dbtablespace, TransactionId *dbminxid) { char buf[MAX_TOKEN]; *************** *** 1031,1041 **** 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"); ! *dbvacuumxid = atoxid(buf); /* expect EOL next */ if (next_token(fp, buf, sizeof(buf))) elog(FATAL, "bad data in flat pg_database file"); --- 1029,1035 ---- next_token(fp, buf, sizeof(buf)); if (!isdigit((unsigned char) buf[0])) elog(FATAL, "bad data in flat pg_database file"); ! *dbminxid = atoxid(buf); /* expect EOL next */ if (next_token(fp, buf, sizeof(buf))) 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.4 diff -c -r1.4 autovacuum.c *** src/backend/postmaster/autovacuum.c 15 Aug 2005 16:25:17 -0000 1.4 --- src/backend/postmaster/autovacuum.c 20 Sep 2005 20:45:55 -0000 *************** *** 76,83 **** { Oid oid; char *name; ! TransactionId frozenxid; ! TransactionId vacuumxid; PgStat_StatDBEntry *entry; int32 age; } autovac_dbase; --- 76,82 ---- { Oid oid; char *name; ! TransactionId minxid; PgStat_StatDBEntry *entry; int32 age; } autovac_dbase; *************** *** 328,335 **** { autovac_dbase *tmp = lfirst(cell); bool this_whole_db; - int32 freeze_age, - vacuum_age; /* * We look for the database that most urgently needs a database-wide --- 327,332 ---- *************** *** 337,349 **** * transactions sooner than vacuum.c's vac_truncate_clog() would * decide to start giving warnings. If any such db is found, we * ignore all other dbs. ! * ! * 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)); --- 334,341 ---- * transactions sooner than vacuum.c's vac_truncate_clog() would * decide to start giving warnings. If any such db is found, we * ignore all other dbs. ! */ ! tmp->age = (int32) (nextXid - tmp->minxid); this_whole_db = (tmp->age > (int32) ((MaxTransactionId >> 3) * 3 - 100000)); *************** *** 443,450 **** FILE *db_file; Oid db_id; Oid db_tablespace; ! TransactionId db_frozenxid; ! TransactionId db_vacuumxid; filename = database_getflatfilename(); db_file = AllocateFile(filename, "r"); --- 435,441 ---- FILE *db_file; Oid db_id; Oid db_tablespace; ! TransactionId db_minxid; filename = database_getflatfilename(); db_file = AllocateFile(filename, "r"); *************** *** 454,461 **** 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; --- 445,451 ---- errmsg("could not open file \"%s\": %m", filename))); while (read_pg_database_line(db_file, thisname, &db_id, ! &db_tablespace, &db_minxid)) { autovac_dbase *db; *************** *** 463,470 **** db->oid = db_id; db->name = pstrdup(thisname); ! db->frozenxid = db_frozenxid; ! db->vacuumxid = db_vacuumxid; /* these get set later: */ db->entry = NULL; db->age = 0; --- 453,459 ---- db->oid = db_id; db->name = pstrdup(thisname); ! db->minxid = db_minxid; /* these get set later: */ db->entry = NULL; db->age = 0; Index: src/backend/utils/init/flatfiles.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/flatfiles.c,v retrieving revision 1.14 diff -c -r1.14 flatfiles.c *** src/backend/utils/init/flatfiles.c 11 Aug 2005 21:11:46 -0000 1.14 --- src/backend/utils/init/flatfiles.c 20 Sep 2005 20:25:32 -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,232 ---- char *datname; Oid datoid; Oid dattablespace; ! TransactionId datminxid; datname = NameStr(dbform->datname); datoid = HeapTupleGetOid(tuple); dattablespace = dbform->dattablespace; ! datminxid = dbform->datminxid; /* ! * 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); --- 242,255 ---- } /* ! * The file format is: "dbname" oid tablespace minxid * ! * 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\n", ! datoid, dattablespace, datminxid); } 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); } --- 270,279 ---- tempname, filename))); /* ! * Set the transaction ID wrap limit using the oldest datminxid */ ! if (oldest_datminxid != InvalidTransactionId) ! SetTransactionIdLimit(oldest_datminxid, &oldest_datname); } Index: src/backend/utils/init/postinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/postinit.c,v retrieving revision 1.157 diff -c -r1.157 postinit.c *** src/backend/utils/init/postinit.c 11 Aug 2005 21:11:46 -0000 1.157 --- src/backend/utils/init/postinit.c 20 Sep 2005 21:01:46 -0000 *************** *** 91,98 **** errmsg("could not open file \"%s\": %m", filename))); while (read_pg_database_line(db_file, thisname, db_id, ! db_tablespace, &dummyxid, ! &dummyxid)) { if (strcmp(thisname, name) == 0) { --- 91,97 ---- errmsg("could not open file \"%s\": %m", filename))); while (read_pg_database_line(db_file, thisname, db_id, ! db_tablespace, &dummyxid)) { if (strcmp(thisname, name) == 0) { Index: src/include/access/transam.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/access/transam.h,v retrieving revision 1.55 diff -c -r1.55 transam.h *** src/include/access/transam.h 12 Aug 2005 01:36:03 -0000 1.55 --- src/include/access/transam.h 20 Sep 2005 20:31:50 -0000 *************** *** 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); Index: src/include/catalog/pg_attribute.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_attribute.h,v retrieving revision 1.118 diff -c -r1.118 pg_attribute.h *** src/include/catalog/pg_attribute.h 28 Jun 2005 05:09:04 -0000 1.118 --- src/include/catalog/pg_attribute.h 15 Sep 2005 21:50:29 -0000 *************** *** 406,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, {"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)); --- 406,413 ---- { 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, {"relacl"}, 1034, -1, -1, 26, 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)); *************** *** 432,438 **** 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)); --- 433,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 relacl 1034 -1 -1 26 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.89 diff -c -r1.89 pg_class.h *** src/include/catalog/pg_class.h 28 Jun 2005 05:09:05 -0000 1.89 --- src/include/catalog/pg_class.h 15 Sep 2005 21:50:29 -0000 *************** *** 74,79 **** --- 74,80 ---- bool relhaspkey; /* has PRIMARY KEY index */ bool relhasrules; /* has associated rules */ bool relhassubclass; /* has derived classes */ + TransactionId relminxid; /* minimum Xid present in table */ /* * 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 --- 84,90 ---- /* Size of fixed part of pg_class tuples, not counting relacl or padding */ #define CLASS_TUPLE_SIZE \ ! (offsetof(FormData_pg_class,relminxid) + 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 --- 104,111 ---- * relacl field. This is a kluge. * ---------------- */ ! #define Natts_pg_class_fixed 25 ! #define Natts_pg_class 26 #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 --- 130,137 ---- #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_relacl 26 /* ---------------- * 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 */ --- 141,153 ---- * ---------------- */ ! 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 _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 _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 _null_ )); DESCR(""); ! DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 26 0 0 0 0 0 t f f f 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.37 diff -c -r1.37 pg_database.h *** src/include/catalog/pg_database.h 31 Jul 2005 17:19:21 -0000 1.37 --- src/include/catalog/pg_database.h 20 Sep 2005 19:48:59 -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,48 ---- 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 */ Oid dattablespace; /* default table space for this DB */ text datconfig[1]; /* database-specific GUC (VAR LENGTH) */ aclitem datacl[1]; /* access permissions (VAR LENGTH) */ *************** *** 60,66 **** * compiler constants for pg_database * ---------------- */ ! #define Natts_pg_database 12 #define Anum_pg_database_datname 1 #define Anum_pg_database_datdba 2 #define Anum_pg_database_encoding 3 --- 59,65 ---- * compiler constants for pg_database * ---------------- */ ! #define Natts_pg_database 11 #define Anum_pg_database_datname 1 #define Anum_pg_database_datdba 2 #define Anum_pg_database_encoding 3 *************** *** 68,80 **** #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 ! DATA(insert OID = 1 ( template1 PGUID ENCODING t t -1 0 0 0 1663 _null_ _null_ )); DESCR("Default template database"); #define TemplateDbOid 1 --- 67,78 ---- #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_dattablespace 9 ! #define Anum_pg_database_datconfig 10 ! #define Anum_pg_database_datacl 11 ! DATA(insert OID = 1 ( template1 PGUID ENCODING t t -1 0 0 1663 _null_ _null_ )); DESCR("Default template database"); #define TemplateDbOid 1 Index: src/include/commands/vacuum.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/vacuum.h,v retrieving revision 1.60 diff -c -r1.60 vacuum.h *** src/include/commands/vacuum.h 14 Jul 2005 05:13:43 -0000 1.60 --- src/include/commands/vacuum.h 21 Sep 2005 15:05:37 -0000 *************** *** 129,138 **** 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); --- 129,137 ---- 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 TransactionId vac_update_relstats(Oid relid, BlockNumber num_pages, ! double num_tuples, bool hasindex, ! TransactionId minxid); extern void vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel, TransactionId *oldestXmin, TransactionId *freezeLimit); *************** *** 142,148 **** 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); --- 141,147 ---- extern void vacuum_delay_point(void); /* in commands/vacuumlazy.c */ ! extern TransactionId lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt); /* 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.40 diff -c -r1.40 hba.h *** src/include/libpq/hba.h 11 Aug 2005 21:11:48 -0000 1.40 --- src/include/libpq/hba.h 20 Sep 2005 20:47:04 -0000 *************** *** 37,43 **** 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 */ --- 37,42 ---- 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); #endif /* HBA_H */