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 "soci/connection-parameters.h" 0012 0013 #include "soci-autostatement.h" 0014 0015 #include <cstdio> 0016 0017 #ifdef _MSC_VER 0018 #pragma warning(disable:4355) 0019 #endif 0020 0021 using namespace soci; 0022 using namespace soci::details; 0023 0024 const char* soci::db2_option_driver_complete = "db2.driver_complete"; 0025 0026 const std::string db2_soci_error::sqlState(std::string const & msg,const SQLSMALLINT htype,const SQLHANDLE hndl) { 0027 std::ostringstream ss(msg, std::ostringstream::app); 0028 0029 0030 SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; 0031 SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; 0032 SQLINTEGER sqlcode; 0033 SQLSMALLINT length; 0034 0035 if ( SQLGetDiagRec(htype, 0036 hndl, 0037 1, 0038 sqlstate, 0039 &sqlcode, 0040 message, 0041 SQL_MAX_MESSAGE_LENGTH + 1, 0042 &length) == SQL_SUCCESS ) { 0043 ss<<" SQLMESSAGE: "; 0044 ss<<message; 0045 } 0046 return ss.str(); 0047 } 0048 0049 void db2_session_backend::parseKeyVal(std::string const & keyVal) { 0050 size_t delimiter=keyVal.find_first_of("="); 0051 std::string key=keyVal.substr(0,delimiter); 0052 std::string value=keyVal.substr(delimiter+1,keyVal.length()); 0053 0054 this->autocommit=true; //Default value 0055 if (!key.compare("autocommit")) { 0056 if (!value.compare("off")) { 0057 this->autocommit=false; 0058 } 0059 } 0060 } 0061 0062 /* DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;AutoCommit=off */ 0063 /* DATABASE=SAMPLE;hostname=server.com;UID=db2inst1;PWD=db2inst1;ServiceName=50000;Protocol=TCPIP; */ 0064 void db2_session_backend::parseConnectString(std::string const & connectString) { 0065 std::string processingString(connectString); 0066 size_t delimiter=processingString.find_first_of(";"); 0067 while(delimiter!=std::string::npos) { 0068 std::string keyVal=processingString.substr(0,delimiter); 0069 parseKeyVal(keyVal); 0070 processingString=processingString.erase(0,delimiter+1); 0071 delimiter=processingString.find_first_of(";"); 0072 } 0073 if (!processingString.empty()) { 0074 parseKeyVal(processingString); 0075 } 0076 } 0077 0078 db2_session_backend::db2_session_backend( 0079 connection_parameters const & parameters) : 0080 in_transaction(false) 0081 { 0082 std::string const& connectString = parameters.get_connect_string(); 0083 parseConnectString(connectString); 0084 0085 SQLRETURN cliRC = SQL_ERROR; 0086 0087 /* Prepare handles */ 0088 cliRC = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&hEnv); 0089 if (cliRC != SQL_SUCCESS) { 0090 throw db2_soci_error("Error while allocating the environment handle",cliRC); 0091 } 0092 0093 cliRC = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc); 0094 if (cliRC != SQL_SUCCESS) { 0095 std::string msg=db2_soci_error::sqlState("Error while allocating the connection handle",SQL_HANDLE_ENV,hEnv); 0096 SQLFreeHandle(SQL_HANDLE_ENV,hEnv); 0097 throw db2_soci_error(msg,cliRC); 0098 } 0099 0100 /* Set autocommit */ 0101 if(this->autocommit) { 0102 cliRC = SQLSetConnectAttr(hDbc,SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_NTS); 0103 } else { 0104 cliRC = SQLSetConnectAttr(hDbc,SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_NTS); 0105 } 0106 if (cliRC != SQL_SUCCESS) { 0107 std::string msg=db2_soci_error::sqlState("Error while setting autocommit attribute",SQL_HANDLE_DBC,hDbc); 0108 SQLFreeHandle(SQL_HANDLE_DBC,hDbc); 0109 SQLFreeHandle(SQL_HANDLE_ENV,hEnv); 0110 throw db2_soci_error(msg,cliRC); 0111 } 0112 0113 /* Connect to database */ 0114 // NOTE: SQLDriverConnect preparation steps below copied from ODBC backend. 0115 SQLCHAR outConnString[1024]; 0116 SQLSMALLINT strLength; 0117 0118 // Prompt the user for any missing information (typically UID/PWD) in the 0119 // connection string by default but allow overriding this using "prompt" 0120 // option. 0121 SQLHWND hwnd_for_prompt = NULL; 0122 unsigned completion = SQL_DRIVER_COMPLETE; 0123 std::string completionString; 0124 if (parameters.get_option(db2_option_driver_complete, completionString)) 0125 { 0126 // The value of the option is supposed to be just the integer value of 0127 // one of SQL_DRIVER_XXX constants but don't check for the exact value in 0128 // case more of them are added in the future, the ODBC driver will return 0129 // an error if we pass it an invalid value anyhow. 0130 if (std::sscanf(completionString.c_str(), "%u", &completion) != 1) 0131 { 0132 throw soci_error("Invalid non-numeric driver completion option value \"" + 0133 completionString + "\"."); 0134 } 0135 } 0136 0137 #ifdef _WIN32 0138 if (completion != SQL_DRIVER_NOPROMPT) 0139 hwnd_for_prompt = ::GetDesktopWindow(); 0140 #endif // _WIN32 0141 0142 cliRC = SQLDriverConnect(hDbc, hwnd_for_prompt, 0143 reinterpret_cast<SQLCHAR*>(const_cast<char*>(connectString.c_str())), 0144 (SQLSMALLINT)connectString.size(), 0145 outConnString, 1024, &strLength, 0146 static_cast<SQLUSMALLINT>(completion)); 0147 0148 if (cliRC != SQL_SUCCESS) { 0149 std::string msg=db2_soci_error::sqlState("Error connecting to database",SQL_HANDLE_DBC,hDbc); 0150 SQLFreeHandle(SQL_HANDLE_DBC,hDbc); 0151 SQLFreeHandle(SQL_HANDLE_ENV,hEnv); 0152 throw db2_soci_error(msg,cliRC); 0153 } 0154 0155 connection_string_.assign((const char*)outConnString, strLength); 0156 } 0157 0158 db2_session_backend::~db2_session_backend() 0159 { 0160 clean_up(); 0161 } 0162 0163 bool db2_session_backend::is_connected() 0164 { 0165 details::auto_statement<db2_statement_backend> st(*this); 0166 0167 // Force preparing the statement immediately (documentation states that 0168 // "Deferred prepare is on by default"). 0169 SQLSetStmtAttr(st.hStmt, SQL_ATTR_DEFERRED_PREPARE, SQL_DEFERRED_PREPARE_OFF, 0); 0170 0171 st.prepare("values 1", st_one_time_query); 0172 0173 return true; 0174 } 0175 0176 void db2_session_backend::begin() 0177 { 0178 // In DB2, transations begin implicitly; however, autocommit must be disabled for the duration of the transaction 0179 if(autocommit) 0180 { 0181 SQLRETURN cliRC = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_NTS); 0182 if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) 0183 { 0184 std::string msg=db2_soci_error::sqlState("Clearing the autocommit attribute failed", SQL_HANDLE_DBC, hDbc); 0185 SQLFreeHandle(SQL_HANDLE_DBC,hDbc); 0186 SQLFreeHandle(SQL_HANDLE_ENV,hEnv); 0187 throw db2_soci_error(msg,cliRC); 0188 } 0189 } 0190 0191 in_transaction = true; 0192 } 0193 0194 void db2_session_backend::commit() 0195 { 0196 if (!autocommit || in_transaction) { 0197 in_transaction = false; 0198 SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_COMMIT); 0199 if(autocommit) 0200 { 0201 SQLRETURN cliRC2 = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_NTS); 0202 if ((cliRC == SQL_SUCCESS || cliRC == SQL_SUCCESS_WITH_INFO) && 0203 cliRC2 != SQL_SUCCESS && cliRC2 != SQL_SUCCESS_WITH_INFO) 0204 { 0205 std::string msg=db2_soci_error::sqlState("Setting the autocommit attribute failed", SQL_HANDLE_DBC, hDbc); 0206 SQLFreeHandle(SQL_HANDLE_DBC,hDbc); 0207 SQLFreeHandle(SQL_HANDLE_ENV,hEnv); 0208 throw db2_soci_error(msg,cliRC); 0209 } 0210 } 0211 if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) { 0212 throw db2_soci_error("Commit failed",cliRC); 0213 } 0214 } 0215 } 0216 0217 void db2_session_backend::rollback() 0218 { 0219 if (!autocommit || in_transaction) { 0220 in_transaction = false; 0221 SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_ROLLBACK); 0222 if(autocommit) 0223 { 0224 SQLRETURN cliRC2 = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_NTS); 0225 if ((cliRC == SQL_SUCCESS || cliRC == SQL_SUCCESS_WITH_INFO) && 0226 cliRC2 != SQL_SUCCESS && cliRC2 != SQL_SUCCESS_WITH_INFO) 0227 { 0228 std::string msg=db2_soci_error::sqlState("Setting the autocommit attribute failed", SQL_HANDLE_DBC, hDbc); 0229 SQLFreeHandle(SQL_HANDLE_DBC,hDbc); 0230 SQLFreeHandle(SQL_HANDLE_ENV,hEnv); 0231 throw db2_soci_error(msg,cliRC); 0232 } 0233 } 0234 if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) { 0235 throw db2_soci_error("Rollback failed",cliRC); 0236 } 0237 } 0238 } 0239 0240 void db2_session_backend::clean_up() 0241 { 0242 // if a transaction is in progress, it will automatically be rolled back upon when the connection is disconnected/freed 0243 in_transaction = false; 0244 0245 SQLDisconnect(hDbc); 0246 SQLFreeHandle(SQL_HANDLE_DBC,hDbc); 0247 SQLFreeHandle(SQL_HANDLE_ENV,hEnv); 0248 } 0249 0250 db2_statement_backend * db2_session_backend::make_statement_backend() 0251 { 0252 return new db2_statement_backend(*this); 0253 } 0254 0255 db2_rowid_backend * db2_session_backend::make_rowid_backend() 0256 { 0257 return new db2_rowid_backend(*this); 0258 } 0259 0260 db2_blob_backend * db2_session_backend::make_blob_backend() 0261 { 0262 return new db2_blob_backend(*this); 0263 }