Deleted Added
full compact
apr_dbd_odbc.c (258602) apr_dbd_odbc.c (272076)
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include "apu.h"
18#if APU_HAVE_ODBC
19
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"
30
31#include <stdlib.h>
32
33/* If library is ODBC-V2, use macros for limited ODBC-V2 support
34 * No random access in V2.
35 */
36#ifdef ODBCV2
37#define ODBCVER 0x0200
38#include "apr_dbd_odbc_v2.h"
39#endif
40
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
49
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include "apu.h"
18#if APU_HAVE_ODBC
19
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"
30
31#include <stdlib.h>
32
33/* If library is ODBC-V2, use macros for limited ODBC-V2 support
34 * No random access in V2.
35 */
36#ifdef ODBCV2
37#define ODBCVER 0x0200
38#include "apr_dbd_odbc_v2.h"
39#endif
40
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
49
50/*
51* MSVC6 does not support intptr_t (C99)
52* APR does not have a signed inptr type until 2.0 (r1557720)
53*/
54#if defined(_MSC_VER) && _MSC_VER < 1400
55#if APR_SIZEOF_VOIDP == 8
56#define ODBC_INTPTR_T apr_int64_t
57#else
58#define ODBC_INTPTR_T apr_int32_t
59#endif
60#else
61#define ODBC_INTPTR_T intptr_t
62#endif
63
64
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.
53 */
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)
62
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
66
67static SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */
68
69/* Use a CHECK_ERROR macro so we can grab the source line numbers
70 * for error reports
71 */
72static 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__)
75
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 */
80
81#define MAX_PARAMS 20
82#define DEFAULTSEPS " \t\r\n,="
83#define CSINGLEQUOTE '\''
84#define SSINGLEQUOTE "\'"
85
86#define TEXTMODE 1 /* used for text (APR 1.2) mode params */
87#define BINARYMODE 0 /* used for binary (APR 1.3+) mode params */
88
89/* Identify datatypes which are LOBs
90 * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB
91 */
92#define IS_LOB(t) (t == SQL_LONGVARCHAR \
93 || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
94 || t == -98 || t == -99)
95
96/* These types are CLOBs
97 * - DB2 DRDA driver uses undefined type -98 for CLOB
98 */
99#define IS_CLOB(t) \
100 (t == SQL_LONGVARCHAR || t == -98)
101
102/* Convert a SQL result to an APR result */
103#define APR_FROM_SQL_RESULT(rc) \
104 (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
105
106/* DBD opaque structures */
107struct apr_dbd_t
108{
109 SQLHANDLE dbc; /* SQL connection handle - NULL after close */
110 apr_pool_t *pool; /* connection lifetime pool */
111 char *dbname; /* ODBC datasource */
112 int lasterrorcode;
113 int lineNumber;
114 char lastError[MAX_ERROR_STRING];
115 int defaultBufferSize; /* used for CLOBs in text mode,
116 * and when fld size is indeterminate */
65/* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver'
66 * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which
67 * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql.
68 */
69#ifndef ODBC_DRIVER_NAME
70#define ODBC_DRIVER_NAME odbc
71#endif
72#define STRINGIFY(x) #x
73#define NAMIFY2(n) apr_dbd_##n##_driver
74#define NAMIFY1(n) NAMIFY2(n)
75#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME)
76#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME)
77
78/* Required APR version for this driver */
79#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION
80#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION
81
82static SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */
83
84/* Use a CHECK_ERROR macro so we can grab the source line numbers
85 * for error reports
86 */
87static void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc,
88 SQLSMALLINT type, SQLHANDLE h, int line);
89#define CHECK_ERROR(a,s,r,t,h) check_error(a,s,r,t,h, __LINE__)
90
91#define SOURCE_FILE __FILE__ /* source file for error messages */
92#define MAX_ERROR_STRING 1024 /* max length of message in dbc */
93#define MAX_COLUMN_NAME 256 /* longest column name recognized */
94#define DEFAULT_BUFFER_SIZE 1024 /* value for defaultBufferSize */
95
96#define MAX_PARAMS 20
97#define DEFAULTSEPS " \t\r\n,="
98#define CSINGLEQUOTE '\''
99#define SSINGLEQUOTE "\'"
100
101#define TEXTMODE 1 /* used for text (APR 1.2) mode params */
102#define BINARYMODE 0 /* used for binary (APR 1.3+) mode params */
103
104/* Identify datatypes which are LOBs
105 * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB
106 */
107#define IS_LOB(t) (t == SQL_LONGVARCHAR \
108 || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \
109 || t == -98 || t == -99)
110
111/* These types are CLOBs
112 * - DB2 DRDA driver uses undefined type -98 for CLOB
113 */
114#define IS_CLOB(t) \
115 (t == SQL_LONGVARCHAR || t == -98)
116
117/* Convert a SQL result to an APR result */
118#define APR_FROM_SQL_RESULT(rc) \
119 (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL)
120
121/* DBD opaque structures */
122struct apr_dbd_t
123{
124 SQLHANDLE dbc; /* SQL connection handle - NULL after close */
125 apr_pool_t *pool; /* connection lifetime pool */
126 char *dbname; /* ODBC datasource */
127 int lasterrorcode;
128 int lineNumber;
129 char lastError[MAX_ERROR_STRING];
130 int defaultBufferSize; /* used for CLOBs in text mode,
131 * and when fld size is indeterminate */
117 intptr_t transaction_mode;
118 intptr_t dboptions; /* driver options re SQLGetData */
119 intptr_t default_transaction_mode;
132 ODBC_INTPTR_T transaction_mode;
133 ODBC_INTPTR_T dboptions; /* driver options re SQLGetData */
134 ODBC_INTPTR_T default_transaction_mode;
120 int can_commit; /* controls end_trans behavior */
121};
122
123struct apr_dbd_results_t
124{
125 SQLHANDLE stmt; /* parent sql statement handle */
126 SQLHANDLE dbc; /* parent sql connection handle */
127 apr_pool_t *pool; /* pool from query or select */
128 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
129 int random; /* random access requested */
130 int ncols; /* number of columns */
131 int isclosed; /* cursor has been closed */
132 char **colnames; /* array of column names (NULL until used) */
133 SQLPOINTER *colptrs; /* pointers to column data */
134 SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */
135 SQLINTEGER *coltextsizes; /* max-sizes if converted to text */
136 SQLSMALLINT *coltypes; /* array of SQL data types for columns */
137 SQLLEN *colinds; /* array of SQL data indicator/strlens */
138 int *colstate; /* array of column states
139 * - avail, bound, present, unavail
140 */
141 int *all_data_fetched; /* flags data as all fetched, for LOBs */
142 void *data; /* buffer for all data for one row */
143};
144
145enum /* results column states */
146{
147 COL_AVAIL, /* data may be retrieved with SQLGetData */
148 COL_PRESENT, /* data has been retrieved with SQLGetData */
149 COL_BOUND, /* column is bound to colptr */
150 COL_RETRIEVED, /* all data from column has been returned */
151 COL_UNAVAIL /* column is unavailable because ODBC driver
152 * requires that columns be retrieved
153 * in ascending order and a higher col
154 * was accessed
155 */
156};
157
158struct apr_dbd_row_t {
159 SQLHANDLE stmt; /* parent ODBC statement handle */
160 SQLHANDLE dbc; /* parent ODBC connection handle */
161 apr_pool_t *pool; /* pool from get_row */
162 apr_dbd_results_t *res;
163};
164
165struct apr_dbd_transaction_t {
166 SQLHANDLE dbc; /* parent ODBC connection handle */
167 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
168};
169
170struct apr_dbd_prepared_t {
171 SQLHANDLE stmt; /* ODBC statement handle */
172 SQLHANDLE dbc; /* parent ODBC connection handle */
173 apr_dbd_t *apr_dbd;
174 int nargs;
175 int nvals;
176 int *types; /* array of DBD data types */
177};
178
179static void odbc_lob_bucket_destroy(void *data);
180static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
181static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
182 apr_size_t *len, apr_read_type_e block);
183
184/* the ODBC LOB bucket type */
185static const apr_bucket_type_t odbc_bucket_type = {
186 "ODBC_LOB", 5, APR_BUCKET_DATA,
187 odbc_lob_bucket_destroy,
188 odbc_lob_bucket_read,
189 odbc_lob_bucket_setaside,
190 apr_bucket_shared_split,
191 apr_bucket_shared_copy
192};
193
194/* ODBC LOB bucket data */
195typedef struct {
196 /** Ref count for shared bucket */
197 apr_bucket_refcount refcount;
198 const apr_dbd_row_t *row;
199 int col;
200 SQLSMALLINT type;
201} odbc_bucket;
202
203/* SQL datatype mappings to DBD datatypes
204 * These tables must correspond *exactly* to the apr_dbd_type_e enum
205 * in apr_dbd.h
206 */
207
208/* ODBC "C" types to DBD datatypes */
209static SQLSMALLINT const sqlCtype[] = {
210 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
211 SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
212 SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
213 SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */
214 SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */
215 SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */
216 SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */
217 SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */
218 SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */
219 SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
220 SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
221 SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
222 SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
223 SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */
224 SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
225 SQL_C_CHAR, /*SQL_C_TYPE_TIME, APR_DBD_TYPE_TIME, \%pDi */
226 SQL_C_CHAR, /*SQL_C_TYPE_DATE, APR_DBD_TYPE_DATE, \%pDd */
227 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
228 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
229 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
230 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
231 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
232 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
233};
234#define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0]))
235
236/* ODBC Base types to DBD datatypes */
237static SQLSMALLINT const sqlBaseType[] = {
238 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
239 SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
240 SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
241 SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */
242 SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */
243 SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */
244 SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */
245 SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */
246 SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */
247 SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
248 SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
249 SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
250 SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
251 SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */
252 SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
253 SQL_CHAR, /*SQL_TIME, APR_DBD_TYPE_TIME, \%pDi */
254 SQL_CHAR, /*SQL_DATE, APR_DBD_TYPE_DATE, \%pDd */
255 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
256 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
257 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
258 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
259 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
260 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
261};
262
263/* result sizes for DBD datatypes (-1 for null-terminated) */
264static int const sqlSizes[] = {
265 0,
266 sizeof(char), /**< \%hhd out: char* */
267 sizeof(unsigned char), /**< \%hhu out: unsigned char* */
268 sizeof(short), /**< \%hd out: short* */
269 sizeof(unsigned short), /**< \%hu out: unsigned short* */
270 sizeof(int), /**< \%d out: int* */
271 sizeof(unsigned int), /**< \%u out: unsigned int* */
272 sizeof(long), /**< \%ld out: long* */
273 sizeof(unsigned long), /**< \%lu out: unsigned long* */
274 sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */
275 sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */
276 sizeof(float), /**< \%f out: float* */
277 sizeof(double), /**< \%lf out: double* */
278 -1, /**< \%s out: char** */
279 -1, /**< \%pDt out: char** */
280 -1, /**< \%pDi out: char** */
281 -1, /**< \%pDd out: char** */
282 -1, /**< \%pDa out: char** */
283 -1, /**< \%pDs out: char** */
284 -1, /**< \%pDz out: char** */
285 sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
286 sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
287 0 /**< \%pDn : in: void*, out: void** */
288};
289
290/*
291 * local functions
292 */
293
294/* close any open results for the connection */
295static apr_status_t odbc_close_results(void *d)
296{
297 apr_dbd_results_t *dbr = (apr_dbd_results_t *)d;
298 SQLRETURN rc = SQL_SUCCESS;
299
300 if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) {
301 if (!dbr->isclosed)
302 rc = SQLCloseCursor(dbr->stmt);
303 dbr->isclosed = 1;
304 }
305 return APR_FROM_SQL_RESULT(rc);
306}
307
308/* close the ODBC statement handle from a prepare */
309static apr_status_t odbc_close_pstmt(void *s)
310{
311 SQLRETURN rc = APR_SUCCESS;
312 apr_dbd_prepared_t *statement = s;
313
314 /* stmt is closed if connection has already been closed */
315 if (statement) {
316 SQLHANDLE hstmt = statement->stmt;
317
318 if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
319 rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
320 }
321 statement->stmt = NULL;
322 }
323 return APR_FROM_SQL_RESULT(rc);
324}
325
326/* close: close/release a connection obtained from open() */
327static apr_status_t odbc_close(apr_dbd_t *handle)
328{
329 SQLRETURN rc = SQL_SUCCESS;
330
331 if (handle->dbc) {
332 rc = SQLDisconnect(handle->dbc);
333 CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
334 rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
335 CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
336 handle->dbc = NULL;
337 }
338 return APR_FROM_SQL_RESULT(rc);
339}
340
341/* odbc_close re-defined for passing to pool cleanup */
342static apr_status_t odbc_close_cleanup(void *handle)
343{
344 return odbc_close((apr_dbd_t *)handle);
345}
346
347/* close the ODBC environment handle at process termination */
348static apr_status_t odbc_close_env(SQLHANDLE henv)
349{
350 SQLRETURN rc;
351
352 rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
353 henv = NULL;
354 return APR_FROM_SQL_RESULT(rc);
355}
356
357/* setup the arrays in results for all the returned columns */
358static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t *res,
359 SQLHANDLE stmt)
360{
361 SQLRETURN rc;
135 int can_commit; /* controls end_trans behavior */
136};
137
138struct apr_dbd_results_t
139{
140 SQLHANDLE stmt; /* parent sql statement handle */
141 SQLHANDLE dbc; /* parent sql connection handle */
142 apr_pool_t *pool; /* pool from query or select */
143 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
144 int random; /* random access requested */
145 int ncols; /* number of columns */
146 int isclosed; /* cursor has been closed */
147 char **colnames; /* array of column names (NULL until used) */
148 SQLPOINTER *colptrs; /* pointers to column data */
149 SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */
150 SQLINTEGER *coltextsizes; /* max-sizes if converted to text */
151 SQLSMALLINT *coltypes; /* array of SQL data types for columns */
152 SQLLEN *colinds; /* array of SQL data indicator/strlens */
153 int *colstate; /* array of column states
154 * - avail, bound, present, unavail
155 */
156 int *all_data_fetched; /* flags data as all fetched, for LOBs */
157 void *data; /* buffer for all data for one row */
158};
159
160enum /* results column states */
161{
162 COL_AVAIL, /* data may be retrieved with SQLGetData */
163 COL_PRESENT, /* data has been retrieved with SQLGetData */
164 COL_BOUND, /* column is bound to colptr */
165 COL_RETRIEVED, /* all data from column has been returned */
166 COL_UNAVAIL /* column is unavailable because ODBC driver
167 * requires that columns be retrieved
168 * in ascending order and a higher col
169 * was accessed
170 */
171};
172
173struct apr_dbd_row_t {
174 SQLHANDLE stmt; /* parent ODBC statement handle */
175 SQLHANDLE dbc; /* parent ODBC connection handle */
176 apr_pool_t *pool; /* pool from get_row */
177 apr_dbd_results_t *res;
178};
179
180struct apr_dbd_transaction_t {
181 SQLHANDLE dbc; /* parent ODBC connection handle */
182 apr_dbd_t *apr_dbd; /* parent DBD connection handle */
183};
184
185struct apr_dbd_prepared_t {
186 SQLHANDLE stmt; /* ODBC statement handle */
187 SQLHANDLE dbc; /* parent ODBC connection handle */
188 apr_dbd_t *apr_dbd;
189 int nargs;
190 int nvals;
191 int *types; /* array of DBD data types */
192};
193
194static void odbc_lob_bucket_destroy(void *data);
195static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool);
196static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
197 apr_size_t *len, apr_read_type_e block);
198
199/* the ODBC LOB bucket type */
200static const apr_bucket_type_t odbc_bucket_type = {
201 "ODBC_LOB", 5, APR_BUCKET_DATA,
202 odbc_lob_bucket_destroy,
203 odbc_lob_bucket_read,
204 odbc_lob_bucket_setaside,
205 apr_bucket_shared_split,
206 apr_bucket_shared_copy
207};
208
209/* ODBC LOB bucket data */
210typedef struct {
211 /** Ref count for shared bucket */
212 apr_bucket_refcount refcount;
213 const apr_dbd_row_t *row;
214 int col;
215 SQLSMALLINT type;
216} odbc_bucket;
217
218/* SQL datatype mappings to DBD datatypes
219 * These tables must correspond *exactly* to the apr_dbd_type_e enum
220 * in apr_dbd.h
221 */
222
223/* ODBC "C" types to DBD datatypes */
224static SQLSMALLINT const sqlCtype[] = {
225 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
226 SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
227 SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
228 SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */
229 SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */
230 SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */
231 SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */
232 SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */
233 SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */
234 SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
235 SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
236 SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
237 SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
238 SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */
239 SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
240 SQL_C_CHAR, /*SQL_C_TYPE_TIME, APR_DBD_TYPE_TIME, \%pDi */
241 SQL_C_CHAR, /*SQL_C_TYPE_DATE, APR_DBD_TYPE_DATE, \%pDd */
242 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
243 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
244 SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
245 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
246 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
247 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
248};
249#define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0]))
250
251/* ODBC Base types to DBD datatypes */
252static SQLSMALLINT const sqlBaseType[] = {
253 SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */
254 SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */
255 SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */
256 SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */
257 SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */
258 SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */
259 SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */
260 SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */
261 SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */
262 SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */
263 SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */
264 SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */
265 SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */
266 SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */
267 SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */
268 SQL_CHAR, /*SQL_TIME, APR_DBD_TYPE_TIME, \%pDi */
269 SQL_CHAR, /*SQL_DATE, APR_DBD_TYPE_DATE, \%pDd */
270 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */
271 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */
272 SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */
273 SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */
274 SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */
275 SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */
276};
277
278/* result sizes for DBD datatypes (-1 for null-terminated) */
279static int const sqlSizes[] = {
280 0,
281 sizeof(char), /**< \%hhd out: char* */
282 sizeof(unsigned char), /**< \%hhu out: unsigned char* */
283 sizeof(short), /**< \%hd out: short* */
284 sizeof(unsigned short), /**< \%hu out: unsigned short* */
285 sizeof(int), /**< \%d out: int* */
286 sizeof(unsigned int), /**< \%u out: unsigned int* */
287 sizeof(long), /**< \%ld out: long* */
288 sizeof(unsigned long), /**< \%lu out: unsigned long* */
289 sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */
290 sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */
291 sizeof(float), /**< \%f out: float* */
292 sizeof(double), /**< \%lf out: double* */
293 -1, /**< \%s out: char** */
294 -1, /**< \%pDt out: char** */
295 -1, /**< \%pDi out: char** */
296 -1, /**< \%pDd out: char** */
297 -1, /**< \%pDa out: char** */
298 -1, /**< \%pDs out: char** */
299 -1, /**< \%pDz out: char** */
300 sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */
301 sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */
302 0 /**< \%pDn : in: void*, out: void** */
303};
304
305/*
306 * local functions
307 */
308
309/* close any open results for the connection */
310static apr_status_t odbc_close_results(void *d)
311{
312 apr_dbd_results_t *dbr = (apr_dbd_results_t *)d;
313 SQLRETURN rc = SQL_SUCCESS;
314
315 if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) {
316 if (!dbr->isclosed)
317 rc = SQLCloseCursor(dbr->stmt);
318 dbr->isclosed = 1;
319 }
320 return APR_FROM_SQL_RESULT(rc);
321}
322
323/* close the ODBC statement handle from a prepare */
324static apr_status_t odbc_close_pstmt(void *s)
325{
326 SQLRETURN rc = APR_SUCCESS;
327 apr_dbd_prepared_t *statement = s;
328
329 /* stmt is closed if connection has already been closed */
330 if (statement) {
331 SQLHANDLE hstmt = statement->stmt;
332
333 if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) {
334 rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
335 }
336 statement->stmt = NULL;
337 }
338 return APR_FROM_SQL_RESULT(rc);
339}
340
341/* close: close/release a connection obtained from open() */
342static apr_status_t odbc_close(apr_dbd_t *handle)
343{
344 SQLRETURN rc = SQL_SUCCESS;
345
346 if (handle->dbc) {
347 rc = SQLDisconnect(handle->dbc);
348 CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc);
349 rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc);
350 CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv);
351 handle->dbc = NULL;
352 }
353 return APR_FROM_SQL_RESULT(rc);
354}
355
356/* odbc_close re-defined for passing to pool cleanup */
357static apr_status_t odbc_close_cleanup(void *handle)
358{
359 return odbc_close((apr_dbd_t *)handle);
360}
361
362/* close the ODBC environment handle at process termination */
363static apr_status_t odbc_close_env(SQLHANDLE henv)
364{
365 SQLRETURN rc;
366
367 rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
368 henv = NULL;
369 return APR_FROM_SQL_RESULT(rc);
370}
371
372/* setup the arrays in results for all the returned columns */
373static SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t *res,
374 SQLHANDLE stmt)
375{
376 SQLRETURN rc;
362 intptr_t maxsize, textsize, realsize, type, isunsigned = 1;
377 ODBC_INTPTR_T maxsize, textsize, realsize, type, isunsigned = 1;
363
364 /* discover the sql type */
365 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
366 (SQLPOINTER)&isunsigned);
367 isunsigned = (isunsigned == SQL_TRUE);
368
369 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
370 (SQLPOINTER)&type);
371 if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE) {
372 /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
373 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
374 0, NULL, (SQLPOINTER)&type);
375 }
376
377 if (!SQL_SUCCEEDED(rc)) {
378 /* if still unknown make it CHAR */
379 type = SQL_C_CHAR;
380 }
381
382 switch (type) {
383 case SQL_INTEGER:
384 case SQL_SMALLINT:
385 case SQL_TINYINT:
386 case SQL_BIGINT:
387 /* fix these numeric binary types up as signed/unsigned for C types */
388 type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
389 break;
390 /* LOB types are not changed to C types */
391 case SQL_LONGVARCHAR:
392 type = SQL_LONGVARCHAR;
393 break;
394 case SQL_LONGVARBINARY:
395 type = SQL_LONGVARBINARY;
396 break;
397 case SQL_FLOAT :
398 type = SQL_C_FLOAT;
399 break;
400 case SQL_DOUBLE :
401 type = SQL_C_DOUBLE;
402 break;
403
404 /* DBD wants times as strings */
405 case SQL_TIMESTAMP:
406 case SQL_DATE:
407 case SQL_TIME:
408 default:
409 type = SQL_C_CHAR;
410 }
411
412 res->coltypes[icol] = (SQLSMALLINT)type;
413
414 /* size if retrieved as text */
415 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
416 NULL, (SQLPOINTER)&textsize);
417 if (!SQL_SUCCEEDED(rc) || textsize < 0) {
418 textsize = res->apr_dbd->defaultBufferSize;
419 }
420 /* for null-term, which sometimes isn't included */
421 textsize++;
422
423 /* real size */
424 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
425 NULL, (SQLPOINTER)&realsize);
426 if (!SQL_SUCCEEDED(rc)) {
427 realsize = textsize;
428 }
429
430 maxsize = (textsize > realsize) ? textsize : realsize;
431 if (IS_LOB(type) || maxsize <= 0) {
432 /* LOB types are never bound and have a NULL colptr for binary.
433 * Ingore their real (1-2gb) length & use a default - the larger
434 * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
435 * If not a LOB, but simply unknown length - always use defaultBufferSize.
436 */
437 maxsize = res->apr_dbd->defaultBufferSize;
438 if (IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE) {
439 maxsize = APR_BUCKET_BUFF_SIZE;
440 }
441
442 res->colptrs[icol] = NULL;
443 res->colstate[icol] = COL_AVAIL;
444 res->colsizes[icol] = (SQLINTEGER)maxsize;
445 rc = SQL_SUCCESS;
446 }
447 else {
448 res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
449 res->colsizes[icol] = (SQLINTEGER)maxsize;
450 if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
451 /* we are allowed to call SQLGetData if we need to */
452 rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
453 res->colptrs[icol], maxsize,
454 &(res->colinds[icol]));
455 CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
456 stmt);
457 res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
458 }
459 else {
460 /* this driver won't allow us to call SQLGetData on bound
461 * columns - so don't bind any
462 */
463 res->colstate[icol] = COL_AVAIL;
464 rc = SQL_SUCCESS;
465 }
466 }
467 return rc;
468}
469
470/* create and populate an apr_dbd_results_t for a select */
471static SQLRETURN odbc_create_results(apr_dbd_t *handle, SQLHANDLE hstmt,
472 apr_pool_t *pool, const int random,
473 apr_dbd_results_t **res)
474{
475 SQLRETURN rc;
476 SQLSMALLINT ncols;
477
478 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
479 (*res)->stmt = hstmt;
480 (*res)->dbc = handle->dbc;
481 (*res)->pool = pool;
482 (*res)->random = random;
483 (*res)->apr_dbd = handle;
484 rc = SQLNumResultCols(hstmt, &ncols);
485 CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
486 (*res)->ncols = ncols;
487
488 if (SQL_SUCCEEDED(rc)) {
489 int i;
490
491 (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
492 (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
493 (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
494 (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
495 (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
496 (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
497 (*res)->ncols = ncols;
498
499 for (i = 0; i < ncols; i++) {
500 odbc_set_result_column(i, (*res), hstmt);
501 }
502 }
503 return rc;
504}
505
506
507/* bind a parameter - input params only, does not support output parameters */
508static SQLRETURN odbc_bind_param(apr_pool_t *pool,
509 apr_dbd_prepared_t *statement, const int narg,
510 const SQLSMALLINT type, int *argp,
511 const void **args, const int textmode)
512{
513 SQLRETURN rc;
514 SQLSMALLINT baseType, cType;
515 void *ptr;
516 SQLULEN len;
517 SQLLEN *indicator;
518 static SQLLEN nullValue = SQL_NULL_DATA;
519 static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */
520
521 /* bind a NULL data value */
522 if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
523 baseType = SQL_CHAR;
524 cType = SQL_C_CHAR;
525 ptr = &nullValue;
526 len = sizeof(SQLINTEGER);
527 indicator = &nullValue;
528 (*argp)++;
529 }
530 /* bind a non-NULL data value */
531 else {
532 if (type < 0 || type >= NUM_APR_DBD_TYPES) {
533 return APR_EGENERAL;
534 }
535
536 baseType = sqlBaseType[type];
537 cType = sqlCtype[type];
538 indicator = NULL;
539 /* LOBs */
540 if (IS_LOB(cType)) {
541 ptr = (void *)args[*argp];
542 len = (SQLULEN) * (apr_size_t *)args[*argp + 1];
543 cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
544 (*argp) += 4; /* LOBs consume 4 args (last two are unused) */
545 }
546 /* non-LOBs */
547 else {
548 switch (baseType) {
549 case SQL_CHAR:
550 case SQL_DATE:
551 case SQL_TIME:
552 case SQL_TIMESTAMP:
553 ptr = (void *)args[*argp];
554 len = (SQLULEN)strlen(ptr);
555 break;
556 case SQL_TINYINT:
557 ptr = apr_palloc(pool, sizeof(unsigned char));
558 len = sizeof(unsigned char);
559 *(unsigned char *)ptr =
560 (textmode ?
561 atoi(args[*argp]) : *(unsigned char *)args[*argp]);
562 break;
563 case SQL_SMALLINT:
564 ptr = apr_palloc(pool, sizeof(short));
565 len = sizeof(short);
566 *(short *)ptr =
567 (textmode ? atoi(args[*argp]) : *(short *)args[*argp]);
568 break;
569 case SQL_INTEGER:
570 ptr = apr_palloc(pool, sizeof(int));
571 len = sizeof(int);
572 *(long *)ptr =
573 (textmode ? atol(args[*argp]) : *(long *)args[*argp]);
574 break;
575 case SQL_FLOAT:
576 ptr = apr_palloc(pool, sizeof(float));
577 len = sizeof(float);
578 *(float *)ptr =
579 (textmode ?
580 (float)atof(args[*argp]) : *(float *)args[*argp]);
581 break;
582 case SQL_DOUBLE:
583 ptr = apr_palloc(pool, sizeof(double));
584 len = sizeof(double);
585 *(double *)ptr =
586 (textmode ? atof(args[*argp]) : *(double *)
587 args[*argp]);
588 break;
589 case SQL_BIGINT:
590 ptr = apr_palloc(pool, sizeof(apr_int64_t));
591 len = sizeof(apr_int64_t);
592 *(apr_int64_t *)ptr =
593 (textmode ?
594 apr_atoi64(args[*argp]) : *(apr_int64_t *)args[*argp]);
595 break;
596 default:
597 return APR_EGENERAL;
598 }
599 (*argp)++; /* non LOBs consume one argument */
600 }
601 }
602 rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
603 baseType, len, 0, ptr, len, indicator);
604 CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
605 statement->stmt);
606 return rc;
607}
608
609/* LOB / Bucket Brigade functions */
610
611/* bucket type specific destroy */
612static void odbc_lob_bucket_destroy(void *data)
613{
614 odbc_bucket *bd = data;
615
616 if (apr_bucket_shared_destroy(bd))
617 apr_bucket_free(bd);
618}
619
620/* set aside a bucket if possible */
621static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
622{
623 odbc_bucket *bd = (odbc_bucket *)e->data;
624
625 /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
626 if (apr_pool_is_ancestor(bd->row->pool, pool))
627 return APR_SUCCESS;
628
629 return apr_bucket_setaside_notimpl(e, pool);
630}
631
632/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
633static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
634 apr_size_t *len, apr_read_type_e block)
635{
636 SQLRETURN rc;
637 SQLLEN len_indicator;
638 SQLSMALLINT type;
639 odbc_bucket *bd = (odbc_bucket *)e->data;
640 apr_bucket *nxt;
641 void *buf;
642 int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
643 int eos;
644
645 /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
646 type = bd->row->res->coltypes[bd->col];
647 type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
648
649 /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE,
650 * but they may be much bigger per the BUFSIZE parameter.
651 */
652 if (bufsize < APR_BUCKET_BUFF_SIZE)
653 bufsize = APR_BUCKET_BUFF_SIZE;
654
655 buf = apr_bucket_alloc(bufsize, e->list);
656 *str = NULL;
657 *len = 0;
658
659 rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
660 type, buf, bufsize,
661 &len_indicator);
662
663 CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
664 SQL_HANDLE_STMT, bd->row->res->stmt);
665
666 if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
667 len_indicator = 0;
668
669 if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
670
671 if (rc == SQL_SUCCESS_WITH_INFO
672 && (len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize)) {
673 /* not the last read = a full buffer. CLOBs have a null terminator */
674 *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
675
676 eos = 0;
677 }
678 else {
679 /* the last read - len_indicator is supposed to be the length,
680 * but some driver get this wrong and return the total length.
681 * We try to handle both interpretations.
682 */
683 *len = (len_indicator > bufsize
684 && len_indicator >= (SQLLEN)e->start)
685 ? (len_indicator - (SQLLEN)e->start) : len_indicator;
686
687 eos = 1;
688 }
689
690 if (!eos) {
691 /* Create a new LOB bucket to append and append it */
692 nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
693 APR_BUCKET_INIT(nxt);
694 nxt->length = -1;
695 nxt->data = e->data;
696 nxt->type = &odbc_bucket_type;
697 nxt->free = apr_bucket_free;
698 nxt->list = e->list;
699 nxt->start = e->start + *len;
700 APR_BUCKET_INSERT_AFTER(e, nxt);
701 }
702 else {
703 odbc_lob_bucket_destroy(e->data);
704 }
705 /* make current bucket into a heap bucket */
706 apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
707 *str = buf;
708
709 /* No data is success in this context */
710 rc = SQL_SUCCESS;
711 }
712 return APR_FROM_SQL_RESULT(rc);
713}
714
715/* Create a bucket brigade on the row pool for a LOB column */
716static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
717 SQLSMALLINT type, apr_bucket_brigade *bb)
718{
719 apr_bucket_alloc_t *list = bb->bucket_alloc;
720 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
721 odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
722 apr_bucket *eos = apr_bucket_eos_create(list);
723
724 bd->row = row;
725 bd->col = col;
726 bd->type = type;
727
728 APR_BUCKET_INIT(b);
729 b->type = &odbc_bucket_type;
730 b->free = apr_bucket_free;
731 b->list = list;
732 /* LOB lengths are unknown in ODBC */
733 b = apr_bucket_shared_make(b, bd, 0, -1);
734
735 APR_BRIGADE_INSERT_TAIL(bb, b);
736 APR_BRIGADE_INSERT_TAIL(bb, eos);
737
738 return APR_SUCCESS;
739}
740
741/* returns a data pointer for a column, returns NULL for NULL value,
742 * return -1 if data not available
743 */
744static void *odbc_get(const apr_dbd_row_t *row, const int col,
745 const SQLSMALLINT sqltype)
746{
747 SQLRETURN rc;
748 SQLLEN indicator;
749 int state = row->res->colstate[col];
378
379 /* discover the sql type */
380 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL,
381 (SQLPOINTER)&isunsigned);
382 isunsigned = (isunsigned == SQL_TRUE);
383
384 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL,
385 (SQLPOINTER)&type);
386 if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE) {
387 /* MANY ODBC v2 datasources only supply CONCISE_TYPE */
388 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL,
389 0, NULL, (SQLPOINTER)&type);
390 }
391
392 if (!SQL_SUCCEEDED(rc)) {
393 /* if still unknown make it CHAR */
394 type = SQL_C_CHAR;
395 }
396
397 switch (type) {
398 case SQL_INTEGER:
399 case SQL_SMALLINT:
400 case SQL_TINYINT:
401 case SQL_BIGINT:
402 /* fix these numeric binary types up as signed/unsigned for C types */
403 type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET;
404 break;
405 /* LOB types are not changed to C types */
406 case SQL_LONGVARCHAR:
407 type = SQL_LONGVARCHAR;
408 break;
409 case SQL_LONGVARBINARY:
410 type = SQL_LONGVARBINARY;
411 break;
412 case SQL_FLOAT :
413 type = SQL_C_FLOAT;
414 break;
415 case SQL_DOUBLE :
416 type = SQL_C_DOUBLE;
417 break;
418
419 /* DBD wants times as strings */
420 case SQL_TIMESTAMP:
421 case SQL_DATE:
422 case SQL_TIME:
423 default:
424 type = SQL_C_CHAR;
425 }
426
427 res->coltypes[icol] = (SQLSMALLINT)type;
428
429 /* size if retrieved as text */
430 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0,
431 NULL, (SQLPOINTER)&textsize);
432 if (!SQL_SUCCEEDED(rc) || textsize < 0) {
433 textsize = res->apr_dbd->defaultBufferSize;
434 }
435 /* for null-term, which sometimes isn't included */
436 textsize++;
437
438 /* real size */
439 rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0,
440 NULL, (SQLPOINTER)&realsize);
441 if (!SQL_SUCCEEDED(rc)) {
442 realsize = textsize;
443 }
444
445 maxsize = (textsize > realsize) ? textsize : realsize;
446 if (IS_LOB(type) || maxsize <= 0) {
447 /* LOB types are never bound and have a NULL colptr for binary.
448 * Ingore their real (1-2gb) length & use a default - the larger
449 * of defaultBufferSize or APR_BUCKET_BUFF_SIZE.
450 * If not a LOB, but simply unknown length - always use defaultBufferSize.
451 */
452 maxsize = res->apr_dbd->defaultBufferSize;
453 if (IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE) {
454 maxsize = APR_BUCKET_BUFF_SIZE;
455 }
456
457 res->colptrs[icol] = NULL;
458 res->colstate[icol] = COL_AVAIL;
459 res->colsizes[icol] = (SQLINTEGER)maxsize;
460 rc = SQL_SUCCESS;
461 }
462 else {
463 res->colptrs[icol] = apr_pcalloc(res->pool, maxsize);
464 res->colsizes[icol] = (SQLINTEGER)maxsize;
465 if (res->apr_dbd->dboptions & SQL_GD_BOUND) {
466 /* we are allowed to call SQLGetData if we need to */
467 rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol],
468 res->colptrs[icol], maxsize,
469 &(res->colinds[icol]));
470 CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT,
471 stmt);
472 res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL;
473 }
474 else {
475 /* this driver won't allow us to call SQLGetData on bound
476 * columns - so don't bind any
477 */
478 res->colstate[icol] = COL_AVAIL;
479 rc = SQL_SUCCESS;
480 }
481 }
482 return rc;
483}
484
485/* create and populate an apr_dbd_results_t for a select */
486static SQLRETURN odbc_create_results(apr_dbd_t *handle, SQLHANDLE hstmt,
487 apr_pool_t *pool, const int random,
488 apr_dbd_results_t **res)
489{
490 SQLRETURN rc;
491 SQLSMALLINT ncols;
492
493 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
494 (*res)->stmt = hstmt;
495 (*res)->dbc = handle->dbc;
496 (*res)->pool = pool;
497 (*res)->random = random;
498 (*res)->apr_dbd = handle;
499 rc = SQLNumResultCols(hstmt, &ncols);
500 CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt);
501 (*res)->ncols = ncols;
502
503 if (SQL_SUCCEEDED(rc)) {
504 int i;
505
506 (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *));
507 (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *));
508 (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER));
509 (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT));
510 (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN));
511 (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int));
512 (*res)->ncols = ncols;
513
514 for (i = 0; i < ncols; i++) {
515 odbc_set_result_column(i, (*res), hstmt);
516 }
517 }
518 return rc;
519}
520
521
522/* bind a parameter - input params only, does not support output parameters */
523static SQLRETURN odbc_bind_param(apr_pool_t *pool,
524 apr_dbd_prepared_t *statement, const int narg,
525 const SQLSMALLINT type, int *argp,
526 const void **args, const int textmode)
527{
528 SQLRETURN rc;
529 SQLSMALLINT baseType, cType;
530 void *ptr;
531 SQLULEN len;
532 SQLLEN *indicator;
533 static SQLLEN nullValue = SQL_NULL_DATA;
534 static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */
535
536 /* bind a NULL data value */
537 if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) {
538 baseType = SQL_CHAR;
539 cType = SQL_C_CHAR;
540 ptr = &nullValue;
541 len = sizeof(SQLINTEGER);
542 indicator = &nullValue;
543 (*argp)++;
544 }
545 /* bind a non-NULL data value */
546 else {
547 if (type < 0 || type >= NUM_APR_DBD_TYPES) {
548 return APR_EGENERAL;
549 }
550
551 baseType = sqlBaseType[type];
552 cType = sqlCtype[type];
553 indicator = NULL;
554 /* LOBs */
555 if (IS_LOB(cType)) {
556 ptr = (void *)args[*argp];
557 len = (SQLULEN) * (apr_size_t *)args[*argp + 1];
558 cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT;
559 (*argp) += 4; /* LOBs consume 4 args (last two are unused) */
560 }
561 /* non-LOBs */
562 else {
563 switch (baseType) {
564 case SQL_CHAR:
565 case SQL_DATE:
566 case SQL_TIME:
567 case SQL_TIMESTAMP:
568 ptr = (void *)args[*argp];
569 len = (SQLULEN)strlen(ptr);
570 break;
571 case SQL_TINYINT:
572 ptr = apr_palloc(pool, sizeof(unsigned char));
573 len = sizeof(unsigned char);
574 *(unsigned char *)ptr =
575 (textmode ?
576 atoi(args[*argp]) : *(unsigned char *)args[*argp]);
577 break;
578 case SQL_SMALLINT:
579 ptr = apr_palloc(pool, sizeof(short));
580 len = sizeof(short);
581 *(short *)ptr =
582 (textmode ? atoi(args[*argp]) : *(short *)args[*argp]);
583 break;
584 case SQL_INTEGER:
585 ptr = apr_palloc(pool, sizeof(int));
586 len = sizeof(int);
587 *(long *)ptr =
588 (textmode ? atol(args[*argp]) : *(long *)args[*argp]);
589 break;
590 case SQL_FLOAT:
591 ptr = apr_palloc(pool, sizeof(float));
592 len = sizeof(float);
593 *(float *)ptr =
594 (textmode ?
595 (float)atof(args[*argp]) : *(float *)args[*argp]);
596 break;
597 case SQL_DOUBLE:
598 ptr = apr_palloc(pool, sizeof(double));
599 len = sizeof(double);
600 *(double *)ptr =
601 (textmode ? atof(args[*argp]) : *(double *)
602 args[*argp]);
603 break;
604 case SQL_BIGINT:
605 ptr = apr_palloc(pool, sizeof(apr_int64_t));
606 len = sizeof(apr_int64_t);
607 *(apr_int64_t *)ptr =
608 (textmode ?
609 apr_atoi64(args[*argp]) : *(apr_int64_t *)args[*argp]);
610 break;
611 default:
612 return APR_EGENERAL;
613 }
614 (*argp)++; /* non LOBs consume one argument */
615 }
616 }
617 rc = SQLBindParameter(statement->stmt, narg, inOut, cType,
618 baseType, len, 0, ptr, len, indicator);
619 CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT,
620 statement->stmt);
621 return rc;
622}
623
624/* LOB / Bucket Brigade functions */
625
626/* bucket type specific destroy */
627static void odbc_lob_bucket_destroy(void *data)
628{
629 odbc_bucket *bd = data;
630
631 if (apr_bucket_shared_destroy(bd))
632 apr_bucket_free(bd);
633}
634
635/* set aside a bucket if possible */
636static apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool)
637{
638 odbc_bucket *bd = (odbc_bucket *)e->data;
639
640 /* Unlikely - but if the row pool is ancestor of this pool then it is OK */
641 if (apr_pool_is_ancestor(bd->row->pool, pool))
642 return APR_SUCCESS;
643
644 return apr_bucket_setaside_notimpl(e, pool);
645}
646
647/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */
648static apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str,
649 apr_size_t *len, apr_read_type_e block)
650{
651 SQLRETURN rc;
652 SQLLEN len_indicator;
653 SQLSMALLINT type;
654 odbc_bucket *bd = (odbc_bucket *)e->data;
655 apr_bucket *nxt;
656 void *buf;
657 int bufsize = bd->row->res->apr_dbd->defaultBufferSize;
658 int eos;
659
660 /* C type is CHAR for CLOBs, DEFAULT for BLOBs */
661 type = bd->row->res->coltypes[bd->col];
662 type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT;
663
664 /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE,
665 * but they may be much bigger per the BUFSIZE parameter.
666 */
667 if (bufsize < APR_BUCKET_BUFF_SIZE)
668 bufsize = APR_BUCKET_BUFF_SIZE;
669
670 buf = apr_bucket_alloc(bufsize, e->list);
671 *str = NULL;
672 *len = 0;
673
674 rc = SQLGetData(bd->row->res->stmt, bd->col + 1,
675 type, buf, bufsize,
676 &len_indicator);
677
678 CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc,
679 SQL_HANDLE_STMT, bd->row->res->stmt);
680
681 if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0)
682 len_indicator = 0;
683
684 if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) {
685
686 if (rc == SQL_SUCCESS_WITH_INFO
687 && (len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize)) {
688 /* not the last read = a full buffer. CLOBs have a null terminator */
689 *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 );
690
691 eos = 0;
692 }
693 else {
694 /* the last read - len_indicator is supposed to be the length,
695 * but some driver get this wrong and return the total length.
696 * We try to handle both interpretations.
697 */
698 *len = (len_indicator > bufsize
699 && len_indicator >= (SQLLEN)e->start)
700 ? (len_indicator - (SQLLEN)e->start) : len_indicator;
701
702 eos = 1;
703 }
704
705 if (!eos) {
706 /* Create a new LOB bucket to append and append it */
707 nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list);
708 APR_BUCKET_INIT(nxt);
709 nxt->length = -1;
710 nxt->data = e->data;
711 nxt->type = &odbc_bucket_type;
712 nxt->free = apr_bucket_free;
713 nxt->list = e->list;
714 nxt->start = e->start + *len;
715 APR_BUCKET_INSERT_AFTER(e, nxt);
716 }
717 else {
718 odbc_lob_bucket_destroy(e->data);
719 }
720 /* make current bucket into a heap bucket */
721 apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
722 *str = buf;
723
724 /* No data is success in this context */
725 rc = SQL_SUCCESS;
726 }
727 return APR_FROM_SQL_RESULT(rc);
728}
729
730/* Create a bucket brigade on the row pool for a LOB column */
731static apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col,
732 SQLSMALLINT type, apr_bucket_brigade *bb)
733{
734 apr_bucket_alloc_t *list = bb->bucket_alloc;
735 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
736 odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list);
737 apr_bucket *eos = apr_bucket_eos_create(list);
738
739 bd->row = row;
740 bd->col = col;
741 bd->type = type;
742
743 APR_BUCKET_INIT(b);
744 b->type = &odbc_bucket_type;
745 b->free = apr_bucket_free;
746 b->list = list;
747 /* LOB lengths are unknown in ODBC */
748 b = apr_bucket_shared_make(b, bd, 0, -1);
749
750 APR_BRIGADE_INSERT_TAIL(bb, b);
751 APR_BRIGADE_INSERT_TAIL(bb, eos);
752
753 return APR_SUCCESS;
754}
755
756/* returns a data pointer for a column, returns NULL for NULL value,
757 * return -1 if data not available
758 */
759static void *odbc_get(const apr_dbd_row_t *row, const int col,
760 const SQLSMALLINT sqltype)
761{
762 SQLRETURN rc;
763 SQLLEN indicator;
764 int state = row->res->colstate[col];
750 intptr_t options = row->res->apr_dbd->dboptions;
765 ODBC_INTPTR_T options = row->res->apr_dbd->dboptions;
751
752 switch (state) {
753 case (COL_UNAVAIL):
754 return (void *)-1;
755 case (COL_RETRIEVED):
756 return NULL;
757
758 case (COL_BOUND):
759 case (COL_PRESENT):
760 if (sqltype == row->res->coltypes[col]) {
761 /* same type and we already have the data */
762 row->res->colstate[col] = COL_RETRIEVED;
763 return (row->res->colinds[col] == SQL_NULL_DATA) ?
764 NULL : row->res->colptrs[col];
765 }
766 }
767
768 /* we need to get the data now */
769 if (!(options & SQL_GD_ANY_ORDER)) {
770 /* this ODBC driver requires columns to be retrieved in order,
771 * so we attempt to get every prior un-gotten non-LOB column
772 */
773 int i;
774 for (i = 0; i < col; i++) {
775 if (row->res->colstate[i] == COL_AVAIL) {
776 if (IS_LOB(row->res->coltypes[i]))
777 row->res->colstate[i] = COL_UNAVAIL;
778 else {
779 odbc_get(row, i, row->res->coltypes[i]);
780 row->res->colstate[i] = COL_PRESENT;
781 }
782 }
783 }
784 }
785
786 if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
787 /* this driver won't let us re-get bound columns */
788 return (void *)-1;
789
790 /* a LOB might not have a buffer allocated yet - so create one */
791 if (!row->res->colptrs[col])
792 row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
793
794 rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
795 row->res->colsizes[col], &indicator);
796 CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
797 row->res->stmt);
798 if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
799 return NULL;
800
801 if (SQL_SUCCEEDED(rc)) {
802 /* whatever it was originally, it is now this sqltype */
803 row->res->coltypes[col] = sqltype;
804 /* this allows getting CLOBs in text mode by calling get_entry
805 * until it returns NULL
806 */
807 row->res->colstate[col] =
808 (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
809 return row->res->colptrs[col];
810 }
811 else
812 return (void *)-1;
813}
814
815/* Parse the parameter string for open */
816static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
817 int *connect, SQLCHAR **datasource,
818 SQLCHAR **user, SQLCHAR **password,
819 int *defaultBufferSize, int *nattrs,
766
767 switch (state) {
768 case (COL_UNAVAIL):
769 return (void *)-1;
770 case (COL_RETRIEVED):
771 return NULL;
772
773 case (COL_BOUND):
774 case (COL_PRESENT):
775 if (sqltype == row->res->coltypes[col]) {
776 /* same type and we already have the data */
777 row->res->colstate[col] = COL_RETRIEVED;
778 return (row->res->colinds[col] == SQL_NULL_DATA) ?
779 NULL : row->res->colptrs[col];
780 }
781 }
782
783 /* we need to get the data now */
784 if (!(options & SQL_GD_ANY_ORDER)) {
785 /* this ODBC driver requires columns to be retrieved in order,
786 * so we attempt to get every prior un-gotten non-LOB column
787 */
788 int i;
789 for (i = 0; i < col; i++) {
790 if (row->res->colstate[i] == COL_AVAIL) {
791 if (IS_LOB(row->res->coltypes[i]))
792 row->res->colstate[i] = COL_UNAVAIL;
793 else {
794 odbc_get(row, i, row->res->coltypes[i]);
795 row->res->colstate[i] = COL_PRESENT;
796 }
797 }
798 }
799 }
800
801 if ((state == COL_BOUND && !(options & SQL_GD_BOUND)))
802 /* this driver won't let us re-get bound columns */
803 return (void *)-1;
804
805 /* a LOB might not have a buffer allocated yet - so create one */
806 if (!row->res->colptrs[col])
807 row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]);
808
809 rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col],
810 row->res->colsizes[col], &indicator);
811 CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT,
812 row->res->stmt);
813 if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA)
814 return NULL;
815
816 if (SQL_SUCCEEDED(rc)) {
817 /* whatever it was originally, it is now this sqltype */
818 row->res->coltypes[col] = sqltype;
819 /* this allows getting CLOBs in text mode by calling get_entry
820 * until it returns NULL
821 */
822 row->res->colstate[col] =
823 (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED;
824 return row->res->colptrs[col];
825 }
826 else
827 return (void *)-1;
828}
829
830/* Parse the parameter string for open */
831static apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params,
832 int *connect, SQLCHAR **datasource,
833 SQLCHAR **user, SQLCHAR **password,
834 int *defaultBufferSize, int *nattrs,
820 int **attrs, intptr_t **attrvals)
835 int **attrs, ODBC_INTPTR_T **attrvals)
821{
822 char *seps, *last, *next, *name[MAX_PARAMS], *val[MAX_PARAMS];
823 int nparams = 0, i, j;
824
825 *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
836{
837 char *seps, *last, *next, *name[MAX_PARAMS], *val[MAX_PARAMS];
838 int nparams = 0, i, j;
839
840 *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *));
826 *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(intptr_t));
841 *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(ODBC_INTPTR_T));
827 *nattrs = 0;
828 seps = DEFAULTSEPS;
829 name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
830
831 /* no params is OK here - let connect return a more useful error msg */
832 if (!name[nparams])
833 return SQL_SUCCESS;
834
835 do {
836 if (last[strspn(last, seps)] == CSINGLEQUOTE) {
837 last += strspn(last, seps);
838 seps=SSINGLEQUOTE;
839 }
840 val[nparams] = apr_strtok(NULL, seps, &last);
841 seps = DEFAULTSEPS;
842
843 ++nparams;
844 next = apr_strtok(NULL, seps, &last);
845 if (!next) {
846 break;
847 }
848 if (nparams >= MAX_PARAMS) {
849 /* too many parameters, no place to store */
850 return APR_EGENERAL;
851 }
852 name[nparams] = next;
853 } while (1);
854
855 for (j = i = 0; i < nparams; i++) {
856 if (!apr_strnatcasecmp(name[i], "CONNECT")) {
857 *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
858 *connect = 1;
859 }
860 else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) {
861 *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
862 *connect = 0;
863 }
864 else if (!apr_strnatcasecmp(name[i], "USER")) {
865 *user = (SQLCHAR *)apr_pstrdup(pool, val[i]);
866 }
867 else if (!apr_strnatcasecmp(name[i], "PASSWORD")) {
868 *password = (SQLCHAR *)apr_pstrdup(pool, val[i]);
869 }
870 else if (!apr_strnatcasecmp(name[i], "BUFSIZE")) {
871 *defaultBufferSize = atoi(val[i]);
872 }
873 else if (!apr_strnatcasecmp(name[i], "ACCESS")) {
874 if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
875 (*attrvals)[j] = SQL_MODE_READ_ONLY;
876 else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
877 (*attrvals)[j] = SQL_MODE_READ_WRITE;
878 else
879 return SQL_ERROR;
880 (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
881 }
882 else if (!apr_strnatcasecmp(name[i], "CTIMEOUT")) {
883 (*attrvals)[j] = atoi(val[i]);
884 (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
885 }
886 else if (!apr_strnatcasecmp(name[i], "STIMEOUT")) {
887 (*attrvals)[j] = atoi(val[i]);
888 (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
889 }
890 else if (!apr_strnatcasecmp(name[i], "TXMODE")) {
891 if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
892 (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
893 else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
894 (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
895 else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
896 (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
897 else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
898 (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
899 else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
900 continue;
901 else
902 return SQL_ERROR;
903 (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
904 }
905 else
906 return SQL_ERROR;
907 }
908 *nattrs = j;
909 return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
910}
911
912/* common handling after ODBC calls - save error info (code and text) in dbc */
913static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
914 SQLSMALLINT type, SQLHANDLE h, int line)
915{
916 SQLCHAR buffer[512];
917 SQLCHAR sqlstate[128];
918 SQLINTEGER native;
919 SQLSMALLINT reslength;
920 char *res, *p, *end, *logval = NULL;
921 int i;
922
923 /* set info about last error in dbc - fast return for SQL_SUCCESS */
924 if (rc == SQL_SUCCESS) {
925 char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
926 apr_size_t successMsgLen = sizeof successMsg - 1;
927
928 dbc->lasterrorcode = SQL_SUCCESS;
929 apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError);
930 apr_cpystrn(dbc->lastError + successMsgLen, step,
931 sizeof dbc->lastError - successMsgLen);
932 return;
933 }
934 switch (rc) {
935 case SQL_INVALID_HANDLE:
936 res = "SQL_INVALID_HANDLE";
937 break;
938 case SQL_ERROR:
939 res = "SQL_ERROR";
940 break;
941 case SQL_SUCCESS_WITH_INFO:
942 res = "SQL_SUCCESS_WITH_INFO";
943 break;
944 case SQL_STILL_EXECUTING:
945 res = "SQL_STILL_EXECUTING";
946 break;
947 case SQL_NEED_DATA:
948 res = "SQL_NEED_DATA";
949 break;
950 case SQL_NO_DATA:
951 res = "SQL_NO_DATA";
952 break;
953 default:
954 res = "unrecognized SQL return code";
955 }
956 /* these two returns are expected during normal execution */
957 if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA
958 && dbc->can_commit != APR_DBD_TRANSACTION_IGNORE_ERRORS) {
959 dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK;
960 }
961 p = dbc->lastError;
962 end = p + sizeof(dbc->lastError);
963 dbc->lasterrorcode = rc;
964 p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
965 step, res, rc, SOURCE_FILE, line - 1);
966 for (i = 1, rc = 0; rc == 0; i++) {
967 rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
968 sizeof(buffer), &reslength);
969 if (SQL_SUCCEEDED(rc) && (p < (end - 280)))
970 p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
971 }
972 apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
973 /* if env var was set or call was init/open (no dbname) - log to stderr */
974 if (logval || !dbc->dbname ) {
975 char timestamp[APR_CTIME_LEN];
976
977 apr_file_t *se;
978 apr_ctime(timestamp, apr_time_now());
979 apr_file_open_stderr(&se, dbc->pool);
980 apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
981 }
982}
983
984static APR_INLINE int odbc_check_rollback(apr_dbd_t *handle)
985{
986 if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) {
987 handle->lasterrorcode = SQL_ERROR;
988 apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ",
989 sizeof handle->lastError);
990 return 1;
991 }
992 return 0;
993}
994
995/*
996 * public functions per DBD driver API
997 */
998
999/** init: allow driver to perform once-only initialisation. **/
1000static void odbc_init(apr_pool_t *pool)
1001{
1002 SQLRETURN rc;
1003 char *step;
1004 apr_version_t apuver;
1005
1006 apu_version(&apuver);
1007 if (apuver.major != DRIVER_APU_VERSION_MAJOR
1008 || apuver.minor != DRIVER_APU_VERSION_MINOR) {
1009 apr_file_t *se;
1010
1011 apr_file_open_stderr(&se, pool);
1012 apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
1013 "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
1014 DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
1015 apuver.major, apuver.minor);
1016 abort();
1017 }
1018
1019 if (henv)
1020 return;
1021
1022 step = "SQLAllocHandle (SQL_HANDLE_ENV)";
1023 rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
1024 apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
1025 if (SQL_SUCCEEDED(rc)) {
1026 step = "SQLSetEnvAttr";
1027 rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
1028 (SQLPOINTER)SQL_OV_ODBC3, 0);
1029 }
1030 else {
1031 apr_dbd_t tmp_dbc;
1032 SQLHANDLE err_h = henv;
1033
1034 tmp_dbc.pool = pool;
1035 tmp_dbc.dbname = NULL;
1036 CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
1037 }
1038}
1039
1040/** native_handle: return the native database handle of the underlying db **/
1041static void *odbc_native_handle(apr_dbd_t *handle)
1042{
1043 return handle->dbc;
1044}
1045
1046/** open: obtain a database connection from the server rec. **/
1047
1048/* It would be more efficient to allocate a single statement handle
1049 * here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
1050 * SQLPrepare, and we don't know whether random-access is
1051 * specified until SQLExecute so we cannot.
1052 */
1053
1054static apr_dbd_t *odbc_open(apr_pool_t *pool, const char *params, const char **error)
1055{
1056 SQLRETURN rc;
1057 SQLHANDLE hdbc = NULL;
1058 apr_dbd_t *handle;
1059 char *err_step;
1060 int err_htype, i;
1061 int defaultBufferSize = DEFAULT_BUFFER_SIZE;
1062 SQLHANDLE err_h = NULL;
1063 SQLCHAR *datasource = (SQLCHAR *)"", *user = (SQLCHAR *)"",
1064 *password = (SQLCHAR *)"";
1065 int nattrs = 0, *attrs = NULL, connect = 0;
842 *nattrs = 0;
843 seps = DEFAULTSEPS;
844 name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last);
845
846 /* no params is OK here - let connect return a more useful error msg */
847 if (!name[nparams])
848 return SQL_SUCCESS;
849
850 do {
851 if (last[strspn(last, seps)] == CSINGLEQUOTE) {
852 last += strspn(last, seps);
853 seps=SSINGLEQUOTE;
854 }
855 val[nparams] = apr_strtok(NULL, seps, &last);
856 seps = DEFAULTSEPS;
857
858 ++nparams;
859 next = apr_strtok(NULL, seps, &last);
860 if (!next) {
861 break;
862 }
863 if (nparams >= MAX_PARAMS) {
864 /* too many parameters, no place to store */
865 return APR_EGENERAL;
866 }
867 name[nparams] = next;
868 } while (1);
869
870 for (j = i = 0; i < nparams; i++) {
871 if (!apr_strnatcasecmp(name[i], "CONNECT")) {
872 *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
873 *connect = 1;
874 }
875 else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) {
876 *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]);
877 *connect = 0;
878 }
879 else if (!apr_strnatcasecmp(name[i], "USER")) {
880 *user = (SQLCHAR *)apr_pstrdup(pool, val[i]);
881 }
882 else if (!apr_strnatcasecmp(name[i], "PASSWORD")) {
883 *password = (SQLCHAR *)apr_pstrdup(pool, val[i]);
884 }
885 else if (!apr_strnatcasecmp(name[i], "BUFSIZE")) {
886 *defaultBufferSize = atoi(val[i]);
887 }
888 else if (!apr_strnatcasecmp(name[i], "ACCESS")) {
889 if (!apr_strnatcasecmp(val[i], "READ_ONLY"))
890 (*attrvals)[j] = SQL_MODE_READ_ONLY;
891 else if (!apr_strnatcasecmp(val[i], "READ_WRITE"))
892 (*attrvals)[j] = SQL_MODE_READ_WRITE;
893 else
894 return SQL_ERROR;
895 (*attrs)[j++] = SQL_ATTR_ACCESS_MODE;
896 }
897 else if (!apr_strnatcasecmp(name[i], "CTIMEOUT")) {
898 (*attrvals)[j] = atoi(val[i]);
899 (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT;
900 }
901 else if (!apr_strnatcasecmp(name[i], "STIMEOUT")) {
902 (*attrvals)[j] = atoi(val[i]);
903 (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT;
904 }
905 else if (!apr_strnatcasecmp(name[i], "TXMODE")) {
906 if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED"))
907 (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED;
908 else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED"))
909 (*attrvals)[j] = SQL_TXN_READ_COMMITTED;
910 else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ"))
911 (*attrvals)[j] = SQL_TXN_REPEATABLE_READ;
912 else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE"))
913 (*attrvals)[j] = SQL_TXN_SERIALIZABLE;
914 else if (!apr_strnatcasecmp(val[i], "DEFAULT"))
915 continue;
916 else
917 return SQL_ERROR;
918 (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION;
919 }
920 else
921 return SQL_ERROR;
922 }
923 *nattrs = j;
924 return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR;
925}
926
927/* common handling after ODBC calls - save error info (code and text) in dbc */
928static void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc,
929 SQLSMALLINT type, SQLHANDLE h, int line)
930{
931 SQLCHAR buffer[512];
932 SQLCHAR sqlstate[128];
933 SQLINTEGER native;
934 SQLSMALLINT reslength;
935 char *res, *p, *end, *logval = NULL;
936 int i;
937
938 /* set info about last error in dbc - fast return for SQL_SUCCESS */
939 if (rc == SQL_SUCCESS) {
940 char successMsg[] = "[dbd_odbc] SQL_SUCCESS ";
941 apr_size_t successMsgLen = sizeof successMsg - 1;
942
943 dbc->lasterrorcode = SQL_SUCCESS;
944 apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError);
945 apr_cpystrn(dbc->lastError + successMsgLen, step,
946 sizeof dbc->lastError - successMsgLen);
947 return;
948 }
949 switch (rc) {
950 case SQL_INVALID_HANDLE:
951 res = "SQL_INVALID_HANDLE";
952 break;
953 case SQL_ERROR:
954 res = "SQL_ERROR";
955 break;
956 case SQL_SUCCESS_WITH_INFO:
957 res = "SQL_SUCCESS_WITH_INFO";
958 break;
959 case SQL_STILL_EXECUTING:
960 res = "SQL_STILL_EXECUTING";
961 break;
962 case SQL_NEED_DATA:
963 res = "SQL_NEED_DATA";
964 break;
965 case SQL_NO_DATA:
966 res = "SQL_NO_DATA";
967 break;
968 default:
969 res = "unrecognized SQL return code";
970 }
971 /* these two returns are expected during normal execution */
972 if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA
973 && dbc->can_commit != APR_DBD_TRANSACTION_IGNORE_ERRORS) {
974 dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK;
975 }
976 p = dbc->lastError;
977 end = p + sizeof(dbc->lastError);
978 dbc->lasterrorcode = rc;
979 p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ",
980 step, res, rc, SOURCE_FILE, line - 1);
981 for (i = 1, rc = 0; rc == 0; i++) {
982 rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer,
983 sizeof(buffer), &reslength);
984 if (SQL_SUCCEEDED(rc) && (p < (end - 280)))
985 p += sprintf(p, "%.256s %.20s ", buffer, sqlstate);
986 }
987 apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool);
988 /* if env var was set or call was init/open (no dbname) - log to stderr */
989 if (logval || !dbc->dbname ) {
990 char timestamp[APR_CTIME_LEN];
991
992 apr_file_t *se;
993 apr_ctime(timestamp, apr_time_now());
994 apr_file_open_stderr(&se, dbc->pool);
995 apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError);
996 }
997}
998
999static APR_INLINE int odbc_check_rollback(apr_dbd_t *handle)
1000{
1001 if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) {
1002 handle->lasterrorcode = SQL_ERROR;
1003 apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ",
1004 sizeof handle->lastError);
1005 return 1;
1006 }
1007 return 0;
1008}
1009
1010/*
1011 * public functions per DBD driver API
1012 */
1013
1014/** init: allow driver to perform once-only initialisation. **/
1015static void odbc_init(apr_pool_t *pool)
1016{
1017 SQLRETURN rc;
1018 char *step;
1019 apr_version_t apuver;
1020
1021 apu_version(&apuver);
1022 if (apuver.major != DRIVER_APU_VERSION_MAJOR
1023 || apuver.minor != DRIVER_APU_VERSION_MINOR) {
1024 apr_file_t *se;
1025
1026 apr_file_open_stderr(&se, pool);
1027 apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n"
1028 "Attempt to load APU version %d.%d driver with APU version %d.%d\n",
1029 DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR,
1030 apuver.major, apuver.minor);
1031 abort();
1032 }
1033
1034 if (henv)
1035 return;
1036
1037 step = "SQLAllocHandle (SQL_HANDLE_ENV)";
1038 rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
1039 apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null);
1040 if (SQL_SUCCEEDED(rc)) {
1041 step = "SQLSetEnvAttr";
1042 rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,
1043 (SQLPOINTER)SQL_OV_ODBC3, 0);
1044 }
1045 else {
1046 apr_dbd_t tmp_dbc;
1047 SQLHANDLE err_h = henv;
1048
1049 tmp_dbc.pool = pool;
1050 tmp_dbc.dbname = NULL;
1051 CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h);
1052 }
1053}
1054
1055/** native_handle: return the native database handle of the underlying db **/
1056static void *odbc_native_handle(apr_dbd_t *handle)
1057{
1058 return handle->dbc;
1059}
1060
1061/** open: obtain a database connection from the server rec. **/
1062
1063/* It would be more efficient to allocate a single statement handle
1064 * here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before
1065 * SQLPrepare, and we don't know whether random-access is
1066 * specified until SQLExecute so we cannot.
1067 */
1068
1069static apr_dbd_t *odbc_open(apr_pool_t *pool, const char *params, const char **error)
1070{
1071 SQLRETURN rc;
1072 SQLHANDLE hdbc = NULL;
1073 apr_dbd_t *handle;
1074 char *err_step;
1075 int err_htype, i;
1076 int defaultBufferSize = DEFAULT_BUFFER_SIZE;
1077 SQLHANDLE err_h = NULL;
1078 SQLCHAR *datasource = (SQLCHAR *)"", *user = (SQLCHAR *)"",
1079 *password = (SQLCHAR *)"";
1080 int nattrs = 0, *attrs = NULL, connect = 0;
1066 intptr_t *attrvals = NULL;
1081 ODBC_INTPTR_T *attrvals = NULL;
1067
1068 err_step = "SQLAllocHandle (SQL_HANDLE_DBC)";
1069 err_htype = SQL_HANDLE_ENV;
1070 err_h = henv;
1071 rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
1072 if (SQL_SUCCEEDED(rc)) {
1073 err_step = "Invalid DBD Parameters - open";
1074 err_htype = SQL_HANDLE_DBC;
1075 err_h = hdbc;
1076 rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
1077 &password, &defaultBufferSize, &nattrs, &attrs,
1078 &attrvals);
1079 }
1080 if (SQL_SUCCEEDED(rc)) {
1081 for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1082 err_step = "SQLSetConnectAttr (from DBD Parameters)";
1083 err_htype = SQL_HANDLE_DBC;
1084 err_h = hdbc;
1085 rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0);
1086 }
1087 }
1088 if (SQL_SUCCEEDED(rc)) {
1089 if (connect) {
1090 SQLCHAR out[1024];
1091 SQLSMALLINT outlen;
1092
1093 err_step = "SQLDriverConnect";
1094 err_htype = SQL_HANDLE_DBC;
1095 err_h = hdbc;
1096 rc = SQLDriverConnect(hdbc, NULL, datasource,
1097 (SQLSMALLINT)strlen((char *)datasource),
1098 out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1099 }
1100 else {
1101 err_step = "SQLConnect";
1102 err_htype = SQL_HANDLE_DBC;
1103 err_h = hdbc;
1104 rc = SQLConnect(hdbc, datasource,
1105 (SQLSMALLINT)strlen((char *)datasource),
1106 user, (SQLSMALLINT)strlen((char *)user),
1107 password, (SQLSMALLINT)strlen((char *)password));
1108 }
1109 }
1110 if (SQL_SUCCEEDED(rc)) {
1111 handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1112 handle->dbname = apr_pstrdup(pool, (char *)datasource);
1113 handle->dbc = hdbc;
1114 handle->pool = pool;
1115 handle->defaultBufferSize = defaultBufferSize;
1116 CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1117 handle->default_transaction_mode = 0;
1118 handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1119 SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1082
1083 err_step = "SQLAllocHandle (SQL_HANDLE_DBC)";
1084 err_htype = SQL_HANDLE_ENV;
1085 err_h = henv;
1086 rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
1087 if (SQL_SUCCEEDED(rc)) {
1088 err_step = "Invalid DBD Parameters - open";
1089 err_htype = SQL_HANDLE_DBC;
1090 err_h = hdbc;
1091 rc = odbc_parse_params(pool, params, &connect, &datasource, &user,
1092 &password, &defaultBufferSize, &nattrs, &attrs,
1093 &attrvals);
1094 }
1095 if (SQL_SUCCEEDED(rc)) {
1096 for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) {
1097 err_step = "SQLSetConnectAttr (from DBD Parameters)";
1098 err_htype = SQL_HANDLE_DBC;
1099 err_h = hdbc;
1100 rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0);
1101 }
1102 }
1103 if (SQL_SUCCEEDED(rc)) {
1104 if (connect) {
1105 SQLCHAR out[1024];
1106 SQLSMALLINT outlen;
1107
1108 err_step = "SQLDriverConnect";
1109 err_htype = SQL_HANDLE_DBC;
1110 err_h = hdbc;
1111 rc = SQLDriverConnect(hdbc, NULL, datasource,
1112 (SQLSMALLINT)strlen((char *)datasource),
1113 out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT);
1114 }
1115 else {
1116 err_step = "SQLConnect";
1117 err_htype = SQL_HANDLE_DBC;
1118 err_h = hdbc;
1119 rc = SQLConnect(hdbc, datasource,
1120 (SQLSMALLINT)strlen((char *)datasource),
1121 user, (SQLSMALLINT)strlen((char *)user),
1122 password, (SQLSMALLINT)strlen((char *)password));
1123 }
1124 }
1125 if (SQL_SUCCEEDED(rc)) {
1126 handle = apr_pcalloc(pool, sizeof(apr_dbd_t));
1127 handle->dbname = apr_pstrdup(pool, (char *)datasource);
1128 handle->dbc = hdbc;
1129 handle->pool = pool;
1130 handle->defaultBufferSize = defaultBufferSize;
1131 CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc);
1132 handle->default_transaction_mode = 0;
1133 handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1134 SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION,
1120 &(handle->default_transaction_mode), sizeof(intptr_t), NULL);
1135 &(handle->default_transaction_mode), sizeof(ODBC_INTPTR_T), NULL);
1121 handle->transaction_mode = handle->default_transaction_mode;
1122 SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1136 handle->transaction_mode = handle->default_transaction_mode;
1137 SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions),
1123 sizeof(intptr_t), NULL);
1138 sizeof(ODBC_INTPTR_T), NULL);
1124 apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1125 return handle;
1126 }
1127 else {
1128 apr_dbd_t tmp_dbc;
1129
1130 tmp_dbc.pool = pool;
1131 tmp_dbc.dbname = NULL;
1132 CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1133 if (error)
1134 *error = apr_pstrdup(pool, tmp_dbc.lastError);
1135 if (hdbc)
1136 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1137 return NULL;
1138 }
1139}
1140
1141/** check_conn: check status of a database connection **/
1142static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1143{
1144 SQLUINTEGER isDead;
1145 SQLRETURN rc;
1146
1147 rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1148 sizeof(SQLUINTEGER), NULL);
1149 CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1150 SQL_HANDLE_DBC, handle->dbc);
1151 /* if driver cannot check connection, say so */
1152 if (rc != SQL_SUCCESS)
1153 return APR_ENOTIMPL;
1154
1155 return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1156}
1157
1158/** set_dbname: select database name. May be a no-op if not supported. **/
1159static int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle,
1160 const char *name)
1161{
1162 if (apr_strnatcmp(name, handle->dbname)) {
1163 return APR_EGENERAL; /* It's illegal to change dbname in ODBC */
1164 }
1165 CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1166 handle->dbc);
1167 return APR_SUCCESS; /* OK if it's the same name */
1168}
1169
1170/** transaction: start a transaction. May be a no-op. **/
1171static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1172 apr_dbd_transaction_t **trans)
1173{
1174 SQLRETURN rc = SQL_SUCCESS;
1175
1176 if (handle->transaction_mode) {
1177 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION,
1178 (SQLPOINTER)handle->transaction_mode, 0);
1179 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1180 SQL_HANDLE_DBC, handle->dbc);
1181 }
1182 if (SQL_SUCCEEDED(rc)) {
1183 /* turn off autocommit for transactions */
1184 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1185 SQL_AUTOCOMMIT_OFF, 0);
1186 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1187 SQL_HANDLE_DBC, handle->dbc);
1188 }
1189 if (SQL_SUCCEEDED(rc)) {
1190 *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1191 (*trans)->dbc = handle->dbc;
1192 (*trans)->apr_dbd = handle;
1193 }
1194 handle->can_commit = APR_DBD_TRANSACTION_COMMIT;
1195 return APR_FROM_SQL_RESULT(rc);
1196}
1197
1198/** end_transaction: end a transaction **/
1199static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1200{
1201 SQLRETURN rc;
1202 int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK)
1203 ? SQL_COMMIT : SQL_ROLLBACK;
1204
1205 rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
1206 CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1207 if (SQL_SUCCEEDED(rc)) {
1208 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1209 (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1210 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1211 rc, SQL_HANDLE_DBC, trans->dbc);
1212 }
1213 trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1214 return APR_FROM_SQL_RESULT(rc);
1215}
1216
1217/** query: execute an SQL statement which doesn't return a result set **/
1218static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1219{
1220 SQLRETURN rc;
1221 SQLHANDLE hstmt = NULL;
1222 size_t len = strlen(statement);
1223
1224 if (odbc_check_rollback(handle))
1225 return APR_EGENERAL;
1226
1227 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1228 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1229 handle->dbc);
1230 if (!SQL_SUCCEEDED(rc))
1231 return APR_FROM_SQL_RESULT(rc);
1232
1233 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1234 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1235
1236 if (SQL_SUCCEEDED(rc)) {
1237 SQLLEN rowcount;
1238
1239 rc = SQLRowCount(hstmt, &rowcount);
1240 *nrows = (int)rowcount;
1241 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1242 }
1243
1244 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1245 return APR_FROM_SQL_RESULT(rc);
1246}
1247
1248/** select: execute an SQL statement which returns a result set **/
1249static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1250 apr_dbd_results_t **res, const char *statement,
1251 int random)
1252{
1253 SQLRETURN rc;
1254 SQLHANDLE hstmt;
1255 apr_dbd_prepared_t *stmt;
1256 size_t len = strlen(statement);
1257
1258 if (odbc_check_rollback(handle))
1259 return APR_EGENERAL;
1260
1261 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1262 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1263 handle->dbc);
1264 if (!SQL_SUCCEEDED(rc))
1265 return APR_FROM_SQL_RESULT(rc);
1266 /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1267 * is not a prepared statement. We want the same cleanup mechanism.
1268 */
1269 stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1270 stmt->apr_dbd = handle;
1271 stmt->dbc = handle->dbc;
1272 stmt->stmt = hstmt;
1273 apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1274 if (random) {
1275 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1276 (SQLPOINTER)SQL_SCROLLABLE, 0);
1277 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1278 SQL_HANDLE_STMT, hstmt);
1279 }
1280 if (SQL_SUCCEEDED(rc)) {
1281 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1282 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1283 }
1284 if (SQL_SUCCEEDED(rc)) {
1285 rc = odbc_create_results(handle, hstmt, pool, random, res);
1286 apr_pool_cleanup_register(pool, *res,
1287 odbc_close_results, apr_pool_cleanup_null);
1288 }
1289 return APR_FROM_SQL_RESULT(rc);
1290}
1291
1292/** num_cols: get the number of columns in a results set **/
1293static int odbc_num_cols(apr_dbd_results_t *res)
1294{
1295 return res->ncols;
1296}
1297
1298/** num_tuples: get the number of rows in a results set **/
1299static int odbc_num_tuples(apr_dbd_results_t *res)
1300{
1301 SQLRETURN rc;
1302 SQLLEN nrows;
1303
1304 rc = SQLRowCount(res->stmt, &nrows);
1305 CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1306 return SQL_SUCCEEDED(rc) ? (int)nrows : -1;
1307}
1308
1309/** get_row: get a row from a result set **/
1310static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
1311 apr_dbd_row_t **row, int rownum)
1312{
1313 SQLRETURN rc;
1314 char *fetchtype;
1315 int c;
1316
1317 *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1318 (*row)->stmt = res->stmt;
1319 (*row)->dbc = res->dbc;
1320 (*row)->res = res;
1321 (*row)->pool = res->pool;
1322
1323 /* mark all the columns as needing SQLGetData unless they are bound */
1324 for (c = 0; c < res->ncols; c++) {
1325 if (res->colstate[c] != COL_BOUND) {
1326 res->colstate[c] = COL_AVAIL;
1327 }
1328 /* some drivers do not null-term zero-len CHAR data */
1329 if (res->colptrs[c])
1330 *(char *)res->colptrs[c] = 0;
1331 }
1332
1333 if (res->random && (rownum > 0)) {
1334 fetchtype = "SQLFetchScroll";
1335 rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1336 }
1337 else {
1338 fetchtype = "SQLFetch";
1339 rc = SQLFetch(res->stmt);
1340 }
1341 CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1342 (*row)->stmt = res->stmt;
1343 if (!SQL_SUCCEEDED(rc) && !res->random) {
1344 /* early close on any error (usually SQL_NO_DATA) if fetching
1345 * sequentially to release resources ASAP
1346 */
1347 odbc_close_results(res);
1348 return -1;
1349 }
1350 return SQL_SUCCEEDED(rc) ? 0 : -1;
1351}
1352
1353/** datum_get: get a binary entry from a row **/
1354static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col,
1355 apr_dbd_type_e dbdtype, void *data)
1356{
1357 SQLSMALLINT sqltype;
1358 void *p;
1359 int len;
1360
1361 if (col >= row->res->ncols)
1362 return APR_EGENERAL;
1363
1364 if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
1365 data = NULL; /* invalid type */
1366 return APR_EGENERAL;
1367 }
1368
1369 len = sqlSizes[dbdtype];
1370 sqltype = sqlCtype[dbdtype];
1371
1372 /* must not memcpy a brigade, sentinals are relative to orig loc */
1373 if (IS_LOB(sqltype))
1374 return odbc_create_bucket(row, col, sqltype, data);
1375
1376 p = odbc_get(row, col, sqltype);
1377 if (p == (void *)-1)
1378 return APR_EGENERAL;
1379
1380 if (p == NULL)
1381 return APR_ENOENT; /* SQL NULL value */
1382
1383 if (len < 0)
1384 *(char**)data = (char *)p;
1385 else
1386 memcpy(data, p, len);
1387
1388 return APR_SUCCESS;
1389
1390}
1391
1392/** get_entry: get an entry from a row (string data) **/
1393static const char *odbc_get_entry(const apr_dbd_row_t *row, int col)
1394{
1395 void *p;
1396
1397 if (col >= row->res->ncols)
1398 return NULL;
1399
1400 p = odbc_get(row, col, SQL_C_CHAR);
1401
1402 /* NULL or invalid (-1) */
1403 if (p == NULL || p == (void *)-1)
1404 return p;
1405 else
1406 return apr_pstrdup(row->pool, p);
1407}
1408
1409/** error: get current error message (if any) **/
1410static const char *odbc_error(apr_dbd_t *handle, int errnum)
1411{
1412 return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1413}
1414
1415/** escape: escape a string so it is safe for use in query/select **/
1416static const char *odbc_escape(apr_pool_t *pool, const char *s,
1417 apr_dbd_t *handle)
1418{
1419 char *newstr, *src, *dst, *sq;
1420 int qcount;
1421
1422 /* return the original if there are no single-quotes */
1423 if (!(sq = strchr(s, '\'')))
1424 return (char *)s;
1425 /* count the single-quotes and allocate a new buffer */
1426 for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
1427 qcount++;
1428 newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1429
1430 /* move chars, doubling all single-quotes */
1431 src = (char *)s;
1432 for (dst = newstr; *src; src++) {
1433 if ((*dst++ = *src) == '\'')
1434 *dst++ = '\'';
1435 }
1436 *dst = 0;
1437 return newstr;
1438}
1439
1440/** prepare: prepare a statement **/
1441static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle,
1442 const char *query, const char *label, int nargs,
1443 int nvals, apr_dbd_type_e *types,
1444 apr_dbd_prepared_t **statement)
1445{
1446 SQLRETURN rc;
1447 size_t len = strlen(query);
1448
1449 if (odbc_check_rollback(handle))
1450 return APR_EGENERAL;
1451
1452 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1453 (*statement)->dbc = handle->dbc;
1454 (*statement)->apr_dbd = handle;
1455 (*statement)->nargs = nargs;
1456 (*statement)->nvals = nvals;
1457 (*statement)->types =
1458 apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1459 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1460 apr_pool_cleanup_register(pool, *statement,
1461 odbc_close_pstmt, apr_pool_cleanup_null);
1462 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1463 SQL_HANDLE_DBC, handle->dbc);
1464 rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len);
1465 CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1466 (*statement)->stmt);
1467 return APR_FROM_SQL_RESULT(rc);
1468}
1469
1470/** pquery: query using a prepared statement + args **/
1471static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1472 apr_dbd_prepared_t *statement, const char **args)
1473{
1474 SQLRETURN rc = SQL_SUCCESS;
1475 int i, argp;
1476
1477 if (odbc_check_rollback(handle))
1478 return APR_EGENERAL;
1479
1480 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1481 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1482 &argp, (const void **)args, TEXTMODE);
1483 }
1484 if (SQL_SUCCEEDED(rc)) {
1485 rc = SQLExecute(statement->stmt);
1486 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1487 statement->stmt);
1488 }
1489 if (SQL_SUCCEEDED(rc)) {
1490 SQLLEN rowcount;
1491
1492 rc = SQLRowCount(statement->stmt, &rowcount);
1493 *nrows = (int)rowcount;
1494 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1495 statement->stmt);
1496 }
1497 return APR_FROM_SQL_RESULT(rc);
1498}
1499
1500/** pvquery: query using a prepared statement + args **/
1501static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1502 apr_dbd_prepared_t *statement, va_list args)
1503{
1504 const char **values;
1505 int i;
1506
1507 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1508 for (i = 0; i < statement->nvals; i++)
1509 values[i] = va_arg(args, const char *);
1510 return odbc_pquery(pool, handle, nrows, statement, values);
1511}
1512
1513/** pselect: select using a prepared statement + args **/
1514static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle,
1515 apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
1516 int random, const char **args)
1517{
1518 SQLRETURN rc = SQL_SUCCESS;
1519 int i, argp;
1520
1521 if (odbc_check_rollback(handle))
1522 return APR_EGENERAL;
1523
1524 if (random) {
1525 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1526 (SQLPOINTER)SQL_SCROLLABLE, 0);
1527 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1528 rc, SQL_HANDLE_STMT, statement->stmt);
1529 }
1530 if (SQL_SUCCEEDED(rc)) {
1531 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1532 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1533 &argp, (const void **)args, TEXTMODE);
1534 }
1535 }
1536 if (SQL_SUCCEEDED(rc)) {
1537 rc = SQLExecute(statement->stmt);
1538 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1539 statement->stmt);
1540 }
1541 if (SQL_SUCCEEDED(rc)) {
1542 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1543 apr_pool_cleanup_register(pool, *res,
1544 odbc_close_results, apr_pool_cleanup_null);
1545 }
1546 return APR_FROM_SQL_RESULT(rc);
1547}
1548
1549/** pvselect: select using a prepared statement + args **/
1550static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle,
1551 apr_dbd_results_t **res,
1552 apr_dbd_prepared_t *statement, int random,
1553 va_list args)
1554{
1555 const char **values;
1556 int i;
1557
1558 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1559 for (i = 0; i < statement->nvals; i++)
1560 values[i] = va_arg(args, const char *);
1561 return odbc_pselect(pool, handle, res, statement, random, values);
1562}
1563
1564/** get_name: get a column title from a result set **/
1565static const char *odbc_get_name(const apr_dbd_results_t *res, int col)
1566{
1567 SQLRETURN rc;
1568 char buffer[MAX_COLUMN_NAME];
1569 SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1570 SQLULEN colsize;
1571
1572 if (col >= res->ncols)
1573 return NULL; /* bogus column number */
1574 if (res->colnames[col] != NULL)
1575 return res->colnames[col]; /* we already retrieved it */
1576 rc = SQLDescribeCol(res->stmt, col + 1,
1577 (SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
1578 &coltype, &colsize, &coldecimal, &colnullable);
1579 CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1580 SQL_HANDLE_STMT, res->stmt);
1581 res->colnames[col] = apr_pstrdup(res->pool, buffer);
1582 return res->colnames[col];
1583}
1584
1585/** transaction_mode_get: get the mode of transaction **/
1586static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans)
1587{
1588 return (int)trans->apr_dbd->can_commit;
1589}
1590
1591/** transaction_mode_set: set the mode of transaction **/
1592static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
1593{
1594 int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS
1595 | APR_DBD_TRANSACTION_COMMIT
1596 | APR_DBD_TRANSACTION_ROLLBACK);
1597
1598 if ((mode & legal) != mode)
1599 return APR_EGENERAL;
1600
1601 trans->apr_dbd->can_commit = mode;
1602 return APR_SUCCESS;
1603}
1604
1605/** pbquery: query using a prepared statement + binary args **/
1606static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1607 apr_dbd_prepared_t *statement, const void **args)
1608{
1609 SQLRETURN rc = SQL_SUCCESS;
1610 int i, argp;
1611
1612 if (odbc_check_rollback(handle))
1613 return APR_EGENERAL;
1614
1615 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1616 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1617 &argp, args, BINARYMODE);
1618
1619 if (SQL_SUCCEEDED(rc)) {
1620 rc = SQLExecute(statement->stmt);
1621 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1622 statement->stmt);
1623 }
1624 if (SQL_SUCCEEDED(rc)) {
1625 SQLLEN rowcount;
1626
1627 rc = SQLRowCount(statement->stmt, &rowcount);
1628 *nrows = (int)rowcount;
1629 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1630 statement->stmt);
1631 }
1632 return APR_FROM_SQL_RESULT(rc);
1633}
1634
1635/** pbselect: select using a prepared statement + binary args **/
1636static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle,
1637 apr_dbd_results_t **res,
1638 apr_dbd_prepared_t *statement,
1639 int random, const void **args)
1640{
1641 SQLRETURN rc = SQL_SUCCESS;
1642 int i, argp;
1643
1644 if (odbc_check_rollback(handle))
1645 return APR_EGENERAL;
1646
1647 if (random) {
1648 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1649 (SQLPOINTER)SQL_SCROLLABLE, 0);
1650 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1651 rc, SQL_HANDLE_STMT, statement->stmt);
1652 }
1653 if (SQL_SUCCEEDED(rc)) {
1654 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1655 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1656 &argp, args, BINARYMODE);
1657 }
1658 }
1659 if (SQL_SUCCEEDED(rc)) {
1660 rc = SQLExecute(statement->stmt);
1661 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1662 statement->stmt);
1663 }
1664 if (SQL_SUCCEEDED(rc)) {
1665 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1666 apr_pool_cleanup_register(pool, *res,
1667 odbc_close_results, apr_pool_cleanup_null);
1668 }
1669
1670 return APR_FROM_SQL_RESULT(rc);
1671}
1672
1673/** pvbquery: query using a prepared statement + binary args **/
1674static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1675 apr_dbd_prepared_t *statement, va_list args)
1676{
1677 const char **values;
1678 int i;
1679
1680 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1681 for (i = 0; i < statement->nvals; i++)
1682 values[i] = va_arg(args, const char *);
1683 return odbc_pbquery(pool, handle, nrows, statement, (const void **)values);
1684}
1685
1686/** pvbselect: select using a prepared statement + binary args **/
1687static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle,
1688 apr_dbd_results_t **res,
1689 apr_dbd_prepared_t *statement,
1690 int random, va_list args)
1691{
1692 const char **values;
1693 int i;
1694
1695 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1696 for (i = 0; i < statement->nvals; i++)
1697 values[i] = va_arg(args, const char *);
1698 return odbc_pbselect(pool, handle, res, statement, random, (const void **)values);
1699}
1700
1701APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1702 ODBC_DRIVER_STRING,
1703 odbc_init,
1704 odbc_native_handle,
1705 odbc_open,
1706 odbc_check_conn,
1707 odbc_close,
1708 odbc_set_dbname,
1709 odbc_start_transaction,
1710 odbc_end_transaction,
1711 odbc_query,
1712 odbc_select,
1713 odbc_num_cols,
1714 odbc_num_tuples,
1715 odbc_get_row,
1716 odbc_get_entry,
1717 odbc_error,
1718 odbc_escape,
1719 odbc_prepare,
1720 odbc_pvquery,
1721 odbc_pvselect,
1722 odbc_pquery,
1723 odbc_pselect,
1724 odbc_get_name,
1725 odbc_transaction_mode_get,
1726 odbc_transaction_mode_set,
1727 "?",
1728 odbc_pvbquery,
1729 odbc_pvbselect,
1730 odbc_pbquery,
1731 odbc_pbselect,
1732 odbc_datum_get
1733};
1734
1735#endif
1139 apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null);
1140 return handle;
1141 }
1142 else {
1143 apr_dbd_t tmp_dbc;
1144
1145 tmp_dbc.pool = pool;
1146 tmp_dbc.dbname = NULL;
1147 CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h);
1148 if (error)
1149 *error = apr_pstrdup(pool, tmp_dbc.lastError);
1150 if (hdbc)
1151 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
1152 return NULL;
1153 }
1154}
1155
1156/** check_conn: check status of a database connection **/
1157static apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle)
1158{
1159 SQLUINTEGER isDead;
1160 SQLRETURN rc;
1161
1162 rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead,
1163 sizeof(SQLUINTEGER), NULL);
1164 CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc,
1165 SQL_HANDLE_DBC, handle->dbc);
1166 /* if driver cannot check connection, say so */
1167 if (rc != SQL_SUCCESS)
1168 return APR_ENOTIMPL;
1169
1170 return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL;
1171}
1172
1173/** set_dbname: select database name. May be a no-op if not supported. **/
1174static int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle,
1175 const char *name)
1176{
1177 if (apr_strnatcmp(name, handle->dbname)) {
1178 return APR_EGENERAL; /* It's illegal to change dbname in ODBC */
1179 }
1180 CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC,
1181 handle->dbc);
1182 return APR_SUCCESS; /* OK if it's the same name */
1183}
1184
1185/** transaction: start a transaction. May be a no-op. **/
1186static int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1187 apr_dbd_transaction_t **trans)
1188{
1189 SQLRETURN rc = SQL_SUCCESS;
1190
1191 if (handle->transaction_mode) {
1192 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION,
1193 (SQLPOINTER)handle->transaction_mode, 0);
1194 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc,
1195 SQL_HANDLE_DBC, handle->dbc);
1196 }
1197 if (SQL_SUCCEEDED(rc)) {
1198 /* turn off autocommit for transactions */
1199 rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT,
1200 SQL_AUTOCOMMIT_OFF, 0);
1201 CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc,
1202 SQL_HANDLE_DBC, handle->dbc);
1203 }
1204 if (SQL_SUCCEEDED(rc)) {
1205 *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t));
1206 (*trans)->dbc = handle->dbc;
1207 (*trans)->apr_dbd = handle;
1208 }
1209 handle->can_commit = APR_DBD_TRANSACTION_COMMIT;
1210 return APR_FROM_SQL_RESULT(rc);
1211}
1212
1213/** end_transaction: end a transaction **/
1214static int odbc_end_transaction(apr_dbd_transaction_t *trans)
1215{
1216 SQLRETURN rc;
1217 int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK)
1218 ? SQL_COMMIT : SQL_ROLLBACK;
1219
1220 rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action);
1221 CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc);
1222 if (SQL_SUCCEEDED(rc)) {
1223 rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT,
1224 (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1225 CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)",
1226 rc, SQL_HANDLE_DBC, trans->dbc);
1227 }
1228 trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS;
1229 return APR_FROM_SQL_RESULT(rc);
1230}
1231
1232/** query: execute an SQL statement which doesn't return a result set **/
1233static int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement)
1234{
1235 SQLRETURN rc;
1236 SQLHANDLE hstmt = NULL;
1237 size_t len = strlen(statement);
1238
1239 if (odbc_check_rollback(handle))
1240 return APR_EGENERAL;
1241
1242 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1243 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1244 handle->dbc);
1245 if (!SQL_SUCCEEDED(rc))
1246 return APR_FROM_SQL_RESULT(rc);
1247
1248 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1249 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1250
1251 if (SQL_SUCCEEDED(rc)) {
1252 SQLLEN rowcount;
1253
1254 rc = SQLRowCount(hstmt, &rowcount);
1255 *nrows = (int)rowcount;
1256 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt);
1257 }
1258
1259 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
1260 return APR_FROM_SQL_RESULT(rc);
1261}
1262
1263/** select: execute an SQL statement which returns a result set **/
1264static int odbc_select(apr_pool_t *pool, apr_dbd_t *handle,
1265 apr_dbd_results_t **res, const char *statement,
1266 int random)
1267{
1268 SQLRETURN rc;
1269 SQLHANDLE hstmt;
1270 apr_dbd_prepared_t *stmt;
1271 size_t len = strlen(statement);
1272
1273 if (odbc_check_rollback(handle))
1274 return APR_EGENERAL;
1275
1276 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt);
1277 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC,
1278 handle->dbc);
1279 if (!SQL_SUCCEEDED(rc))
1280 return APR_FROM_SQL_RESULT(rc);
1281 /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this
1282 * is not a prepared statement. We want the same cleanup mechanism.
1283 */
1284 stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1285 stmt->apr_dbd = handle;
1286 stmt->dbc = handle->dbc;
1287 stmt->stmt = hstmt;
1288 apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null);
1289 if (random) {
1290 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1291 (SQLPOINTER)SQL_SCROLLABLE, 0);
1292 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc,
1293 SQL_HANDLE_STMT, hstmt);
1294 }
1295 if (SQL_SUCCEEDED(rc)) {
1296 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len);
1297 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt);
1298 }
1299 if (SQL_SUCCEEDED(rc)) {
1300 rc = odbc_create_results(handle, hstmt, pool, random, res);
1301 apr_pool_cleanup_register(pool, *res,
1302 odbc_close_results, apr_pool_cleanup_null);
1303 }
1304 return APR_FROM_SQL_RESULT(rc);
1305}
1306
1307/** num_cols: get the number of columns in a results set **/
1308static int odbc_num_cols(apr_dbd_results_t *res)
1309{
1310 return res->ncols;
1311}
1312
1313/** num_tuples: get the number of rows in a results set **/
1314static int odbc_num_tuples(apr_dbd_results_t *res)
1315{
1316 SQLRETURN rc;
1317 SQLLEN nrows;
1318
1319 rc = SQLRowCount(res->stmt, &nrows);
1320 CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt);
1321 return SQL_SUCCEEDED(rc) ? (int)nrows : -1;
1322}
1323
1324/** get_row: get a row from a result set **/
1325static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
1326 apr_dbd_row_t **row, int rownum)
1327{
1328 SQLRETURN rc;
1329 char *fetchtype;
1330 int c;
1331
1332 *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t));
1333 (*row)->stmt = res->stmt;
1334 (*row)->dbc = res->dbc;
1335 (*row)->res = res;
1336 (*row)->pool = res->pool;
1337
1338 /* mark all the columns as needing SQLGetData unless they are bound */
1339 for (c = 0; c < res->ncols; c++) {
1340 if (res->colstate[c] != COL_BOUND) {
1341 res->colstate[c] = COL_AVAIL;
1342 }
1343 /* some drivers do not null-term zero-len CHAR data */
1344 if (res->colptrs[c])
1345 *(char *)res->colptrs[c] = 0;
1346 }
1347
1348 if (res->random && (rownum > 0)) {
1349 fetchtype = "SQLFetchScroll";
1350 rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum);
1351 }
1352 else {
1353 fetchtype = "SQLFetch";
1354 rc = SQLFetch(res->stmt);
1355 }
1356 CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt);
1357 (*row)->stmt = res->stmt;
1358 if (!SQL_SUCCEEDED(rc) && !res->random) {
1359 /* early close on any error (usually SQL_NO_DATA) if fetching
1360 * sequentially to release resources ASAP
1361 */
1362 odbc_close_results(res);
1363 return -1;
1364 }
1365 return SQL_SUCCEEDED(rc) ? 0 : -1;
1366}
1367
1368/** datum_get: get a binary entry from a row **/
1369static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col,
1370 apr_dbd_type_e dbdtype, void *data)
1371{
1372 SQLSMALLINT sqltype;
1373 void *p;
1374 int len;
1375
1376 if (col >= row->res->ncols)
1377 return APR_EGENERAL;
1378
1379 if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) {
1380 data = NULL; /* invalid type */
1381 return APR_EGENERAL;
1382 }
1383
1384 len = sqlSizes[dbdtype];
1385 sqltype = sqlCtype[dbdtype];
1386
1387 /* must not memcpy a brigade, sentinals are relative to orig loc */
1388 if (IS_LOB(sqltype))
1389 return odbc_create_bucket(row, col, sqltype, data);
1390
1391 p = odbc_get(row, col, sqltype);
1392 if (p == (void *)-1)
1393 return APR_EGENERAL;
1394
1395 if (p == NULL)
1396 return APR_ENOENT; /* SQL NULL value */
1397
1398 if (len < 0)
1399 *(char**)data = (char *)p;
1400 else
1401 memcpy(data, p, len);
1402
1403 return APR_SUCCESS;
1404
1405}
1406
1407/** get_entry: get an entry from a row (string data) **/
1408static const char *odbc_get_entry(const apr_dbd_row_t *row, int col)
1409{
1410 void *p;
1411
1412 if (col >= row->res->ncols)
1413 return NULL;
1414
1415 p = odbc_get(row, col, SQL_C_CHAR);
1416
1417 /* NULL or invalid (-1) */
1418 if (p == NULL || p == (void *)-1)
1419 return p;
1420 else
1421 return apr_pstrdup(row->pool, p);
1422}
1423
1424/** error: get current error message (if any) **/
1425static const char *odbc_error(apr_dbd_t *handle, int errnum)
1426{
1427 return (handle) ? handle->lastError : "[dbd_odbc]No error message available";
1428}
1429
1430/** escape: escape a string so it is safe for use in query/select **/
1431static const char *odbc_escape(apr_pool_t *pool, const char *s,
1432 apr_dbd_t *handle)
1433{
1434 char *newstr, *src, *dst, *sq;
1435 int qcount;
1436
1437 /* return the original if there are no single-quotes */
1438 if (!(sq = strchr(s, '\'')))
1439 return (char *)s;
1440 /* count the single-quotes and allocate a new buffer */
1441 for (qcount = 1; (sq = strchr(sq + 1, '\'')); )
1442 qcount++;
1443 newstr = apr_palloc(pool, strlen(s) + qcount + 1);
1444
1445 /* move chars, doubling all single-quotes */
1446 src = (char *)s;
1447 for (dst = newstr; *src; src++) {
1448 if ((*dst++ = *src) == '\'')
1449 *dst++ = '\'';
1450 }
1451 *dst = 0;
1452 return newstr;
1453}
1454
1455/** prepare: prepare a statement **/
1456static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle,
1457 const char *query, const char *label, int nargs,
1458 int nvals, apr_dbd_type_e *types,
1459 apr_dbd_prepared_t **statement)
1460{
1461 SQLRETURN rc;
1462 size_t len = strlen(query);
1463
1464 if (odbc_check_rollback(handle))
1465 return APR_EGENERAL;
1466
1467 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t));
1468 (*statement)->dbc = handle->dbc;
1469 (*statement)->apr_dbd = handle;
1470 (*statement)->nargs = nargs;
1471 (*statement)->nvals = nvals;
1472 (*statement)->types =
1473 apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e));
1474 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt));
1475 apr_pool_cleanup_register(pool, *statement,
1476 odbc_close_pstmt, apr_pool_cleanup_null);
1477 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc,
1478 SQL_HANDLE_DBC, handle->dbc);
1479 rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len);
1480 CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT,
1481 (*statement)->stmt);
1482 return APR_FROM_SQL_RESULT(rc);
1483}
1484
1485/** pquery: query using a prepared statement + args **/
1486static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1487 apr_dbd_prepared_t *statement, const char **args)
1488{
1489 SQLRETURN rc = SQL_SUCCESS;
1490 int i, argp;
1491
1492 if (odbc_check_rollback(handle))
1493 return APR_EGENERAL;
1494
1495 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1496 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1497 &argp, (const void **)args, TEXTMODE);
1498 }
1499 if (SQL_SUCCEEDED(rc)) {
1500 rc = SQLExecute(statement->stmt);
1501 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1502 statement->stmt);
1503 }
1504 if (SQL_SUCCEEDED(rc)) {
1505 SQLLEN rowcount;
1506
1507 rc = SQLRowCount(statement->stmt, &rowcount);
1508 *nrows = (int)rowcount;
1509 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1510 statement->stmt);
1511 }
1512 return APR_FROM_SQL_RESULT(rc);
1513}
1514
1515/** pvquery: query using a prepared statement + args **/
1516static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1517 apr_dbd_prepared_t *statement, va_list args)
1518{
1519 const char **values;
1520 int i;
1521
1522 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1523 for (i = 0; i < statement->nvals; i++)
1524 values[i] = va_arg(args, const char *);
1525 return odbc_pquery(pool, handle, nrows, statement, values);
1526}
1527
1528/** pselect: select using a prepared statement + args **/
1529static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle,
1530 apr_dbd_results_t **res, apr_dbd_prepared_t *statement,
1531 int random, const char **args)
1532{
1533 SQLRETURN rc = SQL_SUCCESS;
1534 int i, argp;
1535
1536 if (odbc_check_rollback(handle))
1537 return APR_EGENERAL;
1538
1539 if (random) {
1540 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1541 (SQLPOINTER)SQL_SCROLLABLE, 0);
1542 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1543 rc, SQL_HANDLE_STMT, statement->stmt);
1544 }
1545 if (SQL_SUCCEEDED(rc)) {
1546 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1547 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1548 &argp, (const void **)args, TEXTMODE);
1549 }
1550 }
1551 if (SQL_SUCCEEDED(rc)) {
1552 rc = SQLExecute(statement->stmt);
1553 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1554 statement->stmt);
1555 }
1556 if (SQL_SUCCEEDED(rc)) {
1557 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1558 apr_pool_cleanup_register(pool, *res,
1559 odbc_close_results, apr_pool_cleanup_null);
1560 }
1561 return APR_FROM_SQL_RESULT(rc);
1562}
1563
1564/** pvselect: select using a prepared statement + args **/
1565static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle,
1566 apr_dbd_results_t **res,
1567 apr_dbd_prepared_t *statement, int random,
1568 va_list args)
1569{
1570 const char **values;
1571 int i;
1572
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_pselect(pool, handle, res, statement, random, values);
1577}
1578
1579/** get_name: get a column title from a result set **/
1580static const char *odbc_get_name(const apr_dbd_results_t *res, int col)
1581{
1582 SQLRETURN rc;
1583 char buffer[MAX_COLUMN_NAME];
1584 SQLSMALLINT colnamelength, coltype, coldecimal, colnullable;
1585 SQLULEN colsize;
1586
1587 if (col >= res->ncols)
1588 return NULL; /* bogus column number */
1589 if (res->colnames[col] != NULL)
1590 return res->colnames[col]; /* we already retrieved it */
1591 rc = SQLDescribeCol(res->stmt, col + 1,
1592 (SQLCHAR *)buffer, sizeof(buffer), &colnamelength,
1593 &coltype, &colsize, &coldecimal, &colnullable);
1594 CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc,
1595 SQL_HANDLE_STMT, res->stmt);
1596 res->colnames[col] = apr_pstrdup(res->pool, buffer);
1597 return res->colnames[col];
1598}
1599
1600/** transaction_mode_get: get the mode of transaction **/
1601static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans)
1602{
1603 return (int)trans->apr_dbd->can_commit;
1604}
1605
1606/** transaction_mode_set: set the mode of transaction **/
1607static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode)
1608{
1609 int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS
1610 | APR_DBD_TRANSACTION_COMMIT
1611 | APR_DBD_TRANSACTION_ROLLBACK);
1612
1613 if ((mode & legal) != mode)
1614 return APR_EGENERAL;
1615
1616 trans->apr_dbd->can_commit = mode;
1617 return APR_SUCCESS;
1618}
1619
1620/** pbquery: query using a prepared statement + binary args **/
1621static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1622 apr_dbd_prepared_t *statement, const void **args)
1623{
1624 SQLRETURN rc = SQL_SUCCESS;
1625 int i, argp;
1626
1627 if (odbc_check_rollback(handle))
1628 return APR_EGENERAL;
1629
1630 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++)
1631 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1632 &argp, args, BINARYMODE);
1633
1634 if (SQL_SUCCEEDED(rc)) {
1635 rc = SQLExecute(statement->stmt);
1636 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1637 statement->stmt);
1638 }
1639 if (SQL_SUCCEEDED(rc)) {
1640 SQLLEN rowcount;
1641
1642 rc = SQLRowCount(statement->stmt, &rowcount);
1643 *nrows = (int)rowcount;
1644 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT,
1645 statement->stmt);
1646 }
1647 return APR_FROM_SQL_RESULT(rc);
1648}
1649
1650/** pbselect: select using a prepared statement + binary args **/
1651static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle,
1652 apr_dbd_results_t **res,
1653 apr_dbd_prepared_t *statement,
1654 int random, const void **args)
1655{
1656 SQLRETURN rc = SQL_SUCCESS;
1657 int i, argp;
1658
1659 if (odbc_check_rollback(handle))
1660 return APR_EGENERAL;
1661
1662 if (random) {
1663 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE,
1664 (SQLPOINTER)SQL_SCROLLABLE, 0);
1665 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)",
1666 rc, SQL_HANDLE_STMT, statement->stmt);
1667 }
1668 if (SQL_SUCCEEDED(rc)) {
1669 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) {
1670 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i],
1671 &argp, args, BINARYMODE);
1672 }
1673 }
1674 if (SQL_SUCCEEDED(rc)) {
1675 rc = SQLExecute(statement->stmt);
1676 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT,
1677 statement->stmt);
1678 }
1679 if (SQL_SUCCEEDED(rc)) {
1680 rc = odbc_create_results(handle, statement->stmt, pool, random, res);
1681 apr_pool_cleanup_register(pool, *res,
1682 odbc_close_results, apr_pool_cleanup_null);
1683 }
1684
1685 return APR_FROM_SQL_RESULT(rc);
1686}
1687
1688/** pvbquery: query using a prepared statement + binary args **/
1689static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows,
1690 apr_dbd_prepared_t *statement, va_list args)
1691{
1692 const char **values;
1693 int i;
1694
1695 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1696 for (i = 0; i < statement->nvals; i++)
1697 values[i] = va_arg(args, const char *);
1698 return odbc_pbquery(pool, handle, nrows, statement, (const void **)values);
1699}
1700
1701/** pvbselect: select using a prepared statement + binary args **/
1702static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle,
1703 apr_dbd_results_t **res,
1704 apr_dbd_prepared_t *statement,
1705 int random, va_list args)
1706{
1707 const char **values;
1708 int i;
1709
1710 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1711 for (i = 0; i < statement->nvals; i++)
1712 values[i] = va_arg(args, const char *);
1713 return odbc_pbselect(pool, handle, res, statement, random, (const void **)values);
1714}
1715
1716APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = {
1717 ODBC_DRIVER_STRING,
1718 odbc_init,
1719 odbc_native_handle,
1720 odbc_open,
1721 odbc_check_conn,
1722 odbc_close,
1723 odbc_set_dbname,
1724 odbc_start_transaction,
1725 odbc_end_transaction,
1726 odbc_query,
1727 odbc_select,
1728 odbc_num_cols,
1729 odbc_num_tuples,
1730 odbc_get_row,
1731 odbc_get_entry,
1732 odbc_error,
1733 odbc_escape,
1734 odbc_prepare,
1735 odbc_pvquery,
1736 odbc_pvselect,
1737 odbc_pquery,
1738 odbc_pselect,
1739 odbc_get_name,
1740 odbc_transaction_mode_get,
1741 odbc_transaction_mode_set,
1742 "?",
1743 odbc_pvbquery,
1744 odbc_pvbselect,
1745 odbc_pbquery,
1746 odbc_pbselect,
1747 odbc_datum_get
1748};
1749
1750#endif