/* * PostgresqlConnection object defines the needed connection functions for the dbConnect PostgrSQL 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 "pgsqlConnection.h" #include "pgsqlQuery.h" // ----------------------------------------------------------------------------- // PRIVATE: // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // PostgresqlConnection::_pgsqlConnect // ----------------------------------------------------------------------------- void PostgresqlConnection::_pgsqlConnect( int index) { // Make sure the handle is valid and we are not already connected. if (index > _numHandles || index < 0) throw Error("_pgsqlConnect(): Invalid index to database handle."); if (_handles[index]->status != BaseHandle::DISCONNECTED) throw AlreadyConnected("_pgsqlConnect(): " "The database connection is already connected to the database."); // Connect to the PostgreSQL database. _handles[index]->__conn = PQsetdbLogin(host.c_str(), _pgsqlOptions.port.c_str(), NULL, NULL, databaseName.c_str(), username.c_str(), password.c_str()); // Check if an error occured while connecting to the database. if (PQstatus(_handles[index]->__conn) == CONNECTION_BAD) { string err = "_pgsqlConnect(): "; err += PQerrorMessage(_handles[index]->__conn); PQfinish(_handles[index]->__conn); _handles[index]->__conn = NULL; throw ErrorConnecting(err); } // Set the date time format to ISO standard format using a query. PGresult *r = PQexec(_handles[index]->__conn, "SET DATESTYLE TO 'ISO'"); if (!r || PQresultStatus(r) != PGRES_COMMAND_OK) { string err = "_pgsqlConnect(): Unable to set the iso date format on the connection using \"SET DATESTYLE TO 'ISO'\". "; if (r) { err += PQresultErrorMessage(r); PQclear(r); } PQfinish(_handles[index]->__conn); _handles[index]->__conn = NULL; throw ErrorConnecting(err); } PQclear(r); _handles[index]->status = BaseHandle::CONNECTED; _handles[index]->lastUsed = time(NULL); } // PostgresqlConnection::_pgsqlConnect // ----------------------------------------------------------------------------- // PostgresqlConnection::_pgsqlDisconnect // ----------------------------------------------------------------------------- void PostgresqlConnection::_pgsqlDisconnect( int index) { // Make sure the handle is valid and we are not already connected. if (index > _numHandles || index < 0) throw Error("_pgsqlDisconnect(): Invalid index to database handle."); if (_handles[index]->status == BaseHandle::DISCONNECTED || _handles[index]->status == BaseHandle::UNKNOWN) throw NotConnected("_pgsqlDisconnect(): " "The database handle does not have a valid connection to the database."); // Disconnect from the server. PQfinish(_handles[index]->__conn); _handles[index]->__conn = NULL; _handles[index]->status = BaseHandle::DISCONNECTED; _handles[index]->lastUsed = time(NULL); } // PostgresqlConnection::_pgsqlDisconnect // ----------------------------------------------------------------------------- // PostgresqlConnection::_pgsqlPing // ----------------------------------------------------------------------------- void PostgresqlConnection::_pgsqlPing( int index) { // Make sure the handle is valid. if (index > _numHandles || index < 0) throw Error("_pgsqlPing(): 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 to simulate a ping PGresult *r = PQexec(_handles[index]->__conn, "SELECT CURRENT_TIMESTAMP"); if (!r || PQresultStatus(r) != PGRES_COMMAND_OK) { // An error occured while pinging, so reconnect. if (r) PQclear(r); // Try and reconnect the lost connection using PQreset function. PQreset(_handles[index]->__conn); if (PQstatus(_handles[index]->__conn) == CONNECTION_BAD) { string err = "_pgsqlPing(): Unable to ping connection, "; err += PQerrorMessage(_handles[index]->__conn); PQfinish(_handles[index]->__conn); _handles[index]->__conn = NULL; throw ErrorPingingConnection(err); } } else { // Ping was OK PQclear(r); } } } // PostgresqlConnection::_pgsqlPing // ----------------------------------------------------------------------------- // PostgresqlConnection::_pgsqlQuery // ----------------------------------------------------------------------------- PGresult* PostgresqlConnection::_pgsqlQuery( int index, const string& sqlStatement) { // Make sure the handle is in the connected used state. if (_handles[index]->status != BaseHandle::CONNECTED_USED) throw ErrorQuerying("_pgsqlQuery(): The database connection is not valid. May have been terminated by the connection object?"); //Execute the query PGresult *r = PQexec(_handles[index]->__conn, sqlStatement.c_str()); if (!r || PQresultStatus(r) == PGRES_BAD_RESPONSE || PQresultStatus(r) == PGRES_FATAL_ERROR) { string err = "_pgsqlQuery(): "; err += "SQL statement: "; err += sqlStatement; if (r) { err += ", "; err += PQresultErrorMessage(r); PQclear(r); } throw ErrorQuerying(err); } // Set the last used _handles[index]->lastUsed = time(NULL); return r; } // PostgresqlConnection::_pgsqlQuery //------------------------------------------------------------------------------ // PostgresqlConnection::_freeCollection //------------------------------------------------------------------------------ void PostgresqlConnection::_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; } } // PostgresqlConnection::_freeCollection // ----------------------------------------------------------------------------- // PUBLIC: // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // PostgresqlConnection::PostgresqlConnection // ----------------------------------------------------------------------------- PostgresqlConnection::PostgresqlConnection( int argc, const char** argv) : BaseConnection(PGSQL_DRIVERNAME), _numHandles(0), _handles(NULL) { // Store any arguments in a easy to use structure. (Must be even number) // All argument names will be in lowercase. if (argc % 2 == 0) for (int i=0; istatus = 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; } _pgsqlDisconnect(i); } } isConnected = false; } // PostgresqlConnection::disconnect // ----------------------------------------------------------------------------- // PostgresqlConnection::requestQueryConnection // ----------------------------------------------------------------------------- void* PostgresqlConnection::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 PostgresqlQuery(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) { _pgsqlConnect(i); _handles[i]->queryObject = new PostgresqlQuery(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."); } // PostgresqlConnection::requestQueryConnection // ----------------------------------------------------------------------------- // PostgresqlConnection::releaseQueryConnection // ----------------------------------------------------------------------------- void PostgresqlConnection::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) { _pgsqlDisconnect(i); } break; } } } // PostgresqlConnection::releaseQueryConnection