Document PostgreSQL row counting fix.
[apr-util.git] / dbd / apr_dbd_odbc.c
blob9f0e17038e50c61662cca054c74df4840fee76ea
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "apu.h"
18 #if APU_HAVE_ODBC
20 #include "apr.h"
21 #include "apr_strings.h"
22 #include "apr_buckets.h"
23 #include "apr_env.h"
24 #include "apr_file_io.h"
25 #include "apr_file_info.h"
26 #include "apr_dbd_internal.h"
27 #include "apr_thread_proc.h"
28 #include "apu_version.h"
29 #include "apu_config.h"
31 #include <stdlib.h>
33 /* If library is ODBC-V2, use macros for limited ODBC-V2 support
34 * No random access in V2.
36 #ifdef ODBCV2
37 #define ODBCVER 0x0200
38 #include "apr_dbd_odbc_v2.h"
39 #endif
41 /* standard ODBC include files */
42 #ifdef HAVE_SQL_H
43 #include <sql.h>
44 #include <sqlext.h>
45 #elif defined(HAVE_ODBC_SQL_H)
46 #include <odbc/sql.h>
47 #include <odbc/sqlext.h>
48 #endif
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
56 #endif
57 #define STRINGIFY(x) #x
58 #define NAMIFY2(n) apr_dbd_##n##_driver
59 #define NAMIFY1(n) NAMIFY2(n)
60 #define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
61 #define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
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
68 static SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */
70 /* Use a CHECK_ERROR macro so we can grab the source line numbers
71 * for error reports */
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 */
81 #define MAX_PARAMS 20
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 */
91 #define IS_LOB(t) (t == SQL_LONGVARCHAR \
92 || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
93 || t == -98 || t == -99)
94 /* These types are CLOBs
95 * - DB2 DRDA driver uses undefined type -98 for CLOB */
96 #define IS_CLOB(t) \
97 (t == SQL_LONGVARCHAR || t == -98)
99 /* Convert a SQL result to an APR result */
100 #define APR_FROM_SQL_RESULT(rc) \
101 (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
103 /* DBD opaque structures */
104 struct apr_dbd_t
106 SQLHANDLE dbc; /* SQL connection handle - NULL after close */
107 apr_pool_t *pool; /* connection lifetime pool */
108 char *dbname; /* ODBC datasource */
109 int lasterrorcode;
110 int lineNumber;
111 char lastError[MAX_ERROR_STRING];
112 int defaultBufferSize; /* used for CLOBs in text mode,
113 * and when fld size is indeterminate */
114 int transaction_mode;
115 int dboptions; /* driver options re SQLGetData */
116 int default_transaction_mode;
117 int can_commit; /* controls end_trans behavior */
120 struct apr_dbd_results_t
122 SQLHANDLE stmt; /* parent sql statement handle */
123 SQLHANDLE dbc; /* parent sql connection handle */
124 apr_pool_t *pool; /* pool from query or select */
125 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
126 int random; /* random access requested */
127 int ncols; /* number of columns */
128 int isclosed; /* cursor has been closed */
129 char **colnames; /* array of column names (NULL until used) */
130 SQLPOINTER *colptrs; /* pointers to column data */
131 SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */
132 SQLINTEGER *coltextsizes; /* max-sizes if converted to text */
133 SQLSMALLINT *coltypes; /* array of SQL data types for columns */
134 SQLLEN *colinds; /* array of SQL data indicator/strlens */
135 int *colstate; /* array of column states
136 * - avail, bound, present, unavail
138 int *all_data_fetched; /* flags data as all fetched, for LOBs */
139 void *data; /* buffer for all data for one row */
141 enum /* results column states */
143 COL_AVAIL, /* data may be retrieved with SQLGetData */
144 COL_PRESENT, /* data has been retrieved with SQLGetData */
145 COL_BOUND, /* column is bound to colptr */
146 COL_RETRIEVED, /* all data from column has been returned */
147 COL_UNAVAIL /* column is unavailable because ODBC driver
148 * requires that columns be retrieved
149 * in ascending order and a higher col
150 * was accessed */
153 struct apr_dbd_row_t {
154 SQLHANDLE stmt; /* parent ODBC statement handle */
155 SQLHANDLE dbc; /* parent ODBC connection handle */
156 apr_pool_t *pool; /* pool from get_row */
157 apr_dbd_results_t *res;
160 struct apr_dbd_transaction_t {
161 SQLHANDLE dbc; /* parent ODBC connection handle */
162 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
165 struct apr_dbd_prepared_t {
166 SQLHANDLE stmt; /* ODBC statement handle */
167 SQLHANDLE dbc; /* parent ODBC connection handle */
168 apr_dbd_t *apr_dbd;
169 int nargs;
170 int nvals;
171 int *types; /* array of DBD data types */
175 static void odbc_lob_bucket_destroy(void *data);
176 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
177 static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
178 apr_size_t *len, apr_read_type_e block);
180 /* the ODBC LOB bucket type */
181 static const apr_bucket_type_t odbc_bucket_type = {
182 "ODBC_LOB", 5, APR_BUCKET_DATA,
183 odbc_lob_bucket_destroy,
184 odbc_lob_bucket_read,
185 odbc_lob_bucket_setaside,
186 apr_bucket_shared_split,
187 apr_bucket_shared_copy
191 /* ODBC LOB bucket data */
192 typedef struct {
193 /** Ref count for shared bucket */
194 apr_bucket_refcount refcount;
195 const apr_dbd_row_t *row;
196 int col;
197 SQLSMALLINT type;
198 } odbc_bucket;
201 /* SQL datatype mappings to DBD datatypes
202 * These tables must correspond *exactly* to the apr_dbd_type_e enum
203 * in apr_dbd_internal.h
206 /* ODBC "C" types to DBD datatypes */
207 static SQLSMALLINT const sqlCtype[] = {
208 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
209 SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
210 SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
211 SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */
212 SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */
213 SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */
214 SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */
215 SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */
216 SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */
217 SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
218 SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
219 SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
220 SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
221 SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */
222 SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
223 SQL_C_CHAR, /*SQL_C_TYPE_TIME, APR_DBD_TYPE_TIME, \%pDi */
224 SQL_C_CHAR, /*SQL_C_TYPE_DATE, APR_DBD_TYPE_DATE, \%pDd */
225 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
226 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
227 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
228 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
229 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
230 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
233 /* ODBC Base types to DBD datatypes */
234 static SQLSMALLINT const sqlBaseType[] = {
235 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
236 SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
237 SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
238 SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */
239 SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */
240 SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */
241 SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */
242 SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */
243 SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */
244 SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
245 SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
246 SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
247 SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
248 SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */
249 SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
250 SQL_CHAR, /*SQL_TIME, APR_DBD_TYPE_TIME, \%pDi */
251 SQL_CHAR, /*SQL_DATE, APR_DBD_TYPE_DATE, \%pDd */
252 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
253 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
254 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
255 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
256 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
257 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
260 /* result sizes for DBD datatypes (-1 for null-terminated) */
261 static int const sqlSizes[] = {
263 sizeof(char), /**< \%hhd out: char* */
264 sizeof(unsigned char), /**< \%hhu out: unsigned char* */
265 sizeof(short), /**< \%hd out: short* */
266 sizeof(unsigned short), /**< \%hu out: unsigned short* */
267 sizeof(int), /**< \%d out: int* */
268 sizeof(unsigned int), /**< \%u out: unsigned int* */
269 sizeof(long), /**< \%ld out: long* */
270 sizeof(unsigned long), /**< \%lu out: unsigned long* */
271 sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */
272 sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */
273 sizeof(float), /**< \%f out: float* */
274 sizeof(double), /**< \%lf out: double* */
275 -1, /**< \%s out: char** */
276 -1, /**< \%pDt out: char** */
277 -1, /**< \%pDi out: char** */
278 -1, /**< \%pDd out: char** */
279 -1, /**< \%pDa out: char** */
280 -1, /**< \%pDs out: char** */
281 -1, /**< \%pDz out: char** */
282 sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
283 sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
284 0 /**< \%pDn : in: void*, out: void** */
288 * local functions
291 /* close any open results for the connection */
292 static apr_status_t odbc_close_results(void *d)
293 { apr_dbd_results_t *dbr = (apr_dbd_results_t *) d;
294 SQLRETURN rc = SQL_SUCCESS;
296 if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) {
297 if (!dbr->isclosed)
298 rc = SQLCloseCursor(dbr->stmt);
299 dbr->isclosed = 1;
301 return APR_FROM_SQL_RESULT(rc);
304 /* close the ODBC statement handle from a prepare */
305 static apr_status_t odbc_close_pstmt(void *s)
307 SQLRETURN rc = APR_SUCCESS;
308 apr_dbd_prepared_t *statement = s;
309 /* stmt is closed if connection has already been closed */
310 if (statement) {
311 SQLHANDLE hstmt = statement->stmt;
313 if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
314 rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
316 statement->stmt = NULL;
318 return APR_FROM_SQL_RESULT(rc);
321 /* close: close/release a connection obtained from open() */
322 static apr_status_t odbc_close(apr_dbd_t *handle)
324 SQLRETURN rc = SQL_SUCCESS;
326 if (handle->dbc) {
327 rc = SQLDisconnect(handle->dbc);
328 CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
329 rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
330 CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
331 handle->dbc = NULL;
333 return APR_FROM_SQL_RESULT(rc);
336 /* odbc_close re-defined for passing to pool cleanup */
337 static apr_status_t odbc_close_cleanup(void *handle)
339 return odbc_close( (apr_dbd_t *) handle);
342 /* close the ODBC environment handle at process termination */
343 static apr_status_t odbc_close_env(SQLHANDLE henv)
345 SQLRETURN rc;
347 rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
348 henv = NULL;
349 return APR_FROM_SQL_RESULT(rc);
352 /* setup the arrays in results for all the returned columns */
353 static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t * res,
354 SQLHANDLE stmt)
356 SQLRETURN rc;
357 int maxsize, textsize, realsize, type, isunsigned = 1;
359 /* discover the sql type */
360 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
361 (SQLPOINTER) &isunsigned);
362 isunsigned = (isunsigned == SQL_TRUE);
364 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
365 (SQLPOINTER) &type);
366 if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE)
367 /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
368 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
369 0, NULL, (SQLPOINTER) &type);
370 if (!SQL_SUCCEEDED(rc))
371 /* if still unknown make it CHAR */
372 type = SQL_C_CHAR;
374 switch (type) {
375 case SQL_INTEGER:
376 case SQL_SMALLINT:
377 case SQL_TINYINT:
378 case SQL_BIGINT:
379 /* fix these numeric binary types up as signed/unsigned for C types */
380 type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
381 break;
382 /* LOB types are not changed to C types */
383 case SQL_LONGVARCHAR:
384 type = SQL_LONGVARCHAR;
385 break;
386 case SQL_LONGVARBINARY:
387 type = SQL_LONGVARBINARY;
388 break;
389 case SQL_FLOAT :
390 type = SQL_C_FLOAT;
391 break;
392 case SQL_DOUBLE :
393 type = SQL_C_DOUBLE;
394 break;
396 /* DBD wants times as strings */
397 case SQL_TIMESTAMP:
398 case SQL_DATE:
399 case SQL_TIME:
400 default:
401 type = SQL_C_CHAR;
404 res->coltypes[icol] = type;
406 /* size if retrieved as text */
407 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
408 NULL, (SQLPOINTER) & textsize);
409 if (!SQL_SUCCEEDED(rc) || textsize < 0)
410 textsize = res->apr_dbd->defaultBufferSize;
411 /* for null-term, which sometimes isn't included */
412 textsize++;
414 /* real size */
415 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
416 NULL, (SQLPOINTER) & realsize);
417 if (!SQL_SUCCEEDED(rc))
418 realsize = textsize;
420 maxsize = (textsize > realsize) ? textsize : realsize;
421 if ( IS_LOB(type) || maxsize <= 0) {
422 /* LOB types are never bound and have a NULL colptr for binary.
423 * Ingore their real (1-2gb) length & use a default - the larger
424 * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
425 * If not a LOB, but simply unknown length - always use defaultBufferSize.
427 maxsize = res->apr_dbd->defaultBufferSize;
428 if ( IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE )
429 maxsize = APR_BUCKET_BUFF_SIZE;
431 res->colptrs[icol] = NULL;
432 res->colstate[icol] = COL_AVAIL;
433 res->colsizes[icol] = maxsize;
434 rc = SQL_SUCCESS;
436 else {
437 res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
438 res->colsizes[icol] = maxsize;
439 if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
440 /* we are allowed to call SQLGetData if we need to */
441 rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
442 res->colptrs[icol], maxsize,
443 &(res->colinds[icol]) );
444 CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
445 stmt);
446 res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
448 else {
449 /* this driver won't allow us to call SQLGetData on bound
450 * columns - so don't bind any */
451 res->colstate[icol] = COL_AVAIL;
452 rc = SQL_SUCCESS;
455 return rc;
458 /* create and populate an apr_dbd_results_t for a select */
459 static SQLRETURN odbc_create_results(apr_dbd_t * handle, SQLHANDLE hstmt,
460 apr_pool_t * pool, const int random,
461 apr_dbd_results_t ** res)
463 SQLRETURN rc;
464 SQLSMALLINT ncols;
466 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
467 (*res)->stmt = hstmt;
468 (*res)->dbc = handle->dbc;
469 (*res)->pool = pool;
470 (*res)->random = random;
471 (*res)->apr_dbd = handle;
472 rc = SQLNumResultCols(hstmt, &ncols);
473 CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
474 (*res)->ncols = ncols;
476 if SQL_SUCCEEDED(rc) {
477 int i;
479 (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
480 (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
481 (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
482 (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
483 (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
484 (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
485 (*res)->ncols = ncols;
487 for (i = 0 ; i < ncols ; i++)
488 odbc_set_result_column(i, (*res), hstmt);
490 return rc;
494 /* bind a parameter - input params only, does not support output parameters */
495 static SQLRETURN odbc_bind_param(apr_pool_t * pool,
496 apr_dbd_prepared_t * statement, const int narg,
497 const SQLSMALLINT type, int *argp,
498 const void **args, const int textmode)
500 SQLRETURN rc;
501 SQLSMALLINT baseType, cType;
502 void *ptr;
503 SQLUINTEGER len;
504 SQLINTEGER *indicator;
505 static SQLINTEGER nullValue = SQL_NULL_DATA;
506 static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */
508 /* bind a NULL data value */
509 if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
510 baseType = SQL_CHAR;
511 cType = SQL_C_CHAR;
512 ptr = &nullValue;
513 len = sizeof(SQLINTEGER);
514 indicator = &nullValue;
515 (*argp)++;
517 /* bind a non-NULL data value */
518 else {
519 baseType = sqlBaseType[type];
520 cType = sqlCtype[type];
521 indicator = NULL;
522 /* LOBs */
523 if (IS_LOB(cType)) {
524 ptr = (void *) args[*argp];
525 len = (SQLUINTEGER) * (apr_size_t *) args[*argp + 1];
526 cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
527 (*argp) += 4; /* LOBs consume 4 args (last two are unused) */
529 /* non-LOBs */
530 else {
531 switch (baseType) {
532 case SQL_CHAR:
533 case SQL_DATE:
534 case SQL_TIME:
535 case SQL_TIMESTAMP:
536 ptr = (void *) args[*argp];
537 len = (SQLUINTEGER) strlen(ptr);
538 break;
539 case SQL_TINYINT:
540 ptr = apr_palloc(pool, sizeof(unsigned char));
541 len = sizeof(unsigned char);
542 *(unsigned char *) ptr =
543 (textmode ?
544 atoi(args[*argp]) : *(unsigned char *) args[*argp]);
545 break;
546 case SQL_SMALLINT:
547 ptr = apr_palloc(pool, sizeof(short));
548 len = sizeof(short);
549 *(short *) ptr =
550 (textmode ? atoi(args[*argp]) : *(short *) args[*argp]);
551 break;
552 case SQL_INTEGER:
553 ptr = apr_palloc(pool, sizeof(int));
554 len = sizeof(int);
555 *(long *) ptr =
556 (textmode ? atol(args[*argp]) : *(long *) args[*argp]);
557 break;
558 case SQL_FLOAT:
559 ptr = apr_palloc(pool, sizeof(float));
560 len = sizeof(float);
561 *(float *) ptr =
562 (textmode ?
563 (float) atof(args[*argp]) : *(float *) args[*argp]);
564 break;
565 case SQL_DOUBLE:
566 ptr = apr_palloc(pool, sizeof(double));
567 len = sizeof(double);
568 *(double *) ptr =
569 (textmode ? atof(args[*argp]) : *(double *)
570 args[*argp]);
571 break;
572 case SQL_BIGINT:
573 ptr = apr_palloc(pool, sizeof(apr_int64_t));
574 len = sizeof(apr_int64_t);
575 *(apr_int64_t *) ptr =
576 (textmode ?
577 apr_atoi64(args[*argp]) : *(apr_int64_t *) args[*argp]);
578 break;
579 default:
580 return APR_EGENERAL;
582 (*argp)++; /* non LOBs consume one argument */
585 rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
586 baseType, len, 0, ptr, len, indicator);
587 CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
588 statement->stmt);
589 return rc;
592 /* LOB / Bucket Brigade functions */
596 /* bucket type specific destroy */
597 static void odbc_lob_bucket_destroy(void *data)
599 odbc_bucket *bd = data;
601 if (apr_bucket_shared_destroy(bd))
602 apr_bucket_free(bd);
605 /* set aside a bucket if possible */
606 static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
608 odbc_bucket *bd = (odbc_bucket *) e->data;
610 /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
611 if (apr_pool_is_ancestor(bd->row->pool, pool))
612 return APR_SUCCESS;
614 return apr_bucket_setaside_notimpl(e, pool);
617 /* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
618 static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
619 apr_size_t *len, apr_read_type_e block)
621 SQLRETURN rc;
622 SQLINTEGER len_indicator;
623 SQLSMALLINT type;
624 odbc_bucket *bd = (odbc_bucket *) e->data;
625 apr_bucket *nxt;
626 void *buf;
627 int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
628 int eos;
630 /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
631 type = bd->row->res->coltypes[bd->col];
632 type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
634 /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE,
635 * but they may be much bigger per the BUFSIZE parameter.
637 if (bufsize < APR_BUCKET_BUFF_SIZE)
638 bufsize = APR_BUCKET_BUFF_SIZE;
640 buf = apr_bucket_alloc(bufsize, e->list);
641 *str = NULL;
642 *len = 0;
644 rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
645 type, buf, bufsize,
646 &len_indicator);
648 CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
649 SQL_HANDLE_STMT, bd->row->res->stmt);
651 if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
652 len_indicator = 0;
654 if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
656 if (rc == SQL_SUCCESS_WITH_INFO
657 && ( len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize) ) {
658 /* not the last read = a full buffer. CLOBs have a null terminator */
659 *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
661 eos = 0;
663 else {
664 /* the last read - len_indicator is supposed to be the length,
665 * but some driver get this wrong and return the total length.
666 * We try to handle both interpretations.
668 *len = (len_indicator > bufsize
669 && len_indicator >= (SQLINTEGER) e->start)
670 ? (len_indicator - (SQLINTEGER) e->start) : len_indicator;
672 eos = 1;
675 if (!eos) {
676 /* Create a new LOB bucket to append and append it */
677 nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
678 APR_BUCKET_INIT(nxt);
679 nxt->length = -1;
680 nxt->data = e->data;
681 nxt->type = &odbc_bucket_type;
682 nxt->free = apr_bucket_free;
683 nxt->list = e->list;
684 nxt->start = e->start + *len;
685 APR_BUCKET_INSERT_AFTER(e, nxt);
687 else {
688 odbc_lob_bucket_destroy(e->data);
690 /* make current bucket into a heap bucket */
691 apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
692 *str = buf;
694 /* No data is success in this context */
695 rc = SQL_SUCCESS;
697 return APR_FROM_SQL_RESULT(rc);
700 /* Create a bucket brigade on the row pool for a LOB column */
701 static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
702 SQLSMALLINT type, apr_bucket_brigade *bb)
704 apr_bucket_alloc_t *list = bb->bucket_alloc;
705 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
706 odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
707 apr_bucket *eos = apr_bucket_eos_create(list);
710 bd->row = row;
711 bd->col = col;
712 bd->type = type;
715 APR_BUCKET_INIT(b);
716 b->type = &odbc_bucket_type;
717 b->free = apr_bucket_free;
718 b->list = list;
719 /* LOB lengths are unknown in ODBC */
720 b = apr_bucket_shared_make(b, bd, 0, -1);
722 APR_BRIGADE_INSERT_TAIL(bb, b);
723 APR_BRIGADE_INSERT_TAIL(bb, eos);
725 return APR_SUCCESS;
728 /* returns a data pointer for a column, returns NULL for NULL value,
729 * return -1 if data not available */
730 static void *odbc_get(const apr_dbd_row_t *row, const int col,
731 const SQLSMALLINT sqltype)
733 SQLRETURN rc;
734 SQLINTEGER indicator;
735 int state = row->res->colstate[col];
736 int options = row->res->apr_dbd->dboptions;
738 switch (state) {
739 case (COL_UNAVAIL) : return (void *) -1;
740 case (COL_RETRIEVED) : return NULL;
742 case (COL_BOUND) :
743 case (COL_PRESENT) :
744 if (sqltype == row->res->coltypes[col]) {
745 /* same type and we already have the data */
746 row->res->colstate[col] = COL_RETRIEVED;
747 return (row->res->colinds[col] == SQL_NULL_DATA) ?
748 NULL : row->res->colptrs[col];
752 /* we need to get the data now */
753 if (!(options & SQL_GD_ANY_ORDER)) {
754 /* this ODBC driver requires columns to be retrieved in order,
755 * so we attempt to get every prior un-gotten non-LOB column */
756 int i;
757 for (i = 0; i < col; i++) {
758 if (row->res->colstate[i] == COL_AVAIL) {
759 if (IS_LOB(row->res->coltypes[i]))
760 row->res->colstate[i] = COL_UNAVAIL;
761 else {
762 odbc_get(row, i, row->res->coltypes[i]);
763 row->res->colstate[i] = COL_PRESENT;
769 if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
770 /* this driver won't let us re-get bound columns */
771 return (void *) -1;
773 /* a LOB might not have a buffer allocated yet - so create one */
774 if (!row->res->colptrs[col])
775 row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
777 rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
778 row->res->colsizes[col], &indicator);
779 CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
780 row->res->stmt);
781 if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
782 return NULL;
784 if (SQL_SUCCEEDED(rc)) {
785 /* whatever it was originally, it is now this sqltype */
786 row->res->coltypes[col] = sqltype;
787 /* this allows getting CLOBs in text mode by calling get_entry
788 * until it returns NULL */
789 row->res->colstate[col] =
790 (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
791 return row->res->colptrs[col];
793 else return (void *) -1;
796 /* Parse the parameter string for open */
797 static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
798 int *connect, SQLCHAR **datasource,
799 SQLCHAR **user, SQLCHAR **password,
800 int *defaultBufferSize, int *nattrs,
801 int **attrs, int **attrvals)
803 char *seps, *last, *name[MAX_PARAMS], *val[MAX_PARAMS];
804 int nparams=0, i, j;
806 *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
807 *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(int));
808 *nattrs = 0;
809 seps = DEFAULTSEPS;
810 name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
811 do {
812 if (last[strspn(last, seps)] == CSINGLEQUOTE) {
813 last += strspn(last, seps);
814 seps=SSINGLEQUOTE;
816 val[nparams] = apr_strtok(NULL, seps, &last);
817 seps = DEFAULTSEPS;
818 name[++nparams] = apr_strtok(NULL, seps, &last);
819 } while ( nparams <= MAX_PARAMS && name[nparams] != NULL);
821 for(j=i=0 ; i< nparams ; i++)
822 { if (!apr_strnatcasecmp(name[i], "CONNECT"))
823 { *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
824 *connect=1;
826 else if (!apr_strnatcasecmp(name[i], "DATASOURCE"))
827 { *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
828 *connect=0;
830 else if (!apr_strnatcasecmp(name[i], "USER"))
831 *user = (SQLCHAR *)apr_pstrdup(pool, val[i]);
832 else if (!apr_strnatcasecmp(name[i], "PASSWORD"))
833 *password = (SQLCHAR *)apr_pstrdup(pool, val[i]);
834 else if (!apr_strnatcasecmp(name[i], "BUFSIZE"))
835 *defaultBufferSize = atoi(val[i]);
836 else if (!apr_strnatcasecmp(name[i], "ACCESS"))
837 { if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
838 (*attrvals)[j] = SQL_MODE_READ_ONLY;
839 else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
840 (*attrvals)[j] = SQL_MODE_READ_WRITE;
841 else return SQL_ERROR;
842 (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
844 else if (!apr_strnatcasecmp(name[i], "CTIMEOUT"))
845 { (*attrvals)[j] = atoi(val[i]);
846 (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
848 else if (!apr_strnatcasecmp(name[i], "STIMEOUT"))
849 { (*attrvals)[j] = atoi(val[i]);
850 (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
852 else if (!apr_strnatcasecmp(name[i], "TXMODE"))
853 { if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
854 (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
855 else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
856 (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
857 else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
858 (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
859 else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
860 (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
861 else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
862 continue;
863 else return SQL_ERROR;
864 (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
866 else return SQL_ERROR;
868 *nattrs = j;
869 return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
872 /* common handling after ODBC calls - save error info (code and text) in dbc */
873 static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
874 SQLSMALLINT type, SQLHANDLE h, int line)
876 SQLCHAR buffer[512];
877 SQLCHAR sqlstate[128];
878 SQLINTEGER native;
879 SQLSMALLINT reslength;
880 char *res, *p, *end, *logval=NULL;
881 int i;
882 apr_status_t r;
884 /* set info about last error in dbc - fast return for SQL_SUCCESS */
885 if (rc == SQL_SUCCESS) {
886 char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
887 dbc->lasterrorcode = SQL_SUCCESS;
888 strcpy(dbc->lastError, successMsg);
889 strcpy(dbc->lastError+sizeof(successMsg)-1, step);
890 return;
892 switch (rc) {
893 case SQL_INVALID_HANDLE : { res = "SQL_INVALID_HANDLE"; break; }
894 case SQL_ERROR : { res = "SQL_ERROR"; break; }
895 case SQL_SUCCESS_WITH_INFO : { res = "SQL_SUCCESS_WITH_INFO"; break; }
896 case SQL_STILL_EXECUTING : { res = "SQL_STILL_EXECUTING"; break; }
897 case SQL_NEED_DATA : { res = "SQL_NEED_DATA"; break; }
898 case SQL_NO_DATA : { res = "SQL_NO_DATA"; break; }
899 default : { res = "unrecognized SQL return code"; }
901 /* these two returns are expected during normal execution */
902 if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA)
903 dbc->can_commit = 0;
904 p = dbc->lastError;
905 end = p + sizeof(dbc->lastError);
906 dbc->lasterrorcode = rc;
907 p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
908 step, res, rc, SOURCE_FILE, line-1);
909 for (i=1, rc=0 ; rc==0 ; i++) {
910 rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
911 sizeof(buffer), &reslength);
912 if (SQL_SUCCEEDED(rc) && (p < (end-280)))
913 p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
915 r = apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
916 /* if env var was set or call was init/open (no dbname) - log to stderr */
917 if (logval || !dbc->dbname ) {
918 char timestamp[APR_CTIME_LEN];
919 apr_file_t *se;
920 apr_ctime(timestamp, apr_time_now());
921 apr_file_open_stderr(&se, dbc->pool);
922 apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
927 * public functions per DBD driver API
930 /** init: allow driver to perform once-only initialisation. **/
931 static void odbc_init(apr_pool_t *pool)
933 SQLRETURN rc;
934 char *step;
935 apr_version_t apuver;
937 apu_version(&apuver);
938 if (apuver.major != DRIVER_APU_VERSION_MAJOR
939 || apuver.minor != DRIVER_APU_VERSION_MINOR) {
940 apr_file_t *se;
942 apr_file_open_stderr(&se, pool);
943 apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
944 "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
945 DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
946 apuver.major, apuver.minor);
947 abort();
950 if (henv)
951 return;
953 step = "SQLAllocHandle (SQL_HANDLE_ENV)";
954 rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
955 apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
956 if (SQL_SUCCEEDED(rc))
957 { step = "SQLSetEnvAttr";
958 rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
959 (SQLPOINTER) SQL_OV_ODBC3, 0);
961 else
962 { apr_dbd_t tmp_dbc;
963 SQLHANDLE err_h = henv;
965 tmp_dbc.pool = pool;
966 tmp_dbc.dbname = NULL;
967 CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
971 /** native_handle: return the native database handle of the underlying db **/
972 static void* odbc_native_handle(apr_dbd_t *handle)
973 { return handle->dbc;
976 /** open: obtain a database connection from the server rec. **/
978 /* It would be more efficient to allocate a single statement handle
979 here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
980 SQLPrepare, and we don't know whether random-access is
981 specified until SQLExecute so we cannot.
984 static apr_dbd_t* odbc_open(apr_pool_t *pool, const char *params, const char **error)
986 SQLRETURN rc;
987 SQLHANDLE hdbc = NULL;
988 apr_dbd_t *handle;
989 char *err_step;
990 int err_htype, i;
991 int defaultBufferSize=DEFAULT_BUFFER_SIZE;
992 SQLHANDLE err_h = NULL;
993 SQLCHAR *datasource=(SQLCHAR *)"", *user=(SQLCHAR *)"",
994 *password=(SQLCHAR *)"";
995 int nattrs=0, *attrs=NULL, *attrvals=NULL, connect=0;
997 err_step="SQLAllocHandle (SQL_HANDLE_DBC)";
998 err_htype = SQL_HANDLE_ENV;
999 err_h = henv;
1000 rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
1001 if (SQL_SUCCEEDED(rc)) {
1002 err_step="Invalid DBD Parameters - open";
1003 err_htype = SQL_HANDLE_DBC;
1004 err_h = hdbc;
1005 rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
1006 &password, &defaultBufferSize, &nattrs, &attrs,
1007 &attrvals);
1009 if (SQL_SUCCEEDED(rc)) {
1010 for (i=0 ; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1011 err_step="SQLSetConnectAttr (from DBD Parameters)";
1012 err_htype = SQL_HANDLE_DBC;
1013 err_h = hdbc;
1014 rc = SQLSetConnectAttr(hdbc, attrs[i], (void *) attrvals[i], 0);
1017 if (SQL_SUCCEEDED(rc)) {
1018 if (connect) {
1019 SQLCHAR out[1024];
1020 SQLSMALLINT outlen;
1021 err_step="SQLDriverConnect";
1022 err_htype = SQL_HANDLE_DBC;
1023 err_h = hdbc;
1024 rc = SQLDriverConnect(hdbc, NULL, datasource,
1025 (SQLSMALLINT) strlen((char *)datasource),
1026 out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1028 else {
1029 err_step="SQLConnect";
1030 err_htype = SQL_HANDLE_DBC;
1031 err_h = hdbc;
1032 rc = SQLConnect(hdbc, datasource,
1033 (SQLSMALLINT) strlen((char *)datasource),
1034 user, (SQLSMALLINT) strlen((char *)user),
1035 password, (SQLSMALLINT) strlen((char *)password));
1038 if (SQL_SUCCEEDED(rc)) {
1039 handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1040 handle->dbname = apr_pstrdup(pool, (char *)datasource);
1041 handle->dbc = hdbc;
1042 handle->pool = pool;
1043 handle->defaultBufferSize = defaultBufferSize;
1044 CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1045 handle->default_transaction_mode = 0;
1046 SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1047 &(handle->default_transaction_mode), sizeof(int), NULL);
1048 handle->transaction_mode = handle->default_transaction_mode;
1049 SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1050 sizeof(int), NULL);
1051 apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1052 return handle;
1054 else {
1055 apr_dbd_t tmp_dbc;
1056 tmp_dbc.pool = pool;
1057 tmp_dbc.dbname = NULL;
1058 CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1059 if (error)
1060 *error = apr_pstrdup(pool, tmp_dbc.lastError);
1061 if (hdbc)
1062 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1063 return NULL;
1067 /** check_conn: check status of a database connection **/
1068 static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1070 SQLUINTEGER isDead;
1071 SQLRETURN rc;
1073 rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1074 sizeof(SQLUINTEGER), NULL);
1075 CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1076 SQL_HANDLE_DBC, handle->dbc);
1077 /* if driver cannot check connection, say so */
1078 if (rc != SQL_SUCCESS)
1079 return APR_ENOTIMPL;
1081 return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1085 /** set_dbname: select database name. May be a no-op if not supported. **/
1086 static int odbc_set_dbname(apr_pool_t* pool, apr_dbd_t *handle,
1087 const char *name)
1089 if (apr_strnatcmp(name, handle->dbname)) {
1090 return APR_EGENERAL; /* It's illegal to change dbname in ODBC */
1092 CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1093 handle->dbc);
1094 return APR_SUCCESS; /* OK if it's the same name */
1097 /** transaction: start a transaction. May be a no-op. **/
1098 static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1099 apr_dbd_transaction_t **trans)
1101 SQLRETURN rc = SQL_SUCCESS;
1103 if (handle->transaction_mode) {
1104 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION, (void *)
1105 handle->transaction_mode, 0);
1106 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1107 SQL_HANDLE_DBC, handle->dbc);
1109 if SQL_SUCCEEDED(rc) {
1110 /* turn off autocommit for transactions */
1111 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1112 SQL_AUTOCOMMIT_OFF, 0);
1113 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1114 SQL_HANDLE_DBC, handle->dbc);
1116 if SQL_SUCCEEDED(rc) {
1117 *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1118 (*trans)->dbc = handle->dbc;
1119 (*trans)->apr_dbd = handle;
1120 handle->can_commit = 1;
1122 return APR_FROM_SQL_RESULT(rc);
1126 /** end_transaction: end a transaction **/
1127 static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1129 SQLRETURN rc;
1131 rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, SQL_COMMIT);
1132 CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1133 if SQL_SUCCEEDED(rc) {
1134 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1135 (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0);
1136 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1137 rc, SQL_HANDLE_DBC, trans->dbc);
1139 return APR_FROM_SQL_RESULT(rc);
1142 /** query: execute an SQL statement which doesn't return a result set **/
1143 static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1145 SQLRETURN rc;
1146 SQLHANDLE hstmt = NULL;
1147 size_t len = strlen(statement);
1149 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1150 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1151 handle->dbc);
1152 if (!SQL_SUCCEEDED(rc))
1153 return APR_FROM_SQL_RESULT(rc);
1155 rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
1156 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1158 if SQL_SUCCEEDED(rc) {
1159 rc = SQLRowCount(hstmt, (SQLINTEGER *) nrows);
1160 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1163 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1164 return APR_FROM_SQL_RESULT(rc);
1167 /** select: execute an SQL statement which returns a result set **/
1168 static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1169 apr_dbd_results_t **res, const char *statement,
1170 int random)
1172 SQLRETURN rc;
1173 SQLHANDLE hstmt;
1174 apr_dbd_prepared_t *stmt;
1175 size_t len = strlen(statement);
1177 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1178 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1179 handle->dbc);
1180 if (!SQL_SUCCEEDED(rc))
1181 return APR_FROM_SQL_RESULT(rc);
1182 /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1183 * is not a prepared statement. We want the same cleanup mechanism.
1185 stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1186 stmt->apr_dbd = handle;
1187 stmt->dbc = handle->dbc;
1188 stmt->stmt = hstmt;
1189 apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1190 if (random) {
1191 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1192 (SQLPOINTER) SQL_SCROLLABLE, 0);
1193 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1194 SQL_HANDLE_STMT, hstmt);
1196 if SQL_SUCCEEDED(rc) {
1197 rc = SQLExecDirect(hstmt, (SQLCHAR *) statement, (SQLINTEGER) len);
1198 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1200 if SQL_SUCCEEDED(rc) {
1201 rc = odbc_create_results(handle, hstmt, pool, random, res);
1202 apr_pool_cleanup_register(pool, *res,
1203 odbc_close_results, apr_pool_cleanup_null);
1205 return APR_FROM_SQL_RESULT(rc);
1208 /** num_cols: get the number of columns in a results set **/
1209 static int odbc_num_cols(apr_dbd_results_t *res)
1211 return res->ncols;
1214 /** num_tuples: get the number of rows in a results set **/
1215 static int odbc_num_tuples(apr_dbd_results_t *res)
1217 SQLRETURN rc;
1218 SQLINTEGER nrows;
1220 rc = SQLRowCount(res->stmt, &nrows);
1221 CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1222 return SQL_SUCCEEDED(rc) ? (int) nrows : -1;
1225 /** get_row: get a row from a result set **/
1226 static int odbc_get_row(apr_pool_t * pool, apr_dbd_results_t * res,
1227 apr_dbd_row_t ** row, int rownum)
1229 SQLRETURN rc;
1230 char *fetchtype;
1231 int c;
1233 *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1234 (*row)->stmt = res->stmt;
1235 (*row)->dbc = res->dbc;
1236 (*row)->res = res;
1237 (*row)->pool = res->pool;
1239 /* mark all the columns as needing SQLGetData unless they are bound */
1240 for (c = 0; c < res->ncols; c++) {
1241 if (res->colstate[c] != COL_BOUND)
1242 res->colstate[c] = COL_AVAIL;
1243 /* some drivers do not null-term zero-len CHAR data */
1244 if (res->colptrs[c] )
1245 * (char *) res->colptrs[c] = 0;
1248 if (res->random && (rownum > 0)) {
1249 fetchtype = "SQLFetchScroll";
1250 rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1252 else {
1253 fetchtype = "SQLFetch";
1254 rc = SQLFetch(res->stmt);
1256 CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1257 (*row)->stmt = res->stmt;
1258 if (!SQL_SUCCEEDED(rc) && !res->random) {
1259 /* early close on any error (usually SQL_NO_DATA) if fetching
1260 * sequentially to release resources ASAP */
1261 odbc_close_results(res);
1262 return -1;
1264 return SQL_SUCCEEDED(rc) ? 0 : -1;
1267 /** datum_get: get a binary entry from a row **/
1268 static apr_status_t odbc_datum_get(const apr_dbd_row_t * row, int col,
1269 apr_dbd_type_e dbdtype, void *data)
1271 SQLSMALLINT sqltype;
1272 void *p;
1273 int len = sqlSizes[dbdtype];
1275 if (col >= row->res->ncols)
1276 return APR_EGENERAL;
1278 if (dbdtype < 0 || dbdtype >= sizeof(sqlCtype)) {
1279 data = NULL; /* invalid type */
1280 return APR_EGENERAL;
1282 sqltype = sqlCtype[dbdtype];
1284 /* must not memcpy a brigade, sentinals are relative to orig loc */
1285 if (IS_LOB(sqltype))
1286 return odbc_create_bucket(row, col, sqltype, data);
1288 p = odbc_get(row, col, sqltype);
1289 if (p == (void *) -1)
1290 return APR_EGENERAL;
1292 if (p == NULL)
1293 return APR_ENOENT; /* SQL NULL value */
1295 if (len < 0)
1296 strcpy(data, p);
1297 else
1298 memcpy(data, p, len);
1300 return APR_SUCCESS;
1304 /** get_entry: get an entry from a row (string data) **/
1305 static const char *odbc_get_entry(const apr_dbd_row_t * row, int col)
1307 void *p;
1309 if (col >= row->res->ncols)
1310 return NULL;
1312 p = odbc_get(row, col, SQL_C_CHAR);
1314 /* NULL or invalid (-1) */
1315 if (p == NULL || p == (void *) -1)
1316 return p;
1317 else
1318 return apr_pstrdup(row->pool, p);
1321 /** error: get current error message (if any) **/
1322 static const char* odbc_error(apr_dbd_t *handle, int errnum)
1324 return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1327 /** escape: escape a string so it is safe for use in query/select **/
1328 static const char* odbc_escape(apr_pool_t *pool, const char *s,
1329 apr_dbd_t *handle)
1331 char *newstr, *src, *dst, *sq;
1332 int qcount;
1334 /* return the original if there are no single-quotes */
1335 if (!(sq = strchr(s, '\'')))
1336 return (char *) s;
1337 /* count the single-quotes and allocate a new buffer */
1338 for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
1339 qcount++;
1340 newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1342 /* move chars, doubling all single-quotes */
1343 src = (char *) s;
1344 for (dst = newstr ; *src ; src++) {
1345 if ((*dst++ = *src) == '\'')
1346 *dst++ = '\'';
1348 *dst = 0;
1349 return newstr;
1351 /** prepare: prepare a statement **/
1352 static int odbc_prepare(apr_pool_t * pool, apr_dbd_t * handle,
1353 const char *query, const char *label, int nargs,
1354 int nvals, apr_dbd_type_e * types,
1355 apr_dbd_prepared_t ** statement)
1357 SQLRETURN rc;
1358 size_t len = strlen(query);
1360 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1361 (*statement)->dbc = handle->dbc;
1362 (*statement)->apr_dbd = handle;
1363 (*statement)->nargs = nargs;
1364 (*statement)->nvals = nvals;
1365 (*statement)->types =
1366 apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1367 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1368 apr_pool_cleanup_register(pool, *statement,
1369 odbc_close_pstmt, apr_pool_cleanup_null);
1370 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1371 SQL_HANDLE_DBC, handle->dbc);
1372 rc = SQLPrepare((*statement)->stmt, (SQLCHAR *) query, (SQLINTEGER) len);
1373 CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1374 (*statement)->stmt);
1375 return APR_FROM_SQL_RESULT(rc);
1378 /** pquery: query using a prepared statement + args **/
1379 static int odbc_pquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1380 apr_dbd_prepared_t * statement, const char **args)
1382 SQLRETURN rc = SQL_SUCCESS;
1383 int i, argp;
1385 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1386 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1387 &argp, (const void **) args, TEXTMODE);
1389 if (SQL_SUCCEEDED(rc)) {
1390 rc = SQLExecute(statement->stmt);
1391 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1392 statement->stmt);
1394 if (SQL_SUCCEEDED(rc)) {
1395 rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
1396 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1397 statement->stmt);
1399 return APR_FROM_SQL_RESULT(rc);
1402 /** pvquery: query using a prepared statement + args **/
1403 static int odbc_pvquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1404 apr_dbd_prepared_t * statement, va_list args)
1406 const char **values;
1407 int i;
1408 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1409 for (i = 0; i < statement->nvals; i++)
1410 values[i] = va_arg(args, const char *);
1411 return odbc_pquery(pool, handle, nrows, statement, values);
1414 /** pselect: select using a prepared statement + args **/
1415 int odbc_pselect(apr_pool_t * pool, apr_dbd_t * handle,
1416 apr_dbd_results_t ** res, apr_dbd_prepared_t * statement,
1417 int random, const char **args)
1419 SQLRETURN rc = SQL_SUCCESS;
1420 int i, argp;
1422 if (random) {
1423 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1424 (SQLPOINTER) SQL_SCROLLABLE, 0);
1425 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1426 rc, SQL_HANDLE_STMT, statement->stmt);
1428 if (SQL_SUCCEEDED(rc)) {
1429 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1430 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1431 &argp, (const void **) args, TEXTMODE);
1433 if (SQL_SUCCEEDED(rc)) {
1434 rc = SQLExecute(statement->stmt);
1435 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1436 statement->stmt);
1438 if (SQL_SUCCEEDED(rc)) {
1439 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1440 apr_pool_cleanup_register(pool, *res,
1441 odbc_close_results, apr_pool_cleanup_null);
1443 return APR_FROM_SQL_RESULT(rc);
1446 /** pvselect: select using a prepared statement + args **/
1447 static int odbc_pvselect(apr_pool_t * pool, apr_dbd_t * handle,
1448 apr_dbd_results_t ** res,
1449 apr_dbd_prepared_t * statement, int random,
1450 va_list args)
1452 const char **values;
1453 int i;
1455 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1456 for (i = 0; i < statement->nvals; i++)
1457 values[i] = va_arg(args, const char *);
1458 return odbc_pselect(pool, handle, res, statement, random, values);
1461 /** get_name: get a column title from a result set **/
1462 static const char *odbc_get_name(const apr_dbd_results_t * res, int col)
1464 SQLRETURN rc;
1465 char buffer[MAX_COLUMN_NAME];
1466 SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1467 SQLUINTEGER colsize;
1469 if (col >= res->ncols)
1470 return NULL; /* bogus column number */
1471 if (res->colnames[col] != NULL)
1472 return res->colnames[col]; /* we already retrieved it */
1473 rc = SQLDescribeCol(res->stmt, col + 1,
1474 (SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
1475 &coltype, &colsize, &coldecimal, &colnullable);
1476 CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1477 SQL_HANDLE_STMT, res->stmt);
1478 res->colnames[col] = apr_pstrdup(res->pool, buffer);
1479 return res->colnames[col];
1482 /** transaction_mode_get: get the mode of transaction **/
1483 static int odbc_transaction_mode_get(apr_dbd_transaction_t * trans)
1485 return (int) trans->apr_dbd->transaction_mode;
1488 /** transaction_mode_set: set the mode of transaction **/
1489 static int odbc_transaction_mode_set(apr_dbd_transaction_t * trans, int mode)
1491 SQLRETURN rc;
1493 int legal = (SQL_TXN_READ_UNCOMMITTED | SQL_TXN_READ_COMMITTED
1494 | SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE);
1496 if ((mode & legal) != mode)
1497 return APR_EGENERAL;
1499 trans->apr_dbd->transaction_mode = mode;
1500 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_TXN_ISOLATION,
1501 (void *) mode, 0);
1502 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)",
1503 rc, SQL_HANDLE_DBC, trans->dbc);
1504 return APR_FROM_SQL_RESULT(rc);
1507 /** pbquery: query using a prepared statement + binary args **/
1508 static int odbc_pbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1509 apr_dbd_prepared_t * statement, const void **args)
1511 SQLRETURN rc = SQL_SUCCESS;
1512 int i, argp;
1514 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1515 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1516 &argp, args, BINARYMODE);
1518 if (SQL_SUCCEEDED(rc)) {
1519 rc = SQLExecute(statement->stmt);
1520 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1521 statement->stmt);
1523 if (SQL_SUCCEEDED(rc)) {
1524 rc = SQLRowCount(statement->stmt, (SQLINTEGER *) nrows);
1525 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1526 statement->stmt);
1528 return APR_FROM_SQL_RESULT(rc);
1531 /** pbselect: select using a prepared statement + binary args **/
1532 static int odbc_pbselect(apr_pool_t * pool, apr_dbd_t * handle,
1533 apr_dbd_results_t ** res,
1534 apr_dbd_prepared_t * statement,
1535 int random, const void **args)
1537 SQLRETURN rc = SQL_SUCCESS;
1538 int i, argp;
1540 if (random) {
1541 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1542 (SQLPOINTER) SQL_SCROLLABLE, 0);
1543 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1544 rc, SQL_HANDLE_STMT, statement->stmt);
1546 if (SQL_SUCCEEDED(rc)) {
1547 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1548 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1549 &argp, args, BINARYMODE);
1552 if (SQL_SUCCEEDED(rc)) {
1553 rc = SQLExecute(statement->stmt);
1554 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1555 statement->stmt);
1557 if (SQL_SUCCEEDED(rc)) {
1558 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1559 apr_pool_cleanup_register(pool, *res,
1560 odbc_close_results, apr_pool_cleanup_null);
1563 return APR_FROM_SQL_RESULT(rc);
1566 /** pvbquery: query using a prepared statement + binary args **/
1567 static int odbc_pvbquery(apr_pool_t * pool, apr_dbd_t * handle, int *nrows,
1568 apr_dbd_prepared_t * statement, va_list args)
1570 const char **values;
1571 int i;
1573 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1574 for (i = 0; i < statement->nvals; i++)
1575 values[i] = va_arg(args, const char *);
1576 return odbc_pbquery(pool, handle, nrows, statement, (const void **) values);
1579 /** pvbselect: select using a prepared statement + binary args **/
1580 static int odbc_pvbselect(apr_pool_t * pool, apr_dbd_t * handle,
1581 apr_dbd_results_t ** res,
1582 apr_dbd_prepared_t * statement,
1583 int random, va_list args)
1585 const char **values;
1586 int i;
1588 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1589 for (i = 0; i < statement->nvals; i++)
1590 values[i] = va_arg(args, const char *);
1591 return odbc_pbselect(pool, handle, res, statement, random, (const void **) values);
1594 APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1595 ODBC_DRIVER_STRING,
1596 odbc_init,
1597 odbc_native_handle,
1598 odbc_open,
1599 odbc_check_conn,
1600 odbc_close,
1601 odbc_set_dbname,
1602 odbc_start_transaction,
1603 odbc_end_transaction,
1604 odbc_query,
1605 odbc_select,
1606 odbc_num_cols,
1607 odbc_num_tuples,
1608 odbc_get_row,
1609 odbc_get_entry,
1610 odbc_error,
1611 odbc_escape,
1612 odbc_prepare,
1613 odbc_pvquery,
1614 odbc_pvselect,
1615 odbc_pquery,
1616 odbc_pselect,
1617 odbc_get_name,
1618 odbc_transaction_mode_get,
1619 odbc_transaction_mode_set,
1620 "?",
1621 odbc_pvbquery,
1622 odbc_pvbselect,
1623 odbc_pbquery,
1624 odbc_pbselect,
1625 odbc_datum_get
1628 #endif