diff --git a/contrib/Makefile b/contrib/Makefile index 971afe6dfe..cec541490f 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/contrib/Makefile,v 1.59 2005/07/29 15:13:10 momjian Exp $ +# $PostgreSQL: pgsql/contrib/Makefile,v 1.60 2005/07/29 19:38:21 tgl Exp $ subdir = contrib top_builddir = .. @@ -20,7 +20,6 @@ WANTED_DIRS = \ lo \ ltree \ oid2name \ - pg_autovacuum \ pg_buffercache \ pg_trgm \ pgbench \ diff --git a/contrib/README b/contrib/README index e1e9a593c7..17e07d7f6a 100644 --- a/contrib/README +++ b/contrib/README @@ -102,10 +102,6 @@ oracle - Converts Oracle database schema to PostgreSQL by Gilles Darold -pg_autovacuum - - Automatically performs vacuum - by Matthew T. O'Connor - pg_buffercache - Real time queries on the shared buffer cache by Mark Kirkwood @@ -142,7 +138,7 @@ tablefunc - Examples of functions returning tables by Joe Conway -tips/apache_logging - +tips - Getting Apache to log to PostgreSQL by Terry Mackintosh diff --git a/contrib/pg_autovacuum/Makefile b/contrib/pg_autovacuum/Makefile deleted file mode 100644 index c512d18b7e..0000000000 --- a/contrib/pg_autovacuum/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -PROGRAM = pg_autovacuum -OBJS = pg_autovacuum.o dllist.o - -PG_CPPFLAGS = -I$(libpq_srcdir) -DFRONTEND -PG_LIBS = $(libpq_pgport) - -DOCS = README.pg_autovacuum - -EXTRA_CLEAN = dllist.c - -ifdef USE_PGXS -PGXS = $(shell pg_config --pgxs) -include $(PGXS) -else -subdir = contrib/pg_autovacuum -top_builddir = ../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif - -dllist.c: $(top_srcdir)/src/backend/lib/dllist.c - rm -f $@ && $(LN_S) $< . diff --git a/contrib/pg_autovacuum/README.pg_autovacuum b/contrib/pg_autovacuum/README.pg_autovacuum deleted file mode 100644 index f20842135b..0000000000 --- a/contrib/pg_autovacuum/README.pg_autovacuum +++ /dev/null @@ -1,246 +0,0 @@ -pg_autovacuum README --------------------- - -pg_autovacuum is a libpq client program that monitors all the -databases associated with a PostgreSQL server. It uses the statistics -collector to monitor insert, update and delete activity. - -When a table exceeds a insert or delete threshold (for more detail on -thresholds, see "Vacuum and Analyze" below) then that table will be -vacuumed and/or analyzed. - -This allows PostgreSQL to keep the FSM (Free Space Map) and table -statistics up to date, and eliminates the need to schedule periodic -vacuums. - -The primary benefit of pg_autovacuum is that the FSM and table -statistic information are updated more nearly as frequently as needed. -When a table is actively changing, pg_autovacuum will perform the -VACUUMs and ANALYZEs that such a table needs, whereas if a table -remains static, no cycles will be wasted performing this -unnecessarily. - -A secondary benefit of pg_autovacuum is that it ensures that a -database wide vacuum is performed prior to XID wraparound. This is an -important, if rare, problem, as failing to do so can result in major -data loss. (See the section in the _Administrator's Guide_ entitled -"Preventing transaction ID wraparound failures" for more details.) - -KNOWN ISSUES: -------------- - -pg_autovacuum has been tested under Redhat Linux (by me) and Debian -GNU/Linux, Solaris, and AIX (by Christopher B. Browne) and all known -bugs have been resolved. Please report any problems to the hackers -list. - -pg_autovacuum requires that the statistics system be enabled and -reporting row level stats. The overhead of the stats system has been -shown to be significant under certain workloads. For instance, a -tight loop of queries performing "select 1" was found to run nearly -30% slower when row-level stats were enabled. However, in practice, -with more realistic workloads, the stats system overhead is usually -nominal. - -pg_autovacuum does not get started automatically by either the -postmaster or by pg_ctl. Similarly, when the postmaster exits, no one -tells pg_autovacuum. The result of that is that at the start of the -next loop, pg_autovacuum will fail to connect to the server and -exit(). Any time it fails to connect pg_autovacuum exit()s. - -While pg_autovacuum can manage vacuums for as many databases as you -may have tied to a particular PostgreSQL postmaster, it can only -connect to a single PostgreSQL postmaster. Thus, if you have multiple -postmasters on a particular host, you will need multiple pg_autovacuum -instances, and they have no way, at present, to coordinate between one -another to ensure that they do not concurrently vacuum big tables. - -When installed as a service under Windows, there is currently no way to -know the name of the PostgreSQL server service (if there even is one) -so it is not possible to specify a startup dependency. It is therefore -possible for pg_autovacuum to start before the server. - -When installed as a service under Windows, if the -P option is used to -specify the connection password, this option (and the password) is -stored in plain text in the registry. - -TODO: ------ - -At present, there are no sample scripts to automatically start up -pg_autovacuum along with the database. It would be desirable to have -a SysV script to start up pg_autovacuum after PostgreSQL has been -started. - -Some users have expressed interest in making pg_autovacuum more -configurable so that certain tables known to be inactive could be -excluded from being vacuumed. It would probably make sense to -introduce this sort of functionality by providing arguments to specify -the database and schema in which to find a configuration table. - -It would also be desirable for the daemon to monitor how busy the -system is, with a view to deferring vacuums until there is less other -activity. - -INSTALL: --------- - -As of postgresql v7.4 pg_autovacuum is included in the main source -tree under contrib. Therefore you merely need to "make && make -install" (similar to most other contrib modules) and it will be -installed for you. - -If you are using an earlier version of PostgreSQL, uncompress the -tar.gz file into the contrib directory and modify the contrib/Makefile -to include the pg_autovacuum directory. pg_autovacuum will then be -built as part of the standard postgresql install. It is known to work -with v7.3 releases; it is not presently compatible with v7.2. - -make sure that the following are set in postgresql.conf: - - stats_start_collector = true - stats_row_level = true - -Start up the postmaster, then execute the pg_autovacuum executable. - -If you have a script that automatically starts up the PostgreSQL -instance, you might add in, after that, something similar to the -following: - - sleep 10 # To give the database some time to start up - $PGBINS/pg_autovacuum -D -s $SBASE -S $SSCALE ... [other arguments] - -Command line arguments: ------------------------ - -pg_autovacuum has the following optional arguments: - --d debug: 0 silent, 1 basic info, 2 more debug info, etc... --D daemonize: Detach from tty and run in background. --s sleep base value: see "Sleeping" below. --S sleep scaling factor: see "Sleeping" below. --v vacuum base threshold: see "Vacuum and Analyze" below. --V vacuum scaling factor: see "Vacuum and Analyze" below. --a analyze base threshold: see "Vacuum and Analyze" below. --A analyze scaling factor: see "Vacuum and Analyze" below. --i update interval: how often (in terms of iterations of the primary loop - over the database list) to update the database list. The default is 2, - which means the list will be updated before every other pass through - the database list. --L log file: Name of file to which output is submitted, otherwise STDERR --U username: Username pg_autovacuum will use to connect with, if not - specified the current username is used. --P password: Password pg_autovacuum will use to connect with. *WARNING* - This option is insecure. When installed as a Windows Service, this - option will be stored in plain text in the registry. When used with - most Unix variants, other users will be able to see the argument to - the "-P" option via ps(1). The ~/.pgpass file can be used to - specify a password more securely. --H host: host name or IP to connect to. --p port: port used for connection. --h help: list of command line options. - -The following 5 autovacuum command line options correspond to the various -cost-based vacuum settings. If not given, then the cluster default values -will be used. - --c vacuum_cost_delay --C vacuum_cost_page_hit --m vacuum_cost_page_miss --n vacuum_cost_page_dirty --l vacuum_cost_limit - - -Numerous arguments have default values defined in pg_autovacuum.h. At -the time of writing they are: - --d 1 --v 1000 --V 2 --a 500 (half of -v if not specified) --A 1 (half of -V if not specified) --s 300 (5 minutes) --S 2 --i 2 - -The following arguments are used on Windows only: - --I Install the executable as a Windows service. Other appropriate command - line options will be stored in the registry and passed to the service - at startup. *WARNING* This includes the connection password which will - be stored in plain text. - --N service user: Name of the Windows user account under which the service - will run. Only used when installing as a Windows service. - --W service password: The password for the service account. Only used when - installing as a Windows service. - --R Uninstall pg_autovacuum as a service. - --E Dependent service that must start before this service. Normally this will be - a PostgreSQL instance, e.g. "-E pgsql-8.0.0". Only used when installing as - a Windows service. - -Vacuum and Analyze: -------------------- - -pg_autovacuum performs either a VACUUM ANALYZE or just ANALYZE -depending on the mixture of table activity (insert, update, or -delete): - -- If the number of (inserts + updates + deletes) > AnalyzeThreshold, then - only an analyze is performed. - -- If the number of (deletes + updates) > VacuumThreshold, then a - vacuum analyze is performed. - -VacuumThreshold is equal to: - vacuum_base_value + (vacuum_scaling_factor * "number of tuples in the table") - -AnalyzeThreshold is equal to: - analyze_base_value + (analyze_scaling_factor * "number of tuples in the table") - -The AnalyzeThreshold defaults to half of the VacuumThreshold since it -represents a much less expensive operation (approx 5%-10% of vacuum), -and running ANALYZE more often should not substantially degrade system -performance. - -Sleeping: ---------- - -pg_autovacuum sleeps for a while after it is done checking all the -databases. It does this in order to limit the amount of system -resources it consumes. This allows the system administrator to -configure pg_autovacuum to be more or less aggressive. - -Reducing the sleep time will cause pg_autovacuum to respond more -quickly to changes, whether they be database addition/removal, table -addition/removal, or just normal table activity. - -On the other hand, setting pg_autovacuum to sleep values too -aggressively (to too short periods of time) can have a negative effect -on server performance. For instance, if a table gets vacuumed 5 times -during the course of a large set of updates, this is likely to take a -lot more work than if the table was vacuumed just once, at the end. - -The total time it sleeps is equal to: - - base_sleep_value + sleep_scaling_factor * "duration of the previous - loop" - -Note that timing measurements are made in seconds; specifying -"pg_vacuum -s 1" means pg_autovacuum could poll the database up to 60 -times minute. In a system with large tables where vacuums may run for -several minutes, rather longer times between vacuums are likely to be -appropriate. - -What pg_autovacuum monitors: ----------------------------- - -pg_autovacuum dynamically generates a list of all databases and tables -that exist on the server. It will dynamically add and remove -databases and tables that are removed from the database server while -pg_autovacuum is running. Overhead is fairly small per object. For -example: 10 databases with 10 tables each appears to less than 10k of -memory on my Linux box. diff --git a/contrib/pg_autovacuum/TODO b/contrib/pg_autovacuum/TODO deleted file mode 100644 index b93f2e2494..0000000000 --- a/contrib/pg_autovacuum/TODO +++ /dev/null @@ -1,45 +0,0 @@ -Todo Items for pg_autovacuum client --------------------------------------------------------------------------- - -_Add Startup Message (with datetime stamp) to Logfile when starting and logging - -_create a FSM export function and see if I can use it for pg_autovacuum - -_look into possible benifits of pgstattuple contrib work - -_Continue trying to reduce server load created by polling. - -Done: --------------------------------------------------------------------------- -_Check if required pg_stats are enables, if not exit with error - -_Reduce the number connections and queries to the server - _Make database adding and removal part of the normal loop - _make table adding and removal part of the normal loop - -_Separate logic for vacuum and analyze - -_all pg_autovacuum specific functions are now static - -_correct usage of snprintf - -_reworked database and table update functions, now they - use the existing database connection and only one query - -_fixed -h option output - -_cleanup of 'constant == variable' used much more consistently now. - -_Guarantee database wide vacuum prior to Xid wraparound - -_change name to pg_autovacuum - -_Add proper table and database removal functions so that we can properly - clear up before we exit, and make sure we don't leak memory when removing tables and such. - -_Decouple insert and delete thresholds - -_Fix Vacuum debug routine to include the database name. - -_Allow it to detach from the tty - diff --git a/contrib/pg_autovacuum/pg_autovacuum.c b/contrib/pg_autovacuum/pg_autovacuum.c deleted file mode 100644 index 36b10d657b..0000000000 --- a/contrib/pg_autovacuum/pg_autovacuum.c +++ /dev/null @@ -1,1862 +0,0 @@ -/* pg_autovacuum.c - * All the code for the pg_autovacuum program - * (c) 2003 Matthew T. O'Connor - * Revisions by Christopher B. Browne, Liberty RMS - * Win32 Service code added by Dave Page - * - * $PostgreSQL: pgsql/contrib/pg_autovacuum/pg_autovacuum.c,v 1.35 2005/06/15 13:55:23 momjian Exp $ - */ - -#include "postgres_fe.h" - -#include -#ifdef HAVE_GETOPT_H -#include -#endif -#include -#include -#ifdef WIN32 -#include -#endif -#include -#include - -#include "pg_autovacuum.h" - -#ifdef WIN32 -SERVICE_STATUS ServiceStatus; -SERVICE_STATUS_HANDLE hStatus; -int appMode = 0; -char deps[255]; -#endif - -/* define atooid */ -#define atooid(x) ((Oid) strtoul((x), NULL, 10)) - - -static cmd_args *args; -static FILE *LOGOUTPUT; -static char logbuffer[4096]; - - -/* The main program loop function */ -static int VacuumLoop(int argc, char **argv); - -/* 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 */ -#ifndef WIN32 -static void daemonize(void); -#endif -static void log_entry(const char *logentry, int level); - -#ifdef WIN32 -/* Windows Service related functions */ -static void ControlHandler(DWORD request); -static int InstallService(); -static int RemoveService(); -#endif - - -static void -log_entry(const char *logentry, int level) -{ - /* - * Note: Under Windows we dump the log entries to the normal - * stderr/logfile as well, otherwise it can be a pain to debug - * service install failures etc. - */ - - time_t curtime; - struct tm *loctime; - char timebuffer[128], - slevel[10]; - - -#ifdef WIN32 - static HANDLE evtHandle = INVALID_HANDLE_VALUE; - static int last_level; - WORD elevel; -#endif - - switch (level) - { - case LVL_DEBUG: - sprintf(slevel, "DEBUG: "); - break; - - case LVL_INFO: - sprintf(slevel, "INFO: "); - break; - - case LVL_WARNING: - sprintf(slevel, "WARNING: "); - break; - - case LVL_ERROR: - sprintf(slevel, "ERROR: "); - break; - - case LVL_EXTRA: - sprintf(slevel, " "); - break; - - default: - sprintf(slevel, " "); - break; - } - - curtime = time(NULL); - loctime = localtime(&curtime); - strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S %Z", loctime); - fprintf(LOGOUTPUT, "[%s] %s%s\n", timebuffer, slevel, logentry); - -#ifdef WIN32 - - /* Restore the previous level if this is extra info */ - if (level == LVL_EXTRA) - level = last_level; - last_level = level; - - switch (level) - { - case LVL_DEBUG: - elevel = EVENTLOG_INFORMATION_TYPE; - break; - - case LVL_INFO: - elevel = EVENTLOG_SUCCESS; - break; - - case LVL_WARNING: - elevel = EVENTLOG_WARNING_TYPE; - break; - - case LVL_ERROR: - elevel = EVENTLOG_ERROR_TYPE; - break; - - default: - elevel = EVENTLOG_SUCCESS; - break; - } - - if (evtHandle == INVALID_HANDLE_VALUE) - { - evtHandle = RegisterEventSource(NULL, "PostgreSQL Auto Vacuum"); - if (evtHandle == NULL) - { - evtHandle = INVALID_HANDLE_VALUE; - return; - } - } - - ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, &logentry, NULL); -#endif -} - -/* - * Function used to detach the pg_autovacuum daemon from the tty and go into - * the background. - * - * This code is ripped directly from pmdaemonize in postmaster.c. - */ -#ifndef WIN32 -static void -daemonize(void) -{ - int i; - pid_t pid; - - pid = fork(); - if (pid == (pid_t) -1) - { - log_entry("cannot disassociate from controlling TTY", LVL_ERROR); - 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("cannot disassociate from controlling TTY", LVL_ERROR); - fflush(LOGOUTPUT); - _exit(1); - } -#endif - i = open(NULL_DEV, O_RDWR); - dup2(i, 0); - dup2(i, 1); - dup2(i, 2); - close(i); -} -#endif /* WIN32 */ - -/* 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", LVL_ERROR); - 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", LVL_ERROR); - 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", LVL_ERROR); - 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) - { - dbi->conn = db_connect(dbi); - disconnect = 1; - } - - 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) - { - dbi->conn = db_connect(dbi); - disconnect = 1; - } - - 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, LVL_DEBUG); - } - } - } /* 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.%s from list.", tbl->dbi->dbname, tbl->table_name); - log_entry(logbuffer, LVL_DEBUG); - 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, LVL_INFO); - sprintf(logbuffer, " relid: %u; relisshared: %d", tbl->relid, tbl->relisshared); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " reltuples: %f; relpages: %u", tbl->reltuples, tbl->relpages); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " curr_analyze_count: %li; curr_vacuum_count: %li", - tbl->curr_analyze_count, tbl->curr_vacuum_count); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " last_analyze_count: %li; last_vacuum_count: %li", - tbl->CountAtLastAnalyze, tbl->CountAtLastVacuum); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " analyze_threshold: %li; vacuum_threshold: %li", - tbl->analyze_threshold, tbl->vacuum_threshold); - log_entry(logbuffer, LVL_INFO); - 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(void) -{ - Dllist *db_list = DLNewList(); - db_info *dbs = NULL; - PGresult *res = NULL; -#ifdef WIN32 - int k = 0; -#endif - - 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.", LVL_ERROR); - fflush(LOGOUTPUT); - return NULL; - } - - /* - * We do this just so we can set the proper oid for the template1 - * database - */ - dbs = ((db_info *) DLE_VAL(DLGetHead(db_list))); - dbs->conn = db_connect(dbs); - -#ifdef WIN32 - while (dbs->conn == NULL && !appMode && k < 10) - { - int j; - - /* Pause for 30 seconds to allow the database to start up */ - log_entry("Pausing 30 seconds to allow the database to startup completely", LVL_INFO); - fflush(LOGOUTPUT); - ServiceStatus.dwWaitHint = 10; - for (j=0; j<6; j++) - { - pg_usleep(5000000); - ServiceStatus.dwCheckPoint++; - SetServiceStatus(hStatus, &ServiceStatus); - fflush(LOGOUTPUT); - } - - /* now try again */ - log_entry("Attempting to connect again.", LVL_INFO); - dbs->conn = db_connect(dbs); - k++; - } -#endif - - if (dbs->conn != NULL) - { - res = send_query(FROZENOID_QUERY, dbs); - if (res != NULL) - { - dbs->oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid"))); - 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; - } - return db_list; -} - -/* 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; - PGresult *res = NULL; - Dlelem *db_elem = DLGetHead(db_list); - db_info *dbi = NULL; - db_info *dbi_template1 = DLE_VAL(db_elem); - int i = 0, - t = 0, - found_match = 0; - - if (args->debug >= 2) - { - log_entry("updating the database list", LVL_DEBUG); - fflush(LOGOUTPUT); - } - - if (dbi_template1->conn == NULL) - { - dbi_template1->conn = db_connect(dbi_template1); - disconnect = 1; - } - - if (dbi_template1->conn != NULL) - { - /* - * Get a result set that has all the information we will need to - * both remove databasews from the list that no longer exist and - * add databases to the list that are new - */ - res = send_query(FROZENOID_QUERY2, dbi_template1); - if (res != NULL) - { - t = PQntuples(res); - - /* - * First: use the db_list as the outer loop and the result set - * as the inner loop, this will determine what databases - * should be removed - */ - while (db_elem != NULL) - { - dbi = ((db_info *) DLE_VAL(db_elem)); - found_match = 0; - - for (i = 0; i < t; i++) - { /* loop through result set looking for a - * match */ - if (dbi->oid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid")))) - { - found_match = 1; - - /* - * update the dbi->age so that we ensure - * xid_wraparound won't happen - */ - dbi->age = atol(PQgetvalue(res, i, PQfnumber(res, "age"))); - break; - } - } - if (found_match == 0) - { /* then we didn't find this db_elem in the - * result set */ - Dlelem *elem_to_remove = db_elem; - - db_elem = DLGetSucc(db_elem); - remove_db_from_list(elem_to_remove); - } - else - db_elem = DLGetSucc(db_elem); - } /* Done removing dropped databases from - * the table_list */ - - /* - * Then loop use result set as outer loop and db_list as the - * inner loop to determine what databases are new - */ - for (i = 0; i < t; i++) - { - db_elem = DLGetHead(db_list); - found_match = 0; - while (db_elem != NULL) - { - dbi = ((db_info *) DLE_VAL(db_elem)); - if (dbi->oid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid")))) - { - found_match = 1; - break; - } - db_elem = DLGetSucc(db_elem); - } - if (found_match == 0) /* then we didn't find this result - * now in the tbl_list */ - { - DLAddTail(db_list, DLNewElem(init_dbinfo - (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, LVL_DEBUG); - } - } - } /* 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); - } -} - -/* xid_wraparound_check - -From the docs: - -With the standard freezing policy, the age column will start at one billion for a -freshly-vacuumed database. When the age approaches two billion, the database must -be vacuumed again to avoid risk of wraparound failures. Recommended practice is -to vacuum each database at least once every half-a-billion (500 million) transactions, -so as to provide plenty of safety margin. - -So we do a full database vacuum if age > 1.5billion -return 0 if nothing happened, -return 1 if the database needed a database wide vacuum -*/ -static int -xid_wraparound_check(db_info * dbi) -{ - /* - * FIXME: should probably do something better here so that we don't - * vacuum all the databases on the server at the same time. We have - * 500million xacts to work with so we should be able to spread the - * load of full database vacuums a bit - */ - if (dbi->age > 1500000000) - { - PGresult *res = NULL; - - res = send_query("VACUUM", dbi); - /* FIXME: Perhaps should add a check for PQ_COMMAND_OK */ - if (res != NULL) - PQclear(res); - return 1; - } - return 0; -} - -/* 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, LVL_DEBUG); - fflush(LOGOUTPUT); - } - DLRemove(db_to_remove); - if (dbi->conn) - db_disconnect(dbi); - if (dbi->dbname) - { - free(dbi->dbname); - dbi->dbname = NULL; - } - if (dbi->username) - { - free(dbi->username); - dbi->username = NULL; - } - if (dbi->password) - { - free(dbi->password); - dbi->password = NULL; - } - if (dbi->table_list) - { - free_tbl_list(dbi->table_list); - dbi->table_list = NULL; - } - if (dbi) - { - free(dbi); - dbi = NULL; - } - DLFreeElem(db_to_remove); -} - -/* 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); - Dlelem *db_elem_to_remove = NULL; - - while (db_elem != NULL) - { - db_elem_to_remove = db_elem; - db_elem = DLGetSucc(db_elem); - remove_db_from_list(db_elem_to_remove); - db_elem_to_remove = NULL; - } - DLFreeList(db_list); -} - -static void -print_db_list(Dllist *db_list, int print_table_lists) -{ - Dlelem *db_elem = DLGetHead(db_list); - - while (db_elem != NULL) - { - print_db_info(((db_info *) DLE_VAL(db_elem)), print_table_lists); - db_elem = DLGetSucc(db_elem); - } -} - -static void -print_db_info(db_info * dbi, int print_tbl_list) -{ - sprintf(logbuffer, "dbname: %s", (dbi->dbname) ? dbi->dbname : "(null)"); - log_entry(logbuffer, LVL_INFO); - - sprintf(logbuffer, " oid: %u", dbi->oid); - log_entry(logbuffer, LVL_INFO); - - sprintf(logbuffer, " username: %s", (dbi->username) ? dbi->username : "(null)"); - log_entry(logbuffer, LVL_INFO); - - sprintf(logbuffer, " password: %s", (dbi->password) ? dbi->password : "(null)"); - log_entry(logbuffer, LVL_INFO); - - if (dbi->conn != NULL) - log_entry(" conn is valid, (connected)", LVL_INFO); - else - log_entry(" conn is null, (not connected)", LVL_INFO); - - sprintf(logbuffer, " default_analyze_threshold: %li", dbi->analyze_threshold); - log_entry(logbuffer, LVL_INFO); - - sprintf(logbuffer, " default_vacuum_threshold: %li", dbi->vacuum_threshold); - log_entry(logbuffer, LVL_INFO); - - fflush(LOGOUTPUT); - if (print_tbl_list > 0) - print_table_list(dbi->table_list); -} - -/* End of DB List Management Function */ - -/* 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, LVL_ERROR); - 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) - { - PQfinish(dbi->conn); - dbi->conn = NULL; - } -} - -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; - - if (dbi->conn == NULL) - return NULL; - - if (args->debug >= 4) - log_entry(query, LVL_DEBUG); - - 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, LVL_ERROR); - sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res)); - log_entry(logbuffer, LVL_EXTRA); - 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, LVL_ERROR); - sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res)); - log_entry(logbuffer, LVL_EXTRA); - fflush(LOGOUTPUT); - PQclear(res); - return NULL; - } - return res; -} /* End of send_query() */ - -/* - * Perform either a vacuum or a vacuum analyze - */ -static void -perform_maintenance_command(db_info * dbi, tbl_info * tbl, int operation) -{ - char buf[256]; - - /* - * Set the vacuum_cost variables if supplied on command line - */ - if (args->av_vacuum_cost_delay != -1) - { - snprintf(buf, sizeof(buf), "set vacuum_cost_delay = %d", - args->av_vacuum_cost_delay); - send_query(buf, dbi); - } - if (args->av_vacuum_cost_page_hit != -1) - { - snprintf(buf, sizeof(buf), "set vacuum_cost_page_hit = %d", - args->av_vacuum_cost_page_hit); - send_query(buf, dbi); - } - if (args->av_vacuum_cost_page_miss != -1) - { - snprintf(buf, sizeof(buf), "set vacuum_cost_page_miss = %d", - args->av_vacuum_cost_page_miss); - send_query(buf, dbi); - } - if (args->av_vacuum_cost_page_dirty != -1) - { - snprintf(buf, sizeof(buf), "set vacuum_cost_page_dirty = %d", - args->av_vacuum_cost_page_dirty); - send_query(buf, dbi); - } - if (args->av_vacuum_cost_limit != -1) - { - snprintf(buf, sizeof(buf), "set vacuum_cost_limit = %d", - args->av_vacuum_cost_limit); - send_query(buf, dbi); - } - - /* - * if ((relisshared = t and database != template1) or - * if operation = ANALYZE_ONLY) - * then only do an analyze - */ - if ((tbl->relisshared > 0 && strcmp("template1", dbi->dbname) != 0) || - (operation == ANALYZE_ONLY)) - snprintf(buf, sizeof(buf), "ANALYZE %s.%s", dbi->dbname, tbl->table_name); - else if (operation == VACUUM_ANALYZE) - snprintf(buf, sizeof(buf), "VACUUM ANALYZE %s.%s", dbi->dbname, tbl->table_name); - else - return; - - if (args->debug >= 1) - { - sprintf(logbuffer, "Performing: %s on database %s", buf, dbi->dbname); - log_entry(logbuffer, LVL_DEBUG); - fflush(LOGOUTPUT); - } - - send_query(buf, dbi); - - update_table_thresholds(dbi, tbl, operation); - - if (args->debug >= 2) - print_table_info(tbl); -} - -static void -free_cmd_args(void) -{ - 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->update_interval = UPDATE_INTERVAL; -#ifndef WIN32 - args->daemonize = 0; -#else - args->service_dependencies = 0; - args->install_as_service = 0; - args->remove_as_service = 0; - args->service_user = 0; - args->service_password = 0; -#endif - args->user = 0; - args->password = 0; - args->host = 0; - args->logfile = 0; - args->port = 0; - - /* - * Cost-Based Vacuum Delay Settings for pg_autovacuum - */ - args->av_vacuum_cost_delay = -1; - args->av_vacuum_cost_page_hit = -1; - args->av_vacuum_cost_page_miss = -1; - args->av_vacuum_cost_page_dirty = -1; - args->av_vacuum_cost_limit = -1; - - /* - * Fixme: Should add some sanity checking such as positive integer - * values etc - */ -#ifndef WIN32 - while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hDc:C:m:n:l:")) != -1) -#else - while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hIRN:W:E:c:C:m:n:l:")) != -1) -#endif - { - 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 'i': - args->update_interval = atoi(optarg); - break; - case 'c': - args->av_vacuum_cost_delay = atoi(optarg); - break; - case 'C': - args->av_vacuum_cost_page_hit = atoi(optarg); - break; - case 'm': - args->av_vacuum_cost_page_miss = atoi(optarg); - break; - case 'n': - args->av_vacuum_cost_page_dirty = atoi(optarg); - break; - case 'l': - args->av_vacuum_cost_limit = atoi(optarg); - break; -#ifndef WIN32 - case 'D': - args->daemonize++; - break; -#endif - 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); -#ifdef WIN32 - case 'E': - /* - * CreateService() expects a list of service - * dependencies as a NUL-separated, double-NUL - * terminated list (although we only allow the user to - * specify a single dependency). So we zero out the - * list first, and make sure to leave room for two NUL - * terminators. - */ - ZeroMemory(deps, sizeof(deps)); - snprintf(deps, sizeof(deps) - 2, "%s", optarg); - args->service_dependencies = deps; - break; - case 'I': - args->install_as_service++; - break; - case 'R': - args->remove_as_service++; - break; - case 'N': - args->service_user = optarg; - break; - case 'W': - args->service_password = optarg; - break; -#endif - 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(void) -{ - int i = 0; - float f = 0; - - fprintf(stderr, "usage: pg_autovacuum \n"); -#ifndef WIN32 - fprintf(stderr, " [-D] Daemonize (Detach from tty and run in the background)\n"); -#else - fprintf(stderr, " [-I] Install as a Windows service\n"); - fprintf(stderr, " [-R] Remove as a Windows service (all other options will be ignored)\n"); - fprintf(stderr, " [-N] Username to run service as (only useful when installing as a Windows service)\n"); - fprintf(stderr, " [-W] Password to run service with (only useful when installing as a Windows service)\n"); - fprintf(stderr, " [-E] Dependent service that must start before this service (only useful when installing as a Windows service)\n"); -#endif - i = AUTOVACUUM_DEBUG; - fprintf(stderr, " [-d] debug (debug level=0,1,2,3; default=%d)\n", i); - - i = SLEEPBASEVALUE; - fprintf(stderr, " [-s] sleep base value (default=%d)\n", i); - f = SLEEPSCALINGFACTOR; - fprintf(stderr, " [-S] sleep scaling factor (default=%f)\n", f); - - i = VACBASETHRESHOLD; - fprintf(stderr, " [-v] vacuum base threshold (default=%d)\n", i); - f = VACSCALINGFACTOR; - fprintf(stderr, " [-V] vacuum scaling factor (default=%f)\n", f); - i = i / 2; - fprintf(stderr, " [-a] analyze base threshold (default=%d)\n", i); - f = f / 2; - fprintf(stderr, " [-A] analyze scaling factor (default=%f)\n", f); - - fprintf(stderr, " [-L] logfile (default=none)\n"); - - fprintf(stderr, " [-c] vacuum_cost_delay (default=none)\n"); - fprintf(stderr, " [-C] vacuum_cost_page_hit (default=none)\n"); - fprintf(stderr, " [-m] vacuum_cost_page_miss (default=none)\n"); - fprintf(stderr, " [-n] vacuum_cost_page_dirty (default=none)\n"); - fprintf(stderr, " [-l] vacuum_cost_limit (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(void) -{ - sprintf(logbuffer, "Printing command_args"); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->host=%s", (args->host) ? args->host : "(null)"); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->port=%s", (args->port) ? args->port : "(null)"); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->username=%s", (args->user) ? args->user : "(null)"); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->password=%s", (args->password) ? args->password : "(null)"); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->logfile=%s", (args->logfile) ? args->logfile : "(null)"); - log_entry(logbuffer, LVL_INFO); -#ifndef WIN32 - sprintf(logbuffer, " args->daemonize=%d", args->daemonize); - log_entry(logbuffer, LVL_INFO); -#else - sprintf(logbuffer, " args->install_as_service=%d", args->install_as_service); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->remove_as_service=%d", args->remove_as_service); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->service_dependencies=%s", (args->service_dependencies) ? args->service_dependencies : "(null)"); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->service_user=%s", (args->service_user) ? args->service_user : "(null)"); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->service_password=%s", (args->service_password) ? args->service_password : "(null)"); - log_entry(logbuffer, LVL_INFO); -#endif - - sprintf(logbuffer, " args->sleep_base_value=%d", args->sleep_base_value); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->sleep_scaling_factor=%f", args->sleep_scaling_factor); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->vacuum_base_threshold=%d", args->vacuum_base_threshold); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->vacuum_scaling_factor=%f", args->vacuum_scaling_factor); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->analyze_base_threshold=%d", args->analyze_base_threshold); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->analyze_scaling_factor=%f", args->analyze_scaling_factor); - log_entry(logbuffer, LVL_INFO); - sprintf(logbuffer, " args->update_interval=%i", args->update_interval); - log_entry(logbuffer, LVL_INFO); - - if (args->av_vacuum_cost_delay != -1) - sprintf(logbuffer, " args->av_vacuum_cost_delay=%d", args->av_vacuum_cost_delay); - else - sprintf(logbuffer, " args->av_vacuum_cost_delay=(default)"); - log_entry(logbuffer, LVL_INFO); - if (args->av_vacuum_cost_page_hit != -1) - sprintf(logbuffer, " args->av_vacuum_cost_page_hit=%d", args->av_vacuum_cost_page_hit); - else - sprintf(logbuffer, " args->av_vacuum_cost_page_hit=(default)"); - log_entry(logbuffer, LVL_INFO); - if (args->av_vacuum_cost_page_miss != -1) - sprintf(logbuffer, " args->av_vacuum_cost_page_miss=%d", args->av_vacuum_cost_page_miss); - else - sprintf(logbuffer, " args->av_vacuum_cost_page_miss=(default)"); - log_entry(logbuffer, LVL_INFO); - if (args->av_vacuum_cost_page_dirty != -1) - sprintf(logbuffer, " args->av_vacuum_cost_page_dirty=%d", args->av_vacuum_cost_page_dirty); - else - sprintf(logbuffer, " args->av_vacuum_cost_page_dirty=(default)"); - log_entry(logbuffer, LVL_INFO); - if (args->av_vacuum_cost_limit != -1) - sprintf(logbuffer, " args->av_vacuum_cost_limit=%d", args->av_vacuum_cost_limit); - else - sprintf(logbuffer, " args->av_vacuum_cost_limit=(default)"); - log_entry(logbuffer, LVL_INFO); - - sprintf(logbuffer, " args->debug=%d", args->debug); - log_entry(logbuffer, LVL_INFO); - - fflush(LOGOUTPUT); -} - -#ifdef WIN32 - -/* Handle control requests from the Service Control Manager */ -static void -ControlHandler(DWORD request) -{ - switch (request) - { - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - log_entry("pg_autovacuum service stopping...", LVL_INFO); - fflush(LOGOUTPUT); - ServiceStatus.dwWin32ExitCode = 0; - ServiceStatus.dwCurrentState = SERVICE_STOPPED; - SetServiceStatus(hStatus, &ServiceStatus); - return; - - default: - break; - } - - /* Report current status */ - SetServiceStatus(hStatus, &ServiceStatus); - - return; -} - -/* Register with the Service Control Manager */ -static int -InstallService(void) -{ - SC_HANDLE schService = NULL; - SC_HANDLE schSCManager = NULL; - char szFilename[MAX_PATH], - szKey[MAX_PATH], - szCommand[MAX_PATH + 1024], - szMsgDLL[MAX_PATH]; - HKEY hk = NULL; - DWORD dwData = 0; - - /* - * Register the service with the SCM - */ - GetModuleFileName(NULL, szFilename, MAX_PATH); - - /* Open the Service Control Manager on the local computer. */ - schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); - if (!schSCManager) - return -1; - - schService = CreateService( - schSCManager, /* SCManager database */ - TEXT("pg_autovacuum"), /* Name of service */ - TEXT("PostgreSQL Auto Vacuum"), /* Name to display */ - SERVICE_ALL_ACCESS, /* Desired access */ - SERVICE_WIN32_OWN_PROCESS, /* Service type */ - SERVICE_AUTO_START, /* Start type */ - SERVICE_ERROR_NORMAL, /* Error control type */ - szFilename, /* Service binary */ - NULL, /* No load ordering group */ - NULL, /* No tag identifier */ - args->service_dependencies, /* Dependencies */ - args->service_user, /* Service account */ - args->service_password); /* Account password */ - - if (!schService) - return -2; - - /* - * Rewrite the command line for the service - */ - sprintf(szKey, "SYSTEM\\CurrentControlSet\\Services\\pg_autovacuum"); - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_ALL_ACCESS, &hk)) - return -3; - - /* Build the command line */ - sprintf(szCommand, "\"%s\"", szFilename); - if (args->host) - sprintf(szCommand, "%s -H %s", szCommand, args->host); - if (args->port) - sprintf(szCommand, "%s -p %s", szCommand, args->port); - if (args->user) - sprintf(szCommand, "%s -U \"%s\"", szCommand, args->user); - if (args->password) - sprintf(szCommand, "%s -P \"%s\"", szCommand, args->password); - if (args->logfile) - sprintf(szCommand, "%s -L \"%s\"", szCommand, args->logfile); - if (args->sleep_base_value != (int) SLEEPBASEVALUE) - sprintf(szCommand, "%s -s %d", szCommand, args->sleep_base_value); - if (args->sleep_scaling_factor != (float) SLEEPSCALINGFACTOR) - sprintf(szCommand, "%s -S %f", szCommand, args->sleep_scaling_factor); - if (args->vacuum_base_threshold != (int) VACBASETHRESHOLD) - sprintf(szCommand, "%s -v %d", szCommand, args->vacuum_base_threshold); - if (args->vacuum_scaling_factor != (float) VACSCALINGFACTOR) - sprintf(szCommand, "%s -V %f", szCommand, args->vacuum_scaling_factor); - if (args->analyze_base_threshold != (int) (VACBASETHRESHOLD / 2)) - sprintf(szCommand, "%s -a %d", szCommand, args->analyze_base_threshold); - if (args->analyze_scaling_factor != (float) (VACSCALINGFACTOR / 2)) - sprintf(szCommand, "%s -A %f", szCommand, args->analyze_scaling_factor); - if (args->debug != (int) AUTOVACUUM_DEBUG) - sprintf(szCommand, "%s -d %d", szCommand, args->debug); - if (args->av_vacuum_cost_delay != -1) - sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_delay); - if (args->av_vacuum_cost_page_hit != -1) - sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_page_hit); - if (args->av_vacuum_cost_page_miss != -1) - sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_page_miss); - if (args->av_vacuum_cost_page_dirty != -1) - sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_page_dirty); - if (args->av_vacuum_cost_limit != -1) - sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_limit); - - /* And write the new value */ - if (RegSetValueEx(hk, "ImagePath", 0, REG_EXPAND_SZ, (LPBYTE) szCommand, (DWORD) strlen(szCommand) + 1)) - return -4; - RegCloseKey(hk); - - /* - * Set the Event source for the application log - */ - sprintf(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\PostgreSQL Auto Vacuum"); - if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hk, NULL)) - return -5; - - /* TODO Try to find pgevent.dll, rather than hope it's in the path. ! */ - /* Message DLL */ - sprintf(szMsgDLL, "pgevent.dll"); - if (RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE) szMsgDLL, (DWORD) strlen(szMsgDLL) + 1)) - return -6; - - /* Set the event types supported */ - dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | EVENTLOG_SUCCESS; - if (RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD, (LPBYTE) & dwData, sizeof(DWORD))) - return -9; - - RegCloseKey(hk); - return 0; -} - -/* Unregister from the Service Control Manager */ -static int -RemoveService(void) -{ - SC_HANDLE schService = NULL; - SC_HANDLE schSCManager = NULL; - char szKey[MAX_PATH]; - HKEY hk = NULL; - - /* Open the SCM */ - schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); - if (!schSCManager) - return -1; - - /* Open the service */ - schService = OpenService(schSCManager, TEXT("pg_autovacuum"), SC_MANAGER_ALL_ACCESS); - if (!schService) - return -2; - - /* Now delete the service */ - if (!DeleteService(schService)) - return -3; - - /* - * Remove the Event source from the application log - */ - sprintf(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"); - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_ALL_ACCESS, &hk)) - return -4; - if (RegDeleteKey(hk, "PostgreSQL Auto Vacuum")) - return -5; - - return 0; -} -#endif /* WIN32 */ - -static -int -VacuumLoop(int argc, char **argv) -{ - 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; - -#ifdef WIN32 - - if (appMode) - log_entry("pg_autovacuum starting in Windows Application mode", LVL_INFO); - else - log_entry("pg_autovacuum starting in Windows Service mode", LVL_INFO); - - ServiceStatus.dwServiceType = SERVICE_WIN32; - ServiceStatus.dwCurrentState = SERVICE_START_PENDING; - ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; - ServiceStatus.dwWin32ExitCode = 0; - ServiceStatus.dwServiceSpecificExitCode = 0; - ServiceStatus.dwCheckPoint = 0; - ServiceStatus.dwWaitHint = 0; - - if (!appMode) - { - hStatus = RegisterServiceCtrlHandler("pg_autovacuum", (LPHANDLER_FUNCTION) ControlHandler); - if (hStatus == (SERVICE_STATUS_HANDLE) 0) - return -1; - } -#endif /* WIN32 */ - - /* 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("GUC variable stats_row_level must be enabled.", LVL_ERROR); - log_entry(" Please fix the problems and try again.", LVL_EXTRA); - fflush(LOGOUTPUT); - - exit(1); - } - - gettimeofday(&then, 0); /* for use later to caluculate sleep time */ - -#ifndef WIN32 - while (1) -#else - /* We can now report the running status to SCM. */ - ServiceStatus.dwCurrentState = SERVICE_RUNNING; - if (!appMode) - SetServiceStatus(hStatus, &ServiceStatus); - - while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) -#endif - { - /* Main Loop */ - - db_elem = DLGetHead(db_list); /* Reset cur_db_node to the - * beginning of the db_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) - { /* Serious problem: We can't connect to - * template1 */ - log_entry("Cannot connect to template1, exiting.", LVL_ERROR); - fflush(LOGOUTPUT); - fclose(LOGOUTPUT); -#ifdef WIN32 - ServiceStatus.dwCurrentState = SERVICE_STOPPED; - ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - ServiceStatus.dwServiceSpecificExitCode = -1; - if (!appMode) - SetServiceStatus(hStatus, &ServiceStatus); -#endif - exit(1); - } - } - - if (loops % args->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 % args->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 */ - if (res != NULL) - { - 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) - perform_maintenance_command(dbs, tbl, VACUUM_ANALYZE); - else if (tbl->curr_analyze_count - tbl->CountAtLastAnalyze >= tbl->analyze_threshold) - perform_maintenance_command(dbs, tbl, ANALYZE_ONLY); - - break; /* We found a match, no need to keep looping. */ - } - - /* - * 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()) */ - /* Done working on this db, Clean up, then advance cur_db */ - PQclear(res); - res = NULL; - db_disconnect(dbs); - } - 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, - "%d All DBs checked in: %.0f usec, will sleep for %d secs.", - loops, diff, sleep_secs); - log_entry(logbuffer, LVL_DEBUG); - fflush(LOGOUTPUT); - } - - /* Larger Pause between outer loops */ - /* - * pg_usleep() is wrong here because its maximum is ~2000 seconds, - * and we don't need signal interruptability on Win32 here. - */ -#ifndef WIN32 - sleep(sleep_secs); /* Unix sleep is seconds */ -#else - sleep(sleep_secs * 1000); /* Win32 sleep() is milliseconds */ -#endif - - 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 0; -} - -/* Beginning of AutoVacuum Main Program */ -int -main(int argc, char *argv[]) -{ - -#ifdef WIN32 - LPVOID lpMsgBuf; - SERVICE_TABLE_ENTRY ServiceTable[2]; -#endif - - args = get_cmd_args(argc, argv); /* Get Command Line Args and put - * them in the args struct */ -#ifndef WIN32 - /* Dameonize if requested */ - if (args->daemonize == 1) - daemonize(); -#endif - - 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(); - -#ifdef WIN32 - /* Install as a Windows service if required */ - if (args->install_as_service) - { - if (InstallService() != 0) - { - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL); - fprintf(stderr, "Error: %s\n", (char *) lpMsgBuf); - exit(-1); - } - else - { - fprintf(stderr, "Successfully installed pg_autovacuum as a service.\n"); - exit(0); - } - } - - /* Remove as a Windows service if required */ - if (args->remove_as_service) - { - if (RemoveService() != 0) - { - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL); - fprintf(stderr, "Error: %s\n", (char *) lpMsgBuf); - exit(-1); - } - else - { - fprintf(stderr, "Successfully removed pg_autovacuum as a service.\n"); - exit(0); - } - } - - /* Normal service startup */ - ServiceTable[0].lpServiceName = "pg_autovacuum"; - ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION) VacuumLoop; - - ServiceTable[1].lpServiceName = NULL; - ServiceTable[1].lpServiceProc = NULL; - - /* Start the control dispatcher thread for our service */ - if (!StartServiceCtrlDispatcher(ServiceTable)) - { - appMode = 1; - VacuumLoop(0, NULL); - } - -#else /* Unix */ - - /* Call the main program loop. */ - VacuumLoop(0, NULL); -#endif /* WIN32 */ - - return EXIT_SUCCESS; -} diff --git a/contrib/pg_autovacuum/pg_autovacuum.h b/contrib/pg_autovacuum/pg_autovacuum.h deleted file mode 100644 index 5c6186d7fb..0000000000 --- a/contrib/pg_autovacuum/pg_autovacuum.h +++ /dev/null @@ -1,121 +0,0 @@ -/* pg_autovacuum.h - * Header file for pg_autovacuum.c - * (c) 2003 Matthew T. O'Connor - * - * $PostgreSQL: pgsql/contrib/pg_autovacuum/pg_autovacuum.h,v 1.15 2005/04/19 03:35:15 momjian Exp $ - */ - -#ifndef _PG_AUTOVACUUM_H -#define _PG_AUTOVACUUM_H - -#include "libpq-fe.h" -#include "lib/dllist.h" - -#define AUTOVACUUM_DEBUG 0 -#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 from pg_class a, pg_stat_all_tables b where a.oid=b.relid and a.relkind = 'r'" - -#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'" - -/* Log levels */ -enum -{ - LVL_DEBUG = 1, - LVL_INFO, - LVL_WARNING, - LVL_ERROR, - LVL_EXTRA -}; - -/* define cmd_args stucture */ -typedef struct cmdargs -{ - int vacuum_base_threshold, - analyze_base_threshold, - update_interval, - sleep_base_value, - debug, - - /* - * Cost-Based Vacuum Delay Settings for pg_autovacuum - */ - av_vacuum_cost_delay, - av_vacuum_cost_page_hit, - av_vacuum_cost_page_miss, - av_vacuum_cost_page_dirty, - av_vacuum_cost_limit, - -#ifndef WIN32 - daemonize; -#else - install_as_service, - remove_as_service; -#endif - float vacuum_scaling_factor, - analyze_scaling_factor, - sleep_scaling_factor; - char *user, - *password, -#ifdef WIN32 - *service_dependencies, - *service_user, - *service_password, -#endif - *host, - *logfile, - *port; -} cmd_args; - -/* - * Might need to add a time value for last time the whole database was - * vacuumed. We need to guarantee this happens approx every 1Billion TX's - */ -typedef struct dbinfo -{ - 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; -} db_info; - -typedef 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 */ -} tbl_info; - -#endif /* _PG_AUTOVACUUM_H */