Make sure PGSQL and MySQL drivers count rows from 1, not 0
[apr-util.git] / dbd / apr_dbd_pgsql.c
blobcb3cfae965954ddca1a5a0ed33c1278798f27d66
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 row->n = sequential ? 0 : --rownum;
236 else {
237 if ( sequential ) {
238 ++row->n;
240 else {
241 row->n = --rownum;
245 if (res->random) {
246 if (row->n >= res->ntuples) {
247 *rowp = NULL;
248 apr_pool_cleanup_run(pool, res->res, clear_result);
249 res->res = NULL;
250 return -1;
253 else {
254 if (row->n >= res->ntuples) {
255 /* no data; we have to fetch some */
256 row->n -= res->ntuples;
257 if (res->res != NULL) {
258 PQclear(res->res);
260 res->res = PQgetResult(res->handle);
261 if (res->res) {
262 res->ntuples = PQntuples(res->res);
263 while (res->ntuples == 0) {
264 /* if we got an empty result, clear it, wait a mo, try
265 * again */
266 PQclear(res->res);
267 apr_sleep(100000); /* 0.1 secs */
268 res->res = PQgetResult(res->handle);
269 if (res->res) {
270 res->ntuples = PQntuples(res->res);
272 else {
273 return -1;
276 if (res->sz == 0) {
277 res->sz = PQnfields(res->res);
280 else {
281 return -1;
285 return 0;
288 static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
290 return PQgetvalue(row->res->res, row->n, n);
293 static apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n,
294 apr_dbd_type_e type, void *data)
296 if (PQgetisnull(row->res->res, row->n, n)) {
297 return APR_ENOENT;
300 switch (type) {
301 case APR_DBD_TYPE_TINY:
302 *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
303 break;
304 case APR_DBD_TYPE_UTINY:
305 *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
306 break;
307 case APR_DBD_TYPE_SHORT:
308 *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
309 break;
310 case APR_DBD_TYPE_USHORT:
311 *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
312 break;
313 case APR_DBD_TYPE_INT:
314 *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
315 break;
316 case APR_DBD_TYPE_UINT:
317 *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
318 break;
319 case APR_DBD_TYPE_LONG:
320 *(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
321 break;
322 case APR_DBD_TYPE_ULONG:
323 *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
324 break;
325 case APR_DBD_TYPE_LONGLONG:
326 *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
327 break;
328 case APR_DBD_TYPE_ULONGLONG:
329 *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
330 break;
331 case APR_DBD_TYPE_FLOAT:
332 *(float*)data = atof(PQgetvalue(row->res->res, row->n, n));
333 break;
334 case APR_DBD_TYPE_DOUBLE:
335 *(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
336 break;
337 case APR_DBD_TYPE_STRING:
338 case APR_DBD_TYPE_TEXT:
339 case APR_DBD_TYPE_TIME:
340 case APR_DBD_TYPE_DATE:
341 case APR_DBD_TYPE_DATETIME:
342 case APR_DBD_TYPE_TIMESTAMP:
343 case APR_DBD_TYPE_ZTIMESTAMP:
344 *(char**)data = PQgetvalue(row->res->res, row->n, n);
345 break;
346 case APR_DBD_TYPE_BLOB:
347 case APR_DBD_TYPE_CLOB:
349 apr_bucket *e;
350 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
352 e = apr_bucket_pool_create(PQgetvalue(row->res->res, row->n, n),
353 PQgetlength(row->res->res, row->n, n),
354 row->res->pool, b->bucket_alloc);
355 APR_BRIGADE_INSERT_TAIL(b, e);
357 break;
358 case APR_DBD_TYPE_NULL:
359 *(void**)data = NULL;
360 break;
361 default:
362 return APR_EGENERAL;
365 return APR_SUCCESS;
368 static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
370 return PQerrorMessage(sql->conn);
373 static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
375 PGresult *res;
376 int ret;
377 if (sql->trans && sql->trans->errnum) {
378 return sql->trans->errnum;
381 if (TXN_IGNORE_ERRORS(sql->trans)) {
382 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
383 if (res) {
384 ret = PQresultStatus(res);
385 PQclear(res);
386 if (!dbd_pgsql_is_success(ret)) {
387 sql->trans->errnum = ret;
388 return PGRES_FATAL_ERROR;
390 } else {
391 return sql->trans->errnum = PGRES_FATAL_ERROR;
395 res = PQexec(sql->conn, query);
396 if (res) {
397 ret = PQresultStatus(res);
398 if (dbd_pgsql_is_success(ret)) {
399 /* ugh, making 0 return-success doesn't fit */
400 ret = 0;
402 *nrows = atoi(PQcmdTuples(res));
403 PQclear(res);
405 else {
406 ret = PGRES_FATAL_ERROR;
409 if (ret != 0){
410 if (TXN_IGNORE_ERRORS(sql->trans)) {
411 PGresult *res = PQexec(sql->conn,
412 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
413 if (res) {
414 ret = PQresultStatus(res);
415 PQclear(res);
416 if (!dbd_pgsql_is_success(ret)) {
417 sql->trans->errnum = ret;
418 return PGRES_FATAL_ERROR;
420 } else {
421 sql->trans->errnum = ret;
422 return PGRES_FATAL_ERROR;
424 } else if (TXN_NOTICE_ERRORS(sql->trans)){
425 sql->trans->errnum = ret;
427 } else {
428 if (TXN_IGNORE_ERRORS(sql->trans)) {
429 PGresult *res = PQexec(sql->conn,
430 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
431 if (res) {
432 ret = PQresultStatus(res);
433 PQclear(res);
434 if (!dbd_pgsql_is_success(ret)) {
435 sql->trans->errnum = ret;
436 return PGRES_FATAL_ERROR;
438 } else {
439 sql->trans->errnum = ret;
440 return PGRES_FATAL_ERROR;
445 return ret;
448 static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
449 apr_dbd_t *sql)
451 size_t len = strlen(arg);
452 char *ret = apr_palloc(pool, 2*len + 2);
453 PQescapeString(ret, arg, len);
454 return ret;
457 static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
458 const char *query, const char *label,
459 int nargs, int nvals, apr_dbd_type_e *types,
460 apr_dbd_prepared_t **statement)
462 char *sqlcmd;
463 char *sqlptr;
464 size_t length, qlen;
465 size_t i = 0;
466 const char **args;
467 size_t alen;
468 int ret;
469 PGresult *res;
471 if (!*statement) {
472 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
474 (*statement)->nargs = nargs;
475 (*statement)->nvals = nvals;
476 (*statement)->types = types;
478 args = apr_palloc(pool, nargs * sizeof(*args));
480 qlen = strlen(query);
481 length = qlen + 1;
483 for (i = 0; i < nargs; i++) {
484 switch (types[i]) {
485 case APR_DBD_TYPE_TINY:
486 case APR_DBD_TYPE_UTINY:
487 case APR_DBD_TYPE_SHORT:
488 case APR_DBD_TYPE_USHORT:
489 args[i] = "smallint";
490 break;
491 case APR_DBD_TYPE_INT:
492 case APR_DBD_TYPE_UINT:
493 args[i] = "integer";
494 break;
495 case APR_DBD_TYPE_LONG:
496 case APR_DBD_TYPE_ULONG:
497 case APR_DBD_TYPE_LONGLONG:
498 case APR_DBD_TYPE_ULONGLONG:
499 args[i] = "bigint";
500 break;
501 case APR_DBD_TYPE_FLOAT:
502 args[i] = "real";
503 break;
504 case APR_DBD_TYPE_DOUBLE:
505 args[i] = "double precision";
506 break;
507 case APR_DBD_TYPE_TEXT:
508 args[i] = "text";
509 break;
510 case APR_DBD_TYPE_TIME:
511 args[i] = "time";
512 break;
513 case APR_DBD_TYPE_DATE:
514 args[i] = "date";
515 break;
516 case APR_DBD_TYPE_DATETIME:
517 case APR_DBD_TYPE_TIMESTAMP:
518 args[i] = "timestamp";
519 break;
520 case APR_DBD_TYPE_ZTIMESTAMP:
521 args[i] = "timestamp with time zone";
522 break;
523 case APR_DBD_TYPE_BLOB:
524 case APR_DBD_TYPE_CLOB:
525 args[i] = "bytea";
526 break;
527 case APR_DBD_TYPE_NULL:
528 args[i] = "varchar"; /* XXX Eh? */
529 break;
530 default:
531 args[i] = "varchar";
532 break;
534 length += 1 + strlen(args[i]);
537 if (!label) {
538 /* don't really prepare; use in execParams instead */
539 (*statement)->prepared = 0;
540 (*statement)->name = apr_pstrdup(pool, query);
541 return 0;
543 (*statement)->name = apr_pstrdup(pool, label);
545 /* length of SQL query that prepares this statement */
546 length = 8 + strlen(label) + 2 + 4 + length + 1;
547 sqlcmd = apr_palloc(pool, length);
548 sqlptr = sqlcmd;
549 memcpy(sqlptr, "PREPARE ", 8);
550 sqlptr += 8;
551 length = strlen(label);
552 memcpy(sqlptr, label, length);
553 sqlptr += length;
554 if (nargs > 0) {
555 memcpy(sqlptr, " (",2);
556 sqlptr += 2;
557 for (i=0; i < nargs; ++i) {
558 alen = strlen(args[i]);
559 memcpy(sqlptr, args[i], alen);
560 sqlptr += alen;
561 *sqlptr++ = ',';
563 sqlptr[-1] = ')';
565 memcpy(sqlptr, " AS ", 4);
566 sqlptr += 4;
567 memcpy(sqlptr, query, qlen);
568 sqlptr += qlen;
569 *sqlptr = 0;
571 res = PQexec(sql->conn, sqlcmd);
572 if ( res ) {
573 ret = PQresultStatus(res);
574 if (dbd_pgsql_is_success(ret)) {
575 ret = 0;
577 /* Hmmm, do we do this here or register it on the pool? */
578 PQclear(res);
580 else {
581 ret = PGRES_FATAL_ERROR;
583 (*statement)->prepared = 1;
585 return ret;
588 static int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
589 int *nrows, apr_dbd_prepared_t *statement,
590 const char **values,
591 const int *len, const int *fmt)
593 int ret;
594 PGresult *res;
596 if (TXN_IGNORE_ERRORS(sql->trans)) {
597 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
598 if (res) {
599 ret = PQresultStatus(res);
600 PQclear(res);
601 if (!dbd_pgsql_is_success(ret)) {
602 sql->trans->errnum = ret;
603 return PGRES_FATAL_ERROR;
605 } else {
606 return sql->trans->errnum = PGRES_FATAL_ERROR;
610 if (statement->prepared) {
611 res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
612 values, len, fmt, 0);
614 else {
615 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
616 values, len, fmt, 0);
618 if (res) {
619 ret = PQresultStatus(res);
620 if (dbd_pgsql_is_success(ret)) {
621 ret = 0;
623 *nrows = atoi(PQcmdTuples(res));
624 PQclear(res);
626 else {
627 ret = PGRES_FATAL_ERROR;
630 if (ret != 0){
631 if (TXN_IGNORE_ERRORS(sql->trans)) {
632 PGresult *res = PQexec(sql->conn,
633 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
634 if (res) {
635 ret = PQresultStatus(res);
636 PQclear(res);
637 if (!dbd_pgsql_is_success(ret)) {
638 sql->trans->errnum = ret;
639 return PGRES_FATAL_ERROR;
641 } else {
642 sql->trans->errnum = ret;
643 return PGRES_FATAL_ERROR;
645 } else if (TXN_NOTICE_ERRORS(sql->trans)){
646 sql->trans->errnum = ret;
648 } else {
649 if (TXN_IGNORE_ERRORS(sql->trans)) {
650 PGresult *res = PQexec(sql->conn,
651 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
652 if (res) {
653 ret = PQresultStatus(res);
654 PQclear(res);
655 if (!dbd_pgsql_is_success(ret)) {
656 sql->trans->errnum = ret;
657 return PGRES_FATAL_ERROR;
659 } else {
660 sql->trans->errnum = ret;
661 return PGRES_FATAL_ERROR;
666 return ret;
669 static void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
670 const char **values,
671 const char **val, int *len, int *fmt)
673 int i, j;
675 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
676 if (values[j] == NULL) {
677 val[i] = NULL;
679 else {
680 switch (statement->types[i]) {
681 case APR_DBD_TYPE_BLOB:
682 case APR_DBD_TYPE_CLOB:
683 val[i] = (char *)values[j];
684 len[i] = atoi(values[++j]);
685 fmt[i] = 1;
687 /* skip table and column */
688 j += 2;
689 break;
690 default:
691 val[i] = values[j];
692 break;
697 return;
700 static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
701 int *nrows, apr_dbd_prepared_t *statement,
702 const char **values)
704 int *len, *fmt;
705 const char **val;
707 if (sql->trans && sql->trans->errnum) {
708 return sql->trans->errnum;
711 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
712 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
713 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
715 dbd_pgsql_bind(statement, values, val, len, fmt);
717 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
718 val, len, fmt);
721 static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
722 int *nrows, apr_dbd_prepared_t *statement,
723 va_list args)
725 const char **values;
726 int i;
728 if (sql->trans && sql->trans->errnum) {
729 return sql->trans->errnum;
732 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
734 for (i = 0; i < statement->nvals; i++) {
735 values[i] = va_arg(args, const char*);
738 return dbd_pgsql_pquery(pool, sql, nrows, statement, values);
741 static int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
742 apr_dbd_results_t **results,
743 apr_dbd_prepared_t *statement,
744 int seek, const char **values,
745 const int *len, const int *fmt)
747 PGresult *res;
748 int rv;
749 int ret = 0;
751 if (seek) { /* synchronous query */
752 if (TXN_IGNORE_ERRORS(sql->trans)) {
753 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
754 if (res) {
755 ret = PQresultStatus(res);
756 PQclear(res);
757 if (!dbd_pgsql_is_success(ret)) {
758 sql->trans->errnum = ret;
759 return PGRES_FATAL_ERROR;
761 } else {
762 sql->trans->errnum = ret;
763 return PGRES_FATAL_ERROR;
766 if (statement->prepared) {
767 res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
768 values, len, fmt, 0);
770 else {
771 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
772 values, len, fmt, 0);
774 if (res) {
775 ret = PQresultStatus(res);
776 if (dbd_pgsql_is_success(ret)) {
777 ret = 0;
779 else {
780 PQclear(res);
783 else {
784 ret = PGRES_FATAL_ERROR;
786 if (ret != 0) {
787 if (TXN_IGNORE_ERRORS(sql->trans)) {
788 PGresult *res = PQexec(sql->conn,
789 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
790 if (res) {
791 ret = PQresultStatus(res);
792 PQclear(res);
793 if (!dbd_pgsql_is_success(ret)) {
794 sql->trans->errnum = ret;
795 return PGRES_FATAL_ERROR;
797 } else {
798 sql->trans->errnum = ret;
799 return PGRES_FATAL_ERROR;
801 } else if (TXN_NOTICE_ERRORS(sql->trans)){
802 sql->trans->errnum = ret;
804 return ret;
805 } else {
806 if (TXN_IGNORE_ERRORS(sql->trans)) {
807 PGresult *res = PQexec(sql->conn,
808 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
809 if (res) {
810 ret = PQresultStatus(res);
811 PQclear(res);
812 if (!dbd_pgsql_is_success(ret)) {
813 sql->trans->errnum = ret;
814 return PGRES_FATAL_ERROR;
816 } else {
817 sql->trans->errnum = ret;
818 return PGRES_FATAL_ERROR;
822 if (!*results) {
823 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
825 (*results)->res = res;
826 (*results)->ntuples = PQntuples(res);
827 (*results)->sz = PQnfields(res);
828 (*results)->random = seek;
829 (*results)->pool = pool;
830 apr_pool_cleanup_register(pool, res, clear_result,
831 apr_pool_cleanup_null);
833 else {
834 if (TXN_IGNORE_ERRORS(sql->trans)) {
835 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
836 if (res) {
837 ret = PQresultStatus(res);
838 PQclear(res);
839 if (!dbd_pgsql_is_success(ret)) {
840 sql->trans->errnum = ret;
841 return PGRES_FATAL_ERROR;
843 } else {
844 sql->trans->errnum = ret;
845 return PGRES_FATAL_ERROR;
848 if (statement->prepared) {
849 rv = PQsendQueryPrepared(sql->conn, statement->name,
850 statement->nargs, values, len, fmt, 0);
852 else {
853 rv = PQsendQueryParams(sql->conn, statement->name,
854 statement->nargs, 0, values, len, fmt, 0);
856 if (rv == 0) {
857 if (TXN_IGNORE_ERRORS(sql->trans)) {
858 PGresult *res = PQexec(sql->conn,
859 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
860 if (res) {
861 ret = PQresultStatus(res);
862 PQclear(res);
863 if (!dbd_pgsql_is_success(ret)) {
864 sql->trans->errnum = ret;
865 return PGRES_FATAL_ERROR;
867 } else {
868 sql->trans->errnum = ret;
869 return PGRES_FATAL_ERROR;
871 } else if (TXN_NOTICE_ERRORS(sql->trans)){
872 sql->trans->errnum = 1;
874 return 1;
875 } else {
876 if (TXN_IGNORE_ERRORS(sql->trans)) {
877 PGresult *res = PQexec(sql->conn,
878 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
879 if (res) {
880 ret = PQresultStatus(res);
881 PQclear(res);
882 if (!dbd_pgsql_is_success(ret)) {
883 sql->trans->errnum = ret;
884 return PGRES_FATAL_ERROR;
886 } else {
887 sql->trans->errnum = ret;
888 return PGRES_FATAL_ERROR;
892 if (!*results) {
893 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
895 (*results)->random = seek;
896 (*results)->handle = sql->conn;
897 (*results)->pool = pool;
900 return ret;
903 static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
904 apr_dbd_results_t **results,
905 apr_dbd_prepared_t *statement,
906 int seek, const char **values)
908 int *len, *fmt;
909 const char **val;
911 if (sql->trans && sql->trans->errnum) {
912 return sql->trans->errnum;
915 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
916 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
917 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
919 dbd_pgsql_bind(statement, values, val, len, fmt);
921 return dbd_pgsql_pselect_internal(pool, sql, results, statement,
922 seek, val, len, fmt);
925 static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
926 apr_dbd_results_t **results,
927 apr_dbd_prepared_t *statement,
928 int seek, va_list args)
930 const char **values;
931 int i;
933 if (sql->trans && sql->trans->errnum) {
934 return sql->trans->errnum;
937 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
939 for (i = 0; i < statement->nvals; i++) {
940 values[i] = va_arg(args, const char*);
943 return dbd_pgsql_pselect(pool, sql, results, statement, seek, values);
946 static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
947 const void **values,
948 const char **val, int *len, int *fmt)
950 int i, j;
951 apr_dbd_type_e type;
953 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
954 type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
956 switch (type) {
957 case APR_DBD_TYPE_TINY:
958 val[i] = apr_itoa(pool, *(char*)values[j]);
959 break;
960 case APR_DBD_TYPE_UTINY:
961 val[i] = apr_itoa(pool, *(unsigned char*)values[j]);
962 break;
963 case APR_DBD_TYPE_SHORT:
964 val[i] = apr_itoa(pool, *(short*)values[j]);
965 break;
966 case APR_DBD_TYPE_USHORT:
967 val[i] = apr_itoa(pool, *(unsigned short*)values[j]);
968 break;
969 case APR_DBD_TYPE_INT:
970 val[i] = apr_itoa(pool, *(int*)values[j]);
971 break;
972 case APR_DBD_TYPE_UINT:
973 val[i] = apr_itoa(pool, *(unsigned int*)values[j]);
974 break;
975 case APR_DBD_TYPE_LONG:
976 val[i] = apr_ltoa(pool, *(long*)values[j]);
977 break;
978 case APR_DBD_TYPE_ULONG:
979 val[i] = apr_ltoa(pool, *(unsigned long*)values[j]);
980 break;
981 case APR_DBD_TYPE_LONGLONG:
982 val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
983 *(apr_int64_t*)values[j]);
984 break;
985 case APR_DBD_TYPE_ULONGLONG:
986 val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
987 *(apr_uint64_t*)values[j]);
988 break;
989 case APR_DBD_TYPE_FLOAT:
990 val[i] = apr_psprintf(pool, "%f", *(float*)values[j]);
991 break;
992 case APR_DBD_TYPE_DOUBLE:
993 val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]);
994 break;
995 case APR_DBD_TYPE_STRING:
996 case APR_DBD_TYPE_TEXT:
997 case APR_DBD_TYPE_TIME:
998 case APR_DBD_TYPE_DATE:
999 case APR_DBD_TYPE_DATETIME:
1000 case APR_DBD_TYPE_TIMESTAMP:
1001 case APR_DBD_TYPE_ZTIMESTAMP:
1002 val[i] = values[j];
1003 break;
1004 case APR_DBD_TYPE_BLOB:
1005 case APR_DBD_TYPE_CLOB:
1006 val[i] = (char*)values[j];
1007 len[i] = *(apr_size_t*)values[++j];
1008 fmt[i] = 1;
1010 /* skip table and column */
1011 j += 2;
1012 break;
1013 case APR_DBD_TYPE_NULL:
1014 default:
1015 val[i] = NULL;
1016 break;
1020 return;
1023 static int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
1024 int *nrows, apr_dbd_prepared_t * statement,
1025 const void **values)
1027 int *len, *fmt;
1028 const char **val;
1030 if (sql->trans && sql->trans->errnum) {
1031 return sql->trans->errnum;
1034 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1035 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1036 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1038 dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1040 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
1041 val, len, fmt);
1044 static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
1045 int *nrows, apr_dbd_prepared_t * statement,
1046 va_list args)
1048 const void **values;
1049 int i;
1051 if (sql->trans && sql->trans->errnum) {
1052 return sql->trans->errnum;
1055 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1057 for (i = 0; i < statement->nvals; i++) {
1058 values[i] = va_arg(args, const void*);
1061 return dbd_pgsql_pbquery(pool, sql, nrows, statement, values);
1064 static int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
1065 apr_dbd_results_t ** results,
1066 apr_dbd_prepared_t * statement,
1067 int seek, const void **values)
1069 int *len, *fmt;
1070 const char **val;
1072 if (sql->trans && sql->trans->errnum) {
1073 return sql->trans->errnum;
1076 val = apr_palloc(pool, sizeof(*val) * statement->nargs);
1077 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
1078 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
1080 dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1082 return dbd_pgsql_pselect_internal(pool, sql, results, statement,
1083 seek, val, len, fmt);
1086 static int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
1087 apr_dbd_results_t ** results,
1088 apr_dbd_prepared_t * statement, int seek,
1089 va_list args)
1091 const void **values;
1092 int i;
1094 if (sql->trans && sql->trans->errnum) {
1095 return sql->trans->errnum;
1098 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1100 for (i = 0; i < statement->nvals; i++) {
1101 values[i] = va_arg(args, const void*);
1104 return dbd_pgsql_pbselect(pool, sql, results, statement, seek, values);
1107 static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1108 apr_dbd_transaction_t **trans)
1110 int ret = 0;
1111 PGresult *res;
1113 /* XXX handle recursive transactions here */
1115 res = PQexec(handle->conn, "BEGIN TRANSACTION");
1116 if (res) {
1117 ret = PQresultStatus(res);
1118 if (dbd_pgsql_is_success(ret)) {
1119 ret = 0;
1120 if (!*trans) {
1121 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1124 PQclear(res);
1125 (*trans)->handle = handle;
1126 handle->trans = *trans;
1128 else {
1129 ret = PGRES_FATAL_ERROR;
1131 return ret;
1134 static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
1136 PGresult *res;
1137 int ret = -1; /* no transaction is an error cond */
1138 if (trans) {
1139 /* rollback on error or explicit rollback request */
1140 if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1141 trans->errnum = 0;
1142 res = PQexec(trans->handle->conn, "ROLLBACK");
1144 else {
1145 res = PQexec(trans->handle->conn, "COMMIT");
1147 if (res) {
1148 ret = PQresultStatus(res);
1149 if (dbd_pgsql_is_success(ret)) {
1150 ret = 0;
1152 PQclear(res);
1154 else {
1155 ret = PGRES_FATAL_ERROR;
1157 trans->handle->trans = NULL;
1159 return ret;
1162 static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
1164 if (!trans)
1165 return APR_DBD_TRANSACTION_COMMIT;
1167 return trans->mode;
1170 static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
1171 int mode)
1173 if (!trans)
1174 return APR_DBD_TRANSACTION_COMMIT;
1176 return trans->mode = (mode & TXN_MODE_BITS);
1179 static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params,
1180 const char **error)
1182 apr_dbd_t *sql;
1184 PGconn *conn = PQconnectdb(params);
1186 /* if there's an error in the connect string or something we get
1187 * back a * bogus connection object, and things like PQreset are
1188 * liable to segfault, so just close it out now. it would be nice
1189 * if we could give an indication of why we failed to connect... */
1190 if (PQstatus(conn) != CONNECTION_OK) {
1191 if (error) {
1192 *error = apr_pstrdup(pool, PQerrorMessage(conn));
1194 PQfinish(conn);
1195 return NULL;
1198 sql = apr_pcalloc (pool, sizeof (*sql));
1200 sql->conn = conn;
1202 return sql;
1205 static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
1207 PQfinish(handle->conn);
1208 return APR_SUCCESS;
1211 static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
1212 apr_dbd_t *handle)
1214 if (PQstatus(handle->conn) != CONNECTION_OK) {
1215 PQreset(handle->conn);
1216 if (PQstatus(handle->conn) != CONNECTION_OK) {
1217 return APR_EGENERAL;
1220 return APR_SUCCESS;
1223 static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
1224 const char *name)
1226 return APR_ENOTIMPL;
1229 static void *dbd_pgsql_native(apr_dbd_t *handle)
1231 return handle->conn;
1234 static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
1236 return res->sz;
1239 static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
1241 if (res->random) {
1242 return res->ntuples;
1244 else {
1245 return -1;
1249 APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
1250 "pgsql",
1251 NULL,
1252 dbd_pgsql_native,
1253 dbd_pgsql_open,
1254 dbd_pgsql_check_conn,
1255 dbd_pgsql_close,
1256 dbd_pgsql_select_db,
1257 dbd_pgsql_start_transaction,
1258 dbd_pgsql_end_transaction,
1259 dbd_pgsql_query,
1260 dbd_pgsql_select,
1261 dbd_pgsql_num_cols,
1262 dbd_pgsql_num_tuples,
1263 dbd_pgsql_get_row,
1264 dbd_pgsql_get_entry,
1265 dbd_pgsql_error,
1266 dbd_pgsql_escape,
1267 dbd_pgsql_prepare,
1268 dbd_pgsql_pvquery,
1269 dbd_pgsql_pvselect,
1270 dbd_pgsql_pquery,
1271 dbd_pgsql_pselect,
1272 dbd_pgsql_get_name,
1273 dbd_pgsql_transaction_mode_get,
1274 dbd_pgsql_transaction_mode_set,
1275 "$%d",
1276 dbd_pgsql_pvbquery,
1277 dbd_pgsql_pvbselect,
1278 dbd_pgsql_pbquery,
1279 dbd_pgsql_pbselect,
1280 dbd_pgsql_datum_get
1282 #endif