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.
21 #include "apr_strings.h"
22 #include "apr_buckets.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"
33 /* If library is ODBC-V2, use macros for limited ODBC-V2 support
34 * No random access in V2.
37 #define ODBCVER 0x0200
38 #include "apr_dbd_odbc_v2.h"
41 /* standard ODBC include files */
45 #elif defined(HAVE_ODBC_SQL_H)
47 #include <odbc/sqlext.h>
50 /* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver'
51 * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which
52 * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql.
54 #ifndef ODBC_DRIVER_NAME
55 #define ODBC_DRIVER_NAME odbc
57 #define STRINGIFY(x) #x
58 #define NAMIFY2(n) apr_dbd_##n##_driver
59 #define NAMIFY1(n) NAMIFY2(n)
60 #define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
61 #define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
63 /* Required APR version for this driver */
64 #define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
65 #define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
67 static SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */
69 /* Use a CHECK_ERROR macro so we can grab the source line numbers
72 static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
73 SQLSMALLINT type, SQLHANDLE h, int line);
74 #define CHECK_ERROR(a,s,r,t,h) check_error(a,s,r,t,h, __LINE__)
76 #define SOURCE_FILE __FILE__ /* source file for error messages */
77 #define MAX_ERROR_STRING 1024 /* max length of message in dbc */
78 #define MAX_COLUMN_NAME 256 /* longest column name recognized */
79 #define DEFAULT_BUFFER_SIZE 1024 /* value for defaultBufferSize */
82 #define DEFAULTSEPS " \t\r\n,="
83 #define CSINGLEQUOTE '\''
84 #define SSINGLEQUOTE "\'"
86 #define TEXTMODE 1 /* used for text (APR 1.2) mode params */
87 #define BINARYMODE 0 /* used for binary (APR 1.3+) mode params */
89 /* Identify datatypes which are LOBs
90 * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB
92 #define IS_LOB(t) (t == SQL_LONGVARCHAR \
93 || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
94 || t == -98 || t == -99)
96 /* These types are CLOBs
97 * - DB2 DRDA driver uses undefined type -98 for CLOB
100 (t == SQL_LONGVARCHAR || t == -98)
102 /* Convert a SQL result to an APR result */
103 #define APR_FROM_SQL_RESULT(rc) \
104 (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
106 /* DBD opaque structures */
109 SQLHANDLE dbc; /* SQL connection handle - NULL after close */
110 apr_pool_t *pool; /* connection lifetime pool */
111 char *dbname; /* ODBC datasource */
114 char lastError[MAX_ERROR_STRING];
115 int defaultBufferSize; /* used for CLOBs in text mode,
116 * and when fld size is indeterminate */
117 intptr_t transaction_mode;
118 intptr_t dboptions; /* driver options re SQLGetData */
119 intptr_t default_transaction_mode;
120 int can_commit; /* controls end_trans behavior */
123 struct apr_dbd_results_t
125 SQLHANDLE stmt; /* parent sql statement handle */
126 SQLHANDLE dbc; /* parent sql connection handle */
127 apr_pool_t *pool; /* pool from query or select */
128 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
129 int random; /* random access requested */
130 int ncols; /* number of columns */
131 int isclosed; /* cursor has been closed */
132 char **colnames; /* array of column names (NULL until used) */
133 SQLPOINTER *colptrs; /* pointers to column data */
134 SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */
135 SQLINTEGER *coltextsizes; /* max-sizes if converted to text */
136 SQLSMALLINT *coltypes; /* array of SQL data types for columns */
137 SQLLEN *colinds; /* array of SQL data indicator/strlens */
138 int *colstate; /* array of column states
139 * - avail, bound, present, unavail
141 int *all_data_fetched; /* flags data as all fetched, for LOBs */
142 void *data; /* buffer for all data for one row */
145 enum /* results column states */
147 COL_AVAIL, /* data may be retrieved with SQLGetData */
148 COL_PRESENT, /* data has been retrieved with SQLGetData */
149 COL_BOUND, /* column is bound to colptr */
150 COL_RETRIEVED, /* all data from column has been returned */
151 COL_UNAVAIL /* column is unavailable because ODBC driver
152 * requires that columns be retrieved
153 * in ascending order and a higher col
158 struct apr_dbd_row_t {
159 SQLHANDLE stmt; /* parent ODBC statement handle */
160 SQLHANDLE dbc; /* parent ODBC connection handle */
161 apr_pool_t *pool; /* pool from get_row */
162 apr_dbd_results_t *res;
165 struct apr_dbd_transaction_t {
166 SQLHANDLE dbc; /* parent ODBC connection handle */
167 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
170 struct apr_dbd_prepared_t {
171 SQLHANDLE stmt; /* ODBC statement handle */
172 SQLHANDLE dbc; /* parent ODBC connection handle */
176 int *types; /* array of DBD data types */
179 static void odbc_lob_bucket_destroy(void *data);
180 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
181 static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
182 apr_size_t *len, apr_read_type_e block);
184 /* the ODBC LOB bucket type */
185 static const apr_bucket_type_t odbc_bucket_type = {
186 "ODBC_LOB", 5, APR_BUCKET_DATA,
187 odbc_lob_bucket_destroy,
188 odbc_lob_bucket_read,
189 odbc_lob_bucket_setaside,
190 apr_bucket_shared_split,
191 apr_bucket_shared_copy
194 /* ODBC LOB bucket data */
196 /** Ref count for shared bucket */
197 apr_bucket_refcount refcount;
198 const apr_dbd_row_t *row;
203 /* SQL datatype mappings to DBD datatypes
204 * These tables must correspond *exactly* to the apr_dbd_type_e enum
208 /* ODBC "C" types to DBD datatypes */
209 static SQLSMALLINT const sqlCtype[] = {
210 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
211 SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
212 SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
213 SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */
214 SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */
215 SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */
216 SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */
217 SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */
218 SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */
219 SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
220 SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
221 SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
222 SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
223 SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */
224 SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
225 SQL_C_CHAR, /*SQL_C_TYPE_TIME, APR_DBD_TYPE_TIME, \%pDi */
226 SQL_C_CHAR, /*SQL_C_TYPE_DATE, APR_DBD_TYPE_DATE, \%pDd */
227 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
228 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
229 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
230 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
231 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
232 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
234 #define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0]))
236 /* ODBC Base types to DBD datatypes */
237 static SQLSMALLINT const sqlBaseType[] = {
238 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
239 SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
240 SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
241 SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */
242 SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */
243 SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */
244 SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */
245 SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */
246 SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */
247 SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
248 SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
249 SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
250 SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
251 SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */
252 SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
253 SQL_CHAR, /*SQL_TIME, APR_DBD_TYPE_TIME, \%pDi */
254 SQL_CHAR, /*SQL_DATE, APR_DBD_TYPE_DATE, \%pDd */
255 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
256 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
257 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
258 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
259 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
260 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
263 /* result sizes for DBD datatypes (-1 for null-terminated) */
264 static int const sqlSizes[] = {
266 sizeof(char), /**< \%hhd out: char* */
267 sizeof(unsigned char), /**< \%hhu out: unsigned char* */
268 sizeof(short), /**< \%hd out: short* */
269 sizeof(unsigned short), /**< \%hu out: unsigned short* */
270 sizeof(int), /**< \%d out: int* */
271 sizeof(unsigned int), /**< \%u out: unsigned int* */
272 sizeof(long), /**< \%ld out: long* */
273 sizeof(unsigned long), /**< \%lu out: unsigned long* */
274 sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */
275 sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */
276 sizeof(float), /**< \%f out: float* */
277 sizeof(double), /**< \%lf out: double* */
278 -1, /**< \%s out: char** */
279 -1, /**< \%pDt out: char** */
280 -1, /**< \%pDi out: char** */
281 -1, /**< \%pDd out: char** */
282 -1, /**< \%pDa out: char** */
283 -1, /**< \%pDs out: char** */
284 -1, /**< \%pDz out: char** */
285 sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
286 sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
287 0 /**< \%pDn : in: void*, out: void** */
294 /* close any open results for the connection */
295 static apr_status_t odbc_close_results(void *d)
297 apr_dbd_results_t *dbr = (apr_dbd_results_t *)d;
298 SQLRETURN rc = SQL_SUCCESS;
300 if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) {
302 rc = SQLCloseCursor(dbr->stmt);
305 return APR_FROM_SQL_RESULT(rc);
308 /* close the ODBC statement handle from a prepare */
309 static apr_status_t odbc_close_pstmt(void *s)
311 SQLRETURN rc = APR_SUCCESS;
312 apr_dbd_prepared_t *statement = s;
314 /* stmt is closed if connection has already been closed */
316 SQLHANDLE hstmt = statement->stmt;
318 if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
319 rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
321 statement->stmt = NULL;
323 return APR_FROM_SQL_RESULT(rc);
326 /* close: close/release a connection obtained from open() */
327 static apr_status_t odbc_close(apr_dbd_t *handle)
329 SQLRETURN rc = SQL_SUCCESS;
332 rc = SQLDisconnect(handle->dbc);
333 CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
334 rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
335 CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
338 return APR_FROM_SQL_RESULT(rc);
341 /* odbc_close re-defined for passing to pool cleanup */
342 static apr_status_t odbc_close_cleanup(void *handle)
344 return odbc_close((apr_dbd_t *)handle);
347 /* close the ODBC environment handle at process termination */
348 static apr_status_t odbc_close_env(SQLHANDLE henv)
352 rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
354 return APR_FROM_SQL_RESULT(rc);
357 /* setup the arrays in results for all the returned columns */
358 static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t *res,
362 intptr_t maxsize, textsize, realsize, type, isunsigned = 1;
364 /* discover the sql type */
365 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
366 (SQLPOINTER)&isunsigned);
367 isunsigned = (isunsigned == SQL_TRUE);
369 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
371 if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE) {
372 /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
373 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
374 0, NULL, (SQLPOINTER)&type);
377 if (!SQL_SUCCEEDED(rc)) {
378 /* if still unknown make it CHAR */
387 /* fix these numeric binary types up as signed/unsigned for C types */
388 type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
390 /* LOB types are not changed to C types */
391 case SQL_LONGVARCHAR:
392 type = SQL_LONGVARCHAR;
394 case SQL_LONGVARBINARY:
395 type = SQL_LONGVARBINARY;
404 /* DBD wants times as strings */
412 res->coltypes[icol] = (SQLSMALLINT)type;
414 /* size if retrieved as text */
415 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
416 NULL, (SQLPOINTER)&textsize);
417 if (!SQL_SUCCEEDED(rc) || textsize < 0) {
418 textsize = res->apr_dbd->defaultBufferSize;
420 /* for null-term, which sometimes isn't included */
424 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
425 NULL, (SQLPOINTER)&realsize);
426 if (!SQL_SUCCEEDED(rc)) {
430 maxsize = (textsize > realsize) ? textsize : realsize;
431 if (IS_LOB(type) || maxsize <= 0) {
432 /* LOB types are never bound and have a NULL colptr for binary.
433 * Ingore their real (1-2gb) length & use a default - the larger
434 * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
435 * If not a LOB, but simply unknown length - always use defaultBufferSize.
437 maxsize = res->apr_dbd->defaultBufferSize;
438 if (IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE) {
439 maxsize = APR_BUCKET_BUFF_SIZE;
442 res->colptrs[icol] = NULL;
443 res->colstate[icol] = COL_AVAIL;
444 res->colsizes[icol] = (SQLINTEGER)maxsize;
448 res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
449 res->colsizes[icol] = (SQLINTEGER)maxsize;
450 if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
451 /* we are allowed to call SQLGetData if we need to */
452 rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
453 res->colptrs[icol], maxsize,
454 &(res->colinds[icol]));
455 CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
457 res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
460 /* this driver won't allow us to call SQLGetData on bound
461 * columns - so don't bind any
463 res->colstate[icol] = COL_AVAIL;
470 /* create and populate an apr_dbd_results_t for a select */
471 static SQLRETURN odbc_create_results(apr_dbd_t *handle, SQLHANDLE hstmt,
472 apr_pool_t *pool, const int random,
473 apr_dbd_results_t **res)
478 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
479 (*res)->stmt = hstmt;
480 (*res)->dbc = handle->dbc;
482 (*res)->random = random;
483 (*res)->apr_dbd = handle;
484 rc = SQLNumResultCols(hstmt, &ncols);
485 CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
486 (*res)->ncols = ncols;
488 if (SQL_SUCCEEDED(rc)) {
491 (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
492 (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
493 (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
494 (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
495 (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
496 (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
497 (*res)->ncols = ncols;
499 for (i = 0; i < ncols; i++) {
500 odbc_set_result_column(i, (*res), hstmt);
507 /* bind a parameter - input params only, does not support output parameters */
508 static SQLRETURN odbc_bind_param(apr_pool_t *pool,
509 apr_dbd_prepared_t *statement, const int narg,
510 const SQLSMALLINT type, int *argp,
511 const void **args, const int textmode)
514 SQLSMALLINT baseType, cType;
518 static SQLLEN nullValue = SQL_NULL_DATA;
519 static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */
521 /* bind a NULL data value */
522 if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
526 len = sizeof(SQLINTEGER);
527 indicator = &nullValue;
530 /* bind a non-NULL data value */
532 if (type < 0 || type >= NUM_APR_DBD_TYPES) {
536 baseType = sqlBaseType[type];
537 cType = sqlCtype[type];
541 ptr = (void *)args[*argp];
542 len = (SQLULEN) * (apr_size_t *)args[*argp + 1];
543 cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
544 (*argp) += 4; /* LOBs consume 4 args (last two are unused) */
553 ptr = (void *)args[*argp];
554 len = (SQLULEN)strlen(ptr);
557 ptr = apr_palloc(pool, sizeof(unsigned char));
558 len = sizeof(unsigned char);
559 *(unsigned char *)ptr =
561 atoi(args[*argp]) : *(unsigned char *)args[*argp]);
564 ptr = apr_palloc(pool, sizeof(short));
567 (textmode ? atoi(args[*argp]) : *(short *)args[*argp]);
570 ptr = apr_palloc(pool, sizeof(int));
573 (textmode ? atol(args[*argp]) : *(long *)args[*argp]);
576 ptr = apr_palloc(pool, sizeof(float));
580 (float)atof(args[*argp]) : *(float *)args[*argp]);
583 ptr = apr_palloc(pool, sizeof(double));
584 len = sizeof(double);
586 (textmode ? atof(args[*argp]) : *(double *)
590 ptr = apr_palloc(pool, sizeof(apr_int64_t));
591 len = sizeof(apr_int64_t);
592 *(apr_int64_t *)ptr =
594 apr_atoi64(args[*argp]) : *(apr_int64_t *)args[*argp]);
599 (*argp)++; /* non LOBs consume one argument */
602 rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
603 baseType, len, 0, ptr, len, indicator);
604 CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
609 /* LOB / Bucket Brigade functions */
611 /* bucket type specific destroy */
612 static void odbc_lob_bucket_destroy(void *data)
614 odbc_bucket *bd = data;
616 if (apr_bucket_shared_destroy(bd))
620 /* set aside a bucket if possible */
621 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
623 odbc_bucket *bd = (odbc_bucket *)e->data;
625 /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
626 if (apr_pool_is_ancestor(bd->row->pool, pool))
629 return apr_bucket_setaside_notimpl(e, pool);
632 /* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
633 static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
634 apr_size_t *len, apr_read_type_e block)
637 SQLLEN len_indicator;
639 odbc_bucket *bd = (odbc_bucket *)e->data;
642 int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
645 /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
646 type = bd->row->res->coltypes[bd->col];
647 type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
649 /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE,
650 * but they may be much bigger per the BUFSIZE parameter.
652 if (bufsize < APR_BUCKET_BUFF_SIZE)
653 bufsize = APR_BUCKET_BUFF_SIZE;
655 buf = apr_bucket_alloc(bufsize, e->list);
659 rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
663 CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
664 SQL_HANDLE_STMT, bd->row->res->stmt);
666 if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
669 if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
671 if (rc == SQL_SUCCESS_WITH_INFO
672 && (len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize)) {
673 /* not the last read = a full buffer. CLOBs have a null terminator */
674 *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
679 /* the last read - len_indicator is supposed to be the length,
680 * but some driver get this wrong and return the total length.
681 * We try to handle both interpretations.
683 *len = (len_indicator > bufsize
684 && len_indicator >= (SQLLEN)e->start)
685 ? (len_indicator - (SQLLEN)e->start) : len_indicator;
691 /* Create a new LOB bucket to append and append it */
692 nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
693 APR_BUCKET_INIT(nxt);
696 nxt->type = &odbc_bucket_type;
697 nxt->free = apr_bucket_free;
699 nxt->start = e->start + *len;
700 APR_BUCKET_INSERT_AFTER(e, nxt);
703 odbc_lob_bucket_destroy(e->data);
705 /* make current bucket into a heap bucket */
706 apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
709 /* No data is success in this context */
712 return APR_FROM_SQL_RESULT(rc);
715 /* Create a bucket brigade on the row pool for a LOB column */
716 static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
717 SQLSMALLINT type, apr_bucket_brigade *bb)
719 apr_bucket_alloc_t *list = bb->bucket_alloc;
720 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
721 odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
722 apr_bucket *eos = apr_bucket_eos_create(list);
729 b->type = &odbc_bucket_type;
730 b->free = apr_bucket_free;
732 /* LOB lengths are unknown in ODBC */
733 b = apr_bucket_shared_make(b, bd, 0, -1);
735 APR_BRIGADE_INSERT_TAIL(bb, b);
736 APR_BRIGADE_INSERT_TAIL(bb, eos);
741 /* returns a data pointer for a column, returns NULL for NULL value,
742 * return -1 if data not available
744 static void *odbc_get(const apr_dbd_row_t *row, const int col,
745 const SQLSMALLINT sqltype)
749 int state = row->res->colstate[col];
750 intptr_t options = row->res->apr_dbd->dboptions;
755 case (COL_RETRIEVED):
760 if (sqltype == row->res->coltypes[col]) {
761 /* same type and we already have the data */
762 row->res->colstate[col] = COL_RETRIEVED;
763 return (row->res->colinds[col] == SQL_NULL_DATA) ?
764 NULL : row->res->colptrs[col];
768 /* we need to get the data now */
769 if (!(options & SQL_GD_ANY_ORDER)) {
770 /* this ODBC driver requires columns to be retrieved in order,
771 * so we attempt to get every prior un-gotten non-LOB column
774 for (i = 0; i < col; i++) {
775 if (row->res->colstate[i] == COL_AVAIL) {
776 if (IS_LOB(row->res->coltypes[i]))
777 row->res->colstate[i] = COL_UNAVAIL;
779 odbc_get(row, i, row->res->coltypes[i]);
780 row->res->colstate[i] = COL_PRESENT;
786 if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
787 /* this driver won't let us re-get bound columns */
790 /* a LOB might not have a buffer allocated yet - so create one */
791 if (!row->res->colptrs[col])
792 row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
794 rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
795 row->res->colsizes[col], &indicator);
796 CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
798 if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
801 if (SQL_SUCCEEDED(rc)) {
802 /* whatever it was originally, it is now this sqltype */
803 row->res->coltypes[col] = sqltype;
804 /* this allows getting CLOBs in text mode by calling get_entry
805 * until it returns NULL
807 row->res->colstate[col] =
808 (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
809 return row->res->colptrs[col];
815 /* Parse the parameter string for open */
816 static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
817 int *connect, SQLCHAR **datasource,
818 SQLCHAR **user, SQLCHAR **password,
819 int *defaultBufferSize, int *nattrs,
820 int **attrs, intptr_t **attrvals)
822 char *seps, *last, *next, *name[MAX_PARAMS], *val[MAX_PARAMS];
823 int nparams = 0, i, j;
825 *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
826 *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(intptr_t));
829 name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
831 /* no params is OK here - let connect return a more useful error msg */
836 if (last[strspn(last, seps)] == CSINGLEQUOTE) {
837 last += strspn(last, seps);
840 val[nparams] = apr_strtok(NULL, seps, &last);
844 next = apr_strtok(NULL, seps, &last);
848 if (nparams >= MAX_PARAMS) {
849 /* too many parameters, no place to store */
852 name[nparams] = next;
855 for (j = i = 0; i < nparams; i++) {
856 if (!apr_strnatcasecmp(name[i], "CONNECT")) {
857 *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
860 else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) {
861 *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
864 else if (!apr_strnatcasecmp(name[i], "USER")) {
865 *user = (SQLCHAR *)apr_pstrdup(pool, val[i]);
867 else if (!apr_strnatcasecmp(name[i], "PASSWORD")) {
868 *password = (SQLCHAR *)apr_pstrdup(pool, val[i]);
870 else if (!apr_strnatcasecmp(name[i], "BUFSIZE")) {
871 *defaultBufferSize = atoi(val[i]);
873 else if (!apr_strnatcasecmp(name[i], "ACCESS")) {
874 if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
875 (*attrvals)[j] = SQL_MODE_READ_ONLY;
876 else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
877 (*attrvals)[j] = SQL_MODE_READ_WRITE;
880 (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
882 else if (!apr_strnatcasecmp(name[i], "CTIMEOUT")) {
883 (*attrvals)[j] = atoi(val[i]);
884 (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
886 else if (!apr_strnatcasecmp(name[i], "STIMEOUT")) {
887 (*attrvals)[j] = atoi(val[i]);
888 (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
890 else if (!apr_strnatcasecmp(name[i], "TXMODE")) {
891 if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
892 (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
893 else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
894 (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
895 else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
896 (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
897 else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
898 (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
899 else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
903 (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
909 return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
912 /* common handling after ODBC calls - save error info (code and text) in dbc */
913 static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
914 SQLSMALLINT type, SQLHANDLE h, int line)
917 SQLCHAR sqlstate[128];
919 SQLSMALLINT reslength;
920 char *res, *p, *end, *logval = NULL;
923 /* set info about last error in dbc - fast return for SQL_SUCCESS */
924 if (rc == SQL_SUCCESS) {
925 char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
926 apr_size_t successMsgLen = sizeof successMsg - 1;
928 dbc->lasterrorcode = SQL_SUCCESS;
929 apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError);
930 apr_cpystrn(dbc->lastError + successMsgLen, step,
931 sizeof dbc->lastError - successMsgLen);
935 case SQL_INVALID_HANDLE:
936 res = "SQL_INVALID_HANDLE";
941 case SQL_SUCCESS_WITH_INFO:
942 res = "SQL_SUCCESS_WITH_INFO";
944 case SQL_STILL_EXECUTING:
945 res = "SQL_STILL_EXECUTING";
948 res = "SQL_NEED_DATA";
954 res = "unrecognized SQL return code";
956 /* these two returns are expected during normal execution */
957 if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA
958 && dbc->can_commit != APR_DBD_TRANSACTION_IGNORE_ERRORS) {
959 dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK;
962 end = p + sizeof(dbc->lastError);
963 dbc->lasterrorcode = rc;
964 p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
965 step, res, rc, SOURCE_FILE, line - 1);
966 for (i = 1, rc = 0; rc == 0; i++) {
967 rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
968 sizeof(buffer), &reslength);
969 if (SQL_SUCCEEDED(rc) && (p < (end - 280)))
970 p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
972 apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
973 /* if env var was set or call was init/open (no dbname) - log to stderr */
974 if (logval || !dbc->dbname ) {
975 char timestamp[APR_CTIME_LEN];
978 apr_ctime(timestamp, apr_time_now());
979 apr_file_open_stderr(&se, dbc->pool);
980 apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
984 static APR_INLINE int odbc_check_rollback(apr_dbd_t *handle)
986 if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) {
987 handle->lasterrorcode = SQL_ERROR;
988 apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ",
989 sizeof handle->lastError);
996 * public functions per DBD driver API
999 /** init: allow driver to perform once-only initialisation. **/
1000 static void odbc_init(apr_pool_t *pool)
1004 apr_version_t apuver;
1006 apu_version(&apuver);
1007 if (apuver.major != DRIVER_APU_VERSION_MAJOR
1008 || apuver.minor != DRIVER_APU_VERSION_MINOR) {
1011 apr_file_open_stderr(&se, pool);
1012 apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
1013 "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
1014 DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
1015 apuver.major, apuver.minor);
1022 step = "SQLAllocHandle (SQL_HANDLE_ENV)";
1023 rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
1024 apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
1025 if (SQL_SUCCEEDED(rc)) {
1026 step = "SQLSetEnvAttr";
1027 rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
1028 (SQLPOINTER)SQL_OV_ODBC3, 0);
1032 SQLHANDLE err_h = henv;
1034 tmp_dbc.pool = pool;
1035 tmp_dbc.dbname = NULL;
1036 CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
1040 /** native_handle: return the native database handle of the underlying db **/
1041 static void *odbc_native_handle(apr_dbd_t *handle)
1046 /** open: obtain a database connection from the server rec. **/
1048 /* It would be more efficient to allocate a single statement handle
1049 * here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
1050 * SQLPrepare, and we don't know whether random-access is
1051 * specified until SQLExecute so we cannot.
1054 static apr_dbd_t *odbc_open(apr_pool_t *pool, const char *params, const char **error)
1057 SQLHANDLE hdbc = NULL;
1061 int defaultBufferSize = DEFAULT_BUFFER_SIZE;
1062 SQLHANDLE err_h = NULL;
1063 SQLCHAR *datasource = (SQLCHAR *)"", *user = (SQLCHAR *)"",
1064 *password = (SQLCHAR *)"";
1065 int nattrs = 0, *attrs = NULL, connect = 0;
1066 intptr_t *attrvals = NULL;
1068 err_step = "SQLAllocHandle (SQL_HANDLE_DBC)";
1069 err_htype = SQL_HANDLE_ENV;
1071 rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
1072 if (SQL_SUCCEEDED(rc)) {
1073 err_step = "Invalid DBD Parameters - open";
1074 err_htype = SQL_HANDLE_DBC;
1076 rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
1077 &password, &defaultBufferSize, &nattrs, &attrs,
1080 if (SQL_SUCCEEDED(rc)) {
1081 for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1082 err_step = "SQLSetConnectAttr (from DBD Parameters)";
1083 err_htype = SQL_HANDLE_DBC;
1085 rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0);
1088 if (SQL_SUCCEEDED(rc)) {
1093 err_step = "SQLDriverConnect";
1094 err_htype = SQL_HANDLE_DBC;
1096 rc = SQLDriverConnect(hdbc, NULL, datasource,
1097 (SQLSMALLINT)strlen((char *)datasource),
1098 out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1101 err_step = "SQLConnect";
1102 err_htype = SQL_HANDLE_DBC;
1104 rc = SQLConnect(hdbc, datasource,
1105 (SQLSMALLINT)strlen((char *)datasource),
1106 user, (SQLSMALLINT)strlen((char *)user),
1107 password, (SQLSMALLINT)strlen((char *)password));
1110 if (SQL_SUCCEEDED(rc)) {
1111 handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1112 handle->dbname = apr_pstrdup(pool, (char *)datasource);
1114 handle->pool = pool;
1115 handle->defaultBufferSize = defaultBufferSize;
1116 CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1117 handle->default_transaction_mode = 0;
1118 handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1119 SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1120 &(handle->default_transaction_mode), sizeof(intptr_t), NULL);
1121 handle->transaction_mode = handle->default_transaction_mode;
1122 SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1123 sizeof(intptr_t), NULL);
1124 apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1130 tmp_dbc.pool = pool;
1131 tmp_dbc.dbname = NULL;
1132 CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1134 *error = apr_pstrdup(pool, tmp_dbc.lastError);
1136 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1141 /** check_conn: check status of a database connection **/
1142 static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1147 rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1148 sizeof(SQLUINTEGER), NULL);
1149 CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1150 SQL_HANDLE_DBC, handle->dbc);
1151 /* if driver cannot check connection, say so */
1152 if (rc != SQL_SUCCESS)
1153 return APR_ENOTIMPL;
1155 return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1158 /** set_dbname: select database name. May be a no-op if not supported. **/
1159 static int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle,
1162 if (apr_strnatcmp(name, handle->dbname)) {
1163 return APR_EGENERAL; /* It's illegal to change dbname in ODBC */
1165 CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1167 return APR_SUCCESS; /* OK if it's the same name */
1170 /** transaction: start a transaction. May be a no-op. **/
1171 static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1172 apr_dbd_transaction_t **trans)
1174 SQLRETURN rc = SQL_SUCCESS;
1176 if (handle->transaction_mode) {
1177 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION,
1178 (SQLPOINTER)handle->transaction_mode, 0);
1179 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1180 SQL_HANDLE_DBC, handle->dbc);
1182 if (SQL_SUCCEEDED(rc)) {
1183 /* turn off autocommit for transactions */
1184 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1185 SQL_AUTOCOMMIT_OFF, 0);
1186 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1187 SQL_HANDLE_DBC, handle->dbc);
1189 if (SQL_SUCCEEDED(rc)) {
1190 *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1191 (*trans)->dbc = handle->dbc;
1192 (*trans)->apr_dbd = handle;
1194 handle->can_commit = APR_DBD_TRANSACTION_COMMIT;
1195 return APR_FROM_SQL_RESULT(rc);
1198 /** end_transaction: end a transaction **/
1199 static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1202 int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK)
1203 ? SQL_COMMIT : SQL_ROLLBACK;
1205 rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
1206 CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1207 if (SQL_SUCCEEDED(rc)) {
1208 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1209 (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1210 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1211 rc, SQL_HANDLE_DBC, trans->dbc);
1213 trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1214 return APR_FROM_SQL_RESULT(rc);
1217 /** query: execute an SQL statement which doesn't return a result set **/
1218 static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1221 SQLHANDLE hstmt = NULL;
1222 size_t len = strlen(statement);
1224 if (odbc_check_rollback(handle))
1225 return APR_EGENERAL;
1227 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1228 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1230 if (!SQL_SUCCEEDED(rc))
1231 return APR_FROM_SQL_RESULT(rc);
1233 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1234 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1236 if (SQL_SUCCEEDED(rc)) {
1239 rc = SQLRowCount(hstmt, &rowcount);
1240 *nrows = (int)rowcount;
1241 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1244 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1245 return APR_FROM_SQL_RESULT(rc);
1248 /** select: execute an SQL statement which returns a result set **/
1249 static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1250 apr_dbd_results_t **res, const char *statement,
1255 apr_dbd_prepared_t *stmt;
1256 size_t len = strlen(statement);
1258 if (odbc_check_rollback(handle))
1259 return APR_EGENERAL;
1261 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1262 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1264 if (!SQL_SUCCEEDED(rc))
1265 return APR_FROM_SQL_RESULT(rc);
1266 /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1267 * is not a prepared statement. We want the same cleanup mechanism.
1269 stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1270 stmt->apr_dbd = handle;
1271 stmt->dbc = handle->dbc;
1273 apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1275 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1276 (SQLPOINTER)SQL_SCROLLABLE, 0);
1277 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1278 SQL_HANDLE_STMT, hstmt);
1280 if (SQL_SUCCEEDED(rc)) {
1281 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1282 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1284 if (SQL_SUCCEEDED(rc)) {
1285 rc = odbc_create_results(handle, hstmt, pool, random, res);
1286 apr_pool_cleanup_register(pool, *res,
1287 odbc_close_results, apr_pool_cleanup_null);
1289 return APR_FROM_SQL_RESULT(rc);
1292 /** num_cols: get the number of columns in a results set **/
1293 static int odbc_num_cols(apr_dbd_results_t *res)
1298 /** num_tuples: get the number of rows in a results set **/
1299 static int odbc_num_tuples(apr_dbd_results_t *res)
1304 rc = SQLRowCount(res->stmt, &nrows);
1305 CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1306 return SQL_SUCCEEDED(rc) ? (int)nrows : -1;
1309 /** get_row: get a row from a result set **/
1310 static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
1311 apr_dbd_row_t **row, int rownum)
1317 *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1318 (*row)->stmt = res->stmt;
1319 (*row)->dbc = res->dbc;
1321 (*row)->pool = res->pool;
1323 /* mark all the columns as needing SQLGetData unless they are bound */
1324 for (c = 0; c < res->ncols; c++) {
1325 if (res->colstate[c] != COL_BOUND) {
1326 res->colstate[c] = COL_AVAIL;
1328 /* some drivers do not null-term zero-len CHAR data */
1329 if (res->colptrs[c])
1330 *(char *)res->colptrs[c] = 0;
1333 if (res->random && (rownum > 0)) {
1334 fetchtype = "SQLFetchScroll";
1335 rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1338 fetchtype = "SQLFetch";
1339 rc = SQLFetch(res->stmt);
1341 CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1342 (*row)->stmt = res->stmt;
1343 if (!SQL_SUCCEEDED(rc) && !res->random) {
1344 /* early close on any error (usually SQL_NO_DATA) if fetching
1345 * sequentially to release resources ASAP
1347 odbc_close_results(res);
1350 return SQL_SUCCEEDED(rc) ? 0 : -1;
1353 /** datum_get: get a binary entry from a row **/
1354 static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col,
1355 apr_dbd_type_e dbdtype, void *data)
1357 SQLSMALLINT sqltype;
1361 if (col >= row->res->ncols)
1362 return APR_EGENERAL;
1364 if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
1365 data = NULL; /* invalid type */
1366 return APR_EGENERAL;
1369 len = sqlSizes[dbdtype];
1370 sqltype = sqlCtype[dbdtype];
1372 /* must not memcpy a brigade, sentinals are relative to orig loc */
1373 if (IS_LOB(sqltype))
1374 return odbc_create_bucket(row, col, sqltype, data);
1376 p = odbc_get(row, col, sqltype);
1377 if (p == (void *)-1)
1378 return APR_EGENERAL;
1381 return APR_ENOENT; /* SQL NULL value */
1384 *(char**)data = (char *)p;
1386 memcpy(data, p, len);
1392 /** get_entry: get an entry from a row (string data) **/
1393 static const char *odbc_get_entry(const apr_dbd_row_t *row, int col)
1397 if (col >= row->res->ncols)
1400 p = odbc_get(row, col, SQL_C_CHAR);
1402 /* NULL or invalid (-1) */
1403 if (p == NULL || p == (void *)-1)
1406 return apr_pstrdup(row->pool, p);
1409 /** error: get current error message (if any) **/
1410 static const char *odbc_error(apr_dbd_t *handle, int errnum)
1412 return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1415 /** escape: escape a string so it is safe for use in query/select **/
1416 static const char *odbc_escape(apr_pool_t *pool, const char *s,
1419 char *newstr, *src, *dst, *sq;
1422 /* return the original if there are no single-quotes */
1423 if (!(sq = strchr(s, '\'')))
1425 /* count the single-quotes and allocate a new buffer */
1426 for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
1428 newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1430 /* move chars, doubling all single-quotes */
1432 for (dst = newstr; *src; src++) {
1433 if ((*dst++ = *src) == '\'')
1440 /** prepare: prepare a statement **/
1441 static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle,
1442 const char *query, const char *label, int nargs,
1443 int nvals, apr_dbd_type_e *types,
1444 apr_dbd_prepared_t **statement)
1447 size_t len = strlen(query);
1449 if (odbc_check_rollback(handle))
1450 return APR_EGENERAL;
1452 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1453 (*statement)->dbc = handle->dbc;
1454 (*statement)->apr_dbd = handle;
1455 (*statement)->nargs = nargs;
1456 (*statement)->nvals = nvals;
1457 (*statement)->types =
1458 apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1459 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1460 apr_pool_cleanup_register(pool, *statement,
1461 odbc_close_pstmt, apr_pool_cleanup_null);
1462 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1463 SQL_HANDLE_DBC, handle->dbc);
1464 rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len);
1465 CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1466 (*statement)->stmt);
1467 return APR_FROM_SQL_RESULT(rc);
1470 /** pquery: query using a prepared statement + args **/
1471 static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1472 apr_dbd_prepared_t *statement, const char **args)
1474 SQLRETURN rc = SQL_SUCCESS;
1477 if (odbc_check_rollback(handle))
1478 return APR_EGENERAL;
1480 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1481 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1482 &argp, (const void **)args, TEXTMODE);
1484 if (SQL_SUCCEEDED(rc)) {
1485 rc = SQLExecute(statement->stmt);
1486 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1489 if (SQL_SUCCEEDED(rc)) {
1492 rc = SQLRowCount(statement->stmt, &rowcount);
1493 *nrows = (int)rowcount;
1494 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1497 return APR_FROM_SQL_RESULT(rc);
1500 /** pvquery: query using a prepared statement + args **/
1501 static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1502 apr_dbd_prepared_t *statement, va_list args)
1504 const char **values;
1507 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1508 for (i = 0; i < statement->nvals; i++)
1509 values[i] = va_arg(args, const char *);
1510 return odbc_pquery(pool, handle, nrows, statement, values);
1513 /** pselect: select using a prepared statement + args **/
1514 static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle,
1515 apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
1516 int random, const char **args)
1518 SQLRETURN rc = SQL_SUCCESS;
1521 if (odbc_check_rollback(handle))
1522 return APR_EGENERAL;
1525 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1526 (SQLPOINTER)SQL_SCROLLABLE, 0);
1527 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1528 rc, SQL_HANDLE_STMT, statement->stmt);
1530 if (SQL_SUCCEEDED(rc)) {
1531 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1532 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1533 &argp, (const void **)args, TEXTMODE);
1536 if (SQL_SUCCEEDED(rc)) {
1537 rc = SQLExecute(statement->stmt);
1538 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1541 if (SQL_SUCCEEDED(rc)) {
1542 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1543 apr_pool_cleanup_register(pool, *res,
1544 odbc_close_results, apr_pool_cleanup_null);
1546 return APR_FROM_SQL_RESULT(rc);
1549 /** pvselect: select using a prepared statement + args **/
1550 static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle,
1551 apr_dbd_results_t **res,
1552 apr_dbd_prepared_t *statement, int random,
1555 const char **values;
1558 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1559 for (i = 0; i < statement->nvals; i++)
1560 values[i] = va_arg(args, const char *);
1561 return odbc_pselect(pool, handle, res, statement, random, values);
1564 /** get_name: get a column title from a result set **/
1565 static const char *odbc_get_name(const apr_dbd_results_t *res, int col)
1568 char buffer[MAX_COLUMN_NAME];
1569 SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1572 if (col >= res->ncols)
1573 return NULL; /* bogus column number */
1574 if (res->colnames[col] != NULL)
1575 return res->colnames[col]; /* we already retrieved it */
1576 rc = SQLDescribeCol(res->stmt, col + 1,
1577 (SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
1578 &coltype, &colsize, &coldecimal, &colnullable);
1579 CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1580 SQL_HANDLE_STMT, res->stmt);
1581 res->colnames[col] = apr_pstrdup(res->pool, buffer);
1582 return res->colnames[col];
1585 /** transaction_mode_get: get the mode of transaction **/
1586 static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans)
1588 return (int)trans->apr_dbd->can_commit;
1591 /** transaction_mode_set: set the mode of transaction **/
1592 static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
1594 int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS
1595 | APR_DBD_TRANSACTION_COMMIT
1596 | APR_DBD_TRANSACTION_ROLLBACK);
1598 if ((mode & legal) != mode)
1599 return APR_EGENERAL;
1601 trans->apr_dbd->can_commit = mode;
1605 /** pbquery: query using a prepared statement + binary args **/
1606 static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1607 apr_dbd_prepared_t *statement, const void **args)
1609 SQLRETURN rc = SQL_SUCCESS;
1612 if (odbc_check_rollback(handle))
1613 return APR_EGENERAL;
1615 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1616 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1617 &argp, args, BINARYMODE);
1619 if (SQL_SUCCEEDED(rc)) {
1620 rc = SQLExecute(statement->stmt);
1621 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1624 if (SQL_SUCCEEDED(rc)) {
1627 rc = SQLRowCount(statement->stmt, &rowcount);
1628 *nrows = (int)rowcount;
1629 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1632 return APR_FROM_SQL_RESULT(rc);
1635 /** pbselect: select using a prepared statement + binary args **/
1636 static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle,
1637 apr_dbd_results_t **res,
1638 apr_dbd_prepared_t *statement,
1639 int random, const void **args)
1641 SQLRETURN rc = SQL_SUCCESS;
1644 if (odbc_check_rollback(handle))
1645 return APR_EGENERAL;
1648 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1649 (SQLPOINTER)SQL_SCROLLABLE, 0);
1650 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1651 rc, SQL_HANDLE_STMT, statement->stmt);
1653 if (SQL_SUCCEEDED(rc)) {
1654 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1655 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1656 &argp, args, BINARYMODE);
1659 if (SQL_SUCCEEDED(rc)) {
1660 rc = SQLExecute(statement->stmt);
1661 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1664 if (SQL_SUCCEEDED(rc)) {
1665 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1666 apr_pool_cleanup_register(pool, *res,
1667 odbc_close_results, apr_pool_cleanup_null);
1670 return APR_FROM_SQL_RESULT(rc);
1673 /** pvbquery: query using a prepared statement + binary args **/
1674 static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1675 apr_dbd_prepared_t *statement, va_list args)
1677 const char **values;
1680 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1681 for (i = 0; i < statement->nvals; i++)
1682 values[i] = va_arg(args, const char *);
1683 return odbc_pbquery(pool, handle, nrows, statement, (const void **)values);
1686 /** pvbselect: select using a prepared statement + binary args **/
1687 static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle,
1688 apr_dbd_results_t **res,
1689 apr_dbd_prepared_t *statement,
1690 int random, va_list args)
1692 const char **values;
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_pbselect(pool, handle, res, statement, random, (const void **)values);
1701 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1709 odbc_start_transaction,
1710 odbc_end_transaction,
1725 odbc_transaction_mode_get,
1726 odbc_transaction_mode_set,