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 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 */ 132 ODBC_INTPTR_T transaction_mode; 133 ODBC_INTPTR_T dboptions; /* driver options re SQLGetData */ 134 ODBC_INTPTR_T default_transaction_mode; 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; 377 ODBC_INTPTR_T maxsize, textsize, realsize, type, isunsigned = 1; 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]; 765 ODBC_INTPTR_T options = row->res->apr_dbd->dboptions; 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, 835 int **attrs, ODBC_INTPTR_T **attrvals) 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 *)); 841 *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(ODBC_INTPTR_T)); 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; 1081 ODBC_INTPTR_T *attrvals = NULL; 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, 1135 &(handle->default_transaction_mode), sizeof(ODBC_INTPTR_T), NULL); 1136 handle->transaction_mode = handle->default_transaction_mode; 1137 SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions), 1138 sizeof(ODBC_INTPTR_T), NULL); 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 1751