apr_dbd_odbc.c revision 258602
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apu.h"
18#if APU_HAVE_ODBC
19
20#include "apr.h"
21#include "apr_strings.h"
22#include "apr_buckets.h"
23#include "apr_env.h"
24#include "apr_file_io.h"
25#include "apr_file_info.h"
26#include "apr_dbd_internal.h"
27#include "apr_thread_proc.h"
28#include "apu_version.h"
29#include "apu_config.h"
30
31#include <stdlib.h>
32
33/* If library is ODBC-V2, use macros for limited ODBC-V2 support
34 * No random access in V2.
35 */
36#ifdef ODBCV2
37#define ODBCVER 0x0200
38#include "apr_dbd_odbc_v2.h"
39#endif
40
41/* standard ODBC include files */
42#ifdef HAVE_SQL_H
43#include <sql.h>
44#include <sqlext.h>
45#elif defined(HAVE_ODBC_SQL_H)
46#include <odbc/sql.h>
47#include <odbc/sqlext.h>
48#endif
49
50/* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver'
51 * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which
52 * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql.
53 */
54#ifndef ODBC_DRIVER_NAME
55#define ODBC_DRIVER_NAME odbc
56#endif
57#define STRINGIFY(x) #x
58#define NAMIFY2(n) apr_dbd_##n##_driver
59#define NAMIFY1(n) NAMIFY2(n)
60#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
61#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
62
63/* Required APR version for this driver */
64#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
65#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
66
67static SQLHANDLE henv = NULL;           /* ODBC ENV handle is process-wide */
68
69/* Use a CHECK_ERROR macro so we can grab the source line numbers
70 * for error reports
71 */
72static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
73                 SQLSMALLINT type, SQLHANDLE h, int line);
74#define CHECK_ERROR(a,s,r,t,h)  check_error(a,s,r,t,h, __LINE__)
75
76#define SOURCE_FILE __FILE__            /* source file for error messages */
77#define MAX_ERROR_STRING 1024           /* max length of message in dbc */
78#define MAX_COLUMN_NAME 256             /* longest column name recognized */
79#define DEFAULT_BUFFER_SIZE 1024        /* value for defaultBufferSize */
80
81#define MAX_PARAMS  20
82#define DEFAULTSEPS " \t\r\n,="
83#define CSINGLEQUOTE '\''
84#define SSINGLEQUOTE "\'"
85
86#define TEXTMODE 1              /* used for text (APR 1.2) mode params */
87#define BINARYMODE 0            /* used for binary (APR 1.3+) mode params */
88
89/* Identify datatypes which are LOBs
90 * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB
91 */
92#define IS_LOB(t)  (t == SQL_LONGVARCHAR \
93     || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
94     || t == -98 || t == -99)
95
96/* These types are CLOBs
97 * - DB2 DRDA driver uses undefined type -98 for CLOB
98 */
99#define IS_CLOB(t) \
100    (t == SQL_LONGVARCHAR || t == -98)
101
102/* Convert a SQL result to an APR result */
103#define APR_FROM_SQL_RESULT(rc) \
104    (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
105
106/* DBD opaque structures */
107struct apr_dbd_t
108{
109    SQLHANDLE dbc;              /* SQL connection handle - NULL after close */
110    apr_pool_t *pool;           /* connection lifetime pool */
111    char *dbname;               /* ODBC datasource */
112    int lasterrorcode;
113    int lineNumber;
114    char lastError[MAX_ERROR_STRING];
115    int defaultBufferSize;      /* used for CLOBs in text mode,
116                                 * and when fld size is indeterminate */
117    intptr_t transaction_mode;
118    intptr_t dboptions;         /* driver options re SQLGetData */
119    intptr_t default_transaction_mode;
120    int can_commit;             /* controls end_trans behavior */
121};
122
123struct apr_dbd_results_t
124{
125    SQLHANDLE stmt;             /* parent sql statement handle */
126    SQLHANDLE dbc;              /* parent sql connection handle */
127    apr_pool_t *pool;           /* pool from query or select */
128    apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
129    int random;                 /* random access requested */
130    int ncols;                  /* number of columns */
131    int isclosed;               /* cursor has been closed */
132    char **colnames;            /* array of column names (NULL until used) */
133    SQLPOINTER *colptrs;        /* pointers to column data */
134    SQLINTEGER *colsizes;       /* sizes for columns (enough for txt or bin) */
135    SQLINTEGER *coltextsizes;   /* max-sizes if converted to text */
136    SQLSMALLINT *coltypes;      /* array of SQL data types for columns */
137    SQLLEN *colinds;            /* array of SQL data indicator/strlens */
138    int *colstate;              /* array of column states
139                                 * - avail, bound, present, unavail
140                                 */
141    int *all_data_fetched;      /* flags data as all fetched, for LOBs  */
142    void *data;                 /* buffer for all data for one row */
143};
144
145enum                            /* results column states */
146{
147    COL_AVAIL,                  /* data may be retrieved with SQLGetData */
148    COL_PRESENT,                /* data has been retrieved with SQLGetData */
149    COL_BOUND,                  /* column is bound to colptr */
150    COL_RETRIEVED,              /* all data from column has been returned */
151    COL_UNAVAIL                 /* column is unavailable because ODBC driver
152                                 *  requires that columns be retrieved
153                                 *  in ascending order and a higher col
154                                 *  was accessed
155                                 */
156};
157
158struct apr_dbd_row_t {
159    SQLHANDLE stmt;             /* parent ODBC statement handle */
160    SQLHANDLE dbc;              /* parent ODBC connection handle */
161    apr_pool_t *pool;           /* pool from get_row */
162    apr_dbd_results_t *res;
163};
164
165struct apr_dbd_transaction_t {
166    SQLHANDLE dbc;              /* parent ODBC connection handle */
167    apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
168};
169
170struct apr_dbd_prepared_t {
171    SQLHANDLE stmt;             /* ODBC statement handle */
172    SQLHANDLE dbc;              /* parent ODBC connection handle */
173    apr_dbd_t *apr_dbd;
174    int nargs;
175    int nvals;
176    int *types;                 /* array of DBD data types */
177};
178
179static void odbc_lob_bucket_destroy(void *data);
180static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
181static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
182                                         apr_size_t *len, apr_read_type_e block);
183
184/* the ODBC LOB bucket type */
185static const apr_bucket_type_t odbc_bucket_type = {
186    "ODBC_LOB", 5, APR_BUCKET_DATA,
187    odbc_lob_bucket_destroy,
188    odbc_lob_bucket_read,
189    odbc_lob_bucket_setaside,
190    apr_bucket_shared_split,
191    apr_bucket_shared_copy
192};
193
194/* ODBC LOB bucket data */
195typedef struct {
196    /** Ref count for shared bucket */
197    apr_bucket_refcount  refcount;
198    const apr_dbd_row_t *row;
199    int col;
200    SQLSMALLINT type;
201} odbc_bucket;
202
203/* SQL datatype mappings to DBD datatypes
204 * These tables must correspond *exactly* to the apr_dbd_type_e enum
205 * in apr_dbd.h
206 */
207
208/* ODBC "C" types to DBD datatypes  */
209static SQLSMALLINT const sqlCtype[] = {
210    SQL_C_DEFAULT,                  /* APR_DBD_TYPE_NONE              */
211    SQL_C_STINYINT,                 /* APR_DBD_TYPE_TINY,       \%hhd */
212    SQL_C_UTINYINT,                 /* APR_DBD_TYPE_UTINY,      \%hhu */
213    SQL_C_SSHORT,                   /* APR_DBD_TYPE_SHORT,      \%hd  */
214    SQL_C_USHORT,                   /* APR_DBD_TYPE_USHORT,     \%hu  */
215    SQL_C_SLONG,                    /* APR_DBD_TYPE_INT,        \%d   */
216    SQL_C_ULONG,                    /* APR_DBD_TYPE_UINT,       \%u   */
217    SQL_C_SLONG,                    /* APR_DBD_TYPE_LONG,       \%ld  */
218    SQL_C_ULONG,                    /* APR_DBD_TYPE_ULONG,      \%lu  */
219    SQL_C_SBIGINT,                  /* APR_DBD_TYPE_LONGLONG,   \%lld */
220    SQL_C_UBIGINT,                  /* APR_DBD_TYPE_ULONGLONG,  \%llu */
221    SQL_C_FLOAT,                    /* APR_DBD_TYPE_FLOAT,      \%f   */
222    SQL_C_DOUBLE,                   /* APR_DBD_TYPE_DOUBLE,     \%lf  */
223    SQL_C_CHAR,                     /* APR_DBD_TYPE_STRING,     \%s   */
224    SQL_C_CHAR,                     /* APR_DBD_TYPE_TEXT,       \%pDt */
225    SQL_C_CHAR, /*SQL_C_TYPE_TIME,      APR_DBD_TYPE_TIME,       \%pDi */
226    SQL_C_CHAR, /*SQL_C_TYPE_DATE,      APR_DBD_TYPE_DATE,       \%pDd */
227    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME,   \%pDa */
228    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP,  \%pDs */
229    SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
230    SQL_LONGVARBINARY,              /* APR_DBD_TYPE_BLOB,       \%pDb */
231    SQL_LONGVARCHAR,                /* APR_DBD_TYPE_CLOB,       \%pDc */
232    SQL_TYPE_NULL                   /* APR_DBD_TYPE_NULL        \%pDn */
233};
234#define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0]))
235
236/*  ODBC Base types to DBD datatypes */
237static SQLSMALLINT const sqlBaseType[] = {
238    SQL_C_DEFAULT,              /* APR_DBD_TYPE_NONE              */
239    SQL_TINYINT,                /* APR_DBD_TYPE_TINY,       \%hhd */
240    SQL_TINYINT,                /* APR_DBD_TYPE_UTINY,      \%hhu */
241    SQL_SMALLINT,               /* APR_DBD_TYPE_SHORT,      \%hd  */
242    SQL_SMALLINT,               /* APR_DBD_TYPE_USHORT,     \%hu  */
243    SQL_INTEGER,                /* APR_DBD_TYPE_INT,        \%d   */
244    SQL_INTEGER,                /* APR_DBD_TYPE_UINT,       \%u   */
245    SQL_INTEGER,                /* APR_DBD_TYPE_LONG,       \%ld  */
246    SQL_INTEGER,                /* APR_DBD_TYPE_ULONG,      \%lu  */
247    SQL_BIGINT,                 /* APR_DBD_TYPE_LONGLONG,   \%lld */
248    SQL_BIGINT,                 /* APR_DBD_TYPE_ULONGLONG,  \%llu */
249    SQL_FLOAT,                  /* APR_DBD_TYPE_FLOAT,      \%f   */
250    SQL_DOUBLE,                 /* APR_DBD_TYPE_DOUBLE,     \%lf  */
251    SQL_CHAR,                   /* APR_DBD_TYPE_STRING,     \%s   */
252    SQL_CHAR,                   /* APR_DBD_TYPE_TEXT,       \%pDt */
253    SQL_CHAR, /*SQL_TIME,          APR_DBD_TYPE_TIME,       \%pDi */
254    SQL_CHAR, /*SQL_DATE,          APR_DBD_TYPE_DATE,       \%pDd */
255    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_DATETIME,   \%pDa */
256    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_TIMESTAMP,  \%pDs */
257    SQL_CHAR, /*SQL_TIMESTAMP,     APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
258    SQL_LONGVARBINARY,          /* APR_DBD_TYPE_BLOB,       \%pDb */
259    SQL_LONGVARCHAR,            /* APR_DBD_TYPE_CLOB,       \%pDc */
260    SQL_TYPE_NULL               /* APR_DBD_TYPE_NULL        \%pDn */
261};
262
263/*  result sizes for DBD datatypes (-1 for null-terminated) */
264static int const sqlSizes[] = {
265    0,
266    sizeof(char),               /**< \%hhd out: char* */
267    sizeof(unsigned char),      /**< \%hhu out: unsigned char* */
268    sizeof(short),              /**< \%hd  out: short* */
269    sizeof(unsigned short),     /**< \%hu  out: unsigned short* */
270    sizeof(int),                /**< \%d   out: int* */
271    sizeof(unsigned int),       /**< \%u   out: unsigned int* */
272    sizeof(long),               /**< \%ld  out: long* */
273    sizeof(unsigned long),      /**< \%lu  out: unsigned long* */
274    sizeof(apr_int64_t),        /**< \%lld out: apr_int64_t* */
275    sizeof(apr_uint64_t),       /**< \%llu out: apr_uint64_t* */
276    sizeof(float),              /**< \%f   out: float* */
277    sizeof(double),             /**< \%lf  out: double* */
278    -1,                         /**< \%s   out: char** */
279    -1,                         /**< \%pDt out: char** */
280    -1,                         /**< \%pDi out: char** */
281    -1,                         /**< \%pDd out: char** */
282    -1,                         /**< \%pDa out: char** */
283    -1,                         /**< \%pDs out: char** */
284    -1,                         /**< \%pDz out: char** */
285    sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
286    sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
287    0                           /**< \%pDn : in: void*, out: void** */
288};
289
290/*
291 * local functions
292 */
293
294/* close any open results for the connection */
295static apr_status_t odbc_close_results(void *d)
296{
297    apr_dbd_results_t *dbr = (apr_dbd_results_t *)d;
298    SQLRETURN rc = SQL_SUCCESS;
299
300    if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) {
301    	if (!dbr->isclosed)
302            rc = SQLCloseCursor(dbr->stmt);
303    	dbr->isclosed = 1;
304    }
305    return APR_FROM_SQL_RESULT(rc);
306}
307
308/* close the ODBC statement handle from a  prepare */
309static apr_status_t odbc_close_pstmt(void *s)
310{
311    SQLRETURN rc = APR_SUCCESS;
312    apr_dbd_prepared_t *statement = s;
313
314    /* stmt is closed if connection has already been closed */
315    if (statement) {
316        SQLHANDLE hstmt = statement->stmt;
317
318        if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
319            rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
320        }
321        statement->stmt = NULL;
322    }
323    return APR_FROM_SQL_RESULT(rc);
324}
325
326/* close: close/release a connection obtained from open() */
327static apr_status_t odbc_close(apr_dbd_t *handle)
328{
329    SQLRETURN rc = SQL_SUCCESS;
330
331    if (handle->dbc) {
332        rc = SQLDisconnect(handle->dbc);
333        CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
334        rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
335        CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
336        handle->dbc = NULL;
337    }
338    return APR_FROM_SQL_RESULT(rc);
339}
340
341/* odbc_close re-defined for passing to pool cleanup */
342static apr_status_t odbc_close_cleanup(void *handle)
343{
344    return odbc_close((apr_dbd_t *)handle);
345}
346
347/* close the ODBC environment handle at process termination */
348static apr_status_t odbc_close_env(SQLHANDLE henv)
349{
350    SQLRETURN rc;
351
352    rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
353    henv = NULL;
354    return APR_FROM_SQL_RESULT(rc);
355}
356
357/* setup the arrays in results for all the returned columns */
358static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t *res,
359                                        SQLHANDLE stmt)
360{
361    SQLRETURN rc;
362    intptr_t maxsize, textsize, realsize, type, isunsigned = 1;
363
364    /* discover the sql type */
365    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
366                         (SQLPOINTER)&isunsigned);
367    isunsigned = (isunsigned == SQL_TRUE);
368
369    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
370                         (SQLPOINTER)&type);
371    if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE) {
372        /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
373        rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
374                             0, NULL, (SQLPOINTER)&type);
375    }
376
377    if (!SQL_SUCCEEDED(rc)) {
378        /* if still unknown make it CHAR */
379        type = SQL_C_CHAR;
380    }
381
382    switch (type) {
383    case SQL_INTEGER:
384    case SQL_SMALLINT:
385    case SQL_TINYINT:
386    case SQL_BIGINT:
387      /* fix these numeric binary types up as signed/unsigned for C types */
388      type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
389      break;
390    /* LOB types are not changed to C types */
391    case SQL_LONGVARCHAR:
392        type = SQL_LONGVARCHAR;
393        break;
394    case SQL_LONGVARBINARY:
395        type = SQL_LONGVARBINARY;
396        break;
397    case SQL_FLOAT :
398        type = SQL_C_FLOAT;
399        break;
400    case SQL_DOUBLE :
401        type = SQL_C_DOUBLE;
402        break;
403
404    /* DBD wants times as strings */
405    case SQL_TIMESTAMP:
406    case SQL_DATE:
407    case SQL_TIME:
408    default:
409      type = SQL_C_CHAR;
410    }
411
412    res->coltypes[icol] = (SQLSMALLINT)type;
413
414    /* size if retrieved as text */
415    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
416                         NULL, (SQLPOINTER)&textsize);
417    if (!SQL_SUCCEEDED(rc) || textsize < 0) {
418        textsize = res->apr_dbd->defaultBufferSize;
419    }
420    /* for null-term, which sometimes isn't included */
421    textsize++;
422
423    /* real size */
424    rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
425                         NULL, (SQLPOINTER)&realsize);
426    if (!SQL_SUCCEEDED(rc)) {
427        realsize = textsize;
428    }
429
430    maxsize = (textsize > realsize) ? textsize : realsize;
431    if (IS_LOB(type) || maxsize <= 0) {
432        /* LOB types are never bound and have a NULL colptr for binary.
433         * Ingore their real (1-2gb) length & use a default - the larger
434         * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
435         * If not a LOB, but simply unknown length - always use defaultBufferSize.
436         */
437        maxsize = res->apr_dbd->defaultBufferSize;
438        if (IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE) {
439            maxsize = APR_BUCKET_BUFF_SIZE;
440        }
441
442        res->colptrs[icol] =  NULL;
443        res->colstate[icol] = COL_AVAIL;
444        res->colsizes[icol] = (SQLINTEGER)maxsize;
445        rc = SQL_SUCCESS;
446    }
447    else {
448        res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
449        res->colsizes[icol] = (SQLINTEGER)maxsize;
450        if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
451            /* we are allowed to call SQLGetData if we need to */
452            rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
453                            res->colptrs[icol], maxsize,
454                            &(res->colinds[icol]));
455            CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
456                        stmt);
457            res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
458        }
459        else {
460            /* this driver won't allow us to call SQLGetData on bound
461             * columns - so don't bind any
462             */
463            res->colstate[icol] = COL_AVAIL;
464            rc = SQL_SUCCESS;
465        }
466    }
467    return rc;
468}
469
470/* create and populate an apr_dbd_results_t for a select */
471static SQLRETURN odbc_create_results(apr_dbd_t *handle, SQLHANDLE hstmt,
472                                     apr_pool_t *pool, const int random,
473                                     apr_dbd_results_t **res)
474{
475    SQLRETURN rc;
476    SQLSMALLINT ncols;
477
478    *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
479    (*res)->stmt = hstmt;
480    (*res)->dbc = handle->dbc;
481    (*res)->pool = pool;
482    (*res)->random = random;
483    (*res)->apr_dbd = handle;
484    rc = SQLNumResultCols(hstmt, &ncols);
485    CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
486    (*res)->ncols = ncols;
487
488    if (SQL_SUCCEEDED(rc)) {
489        int i;
490
491        (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
492        (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
493        (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
494        (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
495        (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
496        (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
497        (*res)->ncols = ncols;
498
499        for (i = 0; i < ncols; i++) {
500            odbc_set_result_column(i, (*res), hstmt);
501        }
502    }
503    return rc;
504}
505
506
507/* bind a parameter - input params only, does not support output parameters */
508static SQLRETURN odbc_bind_param(apr_pool_t *pool,
509                                 apr_dbd_prepared_t *statement, const int narg,
510                                 const SQLSMALLINT type, int *argp,
511                                 const void **args, const int textmode)
512{
513    SQLRETURN rc;
514    SQLSMALLINT baseType, cType;
515    void *ptr;
516    SQLULEN len;
517    SQLLEN *indicator;
518    static SQLLEN nullValue = SQL_NULL_DATA;
519    static SQLSMALLINT inOut = SQL_PARAM_INPUT;     /* only input params */
520
521    /* bind a NULL data value */
522    if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
523        baseType = SQL_CHAR;
524        cType = SQL_C_CHAR;
525        ptr = &nullValue;
526        len = sizeof(SQLINTEGER);
527        indicator = &nullValue;
528        (*argp)++;
529    }
530    /* bind a non-NULL data value */
531    else {
532        if (type < 0 || type >= NUM_APR_DBD_TYPES) {
533            return APR_EGENERAL;
534        }
535
536        baseType = sqlBaseType[type];
537        cType = sqlCtype[type];
538        indicator = NULL;
539        /* LOBs */
540        if (IS_LOB(cType)) {
541            ptr = (void *)args[*argp];
542            len = (SQLULEN) * (apr_size_t *)args[*argp + 1];
543            cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
544            (*argp) += 4;  /* LOBs consume 4 args (last two are unused) */
545        }
546        /* non-LOBs */
547        else {
548            switch (baseType) {
549            case SQL_CHAR:
550            case SQL_DATE:
551            case SQL_TIME:
552            case SQL_TIMESTAMP:
553                ptr = (void *)args[*argp];
554                len = (SQLULEN)strlen(ptr);
555                break;
556            case SQL_TINYINT:
557                ptr = apr_palloc(pool, sizeof(unsigned char));
558                len = sizeof(unsigned char);
559                *(unsigned char *)ptr =
560                    (textmode ?
561                     atoi(args[*argp]) : *(unsigned char *)args[*argp]);
562                break;
563            case SQL_SMALLINT:
564                ptr = apr_palloc(pool, sizeof(short));
565                len = sizeof(short);
566                *(short *)ptr =
567                    (textmode ? atoi(args[*argp]) : *(short *)args[*argp]);
568                break;
569            case SQL_INTEGER:
570                ptr = apr_palloc(pool, sizeof(int));
571                len = sizeof(int);
572                *(long *)ptr =
573                    (textmode ? atol(args[*argp]) : *(long *)args[*argp]);
574                break;
575            case SQL_FLOAT:
576                ptr = apr_palloc(pool, sizeof(float));
577                len = sizeof(float);
578                *(float *)ptr =
579                    (textmode ?
580                     (float)atof(args[*argp]) : *(float *)args[*argp]);
581                break;
582            case SQL_DOUBLE:
583                ptr = apr_palloc(pool, sizeof(double));
584                len = sizeof(double);
585                *(double *)ptr =
586                    (textmode ? atof(args[*argp]) : *(double *)
587                     args[*argp]);
588                break;
589            case SQL_BIGINT:
590                ptr = apr_palloc(pool, sizeof(apr_int64_t));
591                len = sizeof(apr_int64_t);
592                *(apr_int64_t *)ptr =
593                    (textmode ?
594                     apr_atoi64(args[*argp]) : *(apr_int64_t *)args[*argp]);
595                break;
596            default:
597                return APR_EGENERAL;
598            }
599            (*argp)++;          /* non LOBs consume one argument */
600        }
601    }
602    rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
603                          baseType, len, 0, ptr, len, indicator);
604    CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
605                statement->stmt);
606    return rc;
607}
608
609/* LOB / Bucket Brigade functions */
610
611/* bucket type specific destroy */
612static void odbc_lob_bucket_destroy(void *data)
613{
614    odbc_bucket *bd = data;
615
616    if (apr_bucket_shared_destroy(bd))
617        apr_bucket_free(bd);
618}
619
620/* set aside a bucket if possible */
621static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
622{
623    odbc_bucket *bd = (odbc_bucket *)e->data;
624
625    /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
626    if (apr_pool_is_ancestor(bd->row->pool, pool))
627        return APR_SUCCESS;
628
629    return apr_bucket_setaside_notimpl(e, pool);
630}
631
632/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
633static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
634                                         apr_size_t *len, apr_read_type_e block)
635{
636    SQLRETURN rc;
637    SQLLEN len_indicator;
638    SQLSMALLINT type;
639    odbc_bucket *bd = (odbc_bucket *)e->data;
640    apr_bucket *nxt;
641    void *buf;
642    int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
643    int eos;
644
645    /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
646    type = bd->row->res->coltypes[bd->col];
647    type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
648
649    /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE,
650     *   but they may be much bigger per the BUFSIZE parameter.
651     */
652    if (bufsize < APR_BUCKET_BUFF_SIZE)
653        bufsize = APR_BUCKET_BUFF_SIZE;
654
655    buf = apr_bucket_alloc(bufsize, e->list);
656    *str = NULL;
657    *len = 0;
658
659    rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
660                    type, buf, bufsize,
661                    &len_indicator);
662
663    CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
664                SQL_HANDLE_STMT, bd->row->res->stmt);
665
666    if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
667        len_indicator = 0;
668
669    if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
670
671        if (rc == SQL_SUCCESS_WITH_INFO
672            && (len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize)) {
673            /* not the last read = a full buffer. CLOBs have a null terminator */
674            *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
675
676             eos = 0;
677        }
678        else {
679            /* the last read - len_indicator is supposed to be the length,
680             * but some driver get this wrong and return the total length.
681             * We try to handle both interpretations.
682             */
683            *len =  (len_indicator > bufsize
684                     && len_indicator >= (SQLLEN)e->start)
685                ? (len_indicator - (SQLLEN)e->start) : len_indicator;
686
687            eos = 1;
688        }
689
690        if (!eos) {
691            /* Create a new LOB bucket to append and append it */
692            nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
693            APR_BUCKET_INIT(nxt);
694            nxt->length = -1;
695            nxt->data   = e->data;
696            nxt->type   = &odbc_bucket_type;
697            nxt->free   = apr_bucket_free;
698            nxt->list   = e->list;
699            nxt->start  = e->start + *len;
700            APR_BUCKET_INSERT_AFTER(e, nxt);
701        }
702        else {
703            odbc_lob_bucket_destroy(e->data);
704        }
705        /* make current bucket into a heap bucket */
706        apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
707        *str = buf;
708
709        /* No data is success in this context */
710        rc = SQL_SUCCESS;
711    }
712    return APR_FROM_SQL_RESULT(rc);
713}
714
715/* Create a bucket brigade on the row pool for a LOB column */
716static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
717                                       SQLSMALLINT type, apr_bucket_brigade *bb)
718{
719    apr_bucket_alloc_t *list = bb->bucket_alloc;
720    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
721    odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
722    apr_bucket *eos = apr_bucket_eos_create(list);
723
724    bd->row = row;
725    bd->col = col;
726    bd->type = type;
727
728    APR_BUCKET_INIT(b);
729    b->type = &odbc_bucket_type;
730    b->free = apr_bucket_free;
731    b->list = list;
732    /* LOB lengths are unknown in ODBC */
733    b = apr_bucket_shared_make(b, bd, 0, -1);
734
735    APR_BRIGADE_INSERT_TAIL(bb, b);
736    APR_BRIGADE_INSERT_TAIL(bb, eos);
737
738    return APR_SUCCESS;
739}
740
741/* returns a data pointer for a column,  returns NULL for NULL value,
742 * return -1 if data not available
743 */
744static void *odbc_get(const apr_dbd_row_t *row, const int col,
745                      const SQLSMALLINT sqltype)
746{
747    SQLRETURN rc;
748    SQLLEN indicator;
749    int state = row->res->colstate[col];
750    intptr_t options = row->res->apr_dbd->dboptions;
751
752    switch (state) {
753    case (COL_UNAVAIL):
754        return (void *)-1;
755    case (COL_RETRIEVED):
756        return NULL;
757
758    case (COL_BOUND):
759    case (COL_PRESENT):
760        if (sqltype == row->res->coltypes[col]) {
761            /* same type and we already have the data */
762            row->res->colstate[col] = COL_RETRIEVED;
763            return (row->res->colinds[col] == SQL_NULL_DATA) ?
764                NULL : row->res->colptrs[col];
765        }
766    }
767
768    /* we need to get the data now */
769    if (!(options & SQL_GD_ANY_ORDER)) {
770        /* this ODBC driver requires columns to be retrieved in order,
771         * so we attempt to get every prior un-gotten non-LOB column
772         */
773        int i;
774        for (i = 0; i < col; i++) {
775            if (row->res->colstate[i] == COL_AVAIL) {
776                if (IS_LOB(row->res->coltypes[i]))
777                       row->res->colstate[i] = COL_UNAVAIL;
778                else {
779                    odbc_get(row, i, row->res->coltypes[i]);
780                    row->res->colstate[i] = COL_PRESENT;
781                }
782            }
783        }
784    }
785
786    if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
787        /* this driver won't let us re-get bound columns */
788        return (void *)-1;
789
790    /* a LOB might not have a buffer allocated yet - so create one */
791    if (!row->res->colptrs[col])
792        row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
793
794    rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
795                    row->res->colsizes[col], &indicator);
796    CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
797                row->res->stmt);
798    if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
799        return NULL;
800
801    if (SQL_SUCCEEDED(rc)) {
802        /* whatever it was originally, it is now this sqltype */
803        row->res->coltypes[col] = sqltype;
804        /* this allows getting CLOBs in text mode by calling get_entry
805         *   until it returns NULL
806         */
807        row->res->colstate[col] =
808            (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
809        return row->res->colptrs[col];
810    }
811    else
812        return (void *)-1;
813}
814
815/* Parse the parameter string for open */
816static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
817                               int *connect, SQLCHAR **datasource,
818                               SQLCHAR **user, SQLCHAR **password,
819                               int *defaultBufferSize, int *nattrs,
820                               int **attrs, intptr_t **attrvals)
821{
822    char *seps, *last, *next, *name[MAX_PARAMS], *val[MAX_PARAMS];
823    int nparams = 0, i, j;
824
825    *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
826    *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(intptr_t));
827    *nattrs = 0;
828    seps = DEFAULTSEPS;
829    name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
830
831    /* no params is OK here - let connect return a more useful error msg */
832    if (!name[nparams])
833        return SQL_SUCCESS;
834
835    do {
836        if (last[strspn(last, seps)] == CSINGLEQUOTE) {
837            last += strspn(last, seps);
838            seps=SSINGLEQUOTE;
839        }
840        val[nparams] = apr_strtok(NULL, seps, &last);
841        seps = DEFAULTSEPS;
842
843        ++nparams;
844        next = apr_strtok(NULL, seps, &last);
845        if (!next) {
846            break;
847        }
848        if (nparams >= MAX_PARAMS) {
849            /* too many parameters, no place to store */
850            return APR_EGENERAL;
851        }
852        name[nparams] = next;
853    } while (1);
854
855    for (j = i = 0; i < nparams; i++) {
856        if (!apr_strnatcasecmp(name[i], "CONNECT")) {
857            *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
858            *connect = 1;
859        }
860        else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) {
861            *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
862            *connect = 0;
863        }
864        else if (!apr_strnatcasecmp(name[i], "USER")) {
865            *user = (SQLCHAR *)apr_pstrdup(pool, val[i]);
866        }
867        else if (!apr_strnatcasecmp(name[i], "PASSWORD")) {
868            *password = (SQLCHAR *)apr_pstrdup(pool, val[i]);
869        }
870        else if (!apr_strnatcasecmp(name[i], "BUFSIZE")) {
871            *defaultBufferSize = atoi(val[i]);
872        }
873        else if (!apr_strnatcasecmp(name[i], "ACCESS")) {
874            if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
875                (*attrvals)[j] = SQL_MODE_READ_ONLY;
876            else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
877                (*attrvals)[j] = SQL_MODE_READ_WRITE;
878            else
879                return SQL_ERROR;
880            (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
881        }
882        else if (!apr_strnatcasecmp(name[i], "CTIMEOUT")) {
883            (*attrvals)[j] = atoi(val[i]);
884            (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
885        }
886        else if (!apr_strnatcasecmp(name[i], "STIMEOUT")) {
887            (*attrvals)[j] = atoi(val[i]);
888            (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
889        }
890        else if (!apr_strnatcasecmp(name[i], "TXMODE")) {
891            if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
892                (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
893            else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
894                (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
895            else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
896                (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
897            else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
898                (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
899            else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
900                continue;
901            else
902                return SQL_ERROR;
903            (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
904        }
905        else
906            return SQL_ERROR;
907    }
908    *nattrs = j;
909    return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
910}
911
912/* common handling after ODBC calls - save error info (code and text) in dbc */
913static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
914                 SQLSMALLINT type, SQLHANDLE h, int line)
915{
916    SQLCHAR buffer[512];
917    SQLCHAR sqlstate[128];
918    SQLINTEGER native;
919    SQLSMALLINT reslength;
920    char *res, *p, *end, *logval = NULL;
921    int i;
922
923    /* set info about last error in dbc  - fast return for SQL_SUCCESS  */
924    if (rc == SQL_SUCCESS) {
925        char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
926        apr_size_t successMsgLen = sizeof successMsg - 1;
927
928        dbc->lasterrorcode = SQL_SUCCESS;
929        apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError);
930        apr_cpystrn(dbc->lastError + successMsgLen, step,
931                    sizeof dbc->lastError - successMsgLen);
932        return;
933    }
934    switch (rc) {
935    case SQL_INVALID_HANDLE:
936        res = "SQL_INVALID_HANDLE";
937        break;
938    case SQL_ERROR:
939        res = "SQL_ERROR";
940        break;
941    case SQL_SUCCESS_WITH_INFO:
942        res = "SQL_SUCCESS_WITH_INFO";
943        break;
944    case SQL_STILL_EXECUTING:
945        res = "SQL_STILL_EXECUTING";
946        break;
947    case SQL_NEED_DATA:
948        res = "SQL_NEED_DATA";
949        break;
950    case SQL_NO_DATA:
951        res = "SQL_NO_DATA";
952        break;
953    default:
954        res = "unrecognized SQL return code";
955    }
956    /* these two returns are expected during normal execution */
957    if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA
958        && dbc->can_commit != APR_DBD_TRANSACTION_IGNORE_ERRORS) {
959        dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK;
960    }
961    p = dbc->lastError;
962    end = p + sizeof(dbc->lastError);
963    dbc->lasterrorcode = rc;
964    p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
965                 step, res, rc, SOURCE_FILE, line - 1);
966    for (i = 1, rc = 0; rc == 0; i++) {
967        rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
968                            sizeof(buffer), &reslength);
969        if (SQL_SUCCEEDED(rc) && (p < (end - 280)))
970            p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
971    }
972    apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
973    /* if env var was set or call was init/open (no dbname) - log to stderr */
974    if (logval || !dbc->dbname ) {
975        char timestamp[APR_CTIME_LEN];
976
977        apr_file_t *se;
978        apr_ctime(timestamp, apr_time_now());
979        apr_file_open_stderr(&se, dbc->pool);
980        apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
981    }
982}
983
984static APR_INLINE int odbc_check_rollback(apr_dbd_t *handle)
985{
986    if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) {
987        handle->lasterrorcode = SQL_ERROR;
988        apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ",
989                    sizeof handle->lastError);
990        return 1;
991    }
992    return 0;
993}
994
995/*
996 *   public functions per DBD driver API
997 */
998
999/** init: allow driver to perform once-only initialisation. **/
1000static void odbc_init(apr_pool_t *pool)
1001{
1002    SQLRETURN rc;
1003    char *step;
1004    apr_version_t apuver;
1005
1006    apu_version(&apuver);
1007    if (apuver.major != DRIVER_APU_VERSION_MAJOR
1008        || apuver.minor != DRIVER_APU_VERSION_MINOR) {
1009            apr_file_t *se;
1010
1011            apr_file_open_stderr(&se, pool);
1012            apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
1013                "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
1014                DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
1015                apuver.major, apuver.minor);
1016        abort();
1017    }
1018
1019    if (henv)
1020        return;
1021
1022    step = "SQLAllocHandle (SQL_HANDLE_ENV)";
1023    rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
1024    apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
1025    if (SQL_SUCCEEDED(rc)) {
1026        step = "SQLSetEnvAttr";
1027        rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
1028                          (SQLPOINTER)SQL_OV_ODBC3, 0);
1029    }
1030    else {
1031        apr_dbd_t tmp_dbc;
1032        SQLHANDLE err_h = henv;
1033
1034        tmp_dbc.pool = pool;
1035        tmp_dbc.dbname = NULL;
1036        CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
1037    }
1038}
1039
1040/** native_handle: return the native database handle of the underlying db **/
1041static void *odbc_native_handle(apr_dbd_t *handle)
1042{
1043    return handle->dbc;
1044}
1045
1046/** open: obtain a database connection from the server rec. **/
1047
1048/* It would be more efficient to allocate a single statement handle
1049 * here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
1050 * SQLPrepare, and we don't know whether random-access is
1051 * specified until SQLExecute so we cannot.
1052 */
1053
1054static apr_dbd_t *odbc_open(apr_pool_t *pool, const char *params, const char **error)
1055{
1056    SQLRETURN rc;
1057    SQLHANDLE hdbc = NULL;
1058    apr_dbd_t *handle;
1059    char *err_step;
1060    int err_htype, i;
1061    int defaultBufferSize = DEFAULT_BUFFER_SIZE;
1062    SQLHANDLE err_h = NULL;
1063    SQLCHAR  *datasource = (SQLCHAR *)"", *user = (SQLCHAR *)"",
1064             *password = (SQLCHAR *)"";
1065    int nattrs = 0, *attrs = NULL,  connect = 0;
1066    intptr_t *attrvals = NULL;
1067
1068    err_step = "SQLAllocHandle (SQL_HANDLE_DBC)";
1069    err_htype = SQL_HANDLE_ENV;
1070    err_h = henv;
1071    rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
1072    if (SQL_SUCCEEDED(rc)) {
1073        err_step = "Invalid DBD Parameters - open";
1074        err_htype = SQL_HANDLE_DBC;
1075        err_h = hdbc;
1076        rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
1077                               &password, &defaultBufferSize, &nattrs, &attrs,
1078                               &attrvals);
1079    }
1080    if (SQL_SUCCEEDED(rc)) {
1081        for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1082            err_step = "SQLSetConnectAttr (from DBD Parameters)";
1083            err_htype = SQL_HANDLE_DBC;
1084            err_h = hdbc;
1085            rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0);
1086        }
1087    }
1088    if (SQL_SUCCEEDED(rc)) {
1089        if (connect) {
1090            SQLCHAR out[1024];
1091            SQLSMALLINT outlen;
1092
1093            err_step = "SQLDriverConnect";
1094            err_htype = SQL_HANDLE_DBC;
1095            err_h = hdbc;
1096            rc = SQLDriverConnect(hdbc, NULL, datasource,
1097                        (SQLSMALLINT)strlen((char *)datasource),
1098                        out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1099        }
1100        else {
1101            err_step = "SQLConnect";
1102            err_htype = SQL_HANDLE_DBC;
1103            err_h = hdbc;
1104            rc = SQLConnect(hdbc, datasource,
1105                        (SQLSMALLINT)strlen((char *)datasource),
1106                        user, (SQLSMALLINT)strlen((char *)user),
1107                        password, (SQLSMALLINT)strlen((char *)password));
1108        }
1109    }
1110    if (SQL_SUCCEEDED(rc)) {
1111        handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1112        handle->dbname = apr_pstrdup(pool, (char *)datasource);
1113        handle->dbc = hdbc;
1114        handle->pool = pool;
1115        handle->defaultBufferSize = defaultBufferSize;
1116        CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1117        handle->default_transaction_mode = 0;
1118        handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1119        SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1120                   &(handle->default_transaction_mode), sizeof(intptr_t), NULL);
1121        handle->transaction_mode = handle->default_transaction_mode;
1122        SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1123                   sizeof(intptr_t), NULL);
1124        apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1125        return handle;
1126    }
1127    else {
1128        apr_dbd_t tmp_dbc;
1129
1130        tmp_dbc.pool = pool;
1131        tmp_dbc.dbname = NULL;
1132        CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1133        if (error)
1134            *error = apr_pstrdup(pool, tmp_dbc.lastError);
1135        if (hdbc)
1136            SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1137        return NULL;
1138    }
1139}
1140
1141/** check_conn: check status of a database connection **/
1142static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1143{
1144    SQLUINTEGER isDead;
1145    SQLRETURN   rc;
1146
1147    rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1148                            sizeof(SQLUINTEGER), NULL);
1149    CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1150                SQL_HANDLE_DBC, handle->dbc);
1151    /* if driver cannot check connection, say so */
1152    if (rc != SQL_SUCCESS)
1153        return APR_ENOTIMPL;
1154
1155    return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1156}
1157
1158/** set_dbname: select database name.  May be a no-op if not supported. **/
1159static int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle,
1160                           const char *name)
1161{
1162    if (apr_strnatcmp(name, handle->dbname)) {
1163        return APR_EGENERAL;        /* It's illegal to change dbname in ODBC */
1164    }
1165    CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1166                handle->dbc);
1167    return APR_SUCCESS;             /* OK if it's the same name */
1168}
1169
1170/** transaction: start a transaction.  May be a no-op. **/
1171static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1172                                  apr_dbd_transaction_t **trans)
1173{
1174    SQLRETURN rc = SQL_SUCCESS;
1175
1176    if (handle->transaction_mode) {
1177        rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION,
1178                               (SQLPOINTER)handle->transaction_mode, 0);
1179        CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1180                    SQL_HANDLE_DBC, handle->dbc);
1181    }
1182    if (SQL_SUCCEEDED(rc)) {
1183        /* turn off autocommit for transactions */
1184        rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1185                               SQL_AUTOCOMMIT_OFF, 0);
1186        CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1187                    SQL_HANDLE_DBC, handle->dbc);
1188    }
1189    if (SQL_SUCCEEDED(rc)) {
1190        *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1191        (*trans)->dbc = handle->dbc;
1192        (*trans)->apr_dbd = handle;
1193    }
1194    handle->can_commit = APR_DBD_TRANSACTION_COMMIT;
1195    return APR_FROM_SQL_RESULT(rc);
1196}
1197
1198/** end_transaction: end a transaction **/
1199static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1200{
1201    SQLRETURN rc;
1202    int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK)
1203        ? SQL_COMMIT : SQL_ROLLBACK;
1204
1205    rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
1206    CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1207    if (SQL_SUCCEEDED(rc)) {
1208        rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1209                               (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1210        CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1211                    rc, SQL_HANDLE_DBC, trans->dbc);
1212    }
1213    trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1214    return APR_FROM_SQL_RESULT(rc);
1215}
1216
1217/** query: execute an SQL statement which doesn't return a result set **/
1218static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1219{
1220    SQLRETURN rc;
1221    SQLHANDLE hstmt = NULL;
1222    size_t len = strlen(statement);
1223
1224    if (odbc_check_rollback(handle))
1225        return APR_EGENERAL;
1226
1227    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1228    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1229                handle->dbc);
1230    if (!SQL_SUCCEEDED(rc))
1231        return APR_FROM_SQL_RESULT(rc);
1232
1233    rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1234    CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1235
1236    if (SQL_SUCCEEDED(rc)) {
1237        SQLLEN rowcount;
1238
1239        rc = SQLRowCount(hstmt, &rowcount);
1240        *nrows = (int)rowcount;
1241        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1242    }
1243
1244    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1245    return APR_FROM_SQL_RESULT(rc);
1246}
1247
1248/** select: execute an SQL statement which returns a result set **/
1249static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1250                       apr_dbd_results_t **res, const char *statement,
1251                       int random)
1252{
1253    SQLRETURN rc;
1254    SQLHANDLE hstmt;
1255    apr_dbd_prepared_t *stmt;
1256    size_t len = strlen(statement);
1257
1258    if (odbc_check_rollback(handle))
1259        return APR_EGENERAL;
1260
1261    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1262    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1263                handle->dbc);
1264    if (!SQL_SUCCEEDED(rc))
1265        return APR_FROM_SQL_RESULT(rc);
1266    /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1267     * is not a prepared statement.  We want the same cleanup mechanism.
1268     */
1269    stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1270    stmt->apr_dbd = handle;
1271    stmt->dbc = handle->dbc;
1272    stmt->stmt = hstmt;
1273    apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1274    if (random) {
1275        rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1276                            (SQLPOINTER)SQL_SCROLLABLE, 0);
1277        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1278                    SQL_HANDLE_STMT, hstmt);
1279    }
1280    if (SQL_SUCCEEDED(rc)) {
1281        rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1282        CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1283    }
1284    if (SQL_SUCCEEDED(rc)) {
1285        rc = odbc_create_results(handle, hstmt, pool, random, res);
1286        apr_pool_cleanup_register(pool, *res,
1287                                  odbc_close_results, apr_pool_cleanup_null);
1288    }
1289    return APR_FROM_SQL_RESULT(rc);
1290}
1291
1292/** num_cols: get the number of columns in a results set **/
1293static int odbc_num_cols(apr_dbd_results_t *res)
1294{
1295    return res->ncols;
1296}
1297
1298/** num_tuples: get the number of rows in a results set **/
1299static int odbc_num_tuples(apr_dbd_results_t *res)
1300{
1301    SQLRETURN rc;
1302    SQLLEN nrows;
1303
1304    rc = SQLRowCount(res->stmt, &nrows);
1305    CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1306    return SQL_SUCCEEDED(rc) ? (int)nrows : -1;
1307}
1308
1309/** get_row: get a row from a result set **/
1310static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
1311                        apr_dbd_row_t **row, int rownum)
1312{
1313    SQLRETURN rc;
1314    char *fetchtype;
1315    int c;
1316
1317    *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1318    (*row)->stmt = res->stmt;
1319    (*row)->dbc = res->dbc;
1320    (*row)->res = res;
1321    (*row)->pool = res->pool;
1322
1323    /* mark all the columns as needing SQLGetData unless they are bound  */
1324    for (c = 0; c < res->ncols; c++) {
1325        if (res->colstate[c] != COL_BOUND) {
1326            res->colstate[c] = COL_AVAIL;
1327        }
1328        /* some drivers do not null-term zero-len CHAR data */
1329        if (res->colptrs[c])
1330            *(char *)res->colptrs[c] = 0;
1331    }
1332
1333    if (res->random && (rownum > 0)) {
1334        fetchtype = "SQLFetchScroll";
1335        rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1336    }
1337    else {
1338        fetchtype = "SQLFetch";
1339        rc = SQLFetch(res->stmt);
1340    }
1341    CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1342    (*row)->stmt = res->stmt;
1343    if (!SQL_SUCCEEDED(rc) && !res->random) {
1344        /* early close on any error (usually SQL_NO_DATA) if fetching
1345         * sequentially to release resources ASAP
1346         */
1347        odbc_close_results(res);
1348        return -1;
1349    }
1350    return SQL_SUCCEEDED(rc) ? 0 : -1;
1351}
1352
1353/** datum_get: get a binary entry from a row **/
1354static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col,
1355                                   apr_dbd_type_e dbdtype, void *data)
1356{
1357    SQLSMALLINT sqltype;
1358    void *p;
1359    int len;
1360
1361    if (col >= row->res->ncols)
1362        return APR_EGENERAL;
1363
1364    if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
1365        data = NULL;            /* invalid type */
1366        return APR_EGENERAL;
1367    }
1368
1369    len = sqlSizes[dbdtype];
1370    sqltype = sqlCtype[dbdtype];
1371
1372    /* must not memcpy a brigade, sentinals are relative to orig loc */
1373    if (IS_LOB(sqltype))
1374        return odbc_create_bucket(row, col, sqltype, data);
1375
1376    p = odbc_get(row, col, sqltype);
1377    if (p == (void *)-1)
1378        return APR_EGENERAL;
1379
1380    if (p == NULL)
1381        return APR_ENOENT;          /* SQL NULL value */
1382
1383    if (len < 0)
1384       *(char**)data = (char *)p;
1385    else
1386        memcpy(data, p, len);
1387
1388    return APR_SUCCESS;
1389
1390}
1391
1392/** get_entry: get an entry from a row (string data) **/
1393static const char *odbc_get_entry(const apr_dbd_row_t *row, int col)
1394{
1395    void *p;
1396
1397    if (col >= row->res->ncols)
1398        return NULL;
1399
1400    p = odbc_get(row, col, SQL_C_CHAR);
1401
1402    /* NULL or invalid (-1) */
1403    if (p == NULL || p == (void *)-1)
1404        return p;
1405    else
1406        return apr_pstrdup(row->pool, p);
1407}
1408
1409/** error: get current error message (if any) **/
1410static const char *odbc_error(apr_dbd_t *handle, int errnum)
1411{
1412    return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1413}
1414
1415/** escape: escape a string so it is safe for use in query/select **/
1416static const char *odbc_escape(apr_pool_t *pool, const char *s,
1417                               apr_dbd_t *handle)
1418{
1419    char *newstr, *src, *dst, *sq;
1420    int qcount;
1421
1422    /* return the original if there are no single-quotes */
1423    if (!(sq = strchr(s, '\'')))
1424        return (char *)s;
1425    /* count the single-quotes and allocate a new buffer */
1426    for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
1427        qcount++;
1428    newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1429
1430    /* move chars, doubling all single-quotes */
1431    src = (char *)s;
1432    for (dst = newstr; *src; src++) {
1433        if ((*dst++ = *src) == '\'')
1434            *dst++ = '\'';
1435    }
1436    *dst = 0;
1437    return newstr;
1438}
1439
1440/** prepare: prepare a statement **/
1441static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle,
1442                        const char *query, const char *label, int nargs,
1443                        int nvals, apr_dbd_type_e *types,
1444                        apr_dbd_prepared_t **statement)
1445{
1446    SQLRETURN rc;
1447    size_t len = strlen(query);
1448
1449    if (odbc_check_rollback(handle))
1450        return APR_EGENERAL;
1451
1452    *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1453    (*statement)->dbc = handle->dbc;
1454    (*statement)->apr_dbd = handle;
1455    (*statement)->nargs = nargs;
1456    (*statement)->nvals = nvals;
1457    (*statement)->types =
1458        apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1459    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1460    apr_pool_cleanup_register(pool, *statement,
1461                              odbc_close_pstmt, apr_pool_cleanup_null);
1462    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1463                SQL_HANDLE_DBC, handle->dbc);
1464    rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len);
1465    CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1466                (*statement)->stmt);
1467    return APR_FROM_SQL_RESULT(rc);
1468}
1469
1470/** pquery: query using a prepared statement + args **/
1471static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1472                       apr_dbd_prepared_t *statement, const char **args)
1473{
1474    SQLRETURN rc = SQL_SUCCESS;
1475    int i, argp;
1476
1477    if (odbc_check_rollback(handle))
1478        return APR_EGENERAL;
1479
1480    for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1481        rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1482                             &argp, (const void **)args, TEXTMODE);
1483    }
1484    if (SQL_SUCCEEDED(rc)) {
1485        rc = SQLExecute(statement->stmt);
1486        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1487                    statement->stmt);
1488    }
1489    if (SQL_SUCCEEDED(rc)) {
1490        SQLLEN rowcount;
1491
1492        rc = SQLRowCount(statement->stmt, &rowcount);
1493        *nrows = (int)rowcount;
1494        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1495                    statement->stmt);
1496    }
1497    return APR_FROM_SQL_RESULT(rc);
1498}
1499
1500/** pvquery: query using a prepared statement + args **/
1501static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1502                        apr_dbd_prepared_t *statement, va_list args)
1503{
1504    const char **values;
1505    int i;
1506
1507    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1508    for (i = 0; i < statement->nvals; i++)
1509        values[i] = va_arg(args, const char *);
1510    return odbc_pquery(pool, handle, nrows, statement, values);
1511}
1512
1513/** pselect: select using a prepared statement + args **/
1514static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle,
1515                        apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
1516                        int random, const char **args)
1517{
1518    SQLRETURN rc = SQL_SUCCESS;
1519    int i, argp;
1520
1521    if (odbc_check_rollback(handle))
1522        return APR_EGENERAL;
1523
1524    if (random) {
1525        rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1526                            (SQLPOINTER)SQL_SCROLLABLE, 0);
1527        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1528                    rc, SQL_HANDLE_STMT, statement->stmt);
1529    }
1530    if (SQL_SUCCEEDED(rc)) {
1531        for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1532            rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1533                                 &argp, (const void **)args, TEXTMODE);
1534        }
1535    }
1536    if (SQL_SUCCEEDED(rc)) {
1537        rc = SQLExecute(statement->stmt);
1538        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1539                    statement->stmt);
1540    }
1541    if (SQL_SUCCEEDED(rc)) {
1542        rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1543        apr_pool_cleanup_register(pool, *res,
1544                                  odbc_close_results, apr_pool_cleanup_null);
1545    }
1546    return APR_FROM_SQL_RESULT(rc);
1547}
1548
1549/** pvselect: select using a prepared statement + args **/
1550static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle,
1551                         apr_dbd_results_t **res,
1552                         apr_dbd_prepared_t *statement, int random,
1553                         va_list args)
1554{
1555    const char **values;
1556    int i;
1557
1558    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1559    for (i = 0; i < statement->nvals; i++)
1560        values[i] = va_arg(args, const char *);
1561    return odbc_pselect(pool, handle, res, statement, random, values);
1562}
1563
1564/** get_name: get a column title from a result set **/
1565static const char *odbc_get_name(const apr_dbd_results_t *res, int col)
1566{
1567    SQLRETURN rc;
1568    char buffer[MAX_COLUMN_NAME];
1569    SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1570    SQLULEN colsize;
1571
1572    if (col >= res->ncols)
1573        return NULL;            /* bogus column number */
1574    if (res->colnames[col] != NULL)
1575        return res->colnames[col];      /* we already retrieved it */
1576    rc = SQLDescribeCol(res->stmt, col + 1,
1577                        (SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
1578                        &coltype, &colsize, &coldecimal, &colnullable);
1579    CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1580                SQL_HANDLE_STMT, res->stmt);
1581    res->colnames[col] = apr_pstrdup(res->pool, buffer);
1582    return res->colnames[col];
1583}
1584
1585/** transaction_mode_get: get the mode of transaction **/
1586static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans)
1587{
1588    return (int)trans->apr_dbd->can_commit;
1589}
1590
1591/** transaction_mode_set: set the mode of transaction **/
1592static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
1593{
1594    int legal = (  APR_DBD_TRANSACTION_IGNORE_ERRORS
1595                 | APR_DBD_TRANSACTION_COMMIT
1596                 | APR_DBD_TRANSACTION_ROLLBACK);
1597
1598    if ((mode & legal) != mode)
1599        return APR_EGENERAL;
1600
1601    trans->apr_dbd->can_commit = mode;
1602    return APR_SUCCESS;
1603}
1604
1605/** pbquery: query using a prepared statement + binary args **/
1606static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1607                        apr_dbd_prepared_t *statement, const void **args)
1608{
1609    SQLRETURN rc = SQL_SUCCESS;
1610    int i, argp;
1611
1612    if (odbc_check_rollback(handle))
1613        return APR_EGENERAL;
1614
1615    for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1616        rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1617                             &argp, args, BINARYMODE);
1618
1619    if (SQL_SUCCEEDED(rc)) {
1620        rc = SQLExecute(statement->stmt);
1621        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1622                    statement->stmt);
1623    }
1624    if (SQL_SUCCEEDED(rc)) {
1625        SQLLEN rowcount;
1626
1627        rc = SQLRowCount(statement->stmt, &rowcount);
1628        *nrows = (int)rowcount;
1629        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1630                    statement->stmt);
1631    }
1632    return APR_FROM_SQL_RESULT(rc);
1633}
1634
1635/** pbselect: select using a prepared statement + binary args **/
1636static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle,
1637                         apr_dbd_results_t **res,
1638                         apr_dbd_prepared_t *statement,
1639                         int random, const void **args)
1640{
1641    SQLRETURN rc = SQL_SUCCESS;
1642    int i, argp;
1643
1644    if (odbc_check_rollback(handle))
1645        return APR_EGENERAL;
1646
1647    if (random) {
1648        rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1649                            (SQLPOINTER)SQL_SCROLLABLE, 0);
1650        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1651                    rc, SQL_HANDLE_STMT, statement->stmt);
1652    }
1653    if (SQL_SUCCEEDED(rc)) {
1654        for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1655            rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1656                                 &argp, args, BINARYMODE);
1657        }
1658    }
1659    if (SQL_SUCCEEDED(rc)) {
1660        rc = SQLExecute(statement->stmt);
1661        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1662                    statement->stmt);
1663    }
1664    if (SQL_SUCCEEDED(rc)) {
1665        rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1666        apr_pool_cleanup_register(pool, *res,
1667                                  odbc_close_results, apr_pool_cleanup_null);
1668    }
1669
1670    return APR_FROM_SQL_RESULT(rc);
1671}
1672
1673/** pvbquery: query using a prepared statement + binary args **/
1674static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1675                         apr_dbd_prepared_t *statement, va_list args)
1676{
1677    const char **values;
1678    int i;
1679
1680    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1681    for (i = 0; i < statement->nvals; i++)
1682        values[i] = va_arg(args, const char *);
1683    return odbc_pbquery(pool, handle, nrows, statement, (const void **)values);
1684}
1685
1686/** pvbselect: select using a prepared statement + binary args **/
1687static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle,
1688                          apr_dbd_results_t **res,
1689                          apr_dbd_prepared_t *statement,
1690                          int random, va_list args)
1691{
1692    const char **values;
1693    int i;
1694
1695    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1696    for (i = 0; i < statement->nvals; i++)
1697        values[i] = va_arg(args, const char *);
1698    return odbc_pbselect(pool, handle, res, statement, random, (const void **)values);
1699}
1700
1701APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1702    ODBC_DRIVER_STRING,
1703    odbc_init,
1704    odbc_native_handle,
1705    odbc_open,
1706    odbc_check_conn,
1707    odbc_close,
1708    odbc_set_dbname,
1709    odbc_start_transaction,
1710    odbc_end_transaction,
1711    odbc_query,
1712    odbc_select,
1713    odbc_num_cols,
1714    odbc_num_tuples,
1715    odbc_get_row,
1716    odbc_get_entry,
1717    odbc_error,
1718    odbc_escape,
1719    odbc_prepare,
1720    odbc_pvquery,
1721    odbc_pvselect,
1722    odbc_pquery,
1723    odbc_pselect,
1724    odbc_get_name,
1725    odbc_transaction_mode_get,
1726    odbc_transaction_mode_set,
1727    "?",
1728    odbc_pvbquery,
1729    odbc_pvbselect,
1730    odbc_pbquery,
1731    odbc_pbselect,
1732    odbc_datum_get
1733};
1734
1735#endif
1736