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