Destroy already created resources if create fails
[apr-util.git] / dbd / apr_dbd_pgsql.c
blobdaf0dab07df0fc0b8fb6b842dc3e3a0e0c032c0f
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "apu.h"
19 #if APU_HAVE_PGSQL
21 #include "apu_config.h"
23 #include <ctype.h>
24 #include <stdlib.h>
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
32 #include "apr_strings.h"
33 #include "apr_time.h"
34 #include "apr_buckets.h"
36 #include "apr_dbd_internal.h"
38 struct apr_dbd_transaction_t {
39 int mode;
40 int errnum;
41 apr_dbd_t *handle;
44 struct apr_dbd_t {
45 PGconn *conn;
46 apr_dbd_transaction_t *trans;
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;
59 struct apr_dbd_row_t {
60 int n;
61 apr_dbd_results_t *res;
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;
72 #define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
73 || ((x) == PGRES_COMMAND_OK) \
74 || ((x) == PGRES_TUPLES_OK))
76 static apr_status_t clear_result(void *data)
78 PQclear(data);
79 return APR_SUCCESS;
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)
86 PGresult *res;
87 int ret;
88 if ( sql->trans && sql->trans->errnum ) {
89 return sql->trans->errnum;
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 ret = PQresultStatus(res);
96 PQclear(res);
97 if (!dbd_pgsql_is_success(ret)) {
98 sql->trans->errnum = ret;
99 return PGRES_FATAL_ERROR;
101 } else {
102 return sql->trans->errnum = PGRES_FATAL_ERROR;
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);
113 } else {
114 ret = PGRES_FATAL_ERROR;
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 ret = PQresultStatus(res);
122 PQclear(res);
123 if (!dbd_pgsql_is_success(ret)) {
124 sql->trans->errnum = ret;
125 return PGRES_FATAL_ERROR;
127 } else {
128 return sql->trans->errnum = PGRES_FATAL_ERROR;
130 } else if (TXN_NOTICE_ERRORS(sql->trans)){
131 sql->trans->errnum = ret;
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 ret = PQresultStatus(res);
140 PQclear(res);
141 if (!dbd_pgsql_is_success(ret)) {
142 sql->trans->errnum = ret;
143 return PGRES_FATAL_ERROR;
145 } else {
146 return sql->trans->errnum = PGRES_FATAL_ERROR;
150 if (!*results) {
151 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
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);
161 else {
162 if (TXN_IGNORE_ERRORS(sql->trans)) {
163 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
164 if (res) {
165 ret = PQresultStatus(res);
166 PQclear(res);
167 if (!dbd_pgsql_is_success(ret)) {
168 sql->trans->errnum = ret;
169 return PGRES_FATAL_ERROR;
171 } else {
172 return sql->trans->errnum = PGRES_FATAL_ERROR;
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 ret = PQresultStatus(res);
181 PQclear(res);
182 if (!dbd_pgsql_is_success(ret)) {
183 sql->trans->errnum = ret;
184 return PGRES_FATAL_ERROR;
186 } else {
187 return sql->trans->errnum = PGRES_FATAL_ERROR;
189 } else if (TXN_NOTICE_ERRORS(sql->trans)){
190 sql->trans->errnum = 1;
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 ret = PQresultStatus(res);
199 PQclear(res);
200 if (!dbd_pgsql_is_success(ret)) {
201 sql->trans->errnum = ret;
202 return PGRES_FATAL_ERROR;
204 } else {
205 return sql->trans->errnum = PGRES_FATAL_ERROR;
209 if (*results == NULL) {
210 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
212 (*results)->random = seek;
213 (*results)->handle = sql->conn;
214 (*results)->pool = pool;
216 return 0;
219 static const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n)
221 return (res->res ? PQfname(res->res, n) : NULL);
224 static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
225 apr_dbd_row_t **rowp, int rownum)
227 apr_dbd_row_t *row = *rowp;
228 int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
230 if (row == NULL) {
231 row = apr_palloc(pool, sizeof(apr_dbd_row_t));
232 *rowp = row;
233 row->res = res;
234 if ( sequential ) {
235 row->n = 0;
237 else {
238 if (rownum > 0) {
239 row->n = --rownum;
241 else {
242 return -1; /* invalid row */
246 else {
247 if ( sequential ) {
248 ++row->n;
250 else {
251 if (rownum > 0) {
252 row->n = --rownum;
254 else {
255 return -1; /* invalid row */
260 if (res->random) {
261 if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
262 *rowp = NULL;
263 apr_pool_cleanup_run(pool, res->res, clear_result);
264 res->res = NULL;
265 return -1;
268 else {
269 if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
270 /* no data; we have to fetch some */
271 row->n -= res->ntuples;
272 if (res->res != NULL) {
273 PQclear(res->res);
275 res->res = PQgetResult(res->handle);
276 if (res->res) {
277 res->ntuples = PQntuples(res->res);
278 while (res->ntuples == 0) {
279 /* if we got an empty result, clear it, wait a mo, try
280 * again */
281 PQclear(res->res);
282 apr_sleep(100000); /* 0.1 secs */
283 res->res = PQgetResult(res->handle);
284 if (res->res) {
285 res->ntuples = PQntuples(res->res);
287 else {
288 return -1;
291 if (res->sz == 0) {
292 res->sz = PQnfields(res->res);
295 else {
296 return -1;
300 return 0;
303 static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
305 return PQgetvalue(row->res->res, row->n, n);
308 static apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n,
309 apr_dbd_type_e type, void *data)
311 if (PQgetisnull(row->res->res, row->n, n)) {
312 return APR_ENOENT;
315 switch (type) {
316 case APR_DBD_TYPE_TINY:
317 *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
318 break;
319 case APR_DBD_TYPE_UTINY:
320 *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
321 break;
322 case APR_DBD_TYPE_SHORT:
323 *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
324 break;
325 case APR_DBD_TYPE_USHORT:
326 *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
327 break;
328 case APR_DBD_TYPE_INT:
329 *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
330 break;
331 case APR_DBD_TYPE_UINT:
332 *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
333 break;
334 case APR_DBD_TYPE_LONG:
335 *(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
336 break;
337 case APR_DBD_TYPE_ULONG:
338 *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
339 break;
340 case APR_DBD_TYPE_LONGLONG:
341 *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
342 break;
343 case APR_DBD_TYPE_ULONGLONG:
344 *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
345 break;
346 case APR_DBD_TYPE_FLOAT:
347 *(float*)data = (float)atof(PQgetvalue(row->res->res, row->n, n));
348 break;
349 case APR_DBD_TYPE_DOUBLE:
350 *(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
351 break;
352 case APR_DBD_TYPE_STRING:
353 case APR_DBD_TYPE_TEXT:
354 case APR_DBD_TYPE_TIME:
355 case APR_DBD_TYPE_DATE:
356 case APR_DBD_TYPE_DATETIME:
357 case APR_DBD_TYPE_TIMESTAMP:
358 case APR_DBD_TYPE_ZTIMESTAMP:
359 *(char**)data = PQgetvalue(row->res->res, row->n, n);
360 break;
361 case APR_DBD_TYPE_BLOB:
362 case APR_DBD_TYPE_CLOB:
364 apr_bucket *e;
365 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
367 e = apr_bucket_pool_create(PQgetvalue(row->res->res, row->n, n),
368 PQgetlength(row->res->res, row->n, n),
369 row->res->pool, b->bucket_alloc);
370 APR_BRIGADE_INSERT_TAIL(b, e);
372 break;
373 case APR_DBD_TYPE_NULL:
374 *(void**)data = NULL;
375 break;
376 default:
377 return APR_EGENERAL;
380 return APR_SUCCESS;
383 static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
385 return PQerrorMessage(sql->conn);
388 static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
390 PGresult *res;
391 int ret;
392 if (sql->trans && sql->trans->errnum) {
393 return sql->trans->errnum;
396 if (TXN_IGNORE_ERRORS(sql->trans)) {
397 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
398 if (res) {
399 ret = PQresultStatus(res);
400 PQclear(res);
401 if (!dbd_pgsql_is_success(ret)) {
402 sql->trans->errnum = ret;
403 return PGRES_FATAL_ERROR;
405 } else {
406 return sql->trans->errnum = PGRES_FATAL_ERROR;
410 res = PQexec(sql->conn, query);
411 if (res) {
412 ret = PQresultStatus(res);
413 if (dbd_pgsql_is_success(ret)) {
414 /* ugh, making 0 return-success doesn't fit */
415 ret = 0;
417 *nrows = atoi(PQcmdTuples(res));
418 PQclear(res);
420 else {
421 ret = PGRES_FATAL_ERROR;
424 if (ret != 0){
425 if (TXN_IGNORE_ERRORS(sql->trans)) {
426 PGresult *res = PQexec(sql->conn,
427 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
428 if (res) {
429 ret = PQresultStatus(res);
430 PQclear(res);
431 if (!dbd_pgsql_is_success(ret)) {
432 sql->trans->errnum = ret;
433 return PGRES_FATAL_ERROR;
435 } else {
436 sql->trans->errnum = ret;
437 return PGRES_FATAL_ERROR;
439 } else if (TXN_NOTICE_ERRORS(sql->trans)){
440 sql->trans->errnum = ret;
442 } else {
443 if (TXN_IGNORE_ERRORS(sql->trans)) {
444 PGresult *res = PQexec(sql->conn,
445 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
446 if (res) {
447 ret = PQresultStatus(res);
448 PQclear(res);
449 if (!dbd_pgsql_is_success(ret)) {
450 sql->trans->errnum = ret;
451 return PGRES_FATAL_ERROR;
453 } else {
454 sql->trans->errnum = ret;
455 return PGRES_FATAL_ERROR;
460 return ret;
463 static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
464 apr_dbd_t *sql)
466 size_t len = strlen(arg);
467 char *ret = apr_palloc(pool, 2*len + 2);
468 PQescapeString(ret, arg, len);
469 return ret;
472 static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
473 const char *query, const char *label,
474 int nargs, int nvals, apr_dbd_type_e *types,
475 apr_dbd_prepared_t **statement)
477 char *sqlcmd;
478 char *sqlptr;
479 size_t length, qlen;
480 int i = 0;
481 const char **args;
482 size_t alen;
483 int ret;
484 PGresult *res;
486 if (!*statement) {
487 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
489 (*statement)->nargs = nargs;
490 (*statement)->nvals = nvals;
491 (*statement)->types = types;
493 args = apr_palloc(pool, nargs * sizeof(*args));
495 qlen = strlen(query);
496 length = qlen + 1;
498 for (i = 0; i < nargs; i++) {
499 switch (types[i]) {
500 case APR_DBD_TYPE_TINY:
501 case APR_DBD_TYPE_UTINY:
502 case APR_DBD_TYPE_SHORT:
503 case APR_DBD_TYPE_USHORT:
504 args[i] = "smallint";
505 break;
506 case APR_DBD_TYPE_INT:
507 case APR_DBD_TYPE_UINT:
508 args[i] = "integer";
509 break;
510 case APR_DBD_TYPE_LONG:
511 case APR_DBD_TYPE_ULONG:
512 case APR_DBD_TYPE_LONGLONG:
513 case APR_DBD_TYPE_ULONGLONG:
514 args[i] = "bigint";
515 break;
516 case APR_DBD_TYPE_FLOAT:
517 args[i] = "real";
518 break;
519 case APR_DBD_TYPE_DOUBLE:
520 args[i] = "double precision";
521 break;
522 case APR_DBD_TYPE_TEXT:
523 args[i] = "text";
524 break;
525 case APR_DBD_TYPE_TIME:
526 args[i] = "time";
527 break;
528 case APR_DBD_TYPE_DATE:
529 args[i] = "date";
530 break;
531 case APR_DBD_TYPE_DATETIME:
532 case APR_DBD_TYPE_TIMESTAMP:
533 args[i] = "timestamp";
534 break;
535 case APR_DBD_TYPE_ZTIMESTAMP:
536 args[i] = "timestamp with time zone";
537 break;
538 case APR_DBD_TYPE_BLOB:
539 case APR_DBD_TYPE_CLOB:
540 args[i] = "bytea";
541 break;
542 case APR_DBD_TYPE_NULL:
543 args[i] = "varchar"; /* XXX Eh? */
544 break;
545 default:
546 args[i] = "varchar";
547 break;
549 length += 1 + strlen(args[i]);
552 if (!label) {
553 /* don't really prepare; use in execParams instead */
554 (*statement)->prepared = 0;
555 (*statement)->name = apr_pstrdup(pool, query);
556 return 0;
558 (*statement)->name = apr_pstrdup(pool, label);
560 /* length of SQL query that prepares this statement */
561 length = 8 + strlen(label) + 2 + 4 + length + 1;
562 sqlcmd = apr_palloc(pool, length);
563 sqlptr = sqlcmd;
564 memcpy(sqlptr, "PREPARE ", 8);
565 sqlptr += 8;
566 length = strlen(label);
567 memcpy(sqlptr, label, length);
568 sqlptr += length;
569 if (nargs > 0) {
570 memcpy(sqlptr, " (",2);
571 sqlptr += 2;
572 for (i=0; i < nargs; ++i) {
573 alen = strlen(args[i]);
574 memcpy(sqlptr, args[i], alen);
575 sqlptr += alen;
576 *sqlptr++ = ',';
578 sqlptr[-1] = ')';
580 memcpy(sqlptr, " AS ", 4);
581 sqlptr += 4;
582 memcpy(sqlptr, query, qlen);
583 sqlptr += qlen;
584 *sqlptr = 0;
586 res = PQexec(sql->conn, sqlcmd);
587 if ( res ) {
588 ret = PQresultStatus(res);
589 if (dbd_pgsql_is_success(ret)) {
590 ret = 0;
592 /* Hmmm, do we do this here or register it on the pool? */
593 PQclear(res);
595 else {
596 ret = PGRES_FATAL_ERROR;
598 (*statement)->prepared = 1;
600 return ret;
603 static int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
604 int *nrows, apr_dbd_prepared_t *statement,
605 const char **values,
606 const int *len, const int *fmt)
608 int ret;
609 PGresult *res;
611 if (TXN_IGNORE_ERRORS(sql->trans)) {
612 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
613 if (res) {
614 ret = PQresultStatus(res);
615 PQclear(res);
616 if (!dbd_pgsql_is_success(ret)) {
617 sql->trans->errnum = ret;
618 return PGRES_FATAL_ERROR;
620 } else {
621 return sql->trans->errnum = PGRES_FATAL_ERROR;
625 if (statement->prepared) {
626 res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
627 values, len, fmt, 0);
629 else {
630 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
631 values, len, fmt, 0);
633 if (res) {
634 ret = PQresultStatus(res);
635 if (dbd_pgsql_is_success(ret)) {
636 ret = 0;
638 *nrows = atoi(PQcmdTuples(res));
639 PQclear(res);
641 else {
642 ret = PGRES_FATAL_ERROR;
645 if (ret != 0){
646 if (TXN_IGNORE_ERRORS(sql->trans)) {
647 PGresult *res = PQexec(sql->conn,
648 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
649 if (res) {
650 ret = PQresultStatus(res);
651 PQclear(res);
652 if (!dbd_pgsql_is_success(ret)) {
653 sql->trans->errnum = ret;
654 return PGRES_FATAL_ERROR;
656 } else {
657 sql->trans->errnum = ret;
658 return PGRES_FATAL_ERROR;
660 } else if (TXN_NOTICE_ERRORS(sql->trans)){
661 sql->trans->errnum = ret;
663 } else {
664 if (TXN_IGNORE_ERRORS(sql->trans)) {
665 PGresult *res = PQexec(sql->conn,
666 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
667 if (res) {
668 ret = PQresultStatus(res);
669 PQclear(res);
670 if (!dbd_pgsql_is_success(ret)) {
671 sql->trans->errnum = ret;
672 return PGRES_FATAL_ERROR;
674 } else {
675 sql->trans->errnum = ret;
676 return PGRES_FATAL_ERROR;
681 return ret;
684 static void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
685 const char **values,
686 const char **val, int *len, int *fmt)
688 int i, j;
690 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
691 if (values[j] == NULL) {
692 val[i] = NULL;
694 else {
695 switch (statement->types[i]) {
696 case APR_DBD_TYPE_BLOB:
697 case APR_DBD_TYPE_CLOB:
698 val[i] = (char *)values[j];
699 len[i] = atoi(values[++j]);
700 fmt[i] = 1;
702 /* skip table and column */
703 j += 2;
704 break;
705 default:
706 val[i] = values[j];
707 break;
712 return;
715 static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
716 int *nrows, apr_dbd_prepared_t *statement,
717 const char **values)
719 int *len, *fmt;
720 const char **val;
722 if (sql->trans && sql->trans->errnum) {
723 return sql->trans->errnum;
726 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
727 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
728 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
730 dbd_pgsql_bind(statement, values, val, len, fmt);
732 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
733 val, len, fmt);
736 static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
737 int *nrows, apr_dbd_prepared_t *statement,
738 va_list args)
740 const char **values;
741 int i;
743 if (sql->trans && sql->trans->errnum) {
744 return sql->trans->errnum;
747 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
749 for (i = 0; i < statement->nvals; i++) {
750 values[i] = va_arg(args, const char*);
753 return dbd_pgsql_pquery(pool, sql, nrows, statement, values);
756 static int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
757 apr_dbd_results_t **results,
758 apr_dbd_prepared_t *statement,
759 int seek, const char **values,
760 const int *len, const int *fmt)
762 PGresult *res;
763 int rv;
764 int ret = 0;
766 if (seek) { /* synchronous query */
767 if (TXN_IGNORE_ERRORS(sql->trans)) {
768 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
769 if (res) {
770 ret = PQresultStatus(res);
771 PQclear(res);
772 if (!dbd_pgsql_is_success(ret)) {
773 sql->trans->errnum = ret;
774 return PGRES_FATAL_ERROR;
776 } else {
777 sql->trans->errnum = ret;
778 return PGRES_FATAL_ERROR;
781 if (statement->prepared) {
782 res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
783 values, len, fmt, 0);
785 else {
786 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
787 values, len, fmt, 0);
789 if (res) {
790 ret = PQresultStatus(res);
791 if (dbd_pgsql_is_success(ret)) {
792 ret = 0;
794 else {
795 PQclear(res);
798 else {
799 ret = PGRES_FATAL_ERROR;
801 if (ret != 0) {
802 if (TXN_IGNORE_ERRORS(sql->trans)) {
803 PGresult *res = PQexec(sql->conn,
804 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
805 if (res) {
806 ret = PQresultStatus(res);
807 PQclear(res);
808 if (!dbd_pgsql_is_success(ret)) {
809 sql->trans->errnum = ret;
810 return PGRES_FATAL_ERROR;
812 } else {
813 sql->trans->errnum = ret;
814 return PGRES_FATAL_ERROR;
816 } else if (TXN_NOTICE_ERRORS(sql->trans)){
817 sql->trans->errnum = ret;
819 return ret;
820 } else {
821 if (TXN_IGNORE_ERRORS(sql->trans)) {
822 PGresult *res = PQexec(sql->conn,
823 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
824 if (res) {
825 ret = PQresultStatus(res);
826 PQclear(res);
827 if (!dbd_pgsql_is_success(ret)) {
828 sql->trans->errnum = ret;
829 return PGRES_FATAL_ERROR;
831 } else {
832 sql->trans->errnum = ret;
833 return PGRES_FATAL_ERROR;
837 if (!*results) {
838 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
840 (*results)->res = res;
841 (*results)->ntuples = PQntuples(res);
842 (*results)->sz = PQnfields(res);
843 (*results)->random = seek;
844 (*results)->pool = pool;
845 apr_pool_cleanup_register(pool, res, clear_result,
846 apr_pool_cleanup_null);
848 else {
849 if (TXN_IGNORE_ERRORS(sql->trans)) {
850 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
851 if (res) {
852 ret = PQresultStatus(res);
853 PQclear(res);
854 if (!dbd_pgsql_is_success(ret)) {
855 sql->trans->errnum = ret;
856 return PGRES_FATAL_ERROR;
858 } else {
859 sql->trans->errnum = ret;
860 return PGRES_FATAL_ERROR;
863 if (statement->prepared) {
864 rv = PQsendQueryPrepared(sql->conn, statement->name,
865 statement->nargs, values, len, fmt, 0);
867 else {
868 rv = PQsendQueryParams(sql->conn, statement->name,
869 statement->nargs, 0, values, len, fmt, 0);
871 if (rv == 0) {
872 if (TXN_IGNORE_ERRORS(sql->trans)) {
873 PGresult *res = PQexec(sql->conn,
874 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
875 if (res) {
876 ret = PQresultStatus(res);
877 PQclear(res);
878 if (!dbd_pgsql_is_success(ret)) {
879 sql->trans->errnum = ret;
880 return PGRES_FATAL_ERROR;
882 } else {
883 sql->trans->errnum = ret;
884 return PGRES_FATAL_ERROR;
886 } else if (TXN_NOTICE_ERRORS(sql->trans)){
887 sql->trans->errnum = 1;
889 return 1;
890 } else {
891 if (TXN_IGNORE_ERRORS(sql->trans)) {
892 PGresult *res = PQexec(sql->conn,
893 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
894 if (res) {
895 ret = PQresultStatus(res);
896 PQclear(res);
897 if (!dbd_pgsql_is_success(ret)) {
898 sql->trans->errnum = ret;
899 return PGRES_FATAL_ERROR;
901 } else {
902 sql->trans->errnum = ret;
903 return PGRES_FATAL_ERROR;
907 if (!*results) {
908 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
910 (*results)->random = seek;
911 (*results)->handle = sql->conn;
912 (*results)->pool = pool;
915 return ret;
918 static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
919 apr_dbd_results_t **results,
920 apr_dbd_prepared_t *statement,
921 int seek, const char **values)
923 int *len, *fmt;
924 const char **val;
926 if (sql->trans && sql->trans->errnum) {
927 return sql->trans->errnum;
930 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
931 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
932 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
934 dbd_pgsql_bind(statement, values, val, len, fmt);
936 return dbd_pgsql_pselect_internal(pool, sql, results, statement,
937 seek, val, len, fmt);
940 static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
941 apr_dbd_results_t **results,
942 apr_dbd_prepared_t *statement,
943 int seek, va_list args)
945 const char **values;
946 int i;
948 if (sql->trans && sql->trans->errnum) {
949 return sql->trans->errnum;
952 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
954 for (i = 0; i < statement->nvals; i++) {
955 values[i] = va_arg(args, const char*);
958 return dbd_pgsql_pselect(pool, sql, results, statement, seek, values);
961 static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
962 const void **values,
963 const char **val, int *len, int *fmt)
965 int i, j;
966 apr_dbd_type_e type;
968 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
969 type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
971 switch (type) {
972 case APR_DBD_TYPE_TINY:
973 val[i] = apr_itoa(pool, *(char*)values[j]);
974 break;
975 case APR_DBD_TYPE_UTINY:
976 val[i] = apr_itoa(pool, *(unsigned char*)values[j]);
977 break;
978 case APR_DBD_TYPE_SHORT:
979 val[i] = apr_itoa(pool, *(short*)values[j]);
980 break;
981 case APR_DBD_TYPE_USHORT:
982 val[i] = apr_itoa(pool, *(unsigned short*)values[j]);
983 break;
984 case APR_DBD_TYPE_INT:
985 val[i] = apr_itoa(pool, *(int*)values[j]);
986 break;
987 case APR_DBD_TYPE_UINT:
988 val[i] = apr_itoa(pool, *(unsigned int*)values[j]);
989 break;
990 case APR_DBD_TYPE_LONG:
991 val[i] = apr_ltoa(pool, *(long*)values[j]);
992 break;
993 case APR_DBD_TYPE_ULONG:
994 val[i] = apr_ltoa(pool, *(unsigned long*)values[j]);
995 break;
996 case APR_DBD_TYPE_LONGLONG:
997 val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
998 *(apr_int64_t*)values[j]);
999 break;
1000 case APR_DBD_TYPE_ULONGLONG:
1001 val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
1002 *(apr_uint64_t*)values[j]);
1003 break;
1004 case APR_DBD_TYPE_FLOAT:
1005 val[i] = apr_psprintf(pool, "%f", *(float*)values[j]);
1006 break;
1007 case APR_DBD_TYPE_DOUBLE:
1008 val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]);
1009 break;
1010 case APR_DBD_TYPE_STRING:
1011 case APR_DBD_TYPE_TEXT:
1012 case APR_DBD_TYPE_TIME:
1013 case APR_DBD_TYPE_DATE:
1014 case APR_DBD_TYPE_DATETIME:
1015 case APR_DBD_TYPE_TIMESTAMP:
1016 case APR_DBD_TYPE_ZTIMESTAMP:
1017 val[i] = values[j];
1018 break;
1019 case APR_DBD_TYPE_BLOB:
1020 case APR_DBD_TYPE_CLOB:
1021 val[i] = (char*)values[j];
1022 len[i] = *(apr_size_t*)values[++j];
1023 fmt[i] = 1;
1025 /* skip table and column */
1026 j += 2;
1027 break;
1028 case APR_DBD_TYPE_NULL:
1029 default:
1030 val[i] = NULL;
1031 break;
1035 return;
1038 static int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
1039 int *nrows, apr_dbd_prepared_t * statement,
1040 const void **values)
1042 int *len, *fmt;
1043 const char **val;
1045 if (sql->trans && sql->trans->errnum) {
1046 return sql->trans->errnum;
1049 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1050 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1051 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1053 dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1055 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
1056 val, len, fmt);
1059 static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
1060 int *nrows, apr_dbd_prepared_t * statement,
1061 va_list args)
1063 const void **values;
1064 int i;
1066 if (sql->trans && sql->trans->errnum) {
1067 return sql->trans->errnum;
1070 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1072 for (i = 0; i < statement->nvals; i++) {
1073 values[i] = va_arg(args, const void*);
1076 return dbd_pgsql_pbquery(pool, sql, nrows, statement, values);
1079 static int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
1080 apr_dbd_results_t ** results,
1081 apr_dbd_prepared_t * statement,
1082 int seek, const void **values)
1084 int *len, *fmt;
1085 const char **val;
1087 if (sql->trans && sql->trans->errnum) {
1088 return sql->trans->errnum;
1091 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1092 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1093 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1095 dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1097 return dbd_pgsql_pselect_internal(pool, sql, results, statement,
1098 seek, val, len, fmt);
1101 static int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
1102 apr_dbd_results_t ** results,
1103 apr_dbd_prepared_t * statement, int seek,
1104 va_list args)
1106 const void **values;
1107 int i;
1109 if (sql->trans && sql->trans->errnum) {
1110 return sql->trans->errnum;
1113 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1115 for (i = 0; i < statement->nvals; i++) {
1116 values[i] = va_arg(args, const void*);
1119 return dbd_pgsql_pbselect(pool, sql, results, statement, seek, values);
1122 static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1123 apr_dbd_transaction_t **trans)
1125 int ret = 0;
1126 PGresult *res;
1128 /* XXX handle recursive transactions here */
1130 res = PQexec(handle->conn, "BEGIN TRANSACTION");
1131 if (res) {
1132 ret = PQresultStatus(res);
1133 if (dbd_pgsql_is_success(ret)) {
1134 ret = 0;
1135 if (!*trans) {
1136 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1139 PQclear(res);
1140 (*trans)->handle = handle;
1141 handle->trans = *trans;
1143 else {
1144 ret = PGRES_FATAL_ERROR;
1146 return ret;
1149 static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
1151 PGresult *res;
1152 int ret = -1; /* no transaction is an error cond */
1153 if (trans) {
1154 /* rollback on error or explicit rollback request */
1155 if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1156 trans->errnum = 0;
1157 res = PQexec(trans->handle->conn, "ROLLBACK");
1159 else {
1160 res = PQexec(trans->handle->conn, "COMMIT");
1162 if (res) {
1163 ret = PQresultStatus(res);
1164 if (dbd_pgsql_is_success(ret)) {
1165 ret = 0;
1167 PQclear(res);
1169 else {
1170 ret = PGRES_FATAL_ERROR;
1172 trans->handle->trans = NULL;
1174 return ret;
1177 static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
1179 if (!trans)
1180 return APR_DBD_TRANSACTION_COMMIT;
1182 return trans->mode;
1185 static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
1186 int mode)
1188 if (!trans)
1189 return APR_DBD_TRANSACTION_COMMIT;
1191 return trans->mode = (mode & TXN_MODE_BITS);
1194 static void null_notice_receiver(void *arg, const PGresult *res)
1196 /* nothing */
1199 static void null_notice_processor(void *arg, const char *message)
1201 /* nothing */
1204 static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params,
1205 const char **error)
1207 apr_dbd_t *sql;
1209 PGconn *conn = PQconnectdb(params);
1211 /* if there's an error in the connect string or something we get
1212 * back a * bogus connection object, and things like PQreset are
1213 * liable to segfault, so just close it out now. it would be nice
1214 * if we could give an indication of why we failed to connect... */
1215 if (PQstatus(conn) != CONNECTION_OK) {
1216 if (error) {
1217 *error = apr_pstrdup(pool, PQerrorMessage(conn));
1219 PQfinish(conn);
1220 return NULL;
1223 PQsetNoticeReceiver(conn, null_notice_receiver, NULL);
1224 PQsetNoticeProcessor(conn, null_notice_processor, NULL);
1226 sql = apr_pcalloc (pool, sizeof (*sql));
1228 sql->conn = conn;
1230 return sql;
1233 static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
1235 PQfinish(handle->conn);
1236 return APR_SUCCESS;
1239 static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
1240 apr_dbd_t *handle)
1242 if (PQstatus(handle->conn) != CONNECTION_OK) {
1243 PQreset(handle->conn);
1244 if (PQstatus(handle->conn) != CONNECTION_OK) {
1245 return APR_EGENERAL;
1248 return APR_SUCCESS;
1251 static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
1252 const char *name)
1254 return APR_ENOTIMPL;
1257 static void *dbd_pgsql_native(apr_dbd_t *handle)
1259 return handle->conn;
1262 static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
1264 return res->sz;
1267 static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
1269 if (res->random) {
1270 return res->ntuples;
1272 else {
1273 return -1;
1277 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
1278 "pgsql",
1279 NULL,
1280 dbd_pgsql_native,
1281 dbd_pgsql_open,
1282 dbd_pgsql_check_conn,
1283 dbd_pgsql_close,
1284 dbd_pgsql_select_db,
1285 dbd_pgsql_start_transaction,
1286 dbd_pgsql_end_transaction,
1287 dbd_pgsql_query,
1288 dbd_pgsql_select,
1289 dbd_pgsql_num_cols,
1290 dbd_pgsql_num_tuples,
1291 dbd_pgsql_get_row,
1292 dbd_pgsql_get_entry,
1293 dbd_pgsql_error,
1294 dbd_pgsql_escape,
1295 dbd_pgsql_prepare,
1296 dbd_pgsql_pvquery,
1297 dbd_pgsql_pvselect,
1298 dbd_pgsql_pquery,
1299 dbd_pgsql_pselect,
1300 dbd_pgsql_get_name,
1301 dbd_pgsql_transaction_mode_get,
1302 dbd_pgsql_transaction_mode_set,
1303 "$%d",
1304 dbd_pgsql_pvbquery,
1305 dbd_pgsql_pvbselect,
1306 dbd_pgsql_pbquery,
1307 dbd_pgsql_pbselect,
1308 dbd_pgsql_datum_get
1310 #endif