PgSqlHostDataSourceImpl::addStatement(StatementIndex stindex,
PsqlBindArrayPtr& bind_array,
const bool return_last_id) {
+ PgSqlHolder& holderHandle = conn_.handle();
uint64_t last_id = 0;
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+
+ PgSqlResult r(PQexecPrepared(holderHandle, tagged_statements[stindex].name,
tagged_statements[stindex].nbparams,
&bind_array->values_[0],
&bind_array->lengths_[0],
bool
PgSqlHostDataSourceImpl::delStatement(StatementIndex stindex,
PsqlBindArrayPtr& bind_array) {
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+ PgSqlHolder& holderHandle = conn_.handle();
+
+ PgSqlResult r(PQexecPrepared(holderHandle, tagged_statements[stindex].name,
tagged_statements[stindex].nbparams,
&bind_array->values_[0],
&bind_array->lengths_[0],
getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind_array,
std::shared_ptr<PgSqlHostExchange> exchange,
ConstHostCollection& result, bool single) const {
+ PgSqlHolder& holderHandle = conn_.handle();
exchange->clear();
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+ PgSqlResult r(PQexecPrepared(holderHandle, tagged_statements[stindex].name,
tagged_statements[stindex].nbparams,
&bind_array->values_[0],
&bind_array->lengths_[0],
PgSqlHostDataSourceImpl::getVersion() const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_PGSQL_HOST_DB_GET_VERSION);
+
+ PgSqlHolder& holderHandle = conn_.handle();
const char* version_sql = "SELECT version, minor FROM schema_version;";
- PgSqlResult r(PQexec(conn_, version_sql));
+
+ PgSqlResult r(PQexec(holderHandle, version_sql));
if(PQresultStatus(r) != PGRES_TUPLES_OK) {
isc_throw(DbOperationError, "unable to execute PostgreSQL statement <"
- << version_sql << ">, reason: " << PQerrorMessage(conn_));
+ << version_sql << ">, reason: " << PQerrorMessage(holderHandle));
}
uint32_t major;
/// parameters (for all subnets), a subnet id for a single subnet, or
/// a first and last subnet id for a subnet range.
void start() {
+ PgSqlHolder& holderHandle = conn_.handle();
if (getSelectMode() == ALL_SUBNETS) {
// Run the query with no where clause parameters.
- result_set_.reset(new PgSqlResult(PQexecPrepared(conn_, statement_.name,
+ result_set_.reset(new PgSqlResult(PQexecPrepared(holderHandle, statement_.name,
0, 0, 0, 0, 0)));
} else {
// Set up the WHERE clause values
}
// Run the query with where clause parameters.
- result_set_.reset(new PgSqlResult(PQexecPrepared(conn_, statement_.name,
+ result_set_.reset(new PgSqlResult(PQexecPrepared(holderHandle, statement_.name,
parms.size(), &parms.values_[0],
&parms.lengths_[0], &parms.formats_[0], 0)));
}
bool
PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
PsqlBindArray& bind_array) {
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+ PgSqlHolder& holderHandle = conn_.handle();
+
+ PgSqlResult r(PQexecPrepared(holderHandle, tagged_statements[stindex].name,
tagged_statements[stindex].nbparams,
&bind_array.values_[0],
&bind_array.lengths_[0],
Exchange& exchange,
LeaseCollection& result,
bool single) const {
+ PgSqlHolder& holderHandle = conn_.handle();
const int n = tagged_statements[stindex].nbparams;
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name, n,
+
+ PgSqlResult r(PQexecPrepared(holderHandle, tagged_statements[stindex].name, n,
n > 0 ? &bind_array.values_[0] : NULL,
n > 0 ? &bind_array.lengths_[0] : NULL,
n > 0 ? &bind_array.formats_[0] : NULL, 0));
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_PGSQL_ADD_ADDR4).arg(tagged_statements[stindex].name);
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+ PgSqlHolder& holderHandle = conn_.handle();
+
+ PgSqlResult r(PQexecPrepared(holderHandle, tagged_statements[stindex].name,
tagged_statements[stindex].nbparams,
&bind_array.values_[0],
&bind_array.lengths_[0],
uint64_t
PgSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
PsqlBindArray& bind_array) {
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+ PgSqlHolder& holderHandle = conn_.handle();
+
+ PgSqlResult r(PQexecPrepared(holderHandle, tagged_statements[stindex].name,
tagged_statements[stindex].nbparams,
&bind_array.values_[0],
&bind_array.lengths_[0],
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_PGSQL_GET_VERSION);
+ PgSqlHolder& holderHandle = conn_.handle();
const char* version_sql = "SELECT version, minor FROM schema_version;";
- PgSqlResult r(PQexec(conn_, version_sql));
+
+ PgSqlResult r(PQexec(holderHandle, version_sql));
if(PQresultStatus(r) != PGRES_TUPLES_OK) {
isc_throw(DbOperationError, "unable to execute PostgreSQL statement <"
- << version_sql << ", reason: " << PQerrorMessage(conn_));
+ << version_sql << ">, reason: " << PQerrorMessage(holderHandle));
}
uint32_t major;
PgSqlConnection conn(params);
conn.openDatabase();
- PgSqlResult r(PQexec(conn, query.c_str()));
+ PgSqlHolder& holderHandle = conn.handle();
+
+ PgSqlResult r(PQexec(holderHandle, query.c_str()));
if (PQresultStatus(r) != PGRES_TUPLES_OK) {
- isc_throw(DbOperationError, "Query failed:" << PQerrorMessage(conn));
+ isc_throw(DbOperationError, "Query failed:" << PQerrorMessage(holderHandle));
}
int numrows = PQntuples(r);
PgSqlConnection conn(params);
ASSERT_NO_THROW(conn.openDatabase());
- PgSqlResult r(PQexec(conn, "DROP TABLE IF EXISTS ipv6_reservations"));
- ASSERT_TRUE (PQresultStatus(r) == PGRES_COMMAND_OK)
- << " drop command failed :" << PQerrorMessage(conn);
+ PgSqlHolder& holderHandle = conn.handle();
+
+ ConstHostCollection collection = hdsptr_->getAll4(0);
+ ASSERT_EQ(collection.size(), 0);
+
+ PgSqlResult r(PQexec(holderHandle, "DROP TABLE IF EXISTS ipv6_reservations"));
+ ASSERT_TRUE(PQresultStatus(r) == PGRES_COMMAND_OK)
+ << " drop command failed :" << PQerrorMessage(holderHandle);
// Create a host with a reservation.
HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1",
const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION;
+void
+PgSqlHolder::setConnection(PGconn* connection) {
+ clearPrepared();
+ if (pgconn_ != NULL) {
+ PQfinish(pgconn_);
+ }
+ pgconn_ = connection;
+ connected_ = false;
+ prepared_ = false;
+}
+
+void
+PgSqlHolder::clearPrepared() {
+ if (pgconn_ != NULL) {
+ // Deallocate the prepared queries.
+ if (PQstatus(pgconn_) == CONNECTION_OK) {
+ PgSqlResult r(PQexec(pgconn_, "DEALLOCATE all"));
+ if(PQresultStatus(r) != PGRES_COMMAND_OK) {
+ // Highly unlikely but we'll log it and go on.
+ DB_LOG_ERROR(PGSQL_DEALLOC_ERROR)
+ .arg(PQerrorMessage(pgconn_));
+ }
+ }
+ }
+}
+
+void
+PgSqlHolder::openDatabase(PgSqlConnection& connection) {
+ if (connected_) {
+ return;
+ }
+ connected_ = true;
+ prepared_ = true;
+ connection.openDatabase();
+ prepared_ = false;
+}
+
+void
+PgSqlHolder::prepareStatements(PgSqlConnection& connection) {
+ if (prepared_) {
+ return;
+ }
+ clearPrepared();
+ // Prepare all statements queries with all known fields datatype
+ for (auto it = connection.statements_.begin();
+ it != connection.statements_.end(); ++it) {
+ PgSqlResult r(PQprepare(pgconn_, (*it)->name, (*it)->text,
+ (*it)->nbparams, (*it)->types));
+ if (PQresultStatus(r) != PGRES_COMMAND_OK) {
+ isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
+ << (*it)->text << ", reason: " << PQerrorMessage(pgconn_));
+ }
+ }
+ prepared_ = true;
+}
+
PgSqlResult::PgSqlResult(PGresult *result)
: result_(result), rows_(0), cols_(0) {
if (!result) {
PgSqlTransaction::~PgSqlTransaction() {
// If commit() wasn't explicitly called, rollback.
if (!committed_) {
- conn_.rollback();
+ try {
+ conn_.rollback();
+ } catch (...) {
+ }
}
}
}
PgSqlConnection::~PgSqlConnection() {
- if (conn_) {
- // Deallocate the prepared queries.
- if (PQstatus(conn_) == CONNECTION_OK) {
- PgSqlResult r(PQexec(conn_, "DEALLOCATE all"));
- if(PQresultStatus(r) != PGRES_COMMAND_OK) {
- // Highly unlikely but we'll log it and go on.
- DB_LOG_ERROR(PGSQL_DEALLOC_ERROR)
- .arg(PQerrorMessage(conn_));
- }
- }
- }
+ statements_.clear();
+ handle().clear();
}
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_));
- }
+ statements_.push_back(&statement);
+ prepared_ = true;
}
void
}
// We have a valid connection, so let's save it to our holder
- conn_.setConnection(new_conn);
+ PgSqlHolder& holderHandle = handle();
+ holderHandle.setConnection(new_conn);
+ holderHandle.connected_ = true;
+ connected_ = true;
}
bool
// error class. Note, there is a severity field, but it can be
// misleadingly returned as fatal. However, a loss of connectivity
// can lead to a NULL sqlstate with a status of PGRES_FATAL_ERROR.
+
+ PgSqlHolder& holderHandle = handle();
+
const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
if ((sqlstate == NULL) ||
((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception
(memcmp(sqlstate, "58", 2) == 0))) { // System error
DB_LOG_ERROR(PGSQL_FATAL_ERROR)
.arg(statement.name)
- .arg(PQerrorMessage(conn_))
+ .arg(PQerrorMessage(holderHandle))
.arg(sqlstate ? sqlstate : "<sqlstate null>");
// If there's no lost db callback or it returns false,
}
// Apparently it wasn't fatal, so we throw with a helpful message.
- const char* error_message = PQerrorMessage(conn_);
+ const char* error_message = PQerrorMessage(holderHandle);
isc_throw(DbOperationError, "Statement exec failed:" << " for: "
<< statement.name << ", status: " << s
<< "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
void
PgSqlConnection::startTransaction() {
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_START_TRANSACTION);
- PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
+
+ PgSqlHolder& holderHandle = handle();
+
+ PgSqlResult r(PQexec(holderHandle, "START TRANSACTION"));
if (PQresultStatus(r) != PGRES_COMMAND_OK) {
- const char* error_message = PQerrorMessage(conn_);
+ const char* error_message = PQerrorMessage(holderHandle);
isc_throw(DbOperationError, "unable to start transaction"
<< error_message);
}
void
PgSqlConnection::commit() {
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_COMMIT);
- PgSqlResult r(PQexec(conn_, "COMMIT"));
+
+ PgSqlHolder& holderHandle = handle();
+
+ PgSqlResult r(PQexec(holderHandle, "COMMIT"));
if (PQresultStatus(r) != PGRES_COMMAND_OK) {
- const char* error_message = PQerrorMessage(conn_);
+ const char* error_message = PQerrorMessage(holderHandle);
isc_throw(DbOperationError, "commit failed: " << error_message);
}
}
void
PgSqlConnection::rollback() {
DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_ROLLBACK);
- PgSqlResult r(PQexec(conn_, "ROLLBACK"));
+
+ PgSqlHolder& holderHandle = handle();
+
+ PgSqlResult r(PQexec(holderHandle, "ROLLBACK"));
if (PQresultStatus(r) != PGRES_COMMAND_OK) {
- const char* error_message = PQerrorMessage(conn_);
+ const char* error_message = PQerrorMessage(holderHandle);
isc_throw(DbOperationError, "rollback failed: " << error_message);
}
}
-}; // end of isc::db namespace
-}; // end of isc namespace
+} // namespace db
+} // namespace isc
+
#define PGSQL_CONNECTION_H
#include <database/database_connection.h>
+#include <dhcpsrv/thread_resource_mgr.h>
#include <libpq-fe.h>
#include <boost/scoped_ptr.hpp>
}
private:
- PGresult* result_; ///< Result set to be freed
- int rows_; ///< Number of rows in the result set
- int cols_; ///< Number of columns in the result set
+ PGresult* result_; ///< Result set to be freed
+ int rows_; ///< Number of rows in the result set
+ int cols_; ///< Number of columns in the result set
};
+/// @brief Forward declaration to @ref PgSqlConnection.
+class PgSqlConnection;
/// @brief Postgresql connection handle Holder
///
/// For this reason, the class is declared noncopyable.
class PgSqlHolder : public boost::noncopyable {
public:
-
/// @brief Constructor
///
/// Sets the Postgresql API connector handle to NULL.
///
- PgSqlHolder() : pgconn_(NULL) {
+ PgSqlHolder() : connected_(false), prepared_(false), pgconn_(NULL) {
}
/// @brief Destructor
///
/// Frees up resources allocated by the connection.
~PgSqlHolder() {
- if (pgconn_ != NULL) {
- PQfinish(pgconn_);
- }
+ clear();
}
+ void clear() {
+ setConnection(NULL);
+ }
+
+ void clearPrepared();
+
/// @brief Sets the connection to the value given
///
/// @param connection - pointer to the Postgresql connection instance
- void setConnection(PGconn* connection) {
- if (pgconn_ != NULL) {
- // Already set? Release the current connection first.
- // Maybe this should be an error instead?
- PQfinish(pgconn_);
- }
+ void setConnection(PGconn* connection);
- pgconn_ = connection;
- }
+ void openDatabase(PgSqlConnection& connection);
+
+ void prepareStatements(PgSqlConnection& connection);
/// @brief Conversion Operator
///
return (pgconn_);
}
- /// @brief Boolean Operator
- ///
- /// Allows testing the connection for emptiness: "if (holder)"
- operator bool() const {
- return (pgconn_);
- }
+ bool connected_; ///< Flag to indicate openDatabase has been called
private:
- PGconn* pgconn_; ///< Postgresql connection
-};
+ bool prepared_; ///< Flag to indicate prepareStatements has been called
-/// @brief Forward declaration to @ref PgSqlConnection.
-class PgSqlConnection;
+ PGconn* pgconn_; ///< Postgresql connection
+};
/// @brief RAII object representing a PostgreSQL transaction.
///
/// @brief Constructor
///
/// Initialize PgSqlConnection object with parameters needed for connection.
- PgSqlConnection(const ParameterMap& parameters)
- : DatabaseConnection(parameters) {
+ PgSqlConnection(const ParameterMap& parameters) :
+ DatabaseConnection(parameters), connected_(false), prepared_(false) {
}
/// @brief Destructor
void checkStatementError(const PgSqlResult& r,
PgSqlTaggedStatement& statement) const;
- /// @brief PgSql connection handle
+ /// @brief Raw statements
///
- /// This field is public, because it is used heavily from PgSqlLeaseMgr
+ /// This field is public, because it is used heavily from PgSqlConnection
/// and from PgSqlHostDataSource.
- PgSqlHolder conn_;
+ std::vector<const PgSqlTaggedStatement*> statements_;
- /// @brief Conversion Operator
+ /// @brief PgSql connection handle
///
- /// Allows the PgConnection object to be passed as the context argument to
- /// PQxxxx functions.
- operator PGconn*() const {
- return (conn_);
+ /// This field is public, because it is used heavily from PgSqlLeaseMgr
+ /// and from PgSqlHostDataSource.
+ PgSqlHolder& handle() const {
+ auto result = handles_.resource();
+ // thread_local std::shared_ptr<PgSqlHolder> result(std::make_shared<PgSqlHolder>());
+ if (connected_) {
+ result->openDatabase(*(const_cast<PgSqlConnection*>(this)));
+ }
+ if (prepared_) {
+ result->prepareStatements(*(const_cast<PgSqlConnection*>(this)));
+ }
+ return *result;
}
- /// @brief Boolean Operator
- ///
- /// Allows testing the PgConnection for initialized connection
- operator bool() const {
- return (conn_);
- }
+private:
+ bool connected_; ///< Flag to indicate openDatabase has been called
+
+ bool prepared_; ///< Flag to indicate prepareStatements has been called
+ mutable isc::dhcp::ThreadResourceMgr<PgSqlHolder> handles_;
};
-}; // end of isc::db namespace
-}; // end of isc namespace
+} // namespace db
+} // namespace isc
#endif // PGSQL_CONNECTION_H
/// @return Returns true if there are no entries in the array, false
/// otherwise.
bool empty() const {
-
return (values_.empty());
}
protected:
/// @brief Stores text labels for columns, currently only used for
/// logging and errors.
- std::vector<std::string>columns_;
+ std::vector<std::string> columns_;
};
}; // end of isc::db namespace
" varchar_col VARCHAR(255) "
"); ";
- PgSqlResult r(PQexec(*conn_, sql));
+ PgSqlHolder& holderHandle = conn_->handle();
+
+ PgSqlResult r(PQexec(holderHandle, sql));
ASSERT_EQ(PQresultStatus(r), PGRES_COMMAND_OK)
- << " create basics table failed: " << PQerrorMessage(*conn_);
+ << " create basics table failed: " << PQerrorMessage(holderHandle);
}
/// @brief Destroys the basics table
/// Asserts if the destruction fails
void destroySchema() {
if (conn_) {
- PgSqlResult r(PQexec(*conn_, "DROP TABLE IF EXISTS basics;"));
+ PgSqlHolder& holderHandle = conn_->handle();
+
+ PgSqlResult r(PQexec(holderHandle, "DROP TABLE IF EXISTS basics;"));
ASSERT_EQ(PQresultStatus(r), PGRES_COMMAND_OK)
- << " drop basics table failed: " << PQerrorMessage(*conn_);
+ << " drop basics table failed: " << PQerrorMessage(holderHandle);
}
}
/// Asserts if the result set status does not equal the expected outcome.
void runSql(PgSqlResultPtr& r, const std::string& sql, int exp_outcome,
int lineno) {
- r.reset(new PgSqlResult(PQexec(*conn_, sql.c_str())));
+ PgSqlHolder& holderHandle = conn_->handle();
+
+ r.reset(new PgSqlResult(PQexec(holderHandle, sql.c_str())));
ASSERT_EQ(PQresultStatus(*r), exp_outcome)
<< " runSql at line: " << lineno << " failed, sql:[" << sql
- << "]\n reason: " << PQerrorMessage(*conn_);
+ << "]\n reason: " << PQerrorMessage(holderHandle);
}
/// @brief Executes a SQL statement and tests for an expected outcome
PgSqlTaggedStatement& statement,
PsqlBindArrayPtr bind_array, int exp_outcome,
int lineno) {
- r.reset(new PgSqlResult(PQexecPrepared(*conn_, statement.name,
+ PgSqlHolder& holderHandle = conn_->handle();
+
+ r.reset(new PgSqlResult(PQexecPrepared(holderHandle, statement.name,
statement.nbparams,
&bind_array->values_[0],
&bind_array->lengths_[0],
ASSERT_EQ(PQresultStatus(*r), exp_outcome)
<< " runPreparedStatement at line: " << lineno
<< " statement name:[" << statement.name
- << "]\n reason: " << PQerrorMessage(*conn_);
+ << "]\n reason: " << PQerrorMessage(holderHandle);
}
/// @brief Fetches all of the rows currently in the table