~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to srclib/apr-util/dbd/apr_dbd_pgsql.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
 
2
 * applicable.
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
#include "apu.h"
 
18
 
 
19
#if APU_HAVE_PGSQL
 
20
 
 
21
#include <ctype.h>
 
22
#include <stdlib.h>
 
23
 
 
24
#include <libpq-fe.h>
 
25
 
 
26
#include "apr_strings.h"
 
27
#include "apr_time.h"
 
28
 
 
29
#include "apr_dbd_internal.h"
 
30
 
 
31
#define QUERY_MAX_ARGS 40
 
32
 
 
33
struct apr_dbd_transaction_t {
 
34
    int errnum;
 
35
    apr_dbd_t *handle;
 
36
};
 
37
 
 
38
struct apr_dbd_t {
 
39
    PGconn *conn;
 
40
    apr_dbd_transaction_t *trans;
 
41
};
 
42
 
 
43
struct apr_dbd_results_t {
 
44
    int random;
 
45
    PGconn *handle;
 
46
    PGresult *res;
 
47
    size_t ntuples;
 
48
    size_t sz;
 
49
    size_t index;
 
50
};
 
51
 
 
52
struct apr_dbd_row_t {
 
53
    int n;
 
54
    apr_dbd_results_t *res;
 
55
};
 
56
 
 
57
struct apr_dbd_prepared_t {
 
58
    const char *name;
 
59
    int prepared;
 
60
};
 
61
 
 
62
#define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
 
63
                                 || ((x) == PGRES_COMMAND_OK) \
 
64
                                 || ((x) == PGRES_TUPLES_OK))
 
65
 
 
66
static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
 
67
                            apr_dbd_results_t **results,
 
68
                            const char *query, int seek)
 
69
{
 
70
    PGresult *res;
 
71
    int ret;
 
72
    if ( sql->trans && sql->trans->errnum ) {
 
73
        return sql->trans->errnum;
 
74
    }
 
75
    if (seek) { /* synchronous query */
 
76
        res = PQexec(sql->conn, query);
 
77
        if (res) {
 
78
            ret = PQresultStatus(res);
 
79
            if (dbd_pgsql_is_success(ret)) {
 
80
                ret = 0;
 
81
            } else {
 
82
                PQclear(res);
 
83
            }
 
84
        } else {
 
85
            ret = PGRES_FATAL_ERROR;
 
86
        }
 
87
        if (ret != 0) {
 
88
            if (sql->trans) {
 
89
                sql->trans->errnum = ret;
 
90
            }
 
91
            return ret;
 
92
        }
 
93
        if (!*results) {
 
94
            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
 
95
        }
 
96
        (*results)->res = res;
 
97
        (*results)->ntuples = PQntuples(res);
 
98
        (*results)->sz = PQnfields(res);
 
99
        (*results)->random = seek;
 
100
        apr_pool_cleanup_register(pool, res, (void*)PQclear,
 
101
                                  apr_pool_cleanup_null);
 
102
    }
 
103
    else {
 
104
        if (PQsendQuery(sql->conn, query) == 0) {
 
105
            if (sql->trans) {
 
106
                sql->trans->errnum = 1;
 
107
            }
 
108
            return 1;
 
109
        }
 
110
        if (*results == NULL) {
 
111
            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
 
112
        }
 
113
        (*results)->random = seek;
 
114
        (*results)->handle = sql->conn;
 
115
    }
 
116
    return 0;
 
117
}
 
118
 
 
119
static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
 
120
                             apr_dbd_row_t **rowp, int rownum)
 
