diff options
author | Thomas Markwalder <tmark@isc.org> | 2022-01-11 16:44:41 +0100 |
---|---|---|
committer | Thomas Markwalder <tmark@isc.org> | 2022-01-18 18:04:10 +0100 |
commit | 4d1ade7ea3dce83e090610acb03b63e8517295bf (patch) | |
tree | 54eb676528f9f7e04d4092ac308e3d7aec7f8f2c /src | |
parent | [#2254] fixed compilation warnings (diff) | |
download | kea-4d1ade7ea3dce83e090610acb03b63e8517295bf.tar.xz kea-4d1ade7ea3dce83e090610acb03b63e8517295bf.zip |
[#95] All Queries - CRUD for servers
src/hooks/dhcp/pgsql_cb/Makefile.am
New files:
pgsql_query_macros.h pgsql_cb_dhcp4.cc pgsql_cb_dhcp4.h
src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.*
Initial implementation - includes CRUD for servers
src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.*
PgSqlConfigBackendImpl::getStatement()
PgSqlConfigBackendImpl::selectQuery()
PgSqlConfigBackendImpl::insertQuery()
PgSqlConfigBackendImpl::updateDeleteQuery()
PgSqlConfigBackendImpl::createAuditRevision()
PgSqlConfigBackendImpl::getRecentAuditEntries()
PgSqlConfigBackendImpl::deleteFromTable()
PgSqlConfigBackendImpl::getLastInsertId()
PgSqlConfigBackendImpl::createInputRelayBinding()
PgSqlConfigBackendImpl::createOptionValueBinding()
PgSqlConfigBackendImpl::getServer()
PgSqlConfigBackendImpl::getServers()
PgSqlConfigBackendImpl::createUpdateServer()
PgSqlConfigBackendImpl::attachElementToServers()
PgSqlConfigBackendImpl::addRelayBinding()
PgSqlConfigBackendImpl::addOptionValueBinding()
PgSqlConfigBackendImpl::addRequiredClassesBinding()
PgSqlConfigBackendImpl::addOptionValueBinding()
src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.mes
New messages
src/hooks/dhcp/pgsql_cb/tests/Makefile.am
New file:pgsql_cb_dhcp4_unittest.cc
src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_dhcp4_unittest.cc
New file
src/lib/config_backend/base_config_backend.h
src/lib/config_backend/tests/config_backend_mgr_unittest.cc
src/lib/database/backend_selector.h
src/lib/database/backend_selector.cc
src/lib/database/tests/backend_selector_unittest.cc
"pgsql" -> "postgresql"
BackendSelector::Type::PGSQL -> BackendSelector::Type::POSTGRESQL
src/lib/pgsql/pgsql_connection.cc
Added NULL_KEY
PgSqlConnection::checkStatementError() - throws DuplicateEntry and NullKeyError
src/lib/pgsql/pgsql_connection.h
Added NULL_KEY
Upped PGSQL_MAX_PARAMETERS_IN_QUERY to 128;
src/lib/pgsql/pgsql_exchange.*
PsqlBindArray::popBack() - new function
Cleaned up AddOptional functions
PsqlBindArray::add(const ConstElementPtr& value) - new
Diffstat (limited to 'src')
23 files changed, 7380 insertions, 1215 deletions
diff --git a/src/hooks/dhcp/pgsql_cb/Makefile.am b/src/hooks/dhcp/pgsql_cb/Makefile.am index 9ff5dd06b7..4583545676 100644 --- a/src/hooks/dhcp/pgsql_cb/Makefile.am +++ b/src/hooks/dhcp/pgsql_cb/Makefile.am @@ -24,7 +24,8 @@ libpgsqlcb_la_SOURCES = pgsql_cb_callouts.cc libpgsqlcb_la_SOURCES += pgsql_cb_impl.cc pgsql_cb_impl.h libpgsqlcb_la_SOURCES += pgsql_cb_messages.cc pgsql_cb_messages.h libpgsqlcb_la_SOURCES += pgsql_cb_log.cc pgsql_cb_log.h -libpgsqlcb_la_SOURCES += pgsql_macros.h +libpgsqlcb_la_SOURCES += pgsql_query_macros.h +libpgsqlcb_la_SOURCES += pgsql_cb_dhcp4.cc pgsql_cb_dhcp4.h libpgsqlcb_la_SOURCES += version.cc libpgsqlcb_la_CXXFLAGS = $(AM_CXXFLAGS) diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.cc b/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.cc new file mode 100644 index 0000000000..bfb77400fd --- /dev/null +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.cc @@ -0,0 +1,3366 @@ +// 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/. + +#include <config.h> + +#include <pgsql_cb_dhcp4.h> +#include <pgsql_cb_impl.h> +#include <pgsql_query_macros_dhcp.h> +#include <cc/data.h> +#include <config_backend/constants.h> +#include <database/database_connection.h> +#include <database/db_exceptions.h> +#include <dhcp/classify.h> +#include <dhcp/dhcp6.h> +#include <dhcp/libdhcp++.h> +#include <dhcp/option_data_types.h> +#include <dhcp/option_space.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/config_backend_dhcp4_mgr.h> +#include <dhcpsrv/network.h> +#include <dhcpsrv/pool.h> +#include <dhcpsrv/lease.h> +#include <dhcpsrv/timer_mgr.h> +#include <dhcpsrv/parsers/client_class_def_parser.h> +#include <util/buffer.h> +#include <util/boost_time_utils.h> +#include <util/multi_threading_mgr.h> +#include <pgsql/pgsql_connection.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/pointer_cast.hpp> +#include <boost/scoped_ptr.hpp> + +#include <array> +#include <sstream> +#include <utility> +#include <vector> + +using namespace isc::cb; +using namespace isc::db; +using namespace isc::data; +using namespace isc::asiolink; +using namespace isc::log; +using namespace isc::util; + +namespace isc { +namespace dhcp { + +/// @brief Implementation of the PgSQL Configuration Backend. +class PgSqlConfigBackendDHCPv4Impl : public PgSqlConfigBackendImpl { +public: + + /// @brief Statement tags. + /// + /// The contents of the enum are indexes into the list of SQL statements. + /// It is assumed that the order is such that the indices of statements + /// reading the database are less than those of statements modifying the + /// database. + enum StatementIndex { + CREATE_AUDIT_REVISION, + CHECK_CLIENT_CLASS_KNOWN_DEPENDENCY_CHANGE, + GET_GLOBAL_PARAMETER4, + GET_ALL_GLOBAL_PARAMETERS4, + GET_MODIFIED_GLOBAL_PARAMETERS4, + GET_SUBNET4_ID_NO_TAG, + GET_SUBNET4_ID_ANY, + GET_SUBNET4_ID_UNASSIGNED, + GET_SUBNET4_PREFIX_NO_TAG, + GET_SUBNET4_PREFIX_ANY, + GET_SUBNET4_PREFIX_UNASSIGNED, + GET_ALL_SUBNETS4, + GET_ALL_SUBNETS4_UNASSIGNED, + GET_MODIFIED_SUBNETS4, + GET_MODIFIED_SUBNETS4_UNASSIGNED, + GET_SHARED_NETWORK_SUBNETS4, + GET_POOL4_RANGE, + GET_POOL4_RANGE_ANY, + GET_SHARED_NETWORK4_NAME_NO_TAG, + GET_SHARED_NETWORK4_NAME_ANY, + GET_SHARED_NETWORK4_NAME_UNASSIGNED, + GET_ALL_SHARED_NETWORKS4, + GET_ALL_SHARED_NETWORKS4_UNASSIGNED, + GET_MODIFIED_SHARED_NETWORKS4, + GET_MODIFIED_SHARED_NETWORKS4_UNASSIGNED, + GET_OPTION_DEF4_CODE_SPACE, + GET_ALL_OPTION_DEFS4, + GET_MODIFIED_OPTION_DEFS4, + GET_OPTION4_CODE_SPACE, + GET_ALL_OPTIONS4, + GET_MODIFIED_OPTIONS4, + GET_OPTION4_SUBNET_ID_CODE_SPACE, + GET_OPTION4_POOL_ID_CODE_SPACE, + GET_OPTION4_SHARED_NETWORK_CODE_SPACE, + GET_CLIENT_CLASS4_NAME, + GET_ALL_CLIENT_CLASSES4, + GET_ALL_CLIENT_CLASSES4_UNASSIGNED, + GET_MODIFIED_CLIENT_CLASSES4, + GET_MODIFIED_CLIENT_CLASSES4_UNASSIGNED, + GET_AUDIT_ENTRIES4_TIME, + GET_SERVER4, + GET_ALL_SERVERS4, + INSERT_GLOBAL_PARAMETER4, + INSERT_GLOBAL_PARAMETER4_SERVER, + INSERT_SUBNET4, + INSERT_SUBNET4_SERVER, + INSERT_POOL4, + INSERT_SHARED_NETWORK4, + INSERT_SHARED_NETWORK4_SERVER, + INSERT_OPTION_DEF4, + INSERT_OPTION_DEF4_CLIENT_CLASS, + INSERT_OPTION_DEF4_SERVER, + INSERT_OPTION4, + INSERT_OPTION4_SERVER, + INSERT_CLIENT_CLASS4, + INSERT_CLIENT_CLASS4_SERVER, + INSERT_CLIENT_CLASS4_DEPENDENCY, + INSERT_SERVER4, + UPDATE_GLOBAL_PARAMETER4, + UPDATE_SUBNET4, + UPDATE_SHARED_NETWORK4, + UPDATE_OPTION_DEF4, + UPDATE_OPTION_DEF4_CLIENT_CLASS, + UPDATE_OPTION4, + UPDATE_OPTION4_SUBNET_ID, + UPDATE_OPTION4_POOL_ID, + UPDATE_OPTION4_SHARED_NETWORK, + UPDATE_OPTION4_CLIENT_CLASS, + UPDATE_CLIENT_CLASS4, + UPDATE_CLIENT_CLASS4_SAME_POSITION, + UPDATE_SERVER4, + DELETE_GLOBAL_PARAMETER4, + DELETE_ALL_GLOBAL_PARAMETERS4, + DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED, + DELETE_SUBNET4_ID_WITH_TAG, + DELETE_SUBNET4_ID_ANY, + DELETE_SUBNET4_PREFIX_WITH_TAG, + DELETE_SUBNET4_PREFIX_ANY, + DELETE_ALL_SUBNETS4, + DELETE_ALL_SUBNETS4_UNASSIGNED, + DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME, + DELETE_SUBNET4_SERVER, + DELETE_POOLS4, + DELETE_SHARED_NETWORK4_NAME_WITH_TAG, + DELETE_SHARED_NETWORK4_NAME_ANY, + DELETE_ALL_SHARED_NETWORKS4, + DELETE_ALL_SHARED_NETWORKS4_UNASSIGNED, + DELETE_SHARED_NETWORK4_SERVER, + DELETE_OPTION_DEF4_CODE_NAME, + DELETE_ALL_OPTION_DEFS4, + DELETE_ALL_OPTION_DEFS4_UNASSIGNED, + DELETE_OPTION_DEFS4_CLIENT_CLASS, + DELETE_OPTION4, + DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED, + DELETE_OPTION4_SUBNET_ID, + DELETE_OPTION4_POOL_RANGE, + DELETE_OPTION4_SHARED_NETWORK, + DELETE_OPTIONS4_SUBNET_ID_PREFIX, + DELETE_OPTIONS4_SHARED_NETWORK, + DELETE_OPTIONS4_CLIENT_CLASS, + DELETE_CLIENT_CLASS4_DEPENDENCY, + DELETE_CLIENT_CLASS4_SERVER, + DELETE_ALL_CLIENT_CLASSES4, + DELETE_ALL_CLIENT_CLASSES4_UNASSIGNED, + DELETE_CLIENT_CLASS4, + DELETE_CLIENT_CLASS4_ANY, + DELETE_SERVER4, + DELETE_ALL_SERVERS4, + GET_LAST_INSERT_ID4, + NUM_STATEMENTS + }; + + /// @brief Constructor. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + explicit PgSqlConfigBackendDHCPv4Impl(const DatabaseConnection::ParameterMap& + parameters); + + /// @brief Destructor. + ~PgSqlConfigBackendDHCPv4Impl(); + + /// @brief Fetches the SQL statement for a given statement index. + /// + /// @param index index of the desired statement. + /// @throw BadValue if there is no statement corresponding to + /// the index. + virtual PgSqlTaggedStatement& getStatement(size_t index) const; + + /// @brief Sends query to retrieve global parameter. + /// + /// @param server_selector Server selector. + /// @param name Name of the parameter to be retrieved. + /// + /// @return Pointer to the retrieved value or null if such parameter + /// doesn't exist. + StampedValuePtr getGlobalParameter4(const ServerSelector& /* server_selector */, + const std::string& /* name */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update global parameter. + /// + /// @param server_selector Server selector. + /// @param value StampedValue describing the parameter to create/update. + void createUpdateGlobalParameter4(const db::ServerSelector& /* server_selector */, + const StampedValuePtr& /* value */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to the database to retrieve multiple subnets. + /// + /// Query should order subnets by subnet_id. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @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] subnets Reference to the container where fetched subnets + /// will be inserted. + void getSubnets4(const StatementIndex& /* index */, + const ServerSelector& /* server_selector */, + const PsqlBindArray& /* in_bindings */, + Subnet4Collection& /* subnets */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve single subnet by id. + /// + /// @param server_selector Server selector. + /// @param subnet_id Subnet identifier. + /// + /// @return Pointer to the returned subnet or NULL if such subnet + /// doesn't exist. + Subnet4Ptr getSubnet4(const ServerSelector& /* server_selector */, + const SubnetID& /* subnet_id */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve single subnet by prefix. + /// + /// The prefix should be in the following format: "192.0.2.0/24". + /// + /// @param server_selector Server selector. + /// @param subnet_id Subnet identifier. + /// + /// @return Pointer to the returned subnet or NULL if such subnet + /// doesn't exist. + Subnet4Ptr getSubnet4(const ServerSelector& /* server_selector */, + const std::string& /* subnet_prefix */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve all subnets. + /// + /// @param server_selector Server selector. + /// @param [out] subnets Reference to the subnet collection structure where + /// subnets should be inserted. + void getAllSubnets4(const ServerSelector& /* server_selector */, + Subnet4Collection& /* subnets */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve modified subnets. + /// + /// @param server_selector Server selector. + /// @param modification_ts Lower bound modification timestamp. + /// @param [out] subnets Reference to the subnet collection structure where + /// subnets should be inserted. + void getModifiedSubnets4(const ServerSelector& /* server_selector */, + const boost::posix_time::ptime& /* modification_ts */, + Subnet4Collection& /* subnets */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve all subnets belonging to a shared network. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of the shared network for which the + /// subnets should be retrieved. + /// @param [out] subnets Reference to the subnet collection structure where + /// subnets should be inserted. + void getSharedNetworkSubnets4(const ServerSelector& /* server_selector */, + const std::string& /* shared_network_name */, + Subnet4Collection& /* subnets */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve multiple pools. + /// + /// Query should order pools 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] pools Reference to the container where fetched pools + /// will be inserted. + /// @param [out] pool_ids Identifiers of the pools returned in @c pools + /// argument. + void getPools(const StatementIndex& /* index */, + const PsqlBindArray& /* in_bindings */, + PoolCollection& /* pools */, + std::vector<uint64_t>& /* pool_ids */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve single pool by address range. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound pool address. + /// @param pool_end_address Upper bound pool address. + /// @param pool_id Pool identifier for the returned pool. + /// @return Pointer to the pool or null if no such pool found. + Pool4Ptr getPool4(const ServerSelector& /* server_selector */, + const IOAddress& /* pool_start_address */, + const IOAddress& /* pool_end_address */, + uint64_t& /* pool_id */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update subnet. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the subnet to be inserted or updated. + void createUpdateSubnet4(const ServerSelector& /* server_selector */, + const Subnet4Ptr& /* subnet */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Inserts new IPv4 pool to the database. + /// + /// @param server_selector Server selector. + /// @param pool Pointer to the pool to be inserted. + /// @param subnet Pointer to the subnet that this pool belongs to. + void createPool4(const ServerSelector& /* server_selector */, const Pool4Ptr& /* pool */, + const Subnet4Ptr& /* subnet */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends a query to delete data from a table. + /// + /// If creates a new audit revision for this change if such audit + /// revision doesn't exist yet (using ScopedAuditRevision mechanism). + /// + /// @tparam Args type of the arguments to be passed to one of the existing + /// @c deleteFromTable methods. + /// @param server_selector server selector. + /// @param operation operation which results in calling this function. This is + /// used for logging purposes. + /// @param log_message log message to be associated with the audit revision. + /// @param cascade_delete boolean flag indicating if we're performing + /// cascade delete. If set to true, the audit entries for the child + /// objects (e.g. DHCPoptions) won't be created. + /// @param keys arguments to be passed to one of the existing + /// @c deleteFromTable methods. + /// + /// @return Number of deleted entries. + template<typename... Args> + uint64_t deleteTransactional(const int index, + const db::ServerSelector& server_selector, + const std::string& operation, + const std::string& log_message, + const bool cascade_delete, + Args&&... keys) { + + PgSqlTransaction transaction(conn_); + + // Create scoped audit revision. As long as this instance exists + // no new audit revisions are created in any subsequent calls. + ScopedAuditRevision + audit_revision(this, + PgSqlConfigBackendDHCPv4Impl::CREATE_AUDIT_REVISION, + server_selector, log_message, cascade_delete); + + auto count = deleteFromTable(index, server_selector, operation, keys...); + + transaction.commit(); + + return (count); + } + + /// @brief Sends query to delete subnet by id. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to be deleted. + /// @return Number of deleted subnets. + uint64_t deleteSubnet4(const ServerSelector& /* server_selector */, + const SubnetID& /* subnet_id */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to delete subnet by id. + /// + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be deleted. + /// @return Number of deleted subnets. + uint64_t deleteSubnet4(const ServerSelector& /* server_selector */, + const std::string& /* subnet_prefix */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes pools belonging to a subnet from the database. + /// + /// The query deletes all pools associated with the subnet's + /// identifier or prefix. + /// @param subnet Pointer to the subnet for which pools should be + /// deleted. + uint64_t deletePools4(const Subnet4Ptr& /* subnet */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to the database to retrieve multiple shared + /// networks. + /// + /// Query should order shared networks by id. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @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] shared_networks Reference to the container where fetched + /// shared networks will be inserted. + void getSharedNetworks4(const StatementIndex& /* index */, + const ServerSelector& /* server_selector */, + const PsqlBindArray& /* in_bindings */, + SharedNetwork4Collection& /* shared_networks */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve single shared network by name. + /// + /// @param server_selector Server selector. + /// @param name Shared network name. + /// + /// @return Pointer to the returned shared network or NULL if such shared + /// network doesn't exist. + SharedNetwork4Ptr getSharedNetwork4(const ServerSelector& /* server_selector */, + const std::string& /* name */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve all shared networks. + /// + /// @param server_selector Server selector. + /// @param [out] shared_networks Reference to the shared networks collection + /// structure where shared networks should be inserted. + void getAllSharedNetworks4(const ServerSelector& /* server_selector */, + SharedNetwork4Collection& /* shared_networks */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve modified shared networks. + /// + /// @param server_selector Server selector. + /// @param modification_ts Lower bound modification timestamp. + /// @param [out] shared_networks Reference to the shared networks collection + /// structure where shared networks should be inserted. + void getModifiedSharedNetworks4(const ServerSelector& /* server_selector */, + const boost::posix_time::ptime& /* modification_ts */, + SharedNetwork4Collection& /* shared_networks */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update shared network. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the shared network to be inserted or updated. + void createUpdateSharedNetwork4(const ServerSelector& /* server_selector */, + const SharedNetwork4Ptr& /* shared_network */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert DHCP option. + /// + /// This method expects that the server selector contains exactly one + /// server tag. It is intended to be used within a transaction. + /// + /// @param server_selector Server selector. + /// @param in_bindings Collection of bindings representing an option. + /// @param modification_ts option's modification timestamp + void insertOption4(const ServerSelector& /* server_selector */, + const PsqlBindArray& /* in_bindings */, + const boost::posix_time::ptime& /* modification_ts */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update global DHCP option. + /// + /// @param server_selector Server selector. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption4(const ServerSelector& /* server_selector */, + const OptionDescriptorPtr& /* option */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update DHCP option in a subnet. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet the option belongs to. + /// @param option Pointer to the option descriptor encapsulating the option. + /// @param cascade_update Boolean value indicating whether the update is + /// performed as part of the owning element, e.g. subnet. + void createUpdateOption4(const ServerSelector& /* server_selector */, + const SubnetID& /* subnet_id */, + const OptionDescriptorPtr& /* option */, + const bool /* cascade_update */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update DHCP option in a pool. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool. + /// @param pool_end_address Upper bound address of the pool. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption4(const ServerSelector& /* server_selector */, + const IOAddress& /* pool_start_address */, + const IOAddress& /* pool_end_address */, + const OptionDescriptorPtr& /* option */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update DHCP option in a pool. + /// + /// @param selector Server selector. + /// @param pool_id Identifier of the pool the option belongs to. + /// @param option Pointer to the option descriptor encapsulating the option. + /// @param cascade_update Boolean value indicating whether the update is + /// performed as part of the owning element, e.g. subnet. + void createUpdateOption4(const ServerSelector& /* server_selector */, + const uint64_t /* pool_id */, + const OptionDescriptorPtr& /* option */, + const bool /* cascade_update */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update DHCP option in a shared network. + /// + /// @param selector Server selector. + /// @param shared_network_name Name of the shared network the option + /// belongs to. + /// @param option Pointer to the option descriptor encapsulating the option. + /// @param cascade_update Boolean value indicating whether the update is + /// performed as part of the owning element, e.g. shared network. + void createUpdateOption4(const ServerSelector& /* server_selector */, + const std::string& /* shared_network_name */, + const OptionDescriptorPtr& /* option */, + const bool /* cascade_update */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update DHCP option in a client class. + /// + /// @param selector Server selector. + /// @param client_class Pointer to the client_class the option belongs to. + /// @param option Pointer to the option descriptor encapsulating the option.. + void createUpdateOption4(const ServerSelector& /* server_selector */, + const ClientClassDefPtr& /* client_class */, + const OptionDescriptorPtr& /* option */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update option definition. + /// + /// @param server_selector Server selector. + /// @param option_def Pointer to the option definition to be inserted or updated. + void createUpdateOptionDef4(const ServerSelector& /* server_selector */, + const OptionDefinitionPtr& /* option_def */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to insert or update option definition + /// for a client class. + /// + /// @param server_selector Server selector. + /// @param option_def Pointer to the option definition to be inserted or updated. + /// @param client_class Client class name. + void createUpdateOptionDef4(const ServerSelector& /* server_selector */, + const OptionDefinitionPtr& /* option_def */, + const std::string& /* client_class_name */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to delete option definition by code and + /// option space name. + /// + /// @param server_selector Server selector. + /// @param code Option code. + /// @param name Option name. + /// @return Number of deleted option definitions. + uint64_t deleteOptionDef4(const ServerSelector& /* server_selector */, + const uint16_t /* code */, + const std::string& /* space */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to delete option definitions for a client class. + /// + /// @param server_selector Server selector. + /// @param client_class Pointer to the client class for which option + /// definitions should be deleted. + /// @return Number of deleted option definitions. + uint64_t deleteOptionDefs4(const ServerSelector& /* server_selector */, + const ClientClassDefPtr& /* client_class */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes global option. + /// + /// @param server_selector Server selector. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption4(const ServerSelector& /* server_selector */, + const uint16_t /* code */, + const std::string& /* space */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes subnet level option. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to which deleted option + /// belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption4(const ServerSelector& /* server_selector */, + const SubnetID& /* subnet_id */, + const uint16_t /* code */, + const std::string& /* space */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes pool level option. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound pool address. + /// @param pool_end_address Upper bound pool address. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption4(const db::ServerSelector& /* server_selector */, + const IOAddress& /* pool_start_address */, + const IOAddress& /* pool_end_address */, + const uint16_t /* code */, + const std::string& /* space */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes shared network level option. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of the shared network which deleted + /// option belongs to + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption4(const db::ServerSelector& /* server_selector */, + const std::string& /* shared_network_name */, + const uint16_t /* code */, + const std::string& /* space */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes options belonging to a subnet from the database. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the subnet for which options should be + /// deleted. + /// @return Number of deleted options. + uint64_t deleteOptions4(const ServerSelector& /* server_selector */, + const Subnet4Ptr& /* subnet */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes options belonging to a shared network from the database. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the subnet for which options should be + /// deleted. + /// @return Number of deleted options. + uint64_t deleteOptions4(const ServerSelector& /* server_selector */, + const SharedNetwork4Ptr& /* shared_network */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Deletes options belonging to a client class from the database. + /// + /// @param server_selector Server selector. + /// @param client_class Pointer to the client class for which options + /// should be deleted. + /// @return Number of deleted options. + uint64_t deleteOptions4(const ServerSelector& /* server_selector */, + const ClientClassDefPtr& /* client_class */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Common function to retrieve client classes. + /// + /// @param index Index of the query to be used. + /// @param server_selector Server selector. + /// @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] client_classes Reference to a container where fetched client + /// classes will be inserted. + void getClientClasses4(const StatementIndex& /* index */, + const ServerSelector& /* server_selector */, + const PsqlBindArray& /* in_bindings */, + ClientClassDictionary& /* client_classes */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve a client class by name. + /// + /// @param server_selector Server selector. + /// @param name Name of the class to be retrieved. + /// @return Pointer to the client class or null if the class is not found. + ClientClassDefPtr getClientClass4(const ServerSelector& /* server_selector */, + const std::string& /* name */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve all client classes. + /// + /// @param server_selector Server selector. + /// @param [out] client_classes Reference to the client classes collection + /// where retrieved classes will be stored. + void getAllClientClasses4(const ServerSelector& /* server_selector */, + ClientClassDictionary& /* client_classes */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Sends query to retrieve modified client classes. + /// + /// @param server_selector Server selector. + /// @param modification_ts Lower bound modification timestamp. + /// @param [out] client_classes Reference to the client classes collection + /// where retrieved classes will be stored. + void getModifiedClientClasses4(const ServerSelector& /* server_selector */, + const boost::posix_time::ptime& /* modification_ts */, + ClientClassDictionary& /* client_classes */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + + /// @brief Upserts client class. + /// + /// @param server_selector Server selector. + /// @param client_class Pointer to the upserted client class. + /// @param follow_class_name name of the class after which the + /// new or updated class should be positioned. An empty value + /// causes the class to be appended at the end of the class + /// hierarchy. + void createUpdateClientClass4(const ServerSelector& /* server_selector */, + const ClientClassDefPtr& /* client_class */, + const std::string& /* follow_class_name */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Removes client class by name. + /// + /// @param server_selector Server selector. + /// @param name Removed client class name. + /// @return Number of deleted client classes. + uint64_t deleteClientClass4(const ServerSelector& /* server_selector */, + const std::string& /* name */) { + isc_throw(NotImplemented, NOT_IMPL_STR); + } + + /// @brief Removes unassigned global parameters, global options and + /// option definitions. + /// + /// This function is called when one or more servers are deleted and + /// it is likely that there are some orphaned configuration elements + /// left in the database. This method removes those elements. + void purgeUnassignedConfig() { + multipleUpdateDeleteQueries(DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED, + DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED, + DELETE_ALL_OPTION_DEFS4_UNASSIGNED); + } + + /// @brief Attempts to delete a server having a given tag. + /// + /// @param server_tag Tag of the server to be deleted. + /// @return Number of deleted servers. + /// @throw isc::InvalidOperation when trying to delete the logical + /// server 'all'. + uint64_t deleteServer4(const data::ServerTag& server_tag) { + // It is not allowed to delete 'all' logical server. + if (server_tag.amAll()) { + isc_throw(InvalidOperation, "'all' is a name reserved for the server tag which" + " associates the configuration elements with all servers connecting" + " to the database and may not be deleted"); + } + + PgSqlTransaction transaction(conn_); + + // Create scoped audit revision. As long as this instance exists + // no new audit revisions are created in any subsequent calls. + ScopedAuditRevision + audit_revision(this, PgSqlConfigBackendDHCPv4Impl::CREATE_AUDIT_REVISION, + ServerSelector::ALL(), "deleting a server", false); + + // Specify which server should be deleted. + PsqlBindArray in_bindings; + in_bindings.addTempString(server_tag.get()); + + // Attempt to delete the server. + auto count = updateDeleteQuery(PgSqlConfigBackendDHCPv4Impl::DELETE_SERVER4, + in_bindings); + + // If we have deleted any servers we have to remove any dangling global + // parameters, options and option definitions. + if (count > 0) { + purgeUnassignedConfig(); + } + + transaction.commit(); + return (count); + } + + /// @brief Attempts to delete all servers. + /// + /// This method deletes all servers added by the user. It does not + /// delete the logical server 'all'. + /// + /// @return Number of deleted servers. + uint64_t deleteAllServers4() { + // Start transaction. + PgSqlTransaction transaction(conn_); + + // Create scoped audit revision. As long as this instance exists + // no new audit revisions are created in any subsequent calls. + ScopedAuditRevision + audit_revision(this, PgSqlConfigBackendDHCPv4Impl::CREATE_AUDIT_REVISION, + ServerSelector::ALL(), "deleting all servers", + false); + + // No argumens, hence empty input bindings. + PsqlBindArray in_bindings; + + // Attempt to delete the servers. + auto count = updateDeleteQuery(PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SERVERS4, + in_bindings); + + // If we have deleted any servers we have to remove any dangling global + // parameters, options and option definitions. + if (count > 0) { + purgeUnassignedConfig(); + } + + // Commit the transaction. + transaction.commit(); + return (count); + } + + /// @brief Returns the last sequence value for the given table and + /// column name. + /// + /// @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 getLastInsertId4(const std::string& table, const std::string& column) { + return (getLastInsertId(PgSqlConfigBackendDHCPv4Impl::GET_LAST_INSERT_ID4, + table, column)); + } + + /// @brief Attempts to reconnect the server to the config DB backend manager. + /// + /// This is a self-rescheduling function that attempts to reconnect to the + /// server's config DB backends after connectivity to one or more have been + /// lost. Upon entry it will attempt to reconnect via + /// @ref ConfigBackendDHCPv4Mgr.addBackend. + /// If this is successful, DHCP servicing is re-enabled and server returns + /// to normal operation. + /// + /// If reconnection fails and the maximum number of retries has not been + /// exhausted, it will schedule a call to itself to occur at the + /// configured retry interval. DHCP service remains disabled. + /// + /// If the maximum number of retries has been exhausted an error is logged + /// and the server shuts down. + /// + /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the + /// configured reconnect parameters. + /// @return true if connection has been recovered, false otherwise. + static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { + MultiThreadingCriticalSection cs; + + // Invoke application layer connection lost callback. + if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) { + return (false); + } + + bool reopened = false; + + const std::string timer_name = db_reconnect_ctl->timerName(); + + // At least one connection was lost. + try { + auto srv_cfg = CfgMgr::instance().getCurrentCfg(); + auto config_ctl = srv_cfg->getConfigControlInfo(); + // Iterate over the configured DBs and instantiate them. + for (auto db : config_ctl->getConfigDatabases()) { + const std::string& access = db.getAccessString(); + auto parameters = db.getParameters(); + if (ConfigBackendDHCPv4Mgr::instance().delBackend(parameters["type"], access, true)) { + ConfigBackendDHCPv4Mgr::instance().addBackend(db.getAccessString()); + } + } + + reopened = true; + } catch (const std::exception& ex) { + LOG_ERROR(pgsql_cb_logger, PGSQL_CB_RECONNECT_ATTEMPT_FAILED4) + .arg(ex.what()); + } + + if (reopened) { + // Cancel the timer. + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } + + // Invoke application layer connection recovered callback. + if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) { + return (false); + } + } else { + if (!db_reconnect_ctl->checkRetries()) { + // We're out of retries, log it and initiate shutdown. + LOG_ERROR(pgsql_cb_logger, PGSQL_CB_RECONNECT_FAILED4) + .arg(db_reconnect_ctl->maxRetries()); + + // Cancel the timer. + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } + + // Invoke application layer connection failed callback. + DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl); + + return (false); + } + + LOG_INFO(pgsql_cb_logger, PGSQL_CB_RECONNECT_ATTEMPT_SCHEDULE4) + .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1) + .arg(db_reconnect_ctl->maxRetries()) + .arg(db_reconnect_ctl->retryInterval()); + + // Start the timer. + if (!TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->registerTimer(timer_name, + std::bind(&PgSqlConfigBackendDHCPv4Impl::dbReconnect, db_reconnect_ctl), + db_reconnect_ctl->retryInterval(), + asiolink::IntervalTimer::ONE_SHOT); + } + TimerMgr::instance()->setup(timer_name); + } + + return (true); + } +}; + +namespace { + +/// @brief Array of tagged statements. +typedef std::array<PgSqlTaggedStatement, PgSqlConfigBackendDHCPv4Impl::NUM_STATEMENTS> +TaggedStatementArray; + +/// @brief Prepared PgSQL statements used by the backend to insert and +/// retrieve data from the database. They must be in the same order as +/// PgSqlConfigBackendDHCPv4Impl::StatementIndex. The statement is +/// the corresponding enum name. +TaggedStatementArray tagged_statements = { { + { + // PgSqlConfigBackendDHCPv4Impl::CREATE_AUDIT_REVISION, + 4, + { + OID_TIMESTAMP, // 1 audit_ts + OID_VARCHAR, // 2 server_tag + OID_TEXT, // 3 audit_log_message + OID_BOOL // 4 cascade_transaction + }, + "CREATE_AUDIT_REVISION", + "select createAuditRevisionDHCP4($1, $2, $3, $4)" + }, + + // Verify that dependency on KNOWN/UNKNOWN class has not changed. + { + // PgSqlConfigBackendDHCPv4Impl::CHECK_CLIENT_CLASS_KNOWN_DEPENDENCY_CHANGE, + 0, { OID_NONE }, + "CHECK_CLIENT_CLASS_KNOWN_DEPENDENCY_CHANGE", + "select checkDHCPv4ClientClassKnownDependencyChange()" + }, + + // Select global parameter by name. + { + // PgSqlConfigBackendDHCPv4Impl::GET_GLOBAL_PARAMETER4, + 2, + { + OID_VARCHAR, // 1 server_tag + OID_VARCHAR // 2 name + }, + "GET_GLOBAL_PARAMETER4", + PGSQL_GET_GLOBAL_PARAMETER(dhcp4, AND g.name = $2) + }, + + // Select all global parameters. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_GLOBAL_PARAMETERS4, + 1, + { + OID_VARCHAR // 1 server_tag + }, + "GET_ALL_GLOBAL_PARAMETERS4", + PGSQL_GET_GLOBAL_PARAMETER(dhcp4) + }, + + // Select modified global parameters. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_GLOBAL_PARAMETERS4, + 2, + { + OID_VARCHAR, // 1 server_tag + OID_TIMESTAMP // 2 modification_ts + }, + "GET_MODIFIED_GLOBAL_PARAMETERS4", + PGSQL_GET_GLOBAL_PARAMETER(dhcp4, AND g.modification_ts >= $2) + }, + + // Select subnet by id. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SUBNET4_ID_NO_TAG, + 1, + { + OID_INT8 // 1 subnet_id + }, + "GET_SUBNET4_ID_NO_TAG", + PGSQL_GET_SUBNET4_NO_TAG(WHERE s.subnet_id = $1) + }, + + // Select subnet by id without specifying server tags. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SUBNET4_ID_ANY, + 1, + { + OID_INT8 // 1 subnet_id + }, + "GET_SUBNET4_ID_ANY", + PGSQL_GET_SUBNET4_ANY(WHERE s.subnet_id = $1) + }, + + // Select unassigned subnet by id. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SUBNET4_ID_UNASSIGNED, + 1, + { + OID_INT8 // 1 subnet_id + }, + "GET_SUBNET4_ID_UNASSIGNED", + PGSQL_GET_SUBNET4_UNASSIGNED(AND s.subnet_id = $1) + }, + + // Select subnet by prefix. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX_NO_TAG, + 1, + { + OID_VARCHAR // 1 subnet_prefix + }, + "GET_SUBNET4_PREFIX_NO_TAG", + PGSQL_GET_SUBNET4_NO_TAG(WHERE s.subnet_prefix = $1) + }, + + // Select subnet by prefix without specifying server tags. + { + //PgSqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX_ANY, + 1, + { + OID_VARCHAR // 1 subnet_prefix + }, + "GET_SUBNET4_PREFIX_ANY", + PGSQL_GET_SUBNET4_ANY(WHERE s.subnet_prefix = $1) + }, + + // Select unassigned subnet by prefix. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX_UNASSIGNED, + 1, + { + OID_VARCHAR // 1 subnet_prefix + }, + "GET_SUBNET4_PREFIX_UNASSIGNED", + PGSQL_GET_SUBNET4_UNASSIGNED(AND s.subnet_prefix = $1) + }, + + // Select all subnets. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_SUBNETS4, + 0, { OID_NONE }, + "GET_ALL_SUBNETS4", + PGSQL_GET_SUBNET4_NO_TAG() + }, + + // Select all unassigned subnets. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_SUBNETS4_UNASSIGNED, + 0, { OID_NONE }, + "GET_ALL_SUBNETS4_UNASSIGNED", + PGSQL_GET_SUBNET4_UNASSIGNED() + }, + + // Select subnets having modification time later than X. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_SUBNETS4, + 1, + { + OID_TIMESTAMP // 1 modification_ts + }, + "GET_MODIFIED_SUBNETS4", + PGSQL_GET_SUBNET4_NO_TAG(WHERE s.modification_ts >= $1) + }, + + // Select modified and unassigned subnets. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_SUBNETS4_UNASSIGNED, + 1, + { + OID_TIMESTAMP // 1 modification_ts + }, + "GET_MODIFIED_SUBNETS4_UNASSIGNED", + PGSQL_GET_SUBNET4_UNASSIGNED(AND s.modification_ts >= $1) + }, + + // Select subnets belonging to a shared network. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK_SUBNETS4, + 1, + { + OID_VARCHAR // 1 share_network_name + }, + "GET_SHARED_NETWORK_SUBNETS4", + PGSQL_GET_SUBNET4_ANY(WHERE s.shared_network_name = $1) + }, + + // Select pool by address range for a server. + { + // PgSqlConfigBackendDHCPv4Impl::GET_POOL4_RANGE, + 3, + { + OID_VARCHAR, // 1 server_tag + OID_TEXT, // 2 start_address - cast as inet + OID_TEXT // 3 end_address - cast as inet + }, + "GET_POOL4_RANGE", + PGSQL_GET_POOL4_RANGE_WITH_TAG(WHERE (srv.tag = $1 OR srv.id = 1) \ + AND (p.start_address = cast($2 as inet)) \ + AND (p.end_address = cast($3 as inet))) + }, + + // Select pool by address range for any server + { + // PgSqlConfigBackendDHCPv4Impl::GET_POOL4_RANGE_ANY, + 2, + { + OID_TEXT, // 1 start_address - cast as inet + OID_TEXT // 2 end_address - cast as inet + }, + "GET_POOL4_RANGE_ANY", + PGSQL_GET_POOL4_RANGE_NO_TAG(WHERE (p.start_address = cast($1 as inet)) AND \ + (p.end_address = cast($2 as inet))) + }, + + // Select shared network by name. + { + //PgSqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK4_NAME_NO_TAG, + 1, + { + OID_VARCHAR // name of network + }, + "GET_SHARED_NETWORK4_NAME_NO_TAG", + PGSQL_GET_SHARED_NETWORK4_NO_TAG(WHERE n.name = $1) + }, + + // Select shared network by name without specifying server tags. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK4_NAME_ANY, + 1, + { + OID_VARCHAR // name of network + }, + "GET_SHARED_NETWORK4_NAME_ANY", + PGSQL_GET_SHARED_NETWORK4_ANY(WHERE n.name = $1) + }, + + // Select unassigned shared network by name. + { + //PgSqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK4_NAME_UNASSIGNED, + 1, + { + OID_VARCHAR // name of network + }, + "GET_SHARED_NETWORK4_NAME_UNASSIGNED", + PGSQL_GET_SHARED_NETWORK4_UNASSIGNED(AND n.name = $1) + }, + + // Select all shared networks. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_SHARED_NETWORKS4, + 0, { OID_NONE }, + "GET_ALL_SHARED_NETWORKS4", + PGSQL_GET_SHARED_NETWORK4_NO_TAG() + }, + + // Select all unassigned shared networks. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_SHARED_NETWORKS4_UNASSIGNED, + 0, { OID_NONE }, + "GET_ALL_SHARED_NETWORKS4_UNASSIGNED", + PGSQL_GET_SHARED_NETWORK4_UNASSIGNED() + }, + + // Select modified shared networks. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_SHARED_NETWORKS4, + 1, + { + OID_TIMESTAMP // 1 modification_ts + }, + "GET_MODIFIED_SHARED_NETWORKS4", + PGSQL_GET_SHARED_NETWORK4_NO_TAG(WHERE n.modification_ts >= $1) + }, + + // Select modified and unassigned shared networks. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_SHARED_NETWORKS4_UNASSIGNED, + 1, + { + OID_TIMESTAMP // 1 modification_ts + }, + "GET_MODIFIED_SHARED_NETWORKS4_UNASSIGNED", + PGSQL_GET_SHARED_NETWORK4_UNASSIGNED(AND n.modification_ts >= $1) + }, + + // Retrieves option definition by code and space. + { + // PgSqlConfigBackendDHCPv4Impl::GET_OPTION_DEF4_CODE_SPACE, + 3, + { + OID_VARCHAR, // 1 server_tag + OID_INT2, // 2 code + OID_VARCHAR // 3 space + }, + "GET_OPTION_DEF4_CODE_SPACE", + PGSQL_GET_OPTION_DEF(dhcp4, AND d.code = $2 AND d.space = $3) + }, + + // Retrieves all option definitions. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_OPTION_DEFS4, + 1, + { + OID_VARCHAR // 1 server_tag + }, + "GET_ALL_OPTION_DEFS4", + PGSQL_GET_OPTION_DEF(dhcp4) + }, + + // Retrieves modified option definitions. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTION_DEFS4, + // server tag is $1 + 2, + { + OID_VARCHAR, // 1 server_tag + OID_TIMESTAMP // 2 modification_ts + }, + "GET_MODIFIED_OPTION_DEFS4", + PGSQL_GET_OPTION_DEF(dhcp4, AND d.modification_ts >= $2) + }, + + // Retrieves global option by code and space. + { + // PgSqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE, + 3, + { + OID_VARCHAR, // 1 server_tag + OID_INT2, // 2 code + OID_VARCHAR // 3 space + }, + "GET_OPTION4_CODE_SPACE", + PGSQL_GET_OPTION4(AND o.scope_id = 0 AND o.code = $2 AND o.space = $3) + }, + + // Retrieves all global options. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4, + // server tag is $1 + 1, + { + OID_VARCHAR // 1 server_tag + }, + "GET_ALL_OPTIONS4", + PGSQL_GET_OPTION4(AND o.scope_id = 0) + }, + + // Retrieves modified options. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4, + // server tag is $1 + 2, + { + OID_VARCHAR, // 1 server_tag + OID_TIMESTAMP // 2 modification_ts + }, + "GET_MODIFIED_OPTIONS4", + PGSQL_GET_OPTION4(AND o.scope_id = 0 AND o.modification_ts >= $2) + }, + + // Retrieves an option for a given subnet, option code and space. + { + // PgSqlConfigBackendDHCPv4Impl::GET_OPTION4_SUBNET_ID_CODE_SPACE, + // server tag is $1 + 4, + { + OID_VARCHAR, // 1 server_tag + OID_INT8, // 2 subnet_id + OID_INT2, // 3 code + OID_VARCHAR // 4 space + }, + "GET_OPTION4_SUBNET_ID_CODE_SPACE", + PGSQL_GET_OPTION4(AND o.scope_id = 1 AND o.dhcp4_subnet_id = $2 AND o.code = $3 AND o.space = $4) + }, + + // Retrieves an option for a given pool, option code and space. + { + // PgSqlConfigBackendDHCPv4Impl::GET_OPTION4_POOL_ID_CODE_SPACE, + // server tag is $1 + 4, + { + OID_VARCHAR, // 1 server_tag + OID_INT8, // 2 pool_id + OID_INT2, // 3 code + OID_VARCHAR // 4 space + }, + "GET_OPTION4_POOL_ID_CODE_SPACE", + PGSQL_GET_OPTION4(AND o.scope_id = 5 AND o.pool_id = $2 AND o.code = $3 AND o.space = $4) + }, + + // Retrieves an option for a given shared network, option code and space. + { + // PgSqlConfigBackendDHCPv4Impl::GET_OPTION4_SHARED_NETWORK_CODE_SPACE, + 4, + { + OID_VARCHAR, // 1 server_tag + OID_VARCHAR, // 2 shared_network_name + OID_INT2, // 3 code + OID_VARCHAR // 4 space + }, + "GET_OPTION4_SHARED_NETWORK_CODE_SPACE", + PGSQL_GET_OPTION4(AND o.scope_id = 4 AND o.shared_network_name = $2 AND o.code = $3 AND o.space = $4) + }, + + // Select a client class by name. + { + // PgSqlConfigBackendDHCPv4Impl::GET_CLIENT_CLASS4_NAME, + 1, + { + OID_VARCHAR // name of class + }, + "GET_CLIENT_CLASS4_NAME", + PGSQL_GET_CLIENT_CLASS4_WITH_TAG(WHERE c.name = $1) + }, + + // Select all client classes. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_CLIENT_CLASSES4, + 0, { OID_NONE }, + "GET_ALL_CLIENT_CLASSES4", + PGSQL_GET_CLIENT_CLASS4_WITH_TAG() + }, + + // Select all unassigned client classes. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_CLIENT_CLASSES4_UNASSIGNED, + 0, { OID_NONE }, + "GET_ALL_CLIENT_CLASSES4_UNASSIGNED", + PGSQL_GET_CLIENT_CLASS4_UNASSIGNED() + }, + + // Select modified client classes. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_CLIENT_CLASSES4, + 1, + { + OID_TIMESTAMP // 1 modification_ts + }, + "GET_MODIFIED_CLIENT_CLASSES4", + PGSQL_GET_CLIENT_CLASS4_WITH_TAG(WHERE c.modification_ts >= $1) + }, + + // Select modified client classes. + { + // PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_CLIENT_CLASSES4_UNASSIGNED, + 1, + { + OID_TIMESTAMP // 1 modification_ts + }, + "GET_MODIFIED_CLIENT_CLASSES4_UNASSIGNED", + PGSQL_GET_CLIENT_CLASS4_UNASSIGNED(AND c.modification_ts >= $1) + }, + // Retrieves the most recent audit entries. + { + // PgSqlConfigBackendDHCPv4Impl::GET_AUDIT_ENTRIES4_TIME, + 3, + { + OID_VARCHAR, // 1 server_tag + OID_TIMESTAMP, // 2 modification_ts + OID_INT8 // 3 revision id + }, + "GET_AUDIT_ENTRIES4_TIME", + PGSQL_GET_AUDIT_ENTRIES_TIME(dhcp4) + }, + + // Retrieves a server by tag. + { + // PgSqlConfigBackendDHCPv4Impl::GET_SERVER4, + 1, + { + OID_VARCHAR // 1 server_tag + }, + "GET_SERVER4", + PGSQL_GET_SERVER(dhcp4) + }, + + // Retrieves all servers. + { + // PgSqlConfigBackendDHCPv4Impl::GET_ALL_SERVERS4, + 0, { OID_NONE }, + "GET_ALL_SERVERS4", + PGSQL_GET_ALL_SERVERS(dhcp4) + }, + + // Insert global parameter. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4, + 4, + { + OID_VARCHAR, // 1 name + OID_TEXT, // 2 value + OID_INT2, // 3 parameter_type + OID_TIMESTAMP // 4 modification_ts + }, + "INSERT_GLOBAL_PARAMETER4", + PGSQL_INSERT_GLOBAL_PARAMETER(dhcp4) + }, + + // Insert association of the global parameter with a server. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4_SERVER, + 3, + { + OID_INT8, // 1 parameter_id + OID_TIMESTAMP, // 2 modification_ts + OID_VARCHAR // 3 server_tag + }, + "INSERT_GLOBAL_PARAMETER4_SERVER", + PGSQL_INSERT_GLOBAL_PARAMETER_SERVER(dhcp4) + }, + + // Insert a subnet. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_SUBNET4, + 36, + { + OID_INT8, // 1 subnet_id, + OID_VARCHAR, // 2 subnet_prefix + OID_VARCHAR, // 3 interface_4o6 + OID_VARCHAR, // 4 interface_id_4o6 + OID_VARCHAR, // 5 subnet_4o6 + OID_VARCHAR, // 6 boot_file_name + OID_VARCHAR, // 7 client_class + OID_VARCHAR, // 8 interface + OID_BOOL, // 9 match_client_id + OID_TIMESTAMP, // 10 modification_ts + OID_TEXT, // 11 next_server --- cast as INET + OID_INT8, // 12 rebind_timer + OID_TEXT, // 13 relay + OID_INT8, // 14 renew_timer + OID_TEXT, // 15 require_client_classes + OID_BOOL, // 16 reservations_global + OID_VARCHAR, // 17 server_hostname + OID_VARCHAR, // 18 shared_network_name + OID_TEXT, // 19 user_context + OID_INT8, // 20 valid_lifetime + OID_INT8, // 21 min_valid_lifetime + OID_INT8, // 22 max_valid_lifetime + OID_BOOL, // 23 calculate_tee_times + OID_TEXT, // 24 t1_percent --- cast as FLOAT + OID_TEXT, // 25 t2_percent --- cast as FLOAT + OID_BOOL, // 26 authoritative + OID_BOOL, // 27 ddns_send_updates + OID_BOOL, // 28 ddns_override_no_update + OID_BOOL, // 29 ddns_override_client_update + OID_INT8, // 30 ddns_replace_client_name + OID_VARCHAR, // 31 ddns_generated_prefix + OID_VARCHAR, // 32 ddns_qualifying_suffix + OID_BOOL, // 33 reservations_in_subnet + OID_BOOL, // 34 reservations_out_of_pool + OID_TEXT, // 35 cache_threshold -- cast as FLOAT + OID_INT8 // 36 cache_max_age" + }, + "INSERT_SUBNET4", + "INSERT INTO dhcp4_subnet(" + " subnet_id," + " subnet_prefix," + " interface_4o6," + " interface_id_4o6," + " subnet_4o6," + " boot_file_name," + " client_class," + " interface," + " match_client_id," + " modification_ts," + " next_server," + " rebind_timer," + " relay," + " renew_timer," + " require_client_classes," + " reservations_global," + " server_hostname," + " shared_network_name," + " user_context," + " valid_lifetime," + " min_valid_lifetime," + " max_valid_lifetime," + " calculate_tee_times," + " t1_percent," + " t2_percent," + " authoritative," + " ddns_send_updates," + " ddns_override_no_update," + " ddns_override_client_update," + " ddns_replace_client_name," + " ddns_generated_prefix," + " ddns_qualifying_suffix," + " reservations_in_subnet," + " reservations_out_of_pool," + " cache_threshold," + " cache_max_age" + ") VALUES (" + "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, " + "cast($11 as inet), $12, $13, $14, $15, $16, $17, $18, cast($19 as json), $20, " + "$21, $22, $23, cast($24 as float), cast($25 as float), $26, $27, $28, $29, $30, " + "$31, $32, $33, $34, cast($35 as float), $36" + ")" + }, + + // Insert association of the subnet with a server. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_SUBNET4_SERVER, + 3, + { + OID_INT8, // 1 subnet_id + OID_TIMESTAMP, // 2 modification_ts + OID_VARCHAR // 3 server_tag + }, + "INSERT_SUBNET4_SERVER", + PGSQL_INSERT_SUBNET_SERVER(dhcp4) + }, + + // Insert pool for a subnet. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_POOL4, + 7, + { + OID_TEXT, // 1 start_address - cast as inet + OID_TEXT, // 2 end_address - cast as inet + OID_INT8, // 3 subnet_id + OID_VARCHAR, // 4 client_class + OID_TEXT, // 5 require_client_classes + OID_TEXT, // 6 user_context + OID_TIMESTAMP // 7 modification_ts + }, + "INSERT_POOL4", + PGSQL_INSERT_POOL(dhcp4) + }, + + // Insert a shared network. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_SHARED_NETWORK4, + 31, + { + OID_VARCHAR, // 1 name, + OID_VARCHAR, // 2 client_class, + OID_VARCHAR, // 3 interface, + OID_BOOL, // 4 match_client_id, + OID_TIMESTAMP, // 5 modification_ts, + OID_INT8, // 6 rebind_timer, + OID_TEXT, // 7 relay, + OID_INT8, // 8 renew_timer, + OID_TEXT, // 9 require_client_classes, + OID_BOOL, // 10 reservations_global, + OID_TEXT, // 11 user_context, + OID_INT8, // 12 valid_lifetime, + OID_INT8, // 13 min_valid_lifetime, + OID_INT8, // 14 max_valid_lifetime, + OID_BOOL, // 15 calculate_tee_times, + OID_TEXT, // 16 t1_percent - cast as float + OID_TEXT, // 17 t2_percent - cast as float + OID_BOOL, // 18 authoritative, + OID_VARCHAR, // 19 boot_file_name, + OID_TEXT, // 20 next_server - cast as inet + OID_VARCHAR, // 21 server_hostname, + OID_BOOL, // 22 ddns_send_updates, + OID_BOOL, // 23 ddns_override_no_update, + OID_BOOL, // 24 ddns_override_client_update, + OID_INT8, // 25 ddns_replace_client_name, + OID_VARCHAR, // 26 ddns_generated_prefix, + OID_VARCHAR, // 27 ddns_qualifying_suffix, + OID_BOOL, // 28 reservations_in_subnet, + OID_BOOL, // 29 reservations_out_of_pool, + OID_TEXT, // 30 cache_threshold - cast as float + OID_INT8 // 31 cache_max_age + }, + "INSERT_SHARED_NETWORK4", + "INSERT INTO dhcp4_shared_network(" + " name," + " client_class," + " interface," + " match_client_id," + " modification_ts," + " rebind_timer," + " relay," + " renew_timer," + " require_client_classes," + " reservations_global," + " user_context," + " valid_lifetime," + " min_valid_lifetime," + " max_valid_lifetime," + " calculate_tee_times," + " t1_percent," + " t2_percent," + " authoritative," + " boot_file_name," + " next_server," + " server_hostname," + " ddns_send_updates," + " ddns_override_no_update," + " ddns_override_client_update," + " ddns_replace_client_name," + " ddns_generated_prefix," + " ddns_qualifying_suffix," + " reservations_in_subnet," + " reservations_out_of_pool," + " cache_threshold," + " cache_max_age" + ") VALUES (" + "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10," + "cast($11 as json), $12, $13, $14, $15, " + "cast($16 as float), cast($17 as float), $18, $19, cast($20 as inet), " + "$21, $22, $23, $24, $25, $26, $27, $28, $29, cast($30 as float), $31" + ")" + }, + + // Insert association of the shared network with a server. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_SHARED_NETWORK4_SERVER, + 3, + { + OID_VARCHAR, // 1 shared_network_name + OID_TIMESTAMP, // 2 modification_ts + OID_VARCHAR // 3 server_tag + }, + "INSERT_SHARED_NETWORK4_SERVER", + PGSQL_INSERT_SHARED_NETWORK_SERVER(dhcp4) + }, + + // Insert option definition. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION_DEF4, + 10, + { + OID_INT2, // 1 code + OID_VARCHAR, // 2 name + OID_VARCHAR, // 3 space + OID_INT2, // 4 type + OID_TIMESTAMP, // 5 modification_ts + OID_BOOL, // 6 is_array + OID_VARCHAR, // 7 encapsulate + OID_VARCHAR, // 8 record_types + OID_VARCHAR, // 9 user_context + OID_INT8 // 10 class_id" -- column is missing from dhcpX_option_def tables + }, + "INSERT_OPTION_DEF4", + PGSQL_INSERT_OPTION_DEF(dhcp4) + }, + + // Insert option definition for client class. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION_DEF4_CLIENT_CLASS, + 10, + { + OID_INT2, // 1 code + OID_VARCHAR, // 2 name + OID_VARCHAR, // 3 space + OID_INT2, // 4 type + OID_TIMESTAMP, // 5 modification_ts + OID_BOOL, // 6 is_array + OID_VARCHAR, // 7 encapsulate + OID_VARCHAR, // 8 record_types + OID_VARCHAR, // 9 user_context + OID_VARCHAR // 10 class name for where clause + }, + "INSERT_OPTION_DEF4_CLIENT_CLASS", + PGSQL_INSERT_OPTION_DEF_CLIENT_CLASS(dhcp4) + }, + + // Insert association of the option definition with a server. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION_DEF4_SERVER, + 3, + { + OID_INT8, // 1 option_def_id + OID_TIMESTAMP, // 2 modification_ts + OID_VARCHAR // 3 server_tag + }, + "INSERT_OPTION_DEF4_SERVER", + PGSQL_INSERT_OPTION_DEF_SERVER(dhcp4) + }, + + // Insert subnet specific option. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION4, + 12, + { + OID_INT2, // 1 code + OID_BYTEA, // 2 value + OID_TEXT, // 3 formatted_value + OID_VARCHAR, // 4 space + OID_BOOL, // 5 persistent + OID_VARCHAR, // 6 dhcp_client_class + OID_INT8, // 7 dhcp4_subnet_id + OID_INT2, // 8 scope_id + OID_TEXT, // 9 user_context + OID_VARCHAR, // 10 shared_network_name + OID_INT8, // 11 pool_id + OID_TIMESTAMP // 12 modification_ts + }, + "INSERT_OPTION4", + PGSQL_INSERT_OPTION4() + }, + + // Insert association of the DHCP option with a server. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION4_SERVER, + 3, + { + OID_INT8, // 1 option_id + OID_TIMESTAMP, // 2 modification_ts + OID_VARCHAR // 3 server_tag + }, + "INSERT_OPTION4_SERVER", + PGSQL_INSERT_OPTION_SERVER(dhcp4) + }, + + // Insert client class. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_CLIENT_CLASS4, + 12, + { + OID_VARCHAR, // 1 name + OID_TEXT, // 2 test + OID_TEXT, // 3 next_server - cast as inet + OID_VARCHAR, // 4 server_hostname + OID_VARCHAR, // 5 boot_file_name + OID_BOOL, // 6 only_if_required + OID_INT8, // 7 valid_lifetime + OID_INT8, // 8 min_valid_lifetime + OID_INT8, // 9 max_valid_lifetime + OID_BOOL, // 10 depend_on_known_directly + OID_VARCHAR, // 11 follow_class_name + OID_TIMESTAMP // 12 modification_ts + }, + "INSERT_CLIENT_CLASS4", + "INSERT INTO dhcp4_client_class(" + " name," + " test," + " next_server," + " server_hostname," + " boot_file_name," + " only_if_required," + " valid_lifetime," + " min_valid_lifetime," + " max_valid_lifetime," + " depend_on_known_directly," + " follow_class_name," + " modification_ts" + ") VALUES (" + "$1, $2, cast($3 as inet), $4, $5, $6, $7, $8, $9, $10, $11, $12" + ")" + }, + + // Insert association of a client class with a server. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_CLIENT_CLASS4_SERVER, + 3, + { + OID_VARCHAR, // 1 class_name + OID_TIMESTAMP, // 2 modification_ts + OID_VARCHAR // 3 server_tag + }, + "INSERT_CLIENT_CLASS4_SERVER", + PGSQL_INSERT_CLIENT_CLASS_SERVER(dhcp4) + }, + + // Insert client class dependency. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_CLIENT_CLASS4_DEPENDENCY, + 2, + { + OID_VARCHAR, // class name + OID_VARCHAR // dependency class name + }, + "INSERT_CLIENT_CLASS4_DEPENDENCY", + PGSQL_INSERT_CLIENT_CLASS_DEPENDENCY(dhcp4) + }, + + // Insert server with server tag and description. + { + // PgSqlConfigBackendDHCPv4Impl::INSERT_SERVER4, + 3, + { + OID_VARCHAR, // 1 server_tag + OID_VARCHAR, // 2 description + OID_TIMESTAMP // 3 modification_ts + }, + "INSERT_SERVER4", + PGSQL_INSERT_SERVER(dhcp4) + }, + + // Update existing global parameter. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_GLOBAL_PARAMETER4, + 6, + { + OID_VARCHAR, // 1 name + OID_TEXT, // 2 value + OID_INT2, // 3 parameter_type + OID_TIMESTAMP, // 4 modification_ts + OID_VARCHAR, // 5 server_tag + OID_VARCHAR, // 6 name (of global to update) + }, + "UPDATE_GLOBAL_PARAMETER4", + PGSQL_UPDATE_GLOBAL_PARAMETER(dhcp4) + }, + + // Update existing subnet. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_SUBNET4, + 38, + { + OID_INT8, // 1 subnet_id, + OID_VARCHAR, // 2 subnet_prefix + OID_VARCHAR, // 3 interface_4o6 + OID_VARCHAR, // 4 interface_id_4o6 + OID_VARCHAR, // 5 subnet_4o6 + OID_VARCHAR, // 6 boot_file_name + OID_VARCHAR, // 7 client_class + OID_VARCHAR, // 8 interface + OID_BOOL, // 9 match_client_id + OID_TIMESTAMP, // 10 modification_ts + OID_TEXT, // 11 next_server - cast as INET + OID_INT8, // 12 rebind_timer + OID_TEXT, // 13 relay + OID_INT8, // 14 renew_timer + OID_TEXT, // 15 require_client_classes + OID_BOOL, // 16 reservations_global + OID_VARCHAR, // 17 server_hostname + OID_VARCHAR, // 18 shared_network_name + OID_TEXT, // 19 user_context + OID_INT8, // 20 valid_lifetime + OID_INT8, // 21 min_valid_lifetime + OID_INT8, // 22 max_valid_lifetime + OID_BOOL, // 23 calculate_tee_times + OID_TEXT, // 24 t1_percent - cast as FLOAT + OID_TEXT, // 25 t2_percent - cast as FLOAT + OID_BOOL, // 26 authoritative + OID_BOOL, // 27 ddns_send_updates + OID_BOOL, // 28 ddns_override_no_update + OID_BOOL, // 29 ddns_override_client_update + OID_INT8, // 30 ddns_replace_client_name + OID_VARCHAR, // 31 ddns_generated_prefix + OID_VARCHAR, // 32 ddns_qualifying_suffix + OID_BOOL, // 33 reservations_in_subnet + OID_BOOL, // 34 reservations_out_of_pool + OID_TEXT, // 35 cache_threshold - cast as FLOAT + OID_INT8, // 36 cache_max_age" + OID_INT8, // 37 subnet_id (of subnet to update) + OID_VARCHAR // 38 subnet_prefix (of subnet to update) + }, + "UPDATE_SUBNET4,", + "UPDATE dhcp4_subnet SET" + " subnet_id = $1," + " subnet_prefix = $2," + " interface_4o6 = $3," + " interface_id_4o6 = $4," + " subnet_4o6 = $5," + " boot_file_name = $6," + " client_class = $7," + " interface = $8," + " match_client_id = $9," + " modification_ts = $10," + " next_server = cast($11 as inet)," + " rebind_timer = $12," + " relay = $13," + " renew_timer = $14," + " require_client_classes = $15," + " reservations_global = $16," + " server_hostname = $17," + " shared_network_name = $18," + " user_context = cast($19 as json)," + " valid_lifetime = $20," + " min_valid_lifetime = $21," + " max_valid_lifetime = $22," + " calculate_tee_times = $23," + " t1_percent = cast($24 as float)," + " t2_percent = cast($25 as float)," + " authoritative = $26," + " ddns_send_updates = $27," + " ddns_override_no_update = $28," + " ddns_override_client_update = $29," + " ddns_replace_client_name = $30," + " ddns_generated_prefix = $31," + " ddns_qualifying_suffix = $32," + " reservations_in_subnet = $33," + " reservations_out_of_pool = $34," + " cache_threshold = cast($35 as float)," + " cache_max_age = $36 " + "WHERE subnet_id = $37 OR subnet_prefix = $38" + }, + + // Update existing shared network. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_SHARED_NETWORK4, + 32, + { + OID_VARCHAR, // 1 name, + OID_VARCHAR, // 2 client_class, + OID_VARCHAR, // 3 interface, + OID_BOOL, // 4 match_client_id, + OID_TIMESTAMP, // 5 modification_ts, + OID_INT8, // 6 rebind_timer, + OID_TEXT, // 7 relay, + OID_INT8, // 8 renew_timer, + OID_TEXT, // 9 require_client_classes, + OID_BOOL, // 10 reservations_global, + OID_TEXT, // 11 user_context, + OID_INT8, // 12 valid_lifetime, + OID_INT8, // 13 min_valid_lifetime, + OID_INT8, // 14 max_valid_lifetime, + OID_BOOL, // 15 calculate_tee_times, + OID_TEXT, // 16 t1_percent - cast as float + OID_TEXT, // 17 t2_percent - cast as float + OID_BOOL, // 18 authoritative, + OID_VARCHAR, // 19 boot_file_name, + OID_TEXT, // 20 next_server - cast as inet + OID_VARCHAR, // 21 server_hostname, + OID_BOOL, // 22 ddns_send_updates, + OID_BOOL, // 23 ddns_override_no_update, + OID_BOOL, // 24 ddns_override_client_update, + OID_INT8, // 25 ddns_replace_client_name, + OID_VARCHAR, // 26 ddns_generated_prefix, + OID_VARCHAR, // 27 ddns_qualifying_suffix, + OID_BOOL, // 28 reservations_in_subnet, + OID_BOOL, // 29 reservations_out_of_pool, + OID_TEXT, // 30 cache_threshold - cast as float + OID_INT8, // 31 cache_max_age + OID_VARCHAR // 32 name (of network to update) + }, + "UPDATE_SHARED_NETWORK4", + "UPDATE dhcp4_shared_network SET" + " name = $1," + " client_class = $2," + " interface = $3," + " match_client_id = $4," + " modification_ts = $5," + " rebind_timer = $6," + " relay = $7," + " renew_timer = $8," + " require_client_classes = $9," + " reservations_global = $10," + " user_context = cast($11 as json)," + " valid_lifetime = $12," + " min_valid_lifetime = $13," + " max_valid_lifetime = $14," + " calculate_tee_times = $15," + " t1_percent = cast($16 as float)," + " t2_percent = cast($17 as float)," + " authoritative = $18," + " boot_file_name = $19," + " next_server = cast($20 as inet)," + " server_hostname = $21," + " ddns_send_updates = $22," + " ddns_override_no_update = $23," + " ddns_override_client_update = $24," + " ddns_replace_client_name = $25," + " ddns_generated_prefix = $26," + " ddns_qualifying_suffix = $27," + " reservations_in_subnet = $28," + " reservations_out_of_pool = $29," + " cache_threshold = cast($30 as float)," + " cache_max_age = $31 " + "WHERE name = $32" + }, + + // Update existing option definition. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION_DEF4, + 13, + { + OID_INT2, // 1 code + OID_VARCHAR, // 2 name + OID_VARCHAR, // 3 space + OID_INT2, // 4 type + OID_TIMESTAMP, // 5 modification_ts + OID_BOOL, // 6 is_array + OID_VARCHAR, // 7 encapsulate + OID_VARCHAR, // 8 record_types + OID_TEXT, // 9 user_context + OID_INT2, // 10 class_id + OID_VARCHAR, // 11 server_tag + OID_INT2, // 12 code (of option to update) + OID_VARCHAR, // 13 space (of option to update) + }, + "UPDATE_OPTION_DEF4", + PGSQL_UPDATE_OPTION_DEF(dhcp4) + }, + + // Update existing option definition. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION_DEF4_CLIENT_CLASS, + 13, + { + OID_INT2, // 1 code + OID_VARCHAR, // 2 name + OID_VARCHAR, // 3 space + OID_INT2, // 4 type + OID_TIMESTAMP, // 5 modification_ts + OID_BOOL, // 6 is_array + OID_VARCHAR, // 7 encapsulate + OID_VARCHAR, // 8 record_types + OID_TEXT, // 9 user_context + OID_VARCHAR, // 10 name (of class option belongs to) + OID_VARCHAR, // 11 server_tag + OID_INT2, // 12 code (of option to update) + OID_VARCHAR, // 13 space (of option to update) + }, + "UPDATE_OPTION_DEF4_CLIENT_CLASS", + PGSQL_UPDATE_OPTION_DEF_CLIENT_CLASS(dhcp4) + }, + + // Update existing global option. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4, + 15, + { + OID_INT2, // 1 code + OID_BYTEA, // 2 value + OID_TEXT, // 3 formatted_value + OID_VARCHAR, // 4 space + OID_BOOL, // 5 persistent + OID_VARCHAR, // 6 dhcp_client_class + OID_INT8, // 7 dhcp4_subnet_id + OID_INT2, // 8 scope_id + OID_TEXT, // 9 user_context + OID_VARCHAR, // 10 shared_network_name + OID_INT8, // 11 pool_id + OID_TIMESTAMP, // 12 modification_ts + OID_VARCHAR, // 13 server_tag + OID_INT2, // 14 code (of option to update) + OID_VARCHAR, // 15 space (of option to update) + }, + "UPDATE_OPTION4", + PGSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = $14 AND o.space = $15) + }, + + // Update existing subnet level option. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID, + 15, + { + OID_INT2, // 1 code + OID_BYTEA, // 2 value + OID_TEXT, // 3 formatted_value + OID_VARCHAR, // 4 space + OID_BOOL, // 5 persistent + OID_VARCHAR, // 6 dhcp_client_class + OID_INT8, // 7 dhcp4_subnet_id + OID_INT2, // 8 scope_id + OID_TEXT, // 9 user_context + OID_VARCHAR, // 10 shared_network_name + OID_INT8, // 11 pool_id + OID_TIMESTAMP, // 12 modification_ts + OID_INT8, // 13 subnet_id (of option to update) + OID_INT2, // 14 code (of option to update) + OID_VARCHAR // 15 space (of option to update) + }, + "UPDATE_OPTION4_SUBNET_ID", + PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = $13 AND o.code = $14 AND o.space = $15) + }, + + // Update existing pool level option. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID, + 15, + { + OID_INT2, // 1 code + OID_BYTEA, // 2 value + OID_TEXT, // 3 formatted_value + OID_VARCHAR, // 4 space + OID_BOOL, // 5 persistent + OID_VARCHAR, // 6 dhcp_client_class + OID_INT8, // 7 dhcp4_subnet_id + OID_INT2, // 8 scope_id + OID_TEXT, // 9 user_context + OID_VARCHAR, // 10 shared_network_name + OID_INT8, // 11 pool_id + OID_TIMESTAMP, // 12 modification_ts + OID_INT8, // 13 pool_id (of option to update) + OID_INT2, // 14 code (of option to update) + OID_VARCHAR // 15 space (of option to update) + }, + "UPDATE_OPTION4_POOL_ID", + PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 5 AND o.pool_id = $13 AND o.code = $14 AND o.space = $15) + }, + + // Update existing shared network level option. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SHARED_NETWORK, + 15, + { + OID_INT2, // 1 code + OID_BYTEA, // 2 value + OID_TEXT, // 3 formatted_value + OID_VARCHAR, // 4 space + OID_BOOL, // 5 persistent + OID_VARCHAR, // 6 dhcp_client_class + OID_INT8, // 7 dhcp4_subnet_id + OID_INT2, // 8 scope_id + OID_TEXT, // 9 user_context + OID_VARCHAR, // 10 shared_network_name + OID_INT8, // 11 pool_id + OID_TIMESTAMP, // 12 modification_ts + OID_VARCHAR, // 13 shared_network_name (of option to update) + OID_INT2, // 14 code (of option to update) + OID_VARCHAR // 15 space (of option to update) + }, + "UPDATE_OPTION4_SHARED_NETWORK", + PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $13 AND o.code = $14 AND o.space = $15) + }, + + // Update existing client class level option. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_CLIENT_CLASS, + 15, + { + OID_INT2, // 1 code + OID_BYTEA, // 2 value + OID_TEXT, // 3 formatted_value + OID_VARCHAR, // 4 space + OID_BOOL, // 5 persistent + OID_VARCHAR, // 6 dhcp_client_class + OID_INT8, // 7 dhcp4_subnet_id + OID_INT2, // 8 scope_id + OID_TEXT, // 9 user_context + OID_VARCHAR, // 10 shared_network_name + OID_INT8, // 11 pool_id + OID_TIMESTAMP, // 12 modification_ts + OID_VARCHAR, // 13 dhcp_client_class (of option to update) + OID_INT2, // 14 code (of option to update) + OID_VARCHAR, // 15 space (of option to update) + }, + "UPDATE_OPTION4_CLIENT_CLASS", + PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $13 AND o.code = $14 AND o.space = $15) + }, + + // Update existing client class with specifying its position. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_CLIENT_CLASS4, + 13, + { + OID_VARCHAR, // 1 name + OID_TEXT, // 2 test + OID_TEXT, // 3 next_server - cast as inet + OID_VARCHAR, // 4 server_hostname + OID_VARCHAR, // 5 boot_file_name + OID_BOOL, // 6 only_if_required + OID_INT8, // 7 valid_lifetime + OID_INT8, // 8 min_valid_lifetime + OID_INT8, // 9 max_valid_lifetime + OID_BOOL, // 10 depend_on_known_directly + OID_TIMESTAMP, // 11 modification_ts + OID_VARCHAR, // 12 name (of class to update) + OID_VARCHAR // 13 follow_class_name + }, + "UPDATE_CLIENT_CLASS4", + PGSQL_UPDATE_CLIENT_CLASS4("follow_class_name = $13,") + }, + + // Update existing client class without specifying its position. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_CLIENT_CLASS4_SAME_POSITION, + 12, + { + OID_VARCHAR, // 1 name + OID_TEXT, // 2 test + OID_TEXT, // 3 next_server - cast as inet + OID_VARCHAR, // 4 server_hostname + OID_VARCHAR, // 5 boot_file_name + OID_BOOL, // 6 only_if_required + OID_INT8, // 7 valid_lifetime + OID_INT8, // 8 min_valid_lifetime + OID_INT8, // 9 max_valid_lifetime + OID_BOOL, // 10 depend_on_known_directly + OID_TIMESTAMP, // 11 modification_ts + OID_VARCHAR // 12 name (of class to update) + }, + "UPDATE_CLIENT_CLASS4_SAME_POSITION", + PGSQL_UPDATE_CLIENT_CLASS4("") + }, + + // Update existing server, e.g. server description. + { + // PgSqlConfigBackendDHCPv4Impl::UPDATE_SERVER4, + 4, + { + OID_VARCHAR, // 1 tag + OID_VARCHAR, // 2 description + OID_TIMESTAMP, // 3 modification_ts + OID_VARCHAR // 4 tag (of server to update) + }, + "UPDATE_SERVER4", + PGSQL_UPDATE_SERVER(dhcp4) + }, + + // Delete global parameter by name. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_GLOBAL_PARAMETER4, + // args: server_tag, name + 2, + { + OID_VARCHAR, // 1 server_tag + OID_VARCHAR // 2 name of parameter + }, + "DELETE_GLOBAL_PARAMETER4", + PGSQL_DELETE_GLOBAL_PARAMETER(dhcp4, AND g.name = $2) + }, + + // Delete all global parameters. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_GLOBAL_PARAMETERS4, + 1, + { + OID_VARCHAR // 1 server_tag + }, + "DELETE_ALL_GLOBAL_PARAMETERS4", + PGSQL_DELETE_GLOBAL_PARAMETER(dhcp4) + }, + + // Delete all global parameters which are unassigned to any servers. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED, + 0, { OID_NONE }, + "DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED", + PGSQL_DELETE_GLOBAL_PARAMETER_UNASSIGNED(dhcp4) + }, + + // Delete subnet by id with specifying server tag. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID_WITH_TAG, + // args: server_tag, subnet_id + 2, + { + OID_VARCHAR, // 1 server_tag + OID_INT8 // 2 subnet_id + }, + "DELETE_SUBNET4_ID_WITH_TAG", + PGSQL_DELETE_SUBNET_WITH_TAG(dhcp4, AND s.subnet_id = $2) + }, + + // Delete subnet by id without specifying server tag. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID_ANY, + 1, + { + OID_INT8 // 1 subnet_id + }, + "DELETE_SUBNET4_ID_ANY", + PGSQL_DELETE_SUBNET_ANY(dhcp4, WHERE s.subnet_id = $1) + }, + + // Delete subnet by prefix with specifying server tag. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX_WITH_TAG, + 2, + { + OID_VARCHAR, // 1 server_tag + OID_VARCHAR // 2 subnet_prefix + }, + "DELETE_SUBNET4_PREFIX_WITH_TAG", + PGSQL_DELETE_SUBNET_WITH_TAG(dhcp4, AND s.subnet_prefix = $2) + }, + + // Delete subnet by prefix without specifying server tag. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX_ANY, + 1, + { + OID_VARCHAR // 1 subnet_prefix + }, + "DELETE_SUBNET4_PREFIX_ANY", + PGSQL_DELETE_SUBNET_ANY(dhcp4, WHERE s.subnet_prefix = $1) + }, + + // Delete all subnets. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4, + 1, + { + OID_VARCHAR // 1 server_tag + }, + "DELETE_ALL_SUBNETS4", + PGSQL_DELETE_SUBNET_WITH_TAG(dhcp4) + }, + + // Delete all unassigned subnets. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_UNASSIGNED, + 0, { OID_NONE }, + "DELETE_ALL_SUBNETS4_UNASSIGNED", + PGSQL_DELETE_SUBNET_UNASSIGNED(dhcp4) + }, + + // Delete all subnets for a shared network. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME, + 1, + { + OID_VARCHAR // 1 shared_network_name + }, + "DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME", + PGSQL_DELETE_SUBNET_ANY(dhcp4, WHERE s.shared_network_name = $1) + }, + + // Delete associations of a subnet with server. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_SERVER, + 1, + { + OID_INT8 // 1 subnet_id + }, + "DELETE_SUBNET4_SERVER", + PGSQL_DELETE_SUBNET_SERVER(dhcp4), + }, + + // Delete pools for a subnet. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_POOLS4, + // args: subnet_id, subnet_prefix + 2, + { + OID_INT8, // 1 subnet_id + OID_VARCHAR // 2 subnet_prefix + }, + "DELETE_POOLS4", + PGSQL_DELETE_POOLS(dhcp4) + }, + + // Delete shared network by name with specifying server tag. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SHARED_NETWORK4_NAME_WITH_TAG, + 2, + { + OID_VARCHAR, // 1 server_tag + OID_VARCHAR // 2 shared_network_name + }, + "DELETE_SHARED_NETWORK4_NAME_WITH_TAG", + PGSQL_DELETE_SHARED_NETWORK_WITH_TAG(dhcp4, AND n.name = $2) + }, + + // Delete shared network by name without specifying server tag. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SHARED_NETWORK4_NAME_ANY, + 1, + { + OID_VARCHAR // 1 shared_network_name + }, + "DELETE_SHARED_NETWORK4_NAME_ANY", + PGSQL_DELETE_SHARED_NETWORK_ANY(dhcp4, WHERE n.name = $1) + }, + + // Delete all shared networks. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SHARED_NETWORKS4, + 1, + { + OID_VARCHAR // 1 server_tag + }, + "DELETE_ALL_SHARED_NETWORKS4", + PGSQL_DELETE_SHARED_NETWORK_WITH_TAG(dhcp4) + }, + + // Delete all unassigned shared networks. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SHARED_NETWORKS4_UNASSIGNED, + 0, { OID_NONE }, + "DELETE_ALL_SHARED_NETWORKS4_UNASSIGNED", + PGSQL_DELETE_SHARED_NETWORK_UNASSIGNED(dhcp4) + }, + + // Delete associations of a shared network with server. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SHARED_NETWORK4_SERVER, + 1, + { + OID_VARCHAR // 1 shared_network_name + }, + "DELETE_SHARED_NETWORK4_SERVER", + PGSQL_DELETE_SHARED_NETWORK_SERVER(dhcp4) + }, + + // Delete option definition. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION_DEF4_CODE_NAME, + 3, + { + OID_VARCHAR, // 1 server_tag + OID_INT2, // 2 code + OID_VARCHAR // 3 space + }, + "DELETE_OPTION_DEF4_CODE_NAME", + PGSQL_DELETE_OPTION_DEF(dhcp4, AND code = $2 AND space = $3) + }, + + // Delete all option definitions. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_OPTION_DEFS4, + // args: server_tag + 1, + { + OID_VARCHAR // 1 server_tag + }, + "DELETE_ALL_OPTION_DEFS4", + PGSQL_DELETE_OPTION_DEF(dhcp4) + }, + + + // Delete all option definitions which are assigned to no servers. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_OPTION_DEFS4_UNASSIGNED, + 0, { OID_NONE }, + "DELETE_ALL_OPTION_DEFS4_UNASSIGNED", + PGSQL_DELETE_OPTION_DEF_UNASSIGNED(dhcp4) + }, + + // Delete client class specific option definitions. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION_DEFS4_CLIENT_CLASS, + 1, + { + OID_VARCHAR // 1 class name + }, + "DELETE_OPTION_DEFS4_CLIENT_CLASS", + PGSQL_DELETE_OPTION_DEFS_CLIENT_CLASS(dhcp4) + }, + + // Delete single global option. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4, + 3, + { + OID_VARCHAR, // 1 server_tag + OID_INT2, // 2 code + OID_VARCHAR // 3 space + }, + "DELETE_OPTION4", + PGSQL_DELETE_OPTION_WITH_TAG(dhcp4, AND o.scope_id = 0 AND o.code = $2 AND o.space = $3) + }, + + // Delete all global options which are unassigned to any servers. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED, + 1, + { + OID_INT2 // 1 scope_id + }, + "DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED", + PGSQL_DELETE_OPTION_UNASSIGNED(dhcp4, AND scope_id = $1) + }, + + // Delete single option from a subnet. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SUBNET_ID, + 3, + { + OID_INT8, // 1 subnet_id + OID_INT2, // 2 code + OID_VARCHAR // 3 space + }, + "DELETE_OPTION4_SUBNET_ID", + PGSQL_DELETE_OPTION_NO_TAG(dhcp4, + WHERE o.scope_id = 1 AND o.dhcp4_subnet_id = $1 AND o.code = $2 AND o.space = $3) + }, + + // Delete single option from a pool. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_POOL_RANGE, + 4, + { + OID_TEXT, // 1 start_address - cast as inet + OID_TEXT, // 2 start_address - cast as inet + OID_INT2, // 3 code + OID_VARCHAR // 4 space + }, + "DELETE_OPTION4_POOL_RANGE", + PGSQL_DELETE_OPTION_POOL_RANGE(dhcp4, o.scope_id = 5 AND o.code = $3 AND o.space = $4) + }, + + // Delete single option from a shared network. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SHARED_NETWORK, + 3, + { + OID_VARCHAR, // 1 shared_network_name + OID_INT2, // 2 code + OID_VARCHAR // 3 space + }, + "DELETE_OPTION4_SHARED_NETWORK", + PGSQL_DELETE_OPTION_NO_TAG(dhcp4, + WHERE o.scope_id = 4 AND o.shared_network_name = $1 AND o.code = $2 AND o.space = $3) + }, + + // Delete options belonging to a subnet. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTIONS4_SUBNET_ID_PREFIX, + 2, + { + OID_INT8, // 1 subnet_id + OID_VARCHAR // 2 subnet_prefix + }, + "DELETE_OPTIONS4_SUBNET_ID_PREFIX", + PGSQL_DELETE_OPTION_SUBNET_ID_PREFIX(dhcp4) + }, + + // Delete options belonging to a shared_network. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTIONS4_SHARED_NETWORK, + 1, + { + OID_VARCHAR // shared_network_name + }, + "DELETE_OPTIONS4_SHARED_NETWORK", + PGSQL_DELETE_OPTION_NO_TAG(dhcp4, WHERE o.scope_id = 4 AND o.shared_network_name = $1) + }, + + // Delete options belonging to a client class. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_OPTIONS4_CLIENT_CLASS, + 1, + { + OID_VARCHAR // dhcp_client_class + }, + "DELETE_OPTIONS4_CLIENT_CLASS", + PGSQL_DELETE_OPTION_NO_TAG(dhcp4, WHERE o.scope_id = 2 AND o.dhcp_client_class = $1) + }, + + // Delete all dependencies of a client class. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_CLIENT_CLASS4_DEPENDENCY, + 1, + { + OID_VARCHAR, // 1 classname + }, + "DELETE_CLIENT_CLASS4_DEPENDENCY", + PGSQL_DELETE_CLIENT_CLASS_DEPENDENCY(dhcp4) + }, + + // Delete associations of a client class with server. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_CLIENT_CLASS4_SERVER, + 1, + { + OID_VARCHAR // 1 classname + }, + "DELETE_CLIENT_CLASS4_SERVER", + PGSQL_DELETE_CLIENT_CLASS_SERVER(dhcp4), + }, + + // Delete all client classes. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_CLIENT_CLASSES4, + 1, + { + OID_VARCHAR // 1 server_tag + }, + "DELETE_ALL_CLIENT_CLASSES4", + PGSQL_DELETE_CLIENT_CLASS_WITH_TAG(dhcp4) + }, + + // Delete all unassigned client classes. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_CLIENT_CLASSES4_UNASSIGNED, + 0, { OID_NONE }, + "DELETE_ALL_CLIENT_CLASSES4_UNASSIGNED", + PGSQL_DELETE_CLIENT_CLASS_UNASSIGNED(dhcp4) + }, + + // Delete specified client class. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_CLIENT_CLASS4, + 2, + { + OID_VARCHAR, // 1 server_tag + OID_VARCHAR // 2 name + }, + "DELETE_CLIENT_CLASS4", + PGSQL_DELETE_CLIENT_CLASS_WITH_TAG(dhcp4, AND name = $2) + }, + + // Delete any client class with a given name. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_CLIENT_CLASS4_ANY, + 1, + { + OID_VARCHAR // 1 name + }, + "DELETE_CLIENT_CLASS4_ANY", + PGSQL_DELETE_CLIENT_CLASS_ANY(dhcp4, AND name = $1) + }, + + // Delete a server by tag. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_SERVER4, + 1, + { + OID_VARCHAR // server_tag + }, + "DELETE_SERVER4", + PGSQL_DELETE_SERVER(dhcp4) + }, + + // Deletes all servers except logical server 'all'. + { + // PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SERVERS4, + 0, { OID_NONE }, + "DELETE_ALL_SERVERS4", + PGSQL_DELETE_ALL_SERVERS(dhcp4) + }, + + // Fetches the last sequence id for the given table and column. + { + // PgSqlConfigBackendDHCPv4Impl::GET_LAST_INSERT_ID4, + // args are: table name, sequence column name + 2, { OID_VARCHAR, OID_VARCHAR }, + "GET_LAST_INSERT_ID4", + "SELECT CURRVAL(PG_GET_SERIAL_SEQUENCE($1, $2))" + } +} +}; + +} // end anonymous namespace + +PgSqlConfigBackendDHCPv4Impl::PgSqlConfigBackendDHCPv4Impl(const DatabaseConnection::ParameterMap& parameters) + : PgSqlConfigBackendImpl(parameters, &PgSqlConfigBackendDHCPv4Impl::dbReconnect) { + // Prepare query statements. Those are will be only used to retrieve + // information from the database, so they can be used even if the + // database is read only for the current user. + conn_.prepareStatements(tagged_statements.begin(), + tagged_statements.end()); +// tagged_statements.begin() + WRITE_STMTS_BEGIN); + + // Create unique timer name per instance. + timer_name_ = "PgSqlConfigBackend4["; + timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this)); + timer_name_ += "]DbReconnectTimer"; + + // Create ReconnectCtl for this connection. + conn_.makeReconnectCtl(timer_name_); +} + +PgSqlTaggedStatement& +PgSqlConfigBackendDHCPv4Impl::getStatement(size_t index) const { + if (index >= tagged_statements.size()) { + isc_throw(BadValue, "PgSqlConfigBackendDHCPv4Impl::getStatement index: " + << index << ", is invalid"); + } + + return(tagged_statements[index]); +} + +PgSqlConfigBackendDHCPv4Impl::~PgSqlConfigBackendDHCPv4Impl() { +} + +PgSqlConfigBackendDHCPv4::PgSqlConfigBackendDHCPv4(const DatabaseConnection::ParameterMap& parameters) + : impl_(new PgSqlConfigBackendDHCPv4Impl(parameters)), base_impl_(impl_) { +} + +bool +PgSqlConfigBackendDHCPv4::isUnusable() { + return (impl_->conn_.isUnusable()); +} + +DatabaseConnection::ParameterMap +PgSqlConfigBackendDHCPv4::getParameters() const { + return (impl_->getParameters()); +} + +Subnet4Ptr +PgSqlConfigBackendDHCPv4::getSubnet4(const ServerSelector& server_selector, + const std::string& subnet_prefix) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SUBNET4_BY_PREFIX) + .arg(subnet_prefix); + return (impl_->getSubnet4(server_selector, subnet_prefix)); +} + +Subnet4Ptr +PgSqlConfigBackendDHCPv4::getSubnet4(const ServerSelector& server_selector, + const SubnetID& subnet_id) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SUBNET4_BY_SUBNET_ID) + .arg(subnet_id); + return (impl_->getSubnet4(server_selector, subnet_id)); +} + +Subnet4Collection +PgSqlConfigBackendDHCPv4::getAllSubnets4(const ServerSelector& server_selector) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SUBNETS4); + Subnet4Collection subnets; + impl_->getAllSubnets4(server_selector, subnets); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SUBNETS4_RESULT) + .arg(subnets.size()); + return (subnets); +} + +Subnet4Collection +PgSqlConfigBackendDHCPv4::getModifiedSubnets4(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SUBNETS4) + .arg(util::ptimeToText(modification_time)); + Subnet4Collection subnets; + impl_->getModifiedSubnets4(server_selector, modification_time, subnets); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SUBNETS4_RESULT) + .arg(subnets.size()); + return (subnets); +} + +Subnet4Collection +PgSqlConfigBackendDHCPv4::getSharedNetworkSubnets4(const ServerSelector& /* server_selector */, + const std::string& shared_network_name) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SHARED_NETWORK_SUBNETS4) + .arg(shared_network_name); + Subnet4Collection subnets; + impl_->getSharedNetworkSubnets4(ServerSelector::ANY(), shared_network_name, subnets); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SHARED_NETWORK_SUBNETS4_RESULT) + .arg(subnets.size()); + return (subnets); +} + +SharedNetwork4Ptr +PgSqlConfigBackendDHCPv4::getSharedNetwork4(const ServerSelector& server_selector, + const std::string& name) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SHARED_NETWORK4) + .arg(name); + return (impl_->getSharedNetwork4(server_selector, name)); +} + +SharedNetwork4Collection +PgSqlConfigBackendDHCPv4::getAllSharedNetworks4(const ServerSelector& server_selector) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SHARED_NETWORKS4); + SharedNetwork4Collection shared_networks; + impl_->getAllSharedNetworks4(server_selector, shared_networks); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SHARED_NETWORKS4_RESULT) + .arg(shared_networks.size()); + return (shared_networks); +} + +SharedNetwork4Collection +PgSqlConfigBackendDHCPv4::getModifiedSharedNetworks4(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SHARED_NETWORKS4) + .arg(util::ptimeToText(modification_time)); + SharedNetwork4Collection shared_networks; + impl_->getModifiedSharedNetworks4(server_selector, modification_time, shared_networks); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_SHARED_NETWORKS4_RESULT) + .arg(shared_networks.size()); + return (shared_networks); +} + +OptionDefinitionPtr +PgSqlConfigBackendDHCPv4::getOptionDef4(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_OPTION_DEF4) + .arg(code).arg(space); + return (impl_->getOptionDef(PgSqlConfigBackendDHCPv4Impl::GET_OPTION_DEF4_CODE_SPACE, + server_selector, code, space)); +} + +OptionDefContainer +PgSqlConfigBackendDHCPv4::getAllOptionDefs4(const ServerSelector& server_selector) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTION_DEFS4); + OptionDefContainer option_defs; + impl_->getAllOptionDefs(PgSqlConfigBackendDHCPv4Impl::GET_ALL_OPTION_DEFS4, + server_selector, option_defs); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTION_DEFS4_RESULT) + .arg(option_defs.size()); + return (option_defs); +} + +OptionDefContainer +PgSqlConfigBackendDHCPv4::getModifiedOptionDefs4(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTION_DEFS4) + .arg(util::ptimeToText(modification_time)); + OptionDefContainer option_defs; + impl_->getModifiedOptionDefs(PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTION_DEFS4, + server_selector, modification_time, option_defs); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTION_DEFS4_RESULT) + .arg(option_defs.size()); + return (option_defs); +} + +OptionDescriptorPtr +PgSqlConfigBackendDHCPv4::getOption4(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_OPTION4) + .arg(code).arg(space); + return (impl_->getOption(PgSqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE, + Option::V4, server_selector, code, space)); +} + +OptionContainer +PgSqlConfigBackendDHCPv4::getAllOptions4(const ServerSelector& server_selector) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTIONS4); + OptionContainer options = impl_->getAllOptions(PgSqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4, + Option::V4, server_selector); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_OPTIONS4_RESULT) + .arg(options.size()); + return (options); +} + +OptionContainer +PgSqlConfigBackendDHCPv4::getModifiedOptions4(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTIONS4) + .arg(util::ptimeToText(modification_time)); + OptionContainer options = impl_->getModifiedOptions(PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4, + Option::V4, server_selector, modification_time); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_OPTIONS4_RESULT) + .arg(options.size()); + return (options); +} + +StampedValuePtr +PgSqlConfigBackendDHCPv4::getGlobalParameter4(const ServerSelector& server_selector, + const std::string& name) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_GLOBAL_PARAMETER4) + .arg(name); + return (impl_->getGlobalParameter4(server_selector, name)); +} + +StampedValueCollection +PgSqlConfigBackendDHCPv4::getAllGlobalParameters4(const ServerSelector& server_selector) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4); + StampedValueCollection parameters; + auto tags = server_selector.getTags(); + for (auto tag : tags) { + PsqlBindArray in_bindings; + in_bindings.addTempString(tag.get()); + impl_->getGlobalParameters(PgSqlConfigBackendDHCPv4Impl::GET_ALL_GLOBAL_PARAMETERS4, + in_bindings, parameters); + } + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4_RESULT) + .arg(parameters.size()); + return (parameters); +} + +StampedValueCollection +PgSqlConfigBackendDHCPv4::getModifiedGlobalParameters4(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4) + .arg(util::ptimeToText(modification_time)); + StampedValueCollection parameters; + auto tags = server_selector.getTags(); + for (auto tag : tags) { + PsqlBindArray in_bindings; + in_bindings.addTempString(tag.get()); + in_bindings.addTimestamp(modification_time); + + impl_->getGlobalParameters(PgSqlConfigBackendDHCPv4Impl::GET_MODIFIED_GLOBAL_PARAMETERS4, + in_bindings, parameters); + } + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4_RESULT) + .arg(parameters.size()); + return (parameters); +} + +ClientClassDefPtr +PgSqlConfigBackendDHCPv4::getClientClass4(const db::ServerSelector& server_selector, + const std::string& name) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_CLIENT_CLASS4) + .arg(name); + return (impl_->getClientClass4(server_selector, name)); +} + +ClientClassDictionary +PgSqlConfigBackendDHCPv4::getAllClientClasses4(const db::ServerSelector& server_selector) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_CLIENT_CLASSES4); + ClientClassDictionary client_classes; + impl_->getAllClientClasses4(server_selector, client_classes); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_CLIENT_CLASSES4_RESULT) + .arg(client_classes.getClasses()->size()); + return (client_classes); +} + +ClientClassDictionary +PgSqlConfigBackendDHCPv4::getModifiedClientClasses4(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4) + .arg(util::ptimeToText(modification_time)); + ClientClassDictionary client_classes; + impl_->getModifiedClientClasses4(server_selector, modification_time, client_classes); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4_RESULT) + .arg(client_classes.getClasses()->size()); + return (client_classes); +} + +AuditEntryCollection +PgSqlConfigBackendDHCPv4::getRecentAuditEntries(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_RECENT_AUDIT_ENTRIES4) + .arg(util::ptimeToText(modification_time)) + .arg(modification_id); + AuditEntryCollection audit_entries; + impl_->getRecentAuditEntries(PgSqlConfigBackendDHCPv4Impl::GET_AUDIT_ENTRIES4_TIME, + server_selector, modification_time, + modification_id, audit_entries); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_RECENT_AUDIT_ENTRIES4_RESULT) + .arg(audit_entries.size()); + return (audit_entries); +} + +ServerCollection +PgSqlConfigBackendDHCPv4::getAllServers4() const { + ServerCollection servers; + + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SERVERS4); + impl_->getAllServers(PgSqlConfigBackendDHCPv4Impl::GET_ALL_SERVERS4, + servers); + + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_ALL_SERVERS4_RESULT) + .arg(servers.size()); + return (servers); +} + +ServerPtr +PgSqlConfigBackendDHCPv4::getServer4(const data::ServerTag& server_tag) const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_SERVER4) + .arg(server_tag.get()); + return (impl_->getServer(PgSqlConfigBackendDHCPv4Impl::GET_SERVER4, server_tag)); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateSubnet4(const ServerSelector& server_selector, + const Subnet4Ptr& subnet) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SUBNET4) + .arg(subnet); + impl_->createUpdateSubnet4(server_selector, subnet); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateSharedNetwork4(const ServerSelector& server_selector, + const SharedNetwork4Ptr& shared_network) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SHARED_NETWORK4) + .arg(shared_network->getName()); + impl_->createUpdateSharedNetwork4(server_selector, shared_network); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateOptionDef4(const ServerSelector& server_selector, + const OptionDefinitionPtr& option_def) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_OPTION_DEF4) + .arg(option_def->getName()).arg(option_def->getCode()); + impl_->createUpdateOptionDef4(server_selector, option_def); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateOption4(const ServerSelector& server_selector, + const OptionDescriptorPtr& option) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_OPTION4); + impl_->createUpdateOption4(server_selector, option); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateOption4(const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const OptionDescriptorPtr& option) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SHARED_NETWORK_OPTION4) + .arg(shared_network_name); + impl_->createUpdateOption4(server_selector, shared_network_name, option, false); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateOption4(const ServerSelector& server_selector, + const SubnetID& subnet_id, + const OptionDescriptorPtr& option) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION4) + .arg(subnet_id); + impl_->createUpdateOption4(server_selector, subnet_id, option, false); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateOption4(const ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const OptionDescriptorPtr& option) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_BY_POOL_OPTION4) + .arg(pool_start_address.toText()).arg(pool_end_address.toText()); + impl_->createUpdateOption4(server_selector, pool_start_address, pool_end_address, + option); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateGlobalParameter4(const ServerSelector& server_selector, + const StampedValuePtr& value) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER4) + .arg(value->getName()); + impl_->createUpdateGlobalParameter4(server_selector, value); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateClientClass4(const db::ServerSelector& server_selector, + const ClientClassDefPtr& client_class, + const std::string& follow_class_name) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS4) + .arg(client_class->getName()); + impl_->createUpdateClientClass4(server_selector, client_class, follow_class_name); +} + +void +PgSqlConfigBackendDHCPv4::createUpdateServer4(const ServerPtr& server) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_CREATE_UPDATE_SERVER4) + .arg(server->getServerTagAsText()); + impl_->createUpdateServer(PgSqlConfigBackendDHCPv4Impl::CREATE_AUDIT_REVISION, + PgSqlConfigBackendDHCPv4Impl::INSERT_SERVER4, + PgSqlConfigBackendDHCPv4Impl::UPDATE_SERVER4, + server); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteSubnet4(const ServerSelector& server_selector, + const std::string& subnet_prefix) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_PREFIX_SUBNET4) + .arg(subnet_prefix); + uint64_t result = impl_->deleteSubnet4(server_selector, subnet_prefix); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_PREFIX_SUBNET4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteSubnet4(const ServerSelector& server_selector, + const SubnetID& subnet_id) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4) + .arg(subnet_id); + uint64_t result = impl_->deleteSubnet4(server_selector, subnet_id); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteAllSubnets4(const ServerSelector& server_selector) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SUBNETS4); + + int index = (server_selector.amUnassigned() ? + PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_UNASSIGNED : + PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4); + uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all subnets", + "deleted all subnets", true); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SUBNETS4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteSharedNetworkSubnets4(const db::ServerSelector& server_selector, + const std::string& shared_network_name) { + if (!server_selector.amAny()) { + isc_throw(InvalidOperation, "deleting all subnets from a shared " + "network requires using ANY server selector"); + } + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4) + .arg(shared_network_name); + uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME, + server_selector, + "deleting all subnets for a shared network", + "deleted all subnets for a shared network", + true, shared_network_name); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteSharedNetwork4(const ServerSelector& server_selector, + const std::string& name) { + /// @todo Using UNASSIGNED selector is allowed by the CB API but we don't have + /// dedicated query for this at the moment. The user should use ANY to delete + /// the shared network by name. + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "deleting an unassigned shared network requires " + "an explicit server tag or using ANY server. The UNASSIGNED server " + "selector is currently not supported"); + } + + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK4) + .arg(name); + + int index = (server_selector.amAny() ? + PgSqlConfigBackendDHCPv4Impl::DELETE_SHARED_NETWORK4_NAME_ANY : + PgSqlConfigBackendDHCPv4Impl::DELETE_SHARED_NETWORK4_NAME_WITH_TAG); + uint64_t result = impl_->deleteTransactional(index, server_selector, + "deleting a shared network", + "shared network deleted", true, name); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteAllSharedNetworks4(const ServerSelector& server_selector) { + if (server_selector.amAny()) { + isc_throw(InvalidOperation, "deleting all shared networks for ANY server is not" + " supported"); + } + + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SHARED_NETWORKS4); + + int index = (server_selector.amUnassigned() ? + PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SHARED_NETWORKS4_UNASSIGNED : + PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_SHARED_NETWORKS4); + uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all shared networks", + "deleted all shared networks", true); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SHARED_NETWORKS4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteOptionDef4(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION_DEF4) + .arg(code).arg(space); + uint64_t result = impl_->deleteOptionDef4(server_selector, code, space); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION_DEF4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteAllOptionDefs4(const ServerSelector& server_selector) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_OPTION_DEFS4); + uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_OPTION_DEFS4, + server_selector, "deleting all option definitions", + "deleted all option definitions", true); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_OPTION_DEFS4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION4) + .arg(code).arg(space); + uint64_t result = impl_->deleteOption4(server_selector, code, space); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */, + const std::string& shared_network_name, + const uint16_t code, + const std::string& space) { + /// @todo In the future we might use the server selector to make sure that the + /// option is only deleted if the pool belongs to a given server. For now, we + /// just delete it when there is a match with the parent object. + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_OPTION4) + .arg(shared_network_name).arg(code).arg(space); + uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), shared_network_name, + code, space); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_OPTION4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */, + const SubnetID& subnet_id, + const uint16_t code, + const std::string& space) { + /// @todo In the future we might use the server selector to make sure that the + /// option is only deleted if the pool belongs to a given server. For now, we + /// just delete it when there is a match with the parent object. + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_OPTION4) + .arg(subnet_id).arg(code).arg(space); + uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), subnet_id, code, space); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_OPTION4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const uint16_t code, + const std::string& space) { + /// @todo In the future we might use the server selector to make sure that the + /// option is only deleted if the pool belongs to a given server. For now, we + /// just delete it when there is a match with the parent object. + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_OPTION4) + .arg(pool_start_address.toText()).arg(pool_end_address.toText()).arg(code).arg(space); + uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), pool_start_address, + pool_end_address, code, space); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_OPTION4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteGlobalParameter4(const ServerSelector& server_selector, + const std::string& name) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_GLOBAL_PARAMETER4) + .arg(name); + uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_GLOBAL_PARAMETER4, + server_selector, "deleting global parameter", + "global parameter deleted", false, name); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_GLOBAL_PARAMETER4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteAllGlobalParameters4(const ServerSelector& server_selector) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4); + uint64_t result = impl_->deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_GLOBAL_PARAMETERS4, + server_selector, "deleting all global parameters", + "all global parameters deleted", true); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteClientClass4(const db::ServerSelector& server_selector, + const std::string& name) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_CLIENT_CLASS4) + .arg(name); + auto result = impl_->deleteClientClass4(server_selector, name); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_CLIENT_CLASS4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteAllClientClasses4(const db::ServerSelector& server_selector) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4); + + int index = (server_selector.amUnassigned() ? + PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_CLIENT_CLASSES4_UNASSIGNED : + PgSqlConfigBackendDHCPv4Impl::DELETE_ALL_CLIENT_CLASSES4); + uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all client classes", + "deleted all client classes", true); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteServer4(const ServerTag& server_tag) { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SERVER4) + .arg(server_tag.get()); + uint64_t result = impl_->deleteServer4(server_tag); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SERVER4_RESULT) + .arg(result); + return (result); +} + +uint64_t +PgSqlConfigBackendDHCPv4::deleteAllServers4() { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SERVERS4); + uint64_t result = impl_->deleteAllServers4(); + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_ALL_SERVERS4_RESULT) + .arg(result); + return (result); +} + +std::string +PgSqlConfigBackendDHCPv4::getType() const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_TYPE4); + return (impl_->getType()); +} + +std::string +PgSqlConfigBackendDHCPv4::getHost() const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_HOST4); + return (impl_->getHost()); +} + +uint16_t +PgSqlConfigBackendDHCPv4::getPort() const { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_PORT4); + return (impl_->getPort()); +} + +bool +PgSqlConfigBackendDHCPv4::registerBackendType() { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_REGISTER_BACKEND_TYPE4); + return ( + dhcp::ConfigBackendDHCPv4Mgr::instance().registerBackendFactory("postgresql", + [](const db::DatabaseConnection::ParameterMap& params) -> dhcp::ConfigBackendDHCPv4Ptr { + return (dhcp::PgSqlConfigBackendDHCPv4Ptr(new dhcp::PgSqlConfigBackendDHCPv4(params))); + }) + ); +} + +void +PgSqlConfigBackendDHCPv4::unregisterBackendType() { + LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_UNREGISTER_BACKEND_TYPE4); + dhcp::ConfigBackendDHCPv4Mgr::instance().unregisterBackendFactory("postgresql"); +} + +} // end of namespace isc::dhcp +} // end of namespace isc diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.h b/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.h new file mode 100644 index 0000000000..d78698ae88 --- /dev/null +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.h @@ -0,0 +1,631 @@ +// 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_DHCP4_H +#define PGSQL_CONFIG_BACKEND_DHCP4_H + +#include <pgsql_cb_impl.h> +#include <database/database_connection.h> +#include <dhcpsrv/client_class_def.h> +#include <dhcpsrv/config_backend_dhcp4.h> +#include <pgsql_cb_log.h> +#include <boost/shared_ptr.hpp> + +namespace isc { +namespace dhcp { + +class PgSqlConfigBackendDHCPv4Impl; + +/// @brief Implementation of the PgSql Configuration Backend for +/// Kea DHCPv4 server. +/// +/// All POSIX times specified in the methods belonging to this +/// class must be local times. +/// +/// The server selection mechanisms used by this backend generally adhere +/// to the rules described for @c ConfigBackendDHCPv4, but support for +/// some of the selectors is not implemented. Whenever this is the case, +/// the methods throw @c isc::NotImplemented exception. +class PgSqlConfigBackendDHCPv4 : public ConfigBackendDHCPv4 { +public: + + /// @brief Constructor. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + explicit PgSqlConfigBackendDHCPv4(const db::DatabaseConnection::ParameterMap& parameters); + + /// @brief Retrieves a single subnet by subnet_prefix. + /// + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + virtual Subnet4Ptr + getSubnet4(const db::ServerSelector& server_selector, + const std::string& subnet_prefix) const; + + /// @brief Retrieves a single subnet by subnet identifier. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of a subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + virtual Subnet4Ptr + getSubnet4(const db::ServerSelector& server_selector, const SubnetID& subnet_id) const; + + /// @brief Retrieves all subnets. + /// + /// @param server_selector Server selector. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet4Collection + getAllSubnets4(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves subnets modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound subnet modification time. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet4Collection + getModifiedSubnets4(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves all subnets belonging to a specified shared network. + /// + /// The server selector is currently ignored by this method. All subnets + /// for the given shared network are returned regardless of their + /// associations with the servers. + /// + /// @param server_selector Server selector (currently ignored). + /// @param shared_network_name Name of the shared network for which the + /// subnets should be retrieved. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet4Collection + getSharedNetworkSubnets4(const db::ServerSelector& server_selector, + const std::string& shared_network_name) const; + + /// @brief Retrieves shared network by name. + /// + /// @param server_selector Server selector. + /// @param name Name of the shared network to be retrieved. + /// @return Pointer to the shared network or NULL if not found. + /// @throw NotImplemented if server selector is "unassigned". + virtual SharedNetwork4Ptr + getSharedNetwork4(const db::ServerSelector& server_selector, + const std::string& name) const; + + /// @brief Retrieves all shared networks. + /// + /// @param server_selector Server selector. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork4Collection + getAllSharedNetworks4(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves shared networks modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound shared network modification time. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork4Collection + getModifiedSharedNetworks4(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves single option definition by code and space. + /// + /// @param server_selector Server selector. + /// @param code Code of the option to be retrieved. + /// @param space Option space of the option to be retrieved. + /// @return Pointer to the option definition or NULL if not found. + /// @throw NotImplemented if server selector is "unassigned". + virtual OptionDefinitionPtr + getOptionDef4(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space) const; + + /// @brief Retrieves all option definitions. + /// + /// @param server_selector Server selector. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getAllOptionDefs4(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves option definitions modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound option definition modification + /// time. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getModifiedOptionDefs4(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves single option by code and space. + /// + /// @param server_selector Server selector. + /// @return Pointer to the retrieved option descriptor or null if + /// no option was found. + /// @throw NotImplemented if server selector is "unassigned". + virtual OptionDescriptorPtr + getOption4(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space) const; + + /// @brief Retrieves all global options. + /// + /// @param server_selector Server selector. + /// @return Collection of global options or empty collection if no + /// option found. + virtual OptionContainer + getAllOptions4(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves option modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound option modification time. + /// @return Collection of global options or empty collection if no + /// option found. + virtual OptionContainer + getModifiedOptions4(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves global parameter value. + /// + /// Typically, the server selector used for this query should be set to + /// ONE. It is possible to use the MULTIPLE server selector but in that + /// case only the first found parameter is returned. + /// + /// @param server_selector Server selector. + /// @param name Name of the global parameter to be retrieved. + /// @return Value of the global parameter. + /// @throw NotImplemented if server selector is "unassigned". + virtual data::StampedValuePtr + getGlobalParameter4(const db::ServerSelector& server_selector, + const std::string& name) const; + + /// @brief Retrieves all global parameters. + /// + /// Using the server selector it is possible to fetch the parameters for + /// one or more servers. The following list describes what parameters are + /// returned depending on the server selector specified: + /// - ALL: only common parameters are returned which are associated with + /// the logical server 'all'. No parameters associated with the explicit + /// server tags are returned. + /// + /// - ONE: parameters used by the particular sever are returned. This includes + /// parameters associated with the particular server (identified by tag) + /// and parameters associated with the logical server 'all' when server + /// specific parameters are not given. For example, if there is a + /// renew-timer specified for 'server1' tag, different value of the + /// renew-timer specified for 'all' servers and a rebind-timer specified + /// for 'all' servers, the caller will receive renew-timer value associated + /// with the server1 and the rebind-timer value associated with all servers, + /// because there is no explicit rebind-timer specified for server1. + /// + /// - MULTIPLE: parameters used by multiple servers, but those associated + /// with specific server tags take precedence over the values specified for + /// 'all' servers. This is similar to the case of ONE server described + /// above. The effect of querying for parameters belonging to multiple + /// servers is the same as issuing multiple queries with ONE server + /// being selected multiple times. + /// + /// - UNASSIGNED: parameters not associated with any servers. + /// + /// + /// @param server_selector Server selector. + virtual data::StampedValueCollection + getAllGlobalParameters4(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves global parameters modified after specified time. + /// + /// @param modification_time Lower bound modification time. + /// @return Collection of modified global parameters. + virtual data::StampedValueCollection + getModifiedGlobalParameters4(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves a client class by name. + /// + /// @param server_selector Server selector. + /// @param name Client class name. + /// @return Pointer to the retrieved client class. + virtual ClientClassDefPtr + getClientClass4(const db::ServerSelector& selector, const std::string& name) const; + + /// @brief Retrieves all client classes. + /// + /// @param selector Server selector. + /// @return Collection of client classes. + virtual ClientClassDictionary + getAllClientClasses4(const db::ServerSelector& selector) const; + + /// @brief Retrieves client classes modified after specified time. + /// + /// @param selector Server selector. + /// @param modification_time Modification time. + /// @return Collection of client classes. + virtual ClientClassDictionary + getModifiedClientClasses4(const db::ServerSelector& selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves the most recent audit entries. + /// + /// @param 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) entries have the same + /// modification_time. + /// @return Collection of audit entries. + virtual db::AuditEntryCollection + getRecentAuditEntries(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const; + + /// @brief Retrieves all servers. + /// + /// This method returns the list of servers excluding the logical server + /// 'all'. + /// + /// @return Collection of servers from the backend. + virtual db::ServerCollection + getAllServers4() const; + + /// @brief Retrieves a server. + /// + /// @param server_tag Tag of the server to be retrieved. + /// @return Pointer to the server instance or null pointer if no server + /// with the particular tag was found. + virtual db::ServerPtr + getServer4(const data::ServerTag& server_tag) const; + + /// @brief Creates or updates a subnet. + /// + /// @param server_selector Server selector. + /// @param subnet Subnet to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateSubnet4(const db::ServerSelector& server_selector, + const Subnet4Ptr& subnet); + + /// @brief Creates or updates a shared network. + /// + /// @param server_selector Server selector. + /// @param shared_network Shared network to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateSharedNetwork4(const db::ServerSelector& server_selector, + const SharedNetwork4Ptr& shared_network); + + /// @brief Creates or updates an option definition. + /// + /// @param server_selector Server selector. + /// @param option_def Option definition to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOptionDef4(const db::ServerSelector& server_selector, + const OptionDefinitionPtr& option_def); + + /// @brief Creates or updates global option. + /// + /// @param server_selector Server selector. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption4(const db::ServerSelector& server_selector, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates shared network level option. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of a shared network to which option + /// belongs. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption4(const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates subnet level option. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of a subnet to which option belongs. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption4(const db::ServerSelector& server_selector, + const SubnetID& subnet_id, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates pool level option. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// the option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// option belongs. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption4(const db::ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates global parameter. + /// + /// @param server_selector Server selector. + /// @param name Name of the global parameter. + /// @param value Value of the global parameter. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateGlobalParameter4(const db::ServerSelector& server_selector, + const data::StampedValuePtr& value); + + /// @brief Creates or updates DHCPv4 client class. + /// + /// @param server_selector Server selector. + /// @param client_class Client class to be added or updated. + /// @param follow_class_name name of the class after which the + /// new or updated class should be positioned. An empty value + /// causes the class to be appended at the end of the class + /// hierarchy. + virtual void + createUpdateClientClass4(const db::ServerSelector& server_selector, + const ClientClassDefPtr& client_class, + const std::string& follow_class_name); + + /// @brief Creates or updates a server. + /// + /// @param server Instance of the server to be stored. + /// @throw InvalidOperation when trying to create a duplicate or + /// update the logical server 'all'. + virtual void + createUpdateServer4(const db::ServerPtr& server); + + /// @brief Deletes subnet by prefix. + /// + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be deleted. + /// @return Number of deleted subnets. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteSubnet4(const db::ServerSelector& server_selector, + const std::string& subnet_prefix); + + /// @brief Deletes subnet by identifier. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to be deleted. + /// @return Number of deleted subnets. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteSubnet4(const db::ServerSelector& server_selector, const SubnetID& subnet_id); + + /// @brief Deletes all subnets. + /// + /// @param server_selector Server selector. + /// @return Number of deleted subnets. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteAllSubnets4(const db::ServerSelector& server_selector); + + /// @brief Deletes all subnets belonging to a specified shared network. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of the shared network for which the + /// subnets should be deleted. + /// @return Number of deleted subnets. + virtual uint64_t + deleteSharedNetworkSubnets4(const db::ServerSelector& server_selector, + const std::string& shared_network_name); + + /// @brief Deletes shared network by name. + /// + /// @param server_selector Server selector. + /// @param name Name of the shared network to be deleted. + /// @return Number of deleted shared networks. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteSharedNetwork4(const db::ServerSelector& server_selector, + const std::string& name); + + /// @brief Deletes all shared networks. + /// + /// @param server_selector Server selector. + /// @return Number of deleted shared networks. + virtual uint64_t + deleteAllSharedNetworks4(const db::ServerSelector& server_selector); + + /// @brief Deletes option definition. + /// + /// @param server_selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + /// @return Number of deleted option definitions. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOptionDef4(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space); + + /// @brief Deletes all option definitions. + /// + /// @param server_selector Server selector. + /// @return Number of deleted option definitions. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteAllOptionDefs4(const db::ServerSelector& server_selector); + + /// @brief Deletes global option. + /// + /// @param server_selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + /// @return Number of deleted options. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption4(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space); + + /// @brief Deletes shared network level option. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of the shared network which deleted + /// option belongs to + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption4(const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const uint16_t code, + const std::string& space); + + /// @brief Deletes subnet level option. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to which deleted option + /// belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption4(const db::ServerSelector& server_selector, const SubnetID& subnet_id, + const uint16_t code, const std::string& space); + + /// @brief Deletes pool level option. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// deleted option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// deleted option belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption4(const db::ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const uint16_t code, + const std::string& space); + + /// @brief Deletes global parameter. + /// + /// @param server_selector Server selector. + /// @param name Name of the global parameter to be deleted. + /// @return Number of deleted global parameters. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteGlobalParameter4(const db::ServerSelector& server_selector, + const std::string& name); + + /// @brief Deletes all global parameters. + /// + /// @param server_selector Server selector. + /// @return Number of deleted global parameters. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteAllGlobalParameters4(const db::ServerSelector& server_selector); + + /// @brief Deletes DHCPv4 client class. + /// + /// @param server_selector Server selector. + /// @param name Name of the class to be deleted. + /// @return Number of deleted client classes. + virtual uint64_t + deleteClientClass4(const db::ServerSelector& server_selector, + const std::string& name); + + /// @brief Deletes all client classes. + /// + /// @param server_selector Server selector. + /// @return Number of deleted client classes. + virtual uint64_t + deleteAllClientClasses4(const db::ServerSelector& server_selector); + + /// @brief Deletes a server from the backend. + /// + /// @param server_tag Tag of the server to be deleted. + /// @return Number of deleted servers. + /// @throw isc::InvalidOperation when trying to delete the logical + /// server 'all'. + virtual uint64_t + deleteServer4(const data::ServerTag& server_tag); + + /// @brief Deletes all servers from the backend except the logical + /// server 'all'. + /// + /// @return Number of deleted servers. + virtual uint64_t + deleteAllServers4(); + + /// @brief Returns backend type in the textual format. + /// + /// @return "postgresql". + virtual 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. + virtual 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. + virtual uint16_t getPort() const; + + /// @brief Registers the PgSQL backend factory with backend config manager + /// + /// This should be called by the hook lib load() function. + /// @return True if the factory was registered successfully, false otherwise. + static bool registerBackendType(); + + /// @brief Unregisters the PgSQL backend factory and discards PgSQL backends + /// + /// This should be called by the hook lib unload() function. + static void unregisterBackendType(); + + /// @brief Flag which indicates if the config backend has an unusable + /// connection. + /// + /// @return true if there is at least one unusable connection, false + /// otherwise + virtual bool isUnusable(); + + /// @brief Return backend parameters + /// + /// Returns the backend parameters + /// + /// @return Parameters of the backend. + isc::db::DatabaseConnection::ParameterMap getParameters() const; + +protected: + + /// @brief Pointer to the implementation of the @c PgSqlConfigBackendDHCPv4 + /// class. + boost::shared_ptr<PgSqlConfigBackendDHCPv4Impl> impl_; + + /// @brief Pointer to the base implementation of the backend shared by + /// DHCPv4 and DHCPv6 servers. + boost::shared_ptr<PgSqlConfigBackendImpl> base_impl_; +}; + +/// @brief Pointer to the @c PgSqlConfigBackendDHCPv4 class. +typedef boost::shared_ptr<PgSqlConfigBackendDHCPv4> PgSqlConfigBackendDHCPv4Ptr; + +} // end of namespace isc::cb +} // end of namespace isc + +#endif // PGSQL_CONFIG_BACKEND_DHCP4_H diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.cc b/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.cc index 66145aa728..1122600104 100644 --- a/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.cc +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// 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 @@ -9,6 +9,7 @@ #include <asiolink/io_address.h> #include <config_backend/constants.h> #include <dhcp/option_space.h> +#include <database/db_exceptions.h> #include <pgsql/pgsql_exchange.h> #include <util/buffer.h> @@ -28,6 +29,30 @@ namespace dhcp { isc::asiolink::IOServicePtr PgSqlConfigBackendImpl::io_service_ = isc::asiolink::IOServicePtr(); +PgSqlTaggedStatement& +PgSqlConfigBackendImpl::getStatement(size_t /* index */) const { + isc_throw(NotImplemented, "derivations must override this"); +} + +void +PgSqlConfigBackendImpl::selectQuery(size_t index, + const PsqlBindArray& in_bindings, + PgSqlConnection::ConsumeResultRowFun process_result_row) { + conn_.selectQuery(getStatement(index), in_bindings, process_result_row); +} + +void +PgSqlConfigBackendImpl::insertQuery(size_t index, + const PsqlBindArray& in_bindings) { + conn_.insertQuery(getStatement(index), in_bindings); +} + +uint64_t +PgSqlConfigBackendImpl::updateDeleteQuery(size_t index, + const PsqlBindArray& in_bindings) { + return(conn_.updateDeleteQuery(getStatement(index), in_bindings)); +} + PgSqlConfigBackendImpl::ScopedAuditRevision::ScopedAuditRevision( PgSqlConfigBackendImpl* impl, const int index, @@ -93,11 +118,11 @@ PgSqlConfigBackendImpl::~PgSqlConfigBackendImpl() { } void -PgSqlConfigBackendImpl::createAuditRevision(const int /* index */, +PgSqlConfigBackendImpl::createAuditRevision(const int index, const ServerSelector& server_selector, - const boost::posix_time::ptime& /* audit_ts */, - const std::string& /* log_message */, - const bool /* cascade_transaction */) { + const boost::posix_time::ptime& audit_ts, + const std::string& log_message, + const bool cascade_transaction) { // Do not touch existing audit revision in case of the cascade update. if (audit_revision_created_) { return; @@ -115,7 +140,13 @@ PgSqlConfigBackendImpl::createAuditRevision(const int /* index */, tag = tags.begin()->get(); } - isc_throw(NotImplemented, "todo"); + PsqlBindArray in_bindings; + in_bindings.addTimestamp(audit_ts); + in_bindings.add(tag); + in_bindings.add(log_message); + in_bindings.add(cascade_transaction); + + insertQuery(index, in_bindings); } void @@ -124,12 +155,59 @@ PgSqlConfigBackendImpl::clearAuditRevision() { } void -PgSqlConfigBackendImpl::getRecentAuditEntries(const int /* index */, - const db::ServerSelector& /* server_selector */, - const boost::posix_time::ptime& /* modification_time */, - const uint64_t& /* modification_id */, - AuditEntryCollection& /* audit_entries */) { - isc_throw(NotImplemented, "todo"); +PgSqlConfigBackendImpl::getRecentAuditEntries(const int index, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id, + AuditEntryCollection& audit_entries) { + auto tags = server_selector.getTags(); + for (auto tag : tags) { + // Create the input parameters. + PsqlBindArray in_bindings; + in_bindings.addTempString(tag.get()); + in_bindings.addTimestamp(modification_time); + in_bindings.add(modification_id); + + // Execute select. + selectQuery(index, in_bindings, + [&audit_entries] (PgSqlResult& r, int row) { + // Extract the column values for r[row]. + + // Get the object type. Column 0 is the entry ID which + // we don't need here. + std::string object_type; + PgSqlExchange::getColumnValue(r, row, 1, object_type); + + // Get the object ID. + uint64_t object_id; + PgSqlExchange::getColumnValue(r, row, 2, object_id); + + // Get the modification type. + uint8_t mod_typ_int; + PgSqlExchange::getColumnValue(r, row, 3, mod_typ_int); + AuditEntry::ModificationType mod_type = + static_cast<AuditEntry::ModificationType>(mod_typ_int); + + // Get the modification time. + boost::posix_time::ptime mod_time; + PgSqlExchange::getColumnValue(r, row, 4, mod_time); + + // Get the revision ID. + uint64_t revision_id; + PgSqlExchange::getColumnValue(r, row, 5, revision_id); + + // Get the revision log message. + std::string log_message; + PgSqlExchange::getColumnValue(r, row, 6, log_message); + + // Create new audit entry and add it to the collection of received + // entries. + AuditEntryPtr audit_entry = + AuditEntry::create(object_type, object_id, mod_type, mod_time, + revision_id, log_message); + audit_entries.insert(audit_entry); + }); + } } uint64_t @@ -147,37 +225,49 @@ PgSqlConfigBackendImpl::deleteFromTable(const int index, } uint64_t -PgSqlConfigBackendImpl::deleteFromTable(const int /* index */, - const db::ServerSelector& /* server_selector */, - const std::string& /* operation */, - db::PsqlBindArray& /* bindings */) { - isc_throw(NotImplemented, "todo"); +PgSqlConfigBackendImpl::deleteFromTable(const int index, + const db::ServerSelector& server_selector, + const std::string& operation, + db::PsqlBindArray& in_bindings) { + // For ANY server, we use queries that lack server tag, otherwise + // we need to insert the server tag as the first input parameter. + if (!server_selector.amAny() && !server_selector.amUnassigned()) { + std::string tag = getServerTag(server_selector, operation); + in_bindings.insert(tag, 0); + } + + return (updateDeleteQuery(index, in_bindings)); +} + +uint64_t +PgSqlConfigBackendImpl::getLastInsertId(const int index, const std::string& table, + const std::string& column) { + PsqlBindArray in_bindings; + in_bindings.add(table); + in_bindings.add(column); + uint64_t last_id = 0; + conn_.selectQuery(getStatement(index), in_bindings, + [&last_id] (PgSqlResult& r, int row) { + // Get the object type. Column 0 is the entry ID which + PgSqlExchange::getColumnValue(r, row, 0, last_id); + }); + + return (last_id); } void PgSqlConfigBackendImpl::getGlobalParameters(const int /* index */, const PsqlBindArray& /* in_bindings */, StampedValueCollection& /* parameters */) { - - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); } OptionDefinitionPtr PgSqlConfigBackendImpl::getOptionDef(const int /* index */, - const ServerSelector& server_selector, + const ServerSelector& /* server_selector */, const uint16_t /* code */, const std::string& /* space */) { - - if (server_selector.amUnassigned()) { - isc_throw(NotImplemented, "managing configuration for no particular server" - " (unassigned) is unsupported at the moment"); - } - - auto tag = getServerTag(server_selector, "fetching option definition"); - - OptionDefContainer option_defs; - isc_throw(NotImplemented, "todo"); - return (option_defs.empty() ? OptionDefinitionPtr() : *option_defs.begin()); + isc_throw(NotImplemented, NOT_IMPL_STR); } void @@ -185,7 +275,7 @@ PgSqlConfigBackendImpl::getAllOptionDefs(const int /* index */, const ServerSelector& server_selector, OptionDefContainer& /* option_defs */) { auto tags = server_selector.getTags(); - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); } void @@ -194,7 +284,7 @@ PgSqlConfigBackendImpl::getModifiedOptionDefs(const int /* index */, const boost::posix_time::ptime& /* modification_time */, OptionDefContainer& /* option_defs */) { auto tags = server_selector.getTags(); - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); } void @@ -203,7 +293,7 @@ PgSqlConfigBackendImpl::getOptionDefs(const int /* index */, OptionDefContainer& /* option_defs*/ ) { // Create output bindings. The order must match that in the prepared // statement. - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); } void @@ -227,7 +317,7 @@ PgSqlConfigBackendImpl::createUpdateOptionDef(const db::ServerSelector& server_s for (auto field : option_def->getRecordFields()) { record_types->add(Element::create(static_cast<int>(field))); } - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); } OptionDescriptorPtr @@ -245,7 +335,7 @@ PgSqlConfigBackendImpl::getOption(const int /* index */, auto tag = getServerTag(server_selector, "fetching global option"); OptionContainer options; - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptor::create(*options.begin())); } @@ -257,7 +347,7 @@ PgSqlConfigBackendImpl::getAllOptions(const int /* index */, OptionContainer options; auto tags = server_selector.getTags(); - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); return (options); } @@ -274,7 +364,7 @@ PgSqlConfigBackendImpl::getModifiedOptions(const int index, PsqlBindArray in_bindings; /// need to define binding parameters - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); getOptions(index, in_bindings, universe, options); } @@ -299,7 +389,7 @@ PgSqlConfigBackendImpl::getOption(const int index, OptionContainer options; PsqlBindArray in_bindings; - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); getOptions(index, in_bindings, universe, options); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptor::create(*options.begin())); @@ -330,7 +420,7 @@ PgSqlConfigBackendImpl::getOption(const int index, Option::Universe universe = Option::V4; OptionContainer options; PsqlBindArray in_bindings; - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); getOptions(index, in_bindings, universe, options); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptor::create(*options.begin())); @@ -353,7 +443,7 @@ PgSqlConfigBackendImpl::getOption(const int index, OptionContainer options; PsqlBindArray in_bindings; - isc_throw(NotImplemented, "todo"); + isc_throw(NotImplemented, NOT_IMPL_STR); getOptions(index, in_bindings, universe, options); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptor::create(*options.begin())); } @@ -363,46 +453,16 @@ PgSqlConfigBackendImpl::getOptions(const int /* index */, const db::PsqlBindArray& /* in_bindings */, const Option::Universe& /* universe */, OptionContainer& /* options */) { - isc_throw(NotImplemented, "todo"); -} - -PsqlBindArrayPtr -PgSqlConfigBackendImpl::createInputRelayBinding(const NetworkPtr& network) { - ElementPtr relay_element = Element::createList(); - const auto& addresses = network->getRelayAddresses(); - if (!addresses.empty()) { - for (const auto& address : addresses) { - relay_element->add(Element::create(address.toText())); - } - } - isc_throw(NotImplemented, "todo"); -} - -PsqlBindArrayPtr -PgSqlConfigBackendImpl::createOptionValueBinding(const OptionDescriptorPtr& option) { - - PsqlBindArrayPtr p(new PsqlBindArray()); - OptionPtr opt = option->option_; - if (option->formatted_value_.empty() && (opt->len() > opt->getHeaderLen())) { - OutputBuffer buf(opt->len()); - opt->pack(buf); - const char* buf_ptr = static_cast<const char*>(buf.getData()); - std::vector<uint8_t> blob(buf_ptr + opt->getHeaderLen(), buf_ptr + buf.getLength()); - - // return (PsqlBindArray::createBlob(blob.begin(), blob.end())); - } - - // return (PsqlBindArray::createNull()); - return (p); + isc_throw(NotImplemented, NOT_IMPL_STR); } ServerPtr -PgSqlConfigBackendImpl::getServer(const int index, const ServerTag& /* server_tag */) { +PgSqlConfigBackendImpl::getServer(const int index, const ServerTag& server_tag) { ServerCollection servers; - PsqlBindArray in_bindings; /* = { - PsqlBindArray::createString(server_tag.get()) - }; */ - isc_throw(NotImplemented, "todo"); + + // Create input parameter bindings. + PsqlBindArray in_bindings; + in_bindings.addTempString(server_tag.get()); getServers(index, in_bindings, servers); @@ -416,16 +476,52 @@ PgSqlConfigBackendImpl::getAllServers(const int index, db::ServerCollection& ser } void -PgSqlConfigBackendImpl::getServers(const int /* index */, - const PsqlBindArray& /* in_bindings */, - ServerCollection& /* servers */) { - isc_throw(NotImplemented, "todo"); +PgSqlConfigBackendImpl::getServers(const int index, + const PsqlBindArray& in_bindings, + ServerCollection& servers) { + // Track the last server added to avoid duplicates. This + // assumes the rows are ordered by server ID. + ServerPtr last_server; + selectQuery(index, in_bindings, + [&servers, &last_server](PgSqlResult& r, int row) { + // Extract the column values for r[row]. + + // Get the server ID. + uint64_t id; + PgSqlExchange::getColumnValue(r, row, 0, id); + + // Get the server tag. + std::string tag; + PgSqlExchange::getColumnValue(r, row, 1, tag); + + // Get the description. + std::string description; + PgSqlExchange::getColumnValue(r, row, 2, description); + + // Get the modification time. + boost::posix_time::ptime mod_time; + PgSqlExchange::getColumnValue(r, row, 3, mod_time); + + if (!last_server || (last_server->getId() != id)) { + // Create the server instance. + last_server = Server::create(ServerTag(tag), description); + + // id + last_server->setId(id); + + // modification_ts + last_server->setModificationTime(mod_time); + + // New server fetched. Let's store it. + servers.insert(last_server); + } + }); } void PgSqlConfigBackendImpl::createUpdateServer(const int& create_audit_revision, - const int& /* create_index */, - const int& /* update_index */, + const int& create_index, + const int& update_index, const ServerPtr& server) { // The server tag 'all' is reserved. if (server->getServerTag().amAll()) { @@ -435,36 +531,58 @@ PgSqlConfigBackendImpl::createUpdateServer(const int& create_audit_revision, " to the database and a server with this name may not be created"); } - // Create scoped audit revision. As long as this instance exists - // no new audit revisions are created in any subsequent calls. - ScopedAuditRevision audit_revision(this, create_audit_revision, ServerSelector::ALL(), - "server set", true); - - PgSqlTransaction transaction(conn_); - + // Populate the input bindings. PsqlBindArray in_bindings; - isc_throw(NotImplemented, "todo"); - - /* = { - PsqlBindArray::createString(server->getServerTagAsText()), - PsqlBindArray::createString(server->getDescription()), - PsqlBindArray::createTimestamp(server->getModificationTime()) - }; - - try { - conn_.insertQuery(create_index, in_bindings); - - } catch (const DuplicateEntry&) { - in_bindings.push_back(PsqlBindArray::createString(server->getServerTagAsText())); - conn_.updateDeleteQuery(update_index, in_bindings); - }*/ + std::string tag = server->getServerTagAsText(); + in_bindings.add(tag); + in_bindings.addTempString(server->getDescription()); + in_bindings.addTimestamp(server->getModificationTime()); + + bool inserted = false; + for (auto attempts = 0; !inserted && attempts < 2; ++attempts) { + // Start a new transaction. + PgSqlTransaction transaction(conn_); + + // Create scoped audit revision. As long as this instance exists + // no new audit revisions are created in any subsequent calls. + ScopedAuditRevision audit_revision(this, create_audit_revision, ServerSelector::ALL(), + "server set", true); + + // On the first attempt we try to insert. + if (attempts == 0) { + try { + // Attempt to insert the server. + insertQuery(create_index, in_bindings); + inserted = true; + } catch (const DuplicateEntry&) { + // Server already exists that means our current transaction has + // been aborted by PostgreSQL. We need to start over with a new + // transaction, but this time we'll do the update. + continue; + } + } else { + // Add another instance of tag to the bindings to be used + // as the where clause parameter. PostgreSQL uses + // numbered placeholders so we could use $1 again, but + // doing it this way leaves the SQL more generic. + in_bindings.add(tag); + + // Attempt to update the server. + if (!updateDeleteQuery(update_index, in_bindings)) { + // Possible only if someone deleted it since we tried to insert it, + // the query is broken, or the bindings are nonesense. + isc_throw(Unexpected, "Update server failed to find server tag: " << tag); + } + } - transaction.commit(); + // Commit the transaction. + transaction.commit(); + } } std::string PgSqlConfigBackendImpl::getType() const { - return ("pgsql"); + return ("postgresql"); } std::string @@ -490,5 +608,55 @@ PgSqlConfigBackendImpl::getPort() const { return (0); } +void +PgSqlConfigBackendImpl::attachElementToServers(const int index, + const ServerSelector& server_selector, + const PsqlBindArray& in_bindings) { + // Copy the bindings because we're going to modify them. + PsqlBindArray server_bindings = in_bindings; + for (auto tag : server_selector.getTags()) { + // Add the server tag to end of the bindings. + std::string server_tag = tag.get(); + server_bindings.add(server_tag); + + // Insert the server assocation. + insertQuery(index, server_bindings); + + // Remove the prior server tag. + server_bindings.popBack(); + } +} + +void +PgSqlConfigBackendImpl::addRelayBinding(PsqlBindArray& bindings, + const NetworkPtr& network) { + ElementPtr relay_element = Element::createList(); + const auto& addresses = network->getRelayAddresses(); + if (!addresses.empty()) { + for (const auto& address : addresses) { + relay_element->add(Element::create(address.toText())); + } + } + + bindings.add(relay_element); +} + +void +PgSqlConfigBackendImpl::addOptionValueBinding(PsqlBindArray& bindings, + const OptionDescriptorPtr& option) { + OptionPtr opt = option->option_; + if (option->formatted_value_.empty() && (opt->len() > opt->getHeaderLen())) { + OutputBuffer buf(opt->len()); + opt->pack(buf); + const char* buf_ptr = static_cast<const char*>(buf.getData()); + std::vector<uint8_t> blob(buf_ptr + opt->getHeaderLen(), + buf_ptr + buf.getLength()); + bindings.add(blob); + } else { + bindings.addNull(); + } +} + + } // namespace dhcp } // end of namespace isc diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h b/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h index d6bca6a130..b04fab1ba3 100644 --- a/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h @@ -1,4 +1,4 @@ -// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// 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 @@ -28,6 +28,9 @@ #include <string> #include <vector> +// Convenience string for emitting location info NotImplemented exceptions. +#define NOT_IMPL_STR __FILE__ << ":" << __LINE__ << " - " << __FUNCTION__ + namespace isc { namespace dhcp { @@ -154,7 +157,7 @@ public: return (s.str()); } - /// @brief Invokes the corresponding stored procedure in MySQL. + /// @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 @@ -212,11 +215,67 @@ public: 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 int index, 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. @@ -424,22 +483,60 @@ public: /// @todo implement OptionDescriptorPtr processOptionRow(const Option::Universe& universe, ...) - /// @todo implement void attachElementToServers(const int index, ...) - - /// @todo implement - /// @note this needs to accept an PsqlBindArrayPtr and should add - /// necessary bindings to it. It cannot create a new array. - db::PsqlBindArrayPtr createInputRelayBinding(const NetworkPtr& network); - - /// @todo implement template<typename T> db::MySqlBindingPtr - /// createInputRequiredClassesBinding(const T& object) + /// @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 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 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)); + } - /// @todo implement db::MySqlBindingPtr createInputContextBinding(const T& config_element) { + bindings.add(required_classes_element); + } - /// @todo implement - /// @note this needs to accept an PsqlBindArrayPtr and should add - /// necessary bindings to it. It cannot create a new array. - db::PsqlBindArrayPtr createOptionValueBinding(const OptionDescriptorPtr& option); + /// @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. /// @@ -460,7 +557,7 @@ public: /// @brief Sends query to retrieve servers. /// /// @param index Index of the query to be used. - /// @param bindings Reference to the MySQL input bindings. + /// @param bindings Reference to the PgSQL input bindings. /// @param [out] servers Reference to the container where fetched servers /// will be inserted. void @@ -486,8 +583,27 @@ public: const int& update_index, const db::ServerPtr& server); - /// @todo implement template<typename T, typename... R> void multipleUpdateDeleteQueries(T - /// first_index, R... other_indexes) + /// @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. @@ -603,6 +719,66 @@ public: 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 Postgres database. db::PgSqlConnection conn_; diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.cc b/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.cc index 8f80623f0d..2baed5c5e5 100644 --- a/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.cc +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.cc @@ -12,6 +12,8 @@ extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_POOL_OPTION6 = "PGSQL extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_PREFIX_OPTION6 = "PGSQL_CB_CREATE_UPDATE_BY_PREFIX_OPTION6"; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION4 = "PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION4"; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION6 = "PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION6"; +extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS4 = "PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS4"; +extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS6 = "PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS6"; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER4 = "PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER4"; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER6 = "PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER6"; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_OPTION4 = "PGSQL_CB_CREATE_UPDATE_OPTION4"; @@ -27,6 +29,10 @@ extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_SHARED_NETWORK_OPTION6 = extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_SUBNET4 = "PGSQL_CB_CREATE_UPDATE_SUBNET4"; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_SUBNET6 = "PGSQL_CB_CREATE_UPDATE_SUBNET6"; extern const isc::log::MessageID PGSQL_CB_DEINIT_OK = "PGSQL_CB_DEINIT_OK"; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4 = "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4"; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4_RESULT = "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4_RESULT"; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6 = "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6"; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6_RESULT = "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6_RESULT"; extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4 = "PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4"; extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4_RESULT = "PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4_RESULT"; extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS6 = "PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS6"; @@ -65,6 +71,10 @@ extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4 = "PGSQL_C extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4_RESULT = "PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4_RESULT"; extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6 = "PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6"; extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6_RESULT = "PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6_RESULT"; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS4 = "PGSQL_CB_DELETE_CLIENT_CLASS4"; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS4_RESULT = "PGSQL_CB_DELETE_CLIENT_CLASS4_RESULT"; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS6 = "PGSQL_CB_DELETE_CLIENT_CLASS6"; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS6_RESULT = "PGSQL_CB_DELETE_CLIENT_CLASS6_RESULT"; extern const isc::log::MessageID PGSQL_CB_DELETE_GLOBAL_PARAMETER4 = "PGSQL_CB_DELETE_GLOBAL_PARAMETER4"; extern const isc::log::MessageID PGSQL_CB_DELETE_GLOBAL_PARAMETER4_RESULT = "PGSQL_CB_DELETE_GLOBAL_PARAMETER4_RESULT"; extern const isc::log::MessageID PGSQL_CB_DELETE_GLOBAL_PARAMETER6 = "PGSQL_CB_DELETE_GLOBAL_PARAMETER6"; @@ -93,6 +103,10 @@ extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4 = "PGSQ extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4_RESULT = "PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4_RESULT"; extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6 = "PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6"; extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6_RESULT = "PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6_RESULT"; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES4 = "PGSQL_CB_GET_ALL_CLIENT_CLASSES4"; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES4_RESULT = "PGSQL_CB_GET_ALL_CLIENT_CLASSES4_RESULT"; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES6 = "PGSQL_CB_GET_ALL_CLIENT_CLASSES6"; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES6_RESULT = "PGSQL_CB_GET_ALL_CLIENT_CLASSES6_RESULT"; extern const isc::log::MessageID PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4 = "PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4"; extern const isc::log::MessageID PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4_RESULT = "PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4_RESULT"; extern const isc::log::MessageID PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS6 = "PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS6"; @@ -117,10 +131,16 @@ extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS4 = "PGSQL_CB_GET_ALL_S extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS4_RESULT = "PGSQL_CB_GET_ALL_SUBNETS4_RESULT"; extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS6 = "PGSQL_CB_GET_ALL_SUBNETS6"; extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS6_RESULT = "PGSQL_CB_GET_ALL_SUBNETS6_RESULT"; +extern const isc::log::MessageID PGSQL_CB_GET_CLIENT_CLASS4 = "PGSQL_CB_GET_CLIENT_CLASS4"; +extern const isc::log::MessageID PGSQL_CB_GET_CLIENT_CLASS6 = "PGSQL_CB_GET_CLIENT_CLASS6"; extern const isc::log::MessageID PGSQL_CB_GET_GLOBAL_PARAMETER4 = "PGSQL_CB_GET_GLOBAL_PARAMETER4"; extern const isc::log::MessageID PGSQL_CB_GET_GLOBAL_PARAMETER6 = "PGSQL_CB_GET_GLOBAL_PARAMETER6"; extern const isc::log::MessageID PGSQL_CB_GET_HOST4 = "PGSQL_CB_GET_HOST4"; extern const isc::log::MessageID PGSQL_CB_GET_HOST6 = "PGSQL_CB_GET_HOST6"; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4 = "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4"; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4_RESULT = "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4_RESULT"; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6 = "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6"; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6_RESULT = "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6_RESULT"; extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4 = "PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4"; extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4_RESULT = "PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4_RESULT"; extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS6 = "PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS6"; @@ -190,6 +210,8 @@ const char* values[] = { "PGSQL_CB_CREATE_UPDATE_BY_PREFIX_OPTION6", "create or update option prefix: %1 prefix len: %2", "PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION4", "create or update option by subnet id: %1", "PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION6", "create or update option by subnet id: %1", + "PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS4", "create or update client class: %1", + "PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS6", "create or update client class: %1", "PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER4", "create or update global parameter: %1", "PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER6", "create or update global parameter: %1", "PGSQL_CB_CREATE_UPDATE_OPTION4", "create or update option", @@ -205,6 +227,10 @@ const char* values[] = { "PGSQL_CB_CREATE_UPDATE_SUBNET4", "create or update subnet: %1", "PGSQL_CB_CREATE_UPDATE_SUBNET6", "create or update subnet: %1", "PGSQL_CB_DEINIT_OK", "unloading Postgres CB hooks library successful", + "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4", "delete all client classes", + "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4_RESULT", "deleted: %1 entries", + "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6", "delete all client classes", + "PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6_RESULT", "deleted: %1 entries", "PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4", "delete all global parameters", "PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4_RESULT", "deleted: %1 entries", "PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS6", "delete all global parameters", @@ -243,6 +269,10 @@ const char* values[] = { "PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4_RESULT", "deleted: %1 entries", "PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6", "delete subnet by subnet id: %1", "PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6_RESULT", "deleted: %1 entries", + "PGSQL_CB_DELETE_CLIENT_CLASS4", "delete client class: %1", + "PGSQL_CB_DELETE_CLIENT_CLASS4_RESULT", "deleted: %1 entries", + "PGSQL_CB_DELETE_CLIENT_CLASS6", "delete client class: %1", + "PGSQL_CB_DELETE_CLIENT_CLASS6_RESULT", "deleted: %1 entries", "PGSQL_CB_DELETE_GLOBAL_PARAMETER4", "delete global parameter: %1", "PGSQL_CB_DELETE_GLOBAL_PARAMETER4_RESULT", "deleted: %1 entries", "PGSQL_CB_DELETE_GLOBAL_PARAMETER6", "delete global parameter: %1", @@ -271,6 +301,10 @@ const char* values[] = { "PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4_RESULT", "deleted: %1 entries", "PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6", "delete shared network: %1 subnets", "PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6_RESULT", "deleted: %1 entries", + "PGSQL_CB_GET_ALL_CLIENT_CLASSES4", "retrieving all client classes", + "PGSQL_CB_GET_ALL_CLIENT_CLASSES4_RESULT", "retrieving: %1 elements", + "PGSQL_CB_GET_ALL_CLIENT_CLASSES6", "retrieving all client classes", + "PGSQL_CB_GET_ALL_CLIENT_CLASSES6_RESULT", "retrieving: %1 elements", "PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4", "retrieving all global parameters", "PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4_RESULT", "retrieving: %1 elements", "PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS6", "retrieving all global parameters", @@ -295,10 +329,16 @@ const char* values[] = { "PGSQL_CB_GET_ALL_SUBNETS4_RESULT", "retrieving: %1 elements", "PGSQL_CB_GET_ALL_SUBNETS6", "retrieving all subnets", "PGSQL_CB_GET_ALL_SUBNETS6_RESULT", "retrieving: %1 elements", + "PGSQL_CB_GET_CLIENT_CLASS4", "retrieving client class: %1", + "PGSQL_CB_GET_CLIENT_CLASS6", "retrieving client class: %1", "PGSQL_CB_GET_GLOBAL_PARAMETER4", "retrieving global parameter: %1", "PGSQL_CB_GET_GLOBAL_PARAMETER6", "retrieving global parameter: %1", "PGSQL_CB_GET_HOST4", "get host", "PGSQL_CB_GET_HOST6", "get host", + "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4", "retrieving modified client classes from: %1", + "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4_RESULT", "retrieving: %1 elements", + "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6", "retrieving modified client classes from: %1", + "PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6_RESULT", "retrieving: %1 elements", "PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4", "retrieving modified global parameters from: %1", "PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4_RESULT", "retrieving: %1 elements", "PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS6", "retrieving modified global parameters from: %1", diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.h b/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.h index 57e3f744a5..57a41d30cc 100644 --- a/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.h +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.h @@ -13,6 +13,8 @@ extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_POOL_OPTION6; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_PREFIX_OPTION6; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION4; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION6; +extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS4; +extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS6; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER4; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER6; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_OPTION4; @@ -28,6 +30,10 @@ extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_SHARED_NETWORK_OPTION6; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_SUBNET4; extern const isc::log::MessageID PGSQL_CB_CREATE_UPDATE_SUBNET6; extern const isc::log::MessageID PGSQL_CB_DEINIT_OK; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4_RESULT; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6; +extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6_RESULT; extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4; extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4_RESULT; extern const isc::log::MessageID PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS6; @@ -66,6 +72,10 @@ extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4; extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET4_RESULT; extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6; extern const isc::log::MessageID PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6_RESULT; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS4; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS4_RESULT; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS6; +extern const isc::log::MessageID PGSQL_CB_DELETE_CLIENT_CLASS6_RESULT; extern const isc::log::MessageID PGSQL_CB_DELETE_GLOBAL_PARAMETER4; extern const isc::log::MessageID PGSQL_CB_DELETE_GLOBAL_PARAMETER4_RESULT; extern const isc::log::MessageID PGSQL_CB_DELETE_GLOBAL_PARAMETER6; @@ -94,6 +104,10 @@ extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4; extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS4_RESULT; extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6; extern const isc::log::MessageID PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6_RESULT; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES4; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES4_RESULT; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES6; +extern const isc::log::MessageID PGSQL_CB_GET_ALL_CLIENT_CLASSES6_RESULT; extern const isc::log::MessageID PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4; extern const isc::log::MessageID PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4_RESULT; extern const isc::log::MessageID PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS6; @@ -118,10 +132,16 @@ extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS4; extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS4_RESULT; extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS6; extern const isc::log::MessageID PGSQL_CB_GET_ALL_SUBNETS6_RESULT; +extern const isc::log::MessageID PGSQL_CB_GET_CLIENT_CLASS4; +extern const isc::log::MessageID PGSQL_CB_GET_CLIENT_CLASS6; extern const isc::log::MessageID PGSQL_CB_GET_GLOBAL_PARAMETER4; extern const isc::log::MessageID PGSQL_CB_GET_GLOBAL_PARAMETER6; extern const isc::log::MessageID PGSQL_CB_GET_HOST4; extern const isc::log::MessageID PGSQL_CB_GET_HOST6; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4_RESULT; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6; +extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6_RESULT; extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4; extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4_RESULT; extern const isc::log::MessageID PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS6; diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.mes b/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.mes index 26bbdfef29..8c10c122ff 100644 --- a/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.mes +++ b/src/hooks/dhcp/pgsql_cb/pgsql_cb_messages.mes @@ -17,6 +17,12 @@ Debug message issued when triggered an action to create or update option by subn % PGSQL_CB_CREATE_UPDATE_BY_SUBNET_ID_OPTION6 create or update option by subnet id: %1 Debug message issued when triggered an action to create or update option by subnet id +% PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS4 create or update client class: %1 +Debug message issued when triggered an action to create or update client class + +% PGSQL_CB_CREATE_UPDATE_CLIENT_CLASS6 create or update client class: %1 +Debug message issued when triggered an action to create or update client class + % PGSQL_CB_CREATE_UPDATE_GLOBAL_PARAMETER4 create or update global parameter: %1 Debug message issued when triggered an action to create or update global parameter @@ -65,6 +71,18 @@ Debug message issued when triggered an action to create or update subnet This informational message indicates that the Postgres Configuration Backend hooks library has been unloaded successfully. +% PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4 delete all client classes +Debug message issued when triggered an action to delete all client classes + +% PGSQL_CB_DELETE_ALL_CLIENT_CLASSES4_RESULT deleted: %1 entries +Debug message indicating the result of an action to delete all client classes + +% PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6 delete all client classes +Debug message issued when triggered an action to delete all client classes + +% PGSQL_CB_DELETE_ALL_CLIENT_CLASSES6_RESULT deleted: %1 entries +Debug message indicating the result of an action to delete all client classes + % PGSQL_CB_DELETE_ALL_GLOBAL_PARAMETERS4 delete all global parameters Debug message issued when triggered an action to delete all global parameters @@ -179,6 +197,18 @@ Debug message issued when triggered an action to delete subnet by subnet id % PGSQL_CB_DELETE_BY_SUBNET_ID_SUBNET6_RESULT deleted: %1 entries Debug message indicating the result of an action to delete subnet by subnet id +% PGSQL_CB_DELETE_CLIENT_CLASS4 delete client class: %1 +Debug message issued when triggered an action to delete client class + +% PGSQL_CB_DELETE_CLIENT_CLASS4_RESULT deleted: %1 entries +Debug message indicating the result of an action to delete client class + +% PGSQL_CB_DELETE_CLIENT_CLASS6 delete client class: %1 +Debug message issued when triggered an action to delete client class + +% PGSQL_CB_DELETE_CLIENT_CLASS6_RESULT deleted: %1 entries +Debug message indicating the result of an action to delete client class + % PGSQL_CB_DELETE_GLOBAL_PARAMETER4 delete global parameter: %1 Debug message issued when triggered an action to delete global parameter @@ -263,6 +293,18 @@ Debug message issued when triggered an action to delete shared network subnets % PGSQL_CB_DELETE_SHARED_NETWORK_SUBNETS6_RESULT deleted: %1 entries Debug message indicating the result of an action to delete shared network subnets +% PGSQL_CB_GET_ALL_CLIENT_CLASSES4 retrieving all client classes +Debug message issued when triggered an action to retrieve all client classes + +% PGSQL_CB_GET_ALL_CLIENT_CLASSES4_RESULT retrieving: %1 elements +Debug message indicating the result of an action to retrieve all client classes + +% PGSQL_CB_GET_ALL_CLIENT_CLASSES6 retrieving all client classes +Debug message issued when triggered an action to retrieve all client classes + +% PGSQL_CB_GET_ALL_CLIENT_CLASSES6_RESULT retrieving: %1 elements +Debug message indicating the result of an action to retrieve all client classes + % PGSQL_CB_GET_ALL_GLOBAL_PARAMETERS4 retrieving all global parameters Debug message issued when triggered an action to retrieve all global parameters @@ -339,6 +381,12 @@ Debug message issued when triggered an action to retrieve all subnets % PGSQL_CB_GET_ALL_SUBNETS6_RESULT retrieving: %1 elements Debug message indicating the result of an action to retrieve all subnets +% PGSQL_CB_GET_CLIENT_CLASS4 retrieving client class: %1 +Debug message issued when triggered an action to retrieve a client class + +% PGSQL_CB_GET_CLIENT_CLASS6 retrieving client class: %1 +Debug message issued when triggered an action to retrieve a client class + % PGSQL_CB_GET_GLOBAL_PARAMETER4 retrieving global parameter: %1 Debug message issued when triggered an action to retrieve global parameter @@ -351,6 +399,18 @@ Debug message issued when triggered an action to retrieve host % PGSQL_CB_GET_HOST6 get host Debug message issued when triggered an action to retrieve host +% PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4 retrieving modified client classes from: %1 +Debug message issued when triggered an action to retrieve modified client classes from specified time + +% PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES4_RESULT retrieving: %1 elements +Debug message indicating the result of an action to retrieve modified client classes from specified time + +% PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6 retrieving modified client classes from: %1 +Debug message issued when triggered an action to retrieve modified client classes from specified time + +% PGSQL_CB_GET_MODIFIED_CLIENT_CLASSES6_RESULT retrieving: %1 elements +Debug message indicating the result of an action to retrieve modified client classes from specified time + % PGSQL_CB_GET_MODIFIED_GLOBAL_PARAMETERS4 retrieving modified global parameters from: %1 Debug message issued when triggered an action to retrieve modified global parameters from specified time diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_macros.h b/src/hooks/dhcp/pgsql_cb/pgsql_macros.h deleted file mode 100644 index 05f61ac5aa..0000000000 --- a/src/hooks/dhcp/pgsql_cb/pgsql_macros.h +++ /dev/null @@ -1,1009 +0,0 @@ -// Copyright (C) 2021 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_QUERY_MACROS_DHCP_H -#define PGSQL_QUERY_MACROS_DHCP_H - -/// @file pgsql_macros.h -/// Collection of common macros defining PostgreSQL prepared statements -/// used to manage Kea DHCP configuration in the database. -/// -/// Some of the macros are DHCPv4 specific, other are DHCPv6 specific. -/// Some macros are common for both DHCP server types. The first -/// parameter @c table_prefix should be set to @c dhcp4 or @c dhcp6, -/// depending which DHCP server type it relates to. Provided value -/// is used as a prefix for PgSQL table names. For example, if the -/// prefix is set to @c dhcp4, the table name referred to in the -/// query may be dhcp4_subnet etc. The second argument in the variadic -/// macro is a part of the WHERE clause in the PgSQL query. The fixed -/// part of the WHERE clause is included in the macro. - -/// @todo Update queries to also fetch server tags to associate -/// returned configuration elements with particular servers. - -namespace isc { -namespace dhcp { - -namespace { - -#ifndef PGSQL_GET_GLOBAL_PARAMETER -#define PGSQL_GET_GLOBAL_PARAMETER(table_prefix, ...) \ - "SELECT" \ - " g.id," \ - " g.name," \ - " g.value," \ - " g.parameter_type," \ - " g.modification_ts," \ - " s.tag " \ - "FROM " #table_prefix "_global_parameter AS g " \ - "INNER JOIN " #table_prefix "_global_parameter_server AS a " \ - " ON g.id = a.parameter_id " \ - "INNER JOIN " #table_prefix "_server AS s " \ - " ON (a.server_id = s.id) " \ - "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ " ORDER BY g.id, s.id" - -#endif - -#ifndef PGSQL_GET_SUBNET4 -#define PGSQL_GET_SUBNET4_COMMON(server_join, ...) \ - "SELECT" \ - " s.subnet_id," \ - " s.subnet_prefix," \ - " s.4o6_interface," \ - " s.4o6_interface_id," \ - " s.4o6_subnet," \ - " s.boot_file_name," \ - " s.client_class," \ - " s.interface," \ - " s.match_client_id," \ - " s.modification_ts," \ - " s.next_server," \ - " s.rebind_timer," \ - " s.relay," \ - " s.renew_timer," \ - " s.require_client_classes," \ - " s.reservations_global," \ - " s.server_hostname," \ - " s.shared_network_name," \ - " s.user_context," \ - " s.valid_lifetime," \ - " p.id," \ - " p.start_address," \ - " p.end_address," \ - " p.subnet_id," \ - " p.modification_ts," \ - " x.option_id," \ - " x.code," \ - " x.value," \ - " x.formatted_value," \ - " x.space," \ - " x.persistent," \ - " x.dhcp4_subnet_id," \ - " x.scope_id," \ - " x.user_context," \ - " x.shared_network_name," \ - " x.pool_id," \ - " x.modification_ts," \ - " o.option_id," \ - " o.code," \ - " o.value," \ - " o.formatted_value," \ - " o.space," \ - " o.persistent," \ - " o.dhcp4_subnet_id," \ - " o.scope_id," \ - " o.user_context," \ - " o.shared_network_name," \ - " o.pool_id," \ - " o.modification_ts," \ - " s.calculate_tee_times," \ - " s.t1_percent," \ - " s.t2_percent," \ - " s.authoritative," \ - " s.min_valid_lifetime," \ - " s.max_valid_lifetime," \ - " p.client_class," \ - " p.require_client_classes," \ - " p.user_context," \ - " s.ddns_send_updates," \ - " s.ddns_override_no_update," \ - " s.ddns_override_client_update," \ - " s.ddns_replace_client_name," \ - " s.ddns_generated_prefix," \ - " s.ddns_qualifying_suffix," \ - " s.reservations_in_subnet," \ - " s.reservations_out_of_pool," \ - " s.cache_threshold," \ - " s.cache_max_age," \ - " srv.tag " \ - "FROM dhcp4_subnet AS s " server_join \ - "LEFT JOIN dhcp4_pool AS p ON s.subnet_id = p.subnet_id " \ - "LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \ - "LEFT JOIN dhcp4_options AS o ON o.scope_id = 1 AND s.subnet_id = " \ - "o.dhcp4_subnet_id " #__VA_ARGS__ " ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" - -#define PGSQL_GET_SUBNET4_NO_TAG(...) \ - PGSQL_GET_SUBNET4_COMMON("INNER JOIN dhcp4_subnet_server AS a " \ - " ON s.subnet_id = a.subnet_id " \ - "INNER JOIN dhcp4_server AS srv " \ - " ON (a.server_id = srv.id) ", \ - __VA_ARGS__) - -#define PGSQL_GET_SUBNET4_ANY(...) \ - PGSQL_GET_SUBNET4_COMMON("LEFT JOIN dhcp4_subnet_server AS a " \ - " ON s.subnet_id = a.subnet_id " \ - "LEFT JOIN dhcp4_server AS srv " \ - " ON a.server_id = srv.id ", \ - __VA_ARGS__) - -#define PGSQL_GET_SUBNET4_UNASSIGNED(...) \ - PGSQL_GET_SUBNET4_COMMON("LEFT JOIN dhcp4_subnet_server AS a " \ - " ON s.subnet_id = a.subnet_id " \ - "LEFT JOIN dhcp4_server AS srv " \ - " ON a.server_id = srv.id ", \ - WHERE a.subnet_id IS NULL __VA_ARGS__) - -#endif - -#ifndef PGSQL_GET_SUBNET6 -#define PGSQL_GET_SUBNET6_COMMON(server_join, ...) \ - "SELECT" \ - " s.subnet_id," \ - " s.subnet_prefix," \ - " s.client_class," \ - " s.interface," \ - " s.modification_ts," \ - " s.preferred_lifetime," \ - " s.rapid_commit," \ - " s.rebind_timer," \ - " s.relay," \ - " s.renew_timer," \ - " s.require_client_classes," \ - " s.reservations_global," \ - " s.shared_network_name," \ - " s.user_context," \ - " s.valid_lifetime," \ - " p.id," \ - " p.start_address," \ - " p.end_address," \ - " p.subnet_id," \ - " p.modification_ts," \ - " d.id," \ - " d.prefix," \ - " d.prefix_length," \ - " d.delegated_prefix_length," \ - " d.subnet_id," \ - " d.modification_ts," \ - " x.option_id," \ - " x.code," \ - " x.value," \ - " x.formatted_value," \ - " x.space," \ - " x.persistent," \ - " x.dhcp6_subnet_id," \ - " x.scope_id," \ - " x.user_context," \ - " x.shared_network_name," \ - " x.pool_id," \ - " x.modification_ts," \ - " x.pd_pool_id," \ - " y.option_id," \ - " y.code," \ - " y.value," \ - " y.formatted_value," \ - " y.space," \ - " y.persistent," \ - " y.dhcp6_subnet_id," \ - " y.scope_id," \ - " y.user_context," \ - " y.shared_network_name," \ - " y.pool_id," \ - " y.modification_ts," \ - " y.pd_pool_id," \ - " o.option_id," \ - " o.code," \ - " o.value," \ - " o.formatted_value," \ - " o.space," \ - " o.persistent," \ - " o.dhcp6_subnet_id," \ - " o.scope_id," \ - " o.user_context," \ - " o.shared_network_name," \ - " o.pool_id," \ - " o.modification_ts," \ - " o.pd_pool_id, " \ - " s.calculate_tee_times," \ - " s.t1_percent," \ - " s.t2_percent," \ - " s.interface_id," \ - " s.min_preferred_lifetime," \ - " s.max_preferred_lifetime," \ - " s.min_valid_lifetime," \ - " s.max_valid_lifetime," \ - " p.client_class," \ - " p.require_client_classes," \ - " p.user_context," \ - " d.excluded_prefix," \ - " d.excluded_prefix_length," \ - " d.client_class," \ - " d.require_client_classes," \ - " d.user_context," \ - " s.ddns_send_updates," \ - " s.ddns_override_no_update," \ - " s.ddns_override_client_update," \ - " s.ddns_replace_client_name," \ - " s.ddns_generated_prefix," \ - " s.ddns_qualifying_suffix," \ - " s.reservations_in_subnet," \ - " s.reservations_out_of_pool," \ - " s.cache_threshold," \ - " s.cache_max_age," \ - " srv.tag " \ - "FROM dhcp6_subnet AS s " server_join \ - "LEFT JOIN dhcp6_pool AS p ON s.subnet_id = p.subnet_id " \ - "LEFT JOIN dhcp6_pd_pool AS d ON s.subnet_id = d.subnet_id " \ - "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \ - "LEFT JOIN dhcp6_options AS y ON y.scope_id = 6 AND d.id = y.pd_pool_id " \ - "LEFT JOIN dhcp6_options AS o ON o.scope_id = 1 AND s.subnet_id = " \ - "o.dhcp6_subnet_id " #__VA_ARGS__ \ - " ORDER BY s.subnet_id, p.id, d.id, x.option_id, y.option_id, o.option_id" - -#define PGSQL_GET_SUBNET6_NO_TAG(...) \ - PGSQL_GET_SUBNET6_COMMON("INNER JOIN dhcp6_subnet_server AS a " \ - " ON s.subnet_id = a.subnet_id " \ - "INNER JOIN dhcp6_server AS srv " \ - " ON (a.server_id = srv.id) ", \ - __VA_ARGS__) - -#define PGSQL_GET_SUBNET6_ANY(...) \ - PGSQL_GET_SUBNET6_COMMON("LEFT JOIN dhcp6_subnet_server AS a " \ - " ON s.subnet_id = a.subnet_id " \ - "LEFT JOIN dhcp6_server AS srv " \ - " ON a.server_id = srv.id ", \ - __VA_ARGS__) - -#define PGSQL_GET_SUBNET6_UNASSIGNED(...) \ - PGSQL_GET_SUBNET6_COMMON("LEFT JOIN dhcp6_subnet_server AS a " \ - " ON s.subnet_id = a.subnet_id " \ - "LEFT JOIN dhcp6_server AS srv " \ - " ON a.server_id = srv.id ", \ - WHERE a.subnet_id IS NULL __VA_ARGS__) - -#endif - -#ifndef PGSQL_GET_POOL4_COMMON -#define PGSQL_GET_POOL4_COMMON(server_join, ...) \ - "SELECT" \ - " p.id," \ - " p.start_address," \ - " p.end_address," \ - " p.subnet_id," \ - " p.client_class," \ - " p.require_client_classes," \ - " p.user_context," \ - " p.modification_ts," \ - " x.option_id," \ - " x.code," \ - " x.value," \ - " x.formatted_value," \ - " x.space," \ - " x.persistent," \ - " x.dhcp4_subnet_id," \ - " x.scope_id," \ - " x.user_context," \ - " x.shared_network_name," \ - " x.pool_id," \ - " x.modification_ts " \ - "FROM dhcp4_pool AS p " server_join \ - "LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " #__VA_ARGS__ \ - " ORDER BY p.id, x.option_id" - -#define PGSQL_GET_POOL4_RANGE_WITH_TAG(...) \ - PGSQL_GET_POOL4_COMMON("INNER JOIN dhcp4_subnet_server AS s ON p.subnet_id = s.subnet_id " \ - "INNER JOIN dhcp4_server AS srv " \ - " ON (s.server_id = srv.id) OR (s.server_id = 1) ", \ - __VA_ARGS__) - -#define PGSQL_GET_POOL4_RANGE_NO_TAG(...) PGSQL_GET_POOL4_COMMON("", __VA_ARGS__) -#endif - -#ifndef PGSQL_GET_POOL6_COMMON -#define PGSQL_GET_POOL6_COMMON(server_join, ...) \ - "SELECT" \ - " p.id," \ - " p.start_address," \ - " p.end_address," \ - " p.subnet_id," \ - " p.client_class," \ - " p.require_client_classes," \ - " p.user_context," \ - " p.modification_ts," \ - " x.option_id," \ - " x.code," \ - " x.value," \ - " x.formatted_value," \ - " x.space," \ - " x.persistent," \ - " x.dhcp6_subnet_id," \ - " x.scope_id," \ - " x.user_context," \ - " x.shared_network_name," \ - " x.pool_id," \ - " x.modification_ts," \ - " x.pd_pool_id " \ - "FROM dhcp6_pool AS p " server_join \ - "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " #__VA_ARGS__ \ - " ORDER BY p.id, x.option_id" - -#define PGSQL_GET_POOL6_RANGE_WITH_TAG(...) \ - PGSQL_GET_POOL6_COMMON("INNER JOIN dhcp6_subnet_server AS s ON p.subnet_id = s.subnet_id " \ - "INNER JOIN dhcp6_server AS srv " \ - " ON (s.server_id = srv.id) OR (s.server_id = 1) ", \ - __VA_ARGS__) - -#define PGSQL_GET_POOL6_RANGE_NO_TAG(...) PGSQL_GET_POOL6_COMMON("", __VA_ARGS__) -#endif - -#ifndef PGSQL_GET_PD_POOL_COMMON -#define PGSQL_GET_PD_POOL_COMMON(server_join, ...) \ - "SELECT" \ - " p.id," \ - " p.prefix," \ - " p.prefix_length," \ - " p.delegated_prefix_length," \ - " p.subnet_id," \ - " p.excluded_prefix," \ - " p.excluded_prefix_length," \ - " p.client_class," \ - " p.require_client_classes," \ - " p.user_context," \ - " p.modification_ts," \ - " x.option_id," \ - " x.code," \ - " x.value," \ - " x.formatted_value," \ - " x.space," \ - " x.persistent," \ - " x.dhcp6_subnet_id," \ - " x.scope_id," \ - " x.user_context," \ - " x.shared_network_name," \ - " x.pool_id," \ - " x.modification_ts," \ - " x.pd_pool_id " \ - "FROM dhcp6_pd_pool AS p " server_join \ - "LEFT JOIN dhcp6_options AS x ON x.scope_id = 6 AND p.id = x.pd_pool_id " #__VA_ARGS__ \ - " ORDER BY p.id, x.option_id" - -#define PGSQL_GET_PD_POOL_WITH_TAG(...) \ - PGSQL_GET_PD_POOL_COMMON("INNER JOIN dhcp6_subnet_server AS s ON p.subnet_id = s.subnet_id " \ - "INNER JOIN dhcp6_server AS srv " \ - " ON (s.server_id = srv.id) OR (s.server_id = 1) ", \ - __VA_ARGS__) - -#define PGSQL_GET_PD_POOL_NO_TAG(...) PGSQL_GET_PD_POOL_COMMON("", __VA_ARGS__) -#endif - -#ifndef PGSQL_GET_SHARED_NETWORK4 -#define PGSQL_GET_SHARED_NETWORK4_COMMON(server_join, ...) \ - "SELECT" \ - " n.id," \ - " n.name," \ - " n.client_class," \ - " n.interface," \ - " n.match_client_id," \ - " n.modification_ts," \ - " n.rebind_timer," \ - " n.relay," \ - " n.renew_timer," \ - " n.require_client_classes," \ - " n.reservations_global," \ - " n.user_context," \ - " n.valid_lifetime," \ - " o.option_id," \ - " o.code," \ - " o.value," \ - " o.formatted_value," \ - " o.space," \ - " o.persistent," \ - " o.dhcp4_subnet_id," \ - " o.scope_id," \ - " o.user_context," \ - " o.shared_network_name," \ - " o.pool_id," \ - " o.modification_ts," \ - " n.calculate_tee_times," \ - " n.t1_percent," \ - " n.t2_percent," \ - " n.authoritative," \ - " n.boot_file_name," \ - " n.next_server," \ - " n.server_hostname," \ - " n.min_valid_lifetime," \ - " n.max_valid_lifetime," \ - " n.ddns_send_updates," \ - " n.ddns_override_no_update," \ - " n.ddns_override_client_update," \ - " n.ddns_replace_client_name," \ - " n.ddns_generated_prefix," \ - " n.ddns_qualifying_suffix," \ - " n.reservations_in_subnet," \ - " n.reservations_out_of_pool," \ - " n.cache_threshold," \ - " n.cache_max_age," \ - " s.tag " \ - "FROM dhcp4_shared_network AS n " server_join \ - "LEFT JOIN dhcp4_options AS o ON o.scope_id = 4 AND n.name = " \ - "o.shared_network_name " #__VA_ARGS__ " ORDER BY n.id, s.id, o.option_id" - -#define PGSQL_GET_SHARED_NETWORK4_NO_TAG(...) \ - PGSQL_GET_SHARED_NETWORK4_COMMON("INNER JOIN dhcp4_shared_network_server AS a " \ - " ON n.id = a.shared_network_id " \ - "INNER JOIN dhcp4_server AS s " \ - " ON (a.server_id = s.id) ", \ - __VA_ARGS__) - -#define PGSQL_GET_SHARED_NETWORK4_ANY(...) \ - PGSQL_GET_SHARED_NETWORK4_COMMON("LEFT JOIN dhcp4_shared_network_server AS a " \ - " ON n.id = a.shared_network_id " \ - "LEFT JOIN dhcp4_server AS s " \ - " ON a.server_id = s.id ", \ - __VA_ARGS__) - -#define PGSQL_GET_SHARED_NETWORK4_UNASSIGNED(...) \ - PGSQL_GET_SHARED_NETWORK4_COMMON("LEFT JOIN dhcp4_shared_network_server AS a " \ - " ON n.id = a.shared_network_id " \ - "LEFT JOIN dhcp4_server AS s " \ - " ON a.server_id = s.id ", \ - WHERE a.shared_network_id IS NULL __VA_ARGS__) - -#endif - -#ifndef PGSQL_GET_SHARED_NETWORK6 -#define PGSQL_GET_SHARED_NETWORK6_COMMON(server_join, ...) \ - "SELECT" \ - " n.id," \ - " n.name," \ - " n.client_class," \ - " n.interface," \ - " n.modification_ts," \ - " n.preferred_lifetime," \ - " n.rapid_commit," \ - " n.rebind_timer," \ - " n.relay," \ - " n.renew_timer," \ - " n.require_client_classes," \ - " n.reservations_global," \ - " n.user_context," \ - " n.valid_lifetime," \ - " o.option_id," \ - " o.code," \ - " o.value," \ - " o.formatted_value," \ - " o.space," \ - " o.persistent," \ - " o.dhcp6_subnet_id," \ - " o.scope_id," \ - " o.user_context," \ - " o.shared_network_name," \ - " o.pool_id," \ - " o.modification_ts," \ - " o.pd_pool_id, " \ - " n.calculate_tee_times," \ - " n.t1_percent," \ - " n.t2_percent," \ - " n.interface_id," \ - " n.min_preferred_lifetime," \ - " n.max_preferred_lifetime," \ - " n.min_valid_lifetime," \ - " n.max_valid_lifetime," \ - " n.ddns_send_updates," \ - " n.ddns_override_no_update," \ - " n.ddns_override_client_update," \ - " n.ddns_replace_client_name," \ - " n.ddns_generated_prefix," \ - " n.ddns_qualifying_suffix," \ - " n.reservations_in_subnet," \ - " n.reservations_out_of_pool," \ - " n.cache_threshold," \ - " n.cache_max_age," \ - " s.tag " \ - "FROM dhcp6_shared_network AS n " server_join \ - "LEFT JOIN dhcp6_options AS o ON o.scope_id = 4 AND n.name = " \ - "o.shared_network_name " #__VA_ARGS__ " ORDER BY n.id, s.id, o.option_id" - -#define PGSQL_GET_SHARED_NETWORK6_NO_TAG(...) \ - PGSQL_GET_SHARED_NETWORK6_COMMON("INNER JOIN dhcp6_shared_network_server AS a " \ - " ON n.id = a.shared_network_id " \ - "INNER JOIN dhcp6_server AS s " \ - " ON (a.server_id = s.id) ", \ - __VA_ARGS__) - -#define PGSQL_GET_SHARED_NETWORK6_ANY(...) \ - PGSQL_GET_SHARED_NETWORK6_COMMON("LEFT JOIN dhcp6_shared_network_server AS a " \ - " ON n.id = a.shared_network_id " \ - "LEFT JOIN dhcp6_server AS s " \ - " ON a.server_id = s.id ", \ - __VA_ARGS__) - -#define PGSQL_GET_SHARED_NETWORK6_UNASSIGNED(...) \ - PGSQL_GET_SHARED_NETWORK6_COMMON("LEFT JOIN dhcp6_shared_network_server AS a " \ - " ON n.id = a.shared_network_id " \ - "LEFT JOIN dhcp6_server AS s " \ - " ON a.server_id = s.id ", \ - WHERE a.shared_network_id IS NULL __VA_ARGS__) - -#endif - -#ifndef PGSQL_GET_OPTION_DEF -#define PGSQL_GET_OPTION_DEF(table_prefix, ...) \ - "SELECT" \ - " d.id," \ - " d.code," \ - " d.name," \ - " d.space," \ - " d.type," \ - " d.modification_ts," \ - " d.is_array," \ - " d.encapsulate," \ - " d.record_types," \ - " d.user_context," \ - " s.tag " \ - "FROM " #table_prefix "_option_def AS d " \ - "INNER JOIN " #table_prefix "_option_def_server AS a" \ - " ON d.id = a.option_def_id " \ - "INNER JOIN " #table_prefix "_server AS s " \ - " ON a.server_id = s.id " \ - "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ " ORDER BY d.id" -#endif - -#ifndef PGSQL_GET_OPTION_COMMON -#define PGSQL_GET_OPTION_COMMON(table_prefix, pd_pool_id, ...) \ - "SELECT" \ - " o.option_id," \ - " o.code," \ - " o.value," \ - " o.formatted_value," \ - " o.space," \ - " o.persistent," \ - " o." #table_prefix "_subnet_id," \ - " o.scope_id," \ - " o.user_context," \ - " o.shared_network_name," \ - " o.pool_id," \ - " o.modification_ts," \ - " s.tag " pd_pool_id "FROM " #table_prefix "_options AS o " \ - "INNER JOIN " #table_prefix "_options_server AS a" \ - " ON o.option_id = a.option_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON a.server_id = s.id " \ - "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ " ORDER BY o.option_id, s.id" - -#define PGSQL_GET_OPTION4(...) PGSQL_GET_OPTION_COMMON(dhcp4, "", __VA_ARGS__) -#define PGSQL_GET_OPTION6(...) PGSQL_GET_OPTION_COMMON(dhcp6, ", o.pd_pool_id ", __VA_ARGS__) -#endif - -#ifndef PGSQL_GET_AUDIT_ENTRIES_TIME -#define PGSQL_GET_AUDIT_ENTRIES_TIME(table_prefix) \ - "SELECT" \ - " a.id," \ - " a.object_type," \ - " a.object_id," \ - " a.modification_type," \ - " r.modification_ts," \ - " r.id, " \ - " r.log_message " \ - "FROM " #table_prefix "_audit AS a " \ - "INNER JOIN " #table_prefix "_audit_revision AS r " \ - " ON a.revision_id = r.id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON r.server_id = s.id " \ - "WHERE (s.tag = ? OR s.id = 1) AND ((r.modification_ts, r.id) > (?, ?))" \ - " ORDER BY r.modification_ts, r.id" -#endif - -#ifndef PGSQL_GET_SERVERS_COMMON -#define PGSQL_GET_SERVERS_COMMON(table_prefix, ...) \ - "SELECT" \ - " s.id," \ - " s.tag," \ - " s.description," \ - " s.modification_ts " \ - "FROM " #table_prefix "_server AS s " \ - "WHERE s.id > 1 " __VA_ARGS__ "ORDER BY s.id" -#define PGSQL_GET_ALL_SERVERS(table_prefix) PGSQL_GET_SERVERS_COMMON(table_prefix, "") -#define PGSQL_GET_SERVER(table_prefix) PGSQL_GET_SERVERS_COMMON(table_prefix, "AND s.tag = ? ") -#endif - -#ifndef PGSQL_INSERT_GLOBAL_PARAMETER -#define PGSQL_INSERT_GLOBAL_PARAMETER(table_prefix) \ - "INSERT INTO " #table_prefix "_global_parameter(" \ - " name," \ - " value," \ - " parameter_type," \ - " modification_ts" \ - ") VALUES (?, ?, ?, ?)" -#endif - -#ifndef PGSQL_INSERT_GLOBAL_PARAMETER_SERVER -#define PGSQL_INSERT_GLOBAL_PARAMETER_SERVER(table_prefix) \ - "INSERT INTO " #table_prefix "_global_parameter_server(" \ - " parameter_id," \ - " modification_ts," \ - " server_id" \ - ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))" -#endif - -#ifndef PGSQL_INSERT_SUBNET_SERVER -#define PGSQL_INSERT_SUBNET_SERVER(table_prefix) \ - "INSERT INTO " #table_prefix "_subnet_server(" \ - " subnet_id," \ - " modification_ts," \ - " server_id" \ - ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))" -#endif - -#ifndef PGSQL_INSERT_POOL -#define PGSQL_INSERT_POOL(table_prefix) \ - "INSERT INTO " #table_prefix "_pool(" \ - " start_address," \ - " end_address," \ - " subnet_id," \ - " client_class," \ - " require_client_classes," \ - " user_context," \ - " modification_ts" \ - ") VALUES (?, ?, ?, ?, ?, ?, ?)" -#endif - -#ifndef PGSQL_INSERT_PD_POOL -#define PGSQL_INSERT_PD_POOL() \ - "INSERT INTO dhcp6_pd_pool(" \ - " prefix," \ - " prefix_length," \ - " delegated_prefix_length," \ - " subnet_id," \ - " excluded_prefix," \ - " excluded_prefix_length," \ - " client_class," \ - " require_client_classes," \ - " user_context," \ - " modification_ts" \ - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" -#endif - -#ifndef PGSQL_INSERT_SHARED_NETWORK_SERVER -#define PGSQL_INSERT_SHARED_NETWORK_SERVER(table_prefix) \ - "INSERT INTO " #table_prefix "_shared_network_server(" \ - " shared_network_id," \ - " modification_ts," \ - " server_id" \ - ") VALUES (" \ - " (SELECT id FROM " #table_prefix "_shared_network WHERE name = ?), ?," \ - " (SELECT id FROM " #table_prefix "_server WHERE tag = ?)" \ - ")" -#endif - -#ifndef PGSQL_INSERT_OPTION_DEF -#define PGSQL_INSERT_OPTION_DEF(table_prefix) \ - "INSERT INTO " #table_prefix "_option_def (" \ - " code," \ - " name," \ - " space," \ - " type," \ - " modification_ts," \ - " is_array," \ - " encapsulate," \ - " record_types," \ - " user_context" \ - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" -#endif - -#ifndef PGSQL_INSERT_OPTION_DEF_SERVER -#define PGSQL_INSERT_OPTION_DEF_SERVER(table_prefix) \ - "INSERT INTO " #table_prefix "_option_def_server(" \ - " option_def_id," \ - " modification_ts," \ - " server_id" \ - ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))" -#endif - -#ifndef PGSQL_INSERT_OPTION_COMMON -#define PGSQL_INSERT_OPTION_COMMON(table_prefix, pd_pool_id, last) \ - "INSERT INTO " #table_prefix "_options (" \ - " code," \ - " value," \ - " formatted_value," \ - " space," \ - " persistent," \ - " dhcp_client_class," \ - " " #table_prefix "_subnet_id," \ - " scope_id," \ - " user_context," \ - " shared_network_name," \ - " pool_id," \ - " modification_ts" pd_pool_id ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?" last ")" - -#define PGSQL_INSERT_OPTION4() PGSQL_INSERT_OPTION_COMMON(dhcp4, "", "") -#define PGSQL_INSERT_OPTION6() PGSQL_INSERT_OPTION_COMMON(dhcp6, ", pd_pool_id ", ", ?") -#endif - -#ifndef PGSQL_INSERT_OPTION_SERVER -#define PGSQL_INSERT_OPTION_SERVER(table_prefix) \ - "INSERT INTO " #table_prefix "_options_server (" \ - " option_id," \ - " modification_ts," \ - " server_id" \ - ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))" -#endif - -#ifndef PGSQL_INSERT_SERVER -#define PGSQL_INSERT_SERVER(table_prefix) \ - "INSERT INTO " #table_prefix "_server (" \ - " tag," \ - " description," \ - " modification_ts" \ - ") VALUES (?, ?, ?)" -#endif - -#ifndef PGSQL_UPDATE_GLOBAL_PARAMETER -#define PGSQL_UPDATE_GLOBAL_PARAMETER(table_prefix) \ - "UPDATE " #table_prefix "_global_parameter AS g " \ - "INNER JOIN " #table_prefix "_global_parameter_server AS a" \ - " ON g.id = a.parameter_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON a.server_id = s.id " \ - "SET" \ - " g.name = ?," \ - " g.value = ?," \ - " g.parameter_type = ?," \ - " g.modification_ts = ? " \ - "WHERE s.tag = ? AND g.name = ?" -#endif - -#ifndef PGSQL_UPDATE_OPTION_DEF -#define PGSQL_UPDATE_OPTION_DEF(table_prefix) \ - "UPDATE " #table_prefix "_option_def AS d " \ - "INNER JOIN " #table_prefix "_option_def_server AS a" \ - " ON d.id = a.option_def_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON a.server_id = s.id " \ - "SET" \ - " d.code = ?," \ - " d.name = ?," \ - " d.space = ?," \ - " d.type = ?," \ - " d.modification_ts = ?," \ - " d.is_array = ?," \ - " d.encapsulate = ?," \ - " d.record_types = ?," \ - " d.user_context = ? " \ - "WHERE s.tag = ? AND d.code = ? AND d.space = ?" -#endif - -#ifndef PGSQL_UPDATE_OPTION_COMMON -#define PGSQL_UPDATE_OPTION_COMMON(table_prefix, server_join, pd_pool_id, ...) \ - "UPDATE " #table_prefix "_options AS o " server_join "SET" \ - " o.code = ?," \ - " o.value = ?," \ - " o.formatted_value = ?," \ - " o.space = ?," \ - " o.persistent = ?," \ - " o.dhcp_client_class = ?," \ - " o." #table_prefix "_subnet_id = ?," \ - " o.scope_id = ?," \ - " o.user_context = ?," \ - " o.shared_network_name = ?," \ - " o.pool_id = ?," \ - " o.modification_ts = ? " pd_pool_id "WHERE " #__VA_ARGS__ - -#define PGSQL_UPDATE_OPTION4_WITH_TAG(...) \ - PGSQL_UPDATE_OPTION_COMMON(dhcp4, \ - "INNER JOIN dhcp4_options_server AS a" \ - " ON o.option_id = a.option_id " \ - "INNER JOIN dhcp4_server AS s" \ - " ON a.server_id = s.id ", \ - "", s.tag = ? __VA_ARGS__) - -#define PGSQL_UPDATE_OPTION4_NO_TAG(...) PGSQL_UPDATE_OPTION_COMMON(dhcp4, "", "", __VA_ARGS__) - -#define PGSQL_UPDATE_OPTION6_WITH_TAG(...) \ - PGSQL_UPDATE_OPTION_COMMON(dhcp6, \ - "INNER JOIN dhcp6_options_server AS a" \ - " ON o.option_id = a.option_id " \ - "INNER JOIN dhcp6_server AS s" \ - " ON a.server_id = s.id ", \ - ", o.pd_pool_id = ? ", s.tag = ? __VA_ARGS__) - -#define PGSQL_UPDATE_OPTION6_NO_TAG(...) \ - PGSQL_UPDATE_OPTION_COMMON(dhcp6, "", ", o.pd_pool_id = ? ", __VA_ARGS__) -#endif - -#ifndef PGSQL_UPDATE_SERVER -#define PGSQL_UPDATE_SERVER(table_prefix) \ - "UPDATE " #table_prefix "_server " \ - "SET" \ - " tag = ?," \ - " description = ?," \ - " modification_ts = ? " \ - "WHERE tag = ?" -#endif - -#ifndef PGSQL_DELETE_GLOBAL_PARAMETER -#define PGSQL_DELETE_GLOBAL_PARAMETER(table_prefix, ...) \ - "DELETE g FROM " #table_prefix "_global_parameter AS g " \ - "INNER JOIN " #table_prefix "_global_parameter_server AS a " \ - " ON g.id = a.parameter_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON (a.server_id = s.id) " \ - "WHERE s.tag = ? " #__VA_ARGS__ -#endif - -#ifndef PGSQL_DELETE_GLOBAL_PARAMETER_UNASSIGNED -#define PGSQL_DELETE_GLOBAL_PARAMETER_UNASSIGNED(table_prefix, ...) \ - "DELETE g FROM " #table_prefix "_global_parameter AS g " \ - "LEFT JOIN " #table_prefix "_global_parameter_server AS a " \ - " ON g.id = a.parameter_id " \ - "WHERE a.parameter_id IS NULL " #__VA_ARGS__ -#endif - -#ifndef PGSQL_DELETE_SUBNET -#define PGSQL_DELETE_SUBNET_COMMON(table_prefix, ...) \ - "DELETE s FROM " #table_prefix "_subnet AS s " \ - "INNER JOIN " #table_prefix "_subnet_server AS a " \ - " ON s.subnet_id = a.subnet_id " \ - "INNER JOIN " #table_prefix "_server AS srv" \ - " ON a.server_id = srv.id " #__VA_ARGS__ - -#define PGSQL_DELETE_SUBNET_WITH_TAG(table_prefix, ...) \ - PGSQL_DELETE_SUBNET_COMMON(table_prefix, WHERE srv.tag = ? __VA_ARGS__) - -#define PGSQL_DELETE_SUBNET_ANY(table_prefix, ...) \ - "DELETE s FROM " #table_prefix "_subnet AS s " #__VA_ARGS__ - -#define PGSQL_DELETE_SUBNET_UNASSIGNED(table_prefix, ...) \ - "DELETE s FROM " #table_prefix "_subnet AS s " \ - "LEFT JOIN " #table_prefix "_subnet_server AS a" \ - " ON s.subnet_id = a.subnet_id " \ - "WHERE a.subnet_id IS NULL " #__VA_ARGS__ - -#endif - -#ifndef PGSQL_DELETE_SUBNET_SERVER -#define PGSQL_DELETE_SUBNET_SERVER(table_prefix) \ - "DELETE FROM " #table_prefix "_subnet_server " \ - "WHERE subnet_id = ?" -#endif - -#ifndef PGSQL_DELETE_POOLS -#define PGSQL_DELETE_POOLS(table_prefix) \ - "DELETE FROM " #table_prefix "_pool " \ - "WHERE subnet_id = ? OR subnet_id = " \ - "(SELECT subnet_id FROM " #table_prefix "_subnet" \ - " WHERE subnet_prefix = ?)" -#endif - -#ifndef PGSQL_DELETE_PD_POOLS -#define PGSQL_DELETE_PD_POOLS() \ - "DELETE FROM dhcp6_pd_pool " \ - "WHERE subnet_id = ? OR subnet_id = " \ - "(SELECT subnet_id FROM dhcp6_subnet" \ - " WHERE subnet_prefix = ?)" -#endif - -#ifndef PGSQL_DELETE_SHARED_NETWORK_COMMON -#define PGSQL_DELETE_SHARED_NETWORK_COMMON(table_prefix, ...) \ - "DELETE n FROM " #table_prefix "_shared_network AS n " \ - "INNER JOIN " #table_prefix "_shared_network_server AS a" \ - " ON n.id = a.shared_network_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON a.server_id = s.id " #__VA_ARGS__ - -#define PGSQL_DELETE_SHARED_NETWORK_WITH_TAG(table_prefix, ...) \ - PGSQL_DELETE_SHARED_NETWORK_COMMON(table_prefix, WHERE s.tag = ? __VA_ARGS__) - -#define PGSQL_DELETE_SHARED_NETWORK_ANY(table_prefix, ...) \ - "DELETE n FROM " #table_prefix "_shared_network AS n " #__VA_ARGS__ - -#define PGSQL_DELETE_SHARED_NETWORK_UNASSIGNED(table_prefix, ...) \ - "DELETE n FROM " #table_prefix "_shared_network AS n " \ - "LEFT JOIN " #table_prefix "_shared_network_server AS a" \ - " ON n.id = a.shared_network_id " \ - "WHERE a.shared_network_id IS NULL " #__VA_ARGS__ - -#endif - -#ifndef PGSQL_DELETE_SHARED_NETWORK_SERVER -#define PGSQL_DELETE_SHARED_NETWORK_SERVER(table_prefix) \ - "DELETE FROM " #table_prefix "_shared_network_server " \ - "WHERE shared_network_id = " \ - "(SELECT id FROM " #table_prefix "_shared_network WHERE name = ?)" -#endif - -#ifndef PGSQL_DELETE_OPTION_DEF -#define PGSQL_DELETE_OPTION_DEF(table_prefix, ...) \ - "DELETE d FROM " #table_prefix "_option_def AS d " \ - "INNER JOIN " #table_prefix "_option_def_server AS a" \ - " ON d.id = a.option_def_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON a.server_id = s.id " \ - "WHERE s.tag = ? " #__VA_ARGS__ -#endif - -#ifndef PGSQL_DELETE_OPTION_DEF_UNASSIGNED -#define PGSQL_DELETE_OPTION_DEF_UNASSIGNED(table_prefix, ...) \ - "DELETE d FROM " #table_prefix "_option_def AS d " \ - "LEFT JOIN " #table_prefix "_option_def_server AS a " \ - " ON d.id = a.option_def_id " \ - "WHERE a.option_def_id IS NULL " #__VA_ARGS__ -#endif - -#ifndef PGSQL_DELETE_OPTION_WITH_TAG -#define PGSQL_DELETE_OPTION_WITH_TAG(table_prefix, ...) \ - "DELETE o FROM " #table_prefix "_options AS o " \ - "INNER JOIN " #table_prefix "_options_server AS a" \ - " ON o.option_id = a.option_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ - " ON a.server_id = s.id " \ - "WHERE s.tag = ? " #__VA_ARGS__ -#endif - -#ifndef PGSQL_DELETE_OPTION_NO_TAG -#define PGSQL_DELETE_OPTION_NO_TAG(table_prefix, ...) \ - "DELETE o FROM " #table_prefix "_options AS o " #__VA_ARGS__ -#endif - -#ifndef PGSQL_DELETE_OPTION_SUBNET_ID_PREFIX -#define PGSQL_DELETE_OPTION_SUBNET_ID_PREFIX(table_prefix) \ - "DELETE o FROM " #table_prefix "_options AS o " \ - "INNER JOIN " #table_prefix "_subnet AS s " \ - " ON s.subnet_id = o." #table_prefix "_subnet_id " \ - "WHERE o.scope_id = 1 AND (s.subnet_id = ? OR s.subnet_prefix = ?)" -#endif - -#ifndef PGSQL_DELETE_OPTION_UNASSIGNED -#define PGSQL_DELETE_OPTION_UNASSIGNED(table_prefix, ...) \ - "DELETE o FROM " #table_prefix "_options AS o " \ - "LEFT JOIN " #table_prefix "_options_server AS a " \ - " ON o.option_id = a.option_id " \ - "WHERE a.option_id IS NULL " #__VA_ARGS__ -#endif - -#ifndef PGSQL_DELETE_OPTION_POOL_RANGE -#define PGSQL_DELETE_OPTION_POOL_RANGE(table_prefix, ...) \ - "DELETE o FROM " #table_prefix "_options AS o " \ - "WHERE " #__VA_ARGS__ " AND o.pool_id = " \ - " (SELECT id FROM " #table_prefix "_pool" \ - " WHERE start_address = ? AND end_address = ?)" -#endif - -#ifndef PGSQL_DELETE_OPTION_PD_POOL -#define PGSQL_DELETE_OPTION_PD_POOL(...) \ - "DELETE o FROM dhcp6_options AS o " \ - "WHERE " #__VA_ARGS__ " AND o.pd_pool_id = " \ - " (SELECT id FROM dhcp6_pd_pool" \ - " WHERE prefix = ? AND prefix_length = ?)" -#endif - -#ifndef PGSQL_DELETE_SERVER -#define PGSQL_DELETE_SERVER(table_prefix) \ - "DELETE FROM " #table_prefix "_server " \ - "WHERE tag = ?" -#endif - -#ifndef PGSQL_DELETE_ALL_SERVERS -#define PGSQL_DELETE_ALL_SERVERS(table_prefix) \ - "DELETE FROM " #table_prefix "_server " \ - "WHERE id > 1" -#endif - -} // end of anonymous namespace - -} // namespace dhcp -} // end of namespace isc - -#endif diff --git a/src/hooks/dhcp/pgsql_cb/pgsql_query_macros_dhcp.h b/src/hooks/dhcp/pgsql_cb/pgsql_query_macros_dhcp.h new file mode 100644 index 0000000000..e59fc8e63f --- /dev/null +++ b/src/hooks/dhcp/pgsql_cb/pgsql_query_macros_dhcp.h @@ -0,0 +1,1369 @@ +// 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_QUERY_MACROS_DHCP_H +#define PGSQL_QUERY_MACROS_DHCP_H + +/// @file pgsql_query_macros_dhcp.h +/// Collection of common macros defining PgSQL prepared statements used +/// to manage Kea DHCP configuration in the database. +/// +/// Some of the macros are DHCPv4 specific, other are DHCPv6 specific. +/// Some macros are common for both DHCP server types. The first +/// parameter @c table_prefix should be set to @c dhcp4 or @c dhcp6, +/// depending which DHCP server type it relates to. Provided value +/// is used as a prefix for PgSQL table names. For example, if the +/// prefix is set to @c dhcp4, the table name referred to in the +/// query may be dhcp4_subnet etc. The second argument in the variadic +/// macro is a part of the WHERE clause in the PgSQL query. The fixed +/// part of the WHERE clause is included in the macro. + +/// @todo Update queries to also fetch server tags to associate +/// returned configuration elements with particular servers. + +namespace isc { +namespace dhcp { + +namespace { + +#ifndef PGSQL_GET_GLOBAL_PARAMETER +#define PGSQL_GET_GLOBAL_PARAMETER(table_prefix, ...) \ + "SELECT" \ + " g.id," \ + " g.name," \ + " g.value," \ + " g.parameter_type," \ + " extract(epoch from g.modification_ts)::bigint as modification_ts, " \ + " s.tag " \ + "FROM " #table_prefix "_global_parameter AS g " \ + "INNER JOIN " #table_prefix "_global_parameter_server AS a " \ + " ON g.id = a.parameter_id " \ + "INNER JOIN " #table_prefix "_server AS s " \ + " ON (a.server_id = s.id) " \ + "WHERE (s.tag = $1 OR s.id = 1) " #__VA_ARGS__ \ + " ORDER BY g.id, s.id" + +#endif + +#ifndef PGSQL_GET_SUBNET4 +#define PGSQL_GET_SUBNET4_COMMON(server_join, ...) \ + "SELECT" \ + " s.subnet_id," \ + " s.subnet_prefix," \ + " s.interface_4o6," \ + " s.interface_id_4o6," \ + " s.subnet_4o6," \ + " s.boot_file_name," \ + " s.client_class," \ + " s.interface," \ + " s.match_client_id," \ + " extract(epoch from s.modification_ts)::bigint as modification_ts, " \ + " s.next_server," \ + " s.rebind_timer," \ + " s.relay," \ + " s.renew_timer," \ + " s.require_client_classes," \ + " s.reservations_global," \ + " s.server_hostname," \ + " s.shared_network_name," \ + " s.user_context," \ + " s.valid_lifetime," \ + " p.id," \ + " p.start_address," \ + " p.end_address," \ + " p.subnet_id," \ + " extract(epoch from p.modification_ts)::bigint as modification_ts, " \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp4_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " extract(epoch from x.modification_ts)::bigint as modification_ts, " \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o.dhcp4_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " extract(epoch from o.modification_ts)::bigint as modification_ts, " \ + " s.calculate_tee_times," \ + " s.t1_percent," \ + " s.t2_percent," \ + " s.authoritative," \ + " s.min_valid_lifetime," \ + " s.max_valid_lifetime," \ + " p.client_class," \ + " p.require_client_classes," \ + " p.user_context," \ + " s.ddns_send_updates," \ + " s.ddns_override_no_update," \ + " s.ddns_override_client_update," \ + " s.ddns_replace_client_name," \ + " s.ddns_generated_prefix," \ + " s.ddns_qualifying_suffix," \ + " s.reservations_in_subnet," \ + " s.reservations_out_of_pool," \ + " s.cache_threshold," \ + " s.cache_max_age," \ + " srv.tag " \ + "FROM dhcp4_subnet AS s " \ + server_join \ + "LEFT JOIN dhcp4_pool AS p ON s.subnet_id = p.subnet_id " \ + "LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \ + "LEFT JOIN dhcp4_options AS o ON o.scope_id = 1 AND s.subnet_id = o.dhcp4_subnet_id " \ + #__VA_ARGS__ \ + " ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" + +#define PGSQL_GET_SUBNET4_NO_TAG(...) \ + PGSQL_GET_SUBNET4_COMMON( \ + "INNER JOIN dhcp4_subnet_server AS a " \ + " ON s.subnet_id = a.subnet_id " \ + "INNER JOIN dhcp4_server AS srv " \ + " ON (a.server_id = srv.id) ", \ + __VA_ARGS__) + +#define PGSQL_GET_SUBNET4_ANY(...) \ + PGSQL_GET_SUBNET4_COMMON( \ + "LEFT JOIN dhcp4_subnet_server AS a "\ + " ON s.subnet_id = a.subnet_id " \ + "LEFT JOIN dhcp4_server AS srv " \ + " ON a.server_id = srv.id ", \ + __VA_ARGS__) + +#define PGSQL_GET_SUBNET4_UNASSIGNED(...) \ + PGSQL_GET_SUBNET4_COMMON( \ + "LEFT JOIN dhcp4_subnet_server AS a "\ + " ON s.subnet_id = a.subnet_id " \ + "LEFT JOIN dhcp4_server AS srv " \ + " ON a.server_id = srv.id ", \ + WHERE a.subnet_id IS NULL __VA_ARGS__) + +#endif + +#ifndef PGSQL_GET_SUBNET6 +#define PGSQL_GET_SUBNET6_COMMON(server_join, ...) \ + "SELECT" \ + " s.subnet_id," \ + " s.subnet_prefix," \ + " s.client_class," \ + " s.interface," \ + " extract(epoch from s.modification_ts)::bigint as modification_ts, " \ + " s.preferred_lifetime," \ + " s.rapid_commit," \ + " s.rebind_timer," \ + " s.relay," \ + " s.renew_timer," \ + " s.require_client_classes," \ + " s.reservations_global," \ + " s.shared_network_name," \ + " s.user_context," \ + " s.valid_lifetime," \ + " p.id," \ + " p.start_address," \ + " p.end_address," \ + " p.subnet_id," \ + " extract(epoch from p.modification_ts)::bigint as modification_ts, " \ + " d.id," \ + " d.prefix," \ + " d.prefix_length," \ + " d.delegated_prefix_length," \ + " d.subnet_id," \ + " extract(epoch from d.modification_ts)::bigint as modification_ts, " \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp6_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " extract(epoch from x.modification_ts)::bigint as modification_ts, " \ + " x.pd_pool_id," \ + " y.option_id," \ + " y.code," \ + " y.value," \ + " y.formatted_value," \ + " y.space," \ + " y.persistent," \ + " y.dhcp6_subnet_id," \ + " y.scope_id," \ + " y.user_context," \ + " y.shared_network_name," \ + " y.pool_id," \ + " extract(epoch from y.modification_ts)::bigint as modification_ts, " \ + " y.pd_pool_id," \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o.dhcp6_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " extract(epoch from o.modification_ts)::bigint as modification_ts, " \ + " o.pd_pool_id, " \ + " s.calculate_tee_times," \ + " s.t1_percent," \ + " s.t2_percent," \ + " s.interface_id," \ + " s.min_preferred_lifetime," \ + " s.max_preferred_lifetime," \ + " s.min_valid_lifetime," \ + " s.max_valid_lifetime," \ + " p.client_class," \ + " p.require_client_classes," \ + " p.user_context," \ + " d.excluded_prefix," \ + " d.excluded_prefix_length," \ + " d.client_class," \ + " d.require_client_classes," \ + " d.user_context," \ + " s.ddns_send_updates," \ + " s.ddns_override_no_update," \ + " s.ddns_override_client_update," \ + " s.ddns_replace_client_name," \ + " s.ddns_generated_prefix," \ + " s.ddns_qualifying_suffix," \ + " s.reservations_in_subnet," \ + " s.reservations_out_of_pool," \ + " s.cache_threshold," \ + " s.cache_max_age," \ + " srv.tag " \ + "FROM dhcp6_subnet AS s " \ + server_join \ + "LEFT JOIN dhcp6_pool AS p ON s.subnet_id = p.subnet_id " \ + "LEFT JOIN dhcp6_pd_pool AS d ON s.subnet_id = d.subnet_id " \ + "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \ + "LEFT JOIN dhcp6_options AS y ON y.scope_id = 6 AND d.id = y.pd_pool_id " \ + "LEFT JOIN dhcp6_options AS o ON o.scope_id = 1 AND s.subnet_id = o.dhcp6_subnet_id " \ + #__VA_ARGS__ \ + " ORDER BY s.subnet_id, p.id, d.id, x.option_id, y.option_id, o.option_id" + +#define PGSQL_GET_SUBNET6_NO_TAG(...) \ + PGSQL_GET_SUBNET6_COMMON( \ + "INNER JOIN dhcp6_subnet_server AS a " \ + " ON s.subnet_id = a.subnet_id " \ + "INNER JOIN dhcp6_server AS srv " \ + " ON (a.server_id = srv.id) ", \ + __VA_ARGS__) + +#define PGSQL_GET_SUBNET6_ANY(...) \ + PGSQL_GET_SUBNET6_COMMON( \ + "LEFT JOIN dhcp6_subnet_server AS a "\ + " ON s.subnet_id = a.subnet_id " \ + "LEFT JOIN dhcp6_server AS srv " \ + " ON a.server_id = srv.id ", \ + __VA_ARGS__) + +#define PGSQL_GET_SUBNET6_UNASSIGNED(...) \ + PGSQL_GET_SUBNET6_COMMON( \ + "LEFT JOIN dhcp6_subnet_server AS a "\ + " ON s.subnet_id = a.subnet_id " \ + "LEFT JOIN dhcp6_server AS srv " \ + " ON a.server_id = srv.id ", \ + WHERE a.subnet_id IS NULL __VA_ARGS__) + +#endif + +#ifndef PGSQL_GET_POOL4_COMMON +#define PGSQL_GET_POOL4_COMMON(server_join, ...) \ + "SELECT" \ + " p.id," \ + " p.start_address," \ + " p.end_address," \ + " p.subnet_id," \ + " p.client_class," \ + " p.require_client_classes," \ + " p.user_context," \ + " extract(epoch from p.modification_ts)::bigint as modification_ts, " \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp4_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " extract(epoch from x.modification_ts)::bigint as modification_ts " \ + "FROM dhcp4_pool AS p " \ + server_join \ + "LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \ + #__VA_ARGS__ \ + " ORDER BY p.id, x.option_id" + +#define PGSQL_GET_POOL4_RANGE_WITH_TAG(...) \ + PGSQL_GET_POOL4_COMMON( \ + "INNER JOIN dhcp4_subnet_server AS s ON p.subnet_id = s.subnet_id " \ + "INNER JOIN dhcp4_server AS srv " \ + " ON (s.server_id = srv.id) OR (s.server_id = 1) ", \ + __VA_ARGS__) + +#define PGSQL_GET_POOL4_RANGE_NO_TAG(...) \ + PGSQL_GET_POOL4_COMMON("", __VA_ARGS__) +#endif + +#ifndef PGSQL_GET_POOL6_COMMON +#define PGSQL_GET_POOL6_COMMON(server_join, ...) \ + "SELECT" \ + " p.id," \ + " p.start_address," \ + " p.end_address," \ + " p.subnet_id," \ + " p.client_class," \ + " p.require_client_classes," \ + " p.user_context," \ + " extract(epoch from p.modification_ts)::bigint as modification_ts, " \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp6_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " extract(epoch from x.modification_ts)::bigint as modification_ts, " \ + " x.pd_pool_id " \ + "FROM dhcp6_pool AS p " \ + server_join \ + "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \ + #__VA_ARGS__ \ + " ORDER BY p.id, x.option_id" + +#define PGSQL_GET_POOL6_RANGE_WITH_TAG(...) \ + PGSQL_GET_POOL6_COMMON( \ + "INNER JOIN dhcp6_subnet_server AS s ON p.subnet_id = s.subnet_id " \ + "INNER JOIN dhcp6_server AS srv " \ + " ON (s.server_id = srv.id) OR (s.server_id = 1) ", \ + __VA_ARGS__) + +#define PGSQL_GET_POOL6_RANGE_NO_TAG(...) \ + PGSQL_GET_POOL6_COMMON("", __VA_ARGS__) +#endif + +#ifndef PGSQL_GET_PD_POOL_COMMON +#define PGSQL_GET_PD_POOL_COMMON(server_join, ...) \ + "SELECT" \ + " p.id," \ + " p.prefix," \ + " p.prefix_length," \ + " p.delegated_prefix_length," \ + " p.subnet_id," \ + " p.excluded_prefix," \ + " p.excluded_prefix_length," \ + " p.client_class," \ + " p.require_client_classes," \ + " p.user_context," \ + " extract(epoch from p.modification_ts)::bigint as modification_ts, " \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp6_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " extract(epoch from x.modification_ts)::bigint as modification_ts, " \ + " x.pd_pool_id " \ + "FROM dhcp6_pd_pool AS p " \ + server_join \ + "LEFT JOIN dhcp6_options AS x ON x.scope_id = 6 AND p.id = x.pd_pool_id " \ + #__VA_ARGS__ \ + " ORDER BY p.id, x.option_id" \ + +#define PGSQL_GET_PD_POOL_WITH_TAG(...) \ + PGSQL_GET_PD_POOL_COMMON( \ + "INNER JOIN dhcp6_subnet_server AS s ON p.subnet_id = s.subnet_id " \ + "INNER JOIN dhcp6_server AS srv " \ + " ON (s.server_id = srv.id) OR (s.server_id = 1) ", \ + __VA_ARGS__) + +#define PGSQL_GET_PD_POOL_NO_TAG(...) \ + PGSQL_GET_PD_POOL_COMMON("", __VA_ARGS__) +#endif + +#ifndef PGSQL_GET_SHARED_NETWORK4 +#define PGSQL_GET_SHARED_NETWORK4_COMMON(server_join, ...) \ + "SELECT" \ + " n.id," \ + " n.name," \ + " n.client_class," \ + " n.interface," \ + " n.match_client_id," \ + " extract(epoch from n.modification_ts)::bigint as modification_ts, " \ + " n.rebind_timer," \ + " n.relay," \ + " n.renew_timer," \ + " n.require_client_classes," \ + " n.reservations_global," \ + " n.user_context," \ + " n.valid_lifetime," \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o.dhcp4_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " extract(epoch from o.modification_ts)::bigint as modification_ts, " \ + " n.calculate_tee_times," \ + " n.t1_percent," \ + " n.t2_percent," \ + " n.authoritative," \ + " n.boot_file_name," \ + " n.next_server," \ + " n.server_hostname," \ + " n.min_valid_lifetime," \ + " n.max_valid_lifetime," \ + " n.ddns_send_updates," \ + " n.ddns_override_no_update," \ + " n.ddns_override_client_update," \ + " n.ddns_replace_client_name," \ + " n.ddns_generated_prefix," \ + " n.ddns_qualifying_suffix," \ + " n.reservations_in_subnet," \ + " n.reservations_out_of_pool," \ + " n.cache_threshold," \ + " n.cache_max_age," \ + " s.tag " \ + "FROM dhcp4_shared_network AS n " \ + server_join \ + "LEFT JOIN dhcp4_options AS o ON o.scope_id = 4 AND n.name = o.shared_network_name " \ + #__VA_ARGS__ \ + " ORDER BY n.id, s.id, o.option_id" + +#define PGSQL_GET_SHARED_NETWORK4_NO_TAG(...) \ + PGSQL_GET_SHARED_NETWORK4_COMMON( \ + "INNER JOIN dhcp4_shared_network_server AS a " \ + " ON n.id = a.shared_network_id " \ + "INNER JOIN dhcp4_server AS s " \ + " ON (a.server_id = s.id) ", \ + __VA_ARGS__) + +#define PGSQL_GET_SHARED_NETWORK4_ANY(...) \ + PGSQL_GET_SHARED_NETWORK4_COMMON( \ + "LEFT JOIN dhcp4_shared_network_server AS a " \ + " ON n.id = a.shared_network_id " \ + "LEFT JOIN dhcp4_server AS s " \ + " ON a.server_id = s.id ", \ + __VA_ARGS__) + +#define PGSQL_GET_SHARED_NETWORK4_UNASSIGNED(...) \ + PGSQL_GET_SHARED_NETWORK4_COMMON( \ + "LEFT JOIN dhcp4_shared_network_server AS a " \ + " ON n.id = a.shared_network_id " \ + "LEFT JOIN dhcp4_server AS s " \ + " ON a.server_id = s.id ", \ + WHERE a.shared_network_id IS NULL __VA_ARGS__) + +#endif + +#ifndef PGSQL_GET_SHARED_NETWORK6 +#define PGSQL_GET_SHARED_NETWORK6_COMMON(server_join, ...) \ + "SELECT" \ + " n.id," \ + " n.name," \ + " n.client_class," \ + " n.interface," \ + " extract(epoch from n.modification_ts)::bigint as modification_ts, " \ + " n.preferred_lifetime," \ + " n.rapid_commit," \ + " n.rebind_timer," \ + " n.relay," \ + " n.renew_timer," \ + " n.require_client_classes," \ + " n.reservations_global," \ + " n.user_context," \ + " n.valid_lifetime," \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o.dhcp6_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " extract(epoch from o.modification_ts)::bigint as modification_ts, " \ + " o.pd_pool_id, " \ + " n.calculate_tee_times," \ + " n.t1_percent," \ + " n.t2_percent," \ + " n.interface_id," \ + " n.min_preferred_lifetime," \ + " n.max_preferred_lifetime," \ + " n.min_valid_lifetime," \ + " n.max_valid_lifetime," \ + " n.ddns_send_updates," \ + " n.ddns_override_no_update," \ + " n.ddns_override_client_update," \ + " n.ddns_replace_client_name," \ + " n.ddns_generated_prefix," \ + " n.ddns_qualifying_suffix," \ + " n.reservations_in_subnet," \ + " n.reservations_out_of_pool," \ + " n.cache_threshold," \ + " n.cache_max_age," \ + " s.tag " \ + "FROM dhcp6_shared_network AS n " \ + server_join \ + "LEFT JOIN dhcp6_options AS o ON o.scope_id = 4 AND n.name = o.shared_network_name " \ + #__VA_ARGS__ \ + " ORDER BY n.id, s.id, o.option_id" + +#define PGSQL_GET_SHARED_NETWORK6_NO_TAG(...) \ + PGSQL_GET_SHARED_NETWORK6_COMMON( \ + "INNER JOIN dhcp6_shared_network_server AS a " \ + " ON n.id = a.shared_network_id " \ + "INNER JOIN dhcp6_server AS s " \ + " ON (a.server_id = s.id) ", \ + __VA_ARGS__) + +#define PGSQL_GET_SHARED_NETWORK6_ANY(...) \ + PGSQL_GET_SHARED_NETWORK6_COMMON( \ + "LEFT JOIN dhcp6_shared_network_server AS a " \ + " ON n.id = a.shared_network_id " \ + "LEFT JOIN dhcp6_server AS s " \ + " ON a.server_id = s.id ", \ + __VA_ARGS__) + +#define PGSQL_GET_SHARED_NETWORK6_UNASSIGNED(...) \ + PGSQL_GET_SHARED_NETWORK6_COMMON( \ + "LEFT JOIN dhcp6_shared_network_server AS a " \ + " ON n.id = a.shared_network_id " \ + "LEFT JOIN dhcp6_server AS s " \ + " ON a.server_id = s.id ", \ + WHERE a.shared_network_id IS NULL __VA_ARGS__) + +#endif + +#ifndef PGSQL_GET_OPTION_DEF +#define PGSQL_GET_OPTION_DEF(table_prefix, ...) \ + "SELECT" \ + " d.id," \ + " d.code," \ + " d.name," \ + " d.space," \ + " d.type," \ + " extract(epoch from d.modification_ts)::bigint as modification_ts, " \ + " d.is_array," \ + " d.encapsulate," \ + " d.record_types," \ + " d.user_context," \ + " s.tag " \ + "FROM " #table_prefix "_option_def AS d " \ + "INNER JOIN " #table_prefix "_option_def_server AS a" \ + " ON d.id = a.option_def_id " \ + "INNER JOIN " #table_prefix "_server AS s " \ + " ON a.server_id = s.id " \ + "WHERE (s.tag = $1 OR s.id = 1) " #__VA_ARGS__ \ + " ORDER BY d.id" +#endif + +#ifndef PGSQL_GET_OPTION_COMMON +#define PGSQL_GET_OPTION_COMMON(table_prefix, pd_pool_id, ...) \ + "SELECT" \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o." #table_prefix "_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " extract(epoch from o.modification_ts)::bigint as modification_ts, " \ + " s.tag " \ + pd_pool_id \ + "FROM " #table_prefix "_options AS o " \ + "INNER JOIN " #table_prefix "_options_server AS a" \ + " ON o.option_id = a.option_id " \ + "INNER JOIN " #table_prefix "_server AS s" \ + " ON a.server_id = s.id " \ + "WHERE (s.tag = $1 OR s.id = 1) " #__VA_ARGS__ \ + " ORDER BY o.option_id, s.id" + +#define PGSQL_GET_OPTION4(...) \ + PGSQL_GET_OPTION_COMMON(dhcp4, "", __VA_ARGS__) +#define PGSQL_GET_OPTION6(...) \ + PGSQL_GET_OPTION_COMMON(dhcp6, ", o.pd_pool_id ", __VA_ARGS__) +#endif + +#ifndef PGSQL_GET_AUDIT_ENTRIES_TIME +#define PGSQL_GET_AUDIT_ENTRIES_TIME(table_prefix) \ + "SELECT" \ + " a.id," \ + " a.object_type," \ + " a.object_id," \ + " a.modification_type," \ + " extract(epoch from r.modification_ts)::bigint as modification_ts, " \ + " r.id, " \ + " r.log_message " \ + "FROM " #table_prefix "_audit AS a " \ + "INNER JOIN " #table_prefix "_audit_revision AS r " \ + " ON a.revision_id = r.id " \ + "INNER JOIN " #table_prefix "_server AS s" \ + " ON r.server_id = s.id " \ + "WHERE (s.tag = $1 OR s.id = 1) AND ((r.modification_ts, r.id) > (cast($2 as timestamp), $3))" \ + " ORDER BY r.modification_ts, r.id, a.id" +#endif + +#ifndef PGSQL_GET_SERVERS_COMMON +#define PGSQL_GET_SERVERS_COMMON(table_prefix, ...) \ + "SELECT" \ + " s.id," \ + " s.tag," \ + " s.description," \ + " extract(epoch from s.modification_ts)::bigint as modification_ts " \ + "FROM " #table_prefix "_server AS s " \ + "WHERE s.id > 1 " \ + __VA_ARGS__ \ + "ORDER BY s.id" +#define PGSQL_GET_ALL_SERVERS(table_prefix) \ + PGSQL_GET_SERVERS_COMMON(table_prefix, "") +#define PGSQL_GET_SERVER(table_prefix) \ + PGSQL_GET_SERVERS_COMMON(table_prefix, "AND s.tag = $1") +#endif + +#ifndef PGSQL_GET_CLIENT_CLASS4_COMMON +#define PGSQL_GET_CLIENT_CLASS4_COMMON(server_join, ...) \ + "SELECT " \ + " c.id," \ + " c.name," \ + " c.test," \ + " c.next_server," \ + " c.server_hostname," \ + " c.boot_file_name," \ + " c.only_if_required," \ + " c.valid_lifetime," \ + " c.min_valid_lifetime," \ + " c.max_valid_lifetime," \ + " c.depend_on_known_directly," \ + " o.depend_on_known_indirectly, " \ + " extract(epoch from c.modification_ts)::bigint as modification_ts, " \ + " d.id," \ + " d.code," \ + " d.name," \ + " d.space," \ + " d.type," \ + " extract(epoch from d.modification_ts)::bigint as modification_ts, " \ + " d.is_array," \ + " d.encapsulate," \ + " d.record_types," \ + " d.user_context," \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp4_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " extract(epoch from x.modification_ts)::bigint as modification_ts, " \ + " s.tag " \ + "FROM dhcp4_client_class AS c " \ + "INNER JOIN dhcp4_client_class_order AS o " \ + " ON c.id = o.class_id " \ + server_join \ + "LEFT JOIN dhcp4_option_def AS d ON c.id = d.class_id " \ + "LEFT JOIN dhcp4_options AS x ON x.scope_id = 2 AND c.name = x.dhcp_client_class " \ + #__VA_ARGS__ \ + " ORDER BY o.order_index, d.id, x.option_id" + +#define PGSQL_GET_CLIENT_CLASS4_WITH_TAG(...) \ + PGSQL_GET_CLIENT_CLASS4_COMMON( \ + "INNER JOIN dhcp4_client_class_server AS a " \ + " ON c.id = a.class_id " \ + "INNER JOIN dhcp4_server AS s " \ + " ON a.server_id = s.id ", \ + __VA_ARGS__) + +#define PGSQL_GET_CLIENT_CLASS4_UNASSIGNED(...) \ + PGSQL_GET_CLIENT_CLASS4_COMMON( \ + "LEFT JOIN dhcp4_client_class_server AS a " \ + " ON c.id = a.class_id " \ + "LEFT JOIN dhcp4_server AS s " \ + " ON a.server_id = s.id ", \ + WHERE a.class_id IS NULL __VA_ARGS__) + +#endif + +#ifndef PGSQL_GET_CLIENT_CLASS6_COMMON +#define PGSQL_GET_CLIENT_CLASS6_COMMON(server_join, ...) \ + "SELECT " \ + " c.id," \ + " c.name," \ + " c.test," \ + " c.only_if_required," \ + " c.valid_lifetime," \ + " c.min_valid_lifetime," \ + " c.max_valid_lifetime," \ + " c.depend_on_known_directly," \ + " o.depend_on_known_indirectly, " \ + " extract(epoch from c.modification_ts)::bigint as modification_ts, " \ + " d.id," \ + " d.code," \ + " d.name," \ + " d.space," \ + " d.type," \ + " extract(epoch from d.modification_ts)::bigint as modification_ts, " \ + " d.is_array," \ + " d.encapsulate," \ + " d.record_types," \ + " d.user_context," \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp6_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " extract(epoch from x.modification_ts)::bigint as modification_ts, " \ + " s.tag, " \ + " c.preferred_lifetime," \ + " c.min_preferred_lifetime, " \ + " c.max_preferred_lifetime " \ + "FROM dhcp6_client_class AS c " \ + "INNER JOIN dhcp6_client_class_order AS o " \ + " ON c.id = o.class_id " \ + server_join \ + "LEFT JOIN dhcp6_option_def AS d ON c.id = d.class_id " \ + "LEFT JOIN dhcp6_options AS x ON x.scope_id = 2 AND c.name = x.dhcp_client_class " \ + #__VA_ARGS__ \ + " ORDER BY o.order_index, d.id, x.option_id" + +#define PGSQL_GET_CLIENT_CLASS6_WITH_TAG(...) \ + PGSQL_GET_CLIENT_CLASS6_COMMON( \ + "INNER JOIN dhcp6_client_class_server AS a " \ + " ON c.id = a.class_id " \ + "INNER JOIN dhcp6_server AS s " \ + " ON a.server_id = s.id ", \ + __VA_ARGS__) + +#define PGSQL_GET_CLIENT_CLASS6_UNASSIGNED(...) \ + PGSQL_GET_CLIENT_CLASS6_COMMON( \ + "LEFT JOIN dhcp6_client_class_server AS a " \ + " ON c.id = a.class_id " \ + "LEFT JOIN dhcp6_server AS s " \ + " ON a.server_id = s.id ", \ + WHERE a.class_id IS NULL __VA_ARGS__) + +#endif + +#ifndef PGSQL_INSERT_GLOBAL_PARAMETER +#define PGSQL_INSERT_GLOBAL_PARAMETER(table_prefix) \ + "INSERT INTO " #table_prefix "_global_parameter(" \ + " name," \ + " value," \ + " parameter_type," \ + " modification_ts" \ + ") VALUES ($1, $2, $3, $4)" +#endif + +#ifndef PGSQL_INSERT_GLOBAL_PARAMETER_SERVER +#define PGSQL_INSERT_GLOBAL_PARAMETER_SERVER(table_prefix) \ + "INSERT INTO " #table_prefix "_global_parameter_server(" \ + " parameter_id," \ + " modification_ts," \ + " server_id" \ + ") VALUES ($1, $2, (SELECT id FROM " #table_prefix "_server WHERE tag = $3))" +#endif + +#ifndef PGSQL_INSERT_SUBNET_SERVER +#define PGSQL_INSERT_SUBNET_SERVER(table_prefix) \ + "INSERT INTO " #table_prefix "_subnet_server(" \ + " subnet_id," \ + " modification_ts," \ + " server_id" \ + ") VALUES ($1, $2, (SELECT id FROM " #table_prefix "_server WHERE tag = $3))" +#endif + +#ifndef PGSQL_INSERT_POOL +#define PGSQL_INSERT_POOL(table_prefix) \ + "INSERT INTO " #table_prefix "_pool(" \ + " start_address," \ + " end_address," \ + " subnet_id," \ + " client_class," \ + " require_client_classes," \ + " user_context," \ + " modification_ts" \ + ") VALUES (cast($1 as inet), cast($2 as inet), $3, $4, $5, cast($6 as json), $7)" +#endif + +#ifndef PGSQL_INSERT_PD_POOL +#define PGSQL_INSERT_PD_POOL() \ + "INSERT INTO dhcp6_pd_pool(" \ + " prefix," \ + " prefix_length," \ + " delegated_prefix_length," \ + " subnet_id," \ + " excluded_prefix," \ + " excluded_prefix_length," \ + " client_class," \ + " require_client_classes," \ + " user_context," \ + " modification_ts" \ + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, cast($9 as json), $10)" +#endif + +#ifndef PGSQL_INSERT_SHARED_NETWORK_SERVER +#define PGSQL_INSERT_SHARED_NETWORK_SERVER(table_prefix) \ + "INSERT INTO " #table_prefix "_shared_network_server(" \ + " shared_network_id," \ + " modification_ts," \ + " server_id" \ + ") VALUES (" \ + " (SELECT id FROM " #table_prefix "_shared_network WHERE name = $1), $2," \ + " (SELECT id FROM " #table_prefix "_server WHERE tag = $3)" \ + ")" +#endif + +#ifndef PGSQL_INSERT_OPTION_DEF +#define PGSQL_INSERT_OPTION_DEF(table_prefix) \ + "INSERT INTO " #table_prefix "_option_def (" \ + " code," \ + " name," \ + " space," \ + " type," \ + " modification_ts," \ + " is_array," \ + " encapsulate," \ + " record_types," \ + " user_context," \ + " class_id" \ + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, cast($9 as json), $10)" +#endif + +#ifndef PGSQL_INSERT_OPTION_DEF_CLIENT_CLASS +#define PGSQL_INSERT_OPTION_DEF_CLIENT_CLASS(table_prefix) \ + "INSERT INTO " #table_prefix "_option_def (" \ + " code," \ + " name," \ + " space," \ + " type," \ + " modification_ts," \ + " is_array," \ + " encapsulate," \ + " record_types," \ + " user_context," \ + " class_id" \ + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, cast($9 as json), " \ + " (SELECT id FROM " #table_prefix "_client_class WHERE name = $10))" +#endif + +#ifndef PGSQL_INSERT_OPTION_DEF_SERVER +#define PGSQL_INSERT_OPTION_DEF_SERVER(table_prefix) \ + "INSERT INTO " #table_prefix "_option_def_server(" \ + " option_def_id," \ + " modification_ts," \ + " server_id" \ + ") VALUES ($1, $2, (SELECT id FROM " #table_prefix "_server WHERE tag = $3))" +#endif + +#ifndef PGSQL_INSERT_OPTION_COMMON +#define PGSQL_INSERT_OPTION_COMMON(table_prefix, pd_pool_id, last) \ + "INSERT INTO " #table_prefix "_options (" \ + " code," \ + " value," \ + " formatted_value," \ + " space," \ + " persistent," \ + " dhcp_client_class," \ + " " #table_prefix "_subnet_id," \ + " scope_id," \ + " user_context," \ + " shared_network_name," \ + " pool_id," \ + " modification_ts" \ + pd_pool_id \ + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, cast($9 as json), $10, $11, $12" last ")" + +#define PGSQL_INSERT_OPTION4() \ + PGSQL_INSERT_OPTION_COMMON(dhcp4, "", "") +#define PGSQL_INSERT_OPTION6() \ + PGSQL_INSERT_OPTION_COMMON(dhcp6, ", pd_pool_id ", ", $13") +#endif + +#ifndef PGSQL_INSERT_OPTION_SERVER +#define PGSQL_INSERT_OPTION_SERVER(table_prefix) \ + "INSERT INTO " #table_prefix "_options_server (" \ + " option_id," \ + " modification_ts," \ + " server_id" \ + ") VALUES ($1, $2, (SELECT id FROM " #table_prefix "_server WHERE tag = $3))" +#endif + +#ifndef PGSQL_INSERT_CLIENT_CLASS_SERVER +#define PGSQL_INSERT_CLIENT_CLASS_SERVER(table_prefix) \ + "INSERT INTO " #table_prefix "_client_class_server (" \ + " class_id," \ + " modification_ts," \ + " server_id" \ + ") VALUES ((SELECT id FROM " #table_prefix "_client_class WHERE name = $1), $2," \ + " (SELECT id FROM " #table_prefix "_server WHERE tag = $3))" +#endif + +#ifndef PGSQL_INSERT_CLIENT_CLASS_DEPENDENCY +#define PGSQL_INSERT_CLIENT_CLASS_DEPENDENCY(table_prefix) \ + "INSERT INTO " #table_prefix "_client_class_dependency (" \ + " class_id," \ + " dependency_id" \ + ") VALUES ((SELECT id FROM " #table_prefix "_client_class WHERE name = $1), " \ + " (SELECT id FROM " #table_prefix "_client_class WHERE name = $2))" +#endif + +#ifndef PGSQL_INSERT_SERVER +#define PGSQL_INSERT_SERVER(table_prefix) \ + "INSERT INTO " #table_prefix "_server (" \ + " tag," \ + " description," \ + " modification_ts" \ + ") VALUES ($1, $2, $3)" +#endif + +#ifndef PGSQL_UPDATE_GLOBAL_PARAMETER +#define PGSQL_UPDATE_GLOBAL_PARAMETER(table_prefix) \ + "UPDATE " #table_prefix "_global_parameter AS g " \ + "SET " \ + " name = $1, " \ + " value = $2, " \ + " parameter_type = $3, " \ + " modification_ts = $4 " \ + "FROM " #table_prefix "_global_parameter_server as a, " \ + " " #table_prefix "_server as s " \ + "WHERE g.id = a.parameter_id and " \ + " a.server_id = s.id and " \ + " s.tag = $5 AND g.name = $6" +#endif + +#ifndef PGSQL_UPDATE_OPTION_DEF +#define PGSQL_UPDATE_OPTION_DEF(table_prefix) \ + "UPDATE " #table_prefix "_option_def AS d " \ + "SET" \ + " code = $1," \ + " name = $2," \ + " space = $3," \ + " type = $4," \ + " modification_ts = $5," \ + " is_array = $6," \ + " encapsulate = $7," \ + " record_types = $8," \ + " user_context = cast($9 as json), " \ + " class_id = $10 " \ + "FROM " #table_prefix "_option_def_server as a, " \ + " " #table_prefix "_server as s " \ + "WHERE d.id = a.option_def_id AND" \ + " a.server_id = s.id AND " \ + " s.tag = $11 AND d.code = $12 AND d.space = $13 " +#endif + +#ifndef PGSQL_UPDATE_OPTION_DEF_CLIENT_CLASS +#define PGSQL_UPDATE_OPTION_DEF_CLIENT_CLASS(table_prefix) \ + "UPDATE " #table_prefix "_option_def AS d " \ + "SET" \ + " code = $1," \ + " name = $2," \ + " space = $3," \ + " type = $4," \ + " modification_ts = $5," \ + " is_array = $6," \ + " encapsulate = $7," \ + " record_types = $8," \ + " user_context = cast($9 as json)" \ + "FROM " #table_prefix "_option_def_server as a, " \ + " " #table_prefix "_server as s " \ + "WHERE d.id = a.option_def_id AND " \ + " a.server_id = s.id AND " \ + " d.class_id = (SELECT id FROM dhcp4_client_class WHERE name = $10)" +#endif + + +#ifndef PGSQL_UPDATE_OPTION_NO_TAG +#define PGSQL_UPDATE_OPTION_NO_TAG(table_prefix, pd_pool_id, ...) \ + "UPDATE " #table_prefix "_options AS o " \ + "SET" \ + " code = $1," \ + " value = $2," \ + " formatted_value = $3," \ + " space = $4," \ + " persistent = $5," \ + " dhcp_client_class = $6," \ + " " #table_prefix "_subnet_id = $7," \ + " scope_id = $8," \ + " user_context = cast($9 as json)," \ + " shared_network_name = $10," \ + " pool_id = $11," \ + " modification_ts = $12 " \ + pd_pool_id \ + "WHERE " #__VA_ARGS__ +#endif + +#define PGSQL_UPDATE_OPTION4_NO_TAG(...) \ + PGSQL_UPDATE_OPTION_NO_TAG(dhcp4, "", __VA_ARGS__) + +#define PGSQL_UPDATE_OPTION6_NO_TAG(...) \ + PGSQL_UPDATE_OPTION_NO_TAG(dhcp6, ", pd_pool_id = $13 ", __VA_ARGS__) + +#ifndef PGSQL_UPDATE_OPTION_WITH_TAG +#define PGSQL_UPDATE_OPTION_WITH_TAG(table_prefix, pd_pool_id, ...) \ + "UPDATE " #table_prefix "_options AS o " \ + "SET" \ + " code = $1," \ + " value = $2," \ + " formatted_value = $3," \ + " space = $4," \ + " persistent = $5," \ + " dhcp_client_class = $6," \ + " " #table_prefix "_subnet_id = $7," \ + " scope_id = $8," \ + " user_context = cast($9 as json)," \ + " shared_network_name = $10," \ + " pool_id = $11," \ + " modification_ts = $12 " \ + pd_pool_id \ + "FROM " #table_prefix "_options_server as a, " \ + " " #table_prefix "_server as s " \ + "WHERE o.option_id = a.option_id AND " \ + " a.server_id = s.id " \ + #__VA_ARGS__ +#endif + +#define PGSQL_UPDATE_OPTION4_WITH_TAG(...) \ + PGSQL_UPDATE_OPTION_WITH_TAG(dhcp4, "", AND s.tag = $13 __VA_ARGS__) + +#define PGSQL_UPDATE_OPTION6_WITH_TAG(...) \ + PGSQL_UPDATE_OPTION_WITH_TAG(dhcp4, \ + ", pd_pool_id = $13 ", AND s.tag = $14 __VA_ARGS__) + +#ifndef PGSQL_UPDATE_CLIENT_CLASS4 +#define PGSQL_UPDATE_CLIENT_CLASS4(follow_class_name_set) \ + "UPDATE dhcp4_client_class SET" \ + " name = $1," \ + " test = $2," \ + " next_server = cast($3 as inet)," \ + " server_hostname = $4," \ + " boot_file_name = $5," \ + " only_if_required = $6," \ + " valid_lifetime = $7," \ + " min_valid_lifetime = $8," \ + " max_valid_lifetime = $9," \ + " depend_on_known_directly = $10," \ + follow_class_name_set \ + " modification_ts = $11 " \ + "WHERE name = $12" +#endif + +#ifndef PGSQL_UPDATE_CLIENT_CLASS6 +#define PGSQL_UPDATE_CLIENT_CLASS6(follow_class_name_set) \ + "UPDATE dhcp6_client_class SET" \ + " name = $1," \ + " test = $2," \ + " only_if_required = $3," \ + " valid_lifetime = $4," \ + " min_valid_lifetime = $5," \ + " max_valid_lifetime = $6," \ + " depend_on_known_directly = $7," \ + follow_class_name_set \ + " modification_ts = $8, " \ + " preferred_lifetime = $9, " \ + " min_preferred_lifetime = $10, " \ + " max_preferred_lifetime = $11 " \ + "WHERE name = $12" +#endif + +#ifndef PGSQL_UPDATE_SERVER +#define PGSQL_UPDATE_SERVER(table_prefix) \ + "UPDATE " #table_prefix "_server " \ + "SET" \ + " tag = $1," \ + " description = $2," \ + " modification_ts = $3 " \ + "WHERE tag = $4" +#endif + +#ifndef PGSQL_DELETE_GLOBAL_PARAMETER +#define PGSQL_DELETE_GLOBAL_PARAMETER(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_global_parameter AS g " \ + "USING " \ + " " #table_prefix "_global_parameter_server AS a, " \ + " " #table_prefix "_server AS s " \ + "WHERE " \ + " g.id = a.parameter_id AND " \ + " a.server_id = s.id AND " \ + " s.tag = $1 " #__VA_ARGS__ +#endif + +#ifndef PGSQL_DELETE_GLOBAL_PARAMETER_UNASSIGNED +#define PGSQL_DELETE_GLOBAL_PARAMETER_UNASSIGNED(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_global_parameter AS g " \ + "WHERE g.id in ( " \ + " SELECT g.id FROM " #table_prefix "_global_parameter AS g " \ + " LEFT JOIN " #table_prefix "_global_parameter_server AS a ON g.id = a.parameter_id " \ + " WHERE a.parameter_id IS NULL) " #__VA_ARGS__ +#endif + +#ifndef PGSQL_DELETE_SUBNET_COMMON +#define PGSQL_DELETE_SUBNET_COMMON(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_subnet AS s " \ + "USING " \ + " " #table_prefix "_subnet_server AS a, " \ + " " #table_prefix "_server AS srv " \ + "WHERE " \ + " s.subnet_id = a.subnet_id AND " \ + " a.server_id = srv.id " \ + #__VA_ARGS__ +#endif + +#define PGSQL_DELETE_SUBNET_WITH_TAG(table_prefix, ...) \ + PGSQL_DELETE_SUBNET_COMMON(table_prefix, AND srv.tag = $1 __VA_ARGS__) + +#define PGSQL_DELETE_SUBNET_ANY(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_subnet AS s " \ + #__VA_ARGS__ + +#define PGSQL_DELETE_SUBNET_UNASSIGNED(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_subnet AS s " \ + "WHERE s.subnet_id in ( " \ + " SELECT s.subnet_id FROM " #table_prefix "_subnet AS s " \ + " LEFT JOIN " #table_prefix "_subnet_server AS a ON s.subnet_id = a.subnet_id " \ + " WHERE a.subnet_id IS NULL) " #__VA_ARGS__ + +#ifndef PGSQL_DELETE_SUBNET_SERVER +#define PGSQL_DELETE_SUBNET_SERVER(table_prefix) \ + "DELETE FROM " #table_prefix "_subnet_server " \ + "WHERE subnet_id = $1" +#endif + +#ifndef PGSQL_DELETE_POOLS +#define PGSQL_DELETE_POOLS(table_prefix) \ + "DELETE FROM " #table_prefix "_pool " \ + "WHERE subnet_id = $1 OR subnet_id = " \ + "(SELECT subnet_id FROM " #table_prefix "_subnet" \ + " WHERE subnet_prefix = $2)" +#endif + +#ifndef PGSQL_DELETE_PD_POOLS +#define PGSQL_DELETE_PD_POOLS() \ + "DELETE FROM dhcp6_pd_pool " \ + "WHERE subnet_id = $1 OR subnet_id = " \ + "(SELECT subnet_id FROM dhcp6_subnet" \ + " WHERE subnet_prefix = $2)" +#endif + +#ifndef PGSQL_DELETE_SHARED_NETWORK_COMMON +#define PGSQL_DELETE_SHARED_NETWORK_COMMON(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_shared_network AS n " \ + "USING " \ + " " #table_prefix "_shared_network_server AS a, " \ + " " #table_prefix "_server AS s " \ + "WHERE " \ + " n.id = a.shared_network_id AND " \ + " a.server_id = s.id " \ + #__VA_ARGS__ +#endif + +#define PGSQL_DELETE_SHARED_NETWORK_WITH_TAG(table_prefix, ...) \ + PGSQL_DELETE_SHARED_NETWORK_COMMON(table_prefix, AND s.tag = $1 __VA_ARGS__) + +#define PGSQL_DELETE_SHARED_NETWORK_ANY(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_shared_network AS n " \ + #__VA_ARGS__ + +#define PGSQL_DELETE_SHARED_NETWORK_UNASSIGNED(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_shared_network AS n " \ + "WHERE n.id in ( " \ + " SELECT n.id FROM " #table_prefix "_shared_network AS n " \ + " LEFT JOIN " #table_prefix "_shared_network_server AS a ON n.id = a.shared_network_id " \ + " WHERE a.shared_network_id IS NULL) " \ + #__VA_ARGS__ + +#ifndef PGSQL_DELETE_SHARED_NETWORK_SERVER +#define PGSQL_DELETE_SHARED_NETWORK_SERVER(table_prefix) \ + "DELETE FROM " #table_prefix "_shared_network_server " \ + "WHERE shared_network_id = " \ + "(SELECT id FROM " #table_prefix "_shared_network WHERE name = $1)" +#endif + +#ifndef PGSQL_DELETE_OPTION_DEF +#define PGSQL_DELETE_OPTION_DEF(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_option_def AS d " \ + "USING " \ + " " #table_prefix "_option_def_server AS a, " \ + " " #table_prefix "_server AS s " \ + "WHERE " \ + " d.id = a.option_def_id AND " \ + " a.server_id = s.id AND " \ + " s.tag = $1 " #__VA_ARGS__ +#endif + +#ifndef PGSQL_DELETE_OPTION_DEF_UNASSIGNED +#define PGSQL_DELETE_OPTION_DEF_UNASSIGNED(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_option_def AS d " \ + "WHERE d.id in ( " \ + " SELECT d.id FROM " #table_prefix "_option_def AS d " \ + " LEFT JOIN " #table_prefix "_option_def_server AS a ON d.id = a.option_def_id " \ + " WHERE a.option_def_id IS NULL) " #__VA_ARGS__ +#endif + +#ifndef PGSQL_DELETE_OPTION_DEFS_CLIENT_CLASS +#define PGSQL_DELETE_OPTION_DEFS_CLIENT_CLASS(table_prefix) \ + "DELETE FROM " #table_prefix "_option_def " \ + "WHERE class_id = (SELECT id FROM " #table_prefix "_client_class WHERE name = $1)" +#endif + +#ifndef PGSQL_DELETE_OPTION_WITH_TAG +#define PGSQL_DELETE_OPTION_WITH_TAG(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_options AS o " \ + "USING " \ + " " #table_prefix "_options_server AS a, " \ + " " #table_prefix "_server AS s " \ + "WHERE " \ + " o.option_id = a.option_id AND " \ + " a.server_id = s.id AND " \ + " s.tag = $1 " #__VA_ARGS__ +#endif + +#ifndef PGSQL_DELETE_OPTION_NO_TAG +#define PGSQL_DELETE_OPTION_NO_TAG(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_options AS o " \ + #__VA_ARGS__ +#endif + +#ifndef PGSQL_DELETE_OPTION_SUBNET_ID_PREFIX +#define PGSQL_DELETE_OPTION_SUBNET_ID_PREFIX(table_prefix) \ + "DELETE FROM " #table_prefix "_options AS o " \ + "USING " \ + " " #table_prefix "_subnet AS s " \ + "WHERE " \ + " s.subnet_id = o." #table_prefix "_subnet_id AND " \ + " o.scope_id = 1 AND (s.subnet_id = $1 OR s.subnet_prefix = $2)" +#endif + +#ifndef PGSQL_DELETE_OPTION_UNASSIGNED +#define PGSQL_DELETE_OPTION_UNASSIGNED(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_options AS o " \ + "WHERE o.option_id in ( " \ + " SELECT o.option_id FROM " #table_prefix "_options AS o " \ + " LEFT JOIN " #table_prefix "_options_server AS a ON o.option_id = a.option_id " \ + " WHERE a.option_id IS NULL) " #__VA_ARGS__ +#endif + +#ifndef PGSQL_DELETE_OPTION_POOL_RANGE +#define PGSQL_DELETE_OPTION_POOL_RANGE(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_options AS o " \ + "WHERE " #__VA_ARGS__ \ + " AND o.pool_id = " \ + " (SELECT id FROM " #table_prefix "_pool" \ + " WHERE start_address = cast($1 as inet) AND end_address = cast($2 as inet))" +#endif + +#ifndef PGSQL_DELETE_OPTION_PD_POOL +#define PGSQL_DELETE_OPTION_PD_POOL(...) \ + "DELETE o FROM dhcp6_options AS o " \ + "WHERE " #__VA_ARGS__ \ + " AND o.pd_pool_id = " \ + " (SELECT id FROM dhcp6_pd_pool" \ + " WHERE prefix = $1 AND prefix_length = $2)" +#endif + +#ifndef PGSQL_DELETE_CLIENT_CLASS_DEPENDENCY +#define PGSQL_DELETE_CLIENT_CLASS_DEPENDENCY(table_prefix) \ + "DELETE FROM " #table_prefix "_client_class_dependency " \ + "WHERE class_id = (SELECT id FROM " #table_prefix "_client_class WHERE name = $1)" +#endif + +#ifndef PGSQL_DELETE_CLIENT_CLASS_SERVER +#define PGSQL_DELETE_CLIENT_CLASS_SERVER(table_prefix) \ + "DELETE FROM " #table_prefix "_client_class_server " \ + "WHERE class_id = " \ + "(SELECT id FROM " #table_prefix "_client_class WHERE name = $1)" +#endif + +#ifndef PGSQL_DELETE_CLIENT_CLASS +#define PGSQL_DELETE_CLIENT_CLASS_COMMON(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_client_class AS c " \ + "USING " \ + " " #table_prefix "_client_class_server AS a, " \ + " " #table_prefix "_server AS s " \ + "WHERE " \ + " c.id = a.class_id AND " \ + " a.server_id = s.id " \ + #__VA_ARGS__ + +#define PGSQL_DELETE_CLIENT_CLASS_WITH_TAG(table_prefix, ...) \ + PGSQL_DELETE_CLIENT_CLASS_COMMON(table_prefix, AND s.tag = $1 __VA_ARGS__) + +#define PGSQL_DELETE_CLIENT_CLASS_ANY(table_prefix, ...) \ + PGSQL_DELETE_CLIENT_CLASS_COMMON(table_prefix, __VA_ARGS__) + +#define PGSQL_DELETE_CLIENT_CLASS_UNASSIGNED(table_prefix, ...) \ + "DELETE FROM " #table_prefix "_client_class AS c " \ + "WHERE c.id in (" \ + " SELECT c.id FROM " #table_prefix "_client_class AS c " \ + " LEFT JOIN " #table_prefix "_client_class_server AS a ON c.id = a.class_id " \ + " WHERE a.class_id IS NULL) " #__VA_ARGS__ + +#endif // PGSQL_DELETE_CLIENT_CLASS + +#ifndef PGSQL_DELETE_SERVER +#define PGSQL_DELETE_SERVER(table_prefix) \ + "DELETE FROM " #table_prefix "_server " \ + "WHERE tag = $1" +#endif + +#ifndef PGSQL_DELETE_ALL_SERVERS +#define PGSQL_DELETE_ALL_SERVERS(table_prefix) \ + "DELETE FROM " #table_prefix "_server " \ + "WHERE id > 1" +#endif + +} // end of anonymous namespace + +} // end of namespace isc::dhcp +} // end of namespace isc + +#endif diff --git a/src/hooks/dhcp/pgsql_cb/tests/Makefile.am b/src/hooks/dhcp/pgsql_cb/tests/Makefile.am index b014bca13d..843416f3e6 100644 --- a/src/hooks/dhcp/pgsql_cb/tests/Makefile.am +++ b/src/hooks/dhcp/pgsql_cb/tests/Makefile.am @@ -24,9 +24,9 @@ if HAVE_GTEST TESTS += pgsql_cb_unittests pgsql_cb_unittests_SOURCES = pgsql_cb_impl_unittest.cc +pgsql_cb_unittests_SOURCES += pgsql_cb_dhcp4_unittest.cc # disabled for now, to be added in #95 -#pgsql_cb_unittests_SOURCES = pgsql_cb_dhcp4_unittest.cc #pgsql_cb_unittests_SOURCES += pgsql_cb_dhcp4_mgr_unittest.cc # disabled for now, to be added in #96 diff --git a/src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_dhcp4_unittest.cc b/src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_dhcp4_unittest.cc new file mode 100644 index 0000000000..836306f8ba --- /dev/null +++ b/src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_dhcp4_unittest.cc @@ -0,0 +1,1261 @@ +// 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/. + +#include <config.h> +#include <pgsql_cb_dhcp4.h> +#include <pgsql_cb_impl.h> +#include <database/database_connection.h> +#include <database/db_exceptions.h> +#include <database/server.h> +#include <database/testutils/schema.h> +#include <dhcp/dhcp6.h> +#include <dhcp/libdhcp++.h> +#include <dhcp/option4_addrlst.h> +#include <dhcp/option_int.h> +#include <dhcp/option_space.h> +#include <dhcp/option_string.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/client_class_def.h> +#include <dhcpsrv/config_backend_dhcp4_mgr.h> +#include <dhcpsrv/pool.h> +#include <dhcpsrv/subnet.h> +#include <dhcpsrv/testutils/pgsql_generic_backend_unittest.h> +#include <dhcpsrv/testutils/test_utils.h> +#include <pgsql/testutils/pgsql_schema.h> +#include <testutils/multi_threading_utils.h> +#include <testutils/gtest_utils.h> + +#include <boost/make_shared.hpp> +#include <boost/shared_ptr.hpp> +#include <gtest/gtest.h> +#include <map> +#include <sstream> + +using namespace isc; +using namespace isc::util; +using namespace isc::asiolink; +using namespace isc::db; +using namespace isc::db::test; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace isc::process; +using namespace isc::test; +namespace ph = std::placeholders; + +namespace { + +/// @brief Test implementation of the PgSQL configuration backend. +/// +/// It exposes protected members of the @c PgSqlConfigBackendDHCPv4. +class TestPgSqlConfigBackendDHCPv4 : public PgSqlConfigBackendDHCPv4 { +public: + + /// @brief Constructor. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + explicit TestPgSqlConfigBackendDHCPv4(const DatabaseConnection::ParameterMap& parameters) + : PgSqlConfigBackendDHCPv4(parameters) { + } + + using PgSqlConfigBackendDHCPv4::base_impl_; + +}; + +/// @brief Test fixture class for @c PgSqlConfigBackendDHCPv4. +/// +/// @todo The tests we're providing here only test cases when the +/// server selector is set to 'ALL' (configuration elements belong to +/// all servers). Currently we have no API to insert servers into +/// the database, and therefore we can't test the case when +/// configuration elements are assigned to particular servers by +/// server tags. We will have to expand existing tests when +/// the API is extended allowing for inserting servers to the +/// database. +class PgSqlConfigBackendDHCPv4Test : public PgSqlGenericBackendTest { +public: + + /// @brief Constructor. + PgSqlConfigBackendDHCPv4Test() + : test_subnets_(), test_networks_(), test_option_defs_(), + test_options_(), test_client_classes_(), test_servers_(), timestamps_(), + cbptr_(), audit_entries_() { + // Ensure we have the proper schema with no transient data. + createPgSQLSchema(); + + try { + // Create PgSQL connection and use it to start the backend. + DatabaseConnection::ParameterMap params = + DatabaseConnection::parse(validPgSQLConnectionString()); + cbptr_.reset(new TestPgSqlConfigBackendDHCPv4(params)); + + } catch (...) { + std::cerr << "*** ERROR: unable to open database. The test\n" + "*** environment is broken and must be fixed before\n" + "*** the PgSQL tests will run correctly.\n" + "*** The reason for the problem is described in the\n" + "*** accompanying exception output.\n"; + throw; + } + + // Create test data. + initTestServers(); + initTestOptions(); + initTestSubnets(); + initTestSharedNetworks(); + initTestOptionDefs(); + initTestClientClasses(); + initTimestamps(); + } + + /// @brief Destructor. + virtual ~PgSqlConfigBackendDHCPv4Test() { + cbptr_.reset(); + // If data wipe enabled, delete transient data otherwise destroy the schema. + destroyPgSQLSchema(); + } + + /// @brief Counts rows in a selected table in PgSQL database. + /// + /// This method can be used to verify that some configuration elements were + /// deleted from a selected table as a result of cascade delete or a trigger. + /// For example, deleting a subnet should trigger deletion of its address + /// pools and options. By counting the rows on each table we can determine + /// whether the deletion took place on all tables for which it was expected. + /// + /// @param table Table name. + /// @return Number of rows in the specified table. + size_t countRows(const std::string& table) const { + auto p = boost::dynamic_pointer_cast<TestPgSqlConfigBackendDHCPv4>(cbptr_); + if (!p) { + ADD_FAILURE() << "cbptr_ does not cast to TestPgSqlConfigBackendDHCPv4"; + return (0); + } + + // Reuse the existing connection of the backend. + auto impl = boost::dynamic_pointer_cast<PgSqlConfigBackendImpl>(p->base_impl_); + auto& conn = impl->conn_; + + return (PgSqlGenericBackendTest::countRows(conn, table)); + } + + /// @brief Creates several servers used in tests. + void initTestServers() { + test_servers_.push_back(Server::create(ServerTag("server1"), "this is server 1")); + test_servers_.push_back(Server::create(ServerTag("server1"), "this is server 1 bis")); + test_servers_.push_back(Server::create(ServerTag("server2"), "this is server 2")); + test_servers_.push_back(Server::create(ServerTag("server3"), "this is server 3")); + } + + /// @brief Creates several subnets used in tests. + void initTestSubnets() { + // First subnet includes all parameters. + std::string interface_id_text = "whale"; + OptionBuffer interface_id(interface_id_text.begin(), interface_id_text.end()); + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 30, 40, 60, 1024)); + subnet->get4o6().setIface4o6("eth0"); + subnet->get4o6().setInterfaceId(OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, + interface_id))); + subnet->get4o6().setSubnet4o6(IOAddress("2001:db8:1::"), 64); + subnet->setFilename("/tmp/filename"); + subnet->allowClientClass("home"); + subnet->setIface("eth1"); + subnet->setMatchClientId(false); + subnet->setSiaddr(IOAddress("10.1.2.3")); + subnet->setT2(323212); + subnet->addRelayAddress(IOAddress("10.2.3.4")); + subnet->addRelayAddress(IOAddress("10.5.6.7")); + subnet->setT1(1234); + subnet->requireClientClass("required-class1"); + subnet->requireClientClass("required-class2"); + subnet->setReservationsGlobal(false); + subnet->setReservationsInSubnet(false); + subnet->setSname("server-hostname"); + subnet->setContext(user_context); + subnet->setValid(555555); + subnet->setAuthoritative(true); + subnet->setCalculateTeeTimes(true); + subnet->setT1Percent(0.345); + subnet->setT2Percent(0.444); + subnet->setDdnsSendUpdates(false); + + Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.10"), IOAddress("192.0.2.20"))); + subnet->addPool(pool1); + + Pool4Ptr pool2(new Pool4(IOAddress("192.0.2.50"), IOAddress("192.0.2.60"))); + subnet->addPool(pool2); + + // Add several options to the subnet. + subnet->getCfgOption()->add(test_options_[0]->option_, + test_options_[0]->persistent_, + test_options_[0]->space_name_); + + subnet->getCfgOption()->add(test_options_[1]->option_, + test_options_[1]->persistent_, + test_options_[1]->space_name_); + + subnet->getCfgOption()->add(test_options_[2]->option_, + test_options_[2]->persistent_, + test_options_[2]->space_name_); + + test_subnets_.push_back(subnet); + + // Adding another subnet with the same subnet id to test + // cases that this second instance can override existing + // subnet instance. + subnet.reset(new Subnet4(IOAddress("10.0.0.0"), 8, 20, 30, 40, 1024)); + + pool1.reset(new Pool4(IOAddress("10.0.0.10"), IOAddress("10.0.0.20"))); + subnet->addPool(pool1); + + pool1->getCfgOption()->add(test_options_[3]->option_, + test_options_[3]->persistent_, + test_options_[3]->space_name_); + + pool1->getCfgOption()->add(test_options_[4]->option_, + test_options_[4]->persistent_, + test_options_[4]->space_name_); + + pool2.reset(new Pool4(IOAddress("10.0.0.50"), IOAddress("10.0.0.60"))); + + pool2->allowClientClass("work"); + pool2->requireClientClass("required-class3"); + pool2->requireClientClass("required-class4"); + user_context = Element::createMap(); + user_context->set("bar", Element::create("foo")); + pool2->setContext(user_context); + + subnet->addPool(pool2); + + test_subnets_.push_back(subnet); + + subnet.reset(new Subnet4(IOAddress("192.0.3.0"), 24, 20, 30, 40, 2048)); + Triplet<uint32_t> null_timer; + subnet->setT1(null_timer); + subnet->setT2(null_timer); + subnet->setValid(null_timer); + subnet->setDdnsSendUpdates(true); + subnet->setDdnsOverrideNoUpdate(true); + subnet->setDdnsOverrideClientUpdate(false); + subnet->setDdnsReplaceClientNameMode(D2ClientConfig::ReplaceClientNameMode::RCM_WHEN_PRESENT); + subnet->setDdnsGeneratedPrefix("myhost"); + subnet->setDdnsQualifyingSuffix("example.org"); + + subnet->getCfgOption()->add(test_options_[0]->option_, + test_options_[0]->persistent_, + test_options_[0]->space_name_); + + test_subnets_.push_back(subnet); + + // Add a subnet with all defaults. + subnet.reset(new Subnet4(IOAddress("192.0.4.0"), 24, Triplet<uint32_t>(), + Triplet<uint32_t>(), Triplet<uint32_t>(), 4096)); + test_subnets_.push_back(subnet); + } + + /// @brief Creates several subnets used in tests. + void initTestSharedNetworks() { + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + SharedNetwork4Ptr shared_network(new SharedNetwork4("level1")); + shared_network->allowClientClass("foo"); + shared_network->setIface("eth1"); + shared_network->setMatchClientId(false); + shared_network->setT2(323212); + shared_network->addRelayAddress(IOAddress("10.2.3.4")); + shared_network->addRelayAddress(IOAddress("10.5.6.7")); + shared_network->setT1(1234); + shared_network->requireClientClass("required-class1"); + shared_network->requireClientClass("required-class2"); + shared_network->setReservationsGlobal(false); + shared_network->setReservationsInSubnet(false); + shared_network->setContext(user_context); + shared_network->setValid(5555); + shared_network->setCalculateTeeTimes(true); + shared_network->setT1Percent(0.345); + shared_network->setT2Percent(0.444); + shared_network->setSiaddr(IOAddress("192.0.1.2")); + shared_network->setSname("frog"); + shared_network->setFilename("/dev/null"); + shared_network->setAuthoritative(true); + shared_network->setDdnsSendUpdates(false); + + // Add several options to the shared network. + shared_network->getCfgOption()->add(test_options_[2]->option_, + test_options_[2]->persistent_, + test_options_[2]->space_name_); + + shared_network->getCfgOption()->add(test_options_[3]->option_, + test_options_[3]->persistent_, + test_options_[3]->space_name_); + + shared_network->getCfgOption()->add(test_options_[4]->option_, + test_options_[4]->persistent_, + test_options_[4]->space_name_); + + test_networks_.push_back(shared_network); + + // Adding another shared network called "level1" to test + // cases that this second instance can override existing + // "level1" instance. + shared_network.reset(new SharedNetwork4("level1")); + test_networks_.push_back(shared_network); + + // Add more shared networks. + shared_network.reset(new SharedNetwork4("level2")); + Triplet<uint32_t> null_timer; + shared_network->setT1(null_timer); + shared_network->setT2(null_timer); + shared_network->setValid(null_timer); + shared_network->setDdnsSendUpdates(true); + shared_network->setDdnsOverrideNoUpdate(true); + shared_network->setDdnsOverrideClientUpdate(false); + shared_network->setDdnsReplaceClientNameMode(D2ClientConfig::ReplaceClientNameMode::RCM_WHEN_PRESENT); + shared_network->setDdnsGeneratedPrefix("myhost"); + shared_network->setDdnsQualifyingSuffix("example.org"); + + shared_network->getCfgOption()->add(test_options_[0]->option_, + test_options_[0]->persistent_, + test_options_[0]->space_name_); + test_networks_.push_back(shared_network); + + shared_network.reset(new SharedNetwork4("level3")); + test_networks_.push_back(shared_network); + } + + /// @brief Creates several option definitions used in tests. + void initTestOptionDefs() { + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + OptionDefinitionPtr option_def(new OptionDefinition("foo", 234, + DHCP4_OPTION_SPACE, + "string", + "espace")); + test_option_defs_.push_back(option_def); + + option_def.reset(new OptionDefinition("bar", 234, DHCP4_OPTION_SPACE, + "uint32", true)); + test_option_defs_.push_back(option_def); + + option_def.reset(new OptionDefinition("fish", 235, DHCP4_OPTION_SPACE, + "record", true)); + option_def->addRecordField("uint32"); + option_def->addRecordField("string"); + test_option_defs_.push_back(option_def); + + option_def.reset(new OptionDefinition("whale", 236, "xyz", "string")); + test_option_defs_.push_back(option_def); + + option_def.reset(new OptionDefinition("foobar", 234, DHCP4_OPTION_SPACE, + "uint64", true)); + test_option_defs_.push_back(option_def); + } + + /// @brief Creates several DHCP options used in tests. + void initTestOptions() { + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + OptionDefSpaceContainer defs; + + OptionDescriptor desc = + createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME, + true, false, "my-boot-file"); + desc.space_name_ = DHCP4_OPTION_SPACE; + desc.setContext(user_context); + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createOption<OptionUint8>(Option::V4, DHO_DEFAULT_IP_TTL, + false, true, 64); + desc.space_name_ = DHCP4_OPTION_SPACE; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createOption<OptionUint32>(Option::V4, 1, false, false, 312131), + desc.space_name_ = "vendor-encapsulated-options"; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createAddressOption<Option4AddrLst>(254, true, true, + "192.0.2.3"); + desc.space_name_ = DHCP4_OPTION_SPACE; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createEmptyOption(Option::V4, 1, true); + desc.space_name_ = "isc"; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createAddressOption<Option4AddrLst>(2, false, true, "10.0.0.5", + "10.0.0.3", "10.0.3.4"); + desc.space_name_ = "isc"; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME, + true, false, "my-boot-file-2"); + desc.space_name_ = DHCP4_OPTION_SPACE; + desc.setContext(user_context); + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME, + true, false, "my-boot-file-3"); + desc.space_name_ = DHCP4_OPTION_SPACE; + desc.setContext(user_context); + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + // Add definitions for DHCPv4 non-standard options in case we need to + // compare subnets, networks and pools in JSON format. In that case, + // the @c toElement functions require option definitions to generate the + // proper output. + defs.addItem(OptionDefinitionPtr(new OptionDefinition( + "vendor-encapsulated-1", 1, + "vendor-encapsulated-options", "uint32"))); + defs.addItem(OptionDefinitionPtr(new OptionDefinition( + "option-254", 254, DHCP4_OPTION_SPACE, + "ipv4-address", true))); + defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-1", 1, "isc", "empty"))); + defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-2", 2, "isc", "ipv4-address", true))); + + // Register option definitions. + LibDHCP::setRuntimeOptionDefs(defs); + } + + /// @brief Creates several client classes used in tests. + void initTestClientClasses() { + ExpressionPtr match_expr = boost::make_shared<Expression>(); + CfgOptionPtr cfg_option = boost::make_shared<CfgOption>(); + auto class1 = boost::make_shared<ClientClassDef>("foo", match_expr, cfg_option); + class1->setRequired(true); + class1->setNextServer(IOAddress("1.2.3.4")); + class1->setSname("cool"); + class1->setFilename("epc.cfg"); + class1->setValid(Triplet<uint32_t>(30, 60, 90)); + test_client_classes_.push_back(class1); + + auto class2 = boost::make_shared<ClientClassDef>("bar", match_expr, cfg_option); + class2->setTest("member('foo')"); + test_client_classes_.push_back(class2); + + auto class3 = boost::make_shared<ClientClassDef>("foobar", match_expr, cfg_option); + class3->setTest("member('foo') and member('bar')"); + test_client_classes_.push_back(class3); + } + + /// @brief Initialize posix time values used in tests. + void initTimestamps() { + // Current time minus 1 hour to make sure it is in the past. + timestamps_["today"] = boost::posix_time::second_clock::local_time() + - boost::posix_time::hours(1); + // One second after today. + timestamps_["after today"] = timestamps_["today"] + boost::posix_time::seconds(1); + // Yesterday. + timestamps_["yesterday"] = timestamps_["today"] - boost::posix_time::hours(24); + // One second after yesterday. + timestamps_["after yesterday"] = timestamps_["yesterday"] + boost::posix_time::seconds(1); + // Two days ago. + timestamps_["two days ago"] = timestamps_["today"] - boost::posix_time::hours(48); + // Tomorrow. + timestamps_["tomorrow"] = timestamps_["today"] + boost::posix_time::hours(24); + // One second after tomorrow. + timestamps_["after tomorrow"] = timestamps_["tomorrow"] + boost::posix_time::seconds(1); + } + + /// @brief Logs audit entries in the @c audit_entries_ member. + /// + /// This function is called in case of an error. + /// + /// @param server_tag Server tag for which the audit entries should be logged. + std::string logExistingAuditEntries(const std::string& server_tag) { + std::ostringstream s; + + auto& mod_time_idx = audit_entries_[server_tag].get<AuditEntryModificationTimeIdTag>(); + + for (auto audit_entry_it = mod_time_idx.begin(); + audit_entry_it != mod_time_idx.end(); + ++audit_entry_it) { + auto audit_entry = *audit_entry_it; + s << audit_entry->getObjectType() << ", " + << audit_entry->getObjectId() << ", " + << static_cast<int>(audit_entry->getModificationType()) << ", " + << audit_entry->getModificationTime() << ", " + << audit_entry->getRevisionId() << ", " + << audit_entry->getLogMessage() + << std::endl; + } + + return (s.str()); + } + + /// @brief Tests that the new audit entry is added. + /// + /// This method retrieves a collection of the existing audit entries and + /// checks that the new one has been added at the end of this collection. + /// It then verifies the values of the audit entry against the values + /// specified by the caller. + /// + /// @param exp_object_type Expected object type. + /// @param exp_modification_type Expected modification type. + /// @param exp_log_message Expected log message. + /// @param server_selector Server selector to be used for next query. + /// @param new_entries_num Number of the new entries expected to be inserted. + /// @param max_tested_entries Maximum number of entries tested. + void testNewAuditEntry(const std::string& exp_object_type, + const AuditEntry::ModificationType& exp_modification_type, + const std::string& exp_log_message, + const ServerSelector& server_selector = ServerSelector::ALL(), + const size_t new_entries_num = 1, + const size_t max_tested_entries = 65535) { + + // Get the server tag for which the entries are fetched. + std::string tag; + if (server_selector.getType() == ServerSelector::Type::ALL) { + // Server tag is 'all'. + tag = "all"; + + } else { + auto tags = server_selector.getTags(); + // This test is not meant to handle multiple server tags all at once. + if (tags.size() > 1) { + ADD_FAILURE() << "Test error: do not use multiple server tags"; + + } else if (tags.size() == 1) { + // Get the server tag for which we run the current test. + tag = tags.begin()->get(); + } + } + + auto audit_entries_size_save = audit_entries_[tag].size(); + + // Audit entries for different server tags are stored in separate + // containers. + ASSERT_NO_THROW_LOG(audit_entries_[tag] = cbptr_->getRecentAuditEntries(server_selector, + timestamps_["two days ago"], 0)); + ASSERT_EQ(audit_entries_size_save + new_entries_num, audit_entries_[tag].size()) + << logExistingAuditEntries(tag); + + auto& mod_time_idx = audit_entries_[tag].get<AuditEntryModificationTimeIdTag>(); + + // Iterate over specified number of entries starting from the most recent + // one and check they have correct values. + for (auto audit_entry_it = mod_time_idx.rbegin(); + ((std::distance(mod_time_idx.rbegin(), audit_entry_it) < new_entries_num) && + (std::distance(mod_time_idx.rbegin(), audit_entry_it) < max_tested_entries)); + ++audit_entry_it) { + auto audit_entry = *audit_entry_it; + EXPECT_EQ(exp_object_type, audit_entry->getObjectType()) + << logExistingAuditEntries(tag); + EXPECT_EQ(exp_modification_type, audit_entry->getModificationType()) + << logExistingAuditEntries(tag); + EXPECT_EQ(exp_log_message, audit_entry->getLogMessage()) + << logExistingAuditEntries(tag); + } + } + + /// @brief Holds pointers to subnets used in tests. + std::vector<Subnet4Ptr> test_subnets_; + + /// @brief Holds pointers to shared networks used in tests. + std::vector<SharedNetwork4Ptr> test_networks_; + + /// @brief Holds pointers to option definitions used in tests. + std::vector<OptionDefinitionPtr> test_option_defs_; + + /// @brief Holds pointers to options used in tests. + std::vector<OptionDescriptorPtr> test_options_; + + /// @brief Holds pointers to classes used in tests. + std::vector<ClientClassDefPtr> test_client_classes_; + + /// @brief Holds pointers to the servers used in tests. + std::vector<ServerPtr> test_servers_; + + /// @brief Holds timestamp values used in tests. + std::map<std::string, boost::posix_time::ptime> timestamps_; + + /// @brief Holds pointer to the backend. + boost::shared_ptr<ConfigBackendDHCPv4> cbptr_; + + /// @brief Holds the most recent audit entries. + std::map<std::string, AuditEntryCollection> audit_entries_; +}; + +// This test verifies that the expected backend type is returned. +TEST_F(PgSqlConfigBackendDHCPv4Test, getType) { + DatabaseConnection::ParameterMap params; + params["name"] = "keatest"; + params["password"] = "keatest"; + params["user"] = "keatest"; + ASSERT_NO_THROW(cbptr_.reset(new PgSqlConfigBackendDHCPv4(params))); + ASSERT_NE(cbptr_->getParameters(), DatabaseConnection::ParameterMap()); + EXPECT_EQ("postgresql", cbptr_->getType()); +} + +// This test verifies that by default localhost is returned as PgSQL connection +// host. +TEST_F(PgSqlConfigBackendDHCPv4Test, getHost) { + DatabaseConnection::ParameterMap params; + params["name"] = "keatest"; + params["password"] = "keatest"; + params["user"] = "keatest"; + ASSERT_NO_THROW(cbptr_.reset(new PgSqlConfigBackendDHCPv4(params))); + ASSERT_NE(cbptr_->getParameters(), DatabaseConnection::ParameterMap()); + EXPECT_EQ("localhost", cbptr_->getHost()); +} + +// This test verifies that by default port of 0 is returned as PgSQL connection +// port. +TEST_F(PgSqlConfigBackendDHCPv4Test, getPort) { + DatabaseConnection::ParameterMap params; + params["name"] = "keatest"; + params["password"] = "keatest"; + params["user"] = "keatest"; + ASSERT_NE(cbptr_->getParameters(), DatabaseConnection::ParameterMap()); + ASSERT_NO_THROW(cbptr_.reset(new PgSqlConfigBackendDHCPv4(params))); + EXPECT_EQ(0, cbptr_->getPort()); +} + +// This test verifies that the server can be added, updated and deleted. +TEST_F(PgSqlConfigBackendDHCPv4Test, createUpdateDeleteServer) { + // Explicitly set modification time to make sure that the time + // returned from the database is correct. + test_servers_[0]->setModificationTime(timestamps_["yesterday"]); + test_servers_[1]->setModificationTime(timestamps_["today"]); + + // Insert the server1 into the database. + ASSERT_NO_THROW_LOG(cbptr_->createUpdateServer4(test_servers_[0])); + + { + SCOPED_TRACE("CREATE audit entry for server"); + testNewAuditEntry("dhcp4_server", + AuditEntry::ModificationType::CREATE, + "server set"); + } + + // It should not be possible to create a duplicate of the logical + // server 'all'. + auto all_server = Server::create(ServerTag("all"), "this is logical server all"); + EXPECT_THROW(cbptr_->createUpdateServer4(all_server), isc::InvalidOperation); + + ServerPtr returned_server; + + // An attempt to fetch the server that hasn't been inserted should return + // a null pointer. + ASSERT_NO_THROW_LOG(returned_server = cbptr_->getServer4(ServerTag("server2"))); + EXPECT_FALSE(returned_server); + + // Try to fetch the server which we expect to exist. + ASSERT_NO_THROW_LOG(returned_server = cbptr_->getServer4(ServerTag("server1"))); + ASSERT_TRUE(returned_server); + EXPECT_EQ("server1", returned_server->getServerTag().get()); + EXPECT_EQ("this is server 1", returned_server->getDescription()); + EXPECT_EQ(timestamps_["yesterday"], returned_server->getModificationTime()); + + // This call is expected to update the existing server. + ASSERT_NO_THROW_LOG(cbptr_->createUpdateServer4(test_servers_[1])); + + { + SCOPED_TRACE("UPDATE audit entry for server"); + testNewAuditEntry("dhcp4_server", + AuditEntry::ModificationType::UPDATE, + "server set"); + } + + // Verify that the server has been updated. + ASSERT_NO_THROW_LOG(returned_server = cbptr_->getServer4(ServerTag("server1"))); + ASSERT_TRUE(returned_server); + EXPECT_EQ("server1", returned_server->getServerTag().get()); + EXPECT_EQ("this is server 1 bis", returned_server->getDescription()); + EXPECT_EQ(timestamps_["today"], returned_server->getModificationTime()); + + uint64_t servers_deleted = 0; + + // Try to delete non-existing server. + ASSERT_NO_THROW_LOG(servers_deleted = cbptr_->deleteServer4(ServerTag("server2"))); + EXPECT_EQ(0, servers_deleted); + + // Make sure that the server1 wasn't deleted. + ASSERT_NO_THROW_LOG(returned_server = cbptr_->getServer4(ServerTag("server1"))); + EXPECT_TRUE(returned_server); + + // Deleting logical server 'all' is not allowed. + EXPECT_THROW(cbptr_->deleteServer4(ServerTag()), isc::InvalidOperation); + + // Delete the existing server. + ASSERT_NO_THROW_LOG(servers_deleted = cbptr_->deleteServer4(ServerTag("server1"))); + EXPECT_EQ(1, servers_deleted); + + { + SCOPED_TRACE("DELETE audit entry for server"); + testNewAuditEntry("dhcp4_server", + AuditEntry::ModificationType::DELETE, + "deleting a server"); + } + + // Make sure that the server is gone. + ASSERT_NO_THROW_LOG(returned_server = cbptr_->getServer4(ServerTag("server1"))); + EXPECT_FALSE(returned_server); +} + +// This test verifies that it is possible to retrieve all servers from the +// database and then delete all of them. +TEST_F(PgSqlConfigBackendDHCPv4Test, getAndDeleteAllServers) { + for (auto i = 1; i < test_servers_.size(); ++i) { + ASSERT_NO_THROW_LOG(cbptr_->createUpdateServer4(test_servers_[i])); + } + + ServerCollection servers; + ASSERT_NO_THROW_LOG(servers = cbptr_->getAllServers4()); + ASSERT_EQ(test_servers_.size() - 1, servers.size()); + + // All servers should have been returned. + EXPECT_TRUE(ServerFetcher::get(servers, ServerTag("server1"))); + EXPECT_TRUE(ServerFetcher::get(servers, ServerTag("server2"))); + EXPECT_TRUE(ServerFetcher::get(servers, ServerTag("server3"))); + + // The logical server all should not be returned. We merely return the + // user configured servers. + EXPECT_FALSE(ServerFetcher::get(servers, ServerTag())); + + // Delete all servers and make sure they are gone. + uint64_t deleted_servers = 0; + ASSERT_NO_THROW_LOG(deleted_servers = cbptr_->deleteAllServers4()); + + ASSERT_NO_THROW_LOG(servers = cbptr_->getAllServers4()); + EXPECT_TRUE(servers.empty()); + + // All servers should be gone. + EXPECT_FALSE(ServerFetcher::get(servers, ServerTag("server1"))); + EXPECT_FALSE(ServerFetcher::get(servers, ServerTag("server2"))); + EXPECT_FALSE(ServerFetcher::get(servers, ServerTag("server3"))); + + // The number of deleted server should be equal to the number of + // inserted servers. The logical 'all' server should be excluded. + EXPECT_EQ(test_servers_.size() - 1, deleted_servers); + + EXPECT_EQ(1, countRows("dhcp4_server")); +} + +class PgSqlConfigBackendDHCPv4DbLostCallbackTest : public ::testing::Test { +public: + PgSqlConfigBackendDHCPv4DbLostCallbackTest() + : db_lost_callback_called_(0), db_recovered_callback_called_(0), + db_failed_callback_called_(0), + io_service_(boost::make_shared<isc::asiolink::IOService>()) { + isc::db::DatabaseConnection::db_lost_callback_ = 0; + isc::db::DatabaseConnection::db_recovered_callback_ = 0; + isc::db::DatabaseConnection::db_failed_callback_ = 0; + isc::dhcp::PgSqlConfigBackendImpl::setIOService(io_service_); + isc::dhcp::TimerMgr::instance()->setIOService(io_service_); + isc::dhcp::CfgMgr::instance().clear(); + } + + virtual ~PgSqlConfigBackendDHCPv4DbLostCallbackTest() { + isc::db::DatabaseConnection::db_lost_callback_ = 0; + isc::db::DatabaseConnection::db_recovered_callback_ = 0; + isc::db::DatabaseConnection::db_failed_callback_ = 0; + isc::dhcp::PgSqlConfigBackendImpl::setIOService(isc::asiolink::IOServicePtr()); + isc::dhcp::TimerMgr::instance()->unregisterTimers(); + isc::dhcp::CfgMgr::instance().clear(); + } + + /// @brief Prepares the class for a test. + /// + /// Invoked by gtest prior test entry, we create the + /// appropriate schema and create a basic DB manager to + /// wipe out any prior instance + virtual void SetUp() { + // Ensure we have the proper schema with no transient data. + createPgSQLSchema(); + isc::dhcp::CfgMgr::instance().clear(); + isc::dhcp::PgSqlConfigBackendDHCPv4::registerBackendType(); + } + + /// @brief Pre-text exit clean up + /// + /// Invoked by gtest upon test exit, we destroy the schema + /// we created. + virtual void TearDown() { + // If data wipe enabled, delete transient data otherwise destroy the schema + destroyPgSQLSchema(); + isc::dhcp::CfgMgr::instance().clear(); + isc::dhcp::PgSqlConfigBackendDHCPv4::unregisterBackendType(); + } + + /// @brief Method which returns the back end specific connection + /// string + virtual std::string validConnectString() { + return (validPgSQLConnectionString()); + } + + /// @brief Method which returns invalid back end specific connection + /// string + virtual std::string invalidConnectString() { + return (connectionString(PGSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, + VALID_USER, VALID_PASSWORD)); + } + + /// @brief Verifies open failures do NOT invoke db lost callback + /// + /// The db lost callback should only be invoked after successfully + /// opening the DB and then subsequently losing it. Failing to + /// open should be handled directly by the application layer. + void testNoCallbackOnOpenFailure(); + + /// @brief Verifies the CB manager's behavior if DB connection is lost + /// + /// This function creates a CB manager with a back end that supports + /// connectivity lost callback. It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifying and + /// closing the socket connection to the CB backend. It then reissues the + /// query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbRecoveredCallback was invoked + void testDbLostAndRecoveredCallback(); + + /// @brief Verifies the CB manager's behavior if DB connection is lost + /// + /// This function creates a CB manager with a back end that supports + /// connectivity lost callback. It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifying and + /// closing the socket connection to the CB backend. It then reissues the + /// query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbFailedCallback was invoked + void testDbLostAndFailedCallback(); + + /// @brief Verifies the CB manager's behavior if DB connection is lost + /// + /// This function creates a CB manager with a back end that supports + /// connectivity lost callback. It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifying and + /// closing the socket connection to the CB backend. It then reissues the + /// query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbRecoveredCallback was invoked after two reconnect + /// attempts (once failing and second triggered by timer) + void testDbLostAndRecoveredAfterTimeoutCallback(); + + /// @brief Verifies the CB manager's behavior if DB connection is lost + /// + /// This function creates a CB manager with a back end that supports + /// connectivity lost callback. It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifying and + /// closing the socket connection to the CB backend. It then reissues the + /// query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbFailedCallback was invoked after two reconnect + /// attempts (once failing and second triggered by timer) + void testDbLostAndFailedAfterTimeoutCallback(); + + /// @brief Callback function registered with the CB manager + bool db_lost_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_lost_callback_called_); + } + + /// @brief Flag used to detect calls to db_lost_callback function + uint32_t db_lost_callback_called_; + + /// @brief Callback function registered with the CB manager + bool db_recovered_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_recovered_callback_called_); + } + + /// @brief Flag used to detect calls to db_recovered_callback function + uint32_t db_recovered_callback_called_; + + /// @brief Callback function registered with the CB manager + bool db_failed_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_failed_callback_called_); + } + + /// @brief Flag used to detect calls to db_failed_callback function + uint32_t db_failed_callback_called_; + + /// The IOService object, used for all ASIO operations. + isc::asiolink::IOServicePtr io_service_; +}; + +void +PgSqlConfigBackendDHCPv4DbLostCallbackTest::testNoCallbackOnOpenFailure() { + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = invalidConnectString(); + + // Connect to the CB backend. + ASSERT_THROW(ConfigBackendDHCPv4Mgr::instance().addBackend(access), DbOpenError); + + io_service_->poll(); + + EXPECT_EQ(0, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +PgSqlConfigBackendDHCPv4DbLostCallbackTest::testDbLostAndRecoveredCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + + ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the CB backend. + ASSERT_NO_THROW(ConfigBackendDHCPv4Mgr::instance().addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ServerCollection servers; + ASSERT_NO_THROW_LOG(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector())); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector()), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and recovered connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(1, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +PgSqlConfigBackendDHCPv4DbLostCallbackTest::testDbLostAndFailedCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the CB backend. + ASSERT_NO_THROW(ConfigBackendDHCPv4Mgr::instance().addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ServerCollection servers; + ASSERT_NO_THROW(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector())); + + access = invalidConnectString(); + CfgMgr::instance().clear(); + // by adding an invalid access will cause the manager factory to throw + // resulting in failure to recreate the manager + config_ctl_info.reset(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + const ConfigDbInfoList& cfg = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo()->getConfigDatabases(); + (const_cast<ConfigDbInfoList&>(cfg))[0].setAccessString(access, true); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector()), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and failed connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(1, db_failed_callback_called_); +} + +void +PgSqlConfigBackendDHCPv4DbLostCallbackTest::testDbLostAndRecoveredAfterTimeoutCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + std::string extra = " max-reconnect-tries=3 reconnect-wait-time=1"; + access += extra; + ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the CB backend. + ASSERT_NO_THROW(ConfigBackendDHCPv4Mgr::instance().addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ServerCollection servers; + ASSERT_NO_THROW(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector())); + + access = invalidConnectString(); + access += extra; + CfgMgr::instance().clear(); + // by adding an invalid access will cause the manager factory to throw + // resulting in failure to recreate the manager + config_ctl_info.reset(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + const ConfigDbInfoList& cfg = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo()->getConfigDatabases(); + (const_cast<ConfigDbInfoList&>(cfg))[0].setAccessString(access, true); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector()), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + access = validConnectString(); + access += extra; + CfgMgr::instance().clear(); + config_ctl_info.reset(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + + sleep(1); + + io_service_->poll(); + + // Our lost and recovered connectivity callback should have been invoked. + EXPECT_EQ(2, db_lost_callback_called_); + EXPECT_EQ(1, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + sleep(1); + + io_service_->poll(); + + // No callback should have been invoked. + EXPECT_EQ(2, db_lost_callback_called_); + EXPECT_EQ(1, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +PgSqlConfigBackendDHCPv4DbLostCallbackTest::testDbLostAndFailedAfterTimeoutCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&PgSqlConfigBackendDHCPv4DbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + std::string extra = " max-reconnect-tries=3 reconnect-wait-time=1"; + access += extra; + ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the CB backend. + ASSERT_NO_THROW(ConfigBackendDHCPv4Mgr::instance().addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ServerCollection servers; + ASSERT_NO_THROW(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector())); + + access = invalidConnectString(); + access += extra; + CfgMgr::instance().clear(); + // by adding an invalid access will cause the manager factory to throw + // resulting in failure to recreate the manager + config_ctl_info.reset(new ConfigControlInfo()); + config_ctl_info->addConfigDatabase(access); + CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info); + const ConfigDbInfoList& cfg = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo()->getConfigDatabases(); + (const_cast<ConfigDbInfoList&>(cfg))[0].setAccessString(access, true); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(servers = ConfigBackendDHCPv4Mgr::instance().getPool()->getAllServers4(BackendSelector()), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + sleep(1); + + io_service_->poll(); + + // Our lost connectivity callback should have been invoked. + EXPECT_EQ(2, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + sleep(1); + + io_service_->poll(); + + // Our lost and failed connectivity callback should have been invoked. + EXPECT_EQ(3, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(1, db_failed_callback_called_); +} + +/// @brief Verifies that db lost callback is not invoked on an open failure +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testNoCallbackOnOpenFailure) { + MultiThreadingTest mt(false); + testNoCallbackOnOpenFailure(); +} + +/// @brief Verifies that db lost callback is not invoked on an open failure +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreading) { + MultiThreadingTest mt(true); + testNoCallbackOnOpenFailure(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndRecoveredCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndFailedCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PgSQL is handled correctly. +TEST_F(PgSqlConfigBackendDHCPv4DbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedAfterTimeoutCallback(); +} + +} diff --git a/src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_impl_unittest.cc b/src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_impl_unittest.cc index 9089c9f17f..2c4f98aeb3 100644 --- a/src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_impl_unittest.cc +++ b/src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_impl_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// 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 @@ -74,7 +74,7 @@ public: // Setup() and which will ASSERT on failures. TEST_F(PgsqlConfigBackendTest, constructor) { // Is this the right config backend type? - EXPECT_EQ("pgsql", cbptr_->getType()); + EXPECT_EQ("postgresql", cbptr_->getType()); } } // namespace diff --git a/src/lib/config_backend/base_config_backend.h b/src/lib/config_backend/base_config_backend.h index 821a035509..5348b201e4 100644 --- a/src/lib/config_backend/base_config_backend.h +++ b/src/lib/config_backend/base_config_backend.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-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 @@ -40,7 +40,7 @@ public: /// @brief Returns backend type in the textual format. /// /// @return Name of the storage for configurations, e.g. "mysql", - /// "pgsql" and so forth. + /// "postgresql" and so forth. virtual std::string getType() const = 0; /// @brief Returns backend host. diff --git a/src/lib/config_backend/tests/config_backend_mgr_unittest.cc b/src/lib/config_backend/tests/config_backend_mgr_unittest.cc index a635a3e1a6..1449676361 100644 --- a/src/lib/config_backend/tests/config_backend_mgr_unittest.cc +++ b/src/lib/config_backend/tests/config_backend_mgr_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-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 @@ -158,22 +158,22 @@ typedef boost::shared_ptr<TestConfigBackendImpl1> TestConfigBackendImpl1Ptr; /// @brief Second implementation of the test config backend. /// /// It simulates being a Postgres backend installed on the -/// "pgsql-host" host and running on port 1234. +/// "postgresql-host" host and running on port 1234. class TestConfigBackendImpl2 : public TestConfigBackend { public: /// @brief Returns backend type. /// - /// @return "pgsql". + /// @return "postgresql". virtual std::string getType() const { - return (std::string("pgsql")); + return (std::string("postgresql")); } /// @brief Returns backend host. /// - /// @return "pgsql-host". + /// @return "postgresql-host". virtual std::string getHost() const { - return (std::string("pgsql-host")); + return (std::string("postgresql-host")); } /// @brief Returns backend port. @@ -350,20 +350,20 @@ public: config_mgr_.addBackend("type=mysql"); } - /// @brief Creates example database backend called "pgsql". + /// @brief Creates example database backend called "postgresql". /// /// It uses @c TestConfigBackendImpl2. void addTestPgSQLBackend() { - config_mgr_.registerBackendFactory("pgsql", [](const DatabaseConnection::ParameterMap&) + config_mgr_.registerBackendFactory("postgresql", [](const DatabaseConnection::ParameterMap&) -> TestConfigBackendPtr { return (TestConfigBackendImpl2Ptr(new TestConfigBackendImpl2())); }); // Actually create the backends. - config_mgr_.addBackend("type=pgsql"); + config_mgr_.addBackend("type=postgresql"); } - /// @brief Creates two example database backends called "mysql" and "pgsql". + /// @brief Creates two example database backends called "mysql" and "postgresql". /// /// It uses @c TestConfigBackendImpl1 and @c TestConfigBackendImpl2. void addTestBackends() { @@ -382,9 +382,9 @@ public: // Add two properties into the second backend. Both properties share the // name so as we can test retrieving multiple records from the same backend. config_mgr_.getPool()->createProperty(std::make_pair("cats", 2), - BackendSelector(BackendSelector::Type::PGSQL)); + BackendSelector(BackendSelector::Type::POSTGRESQL)); config_mgr_.getPool()->createProperty(std::make_pair("cats", 4), - BackendSelector(BackendSelector::Type::PGSQL)); + BackendSelector(BackendSelector::Type::POSTGRESQL)); } /// Instance of the test configuration manager. @@ -445,9 +445,9 @@ TEST_F(ConfigBackendMgrTest, getSingleProperty) { EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs")); EXPECT_EQ(2, config_mgr_.getPool()->getProperty("cats")); - // No dogs in the pgsql backend and no cats in mysql backend. + // No dogs in the postgresql backend and no cats in mysql backend. EXPECT_EQ(0, config_mgr_.getPool()->getProperty("dogs", - BackendSelector(BackendSelector::Type::PGSQL))); + BackendSelector(BackendSelector::Type::POSTGRESQL))); EXPECT_EQ(0, config_mgr_.getPool()->getProperty("cats", BackendSelector(BackendSelector::Type::MYSQL))); @@ -456,7 +456,7 @@ TEST_F(ConfigBackendMgrTest, getSingleProperty) { EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs", BackendSelector(BackendSelector::Type::MYSQL))); EXPECT_EQ(2, config_mgr_.getPool()->getProperty("cats", - BackendSelector(BackendSelector::Type::PGSQL))); + BackendSelector(BackendSelector::Type::POSTGRESQL))); // Also make sure that the variant of getProperty function taking two arguments // would return the value. @@ -490,11 +490,11 @@ TEST_F(ConfigBackendMgrTest, getMultipleProperties) { BackendSelector(BackendSelector::Type::MYSQL)); ASSERT_EQ(1, mysql_list.size()); - // There are two cats entries in pgsql. - PropertiesList pgsql_list = + // There are two cats entries in postgresql. + PropertiesList postgresql_list = config_mgr_.getPool()->getProperties("cats", - BackendSelector(BackendSelector::Type::PGSQL)); - ASSERT_EQ(2, pgsql_list.size()); + BackendSelector(BackendSelector::Type::POSTGRESQL)); + ASSERT_EQ(2, postgresql_list.size()); // Try to use the backend that is not present. EXPECT_THROW(config_mgr_.getPool()->getProperties("cats", @@ -514,10 +514,10 @@ TEST_F(ConfigBackendMgrTest, getAllProperties) { config_mgr_.getPool()->getAllProperties(BackendSelector(BackendSelector::Type::MYSQL)); ASSERT_EQ(2, mysql_list.size()); - // The pgsql backends also holds two entries. - PropertiesList pgsql_list = - config_mgr_.getPool()->getAllProperties(BackendSelector(BackendSelector::Type::PGSQL)); - ASSERT_EQ(2, pgsql_list.size()); + // The postgresql backends also holds two entries. + PropertiesList postgresql_list = + config_mgr_.getPool()->getAllProperties(BackendSelector(BackendSelector::Type::POSTGRESQL)); + ASSERT_EQ(2, postgresql_list.size()); // Try to use the backend that is not present. EXPECT_THROW(config_mgr_.getPool()->getProperties("cats", diff --git a/src/lib/database/backend_selector.cc b/src/lib/database/backend_selector.cc index 3ad8e219c6..91cac343a6 100644 --- a/src/lib/database/backend_selector.cc +++ b/src/lib/database/backend_selector.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-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 @@ -132,8 +132,8 @@ BackendSelector::stringToBackendType(const std::string& type) { if (type == "mysql") { return (BackendSelector::Type::MYSQL); - } else if (type == "pgsql") { - return (BackendSelector::Type::PGSQL); + } else if (type == "postgresql") { + return (BackendSelector::Type::POSTGRESQL); } else if (type == "cql") { return (BackendSelector::Type::CQL); @@ -148,8 +148,8 @@ BackendSelector::backendTypeToString(const BackendSelector::Type& type) { switch (type) { case BackendSelector::Type::MYSQL: return ("mysql"); - case BackendSelector::Type::PGSQL: - return ("pgsql"); + case BackendSelector::Type::POSTGRESQL: + return ("postgresql"); case BackendSelector::Type::CQL: return ("cql"); default: diff --git a/src/lib/database/backend_selector.h b/src/lib/database/backend_selector.h index bc969d68aa..cc2db1527b 100644 --- a/src/lib/database/backend_selector.h +++ b/src/lib/database/backend_selector.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-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 @@ -61,7 +61,7 @@ public: /// as selection criteria. enum class Type { MYSQL, - PGSQL, + POSTGRESQL, CQL, UNSPEC }; diff --git a/src/lib/database/tests/backend_selector_unittest.cc b/src/lib/database/tests/backend_selector_unittest.cc index 6061e1bd41..d30941b069 100644 --- a/src/lib/database/tests/backend_selector_unittest.cc +++ b/src/lib/database/tests/backend_selector_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-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 @@ -152,8 +152,8 @@ TEST(BackendSelectorTest, accessMapPortSpec) { TEST(BackendSelectorTest, stringToBackendType) { EXPECT_EQ(BackendSelector::Type::MYSQL, BackendSelector::stringToBackendType("mysql")); - EXPECT_EQ(BackendSelector::Type::PGSQL, - BackendSelector::stringToBackendType("pgsql")); + EXPECT_EQ(BackendSelector::Type::POSTGRESQL, + BackendSelector::stringToBackendType("postgresql")); EXPECT_EQ(BackendSelector::Type::CQL, BackendSelector::stringToBackendType("cql")); EXPECT_THROW(BackendSelector::stringToBackendType("unsupported"), @@ -164,8 +164,8 @@ TEST(BackendSelectorTest, stringToBackendType) { TEST(BackendSelectorTest, backendTypeToString) { EXPECT_EQ("mysql", BackendSelector::backendTypeToString(BackendSelector::Type::MYSQL)); - EXPECT_EQ("pgsql", - BackendSelector::backendTypeToString(BackendSelector::Type::PGSQL)); + EXPECT_EQ("postgresql", + BackendSelector::backendTypeToString(BackendSelector::Type::POSTGRESQL)); EXPECT_EQ("cql", BackendSelector::backendTypeToString(BackendSelector::Type::CQL)); } diff --git a/src/lib/pgsql/pgsql_connection.cc b/src/lib/pgsql/pgsql_connection.cc index efd0d06b08..e5cd0b635e 100644 --- a/src/lib/pgsql/pgsql_connection.cc +++ b/src/lib/pgsql/pgsql_connection.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -6,6 +6,7 @@ #include <config.h> +#include <database/db_exceptions.h> #include <database/db_log.h> #include <pgsql/pgsql_connection.h> @@ -38,6 +39,7 @@ namespace db { const int PGSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION; +const char PgSqlConnection::NULL_KEY[] = ERRCODE_NOT_NULL_VIOLATION; bool PgSqlConnection::warned_about_tls = false; @@ -162,7 +164,9 @@ PgSqlConnection::prepareStatement(const PgSqlTaggedStatement& statement) { statement.nbparams, statement.types)); if (PQresultStatus(r) != PGRES_COMMAND_OK) { isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: " - << statement.text << ", reason: " << PQerrorMessage(conn_)); + << " name: " << statement.name + << ", reason: " << PQerrorMessage(conn_) + << ", text: " << statement.text); } } @@ -348,6 +352,18 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r, "fatal database error or connectivity lost"); } + // Failure: check for the special case of duplicate entry. + if (compareError(r, PgSqlConnection::DUPLICATE_KEY)) { + isc_throw(DuplicateEntry, "statement: " << statement.name + << ", reason: " << PQerrorMessage(conn_)); + } + + // Failure: check for the special case of null key violation. + if (compareError(r, PgSqlConnection::NULL_KEY)) { + isc_throw(NullKeyError, "statement: " << statement.name + << ", reason: " << PQerrorMessage(conn_)); + } + // Apparently it wasn't fatal, so we throw with a helpful message. const char* error_message = PQerrorMessage(conn_); isc_throw(DbOperationError, "Statement exec failed for: " diff --git a/src/lib/pgsql/pgsql_connection.h b/src/lib/pgsql/pgsql_connection.h index 1de441f87c..2de244e73d 100644 --- a/src/lib/pgsql/pgsql_connection.h +++ b/src/lib/pgsql/pgsql_connection.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -25,7 +25,7 @@ const uint32_t PGSQL_SCHEMA_VERSION_MINOR = 0; // @todo This allows us to use an initializer list (since we can't // require C++11). It's unlikely we'd go past this many a single // statement. -const size_t PGSQL_MAX_PARAMETERS_IN_QUERY = 32; +const size_t PGSQL_MAX_PARAMETERS_IN_QUERY = 128; /// @brief Define a PostgreSQL statement. /// @@ -199,6 +199,8 @@ class PgSqlConnection : public db::DatabaseConnection { public: /// @brief Define the PgSql error state for a duplicate key error. static const char DUPLICATE_KEY[]; + /// @brief Define the PgSql error state for a null foreign key error. + static const char NULL_KEY[]; /// @brief Function invoked to process fetched row. typedef std::function<void(PgSqlResult&, int)> ConsumeResultRowFun; diff --git a/src/lib/pgsql/pgsql_exchange.cc b/src/lib/pgsql/pgsql_exchange.cc index 9cee1d5740..a16d8e057c 100644 --- a/src/lib/pgsql/pgsql_exchange.cc +++ b/src/lib/pgsql/pgsql_exchange.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -56,7 +56,7 @@ void PsqlBindArray::insert(const char* value, size_t index) { } void PsqlBindArray::insert(const std::string& value, size_t index) { - if (index >= values_.size()) { + if (index && index >= values_.size()) { isc_throw(OutOfRange, "PsqlBindArray::insert - index: " << index << ", is larger than the array size: " << values_.size()); } @@ -66,6 +66,16 @@ void PsqlBindArray::insert(const std::string& value, size_t index) { formats_.insert(formats_.begin() + index, TEXT_FMT); } +void PsqlBindArray::popBack() { + if (values_.size() == 0) { + isc_throw(OutOfRange, "PsqlBindArray::pop_back - array empty"); + } + + values_.erase(values_.end() - 1); + lengths_.erase(lengths_.end() - 1); + formats_.erase(formats_.end() - 1); +} + void PsqlBindArray::add(const std::vector<uint8_t>& data) { values_.push_back(reinterpret_cast<const char*>(&(data[0]))); lengths_.push_back(data.size()); @@ -146,20 +156,11 @@ void PsqlBindArray::addTempString(const std::string& str) { } void -PsqlBindArray::addOptionalString(const util::Optional<std::string>& value) { - if (value.unspecified()) { - addNull(); - } else { - add(value); - } -} - -void -PsqlBindArray::addOptionalBool(const util::Optional<bool>& value) { +PsqlBindArray::addOptional(const util::Optional<std::string>& value) { if (value.unspecified()) { addNull(); } else { - add(value); + addTempString(value); } } @@ -243,6 +244,18 @@ PsqlBindArray::add(const ElementPtr& value) { addTempString(ss.str()); } +void +PsqlBindArray::add(const ConstElementPtr& value) { + if (!value) { + addNull(); + return; + } + + std::ostringstream ss; + value->toJSON(ss); + addTempString(ss.str()); +} + std::string PsqlBindArray::toText() const { std::ostringstream stream; diff --git a/src/lib/pgsql/pgsql_exchange.h b/src/lib/pgsql/pgsql_exchange.h index f45a5391f6..d81474da86 100644 --- a/src/lib/pgsql/pgsql_exchange.h +++ b/src/lib/pgsql/pgsql_exchange.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -222,17 +222,25 @@ struct PsqlBindArray { /// @brief Inserts a string value to the bind array before the given index /// /// Inserts a TEXT_FMT value into the bind array before the element - /// position given by index, using the given given string as the data source. + /// position given by index, using the given given string as the data + /// source. + /// /// The caller is responsible for ensuring that string parameter remains in /// scope until the bind array has been discarded. /// /// @param value char array containing the null-terminated text to add. - /// @param index element position before which the string should be inserted. + /// @param index element position before which the string should be + /// inserted. /// /// @throw DbOperationError if value is NULL. /// @throw OutOfRange if the index is beyond the end of the array. void insert(const std::string& value, size_t index); + /// @brief Removes the last entry in the bind array. + /// + /// @throw OutOfRange if array is empty. + void popBack(); + /// @brief Adds a vector of binary data to the bind array. /// /// Adds a BINARY_FMT value to the end of the bind array using the @@ -338,21 +346,19 @@ struct PsqlBindArray { /// @brief Adds an @c Optional string to the bind array. /// - /// @param value Optional string value to add - void addOptionalString(const util::Optional<std::string>& value); - - /// @brief Adds an @c Optional boolean to the bind array. + /// Optional strings require adding a temp string to the + /// bind array, unlike other types which implicitly do so. /// - /// @param value Optional boolean value to add - void addOptionalBool(const util::Optional<bool>& value); + /// @param value Optional string value to add + void addOptional(const util::Optional<std::string>& value); - /// @brief Adds an @c Optional of integer type to the bind array. + /// @brief Adds an @c Optional<type> value to the bind array. /// - /// @tparam T Numeric type corresponding to the binding type, e.g. - /// @c uint8_t, @c uint16_t etc. - /// @param value Optional integer of type T. + /// @tparam T variable type corresponding to the binding type, e.g. + /// @c string, bool, uint8_t, @c uint16_t etc. + /// @param value Optional of type T. template<typename T> - void addOptionalInteger(const util::Optional<T>& value) { + void addOptional(const util::Optional<T>& value) { if (value.unspecified()) { addNull(); } else { @@ -417,6 +423,15 @@ struct PsqlBindArray { /// @throw DbOperationError if value is NULL. void add(const data::ElementPtr& value); + /// @brief Adds a ConstElementPtr to the bind array + /// + /// Adds a TEXT_FMT value to the end of the bind array containing + /// the JSON text output by given ElementPtr::toJSON(). + /// + /// @param value ElementPtr containing Element tree to add. + /// @throw DbOperationError if value is NULL. + void add(const data::ConstElementPtr& value); + /// @brief Dumps the contents of the array to a string. /// @return std::string containing the dump std::string toText() const; diff --git a/src/lib/pgsql/tests/pgsql_exchange_unittest.cc b/src/lib/pgsql/tests/pgsql_exchange_unittest.cc index 92d9c77fca..2c6939d558 100644 --- a/src/lib/pgsql/tests/pgsql_exchange_unittest.cc +++ b/src/lib/pgsql/tests/pgsql_exchange_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -171,8 +171,8 @@ TEST(PsqlBindArray, addOptionalString) { Optional<std::string> not_empty("whoopee!"); // Add strings to the array. - b.addOptionalString(empty); - b.addOptionalString(not_empty); + b.addOptional(empty); + b.addOptional(not_empty); } // We've left bind scope, everything should be intact. @@ -200,9 +200,9 @@ TEST(PsqlBindArray, addOptionalBool) { Optional<bool> am_true(true); // Add booleans to the array. - b.addOptionalBool(empty); - b.addOptionalBool(am_false); - b.addOptionalBool(am_true); + b.addOptional(empty); + b.addOptional(am_false); + b.addOptional(am_true); } // We've left bind scope, everything should be intact. @@ -231,8 +231,8 @@ TEST(PsqlBindArray, addOptionalInteger) { Optional<uint32_t> not_empty(123); // Add the integers to the array.. - b.addOptionalInteger(empty); - b.addOptionalInteger(not_empty); + b.addOptional(empty); + b.addOptional(not_empty); } // We've left bind scope, everything should be intact. @@ -1046,6 +1046,42 @@ TEST(PsqlBindArray, insertString) { EXPECT_EQ(expected, b.toText()); } +TEST(PsqlBindArray, popBackTest) { + PsqlBindArray b; + + // Popping on an empty array should throw. + ASSERT_THROW_MSG(b.popBack(), OutOfRange, + "PsqlBindArray::pop_back - array empty"); + + // Add five integers. + for (int i = 1; i < 6; ++i) { + b.add(i); + } + + // Verify size. + EXPECT_EQ(b.size(), 5); + + // Pop one off. + ASSERT_NO_THROW_LOG(b.popBack()); + + // Verify size. + EXPECT_EQ(b.size(), 4); + + // Pop another one off. + ASSERT_NO_THROW_LOG(b.popBack()); + + // Verify size. + EXPECT_EQ(b.size(), 3); + + // This is what we should have left. + std::string expected = + "0 : \"1\"\n" + "1 : \"2\"\n" + "2 : \"3\"\n"; + + EXPECT_EQ(expected, b.toText()); +} + /// @brief Verify that we can read and write IPv4 addresses /// using INET columns. TEST_F(PgSqlBasicsTest, inetTest4) { |