~ubuntu-branches/ubuntu/oneiric/jabberd2/oneiric-security

« back to all changes in this revision

Viewing changes to sm/storage_pgsql.c

  • Committer: Bazaar Package Importer
  • Author(s): Nicolai Spohrer
  • Date: 2008-08-12 16:13:43 UTC
  • mfrom: (1.1.3 upstream) (0.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20080812161343-6trz3r97dtevxd17
Tags: 2.2.1-1ubuntu1
* Merge with Debian unstable (LP: #257130), remaining changes:
  - debian/control:
    + Modify Maintainer field as per spec
    + Depend on libdb4.6-dev instead of libdb4.4-dev
    + Added Conflicts and Replaces: ..., jabber for jabberd2
  - debian/rules: Added libtoolize call (jabberd2 ships with
     an older ltmain.sh version that conflicts with the
     current libtool version)
  - debian/init: create /var/run/jabber directory with correct
     permissions
* Dropped changes:
  - Debian already depends on libpq-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * jabberd - Jabber Open Source Server
3
 
 * Copyright (c) 2002-2003 Jeremie Miller, Thomas Muldowney,
4
 
 *                         Ryan Eatmon, Robert Norris
5
 
 *
6
 
 * This program is free software; you can redistribute it and/or modify
7
 
 * it under the terms of the GNU General Public License as published by
8
 
 * the Free Software Foundation; either version 2 of the License, or
9
 
 * (at your option) any later version.
10
 
 *
11
 
 * This program is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14
 
 * GNU General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU General Public License
17
 
 * along with this program; if not, write to the Free Software
18
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19
 
 */
20
 
 
21
 
/** @file sm/storage_pgsql.c
22
 
  * @brief postgresql storage module
23
 
  * @author Robert Norris
24
 
  * $Date: 2005/03/23 18:01:25 $
25
 
  * $Revision: 1.18.2.8 $
26
 
  */
27
 
 
28
 
#include "sm.h"
29
 
 
30
 
#ifdef STORAGE_PGSQL
31
 
 
32
 
#include <libpq-fe.h>
33
 
 
34
 
/** internal structure, holds our data */
35
 
typedef struct drvdata_st {
36
 
    PGconn *conn;
37
 
 
38
 
    char *prefix;
39
 
 
40
 
    xht filters;
41
 
    
42
 
    int txn;
43
 
} *drvdata_t;
44
 
 
45
 
#define FALLBACK_BLOCKSIZE (4096)
46
 
 
47
 
/** internal: do and return the math and ensure it gets realloc'd */
48
 
static size_t _st_pgsql_realloc(char **oblocks, size_t len) {
49
 
    void *nblocks;
50
 
    size_t nlen;
51
 
    static size_t block_size = 0;
52
 
 
53
 
    if (block_size == 0) {
54
 
#ifdef HAVE_GETPAGESIZE
55
 
        block_size = getpagesize();
56
 
#elif defined(_SC_PAGESIZE)
57
 
        block_size = sysconf(_SC_PAGESIZE);
58
 
#elif defined(_SC_PAGE_SIZE)
59
 
        block_size = sysconf(_SC_PAGE_SIZE);    
60
 
#else
61
 
        block_size = FALLBACK_BLOCKSIZE;
62
 
#endif
63
 
    }
64
 
    /* round up to standard block sizes */
65
 
    nlen = (((len-1)/block_size)+1)*block_size;
66
 
 
67
 
    /* keep trying till we get it */
68
 
    while((nblocks = realloc(*oblocks, nlen)) == NULL) sleep(1);
69
 
    *oblocks = nblocks;
70
 
    return nlen;
71
 
}
72
 
 
73
 
/** this is the safety check used to make sure there's always enough mem */
74
 
#define PGSQL_SAFE(blocks, size, len) if((size) >= len) len = _st_pgsql_realloc(&(blocks),(size + 1));
75
 
 
76
 
static void _st_pgsql_convert_filter_recursive(st_driver_t drv, st_filter_t f, char **buf, int *buflen, int *nbuf) {
77
 
    st_filter_t scan;
78
 
    int vlen;
79
 
    char *cval;
80
 
 
81
 
    switch(f->type) {
82
 
        case st_filter_type_PAIR:
83
 
            /* do sql escaping for apostrophes */
84
 
            cval = (char *) malloc(sizeof(char) * ((strlen(f->val) * 2) + 1));
85
 
            vlen = PQescapeString(cval, f->val, strlen(f->val));
86
 
 
87
 
            PGSQL_SAFE((*buf), *buflen + 12 + vlen - strlen(f->val), *buflen);
88
 
            *nbuf += sprintf(&((*buf)[*nbuf]), "( \"%s\" = \'%s\' ) ", f->key, f->val);
89
 
            free(cval);
90
 
 
91
 
            break;
92
 
 
93
 
        case st_filter_type_AND:
94
 
            PGSQL_SAFE((*buf), *buflen + 2, *buflen);
95
 
            *nbuf += sprintf(&((*buf)[*nbuf]), "( ");
96
 
 
97
 
            for(scan = f->sub; scan != NULL; scan = scan->next) {
98
 
                _st_pgsql_convert_filter_recursive(drv, scan, buf, buflen, nbuf);
99
 
 
100
 
                if(scan->next != NULL) {
101
 
                    PGSQL_SAFE((*buf), *buflen + 4, *buflen);
102
 
                    *nbuf += sprintf(&((*buf)[*nbuf]), "AND ");
103
 
                }
104
 
            }
105
 
 
106
 
            PGSQL_SAFE((*buf), *buflen + 2, *buflen);
107
 
            *nbuf += sprintf(&((*buf)[*nbuf]), ") ");
108
 
 
109
 
            return;
110
 
 
111
 
        case st_filter_type_OR:
112
 
            PGSQL_SAFE((*buf), *buflen + 2, *buflen);
113
 
            *nbuf += sprintf(&((*buf)[*nbuf]), "( ");
114
 
 
115
 
            for(scan = f->sub; scan != NULL; scan = scan->next) {
116
 
                _st_pgsql_convert_filter_recursive(drv, scan, buf, buflen, nbuf);
117
 
 
118
 
                if(scan->next != NULL) {
119
 
                    PGSQL_SAFE((*buf), *buflen + 3, *buflen);
120
 
                    *nbuf += sprintf(&((*buf)[*nbuf]), "OR ");
121
 
                }
122
 
            }
123
 
 
124
 
            PGSQL_SAFE((*buf), *buflen + 2, *buflen);
125
 
            *nbuf += sprintf(&((*buf)[*nbuf]), ") ");
126
 
 
127
 
            return;
128
 
 
129
 
        case st_filter_type_NOT:
130
 
            PGSQL_SAFE((*buf), *buflen + 6, *buflen);
131
 
            *nbuf += sprintf(&((*buf)[*nbuf]), "( NOT ");
132
 
 
133
 
            _st_pgsql_convert_filter_recursive(drv, f->sub, buf, buflen, nbuf);
134
 
 
135
 
            PGSQL_SAFE((*buf), *buflen + 2, *buflen);
136
 
            *nbuf += sprintf(&((*buf)[*nbuf]), ") ");
137
 
 
138
 
            return;
139
 
    }
140
 
}
141
 
 
142
 
static char *_st_pgsql_convert_filter(st_driver_t drv, const char *owner, const char *filter) {
143
 
    drvdata_t data = (drvdata_t) drv->private;
144
 
    char *buf = NULL, *sbuf = NULL, *cfilter;
145
 
    int buflen = 0, nbuf = 0, fbuf;
146
 
    st_filter_t f;
147
 
 
148
 
    PGSQL_SAFE(buf, 24 + strlen(owner), buflen);
149
 
 
150
 
    nbuf = sprintf(buf, "\"collection-owner\" = '%s'", owner);
151
 
 
152
 
    sbuf = xhash_get(data->filters, filter);
153
 
    if(sbuf != NULL) {
154
 
        PGSQL_SAFE(buf, buflen + strlen(sbuf) + 7, buflen);
155
 
        nbuf += sprintf(&buf[nbuf], " AND %s", sbuf);
156
 
        return buf;
157
 
    }
158
 
 
159
 
    cfilter = pstrdup(xhash_pool(data->filters), filter);
160
 
 
161
 
    f = storage_filter(filter);
162
 
    if(f == NULL)
163
 
        return buf;
164
 
 
165
 
    PGSQL_SAFE(buf, buflen + 5, buflen);
166
 
    nbuf += sprintf(&buf[nbuf], " AND ");
167
 
 
168
 
    fbuf = nbuf;
169
 
 
170
 
    _st_pgsql_convert_filter_recursive(drv, f, &buf, &buflen, &nbuf);
171
 
 
172
 
    xhash_put(data->filters, cfilter, pstrdup(xhash_pool(data->filters), &buf[fbuf]));
173
 
 
174
 
    pool_free(f->p);
175
 
 
176
 
    return buf;
177
 
}
178
 
 
179
 
static st_ret_t _st_pgsql_add_type(st_driver_t drv, const char *type) {
180
 
    return st_SUCCESS;
181
 
}
182
 
 
183
 
static st_ret_t _st_pgsql_put_guts(st_driver_t drv, const char *type, const char *owner, os_t os) {
184
 
    drvdata_t data = (drvdata_t) drv->private;
185
 
    char *left = NULL, *right = NULL;
186
 
    int lleft = 0, lright = 0, nleft, nright;
187
 
    os_object_t o;
188
 
    char *key, *cval = NULL;
189
 
    void *val;
190
 
    int vlen;
191
 
    os_type_t ot;
192
 
    char *xml;
193
 
    int xlen;
194
 
    PGresult *res;
195
 
    char tbuf[128];
196
 
 
197
 
    if(os_count(os) == 0)
198
 
        return st_SUCCESS;
199
 
 
200
 
    if(data->prefix != NULL) {
201
 
        snprintf(tbuf, sizeof(tbuf), "%s%s", data->prefix, type);
202
 
        type = tbuf;
203
 
    }
204
 
 
205
 
    if(os_iter_first(os))
206
 
        do {
207
 
            PGSQL_SAFE(left, strlen(type) + 55, lleft);
208
 
            nleft = sprintf(left, "INSERT INTO \"%s\" ( \"collection-owner\", \"object-sequence\"", type);
209
 
 
210
 
            PGSQL_SAFE(right, strlen(owner) + 43, lright);
211
 
            nright = sprintf(right, " ) VALUES ( '%s', nextval('object-sequence')", owner);
212
 
 
213
 
            o = os_iter_object(os);
214
 
            if(os_object_iter_first(o))
215
 
                do {
216
 
                    os_object_iter_get(o, &key, &val, &ot);
217
 
 
218
 
                    switch(ot) {
219
 
                        case os_type_BOOLEAN:
220
 
                            cval = val ? strdup("t") : strdup("f");
221
 
                            vlen = 1;
222
 
                            break;
223
 
 
224
 
                        case os_type_INTEGER:
225
 
                            cval = (char *) malloc(sizeof(char) * 20);
226
 
                            sprintf(cval, "%d", (int) val);
227
 
                            vlen = strlen(cval);
228
 
                            break;
229
 
 
230
 
                        case os_type_STRING:
231
 
                            cval = (char *) malloc(sizeof(char) * ((strlen((char *) val) * 2) + 1));
232
 
                            vlen = PQescapeString(cval, (char *) val, strlen((char *) val));
233
 
                            break;
234
 
 
235
 
                        case os_type_NAD:
236
 
                            nad_print((nad_t) val, 0, &xml, &xlen);
237
 
                            cval = (char *) malloc(sizeof(char) * ((xlen * 2) + 4));
238
 
                            vlen = PQescapeString(&cval[3], xml, xlen) + 3;
239
 
                            strncpy(cval, "NAD", 3);
240
 
                            break;
241
 
 
242
 
                        case os_type_UNKNOWN:
243
 
                            break;
244
 
                    }
245
 
 
246
 
                    log_debug(ZONE, "key %s val %s", key, cval);
247
 
 
248
 
                    PGSQL_SAFE(left, lleft + strlen(key) + 4, lleft);
249
 
                    nleft += sprintf(&left[nleft], ", \"%s\"", key);
250
 
 
251
 
                    PGSQL_SAFE(right, lright + strlen(cval) + 4, lright);
252
 
                    nright += sprintf(&right[nright], ", '%s'", cval);
253
 
 
254
 
                    free(cval);
255
 
                } while(os_object_iter_next(o));
256
 
 
257
 
            PGSQL_SAFE(left, lleft + strlen(right) + 3, lleft);
258
 
            sprintf(&left[nleft], "%s );", right);
259
 
    
260
 
            log_debug(ZONE, "prepared sql: %s", left);
261
 
 
262
 
            res = PQexec(data->conn, left);
263
 
 
264
 
            if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
265
 
                log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
266
 
                PQclear(res);
267
 
                PQreset(data->conn);
268
 
                res = PQexec(data->conn, left);
269
 
            }
270
 
            if(PQresultStatus(res) != PGRES_COMMAND_OK) {
271
 
                log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql insert failed: %s", PQresultErrorMessage(res));
272
 
                free(left);
273
 
                free(right);
274
 
                PQclear(res);
275
 
                return st_FAILED;
276
 
            }
277
 
 
278
 
            PQclear(res);
279
 
 
280
 
        } while(os_iter_next(os));
281
 
 
282
 
    free(left);
283
 
    free(right);
284
 
 
285
 
    return st_SUCCESS;
286
 
}
287
 
 
288
 
static st_ret_t _st_pgsql_put(st_driver_t drv, const char *type, const char *owner, os_t os) {
289
 
    drvdata_t data = (drvdata_t) drv->private;
290
 
    PGresult *res;
291
 
 
292
 
    if(os_count(os) == 0)
293
 
        return st_SUCCESS;
294
 
 
295
 
    if(data->txn) {
296
 
        res = PQexec(data->conn, "BEGIN;");
297
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
298
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
299
 
            PQclear(res);
300
 
            PQreset(data->conn);
301
 
            res = PQexec(data->conn, "BEGIN;");
302
 
        }
303
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK) {
304
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql transaction begin failed: %s", PQresultErrorMessage(res));
305
 
            PQclear(res);
306
 
            return st_FAILED;
307
 
        }
308
 
        PQclear(res);
309
 
 
310
 
        res = PQexec(data->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;");
311
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
312
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
313
 
            PQclear(res);
314
 
            PQreset(data->conn);
315
 
            res = PQexec(data->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;");
316
 
        }
317
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK) {
318
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql transaction setup failed: %s", PQresultErrorMessage(res));
319
 
            PQclear(res);
320
 
            PQclear(PQexec(data->conn, "ROLLBACK;"));
321
 
            return st_FAILED;
322
 
        }
323
 
        PQclear(res);
324
 
    }
