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    int transaction_mode;
118    int dboptions;              /* driver options re SQLGetData */
119    int 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    int 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] = 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] = maxsize;
445        rc = SQL_SUCCESS;
446    }
447    else {
448        res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
449        res->colsizes[icol] = 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    int 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, int **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(int));
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, *attrvals = NULL, connect = 0;
1066
1067    err_step = "SQLAllocHandle (SQL_HANDLE_DBC)";
1068    err_htype = SQL_HANDLE_ENV;
1069    err_h = henv;
1070    rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
1071    if (SQL_SUCCEEDED(rc)) {
1072        err_step = "Invalid DBD Parameters - open";
1073        err_htype = SQL_HANDLE_DBC;
1074        err_h = hdbc;
1075        rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
1076                               &password, &defaultBufferSize, &nattrs, &attrs,
1077                               &attrvals);
1078    }
1079    if (SQL_SUCCEEDED(rc)) {
1080        for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1081            err_step = "SQLSetConnectAttr (from DBD Parameters)";
1082            err_htype = SQL_HANDLE_DBC;
1083            err_h = hdbc;
1084            rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0);
1085        }
1086    }
1087    if (SQL_SUCCEEDED(rc)) {
1088        if (connect) {
1089            SQLCHAR out[1024];
1090            SQLSMALLINT outlen;
1091
1092            err_step = "SQLDriverConnect";
1093            err_htype = SQL_HANDLE_DBC;
1094            err_h = hdbc;
1095            rc = SQLDriverConnect(hdbc, NULL, datasource,
1096                        (SQLSMALLINT)strlen((char *)datasource),
1097                        out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1098        }
1099        else {
1100            err_step = "SQLConnect";
1101            err_htype = SQL_HANDLE_DBC;
1102            err_h = hdbc;
1103            rc = SQLConnect(hdbc, datasource,
1104                        (SQLSMALLINT)strlen((char *)datasource),
1105                        user, (SQLSMALLINT)strlen((char *)user),
1106                        password, (SQLSMALLINT)strlen((char *)password));
1107        }
1108    }
1109    if (SQL_SUCCEEDED(rc)) {
1110        handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1111        handle->dbname = apr_pstrdup(pool, (char *)datasource);
1112        handle->dbc = hdbc;
1113        handle->pool = pool;
1114        handle->defaultBufferSize = defaultBufferSize;
1115        CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1116        handle->default_transaction_mode = 0;
1117        handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1118        SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1119                   &(handle->default_transaction_mode), sizeof(int), NULL);
1120        handle->transaction_mode = handle->default_transaction_mode;
1121        SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1122                   sizeof(int), NULL);
1123        apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1124        return handle;
1125    }
1126    else {
1127        apr_dbd_t tmp_dbc;
1128
1129        tmp_dbc.pool = pool;
1130        tmp_dbc.dbname = NULL;
1131        CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1132        if (error)
1133            *error = apr_pstrdup(pool, tmp_dbc.lastError);
1134        if (hdbc)
1135            SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1136        return NULL;
1137    }
1138}
1139
1140/** check_conn: check status of a database connection **/
1141static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1142{
1143    SQLUINTEGER isDead;
1144    SQLRETURN   rc;
1145
1146    rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1147                            sizeof(SQLUINTEGER), NULL);
1148    CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1149                SQL_HANDLE_DBC, handle->dbc);
1150    /* if driver cannot check connection, say so */
1151    if (rc != SQL_SUCCESS)
1152        return APR_ENOTIMPL;
1153
1154    return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1155}
1156
1157/** set_dbname: select database name.  May be a no-op if not supported. **/
1158static int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle,
1159                           const char *name)
1160{
1161    if (apr_strnatcmp(name, handle->dbname)) {
1162        return APR_EGENERAL;        /* It's illegal to change dbname in ODBC */
1163    }
1164    CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1165                handle->dbc);
1166    return APR_SUCCESS;             /* OK if it's the same name */
1167}
1168
1169/** transaction: start a transaction.  May be a no-op. **/
1170static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1171                                  apr_dbd_transaction_t **trans)
1172{
1173    SQLRETURN rc = SQL_SUCCESS;
1174
1175    if (handle->transaction_mode) {
1176        rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION,
1177                               (SQLPOINTER)handle->transaction_mode, 0);
1178        CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1179                    SQL_HANDLE_DBC, handle->dbc);
1180    }
1181    if (SQL_SUCCEEDED(rc)) {
1182        /* turn off autocommit for transactions */
1183        rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1184                               SQL_AUTOCOMMIT_OFF, 0);
1185        CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1186                    SQL_HANDLE_DBC, handle->dbc);
1187    }
1188    if (SQL_SUCCEEDED(rc)) {
1189        *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1190        (*trans)->dbc = handle->dbc;
1191        (*trans)->apr_dbd = handle;
1192    }
1193    handle->can_commit = APR_DBD_TRANSACTION_COMMIT;
1194    return APR_FROM_SQL_RESULT(rc);
1195}
1196
1197/** end_transaction: end a transaction **/
1198static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1199{
1200    SQLRETURN rc;
1201    int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK)
1202        ? SQL_COMMIT : SQL_ROLLBACK;
1203
1204    rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
1205    CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1206    if (SQL_SUCCEEDED(rc)) {
1207        rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1208                               (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1209        CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1210                    rc, SQL_HANDLE_DBC, trans->dbc);
1211    }
1212    trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1213    return APR_FROM_SQL_RESULT(rc);
1214}
1215
1216/** query: execute an SQL statement which doesn't return a result set **/
1217static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1218{
1219    SQLRETURN rc;
1220    SQLHANDLE hstmt = NULL;
1221    size_t len = strlen(statement);
1222
1223    if (odbc_check_rollback(handle))
1224        return APR_EGENERAL;
1225
1226    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1227    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1228                handle->dbc);
1229    if (!SQL_SUCCEEDED(rc))
1230        return APR_FROM_SQL_RESULT(rc);
1231
1232    rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1233    CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1234
1235    if (SQL_SUCCEEDED(rc)) {
1236        SQLLEN rowcount;
1237
1238        rc = SQLRowCount(hstmt, &rowcount);
1239        *nrows = (int)rowcount;
1240        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1241    }
1242
1243    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1244    return APR_FROM_SQL_RESULT(rc);
1245}
1246
1247/** select: execute an SQL statement which returns a result set **/
1248static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1249                       apr_dbd_results_t **res, const char *statement,
1250                       int random)
1251{
1252    SQLRETURN rc;
1253    SQLHANDLE hstmt;
1254    apr_dbd_prepared_t *stmt;
1255    size_t len = strlen(statement);
1256
1257    if (odbc_check_rollback(handle))
1258        return APR_EGENERAL;
1259
1260    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1261    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1262                handle->dbc);
1263    if (!SQL_SUCCEEDED(rc))
1264        return APR_FROM_SQL_RESULT(rc);
1265    /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1266     * is not a prepared statement.  We want the same cleanup mechanism.
1267     */
1268    stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1269    stmt->apr_dbd = handle;
1270    stmt->dbc = handle->dbc;
1271    stmt->stmt = hstmt;
1272    apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1273    if (random) {
1274        rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1275                            (SQLPOINTER)SQL_SCROLLABLE, 0);
1276        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1277                    SQL_HANDLE_STMT, hstmt);
1278    }
1279    if (SQL_SUCCEEDED(rc)) {
1280        rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1281        CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1282    }
1283    if (SQL_SUCCEEDED(rc)) {
1284        rc = odbc_create_results(handle, hstmt, pool, random, res);
1285        apr_pool_cleanup_register(pool, *res,
1286                                  odbc_close_results, apr_pool_cleanup_null);
1287    }
1288    return APR_FROM_SQL_RESULT(rc);
1289}
1290
1291/** num_cols: get the number of columns in a results set **/
1292static int odbc_num_cols(apr_dbd_results_t *res)
1293{
1294    return res->ncols;
1295}
1296
1297/** num_tuples: get the number of rows in a results set **/
1298static int odbc_num_tuples(apr_dbd_results_t *res)
1299{
1300    SQLRETURN rc;
1301    SQLLEN nrows;
1302
1303    rc = SQLRowCount(res->stmt, &nrows);
1304    CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1305    return SQL_SUCCEEDED(rc) ? (int)nrows : -1;
1306}
1307
1308/** get_row: get a row from a result set **/
1309static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
1310                        apr_dbd_row_t **row, int rownum)
1311{
1312    SQLRETURN rc;
1313    char *fetchtype;
1314    int c;
1315
1316    *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1317    (*row)->stmt = res->stmt;
1318    (*row)->dbc = res->dbc;
1319    (*row)->res = res;
1320    (*row)->pool = res->pool;
1321
1322    /* mark all the columns as needing SQLGetData unless they are bound  */
1323    for (c = 0; c < res->ncols; c++) {
1324        if (res->colstate[c] != COL_BOUND) {
1325            res->colstate[c] = COL_AVAIL;
1326        }
1327        /* some drivers do not null-term zero-len CHAR data */
1328        if (res->colptrs[c])
1329            *(char *)res->colptrs[c] = 0;
1330    }
1331
1332    if (res->random && (rownum > 0)) {
1333        fetchtype = "SQLFetchScroll";
1334        rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1335    }
1336    else {
1337        fetchtype = "SQLFetch";
1338        rc = SQLFetch(res->stmt);
1339    }
1340    CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1341    (*row)->stmt = res->stmt;
1342    if (!SQL_SUCCEEDED(rc) && !res->random) {
1343        /* early close on any error (usually SQL_NO_DATA) if fetching
1344         * sequentially to release resources ASAP
1345         */
1346        odbc_close_results(res);
1347        return -1;
1348    }
1349    return SQL_SUCCEEDED(rc) ? 0 : -1;
1350}
1351
1352/** datum_get: get a binary entry from a row **/
1353static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col,
1354                                   apr_dbd_type_e dbdtype, void *data)
1355{
1356    SQLSMALLINT sqltype;
1357    void *p;
1358    int len;
1359
1360    if (col >= row->res->ncols)
1361        return APR_EGENERAL;
1362
1363    if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
1364        data = NULL;            /* invalid type */
1365        return APR_EGENERAL;
1366    }
1367
1368    len = sqlSizes[dbdtype];
1369    sqltype = sqlCtype[dbdtype];
1370
1371    /* must not memcpy a brigade, sentinals are relative to orig loc */
1372    if (IS_LOB(sqltype))
1373        return odbc_create_bucket(row, col, sqltype, data);
1374
1375    p = odbc_get(row, col, sqltype);
1376    if (p == (void *)-1)
1377        return APR_EGENERAL;
1378
1379    if (p == NULL)
1380        return APR_ENOENT;          /* SQL NULL value */
1381
1382    if (len < 0)
1383       *(char**)data = (char *)p;
1384    else
1385        memcpy(data, p, len);
1386
1387    return APR_SUCCESS;
1388
1389}
1390
1391/** get_entry: get an entry from a row (string data) **/
1392static const char *odbc_get_entry(const apr_dbd_row_t *row, int col)
1393{
1394    void *p;
1395
1396    if (col >= row->res->ncols)
1397        return NULL;
1398
1399    p = odbc_get(row, col, SQL_C_CHAR);
1400
1401    /* NULL or invalid (-1) */
1402    if (p == NULL || p == (void *)-1)
1403        return p;
1404    else
1405        return apr_pstrdup(row->pool, p);
1406}
1407
1408/** error: get current error message (if any) **/
1409static const char *odbc_error(apr_dbd_t *handle, int errnum)
1410{
1411    return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1412}
1413
1414/** escape: escape a string so it is safe for use in query/select **/
1415static const char *odbc_escape(apr_pool_t *pool, const char *s,
1416                               apr_dbd_t *handle)
1417{
1418    char *newstr, *src, *dst, *sq;
1419    int qcount;
1420
1421    /* return the original if there are no single-quotes */
1422    if (!(sq = strchr(s, '\'')))
1423        return (char *)s;
1424    /* count the single-quotes and allocate a new buffer */
1425    for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
1426        qcount++;
1427    newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1428
1429    /* move chars, doubling all single-quotes */
1430    src = (char *)s;
1431    for (dst = newstr; *src; src++) {
1432        if ((*dst++ = *src) == '\'')
1433            *dst++ = '\'';
1434    }
1435    *dst = 0;
1436    return newstr;
1437}
1438
1439/** prepare: prepare a statement **/
1440static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle,
1441                        const char *query, const char *label, int nargs,
1442                        int nvals, apr_dbd_type_e *types,
1443                        apr_dbd_prepared_t **statement)
1444{
1445    SQLRETURN rc;
1446    size_t len = strlen(query);
1447
1448    if (odbc_check_rollback(handle))
1449        return APR_EGENERAL;
1450
1451    *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1452    (*statement)->dbc = handle->dbc;
1453    (*statement)->apr_dbd = handle;
1454    (*statement)->nargs = nargs;
1455    (*statement)->nvals = nvals;
1456    (*statement)->types =
1457        apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1458    rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1459    apr_pool_cleanup_register(pool, *statement,
1460                              odbc_close_pstmt, apr_pool_cleanup_null);
1461    CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1462                SQL_HANDLE_DBC, handle->dbc);
1463    rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len);
1464    CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1465                (*statement)->stmt);
1466    return APR_FROM_SQL_RESULT(rc);
1467}
1468
1469/** pquery: query using a prepared statement + args **/
1470static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1471                       apr_dbd_prepared_t *statement, const char **args)
1472{
1473    SQLRETURN rc = SQL_SUCCESS;
1474    int i, argp;
1475
1476    if (odbc_check_rollback(handle))
1477        return APR_EGENERAL;
1478
1479    for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1480        rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1481                             &argp, (const void **)args, TEXTMODE);
1482    }
1483    if (SQL_SUCCEEDED(rc)) {
1484        rc = SQLExecute(statement->stmt);
1485        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1486                    statement->stmt);
1487    }
1488    if (SQL_SUCCEEDED(rc)) {
1489        SQLLEN rowcount;
1490
1491        rc = SQLRowCount(statement->stmt, &rowcount);
1492        *nrows = (int)rowcount;
1493        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1494                    statement->stmt);
1495    }
1496    return APR_FROM_SQL_RESULT(rc);
1497}
1498
1499/** pvquery: query using a prepared statement + args **/
1500static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1501                        apr_dbd_prepared_t *statement, va_list args)
1502{
1503    const char **values;
1504    int i;
1505
1506    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1507    for (i = 0; i < statement->nvals; i++)
1508        values[i] = va_arg(args, const char *);
1509    return odbc_pquery(pool, handle, nrows, statement, values);
1510}
1511
1512/** pselect: select using a prepared statement + args **/
1513static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle,
1514                        apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
1515                        int random, const char **args)
1516{
1517    SQLRETURN rc = SQL_SUCCESS;
1518    int i, argp;
1519
1520    if (odbc_check_rollback(handle))
1521        return APR_EGENERAL;
1522
1523    if (random) {
1524        rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1525                            (SQLPOINTER)SQL_SCROLLABLE, 0);
1526        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1527                    rc, SQL_HANDLE_STMT, statement->stmt);
1528    }
1529    if (SQL_SUCCEEDED(rc)) {
1530        for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1531            rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1532                                 &argp, (const void **)args, TEXTMODE);
1533        }
1534    }
1535    if (SQL_SUCCEEDED(rc)) {
1536        rc = SQLExecute(statement->stmt);
1537        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1538                    statement->stmt);
1539    }
1540    if (SQL_SUCCEEDED(rc)) {
1541        rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1542        apr_pool_cleanup_register(pool, *res,
1543                                  odbc_close_results, apr_pool_cleanup_null);
1544    }
1545    return APR_FROM_SQL_RESULT(rc);
1546}
1547
1548/** pvselect: select using a prepared statement + args **/
1549static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle,
1550                         apr_dbd_results_t **res,
1551                         apr_dbd_prepared_t *statement, int random,
1552                         va_list args)
1553{
1554    const char **values;
1555    int i;
1556
1557    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1558    for (i = 0; i < statement->nvals; i++)
1559        values[i] = va_arg(args, const char *);
1560    return odbc_pselect(pool, handle, res, statement, random, values);
1561}
1562
1563/** get_name: get a column title from a result set **/
1564static const char *odbc_get_name(const apr_dbd_results_t *res, int col)
1565{
1566    SQLRETURN rc;
1567    char buffer[MAX_COLUMN_NAME];
1568    SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1569    SQLULEN colsize;
1570
1571    if (col >= res->ncols)
1572        return NULL;            /* bogus column number */
1573    if (res->colnames[col] != NULL)
1574        return res->colnames[col];      /* we already retrieved it */
1575    rc = SQLDescribeCol(res->stmt, col + 1,
1576                        (SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
1577                        &coltype, &colsize, &coldecimal, &colnullable);
1578    CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1579                SQL_HANDLE_STMT, res->stmt);
1580    res->colnames[col] = apr_pstrdup(res->pool, buffer);
1581    return res->colnames[col];
1582}
1583
1584/** transaction_mode_get: get the mode of transaction **/
1585static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans)
1586{
1587    return (int)trans->apr_dbd->can_commit;
1588}
1589
1590/** transaction_mode_set: set the mode of transaction **/
1591static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
1592{
1593    int legal = (  APR_DBD_TRANSACTION_IGNORE_ERRORS
1594                 | APR_DBD_TRANSACTION_COMMIT
1595                 | APR_DBD_TRANSACTION_ROLLBACK);
1596
1597    if ((mode & legal) != mode)
1598        return APR_EGENERAL;
1599
1600    trans->apr_dbd->can_commit = mode;
1601    return APR_SUCCESS;
1602}
1603
1604/** pbquery: query using a prepared statement + binary args **/
1605static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1606                        apr_dbd_prepared_t *statement, const void **args)
1607{
1608    SQLRETURN rc = SQL_SUCCESS;
1609    int i, argp;
1610
1611    if (odbc_check_rollback(handle))
1612        return APR_EGENERAL;
1613
1614    for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1615        rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1616                             &argp, args, BINARYMODE);
1617
1618    if (SQL_SUCCEEDED(rc)) {
1619        rc = SQLExecute(statement->stmt);
1620        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1621                    statement->stmt);
1622    }
1623    if (SQL_SUCCEEDED(rc)) {
1624        SQLLEN rowcount;
1625
1626        rc = SQLRowCount(statement->stmt, &rowcount);
1627        *nrows = (int)rowcount;
1628        CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1629                    statement->stmt);
1630    }
1631    return APR_FROM_SQL_RESULT(rc);
1632}
1633
1634/** pbselect: select using a prepared statement + binary args **/
1635static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle,
1636                         apr_dbd_results_t **res,
1637                         apr_dbd_prepared_t *statement,
1638                         int random, const void **args)
1639{
1640    SQLRETURN rc = SQL_SUCCESS;
1641    int i, argp;
1642
1643    if (odbc_check_rollback(handle))
1644        return APR_EGENERAL;
1645
1646    if (random) {
1647        rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1648                            (SQLPOINTER)SQL_SCROLLABLE, 0);
1649        CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1650                    rc, SQL_HANDLE_STMT, statement->stmt);
1651    }
1652    if (SQL_SUCCEEDED(rc)) {
1653        for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1654            rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1655                                 &argp, args, BINARYMODE);
1656        }
1657    }
1658    if (SQL_SUCCEEDED(rc)) {
1659        rc = SQLExecute(statement->stmt);
1660        CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1661                    statement->stmt);
1662    }
1663    if (SQL_SUCCEEDED(rc)) {
1664        rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1665        apr_pool_cleanup_register(pool, *res,
1666                                  odbc_close_results, apr_pool_cleanup_null);
1667    }
1668
1669    return APR_FROM_SQL_RESULT(rc);
1670}
1671
1672/** pvbquery: query using a prepared statement + binary args **/
1673static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1674                         apr_dbd_prepared_t *statement, va_list args)
1675{
1676    const char **values;
1677    int i;
1678
1679    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1680    for (i = 0; i < statement->nvals; i++)
1681        values[i] = va_arg(args, const char *);
1682    return odbc_pbquery(pool, handle, nrows, statement, (const void **)values);
1683}
1684
1685/** pvbselect: select using a prepared statement + binary args **/
1686static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle,
1687                          apr_dbd_results_t **res,
1688                          apr_dbd_prepared_t *statement,
1689                          int random, va_list args)
1690{
1691    const char **values;
1692    int i;
1693
1694    values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1695    for (i = 0; i < statement->nvals; i++)
1696        values[i] = va_arg(args, const char *);
1697    return odbc_pbselect(pool, handle, res, statement, random, (const void **)values);
1698}
1699
1700APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1701    ODBC_DRIVER_STRING,
1702    odbc_init,
1703    odbc_native_handle,
1704    odbc_open,
1705    odbc_check_conn,
1706    odbc_close,
1707    odbc_set_dbname,
1708    odbc_start_transaction,
1709    odbc_end_transaction,
1710    odbc_query,
1711    odbc_select,
1712    odbc_num_cols,
1713    odbc_num_tuples,
1714    odbc_get_row,
1715    odbc_get_entry,
1716    odbc_error,
1717    odbc_escape,
1718    odbc_prepare,
1719    odbc_pvquery,
1720    odbc_pvselect,
1721    odbc_pquery,
1722    odbc_pselect,
1723    odbc_get_name,
1724    odbc_transaction_mode_get,
1725    odbc_transaction_mode_set,
1726    "?",
1727    odbc_pvbquery,
1728    odbc_pvbselect,
1729    odbc_pbquery,
1730    odbc_pbselect,
1731    odbc_datum_get
1732};
1733
1734#endif
1735