From: Thomas Markwalder Date: Tue, 17 May 2016 15:49:07 +0000 (-0400) Subject: [4276] Initial impl of PgSqlConnection class X-Git-Tag: trac4106_update_base~12^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b1c67df1e8c257d8f8e2dba33b7a4ad890169230;p=thirdparty%2Fkea.git [4276] Initial impl of PgSqlConnection class Initial refactoring of Postgresql connection logic out of PgSqlLeaseMgr into new PgSqlConnection. --- diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 62b6e68224..06a7fe09f3 100755 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -133,6 +133,7 @@ endif libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h if HAVE_PGSQL +libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h endif libkea_dhcpsrv_la_SOURCES += pool.cc pool.h diff --git a/src/lib/dhcpsrv/pgsql_connection.cc b/src/lib/dhcpsrv/pgsql_connection.cc new file mode 100644 index 0000000000..1d565a2e6c --- /dev/null +++ b/src/lib/dhcpsrv/pgsql_connection.cc @@ -0,0 +1,181 @@ +// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +// PostgreSQL errors should be tested based on the SQL state code. Each state +// code is 5 decimal, ASCII, digits, the first two define the category of +// error, the last three are the specific error. PostgreSQL makes the state +// code as a char[5]. Macros for each code are defined in PostgreSQL's +// server/utils/errcodes.h, although they require a second macro, +// MAKE_SQLSTATE for completion. For example, duplicate key error as: +// +// #define ERRCODE_UNIQUE_VIOLATION MAKE_SQLSTATE('2','3','5','0','5') +// +// PostgreSQL deliberately omits the MAKE_SQLSTATE macro so callers can/must +// supply their own. We'll define it as an initlizer_list: +#define MAKE_SQLSTATE(ch1,ch2,ch3,ch4,ch5) {ch1,ch2,ch3,ch4,ch5} +// So we can use it like this: const char some_error[] = ERRCODE_xxxx; +#define PGSQL_STATECODE_LEN 5 +#include + +using namespace std; + +namespace isc { +namespace dhcp { + +const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION; + +PgSqlConnection::~PgSqlConnection() { + if (conn_) { + // Deallocate the prepared queries. + PgSqlResult r(PQexec(conn_, "DEALLOCATE all")); + if(PQresultStatus(r) != PGRES_COMMAND_OK) { + // Highly unlikely but we'll log it and go on. + LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_DEALLOC_ERROR) + .arg(PQerrorMessage(conn_)); + } + } +} + +void +PgSqlConnection::prepareStatement(const PgSqlTaggedStatement& statement) { + // Prepare all statements queries with all known fields datatype + PgSqlResult r(PQprepare(conn_, statement.name, statement.text, + statement.nbparams, statement.types)); + if(PQresultStatus(r) != PGRES_COMMAND_OK) { + isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: " + << statement.text << ", reason: " << PQerrorMessage(conn_)); + } +} + +void +PgSqlConnection::openDatabase() { + string dbconnparameters; + string shost = "localhost"; + try { + shost = getParameter("host"); + } catch(...) { + // No host. Fine, we'll use "localhost" + } + + dbconnparameters += "host = '" + shost + "'" ; + + string suser; + try { + suser = getParameter("user"); + dbconnparameters += " user = '" + suser + "'"; + } catch(...) { + // No user. Fine, we'll use NULL + } + + string spassword; + try { + spassword = getParameter("password"); + dbconnparameters += " password = '" + spassword + "'"; + } catch(...) { + // No password. Fine, we'll use NULL + } + + string sname; + try { + sname = getParameter("name"); + dbconnparameters += " dbname = '" + sname + "'"; + } catch(...) { + // No database name. Throw a "NoDatabaseName" exception + isc_throw(NoDatabaseName, "must specify a name for the database"); + } + + // Connect to Postgres, saving the low level connection pointer + // in the holder object + PGconn* new_conn = PQconnectdb(dbconnparameters.c_str()); + if (!new_conn) { + isc_throw(DbOpenError, "could not allocate connection object"); + } + + if (PQstatus(new_conn) != CONNECTION_OK) { + // If we have a connection object, we have to call finish + // to release it, but grab the error message first. + std::string error_message = PQerrorMessage(new_conn); + PQfinish(new_conn); + isc_throw(DbOpenError, error_message); + } + + // We have a valid connection, so let's save it to our holder + conn_.setConnection(new_conn); +} + +bool +PgSqlConnection::compareError(PGresult*& r, const char* error_state) { + const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE); + // PostgreSQL garuantees it will always be 5 characters long + return ((sqlstate != NULL) && + (memcmp(sqlstate, error_state, PGSQL_STATECODE_LEN) == 0)); +} + +void +PgSqlConnection::checkStatementError(PGresult*& r, + PgSqlTaggedStatement& statement) const { + int s = PQresultStatus(r); + if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) { + // We're testing the first two chars of SQLSTATE, as this is the + // error class. Note, there is a severity field, but it can be + // misleadingly returned as fatal. + const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE); + if ((sqlstate != NULL) && + ((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception + (memcmp(sqlstate, "53", 2) == 0) || // Insufficient resources + (memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded + (memcmp(sqlstate, "57", 2) == 0) || // Operator intervention + (memcmp(sqlstate, "58", 2) == 0))) { // System error + LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_FATAL_ERROR) + .arg(statement.name) + .arg(PQerrorMessage(conn_)) + .arg(sqlstate); + exit (-1); + } + + const char* error_message = PQerrorMessage(conn_); + isc_throw(DbOperationError, "Statement exec failed:" << " for: " + << statement.name << ", reason: " + << error_message); + } +} + +void +PgSqlConnection::commit() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_COMMIT); + PgSqlResult r(PQexec(conn_, "COMMIT")); + if (PQresultStatus(r) != PGRES_COMMAND_OK) { + const char* error_message = PQerrorMessage(conn_); + isc_throw(DbOperationError, "commit failed: " << error_message); + } +} + +void +PgSqlConnection::rollback() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_ROLLBACK); + PgSqlResult r(PQexec(conn_, "ROLLBACK")); + if (PQresultStatus(r) != PGRES_COMMAND_OK) { + const char* error_message = PQerrorMessage(conn_); + isc_throw(DbOperationError, "rollback failed: " << error_message); + } +} + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/dhcpsrv/pgsql_connection.h b/src/lib/dhcpsrv/pgsql_connection.h new file mode 100755 index 0000000000..00405ae3a7 --- /dev/null +++ b/src/lib/dhcpsrv/pgsql_connection.h @@ -0,0 +1,296 @@ +// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#ifndef PGSQL_CONNECTION_H +#define PGSQL_CONNECTION_H + +#include + +#include +#include + +#include + + +namespace isc { +namespace dhcp { + +// Maximum number of parameters that can be used a statement +// @todo This allows us to use an initializer list (since we don't +// require C++11). It's unlikely we'd go past in a single statement. +const size_t PGSQL_MAX_PARAMETERS_IN_QUERY = 32; + +/// @brief Defines a Postgresql SQL statement +/// +/// Each statement is associated with an index, which is used to reference the +/// associated prepared statement. +struct PgSqlTaggedStatement { + + /// Number of parameters for a given query + int nbparams; + + /// @brief OID types + /// + /// Specify parameter types. See /usr/include/postgresql/catalog/pg_type.h. + /// For some reason that header does not export those parameters. + /// Those OIDs must match both input and output parameters. + const Oid types[PGSQL_MAX_PARAMETERS_IN_QUERY]; + + /// Short name of the query. + const char* name; + + /// Text representation of the actual query. + const char* text; +}; + +/// @brief Constants for PostgreSQL data types +/// This are defined by PostreSQL in , but including +/// this file is extrordinarily convoluted, so we'll use these to fill-in. +const size_t OID_NONE = 0; // PostgreSQL infers proper type +const size_t OID_BOOL = 16; +const size_t OID_BYTEA = 17; +const size_t OID_INT8 = 20; // 8 byte int +const size_t OID_INT2 = 21; // 2 byte int +const size_t OID_TIMESTAMP = 1114; +const size_t OID_VARCHAR = 1043; + +//@} + +/// @brief RAII wrapper for Posgtresql Result sets +/// +/// When a Postgresql statement is executed, the results are returned +/// in pointer allocated structure, PGresult*. Data and status information +/// are accessed via calls to functions such as PQgetvalue() which require +/// the results pointer. In order to ensure this structure is freed, any +/// invocation of Psql function which returns a PGresult* (e.g. PQexec and + +/// class. Examples: +/// {{{ +/// PgSqlResult r(PQexec(conn_, "ROLLBACK")); +/// }}} +/// +/// This eliminates the need for an explicit release via, PQclear() and +/// guarantees that the resources are released even if the an exception is +/// thrown. + +class PgSqlResult { +public: + /// @brief Constructor + /// + /// Store the pointer to the result set to being fetched. + /// + PgSqlResult(PGresult *result) : result_(result) + {} + + /// @brief Destructor + /// + /// Frees the result set + ~PgSqlResult() { + if (result_) { + PQclear(result_); + } + } + + /// @brief Conversion Operator + /// + /// Allows the PgSqlResult object to be passed as the context argument to + /// PQxxxx functions. + operator PGresult*() const { + return (result_); + } + + /// @brief Boolean Operator + /// + /// Allows testing the PgSqlResult object for emptiness: "if (result)" + operator bool() const { + return (result_); + } + + +private: + PGresult* result_; ///< Result set to be freed +}; + + +/// @brief PgSql Handle Holder +/// +/// Small RAII object for safer initialization, will close the database +/// connection upon destruction. This means that if an exception is thrown +/// during database initialization, resources allocated to the database are +/// guaranteed to be freed. +/// +/// It makes no sense to copy an object of this class. After the copy, both +/// objects would contain pointers to the same PgSql context object. The +/// destruction of one would invalid the context in the remaining object. +/// For this reason, the class is declared noncopyable. +class PgSqlHolder : public boost::noncopyable { +public: + + /// @brief Constructor + /// + /// Initialize PgSql + /// + PgSqlHolder() : pgconn_(NULL) { + } + + /// @brief Destructor + /// + /// Frees up resources allocated by the connection. + ~PgSqlHolder() { + if (pgconn_ != NULL) { + PQfinish(pgconn_); + } + } + + void setConnection(PGconn* connection) { + if (pgconn_ != NULL) { + // Already set? Release the current connection first. + // Maybe this should be an error instead? + PQfinish(pgconn_); + } + + pgconn_ = connection; + } + + /// @brief Conversion Operator + /// + /// Allows the PgSqlHolder object to be passed as the context argument to + /// PQxxxx functions. + operator PGconn*() const { + return (pgconn_); + } + + /// @brief Boolean Operator + /// + /// Allows testing the connection for emptiness: "if (holder)" + operator bool() const { + return (pgconn_); + } + +private: + PGconn* pgconn_; ///< Postgresql connection +}; + +/// @brief Common PgSql Connector Pool +/// +/// This class provides common operations for PgSql database connection +/// used by both PgSqlLeaseMgr and PgSqlHostDataSource. It manages connecting +/// to the database and preparing compiled statements. Its fields are +/// public, because they are used (both set and retrieved) in classes +/// that use instances of PgSqlConnection. +class PgSqlConnection : public DatabaseConnection { +public: + /// @brief Defines the PgSql error state for a duplicate key error + static const char DUPLICATE_KEY[]; + + /// @brief Constructor + /// + /// Initialize PgSqlConnection object with parameters needed for connection. + PgSqlConnection(const ParameterMap& parameters) + : DatabaseConnection(parameters) { + } + + /// @brief Destructor + virtual ~PgSqlConnection(); + + /// @brief Prepare Single Statement + /// + /// Creates a prepared statement from the text given and adds it to the + /// statements_ vector at the given index. + /// + /// @param index Index into the statements_ vector into which the text + /// should be placed. The vector must be big enough for the index + /// to be valid, else an exception will be thrown. + /// @param text Text of the SQL statement to be prepared. + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + void prepareStatement(const PgSqlTaggedStatement& statement); + + /// @brief Open Database + /// + /// Opens the database using the information supplied in the parameters + /// passed to the constructor. + /// + /// @throw NoDatabaseName Mandatory database name not given + /// @throw DbOpenError Error opening the database + void openDatabase(); + + /// @brief Commit Transactions + /// + /// Commits all pending database operations. On databases that don't + /// support transactions, this is a no-op. + /// + /// @throw DbOperationError If the commit failed. + void commit(); + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. On databases that don't + /// support transactions, this is a no-op. + /// + /// @throw DbOperationError If the rollback failed. + void rollback(); + + /// @brief Checks a result set's SQL state against an error state. + /// + /// @param r result set to check + /// @param error_state error state to compare against + /// + /// @return True if the result set's SQL state equals the error_state, + /// false otherwise. + bool compareError(PGresult*& r, const char* error_state); + + /// @brief Checks result of the r object + /// + /// This function is used to determine whether or not the SQL statement + /// execution succeeded, and in the event of failures, decide whether or + /// not the failures are recoverable. + /// + /// If the error is recoverable, the method will throw a DbOperationError. + /// In the error is deemed unrecoverable, such as a loss of connectivity + /// with the server, this method will log the error and call exit(-1); + /// + /// @todo Calling exit() is viewed as a short term solution for Kea 1.0. + /// Two tickets are likely to alter this behavior, first is #3639, which + /// calls for the ability to attempt to reconnect to the database. The + /// second ticket, #4087 which calls for the implementation of a generic, + /// FatalException class which will propagate outward. + /// + /// @param r result of the last PostgreSQL operation + /// @param statement - tagged statement that was executed + /// + /// @throw isc::dhcp::DbOperationError Detailed PostgreSQL failure + void checkStatementError(PGresult*& r, PgSqlTaggedStatement& statement) const; + + /// @brief PgSql connection handle + /// + /// This field is public, because it is used heavily from PgSqlLeaseMgr + /// and from PgSqlHostDataSource. + PgSqlHolder conn_; + + /// @brief Conversion Operator + /// + /// Allows the PgConnection object to be passed as the context argument to + /// PQxxxx functions. + operator PGconn*() const { + return (conn_); + } + + /// @brief Boolean Operator + /// + /// Allows testing the PgConnection for initialized connection + operator bool() const { + return (conn_); + } + +}; + + + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif // PGSQL_CONNECTION_H diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index 40695e5446..a407ae7310 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -21,64 +21,16 @@ #include #include -// PostgreSQL errors should be tested based on the SQL state code. Each state -// code is 5 decimal, ASCII, digits, the first two define the category of -// error, the last three are the specific error. PostgreSQL makes the state -// code as a char[5]. Macros for each code are defined in PostgreSQL's -// errorcodes.h, although they require a second macro, MAKE_SQLSTATE for -// completion. PostgreSQL deliberately omits this macro from errocodes.h -// so callers can supply their own. -#define MAKE_SQLSTATE(ch1,ch2,ch3,ch4,ch5) {ch1,ch2,ch3,ch4,ch5} -#include -const size_t STATECODE_LEN = 5; - -// Currently the only one we care to look for is duplicate key. -const char DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION; - using namespace isc; using namespace isc::dhcp; using namespace std; namespace { -// Maximum number of parameters used in any single query -const size_t MAX_PARAMETERS_IN_QUERY = 14; - -/// @brief Defines a single query -struct TaggedStatement { - - /// Number of parameters for a given query - int nbparams; - - /// @brief OID types - /// - /// Specify parameter types. See /usr/include/postgresql/catalog/pg_type.h. - /// For some reason that header does not export those parameters. - /// Those OIDs must match both input and output parameters. - const Oid types[MAX_PARAMETERS_IN_QUERY]; - - /// Short name of the query. - const char* name; - - /// Text representation of the actual query. - const char* text; -}; - -/// @brief Constants for PostgreSQL data types -/// This are defined by PostreSQL in , but including -/// this file is extrordinarily convoluted, so we'll use these to fill-in. -const size_t OID_NONE = 0; // PostgreSQL infers proper type -const size_t OID_BOOL = 16; -const size_t OID_BYTEA = 17; -const size_t OID_INT8 = 20; // 8 byte int -const size_t OID_INT2 = 21; // 2 byte int -const size_t OID_TIMESTAMP = 1114; -const size_t OID_VARCHAR = 1043; - /// @brief Catalog of all the SQL statements currently supported. Note /// that the order columns appear in statement body must match the order they /// that the occur in the table. This does not apply to the where clause. -TaggedStatement tagged_statements[] = { +PgSqlTaggedStatement tagged_statements[] = { // DELETE_LEASE4 { 1, { OID_INT8 }, "delete_lease4", @@ -1039,25 +991,12 @@ private: PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) : LeaseMgr(), exchange4_(new PgSqlLease4Exchange()), - exchange6_(new PgSqlLease6Exchange()), dbconn_(parameters), conn_(NULL) { - openDatabase(); + exchange6_(new PgSqlLease6Exchange()), conn_(parameters) { + conn_.openDatabase(); prepareStatements(); } PgSqlLeaseMgr::~PgSqlLeaseMgr() { - if (conn_) { - // Deallocate the prepared queries. - PGresult* r = PQexec(conn_, "DEALLOCATE all"); - if(PQresultStatus(r) != PGRES_COMMAND_OK) { - // Highly unlikely but we'll log it and go on. - LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_DEALLOC_ERROR) - .arg(PQerrorMessage(conn_)); - } - - PQclear(r); - PQfinish(conn_); - conn_ = NULL; - } } std::string @@ -1072,73 +1011,7 @@ PgSqlLeaseMgr::getDBVersion() { void PgSqlLeaseMgr::prepareStatements() { for(int i = 0; tagged_statements[i].text != NULL; ++ i) { - // Prepare all statements queries with all known fields datatype - PGresult* r = PQprepare(conn_, tagged_statements[i].name, - tagged_statements[i].text, - tagged_statements[i].nbparams, - tagged_statements[i].types); - - if(PQresultStatus(r) != PGRES_COMMAND_OK) { - PQclear(r); - isc_throw(DbOperationError, - "unable to prepare PostgreSQL statement: " - << tagged_statements[i].text << ", reason: " - << PQerrorMessage(conn_)); - } - - PQclear(r); - } -} - -void -PgSqlLeaseMgr::openDatabase() { - string dbconnparameters; - string shost = "localhost"; - try { - shost = dbconn_.getParameter("host"); - } catch(...) { - // No host. Fine, we'll use "localhost" - } - - dbconnparameters += "host = '" + shost + "'" ; - - string suser; - try { - suser = dbconn_.getParameter("user"); - dbconnparameters += " user = '" + suser + "'"; - } catch(...) { - // No user. Fine, we'll use NULL - } - - string spassword; - try { - spassword = dbconn_.getParameter("password"); - dbconnparameters += " password = '" + spassword + "'"; - } catch(...) { - // No password. Fine, we'll use NULL - } - - string sname; - try { - sname= dbconn_.getParameter("name"); - dbconnparameters += " dbname = '" + sname + "'"; - } catch(...) { - // No database name. Throw a "NoDatabaseName" exception - isc_throw(NoDatabaseName, "must specify a name for the database"); - } - - conn_ = PQconnectdb(dbconnparameters.c_str()); - if (conn_ == NULL) { - isc_throw(DbOpenError, "could not allocate connection object"); - } - - if (PQstatus(conn_) != CONNECTION_OK) { - // If we have a connection object, we have to call finish - // to release it, but grab the error message first. - std::string error_message = PQerrorMessage(conn_); - PQfinish(conn_); - conn_ = NULL; - isc_throw(DbOpenError, error_message); + conn_.prepareStatement(tagged_statements[i]); } } @@ -1157,12 +1030,12 @@ PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex, // Failure: check for the special case of duplicate entry. If this is // the case, we return false to indicate that the row was not added. // Otherwise we throw an exception. - if (compareError(r, DUPLICATE_KEY)) { + if (conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) { PQclear(r); return (false); } - checkStatementError(r, stindex); + conn_.checkStatementError(r, tagged_statements[stindex]); } PQclear(r); @@ -1170,13 +1043,6 @@ PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex, return (true); } -bool PgSqlLeaseMgr::compareError(PGresult*& r, const char* error_state) { - const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE); - // PostgreSQL garuantees it will always be 5 characters long - return ((sqlstate != NULL) && - (memcmp(sqlstate, error_state, STATECODE_LEN) == 0)); -} - bool PgSqlLeaseMgr::addLease(const Lease4Ptr& lease) { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, @@ -1212,7 +1078,7 @@ void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex, &bind_array.lengths_[0], &bind_array.formats_[0], 0); - checkStatementError(r, stindex); + conn_.checkStatementError(r, tagged_statements[stindex]); int rows = PQntuples(r); if (single && rows > 1) { @@ -1530,7 +1396,7 @@ PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, &bind_array.lengths_[0], &bind_array.formats_[0], 0); - checkStatementError(r, stindex); + conn_.checkStatementError(r, tagged_statements[stindex]); int affected_rows = boost::lexical_cast(PQcmdTuples(r)); PQclear(r); @@ -1601,7 +1467,7 @@ PgSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, &bind_array.lengths_[0], &bind_array.formats_[0], 0); - checkStatementError(r, stindex); + conn_.checkStatementError(r, tagged_statements[stindex]); int affected_rows = boost::lexical_cast(PQcmdTuples(r)); PQclear(r); @@ -1666,43 +1532,13 @@ string PgSqlLeaseMgr::getName() const { string name = ""; try { - name = dbconn_.getParameter("name"); + name = conn_.getParameter("name"); } catch (...) { // Return an empty name } return (name); } -void -PgSqlLeaseMgr::checkStatementError(PGresult*& r, StatementIndex index) const { - int s = PQresultStatus(r); - if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) { - // We're testing the first two chars of SQLSTATE, as this is the - // error class. Note, there is a severity field, but it can be - // misleadingly returned as fatal. - const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE); - if ((sqlstate != NULL) && - ((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception - (memcmp(sqlstate, "53", 2) == 0) || // Insufficient resources - (memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded - (memcmp(sqlstate, "57", 2) == 0) || // Operator intervention - (memcmp(sqlstate, "58", 2) == 0))) { // System error - LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_FATAL_ERROR) - .arg(tagged_statements[index].name) - .arg(PQerrorMessage(conn_)) - .arg(sqlstate); - PQclear(r); - exit (-1); - } - - const char* error_message = PQerrorMessage(conn_); - PQclear(r); - isc_throw(DbOperationError, "Statement exec faild:" << " for: " - << tagged_statements[index].name << ", reason: " - << error_message); - } -} - string PgSqlLeaseMgr::getDescription() const { return (string("PostgreSQL Database")); @@ -1714,7 +1550,7 @@ PgSqlLeaseMgr::getVersion() const { DHCPSRV_PGSQL_GET_VERSION); PGresult* r = PQexecPrepared(conn_, "get_version", 0, NULL, NULL, NULL, 0); - checkStatementError(r, GET_VERSION); + conn_.checkStatementError(r, tagged_statements[GET_VERSION]); istringstream tmp; uint32_t version; @@ -1734,28 +1570,12 @@ PgSqlLeaseMgr::getVersion() const { void PgSqlLeaseMgr::commit() { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_COMMIT); - PGresult* r = PQexec(conn_, "COMMIT"); - if (PQresultStatus(r) != PGRES_COMMAND_OK) { - const char* error_message = PQerrorMessage(conn_); - PQclear(r); - isc_throw(DbOperationError, "commit failed: " << error_message); - } - - PQclear(r); + conn_.commit(); } void PgSqlLeaseMgr::rollback() { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_ROLLBACK); - PGresult* r = PQexec(conn_, "ROLLBACK"); - if (PQresultStatus(r) != PGRES_COMMAND_OK) { - const char* error_message = PQerrorMessage(conn_); - PQclear(r); - isc_throw(DbOperationError, "rollback failed: " << error_message); - } - - PQclear(r); + conn_.rollback(); } }; // end of isc::dhcp namespace diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index 6afd2a21ab..d84f4fe8c4 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -9,10 +9,10 @@ #include #include -#include +#include + #include #include -#include #include @@ -440,15 +440,6 @@ public: /// @throw DbOperationError If the rollback failed. virtual void rollback(); - /// @brief Checks a result set's SQL state against an error state. - /// - /// @param r result set to check - /// @param error_state error state to compare against - /// - /// @return True if the result set's SQL state equals the error_state, - /// false otherwise. - bool compareError(PGresult*& r, const char* error_state); - /// @brief Statement Tags /// /// The contents of the enum are indexes into the list of compiled SQL @@ -700,14 +691,8 @@ private: boost::scoped_ptr exchange4_; ///< Exchange object boost::scoped_ptr exchange6_; ///< Exchange object - /// Database connection object - /// - /// @todo: Implement PgSQLConnection object and collapse - /// dbconn_ and conn_ into a single object. - DatabaseConnection dbconn_; - /// PostgreSQL connection handle - PGconn* conn_; + PgSqlConnection conn_; }; }; // end of isc::dhcp namespace