apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wr...@apache.org
Subject svn commit: r667311 [2/3] - in /apr/apr-util/trunk: dbd/apr_dbd_odbc.c dbd/apr_dbd_odbc.dsp include/private/apr_dbd_odbc_v2.h
Date Fri, 13 Jun 2008 01:34:23 GMT

Modified: apr/apr-util/trunk/dbd/apr_dbd_odbc.c
URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd_odbc.c?rev=667311&r1=667310&r2=667311&view=diff
==============================================================================
--- apr/apr-util/trunk/dbd/apr_dbd_odbc.c (original)
+++ apr/apr-util/trunk/dbd/apr_dbd_odbc.c Thu Jun 12 18:34:23 2008
@@ -1,1620 +1,1620 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "apu.h"
-#if APU_HAVE_ODBC
-
-#include "apr.h"
-#include "apr_strings.h"
-#include "apr_buckets.h"
-#include "apr_env.h"
-#include "apr_file_io.h"
-#include "apr_file_info.h"
-#include "apr_dbd_internal.h"
-#include "apr_thread_proc.h"
-#include "apu_version.h"
-
-/* If library is ODBC-V2, use macros for limited ODBC-V2 support 
- * No random access in V2.
- */
-#ifdef ODBCV2
-#define ODBCVER 0x0200
-#include "apr_dbd_odbc_v2.h"
-#endif
-
-/* standard ODBC include files */
-#ifdef HAVE_SQL_H
-#include <sql.h>
-#include <sqlext.h>
-#elif defined(HAVE_ODBC_SQL_H)
-#include <odbc/sql.h>
-#include <odbc/sqlext.h>
-#endif
-
- /* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver' 
- * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which
- * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql.  
- */
-#ifndef ODBC_DRIVER_NAME
-#define ODBC_DRIVER_NAME odbc
-#endif
-#define STRINGIFY(x) #x
-#define NAMIFY2(n) apr_dbd_##n##_driver
-#define NAMIFY1(n) NAMIFY2(n)
-#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
-#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
-
-/* Required APR version for this driver */
-#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
-#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
-
-
-static SQLHANDLE henv = NULL;           /* ODBC ENV handle is process-wide */
-
-/* Use a CHECK_ERROR macro so we can grab the source line numbers
- * for error reports */
-static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
-                 SQLSMALLINT type, SQLHANDLE h, int line);
-#define CHECK_ERROR(a,s,r,t,h)  check_error(a,s,r,t,h, __LINE__)
-
-#define SOURCE_FILE __FILE__            /* source file for error messages */
-#define MAX_ERROR_STRING 1024           /* max length of message in dbc */
-#define MAX_COLUMN_NAME 256             /* longest column name recognized */
-#define DEFAULT_BUFFER_SIZE 1024        /* value for defaultBufferSize */
-
-#define MAX_PARAMS  20
-#define DEFAULTSEPS " \t\r\n,="
-#define CSINGLEQUOTE '\''
-#define SSINGLEQUOTE "\'"
-
-#define TEXTMODE 1              /* used for text (APR 1.2) mode params */
-#define BINARYMODE 0            /* used for binary (APR 1.3+) mode params */
-
-/* Identify datatypes which are LOBs 
- * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB */
-#define IS_LOB(t)  (t == SQL_LONGVARCHAR \
-     || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
-     || t == -98 || t == -99)
-/* These types are CLOBs 
- * - DB2 DRDA driver uses undefined type -98 for CLOB */
-#define IS_CLOB(t) \
-    (t == SQL_LONGVARCHAR || t == -98)
-
-/* Convert a SQL result to an APR result */
-#define APR_FROM_SQL_RESULT(rc) \
-    (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
-
-/* DBD opaque structures */
-struct apr_dbd_t
-{
-    SQLHANDLE dbc;              /* SQL connection handle - NULL after close */
-    apr_pool_t *pool;           /* connection lifetime pool */
-    char *dbname;               /* ODBC datasource */
-    int lasterrorcode;
-    int lineNumber;
-    char lastError[MAX_ERROR_STRING];
-    int defaultBufferSize;      /* used for CLOBs in text mode, 
-                                 * and when fld size is indeterminate */
-    int transaction_mode;
-    int dboptions;              /* driver options re SQLGetData */
-    int default_transaction_mode;
-    int can_commit;             /* controls end_trans behavior */
-};
-
-struct apr_dbd_results_t
-{
-    SQLHANDLE stmt;             /* parent sql statement handle */
-    SQLHANDLE dbc;              /* parent sql connection handle */
-    apr_pool_t *pool;           /* pool from query or select */
-    apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
-    int random;                 /* random access requested */
-    int ncols;                  /* number of columns */
-    int isclosed;               /* cursor has been closed */
-    char **colnames;            /* array of column names (NULL until used) */
-    SQLPOINTER *colptrs;        /* pointers to column data */
-    SQLINTEGER *colsizes;       /* sizes for columns (enough for txt or bin) */
-    SQLINTEGER *coltextsizes;   /* max-sizes if converted to text */
-    SQLSMALLINT *coltypes;      /* array of SQL data types for columns */
-    SQLLEN *colinds;            /* array of SQL data indicator/strlens */
-    int *colstate;              /* array of column states
-                                 * - avail, bound, present, unavail 
-                                 */
-    int *all_data_fetched;      /* flags data as all fetched, for LOBs  */
-    void *data;                 /* buffer for all data for one row */
-};
-enum                                /* results column states */
-{
-    COL_AVAIL,                  /* data may be retrieved with SQLGetData */
-    COL_PRESENT,                /* data has been retrieved with SQLGetData */
-    COL_BOUND,                  /* column is bound to colptr */
-    COL_RETRIEVED,              /* all data from column has been returned */
-    COL_UNAVAIL                 /* column is unavailable because ODBC driver
-                                 *  requires that columns be retrieved
-                                 *  in ascending order and a higher col 
-                                 *  was accessed */
-};
-
-struct apr_dbd_row_t {
-    SQLHANDLE stmt;             /* parent ODBC statement handle */
-    SQLHANDLE dbc;              /* parent ODBC connection handle */
-    apr_pool_t *pool;           /* pool from get_row */
-    apr_dbd_results_t *res;
-};
-
-struct apr_dbd_transaction_t {
-    SQLHANDLE dbc;              /* parent ODBC connection handle */
-    apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
-};
-
-struct apr_dbd_prepared_t {
-    SQLHANDLE stmt;             /* ODBC statement handle */
-    SQLHANDLE dbc;              /* parent ODBC connection handle */
-    apr_dbd_t *apr_dbd;
-    int nargs;
-    int nvals;
-    int *types;                 /* array of DBD data types */
-
-};
-
-static void odbc_lob_bucket_destroy(void *data);
-static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
-static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
-                                         apr_size_t *len, apr_read_type_e block);
-
-/* the ODBC LOB bucket type */
-static const apr_bucket_type_t odbc_bucket_type = {
-    "ODBC_LOB", 5, APR_BUCKET_DATA,
-    odbc_lob_bucket_destroy,
-    odbc_lob_bucket_read,
-    odbc_lob_bucket_setaside,
-    apr_bucket_shared_split,
-    apr_bucket_shared_copy
-};
-
-
-/* ODBC LOB bucket data */
-typedef struct {
-    /** Ref count for shared bucket */
-    apr_bucket_refcount  refcount;
-    const apr_dbd_row_t *row;
-    int col;
-    SQLSMALLINT type;
-} odbc_bucket;
-
-
-/* SQL datatype mappings to DBD datatypes 
- * These tables must correspond *exactly* to the apr_dbd_type_e enum 
- * in apr_dbd_internal.h 
- */
-
-/* ODBC "C" types to DBD datatypes  */
-static SQLSMALLINT const sqlCtype[] = {
-    SQL_C_DEFAULT,                  /* APR_DBD_TYPE_NONE              */
-    SQL_C_STINYINT,                 /* APR_DBD_TYPE_TINY,       \%hhd */
-    SQL_C_UTINYINT,                 /* APR_DBD_TYPE_UTINY,      \%hhu */
-    SQL_C_SSHORT,                   /* APR_DBD_TYPE_SHORT,      \%hd  */
-    SQL_C_USHORT,                   /* APR_DBD_TYPE_USHORT,     \%hu  */
-    SQL_C_SLONG,                    /* APR_DBD_TYPE_INT,        \%d   */
-    SQL_C_ULONG,                    /* APR_DBD_TYPE_UINT,       \%u   */
-    SQL_C_SLONG,                    /* APR_DBD_TYPE_LONG,       \%ld  */
-    SQL_C_ULONG,                    /* APR_DBD_TYPE_ULONG,      \%lu  */
-    SQL_C_SBIGINT,                  /* APR_DBD_TYPE_LONGLONG,   \%lld */
-    SQL_C_UBIGINT,                  /* APR_DBD_TYPE_ULONGLONG,  \%llu */
-    SQL_C_FLOAT,                    /* APR_DBD_TYPE_FLOAT,      \%f   */
-    SQL_C_DOUBLE,                   /* APR_DBD_TYPE_DOUBLE,     \%lf  */
-    SQL_C_CHAR,                     /* APR_DBD_TYPE_STRING,     \%s   */
-    SQL_C_CHAR,                     /* APR_DBD_TYPE_TEXT,       \%pDt */
-    SQL_C_CHAR, /*SQL_C_TYPE_TIME,      APR_DBD_TYPE_TIME,       \%pDi */
-    SQL_C_CHAR, /*SQL_C_TYPE_DATE,      APR_DBD_TYPE_DATE,       \%pDd */
-    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME,   \%pDa */
-    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP,  \%pDs */
-    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
-    SQL_LONGVARBINARY,              /* APR_DBD_TYPE_BLOB,       \%pDb */
-    SQL_LONGVARCHAR,                /* APR_DBD_TYPE_CLOB,       \%pDc */
-    SQL_TYPE_NULL                   /* APR_DBD_TYPE_NULL        \%pDn */
-};
-
-/*  ODBC Base types to DBD datatypes */
-static SQLSMALLINT const sqlBaseType[] = {
-    SQL_C_DEFAULT,              /* APR_DBD_TYPE_NONE              */
-    SQL_TINYINT,                /* APR_DBD_TYPE_TINY,       \%hhd */
-    SQL_TINYINT,                /* APR_DBD_TYPE_UTINY,      \%hhu */
-    SQL_SMALLINT,               /* APR_DBD_TYPE_SHORT,      \%hd  */
-    SQL_SMALLINT,               /* APR_DBD_TYPE_USHORT,     \%hu  */
-    SQL_INTEGER,                /* APR_DBD_TYPE_INT,        \%d   */
-    SQL_INTEGER,                /* APR_DBD_TYPE_UINT,       \%u   */
-    SQL_INTEGER,                /* APR_DBD_TYPE_LONG,       \%ld  */
-    SQL_INTEGER,                /* APR_DBD_TYPE_ULONG,      \%lu  */
-    SQL_BIGINT,                 /* APR_DBD_TYPE_LONGLONG,   \%lld */
-    SQL_BIGINT,                 /* APR_DBD_TYPE_ULONGLONG,  \%llu */
-    SQL_FLOAT,                  /* APR_DBD_TYPE_FLOAT,      \%f   */
-    SQL_DOUBLE,                 /* APR_DBD_TYPE_DOUBLE,     \%lf  */
-    SQL_CHAR,                   /* APR_DBD_TYPE_STRING,     \%s   */
-    SQL_CHAR,                   /* APR_DBD_TYPE_TEXT,       \%pDt */
-    SQL_CHAR, /*SQL_TIME,          APR_DBD_TYPE_TIME,       \%pDi */
-    SQL_CHAR, /*SQL_DATE,          APR_DBD_TYPE_DATE,       \%pDd */
-    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_DATETIME,   \%pDa */
-    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_TIMESTAMP,  \%pDs */
-    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
-    SQL_LONGVARBINARY,          /* APR_DBD_TYPE_BLOB,       \%pDb */
-    SQL_LONGVARCHAR,            /* APR_DBD_TYPE_CLOB,       \%pDc */
-    SQL_TYPE_NULL               /* APR_DBD_TYPE_NULL        \%pDn */
-};
-
-/*  result sizes for DBD datatypes (-1 for null-terminated) */
-static int const sqlSizes[] = {
-    0,
-    sizeof(char),               /**< \%hhd out: char* */
-    sizeof(unsigned char),      /**< \%hhu out: unsigned char* */
-    sizeof(short),              /**< \%hd  out: short* */
-    sizeof(unsigned short),     /**< \%hu  out: unsigned short* */
-    sizeof(int),                /**< \%d   out: int* */
-    sizeof(unsigned int),       /**< \%u   out: unsigned int* */
-    sizeof(long),               /**< \%ld  out: long* */
-    sizeof(unsigned long),      /**< \%lu  out: unsigned long* */
-    sizeof(apr_int64_t),        /**< \%lld out: apr_int64_t* */
-    sizeof(apr_uint64_t),       /**< \%llu out: apr_uint64_t* */
-    sizeof(float),              /**< \%f   out: float* */
-    sizeof(double),             /**< \%lf  out: double* */
-    -1,                         /**< \%s   out: char** */
-    -1,                         /**< \%pDt out: char** */
-    -1,                         /**< \%pDi out: char** */
-    -1,                         /**< \%pDd out: char** */
-    -1,                         /**< \%pDa out: char** */
-    -1,                         /**< \%pDs out: char** */
-    -1,                         /**< \%pDz out: char** */
-    sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
-    sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
-    0                           /**< \%pDn : in: void*, out: void** */
-};
-
-/*
-*   local functions
-*/
-
-/* close any open results for the connection */
-static apr_status_t odbc_close_results(void *d)
-{   apr_dbd_results_t *dbr = (apr_dbd_results_t *) d;
-    SQLRETURN rc = SQL_SUCCESS;
-    
-    if (dbr && !dbr->isclosed) {
-        rc = SQLCloseCursor(dbr->stmt);
-    }
-    dbr->isclosed = 1;
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/* close the ODBC statement handle from a  prepare */
-static apr_status_t odbc_close_pstmt(void *s)
-{   
-    SQLRETURN rc = APR_SUCCESS;
-    apr_dbd_prepared_t *statement = s;
-    SQLHANDLE hstmt = statement->stmt;
-    /* stmt is closed if connection has already been closed */
-    if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
-        rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
-        }
-    statement->stmt = NULL;
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/* close: close/release a connection obtained from open() */
-static apr_status_t odbc_close(apr_dbd_t *handle)
-{
-    SQLRETURN rc = SQL_SUCCESS;
-
-    if (handle->dbc) {
-        rc = SQLDisconnect(handle->dbc);
-        CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
-        rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
-        CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
-        handle->dbc = NULL;
-    }
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/* odbc_close re-defined for passing to pool cleanup */
-static apr_status_t odbc_close_cleanup(void *handle)
-{
-    return odbc_close( (apr_dbd_t *) handle);
-}
-
-/* close the ODBC environment handle at process termination */
-static apr_status_t odbc_close_env(SQLHANDLE henv)
-{   
-    SQLRETURN rc;
-
-    rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
-    henv = NULL;
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/* setup the arrays in results for all the returned columns */
-static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t * res, 
-                                         SQLHANDLE stmt)
-{
-    SQLRETURN rc;
-    int maxsize, textsize, realsize, type, isunsigned = 1;
-
-     /* discover the sql type */
-    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
-                         (SQLPOINTER) &isunsigned);
-    isunsigned = (isunsigned == SQL_TRUE);
-
-    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
-                         (SQLPOINTER) &type);
-    if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE)
-        /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
-        rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
-                             0, NULL, (SQLPOINTER) &type);
-    if (!SQL_SUCCEEDED(rc))
-        /* if still unknown make it CHAR */
-        type = SQL_C_CHAR;
-
-    switch (type) {
-    case SQL_INTEGER:
-    case SQL_SMALLINT:
-    case SQL_TINYINT:
-    case SQL_BIGINT:
-      /* fix these numeric binary types up as signed/unsigned for C types */
-      type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
-      break;
-    /* LOB types are not changed to C types */
-    case SQL_LONGVARCHAR: 
-        type = SQL_LONGVARCHAR; 
-        break;
-    case SQL_LONGVARBINARY: 
-        type = SQL_LONGVARBINARY; 
-        break;
-    case SQL_FLOAT : 
-        type = SQL_C_FLOAT; 
-        break;
-    case SQL_DOUBLE : 
-        type = SQL_C_DOUBLE; 
-        break;
-
-    /* DBD wants times as strings */
-    case SQL_TIMESTAMP:      
-    case SQL_DATE:
-    case SQL_TIME:
-    default:
-      type = SQL_C_CHAR;
-    }
-
-    res->coltypes[icol] = type;
-
-    /* size if retrieved as text */
-    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
-                         NULL, (SQLPOINTER) & textsize);
-     if (!SQL_SUCCEEDED(rc) || textsize < 0)
-        textsize = res->apr_dbd->defaultBufferSize;
-    /* for null-term, which sometimes isn't included */
-    textsize++;
-
-    /* real size */
-    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
-                         NULL, (SQLPOINTER) & realsize);
-    if (!SQL_SUCCEEDED(rc))
-        realsize = textsize;
-
-    maxsize = (textsize > realsize) ? textsize : realsize;
-    if ( IS_LOB(type) || maxsize <= 0) {
-        /* LOB types are never bound and have a NULL colptr for binary.
-         * Ingore their real (1-2gb) length & use a default - the larger
-         * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
-         * If not a LOB, but simply unknown length - always use defaultBufferSize.
-         */
-        maxsize = res->apr_dbd->defaultBufferSize;
-        if ( IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE )
-            maxsize = APR_BUCKET_BUFF_SIZE;
-
-        res->colptrs[icol] =  NULL;
-        res->colstate[icol] = COL_AVAIL;
-        res->colsizes[icol] = maxsize;
-        rc = SQL_SUCCESS;
-    }
-    else {
-        res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
-        res->colsizes[icol] = maxsize;
-        if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
-            /* we are allowed to call SQLGetData if we need to */
-            rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol], 
-                            res->colptrs[icol], maxsize, 
-                            &(res->colinds[icol]) );
-            CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
-                        stmt);
-            res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
-        }
-        else {
-            /* this driver won't allow us to call SQLGetData on bound 
-             * columns - so don't bind any */
-            res->colstate[icol] = COL_AVAIL;
-            rc = SQL_SUCCESS;
-        }
-    }
-    return rc;
-}
-
-/* create and populate an apr_dbd_results_t for a select */
-static SQLRETURN odbc_create_results(apr_dbd_t * handle, SQLHANDLE hstmt,
-                                     apr_pool_t * pool, const int random,
-                                     apr_dbd_results_t ** res)
-{
-    SQLRETURN rc;
-    SQLSMALLINT ncols;
-
-    *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
-    (*res)->stmt = hstmt;
-    (*res)->dbc = handle->dbc;
-    (*res)->pool = pool;
-    (*res)->random = random;
-    (*res)->apr_dbd = handle;
-    rc = SQLNumResultCols(hstmt, &ncols);
-    CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
-    (*res)->ncols = ncols;
-
-    if SQL_SUCCEEDED(rc) {
-        int i;
-
-        (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
-        (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
-        (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
-        (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
-        (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
-        (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
-        (*res)->ncols = ncols;
-
-        for (i = 0 ; i < ncols ; i++)
-            odbc_set_result_column(i, (*res), hstmt);
-        }
-    return rc;
-}
-
-
-/* bind a parameter - input params only, does not support output parameters */
-static SQLRETURN odbc_bind_param(apr_pool_t * pool,
-                                 apr_dbd_prepared_t * statement, const int narg,
-                                 const SQLSMALLINT type, int *argp,
-                                 const void **args, const int textmode)
-{
-    SQLRETURN rc;
-    SQLSMALLINT baseType, cType;
-    void *ptr;
-    SQLUINTEGER len;
-    SQLINTEGER *indicator;
-    static SQLINTEGER nullValue = SQL_NULL_DATA;
-    static SQLSMALLINT inOut = SQL_PARAM_INPUT;     /* only input params */
-
-    /* bind a NULL data value */
-    if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
-        baseType = SQL_CHAR;
-        cType = SQL_C_CHAR;
-        ptr = &nullValue;
-        len = sizeof(SQLINTEGER);
-        indicator = &nullValue;
-        (*argp)++;
-    }
-    /* bind a non-NULL data value */
-    else {
-        baseType = sqlBaseType[type];
-        cType = sqlCtype[type];
-        indicator = NULL;
-        /* LOBs */
-        if (IS_LOB(cType)) {
-            ptr = (void *) args[*argp];
-            len = (SQLUINTEGER) * (apr_size_t *) args[*argp + 1];
-            cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
-            (*argp) += 4;  /* LOBs consume 4 args (last two are unused) */
-        }
-        /* non-LOBs */
-        else {
-            switch (baseType) {
-            case SQL_CHAR:
-            case SQL_DATE:
-            case SQL_TIME:
-            case SQL_TIMESTAMP:
-                ptr = (void *) args[*argp];
-                len = (SQLUINTEGER) strlen(ptr);
-                break;
-            case SQL_TINYINT:
-                ptr = apr_palloc(pool, sizeof(unsigned char));
-                len = sizeof(unsigned char);
-                *(unsigned char *) ptr =
-                    (textmode ?
-                     atoi(args[*argp]) : *(unsigned char *) args[*argp]);
-                break;
-            case SQL_SMALLINT:
-                ptr = apr_palloc(pool, sizeof(short));
-                len = sizeof(short);
-                *(short *) ptr =
-                    (textmode ? atoi(args[*argp]) : *(short *) args[*argp]);
-                break;
-            case SQL_INTEGER:
-                ptr = apr_palloc(pool, sizeof(int));
-                len = sizeof(int);
-                *(long *) ptr =
-                    (textmode ? atol(args[*argp]) : *(long *) args[*argp]);
-                break;
-            case SQL_FLOAT:
-                ptr = apr_palloc(pool, sizeof(float));
-                len = sizeof(float);
-                *(float *) ptr =
-                    (textmode ?
-                     (float) atof(args[*argp]) : *(float *) args[*argp]);
-                break;
-            case SQL_DOUBLE:
-                ptr = apr_palloc(pool, sizeof(double));
-                len = sizeof(double);
-                *(double *) ptr =
-                    (textmode ? atof(args[*argp]) : *(double *)
-                     args[*argp]);
-                break;
-            case SQL_BIGINT:
-                ptr = apr_palloc(pool, sizeof(apr_int64_t));
-                len = sizeof(apr_int64_t);
-                *(apr_int64_t *) ptr =
-                    (textmode ?
-                     apr_atoi64(args[*argp]) : *(apr_int64_t *) args[*argp]);
-                break;
-            default:
-                return APR_EGENERAL;
-            }
-            (*argp)++;          /* non LOBs consume one argument */
-        }
-    }
-    rc = SQLBindParameter(statement->stmt, narg, inOut, cType, 
-                          baseType, len, 0, ptr, len, indicator);
-    CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
-                statement->stmt);
-    return rc;
-}
-
-/* LOB / Bucket Brigade functions */
-
-
-
-/* bucket type specific destroy */
-static void odbc_lob_bucket_destroy(void *data)
-{
-    odbc_bucket *bd = data;
-
-    if (apr_bucket_shared_destroy(bd))
-        apr_bucket_free(bd);
-}
-
-/* set aside a bucket if possible */
-static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
-{
-    odbc_bucket *bd = (odbc_bucket *) e->data;
-
-    /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
-    if (apr_pool_is_ancestor(bd->row->pool, pool))
-        return APR_SUCCESS;
-    
-    return apr_bucket_setaside_notimpl(e, pool);
-}
-
-/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
-static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
-                                         apr_size_t *len, apr_read_type_e block)
-{
-    SQLRETURN rc;
-    SQLINTEGER len_indicator;
-    SQLSMALLINT type;
-    odbc_bucket *bd = (odbc_bucket *) e->data;
-    apr_bucket *nxt;
-    void *buf;
-    int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
-    int eos;
-    
-    /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
-    type = bd->row->res->coltypes[bd->col];
-    type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
-
-    /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE, 
-     *   but they may be much bigger per the BUFSIZE parameter.
-    */
-    if (bufsize < APR_BUCKET_BUFF_SIZE)
-            bufsize = APR_BUCKET_BUFF_SIZE;
-
-    buf = apr_bucket_alloc(bufsize, e->list);
-    *str = NULL;
-    *len = 0;
-
-    rc = SQLGetData(bd->row->res->stmt, bd->col + 1, 
-                    type, buf, bufsize, 
-                    &len_indicator);
-
-    CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc, 
-                SQL_HANDLE_STMT, bd->row->res->stmt);
-    
-    if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
-        len_indicator = 0;
-
-    if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
-
-        if (rc = SQL_SUCCESS_WITH_INFO
-            && ( len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize) ) {
-            /* not the last read = a full buffer. CLOBs have a null terminator */
-            *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
-
-             eos = 0;
-        }
-        else {
-            /* the last read - len_indicator is supposed to be the length, 
-             * but some driver get this wrong and return the total length.
-             * We try to handle both interpretations.
-             */
-            *len =  (len_indicator > bufsize 
-                     && len_indicator >= (SQLINTEGER) e->start)
-                ? (len_indicator - (SQLINTEGER) e->start) : len_indicator;
-
-            eos = 1;
-        }
-
-        if (!eos) {
-            /* Create a new LOB bucket to append and append it */
-            nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
-            APR_BUCKET_INIT(nxt);
-            nxt->length = -1;
-            nxt->data   = e->data;
-            nxt->type   = &odbc_bucket_type;
-            nxt->free   = apr_bucket_free;
-            nxt->list   = e->list;
-            nxt->start  = e->start + *len;
-            APR_BUCKET_INSERT_AFTER(e, nxt);
-        }
-        else {
-            odbc_lob_bucket_destroy(e->data);
-        }
-        /* make current bucket into a heap bucket */
-        apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
-        *str = buf;
-
-        /* No data is success in this context */
-        rc = SQL_SUCCESS;
-    }
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/* Create a bucket brigade on the row pool for a LOB column */
-static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col, 
-                                       SQLSMALLINT type, apr_bucket_brigade *bb)
-{
-    apr_bucket_alloc_t *list = bb->bucket_alloc;
-    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
-    odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
-    apr_bucket *eos = apr_bucket_eos_create(list);
-    
-
-    bd->row = row;
-    bd->col = col;
-    bd->type = type;
-
-
-    APR_BUCKET_INIT(b);
-    b->type = &odbc_bucket_type;
-    b->free = apr_bucket_free;
-    b->list = list;
-    /* LOB lengths are unknown in ODBC */
-    b = apr_bucket_shared_make(b, bd, 0, -1);
-
-    APR_BRIGADE_INSERT_TAIL(bb, b);
-    APR_BRIGADE_INSERT_TAIL(bb, eos);
-
-    return APR_SUCCESS;
-}
-
-/* returns a data pointer for a column,  returns NULL for NULL value,
- * return -1 if data not available */
-static void *odbc_get(const apr_dbd_row_t *row, const int col, 
-                      const SQLSMALLINT sqltype)
-{
-    SQLRETURN rc;
-    SQLINTEGER indicator;
-    int state = row->res->colstate[col];
-    int options = row->res->apr_dbd->dboptions;
-
-    switch (state) {
-    case (COL_UNAVAIL) : return (void *) -1;
-    case (COL_RETRIEVED) :  return NULL;
-
-    case (COL_BOUND) :
-    case (COL_PRESENT) : 
-        if (sqltype == row->res->coltypes[col]) {
-            /* same type and we already have the data */
-            row->res->colstate[col] = COL_RETRIEVED;
-            return (row->res->colinds[col] == SQL_NULL_DATA) ? 
-                NULL : row->res->colptrs[col];
-        }
-    }
-
-    /* we need to get the data now */
-    if (!(options & SQL_GD_ANY_ORDER)) {
-        /* this ODBC driver requires columns to be retrieved in order,
-         * so we attempt to get every prior un-gotten non-LOB column */
-        int i;
-        for (i = 0; i < col; i++) {
-            if (row->res->colstate[i] == COL_AVAIL) {
-                if (IS_LOB(row->res->coltypes[i]))
-                       row->res->colstate[i] = COL_UNAVAIL;
-                else {
-                    odbc_get(row, i, row->res->coltypes[i]);
-                    row->res->colstate[i] = COL_PRESENT;
-                }
-            }
-        }
-    }
-
-    if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
-        /* this driver won't let us re-get bound columns */
-        return (void *) -1;
-
-    /* a LOB might not have a buffer allocated yet - so create one */
-    if (!row->res->colptrs[col])
-        row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
-
-    rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
-                    row->res->colsizes[col], &indicator);
-    CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT, 
-                row->res->stmt);
-    if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
-        return NULL;
-
-    if (SQL_SUCCEEDED(rc)) {
-        /* whatever it was originally, it is now this sqltype */
-        row->res->coltypes[col] = sqltype;
-        /* this allows getting CLOBs in text mode by calling get_entry
-         *   until it returns NULL */
-        row->res->colstate[col] = 
-            (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
-        return row->res->colptrs[col];
-    }
-    else  return (void *) -1;
-}
-
-/* Parse the parameter string for open */
-static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
-                               int *connect, SQLCHAR **datasource, 
-                               SQLCHAR **user, SQLCHAR **password, 
-                               int *defaultBufferSize, int *nattrs,
-                               int **attrs, int **attrvals)
-{
-    char *seps, *last, *name[MAX_PARAMS], *val[MAX_PARAMS];
-    int nparams=0, i, j;
-
-    *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
-    *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(int));
-    *nattrs = 0;
-    seps = DEFAULTSEPS;
-    name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
-    do {
-        if (last[strspn(last, seps)] == CSINGLEQUOTE) {
-            last += strspn(last, seps);
-            seps=SSINGLEQUOTE;
-        }
-        val[nparams] = apr_strtok(NULL, seps, &last);
-        seps = DEFAULTSEPS;
-        name[++nparams] = apr_strtok(NULL, seps, &last);
-    } while ( nparams <= MAX_PARAMS && name[nparams] != NULL
-              && val[nparams] != NULL);
-
-    for(j=i=0 ; i< nparams ; i++)
-    {   if      (!apr_strnatcasecmp(name[i], "CONNECT"))
-        {   *datasource = apr_pstrdup(pool, val[i]);
-            *connect=1;
-        }
-        else if (!apr_strnatcasecmp(name[i], "DATASOURCE"))
-        {   *datasource = apr_pstrdup(pool, val[i]);
-            *connect=0;
-        }
-        else if (!apr_strnatcasecmp(name[i], "USER"))
-            *user = apr_pstrdup(pool, val[i]);
-        else if (!apr_strnatcasecmp(name[i], "PASSWORD"))
-            *password = apr_pstrdup(pool, val[i]);
-        else if (!apr_strnatcasecmp(name[i], "BUFSIZE"))
-            *defaultBufferSize = atoi(val[i]);
-        else if (!apr_strnatcasecmp(name[i], "ACCESS"))
-        {   if  (!apr_strnatcasecmp(val[i], "READ_ONLY"))
-                (*attrvals)[j] = SQL_MODE_READ_ONLY;
-            else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
-                (*attrvals)[j] = SQL_MODE_READ_WRITE;
-            else return SQL_ERROR;
-            (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
-        }
-        else if (!apr_strnatcasecmp(name[i], "CTIMEOUT"))
-        {   (*attrvals)[j] = atoi(val[i]);
-            (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
-        }
-        else if (!apr_strnatcasecmp(name[i], "STIMEOUT"))
-        {   (*attrvals)[j] = atoi(val[i]);
-            (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
-        }
-        else if (!apr_strnatcasecmp(name[i], "TXMODE"))
-        {   if      (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
-                (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
-            else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
-                (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
-            else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
-                (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
-            else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
-                (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
-            else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
-                continue;
-            else return SQL_ERROR;
-            (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
-        }
-        else return SQL_ERROR;
-    }
-    *nattrs = j;
-    return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
-}
-
-/* common handling after ODBC calls - save error info (code and text) in dbc */
-static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
-                 SQLSMALLINT type, SQLHANDLE h, int line)
-{
-    SQLCHAR buffer[512];
-    SQLCHAR sqlstate[128];
-    SQLINTEGER native;
-    SQLSMALLINT reslength;
-    char *res, *p, *end, *logval=NULL;
-    int i;
-    apr_status_t r;
-
-    /* set info about last error in dbc  - fast return for SQL_SUCCESS  */
-    if (rc == SQL_SUCCESS) {
-        char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
-        dbc->lasterrorcode = SQL_SUCCESS;
-        strcpy(dbc->lastError, successMsg);
-        strcpy(dbc->lastError+sizeof(successMsg)-1, step);
-        return;
-    }
-    switch (rc) {
-        case SQL_INVALID_HANDLE     : { res = "SQL_INVALID_HANDLE"; break; }
-        case SQL_ERROR              : { res = "SQL_ERROR"; break; }   
-        case SQL_SUCCESS_WITH_INFO  : { res = "SQL_SUCCESS_WITH_INFO"; break; }
-        case SQL_STILL_EXECUTING    : { res = "SQL_STILL_EXECUTING"; break; }
-        case SQL_NEED_DATA          : { res = "SQL_NEED_DATA"; break; }
-        case SQL_NO_DATA            : { res = "SQL_NO_DATA"; break; }
-        default                     : { res = "unrecognized SQL return code"; }
-    }
-    /* these two returns are expected during normal execution */
-    if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA)
-        dbc->can_commit = 0;
-    p = dbc->lastError;
-    end = p + sizeof(dbc->lastError);
-    dbc->lasterrorcode = rc;
-    p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
-                 step, res, rc, SOURCE_FILE, line-1);
-    for (i=1, rc=0 ; rc==0 ; i++) {
-        rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer, 
-                            sizeof(buffer), &reslength);
-        if (SQL_SUCCEEDED(rc) && (p < (end-280))) 
-            p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
-    }
-    r = apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
-    /* if env var was set or call was init/open (no dbname) - log to stderr */
-    if (logval || !dbc->dbname ) {
-        char timestamp[APR_CTIME_LEN];
-        apr_file_t *se;
-        apr_ctime(timestamp, apr_time_now());
-        apr_file_open_stderr(&se, dbc->pool);
-        apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
-    }
-}
-
-/*
-*   public functions per DBD driver API
-*/
-
-/** init: allow driver to perform once-only initialisation. **/
-static void odbc_init(apr_pool_t *pool)
-{
-    SQLRETURN rc;
-    char *step;
-    apr_version_t apuver;
-    
-    apu_version(&apuver);
-    if (apuver.major != DRIVER_APU_VERSION_MAJOR 
-        || apuver.minor != DRIVER_APU_VERSION_MINOR) {
-            apr_file_t *se;
-
-            apr_file_open_stderr(&se, pool);
-            apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
-                "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
-                DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR, 
-                apuver.major, apuver.minor);
-        abort();
-    }
-
-    if (henv) 
-        return;
-
-    step = "SQLAllocHandle (SQL_HANDLE_ENV)";
-    rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
-    apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
-    if (SQL_SUCCEEDED(rc))
-    {   step = "SQLSetEnvAttr";
-        rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
-                          (SQLPOINTER) SQL_OV_ODBC3, 0);
-    }
-    else
-    {   apr_dbd_t tmp_dbc;
-        SQLHANDLE err_h = henv;
-
-        tmp_dbc.pool = pool;
-        tmp_dbc.dbname = NULL;
-        CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
-    }
-}
-
-/** native_handle: return the native database handle of the underlying db **/
-static void* odbc_native_handle(apr_dbd_t *handle)
-{   return handle->dbc;
-}
-
-/** open: obtain a database connection from the server rec. **/
-
-/* It would be more efficient to allocate a single statement handle 
-    here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
-    SQLPrepare, and we don't know whether random-access is
-    specified until SQLExecute so we cannot.
-*/
-
-static apr_dbd_t* odbc_open(apr_pool_t *pool, const char *params, const char **error)
-{
-    SQLRETURN   rc;
-    SQLHANDLE   hdbc = NULL;
-    apr_dbd_t   *handle;
-    char *err_step;
-    int err_htype, i;
-    int defaultBufferSize=DEFAULT_BUFFER_SIZE;
-    SQLHANDLE err_h = NULL;
-    SQLCHAR  *datasource="", *user="", *password="";
-    int nattrs=0, *attrs=NULL, *attrvals=NULL, connect=0;
-
-    err_step="SQLAllocHandle (SQL_HANDLE_DBC)";
-    err_htype = SQL_HANDLE_ENV;
-    err_h = henv;
-    rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
-    if (SQL_SUCCEEDED(rc)) {
-        err_step="Invalid DBD Parameters - open";
-        err_htype = SQL_HANDLE_DBC;
-        err_h = hdbc;
-        rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
-                               &password, &defaultBufferSize, &nattrs, &attrs,
-                               &attrvals);
-    }
-    if (SQL_SUCCEEDED(rc)) {
-        for (i=0 ; i < nattrs && SQL_SUCCEEDED(rc); i++) {
-            err_step="SQLSetConnectAttr (from DBD Parameters)";
-            err_htype = SQL_HANDLE_DBC;
-            err_h = hdbc;
-            rc = SQLSetConnectAttr(hdbc, attrs[i], (void *) attrvals[i], 0);
-        }
-    }
-    if (SQL_SUCCEEDED(rc)) {
-        if (connect) {
-            SQLCHAR out[1024];
-            SQLSMALLINT outlen;
-            err_step="SQLDriverConnect";
-            err_htype = SQL_HANDLE_DBC;
-            err_h = hdbc;
-            rc = SQLDriverConnect(hdbc, NULL, datasource,
-                        (SQLSMALLINT) strlen(datasource),
-                        out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
-        }
-        else {
-            err_step="SQLConnect";
-            err_htype = SQL_HANDLE_DBC;
-            err_h = hdbc;
-            rc = SQLConnect(hdbc, datasource, (SQLSMALLINT) strlen(datasource),
-                        user, (SQLSMALLINT) strlen(user),
-                        password, (SQLSMALLINT) strlen(password));
-        }
-    }
-    if (SQL_SUCCEEDED(rc)) {
-        handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
-        handle->dbname = apr_pstrdup(pool, datasource);
-        handle->dbc = hdbc;
-        handle->pool = pool;
-        handle->defaultBufferSize = defaultBufferSize;
-        CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
-        handle->default_transaction_mode = 0;
-        SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
-                   &(handle->default_transaction_mode), sizeof(int), NULL);
-        handle->transaction_mode = handle->default_transaction_mode;
-        SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
-                   sizeof(int), NULL);
-        apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
-        return handle;
-    }
-    else {
-        apr_dbd_t tmp_dbc;
-        tmp_dbc.pool = pool;
-        tmp_dbc.dbname = NULL;
-        CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
-        if (error)
-            *error = apr_pstrdup(pool, tmp_dbc.lastError);
-        if (hdbc)
-            SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
-        return NULL;
-    }
-}
-
-/** check_conn: check status of a database connection **/
-static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
-{
-    SQLUINTEGER isDead;
-    SQLRETURN   rc;
-
-    rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
-                            sizeof(SQLUINTEGER), NULL);
-    CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
-                SQL_HANDLE_DBC, handle->dbc);
-    /* if driver cannot check connection, say so */
-    if (rc != SQL_SUCCESS)
-        return APR_ENOTIMPL;
-
-    return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
-}
-
-
-/** set_dbname: select database name.  May be a no-op if not supported. **/
-static int odbc_set_dbname(apr_pool_t* pool, apr_dbd_t *handle,
-                           const char *name)
-{
-    if (apr_strnatcmp(name, handle->dbname)) {
-        return APR_EGENERAL;        /* It's illegal to change dbname in ODBC */
-    }
-    CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
-                handle->dbc);
-    return APR_SUCCESS;             /* OK if it's the same name */
-}
-
-/** transaction: start a transaction.  May be a no-op. **/
-static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
-                                  apr_dbd_transaction_t **trans)
-{
-    SQLRETURN rc = SQL_SUCCESS;
-
-    if (handle->transaction_mode) {
-        rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION, (void *)
-                               handle->transaction_mode, 0);
-        CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
-                    SQL_HANDLE_DBC, handle->dbc);
-    }
-    if SQL_SUCCEEDED(rc) {
-        /* turn off autocommit for transactions */
-        rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
-                               SQL_AUTOCOMMIT_OFF, 0);
-        CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
-                    SQL_HANDLE_DBC, handle->dbc);
-    }
-    if SQL_SUCCEEDED(rc) {
-        *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
-        (*trans)->dbc = handle->dbc;
-        (*trans)->apr_dbd = handle;
-        handle->can_commit = 1;
-    }
-    return APR_FROM_SQL_RESULT(rc);
-};
-
-
-/** end_transaction: end a transaction **/
-static int odbc_end_transaction(apr_dbd_transaction_t *trans)
-{
-    SQLRETURN rc;
-    int action = trans->apr_dbd->can_commit ? SQL_COMMIT : SQL_ROLLBACK;
-
-    rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, SQL_COMMIT);
-    CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
-    if SQL_SUCCEEDED(rc) {
-        rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
-                               (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0);
-        CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
-                    rc, SQL_HANDLE_DBC, trans->dbc);
-    }
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** query: execute an SQL statement which doesn't return a result set **/
-static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
-{
-    SQLRETURN rc;
-    SQLHANDLE hstmt = NULL;
-    size_t len = strlen(statement);
-
-    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
-    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
-                handle->dbc);
-    if (!SQL_SUCCEEDED(rc))
-        return APR_FROM_SQL_RESULT(rc);
-
-    rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
-    CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
-
-    if SQL_SUCCEEDED(rc) {
-        rc = SQLRowCount(hstmt,  (SQLINTEGER *) nrows);
-        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
-    }
-
-    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** select: execute an SQL statement which returns a result set **/
-static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
-                       apr_dbd_results_t **res, const char *statement,
-                       int random)
-{
-    SQLRETURN rc;
-    SQLHANDLE hstmt;
-    apr_dbd_prepared_t *stmt;
-    size_t len = strlen(statement);
-
-    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
-    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
-                handle->dbc);
-    if (!SQL_SUCCEEDED(rc))
-        return APR_FROM_SQL_RESULT(rc);
-    /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
-    *  is not a prepared statement.  We want the same cleanup mechanism.
-    */
-    stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
-    stmt->apr_dbd = handle;
-    stmt->dbc = handle->dbc;
-    stmt->stmt = hstmt;
-    apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
-    if (random) {
-        rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
-                            (SQLPOINTER) SQL_SCROLLABLE, 0);
-        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
-                    SQL_HANDLE_STMT, hstmt);
-    }
-    if SQL_SUCCEEDED(rc) {
-        rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
-        CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
-    }
-    if SQL_SUCCEEDED(rc) {
-        rc = odbc_create_results(handle, hstmt, pool, random, res);
-        apr_pool_cleanup_register(pool, *res, 
-                                  odbc_close_results, apr_pool_cleanup_null);
-    }
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** num_cols: get the number of columns in a results set **/
-static int odbc_num_cols(apr_dbd_results_t *res)
-{
-    return res->ncols;
-}
-
-/** num_tuples: get the number of rows in a results set **/
-static int odbc_num_tuples(apr_dbd_results_t *res)
-{
-    SQLRETURN rc;
-    SQLINTEGER  nrows;
-
-    rc = SQLRowCount(res->stmt, &nrows);
-    CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
-    return SQL_SUCCEEDED(rc) ? (int) nrows : -1;
-}
-
-/** get_row: get a row from a result set **/
-static int odbc_get_row(apr_pool_t * pool, apr_dbd_results_t * res,
-                        apr_dbd_row_t ** row, int rownum)
-{
-    SQLRETURN rc;
-    char *fetchtype;
-    int c;
-
-    *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
-    (*row)->stmt = res->stmt;
-    (*row)->dbc = res->dbc;
-    (*row)->res = res;
-    (*row)->pool = res->pool;
-
-    /* mark all the columns as needing SQLGetData unless they are bound  */
-    for (c = 0; c < res->ncols; c++) {
-        if (res->colstate[c] != COL_BOUND)
-            res->colstate[c] = COL_AVAIL;
-            /* some drivers do not null-term zero-len CHAR data */
-            if (res->colptrs[c] )
-                * (char *) res->colptrs[c] = 0; 
-    }
-
-    if (res->random && (rownum > 0)) {
-        fetchtype = "SQLFetchScroll";
-        rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
-    }
-    else {
-        fetchtype = "SQLFetch";
-        rc = SQLFetch(res->stmt);
-    }
-    CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
-    (*row)->stmt = res->stmt;
-    if (!SQL_SUCCEEDED(rc) && !res->random) {
-        /* early close on any error (usually SQL_NO_DATA) if fetching
-         * sequentially to release resources ASAP */
-        odbc_close_results(res);
-        return -1;
-    }
-    return SQL_SUCCEEDED(rc) ? 0 : -1;
-}
-
-/** datum_get: get a binary entry from a row **/
-static apr_status_t odbc_datum_get(const apr_dbd_row_t * row, int col,
-                                   apr_dbd_type_e dbdtype, void *data)
-{
-    SQLSMALLINT sqltype;
-    void *p;
-    int len = sqlSizes[dbdtype];
-
-    if (col >= row->res->ncols)
-        return APR_EGENERAL;
-
-    if (dbdtype < 0 || dbdtype >= sizeof(sqlCtype)) {
-        data = NULL;            /* invalid type */
-        return APR_EGENERAL;
-    }
-    sqltype = sqlCtype[dbdtype];
-
-    /* must not memcpy a brigade, sentinals are relative to orig loc */
-    if (IS_LOB(sqltype)) 
-        return odbc_create_bucket(row, col, sqltype, data);
-
-    p = odbc_get(row, col, sqltype);
-    if (p == (void *) -1)
-        return APR_EGENERAL;
-
-    if (p == NULL)
-        return APR_ENOENT;          /* SQL NULL value */
-    
-    if (len < 0)
-        strcpy(data, p);
-    else
-        memcpy(data, p, len);
-    
-    return APR_SUCCESS;
-
-}
-
-/** get_entry: get an entry from a row (string data) **/
-static const char *odbc_get_entry(const apr_dbd_row_t * row, int col)
-{
-    void *p;
-
-    if (col >= row->res->ncols)
-        return NULL;
-
-    p = odbc_get(row, col, SQL_C_CHAR);
-
-    if ((signed int) p > 0)
-        return apr_pstrdup(row->pool, p);   /* row pool lifetime */
-    else
-        return p;     /* NULL or invalid (-1) */
-}
-
-/** error: get current error message (if any) **/
-static const char* odbc_error(apr_dbd_t *handle, int errnum)
-{   
-    return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
-}
-
-/** escape: escape a string so it is safe for use in query/select **/
-static const char* odbc_escape(apr_pool_t *pool, const char *s,
-                        apr_dbd_t *handle)
-{   
-    char *newstr, *src, *dst, *sq;
-    int qcount;
-
-    /* return the original if there are no single-quotes */
-    if (!(sq = strchr(s, '\''))) 
-        return (char *) s;
-    /* count the single-quotes and allocate a new buffer */
-    for (qcount = 1; sq = strchr(sq + 1, '\''); )
-        qcount++;
-    newstr = apr_palloc(pool, strlen(s) + qcount + 1);
-
-    /* move chars, doubling all single-quotes */
-    src = (char *) s;
-    for (dst = newstr ; *src ; src++) {
-        if ((*dst++ = *src) == '\'')  
-            *dst++ = '\'';
-    }
-    *dst = 0;
-    return newstr;
-}
-/** prepare: prepare a statement **/
-static int odbc_prepare(apr_pool_t * pool, apr_dbd_t * handle,
-                        const char *query, const char *label, int nargs,
-                        int nvals, apr_dbd_type_e * types,
-                        apr_dbd_prepared_t ** statement)
-{
-    SQLRETURN rc;
-    size_t len = strlen(query);
-
-    *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
-    (*statement)->dbc = handle->dbc;
-    (*statement)->apr_dbd = handle;
-    (*statement)->nargs = nargs;
-    (*statement)->nvals = nvals;
-    (*statement)->types =
-        apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
-    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
-    apr_pool_cleanup_register(pool, *statement, 
-                              odbc_close_pstmt, apr_pool_cleanup_null);
-    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
-                SQL_HANDLE_DBC, handle->dbc);
-    rc = SQLPrepare((*statement)->stmt, (SQLCHAR *) query, (SQLINTEGER) len);
-    CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
-                (*statement)->stmt);
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** pquery: query using a prepared statement + args **/
-static int odbc_pquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
-                       apr_dbd_prepared_t * statement, const char **args)
-{
-    SQLRETURN rc = SQL_SUCCESS;
-    int i, argp;
-
-    for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
-        rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
-                             &argp, (const void **) args, TEXTMODE);
-    }
-    if (SQL_SUCCEEDED(rc)) {
-        rc = SQLExecute(statement->stmt);
-        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
-                    statement->stmt);
-        }
-    if (SQL_SUCCEEDED(rc)) {
-        rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
-        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
-                    statement->stmt);
-        }
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** pvquery: query using a prepared statement + args **/
-static int odbc_pvquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
-                        apr_dbd_prepared_t * statement, va_list args)
-{
-    const char **values;
-    int i;
-    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
-    for (i = 0; i < statement->nvals; i++)
-        values[i] = va_arg(args, const char *);
-    return odbc_pquery(pool, handle, nrows, statement, values);
-}
-
-/** pselect: select using a prepared statement + args **/
-int odbc_pselect(apr_pool_t * pool, apr_dbd_t * handle,
-                 apr_dbd_results_t ** res, apr_dbd_prepared_t * statement,
-                 int random, const char **args)
-{
-    SQLRETURN rc = SQL_SUCCESS;
-    int i, argp;
-
-    if (random) {
-        rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
-                            (SQLPOINTER) SQL_SCROLLABLE, 0);
-        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
-                    rc, SQL_HANDLE_STMT, statement->stmt);
-    }
-    if (SQL_SUCCEEDED(rc)) {
-        for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
-            rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
-                                 &argp, (const void **) args, TEXTMODE);
-        }
-    if (SQL_SUCCEEDED(rc)) {
-        rc = SQLExecute(statement->stmt);
-        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
-                    statement->stmt);
-        }
-    if (SQL_SUCCEEDED(rc)) {
-        rc = odbc_create_results(handle, statement->stmt, pool, random, res);
-        apr_pool_cleanup_register(pool, *res,
-                                  odbc_close_results, apr_pool_cleanup_null);
-        }
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** pvselect: select using a prepared statement + args **/
-static int odbc_pvselect(apr_pool_t * pool, apr_dbd_t * handle,
-                         apr_dbd_results_t ** res,
-                         apr_dbd_prepared_t * statement, int random,
-                         va_list args)
-{
-    const char **values;
-    int i;
-
-    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
-    for (i = 0; i < statement->nvals; i++)
-        values[i] = va_arg(args, const char *);
-    return odbc_pselect(pool, handle, res, statement, random, values);
-}
-
-/** get_name: get a column title from a result set **/
-static const char *odbc_get_name(const apr_dbd_results_t * res, int col)
-{
-    SQLRETURN rc;
-    char buffer[MAX_COLUMN_NAME];
-    SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
-    SQLUINTEGER colsize;
-
-    if (col >= res->ncols)
-        return NULL;            /* bogus column number */
-    if (res->colnames[col] != NULL)
-        return res->colnames[col];      /* we already retrieved it */
-    rc = SQLDescribeCol(res->stmt, col + 1,
-                        buffer, sizeof(buffer), &colnamelength,
-                        &coltype, &colsize, &coldecimal, &colnullable);
-    CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
-                SQL_HANDLE_STMT, res->stmt);
-    res->colnames[col] = apr_pstrdup(res->pool, buffer);
-    return res->colnames[col];
-}
-
-/** transaction_mode_get: get the mode of transaction **/
-static int odbc_transaction_mode_get(apr_dbd_transaction_t * trans)
-{
-    return (int) trans->apr_dbd->transaction_mode;
-}
-
-/** transaction_mode_set: set the mode of transaction **/
-static int odbc_transaction_mode_set(apr_dbd_transaction_t * trans, int mode)
-{
-    SQLRETURN rc;
-
-    int legal = (SQL_TXN_READ_UNCOMMITTED | SQL_TXN_READ_COMMITTED
-                 | SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE);
-
-    if ((mode & legal) != mode)
-        return APR_EGENERAL;
-
-    trans->apr_dbd->transaction_mode = mode;
-    rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_TXN_ISOLATION, 
-                           (void *) mode, 0);
-    CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)",
-                rc, SQL_HANDLE_DBC, trans->dbc);
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** pbquery: query using a prepared statement + binary args **/
-static int odbc_pbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
-                        apr_dbd_prepared_t * statement, const void **args)
-{
-    SQLRETURN rc = SQL_SUCCESS;
-    int i, argp;
-
-    for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
-        rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
-                             &argp, args, BINARYMODE);
-
-    if (SQL_SUCCEEDED(rc)) {
-        rc = SQLExecute(statement->stmt);
-        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
-                    statement->stmt);
-        }
-    if (SQL_SUCCEEDED(rc)) {
-        rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
-        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
-                    statement->stmt);
-        }
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** pbselect: select using a prepared statement + binary args **/
-static int odbc_pbselect(apr_pool_t * pool, apr_dbd_t * handle,
-                         apr_dbd_results_t ** res,
-                         apr_dbd_prepared_t * statement,
-                         int random, const void **args)
-{
-    SQLRETURN rc = SQL_SUCCESS;
-    int i, argp;
-
-    if (random) {
-        rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
-                            (SQLPOINTER) SQL_SCROLLABLE, 0);
-        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
-                    rc, SQL_HANDLE_STMT, statement->stmt);
-    }
-    if (SQL_SUCCEEDED(rc)) {
-        for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
-            rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
-                                 &argp, args, BINARYMODE);
-        }
-        }
-    if (SQL_SUCCEEDED(rc)) {
-        rc = SQLExecute(statement->stmt);
-        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
-                    statement->stmt);
-        }
-    if (SQL_SUCCEEDED(rc)) {
-        rc = odbc_create_results(handle, statement->stmt, pool, random, res);
-        apr_pool_cleanup_register(pool, *res,
-                                  odbc_close_results, apr_pool_cleanup_null);
-        }
-
-    return APR_FROM_SQL_RESULT(rc);
-}
-
-/** pvbquery: query using a prepared statement + binary args **/
-static int odbc_pvbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
-                         apr_dbd_prepared_t * statement, va_list args)
-{
-    const char **values;
-    int i;
-
-    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
-    for (i = 0; i < statement->nvals; i++)
-        values[i] = va_arg(args, const char *);
-    return odbc_pbquery(pool, handle, nrows, statement, (const void **) values);
-}
-
-/** pvbselect: select using a prepared statement + binary args **/
-static int odbc_pvbselect(apr_pool_t * pool, apr_dbd_t * handle,
-                          apr_dbd_results_t ** res,
-                          apr_dbd_prepared_t * statement,
-                          int random, va_list args)
-{
-    const char **values;
-    int i;
-
-    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
-    for (i = 0; i < statement->nvals; i++)
-        values[i] = va_arg(args, const char *);
-    return odbc_pbselect(pool, handle, res, statement, random, (const void **) values);
-}
-
-APU_MODULE_DECLARE_DATA const apr_dbd_driver_t    ODBC_DRIVER_ENTRY = {
-    ODBC_DRIVER_STRING,
-    odbc_init,
-    odbc_native_handle,
-    odbc_open,
-    odbc_check_conn,
-    odbc_close,
-    odbc_set_dbname,
-    odbc_start_transaction,
-    odbc_end_transaction,
-    odbc_query,
-    odbc_select,
-    odbc_num_cols,
-    odbc_num_tuples,
-    odbc_get_row,
-    odbc_get_entry,
-    odbc_error,
-    odbc_escape,
-    odbc_prepare,
-    odbc_pvquery,
-    odbc_pvselect,
-    odbc_pquery,
-    odbc_pselect,
-    odbc_get_name,
-    odbc_transaction_mode_get,
-    odbc_transaction_mode_set,
-    "?",
-    odbc_pvbquery,
-    odbc_pvbselect,
-    odbc_pbquery,
-    odbc_pbselect,
-    odbc_datum_get
-};
-
-#endif
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apu.h"
+#if APU_HAVE_ODBC
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_env.h"
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_dbd_internal.h"
+#include "apr_thread_proc.h"
+#include "apu_version.h"
+
+/* If library is ODBC-V2, use macros for limited ODBC-V2 support 
+ * No random access in V2.
+ */
+#ifdef ODBCV2
+#define ODBCVER 0x0200
+#include "apr_dbd_odbc_v2.h"
+#endif
+
+/* standard ODBC include files */
+#ifdef HAVE_SQL_H
+#include <sql.h>
+#include <sqlext.h>
+#elif defined(HAVE_ODBC_SQL_H)
+#include <odbc/sql.h>
+#include <odbc/sqlext.h>
+#endif
+
+ /* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver' 
+ * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which
+ * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql.  
+ */
+#ifndef ODBC_DRIVER_NAME
+#define ODBC_DRIVER_NAME odbc
+#endif
+#define STRINGIFY(x) #x
+#define NAMIFY2(n) apr_dbd_##n##_driver
+#define NAMIFY1(n) NAMIFY2(n)
+#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
+#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
+
+/* Required APR version for this driver */
+#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
+#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
+
+
+static SQLHANDLE henv = NULL;           /* ODBC ENV handle is process-wide */
+
+/* Use a CHECK_ERROR macro so we can grab the source line numbers
+ * for error reports */
+static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
+                 SQLSMALLINT type, SQLHANDLE h, int line);
+#define CHECK_ERROR(a,s,r,t,h)  check_error(a,s,r,t,h, __LINE__)
+
+#define SOURCE_FILE __FILE__            /* source file for error messages */
+#define MAX_ERROR_STRING 1024           /* max length of message in dbc */
+#define MAX_COLUMN_NAME 256             /* longest column name recognized */
+#define DEFAULT_BUFFER_SIZE 1024        /* value for defaultBufferSize */
+
+#define MAX_PARAMS  20
+#define DEFAULTSEPS " \t\r\n,="
+#define CSINGLEQUOTE '\''
+#define SSINGLEQUOTE "\'"
+
+#define TEXTMODE 1              /* used for text (APR 1.2) mode params */
+#define BINARYMODE 0            /* used for binary (APR 1.3+) mode params */
+
+/* Identify datatypes which are LOBs 
+ * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB */
+#define IS_LOB(t)  (t == SQL_LONGVARCHAR \
+     || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
+     || t == -98 || t == -99)
+/* These types are CLOBs 
+ * - DB2 DRDA driver uses undefined type -98 for CLOB */
+#define IS_CLOB(t) \
+    (t == SQL_LONGVARCHAR || t == -98)
+
+/* Convert a SQL result to an APR result */
+#define APR_FROM_SQL_RESULT(rc) \
+    (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
+
+/* DBD opaque structures */
+struct apr_dbd_t
+{
+    SQLHANDLE dbc;              /* SQL connection handle - NULL after close */
+    apr_pool_t *pool;           /* connection lifetime pool */
+    char *dbname;               /* ODBC datasource */
+    int lasterrorcode;
+    int lineNumber;
+    char lastError[MAX_ERROR_STRING];
+    int defaultBufferSize;      /* used for CLOBs in text mode, 
+                                 * and when fld size is indeterminate */
+    int transaction_mode;
+    int dboptions;              /* driver options re SQLGetData */
+    int default_transaction_mode;
+    int can_commit;             /* controls end_trans behavior */
+};
+
+struct apr_dbd_results_t
+{
+    SQLHANDLE stmt;             /* parent sql statement handle */
+    SQLHANDLE dbc;              /* parent sql connection handle */
+    apr_pool_t *pool;           /* pool from query or select */
+    apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
+    int random;                 /* random access requested */
+    int ncols;                  /* number of columns */
+    int isclosed;               /* cursor has been closed */
+    char **colnames;            /* array of column names (NULL until used) */
+    SQLPOINTER *colptrs;        /* pointers to column data */
+    SQLINTEGER *colsizes;       /* sizes for columns (enough for txt or bin) */
+    SQLINTEGER *coltextsizes;   /* max-sizes if converted to text */
+    SQLSMALLINT *coltypes;      /* array of SQL data types for columns */
+    SQLLEN *colinds;            /* array of SQL data indicator/strlens */
+    int *colstate;              /* array of column states
+                                 * - avail, bound, present, unavail 
+                                 */
+    int *all_data_fetched;      /* flags data as all fetched, for LOBs  */
+    void *data;                 /* buffer for all data for one row */
+};
+enum                                /* results column states */
+{
+    COL_AVAIL,                  /* data may be retrieved with SQLGetData */
+    COL_PRESENT,                /* data has been retrieved with SQLGetData */
+    COL_BOUND,                  /* column is bound to colptr */
+    COL_RETRIEVED,              /* all data from column has been returned */
+    COL_UNAVAIL                 /* column is unavailable because ODBC driver
+                                 *  requires that columns be retrieved
+                                 *  in ascending order and a higher col 
+                                 *  was accessed */
+};
+
+struct apr_dbd_row_t {
+    SQLHANDLE stmt;             /* parent ODBC statement handle */
+    SQLHANDLE dbc;              /* parent ODBC connection handle */
+    apr_pool_t *pool;           /* pool from get_row */
+    apr_dbd_results_t *res;
+};
+
+struct apr_dbd_transaction_t {
+    SQLHANDLE dbc;              /* parent ODBC connection handle */
+    apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
+};
+
+struct apr_dbd_prepared_t {
+    SQLHANDLE stmt;             /* ODBC statement handle */
+    SQLHANDLE dbc;              /* parent ODBC connection handle */
+    apr_dbd_t *apr_dbd;
+    int nargs;
+    int nvals;
+    int *types;                 /* array of DBD data types */
+
+};
+
+static void odbc_lob_bucket_destroy(void *data);
+static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
+static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
+                                         apr_size_t *len, apr_read_type_e block);
+
+/* the ODBC LOB bucket type */
+static const apr_bucket_type_t odbc_bucket_type = {
+    "ODBC_LOB", 5, APR_BUCKET_DATA,
+    odbc_lob_bucket_destroy,
+    odbc_lob_bucket_read,
+    odbc_lob_bucket_setaside,
+    apr_bucket_shared_split,
+    apr_bucket_shared_copy
+};
+
+
+/* ODBC LOB bucket data */
+typedef struct {
+    /** Ref count for shared bucket */
+    apr_bucket_refcount  refcount;
+    const apr_dbd_row_t *row;
+    int col;
+    SQLSMALLINT type;
+} odbc_bucket;
+
+
+/* SQL datatype mappings to DBD datatypes 
+ * These tables must correspond *exactly* to the apr_dbd_type_e enum 
+ * in apr_dbd_internal.h 
+ */
+
+/* ODBC "C" types to DBD datatypes  */
+static SQLSMALLINT const sqlCtype[] = {
+    SQL_C_DEFAULT,                  /* APR_DBD_TYPE_NONE              */
+    SQL_C_STINYINT,                 /* APR_DBD_TYPE_TINY,       \%hhd */
+    SQL_C_UTINYINT,                 /* APR_DBD_TYPE_UTINY,      \%hhu */
+    SQL_C_SSHORT,                   /* APR_DBD_TYPE_SHORT,      \%hd  */
+    SQL_C_USHORT,                   /* APR_DBD_TYPE_USHORT,     \%hu  */
+    SQL_C_SLONG,                    /* APR_DBD_TYPE_INT,        \%d   */
+    SQL_C_ULONG,                    /* APR_DBD_TYPE_UINT,       \%u   */
+    SQL_C_SLONG,                    /* APR_DBD_TYPE_LONG,       \%ld  */
+    SQL_C_ULONG,                    /* APR_DBD_TYPE_ULONG,      \%lu  */
+    SQL_C_SBIGINT,                  /* APR_DBD_TYPE_LONGLONG,   \%lld */
+    SQL_C_UBIGINT,                  /* APR_DBD_TYPE_ULONGLONG,  \%llu */
+    SQL_C_FLOAT,                    /* APR_DBD_TYPE_FLOAT,      \%f   */
+    SQL_C_DOUBLE,                   /* APR_DBD_TYPE_DOUBLE,     \%lf  */
+    SQL_C_CHAR,                     /* APR_DBD_TYPE_STRING,     \%s   */
+    SQL_C_CHAR,                     /* APR_DBD_TYPE_TEXT,       \%pDt */
+    SQL_C_CHAR, /*SQL_C_TYPE_TIME,      APR_DBD_TYPE_TIME,       \%pDi */
+    SQL_C_CHAR, /*SQL_C_TYPE_DATE,      APR_DBD_TYPE_DATE,       \%pDd */
+    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME,   \%pDa */
+    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP,  \%pDs */
+    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
+    SQL_LONGVARBINARY,              /* APR_DBD_TYPE_BLOB,       \%pDb */
+    SQL_LONGVARCHAR,                /* APR_DBD_TYPE_CLOB,       \%pDc */
+    SQL_TYPE_NULL                   /* APR_DBD_TYPE_NULL        \%pDn */
+};
+
+/*  ODBC Base types to DBD datatypes */
+static SQLSMALLINT const sqlBaseType[] = {
+    SQL_C_DEFAULT,              /* APR_DBD_TYPE_NONE              */
+    SQL_TINYINT,                /* APR_DBD_TYPE_TINY,       \%hhd */
+    SQL_TINYINT,                /* APR_DBD_TYPE_UTINY,      \%hhu */
+    SQL_SMALLINT,               /* APR_DBD_TYPE_SHORT,      \%hd  */
+    SQL_SMALLINT,               /* APR_DBD_TYPE_USHORT,     \%hu  */
+    SQL_INTEGER,                /* APR_DBD_TYPE_INT,        \%d   */
+    SQL_INTEGER,                /* APR_DBD_TYPE_UINT,       \%u   */
+    SQL_INTEGER,                /* APR_DBD_TYPE_LONG,       \%ld  */
+    SQL_INTEGER,                /* APR_DBD_TYPE_ULONG,      \%lu  */
+    SQL_BIGINT,                 /* APR_DBD_TYPE_LONGLONG,   \%lld */
+    SQL_BIGINT,                 /* APR_DBD_TYPE_ULONGLONG,  \%llu */
+    SQL_FLOAT,                  /* APR_DBD_TYPE_FLOAT,      \%f   */
+    SQL_DOUBLE,                 /* APR_DBD_TYPE_DOUBLE,     \%lf  */
+    SQL_CHAR,                   /* APR_DBD_TYPE_STRING,     \%s   */
+    SQL_CHAR,                   /* APR_DBD_TYPE_TEXT,       \%pDt */
+    SQL_CHAR, /*SQL_TIME,          APR_DBD_TYPE_TIME,       \%pDi */
+    SQL_CHAR, /*SQL_DATE,          APR_DBD_TYPE_DATE,       \%pDd */
+    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_DATETIME,   \%pDa */
+    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_TIMESTAMP,  \%pDs */
+    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
+    SQL_LONGVARBINARY,          /* APR_DBD_TYPE_BLOB,       \%pDb */
+    SQL_LONGVARCHAR,            /* APR_DBD_TYPE_CLOB,       \%pDc */
+    SQL_TYPE_NULL               /* APR_DBD_TYPE_NULL        \%pDn */
+};
+
+/*  result sizes for DBD datatypes (-1 for null-terminated) */
+static int const sqlSizes[] = {
+    0,
+    sizeof(char),               /**< \%hhd out: char* */
+    sizeof(unsigned char),      /**< \%hhu out: unsigned char* */
+    sizeof(short),              /**< \%hd  out: short* */
+    sizeof(unsigned short),     /**< \%hu  out: unsigned short* */
+    sizeof(int),                /**< \%d   out: int* */
+    sizeof(unsigned int),       /**< \%u   out: unsigned int* */
+    sizeof(long),               /**< \%ld  out: long* */
+    sizeof(unsigned long),      /**< \%lu  out: unsigned long* */
+    sizeof(apr_int64_t),        /**< \%lld out: apr_int64_t* */
+    sizeof(apr_uint64_t),       /**< \%llu out: apr_uint64_t* */
+    sizeof(float),              /**< \%f   out: float* */
+    sizeof(double),             /**< \%lf  out: double* */
+    -1,                         /**< \%s   out: char** */
+    -1,                         /**< \%pDt out: char** */
+    -1,                         /**< \%pDi out: char** */
+    -1,                         /**< \%pDd out: char** */
+    -1,                         /**< \%pDa out: char** */
+    -1,                         /**< \%pDs out: char** */
+    -1,                         /**< \%pDz out: char** */
+    sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
+    sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
+    0                           /**< \%pDn : in: void*, out: void** */
+};
+
+/*
+*   local functions
+*/
+
+/* close any open results for the connection */
+static apr_status_t odbc_close_results(void *d)
+{   apr_dbd_results_t *dbr = (apr_dbd_results_t *) d;
+    SQLRETURN rc = SQL_SUCCESS;
+    
+    if (dbr && !dbr->isclosed) {
+        rc = SQLCloseCursor(dbr->stmt);
+    }
+    dbr->isclosed = 1;
+    return APR_FROM_SQL_RESULT(rc);
+}
+
+/* close the ODBC statement handle from a  prepare */
+static apr_status_t odbc_close_pstmt(void *s)
+{   
+    SQLRETURN rc = APR_SUCCESS;
+    apr_dbd_prepared_t *statement = s;
+    SQLHANDLE hstmt = statement->stmt;
+    /* stmt is closed if connection has already been closed */
+    if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
+        rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
+        }
+    statement->stmt = NULL;
+    return APR_FROM_SQL_RESULT(rc);
+}
+
+/* close: close/release a connection obtained from open() */
+static apr_status_t odbc_close(apr_dbd_t *handle)
+{
+    SQLRETURN rc = SQL_SUCCESS;
+
+    if (handle->dbc) {
+        rc = SQLDisconnect(handle->dbc);
+        CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
+        rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
+        CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
+        handle->dbc = NULL;
+    }
+    return APR_FROM_SQL_RESULT(rc);
+}
+
+/* odbc_close re-defined for passing to pool cleanup */
+static apr_status_t odbc_close_cleanup(void *handle)
+{
+    return odbc_close( (apr_dbd_t *) handle);
+}
+
+/* close the ODBC environment handle at process termination */
+static apr_status_t odbc_close_env(SQLHANDLE henv)
+{   
+    SQLRETURN rc;
+
+    rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
+    henv = NULL;
+    return APR_FROM_SQL_RESULT(rc);
+}
+
+/* setup the arrays in results for all the returned columns */
+static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t * res, 
+                                         SQLHANDLE stmt)
+{
+    SQLRETURN rc;
+    int maxsize, textsize, realsize, type, isunsigned = 1;
+
+     /* discover the sql type */
+    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
+                         (SQLPOINTER) &isunsigned);
+    isunsigned = (isunsigned == SQL_TRUE);
+
+    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
+                         (SQLPOINTER) &type);
+    if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE)
+        /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
+        rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
+                             0, NULL, (SQLPOINTER) &type);
+    if (!SQL_SUCCEEDED(rc))
+        /* if still unknown make it CHAR */
+        type = SQL_C_CHAR;
+
+    switch (type) {
+    case SQL_INTEGER:
+    case SQL_SMALLINT:
+    case SQL_TINYINT:
+    case SQL_BIGINT:
+      /* fix these numeric binary types up as signed/unsigned for C types */
+      type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
+      break;
+    /* LOB types are not changed to C types */
+    case SQL_LONGVARCHAR: 
+        type = SQL_LONGVARCHAR; 
+        break;
+    case SQL_LONGVARBINARY: 
+        type = SQL_LONGVARBINARY; 
+        break;
+    case SQL_FLOAT : 
+        type = SQL_C_FLOAT; 
+        break;
+    case SQL_DOUBLE : 
+        type = SQL_C_DOUBLE; 
+        break;
+
+    /* DBD wants times as strings */
+    case SQL_TIMESTAMP:      
+    case SQL_DATE:
+    case SQL_TIME:
+    default:
+      type = SQL_C_CHAR;
+    }
+
+    res->coltypes[icol] = type;
+
+    /* size if retrieved as text */
+    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
+                         NULL, (SQLPOINTER) & textsize);
+     if (!SQL_SUCCEEDED(rc) || textsize < 0)
+        textsize = res->apr_dbd->defaultBufferSize;
+    /* for null-term, which sometimes isn't included */
+    textsize++;
+
+    /* real size */
+    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
+                         NULL, (SQLPOINTER) & realsize);
+    if (!SQL_SUCCEEDED(rc))
+        realsize = textsize;
+
+    maxsize = (textsize > realsize) ? textsize : realsize;
+    if ( IS_LOB(type) || maxsize <= 0) {
+        /* LOB types are never bound and have a NULL colptr for binary.
+         * Ingore their real (1-2gb) length & use a default - the larger
+         * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
+         * If not a LOB, but simply unknown length - always use defaultBufferSize.
+         */
+        maxsize = res->apr_dbd->defaultBufferSize;
+        if ( IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE )
+            maxsize = APR_BUCKET_BUFF_SIZE;
+
+        res->colptrs[icol] =  NULL;
+        res->colstate[icol] = COL_AVAIL;
+        res->colsizes[icol] = maxsize;
+        rc = SQL_SUCCESS;
+    }
+    else {
+        res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
+        res->colsizes[icol] = maxsize;
+        if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
+            /* we are allowed to call SQLGetData if we need to */
+            rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol], 
+                            res->colptrs[icol], maxsize, 
+                            &(res->colinds[icol]) );
+            CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
+                        stmt);
+            res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
+        }
+        else {
+            /* this driver won't allow us to call SQLGetData on bound 
+             * columns - so don't bind any */
+            res->colstate[icol] = COL_AVAIL;
+            rc = SQL_SUCCESS;
+        }
+    }
+    return rc;
+}
+
+/* create and populate an apr_dbd_results_t for a select */
+static SQLRETURN odbc_create_results(apr_dbd_t * handle, SQLHANDLE hstmt,
+                                     apr_pool_t * pool, const int random,
+                                     apr_dbd_results_t ** res)
+{
+    SQLRETURN rc;
+    SQLSMALLINT ncols;
+
+    *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+    (*res)->stmt = hstmt;
+    (*res)->dbc = handle->dbc;
+    (*res)->pool = pool;
+    (*res)->random = random;
+    (*res)->apr_dbd = handle;
+    rc = SQLNumResultCols(hstmt, &ncols);
+    CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
+    (*res)->ncols = ncols;
+
+    if SQL_SUCCEEDED(rc) {
+        int i;
+
+        (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
+        (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
+        (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
+        (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
+        (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
+        (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
+        (*res)->ncols = ncols;
+
+        for (i = 0 ; i < ncols ; i++)
+            odbc_set_result_column(i, (*res), hstmt);
+        }
+    return rc;
+}
+
+
+/* bind a parameter - input params only, does not support output parameters */
+static SQLRETURN odbc_bind_param(apr_pool_t * pool,
+                                 apr_dbd_prepared_t * statement, const int narg,
+                                 const SQLSMALLINT type, int *argp,
+                                 const void **args, const int textmode)
+{
+    SQLRETURN rc;
+    SQLSMALLINT baseType, cType;
+    void *ptr;
+    SQLUINTEGER len;
+    SQLINTEGER *indicator;
+    static SQLINTEGER nullValue = SQL_NULL_DATA;
+    static SQLSMALLINT inOut = SQL_PARAM_INPUT;     /* only input params */
+
+    /* bind a NULL data value */
+    if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
+        baseType = SQL_CHAR;
+        cType = SQL_C_CHAR;
+        ptr = &nullValue;
+        len = sizeof(SQLINTEGER);
+        indicator = &nullValue;
+        (*argp)++;
+    }
+    /* bind a non-NULL data value */
+    else {
+        baseType = sqlBaseType[type];
+        cType = sqlCtype[type];
+        indicator = NULL;
+        /* LOBs */
+        if (IS_LOB(cType)) {
+            ptr = (void *) args[*argp];
+            len = (SQLUINTEGER) * (apr_size_t *) args[*argp + 1];
+            cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
+            (*argp) += 4;  /* LOBs consume 4 args (last two are unused) */
+        }
+        /* non-LOBs */
+        else {
+            switch (baseType) {
+            case SQL_CHAR:
+            case SQL_DATE:
+            case SQL_TIME:
+            case SQL_TIMESTAMP:
+                ptr = (void *) args[*argp];
+                len = (SQLUINTEGER) strlen(ptr);
+                break;
+            case SQL_TINYINT:
+                ptr = apr_palloc(pool, sizeof(unsigned char));
+                len = sizeof(unsigned char);
+                *(unsigned char *) ptr =
+                    (textmode ?
+                     atoi(args[*argp]) : *(unsigned char *) args[*argp]);
+                break;
+            case SQL_SMALLINT:
+                ptr = apr_palloc(pool, sizeof(short));
+                len = sizeof(short);
+                *(short *) ptr =
+                    (textmode ? atoi(args[*argp]) : *(short *) args[*argp]);
+                break;
+            case SQL_INTEGER:
+                ptr = apr_palloc(pool, sizeof(int));
+                len = sizeof(int);
+                *(long *) ptr =
+                    (textmode ? atol(args[*argp]) : *(long *) args[*argp]);
+                break;
+            case SQL_FLOAT:
+                ptr = apr_palloc(pool, sizeof(float));
+                len = sizeof(float);
+                *(float *) ptr =
+                    (textmode ?
+                     (float) atof(args[*argp]) : *(float *) args[*argp]);
+                break;
+            case SQL_DOUBLE:
+                ptr = apr_palloc(pool, sizeof(double));
+                len = sizeof(double);
+                *(double *) ptr =
+                    (textmode ? atof(args[*argp]) : *(double *)
+                     args[*argp]);
+                break;
+            case SQL_BIGINT:
+                ptr = apr_palloc(pool, sizeof(apr_int64_t));
+                len = sizeof(apr_int64_t);
+                *(apr_int64_t *) ptr =
+                    (textmode ?
+                     apr_atoi64(args[*argp]) : *(apr_int64_t *) args[*argp]);
+                break;
+            default:
+                return APR_EGENERAL;
+            }
+            (*argp)++;          /* non LOBs consume one argument */
+        }
+    }
+    rc = SQLBindParameter(statement->stmt, narg, inOut, cType, 
+                          baseType, len, 0, ptr, len, indicator);
+    CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
+                statement->stmt);
+    return rc;
+}
+
+/* LOB / Bucket Brigade functions */
+
+
+
+/* bucket type specific destroy */
+static void odbc_lob_bucket_destroy(void *data)
+{
+    odbc_bucket *bd = data;
+
+    if (apr_bucket_shared_destroy(bd))
+        apr_bucket_free(bd);
+}
+
+/* set aside a bucket if possible */
+static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
+{
+    odbc_bucket *bd = (odbc_bucket *) e->data;
+
+    /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
+    if (apr_pool_is_ancestor(bd->row->pool, pool))
+        return APR_SUCCESS;
+    
+    return apr_bucket_setaside_notimpl(e, pool);
+}
+
+/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
+static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
+                                         apr_size_t *len, apr_read_type_e block)
+{
+    SQLRETURN rc;
+    SQLINTEGER len_indicator;
+    SQLSMALLINT type;
+    odbc_bucket *bd = (odbc_bucket *) e->data;
+    apr_bucket *nxt;
+    void *buf;
+    int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
+    int eos;
+    
+    /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
+    type = bd->row->res->coltypes[bd->col];
+    type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
+
+    /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE, 
+     *   but they may be much bigger per the BUFSIZE parameter.
+    */
+    if (bufsize < APR_BUCKET_BUFF_SIZE)
+            bufsize = APR_BUCKET_BUFF_SIZE;
+
+    buf = apr_bucket_alloc(bufsize, e->list);
+    *str = NULL;
+    *len = 0;
+
+    rc = SQLGetData(bd->row->res->stmt, bd->col + 1, 
+                    type, buf, bufsize, 
+                    &len_indicator);
+
+    CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc, 
+                SQL_HANDLE_STMT, bd->row->res->stmt);
+    
+    if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
+        len_indicator = 0;
+
+    if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
+
+        if (rc = SQL_SUCCESS_WITH_INFO
+            && ( len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize) ) {
+            /* not the last read = a full buffer. CLOBs have a null terminator */
+            *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
+
+             eos = 0;
+        }
+        else {
+            /* the last read - len_indicator is supposed to be the length, 
+             * but some driver get this wrong and return the total length.
+             * We try to handle both interpretations.
+             */
+            *len =  (len_indicator > bufsize 
+                     && len_indicator >= (SQLINTEGER) e->start)
+                ? (len_indicator - (SQLINTEGER) e->start) : len_indicator;
+
+            eos = 1;
+        }
+

[... 954 lines stripped ...]


Mime
View raw message