/* * DB2Connection object defines the needed connection functions for the dbConnect IBM DB2 driver * Copyright (C) 2003 Johnathan Ingram, jingram@rogueware.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 US * */ #include "ibmDB2Connection.h" #include "ibmDB2Query.h" #include // ----------------------------------------------------------------------------- // PRIVATE: // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // DB2Connection::_db2Connect // ----------------------------------------------------------------------------- void DB2Connection::_db2Connect( int index) { // Make sure the handle is valid and we are not already connected. if (index > _numHandles || index < 0) throw Error("_db2Connect(): Invalid index to database handle."); if (_handles[index]->status != BaseHandle::DISCONNECTED) throw AlreadyConnected("_db2Connect(): " "The database connection is already connected to the database."); SQLRETURN cliRC = SQL_SUCCESS; // Allocate the connection handle cliRC = SQLAllocHandle(SQL_HANDLE_DBC, _henv, &_handles[index]->_hdbc); if (cliRC != SQL_SUCCESS) throw Error("_db2Connect(): " "Unable to allocate the connection handle."); // Connect to the IBM DB2 database. cliRC = SQLConnect(_handles[index]->_hdbc, (SQLCHAR*)databaseName.c_str(), SQL_NTS, (SQLCHAR*)username.c_str(), SQL_NTS, (SQLCHAR*)password.c_str(), SQL_NTS); // Check if an error occured while connecting to the database. if (cliRC != SQL_SUCCESS) { SQLCHAR sqlState[10]; SQLINTEGER sqlCode; SQLCHAR message[255]; SQLSMALLINT length; SQLGetDiagRec(SQL_HANDLE_DBC, _handles[index]->_hdbc, 1, sqlState, &sqlCode, message, 250, &length); string err = "_db2Connect(): "; err += (const char*)message; // Free the connection handle allocated SQLFreeHandle(SQL_HANDLE_DBC, _handles[index]->_hdbc); _handles[index]->_hdbc = SQL_NULL_HDBC; throw ErrorConnecting(err); } // Set the connection autocommit to off cliRC = SQLSetConnectAttr(_handles[index]->_hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_NTS); // Check if an error occured setting the autocommit to false if (cliRC != SQL_SUCCESS) { SQLCHAR sqlState[10]; SQLINTEGER sqlCode; SQLCHAR message[255]; SQLSMALLINT length; SQLGetDiagRec(SQL_HANDLE_DBC, _handles[index]->_hdbc, 1, sqlState, &sqlCode, message, 250, &length); string err = "_db2Connect(): Unable to set autocommit off, "; err += (const char*)message; // Disconnect from the server. SQLDisconnect(_handles[index]->_hdbc); // Free the connection handle allocated SQLFreeHandle(SQL_HANDLE_DBC, _handles[index]->_hdbc); _handles[index]->_hdbc = SQL_NULL_HDBC; throw ErrorConnecting(err); } _handles[index]->status = BaseHandle::CONNECTED; _handles[index]->lastUsed = time(NULL); } // DB2Connection::_db2Connect // ----------------------------------------------------------------------------- // DB2Connection::_db2Disconnect // ----------------------------------------------------------------------------- void DB2Connection::_db2Disconnect( int index) { // Make sure the handle is valid and we are not already connected. if (index > _numHandles || index < 0) throw Error("_db2Disconnect(): Invalid index to database handle."); if (_handles[index]->status == BaseHandle::DISCONNECTED || _handles[index]->status == BaseHandle::UNKNOWN) throw NotConnected("_db2Disconnect(): " "The database handle does not have a valid connection to the database."); // Disconnect from the server. SQLDisconnect(_handles[index]->_hdbc); // Free the connection handle allocated SQLFreeHandle(SQL_HANDLE_DBC, _handles[index]->_hdbc); _handles[index]->_hdbc = SQL_NULL_HDBC; _handles[index]->status = BaseHandle::DISCONNECTED; _handles[index]->lastUsed = time(NULL); } // DB2Connection::_db2Disconnect // ----------------------------------------------------------------------------- // DB2Connection::_db2Ping // ----------------------------------------------------------------------------- void DB2Connection::_db2Ping( int index) { // Make sure the handle is valid. if (index > _numHandles || index < 0) throw Error("_db2Ping(): Invalid index to database handle."); // Only ping the connection if the ping interval has expired since the // connection was last used. time_t currentTime = time(NULL); if (currentTime - _handles[index]->lastUsed >= pingInterval) { // Do a dummy query SQLRETURN cliRC = SQL_SUCCESS; SQLHANDLE hstmt; // Directly execute the statement after allocating the statement handle cliRC = SQLAllocHandle(SQL_HANDLE_STMT, _handles[index]->_hdbc, &hstmt); if (cliRC == SQL_SUCCESS) cliRC = SQLExecDirect(hstmt, (SQLCHAR*)"SELECT CURRENT DATE FROM SYSIBM.SYSDUMMY1", SQL_NTS); // Directly execute the statement if (cliRC != SQL_SUCCESS) { SQLFreeHandle(SQL_HANDLE_STMT, hstmt); // Connection is stale, need to reconnect // Synchronize the function as we dont want this connection taken from us SimpleThread_Synchronize sync(classMutex); //1. Disconnect ingnoring errors try { _db2Disconnect(index); } catch(...) { } // Ignore any errors from the disconnect. //2. Try get a new connection try { _db2Connect(index); // Force the connection to be in use again _handles[index]->status = BaseHandle::CONNECTED_USED; } catch(...) { string err = "_db2Ping(): Unable to establish new connection, "; throw ErrorPingingConnection(err); } } else { // Ping was OK SQLFreeHandle(SQL_HANDLE_STMT, hstmt); } } } // DB2Connection::_db2Ping //------------------------------------------------------------------------------ // DB2Connection::_freeCollection //------------------------------------------------------------------------------ void DB2Connection::_freeCollection( CollectionType type) { int i; switch (type) { case CONNECTION_HANDLES: if (_handles) { for (i=0; i<_numHandles; i++) { if (_handles[i]) { delete _handles[i]; _handles[i] = NULL; } } free(_handles); _handles = NULL; _numHandles = 0; } break; } } // DB2Connection::_freeCollection // ----------------------------------------------------------------------------- // PUBLIC: // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // DB2Connection::DB2Connection // ----------------------------------------------------------------------------- DB2Connection::DB2Connection( int argc, const char** argv) : BaseConnection(DB2_DRIVERNAME), _henv(SQL_NULL_HENV), _numHandles(0), _handles(NULL) { // Store any arguments in a easy to use structure. (Must be even number) // All argument names will be in lowercase. // -- No argument processing at present // Create the DB2 environment handle SQLRETURN cliRC = SQL_SUCCESS; cliRC = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &_henv); if (cliRC != SQL_SUCCESS) { printf("Unable to initialize DB2 environment. Is DB2INSTANCE=xxx exported correctly\n"); throw Error("Unable to allocate DB2 Environment handle"); } // Set attribute to enable application to run as ODBC 3.0 application cliRC = SQLSetEnvAttr(_henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); } // DB2Connection::DB2Connection // ----------------------------------------------------------------------------- // DB2Connection::~DB2Connection // ----------------------------------------------------------------------------- DB2Connection::~DB2Connection() { // Make sure we disconnect from the database. if (isConnected) { disconnect(120); // 2 Minute timeout. } // Free any handles. _freeCollection(CONNECTION_HANDLES); // Free the DB2 environment handle SQLFreeHandle(SQL_HANDLE_ENV, _henv); } // DB2Connection::~DB2Connection // ----------------------------------------------------------------------------- // DB2Connection::connect // ----------------------------------------------------------------------------- void DB2Connection::connect( const string &username, const string &password, const string &databaseName, const string &host, int maxConnections, int minConnections, const string &optParam1, const string &optParam2) { // Synchronize the function. SimpleThread_Synchronize sync(classMutex); // Make sure we are not already connected. if (isConnected) throw ErrorConnecting("connect(): Already connected to the database."); // Check and set the parameters. (Use the base class for this) BaseConnection::connect(username, password, databaseName, host, maxConnections, minConnections, optParam1, optParam2); // Free any handles used before. _freeCollection(CONNECTION_HANDLES); // Create the collection to hold the needed handle information. _numHandles = maxConnections; _handles = (DB2Handle**)malloc(_numHandles * sizeof(DB2Handle*)); for (int i=0; i<_numHandles; i++) { _handles[i] = new DB2Handle(); _handles[i]->status = BaseHandle::DISCONNECTED; } // Connect the required specified minimum connections. The rest will be on demand. for (int j=0; jstatus == BaseHandle::CONNECTED || _handles[i]->status == BaseHandle::CONNECTED_USED) { // If the handle is USED wait for the timeout. // Only wait once for the timeout for all connections. if (_handles[i]->status == BaseHandle::CONNECTED_USED && !doneTimeout) { SimpleThread::sleep(timeout * 1000); doneTimeout = true; } _db2Disconnect(i); } } isConnected = false; } // DB2Connection::disconnect // ----------------------------------------------------------------------------- // DB2Connection::requestQueryConnection // ----------------------------------------------------------------------------- void* DB2Connection::requestQueryConnection() { // This function must act as a fifo stack. The first thread in must get the first // available connection. // Synchronize the function. SimpleThread_Synchronize sync(classMutex); // Make sure we are connected. if (!isConnected) throw NotConnected("requestQueryConnection(): Not connected to the database."); // Flag that a request is occuring. isRequestQueryConnectionOccuring = true; // Loop until we have a valid connection or an error. // Built in timeout of 1 min per thread to retrieve a connection. int i; time_t now = time(NULL); while (time(NULL) <= now + 60) { // Try and obtain a connection for (i=0; i<_numHandles; i++) { // We have an available handle thats already connected. if (_handles[i]->status == BaseHandle::CONNECTED) { _handles[i]->queryObject = new DB2Query(this, i); _handles[i]->status = BaseHandle::CONNECTED_USED; isRequestQueryConnectionOccuring = false; return _handles[i]->queryObject; } // We have an available handle that needs to be connected. if (_handles[i]->status == BaseHandle::DISCONNECTED) { _db2Connect(i); _handles[i]->queryObject = new DB2Query(this, i); _handles[i]->status = BaseHandle::CONNECTED_USED; isRequestQueryConnectionOccuring = false; return _handles[i]->queryObject; } } // Sleep for a second to conserve resources SimpleThread::sleep(1000); } // If we got this far a timeout occured. isRequestQueryConnectionOccuring = false; throw QueryConnectionTimeout("requestQueryConnection(): A timout occured " "while trying to obtain a query connection."); } // DB2Connection::requestQueryConnection // ----------------------------------------------------------------------------- // DB2Connection::releaseQueryConnection // ----------------------------------------------------------------------------- void DB2Connection::releaseQueryConnection( void* queryObject) { // Don't synchronize as we may need to release a connection to allow // requestQueryConnection to get an available one // Find the handle that has the query connection instance int i; for (i=0; i<_numHandles; i++) { if (_handles[i]->queryObject == queryObject) { _handles[i]->queryObject = NULL; _handles[i]->status = BaseHandle::CONNECTED; // Check if we need to release the connection. // We release the connection if the connection is outside of the minimum // connections and there is no current request for a connection waiting. if (i >= minConnections && !isRequestQueryConnectionOccuring) { _db2Disconnect(i); } break; } } } // DB2Connection::releaseQueryConnection