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 int transaction_mode;
118 int dboptions; /* driver options re SQLGetData */
119 int 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 int 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] = 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] = maxsize;
448 res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
449 res->colsizes[icol] = maxsize;
450 if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
451 /* we are allowed to call SQLGetData if we need to */
452 rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
453 res->colptrs[icol], maxsize,
454 &(res->colinds[icol]));
455 CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
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 int 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, int **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(int));
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, *attrvals = NULL, connect = 0;
1067 err_step = "SQLAllocHandle (SQL_HANDLE_DBC)";
1068 err_htype = SQL_HANDLE_ENV;
1070 rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
1071 if (SQL_SUCCEEDED(rc)) {
1072 err_step = "Invalid DBD Parameters - open";
1073 err_htype = SQL_HANDLE_DBC;
1075 rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
1076 &password, &defaultBufferSize, &nattrs, &attrs,
1079 if (SQL_SUCCEEDED(rc)) {
1080 for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1081 err_step = "SQLSetConnectAttr (from DBD Parameters)";
1082 err_htype = SQL_HANDLE_DBC;
1084 rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0);
1087 if (SQL_SUCCEEDED(rc)) {
1092 err_step = "SQLDriverConnect";
1093 err_htype = SQL_HANDLE_DBC;
1095 rc = SQLDriverConnect(hdbc, NULL, datasource,
1096 (SQLSMALLINT)strlen((char *)datasource),
1097 out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1100 err_step = "SQLConnect";
1101 err_htype = SQL_HANDLE_DBC;
1103 rc = SQLConnect(hdbc, datasource,
1104 (SQLSMALLINT)strlen((char *)datasource),
1105 user, (SQLSMALLINT)strlen((char *)user),
1106 password, (SQLSMALLINT)strlen((char *)password));
1109 if (SQL_SUCCEEDED(rc)) {
1110 handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1111 handle->dbname = apr_pstrdup(pool, (char *)datasource);
1113 handle->pool = pool;
1114 handle->defaultBufferSize = defaultBufferSize;
1115 CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1116 handle->default_transaction_mode = 0;
1117 handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1118 SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1119 &(handle->default_transaction_mode), sizeof(int), NULL);
1120 handle->transaction_mode = handle->default_transaction_mode;
1121 SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1123 apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1129 tmp_dbc.pool = pool;
1130 tmp_dbc.dbname = NULL;
1131 CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1133 *error = apr_pstrdup(pool, tmp_dbc.lastError);
1135 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1140 /** check_conn: check status of a database connection **/
1141 static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1146 rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1147 sizeof(SQLUINTEGER), NULL);
1148 CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1149 SQL_HANDLE_DBC, handle->dbc);
1150 /* if driver cannot check connection, say so */
1151 if (rc != SQL_SUCCESS)
1152 return APR_ENOTIMPL;
1154 return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1157 /** set_dbname: select database name. May be a no-op if not supported. **/
1158 static int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle,
1161 if (apr_strnatcmp(name, handle->dbname)) {
1162 return APR_EGENERAL; /* It's illegal to change dbname in ODBC */
1164 CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1166 return APR_SUCCESS; /* OK if it's the same name */
1169 /** transaction: start a transaction. May be a no-op. **/
1170 static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1171 apr_dbd_transaction_t **trans)
1173 SQLRETURN rc = SQL_SUCCESS;
1175 if (handle->transaction_mode) {
1176 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION,
1177 (SQLPOINTER)handle->transaction_mode, 0);
1178 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1179 SQL_HANDLE_DBC, handle->dbc);
1181 if (SQL_SUCCEEDED(rc)) {
1182 /* turn off autocommit for transactions */
1183 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1184 SQL_AUTOCOMMIT_OFF, 0);
1185 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1186 SQL_HANDLE_DBC, handle->dbc);
1188 if (SQL_SUCCEEDED(rc)) {
1189 *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1190 (*trans)->dbc = handle->dbc;
1191 (*trans)->apr_dbd = handle;
1193 handle->can_commit = APR_DBD_TRANSACTION_COMMIT;
1194 return APR_FROM_SQL_RESULT(rc);
1197 /** end_transaction: end a transaction **/
1198 static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1201 int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK)
1202 ? SQL_COMMIT : SQL_ROLLBACK;
1204 rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
1205 CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1206 if (SQL_SUCCEEDED(rc)) {
1207 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1208 (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1209 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1210 rc, SQL_HANDLE_DBC, trans->dbc);
1212 trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1213 return APR_FROM_SQL_RESULT(rc);
1216 /** query: execute an SQL statement which doesn't return a result set **/
1217 static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1220 SQLHANDLE hstmt = NULL;
1221 size_t len = strlen(statement);
1223 if (odbc_check_rollback(handle))
1224 return APR_EGENERAL;
1226 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1227 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1229 if (!SQL_SUCCEEDED(rc))
1230 return APR_FROM_SQL_RESULT(rc);
1232 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1233 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1235 if (SQL_SUCCEEDED(rc)) {
1238 rc = SQLRowCount(hstmt, &rowcount);
1239 *nrows = (int)rowcount;
1240 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1243 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1244 return APR_FROM_SQL_RESULT(rc);
1247 /** select: execute an SQL statement which returns a result set **/
1248 static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1249 apr_dbd_results_t **res, const char *statement,
1254 apr_dbd_prepared_t *stmt;
1255 size_t len = strlen(statement);
1257 if (odbc_check_rollback(handle))
1258 return APR_EGENERAL;
1260 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1261 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1263 if (!SQL_SUCCEEDED(rc))
1264 return APR_FROM_SQL_RESULT(rc);
1265 /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1266 * is not a prepared statement. We want the same cleanup mechanism.
1268 stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1269 stmt->apr_dbd = handle;
1270 stmt->dbc = handle->dbc;
1272 apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1274 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1275 (SQLPOINTER)SQL_SCROLLABLE, 0);
1276 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1277 SQL_HANDLE_STMT, hstmt);
1279 if (SQL_SUCCEEDED(rc)) {
1280 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1281 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1283 if (SQL_SUCCEEDED(rc)) {
1284 rc = odbc_create_results(handle, hstmt, pool, random, res);
1285 apr_pool_cleanup_register(pool, *res,
1286 odbc_close_results, apr_pool_cleanup_null);
1288 return APR_FROM_SQL_RESULT(rc);
1291 /** num_cols: get the number of columns in a results set **/
1292 static int odbc_num_cols(apr_dbd_results_t *res)
1297 /** num_tuples: get the number of rows in a results set **/
1298 static int odbc_num_tuples(apr_dbd_results_t *res)
1303 rc = SQLRowCount(res->stmt, &nrows);
1304 CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1305 return SQL_SUCCEEDED(rc) ? (int)nrows : -1;
1308 /** get_row: get a row from a result set **/
1309 static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
1310 apr_dbd_row_t **row, int rownum)
1316 *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1317 (*row)->stmt = res->stmt;
1318 (*row)->dbc = res->dbc;
1320 (*row)->pool = res->pool;
1322 /* mark all the columns as needing SQLGetData unless they are bound */
1323 for (c = 0; c < res->ncols; c++) {
1324 if (res->colstate[c] != COL_BOUND) {
1325 res->colstate[c] = COL_AVAIL;
1327 /* some drivers do not null-term zero-len CHAR data */
1328 if (res->colptrs[c])
1329 *(char *)res->colptrs[c] = 0;
1332 if (res->random && (rownum > 0)) {
1333 fetchtype = "SQLFetchScroll";
1334 rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1337 fetchtype = "SQLFetch";
1338 rc = SQLFetch(res->stmt);
1340 CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1341 (*row)->stmt = res->stmt;
1342 if (!SQL_SUCCEEDED(rc) && !res->random) {
1343 /* early close on any error (usually SQL_NO_DATA) if fetching
1344 * sequentially to release resources ASAP
1346 odbc_close_results(res);
1349 return SQL_SUCCEEDED(rc) ? 0 : -1;
1352 /** datum_get: get a binary entry from a row **/
1353 static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col,
1354 apr_dbd_type_e dbdtype, void *data)
1356 SQLSMALLINT sqltype;
1360 if (col >= row->res->ncols)
1361 return APR_EGENERAL;
1363 if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
1364 data = NULL; /* invalid type */
1365 return APR_EGENERAL;
1368 len = sqlSizes[dbdtype];
1369 sqltype = sqlCtype[dbdtype];
1371 /* must not memcpy a brigade, sentinals are relative to orig loc */
1372 if (IS_LOB(sqltype))
1373 return odbc_create_bucket(row, col, sqltype, data);
1375 p = odbc_get(row, col, sqltype);
1376 if (p == (void *)-1)
1377 return APR_EGENERAL;
1380 return APR_ENOENT; /* SQL NULL value */
1383 *(char**)data = (char *)p;
1385 memcpy(data, p, len);
1391 /** get_entry: get an entry from a row (string data) **/
1392 static const char *odbc_get_entry(const apr_dbd_row_t *row, int col)
1396 if (col >= row->res->ncols)
1399 p = odbc_get(row, col, SQL_C_CHAR);
1401 /* NULL or invalid (-1) */
1402 if (p == NULL || p == (void *)-1)
1405 return apr_pstrdup(row->pool, p);
1408 /** error: get current error message (if any) **/
1409 static const char *odbc_error(apr_dbd_t *handle, int errnum)
1411 return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1414 /** escape: escape a string so it is safe for use in query/select **/
1415 static const char *odbc_escape(apr_pool_t *pool, const char *s,
1418 char *newstr, *src, *dst, *sq;
1421 /* return the original if there are no single-quotes */
1422 if (!(sq = strchr(s, '\'')))
1424 /* count the single-quotes and allocate a new buffer */
1425 for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
1427 newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1429 /* move chars, doubling all single-quotes */
1431 for (dst = newstr; *src; src++) {
1432 if ((*dst++ = *src) == '\'')
1439 /** prepare: prepare a statement **/
1440 static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle,
1441 const char *query, const char *label, int nargs,
1442 int nvals, apr_dbd_type_e *types,
1443 apr_dbd_prepared_t **statement)
1446 size_t len = strlen(query);
1448 if (odbc_check_rollback(handle))
1449 return APR_EGENERAL;
1451 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1452 (*statement)->dbc = handle->dbc;
1453 (*statement)->apr_dbd = handle;
1454 (*statement)->nargs = nargs;
1455 (*statement)->nvals = nvals;
1456 (*statement)->types =
1457 apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1458 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1459 apr_pool_cleanup_register(pool, *statement,
1460 odbc_close_pstmt, apr_pool_cleanup_null);
1461 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1462 SQL_HANDLE_DBC, handle->dbc);
1463 rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len);
1464 CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1465 (*statement)->stmt);
1466 return APR_FROM_SQL_RESULT(rc);
1469 /** pquery: query using a prepared statement + args **/
1470 static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1471 apr_dbd_prepared_t *statement, const char **args)
1473 SQLRETURN rc = SQL_SUCCESS;
1476 if (odbc_check_rollback(handle))
1477 return APR_EGENERAL;
1479 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1480 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1481 &argp, (const void **)args, TEXTMODE);
1483 if (SQL_SUCCEEDED(rc)) {
1484 rc = SQLExecute(statement->stmt);
1485 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1488 if (SQL_SUCCEEDED(rc)) {
1491 rc = SQLRowCount(statement->stmt, &rowcount);
1492 *nrows = (int)rowcount;
1493 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1496 return APR_FROM_SQL_RESULT(rc);
1499 /** pvquery: query using a prepared statement + args **/
1500 static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1501 apr_dbd_prepared_t *statement, va_list args)
1503 const char **values;
1506 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1507 for (i = 0; i < statement->nvals; i++)
1508 values[i] = va_arg(args, const char *);
1509 return odbc_pquery(pool, handle, nrows, statement, values);
1512 /** pselect: select using a prepared statement + args **/
1513 static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle,
1514 apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
1515 int random, const char **args)
1517 SQLRETURN rc = SQL_SUCCESS;
1520 if (odbc_check_rollback(handle))
1521 return APR_EGENERAL;
1524 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1525 (SQLPOINTER)SQL_SCROLLABLE, 0);
1526 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1527 rc, SQL_HANDLE_STMT, statement->stmt);
1529 if (SQL_SUCCEEDED(rc)) {
1530 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1531 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1532 &argp, (const void **)args, TEXTMODE);
1535 if (SQL_SUCCEEDED(rc)) {
1536 rc = SQLExecute(statement->stmt);
1537 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1540 if (SQL_SUCCEEDED(rc)) {
1541 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1542 apr_pool_cleanup_register(pool, *res,
1543 odbc_close_results, apr_pool_cleanup_null);
1545 return APR_FROM_SQL_RESULT(rc);
1548 /** pvselect: select using a prepared statement + args **/
1549 static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle,
1550 apr_dbd_results_t **res,
1551 apr_dbd_prepared_t *statement, int random,
1554 const char **values;
1557 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1558 for (i = 0; i < statement->nvals; i++)
1559 values[i] = va_arg(args, const char *);
1560 return odbc_pselect(pool, handle, res, statement, random, values);
1563 /** get_name: get a column title from a result set **/
1564 static const char *odbc_get_name(const apr_dbd_results_t *res, int col)
1567 char buffer[MAX_COLUMN_NAME];
1568 SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1571 if (col >= res->ncols)
1572 return NULL; /* bogus column number */
1573 if (res->colnames[col] != NULL)
1574 return res->colnames[col]; /* we already retrieved it */
1575 rc = SQLDescribeCol(res->stmt, col + 1,
1576 (SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
1577 &coltype, &colsize, &coldecimal, &colnullable);
1578 CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1579 SQL_HANDLE_STMT, res->stmt);
1580 res->colnames[col] = apr_pstrdup(res->pool, buffer);
1581 return res->colnames[col];
1584 /** transaction_mode_get: get the mode of transaction **/
1585 static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans)
1587 return (int)trans->apr_dbd->can_commit;
1590 /** transaction_mode_set: set the mode of transaction **/
1591 static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
1593 int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS
1594 | APR_DBD_TRANSACTION_COMMIT
1595 | APR_DBD_TRANSACTION_ROLLBACK);
1597 if ((mode & legal) != mode)
1598 return APR_EGENERAL;
1600 trans->apr_dbd->can_commit = mode;
1604 /** pbquery: query using a prepared statement + binary args **/
1605 static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1606 apr_dbd_prepared_t *statement, const void **args)
1608 SQLRETURN rc = SQL_SUCCESS;
1611 if (odbc_check_rollback(handle))
1612 return APR_EGENERAL;
1614 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1615 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1616 &argp, args, BINARYMODE);
1618 if (SQL_SUCCEEDED(rc)) {
1619 rc = SQLExecute(statement->stmt);
1620 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1623 if (SQL_SUCCEEDED(rc)) {
1626 rc = SQLRowCount(statement->stmt, &rowcount);
1627 *nrows = (int)rowcount;
1628 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1631 return APR_FROM_SQL_RESULT(rc);
1634 /** pbselect: select using a prepared statement + binary args **/
1635 static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle,
1636 apr_dbd_results_t **res,
1637 apr_dbd_prepared_t *statement,
1638 int random, const void **args)
1640 SQLRETURN rc = SQL_SUCCESS;
1643 if (odbc_check_rollback(handle))
1644 return APR_EGENERAL;
1647 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1648 (SQLPOINTER)SQL_SCROLLABLE, 0);
1649 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1650 rc, SQL_HANDLE_STMT, statement->stmt);
1652 if (SQL_SUCCEEDED(rc)) {
1653 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1654 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1655 &argp, args, BINARYMODE);
1658 if (SQL_SUCCEEDED(rc)) {
1659 rc = SQLExecute(statement->stmt);
1660 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1663 if (SQL_SUCCEEDED(rc)) {
1664 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1665 apr_pool_cleanup_register(pool, *res,
1666 odbc_close_results, apr_pool_cleanup_null);
1669 return APR_FROM_SQL_RESULT(rc);
1672 /** pvbquery: query using a prepared statement + binary args **/
1673 static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1674 apr_dbd_prepared_t *statement, va_list args)
1676 const char **values;
1679 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1680 for (i = 0; i < statement->nvals; i++)
1681 values[i] = va_arg(args, const char *);
1682 return odbc_pbquery(pool, handle, nrows, statement, (const void **)values);
1685 /** pvbselect: select using a prepared statement + binary args **/
1686 static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle,
1687 apr_dbd_results_t **res,
1688 apr_dbd_prepared_t *statement,
1689 int random, va_list args)
1691 const char **values;
1694 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1695 for (i = 0; i < statement->nvals; i++)
1696 values[i] = va_arg(args, const char *);
1697 return odbc_pbselect(pool, handle, res, statement, random, (const void **)values);
1700 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1708 odbc_start_transaction,
1709 odbc_end_transaction,
1724 odbc_transaction_mode_get,
1725 odbc_transaction_mode_set,