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
--- /dev/null
+// 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 <config.h>
+
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/pgsql_connection.h>
+
+#include <boost/static_assert.hpp>
+
+#include <iostream>
+#include <iomanip>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <time.h>
+
+// 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 <utils/errcodes.h>
+
+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
--- /dev/null
+// 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 <dhcpsrv/database_connection.h>
+
+#include <libpq-fe.h>
+#include <boost/scoped_ptr.hpp>
+
+#include <vector>
+
+
+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 <catalog/pg_type.h>, 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
#include <string>
#include <time.h>
-// 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 <utils/errcodes.h>
-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 <catalog/pg_type.h>, 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",
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
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]);
}
}
// 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);
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,
&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) {
&bind_array.lengths_[0],
&bind_array.formats_[0], 0);
- checkStatementError(r, stindex);
+ conn_.checkStatementError(r, tagged_statements[stindex]);
int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
PQclear(r);
&bind_array.lengths_[0],
&bind_array.formats_[0], 0);
- checkStatementError(r, stindex);
+ conn_.checkStatementError(r, tagged_statements[stindex]);
int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
PQclear(r);
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"));
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;
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
#include <dhcp/hwaddr.h>
#include <dhcpsrv/lease_mgr.h>
-#include <dhcpsrv/database_connection.h>
+#include <dhcpsrv/pgsql_connection.h>
+
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
-#include <libpq-fe.h>
#include <vector>
/// @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
boost::scoped_ptr<PgSqlLease4Exchange> exchange4_; ///< Exchange object
boost::scoped_ptr<PgSqlLease6Exchange> 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