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 Fri May 7 14:11:01 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,115 ---- /* 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 + + unsigned int sleep(); + + 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, 0, 1, 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() { --- 118,125 ---- * * 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); } --- 128,134 ---- 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 * --- 143,156 ---- #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; } --- 160,166 ---- 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; } --- 174,180 ---- 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; } --- 186,192 ---- 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 */ --- 377,383 ---- { 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); --- 400,406 ---- 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); } --- 455,474 ---- 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; } --- 487,493 ---- 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); } --- 562,568 ---- 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 */ --- 648,654 ---- 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); --- 706,712 ---- 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) --- 774,800 ---- 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; --- 817,823 ---- { 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); --- 859,865 ---- 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; } --- 868,876 ---- 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; --- 880,888 ---- 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; --- 916,930 ---- 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) { --- 934,945 ---- /* * 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; --- 960,971 ---- 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: /* --- 986,1006 ---- 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); --- 1031,1045 ---- 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"); ! #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) --- 1071,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], ! 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 */ ! 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! */ ! /* Message count */ ! sprintf(szMsgDLL, "pgmessages.dll"); ! if (RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE)szMsgDLL, (DWORD)strlen(szMsgDLL)+1)) return -6; ! ! /* Category message file */ ! if (RegSetValueEx(hk, "CategoryMessageFile", 0, REG_EXPAND_SZ, (LPBYTE)szMsgDLL, (DWORD)strlen(szMsgDLL)+1)) return -7; ! ! /* Category message count */ ! dwData = 0; ! if (RegSetValueEx(hk, "CategoryCount", 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD))) return -8; ! ! /* 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; ! } ! } ! ! #endif /* WIN32 */ ! /* Init the db list with template1 */ db_list = init_db_list(); if (db_list == NULL) *************** *** 1025,1041 **** 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 */ --- 1314,1340 ---- 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 */ *************** *** 1047,1055 **** 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); } } --- 1346,1360 ---- 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); --- 1424,1430 ---- 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); --- 1438,1444 ---- if (args->debug >= 1) { sprintf(logbuffer, "Performing: %s", buf); ! log_entry(logbuffer, LVL_DEBUG); fflush(LOGOUTPUT); } send_query(buf, dbs); *************** *** 1175,1181 **** sprintf(logbuffer, "%i All DBs checked in: %.0f usec, will sleep for %i secs.", loops, diff, sleep_secs); ! log_entry(logbuffer); fflush(LOGOUTPUT); } --- 1480,1486 ---- 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); } *************** *** 1183,1195 **** 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; } --- 1488,1592 ---- 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 ! !