Index: src/backend/access/transam/xact.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.209 diff -c -r1.209 xact.c *** src/backend/access/transam/xact.c 29 Jun 2005 22:51:53 -0000 1.209 --- src/backend/access/transam/xact.c 30 Jun 2005 03:13:27 -0000 *************** *** 1485,1491 **** /* * set the current transaction state information appropriately during ! * the abort processing */ s->state = TRANS_COMMIT; --- 1485,1491 ---- /* * set the current transaction state information appropriately during ! * the commit processing */ s->state = TRANS_COMMIT; Index: src/backend/catalog/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/Makefile,v retrieving revision 1.54 diff -c -r1.54 Makefile *** src/backend/catalog/Makefile 28 Jun 2005 05:08:52 -0000 1.54 --- src/backend/catalog/Makefile 29 Jun 2005 20:15:18 -0000 *************** *** 32,38 **** pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_namespace.h pg_conversion.h pg_database.h \ ! pg_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \ indexing.h \ ) --- 32,38 ---- pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_namespace.h pg_conversion.h pg_database.h \ ! pg_authid.h pg_auth_members.h pg_autovacuum.h pg_tablespace.h pg_depend.h \ indexing.h \ ) Index: src/backend/catalog/heap.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/heap.c,v retrieving revision 1.285 diff -c -r1.285 heap.c *** src/backend/catalog/heap.c 5 Jun 2005 00:38:07 -0000 1.285 --- src/backend/catalog/heap.c 29 Jun 2005 22:26:51 -0000 *************** *** 37,42 **** --- 37,43 ---- #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/pg_attrdef.h" + #include "catalog/pg_autovacuum.h" #include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" *************** *** 75,80 **** --- 76,83 ---- static void StoreRelCheck(Relation rel, char *ccname, char *ccbin); static void StoreConstraints(Relation rel, TupleDesc tupdesc); static void SetRelationNumChecks(Relation rel, int numchecks); + static void DeleteAutovacuumTuple(Oid relid); + static void AddNewAutovacuumTuple(Oid new_rel_oid); /* ---------------------------------------------------------------- *************** *** 296,302 **** /* ---------------------------------------------------------------- * heap_create_with_catalog - Create a cataloged relation * ! * this is done in 6 steps: * * 1) CheckAttributeNamesTypes() is used to make certain the tuple * descriptor contains a valid set of attribute names and types --- 299,305 ---- /* ---------------------------------------------------------------- * heap_create_with_catalog - Create a cataloged relation * ! * this is done in 9 steps: * * 1) CheckAttributeNamesTypes() is used to make certain the tuple * descriptor contains a valid set of attribute names and types *************** *** 310,324 **** * 4) AddNewRelationTuple() is called to register the * relation in pg_class. * ! * 5) TypeCreate() is called to define a new type corresponding * to the new relation. * ! * 6) AddNewAttributeTuples() is called to register the * new relation's schema in pg_attribute. * ! * 7) StoreConstraints is called () - vadim 08/22/97 * ! * 8) the relations are closed and the new relation's oid * is returned. * * ---------------------------------------------------------------- --- 313,330 ---- * 4) AddNewRelationTuple() is called to register the * relation in pg_class. * ! * 5) AddNewAutovacuumTuple() is called to register the relation ! * in pg_autovacuum. ! * ! * 6) TypeCreate() is called to define a new type corresponding * to the new relation. * ! * 7) AddNewAttributeTuples() is called to register the * new relation's schema in pg_attribute. * ! * 8) StoreConstraints is called () - vadim 08/22/97 * ! * 9) the relations are closed and the new relation's oid * is returned. * * ---------------------------------------------------------------- *************** *** 548,553 **** --- 554,593 ---- heap_close(rel, RowExclusiveLock); } + static void + AddNewAutovacuumTuple(Oid new_rel_oid) + { + HeapTuple tup; + Relation rel; + Datum values[Natts_pg_autovacuum]; + bool nulls[Natts_pg_autovacuum]; + + rel = heap_open(AutovacuumRelationId, RowExclusiveLock); + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + /* Default initial values */ + values[Anum_pg_autovacuum_vacrelid - 1] = ObjectIdGetDatum(new_rel_oid); + values[Anum_pg_autovacuum_enabled - 1] = BoolGetDatum(true); + values[Anum_pg_autovacuum_vac_base_thresh - 1] = Int32GetDatum(-1); + values[Anum_pg_autovacuum_vac_scale_factor - 1] = Float4GetDatum(-1.0); + values[Anum_pg_autovacuum_vac_last_cnt - 1] = Float4GetDatum(0.0); + values[Anum_pg_autovacuum_anl_base_thresh - 1] = Int32GetDatum(-1); + values[Anum_pg_autovacuum_anl_scale_factor - 1] = Float4GetDatum(-1.0); + values[Anum_pg_autovacuum_anl_last_cnt - 1] = Float4GetDatum(0.0); + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + + simple_heap_insert(rel, tup); + + /* Keep indexes current */ + CatalogUpdateIndexes(rel, tup); + + heap_freetuple(tup); + heap_close(rel, RowExclusiveLock); + } + /* -------------------------------- * AddNewRelationTuple * *************** *** 752,757 **** --- 792,805 ---- oidislocal, oidinhcount); /* + * Add an autovacuum entry for the relation, if it's a regular table. Skip + * this in bootstrap mode, since the pg_autovacuum relation may not exist + * yet. + */ + if (!IsBootstrapProcessingMode() && relkind == RELKIND_RELATION) + AddNewAutovacuumTuple(new_rel_oid); + + /* * make a dependency link to force the relation to be deleted if its * namespace is. Skip this in bootstrap mode, since we don't make * dependencies while bootstrapping. *************** *** 862,867 **** --- 910,937 ---- heap_close(pg_class_desc, RowExclusiveLock); } + static void + DeleteAutovacuumTuple(Oid relid) + { + Relation avRel; + HeapTuple tup; + + /* Grab an appropiate lock on pg_autovacuum */ + avRel = heap_open(AutovacuumRelationId, RowExclusiveLock); + + tup = SearchSysCache(AUTOVACRELID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + /* delete the relation tuple from pg_autovacuum, and finish up */ + simple_heap_delete(avRel, &tup->t_self); + + ReleaseSysCache(tup); + heap_close(avRel, RowExclusiveLock); + } + /* * DeleteAttributeTuples * *************** *** 1134,1139 **** --- 1204,1210 ---- heap_drop_with_catalog(Oid relid) { Relation rel; + bool drop_autovac; /* * Open and lock the relation. *************** *** 1150,1155 **** --- 1221,1228 ---- smgrscheduleunlink(rel->rd_smgr, rel->rd_istemp); } + drop_autovac = rel->rd_rel->relkind == RELKIND_RELATION; + /* * Close relcache entry, but *keep* AccessExclusiveLock on the * relation until transaction commit. This ensures no one else will *************** *** 1187,1192 **** --- 1260,1271 ---- DeleteAttributeTuples(relid); /* + * delete autovacuum tuple if needed + */ + if (drop_autovac) + DeleteAutovacuumTuple(relid); + + /* * delete relation tuple */ DeleteRelationTuple(relid); Index: src/backend/commands/analyze.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/analyze.c,v retrieving revision 1.86 diff -c -r1.86 analyze.c *** src/backend/commands/analyze.c 6 May 2005 17:24:53 -0000 1.86 --- src/backend/commands/analyze.c 6 Jul 2005 23:23:31 -0000 *************** *** 29,34 **** --- 29,35 ---- #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" + #include "postmaster/autovacuum.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" *************** *** 310,315 **** --- 311,325 ---- if (attr_cnt <= 0 && !analyzableindex) { vac_close_indexes(nindexes, Irel, AccessShareLock); + + /* + * If we are going to quit, at least make autovacuum comfortable + * with its job. If this is a VACUUM ANALYZE, it was done + * already. + */ + if (!vacstmt->vacuum) + update_autovac_tuple(RelationGetRelid(onerel), false, true, NULL); + relation_close(onerel, AccessShareLock); return; } *************** *** 423,428 **** --- 433,444 ---- totalindexrows, false); } + + /* + * Update pg_autovacuum tuple too. If this is a VACUUM ANALYZE, + * this was already done. + */ + update_autovac_tuple(RelationGetRelid(onerel), false, true, NULL); } /* Done with indexes */ Index: src/backend/commands/vacuum.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.310 diff -c -r1.310 vacuum.c *** src/backend/commands/vacuum.c 14 Jun 2005 22:15:32 -0000 1.310 --- src/backend/commands/vacuum.c 6 Jul 2005 23:22:07 -0000 *************** *** 35,40 **** --- 35,42 ---- #include "commands/vacuum.h" #include "executor/executor.h" #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/autovacuum.h" #include "storage/freespace.h" #include "storage/procarray.h" #include "storage/smgr.h" *************** *** 47,53 **** #include "utils/memutils.h" #include "utils/relcache.h" #include "utils/syscache.h" - #include "pgstat.h" typedef struct VacPageData --- 49,54 ---- *************** *** 252,260 **** * after finishing the vacuum. This is mainly so that we can let go * the AccessExclusiveLock that we may be holding.) * ! * ANALYZE (without VACUUM) can run either way. */ ! if (vacstmt->vacuum) { PreventTransactionChain((void *) vacstmt, stmttype); in_outer_xact = false; --- 253,262 ---- * after finishing the vacuum. This is mainly so that we can let go * the AccessExclusiveLock that we may be holding.) * ! * ANALYZE (without VACUUM) can run either way. Also, assume ! * the Autovacuum process knows what it's doing. */ ! if (vacstmt->vacuum && !IsAutoVacuumProcess()) { PreventTransactionChain((void *) vacstmt, stmttype); in_outer_xact = false; *************** *** 1146,1151 **** --- 1148,1158 ---- /* update statistics in pg_class */ vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages, vacrelstats->rel_tuples, vacrelstats->hasindex); + + /* Update pg_autovacuum data, if it's a regular relation. */ + if (onerel->rd_rel->relkind == RELKIND_RELATION) + update_autovac_tuple(RelationGetRelid(onerel), true, vacstmt->analyze, + NULL); } Index: src/backend/commands/vacuumlazy.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuumlazy.c,v retrieving revision 1.54 diff -c -r1.54 vacuumlazy.c *** src/backend/commands/vacuumlazy.c 19 May 2005 21:35:46 -0000 1.54 --- src/backend/commands/vacuumlazy.c 6 Jul 2005 22:11:25 -0000 *************** *** 44,49 **** --- 44,50 ---- #include "access/xlog.h" #include "commands/vacuum.h" #include "miscadmin.h" + #include "postmaster/autovacuum.h" #include "storage/freespace.h" #include "storage/smgr.h" #include "utils/lsyscache.h" *************** *** 179,184 **** --- 180,190 ---- vacrelstats->rel_pages, vacrelstats->rel_tuples, hasindex); + + /* Update pg_autovacuum data, if it's a regular relation. */ + if (onerel->rd_rel->relkind == RELKIND_RELATION) + update_autovac_tuple(RelationGetRelid(onerel), true, vacstmt->analyze, + NULL); } Index: src/backend/postmaster/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/Makefile,v retrieving revision 1.20 diff -c -r1.20 Makefile *** src/backend/postmaster/Makefile 10 Mar 2005 07:14:03 -0000 1.20 --- src/backend/postmaster/Makefile 29 Jun 2005 18:41:49 -0000 *************** *** 12,18 **** top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = bgwriter.o fork_process.o pgarch.o pgstat.o postmaster.o syslogger.o all: SUBSYS.o --- 12,18 ---- top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = bgwriter.o fork_process.o pgarch.o pgstat.o postmaster.o syslogger.o autovacuum.o all: SUBSYS.o Index: src/backend/postmaster/autovacuum.c =================================================================== RCS file: src/backend/postmaster/autovacuum.c diff -N src/backend/postmaster/autovacuum.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/backend/postmaster/autovacuum.c 6 Jul 2005 22:38:25 -0000 *************** *** 0 **** --- 1,946 ---- + /*------------- + * autovacuum.c + * + * PostgreSQL Integrated Autovacuum Daemon + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * $PostgreSQL$ + *------------- + */ + #include "postgres.h" + + #include + #include + #include + #include + + #include "access/heapam.h" + #include "access/skey.h" + #include "access/xact.h" + #include "catalog/indexing.h" + #include "catalog/pg_autovacuum.h" + #include "catalog/pg_database.h" + #include "commands/dbcommands.h" + #include "commands/vacuum.h" + #include "libpq/hba.h" + #include "libpq/pqsignal.h" + #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/autovacuum.h" + #include "postmaster/fork_process.h" + #include "postmaster/postmaster.h" + #include "storage/ipc.h" + #include "storage/fd.h" + #include "storage/lwlock.h" + #include "storage/pmsignal.h" + #include "utils/builtins.h" + #include "utils/flatfiles.h" + #include "utils/fmgroids.h" + #include "utils/guc.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" + #include "utils/ps_status.h" + #include "utils/relcache.h" + #include "utils/syscache.h" + + + bool am_autovacuum = false; + + /* + * GUC parameters + * + * FIXME -- Verify these default values + */ + bool autovacuum_start_daemon = false; + int autovacuum_naptime = 60; + int autovacuum_vac_thresh = 1000; + double autovacuum_vac_scale = 0.4; + int autovacuum_anl_thresh = 500; + double autovacuum_anl_scale = 0.2; + + /* + * Flags set by interrupt handlers for later service in the main loop. + */ + static volatile sig_atomic_t shutdown_requested = false; + static volatile sig_atomic_t got_SIGHUP = false; + static time_t last_autovac_start_time = 0; + + /* struct to keep list of candidate databases for vacuum */ + typedef struct autovac_dbase + { + Oid oid; + char *name; + PgStat_StatDBEntry *entry; + } autovac_dbase; + + /* + * Operation to perform on a table. UpdateCounts means only + * update the pg_autovacuum vac_last_cnt and/or anl_last_cnt + * fields for the table, do nothing to the table proper. + */ + typedef enum AutovacOp + { + AVOperNone, + AVOperUpdateCounts, + AVOperAnalyze, + AVOperVacuumAnalyze + } AutovacOp; + + /* struct to keep track of tables to process */ + typedef struct autovac_table + { + Oid oid; + AutovacOp operation; + float4 reltuples; + bool update_anltuples; + bool update_vactuples; + } autovac_table; + + #ifdef EXEC_BACKEND + static pid_t autovac_forkexec(void); + #endif + NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]); + static void autovac_check_wraparound(void); + static void do_autovacuum(PgStat_StatDBEntry *dbentry); + static List *autovac_get_database_list(void); + static void test_rel_for_autovac(PgStat_StatDBEntry *dbentry, + HeapTuple tuple, List **vacuum_tables, List **analyze_tables, + List **update_tables); + static void autovacuum_do_vac_analyze(autovac_table *tab); + static void check_shutdown_flag(void); + static void autovac_sigquit_handler(SIGNAL_ARGS); + static void autovac_sigusr2_handler(SIGNAL_ARGS); + static void autovac_sighup_handler(SIGNAL_ARGS); + + + /* + * Main entry point for autovacuum controller process. + * + * This code is heavily based on pgarch.c, q.v. + */ + int + autovac_start(void) + { + time_t curtime; + pid_t AutoVacPID; + + /* Do nothing if no autovacuum process needed */ + if (!AutoVacuumingActive()) + return 0; + + /* + * Do nothing if too soon since last autovacuum start. This is a safety + * valve to protect against continuous respawn attempts if the autovacuum + * is dying immediately at launch. Note that since we will be re-called + * from the postmaster main loop, we will get another chance later. + */ + curtime = time(NULL); + if ((unsigned int) (curtime - last_autovac_start_time) < + (unsigned int) autovacuum_naptime) + return 0; + last_autovac_start_time = curtime; + + #ifdef EXEC_BACKEND + switch((AutoVacPID = autovac_forkexec())) + #else + switch((AutoVacPID = fork_process())) + #endif + { + case -1: + ereport(LOG, + (errmsg("could not fork autovacuum process: %m"))); + return 0; + + #ifndef EXEC_BACKEND + case 0: + /* in postmaster child ... */ + /* Close the postmaster's sockets */ + ClosePostmasterPorts(false); + + AutoVacMain(0, NULL); + break; + #endif + default: + return (int) AutoVacPID; + } + + /* shouldn't get here */ + return 0; + } + + #ifdef EXEC_BACKEND + /* + * autovac_forkexec() + * + * Format up the arglist for the autovacuum process, then fork and exec. + */ + static pid_t + autovac_forkexec(void) + { + char *av[10]; + int ac = 0; + + av[ac++] = "postgres"; + av[ac++] = "-forkautovac"; + av[ac++] = NULL; /* filled in by postmaster_forkexec */ + av[ac] = NULL; + + Assert(ac < lengthof(av)); + + return postmaster_forkexec(ac, av); + } + #endif /* EXEC_BACKEND */ + + /* + * AutoVacMain + */ + NON_EXEC_STATIC void + AutoVacMain(int argc, char *argv[]) + { + ListCell *cell; + List *dblist; + autovac_dbase *db = NULL; + sigjmp_buf local_sigjmp_buf; + + /* we are a postmaster subprocess now */ + IsUnderPostmaster = true; + am_autovacuum = true; + + /* reset MyProcPid */ + MyProcPid = getpid(); + + /* Lose the postmaster's on-exit routines */ + on_exit_reset(); + + /* + * Ignore all signals usually bound to some action in the postmaster, + * except for SIGHUP, SIGUSR2 and SIGQUIT. + */ + pqsignal(SIGHUP, autovac_sighup_handler); + pqsignal(SIGINT, SIG_IGN); + pqsignal(SIGTERM, SIG_IGN); + pqsignal(SIGQUIT, autovac_sigquit_handler); + pqsignal(SIGALRM, SIG_IGN); + pqsignal(SIGPIPE, SIG_IGN); + pqsignal(SIGUSR1, SIG_IGN); + pqsignal(SIGUSR2, autovac_sigusr2_handler); + pqsignal(SIGCHLD, SIG_DFL); + pqsignal(SIGTTIN, SIG_DFL); + pqsignal(SIGTTOU, SIG_DFL); + pqsignal(SIGCONT, SIG_DFL); + pqsignal(SIGWINCH, SIG_DFL); + PG_SETMASK(&UnBlockSig); + + /* Identify myself via ps */ + init_ps_display("autovacuum process", "", ""); + set_ps_display(""); + + /* Init XLOG file paths */ + BaseInit(); + + /* + * If an exception is encountered, processing resumes here. + * + * See notes in postgres.c about the design of this coding. + */ + if (sigsetjmp(local_sigjmp_buf, 1) != 0) + { + /* Prevents interrupts while cleaning up */ + HOLD_INTERRUPTS(); + + /* Report the error to the server log */ + EmitErrorReport(); + + /* + * We can now go away. Note that because we'll call InitProcess, + * a callback will be registered to do ProcKill, which will clean + * up necessary state. + */ + proc_exit(0); + } + + /* We can now handle ereport(ERROR) */ + PG_exception_stack = &local_sigjmp_buf; + + /* Get a list of databases */ + dblist = autovac_get_database_list(); + + /* + * Choose a database to connect to. We pick the database that was least + * recently auto-vacuumed. + * + * XXX This could be improved if we had more data about whether it needs + * vacuuming before connecting to it. (We could look at the pgstat data + * for each of its tables.) + */ + foreach(cell, dblist) + { + autovac_dbase *tmp = lfirst(cell); + + tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid); + if (!tmp->entry) + continue; + + /* + * Don't try to access a database that was dropped. This could only + * happen if we read the pg_database flat file right before it was + * modified, after the database was dropped from the pg_database + * table. + */ + if (tmp->entry->destroy != 0) + continue; + + if (!db || + tmp->entry->last_autovac_time < db->entry->last_autovac_time) + db = tmp; + } + + if (db) + { + /* do some work on the database */ + InitPostgres(db->name, NULL); + SetProcessingMode(NormalProcessing); + pgstat_report_autovac(time(NULL)); + + ereport(LOG, + (errmsg("autovacuum (%u) processing database %s", MyProcPid, db->name))); + set_ps_display(db->name); + do_autovacuum(db->entry); + } + + /* One iteration done, go away */ + proc_exit(0); + } + + /* + * autovac_get_database_list + * + * Return a list of all databases with datautovacuum set. Note we + * cannot use pg_database, because we aren't connected yet. + */ + static List * + autovac_get_database_list(void) + { + char *filename; + List *dblist = NIL; + char thisname[NAMEDATALEN]; + FILE *db_file; + Oid db_id; + Oid db_tablespace; + + filename = database_getflatfilename(); + db_file = AllocateFile(filename, "r"); + if (db_file == NULL) + ereport(FATAL, + (errcode_for_file_access(), + errmsg("could not open file \"%s\": %m", filename))); + + while (read_pg_database_line(db_file, thisname, &db_id, &db_tablespace)) + { + autovac_dbase *db; + + db = (autovac_dbase *) palloc(sizeof(autovac_dbase)); + + db->oid = db_id; + db->name = pstrdup(thisname); + /* this gets set later */ + db->entry = NULL; + + dblist = lappend(dblist, db); + } + + FreeFile(db_file); + pfree(filename); + + return dblist; + } + + /* + * Process a database. + * + * Note that test_rel_for_autovac generates three separate lists, one for + * each AutovacOp. This is to facilitate processing all UpdateCounts entries + * on our very first transaction, then all analyzes, and all vacuums last. + * + * Note that check_shutdown_flag is supposed to be used in certain spots in + * order not to lose too much work. Note also that we update a table's + * pg_autovacuum entry only after we have actually processed it, so that + * we won't report work as done if we are told to shut down in the middle of + * processing. + */ + static void + do_autovacuum(PgStat_StatDBEntry *dbentry) + { + Relation avRel; + HeapTuple tuple; + HeapScanDesc scan; + List *vacuum_tables = NIL, + *analyze_tables = NIL, + *update_tables = NIL; + ListCell *cell; + MemoryContext AutovacMemCxt; + + + /* Start a transaction so our commands have one to play into. */ + StartTransactionCommand(); + + /* + * If this database is old enough to need a whole-database VACUUM, + * don't bother checking each table. If that happens, this function + * will issue the VACUUM command and won't return. + */ + autovac_check_wraparound(); + + /* Memory context where cross-transaction state is stored */ + AutovacMemCxt = AllocSetContextCreate(TopMemoryContext, + "Autovacuum context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + MemoryContextSwitchTo(AutovacMemCxt); + + /* + * StartTransactionCommand and CommitTransactionCommand will + * automatically switch to other contexts. We need this one + * to keep the list of relations to vacuum/analyze. + */ + + check_shutdown_flag(); + + avRel = heap_open(AutovacuumRelationId, AccessShareLock); + scan = heap_beginscan(avRel, SnapshotNow, 0, NULL); + + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + test_rel_for_autovac(dbentry, tuple, &vacuum_tables, + &analyze_tables, &update_tables); + } + + heap_endscan(scan); + heap_close(avRel, AccessShareLock); + + /* + * Perform operations on collected tables. We do all updates of + * pg_autovacuum in the same transaction we are in; then we close it + * and start a new one for each VACUUM or ANALYZE. + */ + if (update_tables != NIL) + avRel = heap_open(AutovacuumRelationId, RowExclusiveLock); + + foreach (cell, update_tables) + { + autovac_table *tab = lfirst(cell); + + update_autovac_tuple(tab->oid, tab->update_vactuples, + tab->update_anltuples, avRel); + } + + if (update_tables != NIL) + heap_close(avRel, RowExclusiveLock); + + CommitTransactionCommand(); + check_shutdown_flag(); + + foreach (cell, analyze_tables) + { + autovac_table *tab = lfirst(cell); + + autovacuum_do_vac_analyze(tab); + } + foreach (cell, vacuum_tables) + { + autovac_table *tab = lfirst(cell); + + autovacuum_do_vac_analyze(tab); + } + } + + /* + * test_rel_for_autovac + * + * Check whether a table needs to be vacuumed or analyzed. Add it to the + * respective list if so. + * + * A table needs to be vacuumed if the number of tuples updated and deleted + * since the last vacuum exceeds a threshold. This threshold is calculated + * as + * + * threshold = vac_base_thresh + vac_scale_factor * reltuples + * + * For analyze, the same analysis is done, except that it additionally + * considers inserted tuples since last vacuum, and it uses anl_base_thresh + * and anl_scale_factor as inputs to the threshold equation. + * + * A table whose pg_autovacuum.enabled value is false, is automatically + * skipped. Thus autovacuum can be disabled for specific tables. + * + * A table whose {vac,anl}_base_thresh value is -1, takes the base value + * from the autovacuum_default_base GUC variable. Similarly, a + * {vac,anl}_scale_factor value of -1 is substituted with the value of + * autovacuum_default_scale GUC variable. XXX I assume this works + * for floating point values. + */ + static void + test_rel_for_autovac(PgStat_StatDBEntry *dbentry, HeapTuple tuple, + List **vacuum_tables, List **analyze_tables, + List **update_tables) + { + Form_pg_autovacuum avForm = (Form_pg_autovacuum) GETSTRUCT(tuple); + Relation rel; + int vac_base_thresh, + anl_base_thresh; + float4 reltuples, + vactuples, + anltuples, + vac_scale_factor, + anl_scale_factor, + vacthresh, + anlthresh, + vac_last_cnt, + anl_last_cnt; + autovac_table *tab; + + if (!avForm->enabled) + return; + + /* Get data for this table from the stats system. */ + PgStat_TableEntry *entry = hash_search(dbentry->tables, + &(avForm->vacrelid), + HASH_FIND, NULL); + if (entry == NULL) + { + /* + * We just skip a table not found on the stat hash. This is + * because whatever we do here, there won't be good statistics + * next time around, so we would do this same action again. It is + * tempting to issue an ANALYZE, but it won't work because ANALYZE + * doesn't update the stat system. + */ + return; + } + + /* + * If we can't find the table in the relcache, it must have been dropped. + * (It may also mean a corrupt cache but we won't deal with that here.) + */ + rel = RelationIdGetRelation(avForm->vacrelid); + if (rel == NULL) + return; + + reltuples = rel->rd_rel->reltuples; + RelationClose(rel); + + vactuples = entry->t_tuples_updated + entry->t_tuples_deleted; + anltuples = entry->t_tuples_inserted + entry->t_tuples_updated + + entry->t_tuples_deleted; + + vac_scale_factor = (avForm->vac_scale_factor == -1) ? + autovacuum_vac_scale : avForm->vac_scale_factor; + vac_base_thresh = (avForm->vac_base_thresh == -1) ? + autovacuum_vac_thresh : avForm->vac_base_thresh; + + anl_scale_factor = (avForm->anl_scale_factor == -1) ? + autovacuum_anl_scale : avForm->anl_scale_factor; + anl_base_thresh = (avForm->anl_base_thresh == -1) ? + autovacuum_anl_thresh : avForm->anl_base_thresh; + + vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples; + anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples; + + tab = (autovac_table *) palloc0(sizeof(autovac_table)); + + tab->update_vactuples = false; + tab->update_anltuples = false; + tab->operation = AVOperNone; + tab->oid = avForm->vacrelid; + + /* + * Consider stat-reset: if the differences are negative, reset the + * last-seen counters. + * + * It's OK if tab->operation is rewritten later to VacuumAnalyze or + * Analyze, because these operations take care of updating + * pg_autovacuum as needed. + * + * We reset the counters to 0, not their current value, because that's + * what stat-reset did reset them to. So we will operate on them + * closer to the reality (this is like saying they were last vacuumed + * at the same time the stat-reset happened.) Also use these numbers + * to determine whether to vacuum on this iteration. + */ + if (vactuples - avForm->vac_last_cnt < 0) + { + tab->operation = AVOperUpdateCounts; + tab->reltuples = 0; + tab->update_vactuples = true; + vac_last_cnt = 0; + } + else + vac_last_cnt = avForm->vac_last_cnt; + + if (anltuples - avForm->anl_last_cnt < 0) + { + tab->operation = AVOperUpdateCounts; + tab->reltuples = 0; + tab->update_anltuples = true; + anl_last_cnt = 0; + } + else + anl_last_cnt = avForm->anl_last_cnt; + + elog(DEBUG2, "%s: vactuples diff %f (threshold %f), " + "anltuples diff %f (threshold %f)", + get_rel_name(tab->oid), + vactuples - vac_last_cnt, vacthresh, + anltuples - anl_last_cnt, anlthresh); + + /* Determine whether this table needs vacuum or analyze. */ + if (vactuples - vac_last_cnt > vacthresh) + { + elog(DEBUG2, "will VACUUM ANALYZE this table"); + + tab->operation = AVOperVacuumAnalyze; + } + else if (anltuples - anl_last_cnt > anlthresh) + { + elog(DEBUG2, "will ANALYZE this table"); + tab->operation = AVOperAnalyze; + } + + switch (tab->operation) + { + case AVOperNone: + pfree(tab); + break; + case AVOperAnalyze: + *analyze_tables = lappend(*analyze_tables, tab); + break; + case AVOperVacuumAnalyze: + *vacuum_tables = lappend(*vacuum_tables, tab); + break; + case AVOperUpdateCounts: + *update_tables = lappend(*update_tables, tab); + break; + } + } + + /* + * autovacuum_do_vac_analyze + * Vacuum or analyze one table. + * + * We must not be in a transaction already, and will not be when this + * routine finishes. + * + * It'd be nice to be able to use vacuum's multitransaction machinery, to do + * all collected tables at once. We'd need a new entrypoint there. + * It'd be also nice to be able to vacuum a table by Oid rather than RangeVar. + */ + static void + autovacuum_do_vac_analyze(autovac_table *tab) + { + Oid relid = tab->oid; + VacuumStmt vacstmt; + RangeVar *rv; + + Assert(tab->operation == AVOperVacuumAnalyze || + tab->operation == AVOperAnalyze); + + StartTransactionCommand(); + /* + * This node gets created in TopTransactionContext, so it will + * go away with the transaction. + */ + rv = makeNode(RangeVar); + + /* + * What a pain to have to do all this, when we could just pass + * the relid. + */ + rv->catalogname = NULL; + rv->schemaname = get_namespace_name(get_rel_namespace(relid)); + rv->relname = get_rel_name(relid); + rv->inhOpt = INH_NO; + rv->istemp = false; + rv->alias = NULL; + + /* Check the table still exists. */ + if (rv->schemaname != NULL && rv->relname != NULL) + { + /* + * Ugly. + */ + vacstmt.vacuum = (tab->operation == AVOperVacuumAnalyze); + vacstmt.full = false; + vacstmt.analyze = true; + vacstmt.freeze = false; + vacstmt.verbose = true; + vacstmt.relation = rv; + vacstmt.va_cols = NIL; + + elog(LOG, "autovacuum: %s \"%s\".\"%s\"", + (tab->operation == AVOperVacuumAnalyze) ? + "VACUUM ANALYZE" : "ANALYZE", + get_namespace_name(get_rel_namespace(relid)), + get_rel_name(relid)); + + vacuum(&vacstmt); + } + + /* Release locks */ + CommitTransactionCommand(); + check_shutdown_flag(); + } + + /* + * update_autovac_tuple + * Update a relation's vac_tuple_cnt and/or anl_tuple_cnt attributes + * + * Note that this is called from autovacuum, and also from a regular + * backend executing a manual VACUUM or ANALYZE command. + * + * The dbentry parameter may be NULL, in which case it is fetched from + * the stat system. If it doesn't know about this database, we skip + * the update. + * + * Also, if the stat system doesn't have an entry about the involved + * table, we don't update pg_autovacuum. + * + * avRel may be the already-open and locked pg_autovacuum relation, or NULL. + * + * The boolean parameters indicate whether to update the latest count + * for each type of operation. The number actually stored is taken from + * the stat system. (Note this number is quite different from the + * relation's tuple count.) + */ + void + update_autovac_tuple(Oid relid, bool vacuum, bool analyze, Relation avRel) + { + HeapTuple tuple; + bool opened_rel = false; + PgStat_StatDBEntry *dbentry; + PgStat_TableEntry *tabentry; + Form_pg_autovacuum vacForm; + + if (avRel == NULL) + { + avRel = heap_open(AutovacuumRelationId, RowExclusiveLock); + opened_rel = true; + } + + /* Get a copy so we can modify it. */ + tuple = SearchSysCacheCopy(AUTOVACRELID, + ObjectIdGetDatum(relid), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "autovacuum cache lookup for relation %u failed", relid); + + dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId); + if (dbentry == NULL) + { + elog(NOTICE, "skipping update: dbentry is NULL"); + goto cleanup; + } + + /* Get data for this table from the stats system. */ + tabentry = hash_search(dbentry->tables, &relid, HASH_FIND, NULL); + + if (tabentry == NULL) + { + elog(NOTICE, "skipping update: tabentry is NULL"); + goto cleanup; + } + + vacForm = (Form_pg_autovacuum) GETSTRUCT(tuple); + + if (vacuum) + vacForm->vac_last_cnt = tabentry->t_tuples_updated + + tabentry->t_tuples_deleted; + + if (analyze) + vacForm->anl_last_cnt = tabentry->t_tuples_inserted + + tabentry->t_tuples_updated + tabentry->t_tuples_deleted; + + simple_heap_update(avRel, &tuple->t_self, tuple); + + /* + * Keep indexes current. Could get the result of CatalogOpenIndexes + * from the caller, if it's going to call us a lot of times. + */ + CatalogUpdateIndexes(avRel, tuple); + + heap_freetuple(tuple); + + cleanup: + if (opened_rel) + heap_close(avRel, RowExclusiveLock); + } + + /* + * autovac_check_wraparound + * Check database Xid wraparound + * + * Check pg_database to see if the last database-wide VACUUM was too long ago, + * and issue one now if so. If this comes to pass, we do not return, as there + * is no point in checking individual tables -- they will all get vacuumed + * anyway. + */ + static void + autovac_check_wraparound(void) + { + Relation relation; + ScanKeyData entry[1]; + HeapScanDesc scan; + HeapTuple tuple; + Form_pg_database dbform; + bool whole_db; + NameData datname; + + relation = heap_open(DatabaseRelationId, AccessShareLock); + + /* Must use a heap scan, since there's no syscache for pg_database */ + ScanKeyInit(&entry[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(MyDatabaseId)); + + scan = heap_beginscan(relation, SnapshotNow, 1, entry); + + tuple = heap_getnext(scan, ForwardScanDirection); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for database %u", MyDatabaseId); + + dbform = (Form_pg_database) GETSTRUCT(tuple); + + whole_db = (GetTopTransactionId() - dbform->datfrozenxid) > 1500000000; + namecpy(&datname, &dbform->datname); + + heap_endscan(scan); + + heap_close(relation, AccessShareLock); + + if (whole_db) + { + VacuumStmt vacstmt; + + /* + * Ugly. + */ + vacstmt.vacuum = true; + vacstmt.full = false; + vacstmt.analyze = true; + vacstmt.freeze = false; + vacstmt.verbose = true; + vacstmt.relation = NULL; + vacstmt.va_cols = NIL; + + elog(LOG, "autovacuum: VACUUM ANALYZE"); + + vacuum(&vacstmt); + + elog(LOG, "autovacuum: database %s finished", NameStr(datname)); + proc_exit(0); + } + } + + /* + * AutoVacuumingActive + * Check GUC vars and report whether the autovacuum process should be + * running. + */ + bool + AutoVacuumingActive(void) + { + if (!autovacuum_start_daemon || !pgstat_collect_startcollector || + !pgstat_collect_tuplelevel) + return false; + return true; + } + + /* + * autovac_init + * This is called at postmaster initialization. + * + * Annoy the user if he got it wrong. + */ + void + autovac_init(void) + { + if (!autovacuum_start_daemon) + return; + + if (!pgstat_collect_startcollector || !pgstat_collect_tuplelevel) + { + ereport(WARNING, + (errmsg("autovacuum not started because of misconfiguration"), + errhint("Enable options stats_start_collector and stats_row_level."))); + /* + * Set the GUC var so we don't fork autovacuum uselessly, and also to + * help debugging. + */ + autovacuum_start_daemon = false; + } + } + + /* + * IsAutoVacuumProcess + * Return whether this process is an autovacuum process. + */ + bool + IsAutoVacuumProcess(void) + { + return am_autovacuum; + } + + /* + * check_shutdown_flag + * Check, and act upon, the shutdown flag set by the SIGUSR2 handler. + */ + static void + check_shutdown_flag(void) + { + if (shutdown_requested) + proc_exit(0); + } + + /* + * SIGQUIT: + * + * Some backend has bought the farm, so we need to stop what we're doing + * and exit. + */ + static void + autovac_sigquit_handler(SIGNAL_ARGS) + { + PG_SETMASK(&BlockSig); + + elog(LOG, "autovacuum: quick die requested"); + /* + * DO NOT proc_exit() -- see equivalent comment in bg_quickdie(). + */ + exit(1); + } + + /* + * SIGHUP: set flag to reread config file. + */ + static void + autovac_sighup_handler(SIGNAL_ARGS) + { + got_SIGHUP = true; + } + + /* + * SIGUSR2: set flag to shut down. + */ + static void + autovac_sigusr2_handler(SIGNAL_ARGS) + { + elog(LOG, "autovacuum: shutdown requested"); + + shutdown_requested = true; + } Index: src/backend/postmaster/pgstat.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/pgstat.c,v retrieving revision 1.99 diff -c -r1.99 pgstat.c *** src/backend/postmaster/pgstat.c 4 Jul 2005 04:51:47 -0000 1.99 --- src/backend/postmaster/pgstat.c 4 Jul 2005 16:22:04 -0000 *************** *** 38,43 **** --- 38,44 ---- #include "libpq/pqsignal.h" #include "mb/pg_wchar.h" #include "miscadmin.h" + #include "postmaster/autovacuum.h" #include "postmaster/fork_process.h" #include "postmaster/postmaster.h" #include "storage/backendid.h" *************** *** 172,177 **** --- 173,179 ---- static void pgstat_recv_bestart(PgStat_MsgBestart *msg, int len); static void pgstat_recv_beterm(PgStat_MsgBeterm *msg, int len); + static void pgstat_recv_autovac(PgStat_MsgAutovac *msg, int len); static void pgstat_recv_activity(PgStat_MsgActivity *msg, int len); static void pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len); static void pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len); *************** *** 618,623 **** --- 620,648 ---- } + /* ---------- + * pgstat_report_autovac() - + * + * Called from autovacuum.c to inform that a database was processed + * by the autovacuum daemon. + * ---------- + */ + void + pgstat_report_autovac(time_t m_time) + { + PgStat_MsgAutovac msg; + + if (pgStatSock < 0 || !AutoVacuumingActive()) + return; + + MemSet(&(msg.m_hdr), 0, sizeof(msg.m_hdr)); + msg.m_hdr.m_type = PGSTAT_MTYPE_AUTOVAC; + msg.m_databaseid = MyDatabaseId; + msg.m_time = m_time; + + pgstat_send(&msg, sizeof(msg)); + } + /* ------------------------------------------------------------ * Public functions used by backends follow *------------------------------------------------------------ *************** *** 1632,1637 **** --- 1657,1666 ---- pgstat_recv_beterm((PgStat_MsgBeterm *) &msg, nread); break; + case PGSTAT_MTYPE_AUTOVAC: + pgstat_recv_autovac((PgStat_MsgAutovac *) &msg, nread); + break; + case PGSTAT_MTYPE_TABSTAT: pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, nread); break; *************** *** 2049,2054 **** --- 2078,2084 ---- result->n_blocks_fetched = 0; result->n_blocks_hit = 0; result->destroy = 0; + result->last_autovac_time = 0; memset(&hash_ctl, 0, sizeof(hash_ctl)); hash_ctl.keysize = sizeof(Oid); *************** *** 2319,2330 **** int mcxt_flags; /* ! * If running in the collector we use the DynaHashCxt memory context. ! * If running in a backend, we use the TopTransactionContext instead, ! * so the caller must only know the last XactId when this call ! * happened to know if his tables are still valid or already gone! */ ! if (pgStatRunningInCollector) { use_mcxt = NULL; mcxt_flags = 0; --- 2349,2361 ---- int mcxt_flags; /* ! * If running in the collector or the autovacuum process, we use the ! * DynaHashCxt memory context. If running in a backend, we use the ! * TopTransactionContext instead, so the caller must only know the last ! * XactId when this call happened to know if his tables are still valid or ! * already gone! */ ! if (pgStatRunningInCollector || IsAutoVacuumProcess()) { use_mcxt = NULL; mcxt_flags = 0; *************** *** 2552,2569 **** * * Because we store the hash tables in TopTransactionContext, the result * is good for the entire current main transaction. */ static void backend_read_statsfile(void) { ! TransactionId topXid = GetTopTransactionId(); ! ! if (!TransactionIdEquals(pgStatDBHashXact, topXid)) { Assert(!pgStatRunningInCollector); ! pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, &pgStatBeTable, &pgStatNumBackends); ! pgStatDBHashXact = topXid; } } --- 2583,2618 ---- * * Because we store the hash tables in TopTransactionContext, the result * is good for the entire current main transaction. + * + * XXX inside the autovacuum process, the statfile is assumed to be valid + * "forever" (which is one iteration, within one database, basically. + * So this means we only consider the statistics as they were when the + * autovacuum iteration started.) */ static void backend_read_statsfile(void) { ! if (IsAutoVacuumProcess()) { Assert(!pgStatRunningInCollector); ! /* already read it? */ ! if (pgStatDBHash) ! return; ! pgstat_read_statsfile(&pgStatDBHash, InvalidOid, &pgStatBeTable, &pgStatNumBackends); ! } ! else ! { ! ! TransactionId topXid = GetTopTransactionId(); ! ! if (!TransactionIdEquals(pgStatDBHashXact, topXid)) ! { ! Assert(!pgStatRunningInCollector); ! pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, ! &pgStatBeTable, &pgStatNumBackends); ! pgStatDBHashXact = topXid; ! } } } *************** *** 2606,2611 **** --- 2655,2686 ---- pgstat_sub_backend(msg->m_hdr.m_procpid); } + /* ---------- + * pgstat_recv_autovac() - + * + * Process an autovacuum signalling message. + */ + static void + pgstat_recv_autovac(PgStat_MsgAutovac *msg, int len) + { + PgStat_StatDBEntry *dbentry; + + /* + * Lookup the database in the hashtable. + * + * XXX this creates the entry if it doesn't exist. Is this a problem? (We + * could leak an entry if we send an autovac message and the database is + * later destroyed, _and_ the messages are rearranged. Doesn't seem very + * likely though.) Not sure what to do about it. + */ + dbentry = pgstat_get_db_entry(msg->m_databaseid); + + /* + * Store the last autovacuum time in the database entry. + */ + dbentry->last_autovac_time = msg->m_time; + } + /* ---------- * pgstat_recv_activity() - Index: src/backend/postmaster/postmaster.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/postmaster.c,v retrieving revision 1.458 diff -c -r1.458 postmaster.c *** src/backend/postmaster/postmaster.c 4 Jul 2005 04:51:47 -0000 1.458 --- src/backend/postmaster/postmaster.c 6 Jul 2005 23:25:09 -0000 *************** *** 106,111 **** --- 106,112 ---- #include "miscadmin.h" #include "nodes/nodes.h" #include "pgstat.h" + #include "postmaster/autovacuum.h" #include "postmaster/fork_process.h" #include "postmaster/pgarch.h" #include "postmaster/postmaster.h" *************** *** 206,211 **** --- 207,213 ---- /* PIDs of special child processes; 0 when not running */ static pid_t StartupPID = 0, + AutoVacPID = 0, BgWriterPID = 0, PgArchPID = 0, PgStatPID = 0, *************** *** 873,879 **** * CAUTION: when changing this list, check for side-effects on the signal * handling setup of child processes. See tcop/postgres.c, * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c, ! * postmaster/pgstat.c, and postmaster/syslogger.c. */ pqinitmask(); PG_SETMASK(&BlockSig); --- 875,881 ---- * CAUTION: when changing this list, check for side-effects on the signal * handling setup of child processes. See tcop/postgres.c, * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c, ! * postmaster/pgstat.c, postmaster/autovacuum.c and postmaster/syslogger.c. */ pqinitmask(); PG_SETMASK(&BlockSig); *************** *** 930,935 **** --- 932,942 ---- */ PgStartTime = GetCurrentTimestamp(); + /* + * Initialize the autovacuum daemon + */ + autovac_init(); + status = ServerLoop(); /* *************** *** 1262,1267 **** --- 1269,1284 ---- PgStatPID = pgstat_start(); /* + * Start a new autovacuum process, if there isn't one running already. + * (It'll die relatively quickly.) We check that it's not started + * too frequently in autovac_start. + */ + if (AutoVacPID == 0 && + StartupPID == 0 && !FatalError && Shutdown == NoShutdown && + AutoVacuumingActive()) + AutoVacPID = autovac_start(); + + /* * Touch the socket and lock file every 58 minutes, to * ensure that they are not removed by overzealous /tmp-cleaning * tasks. We assume no one runs cleaners with cutoff times of *************** *** 1822,1827 **** --- 1839,1846 ---- kill(PgArchPID, SIGHUP); if (SysLoggerPID != 0) kill(SysLoggerPID, SIGHUP); + if (AutoVacPID != 0) + kill(AutoVacPID, SIGHUP); /* PgStatPID does not currently need SIGHUP */ /* Reload authentication config files too */ *************** *** 1877,1882 **** --- 1896,1904 ---- */ if (StartupPID != 0 || FatalError) break; /* let reaper() handle this */ + /* Tell autovacuum to shut down */ + if (AutoVacPID != 0) + kill(AutoVacPID, SIGUSR2); /* Start the bgwriter if not running */ if (BgWriterPID == 0) BgWriterPID = StartBackgroundWriter(); *************** *** 1905,1910 **** --- 1927,1936 ---- ereport(LOG, (errmsg("received fast shutdown request"))); + /* Shut down autovacuum */ + if (AutoVacPID != 0) + kill(AutoVacPID, SIGQUIT); + if (DLGetHead(BackendList)) { if (!FatalError) *************** *** 1949,1954 **** --- 1975,1982 ---- */ ereport(LOG, (errmsg("received immediate shutdown request"))); + if (AutoVacPID != 0) + kill(AutoVacPID, SIGQUIT); if (StartupPID != 0) kill(StartupPID, SIGQUIT); if (BgWriterPID != 0) *************** *** 2144,2149 **** --- 2172,2191 ---- } /* + * Was it the autovacuum process? Ignore it -- it will be restarted + * at the next iteration of the postmaster's main loop, if necessary. + */ + if (AutoVacPID != 0 && pid == AutoVacPID) + { + AutoVacPID = 0; + if (exitstatus != 0) + LogChildExit(LOG, _("autovacuum process"), + pid, exitstatus); + elog(NOTICE, "autovacuum process ended"); + continue; + } + + /* * Else do standard backend child cleanup. */ CleanupBackend(pid, exitstatus); *************** *** 2156,2162 **** * StartupDataBase. (We can ignore the archiver and stats * processes here since they are not connected to shmem.) */ ! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0) goto reaper_done; ereport(LOG, (errmsg("all server processes terminated; reinitializing"))); --- 2198,2205 ---- * StartupDataBase. (We can ignore the archiver and stats * processes here since they are not connected to shmem.) */ ! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0 || ! AutoVacPID != 0) goto reaper_done; ereport(LOG, (errmsg("all server processes terminated; reinitializing"))); *************** *** 2173,2178 **** --- 2216,2224 ---- { if (DLGetHead(BackendList) || StartupPID != 0) goto reaper_done; + /* Tell autovacuum to shut down */ + if (AutoVacPID != 0) + kill(AutoVacPID, SIGUSR2); /* Start the bgwriter if not running */ if (BgWriterPID == 0) BgWriterPID = StartBackgroundWriter(); *************** *** 2339,2344 **** --- 2385,2391 ---- kill(PgStatPID, SIGQUIT); } + /* handle autovacuum crash */ /* We do NOT restart the syslogger */ FatalError = true; *************** *** 3154,3159 **** --- 3201,3207 ---- * can attach at the same address the postmaster used. */ if (strcmp(argv[1], "-forkbackend") == 0 || + strcmp(argv[1], "-forkautovac") == 0 || strcmp(argv[1], "-forkboot") == 0) PGSharedMemoryReAttach(); *************** *** 3205,3210 **** --- 3253,3269 ---- BootstrapMain(argc - 2, argv + 2); proc_exit(0); } + if (strcmp(argv[1], "-forkautovac") == 0) + { + /* Close the postmaster's sockets */ + ClosePostmasterPorts(false); + + /* Attached process to shared data structures */ + CreateSharedMemoryAndSemaphores(false, 0); + + AutoVacMain(argc - 2, argv + 2); + proc_exit(0); + } if (strcmp(argv[1], "-forkarch") == 0) { /* Close the postmaster's sockets */ Index: src/backend/utils/cache/syscache.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/cache/syscache.c,v retrieving revision 1.100 diff -c -r1.100 syscache.c *** src/backend/utils/cache/syscache.c 28 Jun 2005 05:09:01 -0000 1.100 --- src/backend/utils/cache/syscache.c 29 Jun 2005 20:17:04 -0000 *************** *** 29,34 **** --- 29,35 ---- #include "catalog/pg_amproc.h" #include "catalog/pg_authid.h" #include "catalog/pg_auth_members.h" + #include "catalog/pg_autovacuum.h" #include "catalog/pg_cast.h" #include "catalog/pg_conversion.h" #include "catalog/pg_index.h" *************** *** 212,217 **** --- 213,228 ---- 0, 0 }}, + {AutovacuumRelationId, /* AUTOVACRELID */ + AutovacuumRelidIndexId, + Anum_pg_autovacuum_vacrelid, + 1, + { + Anum_pg_autovacuum_vacrelid, + 0, + 0, + 0 + }}, { CastRelationId, /* CASTSOURCETARGET */ CastSourceTargetIndexId, Index: src/backend/utils/init/miscinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/miscinit.c,v retrieving revision 1.145 diff -c -r1.145 miscinit.c *** src/backend/utils/init/miscinit.c 4 Jul 2005 04:51:50 -0000 1.145 --- src/backend/utils/init/miscinit.c 4 Jul 2005 16:22:06 -0000 *************** *** 32,37 **** --- 32,38 ---- #include "catalog/pg_authid.h" #include "libpq/libpq-be.h" #include "miscadmin.h" + #include "postmaster/autovacuum.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" *************** *** 394,400 **** InitializeSessionUserIdStandalone(void) { /* This function should only be called in a single-user backend. */ ! AssertState(!IsUnderPostmaster); /* call only once */ AssertState(!OidIsValid(AuthenticatedUserId)); --- 395,401 ---- InitializeSessionUserIdStandalone(void) { /* This function should only be called in a single-user backend. */ ! AssertState(!IsUnderPostmaster || IsAutoVacuumProcess()); /* call only once */ AssertState(!OidIsValid(AuthenticatedUserId)); Index: src/backend/utils/init/postinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/postinit.c,v retrieving revision 1.152 diff -c -r1.152 postinit.c *** src/backend/utils/init/postinit.c 4 Jul 2005 04:51:50 -0000 1.152 --- src/backend/utils/init/postinit.c 4 Jul 2005 16:22:06 -0000 *************** *** 29,34 **** --- 29,35 ---- #include "libpq/hba.h" #include "mb/pg_wchar.h" #include "miscadmin.h" + #include "postmaster/autovacuum.h" #include "postmaster/postmaster.h" #include "storage/backendid.h" #include "storage/fd.h" *************** *** 268,274 **** * InitPostgres * Initialize POSTGRES. * ! * In bootstrap mode neither of the parameters are used. * * The return value indicates whether the userID is a superuser. (That * can only be tested inside a transaction, so we want to do it during --- 269,276 ---- * InitPostgres * Initialize POSTGRES. * ! * In bootstrap mode neither of the parameters are used. In autovacuum ! * mode, the username parameter is not used. * * The return value indicates whether the userID is a superuser. (That * can only be tested inside a transaction, so we want to do it during *************** *** 282,287 **** --- 284,290 ---- InitPostgres(const char *dbname, const char *username) { bool bootstrap = IsBootstrapProcessingMode(); + bool autovacuum = IsAutoVacuumProcess(); bool am_superuser; /* *************** *** 402,411 **** RelationCacheInitializePhase2(); /* ! * Figure out our postgres user id. In standalone mode we use a fixed ! * id, otherwise we figure it out from the authenticated user name. */ ! if (bootstrap) InitializeSessionUserIdStandalone(); else if (!IsUnderPostmaster) { --- 405,415 ---- RelationCacheInitializePhase2(); /* ! * Figure out our postgres user id. In standalone mode and in the ! * autovacuum process, we use a fixed id, otherwise we figure it out from ! * the authenticated user name. */ ! if (bootstrap || autovacuum) InitializeSessionUserIdStandalone(); else if (!IsUnderPostmaster) { *************** *** 441,447 **** /* * Check if user is a superuser. */ ! if (bootstrap) am_superuser = true; else am_superuser = superuser(); --- 445,451 ---- /* * Check if user is a superuser. */ ! if (bootstrap || autovacuum) am_superuser = true; else am_superuser = superuser(); Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.272 diff -c -r1.272 guc.c *** src/backend/utils/misc/guc.c 4 Jul 2005 04:51:51 -0000 1.272 --- src/backend/utils/misc/guc.c 4 Jul 2005 16:22:08 -0000 *************** *** 45,50 **** --- 45,51 ---- #include "optimizer/prep.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" + #include "postmaster/autovacuum.h" #include "postmaster/bgwriter.h" #include "postmaster/syslogger.h" #include "postmaster/postmaster.h" *************** *** 664,669 **** --- 665,678 ---- &pgstat_collect_blocklevel, false, NULL, NULL }, + { + {"autovacuum", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Starts the auto vacuum subprocess."), + NULL + }, + &autovacuum_start_daemon, + false, NULL, NULL + }, { {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS, *************** *** 1375,1380 **** --- 1384,1413 ---- &block_size, BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL }, + { + {"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Minimum number of tuple updates or deletes prior to vacuum."), + NULL + }, + &autovacuum_vac_thresh, + 1000, 0, INT_MAX, NULL, NULL + }, + { + {"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Minimum number of tuple inserts, updates or deletes prior to analyze."), + NULL + }, + &autovacuum_anl_thresh, + 500, 0, INT_MAX, NULL, NULL + }, + { + {"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Time to sleep between autovacuum runs, in seconds."), + NULL + }, + &autovacuum_naptime, + 60, 0, INT_MAX, NULL, NULL + }, /* End-of-list marker */ { *************** *** 1473,1478 **** --- 1506,1529 ---- &phony_random_seed, 0.5, 0.0, 1.0, assign_random_seed, show_random_seed }, + { + {"autovacuum_vacuum_scale_factor", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Number of tuple updates or deletes prior to vacuum as a factor of reltuples."), + NULL + }, + &autovacuum_vac_scale, + /* FIXME -- figure good values for this */ + 0.4, 0.0, 100.0, NULL, NULL + }, + { + {"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Number of tuple inserts, updates or deletes prior to analyze as a factor of reltuples."), + NULL + }, + &autovacuum_anl_scale, + /* FIXME -- figure good values for this */ + 0.2, 0.0, 100.0, NULL, NULL + }, /* End-of-list marker */ { Index: src/backend/utils/misc/postgresql.conf.sample =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/misc/postgresql.conf.sample,v retrieving revision 1.151 diff -c -r1.151 postgresql.conf.sample *** src/backend/utils/misc/postgresql.conf.sample 2 Jul 2005 18:46:45 -0000 1.151 --- src/backend/utils/misc/postgresql.conf.sample 4 Jul 2005 16:22:08 -0000 *************** *** 282,287 **** --- 282,299 ---- #stats_row_level = off #stats_reset_on_server_start = on + #--------------------------------------------------------------------------- + # AUTOVACUUM PARAMETERS + #--------------------------------------------------------------------------- + + # FIXME - figure out good defaults for these + + #autovacuum = false + #autovacuum_naptime = 60 + #autovacuum_vacuum_threshold = 1000 + #autovacuum_analyze_threshold = 500 + #autovacuum_vacuum_scale_factor = 0.4 + #autovacuum_analyze_scale_factor = 0.2 #--------------------------------------------------------------------------- # CLIENT CONNECTION DEFAULTS Index: src/bin/initdb/initdb.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/initdb/initdb.c,v retrieving revision 1.90 diff -c -r1.90 initdb.c *** src/bin/initdb/initdb.c 2 Jul 2005 17:01:50 -0000 1.90 --- src/bin/initdb/initdb.c 4 Jul 2005 16:22:11 -0000 *************** *** 173,178 **** --- 173,179 ---- static void get_set_pwd(void); static void unlimit_systables(void); static void setup_depend(void); + static void setup_autovacuum(void); static void setup_sysviews(void); static void setup_description(void); static void setup_conversion(void); *************** *** 1563,1568 **** --- 1564,1603 ---- } /* + * set up pg_autovacuum + */ + static void + setup_autovacuum(void) + { + PG_CMD_DECL; + char **line; + static char *pg_autovacuum_setup[] = + { + "DELETE FROM pg_autovacuum;\n", + "INSERT INTO pg_autovacuum SELECT oid, 't', -1, -1, 0, -1, -1, 0" + " FROM pg_class WHERE relkind = 'r';\n", + NULL + }; + + fputs(_("initializing pg_autovacuum ... "), stdout); + fflush(stdout); + + snprintf(cmd, sizeof(cmd), + "\"%s\" %s template1 >%s", + backend_exec, backend_options, + DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_autovacuum_setup; *line != NULL; line++) + PG_CMD_PUTS(*line); + + PG_CMD_CLOSE; + + check_ok(); + } + + /* * set up system views */ static void *************** *** 2629,2634 **** --- 2664,2671 ---- setup_depend(); + setup_autovacuum(); + setup_sysviews(); setup_description(); Index: src/include/pgstat.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/pgstat.h,v retrieving revision 1.32 diff -c -r1.32 pgstat.h *** src/include/pgstat.h 29 Jun 2005 22:51:57 -0000 1.32 --- src/include/pgstat.h 30 Jun 2005 03:13:48 -0000 *************** *** 23,33 **** #define PGSTAT_MTYPE_DUMMY 0 #define PGSTAT_MTYPE_BESTART 1 #define PGSTAT_MTYPE_BETERM 2 ! #define PGSTAT_MTYPE_ACTIVITY 3 ! #define PGSTAT_MTYPE_TABSTAT 4 ! #define PGSTAT_MTYPE_TABPURGE 5 ! #define PGSTAT_MTYPE_DROPDB 6 ! #define PGSTAT_MTYPE_RESETCOUNTER 7 /* ---------- * The data type used for counters. --- 23,34 ---- #define PGSTAT_MTYPE_DUMMY 0 #define PGSTAT_MTYPE_BESTART 1 #define PGSTAT_MTYPE_BETERM 2 ! #define PGSTAT_MTYPE_AUTOVAC 3 ! #define PGSTAT_MTYPE_ACTIVITY 4 ! #define PGSTAT_MTYPE_TABSTAT 5 ! #define PGSTAT_MTYPE_TABPURGE 6 ! #define PGSTAT_MTYPE_DROPDB 7 ! #define PGSTAT_MTYPE_RESETCOUNTER 8 /* ---------- * The data type used for counters. *************** *** 115,120 **** --- 116,133 ---- } PgStat_MsgBeterm; /* ---------- + * PgStat_MsgAutovacuum Sent by the autovacuum daemon to signal + * that a database was processed + * ---------- + */ + typedef struct PgStat_MsgAutovac + { + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + time_t m_time; + } PgStat_MsgAutovac; + + /* ---------- * PgStat_MsgActivity Sent by the backends when they start * to parse a query. * ---------- *************** *** 200,205 **** --- 213,219 ---- PgStat_MsgTabpurge msg_tabpurge; PgStat_MsgDropdb msg_dropdb; PgStat_MsgResetcounter msg_resetcounter; + PgStat_MsgAutovac msg_autovacuum; } PgStat_Msg; *************** *** 222,227 **** --- 236,242 ---- PgStat_Counter n_blocks_fetched; PgStat_Counter n_blocks_hit; int destroy; + time_t last_autovac_time; } PgStat_StatDBEntry; *************** *** 323,328 **** --- 338,344 ---- extern void pgstat_ping(void); extern void pgstat_report_activity(const char *what); extern void pgstat_report_tabstat(void); + extern void pgstat_report_autovac(time_t m_time); extern int pgstat_vacuum_tabstat(void); extern void pgstat_reset_counters(void); Index: src/include/catalog/indexing.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/indexing.h,v retrieving revision 1.88 diff -c -r1.88 indexing.h *** src/include/catalog/indexing.h 28 Jun 2005 05:09:04 -0000 1.88 --- src/include/catalog/indexing.h 29 Jun 2005 20:13:54 -0000 *************** *** 203,208 **** --- 203,210 ---- DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index,2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); #define TypeNameNspIndexId 2704 + DECLARE_UNIQUE_INDEX(pg_autovacuum_vacrelid_index,1250, on pg_autovacuum using btree(vacrelid oid_ops)); + #define AutovacuumRelidIndexId 1250 /* last step of initialization script: build the indexes declared above */ BUILD_INDICES Index: src/include/catalog/pg_autovacuum.h =================================================================== RCS file: src/include/catalog/pg_autovacuum.h diff -N src/include/catalog/pg_autovacuum.h *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/include/catalog/pg_autovacuum.h 28 Jun 2005 15:02:07 -0000 *************** *** 0 **** --- 1,64 ---- + /*------------------------------------------------------------------------- + * + * pg_autovacuum.h + * definition of the system "autovacuum" relation (pg_autovacuum) + * + * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + #ifndef PG_AUTOVACUUM_H + #define PG_AUTOVACUUM_H + + /* ---------------- + * postgres.h contains the system type definitions and the + * CATALOG(), BOOTSTRAP and DATA() sugar words so this file + * can be read by both genbki.sh and the C compiler. + * ---------------- + */ + + /* ---------------- + * pg_autovacuum definition. cpp turns this into + * typedef struct FormData_pg_autovacuum + * ---------------- + */ + #define AutovacuumRelationId 1248 + CATALOG(pg_autovacuum,1248) BKI_WITHOUT_OIDS + { + Oid vacrelid; /* OID of table */ + bool enabled; /* enabled for this table? */ + int4 vac_base_thresh; /* base threshold value */ + float4 vac_scale_factor; /* reltuples scaling factor */ + float4 vac_last_cnt; /* actual value as of last vacuum */ + int4 anl_base_thresh; /* base threshold value */ + float4 anl_scale_factor; /* reltuples scaling factor */ + float4 anl_last_cnt; /* actual value as of last analyze */ + } FormData_pg_autovacuum; + + /* ---------------- + * Form_pg_autovacuum corresponds to a pointer to a tuple with + * the format of pg_autovacuum relation. + * ---------------- + */ + typedef FormData_pg_autovacuum *Form_pg_autovacuum; + + /* ---------------- + * compiler constants for pg_autovacuum + * ---------------- + */ + #define Natts_pg_autovacuum 8 + #define Anum_pg_autovacuum_vacrelid 1 + #define Anum_pg_autovacuum_enabled 2 + #define Anum_pg_autovacuum_vac_base_thresh 3 + #define Anum_pg_autovacuum_vac_scale_factor 4 + #define Anum_pg_autovacuum_vac_last_cnt 5 + #define Anum_pg_autovacuum_anl_base_thresh 6 + #define Anum_pg_autovacuum_anl_scale_factor 7 + #define Anum_pg_autovacuum_anl_last_cnt 8 + + /* There are no preloaded tuples in pg_autovacuum.h */ + + #endif /* PG_AUTOVACUUM_H */ Index: src/include/postmaster/autovacuum.h =================================================================== RCS file: src/include/postmaster/autovacuum.h diff -N src/include/postmaster/autovacuum.h *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/include/postmaster/autovacuum.h 6 Jul 2005 23:26:37 -0000 *************** *** 0 **** --- 1,32 ---- + /* + * autovacuum.h + * + * $PostgreSQL$ + */ + + #ifndef AUTOVACUUM_H + #define AUTOVACUUM_H + + #include "utils/rel.h" + + /* GUC variables */ + extern bool autovacuum_start_daemon; + extern int autovacuum_naptime; + extern int autovacuum_vac_thresh; + extern double autovacuum_vac_scale; + extern int autovacuum_anl_thresh; + extern double autovacuum_anl_scale; + + /* Function to start autovacuum process, called from postmaster */ + int autovac_start(void); + void autovac_init(void); + bool AutoVacuumingActive(void); + bool IsAutoVacuumProcess(void); + + #ifdef EXEC_BACKEND + extern void AutoVacMain(int argc, char *argv[]); + #endif + + void update_autovac_tuple(Oid relid, bool vacuum, bool analyze, + Relation avRel); + #endif /* AUTOVACUUM_H */ Index: src/include/utils/guc_tables.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/utils/guc_tables.h,v retrieving revision 1.19 diff -c -r1.19 guc_tables.h *** src/include/utils/guc_tables.h 31 Dec 2004 22:03:46 -0000 1.19 --- src/include/utils/guc_tables.h 28 Jun 2005 14:16:20 -0000 *************** *** 62,67 **** --- 62,68 ---- STATS, STATS_MONITORING, STATS_COLLECTOR, + AUTOVACUUM, CLIENT_CONN, CLIENT_CONN_STATEMENT, CLIENT_CONN_LOCALE, Index: src/include/utils/syscache.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/utils/syscache.h,v retrieving revision 1.60 diff -c -r1.60 syscache.h *** src/include/utils/syscache.h 28 Jun 2005 05:09:13 -0000 1.60 --- src/include/utils/syscache.h 29 Jun 2005 20:19:20 -0000 *************** *** 40,67 **** #define AUTHMEMROLEMEM 9 #define AUTHNAME 10 #define AUTHOID 11 ! #define CASTSOURCETARGET 12 ! #define CLAAMNAMENSP 13 ! #define CLAOID 14 ! #define CONDEFAULT 15 ! #define CONNAMENSP 16 ! #define CONOID 17 ! #define INDEXRELID 18 ! #define INHRELID 19 ! #define LANGNAME 20 ! #define LANGOID 21 ! #define NAMESPACENAME 22 ! #define NAMESPACEOID 23 ! #define OPERNAMENSP 24 ! #define OPEROID 25 ! #define PROCNAMEARGSNSP 26 ! #define PROCOID 27 ! #define RELNAMENSP 28 ! #define RELOID 29 ! #define RULERELNAME 30 ! #define STATRELATT 31 ! #define TYPENAMENSP 32 ! #define TYPEOID 33 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); --- 40,68 ---- #define AUTHMEMROLEMEM 9 #define AUTHNAME 10 #define AUTHOID 11 ! #define AUTOVACRELID 12 ! #define CASTSOURCETARGET 13 ! #define CLAAMNAMENSP 14 ! #define CLAOID 15 ! #define CONDEFAULT 16 ! #define CONNAMENSP 17 ! #define CONOID 18 ! #define INDEXRELID 19 ! #define INHRELID 20 ! #define LANGNAME 21 ! #define LANGOID 22 ! #define NAMESPACENAME 23 ! #define NAMESPACEOID 24 ! #define OPERNAMENSP 25 ! #define OPEROID 26 ! #define PROCNAMEARGSNSP 27 ! #define PROCOID 28 ! #define RELNAMENSP 29 ! #define RELOID 30 ! #define RULERELNAME 31 ! #define STATRELATT 32 ! #define TYPENAMENSP 33 ! #define TYPEOID 34 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); Index: src/test/regress/expected/sanity_check.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/sanity_check.out,v retrieving revision 1.26 diff -c -r1.26 sanity_check.out *** src/test/regress/expected/sanity_check.out 1 Jul 2005 19:19:05 -0000 1.26 --- src/test/regress/expected/sanity_check.out 4 Jul 2005 16:22:47 -0000 *************** *** 40,45 **** --- 40,46 ---- pg_attribute | t pg_auth_members | t pg_authid | t + pg_autovacuum | t pg_cast | t pg_class | t pg_constraint | t *************** *** 65,71 **** shighway | t tenk1 | t tenk2 | t ! (55 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 66,72 ---- shighway | t tenk1 | t tenk2 | t ! (56 rows) -- -- another sanity check: every system catalog that has OIDs should have