1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251876Speter * contributor license agreements.  See the NOTICE file distributed with
3251876Speter * this work for additional information regarding copyright ownership.
4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251876Speter * (the "License"); you may not use this file except in compliance with
6251876Speter * the License.  You may obtain a copy of the License at
7251876Speter *
8251876Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251876Speter *
10251876Speter * Unless required by applicable law or agreed to in writing, software
11251876Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251876Speter * See the License for the specific language governing permissions and
14251876Speter * limitations under the License.
15251876Speter */
16251876Speter
17251876Speter#include "apu.h"
18251876Speter
19251876Speter#if APU_HAVE_PGSQL
20251876Speter
21251876Speter#include "apu_config.h"
22251876Speter
23251876Speter#include <ctype.h>
24251876Speter#include <stdlib.h>
25251876Speter
26251876Speter#ifdef HAVE_LIBPQ_FE_H
27251876Speter#include <libpq-fe.h>
28251876Speter#elif defined(HAVE_POSTGRESQL_LIBPQ_FE_H)
29251876Speter#include <postgresql/libpq-fe.h>
30251876Speter#endif
31251876Speter
32251876Speter#include "apr_strings.h"
33251876Speter#include "apr_time.h"
34251876Speter#include "apr_buckets.h"
35251876Speter
36251876Speter#include "apr_dbd_internal.h"
37251876Speter
38251876Speterstruct apr_dbd_transaction_t {
39251876Speter    int mode;
40251876Speter    int errnum;
41251876Speter    apr_dbd_t *handle;
42251876Speter};
43251876Speter
44251876Speterstruct apr_dbd_t {
45251876Speter    PGconn *conn;
46251876Speter    apr_dbd_transaction_t *trans;
47251876Speter};
48251876Speter
49251876Speterstruct apr_dbd_results_t {
50251876Speter    int random;
51251876Speter    PGconn *handle;
52251876Speter    PGresult *res;
53251876Speter    size_t ntuples;
54251876Speter    size_t sz;
55251876Speter    size_t index;
56251876Speter    apr_pool_t *pool;
57251876Speter};
58251876Speter
59251876Speterstruct apr_dbd_row_t {
60251876Speter    int n;
61251876Speter    apr_dbd_results_t *res;
62251876Speter};
63251876Speter
64251876Speterstruct apr_dbd_prepared_t {
65251876Speter    const char *name;
66251876Speter    int prepared;
67251876Speter    int nargs;
68251876Speter    int nvals;
69251876Speter    apr_dbd_type_e *types;
70251876Speter};
71251876Speter
72251876Speter#define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
73251876Speter                                 || ((x) == PGRES_COMMAND_OK) \
74251876Speter                                 || ((x) == PGRES_TUPLES_OK))
75251876Speter
76251876Speterstatic apr_status_t clear_result(void *data)
77251876Speter{
78251876Speter    PQclear(data);
79251876Speter    return APR_SUCCESS;
80251876Speter}
81251876Speter
82251876Speterstatic int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
83251876Speter                            apr_dbd_results_t **results,
84251876Speter                            const char *query, int seek)
85251876Speter{
86251876Speter    PGresult *res;
87251876Speter    int ret;
88251876Speter    if ( sql->trans && sql->trans->errnum ) {
89251876Speter        return sql->trans->errnum;
90251876Speter    }
91251876Speter    if (seek) { /* synchronous query */
92251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
93251876Speter            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
94251876Speter            if (res) {
95251876Speter                int ret = PQresultStatus(res);
96251876Speter                PQclear(res);
97251876Speter                if (!dbd_pgsql_is_success(ret)) {
98251876Speter                    sql->trans->errnum = ret;
99251876Speter                    return PGRES_FATAL_ERROR;
100251876Speter                }
101251876Speter            } else {
102251876Speter                return sql->trans->errnum = PGRES_FATAL_ERROR;
103251876Speter            }
104251876Speter        }
105251876Speter        res = PQexec(sql->conn, query);
106251876Speter        if (res) {
107251876Speter            ret = PQresultStatus(res);
108251876Speter            if (dbd_pgsql_is_success(ret)) {
109251876Speter                ret = 0;
110251876Speter            } else {
111251876Speter                PQclear(res);
112251876Speter            }
113251876Speter        } else {
114251876Speter            ret = PGRES_FATAL_ERROR;
115251876Speter        }
116251876Speter        if (ret != 0) {
117251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
118251876Speter                PGresult *res = PQexec(sql->conn,
119251876Speter                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
120251876Speter                if (res) {
121251876Speter                    int ret = PQresultStatus(res);
122251876Speter                    PQclear(res);
123251876Speter                    if (!dbd_pgsql_is_success(ret)) {
124251876Speter                        sql->trans->errnum = ret;
125251876Speter                        return PGRES_FATAL_ERROR;
126251876Speter                    }
127251876Speter                } else {
128251876Speter                    return sql->trans->errnum = PGRES_FATAL_ERROR;
129251876Speter                }
130251876Speter            } else if (TXN_NOTICE_ERRORS(sql->trans)){
131251876Speter                sql->trans->errnum = ret;
132251876Speter            }
133251876Speter            return ret;
134251876Speter        } else {
135251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
136251876Speter                PGresult *res = PQexec(sql->conn,
137251876Speter                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
138251876Speter                if (res) {
139251876Speter                    int ret = PQresultStatus(res);
140251876Speter                    PQclear(res);
141251876Speter                    if (!dbd_pgsql_is_success(ret)) {
142251876Speter                        sql->trans->errnum = ret;
143251876Speter                        return PGRES_FATAL_ERROR;
144251876Speter                    }
145251876Speter                } else {
146251876Speter                    return sql->trans->errnum = PGRES_FATAL_ERROR;
147251876Speter                }
148251876Speter            }
149251876Speter        }
150251876Speter        if (!*results) {
151251876Speter            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
152251876Speter        }
153251876Speter        (*results)->res = res;
154251876Speter        (*results)->ntuples = PQntuples(res);
155251876Speter        (*results)->sz = PQnfields(res);
156251876Speter        (*results)->random = seek;
157251876Speter        (*results)->pool = pool;
158251876Speter        apr_pool_cleanup_register(pool, res, clear_result,
159251876Speter                                  apr_pool_cleanup_null);
160251876Speter    }
161251876Speter    else {
162251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
163251876Speter            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
164251876Speter            if (res) {
165251876Speter                int ret = PQresultStatus(res);
166251876Speter                PQclear(res);
167251876Speter                if (!dbd_pgsql_is_success(ret)) {
168251876Speter                    sql->trans->errnum = ret;
169251876Speter                    return PGRES_FATAL_ERROR;
170251876Speter                }
171251876Speter            } else {
172251876Speter                return sql->trans->errnum = PGRES_FATAL_ERROR;
173251876Speter            }
174251876Speter        }
175251876Speter        if (PQsendQuery(sql->conn, query) == 0) {
176251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
177251876Speter                PGresult *res = PQexec(sql->conn,
178251876Speter                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
179251876Speter                if (res) {
180251876Speter                    int ret = PQresultStatus(res);
181251876Speter                    PQclear(res);
182251876Speter                    if (!dbd_pgsql_is_success(ret)) {
183251876Speter                        sql->trans->errnum = ret;
184251876Speter                        return PGRES_FATAL_ERROR;
185251876Speter                    }
186251876Speter                } else {
187251876Speter                    return sql->trans->errnum = PGRES_FATAL_ERROR;
188251876Speter                }
189251876Speter            } else if (TXN_NOTICE_ERRORS(sql->trans)){
190251876Speter                sql->trans->errnum = 1;
191251876Speter            }
192251876Speter            return 1;
193251876Speter        } else {
194251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
195251876Speter                PGresult *res = PQexec(sql->conn,
196251876Speter                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
197251876Speter                if (res) {
198251876Speter                    int ret = PQresultStatus(res);
199251876Speter                    PQclear(res);
200251876Speter                    if (!dbd_pgsql_is_success(ret)) {
201251876Speter                        sql->trans->errnum = ret;
202251876Speter                        return PGRES_FATAL_ERROR;
203251876Speter                    }
204251876Speter                } else {
205251876Speter                    return sql->trans->errnum = PGRES_FATAL_ERROR;
206251876Speter                }
207251876Speter            }
208251876Speter        }
209251876Speter        if (*results == NULL) {
210251876Speter            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
211251876Speter        }
212251876Speter        (*results)->random = seek;
213251876Speter        (*results)->handle = sql->conn;
214251876Speter        (*results)->pool = pool;
215251876Speter    }
216251876Speter    return 0;
217251876Speter}
218251876Speter
219251876Speterstatic const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n)
220251876Speter{
221251876Speter    if (res->res) {
222251876Speter        if ((n>=0) && (PQnfields(res->res) > n)) {
223251876Speter            return PQfname(res->res,n);
224251876Speter        }
225251876Speter    }
226251876Speter    return NULL;
227251876Speter}
228251876Speter
229251876Speterstatic int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
230251876Speter                             apr_dbd_row_t **rowp, int rownum)
231251876Speter{
232251876Speter    apr_dbd_row_t *row = *rowp;
233251876Speter    int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
234251876Speter
235251876Speter    if (row == NULL) {
236251876Speter        row = apr_palloc(pool, sizeof(apr_dbd_row_t));
237251876Speter        *rowp = row;
238251876Speter        row->res = res;
239251876Speter        if ( sequential ) {
240251876Speter            row->n = 0;
241251876Speter        }
242251876Speter        else {
243251876Speter            if (rownum > 0) {
244251876Speter                row->n = --rownum;
245251876Speter            }
246251876Speter            else {
247251876Speter                return -1; /* invalid row */
248251876Speter            }
249251876Speter        }
250251876Speter    }
251251876Speter    else {
252251876Speter        if ( sequential ) {
253251876Speter            ++row->n;
254251876Speter        }
255251876Speter        else {
256251876Speter            if (rownum > 0) {
257251876Speter                row->n = --rownum;
258251876Speter            }
259251876Speter            else {
260251876Speter                return -1; /* invalid row */
261251876Speter            }
262251876Speter        }
263251876Speter    }
264251876Speter
265251876Speter    if (res->random) {
266251876Speter        if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
267251876Speter            *rowp = NULL;
268253734Speter            apr_pool_cleanup_run(res->pool, res->res, clear_result);
269251876Speter            res->res = NULL;
270251876Speter            return -1;
271251876Speter        }
272251876Speter    }
273251876Speter    else {
274251876Speter        if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
275251876Speter            /* no data; we have to fetch some */
276251876Speter            row->n -= res->ntuples;
277251876Speter            if (res->res != NULL) {
278251876Speter                PQclear(res->res);
279251876Speter            }
280251876Speter            res->res = PQgetResult(res->handle);
281251876Speter            if (res->res) {
282251876Speter                res->ntuples = PQntuples(res->res);
283251876Speter                while (res->ntuples == 0) {
284251876Speter                    /* if we got an empty result, clear it, wait a mo, try
285251876Speter                     * again */
286251876Speter                    PQclear(res->res);
287251876Speter                    apr_sleep(100000);        /* 0.1 secs */
288251876Speter                    res->res = PQgetResult(res->handle);
289251876Speter                    if (res->res) {
290251876Speter                        res->ntuples = PQntuples(res->res);
291251876Speter                    }
292251876Speter                    else {
293251876Speter                        return -1;
294251876Speter                    }
295251876Speter                }
296251876Speter                if (res->sz == 0) {
297251876Speter                    res->sz = PQnfields(res->res);
298251876Speter                }
299251876Speter            }
300251876Speter            else {
301251876Speter                return -1;
302251876Speter            }
303251876Speter        }
304251876Speter    }
305251876Speter    return 0;
306251876Speter}
307251876Speter
308251876Speterstatic const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
309251876Speter{
310251876Speter    return PQgetvalue(row->res->res, row->n, n);
311251876Speter}
312251876Speter
313251876Speterstatic apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n,
314251876Speter                                        apr_dbd_type_e type, void *data)
315251876Speter{
316251876Speter    if (PQgetisnull(row->res->res, row->n, n)) {
317251876Speter        return APR_ENOENT;
318251876Speter    }
319251876Speter
320251876Speter    switch (type) {
321251876Speter    case APR_DBD_TYPE_TINY:
322251876Speter        *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
323251876Speter        break;
324251876Speter    case APR_DBD_TYPE_UTINY:
325251876Speter        *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
326251876Speter        break;
327251876Speter    case APR_DBD_TYPE_SHORT:
328251876Speter        *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
329251876Speter        break;
330251876Speter    case APR_DBD_TYPE_USHORT:
331251876Speter        *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
332251876Speter        break;
333251876Speter    case APR_DBD_TYPE_INT:
334251876Speter        *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
335251876Speter        break;
336251876Speter    case APR_DBD_TYPE_UINT:
337251876Speter        *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
338251876Speter        break;
339251876Speter    case APR_DBD_TYPE_LONG:
340251876Speter        *(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
341251876Speter        break;
342251876Speter    case APR_DBD_TYPE_ULONG:
343251876Speter        *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
344251876Speter        break;
345251876Speter    case APR_DBD_TYPE_LONGLONG:
346251876Speter        *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
347251876Speter        break;
348251876Speter    case APR_DBD_TYPE_ULONGLONG:
349251876Speter        *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
350251876Speter        break;
351251876Speter    case APR_DBD_TYPE_FLOAT:
352251876Speter        *(float*)data = (float)atof(PQgetvalue(row->res->res, row->n, n));
353251876Speter        break;
354251876Speter    case APR_DBD_TYPE_DOUBLE:
355251876Speter        *(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
356251876Speter        break;
357251876Speter    case APR_DBD_TYPE_STRING:
358251876Speter    case APR_DBD_TYPE_TEXT:
359251876Speter    case APR_DBD_TYPE_TIME:
360251876Speter    case APR_DBD_TYPE_DATE:
361251876Speter    case APR_DBD_TYPE_DATETIME:
362251876Speter    case APR_DBD_TYPE_TIMESTAMP:
363251876Speter    case APR_DBD_TYPE_ZTIMESTAMP:
364251876Speter        *(char**)data = PQgetvalue(row->res->res, row->n, n);
365251876Speter        break;
366251876Speter    case APR_DBD_TYPE_BLOB:
367251876Speter    case APR_DBD_TYPE_CLOB:
368251876Speter        {
369251876Speter        apr_bucket *e;
370251876Speter        apr_bucket_brigade *b = (apr_bucket_brigade*)data;
371251876Speter
372251876Speter        e = apr_bucket_pool_create(PQgetvalue(row->res->res, row->n, n),
373251876Speter                                   PQgetlength(row->res->res, row->n, n),
374251876Speter                                   row->res->pool, b->bucket_alloc);
375251876Speter        APR_BRIGADE_INSERT_TAIL(b, e);
376251876Speter        }
377251876Speter        break;
378251876Speter    case APR_DBD_TYPE_NULL:
379251876Speter        *(void**)data = NULL;
380251876Speter        break;
381251876Speter    default:
382251876Speter        return APR_EGENERAL;
383251876Speter    }
384251876Speter
385251876Speter    return APR_SUCCESS;
386251876Speter}
387251876Speter
388251876Speterstatic const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
389251876Speter{
390251876Speter    return PQerrorMessage(sql->conn);
391251876Speter}
392251876Speter
393251876Speterstatic int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
394251876Speter{
395251876Speter    PGresult *res;
396251876Speter    int ret;
397251876Speter    if (sql->trans && sql->trans->errnum) {
398251876Speter        return sql->trans->errnum;
399251876Speter    }
400251876Speter
401251876Speter    if (TXN_IGNORE_ERRORS(sql->trans)) {
402251876Speter        PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
403251876Speter        if (res) {
404251876Speter            int ret = PQresultStatus(res);
405251876Speter            PQclear(res);
406251876Speter            if (!dbd_pgsql_is_success(ret)) {
407251876Speter                sql->trans->errnum = ret;
408251876Speter                return PGRES_FATAL_ERROR;
409251876Speter            }
410251876Speter        } else {
411251876Speter            return sql->trans->errnum = PGRES_FATAL_ERROR;
412251876Speter        }
413251876Speter    }
414251876Speter
415251876Speter    res = PQexec(sql->conn, query);
416251876Speter    if (res) {
417251876Speter        ret = PQresultStatus(res);
418251876Speter        if (dbd_pgsql_is_success(ret)) {
419251876Speter            /* ugh, making 0 return-success doesn't fit */
420251876Speter            ret = 0;
421251876Speter        }
422251876Speter        *nrows = atoi(PQcmdTuples(res));
423251876Speter        PQclear(res);
424251876Speter    }
425251876Speter    else {
426251876Speter        ret = PGRES_FATAL_ERROR;
427251876Speter    }
428251876Speter
429251876Speter    if (ret != 0){
430251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
431251876Speter            PGresult *res = PQexec(sql->conn,
432251876Speter                                   "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
433251876Speter            if (res) {
434251876Speter                int ret = PQresultStatus(res);
435251876Speter                PQclear(res);
436251876Speter                if (!dbd_pgsql_is_success(ret)) {
437251876Speter                    sql->trans->errnum = ret;
438251876Speter                    return PGRES_FATAL_ERROR;
439251876Speter                }
440251876Speter            } else {
441251876Speter                sql->trans->errnum = ret;
442251876Speter                return PGRES_FATAL_ERROR;
443251876Speter            }
444251876Speter        } else if (TXN_NOTICE_ERRORS(sql->trans)){
445251876Speter            sql->trans->errnum = ret;
446251876Speter        }
447251876Speter    } else {
448251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
449251876Speter            PGresult *res = PQexec(sql->conn,
450251876Speter                                   "RELEASE SAVEPOINT APR_DBD_TXN_SP");
451251876Speter            if (res) {
452251876Speter                int ret = PQresultStatus(res);
453251876Speter                PQclear(res);
454251876Speter                if (!dbd_pgsql_is_success(ret)) {
455251876Speter                    sql->trans->errnum = ret;
456251876Speter                    return PGRES_FATAL_ERROR;
457251876Speter                }
458251876Speter            } else {
459251876Speter                sql->trans->errnum = ret;
460251876Speter                return PGRES_FATAL_ERROR;
461251876Speter            }
462251876Speter        }
463251876Speter    }
464251876Speter
465251876Speter    return ret;
466251876Speter}
467251876Speter
468251876Speterstatic const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
469251876Speter                                    apr_dbd_t *sql)
470251876Speter{
471251876Speter    size_t len = strlen(arg);
472251876Speter    char *ret = apr_palloc(pool, 2*len + 2);
473253734Speter    PQescapeStringConn(sql->conn, ret, arg, len, NULL);
474251876Speter    return ret;
475251876Speter}
476251876Speter
477251876Speterstatic int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
478251876Speter                             const char *query, const char *label,
479251876Speter                             int nargs, int nvals, apr_dbd_type_e *types,
480251876Speter                             apr_dbd_prepared_t **statement)
481251876Speter{
482251876Speter    char *sqlcmd;
483251876Speter    char *sqlptr;
484251876Speter    size_t length, qlen;
485251876Speter    int i = 0;
486251876Speter    const char **args;
487251876Speter    size_t alen;
488251876Speter    int ret;
489251876Speter    PGresult *res;
490251876Speter
491251876Speter    if (!*statement) {
492251876Speter        *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
493251876Speter    }
494251876Speter    (*statement)->nargs = nargs;
495251876Speter    (*statement)->nvals = nvals;
496251876Speter    (*statement)->types = types;
497251876Speter
498251876Speter    args = apr_palloc(pool, nargs * sizeof(*args));
499251876Speter
500251876Speter    qlen = strlen(query);
501251876Speter    length = qlen + 1;
502251876Speter
503251876Speter    for (i = 0; i < nargs; i++) {
504251876Speter        switch (types[i]) {
505251876Speter        case APR_DBD_TYPE_TINY:
506251876Speter        case APR_DBD_TYPE_UTINY:
507251876Speter        case APR_DBD_TYPE_SHORT:
508251876Speter        case APR_DBD_TYPE_USHORT:
509251876Speter            args[i] = "smallint";
510251876Speter            break;
511251876Speter        case APR_DBD_TYPE_INT:
512251876Speter        case APR_DBD_TYPE_UINT:
513251876Speter            args[i] = "integer";
514251876Speter            break;
515251876Speter        case APR_DBD_TYPE_LONG:
516251876Speter        case APR_DBD_TYPE_ULONG:
517251876Speter        case APR_DBD_TYPE_LONGLONG:
518251876Speter        case APR_DBD_TYPE_ULONGLONG:
519251876Speter            args[i] = "bigint";
520251876Speter            break;
521251876Speter        case APR_DBD_TYPE_FLOAT:
522251876Speter            args[i] = "real";
523251876Speter            break;
524251876Speter        case APR_DBD_TYPE_DOUBLE:
525251876Speter            args[i] = "double precision";
526251876Speter            break;
527251876Speter        case APR_DBD_TYPE_TEXT:
528251876Speter            args[i] = "text";
529251876Speter            break;
530251876Speter        case APR_DBD_TYPE_TIME:
531251876Speter            args[i] = "time";
532251876Speter            break;
533251876Speter        case APR_DBD_TYPE_DATE:
534251876Speter            args[i] = "date";
535251876Speter            break;
536251876Speter        case APR_DBD_TYPE_DATETIME:
537251876Speter        case APR_DBD_TYPE_TIMESTAMP:
538251876Speter            args[i] = "timestamp";
539251876Speter            break;
540251876Speter        case APR_DBD_TYPE_ZTIMESTAMP:
541251876Speter            args[i] = "timestamp with time zone";
542251876Speter            break;
543251876Speter        case APR_DBD_TYPE_BLOB:
544251876Speter        case APR_DBD_TYPE_CLOB:
545251876Speter            args[i] = "bytea";
546251876Speter            break;
547251876Speter        case APR_DBD_TYPE_NULL:
548251876Speter            args[i] = "varchar"; /* XXX Eh? */
549251876Speter            break;
550251876Speter        default:
551251876Speter            args[i] = "varchar";
552251876Speter            break;
553251876Speter        }
554251876Speter        length += 1 + strlen(args[i]);
555251876Speter    }
556251876Speter
557251876Speter    if (!label) {
558251876Speter        /* don't really prepare; use in execParams instead */
559251876Speter        (*statement)->prepared = 0;
560251876Speter        (*statement)->name = apr_pstrdup(pool, query);
561251876Speter        return 0;
562251876Speter    }
563251876Speter    (*statement)->name = apr_pstrdup(pool, label);
564251876Speter
565251876Speter    /* length of SQL query that prepares this statement */
566251876Speter    length = 8 + strlen(label) + 2 + 4 + length + 1;
567251876Speter    sqlcmd = apr_palloc(pool, length);
568251876Speter    sqlptr = sqlcmd;
569251876Speter    memcpy(sqlptr, "PREPARE ", 8);
570251876Speter    sqlptr += 8;
571251876Speter    length = strlen(label);
572251876Speter    memcpy(sqlptr, label, length);
573251876Speter    sqlptr += length;
574251876Speter    if (nargs > 0) {
575251876Speter        memcpy(sqlptr, " (",2);
576251876Speter        sqlptr += 2;
577251876Speter        for (i=0; i < nargs; ++i) {
578251876Speter            alen = strlen(args[i]);
579251876Speter            memcpy(sqlptr, args[i], alen);
580251876Speter            sqlptr += alen;
581251876Speter            *sqlptr++ = ',';
582251876Speter        }
583251876Speter        sqlptr[-1] =  ')';
584251876Speter    }
585251876Speter    memcpy(sqlptr, " AS ", 4);
586251876Speter    sqlptr += 4;
587251876Speter    memcpy(sqlptr, query, qlen);
588251876Speter    sqlptr += qlen;
589251876Speter    *sqlptr = 0;
590251876Speter
591251876Speter    res = PQexec(sql->conn, sqlcmd);
592251876Speter    if ( res ) {
593251876Speter        ret = PQresultStatus(res);
594251876Speter        if (dbd_pgsql_is_success(ret)) {
595251876Speter            ret = 0;
596251876Speter        }
597251876Speter        /* Hmmm, do we do this here or register it on the pool? */
598251876Speter        PQclear(res);
599251876Speter    }
600251876Speter    else {
601251876Speter        ret = PGRES_FATAL_ERROR;
602251876Speter    }
603251876Speter    (*statement)->prepared = 1;
604251876Speter
605251876Speter    return ret;
606251876Speter}
607251876Speter
608251876Speterstatic int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
609251876Speter                                     int *nrows, apr_dbd_prepared_t *statement,
610251876Speter                                     const char **values,
611251876Speter                                     const int *len, const int *fmt)
612251876Speter{
613251876Speter    int ret;
614251876Speter    PGresult *res;
615251876Speter
616251876Speter    if (TXN_IGNORE_ERRORS(sql->trans)) {
617251876Speter        PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
618251876Speter        if (res) {
619251876Speter            int ret = PQresultStatus(res);
620251876Speter            PQclear(res);
621251876Speter            if (!dbd_pgsql_is_success(ret)) {
622251876Speter                sql->trans->errnum = ret;
623251876Speter                return PGRES_FATAL_ERROR;
624251876Speter            }
625251876Speter        } else {
626251876Speter            return sql->trans->errnum = PGRES_FATAL_ERROR;
627251876Speter        }
628251876Speter    }
629251876Speter
630251876Speter    if (statement->prepared) {
631251876Speter        res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
632251876Speter                             values, len, fmt, 0);
633251876Speter    }
634251876Speter    else {
635251876Speter        res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
636251876Speter                           values, len, fmt, 0);
637251876Speter    }
638251876Speter    if (res) {
639251876Speter        ret = PQresultStatus(res);
640251876Speter        if (dbd_pgsql_is_success(ret)) {
641251876Speter            ret = 0;
642251876Speter        }
643251876Speter        *nrows = atoi(PQcmdTuples(res));
644251876Speter        PQclear(res);
645251876Speter    }
646251876Speter    else {
647251876Speter        ret = PGRES_FATAL_ERROR;
648251876Speter    }
649251876Speter
650251876Speter    if (ret != 0){
651251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
652251876Speter            PGresult *res = PQexec(sql->conn,
653251876Speter                                   "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
654251876Speter            if (res) {
655251876Speter                int ret = PQresultStatus(res);
656251876Speter                PQclear(res);
657251876Speter                if (!dbd_pgsql_is_success(ret)) {
658251876Speter                    sql->trans->errnum = ret;
659251876Speter                    return PGRES_FATAL_ERROR;
660251876Speter                }
661251876Speter            } else {
662251876Speter                sql->trans->errnum = ret;
663251876Speter                return PGRES_FATAL_ERROR;
664251876Speter            }
665251876Speter        } else if (TXN_NOTICE_ERRORS(sql->trans)){
666251876Speter            sql->trans->errnum = ret;
667251876Speter        }
668251876Speter    } else {
669251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
670251876Speter            PGresult *res = PQexec(sql->conn,
671251876Speter                                   "RELEASE SAVEPOINT APR_DBD_TXN_SP");
672251876Speter            if (res) {
673251876Speter                int ret = PQresultStatus(res);
674251876Speter                PQclear(res);
675251876Speter                if (!dbd_pgsql_is_success(ret)) {
676251876Speter                    sql->trans->errnum = ret;
677251876Speter                    return PGRES_FATAL_ERROR;
678251876Speter                }
679251876Speter            } else {
680251876Speter                sql->trans->errnum = ret;
681251876Speter                return PGRES_FATAL_ERROR;
682251876Speter            }
683251876Speter        }
684251876Speter    }
685251876Speter
686251876Speter    return ret;
687251876Speter}
688251876Speter
689251876Speterstatic void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
690251876Speter                           const char **values,
691251876Speter                           const char **val, int *len, int *fmt)
692251876Speter{
693251876Speter    int i, j;
694251876Speter
695251876Speter    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
696251876Speter        if (values[j] == NULL) {
697251876Speter            val[i] = NULL;
698251876Speter        }
699251876Speter        else {
700251876Speter            switch (statement->types[i]) {
701251876Speter            case APR_DBD_TYPE_BLOB:
702251876Speter            case APR_DBD_TYPE_CLOB:
703251876Speter                val[i] = (char *)values[j];
704251876Speter                len[i] = atoi(values[++j]);
705251876Speter                fmt[i] = 1;
706251876Speter
707251876Speter                /* skip table and column */
708251876Speter                j += 2;
709251876Speter                break;
710251876Speter            default:
711251876Speter                val[i] = values[j];
712251876Speter                break;
713251876Speter            }
714251876Speter        }
715251876Speter    }
716251876Speter
717251876Speter    return;
718251876Speter}
719251876Speter
720251876Speterstatic int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
721251876Speter                            int *nrows, apr_dbd_prepared_t *statement,
722251876Speter                            const char **values)
723251876Speter{
724251876Speter    int *len, *fmt;
725251876Speter    const char **val;
726251876Speter
727251876Speter    if (sql->trans && sql->trans->errnum) {
728251876Speter        return sql->trans->errnum;
729251876Speter    }
730251876Speter
731251876Speter    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
732251876Speter    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
733251876Speter    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
734251876Speter
735251876Speter    dbd_pgsql_bind(statement, values, val, len, fmt);
736251876Speter
737251876Speter    return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
738251876Speter                                     val, len, fmt);
739251876Speter}
740251876Speter
741251876Speterstatic int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
742251876Speter                             int *nrows, apr_dbd_prepared_t *statement,
743251876Speter                             va_list args)
744251876Speter{
745251876Speter    const char **values;
746251876Speter    int i;
747251876Speter
748251876Speter    if (sql->trans && sql->trans->errnum) {
749251876Speter        return sql->trans->errnum;
750251876Speter    }
751251876Speter
752251876Speter    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
753251876Speter
754251876Speter    for (i = 0; i < statement->nvals; i++) {
755251876Speter        values[i] = va_arg(args, const char*);
756251876Speter    }
757251876Speter
758251876Speter    return dbd_pgsql_pquery(pool, sql, nrows, statement, values);
759251876Speter}
760251876Speter
761251876Speterstatic int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
762251876Speter                                      apr_dbd_results_t **results,
763251876Speter                                      apr_dbd_prepared_t *statement,
764251876Speter                                      int seek, const char **values,
765251876Speter                                      const int *len, const int *fmt)
766251876Speter{
767251876Speter    PGresult *res;
768251876Speter    int rv;
769251876Speter    int ret = 0;
770251876Speter
771251876Speter    if (seek) { /* synchronous query */
772251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
773251876Speter            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
774251876Speter            if (res) {
775251876Speter                int ret = PQresultStatus(res);
776251876Speter                PQclear(res);
777251876Speter                if (!dbd_pgsql_is_success(ret)) {
778251876Speter                    sql->trans->errnum = ret;
779251876Speter                    return PGRES_FATAL_ERROR;
780251876Speter                }
781251876Speter            } else {
782251876Speter                sql->trans->errnum = ret;
783251876Speter                return PGRES_FATAL_ERROR;
784251876Speter            }
785251876Speter        }
786251876Speter        if (statement->prepared) {
787251876Speter            res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
788251876Speter                                 values, len, fmt, 0);
789251876Speter        }
790251876Speter        else {
791251876Speter            res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
792251876Speter                               values, len, fmt, 0);
793251876Speter        }
794251876Speter        if (res) {
795251876Speter            ret = PQresultStatus(res);
796251876Speter            if (dbd_pgsql_is_success(ret)) {
797251876Speter                ret = 0;
798251876Speter            }
799251876Speter            else {
800251876Speter                PQclear(res);
801251876Speter            }
802251876Speter        }
803251876Speter        else {
804251876Speter            ret = PGRES_FATAL_ERROR;
805251876Speter        }
806251876Speter        if (ret != 0) {
807251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
808251876Speter                PGresult *res = PQexec(sql->conn,
809251876Speter                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
810251876Speter                if (res) {
811251876Speter                    int ret = PQresultStatus(res);
812251876Speter                    PQclear(res);
813251876Speter                    if (!dbd_pgsql_is_success(ret)) {
814251876Speter                        sql->trans->errnum = ret;
815251876Speter                        return PGRES_FATAL_ERROR;
816251876Speter                    }
817251876Speter                } else {
818251876Speter                    sql->trans->errnum = ret;
819251876Speter                    return PGRES_FATAL_ERROR;
820251876Speter                }
821251876Speter            } else if (TXN_NOTICE_ERRORS(sql->trans)){
822251876Speter                sql->trans->errnum = ret;
823251876Speter            }
824251876Speter            return ret;
825251876Speter        } else {
826251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
827251876Speter                PGresult *res = PQexec(sql->conn,
828251876Speter                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
829251876Speter                if (res) {
830251876Speter                    int ret = PQresultStatus(res);
831251876Speter                    PQclear(res);
832251876Speter                    if (!dbd_pgsql_is_success(ret)) {
833251876Speter                        sql->trans->errnum = ret;
834251876Speter                        return PGRES_FATAL_ERROR;
835251876Speter                    }
836251876Speter                } else {
837251876Speter                    sql->trans->errnum = ret;
838251876Speter                    return PGRES_FATAL_ERROR;
839251876Speter                }
840251876Speter            }
841251876Speter        }
842251876Speter        if (!*results) {
843251876Speter            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
844251876Speter        }
845251876Speter        (*results)->res = res;
846251876Speter        (*results)->ntuples = PQntuples(res);
847251876Speter        (*results)->sz = PQnfields(res);
848251876Speter        (*results)->random = seek;
849251876Speter        (*results)->pool = pool;
850251876Speter        apr_pool_cleanup_register(pool, res, clear_result,
851251876Speter                                  apr_pool_cleanup_null);
852251876Speter    }
853251876Speter    else {
854251876Speter        if (TXN_IGNORE_ERRORS(sql->trans)) {
855251876Speter            PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
856251876Speter            if (res) {
857251876Speter                int ret = PQresultStatus(res);
858251876Speter                PQclear(res);
859251876Speter                if (!dbd_pgsql_is_success(ret)) {
860251876Speter                    sql->trans->errnum = ret;
861251876Speter                    return PGRES_FATAL_ERROR;
862251876Speter                }
863251876Speter            } else {
864251876Speter                sql->trans->errnum = ret;
865251876Speter                return PGRES_FATAL_ERROR;
866251876Speter            }
867251876Speter        }
868251876Speter        if (statement->prepared) {
869251876Speter            rv = PQsendQueryPrepared(sql->conn, statement->name,
870251876Speter                                     statement->nargs, values, len, fmt, 0);
871251876Speter        }
872251876Speter        else {
873251876Speter            rv = PQsendQueryParams(sql->conn, statement->name,
874251876Speter                                   statement->nargs, 0, values, len, fmt, 0);
875251876Speter        }
876251876Speter        if (rv == 0) {
877251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
878251876Speter                PGresult *res = PQexec(sql->conn,
879251876Speter                                       "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
880251876Speter                if (res) {
881251876Speter                    int ret = PQresultStatus(res);
882251876Speter                    PQclear(res);
883251876Speter                    if (!dbd_pgsql_is_success(ret)) {
884251876Speter                        sql->trans->errnum = ret;
885251876Speter                        return PGRES_FATAL_ERROR;
886251876Speter                    }
887251876Speter                } else {
888251876Speter                    sql->trans->errnum = ret;
889251876Speter                    return PGRES_FATAL_ERROR;
890251876Speter                }
891251876Speter            } else if (TXN_NOTICE_ERRORS(sql->trans)){
892251876Speter                sql->trans->errnum = 1;
893251876Speter            }
894251876Speter            return 1;
895251876Speter        } else {
896251876Speter            if (TXN_IGNORE_ERRORS(sql->trans)) {
897251876Speter                PGresult *res = PQexec(sql->conn,
898251876Speter                                       "RELEASE SAVEPOINT APR_DBD_TXN_SP");
899251876Speter                if (res) {
900251876Speter                    int ret = PQresultStatus(res);
901251876Speter                    PQclear(res);
902251876Speter                    if (!dbd_pgsql_is_success(ret)) {
903251876Speter                        sql->trans->errnum = ret;
904251876Speter                        return PGRES_FATAL_ERROR;
905251876Speter                    }
906251876Speter                } else {
907251876Speter                    sql->trans->errnum = ret;
908251876Speter                    return PGRES_FATAL_ERROR;
909251876Speter                }
910251876Speter            }
911251876Speter        }
912251876Speter        if (!*results) {
913251876Speter            *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
914251876Speter        }
915251876Speter        (*results)->random = seek;
916251876Speter        (*results)->handle = sql->conn;
917251876Speter        (*results)->pool = pool;
918251876Speter    }
919251876Speter
920251876Speter    return ret;
921251876Speter}
922251876Speter
923251876Speterstatic int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
924251876Speter                             apr_dbd_results_t **results,
925251876Speter                             apr_dbd_prepared_t *statement,
926251876Speter                             int seek, const char **values)
927251876Speter{
928251876Speter    int *len, *fmt;
929251876Speter    const char **val;
930251876Speter
931251876Speter    if (sql->trans && sql->trans->errnum) {
932251876Speter        return sql->trans->errnum;
933251876Speter    }
934251876Speter
935251876Speter    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
936251876Speter    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
937251876Speter    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
938251876Speter
939251876Speter    dbd_pgsql_bind(statement, values, val, len, fmt);
940251876Speter
941251876Speter    return dbd_pgsql_pselect_internal(pool, sql, results, statement,
942251876Speter                                      seek, val, len, fmt);
943251876Speter}
944251876Speter
945251876Speterstatic int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
946251876Speter                              apr_dbd_results_t **results,
947251876Speter                              apr_dbd_prepared_t *statement,
948251876Speter                              int seek, va_list args)
949251876Speter{
950251876Speter    const char **values;
951251876Speter    int i;
952251876Speter
953251876Speter    if (sql->trans && sql->trans->errnum) {
954251876Speter        return sql->trans->errnum;
955251876Speter    }
956251876Speter
957251876Speter    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
958251876Speter
959251876Speter    for (i = 0; i < statement->nvals; i++) {
960251876Speter        values[i] = va_arg(args, const char*);
961251876Speter    }
962251876Speter
963251876Speter    return dbd_pgsql_pselect(pool, sql, results, statement, seek, values);
964251876Speter}
965251876Speter
966251876Speterstatic void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
967251876Speter                            const void **values,
968251876Speter                            const char **val, int *len, int *fmt)
969251876Speter{
970251876Speter    int i, j;
971251876Speter    apr_dbd_type_e type;
972251876Speter
973251876Speter    for (i = 0, j = 0; i < statement->nargs; i++, j++) {
974251876Speter        type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
975251876Speter
976251876Speter        switch (type) {
977251876Speter        case APR_DBD_TYPE_TINY:
978251876Speter            val[i] = apr_itoa(pool, *(char*)values[j]);
979251876Speter            break;
980251876Speter        case APR_DBD_TYPE_UTINY:
981251876Speter            val[i] = apr_itoa(pool, *(unsigned char*)values[j]);
982251876Speter            break;
983251876Speter        case APR_DBD_TYPE_SHORT:
984251876Speter            val[i] = apr_itoa(pool, *(short*)values[j]);
985251876Speter            break;
986251876Speter        case APR_DBD_TYPE_USHORT:
987251876Speter            val[i] = apr_itoa(pool, *(unsigned short*)values[j]);
988251876Speter            break;
989251876Speter        case APR_DBD_TYPE_INT:
990251876Speter            val[i] = apr_itoa(pool, *(int*)values[j]);
991251876Speter            break;
992251876Speter        case APR_DBD_TYPE_UINT:
993251876Speter            val[i] = apr_itoa(pool, *(unsigned int*)values[j]);
994251876Speter            break;
995251876Speter        case APR_DBD_TYPE_LONG:
996251876Speter            val[i] = apr_ltoa(pool, *(long*)values[j]);
997251876Speter            break;
998251876Speter        case APR_DBD_TYPE_ULONG:
999251876Speter            val[i] = apr_ltoa(pool, *(unsigned long*)values[j]);
1000251876Speter            break;
1001251876Speter        case APR_DBD_TYPE_LONGLONG:
1002251876Speter            val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
1003251876Speter                                  *(apr_int64_t*)values[j]);
1004251876Speter            break;
1005251876Speter        case APR_DBD_TYPE_ULONGLONG:
1006251876Speter            val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
1007251876Speter                                  *(apr_uint64_t*)values[j]);
1008251876Speter            break;
1009251876Speter        case APR_DBD_TYPE_FLOAT:
1010251876Speter            val[i] = apr_psprintf(pool, "%f", *(float*)values[j]);
1011251876Speter            break;
1012251876Speter        case APR_DBD_TYPE_DOUBLE:
1013251876Speter            val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]);
1014251876Speter            break;
1015251876Speter        case APR_DBD_TYPE_STRING:
1016251876Speter        case APR_DBD_TYPE_TEXT:
1017251876Speter        case APR_DBD_TYPE_TIME:
1018251876Speter        case APR_DBD_TYPE_DATE:
1019251876Speter        case APR_DBD_TYPE_DATETIME:
1020251876Speter        case APR_DBD_TYPE_TIMESTAMP:
1021251876Speter        case APR_DBD_TYPE_ZTIMESTAMP:
1022251876Speter            val[i] = values[j];
1023251876Speter            break;
1024251876Speter        case APR_DBD_TYPE_BLOB:
1025251876Speter        case APR_DBD_TYPE_CLOB:
1026251876Speter            val[i] = (char*)values[j];
1027251876Speter            len[i] = *(apr_size_t*)values[++j];
1028251876Speter            fmt[i] = 1;
1029251876Speter
1030251876Speter            /* skip table and column */
1031251876Speter            j += 2;
1032251876Speter            break;
1033251876Speter        case APR_DBD_TYPE_NULL:
1034251876Speter        default:
1035251876Speter            val[i] = NULL;
1036251876Speter            break;
1037251876Speter        }
1038251876Speter    }
1039251876Speter
1040251876Speter    return;
1041251876Speter}
1042251876Speter
1043251876Speterstatic int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
1044251876Speter                             int *nrows, apr_dbd_prepared_t * statement,
1045251876Speter                             const void **values)
1046251876Speter{
1047251876Speter    int *len, *fmt;
1048251876Speter    const char **val;
1049251876Speter
1050251876Speter    if (sql->trans && sql->trans->errnum) {
1051251876Speter        return sql->trans->errnum;
1052251876Speter    }
1053251876Speter
1054251876Speter    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1055251876Speter    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1056251876Speter    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1057251876Speter
1058251876Speter    dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1059251876Speter
1060251876Speter    return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
1061251876Speter                                     val, len, fmt);
1062251876Speter}
1063251876Speter
1064251876Speterstatic int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
1065251876Speter                              int *nrows, apr_dbd_prepared_t * statement,
1066251876Speter                              va_list args)
1067251876Speter{
1068251876Speter    const void **values;
1069251876Speter    int i;
1070251876Speter
1071251876Speter    if (sql->trans && sql->trans->errnum) {
1072251876Speter        return sql->trans->errnum;
1073251876Speter    }
1074251876Speter
1075251876Speter    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1076251876Speter
1077251876Speter    for (i = 0; i < statement->nvals; i++) {
1078251876Speter        values[i] = va_arg(args, const void*);
1079251876Speter    }
1080251876Speter
1081251876Speter    return dbd_pgsql_pbquery(pool, sql, nrows, statement, values);
1082251876Speter}
1083251876Speter
1084251876Speterstatic int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
1085251876Speter                              apr_dbd_results_t ** results,
1086251876Speter                              apr_dbd_prepared_t * statement,
1087251876Speter                              int seek, const void **values)
1088251876Speter{
1089251876Speter    int *len, *fmt;
1090251876Speter    const char **val;
1091251876Speter
1092251876Speter    if (sql->trans && sql->trans->errnum) {
1093251876Speter        return sql->trans->errnum;
1094251876Speter    }
1095251876Speter
1096251876Speter    val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1097251876Speter    len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1098251876Speter    fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1099251876Speter
1100251876Speter    dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1101251876Speter
1102251876Speter    return dbd_pgsql_pselect_internal(pool, sql, results, statement,
1103251876Speter                                      seek, val, len, fmt);
1104251876Speter}
1105251876Speter
1106251876Speterstatic int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
1107251876Speter                               apr_dbd_results_t ** results,
1108251876Speter                               apr_dbd_prepared_t * statement, int seek,
1109251876Speter                               va_list args)
1110251876Speter{
1111251876Speter    const void **values;
1112251876Speter    int i;
1113251876Speter
1114251876Speter    if (sql->trans && sql->trans->errnum) {
1115251876Speter        return sql->trans->errnum;
1116251876Speter    }
1117251876Speter
1118251876Speter    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1119251876Speter
1120251876Speter    for (i = 0; i < statement->nvals; i++) {
1121251876Speter        values[i] = va_arg(args, const void*);
1122251876Speter    }
1123251876Speter
1124251876Speter    return dbd_pgsql_pbselect(pool, sql, results, statement, seek, values);
1125251876Speter}
1126251876Speter
1127251876Speterstatic int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1128251876Speter                                       apr_dbd_transaction_t **trans)
1129251876Speter{
1130251876Speter    int ret = 0;
1131251876Speter    PGresult *res;
1132251876Speter
1133251876Speter    /* XXX handle recursive transactions here */
1134251876Speter
1135251876Speter    res = PQexec(handle->conn, "BEGIN TRANSACTION");
1136251876Speter    if (res) {
1137251876Speter        ret = PQresultStatus(res);
1138251876Speter        if (dbd_pgsql_is_success(ret)) {
1139251876Speter            ret = 0;
1140251876Speter            if (!*trans) {
1141251876Speter                *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1142251876Speter            }
1143251876Speter        }
1144251876Speter        PQclear(res);
1145251876Speter        (*trans)->handle = handle;
1146251876Speter        handle->trans = *trans;
1147251876Speter    }
1148251876Speter    else {
1149251876Speter        ret = PGRES_FATAL_ERROR;
1150251876Speter    }
1151251876Speter    return ret;
1152251876Speter}
1153251876Speter
1154251876Speterstatic int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
1155251876Speter{
1156251876Speter    PGresult *res;
1157251876Speter    int ret = -1;                /* no transaction is an error cond */
1158251876Speter    if (trans) {
1159251876Speter        /* rollback on error or explicit rollback request */
1160251876Speter        if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1161251876Speter            trans->errnum = 0;
1162251876Speter            res = PQexec(trans->handle->conn, "ROLLBACK");
1163251876Speter        }
1164251876Speter        else {
1165251876Speter            res = PQexec(trans->handle->conn, "COMMIT");
1166251876Speter        }
1167251876Speter        if (res) {
1168251876Speter            ret = PQresultStatus(res);
1169251876Speter            if (dbd_pgsql_is_success(ret)) {
1170251876Speter                ret = 0;
1171251876Speter            }
1172251876Speter            PQclear(res);
1173251876Speter        }
1174251876Speter        else {
1175251876Speter            ret = PGRES_FATAL_ERROR;
1176251876Speter        }
1177251876Speter        trans->handle->trans = NULL;
1178251876Speter    }
1179251876Speter    return ret;
1180251876Speter}
1181251876Speter
1182251876Speterstatic int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
1183251876Speter{
1184251876Speter    if (!trans)
1185251876Speter        return APR_DBD_TRANSACTION_COMMIT;
1186251876Speter
1187251876Speter    return trans->mode;
1188251876Speter}
1189251876Speter
1190251876Speterstatic int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
1191251876Speter                                          int mode)
1192251876Speter{
1193251876Speter    if (!trans)
1194251876Speter        return APR_DBD_TRANSACTION_COMMIT;
1195251876Speter
1196251876Speter    return trans->mode = (mode & TXN_MODE_BITS);
1197251876Speter}
1198251876Speter
1199251876Speterstatic void null_notice_receiver(void *arg, const PGresult *res)
1200251876Speter{
1201251876Speter    /* nothing */
1202251876Speter}
1203251876Speter
1204251876Speterstatic void null_notice_processor(void *arg, const char *message)
1205251876Speter{
1206251876Speter    /* nothing */
1207251876Speter}
1208251876Speter
1209251876Speterstatic apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params,
1210251876Speter                                 const char **error)
1211251876Speter{
1212251876Speter    apr_dbd_t *sql;
1213251876Speter
1214251876Speter    PGconn *conn = PQconnectdb(params);
1215251876Speter
1216251876Speter    /* if there's an error in the connect string or something we get
1217251876Speter     * back a * bogus connection object, and things like PQreset are
1218251876Speter     * liable to segfault, so just close it out now.  it would be nice
1219251876Speter     * if we could give an indication of why we failed to connect... */
1220251876Speter    if (PQstatus(conn) != CONNECTION_OK) {
1221251876Speter        if (error) {
1222251876Speter            *error = apr_pstrdup(pool, PQerrorMessage(conn));
1223251876Speter        }
1224251876Speter        PQfinish(conn);
1225251876Speter        return NULL;
1226251876Speter    }
1227251876Speter
1228251876Speter    PQsetNoticeReceiver(conn, null_notice_receiver, NULL);
1229251876Speter    PQsetNoticeProcessor(conn, null_notice_processor, NULL);
1230251876Speter
1231251876Speter    sql = apr_pcalloc (pool, sizeof (*sql));
1232251876Speter
1233251876Speter    sql->conn = conn;
1234251876Speter
1235251876Speter    return sql;
1236251876Speter}
1237251876Speter
1238251876Speterstatic apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
1239251876Speter{
1240251876Speter    PQfinish(handle->conn);
1241251876Speter    return APR_SUCCESS;
1242251876Speter}
1243251876Speter
1244251876Speterstatic apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
1245251876Speter                                         apr_dbd_t *handle)
1246251876Speter{
1247251876Speter    if (PQstatus(handle->conn) != CONNECTION_OK) {
1248251876Speter        PQreset(handle->conn);
1249251876Speter        if (PQstatus(handle->conn) != CONNECTION_OK) {
1250251876Speter            return APR_EGENERAL;
1251251876Speter        }
1252251876Speter    }
1253251876Speter    return APR_SUCCESS;
1254251876Speter}
1255251876Speter
1256251876Speterstatic int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
1257251876Speter                               const char *name)
1258251876Speter{
1259251876Speter    return APR_ENOTIMPL;
1260251876Speter}
1261251876Speter
1262251876Speterstatic void *dbd_pgsql_native(apr_dbd_t *handle)
1263251876Speter{
1264251876Speter    return handle->conn;
1265251876Speter}
1266251876Speter
1267251876Speterstatic int dbd_pgsql_num_cols(apr_dbd_results_t* res)
1268251876Speter{
1269251876Speter    return res->sz;
1270251876Speter}
1271251876Speter
1272251876Speterstatic int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
1273251876Speter{
1274251876Speter    if (res->random) {
1275251876Speter        return res->ntuples;
1276251876Speter    }
1277251876Speter    else {
1278251876Speter        return -1;
1279251876Speter    }
1280251876Speter}
1281251876Speter
1282251876SpeterAPU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
1283251876Speter    "pgsql",
1284251876Speter    NULL,
1285251876Speter    dbd_pgsql_native,
1286251876Speter    dbd_pgsql_open,
1287251876Speter    dbd_pgsql_check_conn,
1288251876Speter    dbd_pgsql_close,
1289251876Speter    dbd_pgsql_select_db,
1290251876Speter    dbd_pgsql_start_transaction,
1291251876Speter    dbd_pgsql_end_transaction,
1292251876Speter    dbd_pgsql_query,
1293251876Speter    dbd_pgsql_select,
1294251876Speter    dbd_pgsql_num_cols,
1295251876Speter    dbd_pgsql_num_tuples,
1296251876Speter    dbd_pgsql_get_row,
1297251876Speter    dbd_pgsql_get_entry,
1298251876Speter    dbd_pgsql_error,
1299251876Speter    dbd_pgsql_escape,
1300251876Speter    dbd_pgsql_prepare,
1301251876Speter    dbd_pgsql_pvquery,
1302251876Speter    dbd_pgsql_pvselect,
1303251876Speter    dbd_pgsql_pquery,
1304251876Speter    dbd_pgsql_pselect,
1305251876Speter    dbd_pgsql_get_name,
1306251876Speter    dbd_pgsql_transaction_mode_get,
1307251876Speter    dbd_pgsql_transaction_mode_set,
1308251876Speter    "$%d",
1309251876Speter    dbd_pgsql_pvbquery,
1310251876Speter    dbd_pgsql_pvbselect,
1311251876Speter    dbd_pgsql_pbquery,
1312251876Speter    dbd_pgsql_pbselect,
1313251876Speter    dbd_pgsql_datum_get
1314251876Speter};
1315251876Speter#endif
1316