121
{
 
122
    apr_dbd_row_t *row = *rowp;
 
123
    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
 
124
 
 
125
    if (row == NULL) {
 
126
        row = apr_palloc(pool, sizeof(apr_dbd_row_t));
 
127
        *rowp = row;
 
128
        row->res = res;
 
129
        row->n = sequential ? 0 : rownum;
 
130
    }
 
131
    else {
 
132
        if ( sequential ) {
 
133
            ++row->n;
 
134
        }
 
135
        else {
 
136
            row->n = rownum;
 
137
        }
 
138
    }
 
139
 
 
140
    if (res->random) {
 
141
        if (row->n >= res->ntuples) {
 
142
            *rowp = NULL;
 
143
            apr_pool_cleanup_kill(pool, res->res, (void*)PQclear);
 
144
            PQclear(res->res);
 
145
            res->res = NULL;
 
146
            return -1;
 
147
        }
 
148
    }
 
149
    else {
 
150
        if (row->n >= res->ntuples) {
 
151
            /* no data; we have to fetch some */
 
152
            row->n -= res->ntuples;
 
153
            if (res->res != NULL) {
 
154
                PQclear(res->res);
 
155
            }
 
156
            res->res = PQgetResult(res->handle);
 
157
            if (res->res) {
 
158
                res->ntuples = PQntuples(res->res);
 
159
                while (res->ntuples == 0) {
 
160
                    /* if we got an empty result, clear it, wait a mo, try
 
161
                     * again */
 
162
                    PQclear(res->res);
 
163
                    apr_sleep(100000);        /* 0.1 secs */
 
164
                    res->res = PQgetResult(res->handle);
 
165
                    if (res->res) {
 
166
                        res->ntuples = PQntuples(res->res);
 
167
                    }
 
168
                    else {
 
169
                        return -1;
 
170
                    }
 
171
                }
 
172
                if (res->sz == 0) {
 
173
                    res->sz = PQnfields(res->res);
 
174
                }
 
175
            }
 
176
            else {
 
177
                return -1;
 
178
            }
 
179
        }
 
180
    }
 
181
    return 0;
 
182
}
 
183
 
 
184
static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
 
185
{
 
186
    return PQgetvalue(row->res->res, row->n, n);
 
187
}
 
188
 
 
189
static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
 
190
{
 
191
    return PQerrorMessage(sql->conn);
 
192
}
 
193
 
 
194
static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
 
195
{
 
196
    PGresult *res;
 
197
    int ret;
 
198
    if (sql->trans && sql->trans->errnum) {
 
199
        return sql->trans->errnum;
 
200
    }
 
201
    res = PQexec(sql->conn, query);
 
202
    if (res) {
 
203
        ret = PQresultStatus(res);
 
204
        if (dbd_pgsql_is_success(ret)) {
 
205
            /* ugh, making 0 return-success doesn't fit */
 
206
            ret = 0;
 
207
        }
 
208
        *nrows = atoi(PQcmdTuples(res));
 
209
        PQclear(res);
 
210
    }
 
211
    else {
 
212
        ret = PGRES_FATAL_ERROR;
 
213
    }
 
214
    if (sql->trans) {
 
215
        sql->trans->errnum = ret;
 
216
    }
 
217
    return ret;
 
218
}
 
219
 
 
220
static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
 
221
                                    apr_dbd_t *sql)
 
222
{
 
223
    size_t len = strlen(arg);
 
224
    char *ret = apr_palloc(pool, len + 1);
 
225
    PQescapeString(ret, arg, len);
 
226
    return ret;
 
227
}
 
228
 
 
229
static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
 
230
                             const char *query, const char *label,
 
231
                             apr_dbd_prepared_t **statement)
 
232
{
 
233
    char *sqlcmd;
 
234
    char *sqlptr;
 
235
    size_t length;
 
236
    size_t i = 0;
 
237
    const char *args[QUERY_MAX_ARGS];
 
238
    size_t alen;
 
239
    int nargs = 0;
 
240
    int ret;
 
241
    PGresult *res;
 
242
    char *pgquery;
 
243
    char *pgptr;
 
244
 
 
245
    if (!*statement) {
 
246
        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
 
247
    }
 
248
    /* Translate from apr_dbd to native query format */
 
249
    for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
 
250
        if (sqlptr[0] == '%') {
 
251
            if (isalpha(sqlptr[1])) {
 
252
                ++nargs;
 
253
            }
 
254
            else if (sqlptr[1] == '%') {
 
255
                ++sqlptr;
 
256
            }
 
257
        }
 
258
    }
 
259
    length = strlen(query) + 1;
 
260
    if (nargs > 8) {
 
261
        length += nargs - 8;
 
262
    }
 
263
    pgptr = pgquery = apr_palloc(pool, length) ;
 
264
 
 
265
    for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
 
