1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
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.
26
#include "apr_strings.h"
29
#include "apr_dbd_internal.h"
31
#define QUERY_MAX_ARGS 40
33
struct apr_dbd_transaction_t {
40
apr_dbd_transaction_t *trans;
43
struct apr_dbd_results_t {
52
struct apr_dbd_row_t {
54
apr_dbd_results_t *res;
57
struct apr_dbd_prepared_t {
62
#define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
63
|| ((x) == PGRES_COMMAND_OK) \
64
|| ((x) == PGRES_TUPLES_OK))
66
static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
67
apr_dbd_results_t **results,
68
const char *query, int seek)
72
if ( sql->trans && sql->trans->errnum ) {
73
return sql->trans->errnum;
75
if (seek) { /* synchronous query */
76
res = PQexec(sql->conn, query);
78
ret = PQresultStatus(res);
79
if (dbd_pgsql_is_success(ret)) {
85
ret = PGRES_FATAL_ERROR;
89
sql->trans->errnum = ret;
94
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
96
(*results)->res = res;
97
(*results)->ntuples = PQntuples(res);
98
(*results)->sz = PQnfields(res);
99
(*results)->random = seek;
100
apr_pool_cleanup_register(pool, res, (void*)PQclear,
101
apr_pool_cleanup_null);
104
if (PQsendQuery(sql->conn, query) == 0) {
106
sql->trans->errnum = 1;
110
if (*results == NULL) {
111
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
113
(*results)->random = seek;
114
(*results)->handle = sql->conn;
119
static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
120
apr_dbd_row_t **rowp, int rownum)
122
apr_dbd_row_t *row = *rowp;
123
int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
126
row = apr_palloc(pool, sizeof(apr_dbd_row_t));
129
row->n = sequential ? 0 : rownum;
141
if (row->n >= res->ntuples) {
143
apr_pool_cleanup_kill(pool, res->res, (void*)PQclear);
150
if (row->n >= res->ntuples) {
151
/* no data; we have to fetch some */
152
row->n -= res->ntuples;
153
if (res->res != NULL) {
156
res->res = PQgetResult(res->handle);
158
res->ntuples = PQntuples(res->res);
159
while (res->ntuples == 0) {
160
/* if we got an empty result, clear it, wait a mo, try
163
apr_sleep(100000); /* 0.1 secs */
164
res->res = PQgetResult(res->handle);
166
res->ntuples = PQntuples(res->res);
173
res->sz = PQnfields(res->res);
184
static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
186
return PQgetvalue(row->res->res, row->n, n);
189
static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
191
return PQerrorMessage(sql->conn);
194
static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
198
if (sql->trans && sql->trans->errnum) {
199
return sql->trans->errnum;
201
res = PQexec(sql->conn, query);
203
ret = PQresultStatus(res);
204
if (dbd_pgsql_is_success(ret)) {
205
/* ugh, making 0 return-success doesn't fit */
208
*nrows = atoi(PQcmdTuples(res));
212
ret = PGRES_FATAL_ERROR;
215
sql->trans->errnum = ret;
220
static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
223
size_t len = strlen(arg);
224
char *ret = apr_palloc(pool, len + 1);
225
PQescapeString(ret, arg, len);
229
static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
230
const char *query, const char *label,
231
apr_dbd_prepared_t **statement)
237
const char *args[QUERY_MAX_ARGS];
246
*statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
248
/* Translate from apr_dbd to native query format */
249
for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
250
if (sqlptr[0] == '%') {
251
if (isalpha(sqlptr[1])) {
254
else if (sqlptr[1] == '%') {
259
length = strlen(query) + 1;
263
pgptr = pgquery = apr_palloc(pool, length) ;
265
for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
266
if ((sqlptr[0] == '%') && isalpha(sqlptr[1])) {
272
*pgptr++ = '0' + ((i+1)/10);
273
*pgptr++ = '0' + ((i+1)%10);
286
length += 1 + strlen(args[i]);
289
else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
291
*pgptr++ = *sqlptr++;
300
/* don't really prepare; use in execParams instead */
301
(*statement)->prepared = 0;
302
(*statement)->name = apr_pstrdup(pool, pgquery);
305
(*statement)->name = apr_pstrdup(pool, label);
307
/* length of SQL query that prepares this statement */
308
length = 8 + strlen(label) + 2 + 4 + length + 1;
309
sqlcmd = apr_palloc(pool, length);
311
memcpy(sqlptr, "PREPARE ", 8);
313
length = strlen(label);
314
memcpy(sqlptr, label, length);
317
memcpy(sqlptr, " (",2);
319
for (i=0; i<nargs; ++i) {
320
alen = strlen(args[i]);
321
memcpy(sqlptr, args[i], alen);
327
memcpy(sqlptr, " AS ", 4);
329
memcpy(sqlptr, pgquery, strlen(pgquery));
330
sqlptr += strlen(pgquery);
333
res = PQexec(sql->conn, sqlcmd);
335
ret = PQresultStatus(res);
336
if (dbd_pgsql_is_success(ret)) {
339
/* Hmmm, do we do this here or register it on the pool? */
343
ret = PGRES_FATAL_ERROR;
345
(*statement)->prepared = 1;
350
static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
351
int *nrows, apr_dbd_prepared_t *statement,
352
int nargs, const char **values)
356
if (statement->prepared) {
357
res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0, 0,
361
res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0, 0,
365
ret = PQresultStatus(res);
366
if (dbd_pgsql_is_success(ret)) {
372
ret = PGRES_FATAL_ERROR;
376
sql->trans->errnum = ret;
381
static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
382
int *nrows, apr_dbd_prepared_t *statement,
387
const char *values[QUERY_MAX_ARGS];
389
if (sql->trans && sql->trans->errnum) {
390
return sql->trans->errnum;
392
while ( arg = va_arg(args, const char*), arg ) {
393
if ( nargs >= QUERY_MAX_ARGS) {
397
values[nargs++] = apr_pstrdup(pool, arg);
399
values[nargs] = NULL;
400
return dbd_pgsql_pquery(pool, sql, nrows, statement, nargs, values);
403
static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
404
apr_dbd_results_t **results,
405
apr_dbd_prepared_t *statement,
406
int seek, int nargs, const char **values)
411
if (seek) { /* synchronous query */
412
if (statement->prepared) {
413
res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0,
417
res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0,
421
ret = PQresultStatus(res);
422
if (dbd_pgsql_is_success(ret)) {
430
ret = PGRES_FATAL_ERROR;
434
sql->trans->errnum = ret;
439
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
441
(*results)->res = res;
442
(*results)->ntuples = PQntuples(res);
443
(*results)->sz = PQnfields(res);
444
(*results)->random = seek;
445
apr_pool_cleanup_register(pool, res, (void*)PQclear,
446
apr_pool_cleanup_null);
449
if (statement->prepared) {
450
rv = PQsendQueryPrepared(sql->conn, statement->name, nargs, values,
454
rv = PQsendQueryParams(sql->conn, statement->name, nargs, 0,
459
sql->trans->errnum = 1;
464
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
466
(*results)->random = seek;
467
(*results)->handle = sql->conn;
471
sql->trans->errnum = ret;
476
static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
477
apr_dbd_results_t **results,
478
apr_dbd_prepared_t *statement,
479
int seek, va_list args)
483
const char *values[QUERY_MAX_ARGS];
485
if (sql->trans && sql->trans->errnum) {
486
return sql->trans->errnum;
489
while (arg = va_arg(args, const char*), arg) {
490
if ( nargs >= QUERY_MAX_ARGS) {
494
values[nargs++] = apr_pstrdup(pool, arg);
496
return dbd_pgsql_pselect(pool, sql, results, statement,
497
seek, nargs, values) ;
500
static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
501
apr_dbd_transaction_t **trans)
506
/* XXX handle recursive transactions here */
508
res = PQexec(handle->conn, "BEGIN TRANSACTION");
510
ret = PQresultStatus(res);
511
if (dbd_pgsql_is_success(ret)) {
514
*trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
518
(*trans)->handle = handle;
519
handle->trans = *trans;
522
ret = PGRES_FATAL_ERROR;
527
static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
530
int ret = -1; /* no transaction is an error cond */
534
res = PQexec(trans->handle->conn, "ROLLBACK");
537
res = PQexec(trans->handle->conn, "COMMIT");
540
ret = PQresultStatus(res);
541
if (dbd_pgsql_is_success(ret)) {
547
ret = PGRES_FATAL_ERROR;
549
trans->handle->trans = NULL;
554
static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
558
PGconn *conn = PQconnectdb(params);
560
/* if there's an error in the connect string or something we get
561
* back a * bogus connection object, and things like PQreset are
562
* liable to segfault, so just close it out now. it would be nice
563
* if we could give an indication of why we failed to connect... */
564
if (PQstatus(conn) != CONNECTION_OK) {
569
sql = apr_pcalloc (pool, sizeof (*sql));
576
static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
578
PQfinish(handle->conn);
582
static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
585
if (PQstatus(handle->conn) != CONNECTION_OK) {
586
PQreset(handle->conn);
587
if (PQstatus(handle->conn) != CONNECTION_OK) {
594
static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
600
static void *dbd_pgsql_native(apr_dbd_t *handle)
605
static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
610
static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
620
APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
625
dbd_pgsql_check_conn,
628
dbd_pgsql_start_transaction,
629
dbd_pgsql_end_transaction,
633
dbd_pgsql_num_tuples,