apr_dbd_odbc.c revision 289166
198186Sgordon/* Licensed to the Apache Software Foundation (ASF) under one or more 298186Sgordon * contributor license agreements. See the NOTICE file distributed with 378344Sobrien * this work for additional information regarding copyright ownership. 498186Sgordon * The ASF licenses this file to You under the Apache License, Version 2.0 578344Sobrien * (the "License"); you may not use this file except in compliance with 678344Sobrien * the License. You may obtain a copy of the License at 778344Sobrien * 878344Sobrien * http://www.apache.org/licenses/LICENSE-2.0 978344Sobrien * 1078344Sobrien * Unless required by applicable law or agreed to in writing, software 1178344Sobrien * distributed under the License is distributed on an "AS IS" BASIS, 1278344Sobrien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1378344Sobrien * See the License for the specific language governing permissions and 1478344Sobrien * limitations under the License. 1578344Sobrien */ 1678344Sobrien 1778344Sobrien#include "apu.h" 1878344Sobrien#if APU_HAVE_ODBC 1978344Sobrien 2078344Sobrien#include "apr.h" 2178344Sobrien#include "apr_strings.h" 2278344Sobrien#include "apr_buckets.h" 2378344Sobrien#include "apr_env.h" 2478344Sobrien#include "apr_file_io.h" 2578344Sobrien#include "apr_file_info.h" 2678344Sobrien#include "apr_dbd_internal.h" 2778344Sobrien#include "apr_thread_proc.h" 2878344Sobrien#include "apu_version.h" 2978344Sobrien#include "apu_config.h" 3078344Sobrien 3178344Sobrien#include <stdlib.h> 3278344Sobrien 3378344Sobrien/* If library is ODBC-V2, use macros for limited ODBC-V2 support 3478344Sobrien * No random access in V2. 3578344Sobrien */ 3678344Sobrien#ifdef ODBCV2 3778344Sobrien#define ODBCVER 0x0200 3878344Sobrien#include "apr_dbd_odbc_v2.h" 3978344Sobrien#endif 4078344Sobrien 4178344Sobrien/* standard ODBC include files */ 4278344Sobrien#ifdef HAVE_SQL_H 4398186Sgordon#include <sql.h> 4498186Sgordon#include <sqlext.h> 4598186Sgordon#elif defined(HAVE_ODBC_SQL_H) 4698186Sgordon#include <odbc/sql.h> 4798186Sgordon#include <odbc/sqlext.h> 4898186Sgordon#endif 49103018Sgordon 50124832Smtm/* 51124832Smtm* MSVC6 does not support intptr_t (C99) 5298186Sgordon* APR does not have a signed inptr type until 2.0 (r1557720) 53103018Sgordon*/ 5498186Sgordon#if defined(_MSC_VER) && _MSC_VER < 1400 5598186Sgordon#if APR_SIZEOF_VOIDP == 8 5698186Sgordon#define ODBC_INTPTR_T apr_int64_t 5798186Sgordon#else 5898186Sgordon#define ODBC_INTPTR_T apr_int32_t 5998186Sgordon#endif 6098186Sgordon#else 6198186Sgordon#define ODBC_INTPTR_T intptr_t 6298186Sgordon#endif 6378344Sobrien 6478344Sobrien 6578344Sobrien/* Driver name is "odbc" and the entry point is 'apr_dbd_odbc_driver' 6678344Sobrien * unless ODBC_DRIVER_NAME is defined and it is linked with another db library which 6798186Sgordon * is ODBC source-compatible. e.g. DB2, Informix, TimesTen, mysql. 6898186Sgordon */ 6998186Sgordon#ifndef ODBC_DRIVER_NAME 7098186Sgordon#define ODBC_DRIVER_NAME odbc 7198186Sgordon#endif 7298186Sgordon#define STRINGIFY(x) #x 7398186Sgordon#define NAMIFY2(n) apr_dbd_##n##_driver 7498186Sgordon#define NAMIFY1(n) NAMIFY2(n) 7598186Sgordon#define ODBC_DRIVER_STRING STRINGIFY(ODBC_DRIVER_NAME) 7698186Sgordon#define ODBC_DRIVER_ENTRY NAMIFY1(ODBC_DRIVER_NAME) 7798186Sgordon 7898186Sgordon/* Required APR version for this driver */ 7998186Sgordon#define DRIVER_APU_VERSION_MAJOR APU_MAJOR_VERSION 8098186Sgordon#define DRIVER_APU_VERSION_MINOR APU_MINOR_VERSION 8198186Sgordon 8298186Sgordonstatic SQLHANDLE henv = NULL; /* ODBC ENV handle is process-wide */ 8398186Sgordon 84103018Sgordon/* Use a CHECK_ERROR macro so we can grab the source line numbers 8598186Sgordon * for error reports 8698186Sgordon */ 8798186Sgordonstatic void check_error(apr_dbd_t *a, const char *step, SQLRETURN rc, 8898186Sgordon SQLSMALLINT type, SQLHANDLE h, int line); 8998186Sgordon#define CHECK_ERROR(a,s,r,t,h) check_error(a,s,r,t,h, __LINE__) 9098186Sgordon 9198186Sgordon#define SOURCE_FILE __FILE__ /* source file for error messages */ 9298186Sgordon#define MAX_ERROR_STRING 1024 /* max length of message in dbc */ 9398186Sgordon#define MAX_COLUMN_NAME 256 /* longest column name recognized */ 9498186Sgordon#define DEFAULT_BUFFER_SIZE 1024 /* value for defaultBufferSize */ 9598186Sgordon 9698186Sgordon#define MAX_PARAMS 20 9798186Sgordon#define DEFAULTSEPS " \t\r\n,=" 9898186Sgordon#define CSINGLEQUOTE '\'' 9998186Sgordon#define SSINGLEQUOTE "\'" 10098186Sgordon 10198186Sgordon#define TEXTMODE 1 /* used for text (APR 1.2) mode params */ 10298186Sgordon#define BINARYMODE 0 /* used for binary (APR 1.3+) mode params */ 10398186Sgordon 10498186Sgordon/* Identify datatypes which are LOBs 10598186Sgordon * - DB2 DRDA driver uses undefined types -98 and -99 for CLOB & BLOB 10698186Sgordon */ 10798186Sgordon#define IS_LOB(t) (t == SQL_LONGVARCHAR \ 10898186Sgordon || t == SQL_LONGVARBINARY || t == SQL_VARBINARY \ 10998186Sgordon || t == -98 || t == -99) 11098186Sgordon 11198186Sgordon/* These types are CLOBs 11298186Sgordon * - DB2 DRDA driver uses undefined type -98 for CLOB 11398186Sgordon */ 11498186Sgordon#define IS_CLOB(t) \ 11598186Sgordon (t == SQL_LONGVARCHAR || t == -98) 11698186Sgordon 11778344Sobrien/* Convert a SQL result to an APR result */ 11878344Sobrien#define APR_FROM_SQL_RESULT(rc) \ 11978344Sobrien (SQL_SUCCEEDED(rc) ? APR_SUCCESS : APR_EGENERAL) 12078344Sobrien 12178344Sobrien/* DBD opaque structures */ 12278344Sobrienstruct apr_dbd_t 12378344Sobrien{ 12498186Sgordon SQLHANDLE dbc; /* SQL connection handle - NULL after close */ 12578344Sobrien apr_pool_t *pool; /* connection lifetime pool */ 12678344Sobrien char *dbname; /* ODBC datasource */ 12778344Sobrien int lasterrorcode; 12878344Sobrien int lineNumber; 12978344Sobrien char lastError[MAX_ERROR_STRING]; 13078344Sobrien int defaultBufferSize; /* used for CLOBs in text mode, 13178344Sobrien * and when fld size is indeterminate */ 13278344Sobrien ODBC_INTPTR_T transaction_mode; 13378344Sobrien ODBC_INTPTR_T dboptions; /* driver options re SQLGetData */ 13478344Sobrien ODBC_INTPTR_T default_transaction_mode; 13578344Sobrien int can_commit; /* controls end_trans behavior */ 13678344Sobrien}; 137106643Sgordon 13878344Sobrienstruct apr_dbd_results_t 13978344Sobrien{ 14078344Sobrien SQLHANDLE stmt; /* parent sql statement handle */ 14178344Sobrien SQLHANDLE dbc; /* parent sql connection handle */ 14278344Sobrien apr_pool_t *pool; /* pool from query or select */ 14398186Sgordon apr_dbd_t *apr_dbd; /* parent DBD connection handle */ 14498186Sgordon int random; /* random access requested */ 14578344Sobrien int ncols; /* number of columns */ 14698186Sgordon int isclosed; /* cursor has been closed */ 14798186Sgordon char **colnames; /* array of column names (NULL until used) */ 14898186Sgordon SQLPOINTER *colptrs; /* pointers to column data */ 149126286Smtm SQLINTEGER *colsizes; /* sizes for columns (enough for txt or bin) */ 15098186Sgordon SQLINTEGER *coltextsizes; /* max-sizes if converted to text */ 15198186Sgordon SQLSMALLINT *coltypes; /* array of SQL data types for columns */ 15298186Sgordon SQLLEN *colinds; /* array of SQL data indicator/strlens */ 15398186Sgordon int *colstate; /* array of column states 15498186Sgordon * - avail, bound, present, unavail 15578344Sobrien */ 15698186Sgordon int *all_data_fetched; /* flags data as all fetched, for LOBs */ 15798186Sgordon void *data; /* buffer for all data for one row */ 15898186Sgordon}; 15998186Sgordon 16098186Sgordonenum /* results column states */ 16178344Sobrien{ 16278344Sobrien COL_AVAIL, /* data may be retrieved with SQLGetData */ 16398186Sgordon COL_PRESENT, /* data has been retrieved with SQLGetData */ 16478344Sobrien COL_BOUND, /* column is bound to colptr */ 16578344Sobrien COL_RETRIEVED, /* all data from column has been returned */ 166126285Smtm COL_UNAVAIL /* column is unavailable because ODBC driver 16778344Sobrien * requires that columns be retrieved 16878344Sobrien * in ascending order and a higher col 169126285Smtm * was accessed 17078344Sobrien */ 17178344Sobrien}; 172126285Smtm 173126285Smtmstruct apr_dbd_row_t { 174126285Smtm SQLHANDLE stmt; /* parent ODBC statement handle */ 17578344Sobrien SQLHANDLE dbc; /* parent ODBC connection handle */ 17678344Sobrien apr_pool_t *pool; /* pool from get_row */ 17798186Sgordon apr_dbd_results_t *res; 17878344Sobrien}; 17978344Sobrien 18078344Sobrienstruct apr_dbd_transaction_t { 18178344Sobrien SQLHANDLE dbc; /* parent ODBC connection handle */ 18298186Sgordon apr_dbd_t *apr_dbd; /* parent DBD connection handle */ 18398186Sgordon}; 18478344Sobrien 18598186Sgordonstruct apr_dbd_prepared_t { 18698186Sgordon SQLHANDLE stmt; /* ODBC statement handle */ 18778344Sobrien SQLHANDLE dbc; /* parent ODBC connection handle */ 18878344Sobrien apr_dbd_t *apr_dbd; 18978344Sobrien int nargs; 19078344Sobrien int nvals; 19178344Sobrien int *types; /* array of DBD data types */ 19298186Sgordon}; 19378344Sobrien 19498186Sgordonstatic void odbc_lob_bucket_destroy(void *data); 19578344Sobrienstatic apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool); 19678344Sobrienstatic apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str, 19798186Sgordon apr_size_t *len, apr_read_type_e block); 19878344Sobrien 19978344Sobrien/* the ODBC LOB bucket type */ 20078344Sobrienstatic const apr_bucket_type_t odbc_bucket_type = { 20178344Sobrien "ODBC_LOB", 5, APR_BUCKET_DATA, 20298186Sgordon odbc_lob_bucket_destroy, 20378344Sobrien odbc_lob_bucket_read, 20478344Sobrien odbc_lob_bucket_setaside, 20598186Sgordon apr_bucket_shared_split, 20678344Sobrien apr_bucket_shared_copy 20778344Sobrien}; 20878344Sobrien 20998186Sgordon/* ODBC LOB bucket data */ 21078344Sobrientypedef struct { 21198186Sgordon /** Ref count for shared bucket */ 21298186Sgordon apr_bucket_refcount refcount; 21378344Sobrien const apr_dbd_row_t *row; 21478344Sobrien int col; 21578344Sobrien SQLSMALLINT type; 21678344Sobrien} odbc_bucket; 21798186Sgordon 21878344Sobrien/* SQL datatype mappings to DBD datatypes 21998186Sgordon * These tables must correspond *exactly* to the apr_dbd_type_e enum 22078344Sobrien * in apr_dbd.h 22198186Sgordon */ 22298186Sgordon 22398186Sgordon/* ODBC "C" types to DBD datatypes */ 22498186Sgordonstatic SQLSMALLINT const sqlCtype[] = { 22598186Sgordon SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */ 22698186Sgordon SQL_C_STINYINT, /* APR_DBD_TYPE_TINY, \%hhd */ 22798186Sgordon SQL_C_UTINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */ 22898186Sgordon SQL_C_SSHORT, /* APR_DBD_TYPE_SHORT, \%hd */ 22998186Sgordon SQL_C_USHORT, /* APR_DBD_TYPE_USHORT, \%hu */ 23098186Sgordon SQL_C_SLONG, /* APR_DBD_TYPE_INT, \%d */ 23198186Sgordon SQL_C_ULONG, /* APR_DBD_TYPE_UINT, \%u */ 23298186Sgordon SQL_C_SLONG, /* APR_DBD_TYPE_LONG, \%ld */ 23398186Sgordon SQL_C_ULONG, /* APR_DBD_TYPE_ULONG, \%lu */ 23498186Sgordon SQL_C_SBIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */ 23598186Sgordon SQL_C_UBIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */ 23698186Sgordon SQL_C_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */ 23798186Sgordon SQL_C_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */ 23898186Sgordon SQL_C_CHAR, /* APR_DBD_TYPE_STRING, \%s */ 23998186Sgordon SQL_C_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */ 24098186Sgordon SQL_C_CHAR, /*SQL_C_TYPE_TIME, APR_DBD_TYPE_TIME, \%pDi */ 24198186Sgordon SQL_C_CHAR, /*SQL_C_TYPE_DATE, APR_DBD_TYPE_DATE, \%pDd */ 24298186Sgordon SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */ 24398186Sgordon SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */ 24498186Sgordon SQL_C_CHAR, /*SQL_C_TYPE_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */ 24598186Sgordon SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */ 24698186Sgordon SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */ 24798186Sgordon SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */ 24898186Sgordon}; 24998186Sgordon#define NUM_APR_DBD_TYPES (sizeof(sqlCtype) / sizeof(sqlCtype[0])) 25078344Sobrien 25198186Sgordon/* ODBC Base types to DBD datatypes */ 25298186Sgordonstatic SQLSMALLINT const sqlBaseType[] = { 25398186Sgordon SQL_C_DEFAULT, /* APR_DBD_TYPE_NONE */ 25498186Sgordon SQL_TINYINT, /* APR_DBD_TYPE_TINY, \%hhd */ 25598186Sgordon SQL_TINYINT, /* APR_DBD_TYPE_UTINY, \%hhu */ 25698186Sgordon SQL_SMALLINT, /* APR_DBD_TYPE_SHORT, \%hd */ 25778344Sobrien SQL_SMALLINT, /* APR_DBD_TYPE_USHORT, \%hu */ 25898186Sgordon SQL_INTEGER, /* APR_DBD_TYPE_INT, \%d */ 25998186Sgordon SQL_INTEGER, /* APR_DBD_TYPE_UINT, \%u */ 26098186Sgordon SQL_INTEGER, /* APR_DBD_TYPE_LONG, \%ld */ 26198186Sgordon SQL_INTEGER, /* APR_DBD_TYPE_ULONG, \%lu */ 26298186Sgordon SQL_BIGINT, /* APR_DBD_TYPE_LONGLONG, \%lld */ 26398186Sgordon SQL_BIGINT, /* APR_DBD_TYPE_ULONGLONG, \%llu */ 26498186Sgordon SQL_FLOAT, /* APR_DBD_TYPE_FLOAT, \%f */ 26598186Sgordon SQL_DOUBLE, /* APR_DBD_TYPE_DOUBLE, \%lf */ 26698186Sgordon SQL_CHAR, /* APR_DBD_TYPE_STRING, \%s */ 26798186Sgordon SQL_CHAR, /* APR_DBD_TYPE_TEXT, \%pDt */ 26898186Sgordon SQL_CHAR, /*SQL_TIME, APR_DBD_TYPE_TIME, \%pDi */ 26998186Sgordon SQL_CHAR, /*SQL_DATE, APR_DBD_TYPE_DATE, \%pDd */ 27098186Sgordon SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_DATETIME, \%pDa */ 27198186Sgordon SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_TIMESTAMP, \%pDs */ 27298186Sgordon SQL_CHAR, /*SQL_TIMESTAMP, APR_DBD_TYPE_ZTIMESTAMP, \%pDz */ 27398186Sgordon SQL_LONGVARBINARY, /* APR_DBD_TYPE_BLOB, \%pDb */ 27498186Sgordon SQL_LONGVARCHAR, /* APR_DBD_TYPE_CLOB, \%pDc */ 27598186Sgordon SQL_TYPE_NULL /* APR_DBD_TYPE_NULL \%pDn */ 27698186Sgordon}; 27798186Sgordon 27898186Sgordon/* result sizes for DBD datatypes (-1 for null-terminated) */ 27998186Sgordonstatic int const sqlSizes[] = { 28098186Sgordon 0, 28198186Sgordon sizeof(char), /**< \%hhd out: char* */ 282114272Smtm sizeof(unsigned char), /**< \%hhu out: unsigned char* */ 28398186Sgordon sizeof(short), /**< \%hd out: short* */ 28498186Sgordon sizeof(unsigned short), /**< \%hu out: unsigned short* */ 28598186Sgordon sizeof(int), /**< \%d out: int* */ 28698186Sgordon sizeof(unsigned int), /**< \%u out: unsigned int* */ 28798186Sgordon sizeof(long), /**< \%ld out: long* */ 28898186Sgordon sizeof(unsigned long), /**< \%lu out: unsigned long* */ 28998186Sgordon sizeof(apr_int64_t), /**< \%lld out: apr_int64_t* */ 29098186Sgordon sizeof(apr_uint64_t), /**< \%llu out: apr_uint64_t* */ 29198186Sgordon sizeof(float), /**< \%f out: float* */ 292126286Smtm sizeof(double), /**< \%lf out: double* */ 29398186Sgordon -1, /**< \%s out: char** */ 29498186Sgordon -1, /**< \%pDt out: char** */ 29598186Sgordon -1, /**< \%pDi out: char** */ 29698186Sgordon -1, /**< \%pDd out: char** */ 29798186Sgordon -1, /**< \%pDa out: char** */ 29898186Sgordon -1, /**< \%pDs out: char** */ 29998186Sgordon -1, /**< \%pDz out: char** */ 30098186Sgordon sizeof(apr_bucket_brigade), /**< \%pDb out: apr_bucket_brigade* */ 30198186Sgordon sizeof(apr_bucket_brigade), /**< \%pDc out: apr_bucket_brigade* */ 30298186Sgordon 0 /**< \%pDn : in: void*, out: void** */ 30398186Sgordon}; 30498186Sgordon 30598186Sgordon/* 30678344Sobrien * local functions 30798186Sgordon */ 30898186Sgordon 30998186Sgordon/* close any open results for the connection */ 31098186Sgordonstatic apr_status_t odbc_close_results(void *d) 31178344Sobrien{ 31298186Sgordon apr_dbd_results_t *dbr = (apr_dbd_results_t *)d; 31398186Sgordon SQLRETURN rc = SQL_SUCCESS; 31498186Sgordon 31578344Sobrien if (dbr && dbr->apr_dbd && dbr->apr_dbd->dbc) { 31678344Sobrien if (!dbr->isclosed) 31778344Sobrien rc = SQLCloseCursor(dbr->stmt); 31898186Sgordon dbr->isclosed = 1; 31998186Sgordon } 32098186Sgordon return APR_FROM_SQL_RESULT(rc); 32198186Sgordon} 32298186Sgordon 32378344Sobrien/* close the ODBC statement handle from a prepare */ 32498186Sgordonstatic apr_status_t odbc_close_pstmt(void *s) 32598186Sgordon{ 32678344Sobrien SQLRETURN rc = APR_SUCCESS; 32798186Sgordon apr_dbd_prepared_t *statement = s; 32898186Sgordon 32978344Sobrien /* stmt is closed if connection has already been closed */ 33078344Sobrien if (statement) { 33178344Sobrien SQLHANDLE hstmt = statement->stmt; 33298186Sgordon 33398186Sgordon if (hstmt && statement->apr_dbd && statement->apr_dbd->dbc) { 33478344Sobrien rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt); 33578344Sobrien } 33678344Sobrien statement->stmt = NULL; 33798186Sgordon } 33878344Sobrien return APR_FROM_SQL_RESULT(rc); 33978344Sobrien} 34078344Sobrien 34178344Sobrien/* close: close/release a connection obtained from open() */ 34298186Sgordonstatic apr_status_t odbc_close(apr_dbd_t *handle) 34398186Sgordon{ 34498186Sgordon SQLRETURN rc = SQL_SUCCESS; 34578344Sobrien 34678344Sobrien if (handle->dbc) { 34798186Sgordon rc = SQLDisconnect(handle->dbc); 34898186Sgordon CHECK_ERROR(handle, "SQLDisconnect", rc, SQL_HANDLE_DBC, handle->dbc); 34998186Sgordon rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->dbc); 35078344Sobrien CHECK_ERROR(handle, "SQLFreeHandle (DBC)", rc, SQL_HANDLE_ENV, henv); 35198186Sgordon handle->dbc = NULL; 35298186Sgordon } 35378344Sobrien return APR_FROM_SQL_RESULT(rc); 35478344Sobrien} 35578344Sobrien 35678344Sobrien/* odbc_close re-defined for passing to pool cleanup */ 35798186Sgordonstatic apr_status_t odbc_close_cleanup(void *handle) 35878344Sobrien{ 35978344Sobrien return odbc_close((apr_dbd_t *)handle); 36078344Sobrien} 36178344Sobrien 36278344Sobrien/* close the ODBC environment handle at process termination */ 36378344Sobrienstatic apr_status_t odbc_close_env(SQLHANDLE henv) 36478344Sobrien{ 36578344Sobrien SQLRETURN rc; 36678344Sobrien 36778344Sobrien rc = SQLFreeHandle(SQL_HANDLE_ENV, henv); 36878344Sobrien henv = NULL; 36978344Sobrien return APR_FROM_SQL_RESULT(rc); 37098186Sgordon} 37178344Sobrien 37278344Sobrien/* setup the arrays in results for all the returned columns */ 37398186Sgordonstatic SQLRETURN odbc_set_result_column(int icol, apr_dbd_results_t *res, 37478344Sobrien SQLHANDLE stmt) 37598186Sgordon{ 37698186Sgordon SQLRETURN rc; 37798186Sgordon ODBC_INTPTR_T maxsize, textsize, realsize, type, isunsigned = 1; 37878344Sobrien 37998186Sgordon /* discover the sql type */ 38078344Sobrien rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL, 38178344Sobrien (SQLPOINTER)&isunsigned); 38298186Sgordon isunsigned = (isunsigned == SQL_TRUE); 38398186Sgordon 38498186Sgordon rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_TYPE, NULL, 0, NULL, 38598186Sgordon (SQLPOINTER)&type); 38678344Sobrien if (!SQL_SUCCEEDED(rc) || type == SQL_UNKNOWN_TYPE) { 38798186Sgordon /* MANY ODBC v2 datasources only supply CONCISE_TYPE */ 38878344Sobrien rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_CONCISE_TYPE, NULL, 38998186Sgordon 0, NULL, (SQLPOINTER)&type); 39098186Sgordon } 39198186Sgordon 39298186Sgordon if (!SQL_SUCCEEDED(rc)) { 39378344Sobrien /* if still unknown make it CHAR */ 39478344Sobrien type = SQL_C_CHAR; 39578344Sobrien } 39678344Sobrien 39778344Sobrien switch (type) { 39878344Sobrien case SQL_INTEGER: 39978344Sobrien case SQL_SMALLINT: 40078344Sobrien case SQL_TINYINT: 40178344Sobrien case SQL_BIGINT: 40278344Sobrien /* fix these numeric binary types up as signed/unsigned for C types */ 40378344Sobrien type += (isunsigned) ? SQL_UNSIGNED_OFFSET : SQL_SIGNED_OFFSET; 40478344Sobrien break; 40598186Sgordon /* LOB types are not changed to C types */ 40698186Sgordon case SQL_LONGVARCHAR: 40778344Sobrien type = SQL_LONGVARCHAR; 40898186Sgordon break; 40998186Sgordon case SQL_LONGVARBINARY: 41078344Sobrien type = SQL_LONGVARBINARY; 41178344Sobrien break; 41278344Sobrien case SQL_FLOAT : 41378344Sobrien type = SQL_C_FLOAT; 41498186Sgordon break; 41578344Sobrien case SQL_DOUBLE : 41698186Sgordon type = SQL_C_DOUBLE; 41798186Sgordon break; 41898186Sgordon 41998186Sgordon /* DBD wants times as strings */ 42078344Sobrien case SQL_TIMESTAMP: 42198186Sgordon case SQL_DATE: 42298186Sgordon case SQL_TIME: 42378344Sobrien default: 42478344Sobrien type = SQL_C_CHAR; 42578344Sobrien } 42678344Sobrien 42798186Sgordon res->coltypes[icol] = (SQLSMALLINT)type; 42878344Sobrien 42998186Sgordon /* size if retrieved as text */ 43098186Sgordon rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, 43198186Sgordon NULL, (SQLPOINTER)&textsize); 43298186Sgordon if (!SQL_SUCCEEDED(rc) || textsize < 0) { 43398186Sgordon textsize = res->apr_dbd->defaultBufferSize; 43498186Sgordon } 43598186Sgordon /* for null-term, which sometimes isn't included */ 43698186Sgordon textsize++; 43798186Sgordon 43898186Sgordon /* real size */ 43998186Sgordon rc = SQLColAttribute(stmt, icol + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, 44098186Sgordon NULL, (SQLPOINTER)&realsize); 44198186Sgordon if (!SQL_SUCCEEDED(rc)) { 44298186Sgordon realsize = textsize; 44398186Sgordon } 44498186Sgordon 44598186Sgordon maxsize = (textsize > realsize) ? textsize : realsize; 44698186Sgordon if (IS_LOB(type) || maxsize <= 0) { 44798186Sgordon /* LOB types are never bound and have a NULL colptr for binary. 44898186Sgordon * Ingore their real (1-2gb) length & use a default - the larger 44998186Sgordon * of defaultBufferSize or APR_BUCKET_BUFF_SIZE. 45098186Sgordon * If not a LOB, but simply unknown length - always use defaultBufferSize. 45198186Sgordon */ 45298186Sgordon maxsize = res->apr_dbd->defaultBufferSize; 45378344Sobrien if (IS_LOB(type) && maxsize < APR_BUCKET_BUFF_SIZE) { 45478344Sobrien maxsize = APR_BUCKET_BUFF_SIZE; 455116097Smtm } 45698186Sgordon 45778344Sobrien res->colptrs[icol] = NULL; 45898186Sgordon res->colstate[icol] = COL_AVAIL; 45978344Sobrien res->colsizes[icol] = (SQLINTEGER)maxsize; 46078344Sobrien rc = SQL_SUCCESS; 46198186Sgordon } 46278344Sobrien else { 46398186Sgordon res->colptrs[icol] = apr_pcalloc(res->pool, maxsize); 46498186Sgordon res->colsizes[icol] = (SQLINTEGER)maxsize; 46578344Sobrien if (res->apr_dbd->dboptions & SQL_GD_BOUND) { 46678344Sobrien /* we are allowed to call SQLGetData if we need to */ 46798186Sgordon rc = SQLBindCol(stmt, icol + 1, res->coltypes[icol], 46898186Sgordon res->colptrs[icol], maxsize, 46978344Sobrien &(res->colinds[icol])); 47078344Sobrien CHECK_ERROR(res->apr_dbd, "SQLBindCol", rc, SQL_HANDLE_STMT, 47178344Sobrien stmt); 47278344Sobrien res->colstate[icol] = SQL_SUCCEEDED(rc) ? COL_BOUND : COL_AVAIL; 47378344Sobrien } 47478344Sobrien else { 47598186Sgordon /* this driver won't allow us to call SQLGetData on bound 47698186Sgordon * columns - so don't bind any 47798186Sgordon */ 47898186Sgordon res->colstate[icol] = COL_AVAIL; 47998186Sgordon rc = SQL_SUCCESS; 48078344Sobrien } 48198186Sgordon } 48278344Sobrien return rc; 48398186Sgordon} 48498186Sgordon 48578344Sobrien/* create and populate an apr_dbd_results_t for a select */ 48698186Sgordonstatic SQLRETURN odbc_create_results(apr_dbd_t *handle, SQLHANDLE hstmt, 48778344Sobrien apr_pool_t *pool, const int random, 48898186Sgordon apr_dbd_results_t **res) 48998186Sgordon{ 49098186Sgordon SQLRETURN rc; 49178344Sobrien SQLSMALLINT ncols; 49278344Sobrien 49398186Sgordon *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 49478344Sobrien (*res)->stmt = hstmt; 49578344Sobrien (*res)->dbc = handle->dbc; 49678344Sobrien (*res)->pool = pool; 49798186Sgordon (*res)->random = random; 49878344Sobrien (*res)->apr_dbd = handle; 49978344Sobrien rc = SQLNumResultCols(hstmt, &ncols); 50078344Sobrien CHECK_ERROR(handle, "SQLNumResultCols", rc, SQL_HANDLE_STMT, hstmt); 50178344Sobrien (*res)->ncols = ncols; 50298186Sgordon 50378344Sobrien if (SQL_SUCCEEDED(rc)) { 50498186Sgordon int i; 50578344Sobrien 50698186Sgordon (*res)->colnames = apr_pcalloc(pool, ncols * sizeof(char *)); 50798186Sgordon (*res)->colptrs = apr_pcalloc(pool, ncols * sizeof(void *)); 50898186Sgordon (*res)->colsizes = apr_pcalloc(pool, ncols * sizeof(SQLINTEGER)); 50978344Sobrien (*res)->coltypes = apr_pcalloc(pool, ncols * sizeof(SQLSMALLINT)); 51098186Sgordon (*res)->colinds = apr_pcalloc(pool, ncols * sizeof(SQLLEN)); 511124832Smtm (*res)->colstate = apr_pcalloc(pool, ncols * sizeof(int)); 51298186Sgordon (*res)->ncols = ncols; 51398186Sgordon 51498186Sgordon for (i = 0; i < ncols; i++) { 51598186Sgordon odbc_set_result_column(i, (*res), hstmt); 51678344Sobrien } 51798186Sgordon } 51878344Sobrien return rc; 51978344Sobrien} 52078344Sobrien 52198186Sgordon 52278344Sobrien/* bind a parameter - input params only, does not support output parameters */ 52378344Sobrienstatic SQLRETURN odbc_bind_param(apr_pool_t *pool, 52478344Sobrien apr_dbd_prepared_t *statement, const int narg, 52578344Sobrien const SQLSMALLINT type, int *argp, 52678344Sobrien const void **args, const int textmode) 52778344Sobrien{ 52878344Sobrien SQLRETURN rc; 52978344Sobrien SQLSMALLINT baseType, cType; 53098186Sgordon void *ptr; 53178344Sobrien SQLULEN len; 53278344Sobrien SQLLEN *indicator; 53378344Sobrien static SQLLEN nullValue = SQL_NULL_DATA; 53478344Sobrien static SQLSMALLINT inOut = SQL_PARAM_INPUT; /* only input params */ 53578344Sobrien 53678344Sobrien /* bind a NULL data value */ 53798186Sgordon if (args[*argp] == NULL || type == APR_DBD_TYPE_NULL) { 53898186Sgordon baseType = SQL_CHAR; 53978344Sobrien cType = SQL_C_CHAR; 54078344Sobrien ptr = &nullValue; 54178344Sobrien len = sizeof(SQLINTEGER); 54278344Sobrien indicator = &nullValue; 543116097Smtm (*argp)++; 544116097Smtm } 545116097Smtm /* bind a non-NULL data value */ 546116097Smtm else { 547116097Smtm if (type < 0 || type >= NUM_APR_DBD_TYPES) { 548116097Smtm return APR_EGENERAL; 54978344Sobrien } 55078344Sobrien 551116097Smtm baseType = sqlBaseType[type]; 552116097Smtm cType = sqlCtype[type]; 553116097Smtm indicator = NULL; 554116097Smtm /* LOBs */ 555116097Smtm if (IS_LOB(cType)) { 556116097Smtm ptr = (void *)args[*argp]; 55798186Sgordon len = (SQLULEN) * (apr_size_t *)args[*argp + 1]; 558109582Smtm cType = (IS_CLOB(cType)) ? SQL_C_CHAR : SQL_C_DEFAULT; 559116097Smtm (*argp) += 4; /* LOBs consume 4 args (last two are unused) */ 560116097Smtm } 561116097Smtm /* non-LOBs */ 562116097Smtm else { 563116097Smtm switch (baseType) { 564116097Smtm case SQL_CHAR: 56578344Sobrien case SQL_DATE: 56678344Sobrien case SQL_TIME: 56798186Sgordon case SQL_TIMESTAMP: 56878344Sobrien ptr = (void *)args[*argp]; 56978344Sobrien len = (SQLULEN)strlen(ptr); 57098186Sgordon break; 57198186Sgordon case SQL_TINYINT: 57278344Sobrien ptr = apr_palloc(pool, sizeof(unsigned char)); 57378344Sobrien len = sizeof(unsigned char); 57478344Sobrien *(unsigned char *)ptr = 57578344Sobrien (textmode ? 57678344Sobrien atoi(args[*argp]) : *(unsigned char *)args[*argp]); 57778344Sobrien break; 57878344Sobrien case SQL_SMALLINT: 57998186Sgordon ptr = apr_palloc(pool, sizeof(short)); 58098186Sgordon len = sizeof(short); 58178344Sobrien *(short *)ptr = 58278344Sobrien (textmode ? atoi(args[*argp]) : *(short *)args[*argp]); 58378344Sobrien break; 58478344Sobrien case SQL_INTEGER: 58598186Sgordon ptr = apr_palloc(pool, sizeof(int)); 58678344Sobrien len = sizeof(int); 58778344Sobrien *(long *)ptr = 58878344Sobrien (textmode ? atol(args[*argp]) : *(long *)args[*argp]); 58978344Sobrien break; 59078344Sobrien case SQL_FLOAT: 59178344Sobrien ptr = apr_palloc(pool, sizeof(float)); 59278344Sobrien len = sizeof(float); 59378344Sobrien *(float *)ptr = 59478344Sobrien (textmode ? 59598186Sgordon (float)atof(args[*argp]) : *(float *)args[*argp]); 59678344Sobrien break; 59778344Sobrien case SQL_DOUBLE: 59878344Sobrien ptr = apr_palloc(pool, sizeof(double)); 59978344Sobrien len = sizeof(double); 60078344Sobrien *(double *)ptr = 60178344Sobrien (textmode ? atof(args[*argp]) : *(double *) 60278344Sobrien args[*argp]); 60398186Sgordon break; 60478344Sobrien case SQL_BIGINT: 60578344Sobrien ptr = apr_palloc(pool, sizeof(apr_int64_t)); 60678344Sobrien len = sizeof(apr_int64_t); 60778344Sobrien *(apr_int64_t *)ptr = 60878344Sobrien (textmode ? 60978344Sobrien apr_atoi64(args[*argp]) : *(apr_int64_t *)args[*argp]); 61078344Sobrien break; 61198186Sgordon default: 61278344Sobrien return APR_EGENERAL; 61378344Sobrien } 61478344Sobrien (*argp)++; /* non LOBs consume one argument */ 61578344Sobrien } 61678344Sobrien } 61778344Sobrien rc = SQLBindParameter(statement->stmt, narg, inOut, cType, 61878344Sobrien baseType, len, 0, ptr, len, indicator); 61978344Sobrien CHECK_ERROR(statement->apr_dbd, "SQLBindParameter", rc, SQL_HANDLE_STMT, 620116097Smtm statement->stmt); 621116097Smtm return rc; 622116097Smtm} 623116097Smtm 624116097Smtm/* LOB / Bucket Brigade functions */ 625116097Smtm 62678344Sobrien/* bucket type specific destroy */ 62778344Sobrienstatic void odbc_lob_bucket_destroy(void *data) 62878344Sobrien{ 62978344Sobrien odbc_bucket *bd = data; 63078344Sobrien 63178344Sobrien if (apr_bucket_shared_destroy(bd)) 63278344Sobrien apr_bucket_free(bd); 63378344Sobrien} 63478344Sobrien 63598186Sgordon/* set aside a bucket if possible */ 63678344Sobrienstatic apr_status_t odbc_lob_bucket_setaside(apr_bucket *e, apr_pool_t *pool) 63778344Sobrien{ 63878344Sobrien odbc_bucket *bd = (odbc_bucket *)e->data; 63978344Sobrien 64098186Sgordon /* Unlikely - but if the row pool is ancestor of this pool then it is OK */ 64198186Sgordon if (apr_pool_is_ancestor(bd->row->pool, pool)) 64298186Sgordon return APR_SUCCESS; 64398186Sgordon 64478344Sobrien return apr_bucket_setaside_notimpl(e, pool); 64598186Sgordon} 64698186Sgordon 64798186Sgordon/* split a bucket into a heap bucket followed by a LOB bkt w/remaining data */ 64898186Sgordonstatic apr_status_t odbc_lob_bucket_read(apr_bucket *e, const char **str, 64998186Sgordon apr_size_t *len, apr_read_type_e block) 650116097Smtm{ 651116097Smtm SQLRETURN rc; 652116097Smtm SQLLEN len_indicator; 65398186Sgordon SQLSMALLINT type; 65498186Sgordon odbc_bucket *bd = (odbc_bucket *)e->data; 65598186Sgordon apr_bucket *nxt; 656116097Smtm void *buf; 657116097Smtm int bufsize = bd->row->res->apr_dbd->defaultBufferSize; 658116097Smtm int eos; 659116097Smtm 66078344Sobrien /* C type is CHAR for CLOBs, DEFAULT for BLOBs */ 66178344Sobrien type = bd->row->res->coltypes[bd->col]; 66278344Sobrien type = (type == SQL_LONGVARCHAR) ? SQL_C_CHAR : SQL_C_DEFAULT; 66398186Sgordon 66478344Sobrien /* LOB buffers are always at least APR_BUCKET_BUFF_SIZE, 66578344Sobrien * but they may be much bigger per the BUFSIZE parameter. 66678344Sobrien */ 66778344Sobrien if (bufsize < APR_BUCKET_BUFF_SIZE) 66878344Sobrien bufsize = APR_BUCKET_BUFF_SIZE; 66978344Sobrien 67078344Sobrien buf = apr_bucket_alloc(bufsize, e->list); 67178344Sobrien *str = NULL; 67278344Sobrien *len = 0; 67398186Sgordon 67498186Sgordon rc = SQLGetData(bd->row->res->stmt, bd->col + 1, 67598186Sgordon type, buf, bufsize, 676117977Smtm &len_indicator); 677116097Smtm 678116097Smtm CHECK_ERROR(bd->row->res->apr_dbd, "SQLGetData", rc, 679116097Smtm SQL_HANDLE_STMT, bd->row->res->stmt); 680116097Smtm 68178344Sobrien if (rc == SQL_NO_DATA || len_indicator == SQL_NULL_DATA || len_indicator < 0) 68298186Sgordon len_indicator = 0; 68398186Sgordon 68498186Sgordon if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA) { 68578344Sobrien 68698186Sgordon if (rc == SQL_SUCCESS_WITH_INFO 68798186Sgordon && (len_indicator == SQL_NO_TOTAL || len_indicator >= bufsize)) { 68898186Sgordon /* not the last read = a full buffer. CLOBs have a null terminator */ 68998186Sgordon *len = bufsize - (IS_CLOB(bd->type) ? 1 : 0 ); 69098186Sgordon 69198186Sgordon eos = 0; 69298186Sgordon } 69398186Sgordon else { 694116097Smtm /* the last read - len_indicator is supposed to be the length, 695116097Smtm * but some driver get this wrong and return the total length. 696116097Smtm * We try to handle both interpretations. 69798186Sgordon */ 69898186Sgordon *len = (len_indicator > bufsize 69998186Sgordon && len_indicator >= (SQLLEN)e->start) 70098186Sgordon ? (len_indicator - (SQLLEN)e->start) : len_indicator; 701116097Smtm 702116097Smtm eos = 1; 703116097Smtm } 704116097Smtm 70578344Sobrien if (!eos) { 70678344Sobrien /* Create a new LOB bucket to append and append it */ 70778344Sobrien nxt = apr_bucket_alloc(sizeof(apr_bucket *), e->list); 70898186Sgordon APR_BUCKET_INIT(nxt); 70978344Sobrien nxt->length = -1; 71078344Sobrien nxt->data = e->data; 71178344Sobrien nxt->type = &odbc_bucket_type; 71278344Sobrien nxt->free = apr_bucket_free; 71378344Sobrien nxt->list = e->list; 71478344Sobrien nxt->start = e->start + *len; 71578344Sobrien APR_BUCKET_INSERT_AFTER(e, nxt); 71678344Sobrien } 71778344Sobrien else { 718116097Smtm odbc_lob_bucket_destroy(e->data); 719116097Smtm } 720116097Smtm /* make current bucket into a heap bucket */ 721116097Smtm apr_bucket_heap_make(e, buf, *len, apr_bucket_free); 722116097Smtm *str = buf; 72378344Sobrien 72498186Sgordon /* No data is success in this context */ 72598186Sgordon rc = SQL_SUCCESS; 72698186Sgordon } 72798186Sgordon return APR_FROM_SQL_RESULT(rc); 728116097Smtm} 729116097Smtm 730116097Smtm/* Create a bucket brigade on the row pool for a LOB column */ 731116097Smtmstatic apr_status_t odbc_create_bucket(const apr_dbd_row_t *row, const int col, 732116097Smtm SQLSMALLINT type, apr_bucket_brigade *bb) 733116097Smtm{ 73498186Sgordon apr_bucket_alloc_t *list = bb->bucket_alloc; 73578344Sobrien apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 73678344Sobrien odbc_bucket *bd = apr_bucket_alloc(sizeof(odbc_bucket), list); 73778344Sobrien apr_bucket *eos = apr_bucket_eos_create(list); 738116097Smtm 739116097Smtm bd->row = row; 740116097Smtm bd->col = col; 741116097Smtm bd->type = type; 742116097Smtm 74378344Sobrien APR_BUCKET_INIT(b); 74478344Sobrien b->type = &odbc_bucket_type; 74578344Sobrien b->free = apr_bucket_free; 74678344Sobrien b->list = list; 747126285Smtm /* LOB lengths are unknown in ODBC */ 74878344Sobrien b = apr_bucket_shared_make(b, bd, 0, -1); 74978344Sobrien 750126285Smtm APR_BRIGADE_INSERT_TAIL(bb, b); 75178344Sobrien APR_BRIGADE_INSERT_TAIL(bb, eos); 75298186Sgordon 75398186Sgordon return APR_SUCCESS; 75498186Sgordon} 755116097Smtm 756116097Smtm/* returns a data pointer for a column, returns NULL for NULL value, 757116097Smtm * return -1 if data not available 758116097Smtm */ 75978344Sobrienstatic void *odbc_get(const apr_dbd_row_t *row, const int col, 76078344Sobrien const SQLSMALLINT sqltype) 76198186Sgordon{ 76298186Sgordon SQLRETURN rc; 76398186Sgordon SQLLEN indicator; 76498186Sgordon int state = row->res->colstate[col]; 76598186Sgordon ODBC_INTPTR_T options = row->res->apr_dbd->dboptions; 76698186Sgordon 76778344Sobrien switch (state) { 76878344Sobrien case (COL_UNAVAIL): 76978344Sobrien return (void *)-1; 77078344Sobrien case (COL_RETRIEVED): 77178344Sobrien return NULL; 77278344Sobrien 77378344Sobrien case (COL_BOUND): 77478344Sobrien case (COL_PRESENT): 77578344Sobrien if (sqltype == row->res->coltypes[col]) { 77678344Sobrien /* same type and we already have the data */ 77778344Sobrien row->res->colstate[col] = COL_RETRIEVED; 77878344Sobrien return (row->res->colinds[col] == SQL_NULL_DATA) ? 77978344Sobrien NULL : row->res->colptrs[col]; 78078344Sobrien } 78178344Sobrien } 78278344Sobrien 783116097Smtm /* we need to get the data now */ 78478344Sobrien if (!(options & SQL_GD_ANY_ORDER)) { 78578344Sobrien /* this ODBC driver requires columns to be retrieved in order, 78698186Sgordon * so we attempt to get every prior un-gotten non-LOB column 78778344Sobrien */ 78878344Sobrien int i; 78978344Sobrien for (i = 0; i < col; i++) { 79078344Sobrien if (row->res->colstate[i] == COL_AVAIL) { 79178344Sobrien if (IS_LOB(row->res->coltypes[i])) 79278344Sobrien row->res->colstate[i] = COL_UNAVAIL; 79378344Sobrien else { 79478344Sobrien odbc_get(row, i, row->res->coltypes[i]); 79598186Sgordon row->res->colstate[i] = COL_PRESENT; 79698186Sgordon } 79798186Sgordon } 79878344Sobrien } 79978344Sobrien } 80078344Sobrien 80178344Sobrien if ((state == COL_BOUND && !(options & SQL_GD_BOUND))) 80278344Sobrien /* this driver won't let us re-get bound columns */ 80378344Sobrien return (void *)-1; 80478344Sobrien 80578344Sobrien /* a LOB might not have a buffer allocated yet - so create one */ 80678344Sobrien if (!row->res->colptrs[col]) 80798186Sgordon row->res->colptrs[col] = apr_pcalloc(row->pool, row->res->colsizes[col]); 80898186Sgordon 80998186Sgordon rc = SQLGetData(row->res->stmt, col + 1, sqltype, row->res->colptrs[col], 81098186Sgordon row->res->colsizes[col], &indicator); 81198186Sgordon CHECK_ERROR(row->res->apr_dbd, "SQLGetData", rc, SQL_HANDLE_STMT, 81298186Sgordon row->res->stmt); 81398186Sgordon if (indicator == SQL_NULL_DATA || rc == SQL_NO_DATA) 81478344Sobrien return NULL; 81578344Sobrien 81678344Sobrien if (SQL_SUCCEEDED(rc)) { 81778344Sobrien /* whatever it was originally, it is now this sqltype */ 81898186Sgordon row->res->coltypes[col] = sqltype; 81998186Sgordon /* this allows getting CLOBs in text mode by calling get_entry 82098186Sgordon * until it returns NULL 82178344Sobrien */ 82298186Sgordon row->res->colstate[col] = 82398186Sgordon (rc == SQL_SUCCESS_WITH_INFO) ? COL_AVAIL : COL_RETRIEVED; 82498186Sgordon return row->res->colptrs[col]; 82598186Sgordon } 82698186Sgordon else 82798186Sgordon return (void *)-1; 82898186Sgordon} 82998186Sgordon 83078344Sobrien/* Parse the parameter string for open */ 83178344Sobrienstatic apr_status_t odbc_parse_params(apr_pool_t *pool, const char *params, 83278344Sobrien int *connect, SQLCHAR **datasource, 83378344Sobrien SQLCHAR **user, SQLCHAR **password, 83478344Sobrien int *defaultBufferSize, int *nattrs, 83578344Sobrien int **attrs, ODBC_INTPTR_T **attrvals) 83678344Sobrien{ 83778344Sobrien char *seps, *last, *next, *name[MAX_PARAMS], *val[MAX_PARAMS]; 83878344Sobrien int nparams = 0, i, j; 83978344Sobrien 84078344Sobrien *attrs = apr_pcalloc(pool, MAX_PARAMS * sizeof(char *)); 84178344Sobrien *attrvals = apr_pcalloc(pool, MAX_PARAMS * sizeof(ODBC_INTPTR_T)); 84278344Sobrien *nattrs = 0; 84378344Sobrien seps = DEFAULTSEPS; 84478344Sobrien name[nparams] = apr_strtok(apr_pstrdup(pool, params), seps, &last); 845126285Smtm 846126285Smtm /* no params is OK here - let connect return a more useful error msg */ 847126285Smtm if (!name[nparams]) 84898186Sgordon return SQL_SUCCESS; 84998186Sgordon 85098186Sgordon do { 85198186Sgordon if (last[strspn(last, seps)] == CSINGLEQUOTE) { 85298186Sgordon last += strspn(last, seps); 85398186Sgordon seps=SSINGLEQUOTE; 85498186Sgordon } 85598186Sgordon val[nparams] = apr_strtok(NULL, seps, &last); 856126285Smtm seps = DEFAULTSEPS; 85798186Sgordon 85878344Sobrien ++nparams; 85998186Sgordon next = apr_strtok(NULL, seps, &last); 86078344Sobrien if (!next) { 86178344Sobrien break; 862101850Sgordon } 863101850Sgordon if (nparams >= MAX_PARAMS) { 864101850Sgordon /* too many parameters, no place to store */ 865103018Sgordon return APR_EGENERAL; 866101850Sgordon } 867101850Sgordon name[nparams] = next; 868101850Sgordon } while (1); 869101850Sgordon 870101850Sgordon for (j = i = 0; i < nparams; i++) { 871101850Sgordon if (!apr_strnatcasecmp(name[i], "CONNECT")) { 872101850Sgordon *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]); 873101850Sgordon *connect = 1; 874115950Smtm } 875115950Smtm else if (!apr_strnatcasecmp(name[i], "DATASOURCE")) { 876101850Sgordon *datasource = (SQLCHAR *)apr_pstrdup(pool, val[i]); 877101850Sgordon *connect = 0; 878101850Sgordon } 87978344Sobrien else if (!apr_strnatcasecmp(name[i], "USER")) { 88078344Sobrien *user = (SQLCHAR *)apr_pstrdup(pool, val[i]); 88178344Sobrien } 88278344Sobrien else if (!apr_strnatcasecmp(name[i], "PASSWORD")) { 88378344Sobrien *password = (SQLCHAR *)apr_pstrdup(pool, val[i]); 88478344Sobrien } 88578344Sobrien else if (!apr_strnatcasecmp(name[i], "BUFSIZE")) { 88678344Sobrien *defaultBufferSize = atoi(val[i]); 88778344Sobrien } 888106643Sgordon else if (!apr_strnatcasecmp(name[i], "ACCESS")) { 88978344Sobrien if (!apr_strnatcasecmp(val[i], "READ_ONLY")) 89078344Sobrien (*attrvals)[j] = SQL_MODE_READ_ONLY; 891126286Smtm else if (!apr_strnatcasecmp(val[i], "READ_WRITE")) 89278344Sobrien (*attrvals)[j] = SQL_MODE_READ_WRITE; 89378344Sobrien else 89478344Sobrien return SQL_ERROR; 89578344Sobrien (*attrs)[j++] = SQL_ATTR_ACCESS_MODE; 89678344Sobrien } 89778344Sobrien else if (!apr_strnatcasecmp(name[i], "CTIMEOUT")) { 89878344Sobrien (*attrvals)[j] = atoi(val[i]); 89978344Sobrien (*attrs)[j++] = SQL_ATTR_LOGIN_TIMEOUT; 90078344Sobrien } 90178344Sobrien else if (!apr_strnatcasecmp(name[i], "STIMEOUT")) { 90278344Sobrien (*attrvals)[j] = atoi(val[i]); 90378344Sobrien (*attrs)[j++] = SQL_ATTR_CONNECTION_TIMEOUT; 90478344Sobrien } 90578344Sobrien else if (!apr_strnatcasecmp(name[i], "TXMODE")) { 90678344Sobrien if (!apr_strnatcasecmp(val[i], "READ_UNCOMMITTED")) 90778344Sobrien (*attrvals)[j] = SQL_TXN_READ_UNCOMMITTED; 908106643Sgordon else if (!apr_strnatcasecmp(val[i], "READ_COMMITTED")) 909106643Sgordon (*attrvals)[j] = SQL_TXN_READ_COMMITTED; 910106643Sgordon else if (!apr_strnatcasecmp(val[i], "REPEATABLE_READ")) 911106643Sgordon (*attrvals)[j] = SQL_TXN_REPEATABLE_READ; 91278344Sobrien else if (!apr_strnatcasecmp(val[i], "SERIALIZABLE")) 91378344Sobrien (*attrvals)[j] = SQL_TXN_SERIALIZABLE; 91478344Sobrien else if (!apr_strnatcasecmp(val[i], "DEFAULT")) 91578344Sobrien continue; 91678344Sobrien else 91778344Sobrien return SQL_ERROR; 91878344Sobrien (*attrs)[j++] = SQL_ATTR_TXN_ISOLATION; 91978344Sobrien } 92078344Sobrien else 921106643Sgordon return SQL_ERROR; 922106643Sgordon } 923106643Sgordon *nattrs = j; 924106643Sgordon return (*datasource && *defaultBufferSize) ? APR_SUCCESS : SQL_ERROR; 92578344Sobrien} 92698186Sgordon 92798186Sgordon/* common handling after ODBC calls - save error info (code and text) in dbc */ 92898186Sgordonstatic void check_error(apr_dbd_t *dbc, const char *step, SQLRETURN rc, 92998186Sgordon SQLSMALLINT type, SQLHANDLE h, int line) 93098186Sgordon{ 93198186Sgordon SQLCHAR buffer[512]; 93298186Sgordon SQLCHAR sqlstate[128]; 933119170Smtm SQLINTEGER native; 934119170Smtm SQLSMALLINT reslength; 935119170Smtm char *res, *p, *end, *logval = NULL; 936119170Smtm int i; 937119170Smtm 938119170Smtm /* set info about last error in dbc - fast return for SQL_SUCCESS */ 939119170Smtm if (rc == SQL_SUCCESS) { 940119170Smtm char successMsg[] = "[dbd_odbc] SQL_SUCCESS "; 94198186Sgordon apr_size_t successMsgLen = sizeof successMsg - 1; 94298186Sgordon 94398186Sgordon dbc->lasterrorcode = SQL_SUCCESS; 94498186Sgordon apr_cpystrn(dbc->lastError, successMsg, sizeof dbc->lastError); 945106643Sgordon apr_cpystrn(dbc->lastError + successMsgLen, step, 94698186Sgordon sizeof dbc->lastError - successMsgLen); 94798186Sgordon return; 94898186Sgordon } 94998186Sgordon switch (rc) { 95098186Sgordon case SQL_INVALID_HANDLE: 95198186Sgordon res = "SQL_INVALID_HANDLE"; 95298186Sgordon break; 953106700Sgordon case SQL_ERROR: 954106700Sgordon res = "SQL_ERROR"; 955106700Sgordon break; 956106643Sgordon case SQL_SUCCESS_WITH_INFO: 95798186Sgordon res = "SQL_SUCCESS_WITH_INFO"; 95898186Sgordon break; 95998186Sgordon case SQL_STILL_EXECUTING: 96098186Sgordon res = "SQL_STILL_EXECUTING"; 96198186Sgordon break; 96298186Sgordon case SQL_NEED_DATA: 96398186Sgordon res = "SQL_NEED_DATA"; 96498186Sgordon break; 96598186Sgordon case SQL_NO_DATA: 96698186Sgordon res = "SQL_NO_DATA"; 96798186Sgordon break; 96898186Sgordon default: 96998186Sgordon res = "unrecognized SQL return code"; 97098186Sgordon } 97198186Sgordon /* these two returns are expected during normal execution */ 97298186Sgordon if (rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA 97398186Sgordon && dbc->can_commit != APR_DBD_TRANSACTION_IGNORE_ERRORS) { 97498186Sgordon dbc->can_commit = APR_DBD_TRANSACTION_ROLLBACK; 97598186Sgordon } 97698186Sgordon p = dbc->lastError; 97798186Sgordon end = p + sizeof(dbc->lastError); 97898186Sgordon dbc->lasterrorcode = rc; 97998186Sgordon p += sprintf(p, "[dbd_odbc] %.64s returned %.30s (%d) at %.24s:%d ", 98098186Sgordon step, res, rc, SOURCE_FILE, line - 1); 98198186Sgordon for (i = 1, rc = 0; rc == 0; i++) { 98298186Sgordon rc = SQLGetDiagRec(type, h, i, sqlstate, &native, buffer, 98398186Sgordon sizeof(buffer), &reslength); 98498186Sgordon if (SQL_SUCCEEDED(rc) && (p < (end - 280))) 98598186Sgordon p += sprintf(p, "%.256s %.20s ", buffer, sqlstate); 98698186Sgordon } 98798186Sgordon apr_env_get(&logval, "apr_dbd_odbc_log", dbc->pool); 98898186Sgordon /* if env var was set or call was init/open (no dbname) - log to stderr */ 98998186Sgordon if (logval || !dbc->dbname ) { 99098186Sgordon char timestamp[APR_CTIME_LEN]; 99198186Sgordon 99298186Sgordon apr_file_t *se; 99398186Sgordon apr_ctime(timestamp, apr_time_now()); 99498186Sgordon apr_file_open_stderr(&se, dbc->pool); 99598186Sgordon apr_file_printf(se, "[%s] %s\n", timestamp, dbc->lastError); 99698186Sgordon } 99798186Sgordon} 99898186Sgordon 99998186Sgordonstatic APR_INLINE int odbc_check_rollback(apr_dbd_t *handle) 100098186Sgordon{ 100198186Sgordon if (handle->can_commit == APR_DBD_TRANSACTION_ROLLBACK) { 100298186Sgordon handle->lasterrorcode = SQL_ERROR; 100398186Sgordon apr_cpystrn(handle->lastError, "[dbd_odbc] Rollback pending ", 100498186Sgordon sizeof handle->lastError); 100598186Sgordon return 1; 100698186Sgordon } 100798186Sgordon return 0; 100898186Sgordon} 100998186Sgordon 101098186Sgordon/* 101198186Sgordon * public functions per DBD driver API 101298186Sgordon */ 101398186Sgordon 101498186Sgordon/** init: allow driver to perform once-only initialisation. **/ 101598186Sgordonstatic void odbc_init(apr_pool_t *pool) 101698186Sgordon{ 101798186Sgordon SQLRETURN rc; 101898186Sgordon char *step; 101998186Sgordon apr_version_t apuver; 102098186Sgordon 102198186Sgordon apu_version(&apuver); 102298186Sgordon if (apuver.major != DRIVER_APU_VERSION_MAJOR 102398186Sgordon || apuver.minor != DRIVER_APU_VERSION_MINOR) { 102498186Sgordon apr_file_t *se; 102598186Sgordon 102698186Sgordon apr_file_open_stderr(&se, pool); 102798186Sgordon apr_file_printf(se, "Incorrect " ODBC_DRIVER_STRING " dbd driver version\n" 102898186Sgordon "Attempt to load APU version %d.%d driver with APU version %d.%d\n", 102998186Sgordon DRIVER_APU_VERSION_MAJOR, DRIVER_APU_VERSION_MINOR, 103098186Sgordon apuver.major, apuver.minor); 103198186Sgordon abort(); 103298186Sgordon } 103398186Sgordon 103498186Sgordon if (henv) 103598186Sgordon return; 103698186Sgordon 103798186Sgordon step = "SQLAllocHandle (SQL_HANDLE_ENV)"; 103898186Sgordon rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); 103998186Sgordon apr_pool_cleanup_register(pool, henv, odbc_close_env, apr_pool_cleanup_null); 104098186Sgordon if (SQL_SUCCEEDED(rc)) { 104198186Sgordon step = "SQLSetEnvAttr"; 104298186Sgordon rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION, 104398186Sgordon (SQLPOINTER)SQL_OV_ODBC3, 0); 104498186Sgordon } 104598186Sgordon else { 1046119166Smtm apr_dbd_t tmp_dbc; 1047123344Smtm SQLHANDLE err_h = henv; 1048123344Smtm 1049123344Smtm tmp_dbc.pool = pool; 1050123344Smtm tmp_dbc.dbname = NULL; 1051123344Smtm CHECK_ERROR(&tmp_dbc, step, rc, SQL_HANDLE_ENV, err_h); 1052119166Smtm } 1053123344Smtm} 1054119166Smtm 1055123344Smtm/** native_handle: return the native database handle of the underlying db **/ 1056123344Smtmstatic void *odbc_native_handle(apr_dbd_t *handle) 1057123344Smtm{ 1058123344Smtm return handle->dbc; 1059123344Smtm} 1060119166Smtm 1061123344Smtm/** open: obtain a database connection from the server rec. **/ 1062123344Smtm 1063119166Smtm/* It would be more efficient to allocate a single statement handle 1064119166Smtm * here - but SQL_ATTR_CURSOR_SCROLLABLE must be set before 1065123344Smtm * SQLPrepare, and we don't know whether random-access is 1066123344Smtm * specified until SQLExecute so we cannot. 1067119166Smtm */ 1068119166Smtm 1069119166Smtmstatic apr_dbd_t *odbc_open(apr_pool_t *pool, const char *params, const char **error) 1070123344Smtm{ 1071119166Smtm SQLRETURN rc; 1072119166Smtm SQLHANDLE hdbc = NULL; 1073119166Smtm apr_dbd_t *handle; 1074119166Smtm char *err_step; 1075119166Smtm int err_htype, i; 1076119166Smtm int defaultBufferSize = DEFAULT_BUFFER_SIZE; 1077119166Smtm SQLHANDLE err_h = NULL; 1078119166Smtm SQLCHAR *datasource = (SQLCHAR *)"", *user = (SQLCHAR *)"", 1079119166Smtm *password = (SQLCHAR *)""; 1080119166Smtm int nattrs = 0, *attrs = NULL, connect = 0; 1081119166Smtm ODBC_INTPTR_T *attrvals = NULL; 1082119166Smtm 1083119166Smtm err_step = "SQLAllocHandle (SQL_HANDLE_DBC)"; 1084119166Smtm err_htype = SQL_HANDLE_ENV; 1085119166Smtm err_h = henv; 1086119166Smtm rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); 1087119166Smtm if (SQL_SUCCEEDED(rc)) { 1088119166Smtm err_step = "Invalid DBD Parameters - open"; 1089119166Smtm err_htype = SQL_HANDLE_DBC; 1090119166Smtm err_h = hdbc; 1091119166Smtm rc = odbc_parse_params(pool, params, &connect, &datasource, &user, 1092119166Smtm &password, &defaultBufferSize, &nattrs, &attrs, 1093119166Smtm &attrvals); 1094119166Smtm } 1095119166Smtm if (SQL_SUCCEEDED(rc)) { 1096119166Smtm for (i = 0; i < nattrs && SQL_SUCCEEDED(rc); i++) { 1097119166Smtm err_step = "SQLSetConnectAttr (from DBD Parameters)"; 1098119166Smtm err_htype = SQL_HANDLE_DBC; 1099119166Smtm err_h = hdbc; 1100119166Smtm rc = SQLSetConnectAttr(hdbc, attrs[i], (SQLPOINTER)attrvals[i], 0); 1101119166Smtm } 1102119166Smtm } 1103119166Smtm if (SQL_SUCCEEDED(rc)) { 1104119166Smtm if (connect) { 1105119166Smtm SQLCHAR out[1024]; 1106119166Smtm SQLSMALLINT outlen; 1107119166Smtm 1108119166Smtm err_step = "SQLDriverConnect"; 1109119166Smtm err_htype = SQL_HANDLE_DBC; 1110119166Smtm err_h = hdbc; 1111119166Smtm rc = SQLDriverConnect(hdbc, NULL, datasource, 1112119166Smtm (SQLSMALLINT)strlen((char *)datasource), 1113119166Smtm out, sizeof(out), &outlen, SQL_DRIVER_NOPROMPT); 1114119166Smtm } 1115119166Smtm else { 1116119166Smtm err_step = "SQLConnect"; 1117119166Smtm err_htype = SQL_HANDLE_DBC; 1118119166Smtm err_h = hdbc; 1119119166Smtm rc = SQLConnect(hdbc, datasource, 1120119166Smtm (SQLSMALLINT)strlen((char *)datasource), 1121119166Smtm user, (SQLSMALLINT)strlen((char *)user), 1122119166Smtm password, (SQLSMALLINT)strlen((char *)password)); 1123119166Smtm } 1124119166Smtm } 1125119166Smtm if (SQL_SUCCEEDED(rc)) { 1126119166Smtm handle = apr_pcalloc(pool, sizeof(apr_dbd_t)); 1127119166Smtm handle->dbname = apr_pstrdup(pool, (char *)datasource); 1128119166Smtm handle->dbc = hdbc; 1129119166Smtm handle->pool = pool; 1130119166Smtm handle->defaultBufferSize = defaultBufferSize; 1131119166Smtm CHECK_ERROR(handle, "SQLConnect", rc, SQL_HANDLE_DBC, handle->dbc); 1132119166Smtm handle->default_transaction_mode = 0; 1133119166Smtm handle->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS; 1134119166Smtm SQLGetInfo(hdbc, SQL_DEFAULT_TXN_ISOLATION, 1135119166Smtm &(handle->default_transaction_mode), sizeof(ODBC_INTPTR_T), NULL); 1136119166Smtm handle->transaction_mode = handle->default_transaction_mode; 1137119166Smtm SQLGetInfo(hdbc, SQL_GETDATA_EXTENSIONS ,&(handle->dboptions), 1138119166Smtm sizeof(ODBC_INTPTR_T), NULL); 1139119166Smtm apr_pool_cleanup_register(pool, handle, odbc_close_cleanup, apr_pool_cleanup_null); 1140119166Smtm return handle; 1141119166Smtm } 1142119166Smtm else { 1143119166Smtm apr_dbd_t tmp_dbc; 1144119166Smtm 1145119166Smtm tmp_dbc.pool = pool; 1146119166Smtm tmp_dbc.dbname = NULL; 1147119166Smtm CHECK_ERROR(&tmp_dbc, err_step, rc, err_htype, err_h); 1148119166Smtm if (error) 1149119166Smtm *error = apr_pstrdup(pool, tmp_dbc.lastError); 1150119166Smtm if (hdbc) 1151119166Smtm SQLFreeHandle(SQL_HANDLE_DBC, hdbc); 1152119166Smtm return NULL; 1153119166Smtm } 1154119166Smtm} 1155119166Smtm 1156119166Smtm/** check_conn: check status of a database connection **/ 1157119166Smtmstatic apr_status_t odbc_check_conn(apr_pool_t *pool, apr_dbd_t *handle) 1158119166Smtm{ 1159119166Smtm SQLUINTEGER isDead; 1160119166Smtm SQLRETURN rc; 1161119166Smtm 1162119166Smtm rc = SQLGetConnectAttr(handle->dbc, SQL_ATTR_CONNECTION_DEAD, &isDead, 1163119166Smtm sizeof(SQLUINTEGER), NULL); 1164119166Smtm CHECK_ERROR(handle, "SQLGetConnectAttr (SQL_ATTR_CONNECTION_DEAD)", rc, 1165119166Smtm SQL_HANDLE_DBC, handle->dbc); 1166119166Smtm /* if driver cannot check connection, say so */ 1167119166Smtm if (rc != SQL_SUCCESS) 1168119166Smtm return APR_ENOTIMPL; 1169119166Smtm 1170119166Smtm return (isDead == SQL_CD_FALSE) ? APR_SUCCESS : APR_EGENERAL; 1171119166Smtm} 1172119166Smtm 1173119166Smtm/** set_dbname: select database name. May be a no-op if not supported. **/ 1174119166Smtmstatic int odbc_set_dbname(apr_pool_t*pool, apr_dbd_t *handle, 1175119166Smtm const char *name) 1176119166Smtm{ 1177119166Smtm if (apr_strnatcmp(name, handle->dbname)) { 1178119166Smtm return APR_EGENERAL; /* It's illegal to change dbname in ODBC */ 1179119166Smtm } 1180119166Smtm CHECK_ERROR(handle, "set_dbname (no-op)", SQL_SUCCESS, SQL_HANDLE_DBC, 1181119166Smtm handle->dbc); 1182119166Smtm return APR_SUCCESS; /* OK if it's the same name */ 1183119166Smtm} 1184119166Smtm 1185119166Smtm/** transaction: start a transaction. May be a no-op. **/ 1186119166Smtmstatic int odbc_start_transaction(apr_pool_t *pool, apr_dbd_t *handle, 1187119166Smtm apr_dbd_transaction_t **trans) 1188119166Smtm{ 1189119166Smtm SQLRETURN rc = SQL_SUCCESS; 1190119166Smtm 1191119166Smtm if (handle->transaction_mode) { 1192119166Smtm rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_TXN_ISOLATION, 1193119166Smtm (SQLPOINTER)handle->transaction_mode, 0); 1194119166Smtm CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_TXN_ISOLATION)", rc, 1195119166Smtm SQL_HANDLE_DBC, handle->dbc); 1196119166Smtm } 1197119166Smtm if (SQL_SUCCEEDED(rc)) { 1198119166Smtm /* turn off autocommit for transactions */ 1199119166Smtm rc = SQLSetConnectAttr(handle->dbc, SQL_ATTR_AUTOCOMMIT, 1200119166Smtm SQL_AUTOCOMMIT_OFF, 0); 1201119166Smtm CHECK_ERROR(handle, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", rc, 1202119166Smtm SQL_HANDLE_DBC, handle->dbc); 1203119166Smtm } 1204119166Smtm if (SQL_SUCCEEDED(rc)) { 1205119166Smtm *trans = apr_palloc(pool, sizeof(apr_dbd_transaction_t)); 1206119166Smtm (*trans)->dbc = handle->dbc; 1207119166Smtm (*trans)->apr_dbd = handle; 1208119166Smtm } 1209119166Smtm handle->can_commit = APR_DBD_TRANSACTION_COMMIT; 1210119166Smtm return APR_FROM_SQL_RESULT(rc); 1211119166Smtm} 1212119166Smtm 1213119166Smtm/** end_transaction: end a transaction **/ 1214119166Smtmstatic int odbc_end_transaction(apr_dbd_transaction_t *trans) 1215119166Smtm{ 1216119166Smtm SQLRETURN rc; 1217119166Smtm int action = (trans->apr_dbd->can_commit != APR_DBD_TRANSACTION_ROLLBACK) 1218119166Smtm ? SQL_COMMIT : SQL_ROLLBACK; 1219119166Smtm 1220119166Smtm rc = SQLEndTran(SQL_HANDLE_DBC, trans->dbc, action); 1221119166Smtm CHECK_ERROR(trans->apr_dbd, "SQLEndTran", rc, SQL_HANDLE_DBC, trans->dbc); 1222119166Smtm if (SQL_SUCCEEDED(rc)) { 1223119166Smtm rc = SQLSetConnectAttr(trans->dbc, SQL_ATTR_AUTOCOMMIT, 1224119166Smtm (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0); 1225119166Smtm CHECK_ERROR(trans->apr_dbd, "SQLSetConnectAttr (SQL_ATTR_AUTOCOMMIT)", 1226119166Smtm rc, SQL_HANDLE_DBC, trans->dbc); 1227119166Smtm } 1228119166Smtm trans->apr_dbd->can_commit = APR_DBD_TRANSACTION_IGNORE_ERRORS; 1229119166Smtm return APR_FROM_SQL_RESULT(rc); 1230119166Smtm} 1231119166Smtm 1232119166Smtm/** query: execute an SQL statement which doesn't return a result set **/ 1233119166Smtmstatic int odbc_query(apr_dbd_t *handle, int *nrows, const char *statement) 1234119166Smtm{ 1235119166Smtm SQLRETURN rc; 1236119166Smtm SQLHANDLE hstmt = NULL; 1237119166Smtm size_t len = strlen(statement); 1238119166Smtm 1239119166Smtm if (odbc_check_rollback(handle)) 1240124797Scperciva return APR_EGENERAL; 1241119166Smtm 1242119166Smtm rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt); 1243119166Smtm CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC, 1244119166Smtm handle->dbc); 1245119166Smtm if (!SQL_SUCCEEDED(rc)) 1246119166Smtm return APR_FROM_SQL_RESULT(rc); 1247119166Smtm 1248119166Smtm rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len); 1249119166Smtm CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt); 1250119166Smtm 1251119166Smtm if (SQL_SUCCEEDED(rc)) { 1252119166Smtm SQLLEN rowcount; 1253119166Smtm 1254119166Smtm rc = SQLRowCount(hstmt, &rowcount); 1255119166Smtm *nrows = (int)rowcount; 1256119166Smtm CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, hstmt); 1257119166Smtm } 1258119166Smtm 1259119166Smtm SQLFreeHandle(SQL_HANDLE_STMT, hstmt); 1260119166Smtm return APR_FROM_SQL_RESULT(rc); 1261119166Smtm} 1262119166Smtm 1263119166Smtm/** select: execute an SQL statement which returns a result set **/ 1264119166Smtmstatic int odbc_select(apr_pool_t *pool, apr_dbd_t *handle, 1265119166Smtm apr_dbd_results_t **res, const char *statement, 1266 int random) 1267{ 1268 SQLRETURN rc; 1269 SQLHANDLE hstmt; 1270 apr_dbd_prepared_t *stmt; 1271 size_t len = strlen(statement); 1272 1273 if (odbc_check_rollback(handle)) 1274 return APR_EGENERAL; 1275 1276 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &hstmt); 1277 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, SQL_HANDLE_DBC, 1278 handle->dbc); 1279 if (!SQL_SUCCEEDED(rc)) 1280 return APR_FROM_SQL_RESULT(rc); 1281 /* Prepare an apr_dbd_prepared_t for pool cleanup, even though this 1282 * is not a prepared statement. We want the same cleanup mechanism. 1283 */ 1284 stmt = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t)); 1285 stmt->apr_dbd = handle; 1286 stmt->dbc = handle->dbc; 1287 stmt->stmt = hstmt; 1288 apr_pool_cleanup_register(pool, stmt, odbc_close_pstmt, apr_pool_cleanup_null); 1289 if (random) { 1290 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE, 1291 (SQLPOINTER)SQL_SCROLLABLE, 0); 1292 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", rc, 1293 SQL_HANDLE_STMT, hstmt); 1294 } 1295 if (SQL_SUCCEEDED(rc)) { 1296 rc = SQLExecDirect(hstmt, (SQLCHAR *)statement, (SQLINTEGER)len); 1297 CHECK_ERROR(handle, "SQLExecDirect", rc, SQL_HANDLE_STMT, hstmt); 1298 } 1299 if (SQL_SUCCEEDED(rc)) { 1300 rc = odbc_create_results(handle, hstmt, pool, random, res); 1301 apr_pool_cleanup_register(pool, *res, 1302 odbc_close_results, apr_pool_cleanup_null); 1303 } 1304 return APR_FROM_SQL_RESULT(rc); 1305} 1306 1307/** num_cols: get the number of columns in a results set **/ 1308static int odbc_num_cols(apr_dbd_results_t *res) 1309{ 1310 return res->ncols; 1311} 1312 1313/** num_tuples: get the number of rows in a results set **/ 1314static int odbc_num_tuples(apr_dbd_results_t *res) 1315{ 1316 SQLRETURN rc; 1317 SQLLEN nrows; 1318 1319 rc = SQLRowCount(res->stmt, &nrows); 1320 CHECK_ERROR(res->apr_dbd, "SQLRowCount", rc, SQL_HANDLE_STMT, res->stmt); 1321 return SQL_SUCCEEDED(rc) ? (int)nrows : -1; 1322} 1323 1324/** get_row: get a row from a result set **/ 1325static int odbc_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 1326 apr_dbd_row_t **row, int rownum) 1327{ 1328 SQLRETURN rc; 1329 char *fetchtype; 1330 int c; 1331 1332 *row = apr_pcalloc(pool, sizeof(apr_dbd_row_t)); 1333 (*row)->stmt = res->stmt; 1334 (*row)->dbc = res->dbc; 1335 (*row)->res = res; 1336 (*row)->pool = res->pool; 1337 1338 /* mark all the columns as needing SQLGetData unless they are bound */ 1339 for (c = 0; c < res->ncols; c++) { 1340 if (res->colstate[c] != COL_BOUND) { 1341 res->colstate[c] = COL_AVAIL; 1342 } 1343 /* some drivers do not null-term zero-len CHAR data */ 1344 if (res->colptrs[c]) 1345 *(char *)res->colptrs[c] = 0; 1346 } 1347 1348 if (res->random && (rownum > 0)) { 1349 fetchtype = "SQLFetchScroll"; 1350 rc = SQLFetchScroll(res->stmt, SQL_FETCH_ABSOLUTE, rownum); 1351 } 1352 else { 1353 fetchtype = "SQLFetch"; 1354 rc = SQLFetch(res->stmt); 1355 } 1356 CHECK_ERROR(res->apr_dbd, fetchtype, rc, SQL_HANDLE_STMT, res->stmt); 1357 (*row)->stmt = res->stmt; 1358 if (!SQL_SUCCEEDED(rc) && !res->random) { 1359 /* early close on any error (usually SQL_NO_DATA) if fetching 1360 * sequentially to release resources ASAP 1361 */ 1362 odbc_close_results(res); 1363 return -1; 1364 } 1365 return SQL_SUCCEEDED(rc) ? 0 : -1; 1366} 1367 1368/** datum_get: get a binary entry from a row **/ 1369static apr_status_t odbc_datum_get(const apr_dbd_row_t *row, int col, 1370 apr_dbd_type_e dbdtype, void *data) 1371{ 1372 SQLSMALLINT sqltype; 1373 void *p; 1374 int len; 1375 1376 if (col >= row->res->ncols) 1377 return APR_EGENERAL; 1378 1379 if (dbdtype < 0 || dbdtype >= NUM_APR_DBD_TYPES) { 1380 data = NULL; /* invalid type */ 1381 return APR_EGENERAL; 1382 } 1383 1384 len = sqlSizes[dbdtype]; 1385 sqltype = sqlCtype[dbdtype]; 1386 1387 /* must not memcpy a brigade, sentinals are relative to orig loc */ 1388 if (IS_LOB(sqltype)) 1389 return odbc_create_bucket(row, col, sqltype, data); 1390 1391 p = odbc_get(row, col, sqltype); 1392 if (p == (void *)-1) 1393 return APR_EGENERAL; 1394 1395 if (p == NULL) 1396 return APR_ENOENT; /* SQL NULL value */ 1397 1398 if (len < 0) 1399 *(char**)data = (char *)p; 1400 else 1401 memcpy(data, p, len); 1402 1403 return APR_SUCCESS; 1404 1405} 1406 1407/** get_entry: get an entry from a row (string data) **/ 1408static const char *odbc_get_entry(const apr_dbd_row_t *row, int col) 1409{ 1410 void *p; 1411 1412 if (col >= row->res->ncols) 1413 return NULL; 1414 1415 p = odbc_get(row, col, SQL_C_CHAR); 1416 1417 /* NULL or invalid (-1) */ 1418 if (p == NULL || p == (void *)-1) 1419 return p; 1420 else 1421 return apr_pstrdup(row->pool, p); 1422} 1423 1424/** error: get current error message (if any) **/ 1425static const char *odbc_error(apr_dbd_t *handle, int errnum) 1426{ 1427 return (handle) ? handle->lastError : "[dbd_odbc]No error message available"; 1428} 1429 1430/** escape: escape a string so it is safe for use in query/select **/ 1431static const char *odbc_escape(apr_pool_t *pool, const char *s, 1432 apr_dbd_t *handle) 1433{ 1434 char *newstr, *src, *dst, *sq; 1435 int qcount; 1436 1437 /* return the original if there are no single-quotes */ 1438 if (!(sq = strchr(s, '\''))) 1439 return (char *)s; 1440 /* count the single-quotes and allocate a new buffer */ 1441 for (qcount = 1; (sq = strchr(sq + 1, '\'')); ) 1442 qcount++; 1443 newstr = apr_palloc(pool, strlen(s) + qcount + 1); 1444 1445 /* move chars, doubling all single-quotes */ 1446 src = (char *)s; 1447 for (dst = newstr; *src; src++) { 1448 if ((*dst++ = *src) == '\'') 1449 *dst++ = '\''; 1450 } 1451 *dst = 0; 1452 return newstr; 1453} 1454 1455/** prepare: prepare a statement **/ 1456static int odbc_prepare(apr_pool_t *pool, apr_dbd_t *handle, 1457 const char *query, const char *label, int nargs, 1458 int nvals, apr_dbd_type_e *types, 1459 apr_dbd_prepared_t **statement) 1460{ 1461 SQLRETURN rc; 1462 size_t len = strlen(query); 1463 1464 if (odbc_check_rollback(handle)) 1465 return APR_EGENERAL; 1466 1467 *statement = apr_pcalloc(pool, sizeof(apr_dbd_prepared_t)); 1468 (*statement)->dbc = handle->dbc; 1469 (*statement)->apr_dbd = handle; 1470 (*statement)->nargs = nargs; 1471 (*statement)->nvals = nvals; 1472 (*statement)->types = 1473 apr_pmemdup(pool, types, nargs * sizeof(apr_dbd_type_e)); 1474 rc = SQLAllocHandle(SQL_HANDLE_STMT, handle->dbc, &((*statement)->stmt)); 1475 apr_pool_cleanup_register(pool, *statement, 1476 odbc_close_pstmt, apr_pool_cleanup_null); 1477 CHECK_ERROR(handle, "SQLAllocHandle (STMT)", rc, 1478 SQL_HANDLE_DBC, handle->dbc); 1479 rc = SQLPrepare((*statement)->stmt, (SQLCHAR *)query, (SQLINTEGER)len); 1480 CHECK_ERROR(handle, "SQLPrepare", rc, SQL_HANDLE_STMT, 1481 (*statement)->stmt); 1482 return APR_FROM_SQL_RESULT(rc); 1483} 1484 1485/** pquery: query using a prepared statement + args **/ 1486static int odbc_pquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1487 apr_dbd_prepared_t *statement, const char **args) 1488{ 1489 SQLRETURN rc = SQL_SUCCESS; 1490 int i, argp; 1491 1492 if (odbc_check_rollback(handle)) 1493 return APR_EGENERAL; 1494 1495 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) { 1496 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1497 &argp, (const void **)args, TEXTMODE); 1498 } 1499 if (SQL_SUCCEEDED(rc)) { 1500 rc = SQLExecute(statement->stmt); 1501 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1502 statement->stmt); 1503 } 1504 if (SQL_SUCCEEDED(rc)) { 1505 SQLLEN rowcount; 1506 1507 rc = SQLRowCount(statement->stmt, &rowcount); 1508 *nrows = (int)rowcount; 1509 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, 1510 statement->stmt); 1511 } 1512 return APR_FROM_SQL_RESULT(rc); 1513} 1514 1515/** pvquery: query using a prepared statement + args **/ 1516static int odbc_pvquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1517 apr_dbd_prepared_t *statement, va_list args) 1518{ 1519 const char **values; 1520 int i; 1521 1522 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1523 for (i = 0; i < statement->nvals; i++) 1524 values[i] = va_arg(args, const char *); 1525 return odbc_pquery(pool, handle, nrows, statement, values); 1526} 1527 1528/** pselect: select using a prepared statement + args **/ 1529static int odbc_pselect(apr_pool_t *pool, apr_dbd_t *handle, 1530 apr_dbd_results_t **res, apr_dbd_prepared_t *statement, 1531 int random, const char **args) 1532{ 1533 SQLRETURN rc = SQL_SUCCESS; 1534 int i, argp; 1535 1536 if (odbc_check_rollback(handle)) 1537 return APR_EGENERAL; 1538 1539 if (random) { 1540 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE, 1541 (SQLPOINTER)SQL_SCROLLABLE, 0); 1542 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", 1543 rc, SQL_HANDLE_STMT, statement->stmt); 1544 } 1545 if (SQL_SUCCEEDED(rc)) { 1546 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) { 1547 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1548 &argp, (const void **)args, TEXTMODE); 1549 } 1550 } 1551 if (SQL_SUCCEEDED(rc)) { 1552 rc = SQLExecute(statement->stmt); 1553 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1554 statement->stmt); 1555 } 1556 if (SQL_SUCCEEDED(rc)) { 1557 rc = odbc_create_results(handle, statement->stmt, pool, random, res); 1558 apr_pool_cleanup_register(pool, *res, 1559 odbc_close_results, apr_pool_cleanup_null); 1560 } 1561 return APR_FROM_SQL_RESULT(rc); 1562} 1563 1564/** pvselect: select using a prepared statement + args **/ 1565static int odbc_pvselect(apr_pool_t *pool, apr_dbd_t *handle, 1566 apr_dbd_results_t **res, 1567 apr_dbd_prepared_t *statement, int random, 1568 va_list args) 1569{ 1570 const char **values; 1571 int i; 1572 1573 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1574 for (i = 0; i < statement->nvals; i++) 1575 values[i] = va_arg(args, const char *); 1576 return odbc_pselect(pool, handle, res, statement, random, values); 1577} 1578 1579/** get_name: get a column title from a result set **/ 1580static const char *odbc_get_name(const apr_dbd_results_t *res, int col) 1581{ 1582 SQLRETURN rc; 1583 char buffer[MAX_COLUMN_NAME]; 1584 SQLSMALLINT colnamelength, coltype, coldecimal, colnullable; 1585 SQLULEN colsize; 1586 1587 if (col >= res->ncols) 1588 return NULL; /* bogus column number */ 1589 if (res->colnames[col] != NULL) 1590 return res->colnames[col]; /* we already retrieved it */ 1591 rc = SQLDescribeCol(res->stmt, col + 1, 1592 (SQLCHAR *)buffer, sizeof(buffer), &colnamelength, 1593 &coltype, &colsize, &coldecimal, &colnullable); 1594 CHECK_ERROR(res->apr_dbd, "SQLDescribeCol", rc, 1595 SQL_HANDLE_STMT, res->stmt); 1596 res->colnames[col] = apr_pstrdup(res->pool, buffer); 1597 return res->colnames[col]; 1598} 1599 1600/** transaction_mode_get: get the mode of transaction **/ 1601static int odbc_transaction_mode_get(apr_dbd_transaction_t *trans) 1602{ 1603 return (int)trans->apr_dbd->can_commit; 1604} 1605 1606/** transaction_mode_set: set the mode of transaction **/ 1607static int odbc_transaction_mode_set(apr_dbd_transaction_t *trans, int mode) 1608{ 1609 int legal = ( APR_DBD_TRANSACTION_IGNORE_ERRORS 1610 | APR_DBD_TRANSACTION_COMMIT 1611 | APR_DBD_TRANSACTION_ROLLBACK); 1612 1613 if ((mode & legal) != mode) 1614 return APR_EGENERAL; 1615 1616 trans->apr_dbd->can_commit = mode; 1617 return APR_SUCCESS; 1618} 1619 1620/** pbquery: query using a prepared statement + binary args **/ 1621static int odbc_pbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1622 apr_dbd_prepared_t *statement, const void **args) 1623{ 1624 SQLRETURN rc = SQL_SUCCESS; 1625 int i, argp; 1626 1627 if (odbc_check_rollback(handle)) 1628 return APR_EGENERAL; 1629 1630 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) 1631 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1632 &argp, args, BINARYMODE); 1633 1634 if (SQL_SUCCEEDED(rc)) { 1635 rc = SQLExecute(statement->stmt); 1636 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1637 statement->stmt); 1638 } 1639 if (SQL_SUCCEEDED(rc)) { 1640 SQLLEN rowcount; 1641 1642 rc = SQLRowCount(statement->stmt, &rowcount); 1643 *nrows = (int)rowcount; 1644 CHECK_ERROR(handle, "SQLRowCount", rc, SQL_HANDLE_STMT, 1645 statement->stmt); 1646 } 1647 return APR_FROM_SQL_RESULT(rc); 1648} 1649 1650/** pbselect: select using a prepared statement + binary args **/ 1651static int odbc_pbselect(apr_pool_t *pool, apr_dbd_t *handle, 1652 apr_dbd_results_t **res, 1653 apr_dbd_prepared_t *statement, 1654 int random, const void **args) 1655{ 1656 SQLRETURN rc = SQL_SUCCESS; 1657 int i, argp; 1658 1659 if (odbc_check_rollback(handle)) 1660 return APR_EGENERAL; 1661 1662 if (random) { 1663 rc = SQLSetStmtAttr(statement->stmt, SQL_ATTR_CURSOR_SCROLLABLE, 1664 (SQLPOINTER)SQL_SCROLLABLE, 0); 1665 CHECK_ERROR(handle, "SQLSetStmtAttr (SQL_ATTR_CURSOR_SCROLLABLE)", 1666 rc, SQL_HANDLE_STMT, statement->stmt); 1667 } 1668 if (SQL_SUCCEEDED(rc)) { 1669 for (i = argp = 0; i < statement->nargs && SQL_SUCCEEDED(rc); i++) { 1670 rc = odbc_bind_param(pool, statement, i + 1, statement->types[i], 1671 &argp, args, BINARYMODE); 1672 } 1673 } 1674 if (SQL_SUCCEEDED(rc)) { 1675 rc = SQLExecute(statement->stmt); 1676 CHECK_ERROR(handle, "SQLExecute", rc, SQL_HANDLE_STMT, 1677 statement->stmt); 1678 } 1679 if (SQL_SUCCEEDED(rc)) { 1680 rc = odbc_create_results(handle, statement->stmt, pool, random, res); 1681 apr_pool_cleanup_register(pool, *res, 1682 odbc_close_results, apr_pool_cleanup_null); 1683 } 1684 1685 return APR_FROM_SQL_RESULT(rc); 1686} 1687 1688/** pvbquery: query using a prepared statement + binary args **/ 1689static int odbc_pvbquery(apr_pool_t *pool, apr_dbd_t *handle, int *nrows, 1690 apr_dbd_prepared_t *statement, va_list args) 1691{ 1692 const char **values; 1693 int i; 1694 1695 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1696 for (i = 0; i < statement->nvals; i++) 1697 values[i] = va_arg(args, const char *); 1698 return odbc_pbquery(pool, handle, nrows, statement, (const void **)values); 1699} 1700 1701/** pvbselect: select using a prepared statement + binary args **/ 1702static int odbc_pvbselect(apr_pool_t *pool, apr_dbd_t *handle, 1703 apr_dbd_results_t **res, 1704 apr_dbd_prepared_t *statement, 1705 int random, va_list args) 1706{ 1707 const char **values; 1708 int i; 1709 1710 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1711 for (i = 0; i < statement->nvals; i++) 1712 values[i] = va_arg(args, const char *); 1713 return odbc_pbselect(pool, handle, res, statement, random, (const void **)values); 1714} 1715 1716APU_MODULE_DECLARE_DATA const apr_dbd_driver_t ODBC_DRIVER_ENTRY = { 1717 ODBC_DRIVER_STRING, 1718 odbc_init, 1719 odbc_native_handle, 1720 odbc_open, 1721 odbc_check_conn, 1722 odbc_close, 1723 odbc_set_dbname, 1724 odbc_start_transaction, 1725 odbc_end_transaction, 1726 odbc_query, 1727 odbc_select, 1728 odbc_num_cols, 1729 odbc_num_tuples, 1730 odbc_get_row, 1731 odbc_get_entry, 1732 odbc_error, 1733 odbc_escape, 1734 odbc_prepare, 1735 odbc_pvquery, 1736 odbc_pvselect, 1737 odbc_pquery, 1738 odbc_pselect, 1739 odbc_get_name, 1740 odbc_transaction_mode_get, 1741 odbc_transaction_mode_set, 1742 "?", 1743 odbc_pvbquery, 1744 odbc_pvbselect, 1745 odbc_pbquery, 1746 odbc_pbselect, 1747 odbc_datum_get 1748}; 1749 1750#endif 1751