325
 
 
326
 
    if(_st_pgsql_put_guts(drv, type, owner, os) != st_SUCCESS) {
327
 
        if(data->txn)
328
 
            PQclear(PQexec(data->conn, "ROLLBACK;"));
329
 
        return st_FAILED;
330
 
    }
331
 
 
332
 
    if(data->txn) {
333
 
        res = PQexec(data->conn, "COMMIT;");
334
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
335
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
336
 
            PQclear(res);
337
 
            PQreset(data->conn);
338
 
            res = PQexec(data->conn, "COMMIT;");
339
 
        }
340
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK) {
341
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql transaction commit failed: %s", PQresultErrorMessage(res));
342
 
            PQclear(res);
343
 
            PQclear(PQexec(data->conn, "ROLLBACK;"));
344
 
            return st_FAILED;
345
 
        }
346
 
        PQclear(res);
347
 
    }
348
 
 
349
 
    return st_SUCCESS;
350
 
}
351
 
 
352
 
static st_ret_t _st_pgsql_get(st_driver_t drv, const char *type, const char *owner, const char *filter, os_t *os) {
353
 
    drvdata_t data = (drvdata_t) drv->private;
354
 
    char *cond, *buf = NULL;
355
 
    int buflen = 0;
356
 
    PGresult *res;
357
 
    int ntuples, nfields, i, j;
358
 
    os_object_t o;
359
 
    char *fname, *val;
360
 
    os_type_t ot;
361
 
    int ival;
362
 
    char tbuf[128];
363
 
 
364
 
    if(data->prefix != NULL) {
365
 
        snprintf(tbuf, sizeof(tbuf), "%s%s", data->prefix, type);
366
 
        type = tbuf;
367
 
    }
368
 
 
369
 
    cond = _st_pgsql_convert_filter(drv, owner, filter);
370
 
    log_debug(ZONE, "generated filter: %s", cond);
371
 
 
372
 
    PGSQL_SAFE(buf, strlen(type) + strlen(cond) + 51, buflen);
373
 
    sprintf(buf, "SELECT * FROM \"%s\" WHERE %s ORDER BY \"object-sequence\";", type, cond);
374
 
    free(cond);
375
 
 
376
 
    log_debug(ZONE, "prepared sql: %s", buf);
377
 
 
378
 
    res = PQexec(data->conn, buf);
379
 
 
380
 
    if(PQresultStatus(res) != PGRES_TUPLES_OK && PQstatus(data->conn) != CONNECTION_OK) {
381
 
        log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
382
 
        PQclear(res);
383
 
        PQreset(data->conn);
384
 
        res = PQexec(data->conn, buf);
385
 
    }
386
 
 
387
 
    free(buf);
388
 
 
389
 
    if(PQresultStatus(res) != PGRES_TUPLES_OK) {
390
 
        log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql select failed: %s", PQresultErrorMessage(res));
391
 
        PQclear(res);
392
 
        return st_FAILED;
393
 
    }
394
 
 
395
 
    ntuples = PQntuples(res);
396
 
    if(ntuples == 0) {
397
 
        PQclear(res);
398
 
        return st_NOTFOUND;
399
 
    }
400
 
 
401
 
    log_debug(ZONE, "%d tuples returned", ntuples);
402
 
 
403
 
    nfields = PQnfields(res);
404
 
 
405
 
    if(nfields == 0) {
406
 
        log_debug(ZONE, "weird, tuples were returned but no fields *shrug*");
407
 
        PQclear(res);
408
 
        return st_NOTFOUND;
409
 
    }
410
 
 
411
 
    *os = os_new();
412
 
 
413
 
    for(i = 0; i < ntuples; i++) {
414
 
        o = os_object_new(*os);
415
 
 
416
 
        for(j = 0; j < nfields; j++) {
417
 
            fname = PQfname(res, j);
418
 
            if(strcmp(fname, "collection-owner") == 0 || strcmp(fname, "object-sequence") == 0)
419
 
                continue;
420
 
 
421
 
            switch(PQftype(res, j)) {
422
 
                case 16:    /* boolean */
423
 
                    ot = os_type_BOOLEAN;
424
 
                    break;
425
 
 
426
 
                case 23:    /* integer */
427
 
                    ot = os_type_INTEGER;
428
 
                    break;
429
 
 
430
 
                case 25:    /* text */
431
 
                    ot = os_type_STRING;
432
 
                    break;
433
 
 
434
 
                default:
435
 
                    log_debug(ZONE, "unknown oid %d, ignoring it", PQfname(res, j));
436
 
                    continue;
437
 
            }
438
 
 
439
 
            if(PQgetisnull(res, i, j))
440
 
                continue;
441
 
 
442
 
            val = PQgetvalue(res, i, j);
443
 
 
444
 
            switch(ot) {
445
 
                case os_type_BOOLEAN:
446
 
                    ival = (val[0] == 't') ? 1 : 0;
447
 
                    os_object_put(o, fname, &ival, ot);
448
 
                    break;
449
 
 
450
 
                case os_type_INTEGER:
451
 
                    ival = atoi(val);
452
 
                    os_object_put(o, fname, &ival, ot);
453
 
                    break;
454
 
 
455
 
                case os_type_STRING:
456
 
                    os_object_put(o, fname, val, os_type_STRING);
457
 
                    break;
458
 
 
459
 
                case os_type_NAD:
460
 
                case os_type_UNKNOWN:
461
 
                    break;
462
 
            }
463
 
        }
464
 
    }
465
 
 
466
 
    PQclear(res);
467
 
 
468
 
    return st_SUCCESS;
469
 
}
470
 
 
471
 