266
        if ((sqlptr[0] == '%') && isalpha(sqlptr[1])) {
 
267
            *pgptr++ = '$';
 
268
            if (i < 9) {
 
269
                *pgptr++ = '1' + i;
 
270
            }
 
271
            else {
 
272
                *pgptr++ = '0' + ((i+1)/10);
 
273
                *pgptr++ = '0' + ((i+1)%10);
 
274
            }
 
275
            switch (*++sqlptr) {
 
276
            case 'd':
 
277
                args[i] = "integer";
 
278
                break;
 
279
            case 's':
 
280
                args[i] = "varchar";
 
281
                break;
 
282
            default:
 
283
                args[i] = "varchar";
 
284
                break;
 
285
            }
 
286
            length += 1 + strlen(args[i]);
 
287
            ++i;
 
288
        }
 
289
        else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
 
290
            /* reduce %% to % */
 
291
            *pgptr++ = *sqlptr++;
 
292
        }
 
293
        else {
 
294
            *pgptr++ = *sqlptr;
 
295
        }
 
296
    }
 
297
    *pgptr = 0;
 
298
 
 
299
    if (!label) {
 
300
        /* don't really prepare; use in execParams instead */
 
301
        (*statement)->prepared = 0;
 
302
        (*statement)->name = apr_pstrdup(pool, pgquery);
 
303
        return 0;
 
304
    }
 
305
    (*statement)->name = apr_pstrdup(pool, label);
 
306
 
 
307
    /* length of SQL query that prepares this statement */
 
308
    length = 8 + strlen(label) + 2 + 4 + length + 1;
 
309
    sqlcmd = apr_palloc(pool, length);
 
310
    sqlptr = sqlcmd;
 
311
    memcpy(sqlptr, "PREPARE ", 8);
 
312
    sqlptr += 8;
 
313
    length = strlen(label);
 
314
    memcpy(sqlptr, label, length);
 
315
    sqlptr += length;
 
316
    if (nargs > 0) {
 
317
        memcpy(sqlptr, " (",2);
 
318
        sqlptr += 2;
 
319
        for (i=0; i<nargs; ++i) {
 
320
            alen = strlen(args[i]);
 
321
            memcpy(sqlptr, args[i], alen);
 
322
            sqlptr += alen;
 
323
            *sqlptr++ = ',';
 
324
        }
 
325
        sqlptr[-1] =  ')';
 
326
    }
 
327
    memcpy(sqlptr, " AS ", 4);
 
328
    sqlptr += 4;
 
329
    memcpy(sqlptr, pgquery, strlen(pgquery));
 
330
    sqlptr += strlen(pgquery);
 
331
    *sqlptr = 0;
 
332
 
 
333
    res = PQexec(sql->conn, sqlcmd);
 
334
    if ( res ) {
 
335
        ret = PQresultStatus(res);
 
336
        if (dbd_pgsql_is_success(ret)) {
 
337
            ret = 0;
 
338
        }
 
339
        /* Hmmm, do we do this here or register it on the pool? */
 
340
        PQclear(res);
 
341
    }
 
342
    else {
 
343
        ret = PGRES_FATAL_ERROR;
 
344
    }
 
345
    (*statement)->prepared = 1;
 
346
 
 
347
    return ret;
 
348
}
 
349
 
 
350
static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
 
351
                            int *nrows, apr_dbd_prepared_t *statement,
 
352
                            int nargs, const char **values)
 
353
{
 
354
    int ret;
 
355
    PGresult *res;
 
356
    if (statement->prepared) {
 
357
        res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0, 0,
 
358
                             0);
 
359
    }
 
360
    else {
 
361
        res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0, 0,
 
362
                           0);
 
363
    }
 
364
    if (res) {
 
365
        ret = PQresultStatus(res);
 
366
        if (dbd_pgsql_is_success(ret)) {
 
367
            ret = 0;
 
368
        }
 
369
        PQclear(res);
 
370
    }
 
371
    else {
 
372
        ret = PGRES_FATAL_ERROR;
 
373
    }
 
374
 
 
375
    if (sql->trans) {
 
376
        sql->trans->errnum = ret;
 
377
    }
 
378
    return ret;
 
379
}
 
380
 
 
381
static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
 
382
                             int *nrows, apr_dbd_prepared_t *statement,
 
383
                             va_list args)
 
