#include "apu.h"
#if APU_HAVE_ODBC
#include "apr.h"
#include "apr_strings.h"
#include "apr_buckets.h"
#include "apr_env.h"
#include "apr_file_io.h"
#include "apr_file_info.h"
#include "apr_dbd_internal.h"
#include "apr_thread_proc.h"
#include "apu_version.h"
#include "apu_config.h"
#include <stdlib.h>
#ifdef ODBCV2
#define ODBCVER 0x0200
#include "apr_dbd_odbc_v2.h"
#endif
#ifdef HAVE_SQL_H
#include <sql.h>
#include <sqlext.h>
#elif defined(HAVE_ODBC_SQL_H)
#include <odbc/sql.h>
#include <odbc/sqlext.h>
#endif
#ifndef ODBC_DRIVER_NAME
#define ODBC_DRIVER_NAME odbc
#endif
#define STRINGIFY(x) #x
#define NAMIFY2(n) apr_dbd_##n##_driver
#define NAMIFY1(n) NAMIFY2(n)
#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
static SQLHANDLE henv = NULL;
static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
SQLSMALLINT type, SQLHANDLE h, int line);
#define CHECK_ERROR(a,s,r,t,h) check_error(a,s,r,t,h, __LINE__)
#define SOURCE_FILE __FILE__
#define MAX_ERROR_STRING 1024
#define MAX_COLUMN_NAME 256
#define DEFAULT_BUFFER_SIZE 1024
#define MAX_PARAMS 20
#define DEFAULTSEPS " \t\r\n,="
#define CSINGLEQUOTE '\''
#define SSINGLEQUOTE "\'"
#define TEXTMODE 1
#define BINARYMODE 0
#define IS_LOB(t) (t == SQL_LONGVARCHAR \
|| t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
|| t == -98 || t == -99)
#define IS_CLOB(t) \
(t == SQL_LONGVARCHAR || t == -98)
#define APR_FROM_SQL_RESULT(rc) \
(SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
struct apr_dbd_t
{
SQLHANDLE dbc;
apr_pool_t *pool;
char *dbname;
int lasterrorcode;
int lineNumber;
char lastError[MAX_ERROR_STRING];
int defaultBufferSize;
int transaction_mode;
int dboptions;
int default_transaction_mode;
int can_commit;
};
struct apr_dbd_results_t
{
SQLHANDLE stmt;
SQLHANDLE dbc;
apr_pool_t *pool;
apr_dbd_t *apr_dbd;
int random;
int ncols;
int isclosed;
char **colnames;
SQLPOINTER *colptrs;
SQLINTEGER *colsizes;
SQLINTEGER *coltextsizes;
SQLSMALLINT *coltypes;
SQLLEN *colinds;
int *colstate;
int *all_data_fetched;
void *data;
};
enum
{
COL_AVAIL,
COL_PRESENT,
COL_BOUND,
COL_RETRIEVED,
COL_UNAVAIL
};
struct apr_dbd_row_t {
SQLHANDLE stmt;
SQLHANDLE dbc;
apr_pool_t *pool;
apr_dbd_results_t *res;
};
struct apr_dbd_transaction_t {
SQLHANDLE dbc;
apr_dbd_t *apr_dbd;
};
struct apr_dbd_prepared_t {
SQLHANDLE stmt;
SQLHANDLE dbc;
apr_dbd_t *apr_dbd;
int nargs;
int nvals;
int *types;
};
static void odbc_lob_bucket_destroy(void *data);
static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
apr_size_t *len, apr_read_type_e block);
static const apr_bucket_type_t odbc_bucket_type = {
"ODBC_LOB", 5, APR_BUCKET_DATA,
odbc_lob_bucket_destroy,
odbc_lob_bucket_read,
odbc_lob_bucket_setaside,
apr_bucket_shared_split,
apr_bucket_shared_copy
};
typedef struct {
apr_bucket_refcount refcount;
const apr_dbd_row_t *row;
int col;
SQLSMALLINT type;
} odbc_bucket;
static SQLSMALLINT const sqlCtype[] = {
SQL_C_DEFAULT,
SQL_C_STINYINT,
SQL_C_UTINYINT,
SQL_C_SSHORT,
SQL_C_USHORT,
SQL_C_SLONG,
SQL_C_ULONG,
SQL_C_SLONG,
SQL_C_ULONG,
SQL_C_SBIGINT,
SQL_C_UBIGINT,
SQL_C_FLOAT,
SQL_C_DOUBLE,
SQL_C_CHAR,
SQL_C_CHAR,
SQL_C_CHAR,
SQL_C_CHAR,
SQL_C_CHAR,
SQL_C_CHAR,
SQL_C_CHAR,
SQL_LONGVARBINARY,
SQL_LONGVARCHAR,
SQL_TYPE_NULL
};
#define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0]))
static SQLSMALLINT const sqlBaseType[] = {
SQL_C_DEFAULT,
SQL_TINYINT,
SQL_TINYINT,
SQL_SMALLINT,
SQL_SMALLINT,
SQL_INTEGER,
SQL_INTEGER,
SQL_INTEGER,
SQL_INTEGER,
SQL_BIGINT,
SQL_BIGINT,
SQL_FLOAT,
SQL_DOUBLE,
SQL_CHAR,
SQL_CHAR,
SQL_CHAR,
SQL_CHAR,
SQL_CHAR,
SQL_CHAR,
SQL_CHAR,
SQL_LONGVARBINARY,
SQL_LONGVARCHAR,
SQL_TYPE_NULL
};
static int const sqlSizes[] = {
0,
sizeof(char),
sizeof(unsigned char),
sizeof(short),
sizeof(unsigned short),
sizeof(int),
sizeof(unsigned int),
sizeof(long),
sizeof(unsigned long),
sizeof(apr_int64_t),
sizeof(apr_uint64_t),
sizeof(float),
sizeof(double),
-1,
-1,
-1,
-1,
-1,
-1,
-1,
sizeof(apr_bucket_brigade),
sizeof(apr_bucket_brigade),
0
};
static apr_status_t odbc_close_results(void *d)
{
apr_dbd_results_t *dbr = (apr_dbd_results_t *)d;
SQLRETURN rc = SQL_SUCCESS;
if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) {
if (!dbr->isclosed)
rc = SQLCloseCursor(dbr->stmt);
dbr->isclosed = 1;
}
return APR_FROM_SQL_RESULT(rc);
}
static apr_status_t odbc_close_pstmt(void *s)
{
SQLRETURN rc = APR_SUCCESS;
apr_dbd_prepared_t *statement = s;
if (statement) {
SQLHANDLE hstmt = statement->stmt;
if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}
statement->stmt = NULL;
}
return APR_FROM_SQL_RESULT(rc);
}
static apr_status_t odbc_close(apr_dbd_t *handle)
{
SQLRETURN rc = SQL_SUCCESS;
if (handle->dbc) {
rc = SQLDisconnect(handle->dbc);
CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
handle->dbc = NULL;
}
return APR_FROM_SQL_RESULT(rc);
}
static apr_status_t odbc_close_cleanup(void *handle)
{
return odbc_close((apr_dbd_t *)handle);
}
static apr_status_t odbc_close_env(SQLHANDLE henv)
{
SQLRETURN rc;
rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
henv = NULL;
return APR_FROM_SQL_RESULT(rc);
}
static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t *res,
SQLHANDLE stmt)
{
SQLRETURN rc;
int maxsize, textsize, realsize, type, isunsigned = 1;
rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
(SQLPOINTER)&isunsigned);
isunsigned = (isunsigned == SQL_TRUE);
rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
(SQLPOINTER)&type);
if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE) {
rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
0, NULL, (SQLPOINTER)&type);
}
if (!SQL_SUCCEEDED(rc)) {
type = SQL_C_CHAR;
}
switch (type) {
case SQL_INTEGER:
case SQL_SMALLINT:
case SQL_TINYINT:
case SQL_BIGINT:
type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
break;
case SQL_LONGVARCHAR:
type = SQL_LONGVARCHAR;
break;
case SQL_LONGVARBINARY:
type = SQL_LONGVARBINARY;
break;
case SQL_FLOAT :
type = SQL_C_FLOAT;
break;
case SQL_DOUBLE :
type = SQL_C_DOUBLE;
break;
case SQL_TIMESTAMP:
case SQL_DATE:
case SQL_TIME:
default:
type = SQL_C_CHAR;
}
res->coltypes[icol] = type;
rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
NULL, (SQLPOINTER)&textsize);
if (!SQL_SUCCEEDED(rc) || textsize < 0) {
textsize = res->apr_dbd->defaultBufferSize;
}
textsize++;
rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
NULL, (SQLPOINTER)&realsize);
if (!SQL_SUCCEEDED(rc)) {
realsize = textsize;
}
maxsize = (textsize > realsize) ? textsize : realsize;
if (IS_LOB(type) || maxsize <= 0) {
maxsize = res->apr_dbd->defaultBufferSize;
if (IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE) {
maxsize = APR_BUCKET_BUFF_SIZE;
}
res->colptrs[icol] = NULL;
res->colstate[icol] = COL_AVAIL;
res->colsizes[icol] = maxsize;
rc = SQL_SUCCESS;
}
else {
res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
res->colsizes[icol] = maxsize;
if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
res->colptrs[icol], maxsize,
&(res->colinds[icol]));
CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
stmt);
res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
}
else {
res->colstate[icol] = COL_AVAIL;
rc = SQL_SUCCESS;
}
}
return rc;
}
static SQLRETURN odbc_create_results(apr_dbd_t *handle, SQLHANDLE hstmt,
apr_pool_t *pool, const int random,
apr_dbd_results_t **res)
{
SQLRETURN rc;
SQLSMALLINT ncols;
*res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
(*res)->stmt = hstmt;
(*res)->dbc = handle->dbc;
(*res)->pool = pool;
(*res)->random = random;
(*res)->apr_dbd = handle;
rc = SQLNumResultCols(hstmt, &ncols);
CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
(*res)->ncols = ncols;
if (SQL_SUCCEEDED(rc)) {
int i;
(*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
(*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
(*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
(*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
(*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
(*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
(*res)->ncols = ncols;
for (i = 0; i < ncols; i++) {
odbc_set_result_column(i, (*res), hstmt);
}
}
return rc;
}
static SQLRETURN odbc_bind_param(apr_pool_t *pool,
apr_dbd_prepared_t *statement, const int narg,
const SQLSMALLINT type, int *argp,
const void **args, const int textmode)
{
SQLRETURN rc;
SQLSMALLINT baseType, cType;
void *ptr;
SQLULEN len;
SQLLEN *indicator;
static SQLLEN nullValue = SQL_NULL_DATA;
static SQLSMALLINT inOut = SQL_PARAM_INPUT;
if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
baseType = SQL_CHAR;
cType = SQL_C_CHAR;
ptr = &nullValue;
len = sizeof(SQLINTEGER);
indicator = &nullValue;
(*argp)++;
}
else {
if (type < 0 || type >= NUM_APR_DBD_TYPES) {
return APR_EGENERAL;
}
baseType = sqlBaseType[type];
cType = sqlCtype[type];
indicator = NULL;
if (IS_LOB(cType)) {
ptr = (void *)args[*argp];
len = (SQLULEN) * (apr_size_t *)args[*argp + 1];
cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
(*argp) += 4;
}
else {
switch (baseType) {
case SQL_CHAR:
case SQL_DATE:
case SQL_TIME:
case SQL_TIMESTAMP:
ptr = (void *)args[*argp];
len = (SQLULEN)strlen(ptr);
break;
case SQL_TINYINT:
ptr = apr_palloc(pool, sizeof(unsigned char));
len = sizeof(unsigned char);
*(unsigned char *)ptr =
(textmode ?
atoi(args[*argp]) : *(unsigned char *)args[*argp]);
break;
case SQL_SMALLINT:
ptr = apr_palloc(pool, sizeof(short));
len = sizeof(short);
*(short *)ptr =
(textmode ? atoi(args[*argp]) : *(short *)args[*argp]);
break;
case SQL_INTEGER:
ptr = apr_palloc(pool, sizeof(int));
len = sizeof(int);
*(long *)ptr =
(textmode ? atol(args[*argp]) : *(long *)args[*argp]);
break;
case SQL_FLOAT:
ptr = apr_palloc(pool, sizeof(float));
len = sizeof(float);
*(float *)ptr =
(textmode ?
(float)atof(args[*argp]) : *(float *)args[*argp]);
break;
case SQL_DOUBLE:
ptr = apr_palloc(pool, sizeof(double));
len = sizeof(double);
*(double *)ptr =
(textmode ? atof(args[*argp]) : *(double *)
args[*argp]);
break;
case SQL_BIGINT:
ptr = apr_palloc(pool, sizeof(apr_int64_t));
len = sizeof(apr_int64_t);
*(apr_int64_t *)ptr =
(textmode ?
apr_atoi64(args[*argp]) : *(apr_int64_t *)args[*argp]);
break;
default:
return APR_EGENERAL;
}
(*argp)++;
}
}
rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
baseType, len, 0, ptr, len, indicator);
CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
statement->stmt);
return rc;
}
static void odbc_lob_bucket_destroy(void *data)
{
odbc_bucket *bd = data;
if (apr_bucket_shared_destroy(bd))
apr_bucket_free(bd);
}
static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
{
odbc_bucket *bd = (odbc_bucket *)e->data;
if (apr_pool_is_ancestor(bd->row->pool, pool))
return APR_SUCCESS;
return apr_bucket_setaside_notimpl(e, pool);
}
static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
apr_size_t *len, apr_read_type_e block)
{
SQLRETURN rc;
SQLLEN len_indicator;
SQLSMALLINT type;
odbc_bucket *bd = (odbc_bucket *)e->data;
apr_bucket *nxt;
void *buf;
int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
int eos;
type = bd->row->res->coltypes[bd->col];
type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
if (bufsize < APR_BUCKET_BUFF_SIZE)
bufsize = APR_BUCKET_BUFF_SIZE;
buf = apr_bucket_alloc(bufsize, e->list);
*str = NULL;
*len = 0;
rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
type, buf, bufsize,
&len_indicator);
CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
SQL_HANDLE_STMT, bd->row->res->stmt);
if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
len_indicator = 0;
if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
if (rc == SQL_SUCCESS_WITH_INFO
&& (len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize)) {
*len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
eos = 0;
}
else {
*len = (len_indicator > bufsize
&& len_indicator >= (SQLLEN)e->start)
? (len_indicator - (SQLLEN)e->start) : len_indicator;
eos = 1;
}
if (!eos) {
nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
APR_BUCKET_INIT(nxt);
nxt->length = -1;
nxt->data = e->data;
nxt->type = &odbc_bucket_type;
nxt->free = apr_bucket_free;
nxt->list = e->list;
nxt->start = e->start + *len;
APR_BUCKET_INSERT_AFTER(e, nxt);
}
else {
odbc_lob_bucket_destroy(e->data);
}
apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
*str = buf;
rc = SQL_SUCCESS;
}
return APR_FROM_SQL_RESULT(rc);
}
static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
SQLSMALLINT type, apr_bucket_brigade *bb)
{
apr_bucket_alloc_t *list = bb->bucket_alloc;
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
apr_bucket *eos = apr_bucket_eos_create(list);
bd->row = row;
bd->col = col;
bd->type = type;
APR_BUCKET_INIT(b);
b->type = &odbc_bucket_type;
b->free = apr_bucket_free;
b->list = list;
b = apr_bucket_shared_make(b, bd, 0, -1);
APR_BRIGADE_INSERT_TAIL(bb, b);
APR_BRIGADE_INSERT_TAIL(bb, eos);
return APR_SUCCESS;
}
static void *odbc_get(const apr_dbd_row_t *row, const int col,
const SQLSMALLINT sqltype)
{
SQLRETURN rc;
SQLLEN indicator;
int state = row->res->colstate[col];
int options = row->res->apr_dbd->dboptions;
switch (state) {
case (COL_UNAVAIL):
return (void *)-1;
case (COL_RETRIEVED):
return NULL;
case (COL_BOUND):
case (COL_PRESENT):
if (sqltype == row->res->coltypes[col]) {
row->res->colstate[col] = COL_RETRIEVED;
return (row->res->colinds[col] == SQL_NULL_DATA) ?
NULL : row->res->colptrs[col];
}
}
if (!(options & SQL_GD_ANY_ORDER)) {
int i;
for (i = 0; i < col; i++) {
if (row->res->colstate[i] == COL_AVAIL) {
if (IS_LOB(row->res->coltypes[i]))
row->res->colstate[i] = COL_UNAVAIL;
else {
odbc_get(row, i, row->res->coltypes[i]);
row->res->colstate[i] = COL_PRESENT;
}
}
}
}
if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
return (void *)-1;
if (!row->res->colptrs[col])
row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
row->res->colsizes[col], &indicator);
CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
row->res->stmt);
if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
return NULL;
if (SQL_SUCCEEDED(rc)) {
row->res->coltypes[col] = sqltype;
row->res->colstate[col] =
(rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
return row->res->colptrs[col];
}
else
return (void *)-1;
}
static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
int *connect, SQLCHAR **datasource,
SQLCHAR **user, SQLCHAR **password,
int *defaultBufferSize, int *nattrs,
int **attrs, int **attrvals)
{
char *seps, *last, *next, *name[MAX_PARAMS], *val[MAX_PARAMS];
int nparams = 0, i, j;
*attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
*attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(int));
*nattrs = 0;
seps = DEFAULTSEPS;
name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
if (!name[nparams])
return SQL_SUCCESS;
do {
if (last[strspn(last, seps)] == CSINGLEQUOTE) {
last += strspn(last, seps);
seps=SSINGLEQUOTE;
}
val[nparams] = apr_strtok(NULL, seps, &last);
seps = DEFAULTSEPS;
++nparams;
next = apr_strtok(NULL, seps, &last);
if (!next) {
break;
}
if (nparams >= MAX_PARAMS) {
return APR_EGENERAL;
}
name[nparams] = next;
} while (1);
for (j = i = 0; i < nparams; i++) {
if (!apr_strnatcasecmp(name[i], "CONNECT")) {
*datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
*connect = 1;
}
else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) {
*datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
*connect = 0;
}
else if (!apr_strnatcasecmp(name[i], "USER")) {
*user = (SQLCHAR *)apr_pstrdup(pool, val[i]);
}
else if (!apr_strnatcasecmp(name[i], "PASSWORD")) {
*password = (SQLCHAR *)apr_pstrdup(pool, val[i]);
}
else if (!apr_strnatcasecmp(name[i], "BUFSIZE")) {
*defaultBufferSize = atoi(val[i]);
}
else if (!apr_strnatcasecmp(name[i], "ACCESS")) {
if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
(*attrvals)[j] = SQL_MODE_READ_ONLY;
else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
(*attrvals)[j] = SQL_MODE_READ_WRITE;
else
return SQL_ERROR;
(*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
}
else if (!apr_strnatcasecmp(name[i], "CTIMEOUT")) {
(*attrvals)[j] = atoi(val[i]);
(*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
}
else if (!apr_strnatcasecmp(name[i], "STIMEOUT")) {
(*attrvals)[j] = atoi(val[i]);
(*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
}
else if (!apr_strnatcasecmp(name[i], "TXMODE")) {
if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
(*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
(*attrvals)[j] = SQL_TXN_READ_COMMITTED;
else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
(*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
(*attrvals)[j] = SQL_TXN_SERIALIZABLE;
else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
continue;
else
return SQL_ERROR;
(*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
}
else
return SQL_ERROR;
}
*nattrs = j;
return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
}
static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
SQLSMALLINT type, SQLHANDLE h, int line)
{
SQLCHAR buffer[512];
SQLCHAR sqlstate[128];
SQLINTEGER native;
SQLSMALLINT reslength;
char *res, *p, *end, *logval = NULL;
int i;
if (rc == SQL_SUCCESS) {
char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
apr_size_t successMsgLen = sizeof successMsg - 1;
dbc->lasterrorcode = SQL_SUCCESS;
apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError);
apr_cpystrn(dbc->lastError + successMsgLen, step,
sizeof dbc->lastError - successMsgLen);
return;
}
switch (rc) {
case SQL_INVALID_HANDLE:
res = "SQL_INVALID_HANDLE";
break;
case SQL_ERROR:
res = "SQL_ERROR";
break;
case SQL_SUCCESS_WITH_INFO:
res = "SQL_SUCCESS_WITH_INFO";
break;
case SQL_STILL_EXECUTING:
res = "SQL_STILL_EXECUTING";
break;
case SQL_NEED_DATA:
res = "SQL_NEED_DATA";
break;
case SQL_NO_DATA:
res = "SQL_NO_DATA";
break;
default:
res = "unrecognized SQL return code";
}
if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA
&& dbc->can_commit != APR_DBD_TRANSACTION_IGNORE_ERRORS) {
dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK;
}
p = dbc->lastError;
end = p + sizeof(dbc->lastError);
dbc->lasterrorcode = rc;
p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
step, res, rc, SOURCE_FILE, line - 1);
for (i = 1, rc = 0; rc == 0; i++) {
rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
sizeof(buffer), &reslength);
if (SQL_SUCCEEDED(rc) && (p < (end - 280)))
p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
}
apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
if (logval || !dbc->dbname ) {
char timestamp[APR_CTIME_LEN];
apr_file_t *se;
apr_ctime(timestamp, apr_time_now());
apr_file_open_stderr(&se, dbc->pool);
apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
}
}
static APR_INLINE int odbc_check_rollback(apr_dbd_t *handle)
{
if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) {
handle->lasterrorcode = SQL_ERROR;
apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ",
sizeof handle->lastError);
return 1;
}
return 0;
}
static void odbc_init(apr_pool_t *pool)
{
SQLRETURN rc;
char *step;
apr_version_t apuver;
apu_version(&apuver);
if (apuver.major != DRIVER_APU_VERSION_MAJOR
|| apuver.minor != DRIVER_APU_VERSION_MINOR) {
apr_file_t *se;
apr_file_open_stderr(&se, pool);
apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
"Attempt to load APU version %d.%d driver with APU version %d.%d\n",
DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
apuver.major, apuver.minor);
abort();
}
if (henv)
return;
step = "SQLAllocHandle (SQL_HANDLE_ENV)";
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
if (SQL_SUCCEEDED(rc)) {
step = "SQLSetEnvAttr";
rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, 0);
}
else {
apr_dbd_t tmp_dbc;
SQLHANDLE err_h = henv;
tmp_dbc.pool = pool;
tmp_dbc.dbname = NULL;
CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
}
}
static void *odbc_native_handle(apr_dbd_t *handle)
{
return handle->dbc;
}
static apr_dbd_t *odbc_open(apr_pool_t *pool, const char *params, const char **error)
{
SQLRETURN rc;
SQLHANDLE hdbc = NULL;
apr_dbd_t *handle;
char *err_step;
int err_htype, i;
int defaultBufferSize = DEFAULT_BUFFER_SIZE;
SQLHANDLE err_h = NULL;
SQLCHAR *datasource = (SQLCHAR *)"", *user = (SQLCHAR *)"",
*password = (SQLCHAR *)"";
int nattrs = 0, *attrs = NULL, *attrvals = NULL, connect = 0;
err_step = "SQLAllocHandle (SQL_HANDLE_DBC)";
err_htype = SQL_HANDLE_ENV;
err_h = henv;
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
if (SQL_SUCCEEDED(rc)) {
err_step = "Invalid DBD Parameters - open";
err_htype = SQL_HANDLE_DBC;
err_h = hdbc;
rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
&password, &defaultBufferSize, &nattrs, &attrs,
&attrvals);
}
if (SQL_SUCCEEDED(rc)) {
for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) {
err_step = "SQLSetConnectAttr (from DBD Parameters)";
err_htype = SQL_HANDLE_DBC;
err_h = hdbc;
rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0);
}
}
if (SQL_SUCCEEDED(rc)) {
if (connect) {
SQLCHAR out[1024];
SQLSMALLINT outlen;
err_step = "SQLDriverConnect";
err_htype = SQL_HANDLE_DBC;
err_h = hdbc;
rc = SQLDriverConnect(hdbc, NULL, datasource,
(SQLSMALLINT)strlen((char *)datasource),
out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
}
else {
err_step = "SQLConnect";
err_htype = SQL_HANDLE_DBC;
err_h = hdbc;
rc = SQLConnect(hdbc, datasource,
(SQLSMALLINT)strlen((char *)datasource),
user, (SQLSMALLINT)strlen((char *)user),
password, (SQLSMALLINT)strlen((char *)password));
}
}
if (SQL_SUCCEEDED(rc)) {
handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
handle->dbname = apr_pstrdup(pool, (char *)datasource);
handle->dbc = hdbc;
handle->pool = pool;
handle->defaultBufferSize = defaultBufferSize;
CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
handle->default_transaction_mode = 0;
handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
&(handle->default_transaction_mode), sizeof(int), NULL);
handle->transaction_mode = handle->default_transaction_mode;
SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
sizeof(int), NULL);
apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
return handle;
}
else {
apr_dbd_t tmp_dbc;
tmp_dbc.pool = pool;
tmp_dbc.dbname = NULL;
CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
if (error)
*error = apr_pstrdup(pool, tmp_dbc.lastError);
if (hdbc)
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
return NULL;
}
}
static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
{
SQLUINTEGER isDead;
SQLRETURN rc;
rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
sizeof(SQLUINTEGER), NULL);
CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
SQL_HANDLE_DBC, handle->dbc);
if (rc != SQL_SUCCESS)
return APR_ENOTIMPL;
return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
}
static int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle,
const char *name)
{
if (apr_strnatcmp(name, handle->dbname)) {
return APR_EGENERAL;
}
CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
handle->dbc);
return APR_SUCCESS;
}
static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
apr_dbd_transaction_t **trans)
{
SQLRETURN rc = SQL_SUCCESS;
if (handle->transaction_mode) {
rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION,
(SQLPOINTER)handle->transaction_mode, 0);
CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
SQL_HANDLE_DBC, handle->dbc);
}
if (SQL_SUCCEEDED(rc)) {
rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
SQL_AUTOCOMMIT_OFF, 0);
CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
SQL_HANDLE_DBC, handle->dbc);
}
if (SQL_SUCCEEDED(rc)) {
*trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
(*trans)->dbc = handle->dbc;
(*trans)->apr_dbd = handle;
}
handle->can_commit = APR_DBD_TRANSACTION_COMMIT;
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_end_transaction(apr_dbd_transaction_t *trans)
{
SQLRETURN rc;
int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK)
? SQL_COMMIT : SQL_ROLLBACK;
rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
if (SQL_SUCCEEDED(rc)) {
rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
rc, SQL_HANDLE_DBC, trans->dbc);
}
trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
{
SQLRETURN rc;
SQLHANDLE hstmt = NULL;
size_t len = strlen(statement);
if (odbc_check_rollback(handle))
return APR_EGENERAL;
rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
handle->dbc);
if (!SQL_SUCCEEDED(rc))
return APR_FROM_SQL_RESULT(rc);
rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
if (SQL_SUCCEEDED(rc)) {
SQLLEN rowcount;
rc = SQLRowCount(hstmt, &rowcount);
*nrows = (int)rowcount;
CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
}
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
apr_dbd_results_t **res, const char *statement,
int random)
{
SQLRETURN rc;
SQLHANDLE hstmt;
apr_dbd_prepared_t *stmt;
size_t len = strlen(statement);
if (odbc_check_rollback(handle))
return APR_EGENERAL;
rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
handle->dbc);
if (!SQL_SUCCEEDED(rc))
return APR_FROM_SQL_RESULT(rc);
stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
stmt->apr_dbd = handle;
stmt->dbc = handle->dbc;
stmt->stmt = hstmt;
apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
if (random) {
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
(SQLPOINTER)SQL_SCROLLABLE, 0);
CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
SQL_HANDLE_STMT, hstmt);
}
if (SQL_SUCCEEDED(rc)) {
rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
}
if (SQL_SUCCEEDED(rc)) {
rc = odbc_create_results(handle, hstmt, pool, random, res);
apr_pool_cleanup_register(pool, *res,
odbc_close_results, apr_pool_cleanup_null);
}
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_num_cols(apr_dbd_results_t *res)
{
return res->ncols;
}
static int odbc_num_tuples(apr_dbd_results_t *res)
{
SQLRETURN rc;
SQLLEN nrows;
rc = SQLRowCount(res->stmt, &nrows);
CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
return SQL_SUCCEEDED(rc) ? (int)nrows : -1;
}
static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
apr_dbd_row_t **row, int rownum)
{
SQLRETURN rc;
char *fetchtype;
int c;
*row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
(*row)->stmt = res->stmt;
(*row)->dbc = res->dbc;
(*row)->res = res;
(*row)->pool = res->pool;
for (c = 0; c < res->ncols; c++) {
if (res->colstate[c] != COL_BOUND) {
res->colstate[c] = COL_AVAIL;
}
if (res->colptrs[c])
*(char *)res->colptrs[c] = 0;
}
if (res->random && (rownum > 0)) {
fetchtype = "SQLFetchScroll";
rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
}
else {
fetchtype = "SQLFetch";
rc = SQLFetch(res->stmt);
}
CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
(*row)->stmt = res->stmt;
if (!SQL_SUCCEEDED(rc) && !res->random) {
odbc_close_results(res);
return -1;
}
return SQL_SUCCEEDED(rc) ? 0 : -1;
}
static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col,
apr_dbd_type_e dbdtype, void *data)
{
SQLSMALLINT sqltype;
void *p;
int len;
if (col >= row->res->ncols)
return APR_EGENERAL;
if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
data = NULL;
return APR_EGENERAL;
}
len = sqlSizes[dbdtype];
sqltype = sqlCtype[dbdtype];
if (IS_LOB(sqltype))
return odbc_create_bucket(row, col, sqltype, data);
p = odbc_get(row, col, sqltype);
if (p == (void *)-1)
return APR_EGENERAL;
if (p == NULL)
return APR_ENOENT;
if (len < 0)
*(char**)data = (char *)p;
else
memcpy(data, p, len);
return APR_SUCCESS;
}
static const char *odbc_get_entry(const apr_dbd_row_t *row, int col)
{
void *p;
if (col >= row->res->ncols)
return NULL;
p = odbc_get(row, col, SQL_C_CHAR);
if (p == NULL || p == (void *)-1)
return p;
else
return apr_pstrdup(row->pool, p);
}
static const char *odbc_error(apr_dbd_t *handle, int errnum)
{
return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
}
static const char *odbc_escape(apr_pool_t *pool, const char *s,
apr_dbd_t *handle)
{
char *newstr, *src, *dst, *sq;
int qcount;
if (!(sq = strchr(s, '\'')))
return (char *)s;
for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
qcount++;
newstr = apr_palloc(pool, strlen(s) + qcount + 1);
src = (char *)s;
for (dst = newstr; *src; src++) {
if ((*dst++ = *src) == '\'')
*dst++ = '\'';
}
*dst = 0;
return newstr;
}
static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle,
const char *query, const char *label, int nargs,
int nvals, apr_dbd_type_e *types,
apr_dbd_prepared_t **statement)
{
SQLRETURN rc;
size_t len = strlen(query);
if (odbc_check_rollback(handle))
return APR_EGENERAL;
*statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
(*statement)->dbc = handle->dbc;
(*statement)->apr_dbd = handle;
(*statement)->nargs = nargs;
(*statement)->nvals = nvals;
(*statement)->types =
apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
apr_pool_cleanup_register(pool, *statement,
odbc_close_pstmt, apr_pool_cleanup_null);
CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
SQL_HANDLE_DBC, handle->dbc);
rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len);
CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
(*statement)->stmt);
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
apr_dbd_prepared_t *statement, const char **args)
{
SQLRETURN rc = SQL_SUCCESS;
int i, argp;
if (odbc_check_rollback(handle))
return APR_EGENERAL;
for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
&argp, (const void **)args, TEXTMODE);
}
if (SQL_SUCCEEDED(rc)) {
rc = SQLExecute(statement->stmt);
CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
statement->stmt);
}
if (SQL_SUCCEEDED(rc)) {
SQLLEN rowcount;
rc = SQLRowCount(statement->stmt, &rowcount);
*nrows = (int)rowcount;
CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
statement->stmt);
}
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
apr_dbd_prepared_t *statement, va_list args)
{
const char **values;
int i;
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++)
values[i] = va_arg(args, const char *);
return odbc_pquery(pool, handle, nrows, statement, values);
}
static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle,
apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
int random, const char **args)
{
SQLRETURN rc = SQL_SUCCESS;
int i, argp;
if (odbc_check_rollback(handle))
return APR_EGENERAL;
if (random) {
rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
(SQLPOINTER)SQL_SCROLLABLE, 0);
CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
rc, SQL_HANDLE_STMT, statement->stmt);
}
if (SQL_SUCCEEDED(rc)) {
for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
&argp, (const void **)args, TEXTMODE);
}
}
if (SQL_SUCCEEDED(rc)) {
rc = SQLExecute(statement->stmt);
CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
statement->stmt);
}
if (SQL_SUCCEEDED(rc)) {
rc = odbc_create_results(handle, statement->stmt, pool, random, res);
apr_pool_cleanup_register(pool, *res,
odbc_close_results, apr_pool_cleanup_null);
}
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle,
apr_dbd_results_t **res,
apr_dbd_prepared_t *statement, int random,
va_list args)
{
const char **values;
int i;
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++)
values[i] = va_arg(args, const char *);
return odbc_pselect(pool, handle, res, statement, random, values);
}
static const char *odbc_get_name(const apr_dbd_results_t *res, int col)
{
SQLRETURN rc;
char buffer[MAX_COLUMN_NAME];
SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
SQLULEN colsize;
if (col >= res->ncols)
return NULL;
if (res->colnames[col] != NULL)
return res->colnames[col];
rc = SQLDescribeCol(res->stmt, col + 1,
(SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
&coltype, &colsize, &coldecimal, &colnullable);
CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
SQL_HANDLE_STMT, res->stmt);
res->colnames[col] = apr_pstrdup(res->pool, buffer);
return res->colnames[col];
}
static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans)
{
return (int)trans->apr_dbd->can_commit;
}
static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
{
int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS
| APR_DBD_TRANSACTION_COMMIT
| APR_DBD_TRANSACTION_ROLLBACK);
if ((mode & legal) != mode)
return APR_EGENERAL;
trans->apr_dbd->can_commit = mode;
return APR_SUCCESS;
}
static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
apr_dbd_prepared_t *statement, const void **args)
{
SQLRETURN rc = SQL_SUCCESS;
int i, argp;
if (odbc_check_rollback(handle))
return APR_EGENERAL;
for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
&argp, args, BINARYMODE);
if (SQL_SUCCEEDED(rc)) {
rc = SQLExecute(statement->stmt);
CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
statement->stmt);
}
if (SQL_SUCCEEDED(rc)) {
SQLLEN rowcount;
rc = SQLRowCount(statement->stmt, &rowcount);
*nrows = (int)rowcount;
CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
statement->stmt);
}
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle,
apr_dbd_results_t **res,
apr_dbd_prepared_t *statement,
int random, const void **args)
{
SQLRETURN rc = SQL_SUCCESS;
int i, argp;
if (odbc_check_rollback(handle))
return APR_EGENERAL;
if (random) {
rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
(SQLPOINTER)SQL_SCROLLABLE, 0);
CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
rc, SQL_HANDLE_STMT, statement->stmt);
}
if (SQL_SUCCEEDED(rc)) {
for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
&argp, args, BINARYMODE);
}
}
if (SQL_SUCCEEDED(rc)) {
rc = SQLExecute(statement->stmt);
CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
statement->stmt);
}
if (SQL_SUCCEEDED(rc)) {
rc = odbc_create_results(handle, statement->stmt, pool, random, res);
apr_pool_cleanup_register(pool, *res,
odbc_close_results, apr_pool_cleanup_null);
}
return APR_FROM_SQL_RESULT(rc);
}
static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
apr_dbd_prepared_t *statement, va_list args)
{
const char **values;
int i;
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++)
values[i] = va_arg(args, const char *);
return odbc_pbquery(pool, handle, nrows, statement, (const void **)values);
}
static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle,
apr_dbd_results_t **res,
apr_dbd_prepared_t *statement,
int random, va_list args)
{
const char **values;
int i;
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++)
values[i] = va_arg(args, const char *);
return odbc_pbselect(pool, handle, res, statement, random, (const void **)values);
}
APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
ODBC_DRIVER_STRING,
odbc_init,
odbc_native_handle,
odbc_open,
odbc_check_conn,
odbc_close,
odbc_set_dbname,
odbc_start_transaction,
odbc_end_transaction,
odbc_query,
odbc_select,
odbc_num_cols,
odbc_num_tuples,
odbc_get_row,
odbc_get_entry,
odbc_error,
odbc_escape,
odbc_prepare,
odbc_pvquery,
odbc_pvselect,
odbc_pquery,
odbc_pselect,
odbc_get_name,
odbc_transaction_mode_get,
odbc_transaction_mode_set,
"?",
odbc_pvbquery,
odbc_pvbselect,
odbc_pbquery,
odbc_pbselect,
odbc_datum_get
};
#endif