Don't cast cleanup functions, provide wrappers instead (PgSQL, SQLite3).
[apr-util.git] / dbd / apr_dbd_pgsql.c
blob49261fbc6e7f1abffbec984c32a84aee1fb66f86
1 /* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
2 * applicable.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * 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"
35 #include "apr_dbd_internal.h"
37 #define QUERY_MAX_ARGS 40
39 struct apr_dbd_transaction_t {
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;
58 struct apr_dbd_row_t {
59 int n;
60 apr_dbd_results_t *res;
63 struct apr_dbd_prepared_t {
64 const char *name;
65 int prepared;
68 #define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
69 || ((x) == PGRES_COMMAND_OK) \
70 || ((x) == PGRES_TUPLES_OK))
72 static apr_status_t clear_result(void *data)
74 PQclear(data);
75 return APR_SUCCESS;
78 static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
79 apr_dbd_results_t **results,
80 const char *query, int seek)
82 PGresult *res;
83 int ret;
84 if ( sql->trans && sql->trans->errnum ) {
85 return sql->trans->errnum;
87 if (seek) { /* synchronous query */
88 res = PQexec(sql->conn, query);
89 if (res) {
90 ret = PQresultStatus(res);
91 if (dbd_pgsql_is_success(ret)) {
92 ret = 0;
93 } else {
94 PQclear(res);
96 } else {
97 ret = PGRES_FATAL_ERROR;
99 if (ret != 0) {
100 if (sql->trans) {
101 sql->trans->errnum = ret;
103 return ret;
105 if (!*results) {
106 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
108 (*results)->res = res;
109 (*results)->ntuples = PQntuples(res);
110 (*results)->sz = PQnfields(res);
111 (*results)->random = seek;
112 apr_pool_cleanup_register(pool, res, clear_result,
113 apr_pool_cleanup_null);
115 else {
116 if (PQsendQuery(sql->conn, query) == 0) {
117 if (sql->trans) {
118 sql->trans->errnum = 1;
120 return 1;
122 if (*results == NULL) {
123 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
125 (*results)->random = seek;
126 (*results)->handle = sql->conn;
128 return 0;
131 static const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n)
133 return (res->res ? PQfname(res->res, n) : NULL);
136 static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
137 apr_dbd_row_t **rowp, int rownum)
139 apr_dbd_row_t *row = *rowp;
140 int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
142 if (row == NULL) {
143 row = apr_palloc(pool, sizeof(apr_dbd_row_t));
144 *rowp = row;
145 row->res = res;
146 row->n = sequential ? 0 : rownum;
148 else {
149 if ( sequential ) {
150 ++row->n;
152 else {
153 row->n = rownum;
157 if (res->random) {
158 if (row->n >= res->ntuples) {
159 *rowp = NULL;
160 apr_pool_cleanup_kill(pool, res->res, (void*)PQclear);
161 PQclear(res->res);
162 res->res = NULL;
163 return -1;
166 else {
167 if (row->n >= res->ntuples) {
168 /* no data; we have to fetch some */
169 row->n -= res->ntuples;
170 if (res->res != NULL) {
171 PQclear(res->res);
173 res->res = PQgetResult(res->handle);
174 if (res->res) {
175 res->ntuples = PQntuples(res->res);
176 while (res->ntuples == 0) {
177 /* if we got an empty result, clear it, wait a mo, try
178 * again */
179 PQclear(res->res);
180 apr_sleep(100000); /* 0.1 secs */
181 res->res = PQgetResult(res->handle);
182 if (res->res) {
183 res->ntuples = PQntuples(res->res);
185 else {
186 return -1;
189 if (res->sz == 0) {
190 res->sz = PQnfields(res->res);
193 else {
194 return -1;
198 return 0;
201 static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
203 return PQgetvalue(row->res->res, row->n, n);
206 static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
208 return PQerrorMessage(sql->conn);
211 static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
213 PGresult *res;
214 int ret;
215 if (sql->trans && sql->trans->errnum) {
216 return sql->trans->errnum;
218 res = PQexec(sql->conn, query);
219 if (res) {
220 ret = PQresultStatus(res);
221 if (dbd_pgsql_is_success(ret)) {
222 /* ugh, making 0 return-success doesn't fit */
223 ret = 0;
225 *nrows = atoi(PQcmdTuples(res));
226 PQclear(res);
228 else {
229 ret = PGRES_FATAL_ERROR;
231 if (sql->trans) {
232 sql->trans->errnum = ret;
234 return ret;
237 static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
238 apr_dbd_t *sql)
240 size_t len = strlen(arg);
241 char *ret = apr_palloc(pool, 2*len + 2);
242 PQescapeString(ret, arg, len);
243 return ret;
246 static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
247 const char *query, const char *label,
248 apr_dbd_prepared_t **statement)
250 char *sqlcmd;
251 char *sqlptr;
252 size_t length;
253 size_t i = 0;
254 const char *args[QUERY_MAX_ARGS];
255 size_t alen;
256 int nargs = 0;
257 int ret;
258 PGresult *res;
259 char *pgquery;
260 char *pgptr;
262 if (!*statement) {
263 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
265 /* Translate from apr_dbd to native query format */
266 for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
267 if (sqlptr[0] == '%') {
268 if (isalpha(sqlptr[1])) {
269 ++nargs;
271 else if (sqlptr[1] == '%') {
272 ++sqlptr;
276 length = strlen(query) + 1;
277 if (nargs > 8) {
278 length += nargs - 8;
280 pgptr = pgquery = apr_palloc(pool, length) ;
282 for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
283 if ((sqlptr[0] == '%') && isalpha(sqlptr[1])) {
284 *pgptr++ = '$';
285 if (i < 9) {
286 *pgptr++ = '1' + i;
288 else {
289 *pgptr++ = '0' + ((i+1)/10);
290 *pgptr++ = '0' + ((i+1)%10);
292 switch (*++sqlptr) {
293 case 'd':
294 args[i] = "integer";
295 break;
296 case 's':
297 args[i] = "varchar";
298 break;
299 default:
300 args[i] = "varchar";
301 break;
303 length += 1 + strlen(args[i]);
304 ++i;
306 else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
307 /* reduce %% to % */
308 *pgptr++ = *sqlptr++;
310 else {
311 *pgptr++ = *sqlptr;
314 *pgptr = 0;
316 if (!label) {
317 /* don't really prepare; use in execParams instead */
318 (*statement)->prepared = 0;
319 (*statement)->name = apr_pstrdup(pool, pgquery);
320 return 0;
322 (*statement)->name = apr_pstrdup(pool, label);
324 /* length of SQL query that prepares this statement */
325 length = 8 + strlen(label) + 2 + 4 + length + 1;
326 sqlcmd = apr_palloc(pool, length);
327 sqlptr = sqlcmd;
328 memcpy(sqlptr, "PREPARE ", 8);
329 sqlptr += 8;
330 length = strlen(label);
331 memcpy(sqlptr, label, length);
332 sqlptr += length;
333 if (nargs > 0) {
334 memcpy(sqlptr, " (",2);
335 sqlptr += 2;
336 for (i=0; i<nargs; ++i) {
337 alen = strlen(args[i]);
338 memcpy(sqlptr, args[i], alen);
339 sqlptr += alen;
340 *sqlptr++ = ',';
342 sqlptr[-1] = ')';
344 memcpy(sqlptr, " AS ", 4);
345 sqlptr += 4;
346 memcpy(sqlptr, pgquery, strlen(pgquery));
347 sqlptr += strlen(pgquery);
348 *sqlptr = 0;
350 res = PQexec(sql->conn, sqlcmd);
351 if ( res ) {
352 ret = PQresultStatus(res);
353 if (dbd_pgsql_is_success(ret)) {
354 ret = 0;
356 /* Hmmm, do we do this here or register it on the pool? */
357 PQclear(res);
359 else {
360 ret = PGRES_FATAL_ERROR;
362 (*statement)->prepared = 1;
364 return ret;
367 static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
368 int *nrows, apr_dbd_prepared_t *statement,
369 int nargs, const char **values)
371 int ret;
372 PGresult *res;
374 if (sql->trans && sql->trans->errnum) {
375 return sql->trans->errnum;
378 if (statement->prepared) {
379 res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0, 0,
382 else {
383 res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0, 0,
386 if (res) {
387 ret = PQresultStatus(res);
388 if (dbd_pgsql_is_success(ret)) {
389 ret = 0;
391 *nrows = atoi(PQcmdTuples(res));
392 PQclear(res);
394 else {
395 ret = PGRES_FATAL_ERROR;
398 if (sql->trans) {
399 sql->trans->errnum = ret;
401 return ret;
404 static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
405 int *nrows, apr_dbd_prepared_t *statement,
406 va_list args)
408 const char *arg;
409 int nargs = 0;
410 const char *values[QUERY_MAX_ARGS];
412 if (sql->trans && sql->trans->errnum) {
413 return sql->trans->errnum;
415 while ( arg = va_arg(args, const char*), arg ) {
416 if ( nargs >= QUERY_MAX_ARGS) {
417 va_end(args);
418 return -1;
420 values[nargs++] = apr_pstrdup(pool, arg);
422 values[nargs] = NULL;
423 return dbd_pgsql_pquery(pool, sql, nrows, statement, nargs, values);
426 static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
427 apr_dbd_results_t **results,
428 apr_dbd_prepared_t *statement,
429 int seek, int nargs, const char **values)
431 PGresult *res;
432 int rv;
433 int ret = 0;
435 if (sql->trans && sql->trans->errnum) {
436 return sql->trans->errnum;
439 if (seek) { /* synchronous query */
440 if (statement->prepared) {
441 res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0,
442 0, 0);
444 else {
445 res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0,
446 0, 0);
448 if (res) {
449 ret = PQresultStatus(res);
450 if (dbd_pgsql_is_success(ret)) {
451 ret = 0;
453 else {
454 PQclear(res);
457 else {
458 ret = PGRES_FATAL_ERROR;
460 if (ret != 0) {
461 if (sql->trans) {
462 sql->trans->errnum = ret;
464 return ret;
466 if (!*results) {
467 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
469 (*results)->res = res;
470 (*results)->ntuples = PQntuples(res);
471 (*results)->sz = PQnfields(res);
472 (*results)->random = seek;
473 apr_pool_cleanup_register(pool, res, clear_result,
474 apr_pool_cleanup_null);
476 else {
477 if (statement->prepared) {
478 rv = PQsendQueryPrepared(sql->conn, statement->name, nargs, values,
479 0, 0, 0);
481 else {
482 rv = PQsendQueryParams(sql->conn, statement->name, nargs, 0,
483 values, 0, 0, 0);
485 if (rv == 0) {
486 if (sql->trans) {
487 sql->trans->errnum = 1;
489 return 1;
491 if (!*results) {
492 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
494 (*results)->random = seek;
495 (*results)->handle = sql->conn;
498 if (sql->trans) {
499 sql->trans->errnum = ret;
501 return ret;
504 static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
505 apr_dbd_results_t **results,
506 apr_dbd_prepared_t *statement,
507 int seek, va_list args)
509 const char *arg;
510 int nargs = 0;
511 const char *values[QUERY_MAX_ARGS];
513 if (sql->trans && sql->trans->errnum) {
514 return sql->trans->errnum;
517 while (arg = va_arg(args, const char*), arg) {
518 if ( nargs >= QUERY_MAX_ARGS) {
519 va_end(args);
520 return -1;
522 values[nargs++] = apr_pstrdup(pool, arg);
524 return dbd_pgsql_pselect(pool, sql, results, statement,
525 seek, nargs, values) ;
528 static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
529 apr_dbd_transaction_t **trans)
531 int ret = 0;
532 PGresult *res;
534 /* XXX handle recursive transactions here */
536 res = PQexec(handle->conn, "BEGIN TRANSACTION");
537 if (res) {
538 ret = PQresultStatus(res);
539 if (dbd_pgsql_is_success(ret)) {
540 ret = 0;
541 if (!*trans) {
542 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
545 PQclear(res);
546 (*trans)->handle = handle;
547 handle->trans = *trans;
549 else {
550 ret = PGRES_FATAL_ERROR;
552 return ret;
555 static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
557 PGresult *res;
558 int ret = -1; /* no transaction is an error cond */
559 if (trans) {
560 if (trans->errnum) {
561 trans->errnum = 0;
562 res = PQexec(trans->handle->conn, "ROLLBACK");
564 else {
565 res = PQexec(trans->handle->conn, "COMMIT");
567 if (res) {
568 ret = PQresultStatus(res);
569 if (dbd_pgsql_is_success(ret)) {
570 ret = 0;
572 PQclear(res);
574 else {
575 ret = PGRES_FATAL_ERROR;
577 trans->handle->trans = NULL;
579 return ret;
582 static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
584 apr_dbd_t *sql;
586 PGconn *conn = PQconnectdb(params);
588 /* if there's an error in the connect string or something we get
589 * back a * bogus connection object, and things like PQreset are
590 * liable to segfault, so just close it out now. it would be nice
591 * if we could give an indication of why we failed to connect... */
592 if (PQstatus(conn) != CONNECTION_OK) {
593 PQfinish(conn);
594 return NULL;
597 sql = apr_pcalloc (pool, sizeof (*sql));
599 sql->conn = conn;
601 return sql;
604 static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
606 PQfinish(handle->conn);
607 return APR_SUCCESS;
610 static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
611 apr_dbd_t *handle)
613 if (PQstatus(handle->conn) != CONNECTION_OK) {
614 PQreset(handle->conn);
615 if (PQstatus(handle->conn) != CONNECTION_OK) {
616 return APR_EGENERAL;
619 return APR_SUCCESS;
622 static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
623 const char *name)
625 return APR_ENOTIMPL;
628 static void *dbd_pgsql_native(apr_dbd_t *handle)
630 return handle->conn;
633 static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
635 return res->sz;
638 static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
640 if (res->random) {
641 return res->ntuples;
643 else {
644 return -1;
648 APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
649 "pgsql",
650 NULL,
651 dbd_pgsql_native,
652 dbd_pgsql_open,
653 dbd_pgsql_check_conn,
654 dbd_pgsql_close,
655 dbd_pgsql_select_db,
656 dbd_pgsql_start_transaction,
657 dbd_pgsql_end_transaction,
658 dbd_pgsql_query,
659 dbd_pgsql_select,
660 dbd_pgsql_num_cols,
661 dbd_pgsql_num_tuples,
662 dbd_pgsql_get_row,
663 dbd_pgsql_get_entry,
664 dbd_pgsql_error,
665 dbd_pgsql_escape,
666 dbd_pgsql_prepare,
667 dbd_pgsql_pvquery,
668 dbd_pgsql_pvselect,
669 dbd_pgsql_pquery,
670 dbd_pgsql_pselect,
671 dbd_pgsql_get_name
673 #endif