diff -c pg_autovacuum.orig/README.pg_autovacuum pg_autovacuum/README.pg_autovacuum *** pg_autovacuum.orig/README.pg_autovacuum Mon Apr 19 22:30:18 2004 --- pg_autovacuum/README.pg_autovacuum Mon May 10 09:29:07 2004 *************** *** 79,91 **** 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: --- 79,95 ---- 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. ! ! Under Windows the normal build procedure outlined above will work just ! fine under MSYS. The resulting .exe can then be run from the command line ! or as a service by installing with the -I option. make sure that the following are set in postgresql.conf: *************** *** 107,113 **** 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. --- 111,122 ---- 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 (not applicable on Windows) ! -I install service: Install as a service on Windows (other applicable options ! specified will be used for future startups). ! -R remove service: Remove Windows service. ! -N service user: The username to run the Windows service under. ! -W service password: The password to run the Windows service under. -s sleep base value: see "Sleeping" below. -S sleep scaling factor: see "Sleeping" below. -v vacuum base threshold: see "Vacuum and Analyze" below. diff -c pg_autovacuum.orig/TODO pg_autovacuum/TODO *** pg_autovacuum.orig/TODO Mon Mar 22 03:57:08 2004 --- pg_autovacuum/TODO Mon May 10 09:21:00 2004 *************** *** 7,13 **** _look into possible benifits of pgstattuple contrib work ! _Continue trying to reduce server load created by polling. Done: -------------------------------------------------------------------------- --- 7,15 ---- _look into possible benifits of pgstattuple contrib work ! _Continue trying to reduce server load created by polling. ! ! _Use a message resource file under Win32 to get nicely formatted event log entries Done: -------------------------------------------------------------------------- diff -c pg_autovacuum.orig/pg_autovacuum.c pg_autovacuum/pg_autovacuum.c *** pg_autovacuum.orig/pg_autovacuum.c Fri Apr 2 00:35:35 2004 --- pg_autovacuum/pg_autovacuum.c Tue May 11 09:28:11 2004 *************** *** 1,25 **** /* 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); } /* --- 1,113 ---- /* 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 */ ! #include "pg_autovacuum.h" + + #ifdef WIN32 + #include + + SERVICE_STATUS ServiceStatus; + SERVICE_STATUS_HANDLE hStatus; + int appMode = 0; + #endif ! FILE *LOGOUTPUT; ! char logbuffer[4096]; 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, 1, 0, NULL, 1, 0, &logentry, NULL); ! #endif } /* *************** *** 28,34 **** * * This code is mostly ripped directly from pm_dameonize in postmaster.c with * unneeded code removed. ! */ static void daemonize() { --- 116,123 ---- * * This code is mostly ripped directly from pm_dameonize in postmaster.c with * unneeded code removed. ! */ ! #ifndef WIN32 static void daemonize() { *************** *** 37,43 **** pid = fork(); if (pid == (pid_t) -1) { ! log_entry("Error: cannot disassociate from controlling TTY"); fflush(LOGOUTPUT); _exit(1); } --- 126,132 ---- pid = fork(); if (pid == (pid_t) -1) { ! log_entry("cannot disassociate from controlling TTY", LVL_ERROR); fflush(LOGOUTPUT); _exit(1); } *************** *** 52,64 **** #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 * --- 141,154 ---- #ifdef HAVE_SETSID if (setsid() < 0) { ! log_entry("cannot disassociate from controlling TTY", LVL_ERROR); fflush(LOGOUTPUT); _exit(1); } #endif ! } ! #endif /* WIN32 */ /* Create and return tbl_info struct with initialized to values from row or res */ static tbl_info * *************** *** 68,74 **** if (!new_tbl) { ! log_entry("init_table_info: Cannot get memory"); fflush(LOGOUTPUT); return NULL; } --- 158,164 ---- if (!new_tbl) { ! log_entry("init_table_info: Cannot get memory", LVL_ERROR); fflush(LOGOUTPUT); return NULL; } *************** *** 82,88 **** 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; } --- 172,178 ---- 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; } *************** *** 94,100 **** 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; } --- 184,190 ---- 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; } *************** *** 285,291 **** { 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 */ --- 375,381 ---- { 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 */ *************** *** 308,314 **** if (args->debug >= 1) { sprintf(logbuffer, "Removing table: %s from list.", tbl->table_name); ! log_entry(logbuffer); fflush(LOGOUTPUT); } DLRemove(tbl_to_remove); --- 398,404 ---- if (args->debug >= 1) { sprintf(logbuffer, "Removing table: %s from list.", tbl->table_name); ! log_entry(logbuffer, LVL_DEBUG); fflush(LOGOUTPUT); } DLRemove(tbl_to_remove); *************** *** 363,382 **** 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); } --- 453,472 ---- 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: %i", 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); } *************** *** 395,401 **** 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; } --- 485,491 ---- 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; } *************** *** 470,476 **** if (args->debug >= 2) { ! log_entry("updating the database list"); fflush(LOGOUTPUT); } --- 560,566 ---- if (args->debug >= 2) { ! log_entry("updating the database list", LVL_DEBUG); fflush(LOGOUTPUT); } *************** *** 556,562 **** 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 */ --- 646,652 ---- 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 */ *************** *** 614,620 **** if (args->debug >= 1) { sprintf(logbuffer, "Removing db: %s from list.", dbi->dbname); ! log_entry(logbuffer); fflush(LOGOUTPUT); } DLRemove(db_to_remove); --- 704,710 ---- if (args->debug >= 1) { sprintf(logbuffer, "Removing db: %s from list.", dbi->dbname); ! log_entry(logbuffer, LVL_DEBUG); fflush(LOGOUTPUT); } DLRemove(db_to_remove); *************** *** 682,708 **** 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) --- 772,798 ---- 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) *************** *** 725,731 **** { 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; --- 815,821 ---- { 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; *************** *** 767,773 **** return NULL; if (args->debug >= 4) ! log_entry(query); res = PQexec(dbi->conn, query); --- 857,863 ---- return NULL; if (args->debug >= 4) ! log_entry(query, LVL_DEBUG); res = PQexec(dbi->conn, query); *************** *** 776,784 **** 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; } --- 866,874 ---- 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; } *************** *** 788,796 **** 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; --- 878,886 ---- 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; *************** *** 824,831 **** 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; --- 914,928 ---- args->vacuum_scaling_factor = VACSCALINGFACTOR; args->analyze_base_threshold = -1; args->analyze_scaling_factor = -1; ! args->debug = AUTOVACUUM_DEBUG; ! #ifndef WIN32 ! args->daemonize = 0; ! #else ! 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; *************** *** 835,842 **** /* * 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) { --- 932,943 ---- /* * 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:hD")) != -1) ! #else ! while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hIRN:W:")) != -1) ! #endif { switch (c) { *************** *** 857,866 **** break; case 'A': args->analyze_scaling_factor = atof(optarg); ! break; case 'D': args->daemonize++; ! break; case 'd': args->debug = atoi(optarg); break; --- 958,969 ---- break; case 'A': args->analyze_scaling_factor = atof(optarg); ! break; ! #ifndef WIN32 case 'D': args->daemonize++; ! break; ! #endif case 'd': args->debug = atoi(optarg); break; *************** *** 881,887 **** break; case 'h': usage(); ! exit(0); default: /* --- 984,1004 ---- break; case 'h': usage(); ! exit(0); ! #ifdef WIN32 ! 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: /* *************** *** 912,919 **** 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); --- 1029,1043 ---- 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 (all other options will be ignored)\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"); ! #endif i = AUTOVACUUM_DEBUG; fprintf(stderr, " [-d] debug (debug level=0,1,2,3; default=%i)\n", i); *************** *** 945,1023 **** 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) --- 1069,1312 ---- print_cmd_args() { 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=%i", args->daemonize); ! log_entry(logbuffer, LVL_INFO); ! #else ! sprintf(logbuffer, " args->install_as_service=%i", args->install_as_service); ! log_entry(logbuffer, LVL_INFO); ! sprintf(logbuffer, " args->remove_as_service=%i", args->remove_as_service); ! 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=%i", 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=%i", 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=%i", 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->debug=%i", 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() ! { ! SC_HANDLE schService = NULL; ! SC_HANDLE schSCManager = NULL; ! char szFilename[MAX_PATH], ! szKey[MAX_PATH], ! szCommand[MAX_PATH+1024]; ! 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 */ ! NULL, /* 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 %i", 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 %i", 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 %i", 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 %i", szCommand, args->debug); ! ! /* 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 Create an actual message file and enable this! */ ! /* ! * sprintf(szFilename, "C:\\Path\\To\\EventLogMsg.dll"); ! * if (RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE)szFilename, (DWORD)strlen(szFilename)+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() ! { ! 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) ! { 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; ! #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; ! } ! } ! ! if (args->debug >= 1) ! log_entry("Pausing to allow the database to start", LVL_DEBUG); ! ! /* Delay for 10 secs to allow the DB to startup */ ! Sleep((DWORD)10000); ! ! if (args->debug >= 1) ! log_entry("Continuing startup", LVL_DEBUG); ! ! #endif /* WIN32 */ ! /* Init the db list with template1 */ db_list = init_db_list(); if (db_list == NULL) *************** *** 1025,1055 **** 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 */ 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("Error: Cannot connect to template1, exiting."); fflush(LOGOUTPUT); ! fclose(LOGOUTPUT); exit(1); } } --- 1314,1364 ---- 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 ! /* Set the service status but don't report it yet */ ! ServiceStatus.dwCurrentState = SERVICE_RUNNING; ! ! /* Now we can report the running status */ ! if (args->debug >= 1) ! log_entry("Sevice startup complete", LVL_DEBUG); ! 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); } } *************** *** 1119,1125 **** if (args->debug >= 1) { sprintf(logbuffer, "Performing: %s", buf); ! log_entry(logbuffer); fflush(LOGOUTPUT); } send_query(buf, dbs); --- 1428,1434 ---- if (args->debug >= 1) { sprintf(logbuffer, "Performing: %s", buf); ! log_entry(logbuffer, LVL_DEBUG); fflush(LOGOUTPUT); } send_query(buf, dbs); *************** *** 1133,1139 **** if (args->debug >= 1) { sprintf(logbuffer, "Performing: %s", buf); ! log_entry(logbuffer); fflush(LOGOUTPUT); } send_query(buf, dbs); --- 1442,1448 ---- if (args->debug >= 1) { sprintf(logbuffer, "Performing: %s", buf); ! log_entry(logbuffer, LVL_DEBUG); fflush(LOGOUTPUT); } send_query(buf, dbs); *************** *** 1175,1195 **** 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; } --- 1484,1600 ---- sprintf(logbuffer, "%i All DBs checked in: %.0f usec, will sleep for %i secs.", loops, diff, sleep_secs); ! log_entry(logbuffer, LVL_DEBUG); fflush(LOGOUTPUT); } ! ! #ifndef WIN32 ! sleep(sleep_secs); /* Larger Pause between outer loops */ ! #else ! Sleep((DWORD)(sleep_secs * 1000)); /* Larger Pause between outer loops */ ! #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); ! sprintf(logbuffer, "%s", (char *)lpMsgBuf); ! log_entry(logbuffer, LVL_ERROR); ! fflush(LOGOUTPUT); ! exit(-1); ! } ! else ! { ! log_entry("Successfully installed Windows service", LVL_INFO); ! fflush(LOGOUTPUT); ! 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); ! sprintf(logbuffer, "%s", (char *)lpMsgBuf); ! log_entry(logbuffer, LVL_ERROR); ! fflush(LOGOUTPUT); ! exit(-1); ! } ! else ! { ! log_entry("Successfully removed Windows service", LVL_INFO); ! fflush(LOGOUTPUT); ! 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; } Only in pg_autovacuum: pg_autovacuum.exe diff -c pg_autovacuum.orig/pg_autovacuum.h pg_autovacuum/pg_autovacuum.h *** pg_autovacuum.orig/pg_autovacuum.h Mon Mar 15 16:17:30 2004 --- pg_autovacuum/pg_autovacuum.h Fri May 7 10:53:10 2004 *************** *** 1,7 **** ! /* pg_autovacuum.h * Header file for pg_autovacuum.c * (c) 2003 Matthew T. O'Connor */ #include "postgres_fe.h" --- 1,9 ---- ! /* pg_autovacuum.hszCmdline * Header file for pg_autovacuum.c * (c) 2003 Matthew T. O'Connor */ + + #define FRONTEND #include "postgres_fe.h" *************** *** 23,29 **** #include "/usr/include/pgsql/server/lib/dllist.h" */ ! #define AUTOVACUUM_DEBUG 1 #define VACBASETHRESHOLD 1000 #define VACSCALINGFACTOR 2 #define SLEEPBASEVALUE 300 --- 25,31 ---- #include "/usr/include/pgsql/server/lib/dllist.h" */ ! #define AUTOVACUUM_DEBUG 0 #define VACBASETHRESHOLD 1000 #define VACSCALINGFACTOR 2 #define SLEEPBASEVALUE 300 *************** *** 36,65 **** #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 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'" /* 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; --- 38,85 ---- #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'" /* define atooid */ ! #define atooid(x) ((Oid) strtoul((x), NULL, 10)) ! ! /* Log levels */ ! enum ! { ! LVL_DEBUG = 1, ! LVL_INFO, ! LVL_WARNING, ! LVL_ERROR, ! LVL_EXTRA ! }; /* define cmd_args stucture */ struct cmdargs { ! int vacuum_base_threshold, ! analyze_base_threshold, ! sleep_base_value, ! debug, ! #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_user, ! *service_password, ! #endif ! *host, ! *logfile, ! *port; }; typedef struct cmdargs cmd_args; *************** *** 70,110 **** I think we need to guarantee this happens approx every 1Million TX's */ 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; }; 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[]); --- 90,132 ---- I think we need to guarantee this happens approx every 1Million TX's */ 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; }; 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; + + /* 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[]); *************** *** 137,142 **** 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); --- 159,175 ---- 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 ! !