freebsd-dev/contrib/apr-util/dbd/apr_dbd_oracle.c
Peter Wemm 937a200089 Introduce svnlite so that we can check out our source code again.
This is actually a fully functional build except:
* All internal shared libraries are static linked to make sure there
  is no interference with ports (and to reduce build time).
* It does not have the python/perl/etc plugin or API support.
* By default, it installs as "svnlite" rather than "svn".
* If WITH_SVN added in make.conf, you get "svn".
* If WITHOUT_SVNLITE is in make.conf, this is completely disabled.

To be absolutely clear, this is not intended for any use other than
checking out freebsd source and committing, like we once did with cvs.

It should be usable for small scale local repositories that don't
need the python/perl plugin architecture.
2013-06-18 02:53:45 +00:00

2221 lines
72 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.
*/
/* Developed initially by Nick Kew and Chris Darroch.
* Contributed to the APR project by kind permission of
* Pearson Education Core Technology Group (CTG),
* formerly Central Media Group (CMG).
*/
/* apr_dbd_oracle - a painful attempt
*
* Based first on the documentation at
* http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/toc.htm
*
* Those docs have a lot of internal inconsistencies, contradictions, etc
* So I've snarfed the demo programs (from Oracle 8, not included in
* the current downloadable oracle), and used code from them.
*
* Why do cdemo81.c and cdemo82.c do the same thing in very different ways?
* e.g. cdemo82 releases all its handle on shutdown; cdemo81 doesn't
*
* All the ORA* functions return a "sword". Some of them are documented;
* others aren't. So I've adopted a policy of using switch statements
* everywhere, even when we're not doing anything with the return values.
*
* This makes no attempt at performance tuning, such as setting
* prefetch cache size. We need some actual performance data
* to make that meaningful. Input from someone with experience
* as a sysop using oracle would be a good start.
*/
/* shut compiler up */
#ifdef DEBUG
#define int_errorcode int errorcode
#else
#define int_errorcode
#endif
#include "apu.h"
#if APU_HAVE_ORACLE
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <oci.h>
#include "apr_strings.h"
#include "apr_lib.h"
#include "apr_time.h"
#include "apr_hash.h"
#include "apr_buckets.h"
#define TRANS_TIMEOUT 30
#define MAX_ARG_LEN 256 /* in line with other apr_dbd drivers. We alloc this
* lots of times, so a large value gets hungry.
* Should really make it configurable
*/
#define DEFAULT_LONG_SIZE 4096
#define DBD_ORACLE_MAX_COLUMNS 256
#define NUMERIC_FIELD_SIZE 32
#define CHECK_CONN_QUERY "SELECT 1 FROM dual"
#define ERR_BUF_SIZE 200
#ifdef DEBUG
#include <stdio.h>
#endif
#include "apr_dbd_internal.h"
/* declarations */
static const char *dbd_oracle_error(apr_dbd_t *sql, int n);
static int dbd_oracle_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);
static int outputParams(apr_dbd_t*, apr_dbd_prepared_t*);
static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql,
apr_dbd_results_t **results,
apr_dbd_prepared_t *statement,
int seek, const char **values);
static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
int *nrows, apr_dbd_prepared_t *statement,
const char **values);
static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
apr_dbd_transaction_t **trans);
static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans);
struct apr_dbd_transaction_t {
int mode;
enum { TRANS_NONE, TRANS_ERROR, TRANS_1, TRANS_2 } status;
apr_dbd_t *handle;
OCITrans *trans;
OCISnapshot *snapshot1;
OCISnapshot *snapshot2;
};
struct apr_dbd_results_t {
apr_pool_t *pool;
apr_dbd_t* handle;
unsigned int rownum;
int seek;
int nrows;
apr_dbd_prepared_t *statement;
};
struct apr_dbd_t {
sword status;
OCIError *err;
OCIServer *svr;
OCISvcCtx *svc;
OCISession *auth;
apr_dbd_transaction_t* trans;
apr_pool_t *pool;
char buf[ERR_BUF_SIZE]; /* for error messages */
apr_size_t long_size;
apr_dbd_prepared_t *check_conn_stmt;
};
struct apr_dbd_row_t {
int n;
apr_dbd_results_t *res;
apr_pool_t *pool;
};
typedef struct {
apr_dbd_type_e type;
sb2 ind;
sb4 len;
OCIBind *bind;
union {
void *raw;
char *sval;
int ival;
unsigned int uval;
double fval;
OCILobLocator *lobval;
} value;
} bind_arg;
typedef struct {
int type;
sb2 ind;
ub2 len; /* length of actual output */
OCIDefine *defn;
apr_size_t sz; /* length of buf for output */
union {
void *raw;
char *sval;
OCILobLocator *lobval;
} buf;
const char *name;
} define_arg;
struct apr_dbd_prepared_t {
OCIStmt *stmt;
int nargs;
int nvals;
bind_arg *args;
int nout;
define_arg *out;
apr_dbd_t *handle;
apr_pool_t *pool;
ub2 type;
};
/* AFAICT from the docs, the OCIEnv thingey can be used async
* across threads, so lets have a global one.
*
* We'll need shorter-lived envs to deal with requests and connections
*
* Hmmm, that doesn't work: we don't have a usermem framework.
* OK, forget about using APR pools here, until we figure out
* the right way to do it (if such a thing exists).
*/
static OCIEnv *dbd_oracle_env = NULL;
/* Oracle specific bucket for BLOB/CLOB types */
typedef struct apr_bucket_lob apr_bucket_lob;
/**
* A bucket referring to a Oracle BLOB/CLOB
*/
struct apr_bucket_lob {
/** Number of buckets using this memory */
apr_bucket_refcount refcount;
/** The row this bucket refers to */
const apr_dbd_row_t *row;
/** The column this bucket refers to */
int col;
/** The pool into which any needed structures should
* be created while reading from this bucket */
apr_pool_t *readpool;
};
static void lob_bucket_destroy(void *data);
static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
apr_size_t *len, apr_read_type_e block);
static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
const apr_dbd_row_t *row, int col,
apr_off_t offset, apr_size_t len,
apr_pool_t *p);
static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
apr_off_t offset,
apr_size_t len, apr_pool_t *p,
apr_bucket_alloc_t *list);
static const apr_bucket_type_t apr_bucket_type_lob = {
"LOB", 5, APR_BUCKET_DATA,
lob_bucket_destroy,
lob_bucket_read,
apr_bucket_setaside_notimpl,
apr_bucket_shared_split,
apr_bucket_shared_copy
};
static void lob_bucket_destroy(void *data)
{
apr_bucket_lob *f = data;
if (apr_bucket_shared_destroy(f)) {
/* no need to destroy database objects here; it will get
* done automatically when the pool gets cleaned up */
apr_bucket_free(f);
}
}
static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
apr_size_t *len, apr_read_type_e block)
{
apr_bucket_lob *a = e->data;
const apr_dbd_row_t *row = a->row;
apr_dbd_results_t *res = row->res;
int col = a->col;
apr_bucket *b = NULL;
apr_size_t blength = e->length; /* bytes remaining in file past offset */
apr_off_t boffset = e->start;
define_arg *val = &res->statement->out[col];
apr_dbd_t *sql = res->handle;
/* Only with 10g, unfortunately
oraub8 length = APR_BUCKET_BUFF_SIZE;
*/
ub4 length = APR_BUCKET_BUFF_SIZE;
char *buf = NULL;
*str = NULL; /* in case we die prematurely */
/* fetch from offset if not at the beginning */
buf = apr_palloc(row->pool, APR_BUCKET_BUFF_SIZE);
sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
&length, 1 + (size_t)boffset,
(dvoid*) buf, APR_BUCKET_BUFF_SIZE,
NULL, NULL, 0, SQLCS_IMPLICIT);
/* Only with 10g, unfortunately
sql->status = OCILobRead2(sql->svc, sql->err, val->buf.lobval,
&length, NULL, 1 + boffset,
(dvoid*) buf, APR_BUCKET_BUFF_SIZE,
OCI_ONE_PIECE, NULL, NULL, 0, SQLCS_IMPLICIT);
*/
if (sql->status != OCI_SUCCESS) {
return APR_EGENERAL;
}
blength -= length;
*len = length;
*str = buf;
/*
* Change the current bucket to refer to what we read,
* even if we read nothing because we hit EOF.
*/
apr_bucket_pool_make(e, *str, *len, res->pool);
/* If we have more to read from the field, then create another bucket */
if (blength > 0) {
/* for efficiency, we can just build a new apr_bucket struct
* to wrap around the existing LOB bucket */
b = apr_bucket_alloc(sizeof(*b), e->list);
b->start = boffset + *len;
b->length = blength;
b->data = a;
b->type = &apr_bucket_type_lob;
b->free = apr_bucket_free;
b->list = e->list;
APR_BUCKET_INSERT_AFTER(e, b);
}
else {
lob_bucket_destroy(a);
}
return APR_SUCCESS;
}
static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
const apr_dbd_row_t *row, int col,
apr_off_t offset, apr_size_t len,
apr_pool_t *p)
{
apr_bucket_lob *f;
f = apr_bucket_alloc(sizeof(*f), b->list);
f->row = row;
f->col = col;
f->readpool = p;
b = apr_bucket_shared_make(b, f, offset, len);
b->type = &apr_bucket_type_lob;
return b;
}
static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
apr_off_t offset,
apr_size_t len, apr_pool_t *p,
apr_bucket_alloc_t *list)
{
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
APR_BUCKET_INIT(b);
b->free = apr_bucket_free;
b->list = list;
return apr_bucket_lob_make(b, row, col, offset, len, p);
}
static apr_status_t dbd_free_lobdesc(void *lob)
{
switch (OCIDescriptorFree(lob, OCI_DTYPE_LOB)) {
case OCI_SUCCESS:
return APR_SUCCESS;
default:
return APR_EGENERAL;
}
}
static apr_status_t dbd_free_snapshot(void *snap)
{
switch (OCIDescriptorFree(snap, OCI_DTYPE_SNAP)) {
case OCI_SUCCESS:
return APR_SUCCESS;
default:
return APR_EGENERAL;
}
}
static void dbd_oracle_init(apr_pool_t *pool)
{
if (dbd_oracle_env == NULL) {
/* Sadly, OCI_SHARED seems to be impossible to use, due to
* various Oracle bugs. See, for example, Oracle MetaLink bug 2972890
* and PHP bug http://bugs.php.net/bug.php?id=23733
*/
#ifdef OCI_NEW_LENGTH_SEMANTICS
OCIEnvCreate(&dbd_oracle_env, OCI_THREADED|OCI_NEW_LENGTH_SEMANTICS,
NULL, NULL, NULL, NULL, 0, NULL);
#else
OCIEnvCreate(&dbd_oracle_env, OCI_THREADED,
NULL, NULL, NULL, NULL, 0, NULL);
#endif
}
}
static apr_dbd_t *dbd_oracle_open(apr_pool_t *pool, const char *params,
const char **error)
{
apr_dbd_t *ret = apr_pcalloc(pool, sizeof(apr_dbd_t));
int errorcode;
char *BLANK = "";
struct {
const char *field;
char *value;
} fields[] = {
{"user", BLANK},
{"pass", BLANK},
{"dbname", BLANK},
{"server", BLANK},
{NULL, NULL}
};
int i;
const char *ptr;
const char *key;
size_t klen;
const char *value;
size_t vlen;
static const char *const delims = " \r\n\t;|,";
ret->pool = pool;
ret->long_size = DEFAULT_LONG_SIZE;
/* snitch parsing from the MySQL driver */
for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
/* don't dereference memory that may not belong to us */
if (ptr == params) {
++ptr;
continue;
}
for (key = ptr-1; apr_isspace(*key); --key);
klen = 0;
while (apr_isalpha(*key)) {
if (key == params) {
/* Don't parse off the front of the params */
--key;
++klen;
break;
}
--key;
++klen;
}
++key;
for (value = ptr+1; apr_isspace(*value); ++value);
vlen = strcspn(value, delims);
for (i=0; fields[i].field != NULL; ++i) {
if (!strncasecmp(fields[i].field, key, klen)) {
fields[i].value = apr_pstrndup(pool, value, vlen);
break;
}
}
ptr = value+vlen;
}
ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->err,
OCI_HTYPE_ERROR, 0, NULL);
switch (ret->status) {
default:
#ifdef DEBUG
printf("ret->status is %d\n", ret->status);
break;
#else
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svr,
OCI_HTYPE_SERVER, 0, NULL);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (alloc svr): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svc,
OCI_HTYPE_SVCCTX, 0, NULL);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (alloc svc): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
/* All the examples use the #else */
#if CAN_DO_LOGIN
ret->status = OCILogon(dbd_oracle_env, ret->err, &ret->svc, fields[0].value,
strlen(fields[0].value), fields[1].value,
strlen(fields[1].value), fields[2].value,
strlen(fields[2].value));
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR: %s\n", ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
#else
ret->status = OCIServerAttach(ret->svr, ret->err, (text*) fields[3].value,
strlen(fields[3].value), OCI_DEFAULT);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (server attach): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->svr, 0,
OCI_ATTR_SERVER, ret->err);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (attr set): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->auth,
OCI_HTYPE_SESSION, 0, NULL);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (alloc auth): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[0].value,
strlen(fields[0].value), OCI_ATTR_USERNAME, ret->err);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (attr username): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCIAttrSet(ret->auth, OCI_HTYPE_SESSION, fields[1].value,
strlen(fields[1].value), OCI_ATTR_PASSWORD, ret->err);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (attr password): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCISessionBegin(ret->svc, ret->err, ret->auth,
OCI_CRED_RDBMS, OCI_DEFAULT);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (session begin): %s\n", ret->status, ret->buf);
break;
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
case OCI_SUCCESS:
break;
}
ret->status = OCIAttrSet(ret->svc, OCI_HTYPE_SVCCTX, ret->auth, 0,
OCI_ATTR_SESSION, ret->err);
switch (ret->status) {
default:
#ifdef DEBUG
OCIErrorGet(ret->err, 1, NULL, &errorcode, ret->buf,
sizeof(ret->buf), OCI_HTYPE_ERROR);
printf("OPEN ERROR %d (attr session): %s\n", ret->status, ret->buf);
#else
if (error) {
*error = apr_pcalloc(pool, ERR_BUF_SIZE);
OCIErrorGet(ret->err, 1, NULL, &errorcode, (unsigned char*)(*error),
ERR_BUF_SIZE, OCI_HTYPE_ERROR);
}
return NULL;
#endif
break;
case OCI_SUCCESS:
break;
}
#endif
if(dbd_oracle_prepare(pool, ret, CHECK_CONN_QUERY, NULL, 0, 0, NULL,
&ret->check_conn_stmt) != 0) {
return NULL;
}
return ret;
}
#ifdef EXPORT_NATIVE_FUNCS
static apr_size_t dbd_oracle_long_size_set(apr_dbd_t *sql,
apr_size_t long_size)
{
apr_size_t old_size = sql->long_size;
sql->long_size = long_size;
return old_size;
}
#endif
static const char *dbd_oracle_get_name(const apr_dbd_results_t *res, int n)
{
define_arg *val = &res->statement->out[n];
if ((n < 0) || (n >= res->statement->nout)) {
return NULL;
}
return val->name;
}
static int dbd_oracle_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
apr_dbd_row_t **rowp, int rownum)
{
apr_dbd_row_t *row = *rowp;
apr_dbd_t *sql = res->handle;
int_errorcode;
if (row == NULL) {
row = apr_palloc(pool, sizeof(apr_dbd_row_t));
*rowp = row;
row->res = res;
/* Oracle starts counting at 1 according to the docs */
row->n = res->seek ? rownum : 1;
row->pool = pool;
}
else {
if (res->seek) {
row->n = rownum;
}
else {
++row->n;
}
}
if (res->seek) {
sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
OCI_FETCH_ABSOLUTE, row->n, OCI_DEFAULT);
}
else {
sql->status = OCIStmtFetch2(res->statement->stmt, res->handle->err, 1,
OCI_FETCH_NEXT, 0, OCI_DEFAULT);
}
switch (sql->status) {
case OCI_SUCCESS:
(*rowp)->res = res;
return 0;
case OCI_NO_DATA:
return -1;
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Execute error %d: %s\n", sql->status, sql->buf);
#endif
/* fallthrough */
default:
return 1;
}
return 0;
}
static const char *dbd_oracle_error(apr_dbd_t *sql, int n)
{
/* This is ugly. Needs us to pass in a buffer of unknown size.
* Either we put it on the handle, or we have to keep allocing/copying
*/
sb4 errorcode;
switch (sql->status) {
case OCI_SUCCESS:
return "OCI_SUCCESS";
case OCI_SUCCESS_WITH_INFO:
return "OCI_SUCCESS_WITH_INFO";
case OCI_NEED_DATA:
return "OCI_NEED_DATA";
case OCI_NO_DATA:
return "OCI_NO_DATA";
case OCI_INVALID_HANDLE:
return "OCI_INVALID_HANDLE";
case OCI_STILL_EXECUTING:
return "OCI_STILL_EXECUTING";
case OCI_CONTINUE:
return "OCI_CONTINUE";
}
switch (OCIErrorGet(sql->err, 1, NULL, &errorcode,
(text*) sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) {
case OCI_SUCCESS:
return sql->buf;
default:
return "internal error: OCIErrorGet failed";
}
}
static apr_status_t freeStatement(void *statement)
{
int rv = APR_SUCCESS;
OCIStmt *stmt = ((apr_dbd_prepared_t*)statement)->stmt;
#ifdef PREPARE2
OCIError *err;
if (OCIHandleAlloc(dbd_oracle_env, (dvoid**)&err, OCI_HTYPE_ERROR,
0, NULL) != OCI_SUCCESS) {
return APR_EGENERAL;
}
if (OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS) {
rv = APR_EGENERAL;
}
if (OCIHandleFree(err, OCI_HTYPE_ERROR) != OCI_SUCCESS) {
rv = APR_EGENERAL;
}
#else
if (OCIHandleFree(stmt, OCI_HTYPE_STMT) != OCI_SUCCESS) {
rv = APR_EGENERAL;
}
#endif
return rv;
}
static int dbd_oracle_select(apr_pool_t *pool, apr_dbd_t *sql,
apr_dbd_results_t **results,
const char *query, int seek)
{
int ret = 0;
apr_dbd_prepared_t *statement = NULL;
ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement);
if (ret != 0) {
return ret;
}
ret = dbd_oracle_pselect(pool, sql, results, statement, seek, NULL);
if (ret != 0) {
return ret;
}
return ret;
}
static int dbd_oracle_query(apr_dbd_t *sql, int *nrows, const char *query)
{
int ret = 0;
apr_pool_t *pool;
apr_dbd_prepared_t *statement = NULL;
if (sql->trans && sql->trans->status == TRANS_ERROR) {
return 1;
}
/* make our own pool so that APR allocations don't linger and so that
* both Stmt and LOB handles are cleaned up (LOB handles may be
* allocated when preparing APR_DBD_TYPE_CLOB/BLOBs)
*/
apr_pool_create(&pool, sql->pool);
ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement);
if (ret == 0) {
ret = dbd_oracle_pquery(pool, sql, nrows, statement, NULL);
if (ret == 0) {
sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT,
nrows, 0, OCI_ATTR_ROW_COUNT,
sql->err);
}
}
apr_pool_destroy(pool);
return ret;
}
static const char *dbd_oracle_escape(apr_pool_t *pool, const char *arg,
apr_dbd_t *sql)
{
return arg; /* OCI has no concept of string escape */
}
static int dbd_oracle_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)
{
int ret = 0;
int i;
apr_dbd_prepared_t *stmt ;
if (*statement == NULL) {
*statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
}
stmt = *statement;
stmt->handle = sql;
stmt->pool = pool;
stmt->nargs = nargs;
stmt->nvals = nvals;
/* populate our own args, if any */
if (nargs > 0) {
stmt->args = apr_pcalloc(pool, nargs*sizeof(bind_arg));
for (i = 0; i < nargs; i++) {
stmt->args[i].type = types[i];
}
}
sql->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**) &stmt->stmt,
OCI_HTYPE_STMT, 0, NULL);
if (sql->status != OCI_SUCCESS) {
return 1;
}
sql->status = OCIStmtPrepare(stmt->stmt, sql->err, (text*) query,
strlen(query), OCI_NTV_SYNTAX, OCI_DEFAULT);
if (sql->status != OCI_SUCCESS) {
OCIHandleFree(stmt->stmt, OCI_HTYPE_STMT);
return 1;
}
apr_pool_cleanup_register(pool, stmt, freeStatement,
apr_pool_cleanup_null);
/* Perl gets statement type here */
sql->status = OCIAttrGet(stmt->stmt, OCI_HTYPE_STMT, &stmt->type, 0,
OCI_ATTR_STMT_TYPE, sql->err);
if (sql->status != OCI_SUCCESS) {
return 1;
}
/* Perl sets PREFETCH_MEMORY here, but the docs say there's a working default */
#if 0
sql->status = OCIAttrSet(stmt->stmt, OCI_HTYPE_STMT, &prefetch_size,
sizeof(prefetch_size), OCI_ATTR_PREFETCH_MEMORY,
sql->err);
if (sql->status != OCI_SUCCESS) {
return 1;
}
#endif
if (stmt->type == OCI_STMT_SELECT) {
ret = outputParams(sql, stmt);
}
return ret;
}
static void dbd_oracle_bind(apr_dbd_prepared_t *statement, const char **values)
{
OCIStmt *stmt = statement->stmt;
apr_dbd_t *sql = statement->handle;
int i, j;
sb2 null_ind = -1;
for (i = 0, j = 0; i < statement->nargs; i++, j++) {
if (values[j] == NULL) {
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
NULL, 0, SQLT_STR,
&null_ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
}
else {
switch (statement->args[i].type) {
case APR_DBD_TYPE_BLOB:
{
char *data = (char *)values[j];
int size = atoi((char*)values[++j]);
/* skip table and column for now */
j += 2;
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
data, size, SQLT_LBI,
&statement->args[i].ind,
NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
}
break;
case APR_DBD_TYPE_CLOB:
{
char *data = (char *)values[j];
int size = atoi((char*)values[++j]);
/* skip table and column for now */
j += 2;
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
data, size, SQLT_LNG,
&statement->args[i].ind,
NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
}
break;
default:
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
(dvoid*) values[j],
strlen(values[j]) + 1,
SQLT_STR,
&statement->args[i].ind,
NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
}
}
if (sql->status != OCI_SUCCESS) {
return;
}
}
return;
}
static int outputParams(apr_dbd_t *sql, apr_dbd_prepared_t *stmt)
{
OCIParam *parms;
int i;
ub2 paramtype[DBD_ORACLE_MAX_COLUMNS];
ub2 paramsize[DBD_ORACLE_MAX_COLUMNS];
char *paramname[DBD_ORACLE_MAX_COLUMNS];
ub4 paramnamelen[DBD_ORACLE_MAX_COLUMNS];
int_errorcode;
/* Perl uses 0 where we used 1 */
sql->status = OCIStmtExecute(sql->svc, stmt->stmt, sql->err, 0, 0,
NULL, NULL, OCI_DESCRIBE_ONLY);
switch (sql->status) {
case OCI_SUCCESS:
case OCI_SUCCESS_WITH_INFO:
break;
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Describing prepared statement: %s\n", sql->buf);
#endif
default:
return 1;
}
while (sql->status == OCI_SUCCESS) {
sql->status = OCIParamGet(stmt->stmt, OCI_HTYPE_STMT,
sql->err, (dvoid**)&parms, stmt->nout+1);
switch (sql->status) {
case OCI_SUCCESS:
sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
&paramtype[stmt->nout],
0, OCI_ATTR_DATA_TYPE, sql->err);
sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
&paramsize[stmt->nout],
0, OCI_ATTR_DATA_SIZE, sql->err);
sql->status = OCIAttrGet(parms, OCI_DTYPE_PARAM,
&paramname[stmt->nout],
&paramnamelen[stmt->nout],
OCI_ATTR_NAME, sql->err);
++stmt->nout;
}
}
switch (sql->status) {
case OCI_SUCCESS:
break;
case OCI_ERROR:
break; /* this is what we expect at end-of-loop */
default:
return 1;
}
/* OK, the above works. We have the params; now OCIDefine them */
stmt->out = apr_palloc(stmt->pool, stmt->nout*sizeof(define_arg));
for (i=0; i<stmt->nout; ++i) {
stmt->out[i].type = paramtype[i];
stmt->out[i].len = stmt->out[i].sz = paramsize[i];
stmt->out[i].name = apr_pstrmemdup(stmt->pool,
paramname[i], paramnamelen[i]);
switch (stmt->out[i].type) {
default:
switch (stmt->out[i].type) {
case SQLT_NUM: /* 2: numeric, Perl worst case=130+38+3 */
stmt->out[i].sz = 171;
break;
case SQLT_CHR: /* 1: char */
case SQLT_AFC: /* 96: ANSI fixed char */
stmt->out[i].sz *= 4; /* ugh, wasteful UCS-4 handling */
break;
case SQLT_DAT: /* 12: date, depends on NLS date format */
stmt->out[i].sz = 75;
break;
case SQLT_BIN: /* 23: raw binary, perhaps UTF-16? */
stmt->out[i].sz *= 2;
break;
case SQLT_RID: /* 11: rowid */
case SQLT_RDD: /* 104: rowid descriptor */
stmt->out[i].sz = 20;
break;
case SQLT_TIMESTAMP: /* 187: timestamp */
case SQLT_TIMESTAMP_TZ: /* 188: timestamp with time zone */
case SQLT_INTERVAL_YM: /* 189: interval year-to-month */
case SQLT_INTERVAL_DS: /* 190: interval day-to-second */
case SQLT_TIMESTAMP_LTZ: /* 232: timestamp with local time zone */
stmt->out[i].sz = 75;
break;
default:
#ifdef DEBUG
printf("Unsupported data type: %d\n", stmt->out[i].type);
#endif
break;
}
++stmt->out[i].sz;
stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
sql->err, i+1,
stmt->out[i].buf.sval,
stmt->out[i].sz, SQLT_STR,
&stmt->out[i].ind, &stmt->out[i].len,
0, OCI_DEFAULT);
break;
case SQLT_LNG: /* 8: long */
stmt->out[i].sz = sql->long_size * 4 + 4; /* ugh, UCS-4 handling */
stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
sql->err, i+1,
stmt->out[i].buf.raw,
stmt->out[i].sz, SQLT_LVC,
&stmt->out[i].ind, NULL,
0, OCI_DEFAULT);
break;
case SQLT_LBI: /* 24: long binary, perhaps UTF-16? */
stmt->out[i].sz = sql->long_size * 2 + 4; /* room for int prefix */
stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz);
sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
sql->err, i+1,
stmt->out[i].buf.raw,
stmt->out[i].sz, SQLT_LVB,
&stmt->out[i].ind, NULL,
0, OCI_DEFAULT);
break;
case SQLT_BLOB: /* 113 */
case SQLT_CLOB: /* 112 */
/*http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96584/oci05bnd.htm#434937*/
sql->status = OCIDescriptorAlloc(dbd_oracle_env,
(dvoid**)&stmt->out[i].buf.lobval,
OCI_DTYPE_LOB, 0, NULL);
apr_pool_cleanup_register(stmt->pool, stmt->out[i].buf.lobval,
dbd_free_lobdesc,
apr_pool_cleanup_null);
sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn,
sql->err, i+1,
(dvoid*) &stmt->out[i].buf.lobval,
-1, stmt->out[i].type,
&stmt->out[i].ind, &stmt->out[i].len,
0, OCI_DEFAULT);
break;
}
switch (sql->status) {
case OCI_SUCCESS:
break;
default:
return 1;
}
}
return 0;
}
static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
int *nrows, apr_dbd_prepared_t *statement,
const char **values)
{
OCISnapshot *oldsnapshot = NULL;
OCISnapshot *newsnapshot = NULL;
apr_dbd_transaction_t* trans = sql->trans;
int exec_mode;
int_errorcode;
if (trans) {
switch (trans->status) {
case TRANS_ERROR:
return -1;
case TRANS_NONE:
trans = NULL;
break;
case TRANS_1:
oldsnapshot = trans->snapshot1;
newsnapshot = trans->snapshot2;
trans->status = TRANS_2;
break;
case TRANS_2:
oldsnapshot = trans->snapshot2;
newsnapshot = trans->snapshot1;
trans->status = TRANS_1;
break;
}
exec_mode = OCI_DEFAULT;
}
else {
exec_mode = OCI_COMMIT_ON_SUCCESS;
}
dbd_oracle_bind(statement, values);
sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
oldsnapshot, newsnapshot, exec_mode);
switch (sql->status) {
case OCI_SUCCESS:
break;
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Execute error %d: %s\n", sql->status, sql->buf);
#endif
/* fallthrough */
default:
if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
}
sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
OCI_ATTR_ROW_COUNT, sql->err);
return 0;
}
static int dbd_oracle_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->status == TRANS_ERROR) {
return -1;
}
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++) {
values[i] = va_arg(args, const char*);
}
return dbd_oracle_pquery(pool, sql, nrows, statement, values);
}
static int dbd_oracle_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 exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
OCISnapshot *oldsnapshot = NULL;
OCISnapshot *newsnapshot = NULL;
apr_dbd_transaction_t* trans = sql->trans;
int_errorcode;
if (trans) {
switch (trans->status) {
case TRANS_ERROR:
return 1;
case TRANS_NONE:
trans = NULL;
break;
case TRANS_1:
oldsnapshot = trans->snapshot1;
newsnapshot = trans->snapshot2;
trans->status = TRANS_2;
break;
case TRANS_2:
oldsnapshot = trans->snapshot2;
newsnapshot = trans->snapshot1;
trans->status = TRANS_1;
break;
}
}
dbd_oracle_bind(statement, values);
sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
oldsnapshot, newsnapshot, exec_mode);
switch (sql->status) {
case OCI_SUCCESS:
break;
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Executing prepared statement: %s\n", sql->buf);
#endif
/* fallthrough */
default:
if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
}
if (!*results) {
*results = apr_palloc(pool, sizeof(apr_dbd_results_t));
}
(*results)->handle = sql;
(*results)->statement = statement;
(*results)->seek = seek;
(*results)->rownum = seek ? 0 : -1;
(*results)->pool = pool;
return 0;
}
static int dbd_oracle_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->status == TRANS_ERROR) {
return -1;
}
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++) {
values[i] = va_arg(args, const char*);
}
return dbd_oracle_pselect(pool, sql, results, statement, seek, values);
}
static void dbd_oracle_bbind(apr_dbd_prepared_t * statement,
const void **values)
{
OCIStmt *stmt = statement->stmt;
apr_dbd_t *sql = statement->handle;
int i, j;
sb2 null_ind = -1;
apr_dbd_type_e type;
for (i = 0, j = 0; i < statement->nargs; i++, j++) {
type = (values[j] == NULL ? APR_DBD_TYPE_NULL
: statement->args[i].type);
switch (type) {
case APR_DBD_TYPE_TINY:
statement->args[i].value.ival = *(char*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.ival,
sizeof(statement->args[i].value.ival),
SQLT_INT,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_UTINY:
statement->args[i].value.uval = *(unsigned char*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.uval,
sizeof(statement->args[i].value.uval),
SQLT_UIN,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_SHORT:
statement->args[i].value.ival = *(short*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.ival,
sizeof(statement->args[i].value.ival),
SQLT_INT,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_USHORT:
statement->args[i].value.uval = *(unsigned short*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.uval,
sizeof(statement->args[i].value.uval),
SQLT_UIN,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_INT:
statement->args[i].value.ival = *(int*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.ival,
sizeof(statement->args[i].value.ival),
SQLT_INT,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_UINT:
statement->args[i].value.uval = *(unsigned int*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.uval,
sizeof(statement->args[i].value.uval),
SQLT_UIN,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_LONG:
statement->args[i].value.sval =
apr_psprintf(statement->pool, "%ld", *(long*)values[j]);
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
statement->args[i].value.sval,
strlen(statement->args[i].value.sval)+1,
SQLT_STR,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_ULONG:
statement->args[i].value.sval =
apr_psprintf(statement->pool, "%lu",
*(unsigned long*)values[j]);
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
statement->args[i].value.sval,
strlen(statement->args[i].value.sval)+1,
SQLT_STR,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_LONGLONG:
statement->args[i].value.sval =
apr_psprintf(statement->pool, "%" APR_INT64_T_FMT,
*(apr_int64_t*)values[j]);
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
statement->args[i].value.sval,
strlen(statement->args[i].value.sval)+1,
SQLT_STR,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_ULONGLONG:
statement->args[i].value.sval =
apr_psprintf(statement->pool, "%" APR_UINT64_T_FMT,
*(apr_uint64_t*)values[j]);
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
statement->args[i].value.sval,
strlen(statement->args[i].value.sval)+1,
SQLT_UIN,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_FLOAT:
statement->args[i].value.fval = *(float*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.fval,
sizeof(statement->args[i].value.fval),
SQLT_FLT,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_DOUBLE:
statement->args[i].value.fval = *(double*)values[j];
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
&statement->args[i].value.fval,
sizeof(statement->args[i].value.fval),
SQLT_FLT,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
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:
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
(dvoid*) values[j],
strlen(values[j]) + 1,
SQLT_STR,
&statement->args[i].ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
case APR_DBD_TYPE_BLOB:
{
char *data = (char *)values[j];
apr_size_t size = *(apr_size_t*)values[++j];
/* skip table and column for now */
j += 2;
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
data, size, SQLT_LBI,
&statement->args[i].ind,
NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
}
break;
case APR_DBD_TYPE_CLOB:
{
char *data = (char *)values[j];
apr_size_t size = *(apr_size_t*)values[++j];
/* skip table and column for now */
j += 2;
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
data, size, SQLT_LNG,
&statement->args[i].ind,
NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
}
break;
case APR_DBD_TYPE_NULL:
default:
sql->status = OCIBindByPos(stmt, &statement->args[i].bind,
sql->err, i + 1,
NULL, 0, SQLT_STR,
&null_ind, NULL,
(ub2) 0, (ub4) 0,
(ub4 *) 0, OCI_DEFAULT);
break;
}
if (sql->status != OCI_SUCCESS) {
return;
}
}
return;
}
static int dbd_oracle_pbquery(apr_pool_t * pool, apr_dbd_t * sql,
int *nrows, apr_dbd_prepared_t * statement,
const void **values)
{
OCISnapshot *oldsnapshot = NULL;
OCISnapshot *newsnapshot = NULL;
apr_dbd_transaction_t* trans = sql->trans;
int exec_mode;
int_errorcode;
if (trans) {
switch (trans->status) {
case TRANS_ERROR:
return -1;
case TRANS_NONE:
trans = NULL;
break;
case TRANS_1:
oldsnapshot = trans->snapshot1;
newsnapshot = trans->snapshot2;
trans->status = TRANS_2;
break;
case TRANS_2:
oldsnapshot = trans->snapshot2;
newsnapshot = trans->snapshot1;
trans->status = TRANS_1;
break;
}
exec_mode = OCI_DEFAULT;
}
else {
exec_mode = OCI_COMMIT_ON_SUCCESS;
}
dbd_oracle_bbind(statement, values);
sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0,
oldsnapshot, newsnapshot, exec_mode);
switch (sql->status) {
case OCI_SUCCESS:
break;
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Execute error %d: %s\n", sql->status, sql->buf);
#endif
/* fallthrough */
default:
if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
}
sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0,
OCI_ATTR_ROW_COUNT, sql->err);
return 0;
}
static int dbd_oracle_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->status == TRANS_ERROR) {
return -1;
}
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++) {
values[i] = va_arg(args, const void*);
}
return dbd_oracle_pbquery(pool, sql, nrows, statement, values);
}
static int dbd_oracle_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 exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
OCISnapshot *oldsnapshot = NULL;
OCISnapshot *newsnapshot = NULL;
apr_dbd_transaction_t* trans = sql->trans;
int_errorcode;
if (trans) {
switch (trans->status) {
case TRANS_ERROR:
return 1;
case TRANS_NONE:
trans = NULL;
break;
case TRANS_1:
oldsnapshot = trans->snapshot1;
newsnapshot = trans->snapshot2;
trans->status = TRANS_2;
break;
case TRANS_2:
oldsnapshot = trans->snapshot2;
newsnapshot = trans->snapshot1;
trans->status = TRANS_1;
break;
}
}
dbd_oracle_bbind(statement, values);
sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
oldsnapshot, newsnapshot, exec_mode);
switch (sql->status) {
case OCI_SUCCESS:
break;
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Executing prepared statement: %s\n", sql->buf);
#endif
/* fallthrough */
default:
if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
}
if (!*results) {
*results = apr_palloc(pool, sizeof(apr_dbd_results_t));
}
(*results)->handle = sql;
(*results)->statement = statement;
(*results)->seek = seek;
(*results)->rownum = seek ? 0 : -1;
(*results)->pool = pool;
return 0;
}
static int dbd_oracle_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->status == TRANS_ERROR) {
return -1;
}
values = apr_palloc(pool, sizeof(*values) * statement->nvals);
for (i = 0; i < statement->nvals; i++) {
values[i] = va_arg(args, const void*);
}
return dbd_oracle_pbselect(pool, sql, results, statement, seek, values);
}
static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
apr_dbd_transaction_t **trans)
{
int ret = 0;
int_errorcode;
if (*trans) {
dbd_oracle_end_transaction(*trans);
}
else {
*trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
OCIHandleAlloc(dbd_oracle_env, (dvoid**)&(*trans)->trans,
OCI_HTYPE_TRANS, 0, 0);
OCIAttrSet(sql->svc, OCI_HTYPE_SVCCTX, (*trans)->trans, 0,
OCI_ATTR_TRANS, sql->err);
}
sql->status = OCITransStart(sql->svc, sql->err, TRANS_TIMEOUT,
OCI_TRANS_NEW);
switch (sql->status) {
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode, sql->buf,
sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Transaction: %s\n", sql->buf);
#endif
ret = 1;
break;
case OCI_SUCCESS:
(*trans)->handle = sql;
(*trans)->status = TRANS_1;
sql->trans = *trans;
switch (OCIDescriptorAlloc(dbd_oracle_env,
(dvoid**)&(*trans)->snapshot1,
OCI_DTYPE_SNAP, 0, NULL)) {
case OCI_SUCCESS:
apr_pool_cleanup_register(pool, (*trans)->snapshot1,
dbd_free_snapshot, apr_pool_cleanup_null);
break;
case OCI_INVALID_HANDLE:
ret = 1;
break;
}
switch (OCIDescriptorAlloc(dbd_oracle_env,
(dvoid**)&(*trans)->snapshot2,
OCI_DTYPE_SNAP, 0, NULL)) {
case OCI_SUCCESS:
apr_pool_cleanup_register(pool, (*trans)->snapshot2,
dbd_free_snapshot, apr_pool_cleanup_null);
break;
case OCI_INVALID_HANDLE:
ret = 1;
break;
}
break;
default:
ret = 1;
break;
}
return ret;
}
static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans)
{
int ret = 1; /* no transaction is an error cond */
sword status;
apr_dbd_t *handle = trans->handle;
if (trans) {
switch (trans->status) {
case TRANS_NONE: /* No trans is an error here */
status = OCI_ERROR;
break;
case TRANS_ERROR:
status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
break;
default:
/* rollback on explicit rollback request */
if (TXN_DO_ROLLBACK(trans)) {
status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
} else {
status = OCITransCommit(handle->svc, handle->err, OCI_DEFAULT);
}
break;
}
handle->trans = NULL;
switch (status) {
case OCI_SUCCESS:
ret = 0;
break;
default:
ret = 3;
break;
}
}
return ret;
}
static int dbd_oracle_transaction_mode_get(apr_dbd_transaction_t *trans)
{
if (!trans)
return APR_DBD_TRANSACTION_COMMIT;
return trans->mode;
}
static int dbd_oracle_transaction_mode_set(apr_dbd_transaction_t *trans,
int mode)
{
if (!trans)
return APR_DBD_TRANSACTION_COMMIT;
return trans->mode = (mode & TXN_MODE_BITS);
}
/* This doesn't work for BLOB because of NULLs, but it can fake it
* if the BLOB is really a string
*/
static const char *dbd_oracle_get_entry(const apr_dbd_row_t *row, int n)
{
ub4 len = 0;
ub1 csform = 0;
ub2 csid = 0;
apr_size_t buflen = 0;
char *buf = NULL;
define_arg *val = &row->res->statement->out[n];
apr_dbd_t *sql = row->res->handle;
int_errorcode;
if ((n < 0) || (n >= row->res->statement->nout) || (val->ind == -1)) {
return NULL;
}
switch (val->type) {
case SQLT_BLOB:
case SQLT_CLOB:
sql->status = OCILobGetLength(sql->svc, sql->err, val->buf.lobval,
&len);
switch (sql->status) {
case OCI_SUCCESS:
case OCI_SUCCESS_WITH_INFO:
if (len == 0) {
buf = "";
}
break;
case OCI_ERROR:
#ifdef DEBUG
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Finding LOB length: %s\n", sql->buf);
break;
#endif
default:
break;
}
if (len == 0) {
break;
}
if (val->type == APR_DBD_TYPE_CLOB) {
#if 1
/* Is this necessary, or can it be defaulted? */
sql->status = OCILobCharSetForm(dbd_oracle_env, sql->err,
val->buf.lobval, &csform);
if (sql->status == OCI_SUCCESS) {
sql->status = OCILobCharSetId(dbd_oracle_env, sql->err,
val->buf.lobval, &csid);
}
switch (sql->status) {
case OCI_SUCCESS:
case OCI_SUCCESS_WITH_INFO:
buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
/* zeroise all - where the string ends depends on charset */
buf = apr_pcalloc(row->pool, buflen);
break;
#ifdef DEBUG
case OCI_ERROR:
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Reading LOB character set: %s\n", sql->buf);
break; /*** XXX?? ***/
#endif
default:
break; /*** XXX?? ***/
}
#else /* ignore charset */
buflen = (len+1) * 4; /* ugh, wasteful UCS-4 handling */
/* zeroise all - where the string ends depends on charset */
buf = apr_pcalloc(row->pool, buflen);
#endif
} else {
/* BUG: this'll only work if the BLOB looks like a string */
buflen = len;
buf = apr_palloc(row->pool, buflen+1);
buf[buflen] = 0;
}
if (!buf) {
break;
}
sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
&len, 1, (dvoid*) buf, buflen,
NULL, NULL, csid, csform);
switch (sql->status) {
case OCI_SUCCESS:
case OCI_SUCCESS_WITH_INFO:
break;
#ifdef DEBUG
case OCI_ERROR:
OCIErrorGet(sql->err, 1, NULL, &errorcode,
sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
printf("Reading LOB: %s\n", sql->buf);
buf = NULL; /*** XXX?? ***/
break;
#endif
default:
buf = NULL; /*** XXX?? ***/
break;
}
break;
case SQLT_LNG:
case SQLT_LBI:
/* raw is struct { ub4 len; char *buf; } */
len = *(ub4*) val->buf.raw;
buf = apr_pstrndup(row->pool, val->buf.sval + sizeof(ub4), len);
break;
default:
buf = apr_pstrndup(row->pool, val->buf.sval, val->len);
break;
}
return (const char*) buf;
}
/* XXX Should this use Oracle proper API instead of calling get_entry()? */
static apr_status_t dbd_oracle_datum_get(const apr_dbd_row_t *row, int n,
apr_dbd_type_e type, void *data)
{
define_arg *val = &row->res->statement->out[n];
const char *entry;
if ((n < 0) || (n >= row->res->statement->nout)) {
return APR_EGENERAL;
}
if(val->ind == -1) {
return APR_ENOENT;
}
switch (type) {
case APR_DBD_TYPE_TINY:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(char*)data = atoi(entry);
break;
case APR_DBD_TYPE_UTINY:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(unsigned char*)data = atoi(entry);
break;
case APR_DBD_TYPE_SHORT:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(short*)data = atoi(entry);
break;
case APR_DBD_TYPE_USHORT:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(unsigned short*)data = atoi(entry);
break;
case APR_DBD_TYPE_INT:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(int*)data = atoi(entry);
break;
case APR_DBD_TYPE_UINT:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(unsigned int*)data = atoi(entry);
break;
case APR_DBD_TYPE_LONG:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(long*)data = atol(entry);
break;
case APR_DBD_TYPE_ULONG:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(unsigned long*)data = atol(entry);
break;
case APR_DBD_TYPE_LONGLONG:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(apr_int64_t*)data = apr_atoi64(entry);
break;
case APR_DBD_TYPE_ULONGLONG:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(apr_uint64_t*)data = apr_atoi64(entry);
break;
case APR_DBD_TYPE_FLOAT:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(float*)data = (float)atof(entry);
break;
case APR_DBD_TYPE_DOUBLE:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(double*)data = atof(entry);
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:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
*(char**)data = (char*)entry;
break;
case APR_DBD_TYPE_BLOB:
case APR_DBD_TYPE_CLOB:
{
apr_bucket *e;
apr_bucket_brigade *b = (apr_bucket_brigade*)data;
apr_dbd_t *sql = row->res->handle;
ub4 len = 0;
switch (val->type) {
case SQLT_BLOB:
case SQLT_CLOB:
sql->status = OCILobGetLength(sql->svc, sql->err,
val->buf.lobval, &len);
switch(sql->status) {
case OCI_SUCCESS:
case OCI_SUCCESS_WITH_INFO:
if (len == 0) {
e = apr_bucket_eos_create(b->bucket_alloc);
}
else {
e = apr_bucket_lob_create(row, n, 0, len,
row->pool, b->bucket_alloc);
}
break;
default:
return APR_ENOENT;
}
break;
default:
entry = dbd_oracle_get_entry(row, n);
if (entry == NULL) {
return APR_ENOENT;
}
e = apr_bucket_pool_create(entry, strlen(entry),
row->pool, b->bucket_alloc);
break;
}
APR_BRIGADE_INSERT_TAIL(b, e);
}
break;
case APR_DBD_TYPE_NULL:
*(void**)data = NULL;
break;
default:
return APR_EGENERAL;
}
return APR_SUCCESS;
}
static apr_status_t dbd_oracle_close(apr_dbd_t *handle)
{
/* FIXME: none of the oracle docs/examples say anything about
* closing/releasing handles. Which seems unlikely ...
*/
/* OK, let's grab from cdemo again.
* cdemo81 does nothing; cdemo82 does OCIHandleFree on the handles
*/
switch (OCISessionEnd(handle->svc, handle->err, handle->auth,
(ub4)OCI_DEFAULT)) {
default:
break;
}
switch (OCIServerDetach(handle->svr, handle->err, (ub4) OCI_DEFAULT )) {
default:
break;
}
/* does OCISessionEnd imply this? */
switch (OCIHandleFree((dvoid *) handle->auth, (ub4) OCI_HTYPE_SESSION)) {
default:
break;
}
switch (OCIHandleFree((dvoid *) handle->svr, (ub4) OCI_HTYPE_SERVER)) {
default:
break;
}
switch (OCIHandleFree((dvoid *) handle->svc, (ub4) OCI_HTYPE_SVCCTX)) {
default:
break;
}
switch (OCIHandleFree((dvoid *) handle->err, (ub4) OCI_HTYPE_ERROR)) {
default:
break;
}
return APR_SUCCESS;
}
static apr_status_t dbd_oracle_check_conn(apr_pool_t *pool, apr_dbd_t *sql)
{
apr_dbd_results_t *res = NULL;
apr_dbd_row_t *row = NULL;
if(dbd_oracle_pselect(pool, sql, &res, sql->check_conn_stmt,
0, NULL) != 0) {
return APR_EGENERAL;
}
if(dbd_oracle_get_row(pool, res, &row, -1) != 0) {
return APR_EGENERAL;
}
if(dbd_oracle_get_row(pool, res, &row, -1) != -1) {
return APR_EGENERAL;
}
return APR_SUCCESS;
}
static int dbd_oracle_select_db(apr_pool_t *pool, apr_dbd_t *handle,
const char *name)
{
/* FIXME: need to find this in the docs */
return APR_ENOTIMPL;
}
static void *dbd_oracle_native(apr_dbd_t *handle)
{
/* FIXME: can we do anything better? Oracle doesn't seem to have
* a concept of a handle in the sense we use it.
*/
return dbd_oracle_env;
}
static int dbd_oracle_num_cols(apr_dbd_results_t* res)
{
return res->statement->nout;
}
static int dbd_oracle_num_tuples(apr_dbd_results_t* res)
{
if (!res->seek) {
return -1;
}
if (res->nrows >= 0) {
return res->nrows;
}
res->handle->status = OCIAttrGet(res->statement->stmt, OCI_HTYPE_STMT,
&res->nrows, 0, OCI_ATTR_ROW_COUNT,
res->handle->err);
return res->nrows;
}
APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_oracle_driver = {
"oracle",
dbd_oracle_init,
dbd_oracle_native,
dbd_oracle_open,
dbd_oracle_check_conn,
dbd_oracle_close,
dbd_oracle_select_db,
dbd_oracle_start_transaction,
dbd_oracle_end_transaction,
dbd_oracle_query,
dbd_oracle_select,
dbd_oracle_num_cols,
dbd_oracle_num_tuples,
dbd_oracle_get_row,
dbd_oracle_get_entry,
dbd_oracle_error,
dbd_oracle_escape,
dbd_oracle_prepare,
dbd_oracle_pvquery,
dbd_oracle_pvselect,
dbd_oracle_pquery,
dbd_oracle_pselect,
dbd_oracle_get_name,
dbd_oracle_transaction_mode_get,
dbd_oracle_transaction_mode_set,
":apr%d",
dbd_oracle_pvbquery,
dbd_oracle_pvbselect,
dbd_oracle_pbquery,
dbd_oracle_pbselect,
dbd_oracle_datum_get
};
#endif