-// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2023 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
isc::Exception(file, line, what) {}
};
+/// @brief Thrown when it is expected that some rows are affected,
+/// usually during a DELETE or an UPDATE, but none are.
+class NoRowsAffected : public Exception {
+public:
+ NoRowsAffected(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
} // namespace isc
} // namespace db
isc::Exception(file, line, what) { };
};
+/// @brief Exception thrown when a @c Host object is expected, but none are found.
+class HostNotFound : public Exception {
+public:
+ HostNotFound(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
/// @brief Exception thrown when an address is already reserved by a @c Host
/// object (DuplicateHost is same identity, ReservedAddress same address).
class ReservedAddress : public Exception {
const Host::IdentifierType& identifier_type,
const uint8_t* identifier_begin, const size_t identifier_len) = 0;
+ /// @brief Attempts to update an existing host entry.
+ ///
+ /// @param host the host up to date with the requested changes
+ ///
+ /// @return true if deletion was successful, false if the host was not there.
+ virtual void update(HostPtr const& host) = 0;
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
/// @brief HostDataSource list
typedef std::vector<HostDataSourcePtr> HostDataSourceList;
-}
-}
+} // namespace dhcp
+} // namespace isc
#endif // BASE_HOST_DATA_SOURCE_H
using namespace isc::asiolink;
using namespace isc::data;
+using namespace std;
namespace isc {
namespace dhcp {
return (false);
}
+void
+CfgHosts::update(HostPtr const& host) {
+ bool deleted(false);
+ if (CfgMgr::instance().getFamily() == AF_INET) {
+ vector<uint8_t> const& identifier(host->getIdentifier());
+ deleted = del4(host->getIPv4SubnetID(), host->getIdentifierType(), identifier.data(),
+ identifier.size());
+ } else {
+ vector<uint8_t> const& identifier(host->getIdentifier());
+ deleted = del6(host->getIPv6SubnetID(), host->getIdentifierType(), identifier.data(),
+ identifier.size());
+ }
+ if (!deleted) {
+ isc_throw(HostNotFound, "Host not updated (not found).");
+ }
+ add(host);
+}
+
bool
CfgHosts::setIPReservationsUnique(const bool unique) {
ip_reservations_unique_ = unique;
const Host::IdentifierType& identifier_type,
const uint8_t* identifier_begin, const size_t identifier_len);
+ /// @brief Implements @ref BaseHostDataSource::update() for config hosts.
+ void update(HostPtr const& host);
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
return (false);
}
+void
+HostMgr::update(HostPtr const& host) {
+ if (alternate_sources_.empty()) {
+ isc_throw(NoHostDataSourceManager,
+ "Unable to update existing host because there is no hosts-database configured.");
+ }
+ for (HostDataSourcePtr const& source : alternate_sources_) {
+ source->update(host);
+ }
+ // If no backend throws the host should be cached.
+ if (cache_ptr_) {
+ cache(host);
+ }
+}
+
void
HostMgr::cache(ConstHostPtr host) const {
if (cache_ptr_) {
del6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
const uint8_t* identifier_begin, const size_t identifier_len);
+ /// @brief Implements @ref BaseHostDataSource::update() for alternate sources.
+ void update(HostPtr const& host);
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
return (collection);
}
+void
+MySqlHostDataSource::update(HostPtr const& host) {
+ // Get a context.
+ MySqlHostContextAlloc const context(*impl_);
+ MySqlHostContextPtr ctx(context.ctx_);
+
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly(ctx);
+
+ // Initiate MySQL transaction as we will have to make multiple queries
+ // to update host information into multiple tables. If that fails on
+ // any stage, the transaction will be rolled back by the destructor of
+ // the MySqlTransaction class.
+ MySqlTransaction transaction(ctx->conn_);
+
+ // As much as having dedicated prepared statements for updating tables would be consistent with
+ // the implementation of other commands, it's difficult if not impossible to cover all cases for
+ // updating the host to exactly as is described in the command, which may involve inserts and
+ // deletes alongside updates. So let's delete and add. The delete cascades into all tables. The
+ // add explicitly adds into all tables.
+ bool deleted(false);
+ if (CfgMgr::instance().getFamily() == AF_INET) {
+ vector<uint8_t> const& identifier(host->getIdentifier());
+ deleted = del4(host->getIPv4SubnetID(), host->getIdentifierType(), identifier.data(),
+ identifier.size());
+ } else {
+ vector<uint8_t> const& identifier(host->getIdentifier());
+ deleted = del6(host->getIPv6SubnetID(), host->getIdentifierType(), identifier.data(),
+ identifier.size());
+ }
+ if (!deleted) {
+ isc_throw(NoRowsAffected, "Host not updated (not found).");
+ }
+ add(host);
+
+ // Everything went fine, so explicitly commit the transaction.
+ transaction.commit();
+}
+
// Miscellaneous database methods.
std::string
getAll6(const SubnetID& subnet_id,
const asiolink::IOAddress& address) const;
+ /// @brief Implements @ref BaseHostDataSource::update() for MySQL.
+ void update(HostPtr const& host);
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
return (collection);
}
+void
+PgSqlHostDataSource::update(HostPtr const& host) {
+ // Get a context.
+ PgSqlHostContextAlloc const context(*impl_);
+ PgSqlHostContextPtr ctx(context.ctx_);
+
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly(ctx);
+
+ // Initiate PostgreSQL transaction as we will have to make multiple queries
+ // to update host information into multiple tables. If that fails on
+ // any stage, the transaction will be rolled back by the destructor of
+ // the PgSqlTransaction class.
+ PgSqlTransaction transaction(ctx->conn_);
+
+ // As much as having dedicated prepared statements for updating tables would be consistent with
+ // the implementation of other commands, it's difficult if not impossible to cover all cases for
+ // updating the host to exactly as is described in the command, which may involve inserts and
+ // deletes alongside updates. So let's delete and add. The delete cascades into all tables. The
+ // add explicitly adds into all tables.
+ bool deleted(false);
+ if (CfgMgr::instance().getFamily() == AF_INET) {
+ vector<uint8_t> const& identifier(host->getIdentifier());
+ deleted = del4(host->getIPv4SubnetID(), host->getIdentifierType(), identifier.data(),
+ identifier.size());
+ } else {
+ vector<uint8_t> const& identifier(host->getIdentifier());
+ deleted = del6(host->getIPv6SubnetID(), host->getIdentifierType(), identifier.data(),
+ identifier.size());
+ }
+ if (!deleted) {
+ isc_throw(NoRowsAffected, "Host not updated (not found).");
+ }
+ add(host);
+
+ // Everything went fine, so explicitly commit the transaction.
+ transaction.commit();
+}
+
// Miscellaneous database methods.
std::string
getAll6(const SubnetID& subnet_id,
const asiolink::IOAddress& address) const;
+ /// @brief Implements @ref BaseHostDataSource::update() for PostgreSQL.
+ void update(HostPtr const& host);
+
/// @brief Return backend type
///
/// Returns the type of database as the string "postgresql". This is