384
{
 
385
    const char *arg;
 
386
    int nargs = 0;
 
387
    const char *values[QUERY_MAX_ARGS];
 
388
 
 
389
    if (sql->trans && sql->trans->errnum) {
 
390
        return sql->trans->errnum;
 
391
    }
 
392
    while ( arg = va_arg(args, const char*), arg ) {
 
393
        if ( nargs >= QUERY_MAX_ARGS) {
 
394
            va_end(args);
 
395
            return -1;
 
396
        }
 
397
        values[nargs++] = apr_pstrdup(pool, arg);
 
398
    }
 
399
    values[nargs] = NULL;
 
400
    return dbd_pgsql_pquery(pool, sql, nrows, statement, nargs, values);
 
401
}
 
402
 
 
403
static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
 
404
                             apr_dbd_results_t **results,
 
405
                             apr_dbd_prepared_t *statement,
 
406
                             int seek, int nargs, const char **values)
 
407
{
 
408
    PGresult *res;
 
409
    int rv;
 
410
    int ret = 0;
 
411
    if (seek) { /* synchronous query */
 
412
        if (statement->prepared) {
 
413
            res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0,
 
414
                                 0, 0);
 
415
        }
 
416
        else {
 
417
            res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0,
 
418
                               0, 0);
 
419
        }
 
420
        if (res) {
 
421
            ret = PQresultStatus(res);
 
422
            if (dbd_pgsql_is_success(ret)) {
 
423
                ret = 0;
 
424
            }
 
425
            else {
 
426
                PQclear(res);
 
427
            }
 
428
        }
 
429
        else {
 
430
            ret = PGRES_FATAL_ERROR;
 
431
        }
 
432
        if (ret != 0) {
 
433
            if (sql->trans) {
 
434
                sql->trans->errnum = ret;
 
435
            }
 
436
            return ret;
 
437
        }
 
438
        if (!*results) {
 
439
            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
 
440
        }
 
441
        (*results)->res = res;
 
442
        (*results)->ntuples = PQntuples(res);
 
443
        (*results)->sz = PQnfields(res);
 
444
        (*results)->random = seek;
 
445
        apr_pool_cleanup_register(pool, res, (void*)PQclear,
 
446
                                  apr_pool_cleanup_null);
 
447
    }
 
448
    else {
 
449
        if (statement->prepared) {
 
450
            rv = PQsendQueryPrepared(sql->conn, statement->name, nargs, values,
 
451
                                     0, 0, 0);
 
452
        }
 
453
        else {
 
454
            rv = PQsendQueryParams(sql->conn, statement->name, nargs, 0,
 
455
                                   values, 0, 0, 0);
 
456
        }
 
457
        if (rv == 0) {
 
458
            if (sql->trans) {
 
459
                sql->trans->errnum = 1;
 
460
            }
 
461
            return 1;
 
462
        }
 
463
        if (!*results) {
 
464
            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
 
465
        }
 
466
        (*results)->random = seek;
 
467
        (*results)->handle = sql->conn;
 
468
    }
 
469
 
 
470
    if (sql->trans) {
 
471
        sql->trans->errnum = ret;
 
472
    }
 
473
    return ret;
 
474
}
 
475
 
 
476
static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
 
477
                              apr_dbd_results_t **results,
 
478
                              apr_dbd_prepared_t *statement,
 
479
                              int seek, va_list args)
 
480
{
 
481
    const char *arg;
 
482
    int nargs = 0;
 
483
    const char *values[QUERY_MAX_ARGS];
 
484
 
 
485
    if (sql->trans && sql->trans->errnum) {
 
486
        return sql->trans->errnum;
 
487
    }
 
488
 
 
489
    while (arg = va_arg(args, const char*), arg) {
 
490
        if ( nargs >= QUERY_MAX_ARGS) {
 
491
            va_end(args);
 
492
            return -1;
 
493
        }
 
494
        values[nargs++] = apr_pstrdup(pool, arg);
 
495
    }
 
496
    return dbd_pgsql_pselect(pool, sql, results, statement,
 
497
                             seek, nargs, values) ;
 
498
}
 
499
 
 
500
static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
 
501
                                       apr_dbd_transaction_t **trans)
 
502
{
 
503
    int ret = 0;
 
504
    PGresult *res;
 
505
 
 
506
    /* XXX handle recursive transactions here */
 
507
 
 
508
    res = PQexec(handle->conn, "BEGIN TRANSACTION");
 
509
    if (res) {
 
510
        ret = PQresultStatus(res);
 
511
        if (dbd_pgsql_is_success(ret)) {
 
512
            ret = 0;
 
513
            if (!*trans) {
 
514
                *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
 
515
            }
 
516
        }
 
517
        PQclear(res);
 
518
        (*trans)->handle = handle;
 
519
        handle->trans = *trans;
 
520
    }
 
521
    else {
 
522
        ret = PGRES_FATAL_ERROR;
 
523
    }
 
524
    return ret;
 
525
}
 
