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
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 */
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 */
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 */
106 SQLHANDLE dbc
; /* SQL connection handle - NULL after close */
107 apr_pool_t
*pool
; /* connection lifetime pool */
108 char *dbname
; /* ODBC datasource */
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
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 */
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 */
193 /** Ref count for shared bucket */
194 apr_bucket_refcount refcount
;
195 const apr_dbd_row_t
*row
;
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** */
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
) {
298 rc
= SQLCloseCursor(dbr
->stmt
);
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 */
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
;
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
);
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
)
347 rc
= SQLFreeHandle(SQL_HANDLE_ENV
, henv
);
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
,
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
,
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 */
379 /* fix these numeric binary types up as signed/unsigned for C types */
380 type
+= (isunsigned
) ? SQL_UNSIGNED_OFFSET
: SQL_SIGNED_OFFSET
;
382 /* LOB types are not changed to C types */
383 case SQL_LONGVARCHAR
:
384 type
= SQL_LONGVARCHAR
;
386 case SQL_LONGVARBINARY
:
387 type
= SQL_LONGVARBINARY
;
396 /* DBD wants times as strings */
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 */
415 rc
= SQLColAttribute(stmt
, icol
+ 1, SQL_DESC_OCTET_LENGTH
, NULL
, 0,
416 NULL
, (SQLPOINTER
) & realsize
);
417 if (!SQL_SUCCEEDED(rc
))
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
;
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
,
446 res
->colstate
[icol
] = SQL_SUCCEEDED(rc
) ? COL_BOUND
: COL_AVAIL
;
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
;
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
)
466 *res
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
467 (*res
)->stmt
= hstmt
;
468 (*res
)->dbc
= handle
->dbc
;
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
) {
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
);
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
)
501 SQLSMALLINT baseType
, cType
;
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
) {
513 len
= sizeof(SQLINTEGER
);
514 indicator
= &nullValue
;
517 /* bind a non-NULL data value */
519 baseType
= sqlBaseType
[type
];
520 cType
= sqlCtype
[type
];
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) */
536 ptr
= (void *) args
[*argp
];
537 len
= (SQLUINTEGER
) strlen(ptr
);
540 ptr
= apr_palloc(pool
, sizeof(unsigned char));
541 len
= sizeof(unsigned char);
542 *(unsigned char *) ptr
=
544 atoi(args
[*argp
]) : *(unsigned char *) args
[*argp
]);
547 ptr
= apr_palloc(pool
, sizeof(short));
550 (textmode
? atoi(args
[*argp
]) : *(short *) args
[*argp
]);
553 ptr
= apr_palloc(pool
, sizeof(int));
556 (textmode
? atol(args
[*argp
]) : *(long *) args
[*argp
]);
559 ptr
= apr_palloc(pool
, sizeof(float));
563 (float) atof(args
[*argp
]) : *(float *) args
[*argp
]);
566 ptr
= apr_palloc(pool
, sizeof(double));
567 len
= sizeof(double);
569 (textmode
? atof(args
[*argp
]) : *(double *)
573 ptr
= apr_palloc(pool
, sizeof(apr_int64_t
));
574 len
= sizeof(apr_int64_t
);
575 *(apr_int64_t
*) ptr
=
577 apr_atoi64(args
[*argp
]) : *(apr_int64_t
*) args
[*argp
]);
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
,
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
))
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
))
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
)
622 SQLINTEGER len_indicator
;
624 odbc_bucket
*bd
= (odbc_bucket
*) e
->data
;
627 int bufsize
= bd
->row
->res
->apr_dbd
->defaultBufferSize
;
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
);
644 rc
= SQLGetData(bd
->row
->res
->stmt
, bd
->col
+ 1,
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)
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 );
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
;
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
);
681 nxt
->type
= &odbc_bucket_type
;
682 nxt
->free
= apr_bucket_free
;
684 nxt
->start
= e
->start
+ *len
;
685 APR_BUCKET_INSERT_AFTER(e
, nxt
);
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
);
694 /* No data is success in this context */
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
);
716 b
->type
= &odbc_bucket_type
;
717 b
->free
= apr_bucket_free
;
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
);
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
)
734 SQLINTEGER indicator
;
735 int state
= row
->res
->colstate
[col
];
736 int options
= row
->res
->apr_dbd
->dboptions
;
739 case (COL_UNAVAIL
) : return (void *) -1;
740 case (COL_RETRIEVED
) : return NULL
;
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 */
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
;
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 */
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
,
781 if (indicator
== SQL_NULL_DATA
|| rc
== SQL_NO_DATA
)
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
];
806 *attrs
= apr_pcalloc(pool
, MAX_PARAMS
* sizeof(char *));
807 *attrvals
= apr_pcalloc(pool
, MAX_PARAMS
* sizeof(int));
810 name
[nparams
] = apr_strtok(apr_pstrdup(pool
, params
), seps
, &last
);
812 if (last
[strspn(last
, seps
)] == CSINGLEQUOTE
) {
813 last
+= strspn(last
, seps
);
816 val
[nparams
] = apr_strtok(NULL
, seps
, &last
);
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
]);
826 else if (!apr_strnatcasecmp(name
[i
], "DATASOURCE"))
827 { *datasource
= (SQLCHAR
*)apr_pstrdup(pool
, val
[i
]);
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"))
863 else return SQL_ERROR
;
864 (*attrs
)[j
++] = SQL_ATTR_TXN_ISOLATION
;
866 else return SQL_ERROR
;
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
)
877 SQLCHAR sqlstate
[128];
879 SQLSMALLINT reslength
;
880 char *res
, *p
, *end
, *logval
=NULL
;
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
);
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
)
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
];
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
)
935 apr_version_t apuver
;
937 apu_version(&apuver
);
938 if (apuver
.major
!= DRIVER_APU_VERSION_MAJOR
939 || apuver
.minor
!= DRIVER_APU_VERSION_MINOR
) {
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
);
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);
963 SQLHANDLE err_h
= henv
;
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
)
987 SQLHANDLE hdbc
= NULL
;
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
;
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
;
1005 rc
= odbc_parse_params(pool
, params
, &connect
, &datasource
, &user
,
1006 &password
, &defaultBufferSize
, &nattrs
, &attrs
,
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
;
1014 rc
= SQLSetConnectAttr(hdbc
, attrs
[i
], (void *) attrvals
[i
], 0);
1017 if (SQL_SUCCEEDED(rc
)) {
1021 err_step
="SQLDriverConnect";
1022 err_htype
= SQL_HANDLE_DBC
;
1024 rc
= SQLDriverConnect(hdbc
, NULL
, datasource
,
1025 (SQLSMALLINT
) strlen((char *)datasource
),
1026 out
, sizeof(out
), &outlen
, SQL_DRIVER_NOPROMPT
);
1029 err_step
="SQLConnect";
1030 err_htype
= SQL_HANDLE_DBC
;
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
);
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
),
1051 apr_pool_cleanup_register(pool
, handle
, odbc_close_cleanup
, apr_pool_cleanup_null
);
1056 tmp_dbc
.pool
= pool
;
1057 tmp_dbc
.dbname
= NULL
;
1058 CHECK_ERROR(&tmp_dbc
, err_step
, rc
, err_htype
, err_h
);
1060 *error
= apr_pstrdup(pool
, tmp_dbc
.lastError
);
1062 SQLFreeHandle(SQL_HANDLE_DBC
, hdbc
);
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
)
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
,
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
,
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
)
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
)
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
,
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
,
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
,
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
;
1189 apr_pool_cleanup_register(pool
, stmt
, odbc_close_pstmt
, apr_pool_cleanup_null
);
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
)
1214 /** num_tuples: get the number of rows in a results set **/
1215 static int odbc_num_tuples(apr_dbd_results_t
*res
)
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
)
1233 *row
= apr_pcalloc(pool
, sizeof(apr_dbd_row_t
));
1234 (*row
)->stmt
= res
->stmt
;
1235 (*row
)->dbc
= res
->dbc
;
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
);
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
);
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
;
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
;
1293 return APR_ENOENT
; /* SQL NULL value */
1298 memcpy(data
, p
, len
);
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
)
1309 if (col
>= row
->res
->ncols
)
1312 p
= odbc_get(row
, col
, SQL_C_CHAR
);
1314 /* NULL or invalid (-1) */
1315 if (p
== NULL
|| p
== (void *) -1)
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
,
1331 char *newstr
, *src
, *dst
, *sq
;
1334 /* return the original if there are no single-quotes */
1335 if (!(sq
= strchr(s
, '\'')))
1337 /* count the single-quotes and allocate a new buffer */
1338 for (qcount
= 1; (sq
= strchr(sq
+ 1, '\'')); )
1340 newstr
= apr_palloc(pool
, strlen(s
) + qcount
+ 1);
1342 /* move chars, doubling all single-quotes */
1344 for (dst
= newstr
; *src
; src
++) {
1345 if ((*dst
++ = *src
) == '\'')
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
)
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
;
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
,
1394 if (SQL_SUCCEEDED(rc
)) {
1395 rc
= SQLRowCount(statement
->stmt
, (SQLINTEGER
*) nrows
);
1396 CHECK_ERROR(handle
, "SQLRowCount", rc
, SQL_HANDLE_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
;
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
;
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
,
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
,
1452 const char **values
;
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
)
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
)
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
,
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
;
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
,
1523 if (SQL_SUCCEEDED(rc
)) {
1524 rc
= SQLRowCount(statement
->stmt
, (SQLINTEGER
*) nrows
);
1525 CHECK_ERROR(handle
, "SQLRowCount", rc
, SQL_HANDLE_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
;
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
,
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
;
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
;
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
= {
1602 odbc_start_transaction
,
1603 odbc_end_transaction
,
1618 odbc_transaction_mode_get
,
1619 odbc_transaction_mode_set
,