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