static int c_psql_read (void) { int success = 0; int i; for (i = 0; i < databases_num; ++i) { c_psql_database_t *db = databases + i; int j; assert (NULL != db->database); if (0 != c_psql_check_connection (db)) continue; for (j = 0; j < db->queries_num; ++j) { udb_query_t *q; q = db->queries[j]; if ((0 != db->server_version) && (udb_query_check_version (q, db->server_version) <= 0)) continue; c_psql_exec_query (db, q); } ++success; } if (! success) return -1; return 0; } /* c_psql_read */
static int c_psql_read (user_data_t *ud) { c_psql_database_t *db; int success = 0; int i; if ((ud == NULL) || (ud->data == NULL)) { log_err ("c_psql_read: Invalid user data."); return -1; } db = ud->data; assert (NULL != db->database); assert (NULL != db->instance); assert (NULL != db->queries); pthread_mutex_lock (&db->db_lock); if (0 != c_psql_check_connection (db)) { pthread_mutex_unlock (&db->db_lock); return -1; } for (i = 0; i < db->queries_num; ++i) { udb_query_preparation_area_t *prep_area; udb_query_t *q; prep_area = db->q_prep_areas[i]; q = db->queries[i]; if ((0 != db->server_version) && (udb_query_check_version (q, db->server_version) <= 0)) continue; if (0 == c_psql_exec_query (db, q, prep_area)) success = 1; } pthread_mutex_unlock (&db->db_lock); if (! success) return -1; return 0; } /* c_psql_read */
static int c_psql_write (const data_set_t *ds, const value_list_t *vl, user_data_t *ud) { c_psql_database_t *db; char time_str[32]; char values_name_str[1024]; char values_type_str[1024]; char values_str[1024]; const char *params[9]; int success = 0; int i; if ((ud == NULL) || (ud->data == NULL)) { log_err ("c_psql_write: Invalid user data."); return -1; } db = ud->data; assert (db->database != NULL); assert (db->writers != NULL); if (cdtime_to_iso8601 (time_str, sizeof (time_str), vl->time) == 0) { log_err ("c_psql_write: Failed to convert time to ISO 8601 format"); return -1; } if (values_name_to_sqlarray (ds, values_name_str, sizeof (values_name_str)) == NULL) return -1; #define VALUE_OR_NULL(v) ((((v) == NULL) || (*(v) == '\0')) ? NULL : (v)) params[0] = time_str; params[1] = vl->host; params[2] = vl->plugin; params[3] = VALUE_OR_NULL(vl->plugin_instance); params[4] = vl->type; params[5] = VALUE_OR_NULL(vl->type_instance); params[6] = values_name_str; #undef VALUE_OR_NULL pthread_mutex_lock (&db->db_lock); if (0 != c_psql_check_connection (db)) { pthread_mutex_unlock (&db->db_lock); return -1; } if ((db->commit_interval > 0) && (db->next_commit == 0)) c_psql_begin (db); for (i = 0; i < db->writers_num; ++i) { c_psql_writer_t *writer; PGresult *res; writer = db->writers[i]; if (values_type_to_sqlarray (ds, values_type_str, sizeof (values_type_str), writer->store_rates) == NULL) { pthread_mutex_unlock (&db->db_lock); return -1; } if (values_to_sqlarray (ds, vl, values_str, sizeof (values_str), writer->store_rates) == NULL) { pthread_mutex_unlock (&db->db_lock); return -1; } params[7] = values_type_str; params[8] = values_str; res = PQexecParams (db->conn, writer->statement, STATIC_ARRAY_SIZE (params), NULL, (const char *const *)params, NULL, NULL, /* return text data */ 0); if ((PGRES_COMMAND_OK != PQresultStatus (res)) && (PGRES_TUPLES_OK != PQresultStatus (res))) { PQclear (res); if ((CONNECTION_OK != PQstatus (db->conn)) && (0 == c_psql_check_connection (db))) { /* try again */ res = PQexecParams (db->conn, writer->statement, STATIC_ARRAY_SIZE (params), NULL, (const char *const *)params, NULL, NULL, /* return text data */ 0); if ((PGRES_COMMAND_OK == PQresultStatus (res)) || (PGRES_TUPLES_OK == PQresultStatus (res))) { PQclear (res); success = 1; continue; } } log_err ("Failed to execute SQL query: %s", PQerrorMessage (db->conn)); log_info ("SQL query was: '%s', " "params: %s, %s, %s, %s, %s, %s, %s, %s", writer->statement, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]); /* this will abort any current transaction -> restart */ if (db->next_commit > 0) c_psql_commit (db); pthread_mutex_unlock (&db->db_lock); return -1; } PQclear (res); success = 1; } if ((db->next_commit > 0) && (cdtime () > db->next_commit)) c_psql_commit (db); pthread_mutex_unlock (&db->db_lock); if (! success) return -1; return 0; } /* c_psql_write */
/* db->db_lock must be locked when calling this function */ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q, udb_query_preparation_area_t *prep_area) { PGresult *res; c_psql_user_data_t *data; const char *host; char **column_names; char **column_values; int column_num; int rows_num; int status; int row, col; /* The user data may hold parameter information, but may be NULL. */ data = udb_query_get_user_data (q); /* Versions up to `3' don't know how to handle parameters. */ if (3 <= db->proto_version) res = c_psql_exec_query_params (db, q, data); else if ((NULL == data) || (0 == data->params_num)) res = c_psql_exec_query_noparams (db, q); else { log_err ("Connection to database \"%s\" (%s) does not support " "parameters (protocol version %d) - " "cannot execute query \"%s\".", db->database, db->instance, db->proto_version, udb_query_get_name (q)); return -1; } /* give c_psql_write() a chance to acquire the lock if called recursively * through dispatch_values(); this will happen if, both, queries and * writers are configured for a single connection */ pthread_mutex_unlock (&db->db_lock); column_names = NULL; column_values = NULL; if (PGRES_TUPLES_OK != PQresultStatus (res)) { pthread_mutex_lock (&db->db_lock); if ((CONNECTION_OK != PQstatus (db->conn)) && (0 == c_psql_check_connection (db))) { PQclear (res); return c_psql_exec_query (db, q, prep_area); } log_err ("Failed to execute SQL query: %s", PQerrorMessage (db->conn)); log_info ("SQL query was: %s", udb_query_get_statement (q)); PQclear (res); return -1; } #define BAIL_OUT(status) \ sfree (column_names); \ sfree (column_values); \ PQclear (res); \ pthread_mutex_lock (&db->db_lock); \ return status rows_num = PQntuples (res); if (1 > rows_num) { BAIL_OUT (0); } column_num = PQnfields (res); column_names = (char **) calloc (column_num, sizeof (char *)); if (NULL == column_names) { log_err ("calloc failed."); BAIL_OUT (-1); } column_values = (char **) calloc (column_num, sizeof (char *)); if (NULL == column_values) { log_err ("calloc failed."); BAIL_OUT (-1); } for (col = 0; col < column_num; ++col) { /* Pointers returned by `PQfname' are freed by `PQclear' via * `BAIL_OUT'. */ column_names[col] = PQfname (res, col); if (NULL == column_names[col]) { log_err ("Failed to resolve name of column %i.", col); BAIL_OUT (-1); } } if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host) || (0 == strcmp (db->host, "127.0.0.1")) || (0 == strcmp (db->host, "localhost"))) host = hostname_g; else host = db->host; status = udb_query_prepare_result (q, prep_area, host, "postgresql", db->instance, column_names, (size_t) column_num, db->interval); if (0 != status) { log_err ("udb_query_prepare_result failed with status %i.", status); BAIL_OUT (-1); } for (row = 0; row < rows_num; ++row) { for (col = 0; col < column_num; ++col) { /* Pointers returned by `PQgetvalue' are freed by `PQclear' via * `BAIL_OUT'. */ column_values[col] = PQgetvalue (res, row, col); if (NULL == column_values[col]) { log_err ("Failed to get value at (row = %i, col = %i).", row, col); break; } } /* check for an error */ if (col < column_num) continue; status = udb_query_handle_result (q, prep_area, column_values); if (status != 0) { log_err ("udb_query_handle_result failed with status %i.", status); } } /* for (row = 0; row < rows_num; ++row) */ udb_query_finish_result (q, prep_area); BAIL_OUT (0); #undef BAIL_OUT } /* c_psql_exec_query */
static int c_psql_init (void) { int i; if ((NULL == databases) || (0 == databases_num)) return 0; for (i = 0; i < databases_num; ++i) { c_psql_database_t *db = databases + i; char conninfo[4096]; char *buf = conninfo; int buf_len = sizeof (conninfo); int status; char *server_host; int server_version; /* this will happen during reinitialization */ if (NULL != db->conn) { c_psql_check_connection (db); continue; } status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database); if (0 < status) { buf += status; buf_len -= status; } C_PSQL_PAR_APPEND (buf, buf_len, "host", db->host); C_PSQL_PAR_APPEND (buf, buf_len, "port", db->port); C_PSQL_PAR_APPEND (buf, buf_len, "user", db->user); C_PSQL_PAR_APPEND (buf, buf_len, "password", db->password); C_PSQL_PAR_APPEND (buf, buf_len, "sslmode", db->sslmode); C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname); C_PSQL_PAR_APPEND (buf, buf_len, "service", db->service); db->conn = PQconnectdb (conninfo); if (0 != c_psql_check_connection (db)) continue; db->proto_version = PQprotocolVersion (db->conn); server_host = PQhost (db->conn); server_version = PQserverVersion (db->conn); log_info ("Sucessfully connected to database %s (user %s) " "at server %s%s%s (server version: %d.%d.%d, " "protocol version: %d, pid: %d)", PQdb (db->conn), PQuser (db->conn), C_PSQL_SOCKET3 (server_host, PQport (db->conn)), C_PSQL_SERVER_VERSION3 (server_version), db->proto_version, PQbackendPID (db->conn)); if (3 > db->proto_version) log_warn ("Protocol version %d does not support parameters.", db->proto_version); } plugin_register_read ("postgresql", c_psql_read); plugin_register_shutdown ("postgresql", c_psql_shutdown); return 0; } /* c_psql_init */