]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/apr-util/dbd/apr_dbd_odbc.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / apr-util / dbd / apr_dbd_odbc.c
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
67 static 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  */
72 static 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 */
107 struct 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
123 struct 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
145 enum                            /* 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
158 struct 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
165 struct apr_dbd_transaction_t {
166     SQLHANDLE dbc;              /* parent ODBC connection handle */
167     apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
168 };
169
170 struct 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
179 static void odbc_lob_bucket_destroy(void *data);
180 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
181 static 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 */
185 static 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 */
195 typedef 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  */
209 static 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 */
237 static 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) */
264 static 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 */
295 static 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 */
309 static 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() */
327 static 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 */
342 static 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 */
348 static 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 */
358 static 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 */
471 static 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 */
508 static 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 */
612 static 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 */
621 static 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 */
633 static 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 */
716 static 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  */
744 static 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 */
816 static 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 */
913 static 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
984 static 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. **/
1000 static 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 **/
1041 static 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
1054 static 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 **/
1141 static 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. **/
1158 static 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. **/
1170 static 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 **/
1198 static 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 **/
1217 static 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 **/
1248 static 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 **/
1292 static 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 **/
1298 static 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 **/
1309 static 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 **/
1353 static 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) **/
1392 static 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) **/
1409 static 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 **/
1415 static 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 **/
1440 static 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 **/
1470 static 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 **/
1500 static 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 **/
1513 static 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 **/
1549 static 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 **/
1564 static 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 **/
1585 static 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 **/
1591 static 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 **/
1605 static 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 **/
1635 static 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 **/
1673 static 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 **/
1686 static 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
1700 APU_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