static st_ret_t _st_pgsql_delete(st_driver_t drv, const char *type, const char *owner, const char *filter) {
472
 
    drvdata_t data = (drvdata_t) drv->private;
473
 
    char *cond, *buf = NULL;
474
 
    int buflen = 0;
475
 
    PGresult *res;
476
 
    char tbuf[128];
477
 
 
478
 
    if(data->prefix != NULL) {
479
 
        snprintf(tbuf, sizeof(tbuf), "%s%s", data->prefix, type);
480
 
        type = tbuf;
481
 
    }
482
 
 
483
 
    cond = _st_pgsql_convert_filter(drv, owner, filter);
484
 
    log_debug(ZONE, "generated filter: %s", cond);
485
 
 
486
 
    PGSQL_SAFE(buf, strlen(type) + strlen(cond) + 23, buflen);
487
 
    sprintf(buf, "DELETE FROM \"%s\" WHERE %s;", type, cond);
488
 
    free(cond);
489
 
 
490
 
    log_debug(ZONE, "prepared sql: %s", buf);
491
 
 
492
 
    res = PQexec(data->conn, buf);
493
 
 
494
 
    if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
495
 
        log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
496
 
        PQclear(res);
497
 
        PQreset(data->conn);
498
 
        res = PQexec(data->conn, buf);
499
 
    }
