Re: Autovacuum Integration Patch Take 5 - Mailing list pgsql-patches

From Matthew T. O'Connor
Subject Re: Autovacuum Integration Patch Take 5
Date
Msg-id 4112C3C9.6090005@zeut.net
Whole thread Raw
In response to Autovacuum Integration Patch Take 5  ("Matthew T. O'Connor" <matthew@zeut.net>)
Responses Re: Autovacuum Integration Patch Take 5
List pgsql-patches
Just to reply to my own post here, I spent a few hours last night trying
to improve the shutdown sequence for the Autovacuum patch.  I think I
was headed in the right direction, but unfortunatly I wasn't able to get
anything to a high enough quality where I could submit the patch.
Basically I changed the postmaster shutdown code to signal the
autovacuum process before it signals any of it's normal child processes,
then inside the autovacuum signal handler I was issuing a query cancel
in the current database connection is there was one.  For some unknown
reason, when I went to shut down the autovacuum process by editing
postgresql.conf and signaling the postmaster to reread it, I would get
one proc_exit(0) in the log followed by several hundred proc_exit(1) and
it was too late for me to track this down.  I can send in what I have so
far, but it's not pretty.

Unfortunately, I am not going to be available to work on this for the
next few days.  I don't know what to do.

Thanks,

Matthew O'Connor


Matthew T. O'Connor wrote:

