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.55 diff -c -r1.55 Makefile *** src/backend/catalog/Makefile 7 Jul 2005 20:39:57 -0000 1.55 --- src/backend/catalog/Makefile 8 Jul 2005 00:13:48 -0000 *************** *** 27,33 **** # indexing.h had better be last. POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\ ! pg_proc.h pg_type.h pg_attribute.h pg_class.h \ pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h \ pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ --- 27,33 ---- # indexing.h had better be last. POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\ ! pg_proc.h pg_type.h pg_attribute.h pg_class.h pg_autovacuum.h \ pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h \ pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ Index: src/backend/catalog/heap.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/heap.c,v retrieving revision 1.286 diff -c -r1.286 heap.c *** src/backend/catalog/heap.c 7 Jul 2005 20:39:57 -0000 1.286 --- src/backend/catalog/heap.c 12 Jul 2005 04:33:11 -0000 *************** *** 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 --- 296,302 ---- /* ---------------------------------------------------------------- * heap_create_with_catalog - Create a cataloged relation * ! * this is done in 8 steps: * * 1) CheckAttributeNamesTypes() is used to make certain the tuple * descriptor contains a valid set of attribute names and types 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 12 Jul 2005 22:18:41 -0000 *************** *** 29,34 **** --- 29,35 ---- #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" + #include "pgstat.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" *************** *** 77,83 **** MemoryContext col_context); static VacAttrStats *examine_attribute(Relation onerel, int attnum); static int acquire_sample_rows(Relation onerel, HeapTuple *rows, ! int targrows, double *totalrows); static double random_fract(void); static double init_selection_state(int n); static double get_next_S(double t, int n, double *stateptr); --- 78,84 ---- MemoryContext col_context); static VacAttrStats *examine_attribute(Relation onerel, int attnum); static int acquire_sample_rows(Relation onerel, HeapTuple *rows, ! int targrows, double *totalrows, double *totaldeadrows); static double random_fract(void); static double init_selection_state(int n); static double get_next_S(double t, int n, double *stateptr); *************** *** 108,114 **** AnlIndexData *indexdata; int targrows, numrows; ! double totalrows; HeapTuple *rows; if (vacstmt->verbose) --- 109,116 ---- AnlIndexData *indexdata; int targrows, numrows; ! double totalrows, ! totaldeadrows; HeapTuple *rows; if (vacstmt->verbose) *************** *** 310,315 **** --- 312,328 ---- 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 + * will recognize as such. + * + * FIXME -- do something smart here. I don't see exactly what + * could we do. + */ + pgstat_report_analyze(RelationGetRelid(onerel), -1, -1); + relation_close(onerel, AccessShareLock); return; } *************** *** 340,346 **** * Acquire the sample rows */ rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple)); ! numrows = acquire_sample_rows(onerel, rows, targrows, &totalrows); /* * Compute the statistics. Temporary results during the calculations --- 353,360 ---- * Acquire the sample rows */ rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple)); ! numrows = acquire_sample_rows(onerel, rows, targrows, &totalrows, ! &totaldeadrows); /* * Compute the statistics. Temporary results during the calculations *************** *** 423,428 **** --- 437,449 ---- totalindexrows, false); } + + /* + * Update pg_autovacuum tuple too. If this is a VACUUM ANALYZE, + * this was already done. + */ + pgstat_report_analyze(RelationGetRelid(onerel), totalrows, + totaldeadrows); } /* Done with indexes */ *************** *** 752,774 **** * the number of different blocks represented by the sample tends to be * too small. We can live with that for now. Improvements are welcome. * ! * We also estimate the total number of rows in the table, and return that ! * into *totalrows. An important property of this sampling method is that ! * because we do look at a statistically unbiased set of blocks, we should ! * get an unbiased estimate of the average number of live rows per block. ! * The previous sampling method put too much credence in the row density near ! * the start of the table. * * The returned list of tuples is in order by physical position in the table. * (We will rely on this later to derive correlation estimates.) */ static int acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows, ! double *totalrows) { int numrows = 0; /* # rows collected */ double liverows = 0; /* # rows seen */ ! double deadrows = 0; double rowstoskip = -1; /* -1 means not set yet */ BlockNumber totalblocks; BlockSamplerData bs; --- 773,797 ---- * the number of different blocks represented by the sample tends to be * too small. We can live with that for now. Improvements are welcome. * ! * We also estimate the total number of live and dead rows in the table, ! * and return that into *totalrows and *totaldeadrows, respectively. ! * ! * An important property of this sampling method is that because we do ! * look at a statistically unbiased set of blocks, we should get an ! * unbiased estimate of the average number of live and dead rows per ! * block. The previous sampling method put too much credence in the row ! * density near the start of the table. * * The returned list of tuples is in order by physical position in the table. * (We will rely on this later to derive correlation estimates.) */ static int acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows, ! double *totalrows, double *totaldeadrows) { int numrows = 0; /* # rows collected */ double liverows = 0; /* # rows seen */ ! double deadrows = 0; /* # dead rows seen */ double rowstoskip = -1; /* -1 means not set yet */ BlockNumber totalblocks; BlockSamplerData bs; *************** *** 893,901 **** --- 916,930 ---- * Estimate total number of live rows in relation. */ if (bs.m > 0) + { *totalrows = floor((liverows * totalblocks) / bs.m + 0.5); + *totaldeadrows = floor((deadrows * totalblocks) / bs.m + 0.5); + } else + { *totalrows = 0.0; + *totaldeadrows = 0.0; + } /* * Emit some interesting relation info 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 12 Jul 2005 22:59:11 -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; *************** *** 303,312 **** ALLOCSET_DEFAULT_MAXSIZE); /* Assume we are processing everything unless one table is mentioned */ ! all_rels = (vacstmt->relation == NULL); /* Build list of relations to process (note this lives in vac_context) */ ! relations = get_rel_oids(vacstmt->relation, stmttype); if (vacstmt->vacuum && all_rels) { --- 305,326 ---- ALLOCSET_DEFAULT_MAXSIZE); /* Assume we are processing everything unless one table is mentioned */ ! all_rels = (vacstmt->relation == NULL && !OidIsValid(vacstmt->relid)); /* Build list of relations to process (note this lives in vac_context) */ ! if (OidIsValid(vacstmt->relid)) ! { ! MemoryContext old_context = MemoryContextSwitchTo(vac_context); ! ! AssertArg(vacstmt->relation == NULL); ! relations = list_make1_oid(vacstmt->relid); ! MemoryContextSwitchTo(old_context); ! } ! else ! { ! AssertArg(!OidIsValid(vacstmt->relid)); ! relations = get_rel_oids(vacstmt->relation, stmttype); ! } if (vacstmt->vacuum && all_rels) { *************** *** 1146,1151 **** --- 1160,1169 ---- /* update statistics in pg_class */ vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages, vacrelstats->rel_tuples, vacrelstats->hasindex); + + /* Signal the stats collector */ + pgstat_report_vacuum(RelationGetRelid(onerel), vacstmt->analyze, + vacrelstats->rel_tuples); } 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 11 Jul 2005 18:37:19 -0000 *************** *** 44,49 **** --- 44,51 ---- #include "access/xlog.h" #include "commands/vacuum.h" #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/autovacuum.h" #include "storage/freespace.h" #include "storage/smgr.h" #include "utils/lsyscache.h" *************** *** 179,184 **** --- 181,190 ---- vacrelstats->rel_pages, vacrelstats->rel_tuples, hasindex); + + /* Signal the stats collector */ + pgstat_report_vacuum(RelationGetRelid(onerel), vacstmt->analyze, + vacrelstats->rel_tuples); } Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.311 diff -c -r1.311 copyfuncs.c *** src/backend/nodes/copyfuncs.c 2 Jul 2005 23:00:39 -0000 1.311 --- src/backend/nodes/copyfuncs.c 12 Jul 2005 21:31:42 -0000 *************** *** 2237,2242 **** --- 2237,2243 ---- COPY_SCALAR_FIELD(freeze); COPY_SCALAR_FIELD(verbose); COPY_NODE_FIELD(relation); + COPY_SCALAR_FIELD(relid); COPY_NODE_FIELD(va_cols); return newnode; Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.248 diff -c -r1.248 equalfuncs.c *** src/backend/nodes/equalfuncs.c 2 Jul 2005 23:00:39 -0000 1.248 --- src/backend/nodes/equalfuncs.c 12 Jul 2005 21:34:22 -0000 *************** *** 1178,1183 **** --- 1178,1184 ---- COMPARE_SCALAR_FIELD(freeze); COMPARE_SCALAR_FIELD(verbose); COMPARE_NODE_FIELD(relation); + COMPARE_SCALAR_FIELD(relid); COMPARE_NODE_FIELD(va_cols); return true; Index: src/backend/parser/gram.y =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v retrieving revision 2.501 diff -c -r2.501 gram.y *** src/backend/parser/gram.y 29 Jun 2005 20:34:13 -0000 2.501 --- src/backend/parser/gram.y 12 Jul 2005 23:16:23 -0000 *************** *** 4659,4664 **** --- 4659,4665 ---- n->freeze = $3; n->verbose = $4; n->relation = NULL; + n->relid = InvalidOid; n->va_cols = NIL; $$ = (Node *)n; } *************** *** 4671,4676 **** --- 4672,4678 ---- n->freeze = $3; n->verbose = $4; n->relation = $5; + n->relid = InvalidOid; n->va_cols = NIL; $$ = (Node *)n; } *************** *** 4681,4686 **** --- 4683,4689 ---- n->full = $2; n->freeze = $3; n->verbose |= $4; + n->relid = InvalidOid; $$ = (Node *)n; } ; *************** *** 4695,4700 **** --- 4698,4704 ---- n->freeze = false; n->verbose = $2; n->relation = NULL; + n->relid = InvalidOid; n->va_cols = NIL; $$ = (Node *)n; } *************** *** 4707,4712 **** --- 4711,4717 ---- n->freeze = false; n->verbose = $2; n->relation = $3; + n->relid = InvalidOid; n->va_cols = $4; $$ = (Node *)n; } 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 12 Jul 2005 23:11:13 -0000 *************** *** 0 **** --- 1,771 ---- + /*------------- + * 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/genam.h" + #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 "storage/proc.h" + #include "storage/sinval.h" + #include "tcop/tcopprot.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" + + + 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 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. SendStatMsg means send a message to + * the stat system in order to update the last vacuum/analyze tuple + * counts, doing nothing to the table itself. This is used after a stats- + * reset. + */ + typedef enum AutovacOp + { + AVOperNone, + AVOperSendStatMsg, + AVOperAnalyze, + AVOperVacuumAnalyze + } AutovacOp; + + #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(Oid relid, PgStat_StatTabEntry *tabentry, + Form_pg_class classForm, Form_pg_autovacuum avForm, + List **vacuum_tables, List **analyze_tables); + static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum); + 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(); + + /* + * Set up signal handlers. We operate on databases much like a + * regular backend, so we use the same signal handling. See + * equivalent code in tcop/postgres.c. + */ + pqsignal(SIGHUP, autovac_sighup_handler); + /* + * Presently, StatementCancelHandler will lead to autovacuum shutdown, + * because that's how we handle ereport(ERROR). It could be improved + * however. + */ + pqsignal(SIGINT, StatementCancelHandler); + pqsignal(SIGTERM, die); + pqsignal(SIGQUIT, quickdie); + pqsignal(SIGALRM, handle_sig_alarm); + + pqsignal(SIGPIPE, SIG_IGN); + pqsignal(SIGUSR1, CatchupInterruptHandler); + /* We don't listen for async notifies */ + pqsignal(SIGUSR2, SIG_DFL); + pqsignal(SIGCHLD, 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. + */ + 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(); + + 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 two separate lists, one for + * vacuum and other for analyze. This is to facilitate processing all + * analyzes first, and then all vacuums. + * + * Note that CHECK_FOR_INTERRUPTS is supposed to be used in certain spots in + * order not to lose too much work. + */ + static void + do_autovacuum(PgStat_StatDBEntry *dbentry) + { + Relation classRel, + avRel; + HeapTuple tuple; + HeapScanDesc relScan; + List *vacuum_tables = NIL, + *analyze_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); + + /* + * StartTransactionCommand and CommitTransactionCommand will + * automatically switch to other contexts. We need this one + * to keep the list of relations to vacuum/analyze across + * transactions. + */ + MemoryContextSwitchTo(AutovacMemCxt); + + CHECK_FOR_INTERRUPTS(); + + classRel = heap_open(RelationRelationId, AccessShareLock); + avRel = heap_open(AutovacuumRelationId, AccessShareLock); + + relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL); + + /* Scan pg_class looking for tables to vacuum */ + while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL) + { + Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); + Form_pg_autovacuum avForm = NULL; + PgStat_StatTabEntry *tabentry; + SysScanDesc avScan; + HeapTuple avTup; + ScanKeyData entry[1]; + Oid relid; + + /* Skip non-table entries. */ + /* FIXME -- possibly allow RELKIND_TOASTVALUE entries here too. */ + if (classForm->relkind != RELKIND_RELATION) + continue; + + relid = HeapTupleGetOid(tuple); + + ScanKeyInit(&entry[0], + Anum_pg_autovacuum_vacrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + avScan = systable_beginscan(avRel, AutovacuumRelidIndexId, true, + SnapshotNow, 1, entry); + + avTup = systable_getnext(avScan); + + if (HeapTupleIsValid(avTup)) + avForm = (Form_pg_autovacuum) GETSTRUCT(avTup); + + tabentry = hash_search(dbentry->tables, &relid, + HASH_FIND, NULL); + + test_rel_for_autovac(relid, tabentry, classForm, avForm, + &vacuum_tables, &analyze_tables); + + systable_endscan(avScan); + } + + heap_endscan(relScan); + heap_close(avRel, AccessShareLock); + heap_close(classRel, AccessShareLock); + + /* + * Perform operations on collected tables. First close our current + * transaction, so each operation will have its own. + */ + CommitTransactionCommand(); + CHECK_FOR_INTERRUPTS(); + + foreach (cell, analyze_tables) + { + Oid relid = lfirst_oid(cell); + + autovacuum_do_vac_analyze(relid, false); + } + + foreach (cell, vacuum_tables) + { + Oid relid = lfirst_oid(cell); + + autovacuum_do_vac_analyze(relid, true); + } + } + + /* + * 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 dead tuples exceeds a + * threshold. This threshold is calculated as + * + * threshold = vac_base_thresh + vac_scale_factor * reltuples + * + * For analyze, the analysis done is that the number of tuples inserted, + * deleted and updated since the last analyze exceeds a threshold calculated + * in the same fashion as above. Note that the collector actually stores + * the number of tuples (both live and dead) that there were as of the last + * analyze. This is asymmetric to the VACUUM case. + * + * A table whose pg_autovacuum.enabled value is false, is automatically + * skipped. Thus autovacuum can be disabled for specific tables. + * + * A table whose vac_base_thresh value is -1, takes the base value from the + * autovacuum_vacuum_threshold GUC variable. Similarly, a vac_scale_factor + * value of -1 is substituted with the value of + * autovacuum_vacuum_scale_factor GUC variable. Ditto for analyze. + */ + static void + test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, + Form_pg_class classForm, + Form_pg_autovacuum avForm, + List **vacuum_tables, List **analyze_tables) + { + Relation rel; + float4 reltuples; /* pg_class.reltuples */ + /* constants from pg_autovacuum or GUC variables */ + int vac_base_thresh, + anl_base_thresh; + float4 vac_scale_factor, + anl_scale_factor; + /* thresholds calculated from above constants */ + float4 vacthresh, + anlthresh; + /* number of vacuum (resp. analyze) tuples at this time */ + float4 vactuples, + anltuples; + + /* User disabled it in pg_autovacuum */ + if (avForm && !avForm->enabled) + return; + + /* Not found in stat hash */ + if (tabentry == NULL) + { + /* + * Analyze this table. It will emit a stat message for the + * collector that will initialize the entry for the next time + * around, so we won't have to guess again. + */ + elog(DEBUG2, "table %u not known to stat system, will ANALYZE", + relid); + *analyze_tables = lappend_oid(*analyze_tables, relid); + return; + } + + rel = RelationIdGetRelation(relid); + /* The table was recently dropped? */ + if (rel == NULL) + return; + + reltuples = rel->rd_rel->reltuples; + RelationClose(rel); + + vactuples = tabentry->n_dead_tuples; + anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples - + tabentry->last_anl_tuples; + + /* + * If there is a tuple in pg_autovacuum, use it; else, use the GUC + * defaults. Note that the tuple may contain "-1" values, which means + * use the GUC defaults for each individual setting. + */ + if (avForm != NULL) + { + 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; + } + else + { + vac_scale_factor = autovacuum_vac_scale; + vac_base_thresh = autovacuum_vac_thresh; + + anl_scale_factor = autovacuum_anl_scale; + anl_base_thresh = autovacuum_anl_thresh; + } + + vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples; + anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples; + + /* + * Note that we don't need to take special consideration for stat + * reset, because if that happens, the last vacuum and analyze counts + * will be reset too. + */ + + elog(DEBUG2, "%s: vac: %.0f (threshold %.0f), " + "anl: %.0f (threshold %.0f)", + get_rel_name(relid), vactuples, vacthresh, anltuples, anlthresh); + + /* Determine if this table needs vacuum or analyze. */ + if (vactuples > vacthresh) + { + elog(DEBUG2, "will VACUUM ANALYZE this table"); + + *vacuum_tables = lappend_oid(*vacuum_tables, relid); + } + else if (anltuples > anlthresh) + { + elog(DEBUG2, "will ANALYZE this table"); + + *analyze_tables = lappend_oid(*analyze_tables, relid); + } + } + + /* + * 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. + * + * XXX It'd be nice to do all tables at once, passing vacuum() a list + * of Oids instead of a single one. + */ + static void + autovacuum_do_vac_analyze(Oid relid, bool dovacuum) + { + VacuumStmt vacstmt; + + StartTransactionCommand(); + + /* + * Ugly. + */ + vacstmt.vacuum = dovacuum; + vacstmt.full = false; + vacstmt.analyze = true; + vacstmt.freeze = false; + vacstmt.verbose = true; + vacstmt.relid = relid; + vacstmt.relation = NULL; + vacstmt.va_cols = NIL; + + elog(LOG, "autovacuum: %s \"%s\".\"%s\"", + dovacuum ? "VACUUM ANALYZE" : "ANALYZE", + get_namespace_name(get_rel_namespace(relid)), get_rel_name(relid)); + + vacuum(&vacstmt); + + /* Release locks */ + CommitTransactionCommand(); + + CHECK_FOR_INTERRUPTS(); + } + + /* + * 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); + + /* + * XXX Magic number comes from contrib pg_autovacuum.c. + */ + whole_db = (GetTopTransactionId() - dbform->datfrozenxid) > 1500000000L; + 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; + } + + /* + * SIGHUP: set flag to reread config file. + */ + static void + autovac_sighup_handler(SIGNAL_ARGS) + { + got_SIGHUP = 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 12 Jul 2005 23:16:50 -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" *************** *** 167,173 **** int *numbackends); static void backend_read_statsfile(void); ! static void pgstat_setheader(PgStat_MsgHdr *hdr, int mtype); static void pgstat_send(void *msg, int len); static void pgstat_recv_bestart(PgStat_MsgBestart *msg, int len); --- 168,174 ---- int *numbackends); static void backend_read_statsfile(void); ! static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype); static void pgstat_send(void *msg, int len); static void pgstat_recv_bestart(PgStat_MsgBestart *msg, int len); *************** *** 177,182 **** --- 178,186 ---- static void pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len); static void pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len); static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len); + static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len); + static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len); + static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len); /* ------------------------------------------------------------ *************** *** 618,623 **** --- 622,650 ---- } + /* ---------- + * pgstat_report_autovac() - + * + * Called from autovacuum.c to inform that a database was processed + * by the autovacuum daemon. + * ---------- + */ + void + pgstat_report_autovac(void) + { + PgStat_MsgAutovacStart msg; + + if (pgStatSock < 0 || !AutoVacuumingActive()) + return; + + MemSet(&(msg.m_hdr), 0, sizeof(msg.m_hdr)); + msg.m_hdr.m_type = PGSTAT_MTYPE_AUTOVAC_START; + msg.m_databaseid = MyDatabaseId; + msg.m_time = time(NULL); + + pgstat_send(&msg, sizeof(msg)); + } + /* ------------------------------------------------------------ * Public functions used by backends follow *------------------------------------------------------------ *************** *** 652,657 **** --- 679,729 ---- on_proc_exit(pgstat_beshutdown_hook, 0); } + /* --------- + * pgstat_report_vacuum() - + * + * Tell the collector about the table we just vacuumed. + * --------- + */ + void + pgstat_report_vacuum(Oid tableoid, bool analyze, PgStat_Counter tuples) + { + PgStat_MsgVacuum msg; + + if (pgStatSock < 0) + return; + + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); + msg.m_databaseid = MyDatabaseId; + msg.m_tableoid = tableoid; + msg.m_analyze = analyze; + msg.m_tuples = tuples; + pgstat_send(&msg, sizeof(msg)); + } + + /* -------- + * pgstat_report_analyze() - + * + * Tell the collector about the table we just analyzed. + * -------- + */ + void + pgstat_report_analyze(Oid tableoid, PgStat_Counter livetuples, + PgStat_Counter deadtuples) + { + PgStat_MsgAnalyze msg; + + if (pgStatSock < 0) + return; + + pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); + msg.m_databaseid = MyDatabaseId; + msg.m_tableoid = tableoid; + msg.m_live_tuples = livetuples; + msg.m_dead_tuples = deadtuples; + pgstat_send(&msg, sizeof(msg)); + } + /* * Flush any remaining statistics counts out to the collector at process * exit. Without this, operations triggered during backend exit (such as *************** *** 1279,1285 **** * ---------- */ static void ! pgstat_setheader(PgStat_MsgHdr *hdr, int mtype) { hdr->m_type = mtype; hdr->m_backendid = MyBackendId; --- 1351,1357 ---- * ---------- */ static void ! pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype) { hdr->m_type = mtype; hdr->m_backendid = MyBackendId; *************** *** 1653,1658 **** --- 1725,1742 ---- nread); break; + case PGSTAT_MTYPE_AUTOVAC_START: + pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, nread); + break; + + case PGSTAT_MTYPE_VACUUM: + pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, nread); + break; + + case PGSTAT_MTYPE_ANALYZE: + pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, nread); + break; + default: break; } *************** *** 2049,2054 **** --- 2133,2139 ---- 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; --- 2404,2416 ---- 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; } } --- 2638,2673 ---- * * 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 **** --- 2710,2838 ---- pgstat_sub_backend(msg->m_hdr.m_procpid); } + /* ---------- + * pgstat_recv_autovac() - + * + * Process an autovacuum signalling message. + * ---------- + */ + static void + pgstat_recv_autovac(PgStat_MsgAutovacStart *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_vacuum() - + * + * Process a VACUUM message. + * ---------- + */ + static void + pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len) + { + PgStat_StatDBEntry *dbentry; + PgStat_StatTabEntry *tabentry; + bool found; + + dbentry = pgstat_get_db_entry(msg->m_databaseid); + + tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), + HASH_ENTER, &found); + + /* + * If we are creating the entry, initialize it. + */ + if (!found) + { + tabentry->tableid = msg->m_tableoid; + + tabentry->tuples_returned = 0; + tabentry->tuples_fetched = 0; + tabentry->tuples_inserted = msg->m_tuples; + tabentry->tuples_deleted = 0; + tabentry->tuples_updated = 0; + + tabentry->n_live_tuples = msg->m_tuples; + tabentry->n_dead_tuples = 0; + + if (msg->m_analyze) + tabentry->last_anl_tuples = msg->m_tuples; + else + tabentry->last_anl_tuples = 0; + + tabentry->blocks_fetched = 0; + tabentry->blocks_hit = 0; + } + else + { + tabentry->n_dead_tuples = 0; + tabentry->n_live_tuples = msg->m_tuples; + if (msg->m_analyze) + tabentry->last_anl_tuples = msg->m_tuples; + } + } + + /* ---------- + * pgstat_recv_analyze() - + * + * Process an ANALYZE message. + * ---------- + */ + static void + pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len) + { + PgStat_StatDBEntry *dbentry; + PgStat_StatTabEntry *tabentry; + bool found; + + dbentry = pgstat_get_db_entry(msg->m_databaseid); + + tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), + HASH_ENTER, &found); + + /* + * If we are creating the entry, initialize it. + */ + if (!found) + { + tabentry->tableid = msg->m_tableoid; + + tabentry->tuples_returned = 0; + tabentry->tuples_fetched = 0; + tabentry->tuples_inserted = 0; + tabentry->tuples_deleted = 0; + tabentry->tuples_updated = 0; + + tabentry->n_live_tuples = msg->m_live_tuples; + tabentry->n_dead_tuples = msg->m_dead_tuples; + tabentry->last_anl_tuples = msg->m_live_tuples + msg->m_dead_tuples; + + tabentry->blocks_fetched = 0; + tabentry->blocks_hit = 0; + } + else + { + tabentry->n_live_tuples = msg->m_live_tuples; + tabentry->n_dead_tuples = msg->m_dead_tuples; + tabentry->last_anl_tuples = msg->m_live_tuples + msg->m_dead_tuples; + } + } /* ---------- * pgstat_recv_activity() - *************** *** 2690,2695 **** --- 2917,2926 ---- tabentry->tuples_deleted = tabmsg[i].t_tuples_deleted; tabentry->blocks_fetched = tabmsg[i].t_blocks_fetched; tabentry->blocks_hit = tabmsg[i].t_blocks_hit; + + tabentry->n_live_tuples = tabmsg[i].t_tuples_inserted; + tabentry->n_dead_tuples = tabmsg[i].t_tuples_updated + + tabmsg[i].t_tuples_deleted; tabentry->destroy = 0; } *************** *** 2706,2711 **** --- 2937,2946 ---- tabentry->tuples_deleted += tabmsg[i].t_tuples_deleted; tabentry->blocks_fetched += tabmsg[i].t_blocks_fetched; tabentry->blocks_hit += tabmsg[i].t_blocks_hit; + + tabentry->n_live_tuples += tabmsg[i].t_tuples_inserted; + tabentry->n_dead_tuples += tabmsg[i].t_tuples_updated + + tabmsg[i].t_tuples_deleted; } /* 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 12 Jul 2005 23:17:33 -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 */ *************** *** 1872,1877 **** --- 1891,1904 ---- if (DLGetHead(BackendList)) break; /* let reaper() handle this */ + /* Tell autovac to shut down. */ + if (AutoVacPID != 0) + { + /* Use statement cancel to shut it down */ + kill(AutoVacPID, SIGINT); + break; /* let reaper() handle this */ + } + /* * No children left. Begin shutdown of data base system. */ *************** *** 1905,1910 **** --- 1932,1944 ---- ereport(LOG, (errmsg("received fast shutdown request"))); + /* Shut down autovacuum */ + if (AutoVacPID != 0) + { + kill(AutoVacPID, SIGTERM); + break; /* let reaper() handle this */ + } + if (DLGetHead(BackendList)) { if (!FatalError) *************** *** 1949,1954 **** --- 1983,1990 ---- */ ereport(LOG, (errmsg("received immediate shutdown request"))); + if (AutoVacPID != 0) + kill(AutoVacPID, SIGQUIT); if (StartupPID != 0) kill(StartupPID, SIGQUIT); if (BgWriterPID != 0) *************** *** 2144,2149 **** --- 2180,2198 ---- } /* + * 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); + 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"))); --- 2205,2212 ---- * 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"))); *************** *** 2171,2177 **** if (Shutdown > NoShutdown) { ! if (DLGetHead(BackendList) || StartupPID != 0) goto reaper_done; /* Start the bgwriter if not running */ if (BgWriterPID == 0) --- 2221,2227 ---- if (Shutdown > NoShutdown) { ! if (DLGetHead(BackendList) || StartupPID != 0 || AutoVacPID != 0) goto reaper_done; /* Start the bgwriter if not running */ if (BgWriterPID == 0) *************** *** 2339,2345 **** kill(PgStatPID, SIGQUIT); } ! /* We do NOT restart the syslogger */ FatalError = true; } --- 2389,2395 ---- kill(PgStatPID, SIGQUIT); } ! /* We do NOT restart the syslogger, not the autovacuum process */ FatalError = true; } *************** *** 3154,3159 **** --- 3204,3210 ---- * 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 **** --- 3256,3272 ---- 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/tcop/postgres.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/postgres.c,v retrieving revision 1.452 diff -c -r1.452 postgres.c *** src/backend/tcop/postgres.c 4 Jul 2005 04:51:49 -0000 1.452 --- src/backend/tcop/postgres.c 11 Jul 2005 16:19:18 -0000 *************** *** 2110,2116 **** * Query-cancel signal from postmaster: abort current transaction * at soonest convenient time */ ! static void StatementCancelHandler(SIGNAL_ARGS) { int save_errno = errno; --- 2110,2116 ---- * Query-cancel signal from postmaster: abort current transaction * at soonest convenient time */ ! void StatementCancelHandler(SIGNAL_ARGS) { int save_errno = errno; 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.273 diff -c -r1.273 guc.c *** src/backend/utils/misc/guc.c 5 Jul 2005 23:18:10 -0000 1.273 --- src/backend/utils/misc/guc.c 7 Jul 2005 04:31:30 -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" *************** *** 677,682 **** --- 678,691 ---- &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, *************** *** 1388,1393 **** --- 1397,1426 ---- &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 */ { *************** *** 1486,1491 **** --- 1519,1542 ---- &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.152 diff -c -r1.152 postgresql.conf.sample *** src/backend/utils/misc/postgresql.conf.sample 5 Jul 2005 23:18:10 -0000 1.152 --- src/backend/utils/misc/postgresql.conf.sample 7 Jul 2005 04:31:31 -0000 *************** *** 283,288 **** --- 283,300 ---- #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/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 12 Jul 2005 17:29:47 -0000 *************** *** 20,33 **** * The types of backend/postmaster -> collector messages * ---------- */ ! #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. --- 20,39 ---- * The types of backend/postmaster -> collector messages * ---------- */ ! typedef enum StatMsgType ! { ! PGSTAT_MTYPE_DUMMY, ! PGSTAT_MTYPE_BESTART, ! PGSTAT_MTYPE_BETERM, ! PGSTAT_MTYPE_ACTIVITY, ! PGSTAT_MTYPE_TABSTAT, ! PGSTAT_MTYPE_TABPURGE, ! PGSTAT_MTYPE_DROPDB, ! PGSTAT_MTYPE_RESETCOUNTER, ! PGSTAT_MTYPE_AUTOVAC_START, ! PGSTAT_MTYPE_VACUUM, ! PGSTAT_MTYPE_ANALYZE ! } StatMsgType; /* ---------- * The data type used for counters. *************** *** 48,54 **** */ typedef struct PgStat_MsgHdr { ! int m_type; int m_size; int m_backendid; int m_procpid; --- 54,60 ---- */ typedef struct PgStat_MsgHdr { ! StatMsgType m_type; int m_size; int m_backendid; int m_procpid; *************** *** 115,120 **** --- 121,167 ---- } PgStat_MsgBeterm; /* ---------- + * PgStat_MsgAutovacStart Sent by the autovacuum daemon to signal + * that a database is going to be processed + * ---------- + */ + typedef struct PgStat_MsgAutovacStart + { + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + time_t m_time; + } PgStat_MsgAutovacStart; + + /* ---------- + * PgStat_MsgVacuum Sent by the backend or autovacuum daemon + * after VACUUM or VACUUM ANALYZE + * ---------- + */ + typedef struct PgStat_MsgVacuum + { + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + Oid m_tableoid; + bool m_analyze; + PgStat_Counter m_tuples; + } PgStat_MsgVacuum; + + /* ---------- + * PgStat_MsgAnalyze Sent by the backend or autovacuum daemon + * after ANALYZE + * ---------- + */ + typedef struct PgStat_MsgAnalyze + { + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + Oid m_tableoid; + PgStat_Counter m_live_tuples; + PgStat_Counter m_dead_tuples; + } PgStat_MsgAnalyze; + + + /* ---------- * PgStat_MsgActivity Sent by the backends when they start * to parse a query. * ---------- *************** *** 200,205 **** --- 247,255 ---- PgStat_MsgTabpurge msg_tabpurge; PgStat_MsgDropdb msg_dropdb; PgStat_MsgResetcounter msg_resetcounter; + PgStat_MsgAutovacStart msg_autovacuum; + PgStat_MsgVacuum msg_vacuum; + PgStat_MsgAnalyze msg_analyze; } PgStat_Msg; *************** *** 222,227 **** --- 272,278 ---- PgStat_Counter n_blocks_fetched; PgStat_Counter n_blocks_hit; int destroy; + time_t last_autovac_time; } PgStat_StatDBEntry; *************** *** 282,287 **** --- 333,342 ---- PgStat_Counter tuples_updated; PgStat_Counter tuples_deleted; + PgStat_Counter n_live_tuples; + PgStat_Counter n_dead_tuples; + PgStat_Counter last_anl_tuples; + PgStat_Counter blocks_fetched; PgStat_Counter blocks_hit; *************** *** 323,328 **** --- 378,388 ---- extern void pgstat_ping(void); extern void pgstat_report_activity(const char *what); extern void pgstat_report_tabstat(void); + extern void pgstat_report_autovac(void); + extern void pgstat_report_vacuum(Oid tableoid, bool analyze, + PgStat_Counter tuples); + extern void pgstat_report_analyze(Oid tableoid, PgStat_Counter livetuples, + PgStat_Counter deadtuples); 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.89 diff -c -r1.89 indexing.h *** src/include/catalog/indexing.h 7 Jul 2005 20:39:59 -0000 1.89 --- src/include/catalog/indexing.h 8 Jul 2005 00:12:00 -0000 *************** *** 210,215 **** --- 210,217 ---- 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 12 Jul 2005 22:22:03 -0000 *************** *** 0 **** --- 1,60 ---- + /*------------------------------------------------------------------------- + * + * 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 */ + int4 anl_base_thresh; /* base threshold value */ + float4 anl_scale_factor; /* reltuples scaling factor */ + } 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 6 + #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_anl_base_thresh 5 + #define Anum_pg_autovacuum_anl_scale_factor 6 + + /* There are no preloaded tuples in pg_autovacuum.h */ + + #endif /* PG_AUTOVACUUM_H */ Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.285 diff -c -r1.285 parsenodes.h *** src/include/nodes/parsenodes.h 28 Jun 2005 19:51:24 -0000 1.285 --- src/include/nodes/parsenodes.h 12 Jul 2005 21:33:33 -0000 *************** *** 1645,1650 **** --- 1645,1655 ---- * * Even though these are nominally two statements, it's convenient to use * just one node type for both. + * + * The relid field is only used by autovacuum at present; a single-table + * vacuum coming from a user statement will have relation set instead. + * A database-wide vacuum will have relation set to NULL and relid to + * InvalidOid. * ---------------------- */ typedef struct VacuumStmt *************** *** 1656,1661 **** --- 1661,1667 ---- bool freeze; /* early-freeze option */ bool verbose; /* print progress info */ RangeVar *relation; /* single table to process, or NULL */ + Oid relid; /* single table to process, or InvalidOid */ List *va_cols; /* list of column names, or NIL for all */ } VacuumStmt; 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 11 Jul 2005 21:40:39 -0000 *************** *** 0 **** --- 1,30 ---- + /* + * 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 + + #endif /* AUTOVACUUM_H */ Index: src/include/tcop/tcopprot.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/tcop/tcopprot.h,v retrieving revision 1.75 diff -c -r1.75 tcopprot.h *** src/include/tcop/tcopprot.h 3 Jun 2005 23:05:30 -0000 1.75 --- src/include/tcop/tcopprot.h 11 Jul 2005 16:19:33 -0000 *************** *** 58,63 **** --- 58,64 ---- extern void die(SIGNAL_ARGS); extern void quickdie(SIGNAL_ARGS); extern void authdie(SIGNAL_ARGS); + extern void StatementCancelHandler(SIGNAL_ARGS); extern void prepare_for_client_read(void); extern void client_read_ended(void); extern int PostgresMain(int argc, char *argv[], const char *username); 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/test/regress/expected/sanity_check.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/sanity_check.out,v retrieving revision 1.27 diff -c -r1.27 sanity_check.out *** src/test/regress/expected/sanity_check.out 7 Jul 2005 20:40:01 -0000 1.27 --- src/test/regress/expected/sanity_check.out 12 Jul 2005 23:19:15 -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 *************** *** 66,72 **** shighway | t tenk1 | t tenk2 | t ! (56 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 67,73 ---- shighway | t tenk1 | t tenk2 | t ! (57 rows) -- -- another sanity check: every system catalog that has OIDs should have