]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/apr-util/dbd/apr_dbd_pgsql.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / apr-util / dbd / apr_dbd_pgsql.c
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
38 struct apr_dbd_transaction_t {
39     int mode;
40     int errnum;
41     apr_dbd_t *handle;
42 };
43
44 struct apr_dbd_t {
45     PGconn *conn;
46     apr_dbd_transaction_t *trans;
47 };
48
49 struct 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
59 struct apr_dbd_row_t {
60     int n;
61     apr_dbd_results_t *res;
62 };
63
64 struct 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
76 static apr_status_t clear_result(void *data)
77 {
78     PQclear(data);
79     return APR_SUCCESS;
80 }
81
82 static 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
219 static 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
229 static 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
308 static 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
313 static 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
388 static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
389 {
390     return PQerrorMessage(sql->conn);
391 }
392
393 static 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
468 static 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
477 static 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
608 static 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
689 static 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
720 static 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
741 static 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
761 static 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
923 static 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
945 static 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
966 static 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
1043 static 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
1064 static 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
1084 static 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
1106 static 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
1127 static 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
1154 static 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
1182 static 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
1190 static 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
1199 static void null_notice_receiver(void *arg, const PGresult *res)
1200 {
1201     /* nothing */
1202 }
1203
1204 static void null_notice_processor(void *arg, const char *message)
1205 {
1206     /* nothing */
1207 }
1208
1209 static 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
1238 static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
1239 {
1240     PQfinish(handle->conn);
1241     return APR_SUCCESS;
1242 }
1243
1244 static 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
1256 static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
1257                                const char *name)
1258 {
1259     return APR_ENOTIMPL;
1260 }
1261
1262 static void *dbd_pgsql_native(apr_dbd_t *handle)
1263 {
1264     return handle->conn;
1265 }
1266
1267 static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
1268 {
1269     return res->sz;
1270 }
1271
1272 static 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
1282 APU_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