diff options
Diffstat (limited to 'src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h')
-rw-r--r-- | src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h b/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h new file mode 100644 index 0000000..9c5532e --- /dev/null +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h @@ -0,0 +1,901 @@ +// Copyright (C) 2021-2022 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_CONFIG_BACKEND_IMPL_H +#define PGSQL_CONFIG_BACKEND_IMPL_H + +#include <cc/stamped_value.h> +#include <database/audit_entry.h> +#include <database/database_connection.h> +#include <database/server.h> +#include <database/server_collection.h> +#include <database/server_selector.h> +#include <dhcp/option.h> +#include <dhcp/option_definition.h> +#include <dhcpsrv/cfg_option.h> +#include <dhcpsrv/lease.h> +#include <dhcpsrv/network.h> +#include <dhcpsrv/subnet_id.h> +#include <exceptions/exceptions.h> +#include <pgsql/pgsql_connection.h> +#include <pgsql/pgsql_exchange.h> + +#include <set> +#include <sstream> +#include <string> +#include <vector> + +namespace isc { +namespace dhcp { + +/// @brief Base class for PostgreSQL Config Backend implementations. +/// +/// This class contains common methods for manipulating data in the +/// PostgreSQL database, used by all servers. +/// +/// All POSIX times specified in the methods belonging to this +/// class must be local times. +class PgSqlConfigBackendImpl { +protected: + + /// @brief RAII object used to protect against creating multiple + /// audit revisions during cascade configuration updates. + /// + /// Audit revision is created per a single database transaction. + /// It includes log message associated with the configuration + /// change. Single command sent over the control API should + /// result in a single audit revision entry in the database. + /// A single configuration update often consists of multiple + /// insertions, updates and/or deletes in the database. For + /// example, a subnet contains pools and DHCP options which are + /// inserted to their respective tables. We refer to such update + /// as a cascade update. Cascade update should result in a single + /// audit revision and an audit entry for a subnet, rather than + /// multiple audit revisions and audit entries for the subnet, + /// pools and child DHCP options. + /// + /// Creating an instance of the @c ScopedAuditRevision guards + /// against creation of multiple audit revisions when child + /// objects are inserted or updated in the database. When the + /// instance of this object goes out of scope the new audit + /// revisions can be created. The caller must ensure that + /// the instance of this object exists throughout the whole + /// transaction with the database. + class ScopedAuditRevision { + public: + + /// @brief Constructor. + /// + /// Creates new audit revision and sets the flag in the + /// PostgreSQL CB implementation object which prevents new audit + /// revisions to be created while this instance exists. + /// + /// @param impl pointer to the PostgreSQL CB implementation. + /// @param index index of the query to set session variables + /// used for creation of the audit revision and the audit + /// entries. + /// @param server_selector Server selector. + /// @param log_message log message associated with the audit + /// revision to be inserted into the database. + /// @param cascade_transaction boolean flag indicating if + /// we're performing cascade transaction. If set to true, + /// the audit entries for the child objects (e.g. DHCP + /// options) won't be created. + ScopedAuditRevision(PgSqlConfigBackendImpl* impl, + const int index, + const db::ServerSelector& server_selector, + const std::string& log_message, + bool cascade_transaction); + + /// @brief Destructor. + /// + /// Clears the flag which is blocking creation of the new + /// audit revisions. + ~ScopedAuditRevision(); + + private: + + /// @brief Pointer to the PostgreSQL CB implementation. + PgSqlConfigBackendImpl* impl_; + }; + +public: + + /// @brief Constructor. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + /// @param db_reconnect_callback The connection recovery callback. + /// @param last_insert_id_index statement index of the SQL statement to + /// use when fetching the last insert id for a given table. + explicit PgSqlConfigBackendImpl(const db::DatabaseConnection::ParameterMap& parameters, + const db::DbCallback db_reconnect_callback, + const size_t last_insert_id_index); + + /// @brief Destructor. + virtual ~PgSqlConfigBackendImpl(); + + /// @brief Returns server tag associated with the particular selector. + /// + /// This method expects that there is exactly one server tag associated with + /// the server selector. + /// + /// @param server_selector Server selector. + /// @param operation Operation which results in calling this function. This is + /// used for error reporting purposes. + /// @return Server tag. + /// @throw InvalidOperation if the server selector is unassigned or if there + /// is more than one server tag associated with the selector. + std::string getServerTag(const db::ServerSelector& server_selector, + const std::string& operation) const { + auto const& tags = server_selector.getTags(); + if (tags.size() != 1) { + isc_throw(InvalidOperation, "expected exactly one server tag to be specified" + " while " << operation << ". Got: " + << getServerTagsAsText(server_selector)); + } + + return (tags.begin()->get()); + } + + /// @brief Returns server tags associated with the particular selector + /// as text. + /// + /// This method is useful for logging purposes. + std::string getServerTagsAsText(const db::ServerSelector& server_selector) const { + std::ostringstream s; + auto const& server_tags = server_selector.getTags(); + for (auto const& tag : server_tags) { + if (s.tellp() != 0) { + s << ", "; + } + s << tag.get(); + } + + return (s.str()); + } + + /// @brief Invokes the corresponding stored procedure in PgSQL. + /// + /// The @c createAuditRevision stored procedure creates new audit + /// revision and initializes several session variables to be used when + /// the audit entries will be created for the inserted, updated or + /// deleted configuration elements. + /// + /// @param index query index. + /// @param server_selector Server selector. + /// @param audit_ts Timestamp to be associated with the audit + /// revision. + /// @param log_message log message to be used for the audit revision. + /// @param cascade_transaction Boolean value indicating whether the + /// configuration modification is performed as part of the owning + /// element modification, e.g. subnet is modified resulting in + /// modification of the DHCP options it owns. In that case only the + /// audit entry for the owning element should be created. + void createAuditRevision(const int index, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& audit_ts, + const std::string& log_message, + const bool cascade_transaction); + + /// @brief Clears the flag blocking creation of the new audit revisions. + /// + /// This is used by the @c ScopedAuditRevision object. + void clearAuditRevision(); + + /// @brief Sends query to the database to retrieve most recent audit entries. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @param modification_time Timestamp being a lower limit for the returned + /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) revisions have the same + /// modification_time. + /// @param [out] audit_entries Reference to the container where fetched audit + /// entries will be inserted. + void getRecentAuditEntries(const int index, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id, + db::AuditEntryCollection& audit_entries); + + /// @brief Sends query to delete rows from a table. + /// + /// @param index Index of the statement to be executed. + /// @param server_selector Server selector. + /// @param operation Operation which results in calling this function. This is + /// used for error reporting purposes. + /// @return Number of deleted rows. + uint64_t deleteFromTable(const int index, + const db::ServerSelector& server_selector, + const std::string& operation); + + /// @brief Sends query to delete rows from a table. + /// + /// @param index Index of the statement to be executed. + /// @param server_selector Server selector. + /// @param operation Operation which results in calling this function. This is + /// used for logging purposes. + /// @param in_bindings Reference to the PgSQL input bindings. They are modified + /// as a result of this function - server tag is inserted into the beginning + /// of the bindings collection. + /// @return Number of deleted rows. + uint64_t deleteFromTable(const int index, + const db::ServerSelector& server_selector, + const std::string& operation, + db::PsqlBindArray& bindings); + + /// @brief Sends query to delete rows from a table. + /// + /// @tparam KeyType Type of the key used as the second binding. The + /// server tag is used as first binding. + /// + /// @param index Index of the statement to be executed. + /// @param server_selector Server selector. + /// @param operation Operation which results in calling this function. This is + /// used for error reporting purposes. + /// @param key Value to be used as input binding to the delete + /// statement. The default value is empty which indicates that the + /// key should not be used in the query. + /// @return Number of deleted rows. + /// @throw InvalidOperation if the server selector is unassigned or + /// if there are more than one server tags associated with the + /// server selector. + template<typename KeyType> + uint64_t deleteFromTable(const int index, + const db::ServerSelector& server_selector, + const std::string& operation, + KeyType key) { + // When deleting by some key, we must use ANY. + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "deleting an unassigned object requires " + "an explicit server tag or using ANY server. The UNASSIGNED " + "server selector is currently not supported"); + } + + db::PsqlBindArray in_bindings; + in_bindings.add(key); + return (deleteFromTable(index, server_selector, operation, in_bindings)); + } + + /// @brief Returns the last sequence value for the given table and + /// column name. + /// + /// This relies on PostgreSQL's currval() function which will return + /// the last value generated for the sequence within the current session. + /// + /// @param table name of the table + /// @param column name of the sequence column + /// @return returns the most recently modified value for the given + /// sequence + uint64_t getLastInsertId(const std::string& table, const std::string& column); + + /// @brief Sends query to retrieve multiple global parameters. + /// + /// @param index Index of the query to be used. + /// @param in_bindings Input bindings specifying selection criteria. The + /// size of the bindings collection must match the number of placeholders + /// in the prepared statement. The input bindings collection must be empty + /// if the query contains no WHERE clause. + /// @param [out] parameters Reference to the container where fetched parameters + /// will be inserted. + void getGlobalParameters(const int index, + const db::PsqlBindArray& in_bindings, + data::StampedValueCollection& parameters); + + /// @brief Sends query to retrieve single option definition by code and + /// option space. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option definition or NULL if such + /// option definition doesn't exist. + OptionDefinitionPtr getOptionDef(const int index, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space); + + /// @brief Sends query to retrieve all option definitions. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @param [out] option_defs Reference to the container where option + /// definitions are to be stored. + void getAllOptionDefs(const int index, + const db::ServerSelector& server_selector, + OptionDefContainer& option_defs); + + /// @brief Sends query to retrieve option definitions with modification + /// time later than specified timestamp. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @param modification_time Lower bound subnet modification time. + /// @param [out] option_defs Reference to the container where option + /// definitions are to be stored. + void getModifiedOptionDefs(const int index, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time, + OptionDefContainer& option_defs); + + /// @brief Sends query to the database to retrieve multiple option + /// definitions. + /// + /// Query should order option definitions by id. + /// + /// @param index Index of the query to be used. + /// @param in_bindings Input bindings specifying selection criteria. The + /// size of the bindings collection must match the number of placeholders + /// in the prepared statement. The input bindings collection must be empty + /// if the query contains no WHERE clause. + /// @param [out] option_defs Reference to the container where fetched + /// option definitions will be inserted. + void getOptionDefs(const int index, + const db::PsqlBindArray& in_bindings, + OptionDefContainer& option_defs); + + /// @brief Creates or updates an option definition. + /// + /// @param server_selector Server selector. + /// @param universe Option universe, i.e. V4 or V6. + /// @param option_def Option definition to be added or updated. + /// @param space Default option space + /// @param get_option_def_code_space Statement getting option + /// definition by code and space. + /// @param insert_option_def Statement inserting option definition. + /// @param update_option_def Statement updating option definition. + /// @param create_audit_revision Statement creating audit revision. + /// @param insert_option_def_server Statement associating option + /// definition with a server. + /// @param client_class_name Optional client class name to which + /// the option definition belongs. If this value is not specified, + /// it is a global option definition. + /// @throw NotImplemented if server selector is "unassigned". + void createUpdateOptionDef(const db::ServerSelector& server_selector, + const Option::Universe& universe, + const OptionDefinitionPtr& option_def, + const std::string& space, + const int& get_option_def_code_space, + const int& insert_option_def, + const int& update_option_def, + const int& create_audit_revision, + const int& insert_option_def_server, + const std::string& client_class_name = ""); + + /// @brief Sends query to retrieve single global option by code and + /// option space. + /// + /// @param index Index of the query to be used. + /// @param universe Option universe, i.e. V4 or V6. + /// @param server_selector Server selector. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option or NULL if such option + /// doesn't exist. + OptionDescriptorPtr getOption(const int index, + const Option::Universe& universe, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space); + + /// @brief Sends query to retrieve all global options. + /// + /// @param index Index of the query to be used. + /// @param universe Option universe, i.e. V4 or V6. + /// @param server_selector Server selector. + /// @return Container holding returned options. + OptionContainer getAllOptions(const int index, + const Option::Universe& universe, + const db::ServerSelector& server_selector); + + /// @brief Sends query to retrieve global options with modification + /// time later than specified timestamp. + /// + /// @param index Index of the query to be used. + /// @param universe Option universe, i.e. V4 or V6. + /// @param server_selector Server selector. + /// @return Container holding returned options. + OptionContainer getModifiedOptions(const int index, + const Option::Universe& universe, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time); + + /// @brief Sends query to retrieve single option by code and option space + /// for a given subnet id. + /// + /// @param index Index of the query to be used. + /// @param universe Option universe, i.e. V4 or V6. + /// @param server_selector Server selector. + /// @param subnet_id Subnet identifier. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option descriptor or NULL if such + /// option doesn't exist. + OptionDescriptorPtr getOption(const int index, + const Option::Universe& universe, + const db::ServerSelector& server_selector, + const dhcp::SubnetID& subnet_id, + const uint16_t code, + const std::string& space); + + /// @brief Sends query to retrieve single option by code and option space + /// for a given address or prefix delegation (v6) pool id. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @param pool_type Pool type (Lease::TYPE_V4, TYPE_NA or TYPE_PD). + /// @param pool_id Pool identifier in the database. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option descriptor or NULL if such + /// option doesn't exist. + OptionDescriptorPtr getOption(const int index, + const db::ServerSelector& server_selector, + const dhcp::Lease::Type& pool_type, + const uint64_t pool_id, + const uint16_t code, + const std::string& space); + + /// @brief Sends query to retrieve single option by code and option space + /// for a given shared network. + /// + /// @param index Index of the query to be used. + /// @param universe Option universe, i.e. V4 or V6. + /// @param server_selector Server selector. + /// @param shared_network_name Shared network name. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option descriptor or NULL if such + /// option doesn't exist. + OptionDescriptorPtr getOption(const int index, + const Option::Universe& universe, + const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const uint16_t code, + const std::string& space); + + /// @brief Sends query to the database to retrieve multiple options. + /// + /// Query should order by option_id. + /// + /// @note The universe is reused to switch between DHCPv4 and DHCPv6 + /// option layouts. + /// @param family Address family (either AF_INET or AF_INET6). + /// @param index Index of the query to be used. + /// @param in_bindings Input bindings specifying selection criteria. The + /// size of the bindings collection must match the number of placeholders + /// in the prepared statement. The input bindings collection must be empty + /// if the query contains no WHERE clause. + /// @param universe Option universe, i.e. V4 or V6. + /// @param [out] options Reference to the container where fetched options + /// will be inserted. + void getOptions(const int index, + const db::PsqlBindArray& in_bindings, + const Option::Universe& universe, + OptionContainer& options); + + /// @brief Returns DHCP option instance from a set of columns within a + /// result set row. + /// + /// The following is the expected order of columns specified in the SELECT + /// query: + /// - option_id, + /// - code, + /// - value, + /// - formatted_value, + /// - space, + /// - persistent, + /// - dhcp4_subnet_id/dhcp6_subnet_id, + /// - scope_id, + /// - user_context, + /// - shared_network_name, + /// - pool_id, + /// - [pd_pool_id,] + /// - modification_ts + /// + /// @note The universe is reused to switch between DHCPv4 and DHCPv6 + /// option layouts. + /// @param universe V4 or V6. + /// @param worker result set row worker containing the row data + /// @param first_col column index of the first column (i.e. option_id) + /// in the row. + OptionDescriptorPtr processOptionRow(const Option::Universe& universe, + db::PgSqlResultRowWorker& worker, + const size_t first_col); + + /// @brief Returns DHCP option definition instance from output bindings. + /// + /// The following is the expected order of columns specified in the SELECT + /// query: + /// - id, + /// - code, + /// - name, + /// - space, + /// - type, + /// - modification_ts, + /// - is_array, + /// - encapsulate, + /// - record_types, + /// - user_context + /// + /// @param worker result set row worker containing the row data + /// @param first_col column index of the first column (i.e. definition id) + /// in the row. + /// @return Pointer to the option definition. + OptionDefinitionPtr processOptionDefRow(db::PgSqlResultRowWorker& worker, + const size_t first_col); + + /// @brief Associates a configuration element with multiple servers. + /// + /// @param index Query index. + /// @param server_selector Server selector, perhaps with multiple server tags. + /// @param in_bindings Parameter pack holding bindings for the query. The first + /// entry must be the primary key of the element to attach. Note that + /// the server tag (or server id) must be the last binding in the prepared + /// statement. The caller must not include this binding in the parameter pack. + void attachElementToServers(const int index, + const db::ServerSelector& server_selector, + const db::PsqlBindArray& in_bindings); + + /// @brief Adds network ddns-replace-client-name mode to a bind array. + /// + /// If network's value of ddns-replace-client-name mode has been specified + /// it is added to the binding, otherwise a null is added to the binding. + /// + /// @param bindings PsqlBindArray to which the mode should be added. + /// @param network Pointer to shared network or subnet for which mode binding + /// should be created. + void addDdnsReplaceClientNameBinding(db::PsqlBindArray& bindings, + const NetworkPtr& network); + + /// @brief Adds network relays addresses to a bind array. + /// + /// Creates an Element tree of relay addresses add adds that to the end + /// of the given bind array. + /// + /// @param bindings PsqlBindArray to which the relay addresses should be added. + /// @param network Pointer to a shared network or subnet for which binding + /// should be created. + void addRelayBinding(db::PsqlBindArray& bindings, const NetworkPtr& network); + + /// @brief Iterates over the relay addresses in a JSON list element at a + /// given column, adding each to the given Network's relay list. + /// + /// Has no effect if the column is null or is an empty list. + /// + /// @param worker result set row worker containing the row data + /// @param col column index of JSON element column + /// @param network network to update. + /// + /// @throw BadValue if the Element is not a list or if any of the + /// list's elements are not valid IP addresses in string form. + void setRelays(db::PgSqlResultRowWorker& r, size_t col, Network& network); + + /// @brief Adds 'require_client_classes' parameter to a bind array. + /// + /// Creates an Element tree of required class names and adds that to the end + /// of the given bind array. + /// + /// @tparam T of pointer to objects with getRequiredClasses + /// method, e.g. shared network, subnet, pool or prefix delegation pool. + /// @param bindings PsqlBindArray to which the classes should be added. + /// @param object Pointer to an object with getRequiredClasses method + /// @return Pointer to the binding (possibly null binding if there are no + /// required classes specified). + template<typename T> + void addRequiredClassesBinding(db::PsqlBindArray& bindings, const T& object) { + // Create JSON list of required classes. + data::ElementPtr required_classes_element = data::Element::createList(); + const auto& required_classes = object->getRequiredClasses(); + for (auto required_class = required_classes.cbegin(); + required_class != required_classes.cend(); + ++required_class) { + required_classes_element->add(data::Element::create(*required_class)); + } + + bindings.add(required_classes_element); + } + + /// @brief Iterates over the class names in a JSON list element at a + /// given column, invoking a setter function for each one. + /// + /// Has no effect if the column is null or is an empty list. + /// + /// @param worker result set row worker containing the row data + /// @param col column index of JSON element column + /// @param setter function to invoke for each class name in the list + /// + /// @throw BadValue if the Element is not a list or if any of the + /// list's elements are not strings. + void setRequiredClasses(db::PgSqlResultRowWorker& worker, size_t col, + std::function<void(const std::string&)> setter); + + /// @brief Adds an option value to a bind array. + /// + /// @param bindings PsqlBindArray to which the option value should be added. + /// @param option Option descriptor holding the option who's value should + /// added. + void addOptionValueBinding(db::PsqlBindArray& bindings, + const OptionDescriptorPtr& option); + + /// @brief Retrieves a server. + /// + /// @param index Index of the query to be used. + /// @param server_tag Server tag of the server to be retrieved. + /// @return Pointer to the @c Server object representing the server or + /// null if such server doesn't exist. + db::ServerPtr getServer(const int index, const data::ServerTag& server_tag); + + /// @brief Retrieves all servers. + /// + /// @param index Index of the query to be used. + /// @param [out] servers Reference to the container where fetched servers + /// will be inserted. + void getAllServers(const int index, db::ServerCollection& servers); + + /// @brief Sends query to retrieve servers. + /// + /// @param index Index of the query to be used. + /// @param bindings Reference to the PgSQL input bindings. + /// @param [out] servers Reference to the container where fetched servers + /// will be inserted. + void getServers(const int index, + const db::PsqlBindArray& bindings, + db::ServerCollection& servers); + + /// @brief Creates or updates a server. + /// + /// This method attempts to insert a new server into the database using + /// the query identified by @c create_index. If the insertion fails because + /// the server with the given tag already exists in the database, the + /// existing server is updated using the query identified by the + /// @c update_index. + /// + /// @param create_audit_revision Index of the query inserting audit + /// revision. + /// @param create_index Index of the INSERT query to be used. + /// @param update_index Index of the UPDATE query to be used. + /// @param server Pointer to the server to be inserted or updated. + /// @throw InvalidOperation when trying to create a duplicate or + /// update the logical server 'all'. + void createUpdateServer(const int& create_audit_revision, + const int& create_index, + const int& update_index, + const db::ServerPtr& server); + + /// @brief Executes multiple update and/or delete queries with no input + /// bindings. + /// + /// This is a convenience function which takes multiple query indexes as + /// arguments and for each index executes an update or delete query. + /// One of the applications of this function is to remove dangling + /// configuration elements after the server associated with these elements + /// have been deleted. + /// + /// @tparam T type of the indexes, e.g. @c PgSqlConfigBackendDHCPv4Impl::StatementIndex. + /// @tparam R parameter pack holding indexes of type @c T. + /// @param first_index first index. + /// @param other_indexes remaining indexes. + template<typename T, typename... R> + void multipleUpdateDeleteQueries(T first_index, R... other_indexes) { + std::vector<T> indexes({ first_index, other_indexes... }); + db::PsqlBindArray in_bindings; + for (auto index : indexes) { + updateDeleteQuery(index, in_bindings); + } + } + + /// @brief Removes configuration elements from the index which don't match + /// the specified server selector. + /// + /// This is a generic function which removes configuration elements which + /// don't match the specified selector. In order to fetch all server tags + /// for the returned configuration element, the query must not limit the + /// results to the given server tag. Instead, it must post process the + /// result to eliminate those configuration elements for which the desired + /// server tag wasn't found. + /// + /// If the server selector is set to ANY, this method is no-op. + /// + /// @tparam CollectionIndex Type of the collection to be processed. + /// @param server_selector Server selector. + /// @param index Reference to the index holding the returned configuration + /// elements to be processed. + template<typename CollectionIndex> + void tossNonMatchingElements(const db::ServerSelector& server_selector, + CollectionIndex& index) { + // Don't filter the matching server tags if the server selector is + // set to ANY. + if (server_selector.amAny()) { + return; + } + + // Go over the collection of elements. + for (auto elem = index.begin(); elem != index.end();) { + + // If we're asking for shared networks matching all servers, + // we have to make sure that the fetched element has "all" + // server tag. + if (server_selector.amAll()) { + if (!(*elem)->hasAllServerTag()) { + // It doesn't so let's remove it. + elem = index.erase(elem); + continue; + } + + } else if (server_selector.amUnassigned()) { + // Returned element has server tags but we expect that the + // elements are unassigned. + if (!(*elem)->getServerTags().empty()) { + elem = index.erase(elem); + continue; + } + + } else { + // Server selector contains explicit server tags, so + // let's see if the returned elements includes any of + // them. + auto const& tags = server_selector.getTags(); + bool tag_found = false; + for (auto const& tag : tags) { + if ((*elem)->hasServerTag(tag) || + (*elem)->hasAllServerTag()) { + tag_found = true; + break; + } + } + if (!tag_found) { + // Tag not matching, so toss the element. + elem = index.erase(elem); + continue; + } + } + + // Go to the next element if we didn't toss the current one. + // Otherwise, the erase() function should have already taken + // us to the next one. + ++elem; + } + } + + /// @brief Returns backend type in the textual format. + /// + /// @return "postgresql". + std::string getType() const; + + /// @brief Returns backend host. + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return host on which the database is located. + std::string getHost() const; + + /// @brief Returns backend port number. + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return Port number on which database service is available. + uint16_t getPort() const; + + /// @brief Return backend parameters + /// + /// Returns the backend parameters + /// + /// @return Parameters of the backend. + const isc::db::DatabaseConnection::ParameterMap& getParameters() { + return (parameters_); + } + + /// @brief Sets IO service to be used by the PostgreSQL config backend. + /// + /// @param IOService object, used for all ASIO operations. + static void setIOService(const isc::asiolink::IOServicePtr& io_service) { + io_service_ = io_service; + } + + /// @brief Returns pointer to the IO service. + static isc::asiolink::IOServicePtr& getIOService() { + return (io_service_); + } + + /// @brief Fetches the SQL statement for a given statement index. + /// + /// Derivations must override the implementation. The reference + /// returned should be non-volatile over the entire lifetime + /// of the derivation instance. + /// + /// @param index index of the desired statement. + /// @throw NotImplemented, always + virtual db::PgSqlTaggedStatement& getStatement(size_t index) const; + + /// @brief Executes SELECT using the prepared statement specified + /// by the given index. + /// + /// The @c index must refer to an existing prepared statement + /// associated with the connection. The @c in_bindings size must match + /// the number of placeholders in the prepared statement. + /// + /// This method executes prepared statement using provided input bindings and + /// calls @c process_result_row function for each returned row. The + /// @c process_result function is implemented by the caller and should + /// gather and store each returned row in an external data structure prior. + /// + /// @param statement reference to the precompiled tagged statement to execute + /// @param in_bindings input bindings holding values to substitue placeholders + /// in the query. + /// @param process_result_row Pointer to the function to be invoked for each + /// retrieved row. This function consumes the retrieved data from the + /// result set. + void selectQuery(size_t index, const db::PsqlBindArray& in_bindings, + db::PgSqlConnection::ConsumeResultRowFun process_result_row); + + /// @brief Executes INSERT using the prepared statement specified + /// by the given index. + /// + /// The @c index must refer to an existing prepared statement + /// associated with the connection. The @c in_bindings size must match + /// the number of placeholders in the prepared statement. + /// + /// This method executes prepared statement using provided bindings to + /// insert data into the database. + /// + /// @param statement reference to the precompiled tagged statement to execute + /// @param in_bindings input bindings holding values to substitue placeholders + /// in the query. + void insertQuery(size_t index, const db::PsqlBindArray& in_bindings); + + /// @brief Executes UPDATE or DELETE using the prepared statement + /// specified by the given index, and returns the number of affected rows. + /// + /// The @c index must refer to an existing prepared statement + /// associated with the connection. The @c in_bindings size must match + /// the number of placeholders in the prepared statement. + /// + /// @param statement reference to the precompiled tagged statement to execute + /// @param in_bindings Input bindings holding values to substitute placeholders + /// in the query. + /// + /// @return Number of affected rows. + uint64_t updateDeleteQuery(size_t index, const db::PsqlBindArray& in_bindings); + + /// @brief Represents connection to the PostgreSQL database. + db::PgSqlConnection conn_; + +protected: + + /// @brief Timer name used to register database reconnect timer. + std::string timer_name_; + +private: + + /// @brief Reference counter for @ScopedAuditRevision instances. + int audit_revision_ref_count_; + + /// @brief Connection parameters + isc::db::DatabaseConnection::ParameterMap parameters_; + + /// @brief The IOService object, used for all ASIO operations. + static isc::asiolink::IOServicePtr io_service_; + + /// @brief Statement index of the SQL statement to use for fetching + /// last inserted id in a given table. + size_t last_insert_id_index_; +}; + +} // end of namespace isc::dhcp +} // end of namespace isc + +#endif |