apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From n..@apache.org
Subject svn commit: r345223 - in /apr/apr-util/trunk: build/dbd.m4 dbd/apr_dbd_oracle.c
Date Thu, 17 Nov 2005 11:23:47 GMT
Author: niq
Date: Thu Nov 17 03:23:37 2005
New Revision: 345223

URL: http://svn.apache.org/viewcvs?rev=345223&view=rev
Log:
apr_dbd_oracle driver (Nick Kew and Chris Darroch)

Added:
    apr/apr-util/trunk/dbd/apr_dbd_oracle.c
Modified:
    apr/apr-util/trunk/build/dbd.m4

Modified: apr/apr-util/trunk/build/dbd.m4
URL: http://svn.apache.org/viewcvs/apr/apr-util/trunk/build/dbd.m4?rev=345223&r1=345222&r2=345223&view=diff
==============================================================================
--- apr/apr-util/trunk/build/dbd.m4 (original)
+++ apr/apr-util/trunk/build/dbd.m4 Thu Nov 17 03:23:37 2005
@@ -194,4 +194,42 @@
   fi
 ])
 dnl
+AC_DEFUN([APU_CHECK_DBD_ORACLE], [
+  apu_have_oracle=0
 
+  AC_ARG_WITH([oracle], [
+  --with-oracle=DIR         specify ORACLE_HOME location
+  ], [
+    apu_have_oracle=0
+    if test "$withval" = "yes"; then
+      AC_CHECK_HEADER(oci.h, AC_CHECK_LIB(clntsh, OCILogon, [apu_have_oracle=1]))
+    elif test "$withval" = "no"; then
+      apu_have_oracle=0
+    else
+      CPPFLAGS="-I$withval/rdbms/demo -I$withval/rdbms/public"
+      LIBS="-L$withval/lib "
+
+      AC_MSG_NOTICE(checking for oracle in $withval)
+      AC_CHECK_HEADER(oci.h, AC_CHECK_LIB(clntsh, OCILogon, [apu_have_oracle=1]))
+      if test "$apu_have_oracle" != "0"; then
+        APR_ADDTO(APRUTIL_LDFLAGS, [-L$withval/lib])
+        APR_ADDTO(APRUTIL_LDFLAGS, [-R$withval/lib])
+        APR_ADDTO(APRUTIL_INCLUDES, [-I$withval/rdbms/demo])
+        APR_ADDTO(APRUTIL_INCLUDES, [-I$withval/rdbms/public])
+      fi
+    fi
+  ], [
+    apu_have_oracle=0
+    AC_CHECK_HEADER(oci.h, AC_CHECK_LIB(clntsh, OCILogon, [apu_have_oracle=1]))
+  ])
+
+  AC_SUBST(apu_have_oracle)
+
+  dnl Since we have already done the AC_CHECK_LIB tests, if we have it, 
+  dnl we know the library is there.
+  if test "$apu_have_oracle" = "1"; then
+    APR_ADDTO(APRUTIL_EXPORT_LIBS,[-lclntsh])
+    APR_ADDTO(APRUTIL_LIBS,[-lclntsh])
+  fi
+])
+dnl

