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.
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"
35 #include "apr_dbd_internal.h"
37 #define QUERY_MAX_ARGS 40
39 struct apr_dbd_transaction_t
{
46 apr_dbd_transaction_t
*trans
;
49 struct apr_dbd_results_t
{
58 struct apr_dbd_row_t
{
60 apr_dbd_results_t
*res
;
63 struct apr_dbd_prepared_t
{
69 #define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
70 || ((x) == PGRES_COMMAND_OK) \
71 || ((x) == PGRES_TUPLES_OK))
73 static apr_status_t
clear_result(void *data
)
79 static int dbd_pgsql_select(apr_pool_t
*pool
, apr_dbd_t
*sql
,
80 apr_dbd_results_t
**results
,
81 const char *query
, int seek
)
85 if ( sql
->trans
&& sql
->trans
->errnum
) {
86 return sql
->trans
->errnum
;
88 if (seek
) { /* synchronous query */
89 res
= PQexec(sql
->conn
, query
);
91 ret
= PQresultStatus(res
);
92 if (dbd_pgsql_is_success(ret
)) {
98 ret
= PGRES_FATAL_ERROR
;
102 sql
->trans
->errnum
= ret
;
107 *results
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
109 (*results
)->res
= res
;
110 (*results
)->ntuples
= PQntuples(res
);
111 (*results
)->sz
= PQnfields(res
);
112 (*results
)->random
= seek
;
113 apr_pool_cleanup_register(pool
, res
, clear_result
,
114 apr_pool_cleanup_null
);
117 if (PQsendQuery(sql
->conn
, query
) == 0) {
119 sql
->trans
->errnum
= 1;
123 if (*results
== NULL
) {
124 *results
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
126 (*results
)->random
= seek
;
127 (*results
)->handle
= sql
->conn
;
132 static const char *dbd_pgsql_get_name(const apr_dbd_results_t
*res
, int n
)
134 return (res
->res
? PQfname(res
->res
, n
) : NULL
);
137 static int dbd_pgsql_get_row(apr_pool_t
*pool
, apr_dbd_results_t
*res
,
138 apr_dbd_row_t
**rowp
, int rownum
)
140 apr_dbd_row_t
*row
= *rowp
;
141 int sequential
= ((rownum
>= 0) && res
->random
) ? 0 : 1;
144 row
= apr_palloc(pool
, sizeof(apr_dbd_row_t
));
147 row
->n
= sequential
? 0 : rownum
;
159 if (row
->n
>= res
->ntuples
) {
161 apr_pool_cleanup_run(pool
, res
->res
, clear_result
);
167 if (row
->n
>= res
->ntuples
) {
168 /* no data; we have to fetch some */
169 row
->n
-= res
->ntuples
;
170 if (res
->res
!= NULL
) {
173 res
->res
= PQgetResult(res
->handle
);
175 res
->ntuples
= PQntuples(res
->res
);
176 while (res
->ntuples
== 0) {
177 /* if we got an empty result, clear it, wait a mo, try
180 apr_sleep(100000); /* 0.1 secs */
181 res
->res
= PQgetResult(res
->handle
);
183 res
->ntuples
= PQntuples(res
->res
);
190 res
->sz
= PQnfields(res
->res
);
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
)
215 if (sql
->trans
&& sql
->trans
->errnum
) {
216 return sql
->trans
->errnum
;
218 res
= PQexec(sql
->conn
, query
);
220 ret
= PQresultStatus(res
);
221 if (dbd_pgsql_is_success(ret
)) {
222 /* ugh, making 0 return-success doesn't fit */
225 *nrows
= atoi(PQcmdTuples(res
));
229 ret
= PGRES_FATAL_ERROR
;
232 sql
->trans
->errnum
= ret
;
237 static const char *dbd_pgsql_escape(apr_pool_t
*pool
, const char *arg
,
240 size_t len
= strlen(arg
);
241 char *ret
= apr_palloc(pool
, 2*len
+ 2);
242 PQescapeString(ret
, arg
, len
);
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
)
254 const char *args
[QUERY_MAX_ARGS
];
262 *statement
= apr_palloc(pool
, sizeof(apr_dbd_prepared_t
));
264 (*statement
)->nargs
= 0;
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 ++(*statement
)->nargs
;
271 else if (sqlptr
[1] == '%') {
276 length
= strlen(query
) + 1;
277 if ((*statement
)->nargs
> 8) {
278 length
+= (*statement
)->nargs
- 8;
280 pgptr
= pgquery
= apr_palloc(pool
, length
) ;
282 for (sqlptr
= (char*)query
; *sqlptr
; ++sqlptr
) {
283 if ((sqlptr
[0] == '%') && isalpha(sqlptr
[1])) {
289 *pgptr
++ = '0' + ((i
+1)/10);
290 *pgptr
++ = '0' + ((i
+1)%10);
303 length
+= 1 + strlen(args
[i
]);
306 else if ((sqlptr
[0] == '%') && (sqlptr
[1] == '%')) {
308 *pgptr
++ = *sqlptr
++;
317 /* don't really prepare; use in execParams instead */
318 (*statement
)->prepared
= 0;
319 (*statement
)->name
= apr_pstrdup(pool
, pgquery
);
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
);
328 memcpy(sqlptr
, "PREPARE ", 8);
330 length
= strlen(label
);
331 memcpy(sqlptr
, label
, length
);
333 if ((*statement
)->nargs
> 0) {
334 memcpy(sqlptr
, " (",2);
336 for (i
=0; i
< (*statement
)->nargs
; ++i
) {
337 alen
= strlen(args
[i
]);
338 memcpy(sqlptr
, args
[i
], alen
);
344 memcpy(sqlptr
, " AS ", 4);
346 memcpy(sqlptr
, pgquery
, strlen(pgquery
));
347 sqlptr
+= strlen(pgquery
);
350 res
= PQexec(sql
->conn
, sqlcmd
);
352 ret
= PQresultStatus(res
);
353 if (dbd_pgsql_is_success(ret
)) {
356 /* Hmmm, do we do this here or register it on the pool? */
360 ret
= PGRES_FATAL_ERROR
;
362 (*statement
)->prepared
= 1;
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
)
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,
383 res
= PQexecParams(sql
->conn
, statement
->name
, nargs
, 0, values
, 0, 0,
387 ret
= PQresultStatus(res
);
388 if (dbd_pgsql_is_success(ret
)) {
391 *nrows
= atoi(PQcmdTuples(res
));
395 ret
= PGRES_FATAL_ERROR
;
399 sql
->trans
->errnum
= ret
;
404 static int dbd_pgsql_pvquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
405 int *nrows
, apr_dbd_prepared_t
*statement
,
411 if (sql
->trans
&& sql
->trans
->errnum
) {
412 return sql
->trans
->errnum
;
415 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nargs
);
417 for (i
= 0; i
< statement
->nargs
; i
++) {
418 values
[i
] = apr_pstrdup(pool
, va_arg(args
, const char*));
421 return dbd_pgsql_pquery(pool
, sql
, nrows
, statement
,
422 statement
->nargs
, values
);
425 static int dbd_pgsql_pselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
426 apr_dbd_results_t
**results
,
427 apr_dbd_prepared_t
*statement
,
428 int seek
, int nargs
, const char **values
)
434 if (sql
->trans
&& sql
->trans
->errnum
) {
435 return sql
->trans
->errnum
;
438 if (seek
) { /* synchronous query */
439 if (statement
->prepared
) {
440 res
= PQexecPrepared(sql
->conn
, statement
->name
, nargs
, values
, 0,
444 res
= PQexecParams(sql
->conn
, statement
->name
, nargs
, 0, values
, 0,
448 ret
= PQresultStatus(res
);
449 if (dbd_pgsql_is_success(ret
)) {
457 ret
= PGRES_FATAL_ERROR
;
461 sql
->trans
->errnum
= ret
;
466 *results
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
468 (*results
)->res
= res
;
469 (*results
)->ntuples
= PQntuples(res
);
470 (*results
)->sz
= PQnfields(res
);
471 (*results
)->random
= seek
;
472 apr_pool_cleanup_register(pool
, res
, clear_result
,
473 apr_pool_cleanup_null
);
476 if (statement
->prepared
) {
477 rv
= PQsendQueryPrepared(sql
->conn
, statement
->name
, nargs
, values
,
481 rv
= PQsendQueryParams(sql
->conn
, statement
->name
, nargs
, 0,
486 sql
->trans
->errnum
= 1;
491 *results
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
493 (*results
)->random
= seek
;
494 (*results
)->handle
= sql
->conn
;
498 sql
->trans
->errnum
= ret
;
503 static int dbd_pgsql_pvselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
504 apr_dbd_results_t
**results
,
505 apr_dbd_prepared_t
*statement
,
506 int seek
, va_list args
)
511 if (sql
->trans
&& sql
->trans
->errnum
) {
512 return sql
->trans
->errnum
;
515 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nargs
);
517 for (i
= 0; i
< statement
->nargs
; i
++) {
518 values
[i
] = apr_pstrdup(pool
, va_arg(args
, const char*));
521 return dbd_pgsql_pselect(pool
, sql
, results
, statement
,
522 seek
, statement
->nargs
, values
) ;
525 static int dbd_pgsql_start_transaction(apr_pool_t
*pool
, apr_dbd_t
*handle
,
526 apr_dbd_transaction_t
**trans
)
531 /* XXX handle recursive transactions here */
533 res
= PQexec(handle
->conn
, "BEGIN TRANSACTION");
535 ret
= PQresultStatus(res
);
536 if (dbd_pgsql_is_success(ret
)) {
539 *trans
= apr_pcalloc(pool
, sizeof(apr_dbd_transaction_t
));
543 (*trans
)->handle
= handle
;
544 handle
->trans
= *trans
;
547 ret
= PGRES_FATAL_ERROR
;
552 static int dbd_pgsql_end_transaction(apr_dbd_transaction_t
*trans
)
555 int ret
= -1; /* no transaction is an error cond */
559 res
= PQexec(trans
->handle
->conn
, "ROLLBACK");
562 res
= PQexec(trans
->handle
->conn
, "COMMIT");
565 ret
= PQresultStatus(res
);
566 if (dbd_pgsql_is_success(ret
)) {
572 ret
= PGRES_FATAL_ERROR
;
574 trans
->handle
->trans
= NULL
;
579 static apr_dbd_t
*dbd_pgsql_open(apr_pool_t
*pool
, const char *params
)
583 PGconn
*conn
= PQconnectdb(params
);
585 /* if there's an error in the connect string or something we get
586 * back a * bogus connection object, and things like PQreset are
587 * liable to segfault, so just close it out now. it would be nice
588 * if we could give an indication of why we failed to connect... */
589 if (PQstatus(conn
) != CONNECTION_OK
) {
594 sql
= apr_pcalloc (pool
, sizeof (*sql
));
601 static apr_status_t
dbd_pgsql_close(apr_dbd_t
*handle
)
603 PQfinish(handle
->conn
);
607 static apr_status_t
dbd_pgsql_check_conn(apr_pool_t
*pool
,
610 if (PQstatus(handle
->conn
) != CONNECTION_OK
) {
611 PQreset(handle
->conn
);
612 if (PQstatus(handle
->conn
) != CONNECTION_OK
) {
619 static int dbd_pgsql_select_db(apr_pool_t
*pool
, apr_dbd_t
*handle
,
625 static void *dbd_pgsql_native(apr_dbd_t
*handle
)
630 static int dbd_pgsql_num_cols(apr_dbd_results_t
* res
)
635 static int dbd_pgsql_num_tuples(apr_dbd_results_t
* res
)
645 APU_DECLARE_DATA
const apr_dbd_driver_t apr_dbd_pgsql_driver
= {
650 dbd_pgsql_check_conn
,
653 dbd_pgsql_start_transaction
,
654 dbd_pgsql_end_transaction
,
658 dbd_pgsql_num_tuples
,