500
 
 
501
 
    free(buf);
502
 
 
503
 
    if(PQresultStatus(res) != PGRES_COMMAND_OK) {
504
 
        log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql delete failed: %s", PQresultErrorMessage(res));
505
 
        PQclear(res);
506
 
        return st_FAILED;
507
 
    }
508
 
 
509
 
    PQclear(res);
510
 
 
511
 
    return st_SUCCESS;
512
 
}
513
 
 
514
 
static st_ret_t _st_pgsql_replace(st_driver_t drv, const char *type, const char *owner, const char *filter, os_t os) {
515
 
    drvdata_t data = (drvdata_t) drv->private;
516
 
    PGresult *res;
517
 
 
518
 
    if(data->txn) {
519
 
        res = PQexec(data->conn, "BEGIN;");
520
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
521
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
522
 
            PQclear(res);
523
 
            PQreset(data->conn);
524
 
            res = PQexec(data->conn, "BEGIN;");
525
 
        }
526
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK) {
527
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql transaction begin failed: %s", PQresultErrorMessage(res));
528
 
            PQclear(res);
529
 
            return st_FAILED;
530
 
        }
531
 
        PQclear(res);
532
 
 
533
 
        res = PQexec(data->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;");
534
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
535
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
536
 
            PQclear(res);
537
 
            PQreset(data->conn);
538
 
            res = PQexec(data->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;");
539
 
        }
540
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK) {
541
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql transaction setup failed: %s", PQresultErrorMessage(res));
542
 
            PQclear(res);
543
 
            PQclear(PQexec(data->conn, "ROLLBACK;"));
544
 
            return st_FAILED;
545
 
        }
