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.
21 #include "apu_config.h"
26 #ifdef HAVE_LIBPQ_FE_H
28 #elif defined(HAVE_POSTGRESQL_LIBPQ_FE_H)
29 #include <postgresql/libpq-fe.h>
32 #include "apr_strings.h"
34 #include "apr_buckets.h"
36 #include "apr_dbd_internal.h"
38 struct apr_dbd_transaction_t {
46 apr_dbd_transaction_t *trans;
49 struct apr_dbd_results_t {
59 struct apr_dbd_row_t {
61 apr_dbd_results_t *res;
64 struct apr_dbd_prepared_t {
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)
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)
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");
95 int ret = PQresultStatus(res);
97 if (!dbd_pgsql_is_success(ret)) {
98 sql->trans->errnum = ret;
99 return PGRES_FATAL_ERROR;
102 return sql->trans->errnum = PGRES_FATAL_ERROR;
105 res = PQexec(sql->conn, query);
107 ret = PQresultStatus(res);
108 if (dbd_pgsql_is_success(ret)) {
114 ret = PGRES_FATAL_ERROR;
117 if (TXN_IGNORE_ERRORS(sql->trans)) {
118 PGresult *res = PQexec(sql->conn,
119 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
121 int ret = PQresultStatus(res);
123 if (!dbd_pgsql_is_success(ret)) {
124 sql->trans->errnum = ret;
125 return PGRES_FATAL_ERROR;
128 return sql->trans->errnum = PGRES_FATAL_ERROR;
130 } else if (TXN_NOTICE_ERRORS(sql->trans)){
131 sql->trans->errnum = ret;
135 if (TXN_IGNORE_ERRORS(sql->trans)) {
136 PGresult *res = PQexec(sql->conn,
137 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
139 int ret = PQresultStatus(res);
141 if (!dbd_pgsql_is_success(ret)) {
142 sql->trans->errnum = ret;
143 return PGRES_FATAL_ERROR;
146 return sql->trans->errnum = PGRES_FATAL_ERROR;
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);
162 if (TXN_IGNORE_ERRORS(sql->trans)) {
163 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
165 int ret = PQresultStatus(res);
167 if (!dbd_pgsql_is_success(ret)) {
168 sql->trans->errnum = ret;
169 return PGRES_FATAL_ERROR;
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");
180 int ret = PQresultStatus(res);
182 if (!dbd_pgsql_is_success(ret)) {
183 sql->trans->errnum = ret;
184 return PGRES_FATAL_ERROR;
187 return sql->trans->errnum = PGRES_FATAL_ERROR;
189 } else if (TXN_NOTICE_ERRORS(sql->trans)){
190 sql->trans->errnum = 1;
194 if (TXN_IGNORE_ERRORS(sql->trans)) {
195 PGresult *res = PQexec(sql->conn,
196 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
198 int ret = PQresultStatus(res);
200 if (!dbd_pgsql_is_success(ret)) {
201 sql->trans->errnum = ret;
202 return PGRES_FATAL_ERROR;
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;
219 static const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n)
222 if ((n>=0) && (PQnfields(res->res) > n)) {
223 return PQfname(res->res,n);
229 static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
230 apr_dbd_row_t **rowp, int rownum)
232 apr_dbd_row_t *row = *rowp;
233 int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
236 row = apr_palloc(pool, sizeof(apr_dbd_row_t));
247 return -1; /* invalid row */
260 return -1; /* invalid row */
266 if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
268 apr_pool_cleanup_run(res->pool, res->res, clear_result);
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) {
280 res->res = PQgetResult(res->handle);
282 res->ntuples = PQntuples(res->res);
283 while (res->ntuples == 0) {
284 /* if we got an empty result, clear it, wait a mo, try
287 apr_sleep(100000); /* 0.1 secs */
288 res->res = PQgetResult(res->handle);
290 res->ntuples = PQntuples(res->res);
297 res->sz = PQnfields(res->res);
308 static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
310 return PQgetvalue(row->res->res, row->n, n);
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)
316 if (PQgetisnull(row->res->res, row->n, n)) {
321 case APR_DBD_TYPE_TINY:
322 *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
324 case APR_DBD_TYPE_UTINY:
325 *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
327 case APR_DBD_TYPE_SHORT:
328 *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
330 case APR_DBD_TYPE_USHORT:
331 *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
333 case APR_DBD_TYPE_INT:
334 *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
336 case APR_DBD_TYPE_UINT:
337 *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
339 case APR_DBD_TYPE_LONG:
340 *(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
342 case APR_DBD_TYPE_ULONG:
343 *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
345 case APR_DBD_TYPE_LONGLONG:
346 *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
348 case APR_DBD_TYPE_ULONGLONG:
349 *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
351 case APR_DBD_TYPE_FLOAT:
352 *(float*)data = (float)atof(PQgetvalue(row->res->res, row->n, n));
354 case APR_DBD_TYPE_DOUBLE:
355 *(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
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);
366 case APR_DBD_TYPE_BLOB:
367 case APR_DBD_TYPE_CLOB:
370 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
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);
378 case APR_DBD_TYPE_NULL:
379 *(void**)data = NULL;
388 static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
390 return PQerrorMessage(sql->conn);
393 static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
397 if (sql->trans && sql->trans->errnum) {
398 return sql->trans->errnum;
401 if (TXN_IGNORE_ERRORS(sql->trans)) {
402 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
404 int ret = PQresultStatus(res);
406 if (!dbd_pgsql_is_success(ret)) {
407 sql->trans->errnum = ret;
408 return PGRES_FATAL_ERROR;
411 return sql->trans->errnum = PGRES_FATAL_ERROR;
415 res = PQexec(sql->conn, query);
417 ret = PQresultStatus(res);
418 if (dbd_pgsql_is_success(ret)) {
419 /* ugh, making 0 return-success doesn't fit */
422 *nrows = atoi(PQcmdTuples(res));
426 ret = PGRES_FATAL_ERROR;
430 if (TXN_IGNORE_ERRORS(sql->trans)) {
431 PGresult *res = PQexec(sql->conn,
432 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
434 int ret = PQresultStatus(res);
436 if (!dbd_pgsql_is_success(ret)) {
437 sql->trans->errnum = ret;
438 return PGRES_FATAL_ERROR;
441 sql->trans->errnum = ret;
442 return PGRES_FATAL_ERROR;
444 } else if (TXN_NOTICE_ERRORS(sql->trans)){
445 sql->trans->errnum = ret;
448 if (TXN_IGNORE_ERRORS(sql->trans)) {
449 PGresult *res = PQexec(sql->conn,
450 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
452 int ret = PQresultStatus(res);
454 if (!dbd_pgsql_is_success(ret)) {
455 sql->trans->errnum = ret;
456 return PGRES_FATAL_ERROR;
459 sql->trans->errnum = ret;
460 return PGRES_FATAL_ERROR;
468 static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
471 size_t len = strlen(arg);
472 char *ret = apr_palloc(pool, 2*len + 2);
473 PQescapeStringConn(sql->conn, ret, arg, len, NULL);
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)
492 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
494 (*statement)->nargs = nargs;
495 (*statement)->nvals = nvals;
496 (*statement)->types = types;
498 args = apr_palloc(pool, nargs * sizeof(*args));
500 qlen = strlen(query);
503 for (i = 0; i < nargs; 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";
511 case APR_DBD_TYPE_INT:
512 case APR_DBD_TYPE_UINT:
515 case APR_DBD_TYPE_LONG:
516 case APR_DBD_TYPE_ULONG:
517 case APR_DBD_TYPE_LONGLONG:
518 case APR_DBD_TYPE_ULONGLONG:
521 case APR_DBD_TYPE_FLOAT:
524 case APR_DBD_TYPE_DOUBLE:
525 args[i] = "double precision";
527 case APR_DBD_TYPE_TEXT:
530 case APR_DBD_TYPE_TIME:
533 case APR_DBD_TYPE_DATE:
536 case APR_DBD_TYPE_DATETIME:
537 case APR_DBD_TYPE_TIMESTAMP:
538 args[i] = "timestamp";
540 case APR_DBD_TYPE_ZTIMESTAMP:
541 args[i] = "timestamp with time zone";
543 case APR_DBD_TYPE_BLOB:
544 case APR_DBD_TYPE_CLOB:
547 case APR_DBD_TYPE_NULL:
548 args[i] = "varchar"; /* XXX Eh? */
554 length += 1 + strlen(args[i]);
558 /* don't really prepare; use in execParams instead */
559 (*statement)->prepared = 0;
560 (*statement)->name = apr_pstrdup(pool, query);
563 (*statement)->name = apr_pstrdup(pool, label);
565 /* length of SQL query that prepares this statement */
566 length = 8 + strlen(label) + 2 + 4 + length + 1;
567 sqlcmd = apr_palloc(pool, length);
569 memcpy(sqlptr, "PREPARE ", 8);
571 length = strlen(label);
572 memcpy(sqlptr, label, length);
575 memcpy(sqlptr, " (",2);
577 for (i=0; i < nargs; ++i) {
578 alen = strlen(args[i]);
579 memcpy(sqlptr, args[i], alen);
585 memcpy(sqlptr, " AS ", 4);
587 memcpy(sqlptr, query, qlen);
591 res = PQexec(sql->conn, sqlcmd);
593 ret = PQresultStatus(res);
594 if (dbd_pgsql_is_success(ret)) {
597 /* Hmmm, do we do this here or register it on the pool? */
601 ret = PGRES_FATAL_ERROR;
603 (*statement)->prepared = 1;
608 static int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
609 int *nrows, apr_dbd_prepared_t *statement,
611 const int *len, const int *fmt)
616 if (TXN_IGNORE_ERRORS(sql->trans)) {
617 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
619 int ret = PQresultStatus(res);
621 if (!dbd_pgsql_is_success(ret)) {
622 sql->trans->errnum = ret;
623 return PGRES_FATAL_ERROR;
626 return sql->trans->errnum = PGRES_FATAL_ERROR;
630 if (statement->prepared) {
631 res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
632 values, len, fmt, 0);
635 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
636 values, len, fmt, 0);
639 ret = PQresultStatus(res);
640 if (dbd_pgsql_is_success(ret)) {
643 *nrows = atoi(PQcmdTuples(res));
647 ret = PGRES_FATAL_ERROR;
651 if (TXN_IGNORE_ERRORS(sql->trans)) {
652 PGresult *res = PQexec(sql->conn,
653 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
655 int ret = PQresultStatus(res);
657 if (!dbd_pgsql_is_success(ret)) {
658 sql->trans->errnum = ret;
659 return PGRES_FATAL_ERROR;
662 sql->trans->errnum = ret;
663 return PGRES_FATAL_ERROR;
665 } else if (TXN_NOTICE_ERRORS(sql->trans)){
666 sql->trans->errnum = ret;
669 if (TXN_IGNORE_ERRORS(sql->trans)) {
670 PGresult *res = PQexec(sql->conn,
671 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
673 int ret = PQresultStatus(res);
675 if (!dbd_pgsql_is_success(ret)) {
676 sql->trans->errnum = ret;
677 return PGRES_FATAL_ERROR;
680 sql->trans->errnum = ret;
681 return PGRES_FATAL_ERROR;
689 static void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
691 const char **val, int *len, int *fmt)
695 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
696 if (values[j] == NULL) {
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]);
707 /* skip table and column */
720 static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
721 int *nrows, apr_dbd_prepared_t *statement,
727 if (sql->trans && sql->trans->errnum) {
728 return sql->trans->errnum;
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);
735 dbd_pgsql_bind(statement, values, val, len, fmt);
737 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
741 static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
742 int *nrows, apr_dbd_prepared_t *statement,
748 if (sql->trans && sql->trans->errnum) {
749 return sql->trans->errnum;
752 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
754 for (i = 0; i < statement->nvals; i++) {
755 values[i] = va_arg(args, const char*);
758 return dbd_pgsql_pquery(pool, sql, nrows, statement, values);
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)
771 if (seek) { /* synchronous query */
772 if (TXN_IGNORE_ERRORS(sql->trans)) {
773 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
775 int ret = PQresultStatus(res);
777 if (!dbd_pgsql_is_success(ret)) {
778 sql->trans->errnum = ret;
779 return PGRES_FATAL_ERROR;
782 sql->trans->errnum = ret;
783 return PGRES_FATAL_ERROR;
786 if (statement->prepared) {
787 res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
788 values, len, fmt, 0);
791 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
792 values, len, fmt, 0);
795 ret = PQresultStatus(res);
796 if (dbd_pgsql_is_success(ret)) {
804 ret = PGRES_FATAL_ERROR;
807 if (TXN_IGNORE_ERRORS(sql->trans)) {
808 PGresult *res = PQexec(sql->conn,
809 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
811 int ret = PQresultStatus(res);
813 if (!dbd_pgsql_is_success(ret)) {
814 sql->trans->errnum = ret;
815 return PGRES_FATAL_ERROR;
818 sql->trans->errnum = ret;
819 return PGRES_FATAL_ERROR;
821 } else if (TXN_NOTICE_ERRORS(sql->trans)){
822 sql->trans->errnum = ret;
826 if (TXN_IGNORE_ERRORS(sql->trans)) {
827 PGresult *res = PQexec(sql->conn,
828 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
830 int ret = PQresultStatus(res);
832 if (!dbd_pgsql_is_success(ret)) {
833 sql->trans->errnum = ret;
834 return PGRES_FATAL_ERROR;
837 sql->trans->errnum = ret;
838 return PGRES_FATAL_ERROR;
843 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
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);
854 if (TXN_IGNORE_ERRORS(sql->trans)) {
855 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
857 int ret = PQresultStatus(res);
859 if (!dbd_pgsql_is_success(ret)) {
860 sql->trans->errnum = ret;
861 return PGRES_FATAL_ERROR;
864 sql->trans->errnum = ret;
865 return PGRES_FATAL_ERROR;
868 if (statement->prepared) {
869 rv = PQsendQueryPrepared(sql->conn, statement->name,
870 statement->nargs, values, len, fmt, 0);
873 rv = PQsendQueryParams(sql->conn, statement->name,
874 statement->nargs, 0, values, len, fmt, 0);
877 if (TXN_IGNORE_ERRORS(sql->trans)) {
878 PGresult *res = PQexec(sql->conn,
879 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
881 int ret = PQresultStatus(res);
883 if (!dbd_pgsql_is_success(ret)) {
884 sql->trans->errnum = ret;
885 return PGRES_FATAL_ERROR;
888 sql->trans->errnum = ret;
889 return PGRES_FATAL_ERROR;
891 } else if (TXN_NOTICE_ERRORS(sql->trans)){
892 sql->trans->errnum = 1;
896 if (TXN_IGNORE_ERRORS(sql->trans)) {
897 PGresult *res = PQexec(sql->conn,
898 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
900 int ret = PQresultStatus(res);
902 if (!dbd_pgsql_is_success(ret)) {
903 sql->trans->errnum = ret;
904 return PGRES_FATAL_ERROR;
907 sql->trans->errnum = ret;
908 return PGRES_FATAL_ERROR;
913 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
915 (*results)->random = seek;
916 (*results)->handle = sql->conn;
917 (*results)->pool = pool;
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)
931 if (sql->trans && sql->trans->errnum) {
932 return sql->trans->errnum;
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);
939 dbd_pgsql_bind(statement, values, val, len, fmt);
941 return dbd_pgsql_pselect_internal(pool, sql, results, statement,
942 seek, val, len, fmt);
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)
953 if (sql->trans && sql->trans->errnum) {
954 return sql->trans->errnum;
957 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
959 for (i = 0; i < statement->nvals; i++) {
960 values[i] = va_arg(args, const char*);
963 return dbd_pgsql_pselect(pool, sql, results, statement, seek, values);
966 static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
968 const char **val, int *len, int *fmt)
973 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
974 type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
977 case APR_DBD_TYPE_TINY:
978 val[i] = apr_itoa(pool, *(char*)values[j]);
980 case APR_DBD_TYPE_UTINY:
981 val[i] = apr_itoa(pool, *(unsigned char*)values[j]);
983 case APR_DBD_TYPE_SHORT:
984 val[i] = apr_itoa(pool, *(short*)values[j]);
986 case APR_DBD_TYPE_USHORT:
987 val[i] = apr_itoa(pool, *(unsigned short*)values[j]);
989 case APR_DBD_TYPE_INT:
990 val[i] = apr_itoa(pool, *(int*)values[j]);
992 case APR_DBD_TYPE_UINT:
993 val[i] = apr_itoa(pool, *(unsigned int*)values[j]);
995 case APR_DBD_TYPE_LONG:
996 val[i] = apr_ltoa(pool, *(long*)values[j]);
998 case APR_DBD_TYPE_ULONG:
999 val[i] = apr_ltoa(pool, *(unsigned long*)values[j]);
1001 case APR_DBD_TYPE_LONGLONG:
1002 val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
1003 *(apr_int64_t*)values[j]);
1005 case APR_DBD_TYPE_ULONGLONG:
1006 val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
1007 *(apr_uint64_t*)values[j]);
1009 case APR_DBD_TYPE_FLOAT:
1010 val[i] = apr_psprintf(pool, "%f", *(float*)values[j]);
1012 case APR_DBD_TYPE_DOUBLE:
1013 val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]);
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:
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];
1030 /* skip table and column */
1033 case APR_DBD_TYPE_NULL:
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)
1050 if (sql->trans && sql->trans->errnum) {
1051 return sql->trans->errnum;
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);
1058 dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1060 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
1064 static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
1065 int *nrows, apr_dbd_prepared_t * statement,
1068 const void **values;
1071 if (sql->trans && sql->trans->errnum) {
1072 return sql->trans->errnum;
1075 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1077 for (i = 0; i < statement->nvals; i++) {
1078 values[i] = va_arg(args, const void*);
1081 return dbd_pgsql_pbquery(pool, sql, nrows, statement, values);
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)
1092 if (sql->trans && sql->trans->errnum) {
1093 return sql->trans->errnum;
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);
1100 dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
1102 return dbd_pgsql_pselect_internal(pool, sql, results, statement,
1103 seek, val, len, fmt);
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,
1111 const void **values;
1114 if (sql->trans && sql->trans->errnum) {
1115 return sql->trans->errnum;
1118 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1120 for (i = 0; i < statement->nvals; i++) {
1121 values[i] = va_arg(args, const void*);
1124 return dbd_pgsql_pbselect(pool, sql, results, statement, seek, values);
1127 static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1128 apr_dbd_transaction_t **trans)
1133 /* XXX handle recursive transactions here */
1135 res = PQexec(handle->conn, "BEGIN TRANSACTION");
1137 ret = PQresultStatus(res);
1138 if (dbd_pgsql_is_success(ret)) {
1141 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1145 (*trans)->handle = handle;
1146 handle->trans = *trans;
1149 ret = PGRES_FATAL_ERROR;
1154 static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
1157 int ret = -1; /* no transaction is an error cond */
1159 /* rollback on error or explicit rollback request */
1160 if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1162 res = PQexec(trans->handle->conn, "ROLLBACK");
1165 res = PQexec(trans->handle->conn, "COMMIT");
1168 ret = PQresultStatus(res);
1169 if (dbd_pgsql_is_success(ret)) {
1175 ret = PGRES_FATAL_ERROR;
1177 trans->handle->trans = NULL;
1182 static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
1185 return APR_DBD_TRANSACTION_COMMIT;
1190 static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
1194 return APR_DBD_TRANSACTION_COMMIT;
1196 return trans->mode = (mode & TXN_MODE_BITS);
1199 static void null_notice_receiver(void *arg, const PGresult *res)
1204 static void null_notice_processor(void *arg, const char *message)
1209 static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params,
1214 PGconn *conn = PQconnectdb(params);
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) {
1222 *error = apr_pstrdup(pool, PQerrorMessage(conn));
1228 PQsetNoticeReceiver(conn, null_notice_receiver, NULL);
1229 PQsetNoticeProcessor(conn, null_notice_processor, NULL);
1231 sql = apr_pcalloc (pool, sizeof (*sql));
1238 static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
1240 PQfinish(handle->conn);
1244 static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
1247 if (PQstatus(handle->conn) != CONNECTION_OK) {
1248 PQreset(handle->conn);
1249 if (PQstatus(handle->conn) != CONNECTION_OK) {
1250 return APR_EGENERAL;
1256 static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
1259 return APR_ENOTIMPL;
1262 static void *dbd_pgsql_native(apr_dbd_t *handle)
1264 return handle->conn;
1267 static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
1272 static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
1275 return res->ntuples;
1282 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
1287 dbd_pgsql_check_conn,
1289 dbd_pgsql_select_db,
1290 dbd_pgsql_start_transaction,
1291 dbd_pgsql_end_transaction,
1295 dbd_pgsql_num_tuples,
1297 dbd_pgsql_get_entry,
1306 dbd_pgsql_transaction_mode_get,
1307 dbd_pgsql_transaction_mode_set,
1310 dbd_pgsql_pvbselect,