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 }