1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter#include "apu.h" 18251876Speter#if APU_HAVE_ODBC 19251876Speter 20251876Speter#include "apr.h" 21251876Speter#include "apr_strings.h" 22251876Speter#include "apr_buckets.h" 23251876Speter#include "apr_env.h" 24251876Speter#include "apr_file_io.h" 25251876Speter#include "apr_file_info.h" 26251876Speter#include "apr_dbd_internal.h" 27251876Speter#include "apr_thread_proc.h" 28251876Speter#include "apu_version.h" 29251876Speter#include "apu_config.h" 30251876Speter 31251876Speter#include <stdlib.h> 32251876Speter 33251876Speter/* If library is ODBC-V2, use macros for limited ODBC-V2 support 34251876Speter * No random access in V2. 35251876Speter */ 36251876Speter#ifdef ODBCV2 37251876Speter#define ODBCVER 0x0200 38251876Speter#include "apr_dbd_odbc_v2.h" 39251876Speter#endif 40251876Speter 41251876Speter/* standard ODBC include files */ 42251876Speter#ifdef HAVE_SQL_H 43251876Speter#include <sql.h> 44251876Speter#include <sqlext.h> 45251876Speter#elif defined(HAVE_ODBC_SQL_H) 46251876Speter#include <odbc/sql.h> 47251876Speter#include <odbc/sqlext.h> 48251876Speter#endif 49251876Speter 50272076Speter/* 51272076Speter* MSVC6 does not support intptr_t (C99) 52272076Speter* APR does not have a signed inptr type until 2.0 (r1557720) 53272076Speter*/ 54272076Speter#if defined(_MSC_VER) && _MSC_VER < 1400 55272076Speter#if APR_SIZEOF_VOIDP == 8 56272076Speter#define ODBC_INTPTR_T apr_int64_t 57272076Speter#else 58272076Speter#define ODBC_INTPTR_T apr_int32_t 59272076Speter#endif 60272076Speter#else 61272076Speter#define ODBC_INTPTR_T intptr_t 62272076Speter#endif 63272076Speter 64272076Speter 65251876Speter/* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver' 66251876Speter * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which 67251876Speter * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql. 68251876Speter */ 69251876Speter#ifndef ODBC_DRIVER_NAME 70251876Speter#define ODBC_DRIVER_NAME odbc 71251876Speter#endif 72251876Speter#define STRINGIFY(x) #x 73251876Speter#define NAMIFY2(n) apr_dbd_##n##_driver 74251876Speter#define NAMIFY1(n) NAMIFY2(n) 75251876Speter#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME) 76251876Speter#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME) 77251876Speter 78251876Speter/* Required APR version for this driver */ 79251876Speter#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION 80251876Speter#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION 81251876Speter 82251876Speterstatic SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */ 83251876Speter 84251876Speter/* Use a CHECK_ERROR macro so we can grab the source line numbers 85251876Speter * for error reports 86251876Speter */ 87251876Speterstatic void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc, 88251876Speter SQLSMALLINT type, SQLHANDLE h, int line); 89251876Speter#define CHECK_ERROR(a,s,r,t,h) check_error(a,s,r,t,h, __LINE__) 90251876Speter 91251876Speter#define SOURCE_FILE __FILE__ /* source file for error messages */ 92251876Speter#define MAX_ERROR_STRING 1024 /* max length of message in dbc */ 93251876Speter#define MAX_COLUMN_NAME 256 /* longest column name recognized */ 94251876Speter#define DEFAULT_BUFFER_SIZE 1024 /* value for defaultBufferSize */ 95251876Speter 96251876Speter#define MAX_PARAMS 20 97251876Speter#define DEFAULTSEPS " \t\r\n,=" 98251876Speter#define CSINGLEQUOTE '\'' 99251876Speter#define SSINGLEQUOTE "\'" 100251876Speter 101251876Speter#define TEXTMODE 1 /* used for text (APR 1.2) mode params */ 102251876Speter#define BINARYMODE 0 /* used for binary (APR 1.3+) mode params */ 103251876Speter 104251876Speter/* Identify datatypes which are LOBs 105251876Speter * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB 106251876Speter */ 107251876Speter#define IS_LOB(t) (t == SQL_LONGVARCHAR \ 108251876Speter || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \ 109251876Speter || t == -98 || t == -99) 110251876Speter 111251876Speter/* These types are CLOBs 112251876Speter * - DB2 DRDA driver uses undefined type -98 for CLOB 113251876Speter */ 114251876Speter#define IS_CLOB(t) \ 115251876Speter (t == SQL_LONGVARCHAR || t == -98) 116251876Speter 117251876Speter/* Convert a SQL result to an APR result */ 118251876Speter#define APR_FROM_SQL_RESULT(rc) \ 119251876Speter (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL) 120251876Speter 121251876Speter/* DBD opaque structures */ 122251876Speterstruct apr_dbd_t 123251876Speter{ 124251876Speter SQLHANDLE dbc; /* SQL connection handle - NULL after close */ 125251876Speter apr_pool_t *pool; /* connection lifetime pool */ 126251876Speter char *dbname; /* ODBC datasource */ 127251876Speter int lasterrorcode; 128251876Speter int lineNumber; 129251876Speter char lastError[MAX_ERROR_STRING]; 130251876Speter int defaultBufferSize; /* used for CLOBs in text mode, 131251876Speter * and when fld size is indeterminate */ 132272076Speter ODBC_INTPTR_T transaction_mode; 133272076Speter ODBC_INTPTR_T dboptions; /* driver options re SQLGetData */ 134272076Speter ODBC_INTPTR_T default_transaction_mode; 135251876Speter int can_commit; /* controls end_trans behavior */ 136251876Speter}; 137251876Speter 138251876Speterstruct apr_dbd_results_t 139251876Speter{ 140251876Speter SQLHANDLE stmt; /* parent sql statement handle */ 141251876Speter SQLHANDLE dbc; /* parent sql connection handle */ 142251876Speter apr_pool_t *pool; /* pool from query or select */ 143251876Speter apr_dbd_t *apr_dbd; /* parent DBD connection handle */ 144251876Speter int random; /* random access requested */ 145251876Speter int ncols; /* number of columns */ 146251876Speter int isclosed; /* cursor has been closed */ 147251876Speter char **colnames; /* array of column names (NULL until used) */ 148251876Speter SQLPOINTER *colptrs; /* pointers to column data */ 149251876Speter SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */ 150251876Speter SQLINTEGER *coltextsizes; /* max-sizes if converted to text */ 151251876Speter SQLSMALLINT *coltypes; /* array of SQL data types for columns */ 152251876Speter SQLLEN *colinds; /* array of SQL data indicator/strlens */ 153251876Speter int *colstate; /* array of column states 154251876Speter * - avail, bound, present, unavail 155251876Speter */ 156251876Speter int *all_data_fetched; /* flags data as all fetched, for LOBs */ 157251876Speter void *data; /* buffer for all data for one row */ 158251876Speter}; 159251876Speter 160251876Speterenum /* results column states */ 161251876Speter{ 162251876Speter COL_AVAIL, /* data may be retrieved with SQLGetData */ 163251876Speter COL_PRESENT, /* data has been retrieved with SQLGetData */ 164251876Speter COL_BOUND, /* column is bound to colptr */ 165251876Speter COL_RETRIEVED, /* all data from column has been returned */ 166251876Speter COL_UNAVAIL /* column is unavailable because ODBC driver 167251876Speter * requires that columns be retrieved 168251876Speter * in ascending order and a higher col 169251876Speter * was accessed 170251876Speter */ 171251876Speter}; 172251876Speter 173251876Speterstruct apr_dbd_row_t { 174251876Speter SQLHANDLE stmt; /* parent ODBC statement handle */ 175251876Speter SQLHANDLE dbc; /* parent ODBC connection handle */ 176251876Speter apr_pool_t *pool; /* pool from get_row */ 177251876Speter apr_dbd_results_t *res; 178251876Speter}; 179251876Speter 180251876Speterstruct apr_dbd_transaction_t { 181251876Speter SQLHANDLE dbc; /* parent ODBC connection handle */ 182251876Speter apr_dbd_t *apr_dbd; /* parent DBD connection handle */ 183251876Speter}; 184251876Speter 185251876Speterstruct apr_dbd_prepared_t { 186251876Speter SQLHANDLE stmt; /* ODBC statement handle */ 187251876Speter SQLHANDLE dbc; /* parent ODBC connection handle */ 188251876Speter apr_dbd_t *apr_dbd; 189251876Speter int nargs; 190251876Speter int nvals; 191251876Speter int *types; /* array of DBD data types */ 192251876Speter}; 193251876Speter 194251876Speterstatic void odbc_lob_bucket_destroy(void *data); 195251876Speterstatic apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool); 196251876Speterstatic apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str, 197251876Speter apr_size_t *len, apr_read_type_e block); 198251876Speter 199251876Speter/* the ODBC LOB bucket type */ 200251876Speterstatic const apr_bucket_type_t odbc_bucket_type = { 201251876Speter "ODBC_LOB", 5, APR_BUCKET_DATA, 202251876Speter odbc_lob_bucket_destroy, 203251876Speter odbc_lob_bucket_read, 204251876Speter odbc_lob_bucket_setaside, 205251876Speter apr_bucket_shared_split, 206251876Speter apr_bucket_shared_copy 207251876Speter}; 208251876Speter 209251876Speter/* ODBC LOB bucket data */ 210251876Spetertypedef struct { 211251876Speter /** Ref count for shared bucket */ 212251876Speter apr_bucket_refcount refcount; 213251876Speter const apr_dbd_row_t *row; 214251876Speter int col; 215251876Speter SQLSMALLINT type; 216251876Speter} odbc_bucket; 217251876Speter 218251876Speter/* SQL datatype mappings to DBD datatypes 219251876Speter * These tables must correspond *exactly* to the apr_dbd_type_e enum 220251876Speter * in apr_dbd.h 221251876Speter */ 222251876Speter 223251876Speter/* ODBC "C" types to DBD datatypes */ 224251876Speterstatic SQLSMALLINT const sqlCtype[] = { 225251876Speter SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */ 226251876Speter SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */ 227251876Speter SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */ 228251876Speter SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */ 229251876Speter SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */ 230251876Speter SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */ 231251876Speter SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */ 232251876Speter SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */ 233251876Speter SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */ 234251876Speter SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */ 235251876Speter SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */ 236251876Speter SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */ 237251876Speter SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */ 238251876Speter SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */ 239251876Speter SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */ 240251876Speter SQL_C_CHAR, /*SQL_C_TYPE_TIME, APR_DBD_TYPE_TIME, \%pDi */ 241251876Speter SQL_C_CHAR, /*SQL_C_TYPE_DATE, APR_DBD_TYPE_DATE, \%pDd */ 242251876Speter SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */ 243251876Speter SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */ 244251876Speter SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */ 245251876Speter SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */ 246251876Speter SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */ 247251876Speter SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */ 248251876Speter}; 249251876Speter#define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0])) 250251876Speter 251251876Speter/* ODBC Base types to DBD datatypes */ 252251876Speterstatic SQLSMALLINT const sqlBaseType[] = { 253251876Speter SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */ 254251876Speter SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */ 255251876Speter SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */ 256251876Speter SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */ 257251876Speter SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */ 258251876Speter SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */ 259251876Speter SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */ 260251876Speter SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */ 261251876Speter SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */ 262251876Speter SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */ 263251876Speter SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */ 264251876Speter SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */ 265251876Speter SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */ 266251876Speter SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */ 267251876Speter SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */ 268251876Speter SQL_CHAR, /*SQL_TIME, APR_DBD_TYPE_TIME, \%pDi */ 269251876Speter SQL_CHAR, /*SQL_DATE, APR_DBD_TYPE_DATE, \%pDd */ 270251876Speter SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */ 271251876Speter SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */ 272251876Speter SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */ 273251876Speter SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */ 274251876Speter SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */ 275251876Speter SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */ 276251876Speter}; 277251876Speter 278251876Speter/* result sizes for DBD datatypes (-1 for null-terminated) */ 279251876Speterstatic int const sqlSizes[] = { 280251876Speter 0, 281251876Speter sizeof(char), /**< \%hhd out: char* */ 282251876Speter sizeof(unsigned char), /**< \%hhu out: unsigned char* */ 283251876Speter sizeof(short), /**< \%hd out: short* */ 284251876Speter sizeof(unsigned short), /**< \%hu out: unsigned short* */ 285251876Speter sizeof(int), /**< \%d out: int* */ 286251876Speter sizeof(unsigned int), /**< \%u out: unsigned int* */ 287251876Speter sizeof(long), /**< \%ld out: long* */ 288251876Speter sizeof(unsigned long), /**< \%lu out: unsigned long* */ 289251876Speter sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */ 290251876Speter sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */ 291251876Speter sizeof(float), /**< \%f out: float* */ 292251876Speter sizeof(double), /**< \%lf out: double* */ 293251876Speter -1, /**< \%s out: char** */ 294251876Speter -1, /**< \%pDt out: char** */ 295251876Speter -1, /**< \%pDi out: char** */ 296251876Speter -1, /**< \%pDd out: char** */ 297251876Speter -1, /**< \%pDa out: char** */ 298251876Speter -1, /**< \%pDs out: char** */ 299251876Speter -1, /**< \%pDz out: char** */ 300251876Speter sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */ 301251876Speter sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */ 302251876Speter 0 /**< \%pDn : in: void*, out: void** */ 303251876Speter}; 304251876Speter 305251876Speter/* 306251876Speter * local functions 307251876Speter */ 308251876Speter 309251876Speter/* close any open results for the connection */ 310251876Speterstatic apr_status_t odbc_close_results(void *d) 311251876Speter{ 312251876Speter apr_dbd_results_t *dbr = (apr_dbd_results_t *)d; 313251876Speter SQLRETURN rc = SQL_SUCCESS; 314251876Speter 315251876Speter if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) { 316251876Speter if (!dbr->isclosed) 317251876Speter rc = SQLCloseCursor(dbr->stmt); 318251876Speter dbr->isclosed = 1; 319251876Speter } 320251876Speter return APR_FROM_SQL_RESULT(rc); 321251876Speter} 322251876Speter 323251876Speter/* close the ODBC statement handle from a prepare */ 324251876Speterstatic apr_status_t odbc_close_pstmt(void *s) 325251876Speter{ 326251876Speter SQLRETURN rc = APR_SUCCESS; 327251876Speter apr_dbd_prepared_t *statement = s; 328251876Speter 329251876Speter /* stmt is closed if connection has already been closed */ 330251876Speter if (statement) { 331251876Speter SQLHANDLE hstmt = statement->stmt; 332251876Speter 333251876Speter if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) { 334251876Speter rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt); 335251876Speter } 336251876Speter statement->stmt = NULL; 337251876Speter } 338251876Speter return APR_FROM_SQL_RESULT(rc); 339251876Speter} 340251876Speter 341251876Speter/* close: close/release a connection obtained from open() */ 342251876Speterstatic apr_status_t odbc_close(apr_dbd_t *handle) 343251876Speter{ 344251876Speter SQLRETURN rc = SQL_SUCCESS; 345251876Speter 346251876Speter if (handle->dbc) { 347251876Speter rc = SQLDisconnect(handle->dbc); 348251876Speter CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc); 349251876Speter rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc); 350251876Speter CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv); 351251876Speter handle->dbc = NULL; 352251876Speter } 353251876Speter return APR_FROM_SQL_RESULT(rc); 354251876Speter} 355251876Speter 356251876Speter/* odbc_close re-defined for passing to pool cleanup */ 357251876Speterstatic apr_status_t odbc_close_cleanup(void *handle) 358251876Speter{ 359251876Speter return odbc_close((apr_dbd_t *)handle); 360251876Speter} 361251876Speter 362251876Speter/* close the ODBC environment handle at process termination */ 363251876Speterstatic apr_status_t odbc_close_env(SQLHANDLE henv) 364251876Speter{ 365251876Speter SQLRETURN rc; 366251876Speter 367251876Speter rc = SQLFreeHandle(SQL_HANDLE_ENV, henv); 368251876Speter henv = NULL; 369251876Speter return APR_FROM_SQL_RESULT(rc); 370251876Speter} 371251876Speter 372251876Speter/* setup the arrays in results for all the returned columns */ 373251876Speterstatic SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t *res, 374251876Speter SQLHANDLE stmt) 375251876Speter{ 376251876Speter SQLRETURN rc; 377272076Speter ODBC_INTPTR_T maxsize, textsize, realsize, type, isunsigned = 1; 378251876Speter 379251876Speter /* discover the sql type */ 380251876Speter rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL, 381251876Speter (SQLPOINTER)&isunsigned); 382251876Speter isunsigned = (isunsigned == SQL_TRUE); 383251876Speter 384251876Speter rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL, 385251876Speter (SQLPOINTER)&type); 386251876Speter if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE) { 387251876Speter /* MANY ODBC v2 datasources only supply CONCISE_TYPE */ 388251876Speter rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL, 389251876Speter 0, NULL, (SQLPOINTER)&type); 390251876Speter } 391251876Speter 392251876Speter if (!SQL_SUCCEEDED(rc)) { 393251876Speter /* if still unknown make it CHAR */ 394251876Speter type = SQL_C_CHAR; 395251876Speter } 396251876Speter 397251876Speter switch (type) { 398251876Speter case SQL_INTEGER: 399251876Speter case SQL_SMALLINT: 400251876Speter case SQL_TINYINT: 401251876Speter case SQL_BIGINT: 402251876Speter /* fix these numeric binary types up as signed/unsigned for C types */ 403251876Speter type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET; 404251876Speter break; 405251876Speter /* LOB types are not changed to C types */ 406251876Speter case SQL_LONGVARCHAR: 407251876Speter type = SQL_LONGVARCHAR; 408251876Speter break; 409251876Speter case SQL_LONGVARBINARY: 410251876Speter type = SQL_LONGVARBINARY; 411251876Speter break; 412251876Speter case SQL_FLOAT : 413251876Speter type = SQL_C_FLOAT; 414251876Speter break; 415251876Speter case SQL_DOUBLE : 416251876Speter type = SQL_C_DOUBLE; 417251876Speter break; 418251876Speter 419251876Speter /* DBD wants times as strings */ 420251876Speter case SQL_TIMESTAMP: 421251876Speter case SQL_DATE: 422251876Speter case SQL_TIME: 423251876Speter default: 424251876Speter type = SQL_C_CHAR; 425251876Speter } 426251876Speter 427258602Speter res->coltypes[icol] = (SQLSMALLINT)type; 428251876Speter 429251876Speter /* size if retrieved as text */ 430251876Speter rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, 431251876Speter NULL, (SQLPOINTER)&textsize); 432251876Speter if (!SQL_SUCCEEDED(rc) || textsize < 0) { 433251876Speter textsize = res->apr_dbd->defaultBufferSize; 434251876Speter } 435251876Speter /* for null-term, which sometimes isn't included */ 436251876Speter textsize++; 437251876Speter 438251876Speter /* real size */ 439251876Speter rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, 440251876Speter NULL, (SQLPOINTER)&realsize); 441251876Speter if (!SQL_SUCCEEDED(rc)) { 442251876Speter realsize = textsize; 443251876Speter } 444251876Speter 445251876Speter maxsize = (textsize > realsize) ? textsize : realsize; 446251876Speter if (IS_LOB(type) || maxsize <= 0) { 447251876Speter /* LOB types are never bound and have a NULL colptr for binary. 448251876Speter * Ingore their real (1-2gb) length & use a default - the larger 449251876Speter * of defaultBufferSize or APR_BUCKET_BUFF_SIZE. 450251876Speter * If not a LOB, but simply unknown length - always use defaultBufferSize. 451251876Speter */ 452251876Speter maxsize = res->apr_dbd->defaultBufferSize; 453251876Speter if (IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE) { 454251876Speter maxsize = APR_BUCKET_BUFF_SIZE; 455251876Speter } 456251876Speter 457251876Speter res->colptrs[icol] = NULL; 458251876Speter res->colstate[icol] = COL_AVAIL; 459258602Speter res->colsizes[icol] = (SQLINTEGER)maxsize; 460251876Speter rc = SQL_SUCCESS; 461251876Speter } 462251876Speter else { 463251876Speter res->colptrs[icol] = apr_pcalloc(res->pool, maxsize); 464258602Speter res->colsizes[icol] = (SQLINTEGER)maxsize; 465251876Speter if (res->apr_dbd->dboptions & SQL_GD_BOUND) { 466251876Speter /* we are allowed to call SQLGetData if we need to */ 467251876Speter rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol], 468251876Speter res->colptrs[icol], maxsize, 469251876Speter &(res->colinds[icol])); 470251876Speter CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT, 471251876Speter stmt); 472251876Speter res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL; 473251876Speter } 474251876Speter else { 475251876Speter /* this driver won't allow us to call SQLGetData on bound 476251876Speter * columns - so don't bind any 477251876Speter */ 478251876Speter res->colstate[icol] = COL_AVAIL; 479251876Speter rc = SQL_SUCCESS; 480251876Speter } 481251876Speter } 482251876Speter return rc; 483251876Speter} 484251876Speter 485251876Speter/* create and populate an apr_dbd_results_t for a select */ 486251876Speterstatic SQLRETURN odbc_create_results(apr_dbd_t *handle, SQLHANDLE hstmt, 487251876Speter apr_pool_t *pool, const int random, 488251876Speter apr_dbd_results_t **res) 489251876Speter{ 490251876Speter SQLRETURN rc; 491251876Speter SQLSMALLINT ncols; 492251876Speter 493251876Speter *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 494251876Speter (*res)->stmt = hstmt; 495251876Speter (*res)->dbc = handle->dbc; 496251876Speter (*res)->pool = pool; 497251876Speter (*res)->random = random; 498251876Speter (*res)->apr_dbd = handle; 499251876Speter rc = SQLNumResultCols(hstmt, &ncols); 500251876Speter CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt); 501251876Speter (*res)->ncols = ncols; 502251876Speter 503251876Speter if (SQL_SUCCEEDED(rc)) { 504251876Speter int i; 505251876Speter 506251876Speter (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *)); 507251876Speter (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *)); 508251876Speter (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER)); 509251876Speter (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT)); 510251876Speter (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN)); 511251876Speter (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int)); 512251876Speter (*res)->ncols = ncols; 513251876Speter 514251876Speter for (i = 0; i < ncols; i++) { 515251876Speter odbc_set_result_column(i, (*res), hstmt); 516251876Speter } 517251876Speter } 518251876Speter return rc; 519251876Speter} 520251876Speter 521251876Speter 522251876Speter/* bind a parameter - input params only, does not support output parameters */ 523251876Speterstatic SQLRETURN odbc_bind_param(apr_pool_t *pool, 524251876Speter apr_dbd_prepared_t *statement, const int narg, 525251876Speter const SQLSMALLINT type, int *argp, 526251876Speter const void **args, const int textmode) 527251876Speter{ 528251876Speter SQLRETURN rc; 529251876Speter SQLSMALLINT baseType, cType; 530251876Speter void *ptr; 531251876Speter SQLULEN len; 532251876Speter SQLLEN *indicator; 533251876Speter static SQLLEN nullValue = SQL_NULL_DATA; 534251876Speter static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */ 535251876Speter 536251876Speter /* bind a NULL data value */ 537251876Speter if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) { 538251876Speter baseType = SQL_CHAR; 539251876Speter cType = SQL_C_CHAR; 540251876Speter ptr = &nullValue; 541251876Speter len = sizeof(SQLINTEGER); 542251876Speter indicator = &nullValue; 543251876Speter (*argp)++; 544251876Speter } 545251876Speter /* bind a non-NULL data value */ 546251876Speter else { 547251876Speter if (type < 0 || type >= NUM_APR_DBD_TYPES) { 548251876Speter return APR_EGENERAL; 549251876Speter } 550251876Speter 551251876Speter baseType = sqlBaseType[type]; 552251876Speter cType = sqlCtype[type]; 553251876Speter indicator = NULL; 554251876Speter /* LOBs */ 555251876Speter if (IS_LOB(cType)) { 556251876Speter ptr = (void *)args[*argp]; 557251876Speter len = (SQLULEN) * (apr_size_t *)args[*argp + 1]; 558251876Speter cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT; 559251876Speter (*argp) += 4; /* LOBs consume 4 args (last two are unused) */ 560251876Speter } 561251876Speter /* non-LOBs */ 562251876Speter else { 563251876Speter switch (baseType) { 564251876Speter case SQL_CHAR: 565251876Speter case SQL_DATE: 566251876Speter case SQL_TIME: 567251876Speter case SQL_TIMESTAMP: 568251876Speter ptr = (void *)args[*argp]; 569251876Speter len = (SQLULEN)strlen(ptr); 570251876Speter break; 571251876Speter case SQL_TINYINT: 572251876Speter ptr = apr_palloc(pool, sizeof(unsigned char)); 573251876Speter len = sizeof(unsigned char); 574251876Speter *(unsigned char *)ptr = 575251876Speter (textmode ? 576251876Speter atoi(args[*argp]) : *(unsigned char *)args[*argp]); 577251876Speter break; 578251876Speter case SQL_SMALLINT: 579251876Speter ptr = apr_palloc(pool, sizeof(short)); 580251876Speter len = sizeof(short); 581251876Speter *(short *)ptr = 582251876Speter (textmode ? atoi(args[*argp]) : *(short *)args[*argp]); 583251876Speter break; 584251876Speter case SQL_INTEGER: 585251876Speter ptr = apr_palloc(pool, sizeof(int)); 586251876Speter len = sizeof(int); 587251876Speter *(long *)ptr = 588251876Speter (textmode ? atol(args[*argp]) : *(long *)args[*argp]); 589251876Speter break; 590251876Speter case SQL_FLOAT: 591251876Speter ptr = apr_palloc(pool, sizeof(float)); 592251876Speter len = sizeof(float); 593251876Speter *(float *)ptr = 594251876Speter (textmode ? 595251876Speter (float)atof(args[*argp]) : *(float *)args[*argp]); 596251876Speter break; 597251876Speter case SQL_DOUBLE: 598251876Speter ptr = apr_palloc(pool, sizeof(double)); 599251876Speter len = sizeof(double); 600251876Speter *(double *)ptr = 601251876Speter (textmode ? atof(args[*argp]) : *(double *) 602251876Speter args[*argp]); 603251876Speter break; 604251876Speter case SQL_BIGINT: 605251876Speter ptr = apr_palloc(pool, sizeof(apr_int64_t)); 606251876Speter len = sizeof(apr_int64_t); 607251876Speter *(apr_int64_t *)ptr = 608251876Speter (textmode ? 609251876Speter apr_atoi64(args[*argp]) : *(apr_int64_t *)args[*argp]); 610251876Speter break; 611251876Speter default: 612251876Speter return APR_EGENERAL; 613251876Speter } 614251876Speter (*argp)++; /* non LOBs consume one argument */ 615251876Speter } 616251876Speter } 617251876Speter rc = SQLBindParameter(statement->stmt, narg, inOut, cType, 618251876Speter baseType, len, 0, ptr, len, indicator); 619251876Speter CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT, 620251876Speter statement->stmt); 621251876Speter return rc; 622251876Speter} 623251876Speter 624251876Speter/* LOB / Bucket Brigade functions */ 625251876Speter 626251876Speter/* bucket type specific destroy */ 627251876Speterstatic void odbc_lob_bucket_destroy(void *data) 628251876Speter{ 629251876Speter odbc_bucket *bd = data; 630251876Speter 631251876Speter if (apr_bucket_shared_destroy(bd)) 632251876Speter apr_bucket_free(bd); 633251876Speter} 634251876Speter 635251876Speter/* set aside a bucket if possible */ 636251876Speterstatic apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool) 637251876Speter{ 638251876Speter odbc_bucket *bd = (odbc_bucket *)e->data; 639251876Speter 640251876Speter /* Unlikely - but if the row pool is ancestor of this pool then it is OK */ 641251876Speter if (apr_pool_is_ancestor(bd->row->pool, pool)) 642251876Speter return APR_SUCCESS; 643251876Speter 644251876Speter return apr_bucket_setaside_notimpl(e, pool); 645251876Speter} 646251876Speter 647251876Speter/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */ 648251876Speterstatic apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str, 649251876Speter apr_size_t *len, apr_read_type_e block) 650251876Speter{ 651251876Speter SQLRETURN rc; 652251876Speter SQLLEN len_indicator; 653251876Speter SQLSMALLINT type; 654251876Speter odbc_bucket *bd = (odbc_bucket *)e->data; 655251876Speter apr_bucket *nxt; 656251876Speter void *buf; 657251876Speter int bufsize = bd->row->res->apr_dbd->defaultBufferSize; 658251876Speter int eos; 659251876Speter 660251876Speter /* C type is CHAR for CLOBs, DEFAULT for BLOBs */ 661251876Speter type = bd->row->res->coltypes[bd->col]; 662251876Speter type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT; 663251876Speter 664251876Speter /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE, 665251876Speter * but they may be much bigger per the BUFSIZE parameter. 666251876Speter */ 667251876Speter if (bufsize < APR_BUCKET_BUFF_SIZE) 668251876Speter bufsize = APR_BUCKET_BUFF_SIZE; 669251876Speter 670251876Speter buf = apr_bucket_alloc(bufsize, e->list); 671251876Speter *str = NULL; 672251876Speter *len = 0; 673251876Speter 674251876Speter rc = SQLGetData(bd->row->res->stmt, bd->col + 1, 675251876Speter type, buf, bufsize, 676251876Speter &len_indicator); 677251876Speter 678251876Speter CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc, 679251876Speter SQL_HANDLE_STMT, bd->row->res->stmt); 680251876Speter 681251876Speter if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0) 682251876Speter len_indicator = 0; 683251876Speter 684251876Speter if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) { 685251876Speter 686251876Speter if (rc == SQL_SUCCESS_WITH_INFO 687251876Speter && (len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize)) { 688251876Speter /* not the last read = a full buffer. CLOBs have a null terminator */ 689251876Speter *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 ); 690251876Speter 691251876Speter eos = 0; 692251876Speter } 693251876Speter else { 694251876Speter /* the last read - len_indicator is supposed to be the length, 695251876Speter * but some driver get this wrong and return the total length. 696251876Speter * We try to handle both interpretations. 697251876Speter */ 698251876Speter *len = (len_indicator > bufsize 699251876Speter && len_indicator >= (SQLLEN)e->start) 700251876Speter ? (len_indicator - (SQLLEN)e->start) : len_indicator; 701251876Speter 702251876Speter eos = 1; 703251876Speter } 704251876Speter 705251876Speter if (!eos) { 706251876Speter /* Create a new LOB bucket to append and append it */ 707251876Speter nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list); 708251876Speter APR_BUCKET_INIT(nxt); 709251876Speter nxt->length = -1; 710251876Speter nxt->data = e->data; 711251876Speter nxt->type = &odbc_bucket_type; 712251876Speter nxt->free = apr_bucket_free; 713251876Speter nxt->list = e->list; 714251876Speter nxt->start = e->start + *len; 715251876Speter APR_BUCKET_INSERT_AFTER(e, nxt); 716251876Speter } 717251876Speter else { 718251876Speter odbc_lob_bucket_destroy(e->data); 719251876Speter } 720251876Speter /* make current bucket into a heap bucket */ 721251876Speter apr_bucket_heap_make(e, buf, *len, apr_bucket_free); 722251876Speter *str = buf; 723251876Speter 724251876Speter /* No data is success in this context */ 725251876Speter rc = SQL_SUCCESS; 726251876Speter } 727251876Speter return APR_FROM_SQL_RESULT(rc); 728251876Speter} 729251876Speter 730251876Speter/* Create a bucket brigade on the row pool for a LOB column */ 731251876Speterstatic apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col, 732251876Speter SQLSMALLINT type, apr_bucket_brigade *bb) 733251876Speter{ 734251876Speter apr_bucket_alloc_t *list = bb->bucket_alloc; 735251876Speter apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 736251876Speter odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list); 737251876Speter apr_bucket *eos = apr_bucket_eos_create(list); 738251876Speter 739251876Speter bd->row = row; 740251876Speter bd->col = col; 741251876Speter bd->type = type; 742251876Speter 743251876Speter APR_BUCKET_INIT(b); 744251876Speter b->type = &odbc_bucket_type; 745251876Speter b->free = apr_bucket_free; 746251876Speter b->list = list; 747251876Speter /* LOB lengths are unknown in ODBC */ 748251876Speter b = apr_bucket_shared_make(b, bd, 0, -1); 749251876Speter 750251876Speter APR_BRIGADE_INSERT_TAIL(bb, b); 751251876Speter APR_BRIGADE_INSERT_TAIL(bb, eos); 752251876Speter 753251876Speter return APR_SUCCESS; 754251876Speter} 755251876Speter 756251876Speter/* returns a data pointer for a column, returns NULL for NULL value, 757251876Speter * return -1 if data not available 758251876Speter */ 759251876Speterstatic void *odbc_get(const apr_dbd_row_t *row, const int col, 760251876Speter const SQLSMALLINT sqltype) 761251876Speter{ 762251876Speter SQLRETURN rc; 763251876Speter SQLLEN indicator; 764251876Speter int state = row->res->colstate[col]; 765272076Speter ODBC_INTPTR_T options = row->res->apr_dbd->dboptions; 766251876Speter 767251876Speter switch (state) { 768251876Speter case (COL_UNAVAIL): 769251876Speter return (void *)-1; 770251876Speter case (COL_RETRIEVED): 771251876Speter return NULL; 772251876Speter 773251876Speter case (COL_BOUND): 774251876Speter case (COL_PRESENT): 775251876Speter if (sqltype == row->res->coltypes[col]) { 776251876Speter /* same type and we already have the data */ 777251876Speter row->res->colstate[col] = COL_RETRIEVED; 778251876Speter return (row->res->colinds[col] == SQL_NULL_DATA) ? 779251876Speter NULL : row->res->colptrs[col]; 780251876Speter } 781251876Speter } 782251876Speter 783251876Speter /* we need to get the data now */ 784251876Speter if (!(options & SQL_GD_ANY_ORDER)) { 785251876Speter /* this ODBC driver requires columns to be retrieved in order, 786251876Speter * so we attempt to get every prior un-gotten non-LOB column 787251876Speter */ 788251876Speter int i; 789251876Speter for (i = 0; i < col; i++) { 790251876Speter if (row->res->colstate[i] == COL_AVAIL) { 791251876Speter if (IS_LOB(row->res->coltypes[i])) 792251876Speter row->res->colstate[i] = COL_UNAVAIL; 793251876Speter else { 794251876Speter odbc_get(row, i, row->res->coltypes[i]); 795251876Speter row->res->colstate[i] = COL_PRESENT; 796251876Speter } 797251876Speter } 798251876Speter } 799251876Speter } 800251876Speter 801251876Speter if ((state == COL_BOUND && !(options & SQL_GD_BOUND))) 802251876Speter /* this driver won't let us re-get bound columns */ 803251876Speter return (void *)-1; 804251876Speter 805251876Speter /* a LOB might not have a buffer allocated yet - so create one */ 806251876Speter if (!row->res->colptrs[col]) 807251876Speter row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]); 808251876Speter 809251876Speter rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col], 810251876Speter row->res->colsizes[col], &indicator); 811251876Speter CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT, 812251876Speter row->res->stmt); 813251876Speter if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA) 814251876Speter return NULL; 815251876Speter 816251876Speter if (SQL_SUCCEEDED(rc)) { 817251876Speter /* whatever it was originally, it is now this sqltype */ 818251876Speter row->res->coltypes[col] = sqltype; 819251876Speter /* this allows getting CLOBs in text mode by calling get_entry 820251876Speter * until it returns NULL 821251876Speter */ 822251876Speter row->res->colstate[col] = 823251876Speter (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED; 824251876Speter return row->res->colptrs[col]; 825251876Speter } 826251876Speter else 827251876Speter return (void *)-1; 828251876Speter} 829251876Speter 830251876Speter/* Parse the parameter string for open */ 831251876Speterstatic apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params, 832251876Speter int *connect, SQLCHAR **datasource, 833251876Speter SQLCHAR **user, SQLCHAR **password, 834251876Speter int *defaultBufferSize, int *nattrs, 835272076Speter int **attrs, ODBC_INTPTR_T **attrvals) 836251876Speter{ 837251876Speter char *seps, *last, *next, *name[MAX_PARAMS], *val[MAX_PARAMS]; 838251876Speter int nparams = 0, i, j; 839251876Speter 840251876Speter *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *)); 841272076Speter *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(ODBC_INTPTR_T)); 842251876Speter *nattrs = 0; 843251876Speter seps = DEFAULTSEPS; 844251876Speter name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last); 845251876Speter 846251876Speter /* no params is OK here - let connect return a more useful error msg */ 847251876Speter if (!name[nparams]) 848251876Speter return SQL_SUCCESS; 849251876Speter 850251876Speter do { 851251876Speter if (last[strspn(last, seps)] == CSINGLEQUOTE) { 852251876Speter last += strspn(last, seps); 853251876Speter seps=SSINGLEQUOTE; 854251876Speter } 855251876Speter val[nparams] = apr_strtok(NULL, seps, &last); 856251876Speter seps = DEFAULTSEPS; 857251876Speter 858251876Speter ++nparams; 859251876Speter next = apr_strtok(NULL, seps, &last); 860251876Speter if (!next) { 861251876Speter break; 862251876Speter } 863251876Speter if (nparams >= MAX_PARAMS) { 864251876Speter /* too many parameters, no place to store */ 865251876Speter return APR_EGENERAL; 866251876Speter } 867251876Speter name[nparams] = next; 868251876Speter } while (1); 869251876Speter 870251876Speter for (j = i = 0; i < nparams; i++) { 871251876Speter if (!apr_strnatcasecmp(name[i], "CONNECT")) { 872251876Speter *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]); 873251876Speter *connect = 1; 874251876Speter } 875251876Speter else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) { 876251876Speter *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]); 877251876Speter *connect = 0; 878251876Speter } 879251876Speter else if (!apr_strnatcasecmp(name[i], "USER")) { 880251876Speter *user = (SQLCHAR *)apr_pstrdup(pool, val[i]); 881251876Speter } 882251876Speter else if (!apr_strnatcasecmp(name[i], "PASSWORD")) { 883251876Speter *password = (SQLCHAR *)apr_pstrdup(pool, val[i]); 884251876Speter } 885251876Speter else if (!apr_strnatcasecmp(name[i], "BUFSIZE")) { 886251876Speter *defaultBufferSize = atoi(val[i]); 887251876Speter } 888251876Speter else if (!apr_strnatcasecmp(name[i], "ACCESS")) { 889251876Speter if (!apr_strnatcasecmp(val[i], "READ_ONLY")) 890251876Speter (*attrvals)[j] = SQL_MODE_READ_ONLY; 891251876Speter else if (!apr_strnatcasecmp(val[i], "READ_WRITE")) 892251876Speter (*attrvals)[j] = SQL_MODE_READ_WRITE; 893251876Speter else 894251876Speter return SQL_ERROR; 895251876Speter (*attrs)[j++] = SQL_ATTR_ACCESS_MODE; 896251876Speter } 897251876Speter else if (!apr_strnatcasecmp(name[i], "CTIMEOUT")) { 898251876Speter (*attrvals)[j] = atoi(val[i]); 899251876Speter (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT; 900251876Speter } 901251876Speter else if (!apr_strnatcasecmp(name[i], "STIMEOUT")) { 902251876Speter (*attrvals)[j] = atoi(val[i]); 903251876Speter (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT; 904251876Speter } 905251876Speter else if (!apr_strnatcasecmp(name[i], "TXMODE")) { 906251876Speter if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED")) 907251876Speter (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED; 908251876Speter else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED")) 909251876Speter (*attrvals)[j] = SQL_TXN_READ_COMMITTED; 910251876Speter else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ")) 911251876Speter (*attrvals)[j] = SQL_TXN_REPEATABLE_READ; 912251876Speter else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE")) 913251876Speter (*attrvals)[j] = SQL_TXN_SERIALIZABLE; 914251876Speter else if (!apr_strnatcasecmp(val[i], "DEFAULT")) 915251876Speter continue; 916251876Speter else 917251876Speter return SQL_ERROR; 918251876Speter (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION; 919251876Speter } 920251876Speter else 921251876Speter return SQL_ERROR; 922251876Speter } 923251876Speter *nattrs = j; 924251876Speter return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR; 925251876Speter} 926251876Speter 927251876Speter/* common handling after ODBC calls - save error info (code and text) in dbc */ 928251876Speterstatic void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc, 929251876Speter SQLSMALLINT type, SQLHANDLE h, int line) 930251876Speter{ 931251876Speter SQLCHAR buffer[512]; 932251876Speter SQLCHAR sqlstate[128]; 933251876Speter SQLINTEGER native; 934251876Speter SQLSMALLINT reslength; 935251876Speter char *res, *p, *end, *logval = NULL; 936251876Speter int i; 937251876Speter 938251876Speter /* set info about last error in dbc - fast return for SQL_SUCCESS */ 939251876Speter if (rc == SQL_SUCCESS) { 940251876Speter char successMsg[] = "[dbd_odbc] SQL_SUCCESS "; 941251876Speter apr_size_t successMsgLen = sizeof successMsg - 1; 942251876Speter 943251876Speter dbc->lasterrorcode = SQL_SUCCESS; 944251876Speter apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError); 945251876Speter apr_cpystrn(dbc->lastError + successMsgLen, step, 946251876Speter sizeof dbc->lastError - successMsgLen); 947251876Speter return; 948251876Speter } 949251876Speter switch (rc) { 950251876Speter case SQL_INVALID_HANDLE: 951251876Speter res = "SQL_INVALID_HANDLE"; 952251876Speter break; 953251876Speter case SQL_ERROR: 954251876Speter res = "SQL_ERROR"; 955251876Speter break; 956251876Speter case SQL_SUCCESS_WITH_INFO: 957251876Speter res = "SQL_SUCCESS_WITH_INFO"; 958251876Speter break; 959251876Speter case SQL_STILL_EXECUTING: 960251876Speter res = "SQL_STILL_EXECUTING"; 961251876Speter break; 962251876Speter case SQL_NEED_DATA: 963251876Speter res = "SQL_NEED_DATA"; 964251876Speter break; 965251876Speter case SQL_NO_DATA: 966251876Speter res = "SQL_NO_DATA"; 967251876Speter break; 968251876Speter default: 969251876Speter res = "unrecognized SQL return code"; 970251876Speter } 971251876Speter /* these two returns are expected during normal execution */ 972251876Speter if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA 973251876Speter && dbc->can_commit != APR_DBD_TRANSACTION_IGNORE_ERRORS) { 974251876Speter dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK; 975251876Speter } 976251876Speter p = dbc->lastError; 977251876Speter end = p + sizeof(dbc->lastError); 978251876Speter dbc->lasterrorcode = rc; 979251876Speter p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ", 980251876Speter step, res, rc, SOURCE_FILE, line - 1); 981251876Speter for (i = 1, rc = 0; rc == 0; i++) { 982251876Speter rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer, 983251876Speter sizeof(buffer), &reslength); 984251876Speter if (SQL_SUCCEEDED(rc) && (p < (end - 280))) 985251876Speter p += sprintf(p, "%.256s %.20s ", buffer, sqlstate); 986251876Speter } 987251876Speter apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool); 988251876Speter /* if env var was set or call was init/open (no dbname) - log to stderr */ 989251876Speter if (logval || !dbc->dbname ) { 990251876Speter char timestamp[APR_CTIME_LEN]; 991251876Speter 992251876Speter apr_file_t *se; 993251876Speter apr_ctime(timestamp, apr_time_now()); 994251876Speter apr_file_open_stderr(&se, dbc->pool); 995251876Speter apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError); 996251876Speter } 997251876Speter} 998251876Speter 999251876Speterstatic APR_INLINE int odbc_check_rollback(apr_dbd_t *handle) 1000251876Speter{ 1001251876Speter if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) { 1002251876Speter handle->lasterrorcode = SQL_ERROR; 1003251876Speter apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ", 1004251876Speter sizeof handle->lastError); 1005251876Speter return 1; 1006251876Speter } 1007251876Speter return 0; 1008251876Speter} 1009251876Speter 1010251876Speter/* 1011251876Speter * public functions per DBD driver API 1012251876Speter */ 1013251876Speter 1014251876Speter/** init: allow driver to perform once-only initialisation. **/ 1015251876Speterstatic void odbc_init(apr_pool_t *pool) 1016251876Speter{ 1017251876Speter SQLRETURN rc; 1018251876Speter char *step; 1019251876Speter apr_version_t apuver; 1020251876Speter 1021251876Speter apu_version(&apuver); 1022251876Speter if (apuver.major != DRIVER_APU_VERSION_MAJOR 1023251876Speter || apuver.minor != DRIVER_APU_VERSION_MINOR) { 1024251876Speter apr_file_t *se; 1025251876Speter 1026251876Speter apr_file_open_stderr(&se, pool); 1027251876Speter apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n" 1028251876Speter "Attempt to load APU version %d.%d driver with APU version %d.%d\n", 1029251876Speter DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR, 1030251876Speter apuver.major, apuver.minor); 1031251876Speter abort(); 1032251876Speter } 1033251876Speter 1034251876Speter if (henv) 1035251876Speter return; 1036251876Speter 1037251876Speter step = "SQLAllocHandle (SQL_HANDLE_ENV)"; 1038251876Speter rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); 1039251876Speter apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null); 1040251876Speter if (SQL_SUCCEEDED(rc)) { 1041251876Speter step = "SQLSetEnvAttr"; 1042251876Speter rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION, 1043251876Speter (SQLPOINTER)SQL_OV_ODBC3, 0); 1044251876Speter } 1045251876Speter else { 1046251876Speter apr_dbd_t tmp_dbc; 1047251876Speter SQLHANDLE err_h = henv; 1048251876Speter 1049251876Speter tmp_dbc.pool = pool; 1050251876Speter tmp_dbc.dbname = NULL; 1051251876Speter CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h); 1052251876Speter } 1053251876Speter} 1054251876Speter 1055251876Speter/** native_handle: return the native database handle of the underlying db **/ 1056251876Speterstatic void *odbc_native_handle(apr_dbd_t *handle) 1057251876Speter{ 1058251876Speter return handle->dbc; 1059251876Speter} 1060251876Speter 1061251876Speter/** open: obtain a database connection from the server rec. **/ 1062251876Speter 1063251876Speter/* It would be more efficient to allocate a single statement handle 1064251876Speter * here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before 1065251876Speter * SQLPrepare, and we don't know whether random-access is 1066251876Speter * specified until SQLExecute so we cannot. 1067251876Speter */ 1068251876Speter 1069251876Speterstatic apr_dbd_t *odbc_open(apr_pool_t *pool, const char *params, const char **error) 1070251876Speter{ 1071251876Speter SQLRETURN rc; 1072251876Speter SQLHANDLE hdbc = NULL; 1073251876Speter apr_dbd_t *handle; 1074251876Speter char *err_step; 1075251876Speter int err_htype, i; 1076251876Speter int defaultBufferSize = DEFAULT_BUFFER_SIZE; 1077251876Speter SQLHANDLE err_h = NULL; 1078251876Speter SQLCHAR *datasource = (SQLCHAR *)"", *user = (SQLCHAR *)"", 1079251876Speter *password = (SQLCHAR *)""; 1080258602Speter int nattrs = 0, *attrs = NULL, connect = 0; 1081272076Speter ODBC_INTPTR_T *attrvals = NULL; 1082251876Speter 1083251876Speter err_step = "SQLAllocHandle (SQL_HANDLE_DBC)"; 1084251876Speter err_htype = SQL_HANDLE_ENV; 1085251876Speter err_h = henv; 1086251876Speter rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); 1087251876Speter if (SQL_SUCCEEDED(rc)) { 1088251876Speter err_step = "Invalid DBD Parameters - open"; 1089251876Speter err_htype = SQL_HANDLE_DBC; 1090251876Speter err_h = hdbc; 1091251876Speter rc = odbc_parse_params(pool, params, &connect, &datasource, &user, 1092251876Speter &password, &defaultBufferSize, &nattrs, &attrs, 1093251876Speter &attrvals); 1094251876Speter } 1095251876Speter if (SQL_SUCCEEDED(rc)) { 1096251876Speter for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) { 1097251876Speter err_step = "SQLSetConnectAttr (from DBD Parameters)"; 1098251876Speter err_htype = SQL_HANDLE_DBC; 1099251876Speter err_h = hdbc; 1100251876Speter rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0); 1101251876Speter } 1102251876Speter } 1103251876Speter if (SQL_SUCCEEDED(rc)) { 1104251876Speter if (connect) { 1105251876Speter SQLCHAR out[1024]; 1106251876Speter SQLSMALLINT outlen; 1107251876Speter 1108251876Speter err_step = "SQLDriverConnect"; 1109251876Speter err_htype = SQL_HANDLE_DBC; 1110251876Speter err_h = hdbc; 1111251876Speter rc = SQLDriverConnect(hdbc, NULL, datasource, 1112251876Speter (SQLSMALLINT)strlen((char *)datasource), 1113251876Speter out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT); 1114251876Speter } 1115251876Speter else { 1116251876Speter err_step = "SQLConnect"; 1117251876Speter err_htype = SQL_HANDLE_DBC; 1118251876Speter err_h = hdbc; 1119251876Speter rc = SQLConnect(hdbc, datasource, 1120251876Speter (SQLSMALLINT)strlen((char *)datasource), 1121251876Speter user, (SQLSMALLINT)strlen((char *)user), 1122251876Speter password, (SQLSMALLINT)strlen((char *)password)); 1123251876Speter } 1124251876Speter } 1125251876Speter if (SQL_SUCCEEDED(rc)) { 1126251876Speter handle = apr_pcalloc(pool, sizeof(apr_dbd_t)); 1127251876Speter handle->dbname = apr_pstrdup(pool, (char *)datasource); 1128251876Speter handle->dbc = hdbc; 1129251876Speter handle->pool = pool; 1130251876Speter handle->defaultBufferSize = defaultBufferSize; 1131251876Speter CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc); 1132251876Speter handle->default_transaction_mode = 0; 1133251876Speter handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS; 1134251876Speter SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, 1135272076Speter &(handle->default_transaction_mode), sizeof(ODBC_INTPTR_T), NULL); 1136251876Speter handle->transaction_mode = handle->default_transaction_mode; 1137251876Speter SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions), 1138272076Speter sizeof(ODBC_INTPTR_T), NULL); 1139251876Speter apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null); 1140251876Speter return handle; 1141251876Speter } 1142251876Speter else { 1143251876Speter apr_dbd_t tmp_dbc; 1144251876Speter 1145251876Speter tmp_dbc.pool = pool; 1146251876Speter tmp_dbc.dbname = NULL; 1147251876Speter CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h); 1148251876Speter if (error) 1149251876Speter *error = apr_pstrdup(pool, tmp_dbc.lastError); 1150251876Speter if (hdbc) 1151251876Speter SQLFreeHandle(SQL_HANDLE_DBC, hdbc); 1152251876Speter return NULL; 1153251876Speter } 1154251876Speter} 1155251876Speter 1156251876Speter/** check_conn: check status of a database connection **/ 1157251876Speterstatic apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle) 1158251876Speter{ 1159251876Speter SQLUINTEGER isDead; 1160251876Speter SQLRETURN rc; 1161251876Speter 1162251876Speter rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead, 1163251876Speter sizeof(SQLUINTEGER), NULL); 1164251876Speter CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc, 1165251876Speter SQL_HANDLE_DBC, handle->dbc); 1166251876Speter /* if driver cannot check connection, say so */ 1167251876Speter if (rc != SQL_SUCCESS) 1168251876Speter return APR_ENOTIMPL; 1169251876Speter 1170251876Speter return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL; 1171251876Speter} 1172251876Speter 1173251876Speter/** set_dbname: select database name. May be a no-op if not supported. **/ 1174251876Speterstatic int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle, 1175251876Speter const char *name) 1176251876Speter{ 1177251876Speter if (apr_strnatcmp(name, handle->dbname)) { 1178251876Speter return APR_EGENERAL; /* It's illegal to change dbname in ODBC */ 1179251876Speter } 1180251876Speter CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC, 1181251876Speter handle->dbc); 1182251876Speter return APR_SUCCESS; /* OK if it's the same name */ 1183251876Speter} 1184251876Speter 1185251876Speter/** transaction: start a transaction. May be a no-op. **/ 1186251876Speterstatic int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle, 1187251876Speter apr_dbd_transaction_t **trans) 1188251876Speter{ 1189251876Speter SQLRETURN rc = SQL_SUCCESS; 1190251876Speter 1191251876Speter if (handle->transaction_mode) { 1192251876Speter rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION, 1193251876Speter (SQLPOINTER)handle->transaction_mode, 0); 1194251876Speter CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc, 1195251876Speter SQL_HANDLE_DBC, handle->dbc); 1196251876Speter } 1197251876Speter if (SQL_SUCCEEDED(rc)) { 1198251876Speter /* turn off autocommit for transactions */ 1199251876Speter rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT, 1200251876Speter SQL_AUTOCOMMIT_OFF, 0); 1201251876Speter CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc, 1202251876Speter SQL_HANDLE_DBC, handle->dbc); 1203251876Speter } 1204251876Speter if (SQL_SUCCEEDED(rc)) { 1205251876Speter *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t)); 1206251876Speter (*trans)->dbc = handle->dbc; 1207251876Speter (*trans)->apr_dbd = handle; 1208251876Speter } 1209251876Speter handle->can_commit = APR_DBD_TRANSACTION_COMMIT; 1210251876Speter return APR_FROM_SQL_RESULT(rc); 1211251876Speter} 1212251876Speter 1213251876Speter/** end_transaction: end a transaction **/ 1214251876Speterstatic int odbc_end_transaction(apr_dbd_transaction_t *trans) 1215251876Speter{ 1216251876Speter SQLRETURN rc; 1217251876Speter int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK) 1218251876Speter ? SQL_COMMIT : SQL_ROLLBACK; 1219251876Speter 1220251876Speter rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action); 1221251876Speter CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc); 1222251876Speter if (SQL_SUCCEEDED(rc)) { 1223251876Speter rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT, 1224251876Speter (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0); 1225251876Speter CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", 1226251876Speter rc, SQL_HANDLE_DBC, trans->dbc); 1227251876Speter } 1228251876Speter trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS; 1229251876Speter return APR_FROM_SQL_RESULT(rc); 1230251876Speter} 1231251876Speter 1232251876Speter/** query: execute an SQL statement which doesn't return a result set **/ 1233251876Speterstatic int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement) 1234251876Speter{ 1235251876Speter SQLRETURN rc; 1236251876Speter SQLHANDLE hstmt = NULL; 1237251876Speter size_t len = strlen(statement); 1238251876Speter 1239251876Speter if (odbc_check_rollback(handle)) 1240251876Speter return APR_EGENERAL; 1241251876Speter 1242251876Speter rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt); 1243251876Speter CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC, 1244251876Speter handle->dbc); 1245251876Speter if (!SQL_SUCCEEDED(rc)) 1246251876Speter return APR_FROM_SQL_RESULT(rc); 1247251876Speter 1248251876Speter rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len); 1249251876Speter CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt); 1250251876Speter 1251251876Speter if (SQL_SUCCEEDED(rc)) { 1252251876Speter SQLLEN rowcount; 1253251876Speter 1254251876Speter rc = SQLRowCount(hstmt, &rowcount); 1255251876Speter *nrows = (int)rowcount; 1256251876Speter CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt); 1257251876Speter } 1258251876Speter 1259251876Speter SQLFreeHandle(SQL_HANDLE_STMT, hstmt); 1260251876Speter return APR_FROM_SQL_RESULT(rc); 1261251876Speter} 1262251876Speter 1263251876Speter/** select: execute an SQL statement which returns a result set **/ 1264251876Speterstatic int odbc_select(apr_pool_t *pool, apr_dbd_t *handle, 1265251876Speter apr_dbd_results_t **res, const char *statement, 1266251876Speter int random) 1267251876Speter{ 1268251876Speter SQLRETURN rc; 1269251876Speter SQLHANDLE hstmt; 1270251876Speter apr_dbd_prepared_t *stmt; 1271251876Speter size_t len = strlen(statement); 1272251876Speter 1273251876Speter if (odbc_check_rollback(handle)) 1274251876Speter return APR_EGENERAL; 1275251876Speter 1276251876Speter rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt); 1277251876Speter CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC, 1278251876Speter handle->dbc); 1279251876Speter if (!SQL_SUCCEEDED(rc)) 1280251876Speter return APR_FROM_SQL_RESULT(rc); 1281251876Speter /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this 1282251876Speter * is not a prepared statement. We want the same cleanup mechanism. 1283251876Speter */ 1284251876Speter stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t)); 1285251876Speter stmt->apr_dbd = handle; 1286251876Speter stmt->dbc = handle->dbc; 1287251876Speter stmt->stmt = hstmt; 1288251876Speter apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null); 1289251876Speter if (random) { 1290251876Speter rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE, 1291251876Speter (SQLPOINTER)SQL_SCROLLABLE, 0); 1292251876Speter CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc, 1293251876Speter SQL_HANDLE_STMT, hstmt); 1294251876Speter } 1295251876Speter if (SQL_SUCCEEDED(rc)) { 1296251876Speter rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len); 1297251876Speter CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt); 1298251876Speter } 1299251876Speter if (SQL_SUCCEEDED(rc)) { 1300251876Speter rc = odbc_create_results(handle, hstmt, pool, random, res); 1301251876Speter apr_pool_cleanup_register(pool, *res, 1302251876Speter odbc_close_results, apr_pool_cleanup_null); 1303251876Speter } 1304251876Speter return APR_FROM_SQL_RESULT(rc); 1305251876Speter} 1306251876Speter 1307251876Speter/** num_cols: get the number of columns in a results set **/ 1308251876Speterstatic int odbc_num_cols(apr_dbd_results_t *res) 1309251876Speter{ 1310251876Speter return res->ncols; 1311251876Speter} 1312251876Speter 1313251876Speter/** num_tuples: get the number of rows in a results set **/ 1314251876Speterstatic int odbc_num_tuples(apr_dbd_results_t *res) 1315251876Speter{ 1316251876Speter SQLRETURN rc; 1317251876Speter SQLLEN nrows; 1318251876Speter 1319251876Speter rc = SQLRowCount(res->stmt, &nrows); 1320251876Speter CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt); 1321251876Speter return SQL_SUCCEEDED(rc) ? (int)nrows : -1; 1322251876Speter} 1323251876Speter 1324251876Speter/** get_row: get a row from a result set **/ 1325251876Speterstatic int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 1326251876Speter apr_dbd_row_t **row, int rownum) 1327251876Speter{ 1328251876Speter SQLRETURN rc; 1329251876Speter char *fetchtype; 1330251876Speter int c; 1331251876Speter 1332251876Speter *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t)); 1333251876Speter (*row)->stmt = res->stmt; 1334251876Speter (*row)->dbc = res->dbc; 1335251876Speter (*row)->res = res; 1336251876Speter (*row)->pool = res->pool; 1337251876Speter 1338251876Speter /* mark all the columns as needing SQLGetData unless they are bound */ 1339251876Speter for (c = 0; c < res->ncols; c++) { 1340251876Speter if (res->colstate[c] != COL_BOUND) { 1341251876Speter res->colstate[c] = COL_AVAIL; 1342251876Speter } 1343251876Speter /* some drivers do not null-term zero-len CHAR data */ 1344251876Speter if (res->colptrs[c]) 1345251876Speter *(char *)res->colptrs[c] = 0; 1346251876Speter } 1347251876Speter 1348251876Speter if (res->random && (rownum > 0)) { 1349251876Speter fetchtype = "SQLFetchScroll"; 1350251876Speter rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum); 1351251876Speter } 1352251876Speter else { 1353251876Speter fetchtype = "SQLFetch"; 1354251876Speter rc = SQLFetch(res->stmt); 1355251876Speter } 1356251876Speter CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt); 1357251876Speter (*row)->stmt = res->stmt; 1358251876Speter if (!SQL_SUCCEEDED(rc) && !res->random) { 1359251876Speter /* early close on any error (usually SQL_NO_DATA) if fetching 1360251876Speter * sequentially to release resources ASAP 1361251876Speter */ 1362251876Speter odbc_close_results(res); 1363251876Speter return -1; 1364251876Speter } 1365251876Speter return SQL_SUCCEEDED(rc) ? 0 : -1; 1366251876Speter} 1367251876Speter 1368251876Speter/** datum_get: get a binary entry from a row **/ 1369251876Speterstatic apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col, 1370251876Speter apr_dbd_type_e dbdtype, void *data) 1371251876Speter{ 1372251876Speter SQLSMALLINT sqltype; 1373251876Speter void *p; 1374251876Speter int len; 1375251876Speter 1376251876Speter if (col >= row->res->ncols) 1377251876Speter return APR_EGENERAL; 1378251876Speter 1379251876Speter if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) { 1380251876Speter data = NULL; /* invalid type */ 1381251876Speter return APR_EGENERAL; 1382251876Speter } 1383251876Speter 1384251876Speter len = sqlSizes[dbdtype]; 1385251876Speter sqltype = sqlCtype[dbdtype]; 1386251876Speter 1387251876Speter /* must not memcpy a brigade, sentinals are relative to orig loc */ 1388251876Speter if (IS_LOB(sqltype)) 1389251876Speter return odbc_create_bucket(row, col, sqltype, data); 1390251876Speter 1391251876Speter p = odbc_get(row, col, sqltype); 1392251876Speter if (p == (void *)-1) 1393251876Speter return APR_EGENERAL; 1394251876Speter 1395251876Speter if (p == NULL) 1396251876Speter return APR_ENOENT; /* SQL NULL value */ 1397251876Speter 1398251876Speter if (len < 0) 1399251876Speter *(char**)data = (char *)p; 1400251876Speter else 1401251876Speter memcpy(data, p, len); 1402251876Speter 1403251876Speter return APR_SUCCESS; 1404251876Speter 1405251876Speter} 1406251876Speter 1407251876Speter/** get_entry: get an entry from a row (string data) **/ 1408251876Speterstatic const char *odbc_get_entry(const apr_dbd_row_t *row, int col) 1409251876Speter{ 1410251876Speter void *p; 1411251876Speter 1412251876Speter if (col >= row->res->ncols) 1413251876Speter return NULL; 1414251876Speter 1415251876Speter p = odbc_get(row, col, SQL_C_CHAR); 1416251876Speter 1417251876Speter /* NULL or invalid (-1) */ 1418251876Speter if (p == NULL || p == (void *)-1) 1419251876Speter return p; 1420251876Speter else 1421251876Speter return apr_pstrdup(row->pool, p); 1422251876Speter} 1423251876Speter 1424251876Speter/** error: get current error message (if any) **/ 1425251876Speterstatic const char *odbc_error(apr_dbd_t *handle, int errnum) 1426251876Speter{ 1427251876Speter return (handle) ? handle->lastError : "[dbd_odbc]No error message available"; 1428251876Speter} 1429251876Speter 1430251876Speter/** escape: escape a string so it is safe for use in query/select **/ 1431251876Speterstatic const char *odbc_escape(apr_pool_t *pool, const char *s, 1432251876Speter apr_dbd_t *handle) 1433251876Speter{ 1434251876Speter char *newstr, *src, *dst, *sq; 1435251876Speter int qcount; 1436251876Speter 1437251876Speter /* return the original if there are no single-quotes */ 1438251876Speter if (!(sq = strchr(s, '\''))) 1439251876Speter return (char *)s; 1440251876Speter /* count the single-quotes and allocate a new buffer */ 1441251876Speter for (qcount = 1; (sq = strchr(sq + 1, '\'')); ) 1442251876Speter qcount++; 1443251876Speter newstr = apr_palloc(pool, strlen(s) + qcount + 1); 1444251876Speter 1445251876Speter /* move chars, doubling all single-quotes */ 1446251876Speter src = (char *)s; 1447251876Speter for (dst = newstr; *src; src++) { 1448251876Speter if ((*dst++ = *src) == '\'') 1449251876Speter *dst++ = '\''; 1450251876Speter } 1451251876Speter *dst = 0; 1452251876Speter return newstr; 1453251876Speter} 1454251876Speter 1455251876Speter/** prepare: prepare a statement **/ 1456251876Speterstatic int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle, 1457251876Speter const char *query, const char *label, int nargs, 1458251876Speter int nvals, apr_dbd_type_e *types, 1459251876Speter apr_dbd_prepared_t **statement) 1460251876Speter{ 1461251876Speter SQLRETURN rc; 1462251876Speter size_t len = strlen(query); 1463251876Speter 1464251876Speter if (odbc_check_rollback(handle)) 1465251876Speter return APR_EGENERAL; 1466251876Speter 1467251876Speter *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t)); 1468251876Speter (*statement)->dbc = handle->dbc; 1469251876Speter (*statement)->apr_dbd = handle; 1470251876Speter (*statement)->nargs = nargs; 1471251876Speter (*statement)->nvals = nvals; 1472251876Speter (*statement)->types = 1473251876Speter apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e)); 1474251876Speter rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt)); 1475251876Speter apr_pool_cleanup_register(pool, *statement, 1476251876Speter odbc_close_pstmt, apr_pool_cleanup_null); 1477251876Speter CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, 1478251876Speter SQL_HANDLE_DBC, handle->dbc); 1479251876Speter rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len); 1480251876Speter CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT, 1481251876Speter (*statement)->stmt); 1482251876Speter return APR_FROM_SQL_RESULT(rc); 1483251876Speter} 1484251876Speter 1485251876Speter/** pquery: query using a prepared statement + args **/ 1486251876Speterstatic int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1487251876Speter apr_dbd_prepared_t *statement, const char **args) 1488251876Speter{ 1489251876Speter SQLRETURN rc = SQL_SUCCESS; 1490251876Speter int i, argp; 1491251876Speter 1492251876Speter if (odbc_check_rollback(handle)) 1493251876Speter return APR_EGENERAL; 1494251876Speter 1495251876Speter for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) { 1496251876Speter rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1497251876Speter &argp, (const void **)args, TEXTMODE); 1498251876Speter } 1499251876Speter if (SQL_SUCCEEDED(rc)) { 1500251876Speter rc = SQLExecute(statement->stmt); 1501251876Speter CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1502251876Speter statement->stmt); 1503251876Speter } 1504251876Speter if (SQL_SUCCEEDED(rc)) { 1505251876Speter SQLLEN rowcount; 1506251876Speter 1507251876Speter rc = SQLRowCount(statement->stmt, &rowcount); 1508251876Speter *nrows = (int)rowcount; 1509251876Speter CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, 1510251876Speter statement->stmt); 1511251876Speter } 1512251876Speter return APR_FROM_SQL_RESULT(rc); 1513251876Speter} 1514251876Speter 1515251876Speter/** pvquery: query using a prepared statement + args **/ 1516251876Speterstatic int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1517251876Speter apr_dbd_prepared_t *statement, va_list args) 1518251876Speter{ 1519251876Speter const char **values; 1520251876Speter int i; 1521251876Speter 1522251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1523251876Speter for (i = 0; i < statement->nvals; i++) 1524251876Speter values[i] = va_arg(args, const char *); 1525251876Speter return odbc_pquery(pool, handle, nrows, statement, values); 1526251876Speter} 1527251876Speter 1528251876Speter/** pselect: select using a prepared statement + args **/ 1529251876Speterstatic int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle, 1530251876Speter apr_dbd_results_t **res, apr_dbd_prepared_t *statement, 1531251876Speter int random, const char **args) 1532251876Speter{ 1533251876Speter SQLRETURN rc = SQL_SUCCESS; 1534251876Speter int i, argp; 1535251876Speter 1536251876Speter if (odbc_check_rollback(handle)) 1537251876Speter return APR_EGENERAL; 1538251876Speter 1539251876Speter if (random) { 1540251876Speter rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE, 1541251876Speter (SQLPOINTER)SQL_SCROLLABLE, 0); 1542251876Speter CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", 1543251876Speter rc, SQL_HANDLE_STMT, statement->stmt); 1544251876Speter } 1545251876Speter if (SQL_SUCCEEDED(rc)) { 1546251876Speter for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) { 1547251876Speter rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1548251876Speter &argp, (const void **)args, TEXTMODE); 1549251876Speter } 1550251876Speter } 1551251876Speter if (SQL_SUCCEEDED(rc)) { 1552251876Speter rc = SQLExecute(statement->stmt); 1553251876Speter CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1554251876Speter statement->stmt); 1555251876Speter } 1556251876Speter if (SQL_SUCCEEDED(rc)) { 1557251876Speter rc = odbc_create_results(handle, statement->stmt, pool, random, res); 1558251876Speter apr_pool_cleanup_register(pool, *res, 1559251876Speter odbc_close_results, apr_pool_cleanup_null); 1560251876Speter } 1561251876Speter return APR_FROM_SQL_RESULT(rc); 1562251876Speter} 1563251876Speter 1564251876Speter/** pvselect: select using a prepared statement + args **/ 1565251876Speterstatic int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle, 1566251876Speter apr_dbd_results_t **res, 1567251876Speter apr_dbd_prepared_t *statement, int random, 1568251876Speter va_list args) 1569251876Speter{ 1570251876Speter const char **values; 1571251876Speter int i; 1572251876Speter 1573251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1574251876Speter for (i = 0; i < statement->nvals; i++) 1575251876Speter values[i] = va_arg(args, const char *); 1576251876Speter return odbc_pselect(pool, handle, res, statement, random, values); 1577251876Speter} 1578251876Speter 1579251876Speter/** get_name: get a column title from a result set **/ 1580251876Speterstatic const char *odbc_get_name(const apr_dbd_results_t *res, int col) 1581251876Speter{ 1582251876Speter SQLRETURN rc; 1583251876Speter char buffer[MAX_COLUMN_NAME]; 1584251876Speter SQLSMALLINT colnamelength, coltype, coldecimal, colnullable; 1585251876Speter SQLULEN colsize; 1586251876Speter 1587251876Speter if (col >= res->ncols) 1588251876Speter return NULL; /* bogus column number */ 1589251876Speter if (res->colnames[col] != NULL) 1590251876Speter return res->colnames[col]; /* we already retrieved it */ 1591251876Speter rc = SQLDescribeCol(res->stmt, col + 1, 1592251876Speter (SQLCHAR *)buffer, sizeof(buffer), &colnamelength, 1593251876Speter &coltype, &colsize, &coldecimal, &colnullable); 1594251876Speter CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc, 1595251876Speter SQL_HANDLE_STMT, res->stmt); 1596251876Speter res->colnames[col] = apr_pstrdup(res->pool, buffer); 1597251876Speter return res->colnames[col]; 1598251876Speter} 1599251876Speter 1600251876Speter/** transaction_mode_get: get the mode of transaction **/ 1601251876Speterstatic int odbc_transaction_mode_get(apr_dbd_transaction_t *trans) 1602251876Speter{ 1603251876Speter return (int)trans->apr_dbd->can_commit; 1604251876Speter} 1605251876Speter 1606251876Speter/** transaction_mode_set: set the mode of transaction **/ 1607251876Speterstatic int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode) 1608251876Speter{ 1609251876Speter int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS 1610251876Speter | APR_DBD_TRANSACTION_COMMIT 1611251876Speter | APR_DBD_TRANSACTION_ROLLBACK); 1612251876Speter 1613251876Speter if ((mode & legal) != mode) 1614251876Speter return APR_EGENERAL; 1615251876Speter 1616251876Speter trans->apr_dbd->can_commit = mode; 1617251876Speter return APR_SUCCESS; 1618251876Speter} 1619251876Speter 1620251876Speter/** pbquery: query using a prepared statement + binary args **/ 1621251876Speterstatic int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1622251876Speter apr_dbd_prepared_t *statement, const void **args) 1623251876Speter{ 1624251876Speter SQLRETURN rc = SQL_SUCCESS; 1625251876Speter int i, argp; 1626251876Speter 1627251876Speter if (odbc_check_rollback(handle)) 1628251876Speter return APR_EGENERAL; 1629251876Speter 1630251876Speter for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) 1631251876Speter rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1632251876Speter &argp, args, BINARYMODE); 1633251876Speter 1634251876Speter if (SQL_SUCCEEDED(rc)) { 1635251876Speter rc = SQLExecute(statement->stmt); 1636251876Speter CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1637251876Speter statement->stmt); 1638251876Speter } 1639251876Speter if (SQL_SUCCEEDED(rc)) { 1640251876Speter SQLLEN rowcount; 1641251876Speter 1642251876Speter rc = SQLRowCount(statement->stmt, &rowcount); 1643251876Speter *nrows = (int)rowcount; 1644251876Speter CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, 1645251876Speter statement->stmt); 1646251876Speter } 1647251876Speter return APR_FROM_SQL_RESULT(rc); 1648251876Speter} 1649251876Speter 1650251876Speter/** pbselect: select using a prepared statement + binary args **/ 1651251876Speterstatic int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle, 1652251876Speter apr_dbd_results_t **res, 1653251876Speter apr_dbd_prepared_t *statement, 1654251876Speter int random, const void **args) 1655251876Speter{ 1656251876Speter SQLRETURN rc = SQL_SUCCESS; 1657251876Speter int i, argp; 1658251876Speter 1659251876Speter if (odbc_check_rollback(handle)) 1660251876Speter return APR_EGENERAL; 1661251876Speter 1662251876Speter if (random) { 1663251876Speter rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE, 1664251876Speter (SQLPOINTER)SQL_SCROLLABLE, 0); 1665251876Speter CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", 1666251876Speter rc, SQL_HANDLE_STMT, statement->stmt); 1667251876Speter } 1668251876Speter if (SQL_SUCCEEDED(rc)) { 1669251876Speter for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) { 1670251876Speter rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1671251876Speter &argp, args, BINARYMODE); 1672251876Speter } 1673251876Speter } 1674251876Speter if (SQL_SUCCEEDED(rc)) { 1675251876Speter rc = SQLExecute(statement->stmt); 1676251876Speter CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1677251876Speter statement->stmt); 1678251876Speter } 1679251876Speter if (SQL_SUCCEEDED(rc)) { 1680251876Speter rc = odbc_create_results(handle, statement->stmt, pool, random, res); 1681251876Speter apr_pool_cleanup_register(pool, *res, 1682251876Speter odbc_close_results, apr_pool_cleanup_null); 1683251876Speter } 1684251876Speter 1685251876Speter return APR_FROM_SQL_RESULT(rc); 1686251876Speter} 1687251876Speter 1688251876Speter/** pvbquery: query using a prepared statement + binary args **/ 1689251876Speterstatic int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1690251876Speter apr_dbd_prepared_t *statement, va_list args) 1691251876Speter{ 1692251876Speter const char **values; 1693251876Speter int i; 1694251876Speter 1695251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1696251876Speter for (i = 0; i < statement->nvals; i++) 1697251876Speter values[i] = va_arg(args, const char *); 1698251876Speter return odbc_pbquery(pool, handle, nrows, statement, (const void **)values); 1699251876Speter} 1700251876Speter 1701251876Speter/** pvbselect: select using a prepared statement + binary args **/ 1702251876Speterstatic int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle, 1703251876Speter apr_dbd_results_t **res, 1704251876Speter apr_dbd_prepared_t *statement, 1705251876Speter int random, va_list args) 1706251876Speter{ 1707251876Speter const char **values; 1708251876Speter int i; 1709251876Speter 1710251876Speter values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1711251876Speter for (i = 0; i < statement->nvals; i++) 1712251876Speter values[i] = va_arg(args, const char *); 1713251876Speter return odbc_pbselect(pool, handle, res, statement, random, (const void **)values); 1714251876Speter} 1715251876Speter 1716251876SpeterAPU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = { 1717251876Speter ODBC_DRIVER_STRING, 1718251876Speter odbc_init, 1719251876Speter odbc_native_handle, 1720251876Speter odbc_open, 1721251876Speter odbc_check_conn, 1722251876Speter odbc_close, 1723251876Speter odbc_set_dbname, 1724251876Speter odbc_start_transaction, 1725251876Speter odbc_end_transaction, 1726251876Speter odbc_query, 1727251876Speter odbc_select, 1728251876Speter odbc_num_cols, 1729251876Speter odbc_num_tuples, 1730251876Speter odbc_get_row, 1731251876Speter odbc_get_entry, 1732251876Speter odbc_error, 1733251876Speter odbc_escape, 1734251876Speter odbc_prepare, 1735251876Speter odbc_pvquery, 1736251876Speter odbc_pvselect, 1737251876Speter odbc_pquery, 1738251876Speter odbc_pselect, 1739251876Speter odbc_get_name, 1740251876Speter odbc_transaction_mode_get, 1741251876Speter odbc_transaction_mode_set, 1742251876Speter "?", 1743251876Speter odbc_pvbquery, 1744251876Speter odbc_pvbselect, 1745251876Speter odbc_pbquery, 1746251876Speter odbc_pbselect, 1747251876Speter odbc_datum_get 1748251876Speter}; 1749251876Speter 1750251876Speter#endif 1751