546
 
        PQclear(res);
547
 
    }
548
 
 
549
 
    if(_st_pgsql_delete(drv, type, owner, filter) == st_FAILED) {
550
 
        if(data->txn)
551
 
            PQclear(PQexec(data->conn, "ROLLBACK;"));
552
 
        return st_FAILED;
553
 
    }
554
 
 
555
 
    if(_st_pgsql_put_guts(drv, type, owner, os) == st_FAILED) {
556
 
        if(data->txn)
557
 
            PQclear(PQexec(data->conn, "ROLLBACK;"));
558
 
        return st_FAILED;
559
 
    }
560
 
 
561
 
    if(data->txn) {
562
 
        res = PQexec(data->conn, "COMMIT;");
563
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK && PQstatus(data->conn) != CONNECTION_OK) {
564
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: lost connection to database, attempting reconnect");
565
 
            PQclear(res);
566
 
            PQreset(data->conn);
567
 
            res = PQexec(data->conn, "COMMIT;");
568
 
        }
569
 
        if(PQresultStatus(res) != PGRES_COMMAND_OK) {
570
 
            log_write(drv->st->sm->log, LOG_ERR, "pgsql: sql transaction commit failed: %s", PQresultErrorMessage(res));
571
 
            PQclear(res);
572
 
            PQclear(PQexec(data->conn, "ROLLBACK;"));
573
 
            return st_FAILED;
574
 
        }
