using namespace std;
namespace {
+
+// Maximum text representation of an IPv6 address
const size_t ADDRESS6_TEXT_MAX_LEN = 39;
+// Maximum number of parameters used in any signle query
const size_t MAX_PARAMETERS_IN_QUERY = 12;
+// Defines a single query
struct TaggedStatement {
+
+ /// Query index
PgSqlLeaseMgr::StatementIndex index;
+
+ /// Number of parameters for a given query
int nbparams;
+
+ /// @brief OID types
+ ///
+ /// Sspecify 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 + 1];
+
+ /// Short name of the query.
const char* name;
+
+ /// Text represenation of the actual query.
const char* text;
};
TaggedStatement tagged_statements[] = {
{PgSqlLeaseMgr::DELETE_LEASE4, 1,
- { 20 },
- "delete_lease4",
- "DELETE FROM lease4 WHERE address = $1"},
+ { 20 },
+ "delete_lease4",
+ "DELETE FROM lease4 WHERE address = $1"},
{PgSqlLeaseMgr::DELETE_LEASE6, 1,
- { 1043 },
- "delete_lease6",
- "DELETE FROM lease6 WHERE address = $1"},
+ { 1043 },
+ "delete_lease6",
+ "DELETE FROM lease6 WHERE address = $1"},
{PgSqlLeaseMgr::GET_LEASE4_ADDR, 1,
- { 20 },
- "get_lease4_addr",
- "DECLARE get_lease4_addr CURSOR FOR "
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, extract(epoch from expire), subnet_id, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE address = $1"},
+ { 20 },
+ "get_lease4_addr",
+ "DECLARE get_lease4_addr CURSOR FOR "
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, extract(epoch from expire), subnet_id, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE address = $1"},
{PgSqlLeaseMgr::GET_LEASE4_CLIENTID, 1,
- { 17 },
- "get_lease4_clientid",
- "DECLARE get_lease4_clientid CURSOR FOR "
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, extract(epoch from expire), subnet_id, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE client_id = $1"},
+ { 17 },
+ "get_lease4_clientid",
+ "DECLARE get_lease4_clientid CURSOR FOR "
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, extract(epoch from expire), subnet_id, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE client_id = $1"},
{PgSqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID, 2,
- { 17, 20 },
- "get_lease4_clientid_subid",
- "DECLARE get_lease4_clientid_subid CURSOR FOR "
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE client_id = $1 AND subnet_id = $2"},
+ { 17, 20 },
+ "get_lease4_clientid_subid",
+ "DECLARE get_lease4_clientid_subid CURSOR FOR "
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE client_id = $1 AND subnet_id = $2"},
{PgSqlLeaseMgr::GET_LEASE4_HWADDR, 1,
- { 17 },
- "get_lease4_hwaddr",
- "DECLARE get_lease4_hwaddr CURSOR FOR "
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE hwaddr = $1"},
+ { 17 },
+ "get_lease4_hwaddr",
+ "DECLARE get_lease4_hwaddr CURSOR FOR "
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE hwaddr = $1"},
{PgSqlLeaseMgr::GET_LEASE4_HWADDR_SUBID, 2,
- { 17, 20 },
- "get_lease4_hwaddr_subid",
- "DECLARE get_lease4_hwaddr_subid CURSOR FOR "
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE hwaddr = $1 AND subnet_id = $2"},
+ { 17, 20 },
+ "get_lease4_hwaddr_subid",
+ "DECLARE get_lease4_hwaddr_subid CURSOR FOR "
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE hwaddr = $1 AND subnet_id = $2"},
{PgSqlLeaseMgr::GET_LEASE6_ADDR, 2,
- { 1043, 21 },
- "get_lease6_addr",
- "DECLARE get_lease6_addr CURSOR FOR "
- "SELECT address, duid, valid_lifetime, "
- "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease6 "
- "WHERE address = $1 AND lease_type = $2"},
+ { 1043, 21 },
+ "get_lease6_addr",
+ "DECLARE get_lease6_addr CURSOR FOR "
+ "SELECT address, duid, valid_lifetime, "
+ "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease6 "
+ "WHERE address = $1 AND lease_type = $2"},
{PgSqlLeaseMgr::GET_LEASE6_DUID_IAID, 3,
- { 17, 20, 21 },
- "get_lease6_duid_iaid",
- "DECLARE get_lease6_duid_iaid CURSOR FOR "
- "SELECT address, duid, valid_lifetime, "
- "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease6 "
- "WHERE duid = $1 AND iaid = $2 AND lease_type = $3"},
+ { 17, 20, 21 },
+ "get_lease6_duid_iaid",
+ "DECLARE get_lease6_duid_iaid CURSOR FOR "
+ "SELECT address, duid, valid_lifetime, "
+ "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease6 "
+ "WHERE duid = $1 AND iaid = $2 AND lease_type = $3"},
{PgSqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID, 4,
- { 21, 17, 20, 20 },
- "get_lease6_duid_iaid_subid",
- "DECLARE get_lease6_duid_iaid_subid CURSOR FOR "
- "SELECT address, duid, valid_lifetime, "
- "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
- "FROM lease6 "
- "WHERE lease_type = $1 AND duid = $2 AND iaid = $3 AND subnet_id = $4"},
+ { 21, 17, 20, 20 },
+ "get_lease6_duid_iaid_subid",
+ "DECLARE get_lease6_duid_iaid_subid CURSOR FOR "
+ "SELECT address, duid, valid_lifetime, "
+ "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease6 "
+ "WHERE lease_type = $1 AND duid = $2 AND iaid = $3 AND subnet_id = $4"},
{PgSqlLeaseMgr::GET_VERSION, 0,
- { 0 },
- "get_version",
- "DECLARE get_version CURSOR FOR "
- "SELECT version, minor FROM schema_version"},
+ { 0 },
+ "get_version",
+ "DECLARE get_version CURSOR FOR "
+ "SELECT version, minor FROM schema_version"},
{PgSqlLeaseMgr::INSERT_LEASE4, 9,
- { 20, 17, 17, 20, 1114, 20, 16, 16, 1043 },
- "insert_lease4",
- "INSERT INTO lease4(address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname) "
- "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"},
+ { 20, 17, 17, 20, 1114, 20, 16, 16, 1043 },
+ "insert_lease4",
+ "INSERT INTO lease4(address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname) "
+ "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"},
{PgSqlLeaseMgr::INSERT_LEASE6, 12,
- { 1043, 17, 20, 1114, 20, 20, 21, 20, 21, 16, 16, 1043 },
- "insert_lease6",
- "INSERT INTO lease6(address, duid, valid_lifetime, "
- "expire, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname) "
- "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)"},
+ { 1043, 17, 20, 1114, 20, 20, 21, 20, 21, 16, 16, 1043 },
+ "insert_lease6",
+ "INSERT INTO lease6(address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname) "
+ "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)"},
{PgSqlLeaseMgr::UPDATE_LEASE4, 10,
- { 20, 17, 17, 20, 1114, 20, 16, 16, 1043, 20 },
- "update_lease4",
- "UPDATE lease4 SET address = $1, hwaddr = $2, "
- "client_id = $3, valid_lifetime = $4, expire = $5, "
- "subnet_id = $6, fqdn_fwd = $7, fqdn_rev = $8, hostname = $9 "
- "WHERE address = $10"},
+ { 20, 17, 17, 20, 1114, 20, 16, 16, 1043, 20 },
+ "update_lease4",
+ "UPDATE lease4 SET address = $1, hwaddr = $2, "
+ "client_id = $3, valid_lifetime = $4, expire = $5, "
+ "subnet_id = $6, fqdn_fwd = $7, fqdn_rev = $8, hostname = $9 "
+ "WHERE address = $10"},
{PgSqlLeaseMgr::UPDATE_LEASE6, 13,
- { 1043, 17, 20, 1114, 20, 20, 21, 20, 21, 16, 16, 1043, 1043 },
- "update_lease6",
- "UPDATE lease6 SET address = $1, duid = $2, "
- "valid_lifetime = $3, expire = $4, subnet_id = $5, "
- "pref_lifetime = $6, lease_type = $7, iaid = $8, "
- "prefix_len = $9, fqdn_fwd = $10, fqdn_rev = $11, hostname = $12 "
- "WHERE address = $13"},
+ { 1043, 17, 20, 1114, 20, 20, 21, 20, 21, 16, 16, 1043, 1043 },
+ "update_lease6",
+ "UPDATE lease6 SET address = $1, duid = $2, "
+ "valid_lifetime = $3, expire = $4, subnet_id = $5, "
+ "pref_lifetime = $6, lease_type = $7, iaid = $8, "
+ "prefix_len = $9, fqdn_fwd = $10, fqdn_rev = $11, hostname = $12 "
+ "WHERE address = $13"},
+
// End of list sentinel
{PgSqlLeaseMgr::NUM_STATEMENTS, 0, { 0 }, NULL, NULL}
};
namespace isc {
namespace dhcp {
+/// @brief Auxiliary PostgreSQL exchange class
class PgSqlLeaseExchange {
protected:
- bindparams params;
+
+ /// Converts time_t structure to a text representation
+ /// @param expire timestamp to be converted
+ /// @param buffer text version will be written here
void
- convertToTimestamp(const time_t & expire_,
- char buffer_[20]) {
+ convertToTimestamp(const time_t& expire, char buffer[20]) {
struct tm tinfo;
- localtime_r(&expire_, &tinfo);
- strftime(buffer_, 20, "%Y-%m-%d %H:%M:%S", &tinfo);
+ localtime_r(&expire, &tinfo);
+ strftime(buffer, 20, "%Y-%m-%d %H:%M:%S", &tinfo);
}
+ /// Converts available text representations to bool
+ ///
+ /// Allowed values are "t" or "f". Any other will throw.
+ /// @param value text value to be converted
+ /// @throw BadValue if passed any value other than "t" or "f"
bool stringToBool(char* value) {
if (!strlen(value)) {
return (false);
" only accepted values are 't', 'f' or ''");
}
}
+
+ /// Compiled statement bind parameters
+ bindparams params;
};
+/// @brief Represents a single Lease4 exchange
class PgSqlLease4Exchange : public PgSqlLeaseExchange {
static const size_t LEASE_COLUMNS = 9;
public:
+
+ /// Default constructor
PgSqlLease4Exchange() : addr4_(0) {
memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
<< HWAddr::MAX_HWADDR_LEN);
}
- PgSqlParam pdest = { .value = string(lease_->hwaddr_.begin(), lease_->hwaddr_.end()),
+ PgSqlParam pdest = { .value = string(lease_->hwaddr_.begin(),
+ lease_->hwaddr_.end()),
.isbinary = 1,
.binarylen = static_cast<int>(lease_->hwaddr_.size()) };
params.push_back(pdest);
Lease4Ptr
convertFromDatabase(PGresult *& r, int line) {
- const char * addr4_str = PQgetvalue(r, line, 0);
- unsigned char * hwaddr_str = PQunescapeBytea(
- (const unsigned char *)
- PQgetvalue(r, line, 1),
- & hwaddr_length_);
- unsigned char * client_id_str = PQunescapeBytea(
- (const unsigned char *)
- PQgetvalue(r, line, 2),
- & client_id_length_);
- const char * valid_lifetime_str = PQgetvalue(r, line, 3);
- const char * expire_str = PQgetvalue(r, line, 4);
- const char * subnet_id_str = PQgetvalue(r, line, 5);
+ const char* addr4_str = PQgetvalue(r, line, 0);
+ unsigned char* hwaddr_str = PQunescapeBytea((const unsigned char *)
+ PQgetvalue(r, line, 1),
+ & hwaddr_length_);
+ unsigned char* client_id_str = PQunescapeBytea((const unsigned char *)
+ PQgetvalue(r, line, 2),
+ & client_id_length_);
+ const char* valid_lifetime_str = PQgetvalue(r, line, 3);
+ const char* expire_str = PQgetvalue(r, line, 4);
+ const char* subnet_id_str = PQgetvalue(r, line, 5);
unsigned long valid_lifetime, expire, subnet_id;
istringstream tmp;
Lease6Ptr
convertFromDatabase(PGresult * r, int line) {
- const char * addr6_str = PQgetvalue(r, line, 0);
- unsigned char * duid_str = PQunescapeBytea(
- (const unsigned char *)
- PQgetvalue(r, line, 1),
- & duid_length_);
- const char * valid_lifetime_str = PQgetvalue(r, line, 2);
- const char * expire_str = PQgetvalue(r, line, 3);
- const char * subnet_id_str = PQgetvalue(r, line, 4);
- const char * pref_lifetime_str = PQgetvalue(r, line, 5);
- const char * lease_type_str = PQgetvalue(r, line, 6);
- const char * iaid_str = PQgetvalue(r, line, 7);
- const char * prefixlen_str = PQgetvalue(r, line, 8);
+ const char* addr6_str = PQgetvalue(r, line, 0);
+ unsigned char* duid_str = PQunescapeBytea((const unsigned char *)
+ PQgetvalue(r, line, 1),
+ & duid_length_);
+ const char* valid_lifetime_str = PQgetvalue(r, line, 2);
+ const char* expire_str = PQgetvalue(r, line, 3);
+ const char* subnet_id_str = PQgetvalue(r, line, 4);
+ const char* pref_lifetime_str = PQgetvalue(r, line, 5);
+ const char* lease_type_str = PQgetvalue(r, line, 6);
+ const char* iaid_str = PQgetvalue(r, line, 7);
+ const char* prefixlen_str = PQgetvalue(r, line, 8);
unsigned int lease_type, prefixlen;
unsigned long valid_lifetime, expire, subnet_id, pref_lifetime, iaid;
private:
string addr6_;
- // char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
string columns_[LEASE_COLUMNS];
time_t expire_;
vector<uint8_t> duid_;
}
PgSqlLeaseMgr::~PgSqlLeaseMgr() {
- if(status) {
+ if (status) {
// Attempt to deallocate prepared queries set previously with DEALLOCATE query
// No internal libpq function for that, no errors checking as well
- PGresult * r;
+ PGresult* r = NULL;
for(int i = 0; tagged_statements[i].text != NULL; ++ i) {
string deallocate = "DEALLOCATE \"";
deallocate += tagged_statements[i].name;
bool
PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
- bindparams & params) {
+ bindparams& params) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_PGSQL_ADD_ADDR4).arg(statements_[stindex].stmt_name);
- vector<const char *> params_;
- vector<int> lengths_;
- vector<int> formats_;
- convertToQuery(params, params_, lengths_, formats_);
+ vector<const char *> out_values;
+ vector<int> out_lengths;
+ vector<int> out_formats;
+ convertToQuery(params, out_values, out_lengths, out_formats);
PGresult * r = PQexecPrepared(status, statements_[stindex].stmt_name,
statements_[stindex].stmt_nbparams,
- ¶ms_[0], &lengths_[0], &formats_[0], 0);
+ &out_values[0], &out_lengths[0], &out_formats[0], 0);
int s = PQresultStatus(r);
if (s != PGRES_COMMAND_OK) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_PGSQL_GET_ADDR4).arg(statements_[stindex].stmt_name);
- vector<const char *> params_;
- vector<int> lengths_;
- vector<int> formats_;
- convertToQuery(params, params_, lengths_, formats_);
+ vector<const char *> out_values;
+ vector<int> out_lengths;
+ vector<int> out_formats;
+ convertToQuery(params, out_values, out_lengths, out_formats);
PGresult * r = PQexec(status, "SET AUTOCOMMIT TO OFF");
PQclear(r);
PQclear(r);
r = PQexecPrepared(status, statements_[stindex].stmt_name,
- statements_[stindex].stmt_nbparams,
- ¶ms_[0], &lengths_[0], &formats_[0], 0);
+ statements_[stindex].stmt_nbparams, &out_values[0],
+ &out_lengths[0], &out_formats[0], 0);
checkError(r, stindex, "unable to bind WHERE clause parameter");
PQclear(r);
if (!hwaddr.hwaddr_.empty()) {
uint8_t* data = const_cast<uint8_t *>(&hwaddr.hwaddr_[0]);
- PgSqlParam pdest = { .value = reinterpret_cast<char *>(data)
- , .isbinary = 1, .binarylen = static_cast<int>(hwaddr.hwaddr_.size()) };
+ PgSqlParam pdest = { .value = reinterpret_cast<char *>(data),
+ .isbinary = 1,
+ .binarylen = static_cast<int>(hwaddr.hwaddr_.size()) };
inparams.push_back(pdest);
} else {
inparams.push_back(PgSqlParam());
bindparams inparams;
vector<uint8_t> client_data = clientid.getClientId();
- PgSqlParam pdest = { .value = reinterpret_cast<char *>(&client_data[0])
- , .isbinary = 1, .binarylen = static_cast<int>(clientid.getClientId().size()) };
+ PgSqlParam pdest = { .value = reinterpret_cast<char *>(&client_data[0]),
+ .isbinary = 1,
+ .binarylen = static_cast<int>(clientid.getClientId().size()) };
inparams.push_back(pdest);
// Get the data
ostringstream tmp;
vector<uint8_t> client_data = clientid.getClientId();
- PgSqlParam pdest = { .value = reinterpret_cast<char *>(&client_data[0])
- , .isbinary = 1, .binarylen = static_cast<int>(clientid.getClientId().size()) };
+ PgSqlParam pdest = { .value = reinterpret_cast<char *>(&client_data[0]),
+ .isbinary = 1,
+ .binarylen = static_cast<int>(clientid.getClientId().size()) };
inparams.push_back(pdest);
tmp << static_cast<unsigned long>(subnet_id);
bindparams inparams;
ostringstream tmp;
vector<uint8_t> duid_data = duid.getDuid();
- PgSqlParam pdest = { .value = reinterpret_cast<char *>(&duid_data[0])
- , .isbinary = 1, .binarylen = static_cast<int>(duid.getDuid().size()) };
+ PgSqlParam pdest = { .value = reinterpret_cast<char *>(&duid_data[0]),
+ .isbinary = 1,
+ .binarylen = static_cast<int>(duid.getDuid().size()) };
inparams.push_back(pdest);
/// @todo: use type
return (name);
}
+void
+PgSqlLeaseMgr::checkError(PGresult* r, StatementIndex index,
+ const char* what) const {
+ int s = PQresultStatus(r);
+ if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
+ PQclear(r);
+
+ isc_throw(DbOperationError, what << " for <" <<
+ statements_[index].stmt_name << ">, " <<
+ PQerrorMessage(status));
+ }
+}
+
+inline void
+PgSqlLeaseMgr::convertToQuery(const bindparams& params,
+ std::vector<const char *>& out_values,
+ std::vector<int>& out_lengths,
+ std::vector<int>& out_formats) const {
+ out_values.reserve(params.size());
+ out_lengths.reserve(params.size());
+ out_formats.reserve(params.size());
+
+ for(bindparams::const_iterator it = params.begin(); it != params.end();
+ ++it) {
+ out_values.push_back((* it).value.c_str());
+ out_lengths.push_back((* it).binarylen);
+ out_formats.push_back((* it).isbinary);
+ }
+}
+
string
PgSqlLeaseMgr::getDescription() const {
return (string("PostgreSQL Database"));
namespace isc {
namespace dhcp {
+/// @brief An auxiliary structure for marshalling data for compiled statements
+///
+/// It represents a single field used in a query (e.g. one field used in WHERE
+/// or UPDATE clauses).
struct PgSqlParam {
- std::string value;
- int isbinary;
- int binarylen;
+ std::string value; ///< The actual value represented as text
+ bool isbinary; ///< Boolean flag that controls whether the data is binary
+ int binarylen; ///< Specified binary length
};
+/// @brief Defines all parameters for binding a compiled statement
typedef std::vector<PgSqlParam> bindparams;
+/// @brief Describes a single compiled statement
struct PgSqlStatementBind {
- const char * stmt_name;
- int stmt_nbparams;
+ const char* stmt_name; ///< Name of the compiled statement
+ int stmt_nbparams; ///< Number of statement parameters
};
+// Forward definitions (needed for shared_ptr definitions)
+// See pgsql_lease_mgr.cc file for actual class definitions
class PgSqlLease4Exchange;
class PgSqlLease6Exchange;
+/// Defines PostgreSQL backend version: 1.0
const uint32_t PG_CURRENT_VERSION = 1;
const uint32_t PG_CURRENT_MINOR = 0;
+/// @brief PostgreSQL Lease Manager
+///
+/// This class provides the \ref isc::dhcp::LeaseMgr 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 PgSqlLeaseMgr : public LeaseMgr {
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.
PgSqlLeaseMgr(const ParameterMap& parameters);
+
+ /// @brief Destructor (closes database)
virtual ~PgSqlLeaseMgr();
+
+ /// @brief Adds an IPv4 lease
+ ///
+ /// @param lease lease to be added
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual bool addLease(const Lease4Ptr& lease);
+
+ /// @brief Adds an IPv6 lease
+ ///
+ /// @param lease lease to be added
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual bool addLease(const Lease6Ptr& lease);
+ /// @brief Returns an IPv4 lease for specified IPv4 address
+ ///
+ /// This method return a lease that is associated with a given address.
+ /// For other query types (by hardware addr, by Client ID) there can be
+ /// several leases in different subnets (e.g. for mobile clients that
+ /// got address in different subnets). However, for a single address
+ /// there can be only one lease, so this method returns a pointer to
+ /// a single lease, not a container of leases.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address.
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param hwaddr hardware address of the client
+ ///
+ /// @return lease collection
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address
+ /// and a subnet
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param hwaddr hardware address of the client
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
SubnetID subnet_id) const;
+
+ /// @brief Returns existing IPv4 leases for specified client-id
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param clientid client identifier
+ ///
+ /// @return lease collection
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Collection getLease4(const ClientId& clientid) const;
+
+ /// @brief Returns IPv4 lease for the specified client identifier, HW
+ /// address and subnet identifier.
+ ///
+ /// @param client_id A client identifier.
+ /// @param hwaddr Hardware address.
+ /// @param subnet_id A subnet identifier.
+ ///
+ /// @return A pointer to the lease or NULL if the lease is not found.
+ /// @throw isc::NotImplemented On every call as this function is currently
+ /// not implemented for the MySQL backend.
virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
SubnetID subnet_id) const;
+
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param clientid client identifier
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Ptr getLease4(const ClientId& clientid,
SubnetID subnet_id) const;
+ /// @brief Returns existing IPv6 lease for a given IPv6 address.
+ ///
+ /// For a given address, we assume that there will be only one lease.
+ /// The assumption here is that there will not be site or link-local
+ /// addresses used, so there is no way of having address duplication.
+ ///
+ /// @param type specifies lease type: (NA, TA or PD)
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::BadValue record retrieved from database had an invalid
+ /// lease type field.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease6Ptr getLease6(Lease::Type type,
const isc::asiolink::IOAddress& addr) const;
+
+ /// @brief Returns existing IPv6 leases for a given DUID+IA combination
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param type specifies lease type: (NA, TA or PD)
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::BadValue record retrieved from database had an invalid
+ /// lease type field.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
uint32_t iaid) const;
+
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param type specifies lease type: (NA, TA or PD)
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ /// @param subnet_id subnet id of the subnet the lease belongs to
+ ///
+ /// @return lease collection (may be empty if no lease is found)
+ ///
+ /// @throw isc::BadValue record retrieved from database had an invalid
+ /// lease type field.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
uint32_t iaid, SubnetID subnet_id) const;
+ /// @brief Updates IPv4 lease.
+ ///
+ /// Updates the record of the lease in the database (as identified by the
+ /// address) with the data in the passed lease object.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+ /// exist.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual void updateLease4(const Lease4Ptr& lease4);
+
+ /// @brief Updates IPv6 lease.
+ ///
+ /// Updates the record of the lease in the database (as identified by the
+ /// address) with the data in the passed lease object.
+ ///
+ /// @param lease6 The lease to be updated.
+ ///
+ /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+ /// exist.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual void updateLease6(const Lease6Ptr& lease6);
+
+ /// @brief Deletes a lease.
+ ///
+ /// @param addr Address of the lease to be deleted. This can be an IPv4
+ /// address or an IPv6 address.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
+
+ /// @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("postgresql"));
}
+
+ /// @brief Returns name of the database.
+ ///
+ /// @return database name
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 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;
+
+ /// @brief Commit Transactions
+ ///
+ /// Commits all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ ///
+ /// @throw DbOperationError Iif the commit failed.
virtual 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.
virtual void rollback();
+
+ /// @brief Statement Tags
+ ///
+ /// The contents of the enum are indexes into the list of compiled SQL statements
enum StatementIndex {
DELETE_LEASE4, // Delete from lease4 by address
DELETE_LEASE6, // Delete from lease6 by address
UPDATE_LEASE6, // Update a Lease6 entry
NUM_STATEMENTS // Number of statements
};
+
private:
+
+ /// @brief Prepare statements
+ ///
+ /// Creates the prepared statements for all of the SQL statements used
+ /// by the PostgreSQL backend.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::InvalidParameter 'index' is not valid for the vector. This
+ /// represents an internal error within the code.
void prepareStatements();
+
+ /// @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();
- bool addLeaseCommon(StatementIndex stindex, bindparams & params);
+
+ /// @brief Add Lease Common Code
+ ///
+ /// This method performs the common actions for both flavours (V4 and V6)
+ /// of the addLease method. It binds the contents of the lease object to
+ /// the prepared statement and adds it to the database.
+ ///
+ /// @param stindex Index of statemnent being executed
+ /// @param bind MYSQL_BIND array that has been created for the type
+ /// of lease in question.
+ ///
+ /// @return true if the lease was added, false if it was not added because
+ /// a lease with that address already exists in the database.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ bool addLeaseCommon(StatementIndex stindex, bindparams& params);
+
+ /// @brief Get Lease Collection Common Code
+ ///
+ /// This method performs the common actions for obtaining multiple leases
+ /// from the database.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param params PostgreSQL parameters for the query
+ /// @param exchange Exchange object to use
+ /// @param result Returned collection of Leases Note that any leases in
+ /// the collection when this method is called are not erased: the
+ /// new data is appended to the end.
+ /// @param single If true, only a single data item is to be retrieved.
+ /// If more than one is present, a MultipleRecords exception will
+ /// be thrown.
+ ///
+ /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+ /// from the database where only one was expected.
template <typename Exchange, typename LeaseCollection>
- void getLeaseCollection(StatementIndex stindex, bindparams & params,
+ void getLeaseCollection(StatementIndex stindex, bindparams& params,
Exchange& exchange, LeaseCollection& result,
bool single = false) const;
- void getLeaseCollection(StatementIndex stindex, bindparams & params,
+
+ /// @brief Gets Lease4 Collection
+ ///
+ /// Gets a collection of Lease4 objects. This is just an interface to
+ /// the get lease collection common code.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param params PostgreSQL parameters for the query
+ /// @param lease LeaseCollection object returned. Note that any leases in
+ /// the collection when this method is called are not erased: the
+ /// new data is appended to the end.
+ ///
+ /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+ /// from the database where only one was expected.
+ void getLeaseCollection(StatementIndex stindex, bindparams& params,
Lease4Collection& result) const {
getLeaseCollection(stindex, params, exchange4_, result);
}
- void getLeaseCollection(StatementIndex stindex, bindparams & params,
+
+ /// @brief Get Lease6 Collection
+ ///
+ /// Gets a collection of Lease6 objects. This is just an interface to
+ /// the get lease collection common code.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param params PostgreSQL parameters for the query
+ /// @param lease LeaseCollection object returned. Note that any existing
+ /// data in the collection is erased first.
+ ///
+ /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+ /// from the database where only one was expected.
+ void getLeaseCollection(StatementIndex stindex, bindparams& params,
Lease6Collection& result) const {
getLeaseCollection(stindex, params, exchange6_, result);
}
- inline void checkError(PGresult * r, StatementIndex index,
- const char* what) const {
- int s = PQresultStatus(r);
- if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
- PQclear(r);
-
- isc_throw(DbOperationError, what << " for <" <<
- statements_[index].stmt_name << ">, " <<
- PQerrorMessage(status));
- }
- }
- inline void convertToQuery(bindparams & params,
- std::vector<const char *>& params_,
- std::vector<int>& lengths_,
- std::vector<int>& formats_) const {
- params_.reserve(params.size());
- lengths_.reserve(params.size());
- formats_.reserve(params.size());
-
- for(bindparams::const_iterator it = params.begin(); it != params.end();
- ++ it) {
- params_.push_back((* it).value.c_str());
- lengths_.push_back((* it).binarylen);
- formats_.push_back((* it).isbinary);
- }
- }
- void getLease(StatementIndex stindex, bindparams & params,
+
+ /// @brief Checks result of the r object
+ ///
+ /// Checks status of the operation passed as first argument and throws
+ /// DbOperationError with details if it is non-success.
+ ///
+ /// @param r result of the last PostgreSQL operation
+ /// @param index will be used to print out compiled statement name
+ /// @param what operation that cause the error
+ ///
+ /// @throw isc::dhcp::DbOperationError Detailed PostgreSQL failure
+ inline void checkError(PGresult* r, StatementIndex index, const char* what) const;
+
+ /// @brief Converts query parameters to format accepted by PostgreSQL
+ ///
+ /// Converts parameters stored in params into 3 vectors: out_params,
+ /// out_lengths and out_formats.
+ /// @param params input parameters
+ /// @param out_values [out] values of specified parameters
+ /// @param out_lengths [out] lengths of specified values
+ /// @param out_formats [out] specifies format (text (0) or binary (1))
+ inline void convertToQuery(const bindparams& params,
+ std::vector<const char *>& out_values,
+ std::vector<int>& out_lengths,
+ std::vector<int>& out_formats) const;
+
+ /// @brief Get Lease4 Common Code
+ ///
+ /// This method performs the common actions for the various getLease4()
+ /// methods. It acts as an interface to the getLeaseCollection() method,
+ /// but retrieveing only a single lease.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param bindparams PostgreSQL array for input parameters
+ /// @param lease Lease4 object returned
+ void getLease(StatementIndex stindex, bindparams& params,
Lease4Ptr& result) const;
- void getLease(StatementIndex stindex, bindparams & params,
+
+ /// @brief Get Lease6 Common Code
+ ///
+ /// This method performs the common actions for the various getLease4()
+ /// methods. It acts as an interface to the getLeaseCollection() method,
+ /// but retrieveing only a single lease.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param bindparams PostgreSQL array for input parameters
+ /// @param lease Lease6 object returned
+ void getLease(StatementIndex stindex, bindparams& params,
Lease6Ptr& result) const;
+
+
+ /// @brief Update lease common code
+ ///
+ /// Holds the common code for updating a lease. It binds the parameters
+ /// to the prepared statement, executes it, then checks how many rows
+ /// were affected.
+ ///
+ /// @param stindex Index of prepared statement to be executed
+ /// @param bindparams Array of PostgreSQL objects representing the parameters.
+ /// (Note that the number is determined by the number of parameters
+ /// in the statement.)
+ /// @param lease Pointer to the lease object whose record is being updated.
+ ///
+ /// @throw NoSuchLease Could not update a lease because no lease matches
+ /// the address given.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
template <typename LeasePtr>
- void updateLeaseCommon(StatementIndex stindex, bindparams & params,
+ void updateLeaseCommon(StatementIndex stindex, bindparams& params,
const LeasePtr& lease);
- bool deleteLeaseCommon(StatementIndex stindex, bindparams & params);
- boost::scoped_ptr<PgSqlLease4Exchange> exchange4_;
- boost::scoped_ptr<PgSqlLease6Exchange> exchange6_;
+ /// @brief Delete lease common code
+ ///
+ /// Holds the common code for deleting a lease. It binds the parameters
+ /// to the prepared statement, executes the statement and checks to
+ /// see how many rows were deleted.
+ ///
+ /// @param stindex Index of prepared statement to be executed
+ /// @param bindparams Array of PostgreSQL objects representing the parameters.
+ /// (Note that the number is determined by the number of parameters
+ /// in the statement.)
+ ///
+ /// @return true if one or more rows were deleted, false if none were
+ /// deleted.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ bool deleteLeaseCommon(StatementIndex stindex, bindparams& params);
+
+ /// The exchange objects are used for transfer of data to/from the database.
+ /// They are pointed-to objects as the contents may change in "const" calls,
+ /// while the rest of this object does not. (At alternative would be to
+ /// declare them as "mutable".)
+ boost::scoped_ptr<PgSqlLease4Exchange> exchange4_; ///< Exchange object
+ boost::scoped_ptr<PgSqlLease6Exchange> exchange6_; ///< Exchange object
+
+ /// A vector of compiled SQL statements
std::vector<PgSqlStatementBind> statements_;
- PGconn * status;
+
+ /// PostgreSQL connection handle
+ PGconn* status;
};
}; // end of isc::dhcp namespace