From: Tomek Mrugalski Date: Fri, 14 Mar 2014 18:44:40 +0000 (+0100) Subject: [3080] Many comments added for PostgreSQL backend X-Git-Tag: bind10-1.2.0rc1-release~23^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f496ea9a139b6d48277bddc1d63cbab652f40b0e;p=thirdparty%2Fkea.git [3080] Many comments added for PostgreSQL backend --- diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index b4c65d5243..2702236b71 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -33,127 +33,146 @@ using namespace isc::dhcp; 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} }; @@ -163,17 +182,25 @@ TaggedStatement tagged_statements[] = { 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); @@ -188,11 +215,17 @@ protected: " 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_)); @@ -234,7 +267,8 @@ public: << 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(lease_->hwaddr_.size()) }; params.push_back(pdest); @@ -283,18 +317,16 @@ public: 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; @@ -452,18 +484,17 @@ public: 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; @@ -559,7 +590,6 @@ public: private: string addr6_; - // char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1]; string columns_[LEASE_COLUMNS]; time_t expire_; vector duid_; @@ -585,10 +615,10 @@ PgSqlLeaseMgr::PgSqlLeaseMgr(const LeaseMgr::ParameterMap& parameters) } 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; @@ -694,19 +724,19 @@ PgSqlLeaseMgr::openDatabase() { 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 params_; - vector lengths_; - vector formats_; - convertToQuery(params, params_, lengths_, formats_); + vector out_values; + vector out_lengths; + vector 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) { @@ -755,10 +785,10 @@ void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex, LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_ADDR4).arg(statements_[stindex].stmt_name); - vector params_; - vector lengths_; - vector formats_; - convertToQuery(params, params_, lengths_, formats_); + vector out_values; + vector out_lengths; + vector out_formats; + convertToQuery(params, out_values, out_lengths, out_formats); PGresult * r = PQexec(status, "SET AUTOCOMMIT TO OFF"); PQclear(r); @@ -767,8 +797,8 @@ void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex, 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); @@ -878,8 +908,9 @@ PgSqlLeaseMgr::getLease4(const HWAddr& hwaddr) const { if (!hwaddr.hwaddr_.empty()) { uint8_t* data = const_cast(&hwaddr.hwaddr_[0]); - PgSqlParam pdest = { .value = reinterpret_cast(data) - , .isbinary = 1, .binarylen = static_cast(hwaddr.hwaddr_.size()) }; + PgSqlParam pdest = { .value = reinterpret_cast(data), + .isbinary = 1, + .binarylen = static_cast(hwaddr.hwaddr_.size()) }; inparams.push_back(pdest); } else { inparams.push_back(PgSqlParam()); @@ -928,8 +959,9 @@ PgSqlLeaseMgr::getLease4(const ClientId& clientid) const { bindparams inparams; vector client_data = clientid.getClientId(); - PgSqlParam pdest = { .value = reinterpret_cast(&client_data[0]) - , .isbinary = 1, .binarylen = static_cast(clientid.getClientId().size()) }; + PgSqlParam pdest = { .value = reinterpret_cast(&client_data[0]), + .isbinary = 1, + .binarylen = static_cast(clientid.getClientId().size()) }; inparams.push_back(pdest); // Get the data @@ -950,8 +982,9 @@ PgSqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const { ostringstream tmp; vector client_data = clientid.getClientId(); - PgSqlParam pdest = { .value = reinterpret_cast(&client_data[0]) - , .isbinary = 1, .binarylen = static_cast(clientid.getClientId().size()) }; + PgSqlParam pdest = { .value = reinterpret_cast(&client_data[0]), + .isbinary = 1, + .binarylen = static_cast(clientid.getClientId().size()) }; inparams.push_back(pdest); tmp << static_cast(subnet_id); @@ -1006,8 +1039,9 @@ PgSqlLeaseMgr::getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid) con bindparams inparams; ostringstream tmp; vector duid_data = duid.getDuid(); - PgSqlParam pdest = { .value = reinterpret_cast(&duid_data[0]) - , .isbinary = 1, .binarylen = static_cast(duid.getDuid().size()) }; + PgSqlParam pdest = { .value = reinterpret_cast(&duid_data[0]), + .isbinary = 1, + .binarylen = static_cast(duid.getDuid().size()) }; inparams.push_back(pdest); /// @todo: use type @@ -1198,6 +1232,36 @@ PgSqlLeaseMgr::getName() const { 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& out_values, + std::vector& out_lengths, + std::vector& 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")); diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index 2bda355683..4043d520fb 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -25,60 +25,335 @@ 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 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 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 @@ -97,61 +372,212 @@ public: 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 - 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& params_, - std::vector& lengths_, - std::vector& 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& out_values, + std::vector& out_lengths, + std::vector& 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 - void updateLeaseCommon(StatementIndex stindex, bindparams & params, + void updateLeaseCommon(StatementIndex stindex, bindparams& params, const LeasePtr& lease); - bool deleteLeaseCommon(StatementIndex stindex, bindparams & params); - boost::scoped_ptr exchange4_; - boost::scoped_ptr 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 exchange4_; ///< Exchange object + boost::scoped_ptr exchange6_; ///< Exchange object + + /// A vector of compiled SQL statements std::vector statements_; - PGconn * status; + + /// PostgreSQL connection handle + PGconn* status; }; }; // end of isc::dhcp namespace