4705e3668d
components: apr-1.4.6 -> 1.4.8 and apr-util-1.4.1 -> 1.5.2. This is a post point-zero bug-fix / fix-sharp-edges release, including some workarounds for UTF-8 for people who haven't yet turned on WITH_ICONV.
1316 lines
39 KiB
C
1316 lines
39 KiB
C
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You 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_buckets.h"
|
|
|
|
#include "apr_dbd_internal.h"
|
|
|
|
struct apr_dbd_transaction_t {
|
|
int mode;
|
|
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;
|
|
apr_pool_t *pool;
|
|
};
|
|
|
|
struct apr_dbd_row_t {
|
|
int n;
|
|
apr_dbd_results_t *res;
|
|
};
|
|
|
|
struct apr_dbd_prepared_t {
|
|
const char *name;
|
|
int prepared;
|
|
int nargs;
|
|
int nvals;
|
|
apr_dbd_type_e *types;
|
|
};
|
|
|
|
#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 */
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
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 (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
} else if (TXN_NOTICE_ERRORS(sql->trans)){
|
|
sql->trans->errnum = ret;
|
|
}
|
|
return ret;
|
|
} else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"RELEASE SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
(*results)->pool = pool;
|
|
apr_pool_cleanup_register(pool, res, clear_result,
|
|
apr_pool_cleanup_null);
|
|
}
|
|
else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
if (PQsendQuery(sql->conn, query) == 0) {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
} else if (TXN_NOTICE_ERRORS(sql->trans)){
|
|
sql->trans->errnum = 1;
|
|
}
|
|
return 1;
|
|
} else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"RELEASE SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
if (*results == NULL) {
|
|
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
|
|
}
|
|
(*results)->random = seek;
|
|
(*results)->handle = sql->conn;
|
|
(*results)->pool = pool;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n)
|
|
{
|
|
if (res->res) {
|
|
if ((n>=0) && (PQnfields(res->res) > n)) {
|
|
return PQfname(res->res,n);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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;
|
|
if ( sequential ) {
|
|
row->n = 0;
|
|
}
|
|
else {
|
|
if (rownum > 0) {
|
|
row->n = --rownum;
|
|
}
|
|
else {
|
|
return -1; /* invalid row */
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( sequential ) {
|
|
++row->n;
|
|
}
|
|
else {
|
|
if (rownum > 0) {
|
|
row->n = --rownum;
|
|
}
|
|
else {
|
|
return -1; /* invalid row */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (res->random) {
|
|
if ((row->n >= 0) && (size_t)row->n >= res->ntuples) {
|
|
*rowp = NULL;
|
|
apr_pool_cleanup_run(res->pool, res->res, clear_result);
|
|
res->res = NULL;
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
if ((row->n >= 0) && (size_t)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 apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n,
|
|
apr_dbd_type_e type, void *data)
|
|
{
|
|
if (PQgetisnull(row->res->res, row->n, n)) {
|
|
return APR_ENOENT;
|
|
}
|
|
|
|
switch (type) {
|
|
case APR_DBD_TYPE_TINY:
|
|
*(char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_UTINY:
|
|
*(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_SHORT:
|
|
*(short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_USHORT:
|
|
*(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_INT:
|
|
*(int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_UINT:
|
|
*(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_LONG:
|
|
*(long*)data = atol(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_ULONG:
|
|
*(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_LONGLONG:
|
|
*(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_ULONGLONG:
|
|
*(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_FLOAT:
|
|
*(float*)data = (float)atof(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_DOUBLE:
|
|
*(double*)data = atof(PQgetvalue(row->res->res, row->n, n));
|
|
break;
|
|
case APR_DBD_TYPE_STRING:
|
|
case APR_DBD_TYPE_TEXT:
|
|
case APR_DBD_TYPE_TIME:
|
|
case APR_DBD_TYPE_DATE:
|
|
case APR_DBD_TYPE_DATETIME:
|
|
case APR_DBD_TYPE_TIMESTAMP:
|
|
case APR_DBD_TYPE_ZTIMESTAMP:
|
|
*(char**)data = PQgetvalue(row->res->res, row->n, n);
|
|
break;
|
|
case APR_DBD_TYPE_BLOB:
|
|
case APR_DBD_TYPE_CLOB:
|
|
{
|
|
apr_bucket *e;
|
|
apr_bucket_brigade *b = (apr_bucket_brigade*)data;
|
|
|
|
e = apr_bucket_pool_create(PQgetvalue(row->res->res, row->n, n),
|
|
PQgetlength(row->res->res, row->n, n),
|
|
row->res->pool, b->bucket_alloc);
|
|
APR_BRIGADE_INSERT_TAIL(b, e);
|
|
}
|
|
break;
|
|
case APR_DBD_TYPE_NULL:
|
|
*(void**)data = NULL;
|
|
break;
|
|
default:
|
|
return APR_EGENERAL;
|
|
}
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
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 (ret != 0){
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else if (TXN_NOTICE_ERRORS(sql->trans)){
|
|
sql->trans->errnum = ret;
|
|
}
|
|
} else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"RELEASE SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 + 2);
|
|
PQescapeStringConn(sql->conn, ret, arg, len, NULL);
|
|
return ret;
|
|
}
|
|
|
|
static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
|
|
const char *query, const char *label,
|
|
int nargs, int nvals, apr_dbd_type_e *types,
|
|
apr_dbd_prepared_t **statement)
|
|
{
|
|
char *sqlcmd;
|
|
char *sqlptr;
|
|
size_t length, qlen;
|
|
int i = 0;
|
|
const char **args;
|
|
size_t alen;
|
|
int ret;
|
|
PGresult *res;
|
|
|
|
if (!*statement) {
|
|
*statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
|
|
}
|
|
(*statement)->nargs = nargs;
|
|
(*statement)->nvals = nvals;
|
|
(*statement)->types = types;
|
|
|
|
args = apr_palloc(pool, nargs * sizeof(*args));
|
|
|
|
qlen = strlen(query);
|
|
length = qlen + 1;
|
|
|
|
for (i = 0; i < nargs; i++) {
|
|
switch (types[i]) {
|
|
case APR_DBD_TYPE_TINY:
|
|
case APR_DBD_TYPE_UTINY:
|
|
case APR_DBD_TYPE_SHORT:
|
|
case APR_DBD_TYPE_USHORT:
|
|
args[i] = "smallint";
|
|
break;
|
|
case APR_DBD_TYPE_INT:
|
|
case APR_DBD_TYPE_UINT:
|
|
args[i] = "integer";
|
|
break;
|
|
case APR_DBD_TYPE_LONG:
|
|
case APR_DBD_TYPE_ULONG:
|
|
case APR_DBD_TYPE_LONGLONG:
|
|
case APR_DBD_TYPE_ULONGLONG:
|
|
args[i] = "bigint";
|
|
break;
|
|
case APR_DBD_TYPE_FLOAT:
|
|
args[i] = "real";
|
|
break;
|
|
case APR_DBD_TYPE_DOUBLE:
|
|
args[i] = "double precision";
|
|
break;
|
|
case APR_DBD_TYPE_TEXT:
|
|
args[i] = "text";
|
|
break;
|
|
case APR_DBD_TYPE_TIME:
|
|
args[i] = "time";
|
|
break;
|
|
case APR_DBD_TYPE_DATE:
|
|
args[i] = "date";
|
|
break;
|
|
case APR_DBD_TYPE_DATETIME:
|
|
case APR_DBD_TYPE_TIMESTAMP:
|
|
args[i] = "timestamp";
|
|
break;
|
|
case APR_DBD_TYPE_ZTIMESTAMP:
|
|
args[i] = "timestamp with time zone";
|
|
break;
|
|
case APR_DBD_TYPE_BLOB:
|
|
case APR_DBD_TYPE_CLOB:
|
|
args[i] = "bytea";
|
|
break;
|
|
case APR_DBD_TYPE_NULL:
|
|
args[i] = "varchar"; /* XXX Eh? */
|
|
break;
|
|
default:
|
|
args[i] = "varchar";
|
|
break;
|
|
}
|
|
length += 1 + strlen(args[i]);
|
|
}
|
|
|
|
if (!label) {
|
|
/* don't really prepare; use in execParams instead */
|
|
(*statement)->prepared = 0;
|
|
(*statement)->name = apr_pstrdup(pool, query);
|
|
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 (nargs > 0) {
|
|
memcpy(sqlptr, " (",2);
|
|
sqlptr += 2;
|
|
for (i=0; i < nargs; ++i) {
|
|
alen = strlen(args[i]);
|
|
memcpy(sqlptr, args[i], alen);
|
|
sqlptr += alen;
|
|
*sqlptr++ = ',';
|
|
}
|
|
sqlptr[-1] = ')';
|
|
}
|
|
memcpy(sqlptr, " AS ", 4);
|
|
sqlptr += 4;
|
|
memcpy(sqlptr, query, qlen);
|
|
sqlptr += qlen;
|
|
*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_internal(apr_pool_t *pool, apr_dbd_t *sql,
|
|
int *nrows, apr_dbd_prepared_t *statement,
|
|
const char **values,
|
|
const int *len, const int *fmt)
|
|
{
|
|
int ret;
|
|
PGresult *res;
|
|
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
return sql->trans->errnum = PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (statement->prepared) {
|
|
res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
|
|
values, len, fmt, 0);
|
|
}
|
|
else {
|
|
res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
|
|
values, len, fmt, 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 (ret != 0){
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else if (TXN_NOTICE_ERRORS(sql->trans)){
|
|
sql->trans->errnum = ret;
|
|
}
|
|
} else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"RELEASE SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void dbd_pgsql_bind(apr_dbd_prepared_t *statement,
|
|
const char **values,
|
|
const char **val, int *len, int *fmt)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0, j = 0; i < statement->nargs; i++, j++) {
|
|
if (values[j] == NULL) {
|
|
val[i] = NULL;
|
|
}
|
|
else {
|
|
switch (statement->types[i]) {
|
|
case APR_DBD_TYPE_BLOB:
|
|
case APR_DBD_TYPE_CLOB:
|
|
val[i] = (char *)values[j];
|
|
len[i] = atoi(values[++j]);
|
|
fmt[i] = 1;
|
|
|
|
/* skip table and column */
|
|
j += 2;
|
|
break;
|
|
default:
|
|
val[i] = values[j];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
|
|
int *nrows, apr_dbd_prepared_t *statement,
|
|
const char **values)
|
|
{
|
|
int *len, *fmt;
|
|
const char **val;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
val = apr_palloc(pool, sizeof(*val) * statement->nargs);
|
|
len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
|
|
fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
|
|
|
|
dbd_pgsql_bind(statement, values, val, len, fmt);
|
|
|
|
return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
|
|
val, len, fmt);
|
|
}
|
|
|
|
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->nvals);
|
|
|
|
for (i = 0; i < statement->nvals; i++) {
|
|
values[i] = va_arg(args, const char*);
|
|
}
|
|
|
|
return dbd_pgsql_pquery(pool, sql, nrows, statement, values);
|
|
}
|
|
|
|
static int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
|
|
apr_dbd_results_t **results,
|
|
apr_dbd_prepared_t *statement,
|
|
int seek, const char **values,
|
|
const int *len, const int *fmt)
|
|
{
|
|
PGresult *res;
|
|
int rv;
|
|
int ret = 0;
|
|
|
|
if (seek) { /* synchronous query */
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
if (statement->prepared) {
|
|
res = PQexecPrepared(sql->conn, statement->name, statement->nargs,
|
|
values, len, fmt, 0);
|
|
}
|
|
else {
|
|
res = PQexecParams(sql->conn, statement->name, statement->nargs, 0,
|
|
values, len, fmt, 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 (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else if (TXN_NOTICE_ERRORS(sql->trans)){
|
|
sql->trans->errnum = ret;
|
|
}
|
|
return ret;
|
|
} else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"RELEASE SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
(*results)->pool = pool;
|
|
apr_pool_cleanup_register(pool, res, clear_result,
|
|
apr_pool_cleanup_null);
|
|
}
|
|
else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
if (statement->prepared) {
|
|
rv = PQsendQueryPrepared(sql->conn, statement->name,
|
|
statement->nargs, values, len, fmt, 0);
|
|
}
|
|
else {
|
|
rv = PQsendQueryParams(sql->conn, statement->name,
|
|
statement->nargs, 0, values, len, fmt, 0);
|
|
}
|
|
if (rv == 0) {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else if (TXN_NOTICE_ERRORS(sql->trans)){
|
|
sql->trans->errnum = 1;
|
|
}
|
|
return 1;
|
|
} else {
|
|
if (TXN_IGNORE_ERRORS(sql->trans)) {
|
|
PGresult *res = PQexec(sql->conn,
|
|
"RELEASE SAVEPOINT APR_DBD_TXN_SP");
|
|
if (res) {
|
|
int ret = PQresultStatus(res);
|
|
PQclear(res);
|
|
if (!dbd_pgsql_is_success(ret)) {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
} else {
|
|
sql->trans->errnum = ret;
|
|
return PGRES_FATAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
if (!*results) {
|
|
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
|
|
}
|
|
(*results)->random = seek;
|
|
(*results)->handle = sql->conn;
|
|
(*results)->pool = pool;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
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, const char **values)
|
|
{
|
|
int *len, *fmt;
|
|
const char **val;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
val = apr_palloc(pool, sizeof(*val) * statement->nargs);
|
|
len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
|
|
fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
|
|
|
|
dbd_pgsql_bind(statement, values, val, len, fmt);
|
|
|
|
return dbd_pgsql_pselect_internal(pool, sql, results, statement,
|
|
seek, val, len, fmt);
|
|
}
|
|
|
|
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->nvals);
|
|
|
|
for (i = 0; i < statement->nvals; i++) {
|
|
values[i] = va_arg(args, const char*);
|
|
}
|
|
|
|
return dbd_pgsql_pselect(pool, sql, results, statement, seek, values);
|
|
}
|
|
|
|
static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement,
|
|
const void **values,
|
|
const char **val, int *len, int *fmt)
|
|
{
|
|
int i, j;
|
|
apr_dbd_type_e type;
|
|
|
|
for (i = 0, j = 0; i < statement->nargs; i++, j++) {
|
|
type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
|
|
|
|
switch (type) {
|
|
case APR_DBD_TYPE_TINY:
|
|
val[i] = apr_itoa(pool, *(char*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_UTINY:
|
|
val[i] = apr_itoa(pool, *(unsigned char*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_SHORT:
|
|
val[i] = apr_itoa(pool, *(short*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_USHORT:
|
|
val[i] = apr_itoa(pool, *(unsigned short*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_INT:
|
|
val[i] = apr_itoa(pool, *(int*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_UINT:
|
|
val[i] = apr_itoa(pool, *(unsigned int*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_LONG:
|
|
val[i] = apr_ltoa(pool, *(long*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_ULONG:
|
|
val[i] = apr_ltoa(pool, *(unsigned long*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_LONGLONG:
|
|
val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT,
|
|
*(apr_int64_t*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_ULONGLONG:
|
|
val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT,
|
|
*(apr_uint64_t*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_FLOAT:
|
|
val[i] = apr_psprintf(pool, "%f", *(float*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_DOUBLE:
|
|
val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]);
|
|
break;
|
|
case APR_DBD_TYPE_STRING:
|
|
case APR_DBD_TYPE_TEXT:
|
|
case APR_DBD_TYPE_TIME:
|
|
case APR_DBD_TYPE_DATE:
|
|
case APR_DBD_TYPE_DATETIME:
|
|
case APR_DBD_TYPE_TIMESTAMP:
|
|
case APR_DBD_TYPE_ZTIMESTAMP:
|
|
val[i] = values[j];
|
|
break;
|
|
case APR_DBD_TYPE_BLOB:
|
|
case APR_DBD_TYPE_CLOB:
|
|
val[i] = (char*)values[j];
|
|
len[i] = *(apr_size_t*)values[++j];
|
|
fmt[i] = 1;
|
|
|
|
/* skip table and column */
|
|
j += 2;
|
|
break;
|
|
case APR_DBD_TYPE_NULL:
|
|
default:
|
|
val[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
|
|
int *nrows, apr_dbd_prepared_t * statement,
|
|
const void **values)
|
|
{
|
|
int *len, *fmt;
|
|
const char **val;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
val = apr_palloc(pool, sizeof(*val) * statement->nargs);
|
|
len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
|
|
fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
|
|
|
|
dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
|
|
|
|
return dbd_pgsql_pquery_internal(pool, sql, nrows, statement,
|
|
val, len, fmt);
|
|
}
|
|
|
|
static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql,
|
|
int *nrows, apr_dbd_prepared_t * statement,
|
|
va_list args)
|
|
{
|
|
const void **values;
|
|
int i;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
|
|
|
|
for (i = 0; i < statement->nvals; i++) {
|
|
values[i] = va_arg(args, const void*);
|
|
}
|
|
|
|
return dbd_pgsql_pbquery(pool, sql, nrows, statement, values);
|
|
}
|
|
|
|
static int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql,
|
|
apr_dbd_results_t ** results,
|
|
apr_dbd_prepared_t * statement,
|
|
int seek, const void **values)
|
|
{
|
|
int *len, *fmt;
|
|
const char **val;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
val = apr_palloc(pool, sizeof(*val) * statement->nargs);
|
|
len = apr_pcalloc(pool, sizeof(*len) * statement->nargs);
|
|
fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs);
|
|
|
|
dbd_pgsql_bbind(pool, statement, values, val, len, fmt);
|
|
|
|
return dbd_pgsql_pselect_internal(pool, sql, results, statement,
|
|
seek, val, len, fmt);
|
|
}
|
|
|
|
static int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql,
|
|
apr_dbd_results_t ** results,
|
|
apr_dbd_prepared_t * statement, int seek,
|
|
va_list args)
|
|
{
|
|
const void **values;
|
|
int i;
|
|
|
|
if (sql->trans && sql->trans->errnum) {
|
|
return sql->trans->errnum;
|
|
}
|
|
|
|
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
|
|
|
|
for (i = 0; i < statement->nvals; i++) {
|
|
values[i] = va_arg(args, const void*);
|
|
}
|
|
|
|
return dbd_pgsql_pbselect(pool, sql, results, statement, seek, 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) {
|
|
/* rollback on error or explicit rollback request */
|
|
if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
|
|
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 int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
|
|
{
|
|
if (!trans)
|
|
return APR_DBD_TRANSACTION_COMMIT;
|
|
|
|
return trans->mode;
|
|
}
|
|
|
|
static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
|
|
int mode)
|
|
{
|
|
if (!trans)
|
|
return APR_DBD_TRANSACTION_COMMIT;
|
|
|
|
return trans->mode = (mode & TXN_MODE_BITS);
|
|
}
|
|
|
|
static void null_notice_receiver(void *arg, const PGresult *res)
|
|
{
|
|
/* nothing */
|
|
}
|
|
|
|
static void null_notice_processor(void *arg, const char *message)
|
|
{
|
|
/* nothing */
|
|
}
|
|
|
|
static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params,
|
|
const char **error)
|
|
{
|
|
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) {
|
|
if (error) {
|
|
*error = apr_pstrdup(pool, PQerrorMessage(conn));
|
|
}
|
|
PQfinish(conn);
|
|
return NULL;
|
|
}
|
|
|
|
PQsetNoticeReceiver(conn, null_notice_receiver, NULL);
|
|
PQsetNoticeProcessor(conn, null_notice_processor, 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_MODULE_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,
|
|
dbd_pgsql_get_name,
|
|
dbd_pgsql_transaction_mode_get,
|
|
dbd_pgsql_transaction_mode_set,
|
|
"$%d",
|
|
dbd_pgsql_pvbquery,
|
|
dbd_pgsql_pvbselect,
|
|
dbd_pgsql_pbquery,
|
|
dbd_pgsql_pbselect,
|
|
dbd_pgsql_datum_get
|
|
};
|
|
#endif
|