00001 // 00002 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney 00003 // Distributed under the Boost Software License, Version 1.0. 00004 // (See accompanying file LICENSE_1_0.txt or copy at 00005 // http://www.boost.org/LICENSE_1_0.txt) 00006 // 00007 00008 #define SOCI_ODBC_SOURCE 00009 #include "soci-odbc.h" 00010 #include <cctype> 00011 #include <sstream> 00012 #include <cstring> 00013 00014 #ifdef _MSC_VER 00015 // disables the warning about converting int to void*. This is a 64 bit compatibility 00016 // warning, but odbc requires the value to be converted on this line 00017 // SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)number, 0); 00018 #pragma warning(disable:4312) 00019 #endif 00020 00021 using namespace soci; 00022 using namespace soci::details; 00023 00024 00025 odbc_statement_backend::odbc_statement_backend(odbc_session_backend &session) 00026 : session_(session), hstmt_(0), numRowsFetched_(0), 00027 hasVectorUseElements_(false), boundByName_(false), boundByPos_(false) 00028 { 00029 } 00030 00031 void odbc_statement_backend::alloc() 00032 { 00033 SQLRETURN rc; 00034 00035 // Allocate environment handle 00036 rc = SQLAllocHandle(SQL_HANDLE_STMT, session_.hdbc_, &hstmt_); 00037 if (is_odbc_error(rc)) 00038 { 00039 throw odbc_soci_error(SQL_HANDLE_DBC, session_.hdbc_, 00040 "Allocating statement"); 00041 } 00042 } 00043 00044 void odbc_statement_backend::clean_up() 00045 { 00046 SQLFreeHandle(SQL_HANDLE_STMT, hstmt_); 00047 } 00048 00049 00050 void odbc_statement_backend::prepare(std::string const & query, 00051 statement_type /* eType */) 00052 { 00053 // rewrite the query by transforming all named parameters into 00054 // the ODBC numbers ones (:abc -> $1, etc.) 00055 00056 enum { eNormal, eInQuotes, eInName, eInAccessDate } state = eNormal; 00057 00058 std::string name; 00059 00060 for (std::string::const_iterator it = query.begin(), end = query.end(); 00061 it != end; ++it) 00062 { 00063 switch (state) 00064 { 00065 case eNormal: 00066 if (*it == '\'') 00067 { 00068 query_ += *it; 00069 state = eInQuotes; 00070 } 00071 else if (*it == '#') 00072 { 00073 query_ += *it; 00074 state = eInAccessDate; 00075 } 00076 else if (*it == ':') 00077 { 00078 state = eInName; 00079 } 00080 else // regular character, stay in the same state 00081 { 00082 query_ += *it; 00083 } 00084 break; 00085 case eInQuotes: 00086 if (*it == '\'') 00087 { 00088 query_ += *it; 00089 state = eNormal; 00090 } 00091 else // regular quoted character 00092 { 00093 query_ += *it; 00094 } 00095 break; 00096 case eInName: 00097 if (std::isalnum(*it) || *it == '_') 00098 { 00099 name += *it; 00100 } 00101 else // end of name 00102 { 00103 names_.push_back(name); 00104 name.clear(); 00105 std::ostringstream ss; 00106 ss << '?'; 00107 query_ += ss.str(); 00108 query_ += *it; 00109 state = eNormal; 00110 } 00111 break; 00112 case eInAccessDate: 00113 if (*it == '#') 00114 { 00115 query_ += *it; 00116 state = eNormal; 00117 } 00118 else // regular quoted character 00119 { 00120 query_ += *it; 00121 } 00122 break; 00123 } 00124 } 00125 00126 if (state == eInName) 00127 { 00128 names_.push_back(name); 00129 std::ostringstream ss; 00130 ss << '?'; 00131 query_ += ss.str(); 00132 } 00133 00134 SQLRETURN rc = SQLPrepare(hstmt_, (SQLCHAR*)query_.c_str(), (SQLINTEGER)query_.size()); 00135 if (is_odbc_error(rc)) 00136 { 00137 throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_, 00138 query_.c_str()); 00139 } 00140 } 00141 00142 statement_backend::exec_fetch_result 00143 odbc_statement_backend::execute(int number) 00144 { 00145 // made this static because MSVC debugger was reporting 00146 // that there was an attempt to use rows_processed after the stack 00147 // was destroyed. Some ODBC clean_up ? 00148 static SQLUSMALLINT rows_processed = 0; 00149 00150 if (hasVectorUseElements_) 00151 { 00152 SQLSetStmtAttr(hstmt_, SQL_ATTR_PARAMS_PROCESSED_PTR, &rows_processed, 0); 00153 } 00154 00155 // if we are called twice for the same statement we need to close the open 00156 // cursor or an "invalid cursor state" error will occur on execute 00157 SQLCloseCursor(hstmt_); 00158 00159 SQLRETURN rc = SQLExecute(hstmt_); 00160 if (is_odbc_error(rc)) 00161 { 00162 throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_, 00163 "Statement Execute"); 00164 } 00165 00166 SQLSMALLINT colCount; 00167 SQLNumResultCols(hstmt_, &colCount); 00168 00169 if (number > 0 && colCount > 0) 00170 { 00171 return fetch(number); 00172 } 00173 00174 return ef_success; 00175 } 00176 00177 statement_backend::exec_fetch_result 00178 odbc_statement_backend::fetch(int number) 00179 { 00180 numRowsFetched_ = 0; 00181 00182 SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0); 00183 SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)number, 0); 00184 SQLSetStmtAttr(hstmt_, SQL_ATTR_ROWS_FETCHED_PTR, &numRowsFetched_, 0); 00185 00186 SQLRETURN rc = SQLFetch(hstmt_); 00187 00188 if (SQL_NO_DATA == rc) 00189 { 00190 return ef_no_data; 00191 } 00192 00193 if (is_odbc_error(rc)) 00194 { 00195 throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_, 00196 "Statement Fetch"); 00197 } 00198 00199 return ef_success; 00200 } 00201 00202 int odbc_statement_backend::get_number_of_rows() 00203 { 00204 return numRowsFetched_; 00205 } 00206 00207 std::string odbc_statement_backend::rewrite_for_procedure_call( 00208 std::string const &query) 00209 { 00210 return query; 00211 } 00212 00213 int odbc_statement_backend::prepare_for_describe() 00214 { 00215 SQLSMALLINT numCols; 00216 SQLNumResultCols(hstmt_, &numCols); 00217 return numCols; 00218 } 00219 00220 void odbc_statement_backend::describe_column(int colNum, data_type & type, 00221 std::string & columnName) 00222 { 00223 SQLCHAR colNameBuffer[2048]; 00224 SQLSMALLINT colNameBufferOverflow; 00225 SQLSMALLINT dataType; 00226 SQLULEN colSize; 00227 SQLSMALLINT decDigits; 00228 SQLSMALLINT isNullable; 00229 00230 SQLRETURN rc = SQLDescribeCol(hstmt_, static_cast<SQLUSMALLINT>(colNum), 00231 colNameBuffer, 2048, 00232 &colNameBufferOverflow, &dataType, 00233 &colSize, &decDigits, &isNullable); 00234 00235 if (is_odbc_error(rc)) 00236 { 00237 throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_, 00238 "describe Column"); 00239 } 00240 00241 char const *name = reinterpret_cast<char const *>(colNameBuffer); 00242 columnName.assign(name, std::strlen(name)); 00243 00244 switch (dataType) 00245 { 00246 case SQL_TYPE_DATE: 00247 case SQL_TYPE_TIME: 00248 case SQL_TYPE_TIMESTAMP: 00249 type = dt_date; 00250 break; 00251 case SQL_DOUBLE: 00252 case SQL_DECIMAL: 00253 case SQL_REAL: 00254 case SQL_FLOAT: 00255 case SQL_NUMERIC: 00256 type = dt_double; 00257 break; 00258 case SQL_TINYINT: 00259 case SQL_SMALLINT: 00260 case SQL_INTEGER: 00261 case SQL_BIGINT: 00262 type = dt_integer; 00263 break; 00264 case SQL_CHAR: 00265 case SQL_VARCHAR: 00266 default: 00267 type = dt_string; 00268 break; 00269 } 00270 } 00271 00272 std::size_t odbc_statement_backend::column_size(int colNum) 00273 { 00274 SQLCHAR colNameBuffer[2048]; 00275 SQLSMALLINT colNameBufferOverflow; 00276 SQLSMALLINT dataType; 00277 SQLULEN colSize; 00278 SQLSMALLINT decDigits; 00279 SQLSMALLINT isNullable; 00280 00281 SQLRETURN rc = SQLDescribeCol(hstmt_, static_cast<SQLUSMALLINT>(colNum), 00282 colNameBuffer, 2048, 00283 &colNameBufferOverflow, &dataType, 00284 &colSize, &decDigits, &isNullable); 00285 00286 if (is_odbc_error(rc)) 00287 { 00288 throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_, 00289 "column size"); 00290 } 00291 00292 return colSize; 00293 } 00294 00295 odbc_standard_into_type_backend * odbc_statement_backend::make_into_type_backend() 00296 { 00297 return new odbc_standard_into_type_backend(*this); 00298 } 00299 00300 odbc_standard_use_type_backend * odbc_statement_backend::make_use_type_backend() 00301 { 00302 return new odbc_standard_use_type_backend(*this); 00303 } 00304 00305 odbc_vector_into_type_backend * 00306 odbc_statement_backend::make_vector_into_type_backend() 00307 { 00308 return new odbc_vector_into_type_backend(*this); 00309 } 00310 00311 odbc_vector_use_type_backend * odbc_statement_backend::make_vector_use_type_backend() 00312 { 00313 hasVectorUseElements_ = true; 00314 return new odbc_vector_use_type_backend(*this); 00315 }
Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1