root/src/modules/postgres.c

Revision ab6d943800276f521272e785e85b9ea7130e92f1, 7.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

postgres no longer needs server headers, refs #33

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <math.h>
13
14 #include "noit_module.h"
15 #include "noit_check.h"
16 #include "noit_check_tools.h"
17 #include "utils/noit_log.h"
18 #include "utils/noit_hash.h"
19
20 #include <libpq-fe.h>
21
22 /* Ripped from postgres 8.3.3 */
23 #ifndef BOOLOID
24 #define BOOLOID                  16
25 #endif
26 #ifndef INT2OID
27 #define INT2OID                  21
28 #endif
29 #ifndef INT4OID
30 #define INT4OID                  23
31 #endif
32 #ifndef INT8OID
33 #define INT8OID                  20
34 #endif
35 #ifndef FLOAT4OID
36 #define FLOAT4OID                700
37 #endif
38 #ifndef FLOAT8OID
39 #define FLOAT8OID                701
40 #endif
41 #ifndef NUMERICOID
42 #define NUMERICOID               1700
43 #endif
44
45 typedef struct {
46   noit_module_t *self;
47   noit_check_t *check;
48   PGconn *conn;
49   PGresult *result;
50   int rv;
51   noit_hash_table attrs;
52   int timed_out;
53   char *error;
54 } postgres_check_info_t;
55
56 static noit_log_stream_t nlerr = NULL;
57 static noit_log_stream_t nldeb = NULL;
58
59 static void postgres_cleanup(noit_module_t *self, noit_check_t *check) {
60   postgres_check_info_t *ci = check->closure;
61   if(ci) {
62     if(ci->result) PQclear(ci->result);
63     if(ci->conn) PQfinish(ci->conn);
64     noit_check_release_attrs(&ci->attrs);
65     if(ci->error) free(ci->error);
66     memset(ci, 0, sizeof(*ci));
67   }
68 }
69 static void postgres_log_results(noit_module_t *self, noit_check_t *check) {
70   stats_t current;
71   struct timeval duration;
72   postgres_check_info_t *ci = check->closure;
73
74   noit_check_stats_clear(&current);
75
76   gettimeofday(&current.whence, NULL);
77   sub_timeval(current.whence, check->last_fire_time, &duration);
78   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
79   current.available = NP_UNAVAILABLE;
80   current.state = NP_BAD;
81   if(ci->error) current.status = ci->error;
82   else if(ci->timed_out) current.status = "timeout";
83   else if(ci->rv == PGRES_COMMAND_OK) {
84     current.available = NP_AVAILABLE;
85     current.state = NP_GOOD;
86     current.status = "command ok";
87   }
88   else if(ci->rv == PGRES_TUPLES_OK) {
89     current.available = NP_AVAILABLE;
90     current.state = NP_GOOD;
91     current.status = "tuples ok";
92   }
93   else current.status = "internal error";
94
95   if(ci->rv == PGRES_TUPLES_OK) {
96     /* metrics */
97     int nrows, ncols, i, j;
98     nrows = PQntuples(ci->result);
99     ncols = PQnfields(ci->result);
100     for (i=0; i<nrows; i++) {
101       noitL(nldeb, "postgres: row %d [%d cols]:\n", i, ncols);
102       if(ncols<2) continue;
103       if(PQgetisnull(ci->result, i, 0)) continue;
104       for (j=1; j<ncols; j++) {
105         Oid coltype;
106         int iv, *piv;
107         int64_t lv, *plv;
108         double dv, *pdv;
109         char *sv;
110         char mname[128];
111  
112         snprintf(mname, sizeof(mname), "%s`%s",
113                  PQgetvalue(ci->result, i, 0), PQfname(ci->result, j));
114         coltype = PQftype(ci->result, j);
115         noitL(nldeb, "postgres:   col %d (%s) type %d:\n", j, mname, coltype);
116         switch(coltype) {
117           case BOOLOID:
118             if(PQgetisnull(ci->result, i, j)) piv = NULL;
119             else {
120               iv = strcmp(PQgetvalue(ci->result, i, j), "f") ? 1 : 0;
121               piv = &iv;
122             }
123             noit_stats_set_metric(&current, mname, METRIC_INT32, piv);
124             break;
125           case INT2OID:
126           case INT4OID:
127           case INT8OID:
128             if(PQgetisnull(ci->result, i, j)) plv = NULL;
129             else {
130               lv = strtoll(PQgetvalue(ci->result, i, j), NULL, 10);
131               plv = &lv;
132             }
133             noit_stats_set_metric(&current, mname, METRIC_INT64, plv);
134           case FLOAT4OID:
135           case FLOAT8OID:
136           case NUMERICOID:
137             if(PQgetisnull(ci->result, i, j)) pdv = NULL;
138             else {
139               dv = atof(PQgetvalue(ci->result, i, j));
140               pdv = &dv;
141             }
142             noit_stats_set_metric(&current, mname, METRIC_DOUBLE, pdv);
143           default:
144             if(PQgetisnull(ci->result, i, j)) sv = NULL;
145             else sv = PQgetvalue(ci->result, i, j);
146             noit_stats_set_metric(&current, mname, METRIC_GUESS, sv);
147             break;
148         }
149       }
150     }
151   }
152   noit_check_set_stats(self, check, &current);
153 }
154
155 #define FETCH_CONFIG_OR(key, str) do { \
156   if(!noit_hash_retrieve(check->config, #key, strlen(#key), (void **)&key)) \
157     key = str; \
158 } while(0)
159
160 #define AVAIL_BAIL(str) do { \
161   ci->timed_out = 0; \
162   ci->error = strdup(str); \
163   return 0; \
164 } while(0)
165
166 static int postgres_drive_session(eventer_t e, int mask, void *closure,
167                                   struct timeval *now) {
168   const char *dsn, *sql;
169   char sql_buff[8192];
170   char dsn_buff[512];
171   postgres_check_info_t *ci = closure;
172   noit_check_t *check = ci->check;
173
174   if(mask & (EVENTER_READ | EVENTER_WRITE)) {
175     /* this case is impossible from the eventer.  It is called as
176      * such on the synchronous completion of the event.
177      */
178     postgres_log_results(ci->self, ci->check);
179     postgres_cleanup(ci->self, ci->check);
180     check->flags &= ~NP_RUNNING;
181     return 0;
182   }
183   switch(mask) {
184     case EVENTER_ASYNCH_WORK:
185       FETCH_CONFIG_OR(dsn, "");
186       noit_check_interpolate(dsn_buff, sizeof(dsn_buff), dsn,
187                              &ci->attrs, check->config);
188       ci->conn = PQconnectdb(dsn_buff);
189       if(!ci->conn) AVAIL_BAIL("PQconnectdb failed");
190       if(PQstatus(ci->conn) != CONNECTION_OK)
191         AVAIL_BAIL(PQerrorMessage(ci->conn));
192
193       FETCH_CONFIG_OR(sql, "");
194       noit_check_interpolate(sql_buff, sizeof(sql_buff), sql,
195                              &ci->attrs, check->config);
196       ci->result = PQexec(ci->conn, sql_buff);
197       if(!ci->result) AVAIL_BAIL("PQexec failed");
198       ci->rv = PQresultStatus(ci->result);
199       switch(ci->rv) {
200        case PGRES_TUPLES_OK:
201        case PGRES_COMMAND_OK:
202         break;
203        default:
204         AVAIL_BAIL(PQresultErrorMessage(ci->result));
205       }
206       ci->timed_out = 0;
207       return 0;
208       break;
209     case EVENTER_ASYNCH_CLEANUP:
210       /* This sets us up for a completion call. */
211       e->mask = EVENTER_READ | EVENTER_WRITE;
212       break;
213     default:
214       abort();
215   }
216   return 0;
217 }
218
219 static int postgres_initiate(noit_module_t *self, noit_check_t *check) {
220   postgres_check_info_t *ci = check->closure;
221   struct timeval __now;
222
223   /* We cannot be running */
224   assert(!(check->flags & NP_RUNNING));
225   check->flags |= NP_RUNNING;
226
227   ci->self = self;
228   ci->check = check;
229
230   ci->timed_out = 1;
231   ci->rv = -1;
232   noit_check_make_attrs(check, &ci->attrs);
233   gettimeofday(&__now, NULL);
234   memcpy(&check->last_fire_time, &__now, sizeof(__now));
235
236   /* Register a handler for the worker */
237   noit_check_run_full_asynch(check, postgres_drive_session);
238   return 0;
239 }
240
241 static int postgres_initiate_check(noit_module_t *self, noit_check_t *check,
242                                    int once, noit_check_t *parent) {
243   if(!check->closure) check->closure = calloc(1, sizeof(postgres_check_info_t));
244   INITIATE_CHECK(postgres_initiate, self, check);
245   return 0;
246 }
247
248 static int postgres_onload(noit_image_t *self) {
249   nlerr = noit_log_stream_find("error/postgres");
250   nldeb = noit_log_stream_find("debug/postgres");
251   if(!nlerr) nlerr = noit_stderr;
252   if(!nldeb) nldeb = noit_debug;
253
254   eventer_name_callback("http/postgres_drive_session", postgres_drive_session);
255   return 0;
256 }
257
258 noit_module_t postgres = {
259   {
260     NOIT_MODULE_MAGIC,
261     NOIT_MODULE_ABI_VERSION,
262     "postgres",
263     "PostgreSQL Checker",
264     postgres_onload
265   },
266   NULL,
267   NULL,
268   postgres_initiate_check,
269   postgres_cleanup
270 };
271
Note: See TracBrowser for help on using the browser.