Added: apr/apr-util/trunk/dbd/apr_dbd_oracle.c
URL: http://svn.apache.org/viewcvs/apr/apr-util/trunk/dbd/apr_dbd_oracle.c?rev=345223&view=auto
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_oracle.c (added)
+++ apr/apr-util/trunk/dbd/apr_dbd_oracle.c Thu Nov 17 03:23:37 2005
@@ -0,0 +1,1888 @@
+/* Copyright 2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Developed initially by Nick Kew and Chris Darroch.
+ * Contributed to the APR project by kind permission of
+ * Pearson Education 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.
+ */
+
+/*******************************************************************/
+
+/* GLOBAL_PREPARED_STATEMENTS
+ *
+ * This probably needs bindings taken away from prepare and into
+ * execute, or else we'll get race conditions on the parameters
+ * Might be able to do it with some pool refactoring.
+ *
+ * In fact, though the documentation says statements can run with
+ * different interps and threads (IIRC), it doesn't say anything
+ * about race conditions between bind and execute. So we'd better
+ * not even try until and unless someone who knows oracle from the
+ * inside fixes it.
+#define GLOBAL_PREPARED_STATEMENTS
+ */
+
+/* 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_time.h"
+#include "apr_hash.h"
+
+#define TRANS_TIMEOUT 30
+#define MAX_ARG_LEN 256	/*OK what should this be?  */
+#define DBD_ORACLE_MAX_COLUMNS 256
+#define NUMERIC_FIELD_SIZE 32
+
+#define USE_LOB_LOCATOR
+
+typedef enum {
+        APR_DBD_ORACLE_NULL,
+        APR_DBD_ORACLE_STRING,
+        APR_DBD_ORACLE_INT,
+        APR_DBD_ORACLE_FLOAT,
+        APR_DBD_ORACLE_LOB,
+        /* don't work */
+        APR_DBD_ORACLE_BLOB,
+        APR_DBD_ORACLE_CLOB,
+} dbd_field_type;
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#define CHECK_TRANSACTION(trans)                \
+    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;                              \
+        }                                       \
+    }
+
+#include "apr_dbd_internal.h"
+
+#ifdef DEBUG
+#define Static
+#else
+#define Static static
+#endif
+
+/* declarations */
+int dbd_oracle_lob_size(apr_dbd_t *handle, int sz);
+Static int dbd_oracle_prepare(apr_pool_t*, apr_dbd_t*,const char*,const char*,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, int nargs, const char **values);
+Static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+                            int *nrows, apr_dbd_prepared_t *statement,
+                            int nargs, const char **values);
+Static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans);
+Static int dbd_oracle_output_params(apr_dbd_t*, apr_dbd_prepared_t*);
+
+Static int dbd_oracle_query(apr_dbd_t *sql, int *nrows, const char *query);
+Static const char *dbd_oracle_error(apr_dbd_t *sql, int n);
+Static const char *dbd_oracle_get_entry(const apr_dbd_row_t *row, int n);
+Static int dbd_oracle_select(apr_pool_t *pool, apr_dbd_t *sql,
+                            apr_dbd_results_t **results,
+                            const char *query, int seek);
+Static int dbd_oracle_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
+                             apr_dbd_row_t **rowp, int rownum);
+Static int dbd_oracle_num_cols(apr_dbd_results_t* res);
+Static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql,
+                                       apr_dbd_transaction_t **trans);
+Static void dbd_oracle_init(apr_pool_t *pool);
+Static apr_dbd_t *dbd_oracle_open(apr_pool_t *pool, const char *params);
+Static apr_status_t dbd_oracle_close(apr_dbd_t *handle);
+
+struct apr_dbd_transaction_t {
+    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_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[200];        /* for error messages */
+};
+
+struct apr_dbd_row_t {
+    int n;
+    apr_dbd_results_t *res;
+    apr_pool_t *pool;
+};
+
+typedef struct {
+    dbd_field_type type;
+    sb2 ind;
+    ub2 len;
+    OCIBind *bind;
+    union {
+        void *raw;
+        char *stringval;
+        int *ival;
+        double *floatval;
+        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 *stringval;
+        int *ival;
+        double *floatval;
+        OCILobLocator *lobval;
+    } buf;
+} define_arg;
+struct apr_dbd_prepared_t {
+    OCIStmt *stmt;
+    int nargs;
+    bind_arg *args;
+    int nout;
+    define_arg *out;
+    apr_dbd_t *handle;
+    apr_pool_t *pool;
+    int 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 ub4 null = 0;
+Static OCIEnv *dbd_oracle_env = NULL;
+#ifdef GLOBAL_PREPARED_STATEMENTS
+Static apr_hash_t *oracle_statements = NULL;
+#endif
+
+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_terminate(void) {
+    OCITerminate(OCI_DEFAULT);
+}
+#ifdef GLOBAL_PREPARED_STATEMENTS
+static apr_status_t freeStatements(void *ptr)
+{
+    apr_status_t rv = APR_SUCCESS;
+    OCIStmt *stmt;
+    OCIError *err;
+    apr_hash_index_t *index;
+    apr_pool_t *cachepool = apr_hash_pool_get(oracle_statements);
+
+    if (OCIHandleAlloc(dbd_oracle_env, (dvoid**)&err, OCI_HTYPE_ERROR, 0, NULL)
+       != OCI_SUCCESS) {
+        return APR_EGENERAL;
+    }
+
+    for (index = apr_hash_first(cachepool, oracle_statements);
+         index != NULL;
+         index = apr_hash_next(index)) {
+        apr_hash_this(index, NULL, NULL, (void**)&stmt);
+        if (OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS) {
+            rv = APR_EGENERAL;
+        }
+        if (OCIHandleFree(stmt, OCI_HTYPE_STMT) != OCI_SUCCESS) {
+            rv = APR_EGENERAL;
+        }
+    }
+    if (OCIHandleFree(err, OCI_HTYPE_ERROR) != OCI_SUCCESS) {
+        rv = APR_EGENERAL;
+    }
+    return rv;
+}
+#endif
+Static void dbd_oracle_init(apr_pool_t *pool)
+{
+    if (dbd_oracle_env == NULL) {
+#if BELIEVE_THE_DOCS
+        /* The Oracle docs say use OCIEnvCreate.  But that leaves
+         * dbd_oracle_env NULL.  So lets ignore the docs and
+         * snarf something from cdemo81.c instead.
+         */
+        OCIEnvCreate(&dbd_oracle_env, OCI_THREADED|OCI_SHARED, NULL,
+                     NULL, NULL, NULL, 0, NULL);
+#else
+        /* this is from cdemo81.c */
+        OCIInitialize((ub4) OCI_THREADED|OCI_SHARED, (dvoid *)0,
+                      (dvoid * (*)(dvoid *, size_t)) 0,
+                      (dvoid * (*)(dvoid *, dvoid *, size_t))0,
+                      (void (*)(dvoid *, dvoid *)) 0);
+        OCIEnvInit(&dbd_oracle_env, OCI_DEFAULT, 0, NULL);
+#endif
+        atexit(dbd_terminate);
+    }
+#ifdef GLOBAL_PREPARED_STATEMENTS
+    if (oracle_statements == NULL) {
+        
+        oracle_statements = apr_hash_make(pool);
+        apr_pool_cleanup_register(pool, oracle_statements,
+                                  freeStatements, apr_pool_cleanup_null);
+    }
+#endif
+}
+Static apr_dbd_t *dbd_oracle_open(apr_pool_t *pool, const char *params)
+{
+    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;|,";
+
+    /* Use our own pool, to avoid possible race conditions
+     * on pool ops
+     */
+    if (apr_pool_create(&ret->pool, pool) != APR_SUCCESS) {
+        return NULL;
+    }
+
+    /* snitch parsing from the MySQL driver */
+    for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
+        for (key = ptr-1; isspace(*key); --key);
+        klen = 0;
+        while (isalpha(*key)) {
+            --key;
+            ++klen;
+        }
+        ++key;
+        for (value = ptr+1; 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
+        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
+        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
+        return NULL;
+#endif
+    case OCI_SUCCESS:
+        break;
+    }
+#else
+    ret->status = OCIServerAttach(ret->svr, ret->err, 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
+        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
+        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
+        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
+        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
+        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
+        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
+        return NULL;
+#endif
+        break;
+    case OCI_SUCCESS:
+        break;
+    }
+#endif
+    return ret;
+}
+
+
+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,
+                        sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) {
+    case OCI_SUCCESS:
+        return sql->buf; 
+    default:
+        return "internal error: OCIErrorGet failed";
+    }
+}
+
+static int freeStatement(void *statement)
+{
+    int ret = 0;
+
+    apr_dbd_t *sql = ((apr_dbd_prepared_t*)statement)->handle;
+    OCIStmt *stmt = ((apr_dbd_prepared_t*)statement)->stmt;
+    OCIError *err = sql->err;
+
+    sql->status = OCIStmtRelease(stmt, err, NULL, 0, OCI_DEFAULT);
+    switch (sql->status) {
+    case OCI_SUCCESS:
+        break;
+    default:
+        ++ret;
+        break;
+    }
+
+    sql->status = OCIHandleFree(stmt, OCI_HTYPE_STMT);
+    switch (sql->status) {
+    case OCI_SUCCESS:
+        break;
+    default:
+        ++ret;
+        break;
+    }
+
+    return ret;
+}
+
+
+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, &statement);
+    if (ret != 0) {
+        return ret;
+    }
+
+    ret = dbd_oracle_pselect(pool, sql, results, statement, seek, 0, 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_dbd_prepared_t *statement = NULL;
+
+    if (sql->trans && sql->trans->status == TRANS_ERROR) {
+        return 1;
+    }
+
+    ret = dbd_oracle_prepare(sql->pool, sql, query, NULL, &statement);
+    if (ret == 0) {
+        ret = dbd_oracle_pquery(sql->pool, sql, nrows, statement, 0, NULL);
+        if (ret == 0) {
+            sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT,
+                                     nrows, 0, OCI_ATTR_ROW_COUNT,
+                                     sql->err);
+        }
+
+        freeStatement(statement);
+        apr_pool_cleanup_kill(sql->pool, statement, freeStatement);
+    }
+
+    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,
+                             apr_dbd_prepared_t **statement)
+{
+    int ret = 0;
+    size_t length;
+    int i;
+    char *sqlptr;
+    char *orastr;
+    char *oraptr;
+    apr_dbd_prepared_t *stmt ;
+
+/* prepared statements in a global lookup table would be nice,
+ * but we can't do that here because our pool may die leaving
+ * the cached statement orphaned.
+ * OTOH we can do that with Oracle statements, which aren't on
+ * the pool,  so long as we don't register a cleanup on our pool!
+ *
+ * FIXME:
+ * There's a race condition between cache-lookup and cache-set
+ * But the worst outcome is a statement prepared more than once
+ * and leaked.  Is that worth mutexing for?
+ * Hmmm, yes it probably is ...  OK, done
+ */
+
+    if (*statement == NULL) {
+        *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
+    }
+    stmt = *statement;
+    stmt->handle = sql;
+    stmt->pool = pool;
+
+    /* If we have a label, we're going to cache it globally.
+     * Check first if we already have it.  If not, prepare the
+     * statement under mutex, so we don't end up leaking
+     * concurrent statements
+     *
+     * FIXME: Oracle docs say a statement can be used even across
+     * multiple servers, so I assume this is safe .....
+     */
+#ifdef GLOBAL_PREPARED_STATEMENTS
+    if (label != NULL) {
+        stmt->stmt = apr_hash_get(oracle_statements, label, APR_HASH_KEY_STRING);
+        if (stmt->stmt != NULL) {
+            return ret;
+        }
+        apr_dbd_mutex_lock();
+        stmt->stmt = apr_hash_get(oracle_statements, label, APR_HASH_KEY_STRING);
+        if (stmt->stmt != NULL) {
+            apr_dbd_mutex_unlock();
+            return ret;
+        }
+    }
+#endif
+    /* translate from apr_dbd to native query format */
+    for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
+        if ((sqlptr[0] == '%') && isalnum(sqlptr[1])) {
+            ++stmt->nargs;
+        }
+    }
+    length = strlen(query) + 1;
+    if (stmt->nargs > 8) {
+        length += stmt->nargs - 8;
+    }
+    length += 3*stmt->nargs;        /* prefix with "apr" */
+
+    oraptr = orastr = apr_palloc(pool, length);
+
+    /* count native oracle params too */
+    for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
+        if ((sqlptr[0] == ':') && isalpha(sqlptr[1])) {
+            ++stmt->nargs;
+        }
+    }
+    if (stmt->nargs > 0) {
+        stmt->args = apr_pcalloc(pool, stmt->nargs*sizeof(bind_arg));
+        for (i=0; i<stmt->nargs; ++i) {
+            /* SQLT_* stuff documented but inconsistent wiyh observed results */
+            stmt->args[i].type = APR_DBD_ORACLE_STRING; //SQLT_STR;
+        }
+    }
+
+    i = 0;
+    for (sqlptr = (char*)query; *sqlptr; ++sqlptr) {
+        if ((sqlptr[0] == '%') && isalnum(sqlptr[1])) {
+            while (isdigit(*++sqlptr)) {
+                stmt->args[i].len *= 10;
+                stmt->args[i].len += (*sqlptr - '0');
+            }
+            *oraptr++ = ':';
+            *oraptr++ = 'a';
+            *oraptr++ = 'p';
+            *oraptr++ = 'r';
+            if (i < 9) {
+                *oraptr++ = '1' + i;
+            }
+            else {
+                *oraptr++ = '0' + ((i+1)/10);
+                *oraptr++ = '0' + ((i+1)%10);
+            }
+            switch (*sqlptr) {
+            case 'd':
+                stmt->args[i].type = APR_DBD_ORACLE_INT;
+                break;
+            case 'f':
+                stmt->args[i].type = APR_DBD_ORACLE_FLOAT;
+                break;
+            case 'L':
+                stmt->args[i].type = APR_DBD_ORACLE_LOB;
+                break;
+            /* BLOB and CLOB won't work - use LOB instead */
+            case 'C':
+                stmt->args[i].type = APR_DBD_ORACLE_CLOB;
+                break;
+            case 'B':
+                stmt->args[i].type = APR_DBD_ORACLE_BLOB;
+                break;
+            /* default is STRING, but we already set that */
+            default:
+                stmt->args[i].type = APR_DBD_ORACLE_STRING;
+                break;
+            }
+            ++i;
+        }
+        else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) {
+            /* reduce %% to % */
+            *oraptr++ = *sqlptr++;
+        }
+        else {
+            *oraptr++ = *sqlptr;
+        }
+    }
+
+    sql->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**) &stmt->stmt,
+                                 OCI_HTYPE_STMT, 0, NULL);
+    switch (sql->status) {
+    case OCI_SUCCESS:
+        break;
+    default:
+        apr_dbd_mutex_unlock();
+        return 1;
+    }
+
+    sql->status = OCIStmtPrepare(stmt->stmt, sql->err, orastr,
+                                 strlen(orastr), OCI_NTV_SYNTAX, OCI_DEFAULT);
+    switch (sql->status) {
+    case OCI_SUCCESS:
+        break;
+    default:
+        OCIHandleFree(stmt->stmt, OCI_HTYPE_STMT);
+        apr_dbd_mutex_unlock();
+        return 1;
+    }
+    if (label == NULL) {
+        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);
+    switch (sql->status) {
+    case OCI_SUCCESS:
+        break;
+    default:
+        apr_dbd_mutex_unlock();
+        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);
+    switch (sql->status) {
+    case OCI_SUCCESS:
+        break;
+    default:
+        apr_dbd_mutex_unlock();
+        return 1;
+    }
+#endif
+
+    i = 0;
+    sql->status = OCI_SUCCESS;
+    for (sqlptr = (char*)orastr; *sqlptr; ++sqlptr) {
+        if ((sqlptr[0] == ':') && isalpha(sqlptr[1])) {
+            switch (stmt->args[i].type) {
+            default:
+            case APR_DBD_ORACLE_STRING:
+                if (stmt->args[i].len == 0) {
+                    stmt->args[i].len = MAX_ARG_LEN;
+                }
+                stmt->args[i].value.stringval = apr_palloc(pool, stmt->args[i].len);
+                sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
+                                           sql->err, i+1,
+                                           stmt->args[i].value.stringval,
+                                           stmt->args[i].len,
+                                           SQLT_STR,
+                                           &stmt->args[i].ind,
+                                           &stmt->args[i].len,
+                                           (ub2) 0, (ub4) 0,
+                                           (ub4 *) 0, OCI_DEFAULT);
+                break;
+            case APR_DBD_ORACLE_FLOAT:
+                stmt->args[i].value.raw = apr_palloc(pool, NUMERIC_FIELD_SIZE);
+                sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
+                                           sql->err, i+1,
+                                           (void*)&stmt->args[i].value.floatval,
+                                           sizeof(stmt->args[i].value.floatval),
+                                           SQLT_FLT,
+                                           &stmt->args[i].ind,
+                                           &stmt->args[i].len,
+                                           (ub2) 0, (ub4) 0,
+                                           (ub4 *) 0, OCI_DEFAULT);
+                break;
+            case APR_DBD_ORACLE_INT:
+                stmt->args[i].value.raw = apr_palloc(pool, NUMERIC_FIELD_SIZE);
+                sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
+                                           sql->err, i+1,
+                                           (void*)stmt->args[i].value.ival,
+                                           sizeof(*stmt->args[i].value.ival),
+                                           SQLT_INT,
+                                           &stmt->args[i].ind,
+                                           &stmt->args[i].len,
+                                           (ub2) 0, (ub4) 0,
+                                           (ub4 *) 0, OCI_DEFAULT);
+                break;
+            /* lots of examples in the docs use this for LOB
+             * but it relies on knowing the size in advance and
+             * holding it in memory
+             */
+            case APR_DBD_ORACLE_LOB:
+                break;	/* bind LOBs at write-time */
+            /* This is also cited in the docs for LOB, if we don't
+             * want the whole thing in memory
+             */
+            case APR_DBD_ORACLE_BLOB:
+                sql->status = OCIDescriptorAlloc(dbd_oracle_env,
+                                                 (dvoid**)&stmt->args[i].value.lobval,
+                                                 OCI_DTYPE_LOB, 0, NULL);
+                apr_pool_cleanup_register(pool, stmt->args[i].value.lobval,
+                                          dbd_free_lobdesc,
+                                          apr_pool_cleanup_null);
+                sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
+                                           sql->err, i+1,
+                                           (void*) &stmt->args[i].value.lobval,
+                                           -1,
+                                           SQLT_BLOB,
+                                           &stmt->args[i].ind,
+                                           &stmt->args[i].len,
+                                           (ub2) 0, (ub4) 0,
+                                           (ub4 *) 0, OCI_DEFAULT);
+                break;
+            /* This is also cited in the docs for LOB, if we don't
+             * want the whole thing in memory
+             */
+            case APR_DBD_ORACLE_CLOB:
+                sql->status = OCIDescriptorAlloc(dbd_oracle_env,
+                                                 (dvoid**)&stmt->args[i].value.lobval,
+                                                 OCI_DTYPE_LOB, 0, NULL);
+                apr_pool_cleanup_register(pool, stmt->args[i].value.lobval,
+                                          dbd_free_lobdesc,
+                                          apr_pool_cleanup_null);
+                sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind,
+                                           sql->err, i+1,
+                                           (dvoid*) &stmt->args[i].value.lobval,
+                                           -1,
+                                           SQLT_CLOB,
+                                           &stmt->args[i].ind,
+                                           &stmt->args[i].len,
+                                           (ub2) 0, (ub4) 0,
+                                           (ub4 *) 0, OCI_DEFAULT);
+                break;
+            }
+            switch (sql->status) {
+            case OCI_SUCCESS:
+                break;
+            default:
+                apr_dbd_mutex_unlock();
+                return 1;
+            }
+            ++i;
+        }
+    }
+    switch (stmt->type) {
+    case OCI_STMT_SELECT:
+        ret = dbd_oracle_output_params(sql, stmt);
+        break;
+    default:
+        break;
+    }
+#ifdef GLOBAL_PREPARED_STATEMENTS
+    if (label != NULL) {
+        apr_hash_set(oracle_statements, label, APR_HASH_KEY_STRING, stmt->stmt);
+        apr_dbd_mutex_unlock();
+    }
+#endif
+    return ret;
+}
+
+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 *arg = NULL;
+    OCISnapshot *oldsnapshot = NULL;
+    OCISnapshot *newsnapshot = NULL;
+    apr_dbd_transaction_t* trans = sql->trans;
+    int i;
+    int n;
+    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;
+    }
+    for (i=0; i<statement->nargs; ++i) {
+        switch (statement->args[i].type) {
+        case APR_DBD_ORACLE_INT:
+            n = va_arg(args, int);
+            sprintf(statement->args[i].value.stringval, "%d", n);
+            break;
+        case APR_DBD_ORACLE_FLOAT:
+            *statement->args[i].value.floatval = va_arg(args, double);
+            break;
+        case APR_DBD_ORACLE_BLOB:
+        case APR_DBD_ORACLE_CLOB:
+            /* Nothing works */
+            break;
+        case APR_DBD_ORACLE_LOB:
+        /* bind this here and now to avoid big copy, and so we know the size */
+            statement->args[i].value.raw = va_arg(args, char*);
+            statement->args[i].len = strlen(statement->args[i].value.stringval);
+            sql->status = OCIBindByPos(statement->stmt, &statement->args[i].bind,
+                                       sql->err, i+1,
+                                       (void*)statement->args[i].value.raw,
+                                       statement->args[i].len, SQLT_LNG,
+                                       &statement->args[i].ind,
+                                       &statement->args[i].len,
+                                       (ub2) 0, (ub4) 0,
+                                       (ub4 *) 0, OCI_DEFAULT);
+            break;
+        case APR_DBD_ORACLE_STRING:
+        default:
+            arg = va_arg(args, char*);
+            if (strlen(arg) >= statement->args[i].len) {
+                strncpy(statement->args[i].value.stringval, arg,
+                        statement->args[i].len-1);
+            }
+            else {
+                strcpy(statement->args[i].value.stringval, arg);
+            }
+            break;
+        }
+    }
+    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
+        sql->status = 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 (trans) {
+            trans->status = TRANS_ERROR;
+        }
+        return 1;
+    }
+    for (i=0; i<statement->nargs; ++i) {
+        /* what should these really be? */
+        ub1 csfrm = SQLCS_IMPLICIT;
+        ub2 csid = 0;
+        ub4 len = statement->args[i].len;
+        switch (statement->args[i].type) {
+        case APR_DBD_ORACLE_BLOB:
+        case APR_DBD_ORACLE_CLOB:
+            /* doesn't work - use APR_DBD_ORACLE_LOB instead */
+            sql->status = OCILobEnableBuffering(sql->svc, sql->err,statement->args[i].value.lobval);
+#ifdef DEBUG
+            if (sql->status == OCI_ERROR) {
+                sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
+                                          sql->buf, sizeof(sql->buf),
+                                          OCI_HTYPE_ERROR);
+                printf("LOB error %d: %s\n", sql->status, sql->buf);
+            }
+#endif
+            sql->status = OCILobWrite(sql->svc, sql->err,
+                                      statement->args[i].value.lobval,
+                                      &len, 0, (dvoid*) arg, strlen(arg),
+                                      OCI_ONE_PIECE, NULL, NULL, csid, csfrm);
+            break;
+        default:
+            break;
+        }
+        switch (sql->status) {
+        case OCI_SUCCESS:
+            break;
+        case OCI_ERROR:
+#ifdef DEBUG
+            sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
+                                      sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR);
+            printf("LOB error %d: %s\n", sql->status, sql->buf);
+#endif
+            /* fallthrough */
+        default:
+            if (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_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+                            int *nrows, apr_dbd_prepared_t *statement,
+                            int nargs, const char **values)
+{
+    int_errorcode;
+    OCISnapshot *oldsnapshot = NULL;
+    OCISnapshot *newsnapshot = NULL;
+    apr_dbd_transaction_t* trans = sql->trans;
+    int i;
+    int exec_mode;
+
+    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;
+    }
+
+    /* we've bound these vars, so now we just copy data in to them */
+    if (nargs > statement->nargs) {
+        nargs = statement->nargs;
+    }
+    for (i=0; i<nargs; ++i) {
+        switch (statement->args[i].type) {
+        case APR_DBD_ORACLE_INT:
+            sscanf(values[i], "%d", statement->args[i].value.ival);
+            break;
+        case APR_DBD_ORACLE_FLOAT:
+            sscanf(values[i], "%lf", statement->args[i].value.floatval);
+            break;
+        case APR_DBD_ORACLE_BLOB:
+        case APR_DBD_ORACLE_CLOB:
+            sql->status = OCIAttrSet(statement->args[i].value.lobval,
+                                     OCI_DTYPE_LOB, &null, 0,
+                                     OCI_ATTR_LOBEMPTY, sql->err);
+            break;
+        case APR_DBD_ORACLE_LOB:
+        /* bind this here and now to avoid big copy */
+            statement->args[i].value.raw = (char*)values[i];
+            statement->args[i].len = strlen(statement->args[i].value.stringval);
+            sql->status = OCIBindByPos(statement->stmt, &statement->args[i].bind,
+                                       sql->err, i+1,
+                                       (void*)statement->args[i].value.raw,
+                                       statement->args[i].len, SQLT_LNG,
+                                       &statement->args[i].ind,
+                                       &statement->args[i].len,
+                                       (ub2) 0, (ub4) 0,
+                                       (ub4 *) 0, OCI_DEFAULT);
+            break;
+        case APR_DBD_ORACLE_STRING:
+        default:
+            if (strlen(values[i]) >= statement->args[i].len) {
+                strncpy(statement->args[i].value.stringval, values[i],
+                        statement->args[i].len-1);
+            }
+            else {
+                strcpy(statement->args[i].value.stringval, values[i]);
+            }
+            break;
+        }
+    }
+
+    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
+        sql->status = 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 (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_output_params(apr_dbd_t *sql, apr_dbd_prepared_t *stmt)
+{
+    OCIParam *parms;
+    int i;
+    int_errorcode;
+    ub2 paramtype[DBD_ORACLE_MAX_COLUMNS];
+    ub2 paramsize[DBD_ORACLE_MAX_COLUMNS];
+/* 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
+        sql->status = 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);
+            ++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];
+        switch (stmt->out[i].type) {
+#ifdef GET_NUMERICS_WORKING
+        case SQLT_INT:
+        case SQLT_NUM:
+        case SQLT_FLT:
+            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, paramtype[i],
+                                         &stmt->out[i].ind, &stmt->out[i].len,
+                                         0, OCI_DEFAULT);
+            break;
+#endif
+        default:
+            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.stringval,
+                                         stmt->out[i].sz, SQLT_STR,
+                                         &stmt->out[i].ind, &stmt->out[i].len,
+                                         0, OCI_DEFAULT);
+            break;
+        case SQLT_BLOB:
+        case SQLT_CLOB:
+/*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(sql->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_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
+                               apr_dbd_results_t **results,
+                               apr_dbd_prepared_t *statement,
+                               int seek, va_list args)
+{
+    int i;
+    char *arg;
+    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 0
+    CHECK_TRANSACTION(trans);
+#else
+    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;
+        }
+    }
+#endif
+
+    for (i=0; i<statement->nargs; ++i) {
+        int len;
+        switch (statement->args[i].type) {
+        case APR_DBD_ORACLE_INT:
+            *statement->args[i].value.ival = va_arg(args, int);
+            break;
+        case APR_DBD_ORACLE_FLOAT:
+            *statement->args[i].value.floatval = va_arg(args, double);
+            break;
+        case APR_DBD_ORACLE_BLOB:
+        case APR_DBD_ORACLE_CLOB:
+            sql->status = OCIAttrSet(statement->args[i].value.lobval,
+                                     OCI_DTYPE_LOB, &null, 0,
+                                     OCI_ATTR_LOBEMPTY, sql->err);
+            break;
+        case APR_DBD_ORACLE_STRING:
+        default:
+            arg = va_arg(args, char*);
+            len = strlen(arg);
+            if (len >= statement->args[i].len) {
+                len = statement->args[i].len - 1;
+                strncpy(statement->args[i].value.stringval, arg, len);
+                statement->args[i].value.stringval[len] = '\0';
+            }
+            else {
+                strcpy(statement->args[i].value.stringval, arg);
+            }
+            ++len;
+            sql->status = OCIAttrSet(statement->args[i].bind,
+                                     OCI_DTYPE_PARAM, &len, 0,
+                                     OCI_ATTR_DATA_SIZE, sql->err);
+            break;
+        }
+    }
+
+    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
+        sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
+                                  sql->buf, sizeof(sql->buf),
+                                  OCI_HTYPE_ERROR);
+        printf("Executing prepared statement: %s\n", sql->buf);
+#endif
+    default:
+        if (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;
+
+    return 0;
+}
+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, int nargs, const char **values)
+{
+    int i;
+    int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT;
+    OCISnapshot *oldsnapshot = NULL;
+    OCISnapshot *newsnapshot = NULL;
+    apr_dbd_transaction_t* trans = sql->trans;
+
+#if 0
+    CHECK_TRANSACTION(trans);
+#else
+    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;
+        }
+    }
+#endif
+
+    /* we've bound these vars, so now we just copy data in to them */
+    if (nargs > statement->nargs) {
+        nargs = statement->nargs;
+    }
+    for (i=0; i<nargs; ++i) {
+        int foo;
+        switch (statement->args[i].type) {
+        case APR_DBD_ORACLE_INT:
+            sscanf(values[i], "%d", &foo);
+            *statement->args[i].value.ival = foo;
+            break;
+        case APR_DBD_ORACLE_FLOAT:
+            sscanf(values[i], "%lf", statement->args[i].value.floatval);
+            break;
+        case APR_DBD_ORACLE_BLOB:
+        case APR_DBD_ORACLE_CLOB:
+            sql->status = OCIAttrSet(statement->args[i].value.lobval,
+                                     OCI_DTYPE_LOB, &null, 0,
+                                     OCI_ATTR_LOBEMPTY, sql->err);
+            break;
+        case APR_DBD_ORACLE_STRING:
+        default:
+            if (strlen(values[i]) >= MAX_ARG_LEN) {
+                strncpy(statement->args[i].value.stringval, values[i],
+                        MAX_ARG_LEN-1);
+            }
+            else {
+                strcpy(statement->args[i].value.stringval, values[i]);
+            }
+            break;
+        }
+    }
+
+    sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0,
+                                 oldsnapshot, newsnapshot, exec_mode);
+    switch (sql->status) {
+    int_errorcode;
+    case OCI_SUCCESS:
+        break;
+    case OCI_ERROR:
+#ifdef DEBUG
+        sql->status = OCIErrorGet(sql->err, 1, NULL, &errorcode,
+                                  sql->buf, sizeof(sql->buf),
+                                  OCI_HTYPE_ERROR);
+        printf("Executing prepared statement: %s\n", sql->buf);
+#endif
+    default:
+        if (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;
+
+    return 0;
+}
+
+
+
+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:
+            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;
+}
+
+/* chrisd LOB code here */
+/* 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) {
+    int_errorcode;
+    ub4 loblen = 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;
+
+    if ((n < 0) || (n >= row->res->statement->nout) || (val->ind == -1)) {
+        return NULL;
+    }
+
+#ifdef USE_LOB_LOCATOR
+    switch (val->type) {
+    case SQLT_BLOB:
+    case SQLT_CLOB:
+
+        sql->status = OCILobGetLength(sql->svc, sql->err, val->buf.lobval,
+                                      &loblen);
+        switch (sql->status) {
+        case OCI_SUCCESS:
+        case OCI_SUCCESS_WITH_INFO:
+            if (loblen == 0) {
+                buf = "";
+            }
+            break;
+        case OCI_ERROR:
+#ifdef DEBUG
+            sql->status = 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 (loblen == 0) {
+            break;
+        }
+
+        if (val->type == APR_DBD_ORACLE_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 = (loblen+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:
+                sql->status = 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 = (loblen+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 = loblen;
+            buf = apr_palloc(row->pool, buflen+1);
+            buf[buflen] = 0;
+        }
+
+        if (!buf) {
+            break;
+        }
+
+        sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval,
+                                 &loblen, 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:
+            sql->status = 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;
+
+    default:
+        buf = apr_pstrndup(row->pool, val->buf.stringval,val->len);
+        break;
+    }
+#else /* not def USE_LOB_LOCATOR */
+    buf = apr_pstrndup(row->pool, val->buf.stringval,val->len);
+#endif
+    return (const char*) buf;
+}
+
+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;
+    }
+    apr_pool_destroy(handle->pool);
+    return APR_SUCCESS;
+}
+
+Static apr_status_t dbd_oracle_check_conn(apr_pool_t *pool,
+                                          apr_dbd_t *handle)
+{
+    /* FIXME: need to find this in the docs */
+    return APR_ENOTIMPL;
+}
+
+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_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,
+};
+#endif



Mime
View raw message