File indexing completed on 2025-02-23 05:15:15

0001 //
0002 // Copyright (C) 2011-2013 Denis Chapligin
0003 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
0004 // Distributed under the Boost Software License, Version 1.0.
0005 // (See accompanying file LICENSE_1_0.txt or copy at
0006 // http://www.boost.org/LICENSE_1_0.txt)
0007 //
0008 
0009 #define SOCI_DB2_SOURCE
0010 #include "soci/db2/soci-db2.h"
0011 #include <cctype>
0012 
0013 using namespace soci;
0014 using namespace soci::details;
0015 
0016 db2_statement_backend::db2_statement_backend(db2_session_backend &session)
0017     : session_(session),hasVectorUseElements(false),use_binding_method_(details::db2::BOUND_BY_NONE)
0018 {
0019 }
0020 
0021 void db2_statement_backend::alloc()
0022 {
0023     SQLRETURN cliRC = SQL_SUCCESS;
0024 
0025     cliRC = SQLAllocHandle(SQL_HANDLE_STMT,session_.hDbc,&hStmt);
0026     if (cliRC != SQL_SUCCESS) {
0027         throw db2_soci_error("Error while allocation statement handle",cliRC);
0028     }
0029 }
0030 
0031 void db2_statement_backend::clean_up()
0032 {
0033     SQLRETURN cliRC = SQL_SUCCESS;
0034 
0035     cliRC=SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
0036     if (cliRC != SQL_SUCCESS) {
0037         throw db2_soci_error(db2_soci_error::sqlState("Statement handle clean-up error",SQL_HANDLE_STMT,hStmt),cliRC);
0038     }
0039 }
0040 
0041 void db2_statement_backend::prepare(std::string const &  query ,
0042     statement_type /* eType */)
0043 {
0044     // rewrite the query by transforming all named parameters into
0045     // the markers (:abc -> ?, etc.)
0046 
0047     enum { normal, in_quotes, in_name } state = normal;
0048 
0049     std::string name;
0050 
0051     for (std::string::const_iterator it = query.begin(), end = query.end();
0052          it != end; ++it)
0053     {
0054         switch (state)
0055         {
0056         case normal:
0057             if (*it == '\'')
0058             {
0059                 query_ += *it;
0060                 state = in_quotes;
0061             }
0062             else if (*it == ':')
0063             {
0064                 // Check whether this is a cast operator (e.g. 23::float)
0065                 // and treat it as a special case, not as a named binding
0066                 const std::string::const_iterator next_it = it + 1;
0067                 if ((next_it != end) && (*next_it == ':'))
0068                 {
0069                     query_ += "::";
0070                     ++it;
0071                 }
0072                 else
0073                 {
0074                     state = in_name;
0075                 }
0076             }
0077             else // regular character, stay in the same state
0078             {
0079                 query_ += *it;
0080             }
0081             break;
0082         case in_quotes:
0083             if (*it == '\'')
0084             {
0085                 query_ += *it;
0086                 state = normal;
0087             }
0088             else // regular quoted character
0089             {
0090                 query_ += *it;
0091             }
0092             break;
0093         case in_name:
0094             if (std::isalnum(*it) || *it == '_')
0095             {
0096                 name += *it;
0097             }
0098             else // end of name
0099             {
0100                 names_.push_back(name);
0101                 name.clear();
0102                 std::ostringstream ss;
0103                 ss << '?';
0104                 query_ += ss.str();
0105                 query_ += *it;
0106                 state = normal;
0107 
0108             }
0109             break;
0110         }
0111     }
0112 
0113     if (state == in_name)
0114     {
0115         names_.push_back(name);
0116         std::ostringstream ss;
0117         ss << '?';
0118         query_ += ss.str();
0119     }
0120 
0121     SQLRETURN cliRC = SQLPrepare(hStmt, const_cast<SQLCHAR *>((const SQLCHAR *) query_.c_str()), SQL_NTS);
0122     if (cliRC!=SQL_SUCCESS) {
0123         throw db2_soci_error("Error while preparing query",cliRC);
0124     }
0125 }
0126 
0127 statement_backend::exec_fetch_result
0128 db2_statement_backend::execute(int  number )
0129 {
0130     SQLUINTEGER rows_processed = 0;
0131     SQLRETURN cliRC;
0132 
0133     if (hasVectorUseElements)
0134     {
0135         SQLSetStmtAttr(hStmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &rows_processed, 0);
0136     }
0137 
0138     // if we are called twice for the same statement we need to close the open
0139     // cursor or an "invalid cursor state" error will occur on execute
0140     cliRC = SQLFreeStmt(hStmt,SQL_CLOSE);
0141     if (cliRC != SQL_SUCCESS)
0142     {
0143         throw db2_soci_error(db2_soci_error::sqlState("Statement execution error",SQL_HANDLE_STMT,hStmt),cliRC);
0144     }
0145 
0146     cliRC = SQLExecute(hStmt);
0147     if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO && cliRC != SQL_NO_DATA)
0148     {
0149         throw db2_soci_error(db2_soci_error::sqlState("Statement execution error",SQL_HANDLE_STMT,hStmt),cliRC);
0150     }
0151 
0152     SQLSMALLINT colCount;
0153     SQLNumResultCols(hStmt, &colCount);
0154 
0155     if (number > 0 && colCount > 0)
0156     {
0157         return fetch(number);
0158     }
0159 
0160     return ef_success;
0161 }
0162 
0163 statement_backend::exec_fetch_result
0164 db2_statement_backend::fetch(int  number )
0165 {
0166     numRowsFetched = 0;
0167 
0168     SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
0169     SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_ARRAY_SIZE, db2::int_as_ptr(number), 0);
0170     SQLSetStmtAttr(hStmt, SQL_ATTR_ROWS_FETCHED_PTR, &numRowsFetched, 0);
0171 
0172     SQLRETURN cliRC = SQLFetch(hStmt);
0173 
0174     if (SQL_NO_DATA == cliRC)
0175     {
0176         return ef_no_data;
0177     }
0178 
0179     if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO)
0180     {
0181         throw db2_soci_error(db2_soci_error::sqlState("Error while fetching data", SQL_HANDLE_STMT, hStmt), cliRC);
0182     }
0183 
0184     return ef_success;
0185 }
0186 
0187 long long db2_statement_backend::get_affected_rows()
0188 {
0189     SQLLEN rows;
0190 
0191     SQLRETURN cliRC = SQLRowCount(hStmt, &rows);
0192     if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO)
0193     {
0194         throw db2_soci_error(db2_soci_error::sqlState("Error while getting affected row count", SQL_HANDLE_STMT, hStmt), cliRC);
0195     }
0196     else if (rows == -1)
0197     {
0198         throw soci_error("Error getting affected row count: statement did not perform an update, insert, delete, or merge");
0199     }
0200 
0201     return rows;
0202 }
0203 
0204 int db2_statement_backend::get_number_of_rows()
0205 {
0206     return numRowsFetched;
0207 }
0208 
0209 std::string db2_statement_backend::get_parameter_name(int index) const
0210 {
0211     return names_.at(index);
0212 }
0213 
0214 std::string db2_statement_backend::rewrite_for_procedure_call(
0215     std::string const &query)
0216 {
0217     return query;
0218 }
0219 
0220 int db2_statement_backend::prepare_for_describe()
0221 {
0222     SQLSMALLINT numCols;
0223     SQLNumResultCols(hStmt, &numCols);
0224     return numCols;
0225 }
0226 
0227 void db2_statement_backend::describe_column(int  colNum,
0228     data_type &  type, std::string & columnName )
0229 {
0230 SQLCHAR colNameBuffer[2048];
0231     SQLSMALLINT colNameBufferOverflow;
0232     SQLSMALLINT dataType;
0233     SQLULEN colSize;
0234     SQLSMALLINT decDigits;
0235     SQLSMALLINT isNullable;
0236 
0237     SQLRETURN cliRC = SQLDescribeCol(hStmt, static_cast<SQLUSMALLINT>(colNum),
0238                                   colNameBuffer, 2048,
0239                                   &colNameBufferOverflow, &dataType,
0240                                   &colSize, &decDigits, &isNullable);
0241 
0242     if (cliRC != SQL_SUCCESS)
0243     {
0244         throw db2_soci_error(db2_soci_error::sqlState("Error while describing column",SQL_HANDLE_STMT,hStmt),cliRC);
0245     }
0246 
0247     char const *name = reinterpret_cast<char const *>(colNameBuffer);
0248     columnName.assign(name, std::strlen(name));
0249 
0250     switch (dataType)
0251     {
0252     case SQL_TYPE_DATE:
0253     case SQL_TYPE_TIME:
0254     case SQL_TYPE_TIMESTAMP:
0255         type = dt_date;
0256         break;
0257     case SQL_DOUBLE:
0258     case SQL_DECIMAL:
0259     case SQL_REAL:
0260     case SQL_FLOAT:
0261     case SQL_NUMERIC:
0262         type = dt_double;
0263         break;
0264     case SQL_TINYINT:
0265     case SQL_SMALLINT:
0266     case SQL_INTEGER:
0267         type = dt_integer;
0268         break;
0269     case SQL_BIGINT:
0270         type = dt_long_long;
0271         break;
0272     case SQL_CHAR:
0273     case SQL_VARCHAR:
0274     case SQL_LONGVARCHAR:
0275     default:
0276         type = dt_string;
0277         break;
0278     }
0279 }
0280 
0281 size_t db2_statement_backend::column_size(int col) {
0282     SQLCHAR colNameBuffer[2048];
0283     SQLSMALLINT colNameBufferOverflow;
0284     SQLSMALLINT dataType;
0285     SQLULEN colSize;
0286     SQLSMALLINT decDigits;
0287     SQLSMALLINT isNullable;
0288 
0289     SQLRETURN cliRC = SQLDescribeCol(hStmt, static_cast<SQLUSMALLINT>(col),
0290                                   colNameBuffer, 2048,
0291                                   &colNameBufferOverflow, &dataType,
0292                                   &colSize, &decDigits, &isNullable);
0293 
0294     if (cliRC != SQL_SUCCESS)
0295     {
0296         throw db2_soci_error(db2_soci_error::sqlState("Error while detecting column size",SQL_HANDLE_STMT,hStmt),cliRC);
0297     }
0298 
0299     return colSize;
0300 }
0301 
0302 db2_standard_into_type_backend * db2_statement_backend::make_into_type_backend()
0303 {
0304     return new db2_standard_into_type_backend(*this);
0305 }
0306 
0307 db2_standard_use_type_backend * db2_statement_backend::make_use_type_backend()
0308 {
0309     return new db2_standard_use_type_backend(*this);
0310 }
0311 
0312 db2_vector_into_type_backend *
0313 db2_statement_backend::make_vector_into_type_backend()
0314 {
0315     return new db2_vector_into_type_backend(*this);
0316 }
0317 
0318 db2_vector_use_type_backend * db2_statement_backend::make_vector_use_type_backend()
0319 {
0320     hasVectorUseElements = true;
0321     return new db2_vector_use_type_backend(*this);
0322 }