///
/// It is empty, but needs a virtual one, since we will use the derived
/// classes in polymorphic way.
- virtual ~DatabaseAccessor() { }
+ virtual ~DatabaseAccessor() {}
/// \brief Retrieve a zone identifier
///
/// apex), as the DatabaseClient will loop trough the labels itself and
/// find the most suitable zone.
///
- /// It is not specified if and what implementation of this method may throw,
- /// so code should expect anything.
+ /// It is not specified if and what implementation of this method may
+ /// throw, so code should expect anything.
///
/// \param name The (fully qualified) domain name of the zone's apex to be
/// looked up.
/// or was created by this call).
virtual int addZone(const std::string& name) = 0;
+ virtual void deleteZone(int zone_id) = 0;
+
/// \brief This holds the internal context of ZoneIterator for databases
///
/// While the ZoneIterator implementation from DatabaseClient does all the
/// \brief Destructor
///
/// Virtual destructor, so any descendand class is destroyed correctly.
- virtual ~IteratorContext() { }
+ virtual ~IteratorContext() {}
/// \brief Function to provide next resource record
///
/// This function should provide data about the next resource record
/// from the data that is searched. The data is not converted yet.
///
- /// Depending on how the iterator was constructed, there is a difference
- /// in behaviour; for a 'full zone iterator', created with
+ /// Depending on how the iterator was constructed, there is a
+ /// difference in behaviour; for a 'full zone iterator', created with
/// getAllRecords(), all COLUMN_COUNT elements of the array are
/// overwritten.
/// For a 'name iterator', created with getRecords(), the column
DEL_ZONE_NSEC3_RECORDS = 20,
DEL_NSEC3_RECORD = 21,
ADD_ZONE = 22,
- NUM_STATEMENTS = 23
+ DELETE_ZONE = 23,
+ NUM_STATEMENTS = 24
};
const char* const text_statements[NUM_STATEMENTS] = {
"AND rdtype=?3 AND rdata=?4",
// ADD_ZONE: add a zone to the zones table
- "INSERT INTO zones (name, rdclass) VALUES (?1, ?2)" // ADD_ZONE
+ "INSERT INTO zones (name, rdclass) VALUES (?1, ?2)", // ADD_ZONE
+ // DELETE_ZONE: delete a zone from the zones table
+ "DELETE FROM zones WHERE id=?1" // DELETE_ZONE
};
struct SQLite3Parameters {
return (getzone_result.second);
}
+void
+SQLite3Accessor::deleteZone(int zone_id) {
+ // Transaction should have been started by the caller
+ if (!dbparameters_->in_transaction) {
+ isc_throw(InvalidOperation, "performing deleteZone on SQLite3 "
+ "data source without transaction");
+ }
+
+ StatementProcessor proc(*dbparameters_, DELETE_ZONE, "delete zone");
+ proc.bindInt(1, zone_id);
+ proc.exec();
+}
+
namespace {
// Conversion to plain char
/// \return the id of the zone that has been added
virtual int addZone(const std::string& name);
+ virtual void deleteZone(int zone_id);
+
/// \brief Look up all resource records for a name
///
/// This implements the getRecords() method from DatabaseAccessor
"This database datasource can't add zones");
}
+ virtual void deleteZone(int) {
+ isc_throw(isc::NotImplemented,
+ "This database datasource can't delete zones");
+ }
+
virtual boost::shared_ptr<DatabaseAccessor> clone() {
// This accessor is stateless, so we can simply return a new instance.
return (boost::shared_ptr<DatabaseAccessor>(new NopAccessor));
#include <dns/rrclass.h>
+#include <exceptions/exceptions.h>
+
#include <sqlite3.h>
#include <gtest/gtest.h>
EXPECT_FALSE(accessor->getZone(new_zone).first);
}
+//
+// Tests for deleteZone() follow.
+//
+TEST_F(SQLite3Update, deleteZone) {
+ const std::pair<bool, int> zone_info(accessor->getZone("example.com."));
+ ASSERT_TRUE(zone_info.first);
+ zone_id = zone_info.second;
+
+ // Calling deleteZone without transaction should fail
+ EXPECT_THROW(accessor->deleteZone(zone_info.first), isc::InvalidOperation);
+
+ // Delete the zone. Then confirm it, both before and after commit.
+ accessor->startTransaction();
+ accessor->deleteZone(zone_info.second);
+ EXPECT_FALSE(accessor->getZone("example.com.").first);
+ accessor->commit();
+ EXPECT_FALSE(accessor->getZone("example.com.").first);
+
+ // Records are not deleted.
+ std::string data[DatabaseAccessor::COLUMN_COUNT];
+ EXPECT_TRUE(accessor->getRecords("example.com.", zone_id, false)
+ ->getNext(data));
+}
+
+TEST_F(SQLite3Update, deleteZoneWhileLocked) {
+ const std::pair<bool, int> zone_info(accessor->getZone("example.com."));
+ ASSERT_TRUE(zone_info.first);
+ zone_id = zone_info.second;
+
+ // Adding another (not commit yet), it should lock the db
+ const std::string new_zone = "new.example.com.";
+ accessor->startTransaction();
+ zone_id = accessor->addZone(new_zone);
+
+ // deleteZone should throw an exception that it is locked
+ another_accessor->startTransaction();
+ EXPECT_THROW(another_accessor->deleteZone(zone_id), DataSourceError);
+ // Commit should do nothing, but not fail
+ another_accessor->commit();
+
+ accessor->rollback();
+
+ // The zone should still exist.
+ EXPECT_TRUE(accessor->getZone("example.com.").first);
+}
+
} // end anonymous namespace