]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/apr-util/dbd/apr_dbd_odbc.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.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 /*
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
82 static 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  */
87 static 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 */
122 struct 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
138 struct 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
160 enum                            /* 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
173 struct 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
180 struct apr_dbd_transaction_t {
181     SQLHANDLE dbc;              /* parent ODBC connection handle */
182     apr_dbd_t *apr_dbd;         /* parent DBD connection handle */
183 };
184
185 struct 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
194 static void odbc_lob_bucket_destroy(void *data);
195 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
196 static 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 */
200 static 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 */
210 typedef 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  */
224 static 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 */
252 static 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) */
279 static 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 */
310 static 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 */
324 static 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() */
342 static 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 */
357 static 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 */
363 static 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 */
373 static 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 */
486 static 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 */
523 static 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 */
627 static 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 */
636 static 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 */
648 static 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 */
731 static 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  */
759 static 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 */
831 static 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 */
928 static 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
999 static 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. **/
1015 static 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 **/
1056 static 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
1069 static 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 **/
1157 static 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. **/
1174 static 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. **/
1186 static 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 **/
1214 static 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 **/
1233 static 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 **/
1264 static 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 **/
1308 static 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 **/
1314 static 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 **/
1325 static 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 **/
1369 static 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) **/
1408 static 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) **/
1425 static 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 **/
1431 static 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 **/
1456 static 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 **/
1486 static 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 **/
1516 static 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 **/
1529 static 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 **/
1565 static 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 **/
1580 static 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 **/
1601 static 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 **/
1607 static 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 **/
1621 static 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 **/
1651 static 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 **/
1689 static 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 **/
1702 static 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
1716 APU_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