1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  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 "apu_config.h"
22
23#include <ctype.h>
24#include <stdlib.h>
25
26#ifdef HAVE_LIBPQ_FE_H
27#include <libpq-fe.h>
28#elif defined(HAVE_POSTGRESQL_LIBPQ_FE_H)
29#include <postgresql/libpq-fe.h>
30#endif
31
32#include "apr_strings.h"
33#include "apr_time.h"
34#include "apr_buckets.h"
35
36#include "apr_dbd_internal.h"
37
38struct apr_dbd_transaction_t {
39    int mode;
40    int errnum;
41    apr_dbd_t *handle;
42};
43
44struct apr_dbd_t {
45    PGconn *conn;
46    apr_dbd_transaction_t *trans;
47};
48
49struct apr_dbd_results_t {
50    int random;
51    PGconn *handle;
52    PGresult *res;
53    size_t ntuples;
54    size_t sz;
55    size_t index;
56    apr_pool_t *pool;
57};
58
59struct apr_dbd_row_t {
60    int n;
61    apr_dbd_results_t *res;
62};
63
64struct apr_dbd_prepared_t {
65    const char *name;
66    int prepared;
67    int nargs;
68    int nvals;
69    apr_dbd_type_e *types;
70};
71
72#define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
73                                 || ((x) == PGRES_COMMAND_OK) \
74                                 || ((x) == PGRES_TUPLES_OK))
75
76static apr_status_t clear_result(void *data)
77{
78    PQclear(data);
79    return APR_SUCCESS;
80}
81
82static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
83                            apr_dbd_results_t **results,
84                            const char *query, int seek)
85{
86    PGresult *res;
87    int ret;
88    if ( sql->trans && sql->trans->errnum ) {
89        return sql->trans->errnum;
90    }
91    if (seek) { /* synchronous query */
92        if (TXN_IGNORE_ERRORS(sql->trans)) {
93            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
94            if (res) {
95                int ret = PQresultStatus(res);
96                PQclear(res);
97                if (!dbd_pgsql_is_success(ret)) {
98                    sql->trans->errnum = ret;
99                    return PGRES_FATAL_ERROR;
100                }
101            } else {
102                return sql->trans->errnum = PGRES_FATAL_ERROR;
103            }
104        }
105        res = PQexec(sql->conn, query);
106        if (res) {
107            ret = PQresultStatus(res);
108            if (dbd_pgsql_is_success(ret)) {
109                ret = 0;
110            } else {
111                PQclear(res);
112            }
113        } else {
114            ret = PGRES_FATAL_ERROR;
115        }
116        if (ret != 0) {
117            if (TXN_IGNORE_ERRORS(sql->trans)) {
118                PGresult *res = PQexec(sql->conn,
119                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
120                if (res) {
121                    int ret = PQresultStatus(res);
122                    PQclear(res);
123                    if (!dbd_pgsql_is_success(ret)) {
124                        sql->trans->errnum = ret;
125                        return PGRES_FATAL_ERROR;
126                    }
127                } else {
128                    return sql->trans->errnum = PGRES_FATAL_ERROR;
129                }
130            } else if (TXN_NOTICE_ERRORS(sql->trans)){
131                sql->trans->errnum = ret;
132            }
133            return ret;
134        } else {
135            if (TXN_IGNORE_ERRORS(sql->trans)) {
136                PGresult *res = PQexec(sql->conn,
137                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
138                if (res) {
139                    int ret = PQresultStatus(res);
140                    PQclear(res);
141                    if (!dbd_pgsql_is_success(ret)) {
142                        sql->trans->errnum = ret;
143                        return PGRES_FATAL_ERROR;
144                    }
145                } else {
146                    return sql->trans->errnum = PGRES_FATAL_ERROR;
147                }
148            }
149        }
150        if (!*results) {
151            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
152        }
153        (*results)->res = res;
154        (*results)->ntuples = PQntuples(res);
155        (*results)->sz = PQnfields(res);
156        (*results)->random = seek;
157        (*results)->pool = pool;
158        apr_pool_cleanup_register(pool, res, clear_result,
159                                  apr_pool_cleanup_null);
160    }
161    else {
162        if (TXN_IGNORE_ERRORS(sql->trans)) {
163            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
164            if (res) {
165                int ret = PQresultStatus(res);
166                PQclear(res);
167                if (!dbd_pgsql_is_success(ret)) {
168                    sql->trans->errnum = ret;
169                    return PGRES_FATAL_ERROR;
170                }
171            } else {
172                return sql->trans->errnum = PGRES_FATAL_ERROR;
173            }
174        }
175        if (PQsendQuery(sql->conn, query) == 0) {
176            if (TXN_IGNORE_ERRORS(sql->trans)) {
177                PGresult *res = PQexec(sql->conn,
178                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
179                if (res) {
180                    int ret = PQresultStatus(res);
181                    PQclear(res);
182                    if (!dbd_pgsql_is_success(ret)) {
183                        sql->trans->errnum = ret;
184                        return PGRES_FATAL_ERROR;
185                    }
186                } else {
187                    return sql->trans->errnum = PGRES_FATAL_ERROR;
188                }
189            } else if (TXN_NOTICE_ERRORS(sql->trans)){
190                sql->trans->errnum = 1;
191            }
192            return 1;
193        } else {
194            if (TXN_IGNORE_ERRORS(sql->trans)) {
195                PGresult *res = PQexec(sql->conn,
196                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
197                if (res) {
198                    int ret = PQresultStatus(res);
199                    PQclear(res);
200                    if (!dbd_pgsql_is_success(ret)) {
201                        sql->trans->errnum = ret;
202                        return PGRES_FATAL_ERROR;
203                    }
204                } else {
205                    return sql->trans->errnum = PGRES_FATAL_ERROR;
206                }
207            }
208        }
209        if (*results == NULL) {
210            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
211        }
212        (*results)->random = seek;
213        (*results)->handle = sql->conn;
214        (*results)->pool = pool;
215    }
216    return 0;
217}
218
219static const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n)
220{
221    if (res->res) {
222        if ((n>=0) && (PQnfields(res->res) > n)) {
223            return PQfname(res->res,n);
224        }
225    }
226    return NULL;
227}
228
229static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
230                             apr_dbd_row_t **rowp, int rownum)
231{
232    apr_dbd_row_t *row = *rowp;
233    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
234
235    if (row == NULL) {
236        row = apr_palloc(pool, sizeof(apr_dbd_row_t));
237        *rowp = row;
238        row->res = res;
239        if ( sequential ) {
240            row->n = 0;
241        }
242        else {
243            if (rownum > 0) {
244                row->n = --rownum;
245            }
246            else {
247                return -1; /* invalid row */
248            }
249        }
250    }
251    else {
252        if ( sequential ) {
253            ++row->n;
254        }
255        else {
256            if (rownum > 0) {
257                row->n = --rownum;
258            }
259            else {
260                return -1; /* invalid row */
261            }
262        }
263    }
264
265    if (res->random) {
266        if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
267            *rowp = NULL;
268            apr_pool_cleanup_run(res->pool, res->res, clear_result);
269            res->res = NULL;
270            return -1;
271        }
272    }
273    else {
274        if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
275            /* no data; we have to fetch some */
276            row->n -= res->ntuples;
277            if (res->res != NULL) {
278                PQclear(res->res);
279            }
280            res->res = PQgetResult(res->handle);
281            if (res->res) {
282                res->ntuples = PQntuples(res->res);
283                while (res->ntuples == 0) {
284                    /* if we got an empty result, clear it, wait a mo, try
285                     * again */
286                    PQclear(res->res);
287                    apr_sleep(100000);        /* 0.1 secs */
288                    res->res = PQgetResult(res->handle);
289                    if (res->res) {
290                        res->ntuples = PQntuples(res->res);
291                    }
292                    else {
293                        return -1;
294                    }
295                }
296                if (res->sz == 0) {
297                    res->sz = PQnfields(res->res);
298                }
299            }
300            else {
301                return -1;
302            }
303        }
304    }
305    return 0;
306}
307
308static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
309{
310    return PQgetvalue(row->res->res, row->n, n);
311}
312
313static apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n,
314                                        apr_dbd_type_e type, void *data)
315{
316    if (PQgetisnull(row->res->res, row->n, n)) {
317        return APR_ENOENT;
318    }
319
320    switch (type) {
321    case APR_DBD_TYPE_TINY:
322        *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
323        break;
324    case APR_DBD_TYPE_UTINY:
325        *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
326        break;
327    case APR_DBD_TYPE_SHORT:
328        *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
329        break;
330    case APR_DBD_TYPE_USHORT:
331        *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
332        break;
333    case APR_DBD_TYPE_INT:
334        *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
335        break;
336    case APR_DBD_TYPE_UINT:
337        *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
338        break;
339    case APR_DBD_TYPE_LONG:
340        *(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
341        break;
342    case APR_DBD_TYPE_ULONG:
343        *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
344        break;
345    case APR_DBD_TYPE_LONGLONG:
346        *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
347        break;
348    case APR_DBD_TYPE_ULONGLONG:
349        *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
350        break;
351    case APR_DBD_TYPE_FLOAT:
352        *(float*)data = (float)atof(PQgetvalue(row->res->res, row->n, n));
353        break;
354    case APR_DBD_TYPE_DOUBLE:
355        *(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
356        break;
357    case APR_DBD_TYPE_STRING:
358    case APR_DBD_TYPE_TEXT:
359    case APR_DBD_TYPE_TIME:
360    case APR_DBD_TYPE_DATE:
361    case APR_DBD_TYPE_DATETIME:
362    case APR_DBD_TYPE_TIMESTAMP:
363    case APR_DBD_TYPE_ZTIMESTAMP:
364        *(char**)data = PQgetvalue(row->res->res, row->n, n);
365        break;
366    case APR_DBD_TYPE_BLOB:
367    case APR_DBD_TYPE_CLOB:
368        {
369        apr_bucket *e;
370        apr_bucket_brigade *b = (apr_bucket_brigade*)data;
371
372        e = apr_bucket_pool_create(PQgetvalue(row->res->res, row->n, n),
373                                   PQgetlength(row->res->res, row->n, n),
374                                   row->res->pool, b->bucket_alloc);
375        APR_BRIGADE_INSERT_TAIL(b, e);
376        }
377        break;
378    case APR_DBD_TYPE_NULL:
379        *(void**)data = NULL;
380        break;
381    default:
382        return APR_EGENERAL;
383    }
384
385    return APR_SUCCESS;
386}
387
388static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
389{
390    return PQerrorMessage(sql->conn);
391}
392
393static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
394{
395    PGresult *res;
396    int ret;
397    if (sql->trans && sql->trans->errnum) {
398        return sql->trans->errnum;
399    }
400
401    if (TXN_IGNORE_ERRORS(sql->trans)) {
402        PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
403        if (res) {
404            int ret = PQresultStatus(res);
405            PQclear(res);
406            if (!dbd_pgsql_is_success(ret)) {
407                sql->trans->errnum = ret;
408                return PGRES_FATAL_ERROR;
409            }
410        } else {
411            return sql->trans->errnum = PGRES_FATAL_ERROR;
412        }
413    }
414
415    res = PQexec(sql->conn, query);
416    if (res) {
417        ret = PQresultStatus(res);
418        if (dbd_pgsql_is_success(ret)) {
419            /* ugh, making 0 return-success doesn't fit */
420            ret = 0;
421        }
422        *nrows = atoi(PQcmdTuples(res));
423        PQclear(res);
424    }
425    else {
426        ret = PGRES_FATAL_ERROR;
427    }
428
429    if (ret != 0){
430        if (TXN_IGNORE_ERRORS(sql->trans)) {
431            PGresult *res = PQexec(sql->conn,
432                                   "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
433            if (res) {
434                int ret = PQresultStatus(res);
435                PQclear(res);
436                if (!dbd_pgsql_is_success(ret)) {
437                    sql->trans->errnum = ret;
438                    return PGRES_FATAL_ERROR;
439                }
440            } else {
441                sql->trans->errnum = ret;
442                return PGRES_FATAL_ERROR;
443            }
444        } else if (TXN_NOTICE_ERRORS(sql->trans)){
445            sql->trans->errnum = ret;
446        }
447    } else {
448        if (TXN_IGNORE_ERRORS(sql->trans)) {
449            PGresult *res = PQexec(sql->conn,
450                                   "RELEASE SAVEPOINT APR_DBD_TXN_SP");
451            if (res) {
452                int ret = PQresultStatus(res);
453                PQclear(res);
454                if (!dbd_pgsql_is_success(ret)) {
455                    sql->trans->errnum = ret;
456                    return PGRES_FATAL_ERROR;
457                }
458            } else {
459                sql->trans->errnum = ret;
460                return PGRES_FATAL_ERROR;
461            }
462        }
463    }
464
465    return ret;
466}
467
468static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
469                                    apr_dbd_t *sql)
470{
471    size_t len = strlen(arg);
472    char *ret = apr_palloc(pool, 2*len + 2);
473    PQescapeStringConn(sql->conn, ret, arg, len, NULL);
474    return ret;
475}
476
477static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
478                             const char *query, const char *label,
479                             int nargs, int nvals, apr_dbd_type_e *types,
480                             apr_dbd_prepared_t **statement)
481{
482    char *sqlcmd;
483    char *sqlptr;
484    size_t length, qlen;
485    int i = 0;
486    const char **args;
487    size_t alen;
488    int ret;
489    PGresult *res;
490
491    if (!*statement) {
492        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
493    }
494    (*statement)->nargs = nargs;
495    (*statement)->nvals = nvals;
496    (*statement)->types = types;
497
498    args = apr_palloc(pool, nargs * sizeof(*args));
499
500    qlen = strlen(query);
501    length = qlen + 1;
502
503    for (i = 0; i < nargs; i++) {
504        switch (types[i]) {
505        case APR_DBD_TYPE_TINY:
506        case APR_DBD_TYPE_UTINY:
507        case APR_DBD_TYPE_SHORT:
508        case APR_DBD_TYPE_USHORT:
509            args[i] = "smallint";
510            break;
511        case APR_DBD_TYPE_INT:
512        case APR_DBD_TYPE_UINT:
513            args[i] = "integer";
514            break;
515        case APR_DBD_TYPE_LONG:
516        case APR_DBD_TYPE_ULONG:
517        case APR_DBD_TYPE_LONGLONG:
518        case APR_DBD_TYPE_ULONGLONG:
519            args[i] = "bigint";
520            break;
521        case APR_DBD_TYPE_FLOAT:
522            args[i] = "real";
523            break;
524        case APR_DBD_TYPE_DOUBLE:
525            args[i] = "double precision";
526            break;
527        case APR_DBD_TYPE_TEXT:
528            args[i] = "text";
529            break;
530        case APR_DBD_TYPE_TIME:
531            args[i] = "time";
532            break;
533        case APR_DBD_TYPE_DATE:
534            args[i] = "date";
535            break;
536        case APR_DBD_TYPE_DATETIME:
537        case APR_DBD_TYPE_TIMESTAMP:
538            args[i] = "timestamp";
539            break;
540        case APR_DBD_TYPE_ZTIMESTAMP:
541            args[i] = "timestamp with time zone";
542            break;
543        case APR_DBD_TYPE_BLOB:
544        case APR_DBD_TYPE_CLOB:
545            args[i] = "bytea";
546            break;
547        case APR_DBD_TYPE_NULL:
548            args[i] = "varchar"; /* XXX Eh? */
549            break;
550        default:
551            args[i] = "varchar";
552            break;
553        }
554        length += 1 + strlen(args[i]);
555    }
556
557    if (!label) {
558        /* don't really prepare; use in execParams instead */
559        (*statement)->prepared = 0;
560        (*statement)->name = apr_pstrdup(pool, query);
561        return 0;
562    }
563    (*statement)->name = apr_pstrdup(pool, label);
564
565    /* length of SQL query that prepares this statement */
566    length = 8 + strlen(label) + 2 + 4 + length + 1;
567    sqlcmd = apr_palloc(pool, length);
568    sqlptr = sqlcmd;
569    memcpy(sqlptr, "PREPARE ", 8);
570    sqlptr += 8;
571    length = strlen(label);
572    memcpy(sqlptr, label, length);
573    sqlptr += length;
574    if (nargs > 0) {
575        memcpy(sqlptr, " (",2);
576        sqlptr += 2;
577        for (i=0; i < nargs; ++i) {
578            alen = strlen(args[i]);
579            memcpy(sqlptr, args[i], alen);
580            sqlptr += alen;
581            *sqlptr++ = ',';
582        }
583        sqlptr[-1] =  ')';
584    }
585    memcpy(sqlptr, " AS ", 4);
586    sqlptr += 4;
587    memcpy(sqlptr, query, qlen);
588    sqlptr += qlen;
589    *sqlptr = 0;
590
591    res = PQexec(sql->conn, sqlcmd);
592    if ( res ) {
593        ret = PQresultStatus(res);
594        if (dbd_pgsql_is_success(ret)) {
595            ret = 0;
596        }
597        /* Hmmm, do we do this here or register it on the pool? */
598        PQclear(res);
599    }
600    else {
601        ret = PGRES_FATAL_ERROR;
602    }
603    (*statement)->prepared = 1;
604
605    return ret;
606}
607
608static int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
609                                     int *nrows, apr_dbd_prepared_t *statement,
610                                     const char **values,
611                                     const int *len, const int *fmt)
612{
613    int ret;
614    PGresult *res;
615
616    if (TXN_IGNORE_ERRORS(sql->trans)) {
617        PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
618        if (res) {
619            int ret = PQresultStatus(res);
620            PQclear(res);
621            if (!dbd_pgsql_is_success(ret)) {
622                sql->trans->errnum = ret;
623                return PGRES_FATAL_ERROR;
624            }
625        } else {
626            return sql->trans->errnum = PGRES_FATAL_ERROR;
627        }
628    }
629
630    if (statement->prepared) {
631        res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
632                             values, len, fmt, 0);
633    }
634    else {
635        res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
636                           values, len, fmt, 0);
637    }
638    if (res) {
639        ret = PQresultStatus(res);
640        if (dbd_pgsql_is_success(ret)) {
641            ret = 0;
642        }
643        *nrows = atoi(PQcmdTuples(res));
644        PQclear(res);
645    }
646    else {
647        ret = PGRES_FATAL_ERROR;
648    }
649
650    if (ret != 0){
651        if (TXN_IGNORE_ERRORS(sql->trans)) {
652            PGresult *res = PQexec(sql->conn,
653                                   "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
654            if (res) {
655                int ret = PQresultStatus(res);
656                PQclear(res);
657                if (!dbd_pgsql_is_success(ret)) {
658                    sql->trans->errnum = ret;
659                    return PGRES_FATAL_ERROR;
660                }
661            } else {
662                sql->trans->errnum = ret;
663                return PGRES_FATAL_ERROR;
664            }
665        } else if (TXN_NOTICE_ERRORS(sql->trans)){
666            sql->trans->errnum = ret;
667        }
668    } else {
669        if (TXN_IGNORE_ERRORS(sql->trans)) {
670            PGresult *res = PQexec(sql->conn,
671                                   "RELEASE SAVEPOINT APR_DBD_TXN_SP");
672            if (res) {
673                int ret = PQresultStatus(res);
674                PQclear(res);
675                if (!dbd_pgsql_is_success(ret)) {
676                    sql->trans->errnum = ret;
677                    return PGRES_FATAL_ERROR;
678                }
679            } else {
680                sql->trans->errnum = ret;
681                return PGRES_FATAL_ERROR;
682            }
683        }
684    }
685
686    return ret;
687}
688
689static void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
690                           const char **values,
691                           const char **val, int *len, int *fmt)
692{
693    int i, j;
694
695    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
696        if (values[j] == NULL) {
697            val[i] = NULL;
698        }
699        else {
700            switch (statement->types[i]) {
701            case APR_DBD_TYPE_BLOB:
702            case APR_DBD_TYPE_CLOB:
703                val[i] = (char *)values[j];
704                len[i] = atoi(values[++j]);
705                fmt[i] = 1;
706
707                /* skip table and column */
708                j += 2;
709                break;
710            default:
711                val[i] = values[j];
712                break;
713            }
714        }
715    }
716
717    return;
718}
719
720static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
721                            int *nrows, apr_dbd_prepared_t *statement,
722                            const char **values)
723{
724    int *len, *fmt;
725    const char **val;
726
727    if (sql->trans && sql->trans->errnum) {
728        return sql->trans->errnum;
729    }
730
731    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
732    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
733    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
734
735    dbd_pgsql_bind(statement, values, val, len, fmt);
736
737    return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
738                                     val, len, fmt);
739}
740
741static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
742                             int *nrows, apr_dbd_prepared_t *statement,
743                             va_list args)
744{
745    const char **values;
746    int i;
747
748    if (sql->trans && sql->trans->errnum) {
749        return sql->trans->errnum;
750    }
751
752    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
753
754    for (i = 0; i < statement->nvals; i++) {
755        values[i] = va_arg(args, const char*);
756    }
757
758    return dbd_pgsql_pquery(pool, sql, nrows, statement, values);
759}
760
761static int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
762                                      apr_dbd_results_t **results,
763                                      apr_dbd_prepared_t *statement,
764                                      int seek, const char **values,
765                                      const int *len, const int *fmt)
766{
767    PGresult *res;
768    int rv;
769    int ret = 0;
770
771    if (seek) { /* synchronous query */
772        if (TXN_IGNORE_ERRORS(sql->trans)) {
773            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
774            if (res) {
775                int ret = PQresultStatus(res);
776                PQclear(res);
777                if (!dbd_pgsql_is_success(ret)) {
778                    sql->trans->errnum = ret;
779                    return PGRES_FATAL_ERROR;
780                }
781            } else {
782                sql->trans->errnum = ret;
783                return PGRES_FATAL_ERROR;
784            }
785        }
786        if (statement->prepared) {
787            res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
788                                 values, len, fmt, 0);
789        }
790        else {
791            res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
792                               values, len, fmt, 0);
793        }
794        if (res) {
795            ret = PQresultStatus(res);
796            if (dbd_pgsql_is_success(ret)) {
797                ret = 0;
798            }
799            else {
800                PQclear(res);
801            }
802        }
803        else {
804            ret = PGRES_FATAL_ERROR;
805        }
806        if (ret != 0) {
807            if (TXN_IGNORE_ERRORS(sql->trans)) {
808                PGresult *res = PQexec(sql->conn,
809                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
810                if (res) {
811                    int ret = PQresultStatus(res);
812                    PQclear(res);
813                    if (!dbd_pgsql_is_success(ret)) {
814                        sql->trans->errnum = ret;
815                        return PGRES_FATAL_ERROR;
816                    }
817                } else {
818                    sql->trans->errnum = ret;
819                    return PGRES_FATAL_ERROR;
820                }
821            } else if (TXN_NOTICE_ERRORS(sql->trans)){
822                sql->trans->errnum = ret;
823            }
824            return ret;
825        } else {
826            if (TXN_IGNORE_ERRORS(sql->trans)) {
827                PGresult *res = PQexec(sql->conn,
828                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
829                if (res) {
830                    int ret = PQresultStatus(res);
831                    PQclear(res);
832                    if (!dbd_pgsql_is_success(ret)) {
833                        sql->trans->errnum = ret;
834                        return PGRES_FATAL_ERROR;
835                    }
836                } else {
837                    sql->trans->errnum = ret;
838                    return PGRES_FATAL_ERROR;
839                }
840            }
841        }
842        if (!*results) {
843            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
844        }
845        (*results)->res = res;
846        (*results)->ntuples = PQntuples(res);
847        (*results)->sz = PQnfields(res);
848        (*results)->random = seek;
849        (*results)->pool = pool;
850        apr_pool_cleanup_register(pool, res, clear_result,
851                                  apr_pool_cleanup_null);
852    }
853    else {
854        if (TXN_IGNORE_ERRORS(sql->trans)) {
855            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
856            if (res) {
857                int ret = PQresultStatus(res);
858                PQclear(res);
859                if (!dbd_pgsql_is_success(ret)) {
860                    sql->trans->errnum = ret;
861                    return PGRES_FATAL_ERROR;
862                }
863            } else {
864                sql->trans->errnum = ret;
865                return PGRES_FATAL_ERROR;
866            }
867        }
868        if (statement->prepared) {
869            rv = PQsendQueryPrepared(sql->conn, statement->name,
870                                     statement->nargs, values, len, fmt, 0);
871        }
872        else {
873            rv = PQsendQueryParams(sql->conn, statement->name,
874                                   statement->nargs, 0, values, len, fmt, 0);
875        }
876        if (rv == 0) {
877            if (TXN_IGNORE_ERRORS(sql->trans)) {
878                PGresult *res = PQexec(sql->conn,
879                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
880                if (res) {
881                    int ret = PQresultStatus(res);
882                    PQclear(res);
883                    if (!dbd_pgsql_is_success(ret)) {
884                        sql->trans->errnum = ret;
885                        return PGRES_FATAL_ERROR;
886                    }
887                } else {
888                    sql->trans->errnum = ret;
889                    return PGRES_FATAL_ERROR;
890                }
891            } else if (TXN_NOTICE_ERRORS(sql->trans)){
892                sql->trans->errnum = 1;
893            }
894            return 1;
895        } else {
896            if (TXN_IGNORE_ERRORS(sql->trans)) {
897                PGresult *res = PQexec(sql->conn,
898                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
899                if (res) {
900                    int ret = PQresultStatus(res);
901                    PQclear(res);
902                    if (!dbd_pgsql_is_success(ret)) {
903                        sql->trans->errnum = ret;
904                        return PGRES_FATAL_ERROR;
905                    }
906                } else {
907                    sql->trans->errnum = ret;
908                    return PGRES_FATAL_ERROR;
909                }
910            }
911        }
912        if (!*results) {
913            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
914        }
915        (*results)->random = seek;
916        (*results)->handle = sql->conn;
917        (*results)->pool = pool;
918    }
919
920    return ret;
921}
922
923static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
924                             apr_dbd_results_t **results,
925                             apr_dbd_prepared_t *statement,
926                             int seek, const char **values)
927{
928    int *len, *fmt;
929    const char **val;
930
931    if (sql->trans && sql->trans->errnum) {
932        return sql->trans->errnum;
933    }
934
935    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
936    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
937    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
938
939    dbd_pgsql_bind(statement, values, val, len, fmt);
940
941    return dbd_pgsql_pselect_internal(pool, sql, results, statement,
942                                      seek, val, len, fmt);
943}
944
945static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
946                              apr_dbd_results_t **results,
947                              apr_dbd_prepared_t *statement,
948                              int seek, va_list args)
949{
950    const char **values;
951    int i;
952
953    if (sql->trans && sql->trans->errnum) {
954        return sql->trans->errnum;
955    }
956
957    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
958
959    for (i = 0; i < statement->nvals; i++) {
960        values[i] = va_arg(args, const char*);
961    }
962
963    return dbd_pgsql_pselect(pool, sql, results, statement, seek, values);
964}
965
966static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
967                            const void **values,
968                            const char **val, int *len, int *fmt)
969{
970    int i, j;
971    apr_dbd_type_e type;
972
973    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
974        type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
975
976        switch (type) {
977        case APR_DBD_TYPE_TINY:
978            val[i] = apr_itoa(pool, *(char*)values[j]);
979            break;
980        case APR_DBD_TYPE_UTINY:
981            val[i] = apr_itoa(pool, *(unsigned char*)values[j]);
982            break;
983        case APR_DBD_TYPE_SHORT:
984            val[i] = apr_itoa(pool, *(short*)values[j]);
985            break;
986        case APR_DBD_TYPE_USHORT:
987            val[i] = apr_itoa(pool, *(unsigned short*)values[j]);
988            break;
989        case APR_DBD_TYPE_INT:
990            val[i] = apr_itoa(pool, *(int*)values[j]);
991            break;
992        case APR_DBD_TYPE_UINT:
993            val[i] = apr_itoa(pool, *(unsigned int*)values[j]);
994            break;
995        case APR_DBD_TYPE_LONG:
996            val[i] = apr_ltoa(pool, *(long*)values[j]);
997            break;
998        case APR_DBD_TYPE_ULONG:
999            val[i] = apr_ltoa(pool, *(unsigned long*)values[j]);
1000            break;
1001        case APR_DBD_TYPE_LONGLONG:
1002            val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
1003                                  *(apr_int64_t*)values[j]);
1004            break;
1005        case APR_DBD_TYPE_ULONGLONG:
1006            val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
1007                                  *(apr_uint64_t*)values[j]);
1008            break;
1009        case APR_DBD_TYPE_FLOAT:
1010            val[i] = apr_psprintf(pool, "%f", *(float*)values[j]);
1011            break;
1012        case APR_DBD_TYPE_DOUBLE:
1013            val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]);
1014            break;
1015        case APR_DBD_TYPE_STRING:
1016        case APR_DBD_TYPE_TEXT:
1017        case APR_DBD_TYPE_TIME:
1018        case APR_DBD_TYPE_DATE:
1019        case APR_DBD_TYPE_DATETIME:
1020        case APR_DBD_TYPE_TIMESTAMP:
1021        case APR_DBD_TYPE_ZTIMESTAMP:
1022            val[i] = values[j];
1023            break;
1024        case APR_DBD_TYPE_BLOB:
1025        case APR_DBD_TYPE_CLOB:
1026            val[i] = (char*)values[j];
1027            len[i] = *(apr_size_t*)values[++j];
1028            fmt[i] = 1;
1029
1030            /* skip table and column */
1031            j += 2;
1032            break;
1033        case APR_DBD_TYPE_NULL:
1034        default:
1035            val[i] = NULL;
1036            break;
1037        }
1038    }
1039
1040    return;
1041}
1042
1043static int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
1044                             int *nrows, apr_dbd_prepared_t * statement,
1045                             const void **values)
1046{
1047    int *len, *fmt;
1048    const char **val;
1049
1050    if (sql->trans && sql->trans->errnum) {
1051        return sql->trans->errnum;
1052    }
1053
1054    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1055    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1056    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1057
1058    dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1059
1060    return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
1061                                     val, len, fmt);
1062}
1063
1064static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
1065                              int *nrows, apr_dbd_prepared_t * statement,
1066                              va_list args)
1067{
1068    const void **values;
1069    int i;
1070
1071    if (sql->trans && sql->trans->errnum) {
1072        return sql->trans->errnum;
1073    }
1074
1075    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1076
1077    for (i = 0; i < statement->nvals; i++) {
1078        values[i] = va_arg(args, const void*);
1079    }
1080
1081    return dbd_pgsql_pbquery(pool, sql, nrows, statement, values);
1082}
1083
1084static int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
1085                              apr_dbd_results_t ** results,
1086                              apr_dbd_prepared_t * statement,
1087                              int seek, const void **values)
1088{
1089    int *len, *fmt;
1090    const char **val;
1091
1092    if (sql->trans && sql->trans->errnum) {
1093        return sql->trans->errnum;
1094    }
1095
1096    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1097    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1098    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1099
1100    dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1101
1102    return dbd_pgsql_pselect_internal(pool, sql, results, statement,
1103                                      seek, val, len, fmt);
1104}
1105
1106static int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
1107                               apr_dbd_results_t ** results,
1108                               apr_dbd_prepared_t * statement, int seek,
1109                               va_list args)
1110{
1111    const void **values;
1112    int i;
1113
1114    if (sql->trans && sql->trans->errnum) {
1115        return sql->trans->errnum;
1116    }
1117
1118    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1119
1120    for (i = 0; i < statement->nvals; i++) {
1121        values[i] = va_arg(args, const void*);
1122    }
1123
1124    return dbd_pgsql_pbselect(pool, sql, results, statement, seek, values);
1125}
1126
1127static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1128                                       apr_dbd_transaction_t **trans)
1129{
1130    int ret = 0;
1131    PGresult *res;
1132
1133    /* XXX handle recursive transactions here */
1134
1135    res = PQexec(handle->conn, "BEGIN TRANSACTION");
1136    if (res) {
1137        ret = PQresultStatus(res);
1138        if (dbd_pgsql_is_success(ret)) {
1139            ret = 0;
1140            if (!*trans) {
1141                *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1142            }
1143        }
1144        PQclear(res);
1145        (*trans)->handle = handle;
1146        handle->trans = *trans;
1147    }
1148    else {
1149        ret = PGRES_FATAL_ERROR;
1150    }
1151    return ret;
1152}
1153
1154static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
1155{
1156    PGresult *res;
1157    int ret = -1;                /* no transaction is an error cond */
1158    if (trans) {
1159        /* rollback on error or explicit rollback request */
1160        if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1161            trans->errnum = 0;
1162            res = PQexec(trans->handle->conn, "ROLLBACK");
1163        }
1164        else {
1165            res = PQexec(trans->handle->conn, "COMMIT");
1166        }
1167        if (res) {
1168            ret = PQresultStatus(res);
1169            if (dbd_pgsql_is_success(ret)) {
1170                ret = 0;
1171            }
1172            PQclear(res);
1173        }
1174        else {
1175            ret = PGRES_FATAL_ERROR;
1176        }
1177        trans->handle->trans = NULL;
1178    }
1179    return ret;
1180}
1181
1182static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
1183{
1184    if (!trans)
1185        return APR_DBD_TRANSACTION_COMMIT;
1186
1187    return trans->mode;
1188}
1189
1190static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
1191                                          int mode)
1192{
1193    if (!trans)
1194        return APR_DBD_TRANSACTION_COMMIT;
1195
1196    return trans->mode = (mode & TXN_MODE_BITS);
1197}
1198
1199static void null_notice_receiver(void *arg, const PGresult *res)
1200{
1201    /* nothing */
1202}
1203
1204static void null_notice_processor(void *arg, const char *message)
1205{
1206    /* nothing */
1207}
1208
1209static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params,
1210                                 const char **error)
1211{
1212    apr_dbd_t *sql;
1213
1214    PGconn *conn = PQconnectdb(params);
1215
1216    /* if there's an error in the connect string or something we get
1217     * back a * bogus connection object, and things like PQreset are
1218     * liable to segfault, so just close it out now.  it would be nice
1219     * if we could give an indication of why we failed to connect... */
1220    if (PQstatus(conn) != CONNECTION_OK) {
1221        if (error) {
1222            *error = apr_pstrdup(pool, PQerrorMessage(conn));
1223        }
1224        PQfinish(conn);
1225        return NULL;
1226    }
1227
1228    PQsetNoticeReceiver(conn, null_notice_receiver, NULL);
1229    PQsetNoticeProcessor(conn, null_notice_processor, NULL);
1230
1231    sql = apr_pcalloc (pool, sizeof (*sql));
1232
1233    sql->conn = conn;
1234
1235    return sql;
1236}
1237
1238static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
1239{
1240    PQfinish(handle->conn);
1241    return APR_SUCCESS;
1242}
1243
1244static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
1245                                         apr_dbd_t *handle)
1246{
1247    if (PQstatus(handle->conn) != CONNECTION_OK) {
1248        PQreset(handle->conn);
1249        if (PQstatus(handle->conn) != CONNECTION_OK) {
1250            return APR_EGENERAL;
1251        }
1252    }
1253    return APR_SUCCESS;
1254}
1255
1256static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
1257                               const char *name)
1258{
1259    return APR_ENOTIMPL;
1260}
1261
1262static void *dbd_pgsql_native(apr_dbd_t *handle)
1263{
1264    return handle->conn;
1265}
1266
1267static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
1268{
1269    return res->sz;
1270}
1271
1272static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
1273{
1274    if (res->random) {
1275        return res->ntuples;
1276    }
1277    else {
1278        return -1;
1279    }
1280}
1281
1282APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
1283    "pgsql",
1284    NULL,
1285    dbd_pgsql_native,
1286    dbd_pgsql_open,
1287    dbd_pgsql_check_conn,
1288    dbd_pgsql_close,
1289    dbd_pgsql_select_db,
1290    dbd_pgsql_start_transaction,
1291    dbd_pgsql_end_transaction,
1292    dbd_pgsql_query,
1293    dbd_pgsql_select,
1294    dbd_pgsql_num_cols,
1295    dbd_pgsql_num_tuples,
1296    dbd_pgsql_get_row,
1297    dbd_pgsql_get_entry,
1298    dbd_pgsql_error,
1299    dbd_pgsql_escape,
1300    dbd_pgsql_prepare,
1301    dbd_pgsql_pvquery,
1302    dbd_pgsql_pvselect,
1303    dbd_pgsql_pquery,
1304    dbd_pgsql_pselect,
1305    dbd_pgsql_get_name,
1306    dbd_pgsql_transaction_mode_get,
1307    dbd_pgsql_transaction_mode_set,
1308    "$%d",
1309    dbd_pgsql_pvbquery,
1310    dbd_pgsql_pvbselect,
1311    dbd_pgsql_pbquery,
1312    dbd_pgsql_pbselect,
1313    dbd_pgsql_datum_get
1314};
1315#endif
1316