123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664 |
- /* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
- * applicable.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "apu.h"
- #if APU_HAVE_PGSQL
- #include "apu_config.h"
- #include <ctype.h>
- #include <stdlib.h>
- #ifdef HAVE_LIBPQ_FE_H
- #include <libpq-fe.h>
- #elif defined(HAVE_POSTGRESQL_LIBPQ_FE_H)
- #include <postgresql/libpq-fe.h>
- #endif
- #include "apr_strings.h"
- #include "apr_time.h"
- #include "apr_dbd_internal.h"
- #define QUERY_MAX_ARGS 40
- struct apr_dbd_transaction_t {
- int errnum;
- apr_dbd_t *handle;
- };
- struct apr_dbd_t {
- PGconn *conn;
- apr_dbd_transaction_t *trans;
- };
- struct apr_dbd_results_t {
- int random;
- PGconn *handle;
- PGresult *res;
- size_t ntuples;
- size_t sz;
- size_t index;
- };
- struct apr_dbd_row_t {
- int n;
- apr_dbd_results_t *res;
- };
- struct apr_dbd_prepared_t {
- const char *name;
- int prepared;
- int nargs;
- };
- #define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \
- || ((x) == PGRES_COMMAND_OK) \
- || ((x) == PGRES_TUPLES_OK))
- static apr_status_t clear_result(void *data)
- {
- PQclear(data);
- return APR_SUCCESS;
- }
- static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql,
- apr_dbd_results_t **results,
- const char *query, int seek)
- {
- PGresult *res;
- int ret;
- if ( sql->trans && sql->trans->errnum ) {
- return sql->trans->errnum;
- }
- if (seek) { /* synchronous query */
- res = PQexec(sql->conn, query);
- if (res) {
- ret = PQresultStatus(res);
- if (dbd_pgsql_is_success(ret)) {
- ret = 0;
- } else {
- PQclear(res);
- }
- } else {
- ret = PGRES_FATAL_ERROR;
- }
- if (ret != 0) {
- if (sql->trans) {
- sql->trans->errnum = ret;
- }
- return ret;
- }
- if (!*results) {
- *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
- }
- (*results)->res = res;
- (*results)->ntuples = PQntuples(res);
- (*results)->sz = PQnfields(res);
- (*results)->random = seek;
- apr_pool_cleanup_register(pool, res, clear_result,
- apr_pool_cleanup_null);
- }
- else {
- if (PQsendQuery(sql->conn, query) == 0) {
- if (sql->trans) {
- sql->trans->errnum = 1;
- }
- return 1;
- }
- if (*results == NULL) {
- *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
- }
- (*results)->random = seek;
- (*results)->handle = sql->conn;
- }
- return 0;
- }
- static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
- apr_dbd_row_t **rowp, int rownum)
- {
- apr_dbd_row_t *row = *rowp;
- int sequential = ((rownum >= 0) && res->random) ? 0 : 1;
- if (row == NULL) {
- row = apr_palloc(pool, sizeof(apr_dbd_row_t));
- *rowp = row;
- row->res = res;
- row->n = sequential ? 0 : rownum;
- }
- else {
- if ( sequential ) {
- ++row->n;
- }
- else {
- row->n = rownum;
- }
- }
- if (res->random) {
- if (row->n >= res->ntuples) {
- *rowp = NULL;
- apr_pool_cleanup_run(pool, res->res, clear_result);
- res->res = NULL;
- return -1;
- }
- }
- else {
- if (row->n >= res->ntuples) {
- /* no data; we have to fetch some */
- row->n -= res->ntuples;
- if (res->res != NULL) {
- PQclear(res->res);
- }
- res->res = PQgetResult(res->handle);
- if (res->res) {
- res->ntuples = PQntuples(res->res);
- while (res->ntuples == 0) {
- /* if we got an empty result, clear it, wait a mo, try
- * again */
- PQclear(res->res);
- apr_sleep(100000); /* 0.1 secs */
- res->res = PQgetResult(res->handle);
- if (res->res) {
- res->ntuples = PQntuples(res->res);
- }
- else {
- return -1;
- }
- }
- if (res->sz == 0) {
- res->sz = PQnfields(res->res);
- }
- }
- else {
- return -1;
- }
- }
- }
- return 0;
- }
- static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n)
- {
- return PQgetvalue(row->res->res, row->n, n);
- }
- static const char *dbd_pgsql_error(apr_dbd_t *sql, int n)
- {
- return PQerrorMessage(sql->conn);
- }
- static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query)
- {
- PGresult *res;
- int ret;
- if (sql->trans && sql->trans->errnum) {
- return sql->trans->errnum;
- }
- res = PQexec(sql->conn, query);
- if (res) {
- ret = PQresultStatus(res);
- if (dbd_pgsql_is_success(ret)) {
- /* ugh, making 0 return-success doesn't fit */
- ret = 0;
- }
- *nrows = atoi(PQcmdTuples(res));
- PQclear(res);
- }
- else {
- ret = PGRES_FATAL_ERROR;
- }
- if (sql->trans) {
- sql->trans->errnum = ret;
- }
- return ret;
- }
- static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg,
- apr_dbd_t *sql)
- {
- size_t len = strlen(arg);
- char *ret = apr_palloc(pool, 2*(len + 1));
- PQescapeString(ret, arg, len);
- return ret;
- }
- static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
- const char *query, const char *label,
- apr_dbd_prepared_t **statement)
- {
- char *sqlcmd;
- char *sqlptr;
- size_t length;
- size_t i = 0;
- const char *args[QUERY_MAX_ARGS];
- size_t alen;
- int ret;
- PGresult *res;
- char *pgquery;
- char *pgptr;
- if (!*statement) {
- *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
- }
- (*statement)->nargs = 0;
- /* Translate from apr_dbd to native query format */
- for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
- if (sqlptr[0] == '%') {
- if (isalpha(sqlptr[1])) {
- ++(*statement)->nargs;
- }
- else if (sqlptr[1] == '%') {
- ++sqlptr;
- }
- }
- }
- length = strlen(query) + 1;
- if ((*statement)->nargs > 8) {
- length += (*statement)->nargs - 8;
- }
- pgptr = pgquery = apr_palloc(pool, length) ;
- for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
- if ((sqlptr[0] == '%') && isalpha(sqlptr[1])) {
- *pgptr++ = '$';
- if (i < 9) {
- *pgptr++ = '1' + i;
- }
- else {
- *pgptr++ = '0' + ((i+1)/10);
- *pgptr++ = '0' + ((i+1)%10);
- }
- switch (*++sqlptr) {
- case 'd':
- args[i] = "integer";
- break;
- case 's':
- args[i] = "varchar";
- break;
- default:
- args[i] = "varchar";
- break;
- }
- length += 1 + strlen(args[i]);
- ++i;
- }
- else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
- /* reduce %% to % */
- *pgptr++ = *sqlptr++;
- }
- else {
- *pgptr++ = *sqlptr;
- }
- }
- *pgptr = 0;
- if (!label) {
- /* don't really prepare; use in execParams instead */
- (*statement)->prepared = 0;
- (*statement)->name = apr_pstrdup(pool, pgquery);
- return 0;
- }
- (*statement)->name = apr_pstrdup(pool, label);
- /* length of SQL query that prepares this statement */
- length = 8 + strlen(label) + 2 + 4 + length + 1;
- sqlcmd = apr_palloc(pool, length);
- sqlptr = sqlcmd;
- memcpy(sqlptr, "PREPARE ", 8);
- sqlptr += 8;
- length = strlen(label);
- memcpy(sqlptr, label, length);
- sqlptr += length;
- if ((*statement)->nargs > 0) {
- memcpy(sqlptr, " (",2);
- sqlptr += 2;
- for (i=0; i < (*statement)->nargs; ++i) {
- alen = strlen(args[i]);
- memcpy(sqlptr, args[i], alen);
- sqlptr += alen;
- *sqlptr++ = ',';
- }
- sqlptr[-1] = ')';
- }
- memcpy(sqlptr, " AS ", 4);
- sqlptr += 4;
- memcpy(sqlptr, pgquery, strlen(pgquery));
- sqlptr += strlen(pgquery);
- *sqlptr = 0;
- res = PQexec(sql->conn, sqlcmd);
- if ( res ) {
- ret = PQresultStatus(res);
- if (dbd_pgsql_is_success(ret)) {
- ret = 0;
- }
- /* Hmmm, do we do this here or register it on the pool? */
- PQclear(res);
- }
- else {
- ret = PGRES_FATAL_ERROR;
- }
- (*statement)->prepared = 1;
- return ret;
- }
- static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
- int *nrows, apr_dbd_prepared_t *statement,
- int nargs, const char **values)
- {
- int ret;
- PGresult *res;
- if (sql->trans && sql->trans->errnum) {
- return sql->trans->errnum;
- }
- if (statement->prepared) {
- res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0, 0,
- 0);
- }
- else {
- res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0, 0,
- 0);
- }
- if (res) {
- ret = PQresultStatus(res);
- if (dbd_pgsql_is_success(ret)) {
- ret = 0;
- }
- *nrows = atoi(PQcmdTuples(res));
- PQclear(res);
- }
- else {
- ret = PGRES_FATAL_ERROR;
- }
- if (sql->trans) {
- sql->trans->errnum = ret;
- }
- return ret;
- }
- static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql,
- int *nrows, apr_dbd_prepared_t *statement,
- va_list args)
- {
- const char **values;
- int i;
- if (sql->trans && sql->trans->errnum) {
- return sql->trans->errnum;
- }
- values = apr_palloc(pool, sizeof(*values) * statement->nargs);
- for (i = 0; i < statement->nargs; i++) {
- values[i] = apr_pstrdup(pool, va_arg(args, const char*));
- }
- return dbd_pgsql_pquery(pool, sql, nrows, statement,
- statement->nargs, values);
- }
- static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
- apr_dbd_results_t **results,
- apr_dbd_prepared_t *statement,
- int seek, int nargs, const char **values)
- {
- PGresult *res;
- int rv;
- int ret = 0;
- if (sql->trans && sql->trans->errnum) {
- return sql->trans->errnum;
- }
- if (seek) { /* synchronous query */
- if (statement->prepared) {
- res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0,
- 0, 0);
- }
- else {
- res = PQexecParams(sql->conn, statement->name, nargs, 0, values, 0,
- 0, 0);
- }
- if (res) {
- ret = PQresultStatus(res);
- if (dbd_pgsql_is_success(ret)) {
- ret = 0;
- }
- else {
- PQclear(res);
- }
- }
- else {
- ret = PGRES_FATAL_ERROR;
- }
- if (ret != 0) {
- if (sql->trans) {
- sql->trans->errnum = ret;
- }
- return ret;
- }
- if (!*results) {
- *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
- }
- (*results)->res = res;
- (*results)->ntuples = PQntuples(res);
- (*results)->sz = PQnfields(res);
- (*results)->random = seek;
- apr_pool_cleanup_register(pool, res, clear_result,
- apr_pool_cleanup_null);
- }
- else {
- if (statement->prepared) {
- rv = PQsendQueryPrepared(sql->conn, statement->name, nargs, values,
- 0, 0, 0);
- }
- else {
- rv = PQsendQueryParams(sql->conn, statement->name, nargs, 0,
- values, 0, 0, 0);
- }
- if (rv == 0) {
- if (sql->trans) {
- sql->trans->errnum = 1;
- }
- return 1;
- }
- if (!*results) {
- *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
- }
- (*results)->random = seek;
- (*results)->handle = sql->conn;
- }
- if (sql->trans) {
- sql->trans->errnum = ret;
- }
- return ret;
- }
- static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
- apr_dbd_results_t **results,
- apr_dbd_prepared_t *statement,
- int seek, va_list args)
- {
- const char **values;
- int i;
- if (sql->trans && sql->trans->errnum) {
- return sql->trans->errnum;
- }
- values = apr_palloc(pool, sizeof(*values) * statement->nargs);
- for (i = 0; i < statement->nargs; i++) {
- values[i] = apr_pstrdup(pool, va_arg(args, const char*));
- }
- return dbd_pgsql_pselect(pool, sql, results, statement,
- seek, statement->nargs, values) ;
- }
- static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
- apr_dbd_transaction_t **trans)
- {
- int ret = 0;
- PGresult *res;
- /* XXX handle recursive transactions here */
- res = PQexec(handle->conn, "BEGIN TRANSACTION");
- if (res) {
- ret = PQresultStatus(res);
- if (dbd_pgsql_is_success(ret)) {
- ret = 0;
- if (!*trans) {
- *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
- }
- }
- PQclear(res);
- (*trans)->handle = handle;
- handle->trans = *trans;
- }
- else {
- ret = PGRES_FATAL_ERROR;
- }
- return ret;
- }
- static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans)
- {
- PGresult *res;
- int ret = -1; /* no transaction is an error cond */
- if (trans) {
- if (trans->errnum) {
- trans->errnum = 0;
- res = PQexec(trans->handle->conn, "ROLLBACK");
- }
- else {
- res = PQexec(trans->handle->conn, "COMMIT");
- }
- if (res) {
- ret = PQresultStatus(res);
- if (dbd_pgsql_is_success(ret)) {
- ret = 0;
- }
- PQclear(res);
- }
- else {
- ret = PGRES_FATAL_ERROR;
- }
- trans->handle->trans = NULL;
- }
- return ret;
- }
- static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
- {
- apr_dbd_t *sql;
-
- PGconn *conn = PQconnectdb(params);
- /* if there's an error in the connect string or something we get
- * back a * bogus connection object, and things like PQreset are
- * liable to segfault, so just close it out now. it would be nice
- * if we could give an indication of why we failed to connect... */
- if (PQstatus(conn) != CONNECTION_OK) {
- PQfinish(conn);
- return NULL;
- }
- sql = apr_pcalloc (pool, sizeof (*sql));
- sql->conn = conn;
- return sql;
- }
- static apr_status_t dbd_pgsql_close(apr_dbd_t *handle)
- {
- PQfinish(handle->conn);
- return APR_SUCCESS;
- }
- static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool,
- apr_dbd_t *handle)
- {
- if (PQstatus(handle->conn) != CONNECTION_OK) {
- PQreset(handle->conn);
- if (PQstatus(handle->conn) != CONNECTION_OK) {
- return APR_EGENERAL;
- }
- }
- return APR_SUCCESS;
- }
- static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle,
- const char *name)
- {
- return APR_ENOTIMPL;
- }
- static void *dbd_pgsql_native(apr_dbd_t *handle)
- {
- return handle->conn;
- }
- static int dbd_pgsql_num_cols(apr_dbd_results_t* res)
- {
- return res->sz;
- }
- static int dbd_pgsql_num_tuples(apr_dbd_results_t* res)
- {
- if (res->random) {
- return res->ntuples;
- }
- else {
- return -1;
- }
- }
- APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = {
- "pgsql",
- NULL,
- dbd_pgsql_native,
- dbd_pgsql_open,
- dbd_pgsql_check_conn,
- dbd_pgsql_close,
- dbd_pgsql_select_db,
- dbd_pgsql_start_transaction,
- dbd_pgsql_end_transaction,
- dbd_pgsql_query,
- dbd_pgsql_select,
- dbd_pgsql_num_cols,
- dbd_pgsql_num_tuples,
- dbd_pgsql_get_row,
- dbd_pgsql_get_entry,
- dbd_pgsql_error,
- dbd_pgsql_escape,
- dbd_pgsql_prepare,
- dbd_pgsql_pvquery,
- dbd_pgsql_pvselect,
- dbd_pgsql_pquery,
- dbd_pgsql_pselect,
- };
- #endif
|