mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 22:15:23 +00:00
[4277] Bare bones implementation of PgSqlHostDataSource
src/lib/dhcpsrv pgsql_host_data_source.c pgsql_host_data_source.h - new files, preliminary implementation src/lib/dhcpsrv/Makefile.am Added new files pgsql_host_data_source.cc, pgsql_host_data_source.h src/lib/dhcpsrv/dhcpsrv_messages.mes Added log messages DHCPSRV_PGSQL_HOST_DB_GET_VERSION, DHCPSRV_PGSQL_START_TRANSACTION src/lib/dhcpsrv/pgsql_connection.cc src/lib/dhcpsrv/pgsql_connection.h Added PgSqlTransaction Added PgSqlConnection::startTransaction() src/lib/dhcpsrv/pgsql_exchange.cc src/lib/dhcpsrv/pgsql_exchange.h PsqlBindArray - Added storage of conversion strings used for bound values - Added add() variants for uint8_t, IOAddress, uint8_t buffer - Added templated variant for miscellaneous types PgSqlExchange - Removed getColumnValue variants for various integers, replaced with templated version for miscellaneous types src/lib/dhcpsrv/pgsql_lease_mgr.cc Added todo comment to remember to account for hwaddr columns added to lease6 src/lib/dhcpsrv/tests/pgsql_exchange_unittest.cc TEST(PsqlBindArray, basicOperation) - new test to exercise bind functions
This commit is contained in:
@@ -136,6 +136,7 @@ libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h
|
|||||||
if HAVE_PGSQL
|
if HAVE_PGSQL
|
||||||
libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h
|
libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h
|
||||||
libkea_dhcpsrv_la_SOURCES += pgsql_exchange.cc pgsql_exchange.h
|
libkea_dhcpsrv_la_SOURCES += pgsql_exchange.cc pgsql_exchange.h
|
||||||
|
libkea_dhcpsrv_la_SOURCES += pgsql_host_data_source.cc pgsql_host_data_source.h
|
||||||
libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
|
libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
|
||||||
endif
|
endif
|
||||||
libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
|
libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
|
||||||
|
@@ -634,6 +634,10 @@ An error message indicating that communication with the MySQL database server
|
|||||||
has been lost. When this occurs the server exits immediately with a non-zero
|
has been lost. When this occurs the server exits immediately with a non-zero
|
||||||
exit code. This is most likely due to a network issue.
|
exit code. This is most likely due to a network issue.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_HOST_DB_GET_VERSION obtaining schema version information for the PostgreSQL hosts database
|
||||||
|
A debug message issued when the server is about to obtain schema version
|
||||||
|
information from the PostgreSQL hosts database.
|
||||||
|
|
||||||
% DHCPSRV_PGSQL_GET_ADDR4 obtaining IPv4 lease for address %1
|
% DHCPSRV_PGSQL_GET_ADDR4 obtaining IPv4 lease for address %1
|
||||||
A debug message issued when the server is attempting to obtain an IPv4
|
A debug message issued when the server is attempting to obtain an IPv4
|
||||||
lease from the PostgreSQL database for the specified address.
|
lease from the PostgreSQL database for the specified address.
|
||||||
@@ -696,6 +700,15 @@ connection including database name and username needed to access it
|
|||||||
The code has issued a rollback call. All outstanding transaction will
|
The code has issued a rollback call. All outstanding transaction will
|
||||||
be rolled back and not committed to the database.
|
be rolled back and not committed to the database.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_START_TRANSACTION starting a new PostgreSQL transaction
|
||||||
|
A debug message issued when a new PostgreSQL transaction is being started.
|
||||||
|
This message is typically not issued when inserting data into a
|
||||||
|
single table because the server doesn't explicitly start
|
||||||
|
transactions in this case. This message is issued when data is
|
||||||
|
inserted into multiple tables with multiple INSERT statements
|
||||||
|
and there may be a need to rollback the whole transaction if
|
||||||
|
any of these INSERT statements fail.
|
||||||
|
|
||||||
% DHCPSRV_PGSQL_UPDATE_ADDR4 updating IPv4 lease for address %1
|
% DHCPSRV_PGSQL_UPDATE_ADDR4 updating IPv4 lease for address %1
|
||||||
A debug message issued when the server is attempting to update IPv4
|
A debug message issued when the server is attempting to update IPv4
|
||||||
lease from the PostgreSQL database for the specified address.
|
lease from the PostgreSQL database for the specified address.
|
||||||
|
@@ -35,6 +35,25 @@ const int PGSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
|
|||||||
|
|
||||||
const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION;
|
const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION;
|
||||||
|
|
||||||
|
PgSqlTransaction::PgSqlTransaction(PgSqlConnection& conn)
|
||||||
|
: conn_(conn), committed_(false) {
|
||||||
|
conn_.startTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
PgSqlTransaction::~PgSqlTransaction() {
|
||||||
|
// Rollback if the PgSqlTransaction::commit wasn't explicitly
|
||||||
|
// called.
|
||||||
|
if (!committed_) {
|
||||||
|
conn_.rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PgSqlTransaction::commit() {
|
||||||
|
conn_.commit();
|
||||||
|
committed_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
PgSqlConnection::~PgSqlConnection() {
|
PgSqlConnection::~PgSqlConnection() {
|
||||||
if (conn_) {
|
if (conn_) {
|
||||||
// Deallocate the prepared queries.
|
// Deallocate the prepared queries.
|
||||||
@@ -192,6 +211,18 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PgSqlConnection::startTransaction() {
|
||||||
|
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
|
||||||
|
DHCPSRV_PGSQL_START_TRANSACTION);
|
||||||
|
PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
|
||||||
|
if (PQresultStatus(r) != PGRES_COMMAND_OK) {
|
||||||
|
const char* error_message = PQerrorMessage(conn_);
|
||||||
|
isc_throw(DbOperationError, "unable to start transaction"
|
||||||
|
<< error_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PgSqlConnection::commit() {
|
PgSqlConnection::commit() {
|
||||||
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_COMMIT);
|
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_COMMIT);
|
||||||
|
@@ -53,6 +53,7 @@ const size_t OID_NONE = 0; // PostgreSQL infers proper type
|
|||||||
const size_t OID_BOOL = 16;
|
const size_t OID_BOOL = 16;
|
||||||
const size_t OID_BYTEA = 17;
|
const size_t OID_BYTEA = 17;
|
||||||
const size_t OID_INT8 = 20; // 8 byte int
|
const size_t OID_INT8 = 20; // 8 byte int
|
||||||
|
const size_t OID_INT4 = 23; // 4 byte int
|
||||||
const size_t OID_INT2 = 21; // 2 byte int
|
const size_t OID_INT2 = 21; // 2 byte int
|
||||||
const size_t OID_TIMESTAMP = 1114;
|
const size_t OID_TIMESTAMP = 1114;
|
||||||
const size_t OID_VARCHAR = 1043;
|
const size_t OID_VARCHAR = 1043;
|
||||||
@@ -176,6 +177,62 @@ private:
|
|||||||
PGconn* pgconn_; ///< Postgresql connection
|
PGconn* pgconn_; ///< Postgresql connection
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Forward declaration to @ref PgSqlConnection.
|
||||||
|
class PgSqlConnection;
|
||||||
|
|
||||||
|
/// @brief RAII object representing PostgreSQL transaction.
|
||||||
|
///
|
||||||
|
/// An instance of this class should be created in a scope where multiple
|
||||||
|
/// INSERT statements should be executed within a single transaction. The
|
||||||
|
/// transaction is started when the constructor of this class is invoked.
|
||||||
|
/// The transaction is ended when the @ref PgSqlTransaction::commit is
|
||||||
|
/// explicitly called or when the instance of this class is destroyed.
|
||||||
|
/// The @ref PgSqlTransaction::commit commits changes to the database
|
||||||
|
/// and the changes remain in the database when the instance of the
|
||||||
|
/// class is destroyed. If the class instance is destroyed before the
|
||||||
|
/// @ref PgSqlTransaction::commit is called, the transaction is rolled
|
||||||
|
/// back. The rollback on destruction guarantees that partial data is
|
||||||
|
/// not stored in the database when there is an error during any
|
||||||
|
/// of the operations belonging to a transaction.
|
||||||
|
///
|
||||||
|
/// The default PostgreSQL backend configuration enables 'autocommit'.
|
||||||
|
/// Starting a transaction overrides 'autocommit' setting for this
|
||||||
|
/// particular transaction only. It does not affect the global 'autocommit'
|
||||||
|
/// setting for the database connection, i.e. all modifications to the
|
||||||
|
/// database which don't use transactions will still be auto committed.
|
||||||
|
class PgSqlTransaction : public boost::noncopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor.
|
||||||
|
///
|
||||||
|
/// Starts transaction by making a "START TRANSACTION" query.
|
||||||
|
///
|
||||||
|
/// @param conn PostgreSQL connection to use for the transaction. This
|
||||||
|
/// connection will be later used to commit or rollback changes.
|
||||||
|
///
|
||||||
|
/// @throw DbOperationError if "START TRANSACTION" query fails.
|
||||||
|
PgSqlTransaction(PgSqlConnection& conn);
|
||||||
|
|
||||||
|
/// @brief Destructor.
|
||||||
|
///
|
||||||
|
/// Rolls back the transaction if changes haven't been committed.
|
||||||
|
~PgSqlTransaction();
|
||||||
|
|
||||||
|
/// @brief Commits transaction.
|
||||||
|
void commit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Holds reference to the PostgreSQL database connection.
|
||||||
|
PgSqlConnection& conn_;
|
||||||
|
|
||||||
|
/// @brief Boolean flag indicating if the transaction has been committed.
|
||||||
|
///
|
||||||
|
/// This flag is used in the class destructor to assess if the
|
||||||
|
/// transaction should be rolled back.
|
||||||
|
bool committed_;
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Common PgSql Connector Pool
|
/// @brief Common PgSql Connector Pool
|
||||||
///
|
///
|
||||||
/// This class provides common operations for PgSql database connection
|
/// This class provides common operations for PgSql database connection
|
||||||
@@ -218,18 +275,23 @@ public:
|
|||||||
/// @throw DbOpenError Error opening the database
|
/// @throw DbOpenError Error opening the database
|
||||||
void openDatabase();
|
void openDatabase();
|
||||||
|
|
||||||
|
/// @brief Start a transaction
|
||||||
|
///
|
||||||
|
/// Starts a transaction.
|
||||||
|
///
|
||||||
|
/// @throw DbOperationError If the transaction start failed.
|
||||||
|
void startTransaction();
|
||||||
|
|
||||||
/// @brief Commit Transactions
|
/// @brief Commit Transactions
|
||||||
///
|
///
|
||||||
/// Commits all pending database operations. On databases that don't
|
/// Commits all pending database operations.
|
||||||
/// support transactions, this is a no-op.
|
|
||||||
///
|
///
|
||||||
/// @throw DbOperationError If the commit failed.
|
/// @throw DbOperationError If the commit failed.
|
||||||
void commit();
|
void commit();
|
||||||
|
|
||||||
/// @brief Rollback Transactions
|
/// @brief Rollback Transactions
|
||||||
///
|
///
|
||||||
/// Rolls back all pending database operations. On databases that don't
|
/// Rolls back all pending database operations.
|
||||||
/// support transactions, this is a no-op.
|
|
||||||
///
|
///
|
||||||
/// @throw DbOperationError If the rollback failed.
|
/// @throw DbOperationError If the rollback failed.
|
||||||
void rollback();
|
void rollback();
|
||||||
|
@@ -38,10 +38,38 @@ void PsqlBindArray::add(const std::vector<uint8_t>& data) {
|
|||||||
formats_.push_back(BINARY_FMT);
|
formats_.push_back(BINARY_FMT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PsqlBindArray::add(const uint8_t* data, const size_t len) {
|
||||||
|
values_.push_back(reinterpret_cast<const char*>(&(data[0])));
|
||||||
|
lengths_.push_back(len);
|
||||||
|
formats_.push_back(BINARY_FMT);
|
||||||
|
}
|
||||||
|
|
||||||
void PsqlBindArray::add(const bool& value) {
|
void PsqlBindArray::add(const bool& value) {
|
||||||
add(value ? TRUE_STR : FALSE_STR);
|
add(value ? TRUE_STR : FALSE_STR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PsqlBindArray::add(const uint8_t& byte) {
|
||||||
|
// We static_cast to an unsigned int, otherwise lexcial_cast may to
|
||||||
|
// treat byte as a character, which yields "" for unprintable values
|
||||||
|
bindString(boost::lexical_cast<std::string>
|
||||||
|
(static_cast<unsigned int>(byte)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PsqlBindArray::add(const isc::asiolink::IOAddress& addr) {
|
||||||
|
if (addr.isV4()) {
|
||||||
|
bindString(boost::lexical_cast<std::string>
|
||||||
|
(static_cast<uint32_t>(addr)));
|
||||||
|
} else {
|
||||||
|
bindString(addr.toText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eventually this should replace add(std::string)
|
||||||
|
void PsqlBindArray::bindString(const std::string& str) {
|
||||||
|
bound_strs_.push_back(StringPtr(new std::string(str)));
|
||||||
|
PsqlBindArray::add((bound_strs_.back())->c_str());
|
||||||
|
}
|
||||||
|
|
||||||
std::string PsqlBindArray::toText() const {
|
std::string PsqlBindArray::toText() const {
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
for (int i = 0; i < values_.size(); ++i) {
|
for (int i = 0; i < values_.size(); ++i) {
|
||||||
@@ -135,32 +163,6 @@ PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
|
|
||||||
const size_t col, uint32_t &value) const {
|
|
||||||
const char* data = getRawColumnValue(r, row, col);
|
|
||||||
try {
|
|
||||||
value = boost::lexical_cast<uint32_t>(data);
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
isc_throw(DbOperationError, "Invalid uint32_t data: " << data
|
|
||||||
<< " for: " << getColumnLabel(col) << " row:" << row
|
|
||||||
<< " : " << ex.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
|
|
||||||
const size_t col, int32_t &value) const {
|
|
||||||
const char* data = getRawColumnValue(r, row, col);
|
|
||||||
try {
|
|
||||||
value = boost::lexical_cast<int32_t>(data);
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
isc_throw(DbOperationError, "Invalid int32_t data: " << data
|
|
||||||
<< " for: " << getColumnLabel(col) << " row:" << row
|
|
||||||
<< " : " << ex.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
|
PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
|
||||||
const size_t col, uint8_t &value) const {
|
const size_t col, uint8_t &value) const {
|
||||||
|
@@ -7,10 +7,16 @@
|
|||||||
#ifndef PGSQL_EXCHANGE_H
|
#ifndef PGSQL_EXCHANGE_H
|
||||||
#define PGSQL_EXCHANGE_H
|
#define PGSQL_EXCHANGE_H
|
||||||
|
|
||||||
|
#include <asiolink/io_address.h>
|
||||||
#include <dhcpsrv/pgsql_connection.h>
|
#include <dhcpsrv/pgsql_connection.h>
|
||||||
|
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
@@ -24,6 +30,11 @@ namespace dhcp {
|
|||||||
/// be valid for the duration of the PostgreSQL statement execution. In other
|
/// be valid for the duration of the PostgreSQL statement execution. In other
|
||||||
/// words populating them with pointers to values that go out of scope before
|
/// words populating them with pointers to values that go out of scope before
|
||||||
/// statement is executed is a bad idea.
|
/// statement is executed is a bad idea.
|
||||||
|
|
||||||
|
/// @brief smart pointer to strings used by PsqlBindArray to ensure scope
|
||||||
|
/// of strings supplying exchange values
|
||||||
|
typedef boost::shared_ptr<std::string> StringPtr;
|
||||||
|
|
||||||
struct PsqlBindArray {
|
struct PsqlBindArray {
|
||||||
/// @brief Vector of pointers to the data values.
|
/// @brief Vector of pointers to the data values.
|
||||||
std::vector<const char *> values_;
|
std::vector<const char *> values_;
|
||||||
@@ -74,14 +85,27 @@ struct PsqlBindArray {
|
|||||||
/// @param value std::string containing the value to add.
|
/// @param value std::string containing the value to add.
|
||||||
void add(const std::string& value);
|
void add(const std::string& value);
|
||||||
|
|
||||||
/// @brief Adds a binary value to the bind array.
|
/// @brief Adds a vector of binary data to the bind array.
|
||||||
///
|
///
|
||||||
/// Adds a BINARY_FMT value to the end of the bind array using the
|
/// Adds a BINARY_FMT value to the end of the bind array using the
|
||||||
/// given vector as the data source.
|
/// given vector as the data source. NOTE this does not replicate
|
||||||
|
/// the vector, so it must remain in scope until the bind array
|
||||||
|
/// is destroyed.
|
||||||
///
|
///
|
||||||
/// @param data vector of binary bytes.
|
/// @param data vector of binary bytes.
|
||||||
void add(const std::vector<uint8_t>& data);
|
void add(const std::vector<uint8_t>& data);
|
||||||
|
|
||||||
|
/// @brief Adds a buffer of binary data to the bind array.
|
||||||
|
///
|
||||||
|
/// Adds a BINARY_FMT value to the end of the bind array using the
|
||||||
|
/// given vector as the data source. NOTE this does not replicate
|
||||||
|
/// the buffer, so it must remain in scope until the bind array
|
||||||
|
/// is destroyed.
|
||||||
|
///
|
||||||
|
/// @param data buffer of binary data.
|
||||||
|
/// @param len number of bytes of data in buffer
|
||||||
|
void add(const uint8_t* data, const size_t len);
|
||||||
|
|
||||||
/// @brief Adds a boolean value to the bind array.
|
/// @brief Adds a boolean value to the bind array.
|
||||||
///
|
///
|
||||||
/// Converts the given boolean value to its corresponding to PostgreSQL
|
/// Converts the given boolean value to its corresponding to PostgreSQL
|
||||||
@@ -90,11 +114,62 @@ struct PsqlBindArray {
|
|||||||
/// @param value bool value to add.
|
/// @param value bool value to add.
|
||||||
void add(const bool& value);
|
void add(const bool& value);
|
||||||
|
|
||||||
|
/// @brief Adds a uint8_t value to the bind array.
|
||||||
|
///
|
||||||
|
/// Converts the given uint8_t value to its corresponding numeric string
|
||||||
|
/// literal and adds it as a TEXT_FMT value to the bind array.
|
||||||
|
///
|
||||||
|
/// @param value bool value to add.
|
||||||
|
void add(const uint8_t& byte);
|
||||||
|
|
||||||
|
/// @brief Adds a the given IOAddress value to the bind array.
|
||||||
|
///
|
||||||
|
/// Converts the IOAddress, based on its protocol family, to the
|
||||||
|
/// corresponding string literal and adds it as a TEXT_FMT value to
|
||||||
|
/// the bind array.
|
||||||
|
///
|
||||||
|
/// @param value bool value to add.
|
||||||
|
void add(const isc::asiolink::IOAddress& addr);
|
||||||
|
|
||||||
|
/// @brief Adds a the given value to the bind array.
|
||||||
|
///
|
||||||
|
/// Converts the given value its corresponding string literal
|
||||||
|
/// boost::lexical_cast and adds it as a TEXT_FMT value to the bind array.
|
||||||
|
///
|
||||||
|
/// @param value bool value to add.
|
||||||
|
template<typename T>
|
||||||
|
void add(const T& numeric) {
|
||||||
|
bindString(boost::lexical_cast<std::string>(numeric));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Binds a the given string to the bind array.
|
||||||
|
///
|
||||||
|
/// Prior to added the The given string the vector of exchange values,
|
||||||
|
/// it duplicated as a StringPtr and saved internally. This garauntees
|
||||||
|
/// the string remains in scope until the PsqlBindArray is destroyed,
|
||||||
|
/// without the caller maintaining the string values.
|
||||||
|
///
|
||||||
|
/// @param value bool value to add.
|
||||||
|
void bindString(const std::string& str);
|
||||||
|
|
||||||
|
//std::vector<const std::string> getBoundStrs() {
|
||||||
|
std::vector<StringPtr> getBoundStrs() {
|
||||||
|
return (bound_strs_);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Dumps the contents of the array to a string.
|
/// @brief Dumps the contents of the array to a string.
|
||||||
/// @return std::string containing the dump
|
/// @return std::string containing the dump
|
||||||
std::string toText() const;
|
std::string toText() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// @brief vector of strings which supplied the values
|
||||||
|
std::vector<StringPtr> bound_strs_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Defines a smart pointer to PsqlBindArray
|
||||||
|
typedef boost::shared_ptr<PsqlBindArray> PsqlBindArrayPtr;
|
||||||
|
|
||||||
/// @brief Base class for marshalling data to and from PostgreSQL.
|
/// @brief Base class for marshalling data to and from PostgreSQL.
|
||||||
///
|
///
|
||||||
/// Provides the common functionality to set up binding information between
|
/// Provides the common functionality to set up binding information between
|
||||||
@@ -179,30 +254,6 @@ public:
|
|||||||
void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
|
void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
|
||||||
bool &value) const;
|
bool &value) const;
|
||||||
|
|
||||||
/// @brief Fetches an integer text column as a uint32_t.
|
|
||||||
///
|
|
||||||
/// @param r the result set containing the query results
|
|
||||||
/// @param row the row number within the result set
|
|
||||||
/// @param col the column number within the row
|
|
||||||
/// @param[out] value parameter to receive the converted value
|
|
||||||
///
|
|
||||||
/// @throw DbOperationError if the value cannot be fetched or is
|
|
||||||
/// invalid.
|
|
||||||
void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
|
|
||||||
uint32_t &value) const;
|
|
||||||
|
|
||||||
/// @brief Fetches an integer text column as a int32_t.
|
|
||||||
///
|
|
||||||
/// @param r the result set containing the query results
|
|
||||||
/// @param row the row number within the result set
|
|
||||||
/// @param col the column number within the row
|
|
||||||
/// @param[out] value parameter to receive the converted value
|
|
||||||
///
|
|
||||||
/// @throw DbOperationError if the value cannot be fetched or is
|
|
||||||
/// invalid.
|
|
||||||
void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
|
|
||||||
int32_t &value) const;
|
|
||||||
|
|
||||||
/// @brief Fetches an integer text column as a uint8_t.
|
/// @brief Fetches an integer text column as a uint8_t.
|
||||||
///
|
///
|
||||||
/// @param r the result set containing the query results
|
/// @param r the result set containing the query results
|
||||||
@@ -215,6 +266,31 @@ public:
|
|||||||
void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
|
void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
|
||||||
uint8_t &value) const;
|
uint8_t &value) const;
|
||||||
|
|
||||||
|
/// @brief Fetches a text column as the given value type
|
||||||
|
///
|
||||||
|
/// Uses boost::lexicalcast to convert the text column value into
|
||||||
|
/// a value of type T.
|
||||||
|
///
|
||||||
|
/// @param r the result set containing the query results
|
||||||
|
/// @param row the row number within the result set
|
||||||
|
/// @param col the column number within the row
|
||||||
|
/// @param[out] value parameter to receive the converted value
|
||||||
|
///
|
||||||
|
/// @throw DbOperationError if the value cannot be fetched or is
|
||||||
|
/// invalid.
|
||||||
|
template<typename T>
|
||||||
|
void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
|
||||||
|
T& value) const {
|
||||||
|
const char* data = getRawColumnValue(r, row, col);
|
||||||
|
try {
|
||||||
|
value = boost::lexical_cast<T>(data);
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
isc_throw(DbOperationError, "Invalid data: " << data
|
||||||
|
<< " for: " << getColumnLabel(col) << " row:" << row
|
||||||
|
<< " : " << ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Converts a column in a row in a result set to a binary bytes
|
/// @brief Converts a column in a row in a result set to a binary bytes
|
||||||
///
|
///
|
||||||
/// Method is used to convert columns stored as BYTEA into a buffer of
|
/// Method is used to convert columns stored as BYTEA into a buffer of
|
||||||
|
2220
src/lib/dhcpsrv/pgsql_host_data_source.cc
Normal file
2220
src/lib/dhcpsrv/pgsql_host_data_source.cc
Normal file
File diff suppressed because it is too large
Load Diff
255
src/lib/dhcpsrv/pgsql_host_data_source.h
Normal file
255
src/lib/dhcpsrv/pgsql_host_data_source.h
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// 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_HOST_DATA_SOURCE_H
|
||||||
|
#define PGSQL_HOST_DATA_SOURCE_H
|
||||||
|
|
||||||
|
#include <dhcpsrv/base_host_data_source.h>
|
||||||
|
#include <dhcpsrv/pgsql_connection.h>
|
||||||
|
#include <dhcpsrv/pgsql_exchange.h>
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace dhcp {
|
||||||
|
|
||||||
|
/// Forward declaration to the implementation of the @ref PgSqlHostDataSource.
|
||||||
|
class PgSqlHostDataSourceImpl;
|
||||||
|
|
||||||
|
/// @brief PostgreSQL Host Data Source
|
||||||
|
///
|
||||||
|
/// This class implements the @ref isc::dhcp::BaseHostDataSource interface to
|
||||||
|
/// the PostgreSQL database. Use of this backend presupposes that a PostgreSQL
|
||||||
|
/// database is available and that the Kea schema has been created within it.
|
||||||
|
class PgSqlHostDataSource: public BaseHostDataSource {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor
|
||||||
|
///
|
||||||
|
/// Uses the following keywords in the parameters passed to it to
|
||||||
|
/// connect to the database:
|
||||||
|
/// - name - Name of the database to which to connect (mandatory)
|
||||||
|
/// - host - Host to which to connect (optional, defaults to "localhost")
|
||||||
|
/// - user - Username under which to connect (optional)
|
||||||
|
/// - password - Password for "user" on the database (optional)
|
||||||
|
///
|
||||||
|
/// If the database is successfully opened, the version number in the
|
||||||
|
/// schema_version table will be checked against hard-coded value in
|
||||||
|
/// the implementation file.
|
||||||
|
///
|
||||||
|
/// Finally, all the SQL commands are pre-compiled.
|
||||||
|
///
|
||||||
|
/// @param parameters A data structure relating keywords and values
|
||||||
|
/// concerned with the database.
|
||||||
|
///
|
||||||
|
/// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
|
||||||
|
/// @throw isc::dhcp::DbOpenError Error opening the database
|
||||||
|
/// @throw isc::dhcp::DbOperationError An operation on the open database has
|
||||||
|
/// failed.
|
||||||
|
PgSqlHostDataSource(const DatabaseConnection::ParameterMap& parameters);
|
||||||
|
|
||||||
|
/// @brief Virtual destructor.
|
||||||
|
///
|
||||||
|
/// Releases prepared MySQL statements used by the backend.
|
||||||
|
virtual ~PgSqlHostDataSource();
|
||||||
|
|
||||||
|
/// @brief Return all hosts for the specified HW address or DUID.
|
||||||
|
///
|
||||||
|
/// This method returns all @c Host objects which represent reservations
|
||||||
|
/// for the specified HW address or DUID. Note, that this method may
|
||||||
|
/// return multiple reservations because a particular client may have
|
||||||
|
/// reservations in multiple subnets and the same client may be identified
|
||||||
|
/// by HW address or DUID. The server is unable to verify that the specific
|
||||||
|
/// DUID and HW address belong to the same client, until the client sends
|
||||||
|
/// a DHCP message.
|
||||||
|
///
|
||||||
|
/// Specifying both hardware address and DUID is allowed for this method
|
||||||
|
/// and results in returning all objects that are associated with hardware
|
||||||
|
/// address OR duid. For example: if one host is associated with the
|
||||||
|
/// specified hardware address and another host is associated with the
|
||||||
|
/// specified DUID, two hosts will be returned.
|
||||||
|
///
|
||||||
|
/// @param hwaddr HW address of the client or NULL if no HW address
|
||||||
|
/// available.
|
||||||
|
/// @param duid client id or NULL if not available, e.g. DHCPv4 client case.
|
||||||
|
///
|
||||||
|
/// @return Collection of const @c Host objects.
|
||||||
|
virtual ConstHostCollection
|
||||||
|
getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const;
|
||||||
|
|
||||||
|
/// @brief Return all hosts connected to any subnet for which reservations
|
||||||
|
/// have been made using a specified identifier.
|
||||||
|
///
|
||||||
|
/// This method returns all @c Host objects which represent reservations
|
||||||
|
/// for a specified identifier. This method may return multiple hosts
|
||||||
|
/// because a particular client may have reservations in multiple subnets.
|
||||||
|
///
|
||||||
|
/// @param identifier_type Identifier type.
|
||||||
|
/// @param identifier_begin Pointer to a begining of a buffer containing
|
||||||
|
/// an identifier.
|
||||||
|
/// @param identifier_len Identifier length.
|
||||||
|
///
|
||||||
|
/// @return Collection of const @c Host objects.
|
||||||
|
virtual ConstHostCollection
|
||||||
|
getAll(const Host::IdentifierType& identifier_type,
|
||||||
|
const uint8_t* identifier_begin, const size_t identifier_len) const;
|
||||||
|
|
||||||
|
/// @brief Returns a collection of hosts using the specified IPv4 address.
|
||||||
|
///
|
||||||
|
/// This method may return multiple @c Host objects if they are connected
|
||||||
|
/// to different subnets.
|
||||||
|
///
|
||||||
|
/// @param address IPv4 address for which the @c Host object is searched.
|
||||||
|
///
|
||||||
|
/// @return Collection of const @c Host objects.
|
||||||
|
virtual ConstHostCollection
|
||||||
|
getAll4(const asiolink::IOAddress& address) const;
|
||||||
|
|
||||||
|
/// @brief Returns a host connected to the IPv4 subnet.
|
||||||
|
///
|
||||||
|
/// Implementations of this method should guard against the case when
|
||||||
|
/// mutliple instances of the @c Host are present, e.g. when two
|
||||||
|
/// @c Host objects are found, one for the DUID, another one for the
|
||||||
|
/// HW address. In such case, an implementation of this method
|
||||||
|
/// should throw an MultipleRecords exception.
|
||||||
|
///
|
||||||
|
/// @param subnet_id Subnet identifier.
|
||||||
|
/// @param hwaddr HW address of the client or NULL if no HW address
|
||||||
|
/// available.
|
||||||
|
/// @param duid client id or NULL if not available.
|
||||||
|
///
|
||||||
|
/// @return Const @c Host object using a specified HW address or DUID.
|
||||||
|
virtual ConstHostPtr
|
||||||
|
get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
|
||||||
|
const DuidPtr& duid = DuidPtr()) const;
|
||||||
|
|
||||||
|
/// @brief Returns a host connected to the IPv4 subnet.
|
||||||
|
///
|
||||||
|
/// @param subnet_id Subnet identifier.
|
||||||
|
/// @param identifier_type Identifier type.
|
||||||
|
/// @param identifier_begin Pointer to a begining of a buffer containing
|
||||||
|
/// an identifier.
|
||||||
|
/// @param identifier_len Identifier length.
|
||||||
|
///
|
||||||
|
/// @return Const @c Host object for which reservation has been made using
|
||||||
|
/// the specified identifier.
|
||||||
|
virtual ConstHostPtr
|
||||||
|
get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
|
||||||
|
const uint8_t* identifier_begin, const size_t identifier_len) const;
|
||||||
|
|
||||||
|
/// @brief Returns a host connected to the IPv4 subnet and having
|
||||||
|
/// a reservation for a specified IPv4 address.
|
||||||
|
///
|
||||||
|
/// One of the use cases for this method is to detect collisions between
|
||||||
|
/// dynamically allocated addresses and reserved addresses. When the new
|
||||||
|
/// address is assigned to a client, the allocation mechanism should check
|
||||||
|
/// if this address is not reserved for some other host and do not allocate
|
||||||
|
/// this address if reservation is present.
|
||||||
|
///
|
||||||
|
/// Implementations of this method should guard against invalid addresses,
|
||||||
|
/// such as IPv6 address.
|
||||||
|
///
|
||||||
|
/// @param subnet_id Subnet identifier.
|
||||||
|
/// @param address reserved IPv4 address.
|
||||||
|
///
|
||||||
|
/// @return Const @c Host object using a specified IPv4 address.
|
||||||
|
virtual ConstHostPtr
|
||||||
|
get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
|
||||||
|
|
||||||
|
/// @brief Returns a host connected to the IPv6 subnet.
|
||||||
|
///
|
||||||
|
/// Implementations of this method should guard against the case when
|
||||||
|
/// mutliple instances of the @c Host are present, e.g. when two
|
||||||
|
/// @c Host objects are found, one for the DUID, another one for the
|
||||||
|
/// HW address. In such case, an implementation of this method
|
||||||
|
/// should throw an MultipleRecords exception.
|
||||||
|
///
|
||||||
|
/// @param subnet_id Subnet identifier.
|
||||||
|
/// @param hwaddr HW address of the client or NULL if no HW address
|
||||||
|
/// available.
|
||||||
|
/// @param duid DUID or NULL if not available.
|
||||||
|
///
|
||||||
|
/// @return Const @c Host object using a specified HW address or DUID.
|
||||||
|
virtual ConstHostPtr
|
||||||
|
get6(const SubnetID& subnet_id, const DuidPtr& duid,
|
||||||
|
const HWAddrPtr& hwaddr = HWAddrPtr()) const;
|
||||||
|
|
||||||
|
/// @brief Returns a host connected to the IPv6 subnet.
|
||||||
|
///
|
||||||
|
/// @param subnet_id Subnet identifier.
|
||||||
|
/// @param identifier_type Identifier type.
|
||||||
|
/// @param identifier_begin Pointer to a begining of a buffer containing
|
||||||
|
/// an identifier.
|
||||||
|
/// @param identifier_len Identifier length.
|
||||||
|
///
|
||||||
|
/// @return Const @c Host object for which reservation has been made using
|
||||||
|
/// the specified identifier.
|
||||||
|
virtual ConstHostPtr
|
||||||
|
get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
|
||||||
|
const uint8_t* identifier_begin, const size_t identifier_len) const;
|
||||||
|
|
||||||
|
/// @brief Returns a host using the specified IPv6 prefix.
|
||||||
|
///
|
||||||
|
/// @param prefix IPv6 prefix for which the @c Host object is searched.
|
||||||
|
/// @param prefix_len IPv6 prefix length.
|
||||||
|
///
|
||||||
|
/// @return Const @c Host object using a specified HW address or DUID.
|
||||||
|
virtual ConstHostPtr
|
||||||
|
get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
|
||||||
|
|
||||||
|
/// @brief Adds a new host to the collection.
|
||||||
|
///
|
||||||
|
/// The implementations of this method should guard against duplicate
|
||||||
|
/// reservations for the same host, where possible. For example, when the
|
||||||
|
/// reservation for the same HW address and subnet id is added twice, the
|
||||||
|
/// addHost method should throw an DuplicateEntry exception. Note, that
|
||||||
|
/// usually it is impossible to guard against adding duplicated host, where
|
||||||
|
/// one instance is identified by HW address, another one by DUID.
|
||||||
|
///
|
||||||
|
/// @param host Pointer to the new @c Host object being added.
|
||||||
|
virtual void add(const HostPtr& host);
|
||||||
|
|
||||||
|
/// @brief Return backend type
|
||||||
|
///
|
||||||
|
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
|
||||||
|
///
|
||||||
|
/// @return Type of the backend.
|
||||||
|
virtual std::string getType() const {
|
||||||
|
return (std::string("mysql"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns backend name.
|
||||||
|
///
|
||||||
|
/// Each backend have specific name.
|
||||||
|
///
|
||||||
|
/// @return "mysql".
|
||||||
|
virtual std::string getName() const;
|
||||||
|
|
||||||
|
/// @brief Returns description of the backend.
|
||||||
|
///
|
||||||
|
/// This description may be multiline text that describes the backend.
|
||||||
|
///
|
||||||
|
/// @return Description of the backend.
|
||||||
|
virtual std::string getDescription() const;
|
||||||
|
|
||||||
|
/// @brief Returns backend version.
|
||||||
|
///
|
||||||
|
/// @return Version number stored in the database, as a pair of unsigned
|
||||||
|
/// integers. "first" is the major version number, "second" the
|
||||||
|
/// minor number.
|
||||||
|
///
|
||||||
|
/// @throw isc::dhcp::DbOperationError An operation on the open database
|
||||||
|
/// has failed.
|
||||||
|
virtual std::pair<uint32_t, uint32_t> getVersion() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Pointer to the implementation of the @ref PgSqlHostDataSource.
|
||||||
|
PgSqlHostDataSourceImpl* impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PGSQL_HOST_DATA_SOURCE_H
|
@@ -26,6 +26,8 @@ using namespace std;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/// @todo TKM lease6 needs to accomodate hwaddr,hwtype, and hwaddr source columns
|
||||||
|
|
||||||
/// @brief Catalog of all the SQL statements currently supported. Note
|
/// @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 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.
|
/// that the occur in the table. This does not apply to the where clause.
|
||||||
|
@@ -69,5 +69,48 @@ TEST(PgSqlExchangeTest, convertTimeTest) {
|
|||||||
EXPECT_EQ(ref_time, from_time);
|
EXPECT_EQ(ref_time, from_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PsqlBindArray, basicOperation) {
|
||||||
|
|
||||||
|
PsqlBindArray b;
|
||||||
|
|
||||||
|
uint8_t small_int = 25;
|
||||||
|
b.add(small_int);
|
||||||
|
|
||||||
|
int reg_int = 376;
|
||||||
|
b.add(reg_int);
|
||||||
|
|
||||||
|
uint64_t big_int = 86749032;
|
||||||
|
b.add(big_int);
|
||||||
|
|
||||||
|
b.add((bool)(1));
|
||||||
|
b.add((bool)(0));
|
||||||
|
|
||||||
|
b.add(isc::asiolink::IOAddress("192.2.15.34"));
|
||||||
|
b.add(isc::asiolink::IOAddress("3001::1"));
|
||||||
|
|
||||||
|
std::string str("just a string");
|
||||||
|
b.add(str);
|
||||||
|
|
||||||
|
std::vector<uint8_t> bytes;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
bytes.push_back(i+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
b.add(bytes);
|
||||||
|
|
||||||
|
std::string expected =
|
||||||
|
"0 : \"25\"\n"
|
||||||
|
"1 : \"376\"\n"
|
||||||
|
"2 : \"86749032\"\n"
|
||||||
|
"3 : \"TRUE\"\n"
|
||||||
|
"4 : \"FALSE\"\n"
|
||||||
|
"5 : \"3221360418\"\n"
|
||||||
|
"6 : \"3001::1\"\n"
|
||||||
|
"7 : \"just a string\"\n"
|
||||||
|
"8 : 0x010203040506070809\n";
|
||||||
|
|
||||||
|
EXPECT_EQ(expected, b.toText());
|
||||||
|
}
|
||||||
|
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user