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
));
234 row
->n
= sequential
? 0 : rownum
;
246 if (row
->n
>= res
->ntuples
) {
248 apr_pool_cleanup_run(pool
, res
->res
, clear_result
);
254 if (row
->n
>= res
->ntuples
) {
255 /* no data; we have to fetch some */
256 row
->n
-= res
->ntuples
;
257 if (res
->res
!= NULL
) {
260 res
->res
= PQgetResult(res
->handle
);
262 res
->ntuples
= PQntuples(res
->res
);
263 while (res
->ntuples
== 0) {
264 /* if we got an empty result, clear it, wait a mo, try
267 apr_sleep(100000); /* 0.1 secs */
268 res
->res
= PQgetResult(res
->handle
);
270 res
->ntuples
= PQntuples(res
->res
);
277 res
->sz
= PQnfields(res
->res
);
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
)) {
301 case APR_DBD_TYPE_TINY
:
302 *(char*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
304 case APR_DBD_TYPE_UTINY
:
305 *(unsigned char*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
307 case APR_DBD_TYPE_SHORT
:
308 *(short*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
310 case APR_DBD_TYPE_USHORT
:
311 *(unsigned short*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
313 case APR_DBD_TYPE_INT
:
314 *(int*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
316 case APR_DBD_TYPE_UINT
:
317 *(unsigned int*)data
= atoi(PQgetvalue(row
->res
->res
, row
->n
, n
));
319 case APR_DBD_TYPE_LONG
:
320 *(long*)data
= atol(PQgetvalue(row
->res
->res
, row
->n
, n
));
322 case APR_DBD_TYPE_ULONG
:
323 *(unsigned long*)data
= atol(PQgetvalue(row
->res
->res
, row
->n
, n
));
325 case APR_DBD_TYPE_LONGLONG
:
326 *(apr_int64_t
*)data
= apr_atoi64(PQgetvalue(row
->res
->res
, row
->n
, n
));
328 case APR_DBD_TYPE_ULONGLONG
:
329 *(apr_uint64_t
*)data
= apr_atoi64(PQgetvalue(row
->res
->res
, row
->n
, n
));
331 case APR_DBD_TYPE_FLOAT
:
332 *(float*)data
= atof(PQgetvalue(row
->res
->res
, row
->n
, n
));
334 case APR_DBD_TYPE_DOUBLE
:
335 *(double*)data
= atof(PQgetvalue(row
->res
->res
, row
->n
, n
));
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
);
346 case APR_DBD_TYPE_BLOB
:
347 case APR_DBD_TYPE_CLOB
:
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
);
358 case APR_DBD_TYPE_NULL
:
359 *(void**)data
= NULL
;
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
)
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");
384 ret
= PQresultStatus(res
);
386 if (!dbd_pgsql_is_success(ret
)) {
387 sql
->trans
->errnum
= ret
;
388 return PGRES_FATAL_ERROR
;
391 return sql
->trans
->errnum
= PGRES_FATAL_ERROR
;
395 res
= PQexec(sql
->conn
, query
);
397 ret
= PQresultStatus(res
);
398 if (dbd_pgsql_is_success(ret
)) {
399 /* ugh, making 0 return-success doesn't fit */
402 *nrows
= atoi(PQcmdTuples(res
));
406 ret
= PGRES_FATAL_ERROR
;
410 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
411 PGresult
*res
= PQexec(sql
->conn
,
412 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
414 ret
= PQresultStatus(res
);
416 if (!dbd_pgsql_is_success(ret
)) {
417 sql
->trans
->errnum
= ret
;
418 return PGRES_FATAL_ERROR
;
421 sql
->trans
->errnum
= ret
;
422 return PGRES_FATAL_ERROR
;
424 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
425 sql
->trans
->errnum
= ret
;
428 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
429 PGresult
*res
= PQexec(sql
->conn
,
430 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
432 ret
= PQresultStatus(res
);
434 if (!dbd_pgsql_is_success(ret
)) {
435 sql
->trans
->errnum
= ret
;
436 return PGRES_FATAL_ERROR
;
439 sql
->trans
->errnum
= ret
;
440 return PGRES_FATAL_ERROR
;
448 static const char *dbd_pgsql_escape(apr_pool_t
*pool
, const char *arg
,
451 size_t len
= strlen(arg
);
452 char *ret
= apr_palloc(pool
, 2*len
+ 2);
453 PQescapeString(ret
, arg
, len
);
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
)
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
);
483 for (i
= 0; i
< nargs
; 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";
491 case APR_DBD_TYPE_INT
:
492 case APR_DBD_TYPE_UINT
:
495 case APR_DBD_TYPE_LONG
:
496 case APR_DBD_TYPE_ULONG
:
497 case APR_DBD_TYPE_LONGLONG
:
498 case APR_DBD_TYPE_ULONGLONG
:
501 case APR_DBD_TYPE_FLOAT
:
504 case APR_DBD_TYPE_DOUBLE
:
505 args
[i
] = "double precision";
507 case APR_DBD_TYPE_TEXT
:
510 case APR_DBD_TYPE_TIME
:
513 case APR_DBD_TYPE_DATE
:
516 case APR_DBD_TYPE_DATETIME
:
517 case APR_DBD_TYPE_TIMESTAMP
:
518 args
[i
] = "timestamp";
520 case APR_DBD_TYPE_ZTIMESTAMP
:
521 args
[i
] = "timestamp with time zone";
523 case APR_DBD_TYPE_BLOB
:
524 case APR_DBD_TYPE_CLOB
:
527 case APR_DBD_TYPE_NULL
:
528 args
[i
] = "varchar"; /* XXX Eh? */
534 length
+= 1 + strlen(args
[i
]);
538 /* don't really prepare; use in execParams instead */
539 (*statement
)->prepared
= 0;
540 (*statement
)->name
= apr_pstrdup(pool
, query
);
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
);
549 memcpy(sqlptr
, "PREPARE ", 8);
551 length
= strlen(label
);
552 memcpy(sqlptr
, label
, length
);
555 memcpy(sqlptr
, " (",2);
557 for (i
=0; i
< nargs
; ++i
) {
558 alen
= strlen(args
[i
]);
559 memcpy(sqlptr
, args
[i
], alen
);
565 memcpy(sqlptr
, " AS ", 4);
567 memcpy(sqlptr
, query
, qlen
);
571 res
= PQexec(sql
->conn
, sqlcmd
);
573 ret
= PQresultStatus(res
);
574 if (dbd_pgsql_is_success(ret
)) {
577 /* Hmmm, do we do this here or register it on the pool? */
581 ret
= PGRES_FATAL_ERROR
;
583 (*statement
)->prepared
= 1;
588 static int dbd_pgsql_pquery_internal(apr_pool_t
*pool
, apr_dbd_t
*sql
,
589 int *nrows
, apr_dbd_prepared_t
*statement
,
591 const int *len
, const int *fmt
)
596 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
597 PGresult
*res
= PQexec(sql
->conn
, "SAVEPOINT APR_DBD_TXN_SP");
599 ret
= PQresultStatus(res
);
601 if (!dbd_pgsql_is_success(ret
)) {
602 sql
->trans
->errnum
= ret
;
603 return PGRES_FATAL_ERROR
;
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);
615 res
= PQexecParams(sql
->conn
, statement
->name
, statement
->nargs
, 0,
616 values
, len
, fmt
, 0);
619 ret
= PQresultStatus(res
);
620 if (dbd_pgsql_is_success(ret
)) {
623 *nrows
= atoi(PQcmdTuples(res
));
627 ret
= PGRES_FATAL_ERROR
;
631 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
632 PGresult
*res
= PQexec(sql
->conn
,
633 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
635 ret
= PQresultStatus(res
);
637 if (!dbd_pgsql_is_success(ret
)) {
638 sql
->trans
->errnum
= ret
;
639 return PGRES_FATAL_ERROR
;
642 sql
->trans
->errnum
= ret
;
643 return PGRES_FATAL_ERROR
;
645 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
646 sql
->trans
->errnum
= ret
;
649 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
650 PGresult
*res
= PQexec(sql
->conn
,
651 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
653 ret
= PQresultStatus(res
);
655 if (!dbd_pgsql_is_success(ret
)) {
656 sql
->trans
->errnum
= ret
;
657 return PGRES_FATAL_ERROR
;
660 sql
->trans
->errnum
= ret
;
661 return PGRES_FATAL_ERROR
;
669 static void dbd_pgsql_bind(apr_dbd_prepared_t
*statement
,
671 const char **val
, int *len
, int *fmt
)
675 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
676 if (values
[j
] == NULL
) {
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
]);
687 /* skip table and column */
700 static int dbd_pgsql_pquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
701 int *nrows
, apr_dbd_prepared_t
*statement
,
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
,
721 static int dbd_pgsql_pvquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
722 int *nrows
, apr_dbd_prepared_t
*statement
,
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
)
751 if (seek
) { /* synchronous query */
752 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
753 PGresult
*res
= PQexec(sql
->conn
, "SAVEPOINT APR_DBD_TXN_SP");
755 ret
= PQresultStatus(res
);
757 if (!dbd_pgsql_is_success(ret
)) {
758 sql
->trans
->errnum
= ret
;
759 return PGRES_FATAL_ERROR
;
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);
771 res
= PQexecParams(sql
->conn
, statement
->name
, statement
->nargs
, 0,
772 values
, len
, fmt
, 0);
775 ret
= PQresultStatus(res
);
776 if (dbd_pgsql_is_success(ret
)) {
784 ret
= PGRES_FATAL_ERROR
;
787 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
788 PGresult
*res
= PQexec(sql
->conn
,
789 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
791 ret
= PQresultStatus(res
);
793 if (!dbd_pgsql_is_success(ret
)) {
794 sql
->trans
->errnum
= ret
;
795 return PGRES_FATAL_ERROR
;
798 sql
->trans
->errnum
= ret
;
799 return PGRES_FATAL_ERROR
;
801 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
802 sql
->trans
->errnum
= ret
;
806 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
807 PGresult
*res
= PQexec(sql
->conn
,
808 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
810 ret
= PQresultStatus(res
);
812 if (!dbd_pgsql_is_success(ret
)) {
813 sql
->trans
->errnum
= ret
;
814 return PGRES_FATAL_ERROR
;
817 sql
->trans
->errnum
= ret
;
818 return PGRES_FATAL_ERROR
;
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
);
834 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
835 PGresult
*res
= PQexec(sql
->conn
, "SAVEPOINT APR_DBD_TXN_SP");
837 ret
= PQresultStatus(res
);
839 if (!dbd_pgsql_is_success(ret
)) {
840 sql
->trans
->errnum
= ret
;
841 return PGRES_FATAL_ERROR
;
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);
853 rv
= PQsendQueryParams(sql
->conn
, statement
->name
,
854 statement
->nargs
, 0, values
, len
, fmt
, 0);
857 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
858 PGresult
*res
= PQexec(sql
->conn
,
859 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
861 ret
= PQresultStatus(res
);
863 if (!dbd_pgsql_is_success(ret
)) {
864 sql
->trans
->errnum
= ret
;
865 return PGRES_FATAL_ERROR
;
868 sql
->trans
->errnum
= ret
;
869 return PGRES_FATAL_ERROR
;
871 } else if (TXN_NOTICE_ERRORS(sql
->trans
)){
872 sql
->trans
->errnum
= 1;
876 if (TXN_IGNORE_ERRORS(sql
->trans
)) {
877 PGresult
*res
= PQexec(sql
->conn
,
878 "RELEASE SAVEPOINT APR_DBD_TXN_SP");
880 ret
= PQresultStatus(res
);
882 if (!dbd_pgsql_is_success(ret
)) {
883 sql
->trans
->errnum
= ret
;
884 return PGRES_FATAL_ERROR
;
887 sql
->trans
->errnum
= ret
;
888 return PGRES_FATAL_ERROR
;
893 *results
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
895 (*results
)->random
= seek
;
896 (*results
)->handle
= sql
->conn
;
897 (*results
)->pool
= pool
;
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
)
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
)
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
,
948 const char **val
, int *len
, int *fmt
)
953 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
954 type
= (values
[j
] == NULL
? APR_DBD_TYPE_NULL
: statement
->types
[i
]);
957 case APR_DBD_TYPE_TINY
:
958 val
[i
] = apr_itoa(pool
, *(char*)values
[j
]);
960 case APR_DBD_TYPE_UTINY
:
961 val
[i
] = apr_itoa(pool
, *(unsigned char*)values
[j
]);
963 case APR_DBD_TYPE_SHORT
:
964 val
[i
] = apr_itoa(pool
, *(short*)values
[j
]);
966 case APR_DBD_TYPE_USHORT
:
967 val
[i
] = apr_itoa(pool
, *(unsigned short*)values
[j
]);
969 case APR_DBD_TYPE_INT
:
970 val
[i
] = apr_itoa(pool
, *(int*)values
[j
]);
972 case APR_DBD_TYPE_UINT
:
973 val
[i
] = apr_itoa(pool
, *(unsigned int*)values
[j
]);
975 case APR_DBD_TYPE_LONG
:
976 val
[i
] = apr_ltoa(pool
, *(long*)values
[j
]);
978 case APR_DBD_TYPE_ULONG
:
979 val
[i
] = apr_ltoa(pool
, *(unsigned long*)values
[j
]);
981 case APR_DBD_TYPE_LONGLONG
:
982 val
[i
] = apr_psprintf(pool
, "%" APR_INT64_T_FMT
,
983 *(apr_int64_t
*)values
[j
]);
985 case APR_DBD_TYPE_ULONGLONG
:
986 val
[i
] = apr_psprintf(pool
, "%" APR_UINT64_T_FMT
,
987 *(apr_uint64_t
*)values
[j
]);
989 case APR_DBD_TYPE_FLOAT
:
990 val
[i
] = apr_psprintf(pool
, "%f", *(float*)values
[j
]);
992 case APR_DBD_TYPE_DOUBLE
:
993 val
[i
] = apr_psprintf(pool
, "%lf", *(double*)values
[j
]);
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
:
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
];
1010 /* skip table and column */
1013 case APR_DBD_TYPE_NULL
:
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
)
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
,
1044 static int dbd_pgsql_pvbquery(apr_pool_t
* pool
, apr_dbd_t
* sql
,
1045 int *nrows
, apr_dbd_prepared_t
* statement
,
1048 const void **values
;
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
)
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
,
1091 const void **values
;
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
)
1113 /* XXX handle recursive transactions here */
1115 res
= PQexec(handle
->conn
, "BEGIN TRANSACTION");
1117 ret
= PQresultStatus(res
);
1118 if (dbd_pgsql_is_success(ret
)) {
1121 *trans
= apr_pcalloc(pool
, sizeof(apr_dbd_transaction_t
));
1125 (*trans
)->handle
= handle
;
1126 handle
->trans
= *trans
;
1129 ret
= PGRES_FATAL_ERROR
;
1134 static int dbd_pgsql_end_transaction(apr_dbd_transaction_t
*trans
)
1137 int ret
= -1; /* no transaction is an error cond */
1139 /* rollback on error or explicit rollback request */
1140 if (trans
->errnum
|| TXN_DO_ROLLBACK(trans
)) {
1142 res
= PQexec(trans
->handle
->conn
, "ROLLBACK");
1145 res
= PQexec(trans
->handle
->conn
, "COMMIT");
1148 ret
= PQresultStatus(res
);
1149 if (dbd_pgsql_is_success(ret
)) {
1155 ret
= PGRES_FATAL_ERROR
;
1157 trans
->handle
->trans
= NULL
;
1162 static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t
*trans
)
1165 return APR_DBD_TRANSACTION_COMMIT
;
1170 static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t
*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
,
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
) {
1192 *error
= apr_pstrdup(pool
, PQerrorMessage(conn
));
1198 sql
= apr_pcalloc (pool
, sizeof (*sql
));
1205 static apr_status_t
dbd_pgsql_close(apr_dbd_t
*handle
)
1207 PQfinish(handle
->conn
);
1211 static apr_status_t
dbd_pgsql_check_conn(apr_pool_t
*pool
,
1214 if (PQstatus(handle
->conn
) != CONNECTION_OK
) {
1215 PQreset(handle
->conn
);
1216 if (PQstatus(handle
->conn
) != CONNECTION_OK
) {
1217 return APR_EGENERAL
;
1223 static int dbd_pgsql_select_db(apr_pool_t
*pool
, apr_dbd_t
*handle
,
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
)
1239 static int dbd_pgsql_num_tuples(apr_dbd_results_t
* res
)
1242 return res
->ntuples
;
1249 APU_DECLARE_DATA
const apr_dbd_driver_t apr_dbd_pgsql_driver
= {
1254 dbd_pgsql_check_conn
,
1256 dbd_pgsql_select_db
,
1257 dbd_pgsql_start_transaction
,
1258 dbd_pgsql_end_transaction
,
1262 dbd_pgsql_num_tuples
,
1264 dbd_pgsql_get_entry
,
1273 dbd_pgsql_transaction_mode_get
,
1274 dbd_pgsql_transaction_mode_set
,
1277 dbd_pgsql_pvbselect
,