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 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 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 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 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 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 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
)
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;
231 row
= apr_palloc(pool
, sizeof(apr_dbd_row_t
));
242 return -1; /* invalid row */
255 return -1; /* invalid row */
261 if ((row
->n
>= 0) && (size_t)row
->n
>= res
->ntuples
) {
263 apr_pool_cleanup_run(pool
, res
->res
, clear_result
);
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
) {
275 res
->res
= PQgetResult(res
->handle
);
277 res
->ntuples
= PQntuples(res
->res
);
278 while (res
->ntuples
== 0) {
279 /* if we got an empty result, clear it, wait a mo, try
282 apr_sleep(100000); /* 0.1 secs */
283 res
->res
= PQgetResult(res
->handle
);
285 res
->ntuples
= PQntuples(res
->res
);
292 res
->sz
= PQnfields(res
->res
);
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
)) {
316 case APR_DBD_TYPE_TINY
:
317 *(char*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
319 case APR_DBD_TYPE_UTINY
:
320 *(unsigned char*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
322 case APR_DBD_TYPE_SHORT
:
323 *(short*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
325 case APR_DBD_TYPE_USHORT
:
326 *(unsigned short*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
328 case APR_DBD_TYPE_INT
:
329 *(int*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
331 case APR_DBD_TYPE_UINT
:
332 *(unsigned int*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
334 case APR_DBD_TYPE_LONG
:
335 *(long*)data
= atol(PQgetvalue(row
->res
->res
, row
->n
, n
));
337 case APR_DBD_TYPE_ULONG
:
338 *(unsigned long*)data
= atol(PQgetvalue(row
->res
->res
, row
->n
, n
));
340 case APR_DBD_TYPE_LONGLONG
:
341 *(apr_int64_t
*)data
= apr_atoi64(PQgetvalue(row
->res
->res
, row
->n
, n
));
343 case APR_DBD_TYPE_ULONGLONG
:
344 *(apr_uint64_t
*)data
= apr_atoi64(PQgetvalue(row
->res
->res
, row
->n
, n
));
346 case APR_DBD_TYPE_FLOAT
:
347 *(float*)data
= (float)atof(PQgetvalue(row
->res
->res
, row
->n
, n
));
349 case APR_DBD_TYPE_DOUBLE
:
350 *(double*)data
= atof(PQgetvalue(row
->res
->res
, row
->n
, n
));
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
);
361 case APR_DBD_TYPE_BLOB
:
362 case APR_DBD_TYPE_CLOB
:
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
);
373 case APR_DBD_TYPE_NULL
:
374 *(void**)data
= NULL
;
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
)
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");
399 ret
= PQresultStatus(res
);
401 if (!dbd_pgsql_is_success(ret
)) {
402 sql
->trans
->errnum
= ret
;
403 return PGRES_FATAL_ERROR
;
406 return sql
->trans
->errnum
= PGRES_FATAL_ERROR
;
410 res
= PQexec(sql
->conn
, query
);
412 ret
= PQresultStatus(res
);
413 if (dbd_pgsql_is_success(ret
)) {
414 /* ugh, making 0 return-success doesn't fit */
417 *nrows
= atoi(PQcmdTuples(res
));
421 ret
= PGRES_FATAL_ERROR
;
425 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
426 PGresult
*res
= PQexec(sql
->conn
,
427 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
429 ret
= PQresultStatus(res
);
431 if (!dbd_pgsql_is_success(ret
)) {
432 sql
->trans
->errnum
= ret
;
433 return PGRES_FATAL_ERROR
;
436 sql
->trans
->errnum
= ret
;
437 return PGRES_FATAL_ERROR
;
439 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
440 sql
->trans
->errnum
= ret
;
443 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
444 PGresult
*res
= PQexec(sql
->conn
,
445 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
447 ret
= PQresultStatus(res
);
449 if (!dbd_pgsql_is_success(ret
)) {
450 sql
->trans
->errnum
= ret
;
451 return PGRES_FATAL_ERROR
;
454 sql
->trans
->errnum
= ret
;
455 return PGRES_FATAL_ERROR
;
463 static const char *dbd_pgsql_escape(apr_pool_t
*pool
, const char *arg
,
466 size_t len
= strlen(arg
);
467 char *ret
= apr_palloc(pool
, 2*len
+ 2);
468 PQescapeString(ret
, arg
, len
);
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
)
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
);
498 for (i
= 0; i
< nargs
; 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";
506 case APR_DBD_TYPE_INT
:
507 case APR_DBD_TYPE_UINT
:
510 case APR_DBD_TYPE_LONG
:
511 case APR_DBD_TYPE_ULONG
:
512 case APR_DBD_TYPE_LONGLONG
:
513 case APR_DBD_TYPE_ULONGLONG
:
516 case APR_DBD_TYPE_FLOAT
:
519 case APR_DBD_TYPE_DOUBLE
:
520 args
[i
] = "double precision";
522 case APR_DBD_TYPE_TEXT
:
525 case APR_DBD_TYPE_TIME
:
528 case APR_DBD_TYPE_DATE
:
531 case APR_DBD_TYPE_DATETIME
:
532 case APR_DBD_TYPE_TIMESTAMP
:
533 args
[i
] = "timestamp";
535 case APR_DBD_TYPE_ZTIMESTAMP
:
536 args
[i
] = "timestamp with time zone";
538 case APR_DBD_TYPE_BLOB
:
539 case APR_DBD_TYPE_CLOB
:
542 case APR_DBD_TYPE_NULL
:
543 args
[i
] = "varchar"; /* XXX Eh? */
549 length
+= 1 + strlen(args
[i
]);
553 /* don't really prepare; use in execParams instead */
554 (*statement
)->prepared
= 0;
555 (*statement
)->name
= apr_pstrdup(pool
, query
);
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
);
564 memcpy(sqlptr
, "PREPARE ", 8);
566 length
= strlen(label
);
567 memcpy(sqlptr
, label
, length
);
570 memcpy(sqlptr
, " (",2);
572 for (i
=0; i
< nargs
; ++i
) {
573 alen
= strlen(args
[i
]);
574 memcpy(sqlptr
, args
[i
], alen
);
580 memcpy(sqlptr
, " AS ", 4);
582 memcpy(sqlptr
, query
, qlen
);
586 res
= PQexec(sql
->conn
, sqlcmd
);
588 ret
= PQresultStatus(res
);
589 if (dbd_pgsql_is_success(ret
)) {
592 /* Hmmm, do we do this here or register it on the pool? */
596 ret
= PGRES_FATAL_ERROR
;
598 (*statement
)->prepared
= 1;
603 static int dbd_pgsql_pquery_internal(apr_pool_t
*pool
, apr_dbd_t
*sql
,
604 int *nrows
, apr_dbd_prepared_t
*statement
,
606 const int *len
, const int *fmt
)
611 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
612 PGresult
*res
= PQexec(sql
->conn
, "SAVEPOINT APR_DBD_TXN_SP");
614 ret
= PQresultStatus(res
);
616 if (!dbd_pgsql_is_success(ret
)) {
617 sql
->trans
->errnum
= ret
;
618 return PGRES_FATAL_ERROR
;
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);
630 res
= PQexecParams(sql
->conn
, statement
->name
, statement
->nargs
, 0,
631 values
, len
, fmt
, 0);
634 ret
= PQresultStatus(res
);
635 if (dbd_pgsql_is_success(ret
)) {
638 *nrows
= atoi(PQcmdTuples(res
));
642 ret
= PGRES_FATAL_ERROR
;
646 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
647 PGresult
*res
= PQexec(sql
->conn
,
648 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
650 ret
= PQresultStatus(res
);
652 if (!dbd_pgsql_is_success(ret
)) {
653 sql
->trans
->errnum
= ret
;
654 return PGRES_FATAL_ERROR
;
657 sql
->trans
->errnum
= ret
;
658 return PGRES_FATAL_ERROR
;
660 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
661 sql
->trans
->errnum
= ret
;
664 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
665 PGresult
*res
= PQexec(sql
->conn
,
666 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
668 ret
= PQresultStatus(res
);
670 if (!dbd_pgsql_is_success(ret
)) {
671 sql
->trans
->errnum
= ret
;
672 return PGRES_FATAL_ERROR
;
675 sql
->trans
->errnum
= ret
;
676 return PGRES_FATAL_ERROR
;
684 static void dbd_pgsql_bind(apr_dbd_prepared_t
*statement
,
686 const char **val
, int *len
, int *fmt
)
690 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
691 if (values
[j
] == NULL
) {
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
]);
702 /* skip table and column */
715 static int dbd_pgsql_pquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
716 int *nrows
, apr_dbd_prepared_t
*statement
,
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
,
736 static int dbd_pgsql_pvquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
737 int *nrows
, apr_dbd_prepared_t
*statement
,
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
)
766 if (seek
) { /* synchronous query */
767 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
768 PGresult
*res
= PQexec(sql
->conn
, "SAVEPOINT APR_DBD_TXN_SP");
770 ret
= PQresultStatus(res
);
772 if (!dbd_pgsql_is_success(ret
)) {
773 sql
->trans
->errnum
= ret
;
774 return PGRES_FATAL_ERROR
;
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);
786 res
= PQexecParams(sql
->conn
, statement
->name
, statement
->nargs
, 0,
787 values
, len
, fmt
, 0);
790 ret
= PQresultStatus(res
);
791 if (dbd_pgsql_is_success(ret
)) {
799 ret
= PGRES_FATAL_ERROR
;
802 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
803 PGresult
*res
= PQexec(sql
->conn
,
804 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
806 ret
= PQresultStatus(res
);
808 if (!dbd_pgsql_is_success(ret
)) {
809 sql
->trans
->errnum
= ret
;
810 return PGRES_FATAL_ERROR
;
813 sql
->trans
->errnum
= ret
;
814 return PGRES_FATAL_ERROR
;
816 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
817 sql
->trans
->errnum
= ret
;
821 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
822 PGresult
*res
= PQexec(sql
->conn
,
823 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
825 ret
= PQresultStatus(res
);
827 if (!dbd_pgsql_is_success(ret
)) {
828 sql
->trans
->errnum
= ret
;
829 return PGRES_FATAL_ERROR
;
832 sql
->trans
->errnum
= ret
;
833 return PGRES_FATAL_ERROR
;
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
);
849 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
850 PGresult
*res
= PQexec(sql
->conn
, "SAVEPOINT APR_DBD_TXN_SP");
852 ret
= PQresultStatus(res
);
854 if (!dbd_pgsql_is_success(ret
)) {
855 sql
->trans
->errnum
= ret
;
856 return PGRES_FATAL_ERROR
;
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);
868 rv
= PQsendQueryParams(sql
->conn
, statement
->name
,
869 statement
->nargs
, 0, values
, len
, fmt
, 0);
872 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
873 PGresult
*res
= PQexec(sql
->conn
,
874 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
876 ret
= PQresultStatus(res
);
878 if (!dbd_pgsql_is_success(ret
)) {
879 sql
->trans
->errnum
= ret
;
880 return PGRES_FATAL_ERROR
;
883 sql
->trans
->errnum
= ret
;
884 return PGRES_FATAL_ERROR
;
886 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
887 sql
->trans
->errnum
= 1;
891 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
892 PGresult
*res
= PQexec(sql
->conn
,
893 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
895 ret
= PQresultStatus(res
);
897 if (!dbd_pgsql_is_success(ret
)) {
898 sql
->trans
->errnum
= ret
;
899 return PGRES_FATAL_ERROR
;
902 sql
->trans
->errnum
= ret
;
903 return PGRES_FATAL_ERROR
;
908 *results
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
910 (*results
)->random
= seek
;
911 (*results
)->handle
= sql
->conn
;
912 (*results
)->pool
= pool
;
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
)
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
)
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
,
963 const char **val
, int *len
, int *fmt
)
968 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
969 type
= (values
[j
] == NULL
? APR_DBD_TYPE_NULL
: statement
->types
[i
]);
972 case APR_DBD_TYPE_TINY
:
973 val
[i
] = apr_itoa(pool
, *(char*)values
[j
]);
975 case APR_DBD_TYPE_UTINY
:
976 val
[i
] = apr_itoa(pool
, *(unsigned char*)values
[j
]);
978 case APR_DBD_TYPE_SHORT
:
979 val
[i
] = apr_itoa(pool
, *(short*)values
[j
]);
981 case APR_DBD_TYPE_USHORT
:
982 val
[i
] = apr_itoa(pool
, *(unsigned short*)values
[j
]);
984 case APR_DBD_TYPE_INT
:
985 val
[i
] = apr_itoa(pool
, *(int*)values
[j
]);
987 case APR_DBD_TYPE_UINT
:
988 val
[i
] = apr_itoa(pool
, *(unsigned int*)values
[j
]);
990 case APR_DBD_TYPE_LONG
:
991 val
[i
] = apr_ltoa(pool
, *(long*)values
[j
]);
993 case APR_DBD_TYPE_ULONG
:
994 val
[i
] = apr_ltoa(pool
, *(unsigned long*)values
[j
]);
996 case APR_DBD_TYPE_LONGLONG
:
997 val
[i
] = apr_psprintf(pool
, "%" APR_INT64_T_FMT
,
998 *(apr_int64_t
*)values
[j
]);
1000 case APR_DBD_TYPE_ULONGLONG
:
1001 val
[i
] = apr_psprintf(pool
, "%" APR_UINT64_T_FMT
,
1002 *(apr_uint64_t
*)values
[j
]);
1004 case APR_DBD_TYPE_FLOAT
:
1005 val
[i
] = apr_psprintf(pool
, "%f", *(float*)values
[j
]);
1007 case APR_DBD_TYPE_DOUBLE
:
1008 val
[i
] = apr_psprintf(pool
, "%lf", *(double*)values
[j
]);
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
:
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
];
1025 /* skip table and column */
1028 case APR_DBD_TYPE_NULL
:
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
)
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
,
1059 static int dbd_pgsql_pvbquery(apr_pool_t
* pool
, apr_dbd_t
* sql
,
1060 int *nrows
, apr_dbd_prepared_t
* statement
,
1063 const void **values
;
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
)
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
,
1106 const void **values
;
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
)
1128 /* XXX handle recursive transactions here */
1130 res
= PQexec(handle
->conn
, "BEGIN TRANSACTION");
1132 ret
= PQresultStatus(res
);
1133 if (dbd_pgsql_is_success(ret
)) {
1136 *trans
= apr_pcalloc(pool
, sizeof(apr_dbd_transaction_t
));
1140 (*trans
)->handle
= handle
;
1141 handle
->trans
= *trans
;
1144 ret
= PGRES_FATAL_ERROR
;
1149 static int dbd_pgsql_end_transaction(apr_dbd_transaction_t
*trans
)
1152 int ret
= -1; /* no transaction is an error cond */
1154 /* rollback on error or explicit rollback request */
1155 if (trans
->errnum
|| TXN_DO_ROLLBACK(trans
)) {
1157 res
= PQexec(trans
->handle
->conn
, "ROLLBACK");
1160 res
= PQexec(trans
->handle
->conn
, "COMMIT");
1163 ret
= PQresultStatus(res
);
1164 if (dbd_pgsql_is_success(ret
)) {
1170 ret
= PGRES_FATAL_ERROR
;
1172 trans
->handle
->trans
= NULL
;
1177 static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t
*trans
)
1180 return APR_DBD_TRANSACTION_COMMIT
;
1185 static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t
*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
)
1199 static void null_notice_processor(void *arg
, const char *message
)
1204 static apr_dbd_t
*dbd_pgsql_open(apr_pool_t
*pool
, const char *params
,
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
) {
1217 *error
= apr_pstrdup(pool
, PQerrorMessage(conn
));
1223 PQsetNoticeReceiver(conn
, null_notice_receiver
, NULL
);
1224 PQsetNoticeProcessor(conn
, null_notice_processor
, NULL
);
1226 sql
= apr_pcalloc (pool
, sizeof (*sql
));
1233 static apr_status_t
dbd_pgsql_close(apr_dbd_t
*handle
)
1235 PQfinish(handle
->conn
);
1239 static apr_status_t
dbd_pgsql_check_conn(apr_pool_t
*pool
,
1242 if (PQstatus(handle
->conn
) != CONNECTION_OK
) {
1243 PQreset(handle
->conn
);
1244 if (PQstatus(handle
->conn
) != CONNECTION_OK
) {
1245 return APR_EGENERAL
;
1251 static int dbd_pgsql_select_db(apr_pool_t
*pool
, apr_dbd_t
*handle
,
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
)
1267 static int dbd_pgsql_num_tuples(apr_dbd_results_t
* res
)
1270 return res
->ntuples
;
1277 APU_MODULE_DECLARE_DATA
const apr_dbd_driver_t apr_dbd_pgsql_driver
= {
1282 dbd_pgsql_check_conn
,
1284 dbd_pgsql_select_db
,
1285 dbd_pgsql_start_transaction
,
1286 dbd_pgsql_end_transaction
,
1290 dbd_pgsql_num_tuples
,
1292 dbd_pgsql_get_entry
,
1301 dbd_pgsql_transaction_mode_get
,
1302 dbd_pgsql_transaction_mode_set
,
1305 dbd_pgsql_pvbselect
,