Make ODBC DBD driver compile on Unix.
[apr-util.git] / dbd / apr_dbd_odbc.c
blobd77caa4b85aafbc5cf22523d0b01a0452f892f33
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "apu.h"
18 #if APU_HAVE_ODBC
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"
30 /* If library is ODBC-V2, use macros for limited ODBC-V2 support
31 * No random access in V2.
33 #ifdef ODBCV2
34 #define ODBCVER 0x0200
35 #include "apr_dbd_odbc_v2.h"
36 #endif
38 /* standard ODBC include files */
39 #ifdef HAVE_SQL_H
40 #include <sql.h>
41 #include <sqlext.h>
42 #elif defined(HAVE_ODBC_SQL_H)
43 #include <odbc/sql.h>
44 #include <odbc/sqlext.h>
45 #endif
47 /* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver'
48 * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which
49 * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql.
51 #ifndef ODBC_DRIVER_NAME
52 #define ODBC_DRIVER_NAME odbc
53 #endif
54 #define STRINGIFY(x) #x
55 #define NAMIFY2(n) apr_dbd_##n##_driver
56 #define NAMIFY1(n) NAMIFY2(n)
57 #define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
58 #define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
60 /* Required APR version for this driver */
61 #define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
62 #define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
65 static SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */
67 /* Use a CHECK_ERROR macro so we can grab the source line numbers
68 * for error reports */
69 static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
70 SQLSMALLINT type, SQLHANDLE h, int line);
71 #define CHECK_ERROR(a,s,r,t,h) check_error(a,s,r,t,h, __LINE__)
73 #define SOURCE_FILE __FILE__ /* source file for error messages */
74 #define MAX_ERROR_STRING 1024 /* max length of message in dbc */
75 #define MAX_COLUMN_NAME 256 /* longest column name recognized */
76 #define DEFAULT_BUFFER_SIZE 1024 /* value for defaultBufferSize */
78 #define MAX_PARAMS 20
79 #define DEFAULTSEPS " \t\r\n,="
80 #define CSINGLEQUOTE '\''
81 #define SSINGLEQUOTE "\'"
83 #define TEXTMODE 1 /* used for text (APR 1.2) mode params */
84 #define BINARYMODE 0 /* used for binary (APR 1.3+) mode params */
86 /* Identify datatypes which are LOBs
87 * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB */
88 #define IS_LOB(t) (t == SQL_LONGVARCHAR \
89 || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
90 || t == -98 || t == -99)
91 /* These types are CLOBs
92 * - DB2 DRDA driver uses undefined type -98 for CLOB */
93 #define IS_CLOB(t) \
94 (t == SQL_LONGVARCHAR || t == -98)
96 /* Convert a SQL result to an APR result */
97 #define APR_FROM_SQL_RESULT(rc) \
98 (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
100 /* DBD opaque structures */
101 struct apr_dbd_t
103 SQLHANDLE dbc; /* SQL connection handle - NULL after close */
104 apr_pool_t *pool; /* connection lifetime pool */
105 char *dbname; /* ODBC datasource */
106 int lasterrorcode;
107 int lineNumber;
108 char lastError[MAX_ERROR_STRING];
109 int defaultBufferSize; /* used for CLOBs in text mode,
110 * and when fld size is indeterminate */
111 int transaction_mode;
112 int dboptions; /* driver options re SQLGetData */
113 int default_transaction_mode;
114 int can_commit; /* controls end_trans behavior */
117 struct apr_dbd_results_t
119 SQLHANDLE stmt; /* parent sql statement handle */
120 SQLHANDLE dbc; /* parent sql connection handle */
121 apr_pool_t *pool; /* pool from query or select */
122 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
123 int random; /* random access requested */
124 int ncols; /* number of columns */
125 int isclosed; /* cursor has been closed */
126 char **colnames; /* array of column names (NULL until used) */
127 SQLPOINTER *colptrs; /* pointers to column data */
128 SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */
129 SQLINTEGER *coltextsizes; /* max-sizes if converted to text */
130 SQLSMALLINT *coltypes; /* array of SQL data types for columns */
131 SQLLEN *colinds; /* array of SQL data indicator/strlens */
132 int *colstate; /* array of column states
133 * - avail, bound, present, unavail
135 int *all_data_fetched; /* flags data as all fetched, for LOBs */
136 void *data; /* buffer for all data for one row */
138 enum /* results column states */
140 COL_AVAIL, /* data may be retrieved with SQLGetData */
141 COL_PRESENT, /* data has been retrieved with SQLGetData */
142 COL_BOUND, /* column is bound to colptr */
143 COL_RETRIEVED, /* all data from column has been returned */
144 COL_UNAVAIL /* column is unavailable because ODBC driver
145 * requires that columns be retrieved
146 * in ascending order and a higher col
147 * was accessed */
150 struct apr_dbd_row_t {
151 SQLHANDLE stmt; /* parent ODBC statement handle */
152 SQLHANDLE dbc; /* parent ODBC connection handle */
153 apr_pool_t *pool; /* pool from get_row */
154 apr_dbd_results_t *res;
157 struct apr_dbd_transaction_t {
158 SQLHANDLE dbc; /* parent ODBC connection handle */
159 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
162 struct apr_dbd_prepared_t {
163 SQLHANDLE stmt; /* ODBC statement handle */
164 SQLHANDLE dbc; /* parent ODBC connection handle */
165 apr_dbd_t *apr_dbd;
166 int nargs;
167 int nvals;
168 int *types; /* array of DBD data types */
172 static void odbc_lob_bucket_destroy(void *data);
173 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
174 static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
175 apr_size_t *len, apr_read_type_e block);
177 /* the ODBC LOB bucket type */
178 static const apr_bucket_type_t odbc_bucket_type = {
179 "ODBC_LOB", 5, APR_BUCKET_DATA,
180 odbc_lob_bucket_destroy,
181 odbc_lob_bucket_read,
182 odbc_lob_bucket_setaside,
183 apr_bucket_shared_split,
184 apr_bucket_shared_copy
188 /* ODBC LOB bucket data */
189 typedef struct {
190 /** Ref count for shared bucket */
191 apr_bucket_refcount refcount;
192 const apr_dbd_row_t *row;
193 int col;
194 SQLSMALLINT type;
195 } odbc_bucket;
198 /* SQL datatype mappings to DBD datatypes
199 * These tables must correspond *exactly* to the apr_dbd_type_e enum
200 * in apr_dbd_internal.h
203 /* ODBC "C" types to DBD datatypes */
204 static SQLSMALLINT const sqlCtype[] = {
205 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
206 SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
207 SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
208 SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */
209 SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */
210 SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */
211 SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */
212 SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */
213 SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */
214 SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
215 SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
216 SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
217 SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
218 SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */
219 SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
220 SQL_C_CHAR, /*SQL_C_TYPE_TIME, APR_DBD_TYPE_TIME, \%pDi */
221 SQL_C_CHAR, /*SQL_C_TYPE_DATE, APR_DBD_TYPE_DATE, \%pDd */
222 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
223 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
224 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
225 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
226 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
227 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
230 /* ODBC Base types to DBD datatypes */
231 static SQLSMALLINT const sqlBaseType[] = {
232 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
233 SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
234 SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
235 SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */
236 SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */
237 SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */
238 SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */
239 SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */
240 SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */
241 SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
242 SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
243 SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
244 SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
245 SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */
246 SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
247 SQL_CHAR, /*SQL_TIME, APR_DBD_TYPE_TIME, \%pDi */
248 SQL_CHAR, /*SQL_DATE, APR_DBD_TYPE_DATE, \%pDd */
249 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
250 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
251 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
252 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
253 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
254 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
257 /* result sizes for DBD datatypes (-1 for null-terminated) */
258 static int const sqlSizes[] = {
260 sizeof(char), /**< \%hhd out: char* */
261 sizeof(unsigned char), /**< \%hhu out: unsigned char* */
262 sizeof(short), /**< \%hd out: short* */
263 sizeof(unsigned short), /**< \%hu out: unsigned short* */
264 sizeof(int), /**< \%d out: int* */
265 sizeof(unsigned int), /**< \%u out: unsigned int* */
266 sizeof(long), /**< \%ld out: long* */
267 sizeof(unsigned long), /**< \%lu out: unsigned long* */
268 sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */
269 sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */
270 sizeof(float), /**< \%f out: float* */
271 sizeof(double), /**< \%lf out: double* */
272 -1, /**< \%s out: char** */
273 -1, /**< \%pDt out: char** */
274 -1, /**< \%pDi out: char** */
275 -1, /**< \%pDd out: char** */
276 -1, /**< \%pDa out: char** */
277 -1, /**< \%pDs out: char** */
278 -1, /**< \%pDz out: char** */
279 sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
280 sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
281 0 /**< \%pDn : in: void*, out: void** */
285 * local functions
288 /* close any open results for the connection */
289 static apr_status_t odbc_close_results(void *d)
290 { apr_dbd_results_t *dbr = (apr_dbd_results_t *) d;
291 SQLRETURN rc = SQL_SUCCESS;
293 if (dbr && !dbr->isclosed) {
294 rc = SQLCloseCursor(dbr->stmt);
296 dbr->isclosed = 1;
297 return APR_FROM_SQL_RESULT(rc);
300 /* close the ODBC statement handle from a prepare */
301 static apr_status_t odbc_close_pstmt(void *s)
303 SQLRETURN rc = APR_SUCCESS;
304 apr_dbd_prepared_t *statement = s;
305 SQLHANDLE hstmt = statement->stmt;
306 /* stmt is closed if connection has already been closed */
307 if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
308 rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
310 statement->stmt = NULL;
311 return APR_FROM_SQL_RESULT(rc);
314 /* close: close/release a connection obtained from open() */
315 static apr_status_t odbc_close(apr_dbd_t *handle)
317 SQLRETURN rc = SQL_SUCCESS;
319 if (handle->dbc) {
320 rc = SQLDisconnect(handle->dbc);
321 CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
322 rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
323 CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
324 handle->dbc = NULL;
326 return APR_FROM_SQL_RESULT(rc);
329 /* odbc_close re-defined for passing to pool cleanup */
330 static apr_status_t odbc_close_cleanup(void *handle)
332 return odbc_close( (apr_dbd_t *) handle);
335 /* close the ODBC environment handle at process termination */
336 static apr_status_t odbc_close_env(SQLHANDLE henv)
338 SQLRETURN rc;
340 rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
341 henv = NULL;
342 return APR_FROM_SQL_RESULT(rc);
345 /* setup the arrays in results for all the returned columns */
346 static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t * res,
347 SQLHANDLE stmt)
349 SQLRETURN rc;
350 int maxsize, textsize, realsize, type, isunsigned = 1;
352 /* discover the sql type */
353 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
354 (SQLPOINTER) &isunsigned);
355 isunsigned = (isunsigned == SQL_TRUE);
357 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
358 (SQLPOINTER) &type);
359 if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE)
360 /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
361 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
362 0, NULL, (SQLPOINTER) &type);
363 if (!SQL_SUCCEEDED(rc))
364 /* if still unknown make it CHAR */
365 type = SQL_C_CHAR;
367 switch (type) {
368 case SQL_INTEGER:
369 case SQL_SMALLINT:
370 case SQL_TINYINT:
371 case SQL_BIGINT:
372 /* fix these numeric binary types up as signed/unsigned for C types */
373 type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
374 break;
375 /* LOB types are not changed to C types */
376 case SQL_LONGVARCHAR:
377 type = SQL_LONGVARCHAR;
378 break;
379 case SQL_LONGVARBINARY:
380 type = SQL_LONGVARBINARY;
381 break;
382 case SQL_FLOAT :
383 type = SQL_C_FLOAT;
384 break;
385 case SQL_DOUBLE :
386 type = SQL_C_DOUBLE;
387 break;
389 /* DBD wants times as strings */
390 case SQL_TIMESTAMP:
391 case SQL_DATE:
392 case SQL_TIME:
393 default:
394 type = SQL_C_CHAR;
397 res->coltypes[icol] = type;
399 /* size if retrieved as text */
400 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
401 NULL, (SQLPOINTER) & textsize);
402 if (!SQL_SUCCEEDED(rc) || textsize < 0)
403 textsize = res->apr_dbd->defaultBufferSize;
404 /* for null-term, which sometimes isn't included */
405 textsize++;
407 /* real size */
408 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
409 NULL, (SQLPOINTER) & realsize);
410 if (!SQL_SUCCEEDED(rc))
411 realsize = textsize;
413 maxsize = (textsize > realsize) ? textsize : realsize;
414 if ( IS_LOB(type) || maxsize <= 0) {
415 /* LOB types are never bound and have a NULL colptr for binary.
416 * Ingore their real (1-2gb) length & use a default - the larger
417 * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
418 * If not a LOB, but simply unknown length - always use defaultBufferSize.
420 maxsize = res->apr_dbd->defaultBufferSize;
421 if ( IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE )
422 maxsize = APR_BUCKET_BUFF_SIZE;
424 res->colptrs[icol] = NULL;
425 res->colstate[icol] = COL_AVAIL;
426 res->colsizes[icol] = maxsize;
427 rc = SQL_SUCCESS;
429 else {
430 res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
431 res->colsizes[icol] = maxsize;
432 if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
433 /* we are allowed to call SQLGetData if we need to */
434 rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
435 res->colptrs[icol], maxsize,
436 &(res->colinds[icol]) );
437 CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
438 stmt);
439 res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
441 else {
442 /* this driver won't allow us to call SQLGetData on bound
443 * columns - so don't bind any */
444 res->colstate[icol] = COL_AVAIL;
445 rc = SQL_SUCCESS;
448 return rc;
451 /* create and populate an apr_dbd_results_t for a select */
452 static SQLRETURN odbc_create_results(apr_dbd_t * handle, SQLHANDLE hstmt,
453 apr_pool_t * pool, const int random,
454 apr_dbd_results_t ** res)
456 SQLRETURN rc;
457 SQLSMALLINT ncols;
459 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
460 (*res)->stmt = hstmt;
461 (*res)->dbc = handle->dbc;
462 (*res)->pool = pool;
463 (*res)->random = random;
464 (*res)->apr_dbd = handle;
465 rc = SQLNumResultCols(hstmt, &ncols);
466 CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
467 (*res)->ncols = ncols;
469 if SQL_SUCCEEDED(rc) {
470 int i;
472 (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
473 (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
474 (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
475 (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
476 (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
477 (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
478 (*res)->ncols = ncols;
480 for (i = 0 ; i < ncols ; i++)
481 odbc_set_result_column(i, (*res), hstmt);
483 return rc;
487 /* bind a parameter - input params only, does not support output parameters */
488 static SQLRETURN odbc_bind_param(apr_pool_t * pool,
489 apr_dbd_prepared_t * statement, const int narg,
490 const SQLSMALLINT type, int *argp,
491 const void **args, const int textmode)
493 SQLRETURN rc;
494 SQLSMALLINT baseType, cType;
495 void *ptr;
496 SQLUINTEGER len;
497 SQLINTEGER *indicator;
498 static SQLINTEGER nullValue = SQL_NULL_DATA;
499 static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */
501 /* bind a NULL data value */
502 if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
503 baseType = SQL_CHAR;
504 cType = SQL_C_CHAR;
505 ptr = &nullValue;
506 len = sizeof(SQLINTEGER);
507 indicator = &nullValue;
508 (*argp)++;
510 /* bind a non-NULL data value */
511 else {
512 baseType = sqlBaseType[type];
513 cType = sqlCtype[type];
514 indicator = NULL;
515 /* LOBs */
516 if (IS_LOB(cType)) {
517 ptr = (void *) args[*argp];
518 len = (SQLUINTEGER) * (apr_size_t *) args[*argp + 1];
519 cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
520 (*argp) += 4; /* LOBs consume 4 args (last two are unused) */
522 /* non-LOBs */
523 else {
524 switch (baseType) {
525 case SQL_CHAR:
526 case SQL_DATE:
527 case SQL_TIME:
528 case SQL_TIMESTAMP:
529 ptr = (void *) args[*argp];
530 len = (SQLUINTEGER) strlen(ptr);
531 break;
532 case SQL_TINYINT:
533 ptr = apr_palloc(pool, sizeof(unsigned char));
534 len = sizeof(unsigned char);
535 *(unsigned char *) ptr =
536 (textmode ?
537 atoi(args[*argp]) : *(unsigned char *) args[*argp]);
538 break;
539 case SQL_SMALLINT:
540 ptr = apr_palloc(pool, sizeof(short));
541 len = sizeof(short);
542 *(short *) ptr =
543 (textmode ? atoi(args[*argp]) : *(short *) args[*argp]);
544 break;
545 case SQL_INTEGER:
546 ptr = apr_palloc(pool, sizeof(int));
547 len = sizeof(int);
548 *(long *) ptr =
549 (textmode ? atol(args[*argp]) : *(long *) args[*argp]);
550 break;
551 case SQL_FLOAT:
552 ptr = apr_palloc(pool, sizeof(float));
553 len = sizeof(float);
554 *(float *) ptr =
555 (textmode ?
556 (float) atof(args[*argp]) : *(float *) args[*argp]);
557 break;
558 case SQL_DOUBLE:
559 ptr = apr_palloc(pool, sizeof(double));
560 len = sizeof(double);
561 *(double *) ptr =
562 (textmode ? atof(args[*argp]) : *(double *)
563 args[*argp]);
564 break;
565 case SQL_BIGINT:
566 ptr = apr_palloc(pool, sizeof(apr_int64_t));
567 len = sizeof(apr_int64_t);
568 *(apr_int64_t *) ptr =
569 (textmode ?
570 apr_atoi64(args[*argp]) : *(apr_int64_t *) args[*argp]);
571 break;
572 default:
573 return APR_EGENERAL;
575 (*argp)++; /* non LOBs consume one argument */
578 rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
579 baseType, len, 0, ptr, len, indicator);
580 CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
581 statement->stmt);
582 return rc;
585 /* LOB / Bucket Brigade functions */
589 /* bucket type specific destroy */
590 static void odbc_lob_bucket_destroy(void *data)
592 odbc_bucket *bd = data;
594 if (apr_bucket_shared_destroy(bd))
595 apr_bucket_free(bd);
598 /* set aside a bucket if possible */
599 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
601 odbc_bucket *bd = (odbc_bucket *) e->data;
603 /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
604 if (apr_pool_is_ancestor(bd->row->pool, pool))
605 return APR_SUCCESS;
607 return apr_bucket_setaside_notimpl(e, pool);
610 /* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
611 static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
612 apr_size_t *len, apr_read_type_e block)
614 SQLRETURN rc;
615 SQLINTEGER len_indicator;
616 SQLSMALLINT type;
617 odbc_bucket *bd = (odbc_bucket *) e->data;
618 apr_bucket *nxt;
619 void *buf;
620 int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
621 int eos;
623 /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
624 type = bd->row->res->coltypes[bd->col];
625 type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
627 /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE,
628 * but they may be much bigger per the BUFSIZE parameter.
630 if (bufsize < APR_BUCKET_BUFF_SIZE)
631 bufsize = APR_BUCKET_BUFF_SIZE;
633 buf = apr_bucket_alloc(bufsize, e->list);
634 *str = NULL;
635 *len = 0;
637 rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
638 type, buf, bufsize,
639 &len_indicator);
641 CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
642 SQL_HANDLE_STMT, bd->row->res->stmt);
644 if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
645 len_indicator = 0;
647 if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
649 if (rc = SQL_SUCCESS_WITH_INFO
650 && ( len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize) ) {
651 /* not the last read = a full buffer. CLOBs have a null terminator */
652 *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
654 eos = 0;
656 else {
657 /* the last read - len_indicator is supposed to be the length,
658 * but some driver get this wrong and return the total length.
659 * We try to handle both interpretations.
661 *len = (len_indicator > bufsize
662 && len_indicator >= (SQLINTEGER) e->start)
663 ? (len_indicator - (SQLINTEGER) e->start) : len_indicator;
665 eos = 1;
668 if (!eos) {
669 /* Create a new LOB bucket to append and append it */
670 nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
671 APR_BUCKET_INIT(nxt);
672 nxt->length = -1;
673 nxt->data = e->data;
674 nxt->type = &odbc_bucket_type;
675 nxt->free = apr_bucket_free;
676 nxt->list = e->list;
677 nxt->start = e->start + *len;
678 APR_BUCKET_INSERT_AFTER(e, nxt);
680 else {
681 odbc_lob_bucket_destroy(e->data);
683 /* make current bucket into a heap bucket */
684 apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
685 *str = buf;
687 /* No data is success in this context */
688 rc = SQL_SUCCESS;
690 return APR_FROM_SQL_RESULT(rc);
693 /* Create a bucket brigade on the row pool for a LOB column */
694 static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
695 SQLSMALLINT type, apr_bucket_brigade *bb)
697 apr_bucket_alloc_t *list = bb->bucket_alloc;
698 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
699 odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
700 apr_bucket *eos = apr_bucket_eos_create(list);
703 bd->row = row;
704 bd->col = col;
705 bd->type = type;
708 APR_BUCKET_INIT(b);
709 b->type = &odbc_bucket_type;
710 b->free = apr_bucket_free;
711 b->list = list;
712 /* LOB lengths are unknown in ODBC */
713 b = apr_bucket_shared_make(b, bd, 0, -1);
715 APR_BRIGADE_INSERT_TAIL(bb, b);
716 APR_BRIGADE_INSERT_TAIL(bb, eos);
718 return APR_SUCCESS;
721 /* returns a data pointer for a column, returns NULL for NULL value,
722 * return -1 if data not available */
723 static void *odbc_get(const apr_dbd_row_t *row, const int col,
724 const SQLSMALLINT sqltype)
726 SQLRETURN rc;
727 SQLINTEGER indicator;
728 int state = row->res->colstate[col];
729 int options = row->res->apr_dbd->dboptions;
731 switch (state) {
732 case (COL_UNAVAIL) : return (void *) -1;
733 case (COL_RETRIEVED) : return NULL;
735 case (COL_BOUND) :
736 case (COL_PRESENT) :
737 if (sqltype == row->res->coltypes[col]) {
738 /* same type and we already have the data */
739 row->res->colstate[col] = COL_RETRIEVED;
740 return (row->res->colinds[col] == SQL_NULL_DATA) ?
741 NULL : row->res->colptrs[col];
745 /* we need to get the data now */
746 if (!(options & SQL_GD_ANY_ORDER)) {
747 /* this ODBC driver requires columns to be retrieved in order,
748 * so we attempt to get every prior un-gotten non-LOB column */
749 int i;
750 for (i = 0; i < col; i++) {
751 if (row->res->colstate[i] == COL_AVAIL) {
752 if (IS_LOB(row->res->coltypes[i]))
753 row->res->colstate[i] = COL_UNAVAIL;
754 else {
755 odbc_get(row, i, row->res->coltypes[i]);
756 row->res->colstate[i] = COL_PRESENT;
762 if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
763 /* this driver won't let us re-get bound columns */
764 return (void *) -1;
766 /* a LOB might not have a buffer allocated yet - so create one */
767 if (!row->res->colptrs[col])
768 row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
770 rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
771 row->res->colsizes[col], &indicator);
772 CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
773 row->res->stmt);
774 if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
775 return NULL;
777 if (SQL_SUCCEEDED(rc)) {
778 /* whatever it was originally, it is now this sqltype */
779 row->res->coltypes[col] = sqltype;
780 /* this allows getting CLOBs in text mode by calling get_entry
781 * until it returns NULL */
782 row->res->colstate[col] =
783 (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
784 return row->res->colptrs[col];
786 else return (void *) -1;
789 /* Parse the parameter string for open */
790 static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
791 int *connect, SQLCHAR **datasource,
792 SQLCHAR **user, SQLCHAR **password,
793 int *defaultBufferSize, int *nattrs,
794 int **attrs, int **attrvals)
796 char *seps, *last, *name[MAX_PARAMS], *val[MAX_PARAMS];
797 int nparams=0, i, j;
799 *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
800 *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(int));
801 *nattrs = 0;
802 seps = DEFAULTSEPS;
803 name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
804 do {
805 if (last[strspn(last, seps)] == CSINGLEQUOTE) {
806 last += strspn(last, seps);
807 seps=SSINGLEQUOTE;
809 val[nparams] = apr_strtok(NULL, seps, &last);
810 seps = DEFAULTSEPS;
811 name[++nparams] = apr_strtok(NULL, seps, &last);
812 } while ( nparams <= MAX_PARAMS && name[nparams] != NULL
813 && val[nparams] != NULL);
815 for(j=i=0 ; i< nparams ; i++)
816 { if (!apr_strnatcasecmp(name[i], "CONNECT"))
817 { *datasource = apr_pstrdup(pool, val[i]);
818 *connect=1;
820 else if (!apr_strnatcasecmp(name[i], "DATASOURCE"))
821 { *datasource = apr_pstrdup(pool, val[i]);
822 *connect=0;
824 else if (!apr_strnatcasecmp(name[i], "USER"))
825 *user = apr_pstrdup(pool, val[i]);
826 else if (!apr_strnatcasecmp(name[i], "PASSWORD"))
827 *password = apr_pstrdup(pool, val[i]);
828 else if (!apr_strnatcasecmp(name[i], "BUFSIZE"))
829 *defaultBufferSize = atoi(val[i]);
830 else if (!apr_strnatcasecmp(name[i], "ACCESS"))
831 { if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
832 (*attrvals)[j] = SQL_MODE_READ_ONLY;
833 else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
834 (*attrvals)[j] = SQL_MODE_READ_WRITE;
835 else return SQL_ERROR;
836 (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
838 else if (!apr_strnatcasecmp(name[i], "CTIMEOUT"))
839 { (*attrvals)[j] = atoi(val[i]);
840 (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
842 else if (!apr_strnatcasecmp(name[i], "STIMEOUT"))
843 { (*attrvals)[j] = atoi(val[i]);
844 (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
846 else if (!apr_strnatcasecmp(name[i], "TXMODE"))
847 { if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
848 (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
849 else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
850 (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
851 else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
852 (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
853 else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
854 (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
855 else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
856 continue;
857 else return SQL_ERROR;
858 (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
860 else return SQL_ERROR;
862 *nattrs = j;
863 return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
866 /* common handling after ODBC calls - save error info (code and text) in dbc */
867 static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
868 SQLSMALLINT type, SQLHANDLE h, int line)
870 SQLCHAR buffer[512];
871 SQLCHAR sqlstate[128];
872 SQLINTEGER native;
873 SQLSMALLINT reslength;
874 char *res, *p, *end, *logval=NULL;
875 int i;
876 apr_status_t r;
878 /* set info about last error in dbc - fast return for SQL_SUCCESS */
879 if (rc == SQL_SUCCESS) {
880 char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
881 dbc->lasterrorcode = SQL_SUCCESS;
882 strcpy(dbc->lastError, successMsg);
883 strcpy(dbc->lastError+sizeof(successMsg)-1, step);
884 return;
886 switch (rc) {
887 case SQL_INVALID_HANDLE : { res = "SQL_INVALID_HANDLE"; break; }
888 case SQL_ERROR : { res = "SQL_ERROR"; break; }
889 case SQL_SUCCESS_WITH_INFO : { res = "SQL_SUCCESS_WITH_INFO"; break; }
890 case SQL_STILL_EXECUTING : { res = "SQL_STILL_EXECUTING"; break; }
891 case SQL_NEED_DATA : { res = "SQL_NEED_DATA"; break; }
892 case SQL_NO_DATA : { res = "SQL_NO_DATA"; break; }
893 default : { res = "unrecognized SQL return code"; }
895 /* these two returns are expected during normal execution */
896 if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA)
897 dbc->can_commit = 0;
898 p = dbc->lastError;
899 end = p + sizeof(dbc->lastError);
900 dbc->lasterrorcode = rc;
901 p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
902 step, res, rc, SOURCE_FILE, line-1);
903 for (i=1, rc=0 ; rc==0 ; i++) {
904 rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
905 sizeof(buffer), &reslength);
906 if (SQL_SUCCEEDED(rc) && (p < (end-280)))
907 p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
909 r = apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
910 /* if env var was set or call was init/open (no dbname) - log to stderr */
911 if (logval || !dbc->dbname ) {
912 char timestamp[APR_CTIME_LEN];
913 apr_file_t *se;
914 apr_ctime(timestamp, apr_time_now());
915 apr_file_open_stderr(&se, dbc->pool);
916 apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
921 * public functions per DBD driver API
924 /** init: allow driver to perform once-only initialisation. **/
925 static void odbc_init(apr_pool_t *pool)
927 SQLRETURN rc;
928 char *step;
929 apr_version_t apuver;
931 apu_version(&apuver);
932 if (apuver.major != DRIVER_APU_VERSION_MAJOR
933 || apuver.minor != DRIVER_APU_VERSION_MINOR) {
934 apr_file_t *se;
936 apr_file_open_stderr(&se, pool);
937 apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
938 "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
939 DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
940 apuver.major, apuver.minor);
941 abort();
944 if (henv)
945 return;
947 step = "SQLAllocHandle (SQL_HANDLE_ENV)";
948 rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
949 apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
950 if (SQL_SUCCEEDED(rc))
951 { step = "SQLSetEnvAttr";
952 rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
953 (SQLPOINTER) SQL_OV_ODBC3, 0);
955 else
956 { apr_dbd_t tmp_dbc;
957 SQLHANDLE err_h = henv;
959 tmp_dbc.pool = pool;
960 tmp_dbc.dbname = NULL;
961 CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
965 /** native_handle: return the native database handle of the underlying db **/
966 static void* odbc_native_handle(apr_dbd_t *handle)
967 { return handle->dbc;
970 /** open: obtain a database connection from the server rec. **/
972 /* It would be more efficient to allocate a single statement handle
973 here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
974 SQLPrepare, and we don't know whether random-access is
975 specified until SQLExecute so we cannot.
978 static apr_dbd_t* odbc_open(apr_pool_t *pool, const char *params, const char **error)
980 SQLRETURN rc;
981 SQLHANDLE hdbc = NULL;
982 apr_dbd_t *handle;
983 char *err_step;
984 int err_htype, i;
985 int defaultBufferSize=DEFAULT_BUFFER_SIZE;
986 SQLHANDLE err_h = NULL;
987 SQLCHAR *datasource="", *user="", *password="";
988 int nattrs=0, *attrs=NULL, *attrvals=NULL, connect=0;
990 err_step="SQLAllocHandle (SQL_HANDLE_DBC)";
991 err_htype = SQL_HANDLE_ENV;
992 err_h = henv;
993 rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
994 if (SQL_SUCCEEDED(rc)) {
995 err_step="Invalid DBD Parameters - open";
996 err_htype = SQL_HANDLE_DBC;
997 err_h = hdbc;
998 rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
999 &password, &defaultBufferSize, &nattrs, &attrs,
1000 &attrvals);
1002 if (SQL_SUCCEEDED(rc)) {
1003 for (i=0 ; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1004 err_step="SQLSetConnectAttr (from DBD Parameters)";
1005 err_htype = SQL_HANDLE_DBC;
1006 err_h = hdbc;
1007 rc = SQLSetConnectAttr(hdbc, attrs[i], (void *) attrvals[i], 0);
1010 if (SQL_SUCCEEDED(rc)) {
1011 if (connect) {
1012 SQLCHAR out[1024];
1013 SQLSMALLINT outlen;
1014 err_step="SQLDriverConnect";
1015 err_htype = SQL_HANDLE_DBC;
1016 err_h = hdbc;
1017 rc = SQLDriverConnect(hdbc, NULL, datasource,
1018 (SQLSMALLINT) strlen(datasource),
1019 out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1021 else {
1022 err_step="SQLConnect";
1023 err_htype = SQL_HANDLE_DBC;
1024 err_h = hdbc;
1025 rc = SQLConnect(hdbc, datasource, (SQLSMALLINT) strlen(datasource),
1026 user, (SQLSMALLINT) strlen(user),
1027 password, (SQLSMALLINT) strlen(password));
1030 if (SQL_SUCCEEDED(rc)) {
1031 handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1032 handle->dbname = apr_pstrdup(pool, datasource);
1033 handle->dbc = hdbc;
1034 handle->pool = pool;
1035 handle->defaultBufferSize = defaultBufferSize;
1036 CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1037 handle->default_transaction_mode = 0;
1038 SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1039 &(handle->default_transaction_mode), sizeof(int), NULL);
1040 handle->transaction_mode = handle->default_transaction_mode;
1041 SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1042 sizeof(int), NULL);
1043 apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1044 return handle;
1046 else {
1047 apr_dbd_t tmp_dbc;
1048 tmp_dbc.pool = pool;
1049 tmp_dbc.dbname = NULL;
1050 CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1051 if (error)
1052 *error = apr_pstrdup(pool, tmp_dbc.lastError);
1053 if (hdbc)
1054 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1055 return NULL;
1059 /** check_conn: check status of a database connection **/
1060 static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1062 SQLUINTEGER isDead;
1063 SQLRETURN rc;
1065 rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1066 sizeof(SQLUINTEGER), NULL);
1067 CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1068 SQL_HANDLE_DBC, handle->dbc);
1069 /* if driver cannot check connection, say so */
1070 if (rc != SQL_SUCCESS)
1071 return APR_ENOTIMPL;
1073 return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1077 /** set_dbname: select database name. May be a no-op if not supported. **/
1078 static int odbc_set_dbname(apr_pool_t* pool, apr_dbd_t *handle,
1079 const char *name)
1081 if (apr_strnatcmp(name, handle->dbname)) {
1082 return APR_EGENERAL; /* It's illegal to change dbname in ODBC */
1084 CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1085 handle->dbc);
1086 return APR_SUCCESS; /* OK if it's the same name */
1089 /** transaction: start a transaction. May be a no-op. **/
1090 static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1091 apr_dbd_transaction_t **trans)
1093 SQLRETURN rc = SQL_SUCCESS;
1095 if (handle->transaction_mode) {
1096 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION, (void *)
1097 handle->transaction_mode, 0);
1098 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1099 SQL_HANDLE_DBC, handle->dbc);
1101 if SQL_SUCCEEDED(rc) {
1102 /* turn off autocommit for transactions */
1103 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1104 SQL_AUTOCOMMIT_OFF, 0);
1105 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1106 SQL_HANDLE_DBC, handle->dbc);
1108 if SQL_SUCCEEDED(rc) {
1109 *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1110 (*trans)->dbc = handle->dbc;
1111 (*trans)->apr_dbd = handle;
1112 handle->can_commit = 1;
1114 return APR_FROM_SQL_RESULT(rc);
1118 /** end_transaction: end a transaction **/
1119 static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1121 SQLRETURN rc;
1122 int action = trans->apr_dbd->can_commit ? SQL_COMMIT : SQL_ROLLBACK;
1124 rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, SQL_COMMIT);
1125 CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1126 if SQL_SUCCEEDED(rc) {
1127 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1128 (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0);
1129 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1130 rc, SQL_HANDLE_DBC, trans->dbc);
1132 return APR_FROM_SQL_RESULT(rc);
1135 /** query: execute an SQL statement which doesn't return a result set **/
1136 static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1138 SQLRETURN rc;
1139 SQLHANDLE hstmt = NULL;
1140 size_t len = strlen(statement);
1142 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1143 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1144 handle->dbc);
1145 if (!SQL_SUCCEEDED(rc))
1146 return APR_FROM_SQL_RESULT(rc);
1148 rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
1149 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1151 if SQL_SUCCEEDED(rc) {
1152 rc = SQLRowCount(hstmt, (SQLINTEGER *) nrows);
1153 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1156 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1157 return APR_FROM_SQL_RESULT(rc);
1160 /** select: execute an SQL statement which returns a result set **/
1161 static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1162 apr_dbd_results_t **res, const char *statement,
1163 int random)
1165 SQLRETURN rc;
1166 SQLHANDLE hstmt;
1167 apr_dbd_prepared_t *stmt;
1168 size_t len = strlen(statement);
1170 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1171 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1172 handle->dbc);
1173 if (!SQL_SUCCEEDED(rc))
1174 return APR_FROM_SQL_RESULT(rc);
1175 /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1176 * is not a prepared statement. We want the same cleanup mechanism.
1178 stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1179 stmt->apr_dbd = handle;
1180 stmt->dbc = handle->dbc;
1181 stmt->stmt = hstmt;
1182 apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1183 if (random) {
1184 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1185 (SQLPOINTER) SQL_SCROLLABLE, 0);
1186 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1187 SQL_HANDLE_STMT, hstmt);
1189 if SQL_SUCCEEDED(rc) {
1190 rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
1191 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1193 if SQL_SUCCEEDED(rc) {
1194 rc = odbc_create_results(handle, hstmt, pool, random, res);
1195 apr_pool_cleanup_register(pool, *res,
1196 odbc_close_results, apr_pool_cleanup_null);
1198 return APR_FROM_SQL_RESULT(rc);
1201 /** num_cols: get the number of columns in a results set **/
1202 static int odbc_num_cols(apr_dbd_results_t *res)
1204 return res->ncols;
1207 /** num_tuples: get the number of rows in a results set **/
1208 static int odbc_num_tuples(apr_dbd_results_t *res)
1210 SQLRETURN rc;
1211 SQLINTEGER nrows;
1213 rc = SQLRowCount(res->stmt, &nrows);
1214 CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1215 return SQL_SUCCEEDED(rc) ? (int) nrows : -1;
1218 /** get_row: get a row from a result set **/
1219 static int odbc_get_row(apr_pool_t * pool, apr_dbd_results_t * res,
1220 apr_dbd_row_t ** row, int rownum)
1222 SQLRETURN rc;
1223 char *fetchtype;
1224 int c;
1226 *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1227 (*row)->stmt = res->stmt;
1228 (*row)->dbc = res->dbc;
1229 (*row)->res = res;
1230 (*row)->pool = res->pool;
1232 /* mark all the columns as needing SQLGetData unless they are bound */
1233 for (c = 0; c < res->ncols; c++) {
1234 if (res->colstate[c] != COL_BOUND)
1235 res->colstate[c] = COL_AVAIL;
1236 /* some drivers do not null-term zero-len CHAR data */
1237 if (res->colptrs[c] )
1238 * (char *) res->colptrs[c] = 0;
1241 if (res->random && (rownum > 0)) {
1242 fetchtype = "SQLFetchScroll";
1243 rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1245 else {
1246 fetchtype = "SQLFetch";
1247 rc = SQLFetch(res->stmt);
1249 CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1250 (*row)->stmt = res->stmt;
1251 if (!SQL_SUCCEEDED(rc) && !res->random) {
1252 /* early close on any error (usually SQL_NO_DATA) if fetching
1253 * sequentially to release resources ASAP */
1254 odbc_close_results(res);
1255 return -1;
1257 return SQL_SUCCEEDED(rc) ? 0 : -1;
1260 /** datum_get: get a binary entry from a row **/
1261 static apr_status_t odbc_datum_get(const apr_dbd_row_t * row, int col,
1262 apr_dbd_type_e dbdtype, void *data)
1264 SQLSMALLINT sqltype;
1265 void *p;
1266 int len = sqlSizes[dbdtype];
1268 if (col >= row->res->ncols)
1269 return APR_EGENERAL;
1271 if (dbdtype < 0 || dbdtype >= sizeof(sqlCtype)) {
1272 data = NULL; /* invalid type */
1273 return APR_EGENERAL;
1275 sqltype = sqlCtype[dbdtype];
1277 /* must not memcpy a brigade, sentinals are relative to orig loc */
1278 if (IS_LOB(sqltype))
1279 return odbc_create_bucket(row, col, sqltype, data);
1281 p = odbc_get(row, col, sqltype);
1282 if (p == (void *) -1)
1283 return APR_EGENERAL;
1285 if (p == NULL)
1286 return APR_ENOENT; /* SQL NULL value */
1288 if (len < 0)
1289 strcpy(data, p);
1290 else
1291 memcpy(data, p, len);
1293 return APR_SUCCESS;
1297 /** get_entry: get an entry from a row (string data) **/
1298 static const char *odbc_get_entry(const apr_dbd_row_t * row, int col)
1300 void *p;
1302 if (col >= row->res->ncols)
1303 return NULL;
1305 p = odbc_get(row, col, SQL_C_CHAR);
1307 if ((signed int) p > 0)
1308 return apr_pstrdup(row->pool, p); /* row pool lifetime */
1309 else
1310 return p; /* NULL or invalid (-1) */
1313 /** error: get current error message (if any) **/
1314 static const char* odbc_error(apr_dbd_t *handle, int errnum)
1316 return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1319 /** escape: escape a string so it is safe for use in query/select **/
1320 static const char* odbc_escape(apr_pool_t *pool, const char *s,
1321 apr_dbd_t *handle)
1323 char *newstr, *src, *dst, *sq;
1324 int qcount;
1326 /* return the original if there are no single-quotes */
1327 if (!(sq = strchr(s, '\'')))
1328 return (char *) s;
1329 /* count the single-quotes and allocate a new buffer */
1330 for (qcount = 1; sq = strchr(sq + 1, '\''); )
1331 qcount++;
1332 newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1334 /* move chars, doubling all single-quotes */
1335 src = (char *) s;
1336 for (dst = newstr ; *src ; src++) {
1337 if ((*dst++ = *src) == '\'')
1338 *dst++ = '\'';
1340 *dst = 0;
1341 return newstr;
1343 /** prepare: prepare a statement **/
1344 static int odbc_prepare(apr_pool_t * pool, apr_dbd_t * handle,
1345 const char *query, const char *label, int nargs,
1346 int nvals, apr_dbd_type_e * types,
1347 apr_dbd_prepared_t ** statement)
1349 SQLRETURN rc;
1350 size_t len = strlen(query);
1352 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1353 (*statement)->dbc = handle->dbc;
1354 (*statement)->apr_dbd = handle;
1355 (*statement)->nargs = nargs;
1356 (*statement)->nvals = nvals;
1357 (*statement)->types =
1358 apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1359 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1360 apr_pool_cleanup_register(pool, *statement,
1361 odbc_close_pstmt, apr_pool_cleanup_null);
1362 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1363 SQL_HANDLE_DBC, handle->dbc);
1364 rc = SQLPrepare((*statement)->stmt, (SQLCHAR *) query, (SQLINTEGER) len);
1365 CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1366 (*statement)->stmt);
1367 return APR_FROM_SQL_RESULT(rc);
1370 /** pquery: query using a prepared statement + args **/
1371 static int odbc_pquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1372 apr_dbd_prepared_t * statement, const char **args)
1374 SQLRETURN rc = SQL_SUCCESS;
1375 int i, argp;
1377 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1378 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1379 &argp, (const void **) args, TEXTMODE);
1381 if (SQL_SUCCEEDED(rc)) {
1382 rc = SQLExecute(statement->stmt);
1383 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1384 statement->stmt);
1386 if (SQL_SUCCEEDED(rc)) {
1387 rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
1388 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1389 statement->stmt);
1391 return APR_FROM_SQL_RESULT(rc);
1394 /** pvquery: query using a prepared statement + args **/
1395 static int odbc_pvquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1396 apr_dbd_prepared_t * statement, va_list args)
1398 const char **values;
1399 int i;
1400 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1401 for (i = 0; i < statement->nvals; i++)
1402 values[i] = va_arg(args, const char *);
1403 return odbc_pquery(pool, handle, nrows, statement, values);
1406 /** pselect: select using a prepared statement + args **/
1407 int odbc_pselect(apr_pool_t * pool, apr_dbd_t * handle,
1408 apr_dbd_results_t ** res, apr_dbd_prepared_t * statement,
1409 int random, const char **args)
1411 SQLRETURN rc = SQL_SUCCESS;
1412 int i, argp;
1414 if (random) {
1415 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1416 (SQLPOINTER) SQL_SCROLLABLE, 0);
1417 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1418 rc, SQL_HANDLE_STMT, statement->stmt);
1420 if (SQL_SUCCEEDED(rc)) {
1421 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1422 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1423 &argp, (const void **) args, TEXTMODE);
1425 if (SQL_SUCCEEDED(rc)) {
1426 rc = SQLExecute(statement->stmt);
1427 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1428 statement->stmt);
1430 if (SQL_SUCCEEDED(rc)) {
1431 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1432 apr_pool_cleanup_register(pool, *res,
1433 odbc_close_results, apr_pool_cleanup_null);
1435 return APR_FROM_SQL_RESULT(rc);
1438 /** pvselect: select using a prepared statement + args **/
1439 static int odbc_pvselect(apr_pool_t * pool, apr_dbd_t * handle,
1440 apr_dbd_results_t ** res,
1441 apr_dbd_prepared_t * statement, int random,
1442 va_list args)
1444 const char **values;
1445 int i;
1447 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1448 for (i = 0; i < statement->nvals; i++)
1449 values[i] = va_arg(args, const char *);
1450 return odbc_pselect(pool, handle, res, statement, random, values);
1453 /** get_name: get a column title from a result set **/
1454 static const char *odbc_get_name(const apr_dbd_results_t * res, int col)
1456 SQLRETURN rc;
1457 char buffer[MAX_COLUMN_NAME];
1458 SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1459 SQLUINTEGER colsize;
1461 if (col >= res->ncols)
1462 return NULL; /* bogus column number */
1463 if (res->colnames[col] != NULL)
1464 return res->colnames[col]; /* we already retrieved it */
1465 rc = SQLDescribeCol(res->stmt, col + 1,
1466 buffer, sizeof(buffer), &colnamelength,
1467 &coltype, &colsize, &coldecimal, &colnullable);
1468 CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1469 SQL_HANDLE_STMT, res->stmt);
1470 res->colnames[col] = apr_pstrdup(res->pool, buffer);
1471 return res->colnames[col];
1474 /** transaction_mode_get: get the mode of transaction **/
1475 static int odbc_transaction_mode_get(apr_dbd_transaction_t * trans)
1477 return (int) trans->apr_dbd->transaction_mode;
1480 /** transaction_mode_set: set the mode of transaction **/
1481 static int odbc_transaction_mode_set(apr_dbd_transaction_t * trans, int mode)
1483 SQLRETURN rc;
1485 int legal = (SQL_TXN_READ_UNCOMMITTED | SQL_TXN_READ_COMMITTED
1486 | SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE);
1488 if ((mode & legal) != mode)
1489 return APR_EGENERAL;
1491 trans->apr_dbd->transaction_mode = mode;
1492 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_TXN_ISOLATION,
1493 (void *) mode, 0);
1494 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)",
1495 rc, SQL_HANDLE_DBC, trans->dbc);
1496 return APR_FROM_SQL_RESULT(rc);
1499 /** pbquery: query using a prepared statement + binary args **/
1500 static int odbc_pbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1501 apr_dbd_prepared_t * statement, const void **args)
1503 SQLRETURN rc = SQL_SUCCESS;
1504 int i, argp;
1506 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1507 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1508 &argp, args, BINARYMODE);
1510 if (SQL_SUCCEEDED(rc)) {
1511 rc = SQLExecute(statement->stmt);
1512 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1513 statement->stmt);
1515 if (SQL_SUCCEEDED(rc)) {
1516 rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
1517 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1518 statement->stmt);
1520 return APR_FROM_SQL_RESULT(rc);
1523 /** pbselect: select using a prepared statement + binary args **/
1524 static int odbc_pbselect(apr_pool_t * pool, apr_dbd_t * handle,
1525 apr_dbd_results_t ** res,
1526 apr_dbd_prepared_t * statement,
1527 int random, const void **args)
1529 SQLRETURN rc = SQL_SUCCESS;
1530 int i, argp;
1532 if (random) {
1533 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1534 (SQLPOINTER) SQL_SCROLLABLE, 0);
1535 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1536 rc, SQL_HANDLE_STMT, statement->stmt);
1538 if (SQL_SUCCEEDED(rc)) {
1539 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1540 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1541 &argp, args, BINARYMODE);
1544 if (SQL_SUCCEEDED(rc)) {
1545 rc = SQLExecute(statement->stmt);
1546 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1547 statement->stmt);
1549 if (SQL_SUCCEEDED(rc)) {
1550 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1551 apr_pool_cleanup_register(pool, *res,
1552 odbc_close_results, apr_pool_cleanup_null);
1555 return APR_FROM_SQL_RESULT(rc);
1558 /** pvbquery: query using a prepared statement + binary args **/
1559 static int odbc_pvbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1560 apr_dbd_prepared_t * statement, va_list args)
1562 const char **values;
1563 int i;
1565 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1566 for (i = 0; i < statement->nvals; i++)
1567 values[i] = va_arg(args, const char *);
1568 return odbc_pbquery(pool, handle, nrows, statement, (const void **) values);
1571 /** pvbselect: select using a prepared statement + binary args **/
1572 static int odbc_pvbselect(apr_pool_t * pool, apr_dbd_t * handle,
1573 apr_dbd_results_t ** res,
1574 apr_dbd_prepared_t * statement,
1575 int random, va_list args)
1577 const char **values;
1578 int i;
1580 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1581 for (i = 0; i < statement->nvals; i++)
1582 values[i] = va_arg(args, const char *);
1583 return odbc_pbselect(pool, handle, res, statement, random, (const void **) values);
1586 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1587 ODBC_DRIVER_STRING,
1588 odbc_init,
1589 odbc_native_handle,
1590 odbc_open,
1591 odbc_check_conn,
1592 odbc_close,
1593 odbc_set_dbname,
1594 odbc_start_transaction,
1595 odbc_end_transaction,
1596 odbc_query,
1597 odbc_select,
1598 odbc_num_cols,
1599 odbc_num_tuples,
1600 odbc_get_row,
1601 odbc_get_entry,
1602 odbc_error,
1603 odbc_escape,
1604 odbc_prepare,
1605 odbc_pvquery,
1606 odbc_pvselect,
1607 odbc_pquery,
1608 odbc_pselect,
1609 odbc_get_name,
1610 odbc_transaction_mode_get,
1611 odbc_transaction_mode_set,
1612 "?",
1613 odbc_pvbquery,
1614 odbc_pvbselect,
1615 odbc_pbquery,
1616 odbc_pbselect,
1617 odbc_datum_get
1620 #endif