526
 
 
527
static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
 
528
{
 
529
    PGresult *res;
 
530
    int ret = -1;                /* no transaction is an error cond */
 
531
    if (trans) {
 
532
        if (trans->errnum) {
 
533
            trans->errnum = 0;
 
534
            res = PQexec(trans->handle->conn, "ROLLBACK");
 
535
        }
 
536
        else {
 
537
            res = PQexec(trans->handle->conn, "COMMIT");
 
538
        }
 
539
        if (res) {
 
540
            ret = PQresultStatus(res);
 
541
            if (dbd_pgsql_is_success(ret)) {
 
542
                ret = 0;
 
543
            }
 
544
            PQclear(res);
 
545
        }
 
546
        else {
 
547
            ret = PGRES_FATAL_ERROR;
 
548
        }
 
549
        trans->handle->trans = NULL;
 
550
    }
 
551
    return ret;
 
552
}
 
553
 
 
554
static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
 
555
{
 
556
    apr_dbd_t *sql;
 
557
    
 
558
    PGconn *conn = PQconnectdb(params);
 
559
 
 
560
    /* if there's an error in the connect string or something we get
 
561
     * back a * bogus connection object, and things like PQreset are
 
562
     * liable to segfault, so just close it out now.  it would be nice
 
563
     * if we could give an indication of why we failed to connect... */
 
564
    if (PQstatus(conn) != CONNECTION_OK) {
 
565
        PQfinish(conn);
 
566
        return NULL;
 
567
    }
 
568
 
 
569
    sql = apr_pcalloc (pool, sizeof (*sql));
 
570
 
 
571
    sql->conn = conn;
 
572
 
 
573
    return sql;
 
574
}
 
575
 
 
576
static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
 
577
{
 
578
    PQfinish(handle->conn);
 
579
    return APR_SUCCESS;
 
580
}
 
581
 
 
582
static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
 
583
                                         apr_dbd_t *handle)
 
584
{
 
585
    if (PQstatus(handle->conn) != CONNECTION_OK) {
 
586
        PQreset(handle->conn);
 
587
        if (PQstatus(handle->conn) != CONNECTION_OK) {
 
588
            return APR_EGENERAL;
 
589
        }
 
590
    }
 
591
    return APR_SUCCESS;
 
592
}
 
593
 
 
594
static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
 
595
                               const char *name)
 
596
{
 
597
    return APR_ENOTIMPL;
 
598
}
 
599
 
 
600
static void *dbd_pgsql_native(apr_dbd_t *handle)
 
601
{
 
602
    return handle->conn;
 
603
}
 
604
 
 
605
static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
 
606
{
 
607
    return res->sz;
 
608
}
 
609
 
 
610
static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
 
611
{
 
612
    if (res->random) {
 
613
        return res->ntuples;
 
614
    }
 
615
    else {
 
616
        return -1;
 
617
    }
 
618
}
 
619
 
 
620
APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
 
621
    "pgsql",
 
622
    NULL,
 
623
    dbd_pgsql_native,
 
624
    dbd_pgsql_open,
 
625
    dbd_pgsql_check_conn,
 
626
    dbd_pgsql_close,
 
627
    dbd_pgsql_select_db,
 
628
    dbd_pgsql_start_transaction,
 
629
    dbd_pgsql_end_transaction,
 
630
    dbd_pgsql_query,
 
631
    dbd_pgsql_select,
 
632
    dbd_pgsql_num_cols,
 
633
    dbd_pgsql_num_tuples,
 
634
    dbd_pgsql_get_row,
 
635
    dbd_pgsql_get_entry,
 
636
    dbd_pgsql_error,
 
637
    dbd_pgsql_escape,
 
638
    dbd_pgsql_prepare,
 
639
    dbd_pgsql_pvquery,
 
640
    dbd_pgsql_pvselect,
 
641
    dbd_pgsql_pquery,
 
642
    dbd_pgsql_pselect,
 
643
};
 
644
#endif