575
 
        PQclear(res);
576
 
    }
577
 
 
578
 
    return st_SUCCESS;
579
 
}
580
 
 
581
 
static void _st_pgsql_free(st_driver_t drv) {
582
 
    drvdata_t data = (drvdata_t) drv->private;
583
 
 
584
 
    PQfinish(data->conn);
585
 
 
586
 
    xhash_free(data->filters);
587
 
 
588
 
    free(data);
589
 
}
590
 
 
591
 
st_ret_t st_pgsql_init(st_driver_t drv) {
592
 
    char *host, *port, *dbname, *user, *pass;
593
 
    PGconn *conn;
594
 
    drvdata_t data;
595
 
 
596
 
    host = config_get_one(drv->st->sm->config, "storage.pgsql.host", 0);
597
 
    port = config_get_one(drv->st->sm->config, "storage.pgsql.port", 0);
598
 
    dbname = config_get_one(drv->st->sm->config, "storage.pgsql.dbname", 0);
599
 
    user = config_get_one(drv->st->sm->config, "storage.pgsql.user", 0);
600
 
    pass = config_get_one(drv->st->sm->config, "storage.pgsql.pass", 0);
601
 
 
602
 
    if(host == NULL || port == NULL || dbname == NULL || user == NULL || pass == NULL) {
603
 
        log_write(drv->st->sm->log, LOG_ERR, "pgsql: invalid driver config");
604
 
        return st_FAILED;
605
 
    }
606
 
 
607
 
    conn = PQsetdbLogin(host, port, NULL, NULL, dbname, user, pass);
608
 
    if(conn == NULL) {
609
 
        log_write(drv->st->sm->log, LOG_ERR, "pgsql: unable to allocate database connection state");
610
 
        return st_FAILED;
611
 
    }
612
 
 
613
 
    if(PQstatus(conn) != CONNECTION_OK)
614
 
        log_write(drv->st->sm->log, LOG_ERR, "pgsql: connection to database failed: %s", PQerrorMessage(conn));
615
 
 
616
 
    data = (drvdata_t) malloc(sizeof(struct drvdata_st));
617
 
    memset(data, 0, sizeof(struct drvdata_st));
618
 
 
619
 
    data->conn = conn;
620
 
 
621
 
    data->filters = xhash_new(17);
622
 
 
623
 
    if(config_get_one(drv->st->sm->config, "storage.pgsql.transactions", 0) != NULL)
624
 
        data->txn = 1;
625
 
    else
626
 
        log_write(drv->st->sm->log, LOG_WARNING, "pgsql: transactions disabled");
627
 
 
628
 
    data->prefix = config_get_one(drv->st->sm->config, "storage.pgsql.prefix", 0);
629
 
 
630
 
    drv->private = (void *) data;
631
 
 
632
 
    drv->add_type = _st_pgsql_add_type;
633
 
    drv->put = _st_pgsql_put;
634
 
    drv->get = _st_pgsql_get;
635
 
    drv->delete = _st_pgsql_delete;
636
 
    drv->replace = _st_pgsql_replace;
637
 
    drv->free = _st_pgsql_free;
638
 
 
639
 
    return st_SUCCESS;
640
 
}
641
 
 
642
 
#endif