>Well I didn't get out of the office as early as I had hoped, and I have
>stayed up longer than I had planned, but I have a patch that addresses
>many of the issues raised by Tom.  Please take a look at let me know if
>I'm heading in the right direction.
>
>Issues addressed:
>* Add ability to read username and password from autovac.passwd file in
>$PGDATA (format is username:password)
>* Update patch, make sure it doesn't revert the recent write_stderr
>changes in postmaster.c
>* Remove static from functions defined in pg_autovacuum.h
>* Remove if(sigsetjmp ...) code block
>* Removed improper exit()'s, replaced with proc_exit() or
>ereport(ERROR)
>* Change elog() calls to ereport()
>* Change elog(WARNING, "pg_autovacuum: autovac is enabled, but requires
>stats_row_level which is not enabled"); to only be called once on
>postmaster startup
>* Remove unneeded code that was cut and pasted from the bgwriter example
>
>Issues not addressed in this patch:
>* Dynamic linking of libpq: I need someone else to do this.
>* Autovacuum Shutdown: I'm going to take a stab at this tonight
>(Wednesday) we will see what I come up with.
>
>
>To apply this patch:
>1) Move pg_autovacuum.[ch] from contrib to
>src/backend/postmaster/autovacuum.c and
>src/include/postmaster/autovacuum.h respectively.
>2) Place the attached pg_autovacuum.h in src/include/catelog/
>3) Apply the attached diff file
>
>Thanks again,
>
>Matthew O'Connor
>
>
>
>
>------------------------------------------------------------------------
>
>/*-------------------------------------------------------------------------
> *
> * pg_autovacuum.h
> *      definition of the system "autovacuum" relation (pg_autovacuum)
> *
> * NOTE: an object is identified by the OID of the row that primarily
> * defines the object, plus the OID of the table that that row appears in.
> * For example, a function is identified by the OID of its pg_proc row
> * plus the pg_class OID of table pg_proc.    This allows unique identification
> * of objects without assuming that OIDs are unique across tables.
> *
> * Since attributes don't have OIDs of their own, we identify an attribute
> * comment by the objoid+classoid of its parent table, plus an "objsubid"
> * giving the attribute column number.    "objsubid" must be zero in a comment
> * for a table itself, so that it is distinct from any column comment.
> * Currently, objsubid is unused and zero for all other kinds of objects,
> * but perhaps it might be useful someday to associate comments with
> * constituent elements of other kinds of objects (arguments of a function,
> * for example).
> *
> *
> * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
> * Portions Copyright (c) 1994, Regents of the University of California
> *
> * $PostgreSQL: pgsql-server/src/include/catalog/pg_autovacuum.h,v 1.20 2003/11/29 22:40:58 pgsql Exp $
> *
> * NOTES
> *        the genbki.sh script reads this file and generates .bki
> *        information from the DATA() statements.
> *
> *        XXX do NOT break up DATA() statements into multiple lines!
> *            the scripts are not as smart as you might think...
> *
> *-------------------------------------------------------------------------
> */
>#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
> * ----------------
> */
>CATALOG(pg_autovacuum) BKI_WITHOUT_OIDS
>{
>    Oid            table_oid;        /* OID of table */
>    int4        analyze_base_threshold; /* Base Threshold value */
>    int4        vacuum_base_threshold;  /* Base Threshold value */
>    float4        analyze_scaling_factor; /* Threshold of ins/upd/del's before analyze */
>    float4        vacuum_scaling_factor;  /* Threshold of ins/upd/del's before vacuuum */
>    float4        analyze_threshold; /* Threshold of ins/upd/del's before analyze */
>    float4        vacuum_threshold;  /* Threshold of ins/upd/del's before vacuuum */
>    float4        cnt_at_last_analyze;        /* equal to: inserts + updates as
>                                         * of the last analyze or initial
>                                         * values at startup */
>    float4        cnt_at_last_vacuum;        /* equal to: deletes + updates as
>                                         * of the last vacuum or initial
>                                         * values at startup */
>} 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                9
>#define Anum_pg_autovacuum_table_oid    1
>#define Anum_pg_autovacuum_analyze_base_threshold        2
>#define Anum_pg_autovacuum_vacuum_base_threshold        3
>#define Anum_pg_autovacuum_analyze_scaling_factor        4
>#define Anum_pg_autovacuum_vacuum_scaling_factor    5
>#define Anum_pg_autovacuum_analyze_threshold        6
>#define Anum_pg_autovacuum_vacuum_threshold        7
>#define Anum_pg_autovacuum_cnt_at_last_analyze     8
>#define Anum_pg_autovacuum_cnt_at_last_vacuum     9
>
>/* ----------------
> *        initial contents of pg_autovacuum
> * ----------------
> */
>
>/*
> *    Because the contents of this table are taken from the other *.h files,
> *    there is no initialization here.  The initial contents are extracted
> *    by genbki.sh and loaded during initdb.
> */
>
>#endif   /* PG_AUTOVACUUM_H */
>
>
>------------------------------------------------------------------------
>
>*** ./src/backend/bootstrap/bootstrap.c.orig    2004-08-04 01:41:29.044078073 -0400
>--- ./src/backend/bootstrap/bootstrap.c    2004-08-03 00:47:35.000000000 -0400
>***************
>*** 33,38 ****
>--- 33,39 ----
>  #include "libpq/pqsignal.h"
>  #include "miscadmin.h"
>  #include "postmaster/bgwriter.h"
>+ #include "postmaster/autovacuum.h"
>  #include "storage/freespace.h"
>  #include "storage/ipc.h"
>  #include "storage/pg_shmem.h"
>***************
>*** 361,366 ****
>--- 362,370 ----
>              case BS_XLOG_BGWRITER:
>                  statmsg = "writer process";
>                  break;
>+             case BS_XLOG_AUTOVAC:
>+                 statmsg = "auto vacuum process";
>+                 break;
>              default:
>                  statmsg = "??? process";
>                  break;
>***************
>*** 397,402 ****
>--- 401,409 ----
>              case BS_XLOG_BGWRITER:
>                  InitDummyProcess(DUMMY_PROC_BGWRITER);
>                  break;
>+             case BS_XLOG_AUTOVAC:
>+                 InitDummyProcess(DUMMY_PROC_AUTOVAC);
>+                 break;
>
>              default:
>                  InitDummyProcess(DUMMY_PROC_DEFAULT);
>***************
>*** 433,438 ****
>--- 440,451 ----
>              BackgroundWriterMain();
>              proc_exit(1);        /* should never return */
>
>+         case BS_XLOG_AUTOVAC:
>+             /* don't set signals, autovac has its own agenda */
>+             InitXLOGAccess();
>+             AutoVacMain();
>+             proc_exit(1);        /* should never return */
>+
>          default:
>              elog(PANIC, "unrecognized XLOG op: %d", xlogop);
>              proc_exit(1);
>*** ./src/backend/catalog/Makefile.orig    2004-08-04 01:41:52.546499796 -0400
>--- ./src/backend/catalog/Makefile    2004-07-22 23:50:31.000000000 -0400
>***************
>*** 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_shadow.h pg_group.h \
>!     pg_tablespace.h pg_depend.h indexing.h \
>      )
>
>  pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)
>--- 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_shadow.h pg_group.h \
>!     pg_tablespace.h pg_depend.h pg_autovacuum.h indexing.h \
>      )
>
>  pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)
>*** ./src/backend/Makefile.orig    2004-08-04 01:41:41.458716157 -0400
>--- ./src/backend/Makefile    2004-08-03 00:47:34.000000000 -0400
>***************
>*** 29,41 ****
>
>  ##########################################################################
>
>! all: submake-libpgport postgres $(POSTGRES_IMP)
>
>  ifneq ($(PORTNAME), cygwin)
>  ifneq ($(PORTNAME), win32)
>
>  postgres: $(OBJS)
>!     $(CC) $(CFLAGS) $(LDFLAGS) $(export_dynamic) $^ $(LIBS) -o $@
>
>  endif
>  endif
>--- 29,41 ----
>
>  ##########################################################################
>
>! all: submake-libpgport submake-libpq postgres $(POSTGRES_IMP)
>
>  ifneq ($(PORTNAME), cygwin)
>  ifneq ($(PORTNAME), win32)
>
>  postgres: $(OBJS)
>!     $(CC) $(CFLAGS) $(LDFLAGS) -I $(libpq_srcdir) $(export_dynamic) $^ $(LIBS)  $(libpq) -o $@
>
>  endif
>  endif
>*** ./src/backend/postmaster/autovacuum.c.orig    2004-06-29 09:27:14.000000000 -0400
>--- ./src/backend/postmaster/autovacuum.c    2004-08-04 02:00:32.005810127 -0400
>***************
>*** 1,153 ****
>! /* pg_autovacuum.c
>   * All the code for the pg_autovacuum program
>   * (c) 2003 Matthew T. O'Connor
>   * Revisions by Christopher B. Browne, Liberty RMS
>   */
>
>! #include "pg_autovacuum.h"
>
>- FILE       *LOGOUTPUT;
>  char        logbuffer[4096];
>
>! static void
>! log_entry(const char *logentry)
>! {
>!     time_t        curtime;
>!     struct tm  *loctime;
>!     char        timebuffer[128];
>!
>!     curtime = time(NULL);
>!     loctime = localtime(&curtime);
>!     strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S %Z", loctime);
>!     fprintf(LOGOUTPUT, "[%s] %s\n", timebuffer, logentry);
>! }
>
>  /*
>!  * Function used to detach the pg_autovacuum daemon from the tty and go into
>!  * the background.
>   *
>!  * This code is mostly ripped directly from pm_dameonize in postmaster.c with
>!  * unneeded code removed.
>   */
>! static void
>! daemonize()
>  {
>!     pid_t        pid;
>
>!     pid = fork();
>!     if (pid == (pid_t) -1)
>!     {
>!         log_entry("Error: cannot disassociate from controlling TTY");
>!         fflush(LOGOUTPUT);
>!         _exit(1);
>!     }
>!     else if (pid)
>!     {                            /* parent */
>!         /* Parent should just exit, without doing any atexit cleanup */
>!         _exit(0);
>!     }
>
>! /* GH: If there's no setsid(), we hopefully don't need silent mode.
>!  * Until there's a better solution.  */
>! #ifdef HAVE_SETSID
>!     if (setsid() < 0)
>!     {
>!         log_entry("Error: cannot disassociate from controlling TTY");
>!         fflush(LOGOUTPUT);
>!         _exit(1);
>!     }
>  #endif
>
>! }
>!
>! /* Create and return tbl_info struct with initialized to values from row or res */
>! static tbl_info *
>! init_table_info(PGresult *res, int row, db_info * dbi)
>! {
>!     tbl_info   *new_tbl = (tbl_info *) malloc(sizeof(tbl_info));
>
>!     if (!new_tbl)
>!     {
>!         log_entry("init_table_info: Cannot get memory");
>!         fflush(LOGOUTPUT);
>!         return NULL;
>!     }
>
>!     if (res == NULL)
>!         return NULL;
>
>-     new_tbl->dbi = dbi;            /* set pointer to db */
>
>!     new_tbl->schema_name = (char *)
>!         malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "schemaname"))) + 1);
>!     if (!new_tbl->schema_name)
>!     {
>!         log_entry("init_table_info: malloc failed on new_tbl->schema_name");
>!         fflush(LOGOUTPUT);
>!         return NULL;
>!     }
>!     strcpy(new_tbl->schema_name,
>!            PQgetvalue(res, row, PQfnumber(res, "schemaname")));
>
>!     new_tbl->table_name = (char *)
>!         malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "relname"))) +
>!                strlen(new_tbl->schema_name) + 6);
>!     if (!new_tbl->table_name)
>!     {
>!         log_entry("init_table_info: malloc failed on new_tbl->table_name");
>!         fflush(LOGOUTPUT);
>!         return NULL;
>!     }
>
>      /*
>!      * Put both the schema and table name in quotes so that we can work
>!      * with mixed case table names
>       */
>!     strcpy(new_tbl->table_name, "\"");
>!     strcat(new_tbl->table_name, new_tbl->schema_name);
>!     strcat(new_tbl->table_name, "\".\"");
>!     strcat(new_tbl->table_name, PQgetvalue(res, row, PQfnumber(res, "relname")));
>!     strcat(new_tbl->table_name, "\"");
>!
>!     new_tbl->CountAtLastAnalyze =
>!         (atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_ins"))) +
>!          atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_upd"))) +
>!          atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_del"))));
>!     new_tbl->curr_analyze_count = new_tbl->CountAtLastAnalyze;
>!
>!     new_tbl->CountAtLastVacuum =
>!         (atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_del"))) +
>!          atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_upd"))));
>!     new_tbl->curr_vacuum_count = new_tbl->CountAtLastVacuum;
>!
>!     new_tbl->relid = atooid(PQgetvalue(res, row, PQfnumber(res, "oid")));
>!     new_tbl->reltuples = atof(PQgetvalue(res, row, PQfnumber(res, "reltuples")));
>!     new_tbl->relpages = atooid(PQgetvalue(res, row, PQfnumber(res, "relpages")));
>!
>!     if (strcmp("t", PQgetvalue(res, row, PQfnumber(res, "relisshared"))))
>!         new_tbl->relisshared = 0;
>!     else
>!         new_tbl->relisshared = 1;
>!
>!     new_tbl->analyze_threshold =
>!         args->analyze_base_threshold + args->analyze_scaling_factor * new_tbl->reltuples;
>!     new_tbl->vacuum_threshold =
>!         args->vacuum_base_threshold + args->vacuum_scaling_factor * new_tbl->reltuples;
>
>!     if (args->debug >= 2)
>!         print_table_info(new_tbl);
>
>!     return new_tbl;
>  }
>
>  /* Set thresholds = base_value + scaling_factor * reltuples
>     Should be called after a vacuum since vacuum updates values in pg_class */
>! static void
>! update_table_thresholds(db_info * dbi, tbl_info * tbl, int vacuum_type)
>  {
>-     PGresult   *res = NULL;
>      int            disconnect = 0;
>!     char        query[128];
>
>      if (dbi->conn == NULL)
>      {
>--- 1,188 ----
>! /*-------------------------------------------------------------------------
>!  *
>!  * pg_autovacuum.c
>!  *
>!  * The background autovacuum daemon was in 7.4 contribis but is being newly
>!  * integrated into 7.5.  It monitors database activity using data from the
>!  * stats system (though at some point is should also look at FSM data) so
>!  * as to perform vacuum commands on specific tables when and only when
>!  * a sufficient amount activity has been performed on that table.
>!  *
>!  * The autovacuum process is started by the postmaster on startup.
>!  * It remains alive until the postmaster commands it to terminate. Normal
>!  * termination is by SIGUSR2, which instructs the autovacuum process to proc_exit().
>!  * Emergency termination is by SIGQUIT; like any
>!  * backend, the autovacuum process will simply abort and proc_exit on SIGQUIT.
>!  *
>   * All the code for the pg_autovacuum program
>   * (c) 2003 Matthew T. O'Connor
>   * Revisions by Christopher B. Browne, Liberty RMS
>+  *-------------------------------------------------------------------------
>   */
>+ #include "postgres.h"
>+
>+ #include <signal.h>
>+ #include <time.h>
>
>! #include "access/xlog.h"
>!
>! #include "libpq/pqsignal.h"
>! #include "miscadmin.h"
>! #include "storage/bufmgr.h"
>! #include "storage/freespace.h"
>! #include "storage/ipc.h"
>! #include "storage/pmsignal.h"
>! #include "storage/smgr.h"
>! #include "tcop/tcopprot.h"
>! #include "utils/guc.h"
>! #include "postmaster/autovacuum.h"
>
>  char        logbuffer[4096];
>
>! /*
>!  * GUC parameters
>!  */
>! bool        autovacuum_start_daemon = false;
>! int            autovacuum_vacuum_base = 1000;
>! double        autovacuum_vacuum_scaling_factor = 2;
>! int            autovacuum_analyze_base = 500;
>! double        autovacuum_analyze_scaling_factor = 1;
>! int            PostPortNumber = 5432;
>!
>! /*
>!  * These used to be taken from command lines args
>!  * and might need to move to GUC, but I'm not sure.
>!  */
>! char    *pg_user = NULL,
>!         *pg_user_password = NULL;
>!
>! /*
>!  * Flags set by interrupt handlers for later service in the main loop.
>!  */
>! static volatile sig_atomic_t got_SIGHUP = false;
>! static volatile sig_atomic_t shutdown_requested = false;
>
>  /*
>!  * Private state
>!  */
>! static bool        am_autovac = false;
>!
>!
>! static void autovac_quickdie(SIGNAL_ARGS);
>! static void AutoVacSigHupHandler(SIGNAL_ARGS);
>! static void ReqShutdownHandler(SIGNAL_ARGS);
>!
>!
>! /*
>!  * Main entry point for autovacuum sub-process
>   *
>!  * This is invoked from BootstrapMain, which has already created the basic
>!  * execution environment, but not enabled signals yet.
>   */
>! void
>! AutoVacMain(void)
>  {
>!     am_autovac = true;
>
>!     /*
>!      * Properly accept or ignore signals the postmaster might send us
>!      *
>!      * Note: we deliberately ignore SIGTERM, because during a standard Unix
>!      * system shutdown cycle, init will SIGTERM all processes at once.  We
>!      * want to wait for the backends to proc_exit, whereupon the postmaster will
>!      * tell us it's okay to shut down (via SIGUSR2).
>!      *
>!      * SIGUSR1 is presently unused; keep it spare in case someday we want
>!      * this process to participate in sinval messaging.
>!      */
>!     pqsignal(SIGHUP, AutoVacSigHupHandler);    /* set flag to read config file */
>!     pqsignal(SIGTERM, SIG_IGN);            /* ignore SIGTERM */
>!     pqsignal(SIGQUIT, autovac_quickdie);        /* hard crash time */
>!     pqsignal(SIGALRM, SIG_IGN);
>!     pqsignal(SIGPIPE, SIG_IGN);
>!     pqsignal(SIGUSR1, SIG_IGN);            /* reserve for sinval */
>!     pqsignal(SIGUSR2, ReqShutdownHandler);        /* request shutdown */
>
>!     /*
>!      * Reset some signals that are accepted by postmaster but not here
>!      */
>!     pqsignal(SIGCHLD, SIG_DFL);
>!     pqsignal(SIGTTIN, SIG_DFL);
>!     pqsignal(SIGTTOU, SIG_DFL);
>!     pqsignal(SIGCONT, SIG_DFL);
>!     pqsignal(SIGWINCH, SIG_DFL);
>!
>!     /* We allow SIGQUIT (quickdie) at all times */
>! #ifdef HAVE_SIGPROCMASK
>!     sigdelset(&BlockSig, SIGQUIT);
>! #else
>!     BlockSig &= ~(sigmask(SIGQUIT));
>  #endif
>
>!     /*
>!      * If an exception is encountered, processing resumes here.
>!      */
>
>!     /*
>!      * Unblock signals (they were blocked when the postmaster forked us)
>!      */
>!     PG_SETMASK(&UnBlockSig);
>
>!     AutoVacLoop();
>! }
>
>
>! /* --------------------------------
>!  *        signal handler routines
>!  * --------------------------------
>!  */
>
>! /*
>!  * autovac_quickdie() occurs when signalled SIGQUIT by the postmaster.
>!  *
>!  * Some backend has bought the farm,
>!  * so we need to stop what we're doing and proc_exit.
>!  */
>! static void
>! autovac_quickdie(SIGNAL_ARGS)
>! {
>!     PG_SETMASK(&BlockSig);
>
>      /*
>!      * DO NOT proc_exit() -- we're here because shared memory may be
>!      * corrupted, so we don't want to try to clean up our transaction.
>!      * Just nail the windows shut and get out of town.
>!      *
>!      * Note we do exit(1) not exit(0).    This is to force the postmaster into
>!      * a system reset cycle if some idiot DBA sends a manual SIGQUIT to a
>!      * random backend.    This is necessary precisely because we don't clean
>!      * up our shared memory state.
>       */
>!     exit(1);
>! }
>
>! /* SIGHUP: set flag to re-read config file at next convenient time */
>! static void
>! AutoVacSigHupHandler(SIGNAL_ARGS)
>! {
>!     got_SIGHUP = true;
>! }
>
>! /* SIGUSR2: set flag to run a shutdown checkpoint and exit */
>! static void
>! ReqShutdownHandler(SIGNAL_ARGS)
>! {
>!     shutdown_requested = true;
>  }
>
>+
>  /* Set thresholds = base_value + scaling_factor * reltuples
>     Should be called after a vacuum since vacuum updates values in pg_class */
>! void
>! update_table_thresholds(db_info * dbi, Oid table_oid, int vacuum_type)
>  {
>      int            disconnect = 0;
>!     char        query[255];
>
>      if (dbi->conn == NULL)
>      {
>***************
>*** 157,215 ****
>
>      if (dbi->conn != NULL)
>      {
>!         snprintf(query, sizeof(query), PAGES_QUERY, tbl->relid);
>!         res = send_query(query, dbi);
>!         if (res != NULL)
>          {
>!             tbl->reltuples =
>!                 atof(PQgetvalue(res, 0, PQfnumber(res, "reltuples")));
>!             tbl->relpages = atooid(PQgetvalue(res, 0, PQfnumber(res, "relpages")));
>!
>!             /*
>!              * update vacuum thresholds only of we just did a vacuum
>!              * analyze
>!              */
>!             if (vacuum_type == VACUUM_ANALYZE)
>!             {
>!                 tbl->vacuum_threshold =
>!                     (args->vacuum_base_threshold + args->vacuum_scaling_factor * tbl->reltuples);
>!                 tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
>!             }
>!
>!             /* update analyze thresholds */
>!             tbl->analyze_threshold =
>!                 (args->analyze_base_threshold + args->analyze_scaling_factor * tbl->reltuples);
>!             tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
>!
>!             PQclear(res);
>!
>!             /*
>!              * If the stats collector is reporting fewer updates then we
>!              * have on record then the stats were probably reset, so we
>!              * need to reset also
>!              */
>!             if ((tbl->curr_analyze_count < tbl->CountAtLastAnalyze) ||
>!                 (tbl->curr_vacuum_count < tbl->CountAtLastVacuum))
>!             {
>!                 tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
>!                 tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
>!             }
>          }
>      }
>      if (disconnect)
>          db_disconnect(dbi);
>  }
>
>! static void
>! update_table_list(db_info * dbi)
>  {
>      int            disconnect = 0;
>!     PGresult   *res = NULL;
>!     tbl_info   *tbl = NULL;
>!     Dlelem       *tbl_elem = DLGetHead(dbi->table_list);
>!     int            i = 0,
>!                 t = 0,
>!                 found_match = 0;
>
>      if (dbi->conn == NULL)
>      {
>--- 192,244 ----
>
>      if (dbi->conn != NULL)
>      {
>!         /*
>!         * update vacuum and analyze thresholds if
>!         * we did a vacuum analyze
>!         */
>!         if (vacuum_type == VACUUM_ANALYZE)
>          {
>!             sprintf(query, "update pg_autovacuum set cnt_at_last_analyze = n_tup_ins + n_tup_upd, cnt_at_last_vacuum
=n_tup_upd + n_tup_del from pg_stat_all_tables where table_oid = relid and relid = %u", table_oid); 
>!             send_query(query, dbi);
>          }
>+         /*
>+             * update only the analyze thresholds  if
>+             * we only did an analyze
>+             */
>+         else
>+         {
>+             sprintf(query, "update pg_autovacuum set cnt_at_last_analyze = n_tup_ins + n_tup_upd from
pg_stat_all_tableswhere table_oid = relid and relid = %u", table_oid); 
>+             send_query(query, dbi);
>+         }
>+
>+         /* FIXME: Need to think about this and pg_stat roll over issues */
>+         /*
>+          * If the stats collector is reporting fewer updates then we
>+          * have on record then the stats were probably reset, or overflowed,
>+          * so we need to reset also our numbers also
>+          */
>+ /*        if ((tbl->curr_analyze_count < tbl->CountAtLastAnalyze) ||
>+             (tbl->curr_vacuum_count < tbl->CountAtLastVacuum))
>+         {
>+             tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
>+             tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
>+         }*/
>      }
>      if (disconnect)
>          db_disconnect(dbi);
>  }
>
>! void
>! update_pg_autovacuum_table(db_info *dbi)
>  {
>+     /*
>+      * This function will update the pg_autovacuum system table in two steps:
>+      * 1) Delete entries that are no longer in pg_class an
>+      * 2) Add zero'd out entries to the pg_autovacuum table for new tables that exist in pg_class not in
pg_autovacuum
>+      */
>+
>      int            disconnect = 0;
>!     char        query[4096];
>
>      if (dbi->conn == NULL)
>      {
>***************
>*** 219,394 ****
>
>      if (dbi->conn != NULL)
>      {
>          /*
>!          * Get a result set that has all the information we will need to
>!          * both remove tables from the list that no longer exist and add
>!          * tables to the list that are new
>           */
>!         res = send_query((char *) TABLE_STATS_QUERY, dbi);
>!         if (res != NULL)
>!         {
>!             t = PQntuples(res);
>!
>!             /*
>!             * First: use the tbl_list as the outer loop and the result set as
>!             * the inner loop, this will determine what tables should be
>!             * removed
>!             */
>!             while (tbl_elem != NULL)
>!             {
>!                 tbl = ((tbl_info *) DLE_VAL(tbl_elem));
>!                 found_match = 0;
>!
>!                 for (i = 0; i < t; i++)
>!                 {                    /* loop through result set looking for a
>!                                     * match */
>!                     if (tbl->relid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
>!                     {
>!                         found_match = 1;
>!                         break;
>!                     }
>!                 }
>!                 if (found_match == 0)
>!                 {                    /* then we didn't find this tbl_elem in
>!                                     * the result set */
>!                     Dlelem       *elem_to_remove = tbl_elem;
>!
>!                     tbl_elem = DLGetSucc(tbl_elem);
>!                     remove_table_from_list(elem_to_remove);
>!                 }
>!                 else
>!                     tbl_elem = DLGetSucc(tbl_elem);
>!             }                        /* Done removing dropped tables from the
>!                                     * table_list */
>!
>!             /*
>!             * Then loop use result set as outer loop and tbl_list as the
>!             * inner loop to determine what tables are new
>!             */
>!             for (i = 0; i < t; i++)
>!             {
>!                 tbl_elem = DLGetHead(dbi->table_list);
>!                 found_match = 0;
>!                 while (tbl_elem != NULL)
>!                 {
>!                     tbl = ((tbl_info *) DLE_VAL(tbl_elem));
>!                     if (tbl->relid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
>!                     {
>!                         found_match = 1;
>!                         break;
>!                     }
>!                     tbl_elem = DLGetSucc(tbl_elem);
>!                 }
>!                 if (found_match == 0)        /* then we didn't find this result
>!                                             * now in the tbl_list */
>!                 {
>!                     DLAddTail(dbi->table_list, DLNewElem(init_table_info(res, i, dbi)));
>!                     if (args->debug >= 1)
>!                     {
>!                         sprintf(logbuffer, "added table: %s.%s", dbi->dbname,
>!                                 ((tbl_info *) DLE_VAL(DLGetTail(dbi->table_list)))->table_name);
>!                         log_entry(logbuffer);
>!                     }
>!                 }
>!             }                        /* end of for loop that adds tables */
>!         }
>!         fflush(LOGOUTPUT);
>!         PQclear(res);
>!         res = NULL;
>!         if (args->debug >= 3)
>!             print_table_list(dbi->table_list);
>!         if (disconnect)
>!             db_disconnect(dbi);
>      }
>  }
>
>- /* Free memory, and remove the node from the list */
>- static void
>- remove_table_from_list(Dlelem *tbl_to_remove)
>- {
>-     tbl_info   *tbl = ((tbl_info *) DLE_VAL(tbl_to_remove));
>-
>-     if (args->debug >= 1)
>-     {
>-         sprintf(logbuffer, "Removing table: %s from list.", tbl->table_name);
>-         log_entry(logbuffer);
>-         fflush(LOGOUTPUT);
>-     }
>-     DLRemove(tbl_to_remove);
>-
>-     if (tbl->schema_name)
>-     {
>-         free(tbl->schema_name);
>-         tbl->schema_name = NULL;
>-     }
>-     if (tbl->table_name)
>-     {
>-         free(tbl->table_name);
>-         tbl->table_name = NULL;
>-     }
>-     if (tbl)
>-     {
>-         free(tbl);
>-         tbl = NULL;
>-     }
>-     DLFreeElem(tbl_to_remove);
>- }
>-
>- /* Free the entire table list */
>- static void
>- free_tbl_list(Dllist *tbl_list)
>- {
>-     Dlelem       *tbl_elem = DLGetHead(tbl_list);
>-     Dlelem       *tbl_elem_to_remove = NULL;
>-
>-     while (tbl_elem != NULL)
>-     {
>-         tbl_elem_to_remove = tbl_elem;
>-         tbl_elem = DLGetSucc(tbl_elem);
>-         remove_table_from_list(tbl_elem_to_remove);
>-     }
>-     DLFreeList(tbl_list);
>- }
>-
>- static void
>- print_table_list(Dllist *table_list)
>- {
>-     Dlelem       *table_elem = DLGetHead(table_list);
>-
>-     while (table_elem != NULL)
>-     {
>-         print_table_info(((tbl_info *) DLE_VAL(table_elem)));
>-         table_elem = DLGetSucc(table_elem);
>-     }
>- }
>-
>- static void
>- print_table_info(tbl_info * tbl)
>- {
>-     sprintf(logbuffer, "  table name: %s.%s", tbl->dbi->dbname, tbl->table_name);
>-     log_entry(logbuffer);
>-     sprintf(logbuffer, "     relid: %u;   relisshared: %i", tbl->relid, tbl->relisshared);
>-     log_entry(logbuffer);
>-     sprintf(logbuffer, "     reltuples: %f;  relpages: %u", tbl->reltuples, tbl->relpages);
>-     log_entry(logbuffer);
>-     sprintf(logbuffer, "     curr_analyze_count: %li; curr_vacuum_count: %li",
>-             tbl->curr_analyze_count, tbl->curr_vacuum_count);
>-     log_entry(logbuffer);
>-     sprintf(logbuffer, "     last_analyze_count: %li; last_vacuum_count: %li",
>-             tbl->CountAtLastAnalyze, tbl->CountAtLastVacuum);
>-     log_entry(logbuffer);
>-     sprintf(logbuffer, "     analyze_threshold: %li; vacuum_threshold: %li",
>-             tbl->analyze_threshold, tbl->vacuum_threshold);
>-     log_entry(logbuffer);
>-     fflush(LOGOUTPUT);
>- }
>
>  /* End of table Management Functions */
>
>  /* Beginning of DB Management Functions */
>
>  /* init_db_list() creates the db_list and initalizes template1 */
>! static Dllist *
>  init_db_list()
>  {
>      Dllist       *db_list = DLNewList();
>--- 248,276 ----
>
>      if (dbi->conn != NULL)
>      {
>+         /*
>+          * Delete entries in pg_autovacuum that are no longer in pg_class
>+          */
>+         send_query("DELETE from pg_autovacuum where table_oid not in (select relid from pg_stat_all_tables )",dbi);
>+
>          /*
>!          * Insert entires into pg_autovacuum for new tables (ones that exist in pg_class / pg_stat.., but not in
pg_autovacuum) 
>!          * and fill in defaut values for these new tables.
>!          * then add them to the table list.
>           */
>!         sprintf(query, "insert into pg_autovacuum (table_oid, analyze_base_threshold, vacuum_base_threshold,
analyze_scaling_factor,vacuum_scaling_factor, analyze_threshold, vacuum_threshold, cnt_at_last_analyze,
cnt_at_last_vacuum)  select a.oid, current_setting('autovacuum_analyze_threshold_base')::int,
current_setting('autovacuum_vacuum_threshold_base')::int,current_setting('autovacuum_analyze_threshold_sf')::float,
current_setting('autovacuum_vacuum_threshold_sf')::float,-1, -1, 0, 0 from pg_class a inner join pg_stat_all_tables b
ona.oid=b.relid left outer join pg_autovacuum c on a.oid = c.table_oid where a.relkind = 'r' and schemaname not like
'pg_temp_%%'and a.oid not in (select distinct table_oid from pg_autovacuum)");  
>!         elog(DEBUG5, query);
>!         send_query(query,dbi);
>      }
>  }
>
>
>  /* End of table Management Functions */
>
>  /* Beginning of DB Management Functions */
>
>  /* init_db_list() creates the db_list and initalizes template1 */
>! Dllist *
>  init_db_list()
>  {
>      Dllist       *db_list = DLNewList();
>***************
>*** 398,405 ****
>      DLAddHead(db_list, DLNewElem(init_dbinfo((char *) "template1", 0, 0)));
>      if (DLGetHead(db_list) == NULL)
>      {                            /* Make sure init_dbinfo was successful */
>!         log_entry("init_db_list(): Error creating db_list for db: template1.");
>!         fflush(LOGOUTPUT);
>          return NULL;
>      }
>
>--- 280,287 ----
>      DLAddHead(db_list, DLNewElem(init_dbinfo((char *) "template1", 0, 0)));
>      if (DLGetHead(db_list) == NULL)
>      {                            /* Make sure init_dbinfo was successful */
>!         ereport(ERROR,(errmsg("pg_autovacuum: Error initializing db_list")));
>!
>          return NULL;
>      }
>
>***************
>*** 419,427 ****
>              dbs->age = atol(PQgetvalue(res, 0, PQfnumber(res, "age")));
>              if (res)
>                  PQclear(res);
>-
>-             if (args->debug >= 2)
>-                 print_db_list(db_list, 0);
>          }
>          else
>              return NULL;
>--- 301,306 ----
>***************
>*** 431,470 ****
>
>  /* Simple function to create an instance of the dbinfo struct
>      Initalizes all the pointers and connects to the database  */
>! static db_info *
>  init_dbinfo(char *dbname, Oid oid, long age)
>  {
>      db_info    *newdbinfo = (db_info *) malloc(sizeof(db_info));
>
>-     newdbinfo->analyze_threshold = args->vacuum_base_threshold;
>-     newdbinfo->vacuum_threshold = args->analyze_base_threshold;
>      newdbinfo->dbname = (char *) malloc(strlen(dbname) + 1);
>      strcpy(newdbinfo->dbname, dbname);
>      newdbinfo->username = NULL;
>!     if (args->user != NULL)
>      {
>!         newdbinfo->username = (char *) malloc(strlen(args->user) + 1);
>!         strcpy(newdbinfo->username, args->user);
>      }
>      newdbinfo->password = NULL;
>!     if (args->password != NULL)
>      {
>!         newdbinfo->password = (char *) malloc(strlen(args->password) + 1);
>!         strcpy(newdbinfo->password, args->password);
>      }
>      newdbinfo->oid = oid;
>      newdbinfo->age = age;
>-     newdbinfo->table_list = DLNewList();
>      newdbinfo->conn = NULL;
>
>-     if (args->debug >= 2)
>-         print_table_list(newdbinfo->table_list);
>-
>      return newdbinfo;
>  }
>
>  /* Function adds and removes databases from the db_list as appropriate */
>! static void
>  update_db_list(Dllist *db_list)
>  {
>      int            disconnect = 0;
>--- 310,345 ----
>
>  /* Simple function to create an instance of the dbinfo struct
>      Initalizes all the pointers and connects to the database  */
>! db_info *
>  init_dbinfo(char *dbname, Oid oid, long age)
>  {
>      db_info    *newdbinfo = (db_info *) malloc(sizeof(db_info));
>
>      newdbinfo->dbname = (char *) malloc(strlen(dbname) + 1);
>      strcpy(newdbinfo->dbname, dbname);
>      newdbinfo->username = NULL;
>!     if (pg_user != NULL)
>      {
>!         newdbinfo->username = (char *) malloc(strlen(pg_user) + 1);
>!         strcpy(newdbinfo->username, pg_user);
>      }
>+
>      newdbinfo->password = NULL;
>!     if (pg_user_password != NULL)
>      {
>!         newdbinfo->password = (char *) malloc(strlen(pg_user_password) + 1);
>!         strcpy(newdbinfo->password, pg_user_password);
>      }
>+
>      newdbinfo->oid = oid;
>      newdbinfo->age = age;
>      newdbinfo->conn = NULL;
>
>      return newdbinfo;
>  }
>
>  /* Function adds and removes databases from the db_list as appropriate */
>! void
>  update_db_list(Dllist *db_list)
>  {
>      int            disconnect = 0;
>***************
>*** 476,486 ****
>                  t = 0,
>                  found_match = 0;
>
>!     if (args->debug >= 2)
>!     {
>!         log_entry("updating the database list");
>!         fflush(LOGOUTPUT);
>!     }
>
>      if (dbi_template1->conn == NULL)
>      {
>--- 351,357 ----
>                  t = 0,
>                  found_match = 0;
>
>!     ereport(DEBUG2,(errmsg("pg_autovacuum: updating the database list")));
>
>      if (dbi_template1->conn == NULL)
>      {
>***************
>*** 563,581 ****
>                              (PQgetvalue(res, i, PQfnumber(res, "datname")),
>                              atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))),
>                          atol(PQgetvalue(res, i, PQfnumber(res, "age"))))));
>!                     if (args->debug >= 1)
>!                     {
>!                         sprintf(logbuffer, "added database: %s", ((db_info *) DLE_VAL(DLGetTail(db_list)))->dbname);
>!                         log_entry(logbuffer);
>!                     }
>                  }
>!             }                        /* end of for loop that adds tables */
>          }
>-         fflush(LOGOUTPUT);
>          PQclear(res);
>          res = NULL;
>!         if (args->debug >= 3)
>!             print_db_list(db_list, 0);
>          if (disconnect)
>              db_disconnect(dbi_template1);
>      }
>--- 434,446 ----
>                              (PQgetvalue(res, i, PQfnumber(res, "datname")),
>                              atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))),
>                          atol(PQgetvalue(res, i, PQfnumber(res, "age"))))));
>!                     ereport(DEBUG1,(errmsg("pg_autovacuum: added database %s", ((db_info *)
DLE_VAL(DLGetTail(db_list)))->dbname)));
>                  }
>!             }    /* end of for loop that adds tables */
>          }
>          PQclear(res);
>          res = NULL;
>!         print_db_list(db_list, 0);
>          if (disconnect)
>              db_disconnect(dbi_template1);
>      }
>***************
>*** 595,601 ****
>  return 0 if nothing happened,
>  return 1 if the database needed a database wide vacuum
>  */
>! static int
>  xid_wraparound_check(db_info * dbi)
>  {
>      /*
>--- 460,466 ----
>  return 0 if nothing happened,
>  return 1 if the database needed a database wide vacuum
>  */
>! int
>  xid_wraparound_check(db_info * dbi)
>  {
>      /*
>***************
>*** 620,636 ****
>  }
>
>  /* Close DB connection, free memory, and remove the node from the list */
>! static void
>  remove_db_from_list(Dlelem *db_to_remove)
>  {
>      db_info    *dbi = ((db_info *) DLE_VAL(db_to_remove));
>
>!     if (args->debug >= 1)
>!     {
>!         sprintf(logbuffer, "Removing db: %s from list.", dbi->dbname);
>!         log_entry(logbuffer);
>!         fflush(LOGOUTPUT);
>!     }
>      DLRemove(db_to_remove);
>      if (dbi->conn)
>          db_disconnect(dbi);
>--- 485,497 ----
>  }
>
>  /* Close DB connection, free memory, and remove the node from the list */
>! void
>  remove_db_from_list(Dlelem *db_to_remove)
>  {
>      db_info    *dbi = ((db_info *) DLE_VAL(db_to_remove));
>
>!     ereport(DEBUG1,(errmsg("pg_autovacuum: Removing db: %s from list.", dbi->dbname)));
>!
>      DLRemove(db_to_remove);
>      if (dbi->conn)
>          db_disconnect(dbi);
>***************
>*** 649,659 ****
>          free(dbi->password);
>          dbi->password = NULL;
>      }
>-     if (dbi->table_list)
>-     {
>-         free_tbl_list(dbi->table_list);
>-         dbi->table_list = NULL;
>-     }
>      if (dbi)
>      {
>          free(dbi);
>--- 510,515 ----
>***************
>*** 664,670 ****
>
>  /* Function is called before program exit to free all memory
>          mostly it's just to keep valgrind happy */
>! static void
>  free_db_list(Dllist *db_list)
>  {
>      Dlelem       *db_elem = DLGetHead(db_list);
>--- 520,526 ----
>
>  /* Function is called before program exit to free all memory
>          mostly it's just to keep valgrind happy */
>! void
>  free_db_list(Dllist *db_list)
>  {
>      Dlelem       *db_elem = DLGetHead(db_list);
>***************
>*** 680,686 ****
>      DLFreeList(db_list);
>  }
>
>! static void
>  print_db_list(Dllist *db_list, int print_table_lists)
>  {
>      Dlelem       *db_elem = DLGetHead(db_list);
>--- 536,542 ----
>      DLFreeList(db_list);
>  }
>
>! void
>  print_db_list(Dllist *db_list, int print_table_lists)
>  {
>      Dlelem       *db_elem = DLGetHead(db_list);
>***************
>*** 692,726 ****
>      }
>  }
>
>! static void
>  print_db_info(db_info * dbi, int print_tbl_list)
>  {
>!     sprintf(logbuffer, "dbname: %s", (dbi->dbname) ? dbi->dbname : "(null)");
>!     log_entry(logbuffer);
>!
>!     sprintf(logbuffer, "  oid: %u", dbi->oid);
>!     log_entry(logbuffer);
>!
>!     sprintf(logbuffer, "  username: %s", (dbi->username) ? dbi->username : "(null)");
>!     log_entry(logbuffer);
>!
>!     sprintf(logbuffer, "  password: %s", (dbi->password) ? dbi->password : "(null)");
>!     log_entry(logbuffer);
>
>      if (dbi->conn != NULL)
>!         log_entry("  conn is valid, (connected)");
>      else
>!         log_entry("  conn is null, (not connected)");
>!
>!     sprintf(logbuffer, "  default_analyze_threshold: %li", dbi->analyze_threshold);
>!     log_entry(logbuffer);
>!
>!     sprintf(logbuffer, "  default_vacuum_threshold: %li", dbi->vacuum_threshold);
>!     log_entry(logbuffer);
>!
>!     fflush(LOGOUTPUT);
>!     if (print_tbl_list > 0)
>!         print_table_list(dbi->table_list);
>  }
>
>  /* End of DB List Management Function */
>--- 548,565 ----
>      }
>  }
>
>! void
>  print_db_info(db_info * dbi, int print_tbl_list)
>  {
>!     ereport(DEBUG3,(errmsg("pg_autovacuum: dbname = %s", (dbi->dbname) ? dbi->dbname : "(null)")));
>!     ereport(DEBUG3,(errmsg("  oid: %u", dbi->oid)));
>!     ereport(DEBUG3,(errmsg("  username: %s", (dbi->username) ? dbi->username : "(null)")));
>!     ereport(DEBUG3,(errmsg("  password: %s", (dbi->password) ? dbi->password : "(null)")));
>
>      if (dbi->conn != NULL)
>!         ereport(DEBUG3,(errmsg("  conn is valid, (connected)")));
>      else
>!         ereport(DEBUG3,(errmsg("  conn is null, (not connected)")));
>  }
>
>  /* End of DB List Management Function */
>***************
>*** 728,753 ****
>  /* Beginning of misc Functions */
>
>  /* Perhaps add some test to this function to make sure that the stats we need are available */
>! static PGconn *
>  db_connect(db_info * dbi)
>  {
>      PGconn       *db_conn =
>!     PQsetdbLogin(args->host, args->port, NULL, NULL, dbi->dbname,
>                   dbi->username, dbi->password);
>
>      if (PQstatus(db_conn) != CONNECTION_OK)
>      {
>!         sprintf(logbuffer, "Failed connection to database %s with error: %s.",
>!                 dbi->dbname, PQerrorMessage(db_conn));
>!         log_entry(logbuffer);
>!         fflush(LOGOUTPUT);
>          PQfinish(db_conn);
>          db_conn = NULL;
>      }
>      return db_conn;
>  }    /* end of db_connect() */
>
>! static void
>  db_disconnect(db_info * dbi)
>  {
>      if (dbi->conn != NULL)
>--- 567,590 ----
>  /* Beginning of misc Functions */
>
>  /* Perhaps add some test to this function to make sure that the stats we need are available */
>! PGconn *
>  db_connect(db_info * dbi)
>  {
>      PGconn       *db_conn =
>!     PQsetdbLogin(NULL,NULL, NULL, NULL, dbi->dbname,
>                   dbi->username, dbi->password);
>
>      if (PQstatus(db_conn) != CONNECTION_OK)
>      {
>!         ereport(LOG,(errmsg("pg_autovacuum: Failed connection to database %s with error: %s.",
>!                 dbi->dbname, PQerrorMessage(db_conn))));
>          PQfinish(db_conn);
>          db_conn = NULL;
>      }
>      return db_conn;
>  }    /* end of db_connect() */
>
>! void
>  db_disconnect(db_info * dbi)
>  {
>      if (dbi->conn != NULL)
>***************
>*** 757,778 ****
>      }
>  }
>
>! static int
>! check_stats_enabled(db_info * dbi)
>! {
>!     PGresult   *res;
>!     int            ret = 0;
>!
>!     res = send_query("SHOW stats_row_level", dbi);
>!     if (res != NULL)
>!     {
>!         ret = strcmp("on", PQgetvalue(res, 0, PQfnumber(res, "stats_row_level")));
>!         PQclear(res);
>!     }
>!     return ret;
>! }
>!
>! static PGresult *
>  send_query(const char *query, db_info * dbi)
>  {
>      PGresult   *res;
>--- 594,600 ----
>      }
>  }
>
>! PGresult *
>  send_query(const char *query, db_info * dbi)
>  {
>      PGresult   *res;
>***************
>*** 780,811 ****
>      if (dbi->conn == NULL)
>          return NULL;
>
>!     if (args->debug >= 4)
>!         log_entry(query);
>
>      res = PQexec(dbi->conn, query);
>
>      if (!res)
>      {
>!         sprintf(logbuffer,
>!            "Fatal error occured while sending query (%s) to database %s",
>!                 query, dbi->dbname);
>!         log_entry(logbuffer);
>!         sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
>!         log_entry(logbuffer);
>!         fflush(LOGOUTPUT);
>          return NULL;
>      }
>      if (PQresultStatus(res) != PGRES_TUPLES_OK &&
>          PQresultStatus(res) != PGRES_COMMAND_OK)
>      {
>!         sprintf(logbuffer,
>!           "Can not refresh statistics information from the database %s.",
>!                 dbi->dbname);
>!         log_entry(logbuffer);
>!         sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
>!         log_entry(logbuffer);
>!         fflush(LOGOUTPUT);
>          PQclear(res);
>          return NULL;
>      }
>--- 602,622 ----
>      if (dbi->conn == NULL)
>          return NULL;
>
>!     elog(DEBUG5, query);
>
>      res = PQexec(dbi->conn, query);
>
>      if (!res)
>      {
>!         ereport(ERROR,(errmsg("pg_autovacuum: Fatal error occured while sending query (%s) to database %s; The error
is[%s]", 
>!                 query, dbi->dbname, PQresultErrorMessage(res))));
>          return NULL;
>      }
>      if (PQresultStatus(res) != PGRES_TUPLES_OK &&
>          PQresultStatus(res) != PGRES_COMMAND_OK)
>      {
>!         ereport(ERROR,(errmsg("pg_autovacuum: Fatal error occured while sending query (%s) to database %s; The error
is[%s]", 
>!                 query, dbi->dbname, PQresultErrorMessage(res))));
>          PQclear(res);
>          return NULL;
>      }
>***************
>*** 813,1055 ****
>  }    /* End of send_query() */
>
>
>  static void
>! free_cmd_args()
>  {
>!     if (args != NULL)
>      {
>!         if (args->user != NULL)
>!             free(args->user);
>!         if (args->password != NULL)
>!             free(args->password);
>!         free(args);
>      }
>! }
>!
>! static cmd_args *
>! get_cmd_args(int argc, char *argv[])
>! {
>!     int            c;
>
>!     args = (cmd_args *) malloc(sizeof(cmd_args));
>!     args->sleep_base_value = SLEEPBASEVALUE;
>!     args->sleep_scaling_factor = SLEEPSCALINGFACTOR;
>!     args->vacuum_base_threshold = VACBASETHRESHOLD;
>!     args->vacuum_scaling_factor = VACSCALINGFACTOR;
>!     args->analyze_base_threshold = -1;
>!     args->analyze_scaling_factor = -1;
>!     args->debug = AUTOVACUUM_DEBUG;
>!     args->daemonize = 0;
>!     args->user = 0;
>!     args->password = 0;
>!     args->host = 0;
>!     args->logfile = 0;
>!     args->port = 0;
>
>!     /*
>!      * Fixme: Should add some sanity checking such as positive integer
>!      * values etc
>!      */
>!     while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hD")) != -1)
>      {
>!         switch (c)
>!         {
>!             case 's':
>!                 args->sleep_base_value = atoi(optarg);
>!                 break;
>!             case 'S':
>!                 args->sleep_scaling_factor = atof(optarg);
>!                 break;
>!             case 'v':
>!                 args->vacuum_base_threshold = atoi(optarg);
>!                 break;
>!             case 'V':
>!                 args->vacuum_scaling_factor = atof(optarg);
>!                 break;
>!             case 'a':
>!                 args->analyze_base_threshold = atoi(optarg);
>!                 break;
>!             case 'A':
>!                 args->analyze_scaling_factor = atof(optarg);
>!                 break;
>!             case 'D':
>!                 args->daemonize++;
>!                 break;
>!             case 'd':
>!                 args->debug = atoi(optarg);
>!                 break;
>!             case 'U':
>!                 args->user = optarg;
>!                 break;
>!             case 'P':
>!                 args->password = optarg;
>!                 break;
>!             case 'H':
>!                 args->host = optarg;
>!                 break;
>!             case 'L':
>!                 args->logfile = optarg;
>!                 break;
>!             case 'p':
>!                 args->port = optarg;
>!                 break;
>!             case 'h':
>!                 usage();
>!                 exit(0);
>!             default:
>!
>!                 /*
>!                  * It's here that we know that things are invalid... It is
>!                  * not forcibly an error to call usage
>!                  */
>!                 fprintf(stderr, "Error: Invalid Command Line Options.\n");
>!                 usage();
>!                 exit(1);
>!                 break;
>!         }
>
>!         /*
>!          * if values for insert thresholds are not specified, then they
>!          * default to 1/2 of the delete values
>!          */
>!         if (args->analyze_base_threshold == -1)
>!             args->analyze_base_threshold = args->vacuum_base_threshold / 2;
>!         if (args->analyze_scaling_factor == -1)
>!             args->analyze_scaling_factor = args->vacuum_scaling_factor / 2;
>      }
>!     return args;
>! }
>
>! static void
>! usage()
>! {
>!     int            i = 0;
>!     float        f = 0;
>
>!     fprintf(stderr, "usage: pg_autovacuum \n");
>!     fprintf(stderr, "   [-D] Daemonize (Detach from tty and run in the background)\n");
>!     i = AUTOVACUUM_DEBUG;
>!     fprintf(stderr, "   [-d] debug (debug level=0,1,2,3; default=%i)\n", i);
>!
>!     i = SLEEPBASEVALUE;
>!     fprintf(stderr, "   [-s] sleep base value (default=%i)\n", i);
>!     f = SLEEPSCALINGFACTOR;
>!     fprintf(stderr, "   [-S] sleep scaling factor (default=%f)\n", f);
>!
>!     i = VACBASETHRESHOLD;
>!     fprintf(stderr, "   [-v] vacuum base threshold (default=%i)\n", i);
>!     f = VACSCALINGFACTOR;
>!     fprintf(stderr, "   [-V] vacuum scaling factor (default=%f)\n", f);
>!     i = i / 2;
>!     fprintf(stderr, "   [-a] analyze base threshold (default=%i)\n", i);
>!     f = f / 2;
>!     fprintf(stderr, "   [-A] analyze scaling factor (default=%f)\n", f);
>!
>!     fprintf(stderr, "   [-L] logfile (default=none)\n");
>!
>!     fprintf(stderr, "   [-U] username (libpq default)\n");
>!     fprintf(stderr, "   [-P] password (libpq default)\n");
>!     fprintf(stderr, "   [-H] host (libpq default)\n");
>!     fprintf(stderr, "   [-p] port (libpq default)\n");
>
>!     fprintf(stderr, "   [-h] help (Show this output)\n");
>! }
>
>! static void
>! print_cmd_args()
>! {
>!     sprintf(logbuffer, "Printing command_args");
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->host=%s", (args->host) ? args->host : "(null)");
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->port=%s", (args->port) ? args->port : "(null)");
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->username=%s", (args->user) ? args->user : "(null)");
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->password=%s", (args->password) ? args->password : "(null)");
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->logfile=%s", (args->logfile) ? args->logfile : "(null)");
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->daemonize=%i", args->daemonize);
>!     log_entry(logbuffer);
>!
>!     sprintf(logbuffer, "  args->sleep_base_value=%i", args->sleep_base_value);
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->sleep_scaling_factor=%f", args->sleep_scaling_factor);
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->vacuum_base_threshold=%i", args->vacuum_base_threshold);
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->vacuum_scaling_factor=%f", args->vacuum_scaling_factor);
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->analyze_base_threshold=%i", args->analyze_base_threshold);
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->analyze_scaling_factor=%f", args->analyze_scaling_factor);
>!     log_entry(logbuffer);
>!     sprintf(logbuffer, "  args->debug=%i", args->debug);
>!     log_entry(logbuffer);
>
>-     fflush(LOGOUTPUT);
>  }
>
>  /* Beginning of AutoVacuum Main Program */
>! int
>! main(int argc, char *argv[])
>  {
>      char        buf[256];
>      int            j = 0,
>                  loops = 0;
>-
>-     /* int numInserts, numDeletes, */
>      int            sleep_secs;
>      Dllist       *db_list;
>!     Dlelem       *db_elem,
>!                *tbl_elem;
>      db_info    *dbs;
>-     tbl_info   *tbl;
>      PGresult   *res = NULL;
>      double        diff;
>      struct timeval now,
>                  then;
>!
>!     args = get_cmd_args(argc, argv);    /* Get Command Line Args and put
>!                                          * them in the args struct */
>!
>!     /* Dameonize if requested */
>!     if (args->daemonize == 1)
>!         daemonize();
>!
>!     if (args->logfile)
>!     {
>!         LOGOUTPUT = fopen(args->logfile, "a");
>!         if (!LOGOUTPUT)
>!         {
>!             fprintf(stderr, "Could not open log file - [%s]\n", args->logfile);
>!             exit(-1);
>!         }
>!     }
>!     else
>!         LOGOUTPUT = stderr;
>!     if (args->debug >= 2)
>!         print_cmd_args();
>!
>      /* Init the db list with template1 */
>      db_list = init_db_list();
>      if (db_list == NULL)
>!         return 1;
>
>-     if (check_stats_enabled(((db_info *) DLE_VAL(DLGetHead(db_list)))) != 0)
>-     {
>-         log_entry("Error: GUC variable stats_row_level must be enabled.");
>-         log_entry("       Please fix the problems and try again.");
>-         fflush(LOGOUTPUT);
>-
>-         exit(1);
>-     }
>-
>      gettimeofday(&then, 0);        /* for use later to caluculate sleep time */
>
>      while (1)
>!     {                            /* Main Loop */
>          db_elem = DLGetHead(db_list);    /* Reset cur_db_node to the
>                                           * beginning of the db_list */
>
>--- 624,768 ----
>  }    /* End of send_query() */
>
>
>+ /* Get a password from the password file. */
>  static void
>! GetAutovacuumUserAndPasswordFromFile()
>  {
>!     FILE        *fp;
>! #define LINELEN NAMEDATALEN*2
>!     char        buf[LINELEN];
>!     char        *pgpassfile,
>!                 *t = buf,
>!                 *username,
>!                 *password;
>!     int            len;
>!     struct stat stat_buf;
>!
>!
>!     pgpassfile = malloc(strlen(DataDir) + 1 + strlen(AUTOVACPASSFILE) + 1);
>!     if (!pgpassfile)
>      {
>!         printf("out of memory");
>!         return;
>      }
>!     elog(DEBUG3,"pg_autovacuum: autovac.passwd should be: %s/%s", DataDir, AUTOVACPASSFILE);
>
>!     sprintf(pgpassfile, "%s/%s", DataDir, AUTOVACPASSFILE);
>
>!     /* If password file cannot be opened, ignore it. */
>!     if (stat(pgpassfile, &stat_buf) == -1)
>      {
>!         elog(DEBUG3,"pg_autovacuum: %s cannot be opened",pgpassfile);
>!         free(pgpassfile);
>!         return;
>!     }
>
>! #ifndef WIN32
>!     /* If password file is insecure, alert the user and ignore it. */
>!     if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
>!     {
>!         ereport(WARNING,(errmsg("pg_autovacuum: Password file %s has world or group read access; permission should
beu=rw (0600)",pgpassfile))); 
>!         free(pgpassfile);
>!         return;
>      }
>! #endif
>
>!     fp = fopen(pgpassfile, "r");
>!     free(pgpassfile);
>!     if (fp == NULL)
>!         return;
>
>!     fgets(buf, LINELEN - 1, fp);
>
>!     len = strlen(buf);
>!     if (len < 3)
>!         return;
>!
>!     /* Remove trailing newline */
>!     if (buf[len - 1] == '\n')
>!         buf[len - 1] = 0;
>
>!     username = t; /* Password is always first */
>!     if(*username == ':')
>!         username = NULL;
>!
>!     password = NULL;
>!     while(*t != NULL)     /* Find : since it marks the beginning of the password */
>!     {
>!         if (*(t+1) == ':' )
>!         {
>!             *(t+1) = NULL;
>!             password = t+2;
>!         }
>!         t++;
>!     }
>!
>!     ereport(DEBUG3,(errmsg("pg_autovacuum: username from autovac.passwd file is: %s ",username)));
>!     fclose(fp);
>!
>!     /* Now if we have anything for usename and password we set the pg_user and pg_passwd globals */
>!     if((username != NULL) && (strlen(username) > 1) && (strlen(username) < NAMEDATALEN))
>!     {
>!         pg_user = malloc(strlen(username) + 1);
>!         strcpy(pg_user,username);
>!     }
>!     if((password != NULL) && (strlen(password) > 1) && (strlen(password) < NAMEDATALEN))
>!     {
>!         pg_user_password = malloc(strlen(password) + 1);
>!         strcpy(pg_user_password,password);
>!     }
>!     return;
>!
>! #undef LINELEN
>
>  }
>
>+
>  /* Beginning of AutoVacuum Main Program */
>! void AutoVacLoop(void)
>  {
>      char        buf[256];
>      int            j = 0,
>                  loops = 0;
>      int            sleep_secs;
>      Dllist       *db_list;
>!     Dlelem       *db_elem;
>      db_info    *dbs;
>      PGresult   *res = NULL;
>      double        diff;
>      struct timeval now,
>                  then;
>!
>!     GetAutovacuumUserAndPasswordFromFile();
>!
>      /* Init the db list with template1 */
>      db_list = init_db_list();
>      if (db_list == NULL)
>!         ereport(ERROR,(errmsg("pg_autovacuum: Error creating db_list, exiting.")));
>
>      gettimeofday(&then, 0);        /* for use later to caluculate sleep time */
>
>+     /* Main Loop */
>      while (1)
>!     {
>!         /*
>!          * Emergency bailout if postmaster has died.  This is to avoid the
>!          * necessity for manual cleanup of all postmaster children.
>!          */
>!         if (!PostmasterIsAlive(true))
>!             exit(1);
>!
>!         if (got_SIGHUP)
>!         {
>!             got_SIGHUP = false;
>!             ProcessConfigFile(PGC_SIGHUP);
>!         }
>!         if (shutdown_requested)
>!         {
>!             /* Normal exit from pg_autovacuum is here */
>!             proc_exit(0);        /* done */
>!         }
>!
>          db_elem = DLGetHead(db_list);    /* Reset cur_db_node to the
>                                           * beginning of the db_list */
>
>***************
>*** 1061,1095 ****
>              if (dbs->conn == NULL)
>              {                    /* Serious problem: We can't connect to
>                                   * template1 */
>!                 log_entry("Error: Cannot connect to template1, exiting.");
>!                 fflush(LOGOUTPUT);
>!                 fclose(LOGOUTPUT);
>!                 exit(1);
>              }
>          }
>
>!         if (loops % UPDATE_INTERVAL == 0)        /* Update the list if it's
>!                                                  * time */
>!             update_db_list(db_list);    /* Add and remove databases from
>!                                          * the list */
>!
>          while (db_elem != NULL)
>!         {                        /* Loop through databases in list */
>              dbs = ((db_info *) DLE_VAL(db_elem));        /* get pointer to
>                                                           * cur_db's db_info
>                                                           * struct */
>              if (dbs->conn == NULL)
>                  dbs->conn = db_connect(dbs);
>!
>              if (dbs->conn != NULL)
>              {
>-                 if (loops % UPDATE_INTERVAL == 0)        /* Update the list if
>-                                                          * it's time */
>-                     update_table_list(dbs);        /* Add and remove tables
>-                                                  * from the list */
>-
>                  if (xid_wraparound_check(dbs) == 0)
>                  {
>                      res = send_query(TABLE_STATS_QUERY, dbs);    /* Get an updated
>                                                                   * snapshot of this dbs
>                                                                   * table stats */
>--- 774,806 ----
>              if (dbs->conn == NULL)
>              {                    /* Serious problem: We can't connect to
>                                   * template1 */
>!                 ereport(ERROR,(errmsg("pg_autovacuum: Cannot connect to template1, exiting.")));
>              }
>          }
>
>!         update_db_list(db_list);    /* Add and remove databases from
>!                                      * the list */
>!
>!         /* Loop through databases in list */
>          while (db_elem != NULL)
>!         {
>              dbs = ((db_info *) DLE_VAL(db_elem));        /* get pointer to
>                                                           * cur_db's db_info
>                                                           * struct */
>              if (dbs->conn == NULL)
>                  dbs->conn = db_connect(dbs);
>!
>              if (dbs->conn != NULL)
>              {
>                  if (xid_wraparound_check(dbs) == 0)
>                  {
>+                     long curr_vacuum_count, curr_analyze_count;
>+                     long cnt_at_last_vacuum, cnt_at_last_analyze;
>+                     float vacuum_threshold, analyze_threshold;
>+                     char table_name[512];
>+
>+                     update_pg_autovacuum_table(dbs);
>+
>                      res = send_query(TABLE_STATS_QUERY, dbs);    /* Get an updated
>                                                                   * snapshot of this dbs
>                                                                   * table stats */
>***************
>*** 1097,1174 ****
>                      {
>                          for (j = 0; j < PQntuples(res); j++)
>                          {            /* loop through result set */
>!                             tbl_elem = DLGetHead(dbs->table_list);    /* Reset tbl_elem to top
>!                                                                     * of dbs->table_list */
>!                             while (tbl_elem != NULL)
>!                             {        /* Loop through tables in list */
>!                                 tbl = ((tbl_info *) DLE_VAL(tbl_elem));        /* set tbl_info =
>!                                                                             * current_table */
>!                                 if (tbl->relid == atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))))
>!                                 {
>!                                     tbl->curr_analyze_count =
>!                                         (atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_ins"))) +
>!                                         atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_upd"))) +
>!                                         atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_del"))));
>!                                     tbl->curr_vacuum_count =
>!                                         (atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_del"))) +
>!                                         atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_upd"))));
>!
>!                                     /*
>!                                     * Check numDeletes to see if we need to
>!                                     * vacuum, if so: Run vacuum analyze
>!                                     * (adding analyze is small so we might as
>!                                     * well) Update table thresholds and
>!                                     * related information if numDeletes is
>!                                     * not big enough for vacuum then check
>!                                     * numInserts for analyze
>!                                     */
>!                                     if (tbl->curr_vacuum_count - tbl->CountAtLastVacuum >= tbl->vacuum_threshold)
>!                                     {
>!                                         /*
>!                                         * if relisshared = t and database !=
>!                                         * template1 then only do an analyze
>!                                         */
>!                                         if (tbl->relisshared > 0 && strcmp("template1", dbs->dbname))
>!                                             snprintf(buf, sizeof(buf), "ANALYZE %s", tbl->table_name);
>!                                         else
>!                                             snprintf(buf, sizeof(buf), "VACUUM ANALYZE %s", tbl->table_name);
>!                                         if (args->debug >= 1)
>!                                         {
>!                                             sprintf(logbuffer, "Performing: %s", buf);
>!                                             log_entry(logbuffer);
>!                                             fflush(LOGOUTPUT);
>!                                         }
>!                                         send_query(buf, dbs);
>!                                         update_table_thresholds(dbs, tbl, VACUUM_ANALYZE);
>!                                         if (args->debug >= 2)
>!                                             print_table_info(tbl);
>!                                     }
>!                                     else if (tbl->curr_analyze_count - tbl->CountAtLastAnalyze >=
tbl->analyze_threshold)
>!                                     {
>!                                         snprintf(buf, sizeof(buf), "ANALYZE %s", tbl->table_name);
>!                                         if (args->debug >= 1)
>!                                         {
>!                                             sprintf(logbuffer, "Performing: %s", buf);
>!                                             log_entry(logbuffer);
>!                                             fflush(LOGOUTPUT);
>!                                         }
>!                                         send_query(buf, dbs);
>!                                         update_table_thresholds(dbs, tbl, ANALYZE_ONLY);
>!                                         if (args->debug >= 2)
>!                                             print_table_info(tbl);
>!                                     }
>!
>!                                     break;    /* once we have found a match, no
>!                                             * need to keep checking. */
>!                                 }
>!
>                                  /*
>!                                 * Advance the table pointers for the next
>!                                 * loop
>                                  */
>!                                 tbl_elem = DLGetSucc(tbl_elem);
>
>!                             }        /* end for table while loop */
>                          }            /* end for j loop (tuples in PGresult) */
>                      }            /* end if (res != NULL) */
>                  }                /* close of if(xid_wraparound_check()) */
>--- 808,877 ----
>                      {
>                          for (j = 0; j < PQntuples(res); j++)
>                          {            /* loop through result set */
>!                             /*
>!                             * Check to see if we need to
>!                             * vacuum analyze, or just analyze
>!                             * if needed, do so, then update table thresholds and
>!                             * related information
>!                             */
>!                             curr_vacuum_count = (atol(PQgetvalue(res, j, PQfnumber(res, "curr_vacuum_count"))));
>!                             curr_analyze_count = (atol(PQgetvalue(res, j, PQfnumber(res, "curr_analyze_count"))));
>!                             cnt_at_last_vacuum = (atol(PQgetvalue(res, j, PQfnumber(res, "cnt_at_last_vacuum"))));
>!                             cnt_at_last_analyze = (atol(PQgetvalue(res, j, PQfnumber(res, "cnt_at_last_analyze"))));
>!                             vacuum_threshold = (atof(PQgetvalue(res, j, PQfnumber(res, "vacuum_threshold"))));
>!                             analyze_threshold = (atof(PQgetvalue(res, j, PQfnumber(res, "analyze_threshold"))));
>!                             strcpy(table_name,"\"");
>!                             strcat(table_name, PQgetvalue(res,j, PQfnumber(res, "schemaname")));
>!                             strcat(table_name,"\".\"");
>!                             strcat(table_name, PQgetvalue(res,j, PQfnumber(res, "relname")));
>!                             strcat(table_name,"\"");
>!
>!                             if ((curr_vacuum_count - cnt_at_last_vacuum) >= vacuum_threshold)
>!                             {
>                                  /*
>!                                 * if relisshared = t and database !=
>!                                 * template1 then only do an analyze
>                                  */
>!                                 if ((strcmp("t",PQgetvalue(res,j, PQfnumber(res, "relisshared"))) == 0 ) &&
(strcmp(dbs->dbname,"template1")== 0 )) 
>!                                     snprintf(buf, sizeof(buf), "ANALYZE %s", table_name);
>!                                 else
>!                                     snprintf(buf, sizeof(buf), "VACUUM ANALYZE %s", table_name);
>!
>!                                 ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>!
>!                                 send_query(buf, dbs);
>!                                 update_table_thresholds(dbs, atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))),
VACUUM_ANALYZE);
>!                             }
>!                             else if (curr_analyze_count - cnt_at_last_analyze >= analyze_threshold)
>!                             {
>!                                 snprintf(buf, sizeof(buf), "ANALYZE %s", table_name);
>!
>!                                 ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>!
>!                                 send_query(buf, dbs);
>!                                 update_table_thresholds(dbs, atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))),
ANALYZE_ONLY);
>!                             }
>!
>!                             if (curr_vacuum_count < cnt_at_last_vacuum)
>!                             {
>!                                 /*
>!                                  * this should only happen if the stats system gets reset
>!                                  */
>!                                 snprintf(buf, sizeof(buf), "update pg_autovacuum set cnt_at_last_vacuum = %li where
table_oid= %u", curr_vacuum_count, atooid(PQgetvalue(res, j, PQfnumber(res, "oid")))); 
>!
>!                                 ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>!                                 send_query(buf, dbs);
>!                             }
>!                             if (curr_analyze_count < cnt_at_last_analyze)
>!                             {
>!                                 /*
>!                                  * this should only happen if the stats system gets reset
>!                                  */
>!                                 snprintf(buf, sizeof(buf), "update pg_autovacuum set cnt_at_last_analyze = %li where
table_oid= %u", curr_analyze_count, atooid(PQgetvalue(res, j, PQfnumber(res, "oid")))); 
>
>!                                 ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>!                                 send_query(buf, dbs);
>!                             }
>                          }            /* end for j loop (tuples in PGresult) */
>                      }            /* end if (res != NULL) */
>                  }                /* close of if(xid_wraparound_check()) */
>***************
>*** 1179,1212 ****
>              }
>              db_elem = DLGetSucc(db_elem);        /* move on to next DB
>                                                   * regardless */
>!         }                        /* end of db_list while loop */
>!
>          /* Figure out how long to sleep etc ... */
>          gettimeofday(&now, 0);
>          diff = (int) (now.tv_sec - then.tv_sec) * 1000000.0 + (int) (now.tv_usec - then.tv_usec);
>!
>!         sleep_secs = args->sleep_base_value + args->sleep_scaling_factor * diff / 1000000.0;
>          loops++;
>!         if (args->debug >= 2)
>!         {
>!             sprintf(logbuffer,
>!              "%i All DBs checked in: %.0f usec, will sleep for %i secs.",
>!                     loops, diff, sleep_secs);
>!             log_entry(logbuffer);
>!             fflush(LOGOUTPUT);
>!         }
>!
>!         sleep(sleep_secs);        /* Larger Pause between outer loops */
>!
>          gettimeofday(&then, 0); /* Reset time counter */
>!
>!     }                            /* end of while loop */
>
>      /*
>       * program is exiting, this should never run, but is here to make
>       * compiler / valgrind happy
>       */
>      free_db_list(db_list);
>!     free_cmd_args();
>!     return EXIT_SUCCESS;
>  }
>--- 882,911 ----
>              }
>              db_elem = DLGetSucc(db_elem);        /* move on to next DB
>                                                   * regardless */
>!         }    /* end of db_list while loop */
>!
>          /* Figure out how long to sleep etc ... */
>          gettimeofday(&now, 0);
>          diff = (int) (now.tv_sec - then.tv_sec) * 1000000.0 + (int) (now.tv_usec - then.tv_usec);
>!
>!         sleep_secs = SLEEPBASEVALUE + SLEEPSCALINGFACTOR * diff / 1000000.0;
>          loops++;
>!
>!         ereport(DEBUG2,(errmsg("pg_autovacuum: loop %i; All DBs checked in: %.0f usec, will sleep for %i secs.",
>!                 loops, diff, sleep_secs)));
>!
>!         /* Larger Pause between outer loops */
>!         if (!(got_SIGHUP || shutdown_requested))
>!             pg_usleep((long)(sleep_secs * 1000000L));
>!
>          gettimeofday(&then, 0); /* Reset time counter */
>!
>!     }    /* end of while loop */
>
>      /*
>       * program is exiting, this should never run, but is here to make
>       * compiler / valgrind happy
>       */
>      free_db_list(db_list);
>!     proc_exit(0);
>  }
>*** ./src/backend/postmaster/Makefile.orig    2004-08-04 01:40:53.455982146 -0400
>--- ./src/backend/postmaster/Makefile    2004-07-24 02:00:34.000000000 -0400
>***************
>*** 12,18 ****
>  top_builddir = ../../..
>  include $(top_builddir)/src/Makefile.global
>
>! OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
>
>  all: SUBSYS.o
>
>--- 12,18 ----
>  top_builddir = ../../..
>  include $(top_builddir)/src/Makefile.global
>
>! OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o autovacuum.o
>
>  all: SUBSYS.o
>
>*** ./src/backend/postmaster/postmaster.c.orig    2004-08-04 01:40:40.707380679 -0400
>--- ./src/backend/postmaster/postmaster.c    2004-08-03 21:55:54.578203673 -0400
>***************
>*** 55,66 ****
>   *        The Postmaster cleans up after backends if they have an emergency
>   *        exit and/or core dump.
>   *
>-  * Error Reporting:
>-  *        Use write_stderr() only for reporting "interactive" errors
>-  *        (essentially, bogus arguments on the command line).  Once the
>-  *        postmaster is launched, use ereport().  In particular, don't use
>-  *        write_stderr() for anything that occurs after pmdaemonize.
>-  *
>   *-------------------------------------------------------------------------
>   */
>
>--- 55,60 ----
>***************
>*** 198,203 ****
>--- 192,198 ----
>  /* PIDs of special child processes; 0 when not running */
>  static pid_t StartupPID = 0,
>              BgWriterPID = 0,
>+             AutoVacPID = 0,
>              PgArchPID = 0,
>              PgStatPID = 0;
>
>***************
>*** 268,273 ****
>--- 263,272 ----
>  static int    CountChildren(void);
>  static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
>  static pid_t StartChildProcess(int xlop);
>+ static void
>+ postmaster_error(const char *fmt,...)
>+ /* This lets gcc check the format string for consistency. */
>+ __attribute__((format(printf, 1, 2)));
>
>  #ifdef EXEC_BACKEND
>
>***************
>*** 298,303 ****
>--- 297,303 ----
>
>  #define StartupDataBase()        StartChildProcess(BS_XLOG_STARTUP)
>  #define StartBackgroundWriter() StartChildProcess(BS_XLOG_BGWRITER)
>+ #define StartAutoVac() StartChildProcess(BS_XLOG_AUTOVAC)
>
>
>  /*
>***************
>*** 384,390 ****
>  #ifdef USE_ASSERT_CHECKING
>                  SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
>  #else
>!                 write_stderr("%s: assert checking is not compiled in\n", progname);
>  #endif
>                  break;
>              case 'a':
>--- 384,390 ----
>  #ifdef USE_ASSERT_CHECKING
>                  SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
>  #else
>!                 postmaster_error("assert checking is not compiled in");
>  #endif
>                  break;
>              case 'a':
>***************
>*** 508,515 ****
>                  }
>
>              default:
>!                 write_stderr("Try \"%s --help\" for more information.\n",
>!                              progname);
>                  ExitPostmaster(1);
>          }
>      }
>--- 508,516 ----
>                  }
>
>              default:
>!                 fprintf(stderr,
>!                     gettext("Try \"%s --help\" for more information.\n"),
>!                         progname);
>                  ExitPostmaster(1);
>          }
>      }
>***************
>*** 519,528 ****
>       */
>      if (optind < argc)
>      {
>!         write_stderr("%s: invalid argument: \"%s\"\n",
>!                      progname, argv[optind]);
>!         write_stderr("Try \"%s --help\" for more information.\n",
>!                      progname);
>          ExitPostmaster(1);
>      }
>
>--- 520,529 ----
>       */
>      if (optind < argc)
>      {
>!         postmaster_error("invalid argument: \"%s\"", argv[optind]);
>!         fprintf(stderr,
>!                 gettext("Try \"%s --help\" for more information.\n"),
>!                 progname);
>          ExitPostmaster(1);
>      }
>
>***************
>*** 593,605 ****
>           * for lack of buffers.  The specific choices here are somewhat
>           * arbitrary.
>           */
>!         write_stderr("%s: the number of buffers (-B) must be at least twice the number of allowed connections (-N)
andat least 16\n", progname); 
>          ExitPostmaster(1);
>      }
>
>      if (ReservedBackends >= MaxBackends)
>      {
>!         write_stderr("%s: superuser_reserved_connections must be less than max_connections\n", progname);
>          ExitPostmaster(1);
>      }
>
>--- 594,606 ----
>           * for lack of buffers.  The specific choices here are somewhat
>           * arbitrary.
>           */
>!         postmaster_error("the number of buffers (-B) must be at least twice the number of allowed connections (-N)
andat least 16"); 
>          ExitPostmaster(1);
>      }
>
>      if (ReservedBackends >= MaxBackends)
>      {
>!         postmaster_error("superuser_reserved_connections must be less than max_connections");
>          ExitPostmaster(1);
>      }
>
>***************
>*** 608,614 ****
>       */
>      if (!CheckDateTokenTables())
>      {
>!         write_stderr("%s: invalid datetoken tables, please fix\n", progname);
>          ExitPostmaster(1);
>      }
>
>--- 609,615 ----
>       */
>      if (!CheckDateTokenTables())
>      {
>!         postmaster_error("invalid datetoken tables, please fix");
>          ExitPostmaster(1);
>      }
>
>***************
>*** 827,832 ****
>--- 828,835 ----
>       *
>       * 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/pgstat.c,
>+      * and postmaster/autovacuum.c.
>       * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c,
>       * and postmaster/pgstat.c.
>       */
>***************
>*** 977,986 ****
>      fp = AllocateFile(path, PG_BINARY_R);
>      if (fp == NULL)
>      {
>!         write_stderr("%s: could not find the database system\n"
>!                      "Expected to find it in the directory \"%s\",\n"
>!                      "but could not open file \"%s\": %s\n",
>!                      progname, checkdir, path, strerror(errno));
>          ExitPostmaster(2);
>      }
>      FreeFile(fp);
>--- 980,990 ----
>      fp = AllocateFile(path, PG_BINARY_R);
>      if (fp == NULL)
>      {
>!         fprintf(stderr,
>!                 gettext("%s: could not find the database system\n"
>!                         "Expected to find it in the directory \"%s\",\n"
>!                         "but could not open file \"%s\": %s\n"),
>!                 progname, checkdir, path, strerror(errno));
>          ExitPostmaster(2);
>      }
>      FreeFile(fp);
>***************
>*** 1023,1030 ****
>      pid = fork();
>      if (pid == (pid_t) -1)
>      {
>!         write_stderr("%s: could not fork background process: %s\n",
>!                      progname, strerror(errno));
>          ExitPostmaster(1);
>      }
>      else if (pid)
>--- 1027,1034 ----
>      pid = fork();
>      if (pid == (pid_t) -1)
>      {
>!         postmaster_error("could not fork background process: %s",
>!                          strerror(errno));
>          ExitPostmaster(1);
>      }
>      else if (pid)
>***************
>*** 1045,1052 ****
>  #ifdef HAVE_SETSID
>      if (setsid() < 0)
>      {
>!         write_stderr("%s: could not dissociate from controlling TTY: %s\n",
>!                      progname, strerror(errno));
>          ExitPostmaster(1);
>      }
>  #endif
>--- 1049,1056 ----
>  #ifdef HAVE_SETSID
>      if (setsid() < 0)
>      {
>!         postmaster_error("could not dissociate from controlling TTY: %s",
>!                          strerror(errno));
>          ExitPostmaster(1);
>      }
>  #endif
>***************
>*** 1113,1121 ****
>      int            nSockets;
>      time_t        now,
>                  last_touch_time;
>!     struct timeval earlier,
>                  later;
>!     struct timezone tz;
>
>      gettimeofday(&earlier, &tz);
>      last_touch_time = time(NULL);
>--- 1117,1125 ----
>      int            nSockets;
>      time_t        now,
>                  last_touch_time;
>!     struct         timeval earlier,
>                  later;
>!     struct         timezone tz;
>
>      gettimeofday(&earlier, &tz);
>      last_touch_time = time(NULL);
>***************
>*** 1220,1225 ****
>--- 1224,1252 ----
>                  kill(BgWriterPID, SIGUSR2);
>          }
>
>+         /*
>+          * If no AutoVacuum process is running, and we are not in
>+          * a state that prevents it, start one.  It doesn't matter if this
>+          * fails, we'll just try again later.
>+          */
>+         if (autovacuum_start_daemon)
>+         {
>+             if (pgstat_collect_tuplelevel)
>+             {
>+                 if (AutoVacPID == 0 && StartupPID == 0 && !FatalError)
>+                 {
>+                     AutoVacPID = StartAutoVac();
>+
>+                     /* If shutdown is pending, set it going */
>+                     if (Shutdown > NoShutdown && AutoVacPID != 0)
>+                         kill(AutoVacPID, SIGUSR2);
>+                 }
>+             }
>+         }
>+         else if(AutoVacPID > 0)
>+             kill(AutoVacPID, SIGUSR2);
>+
>+
>          /* If we have lost the archiver, try to start a new one */
>          if (XLogArchivingActive() && PgArchPID == 0 &&
>              StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
>***************
>*** 1587,1592 ****
>--- 1614,1634 ----
>      backendPID = (int) ntohl(canc->backendPID);
>      cancelAuthCode = (long) ntohl(canc->cancelAuthCode);
>
>+     if (backendPID == BgWriterPID)
>+     {
>+         ereport(DEBUG2,
>+                 (errmsg_internal("ignoring cancel request for bgwriter process %d",
>+                                  backendPID)));
>+         return;
>+     }
>+     if (backendPID == AutoVacPID)
>+     {
>+         ereport(DEBUG2,
>+                 (errmsg_internal("ignoring cancel request for autovacuum process %d",
>+                                  backendPID)));
>+         return;
>+     }
>+
>      /*
>       * See if we have a matching backend.  In the EXEC_BACKEND case, we
>       * can no longer access the postmaster's own backend list, and must
>***************
>*** 1768,1773 ****
>--- 1810,1817 ----
>          SignalChildren(SIGHUP);
>          if (BgWriterPID != 0)
>              kill(BgWriterPID, SIGHUP);
>+         if (AutoVacPID != 0)
>+             kill(AutoVacPID, SIGHUP);
>          if (PgArchPID != 0)
>              kill(PgArchPID, SIGHUP);
>          /* PgStatPID does not currently need SIGHUP */
>***************
>*** 1828,1833 ****
>--- 1872,1881 ----
>              /* And tell it to shut down */
>              if (BgWriterPID != 0)
>                  kill(BgWriterPID, SIGUSR2);
>+             /* I don't think we need to Start the autovac process if not running */
>+             /* And tell it to shut down */
>+             if (AutoVacPID != 0)
>+                 kill(AutoVacPID, SIGUSR2);
>              /* Tell pgarch to shut down too; nothing left for it to do */
>              if (PgArchPID != 0)
>                  kill(PgArchPID, SIGQUIT);
>***************
>*** 1875,1880 ****
>--- 1923,1931 ----
>              /* And tell it to shut down */
>              if (BgWriterPID != 0)
>                  kill(BgWriterPID, SIGUSR2);
>+             /* And tell it to shut down */
>+             if (AutoVacPID != 0)
>+                 kill(AutoVacPID, SIGUSR2);
>              /* Tell pgarch to shut down too; nothing left for it to do */
>              if (PgArchPID != 0)
>                  kill(PgArchPID, SIGQUIT);
>***************
>*** 1896,1901 ****
>--- 1947,1954 ----
>                  kill(StartupPID, SIGQUIT);
>              if (BgWriterPID != 0)
>                  kill(BgWriterPID, SIGQUIT);
>+             if (AutoVacPID != 0)
>+                 kill(AutoVacPID, SIGQUIT);
>              if (PgArchPID != 0)
>                  kill(PgArchPID, SIGQUIT);
>              if (PgStatPID != 0)
>***************
>*** 1984,1989 ****
>--- 2037,2055 ----
>              BgWriterPID = StartBackgroundWriter();
>
>              /*
>+              *  We won't start autovac here since we are in no hurry to start it up
>+              *  But we will take this opportunity to make some noise about invalid
>+              *  combinations of options in postgresql.conf
>+              */
>+             if (autovacuum_start_daemon)
>+             {
>+                 if (!pgstat_collect_tuplelevel)
>+                     ereport(WARNING,(errmsg("pg_autovacuum: autovac is enabled, but requires stats_row_level which
isnot enabled"))); 
>+                 if (pgstat_collect_resetonpmstart)
>+                     ereport(WARNING,(errmsg("pg_autovacuum: stats_reset_on_server_start should be disabled for
optimalperformance"))); 
>+             }
>+
>+             /*
>               * Go to shutdown mode if a shutdown request was pending.
>               * Otherwise, try to start the archiver and stats collector too.
>               */
>***************
>*** 1996,2001 ****
>--- 2062,2073 ----
>                          PgStatPID = pgstat_start();
>              }
>
>+             /*
>+              * Shutdown autovac if a shutdown request was pending.
>+              */
>+             if (Shutdown > NoShutdown && AutoVacPID != 0)
>+                 kill(AutoVacPID, SIGUSR2);
>+
>              continue;
>          }
>
>***************
>*** 2032,2037 ****
>--- 2104,2129 ----
>          }
>
>          /*
>+          * Was it the autovac?
>+          */
>+         if (AutoVacPID != 0 && pid == AutoVacPID)
>+         {
>+             AutoVacPID = 0;
>+             if (exitstatus != 0)
>+             {
>+                 /*
>+                 * Any unexpected exit of the autovacuum is treated as a crash.
>+                 * FIXME: This is useful for debugging autovac, but I think it should be
>+                 * ripped out before final patch, autovac shouldn't crash the postmaster
>+                 */
>+                 LogChildExit(LOG, gettext("pg_autovacuum process"),
>+                             pid, exitstatus);
>+                 HandleChildCrash(pid, exitstatus);
>+                 continue;
>+             }
>+         }
>+
>+         /*
>           * Was it the archiver?  If so, just try to start a new
>           * one; no need to force reset of the rest of the system.  (If fail,
>           * we'll try again in future cycles of the main loop.)
>***************
>*** 2077,2083 ****
>           * 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")));
>--- 2169,2175 ----
>           * 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")));
>***************
>*** 2100,2105 ****
>--- 2192,2200 ----
>          /* And tell it to shut down */
>          if (BgWriterPID != 0)
>              kill(BgWriterPID, SIGUSR2);
>+         /* Tell AutoVac to shut down */
>+         if (AutoVacPID != 0)
>+             kill(AutoVacPID, SIGUSR2);
>          /* Tell pgarch to shut down too; nothing left for it to do */
>          if (PgArchPID != 0)
>              kill(PgArchPID, SIGQUIT);
>***************
>*** 2265,2270 ****
>--- 2360,2379 ----
>      }
>
>      FatalError = true;
>+
>+     /* Take care of the autovacuum too */
>+     if (pid == AutoVacPID)
>+         AutoVacPID = 0;
>+     else if (AutoVacPID != 0 && !FatalError)
>+     {
>+         ereport(DEBUG2,
>+                 (errmsg_internal("sending %s to process %d",
>+                                  (SendStop ? "SIGSTOP" : "SIGQUIT"),
>+                                  (int) AutoVacPID)));
>+         kill(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
>+     }
>+
>+     FatalError = true;
>  }
>
>  /*
>***************
>*** 3256,3261 ****
>--- 3365,3374 ----
>                  ereport(LOG,
>                          (errmsg("could not fork background writer process: %m")));
>                  break;
>+             case BS_XLOG_AUTOVAC:
>+                 ereport(LOG,
>+                         (errmsg("could not fork auto vacuum process: %m")));
>+                 break;
>              default:
>                  ereport(LOG,
>                          (errmsg("could not fork process: %m")));
>***************
>*** 3310,3315 ****
>--- 3423,3446 ----
>      return true;
>  }
>
>+ /*
>+  * This should be used only for reporting "interactive" errors (essentially,
>+  * bogus arguments on the command line).  Once the postmaster is launched,
>+  * use ereport.  In particular, don't use this for anything that occurs
>+  * after pmdaemonize.
>+  */
>+ static void
>+ postmaster_error(const char *fmt,...)
>+ {
>+     va_list        ap;
>+
>+     fprintf(stderr, "%s: ", progname);
>+     va_start(ap, fmt);
>+     vfprintf(stderr, gettext(fmt), ap);
>+     va_end(ap);
>+     fprintf(stderr, "\n");
>+ }
>+
>
>  #ifdef EXEC_BACKEND
>
>***************
>*** 3749,3755 ****
>      if (r == WAIT_OBJECT_0)
>          pg_queue_signal(SIGCHLD);
>      else
>!         write_stderr("ERROR: failed to wait on child process handle: %d\n",
>                  (int) GetLastError());
>      CloseHandle(procHandle);
>      return 0;
>--- 3880,3886 ----
>      if (r == WAIT_OBJECT_0)
>          pg_queue_signal(SIGCHLD);
>      else
>!         fprintf(stderr, "ERROR: failed to wait on child process handle: %d\n",
>                  (int) GetLastError());
>      CloseHandle(procHandle);
>      return 0;
>*** ./src/backend/utils/misc/guc.c.orig    2004-08-04 01:41:20.000000000 -0400
>--- ./src/backend/utils/misc/guc.c    2004-08-03 00:47:41.000000000 -0400
>***************
>*** 43,49 ****
>--- 43,51 ----
>  #include "optimizer/prep.h"
>  #include "parser/parse_expr.h"
>  #include "parser/parse_relation.h"
>+ #include "pgstat.h"
>  #include "postmaster/bgwriter.h"
>+ #include "postmaster/autovacuum.h"
>  #include "postmaster/postmaster.h"
>  #include "storage/bufmgr.h"
>  #include "storage/fd.h"
>***************
>*** 55,61 ****
>  #include "utils/builtins.h"
>  #include "utils/memutils.h"
>  #include "utils/pg_locale.h"
>- #include "pgstat.h"
>
>  char *guc_pgdata;
>  char *guc_hbafile;
>--- 57,62 ----
>***************
>*** 642,647 ****
>--- 643,656 ----
>          &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,
>***************
>*** 1284,1289 ****
>--- 1293,1322 ----
>          &block_size,
>          BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
>      },
>+     {
>+         {"autovacuum_vacuum_threshold_base", PGC_SIGHUP, AUTOVACUUM,
>+             gettext_noop("Minimum number of tuple updates or deletes prior to vacuum."),
>+             NULL
>+         },
>+         &autovacuum_vacuum_base,
>+         1000, 0, INT_MAX, NULL, NULL
>+     },
>+     {
>+         {"autovacuum_analyze_threshold_base", PGC_SIGHUP, AUTOVACUUM,
>+             gettext_noop("Minimum number of tuple updates or deletes prior to analyze."),
>+             NULL
>+         },
>+         &autovacuum_analyze_base,
>+         500, 0, INT_MAX, NULL, NULL
>+     },
>+     {
>+         {"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM,
>+             gettext_noop("Minimum number of tuple updates or deletes prior to analyze."),
>+             NULL
>+         },
>+         &autovacuum_analyze_base,
>+         500, 0, INT_MAX, NULL, NULL
>+     },
>
>      /* End-of-list marker */
>      {
>***************
>*** 1364,1369 ****
>--- 1397,1418 ----
>          &phony_random_seed,
>          0.5, 0.0, 1.0, assign_random_seed, show_random_seed
>      },
>+     {
>+         {"autovacuum_vacuum_threshold_sf", PGC_SIGHUP, AUTOVACUUM,
>+             gettext_noop("Numer of tuple updates or deletes prior to vacuum as a factor of reltuples."),
>+             NULL
>+         },
>+         &autovacuum_vacuum_scaling_factor,
>+         2, 0, 100, NULL, NULL
>+     },
>+     {
>+         {"autovacuum_analyze_threshold_sf", PGC_SIGHUP, AUTOVACUUM,
>+             gettext_noop("Numer of tuple updates or deletes prior to analyze as a factor of reltuples."),
>+             NULL
>+         },
>+         &autovacuum_analyze_scaling_factor,
>+         1, 0, 100, NULL, NULL
>+     },
>
>      /* End-of-list marker */
>      {
>*** ./src/backend/utils/misc/postgresql.conf.sample.orig    2004-08-04 01:41:06.000000000 -0400
>--- ./src/backend/utils/misc/postgresql.conf.sample    2004-07-22 23:50:32.000000000 -0400
>***************
>*** 238,243 ****
>--- 238,253 ----
>
>
>  #---------------------------------------------------------------------------
>+ # VACUUM DAEMON
>+ #---------------------------------------------------------------------------
>+
>+ #autovacuum = false  # requires stats_row_level = true
>+ #autovacuum_vacuum_threshold_base = 1000
>+ #autovacuum_vacuum_threshold_sf = 2
>+ #autovacuum_analyze_threshold_base = 500
>+ #autovacuum_analyze_threshold_sf = 1
>+
>+ #---------------------------------------------------------------------------
>  # CLIENT CONNECTION DEFAULTS
>  #---------------------------------------------------------------------------
>
>*** ./src/include/bootstrap/bootstrap.h.orig    2004-08-04 01:42:35.927740714 -0400
>--- ./src/include/bootstrap/bootstrap.h    2004-07-22 23:50:32.000000000 -0400
>***************
>*** 59,63 ****
>--- 59,64 ----
>  #define BS_XLOG_BOOTSTRAP    1
>  #define BS_XLOG_STARTUP        2
>  #define BS_XLOG_BGWRITER    3
>+ #define BS_XLOG_AUTOVAC        4
>
>  #endif   /* BOOTSTRAP_H */
>*** ./src/include/postmaster/autovacuum.h.orig    2004-06-29 09:29:39.000000000 -0400
>--- ./src/include/postmaster/autovacuum.h    2004-08-04 00:46:59.293859961 -0400
>***************
>*** 1,42 ****
>! /* pg_autovacuum.h
>!  * Header file for pg_autovacuum.c
>   * (c) 2003 Matthew T. O'Connor
>   */
>
>  #include "postgres_fe.h"
>
>  #include <unistd.h>
>- #ifdef HAVE_GETOPT_H
>- #include <getopt.h>
>- #endif
>  #include <time.h>
>  #include <sys/time.h>
>
>! /* These next two lines are correct when pg_autovaccum is compiled
>!    from within the postgresql source tree  */
>! #include "libpq-fe.h"
>  #include "lib/dllist.h"
>! /* Had to change the last two lines to compile on
>!    Redhat outside of postgresql source tree */
>! /*
>! #include "/usr/include/libpq-fe.h"
>! #include "/usr/include/pgsql/server/lib/dllist.h"
>! */
>!
>! #define AUTOVACUUM_DEBUG    1
>! #define VACBASETHRESHOLD    1000
>! #define VACSCALINGFACTOR    2
>! #define SLEEPBASEVALUE        300
>  #define SLEEPSCALINGFACTOR    2
>- #define UPDATE_INTERVAL        2
>
>  /* these two constants are used to tell update_table_stats what operation we just perfomred */
>  #define VACUUM_ANALYZE        0
>  #define ANALYZE_ONLY        1
>
>! #define TABLE_STATS_QUERY    "select
a.oid,a.relname,a.relnamespace,a.relpages,a.relisshared,a.reltuples,b.schemaname,b.n_tup_ins,b.n_tup_upd,b.n_tup_del
frompg_class a, pg_stat_all_tables b where a.oid=b.relid and a.relkind = 'r' and schemaname not like 'pg_temp_%'" 
>
>- #define FRONTEND
>  #define PAGES_QUERY "select oid,reltuples,relpages from pg_class where oid=%u"
>  #define FROZENOID_QUERY "select oid,age(datfrozenxid) from pg_database where datname = 'template1'"
>  #define FROZENOID_QUERY2 "select oid,datname,age(datfrozenxid) from pg_database where datname!='template0'"
>--- 1,35 ----
>! /* autovacuum.h
>!  * Header file for autovacuum.c
>   * (c) 2003 Matthew T. O'Connor
>   */
>
>+ #ifndef _AUTOVAC_H
>+ #define _AUTOVAC_H
>+
>+ #define FRONTEND
>  #include "postgres_fe.h"
>
>  #include <unistd.h>
>  #include <time.h>
>  #include <sys/time.h>
>+ #include <sys/stat.h>
>
>! #include "../../interfaces/libpq/libpq-fe.h"
>  #include "lib/dllist.h"
>!
>! /* default settings defined here
>!  * These could be changed into GUC variables, but I'm not sure we need to. */
>! #define SLEEPBASEVALUE        15
>  #define SLEEPSCALINGFACTOR    2
>
>  /* these two constants are used to tell update_table_stats what operation we just perfomred */
>  #define VACUUM_ANALYZE        0
>  #define ANALYZE_ONLY        1
>+ #define AUTOVACPASSFILE "autovac.passwd"
>
>! /* define the main queries */
>! #define TABLE_STATS_QUERY "select a.oid, a.relname, a.relnamespace, a.relpages, a.relisshared, a.reltuples,
b.schemaname,b.n_tup_ins, b.n_tup_upd, b.n_tup_del, c.cnt_at_last_analyze, c.cnt_at_last_vacuum, b.n_tup_ins +
b.n_tup_updas curr_analyze_count, b.n_tup_upd + b.n_tup_del as curr_vacuum_count, case when vacuum_threshold < 0 then
c.vacuum_base_threshold+ c.vacuum_scaling_factor * a.reltuples else c.vacuum_threshold end as vacuum_threshold, case
whenanalyze_threshold < 0 then c.analyze_base_threshold + c.analyze_scaling_factor * a.reltuples else
c.analyze_thresholdend as analyze_threshold from pg_class a inner join pg_stat_all_tables b on a.oid=b.relid left outer
joinpg_autovacuum c on a.oid = c.table_oid where a.relkind = 'r' and schemaname not like 'pg_temp_%' and c.table_oid is
notnull" 
>
>  #define PAGES_QUERY "select oid,reltuples,relpages from pg_class where oid=%u"
>  #define FROZENOID_QUERY "select oid,age(datfrozenxid) from pg_database where datname = 'template1'"
>  #define FROZENOID_QUERY2 "select oid,datname,age(datfrozenxid) from pg_database where datname!='template0'"
>***************
>*** 44,70 ****
>  /* define atooid */
>  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
>
>- /* define cmd_args stucture */
>- struct cmdargs
>- {
>-     int            vacuum_base_threshold,
>-                 analyze_base_threshold,
>-                 sleep_base_value,
>-                 debug,
>-                 daemonize;
>-     float        vacuum_scaling_factor,
>-                 analyze_scaling_factor,
>-                 sleep_scaling_factor;
>-     char       *user,
>-                *password,
>-                *host,
>-                *logfile,
>-                *port;
>- };
>- typedef struct cmdargs cmd_args;
>
>! /* define cmd_args as global so we can get to them everywhere */
>! cmd_args   *args;
>
>  /* Might need to add a time value for last time the whold database was vacuumed.
>      I think we need to guarantee this happens approx every 1Million TX's  */
>--- 37,53 ----
>  /* define atooid */
>  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
>
>
>! /* ----------
>!  * GUC parameters
>!  * ----------
>!  */
>! extern bool   autovacuum_start_daemon;
>! extern int    autovacuum_vacuum_base;
>! extern double autovacuum_vacuum_scaling_factor;
>! extern int    autovacuum_analyze_base;
>! extern double autovacuum_analyze_scaling_factor;
>!
>
>  /* Might need to add a time value for last time the whold database was vacuumed.
>      I think we need to guarantee this happens approx every 1Million TX's  */
>***************
>*** 72,142 ****
>  {
>      Oid            oid;
>      long        age;
>-     long        analyze_threshold,
>-                 vacuum_threshold;        /* Use these as defaults for table
>-                                          * thresholds */
>      PGconn       *conn;
>      char       *dbname,
>                 *username,
>                 *password;
>-     Dllist       *table_list;
>  };
>  typedef struct dbinfo db_info;
>
>- struct tableinfo
>- {
>-     char       *schema_name,
>-                *table_name;
>-     float        reltuples;
>-     int            relisshared;
>-     Oid            relid,
>-                 relpages;
>-     long        analyze_threshold,
>-                 vacuum_threshold;
>-     long        CountAtLastAnalyze;        /* equal to: inserts + updates as
>-                                          * of the last analyze or initial
>-                                          * values at startup */
>-     long        CountAtLastVacuum;        /* equal to: deletes + updates as
>-                                          * of the last vacuum or initial
>-                                          * values at startup */
>-     long        curr_analyze_count,
>-                 curr_vacuum_count;        /* Latest values from stats system */
>-     db_info    *dbi;            /* pointer to the database that this table
>-                                  * belongs to */
>- };
>- typedef struct tableinfo tbl_info;
>-
>  /* Functions for dealing with command line arguements */
>! static cmd_args *get_cmd_args(int argc, char *argv[]);
>! static void print_cmd_args(void);
>! static void free_cmd_args(void);
>! static void usage(void);
>
>  /* Functions for managing database lists */
>  static Dllist *init_db_list(void);
>  static db_info *init_dbinfo(char *dbname, Oid oid, long age);
>! static void update_db_list(Dllist *db_list);
>! static void remove_db_from_list(Dlelem *db_to_remove);
>! static void print_db_info(db_info * dbi, int print_table_list);
>! static void print_db_list(Dllist *db_list, int print_table_lists);
>! static int    xid_wraparound_check(db_info * dbi);
>! static void free_db_list(Dllist *db_list);
>
>  /* Functions for managing table lists */
>! static tbl_info *init_table_info(PGresult *conn, int row, db_info * dbi);
>! static void update_table_list(db_info * dbi);
>! static void remove_table_from_list(Dlelem *tbl_to_remove);
>! static void print_table_list(Dllist *tbl_node);
>! static void print_table_info(tbl_info * tbl);
>! static void update_table_thresholds(db_info * dbi, tbl_info * tbl, int vacuum_type);
>! static void free_tbl_list(Dllist *tbl_list);
>
>  /* A few database helper functions */
>! static int    check_stats_enabled(db_info * dbi);
>! static PGconn *db_connect(db_info * dbi);
>! static void db_disconnect(db_info * dbi);
>! static PGresult *send_query(const char *query, db_info * dbi);
>!
>! /* Other Generally needed Functions */
>! static void daemonize(void);
>! static void log_entry(const char *logentry);
>--- 55,88 ----
>  {
>      Oid            oid;
>      long        age;
>      PGconn       *conn;
>      char       *dbname,
>                 *username,
>                 *password;
>  };
>  typedef struct dbinfo db_info;
>
>  /* Functions for dealing with command line arguements */
>! void AutoVacMain(void);
>! void AutoVacLoop(void);
>
>  /* Functions for managing database lists */
>  static Dllist *init_db_list(void);
>  static db_info *init_dbinfo(char *dbname, Oid oid, long age);
>! void update_db_list(Dllist *db_list);
>! void remove_db_from_list(Dlelem *db_to_remove);
>! void print_db_info(db_info * dbi, int print_table_list);
>! void print_db_list(Dllist *db_list, int print_table_lists);
>! int    xid_wraparound_check(db_info * dbi);
>! void free_db_list(Dllist *db_list);
>
>  /* Functions for managing table lists */
>! void update_pg_autovacuum_table(db_info * dbi);
>! void update_table_thresholds(db_info * dbi, Oid table_oid, int vacuum_type);
>
>  /* A few database helper functions */
>! PGconn *db_connect(db_info * dbi);
>! void db_disconnect(db_info * dbi);
>! PGresult *send_query(const char *query, db_info * dbi);
>!
>! #endif /* _AUTOVAC_H_ */
>*** ./src/include/postmaster/postmaster.h.orig    2004-08-04 01:42:10.486531715 -0400
>--- ./src/include/postmaster/postmaster.h    2004-07-22 23:50:32.000000000 -0400
>***************
>*** 29,34 ****
>--- 29,35 ----
>  extern bool Log_connections;
>  extern bool log_hostname;
>  extern char *rendezvous_name;
>+ extern bool autovacuum_start_daemon;
>
>  #ifdef WIN32
>  extern HANDLE PostmasterHandle;
>*** ./src/include/storage/proc.h.orig    2004-08-04 01:48:19.967030481 -0400
>--- ./src/include/storage/proc.h    2004-08-03 00:47:45.000000000 -0400
>***************
>*** 109,115 ****
>
>  #define    DUMMY_PROC_DEFAULT    0
>  #define    DUMMY_PROC_BGWRITER    1
>! #define    NUM_DUMMY_PROCS        2
>
>
>  /* configurable options */
>--- 109,116 ----
>
>  #define    DUMMY_PROC_DEFAULT    0
>  #define    DUMMY_PROC_BGWRITER    1
>! #define    DUMMY_PROC_AUTOVAC    2
>! #define    NUM_DUMMY_PROCS        3
>
>
>  /* configurable options */
>*** ./src/include/utils/guc_tables.h.orig    2004-08-04 01:42:24.235023454 -0400
>--- ./src/include/utils/guc_tables.h    2004-07-22 23:50:32.000000000 -0400
>***************
>*** 71,76 ****
>--- 71,77 ----
>      COMPAT_OPTIONS_CLIENT,
>      DEVELOPER_OPTIONS,
>      COMPILE_OPTIONS,
>+     AUTOVACUUM,
>      CUSTOM_OPTIONS
>  };
>
>
>
>------------------------------------------------------------------------
>
>
>---------------------------(end of broadcast)---------------------------
>TIP 3: if posting/reading through Usenet, please send an appropriate
>      subscribe-nomail command to majordomo@postgresql.org so that your
>      message can get through to the mailing list cleanly
>
>


pgsql-patches by date:

Previous
From: Andreas Pflug
Date:
Subject: Re: logger subprocess including win32
Next
From: "Matthew T. O'Connor"
Date:
Subject: Re: Autovacuum Integration Patch Take 5