diff options
Diffstat (limited to 'contrib/pg_autovacuum/pg_autovacuum.c')
-rw-r--r-- | contrib/pg_autovacuum/pg_autovacuum.c | 1861 |
1 files changed, 1005 insertions, 856 deletions
diff --git a/contrib/pg_autovacuum/pg_autovacuum.c b/contrib/pg_autovacuum/pg_autovacuum.c index dce065d7b6f..01d22dbc6b1 100644 --- a/contrib/pg_autovacuum/pg_autovacuum.c +++ b/contrib/pg_autovacuum/pg_autovacuum.c @@ -6,324 +6,362 @@ #include "pg_autovacuum.h" #define TIMEBUFF 256 -FILE *LOGOUTPUT; -char timebuffer[TIMEBUFF]; -char logbuffer[4096]; +FILE *LOGOUTPUT; +char timebuffer[TIMEBUFF]; +char logbuffer[4096]; void -log_entry (const char *logentry) +log_entry(const char *logentry) { - time_t curtime; - struct tm *loctime; - curtime = time (NULL); - loctime = localtime (&curtime); - strftime (timebuffer, TIMEBUFF, "%Y-%m-%d %r", loctime); /* cbb - %F is not always available */ - fprintf (LOGOUTPUT, "[%s] %s\n", timebuffer, logentry); + time_t curtime; + struct tm *loctime; + + curtime = time(NULL); + loctime = localtime(&curtime); + strftime(timebuffer, TIMEBUFF, "%Y-%m-%d %r", loctime); /* cbb - %F is not + * always available */ + fprintf(LOGOUTPUT, "[%s] %s\n", timebuffer, logentry); } /* Function used to detatch the pg_autovacuum daemon from the tty and go into the background * -* This code is mostly ripped directly from pm_dameonize in postmaster.c * -* with unneeded code removed. */ -void daemonize () +* This code is mostly ripped directly from pm_dameonize in postmaster.c * +* with unneeded code removed. */ +void +daemonize() { - pid_t pid; - - pid = fork(); - if (pid == (pid_t) -1) - { - log_entry("Error: cannot disassociate from controlling TTY"); - fflush(LOGOUTPUT); - _exit(1); - } - else if (pid) - { /* parent */ - /* Parent should just exit, without doing any atexit cleanup */ - _exit(0); - } + pid_t pid; + + pid = fork(); + if (pid == (pid_t) -1) + { + log_entry("Error: cannot disassociate from controlling TTY"); + fflush(LOGOUTPUT); + _exit(1); + } + else if (pid) + { /* parent */ + /* Parent should just exit, without doing any atexit cleanup */ + _exit(0); + } /* GH: If there's no setsid(), we hopefully don't need silent mode. * Until there's a better solution. */ #ifdef HAVE_SETSID - if (setsid() < 0) - { - log_entry("Error: cannot disassociate from controlling TTY"); - fflush(LOGOUTPUT); - _exit(1); - } + 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 */ tbl_info * -init_table_info (PGresult * res, int row, db_info *dbi) +init_table_info(PGresult *res, int row, db_info * dbi) { - tbl_info *new_tbl = (tbl_info *) malloc (sizeof (tbl_info)); - - if (!new_tbl) { - log_entry ("init_table_info: Cannot get memory"); - fflush (LOGOUTPUT); - return NULL; - } - - if (NULL == res) - return NULL; - - new_tbl->dbi = dbi; /* set pointer to db */ - - new_tbl->schema_name = (char *) - malloc (strlen (PQgetvalue (res, row, PQfnumber (res, "schemaname"))) + 1); - if (!new_tbl->schema_name) { - log_entry ("init_table_info: malloc failed on new_tbl->schema_name"); - fflush (LOGOUTPUT); - return NULL; - } - strcpy (new_tbl->schema_name, - PQgetvalue (res, row, PQfnumber (res, "schemaname"))); - - new_tbl->table_name = (char *) - malloc (strlen (PQgetvalue (res, row, PQfnumber (res, "relname"))) + - strlen (new_tbl->schema_name) + 2); - if (!new_tbl->table_name) { - log_entry ("init_table_info: malloc failed on new_tbl->table_name"); - fflush (LOGOUTPUT); - return NULL; - } - strcpy (new_tbl->table_name, new_tbl->schema_name); - strcat (new_tbl->table_name, "."); - strcat (new_tbl->table_name, PQgetvalue (res, row, PQfnumber (res, "relname"))); - - new_tbl->CountAtLastAnalyze = - (atol (PQgetvalue (res, row, PQfnumber (res, "n_tup_ins"))) + - atol (PQgetvalue (res, row, PQfnumber (res, "n_tup_upd")))); - 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->relfilenode = atoi (PQgetvalue (res, row, PQfnumber (res, "relfilenode"))); - new_tbl->reltuples = atoi (PQgetvalue (res, row, PQfnumber (res, "reltuples"))); - new_tbl->relpages = atoi (PQgetvalue (res, row, PQfnumber (res, "relpages"))); - - 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; + tbl_info *new_tbl = (tbl_info *) malloc(sizeof(tbl_info)); + + if (!new_tbl) + { + log_entry("init_table_info: Cannot get memory"); + fflush(LOGOUTPUT); + return NULL; + } + + if (NULL == res) + return NULL; + + new_tbl->dbi = dbi; /* set pointer to db */ + + new_tbl->schema_name = (char *) + malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "schemaname"))) + 1); + if (!new_tbl->schema_name) + { + log_entry("init_table_info: malloc failed on new_tbl->schema_name"); + fflush(LOGOUTPUT); + return NULL; + } + strcpy(new_tbl->schema_name, + PQgetvalue(res, row, PQfnumber(res, "schemaname"))); + + new_tbl->table_name = (char *) + malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "relname"))) + + strlen(new_tbl->schema_name) + 2); + if (!new_tbl->table_name) + { + log_entry("init_table_info: malloc failed on new_tbl->table_name"); + fflush(LOGOUTPUT); + return NULL; + } + strcpy(new_tbl->table_name, new_tbl->schema_name); + strcat(new_tbl->table_name, "."); + strcat(new_tbl->table_name, PQgetvalue(res, row, PQfnumber(res, "relname"))); + + new_tbl->CountAtLastAnalyze = + (atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_ins"))) + + atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_upd")))); + 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->relfilenode = atoi(PQgetvalue(res, row, PQfnumber(res, "relfilenode"))); + new_tbl->reltuples = atoi(PQgetvalue(res, row, PQfnumber(res, "reltuples"))); + new_tbl->relpages = atoi(PQgetvalue(res, row, PQfnumber(res, "relpages"))); + + 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 */ void -update_table_thresholds (db_info * dbi, tbl_info * tbl,int vacuum_type) +update_table_thresholds(db_info * dbi, tbl_info * tbl, int vacuum_type) { - PGresult *res = NULL; - int disconnect = 0; - char query[128]; - - if (NULL == dbi->conn) { - dbi->conn = db_connect (dbi); - disconnect = 1; - } - - if (NULL != dbi->conn) { - snprintf (query, sizeof (query), PAGES_QUERY, tbl->relfilenode); - res = send_query (query, dbi); - if (NULL != res) { - tbl->reltuples = - atoi (PQgetvalue (res, 0, PQfnumber (res, "reltuples"))); - tbl->relpages = atoi (PQgetvalue (res, 0, PQfnumber (res, "relpages"))); - - /* update vacuum thresholds only of we just did a vacuum analyze */ - if(VACUUM_ANALYZE == vacuum_type) - { - 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); + PGresult *res = NULL; + int disconnect = 0; + char query[128]; + + if (NULL == dbi->conn) + { + dbi->conn = db_connect(dbi); + disconnect = 1; + } + + if (NULL != dbi->conn) + { + snprintf(query, sizeof(query), PAGES_QUERY, tbl->relfilenode); + res = send_query(query, dbi); + if (NULL != res) + { + tbl->reltuples = + atoi(PQgetvalue(res, 0, PQfnumber(res, "reltuples"))); + tbl->relpages = atoi(PQgetvalue(res, 0, PQfnumber(res, "relpages"))); + + /* + * update vacuum thresholds only of we just did a vacuum + * analyze + */ + if (VACUUM_ANALYZE == vacuum_type) + { + 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); } void -update_table_list (db_info * dbi) +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 (NULL == dbi->conn) { - dbi->conn = db_connect (dbi); - disconnect = 1; - } - - if (NULL != dbi->conn) { - /* 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 (query_table_stats (dbi), dbi); - 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 (NULL != tbl_elem) { - 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->relfilenode == atoi (PQgetvalue (res, i, PQfnumber (res, "relfilenode")))) { - found_match = 1; - break; - } - } - if (0 == found_match) { /* 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 (NULL != tbl_elem) - { - tbl = ((tbl_info *) DLE_VAL (tbl_elem)); - if (tbl->relfilenode == atoi (PQgetvalue (res, i, PQfnumber (res, "relfilenode")))) - { - found_match = 1; - break; - } - tbl_elem = DLGetSucc (tbl_elem); - } - if (0 == found_match) /*then we didn't find this result now in the tbl_list */ - { - DLAddTail (dbi->table_list, DLNewElem (init_table_info (res, i, dbi))); - if (args->debug >= 1) - { - sprintf (logbuffer, "added table: %s.%s", dbi->dbname, - ((tbl_info *) DLE_VAL (DLGetTail (dbi->table_list)))->table_name); - log_entry (logbuffer); - } - } - } /* end of for loop that adds tables */ - fflush (LOGOUTPUT); - PQclear (res); - res = NULL; - if (args->debug >= 3) { - print_table_list (dbi->table_list); - } - if (disconnect) - db_disconnect (dbi); - } + 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 (NULL == dbi->conn) + { + dbi->conn = db_connect(dbi); + disconnect = 1; + } + + if (NULL != dbi->conn) + { + /* + * 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(query_table_stats(dbi), dbi); + 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 (NULL != tbl_elem) + { + 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->relfilenode == atoi(PQgetvalue(res, i, PQfnumber(res, "relfilenode")))) + { + found_match = 1; + break; + } + } + if (0 == found_match) + { /* 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 (NULL != tbl_elem) + { + tbl = ((tbl_info *) DLE_VAL(tbl_elem)); + if (tbl->relfilenode == atoi(PQgetvalue(res, i, PQfnumber(res, "relfilenode")))) + { + found_match = 1; + break; + } + tbl_elem = DLGetSucc(tbl_elem); + } + if (0 == found_match) /* then we didn't find this result + * now in the tbl_list */ + { + DLAddTail(dbi->table_list, DLNewElem(init_table_info(res, i, dbi))); + if (args->debug >= 1) + { + sprintf(logbuffer, "added table: %s.%s", dbi->dbname, + ((tbl_info *) DLE_VAL(DLGetTail(dbi->table_list)))->table_name); + log_entry(logbuffer); + } + } + } /* end of for loop that adds tables */ + fflush(LOGOUTPUT); + PQclear(res); + res = NULL; + if (args->debug >= 3) + print_table_list(dbi->table_list); + if (disconnect) + db_disconnect(dbi); + } } /* Free memory, and remove the node from the list */ void -remove_table_from_list (Dlelem * tbl_to_remove) +remove_table_from_list(Dlelem *tbl_to_remove) { - tbl_info *tbl = ((tbl_info *) DLE_VAL (tbl_to_remove)); - - if (args->debug >= 1) { - sprintf (logbuffer, "Removing table: %s from list.", tbl->table_name); - log_entry (logbuffer); - fflush (LOGOUTPUT); - } - DLRemove (tbl_to_remove); - - if (tbl->schema_name) { - free (tbl->schema_name); - tbl->schema_name = NULL; - } - if (tbl->table_name) { - free (tbl->table_name); - tbl->table_name = NULL; - } - if (tbl) { - free (tbl); - tbl = NULL; - } - DLFreeElem (tbl_to_remove); + tbl_info *tbl = ((tbl_info *) DLE_VAL(tbl_to_remove)); + + if (args->debug >= 1) + { + sprintf(logbuffer, "Removing table: %s from list.", tbl->table_name); + log_entry(logbuffer); + fflush(LOGOUTPUT); + } + DLRemove(tbl_to_remove); + + if (tbl->schema_name) + { + free(tbl->schema_name); + tbl->schema_name = NULL; + } + if (tbl->table_name) + { + free(tbl->table_name); + tbl->table_name = NULL; + } + if (tbl) + { + free(tbl); + tbl = NULL; + } + DLFreeElem(tbl_to_remove); } /* Free the entire table list */ void -free_tbl_list (Dllist * tbl_list) +free_tbl_list(Dllist *tbl_list) { - Dlelem *tbl_elem = DLGetHead (tbl_list); - Dlelem *tbl_elem_to_remove = NULL; - while (NULL != tbl_elem) { - tbl_elem_to_remove = tbl_elem; - tbl_elem = DLGetSucc (tbl_elem); - remove_table_from_list (tbl_elem_to_remove); - } - DLFreeList (tbl_list); + Dlelem *tbl_elem = DLGetHead(tbl_list); + Dlelem *tbl_elem_to_remove = NULL; + + while (NULL != tbl_elem) + { + tbl_elem_to_remove = tbl_elem; + tbl_elem = DLGetSucc(tbl_elem); + remove_table_from_list(tbl_elem_to_remove); + } + DLFreeList(tbl_list); } void -print_table_list (Dllist * table_list) +print_table_list(Dllist *table_list) { - Dlelem *table_elem = DLGetHead (table_list); - while (NULL != table_elem) { - print_table_info (((tbl_info *) DLE_VAL (table_elem))); - table_elem = DLGetSucc (table_elem); - } + Dlelem *table_elem = DLGetHead(table_list); + + while (NULL != table_elem) + { + print_table_info(((tbl_info *) DLE_VAL(table_elem))); + table_elem = DLGetSucc(table_elem); + } } void -print_table_info (tbl_info * tbl) +print_table_info(tbl_info * tbl) { - sprintf (logbuffer, " table name: %s.%s", tbl->dbi->dbname, tbl->table_name); - log_entry (logbuffer); - sprintf (logbuffer, " relfilenode: %i",tbl->relfilenode); - log_entry (logbuffer); - sprintf (logbuffer, " reltuples: %i; relpages: %i", tbl->reltuples, tbl->relpages); - log_entry (logbuffer); - sprintf (logbuffer, " curr_analyze_count: %li; cur_delete_count: %li", - tbl->curr_analyze_count, tbl->curr_vacuum_count); - log_entry (logbuffer); - sprintf (logbuffer, " ins_at_last_analyze: %li; del_at_last_vacuum: %li", - tbl->CountAtLastAnalyze, tbl->CountAtLastVacuum); - log_entry (logbuffer); - sprintf (logbuffer, " insert_threshold: %li; delete_threshold %li", - tbl->analyze_threshold, tbl->vacuum_threshold); - log_entry (logbuffer); - fflush (LOGOUTPUT); + sprintf(logbuffer, " table name: %s.%s", tbl->dbi->dbname, tbl->table_name); + log_entry(logbuffer); + sprintf(logbuffer, " relfilenode: %i", tbl->relfilenode); + log_entry(logbuffer); + sprintf(logbuffer, " reltuples: %i; relpages: %i", tbl->reltuples, tbl->relpages); + log_entry(logbuffer); + sprintf(logbuffer, " curr_analyze_count: %li; cur_delete_count: %li", + tbl->curr_analyze_count, tbl->curr_vacuum_count); + log_entry(logbuffer); + sprintf(logbuffer, " ins_at_last_analyze: %li; del_at_last_vacuum: %li", + tbl->CountAtLastAnalyze, tbl->CountAtLastVacuum); + log_entry(logbuffer); + sprintf(logbuffer, " insert_threshold: %li; delete_threshold %li", + tbl->analyze_threshold, tbl->vacuum_threshold); + log_entry(logbuffer); + fflush(LOGOUTPUT); } /* End of table Management Functions */ @@ -332,161 +370,188 @@ print_table_info (tbl_info * tbl) /* init_db_list() creates the db_list and initalizes template1 */ Dllist * -init_db_list () +init_db_list() { - Dllist *db_list = DLNewList (); - db_info *dbs = NULL; - PGresult *res = NULL; - - DLAddHead (db_list, DLNewElem (init_dbinfo ((char *) "template1", 0, 0))); - if (NULL == DLGetHead (db_list)) { /* Make sure init_dbinfo was successful */ - log_entry ("init_db_list(): Error creating db_list for db: template1."); - 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); - - if (NULL != dbs->conn) { - res = send_query (FROZENOID_QUERY, dbs); - dbs->oid = atoi (PQgetvalue (res, 0, PQfnumber (res, "oid"))); - dbs->age = atoi (PQgetvalue (res, 0, PQfnumber (res, "age"))); - if (res) - PQclear (res); - - if (args->debug >= 2) { - print_db_list (db_list, 0); - } - } - return db_list; + Dllist *db_list = DLNewList(); + db_info *dbs = NULL; + PGresult *res = NULL; + + DLAddHead(db_list, DLNewElem(init_dbinfo((char *) "template1", 0, 0))); + if (NULL == DLGetHead(db_list)) + { /* Make sure init_dbinfo was successful */ + log_entry("init_db_list(): Error creating db_list for db: template1."); + 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); + + if (NULL != dbs->conn) + { + res = send_query(FROZENOID_QUERY, dbs); + dbs->oid = atoi(PQgetvalue(res, 0, PQfnumber(res, "oid"))); + dbs->age = atoi(PQgetvalue(res, 0, PQfnumber(res, "age"))); + if (res) + PQclear(res); + + if (args->debug >= 2) + print_db_list(db_list, 0); + } + return db_list; } /* Simple function to create an instance of the dbinfo struct - Initalizes all the pointers and connects to the database */ + Initalizes all the pointers and connects to the database */ db_info * -init_dbinfo (char *dbname, int oid, int age) +init_dbinfo(char *dbname, int oid, int 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 (NULL != args->user) { - newdbinfo->username = (char *) malloc (strlen (args->user) + 1); - strcpy (newdbinfo->username, args->user); - } - newdbinfo->password = NULL; - if (NULL != args->password) { - 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; + 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 (NULL != args->user) + { + newdbinfo->username = (char *) malloc(strlen(args->user) + 1); + strcpy(newdbinfo->username, args->user); + } + newdbinfo->password = NULL; + if (NULL != args->password) + { + 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 */ void -update_db_list (Dllist * db_list) +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"); - fflush (LOGOUTPUT); - } - - if (NULL == dbi_template1->conn) { - dbi_template1->conn = db_connect (dbi_template1); - disconnect = 1; - } - - if (NULL != dbi_template1->conn) { - /* 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); - 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 (NULL != db_elem) { - 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 == atoi (PQgetvalue (res, i, PQfnumber (res, "oid")))) { - found_match = 1; - /* update the dbi->age so that we ensure xid_wraparound won't happen */ - dbi->age = atoi (PQgetvalue (res, i, PQfnumber (res, "age"))); - break; - } - } - if (0 == found_match) { /*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 (NULL != db_elem) - { - dbi = ((db_info *) DLE_VAL (db_elem)); - if (dbi->oid == atoi (PQgetvalue (res, i, PQfnumber (res, "oid")))) - { - found_match = 1; - break; - } - db_elem = DLGetSucc (db_elem); - } - if (0 == found_match) /*then we didn't find this result now in the tbl_list */ - { - DLAddTail (db_list, DLNewElem (init_dbinfo - (PQgetvalue(res, i, PQfnumber (res, "datname")), - atoi (PQgetvalue(res, i, PQfnumber (res, "oid"))), - atoi (PQgetvalue(res, i, PQfnumber (res, "age")))))); - if (args->debug >= 1) - { - sprintf (logbuffer, "added database: %s",((db_info *) DLE_VAL (DLGetTail (db_list)))->dbname); - log_entry (logbuffer); - } - } - } /* end of for loop that adds tables */ - fflush (LOGOUTPUT); - PQclear (res); - res = NULL; - if (args->debug >= 3) { - print_db_list (db_list, 0); - } - if (disconnect) - db_disconnect (dbi_template1); - } + 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"); + fflush(LOGOUTPUT); + } + + if (NULL == dbi_template1->conn) + { + dbi_template1->conn = db_connect(dbi_template1); + disconnect = 1; + } + + if (NULL != dbi_template1->conn) + { + /* + * 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); + 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 (NULL != db_elem) + { + 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 == atoi(PQgetvalue(res, i, PQfnumber(res, "oid")))) + { + found_match = 1; + + /* + * update the dbi->age so that we ensure + * xid_wraparound won't happen + */ + dbi->age = atoi(PQgetvalue(res, i, PQfnumber(res, "age"))); + break; + } + } + if (0 == found_match) + { /* 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 (NULL != db_elem) + { + dbi = ((db_info *) DLE_VAL(db_elem)); + if (dbi->oid == atoi(PQgetvalue(res, i, PQfnumber(res, "oid")))) + { + found_match = 1; + break; + } + db_elem = DLGetSucc(db_elem); + } + if (0 == found_match) /* then we didn't find this result + * now in the tbl_list */ + { + DLAddTail(db_list, DLNewElem(init_dbinfo + (PQgetvalue(res, i, PQfnumber(res, "datname")), + atoi(PQgetvalue(res, i, PQfnumber(res, "oid"))), + atoi(PQgetvalue(res, i, PQfnumber(res, "age")))))); + if (args->debug >= 1) + { + sprintf(logbuffer, "added database: %s", ((db_info *) DLE_VAL(DLGetTail(db_list)))->dbname); + log_entry(logbuffer); + } + } + } /* end of for loop that adds tables */ + fflush(LOGOUTPUT); + PQclear(res); + res = NULL; + if (args->debug >= 3) + print_db_list(db_list, 0); + if (disconnect) + db_disconnect(dbi_template1); + } } /* xid_wraparound_check @@ -504,101 +569,116 @@ return 0 if nothing happened, return 1 if the database needed a database wide vacuum */ int -xid_wraparound_check (db_info * dbi) +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 (1500000000 < dbi->age) { - PGresult *res = NULL; - res = send_query ("vacuum", dbi); - /* FIXME: Perhaps should add a check for PQ_COMMAND_OK */ - PQclear (res); - return 1; - } - return 0; + /* + * 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 (1500000000 < dbi->age) + { + PGresult *res = NULL; + + res = send_query("vacuum", dbi); + /* FIXME: Perhaps should add a check for PQ_COMMAND_OK */ + PQclear(res); + return 1; + } + return 0; } /* Close DB connection, free memory, and remove the node from the list */ void -remove_db_from_list (Dlelem * db_to_remove) +remove_db_from_list(Dlelem *db_to_remove) { - db_info *dbi = ((db_info *) DLE_VAL (db_to_remove)); - - if (args->debug >= 1) { - sprintf (logbuffer, "Removing db: %s from list.", dbi->dbname); - log_entry (logbuffer); - fflush (LOGOUTPUT); - } - DLRemove (db_to_remove); - if (dbi->conn) - db_disconnect (dbi); - 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); + db_info *dbi = ((db_info *) DLE_VAL(db_to_remove)); + + if (args->debug >= 1) + { + sprintf(logbuffer, "Removing db: %s from list.", dbi->dbname); + log_entry(logbuffer); + fflush(LOGOUTPUT); + } + DLRemove(db_to_remove); + if (dbi->conn) + db_disconnect(dbi); + 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 */ void -free_db_list (Dllist * db_list) +free_db_list(Dllist *db_list) { - Dlelem *db_elem = DLGetHead (db_list); - Dlelem *db_elem_to_remove = NULL; - while (NULL != db_elem) { - 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); + Dlelem *db_elem = DLGetHead(db_list); + Dlelem *db_elem_to_remove = NULL; + + while (NULL != db_elem) + { + 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); } void -print_db_list (Dllist * db_list, int print_table_lists) +print_db_list(Dllist *db_list, int print_table_lists) { - Dlelem *db_elem = DLGetHead (db_list); - while (NULL != db_elem) { - print_db_info (((db_info *) DLE_VAL (db_elem)), print_table_lists); - db_elem = DLGetSucc (db_elem); - } + Dlelem *db_elem = DLGetHead(db_list); + + while (NULL != db_elem) + { + print_db_info(((db_info *) DLE_VAL(db_elem)), print_table_lists); + db_elem = DLGetSucc(db_elem); + } } void -print_db_info (db_info * dbi, int print_tbl_list) +print_db_info(db_info * dbi, int print_tbl_list) { - sprintf (logbuffer, "dbname: %s Username %s Passwd %s", dbi->dbname, - dbi->username, dbi->password); - log_entry (logbuffer); - sprintf (logbuffer, " oid %i InsertThresh: %i DeleteThresh: %i", dbi->oid, - dbi->analyze_threshold, dbi->vacuum_threshold); - log_entry (logbuffer); - if (NULL != dbi->conn) - log_entry (" conn is valid, we are connected"); - else - log_entry (" conn is null, we are not connected."); - - fflush (LOGOUTPUT); - if (0 < print_tbl_list) - print_table_list (dbi->table_list); + sprintf(logbuffer, "dbname: %s Username %s Passwd %s", dbi->dbname, + dbi->username, dbi->password); + log_entry(logbuffer); + sprintf(logbuffer, " oid %i InsertThresh: %i DeleteThresh: %i", dbi->oid, + dbi->analyze_threshold, dbi->vacuum_threshold); + log_entry(logbuffer); + if (NULL != dbi->conn) + log_entry(" conn is valid, we are connected"); + else + log_entry(" conn is null, we are not connected."); + + fflush(LOGOUTPUT); + if (0 < print_tbl_list) + print_table_list(dbi->table_list); } /* End of DB List Management Function */ @@ -607,406 +687,475 @@ print_db_info (db_info * dbi, int print_tbl_list) char * -query_table_stats (db_info * dbi) +query_table_stats(db_info * dbi) { - if (!strcmp (dbi->dbname, "template1")) /* Use template1 to monitor the system tables */ - return (char *) TABLE_STATS_ALL; - else - return (char *) TABLE_STATS_USER; + if (!strcmp(dbi->dbname, "template1")) /* Use template1 to + * monitor the system + * tables */ + return (char *) TABLE_STATS_ALL; + else + return (char *) TABLE_STATS_USER; } /* Perhaps add some test to this function to make sure that the stats we need are available */ PGconn * -db_connect (db_info * dbi) +db_connect(db_info * dbi) { - PGconn *db_conn = - PQsetdbLogin (args->host, args->port, NULL, NULL, dbi->dbname, - dbi->username, dbi->password); - - if (CONNECTION_OK != PQstatus (db_conn)) { - sprintf (logbuffer, "Failed connection to database %s with error: %s.", - dbi->dbname, PQerrorMessage (db_conn)); - log_entry (logbuffer); - fflush (LOGOUTPUT); - PQfinish (db_conn); - db_conn = NULL; - } - return db_conn; -} /* end of db_connect() */ + PGconn *db_conn = + PQsetdbLogin(args->host, args->port, NULL, NULL, dbi->dbname, + dbi->username, dbi->password); + + if (CONNECTION_OK != PQstatus(db_conn)) + { + sprintf(logbuffer, "Failed connection to database %s with error: %s.", + dbi->dbname, PQerrorMessage(db_conn)); + log_entry(logbuffer); + fflush(LOGOUTPUT); + PQfinish(db_conn); + db_conn = NULL; + } + return db_conn; +} /* end of db_connect() */ void -db_disconnect (db_info * dbi) +db_disconnect(db_info * dbi) { - if (NULL != dbi->conn) { - PQfinish (dbi->conn); - dbi->conn = NULL; - } + if (NULL != dbi->conn) + { + PQfinish(dbi->conn); + dbi->conn = NULL; + } } int -check_stats_enabled (db_info * dbi) +check_stats_enabled(db_info * dbi) { - PGresult *res = NULL; - int ret = 0; - res = send_query ("show stats_row_level", dbi); - ret = - strcmp ("on", PQgetvalue (res, 0, PQfnumber (res, "stats_row_level"))); - PQclear (res); - return ret; + PGresult *res = NULL; + int ret = 0; + + res = send_query("show stats_row_level", dbi); + ret = + strcmp("on", PQgetvalue(res, 0, PQfnumber(res, "stats_row_level"))); + PQclear(res); + return ret; } PGresult * -send_query (const char *query, db_info * dbi) +send_query(const char *query, db_info * dbi) { - PGresult *res; - - if (NULL == dbi->conn) - return NULL; - - res = PQexec (dbi->conn, query); - - if (!res) { - sprintf (logbuffer, - "Fatal error occured while sending query (%s) to database %s", - query, dbi->dbname); - log_entry (logbuffer); - sprintf (logbuffer, "The error is [%s]", PQresultErrorMessage (res)); - log_entry (logbuffer); - fflush (LOGOUTPUT); - return NULL; - } - if (PQresultStatus (res) != PGRES_TUPLES_OK - && PQresultStatus (res) != PGRES_COMMAND_OK) { - sprintf (logbuffer, - "Can not refresh statistics information from the database %s.", - dbi->dbname); - log_entry (logbuffer); - sprintf (logbuffer, "The error is [%s]", PQresultErrorMessage (res)); - log_entry (logbuffer); - fflush (LOGOUTPUT); - PQclear (res); - return NULL; - } - return res; -} /* End of send_query() */ + PGresult *res; + + if (NULL == dbi->conn) + return NULL; + + res = PQexec(dbi->conn, query); + + if (!res) + { + sprintf(logbuffer, + "Fatal error occured while sending query (%s) to database %s", + query, dbi->dbname); + log_entry(logbuffer); + sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res)); + log_entry(logbuffer); + fflush(LOGOUTPUT); + return NULL; + } + if (PQresultStatus(res) != PGRES_TUPLES_OK + && PQresultStatus(res) != PGRES_COMMAND_OK) + { + sprintf(logbuffer, + "Can not refresh statistics information from the database %s.", + dbi->dbname); + log_entry(logbuffer); + sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res)); + log_entry(logbuffer); + fflush(LOGOUTPUT); + PQclear(res); + return NULL; + } + return res; +} /* End of send_query() */ void -free_cmd_args () +free_cmd_args() { - if (NULL != args) { - if (NULL != args->user) - free (args->user); - if (NULL != args->user) - free (args->password); - free (args); - } + if (NULL != args) + { + if (NULL != args->user) + free(args->user); + if (NULL != args->user) + free(args->password); + free(args); + } } cmd_args * -get_cmd_args (int argc, char *argv[]) +get_cmd_args(int argc, char *argv[]) { - int c; - args = (cmd_args *) malloc (sizeof (cmd_args)); - args->sleep_base_value = SLEEPBASEVALUE; - args->sleep_scaling_factor = SLEEPSCALINGFACTOR; - args->vacuum_base_threshold = VACBASETHRESHOLD; - args->vacuum_scaling_factor = VACSCALINGFACTOR; - args->analyze_base_threshold = -1; - args->analyze_scaling_factor = -1; - args->debug = AUTOVACUUM_DEBUG; - args->daemonize = 0; - - /* Fixme: Should add some sanity checking such as positive integer values etc */ - while (-1 != (c = getopt (argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hD"))) { - switch (c) { - case 's': - args->sleep_base_value = atoi (optarg); - break; - case 'S': - args->sleep_scaling_factor = atof (optarg); - break; - case 'v': - args->vacuum_base_threshold = atoi (optarg); - break; - case 'V': - args->vacuum_scaling_factor = atof (optarg); - break; - case 'a': - args->analyze_base_threshold = atoi (optarg); - break; - case 'A': - args->analyze_scaling_factor = atof (optarg); - break; - case 'D': - args->daemonize++; - break; - case 'd': - args->debug = atoi (optarg); - break; - case 'U': - args->user = optarg; - break; - case 'P': - args->password = optarg; - break; - case 'H': - args->host = optarg; - break; - case 'L': - args->logfile = optarg; - break; - case 'p': - args->port = optarg; - break; - case 'h': - usage(); - exit (0); - default: - /* It's here that we know that things are invalid... - It is not forcibly an error to call usage */ - fprintf (stderr, "Error: Invalid Command Line Options.\n"); - usage(); - exit (1); - break; - } - /* if values for insert thresholds are not specified, - then they default to 1/2 of the delete values */ - if(-1 == args->analyze_base_threshold) - args->analyze_base_threshold = args->vacuum_base_threshold / 2; - if(-1 == args->analyze_scaling_factor) - args->analyze_scaling_factor = args->vacuum_scaling_factor / 2; - } - return args; + int c; + + args = (cmd_args *) malloc(sizeof(cmd_args)); + args->sleep_base_value = SLEEPBASEVALUE; + args->sleep_scaling_factor = SLEEPSCALINGFACTOR; + args->vacuum_base_threshold = VACBASETHRESHOLD; + args->vacuum_scaling_factor = VACSCALINGFACTOR; + args->analyze_base_threshold = -1; + args->analyze_scaling_factor = -1; + args->debug = AUTOVACUUM_DEBUG; + args->daemonize = 0; + + /* + * Fixme: Should add some sanity checking such as positive integer + * values etc + */ + while (-1 != (c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hD"))) + { + switch (c) + { + case 's': + args->sleep_base_value = atoi(optarg); + break; + case 'S': + args->sleep_scaling_factor = atof(optarg); + break; + case 'v': + args->vacuum_base_threshold = atoi(optarg); + break; + case 'V': + args->vacuum_scaling_factor = atof(optarg); + break; + case 'a': + args->analyze_base_threshold = atoi(optarg); + break; + case 'A': + args->analyze_scaling_factor = atof(optarg); + break; + case 'D': + args->daemonize++; + break; + case 'd': + args->debug = atoi(optarg); + break; + case 'U': + args->user = optarg; + break; + case 'P': + args->password = optarg; + break; + case 'H': + args->host = optarg; + break; + case 'L': + args->logfile = optarg; + break; + case 'p': + args->port = optarg; + break; + case 'h': + usage(); + exit(0); + default: + + /* + * It's here that we know that things are invalid... It is + * not forcibly an error to call usage + */ + fprintf(stderr, "Error: Invalid Command Line Options.\n"); + usage(); + exit(1); + break; + } + + /* + * if values for insert thresholds are not specified, then they + * default to 1/2 of the delete values + */ + if (-1 == args->analyze_base_threshold) + args->analyze_base_threshold = args->vacuum_base_threshold / 2; + if (-1 == args->analyze_scaling_factor) + args->analyze_scaling_factor = args->vacuum_scaling_factor / 2; + } + return args; } -void usage() +void +usage() { - int i=0; - float f=0; - fprintf (stderr, "usage: pg_autovacuum \n"); - fprintf (stderr, " [-D] Daemonize (Detach from tty and run in the background)\n"); - i=AUTOVACUUM_DEBUG; - fprintf (stderr, " [-d] debug (debug level=0,1,2,3; default=%i)\n",i); - - i=SLEEPBASEVALUE; - fprintf (stderr, " [-s] sleep base value (default=%i)\n",i); - f=SLEEPSCALINGFACTOR; - fprintf (stderr, " [-S] sleep scaling factor (default=%f)\n",f); - - i=VACBASETHRESHOLD; - fprintf (stderr, " [-v] vacuum base threshold (default=%i)\n",i); - f=VACSCALINGFACTOR; - fprintf (stderr, " [-V] vacuum scaling factor (default=%f)\n",f); - i=i/2; - fprintf (stderr, " [-a] analyze base threshold (default=%i)\n",i); - f=f/2; - fprintf (stderr, " [-A] analyze scaling factor (default=%f)\n",f); - - fprintf (stderr, " [-L] logfile (default=none)\n"); - - fprintf (stderr, " [-U] username (libpq default)\n"); - fprintf (stderr, " [-P] password (libpq default)\n"); - fprintf (stderr, " [-H] host (libpq default)\n"); - fprintf (stderr, " [-p] port (libpq default)\n"); - - fprintf (stderr, " [-h] help (Show this output)\n"); + int i = 0; + float f = 0; + + fprintf(stderr, "usage: pg_autovacuum \n"); + fprintf(stderr, " [-D] Daemonize (Detach from tty and run in the background)\n"); + i = AUTOVACUUM_DEBUG; + fprintf(stderr, " [-d] debug (debug level=0,1,2,3; default=%i)\n", i); + + i = SLEEPBASEVALUE; + fprintf(stderr, " [-s] sleep base value (default=%i)\n", i); + f = SLEEPSCALINGFACTOR; + fprintf(stderr, " [-S] sleep scaling factor (default=%f)\n", f); + + i = VACBASETHRESHOLD; + fprintf(stderr, " [-v] vacuum base threshold (default=%i)\n", i); + f = VACSCALINGFACTOR; + fprintf(stderr, " [-V] vacuum scaling factor (default=%f)\n", f); + i = i / 2; + fprintf(stderr, " [-a] analyze base threshold (default=%i)\n", i); + f = f / 2; + fprintf(stderr, " [-A] analyze scaling factor (default=%f)\n", f); + + fprintf(stderr, " [-L] logfile (default=none)\n"); + + fprintf(stderr, " [-U] username (libpq default)\n"); + fprintf(stderr, " [-P] password (libpq default)\n"); + fprintf(stderr, " [-H] host (libpq default)\n"); + fprintf(stderr, " [-p] port (libpq default)\n"); + + fprintf(stderr, " [-h] help (Show this output)\n"); } void -print_cmd_args () +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->user=%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); + 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->user=%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[]) +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; - long long diff = 0; - 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 (1 == args->daemonize){ 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 (NULL == db_list) - return 1; - - if (0 != check_stats_enabled (((db_info *) DLE_VAL (DLGetHead (db_list))))) { - 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 (NULL == dbs->conn) { - dbs->conn = db_connect (dbs); - if (NULL == dbs->conn) { /* Serious problem: We can't connect to template1 */ - log_entry ("Error: Cannot connect to template1, exiting."); - fflush (LOGOUTPUT); - fclose (LOGOUTPUT); - exit (1); - } - } - - if (0 == (loops % UPDATE_INTERVAL)) /* Update the list if it's time */ - update_db_list (db_list); /* Add and remove databases from the list */ - - while (NULL != db_elem) { /* Loop through databases in list */ - dbs = ((db_info *) DLE_VAL (db_elem)); /* get pointer to cur_db's db_info struct */ - if (NULL == dbs->conn) - dbs->conn = db_connect (dbs); - - if (NULL != dbs->conn) { - if (0 == (loops % UPDATE_INTERVAL)) /* Update the list if it's time */ - update_table_list (dbs); /* Add and remove tables from the list */ - - if (0 == xid_wraparound_check (dbs)); - { - res = send_query (query_table_stats (dbs), dbs); /* Get an updated snapshot of this dbs table stats */ - 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 (NULL != tbl_elem) { /* Loop through tables in list */ - tbl = ((tbl_info *) DLE_VAL (tbl_elem)); /* set tbl_info = current_table */ - if (tbl->relfilenode == atoi (PQgetvalue(res, j, PQfnumber (res, "relfilenode")))) { - 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) - { - snprintf (buf, sizeof (buf), "vacuum analyze %s", tbl->table_name); - if (args->debug >= 1) { - sprintf (logbuffer, "Performing: %s", buf); - log_entry (logbuffer); - fflush (LOGOUTPUT); - } - send_query (buf, dbs); - update_table_thresholds (dbs, tbl, VACUUM_ANALYZE); - if (args->debug >= 2) {print_table_info (tbl);} - } - else if ((tbl->curr_analyze_count - tbl->CountAtLastAnalyze) >= tbl->analyze_threshold) - { - snprintf (buf, sizeof (buf), "analyze %s", tbl->table_name); - if (args->debug >= 1) { - sprintf (logbuffer, "Performing: %s", buf); - log_entry (logbuffer); - fflush (LOGOUTPUT); - } - send_query (buf, dbs); - update_table_thresholds (dbs, tbl, ANALYZE_ONLY); - if (args->debug >= 2) { print_table_info (tbl); } - } - - break; /* once we have found a match, no need to keep checking. */ - } - /* Advance the table pointers for the next loop */ - tbl_elem = DLGetSucc (tbl_elem); - - } /* end for table while loop */ - } /* end for j loop (tuples in PGresult) */ - } /* 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 = (now.tv_sec - then.tv_sec) * 1000000 + (now.tv_usec - then.tv_usec); - - sleep_secs = args->sleep_base_value + args->sleep_scaling_factor * diff / 1000000; - loops++; - if (args->debug >= 2) { - sprintf (logbuffer, - "%i All DBs checked in: %lld usec, will sleep for %i secs.", - loops, diff, sleep_secs); - log_entry (logbuffer); - } - - 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; + 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; + long long diff = 0; + 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 (1 == args->daemonize) + 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 (NULL == db_list) + return 1; + + if (0 != check_stats_enabled(((db_info *) DLE_VAL(DLGetHead(db_list))))) + { + 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 (NULL == dbs->conn) + { + dbs->conn = db_connect(dbs); + if (NULL == dbs->conn) + { /* Serious problem: We can't connect to + * template1 */ + log_entry("Error: Cannot connect to template1, exiting."); + fflush(LOGOUTPUT); + fclose(LOGOUTPUT); + exit(1); + } + } + + if (0 == (loops % UPDATE_INTERVAL)) /* Update the list if it's + * time */ + update_db_list(db_list); /* Add and remove databases from + * the list */ + + while (NULL != db_elem) + { /* Loop through databases in list */ + dbs = ((db_info *) DLE_VAL(db_elem)); /* get pointer to + * cur_db's db_info + * struct */ + if (NULL == dbs->conn) + dbs->conn = db_connect(dbs); + + if (NULL != dbs->conn) + { + if (0 == (loops % UPDATE_INTERVAL)) /* Update the list if + * it's time */ + update_table_list(dbs); /* Add and remove tables + * from the list */ + + if (0 == xid_wraparound_check(dbs)); + { + res = send_query(query_table_stats(dbs), dbs); /* Get an updated + * snapshot of this dbs + * table stats */ + 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 (NULL != tbl_elem) + { /* Loop through tables in list */ + tbl = ((tbl_info *) DLE_VAL(tbl_elem)); /* set tbl_info = + * current_table */ + if (tbl->relfilenode == atoi(PQgetvalue(res, j, PQfnumber(res, "relfilenode")))) + { + 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) + { + snprintf(buf, sizeof(buf), "vacuum analyze %s", tbl->table_name); + if (args->debug >= 1) + { + sprintf(logbuffer, "Performing: %s", buf); + log_entry(logbuffer); + fflush(LOGOUTPUT); + } + send_query(buf, dbs); + update_table_thresholds(dbs, tbl, VACUUM_ANALYZE); + if (args->debug >= 2) + print_table_info(tbl); + } + else if ((tbl->curr_analyze_count - tbl->CountAtLastAnalyze) >= tbl->analyze_threshold) + { + snprintf(buf, sizeof(buf), "analyze %s", tbl->table_name); + if (args->debug >= 1) + { + sprintf(logbuffer, "Performing: %s", buf); + log_entry(logbuffer); + fflush(LOGOUTPUT); + } + send_query(buf, dbs); + update_table_thresholds(dbs, tbl, ANALYZE_ONLY); + if (args->debug >= 2) + print_table_info(tbl); + } + + break; /* once we have found a match, no + * need to keep checking. */ + } + + /* + * Advance the table pointers for the next + * loop + */ + tbl_elem = DLGetSucc(tbl_elem); + + } /* end for table while loop */ + } /* end for j loop (tuples in PGresult) */ + } /* 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 = (now.tv_sec - then.tv_sec) * 1000000 + (now.tv_usec - then.tv_usec); + + sleep_secs = args->sleep_base_value + args->sleep_scaling_factor * diff / 1000000; + loops++; + if (args->debug >= 2) + { + sprintf(logbuffer, + "%i All DBs checked in: %lld usec, will sleep for %i secs.", + loops, diff, sleep_secs); + log_entry(logbuffer); + } + + 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; } |