From: JINMEI Tatuya Date: Fri, 13 Apr 2012 20:41:24 +0000 (-0700) Subject: [1781] introduced addRecordToNSEC3Zone interface to DB accessors. X-Git-Tag: trac2351_base~226^2~116^2~26^2~11 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0bc5c68ef6c5811eb93227e1d8d44f7762a3f7b2;p=thirdparty%2Fkea.git [1781] introduced addRecordToNSEC3Zone interface to DB accessors. this is the first step to support updating the NSEC3 namespace of a zone. to help support various cases in addRRset() in a less expensive way, introduced a helper RRParameterConverter class. one simple test case was added to confirm the behavior. --- diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc index 08c7b26b31..024dd8db2a 100644 --- a/src/lib/datasrc/database.cc +++ b/src/lib/datasrc/database.cc @@ -1191,31 +1191,80 @@ DatabaseUpdater::validateAddOrDelete(const char* const op_str, } } +// This is a helper class used in adding/deleting RRsets to/from a database. +// The purpose of this class is to provide conversion interface from various +// parameters of the RRset to corresponding textual representations that the +// underlying database interface expects. The necessary parameters and how +// to convert them depend on several things, such as whether it's NSEC3 related +// or not, or whether journaling is requested. In order to avoid unnecessary +// conversion, this class also performs the conversion in a lazy manner. +// Also, in order to avoid redundant conversion when the conversion is +// requested for the same parameter multiple times, it remembers the +// conversion result first time, and reuses it for subsequent requests +// (this implicitly assumes copying std::string objects is not very expensive; +// this is often the case in some common implementations that have +// copy-on-write semantics for the string class). +class RRParameterConverter { +public: + RRParameterConverter(const AbstractRRset& rrset) : rrset_(rrset) + {} + const string& getName() { + if (name_.empty()) { + name_ = rrset_.getName().toText(); + } + return (name_); + } + const string& getNSEC3Name() { + if (nsec3_name_.empty()) { + nsec3_name_ = rrset_.getName().split(0, 1).toText(true); + } + return (nsec3_name_); + } + const string& getRevName() { + if (revname_.empty()) { + revname_ = rrset_.getName().reverse().toText(); + } + return (revname_); + } + const string& getTTL() { + if (ttl_.empty()) { + ttl_ = rrset_.getTTL().toText(); + } + return (ttl_); + } + const string& getType() { + if (type_.empty()) { + type_ = rrset_.getType().toText(); + } + return (type_); + } + +private: + string name_; + string nsec3_name_; + string revname_; + string ttl_; + string type_; + const AbstractRRset& rrset_; +}; + void DatabaseUpdater::addRRset(const AbstractRRset& rrset) { validateAddOrDelete("add", rrset, DELETE, ADD); // It's guaranteed rrset has at least one RDATA at this point. RdataIteratorPtr it = rrset.getRdataIterator(); - - string columns[Accessor::ADD_COLUMN_COUNT]; // initialized with "" - columns[Accessor::ADD_NAME] = rrset.getName().toText(); - columns[Accessor::ADD_REV_NAME] = rrset.getName().reverse().toText(); - columns[Accessor::ADD_TTL] = rrset.getTTL().toText(); - columns[Accessor::ADD_TYPE] = rrset.getType().toText(); - string journal[Accessor::DIFF_PARAM_COUNT]; if (journaling_) { - journal[Accessor::DIFF_NAME] = columns[Accessor::ADD_NAME]; - journal[Accessor::DIFF_TYPE] = columns[Accessor::ADD_TYPE]; - journal[Accessor::DIFF_TTL] = columns[Accessor::ADD_TTL]; diff_phase_ = ADD; if (rrset.getType() == RRType::SOA()) { - serial_ = - dynamic_cast(it->getCurrent()). + serial_ = dynamic_cast(it->getCurrent()). getSerial(); } } + + RRParameterConverter cvtr(rrset); for (; !it->isLast(); it->next()) { + string sigtype; if (rrset.getType() == RRType::RRSIG()) { // XXX: the current interface (based on the current sqlite3 // data source schema) requires a separate "sigtype" column, @@ -1224,16 +1273,25 @@ DatabaseUpdater::addRRset(const AbstractRRset& rrset) { // the interface, but until then we have to conform to the schema. const generic::RRSIG& rrsig_rdata = dynamic_cast(it->getCurrent()); - columns[Accessor::ADD_SIGTYPE] = - rrsig_rdata.typeCovered().toText(); + sigtype = rrsig_rdata.typeCovered().toText(); } - columns[Accessor::ADD_RDATA] = it->getCurrent().toText(); + const string rdata = it->getCurrent().toText(); if (journaling_) { - journal[Accessor::DIFF_RDATA] = columns[Accessor::ADD_RDATA]; + const string journal[Accessor::DIFF_PARAM_COUNT] = + { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata }; accessor_->addRecordDiff(zone_id_, serial_.getValue(), Accessor::DIFF_ADD, journal); } - accessor_->addRecordToZone(columns); + if (rrset.getType() == RRType::NSEC3()) { + const string nsec3_columns[Accessor::ADD_NSEC3_COLUMN_COUNT] = + { cvtr.getNSEC3Name(), cvtr.getTTL(), cvtr.getType(), rdata }; + accessor_->addRecordToNSEC3Zone(nsec3_columns); + } else { + const string columns[Accessor::ADD_COLUMN_COUNT] = + { cvtr.getName(), cvtr.getRevName(), cvtr.getTTL(), + cvtr.getType(), sigtype, rdata }; + accessor_->addRecordToZone(columns); + } } } diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h index d9d6dfadde..47206be0e8 100644 --- a/src/lib/datasrc/database.h +++ b/src/lib/datasrc/database.h @@ -95,6 +95,16 @@ public: ADD_COLUMN_COUNT = 6 ///< Number of columns }; + enum AddNSEC3RecordColumns { + ADD_NSEC3_HASH = 0, ///< The owner name of the record (a domain name) + ADD_NSEC3_TTL = 1, ///< The TTL of the record (in numeric form) + ADD_NSEC3_TYPE = 2, ///< The RRType of the record (either NSEC3 or + ///< RRSIG) + ADD_NSEC3_RDATA = 3, ///< Full text representation of the record's + ///< RDATA + ADD_NSEC3_COLUMN_COUNT = 4 ///< Number of columns + }; + /// \brief Definitions of the fields to be passed to deleteRecordInZone() /// /// Each derived implementation of deleteRecordInZone() should expect @@ -432,6 +442,9 @@ public: virtual void addRecordToZone( const std::string (&columns)[ADD_COLUMN_COUNT]) = 0; + virtual void addRecordToNSEC3Zone( + const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]) = 0; + /// \brief Delete a single record from the zone to be updated. /// /// This method provides a simple interface to delete a record @@ -684,7 +697,7 @@ public: /// This is used to find previous NSEC3 hashes, to find covering NSEC3 in /// case none match exactly. /// - /// In case a hash before before the lowest or the lowest is provided, + /// In case a hash before the lowest or the lowest is provided, /// this should return the largest one in the zone (NSEC3 needs a /// wrap-around semantics). /// diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 11f364e9d7..929010520d 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -1121,6 +1121,13 @@ SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) { *dbparameters_, ADD_RECORD, columns, "add record to zone"); } +void +SQLite3Accessor::addRecordToNSEC3Zone( + const string (&/*columns*/)[ADD_NSEC3_COLUMN_COUNT]) +{ + isc_throw(NotImplemented, "not yet implemented"); +} + void SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) { if (!dbparameters_->updating_zone) { diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h index 9f3b60e527..bb93a1d15b 100644 --- a/src/lib/datasrc/sqlite3_accessor.h +++ b/src/lib/datasrc/sqlite3_accessor.h @@ -214,6 +214,9 @@ public: virtual void addRecordToZone( const std::string (&columns)[ADD_COLUMN_COUNT]); + virtual void addRecordToNSEC3Zone( + const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]); + virtual void deleteRecordInZone( const std::string (¶ms)[DEL_PARAM_COUNT]); diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc index 6868d0e3d3..8bd4392c8c 100644 --- a/src/lib/datasrc/tests/database_unittest.cc +++ b/src/lib/datasrc/tests/database_unittest.cc @@ -14,6 +14,7 @@ #include +#include #include #include #include @@ -29,6 +30,7 @@ #include +#include #include #include @@ -247,6 +249,8 @@ public: virtual void commit() {} virtual void rollback() {} virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {} + virtual void addRecordToNSEC3Zone(const string (&)[ADD_NSEC3_COLUMN_COUNT]) + {} virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {} virtual void addRecordDiff(int, uint32_t, DiffOperation, const std::string (&)[DIFF_PARAM_COUNT]) {} @@ -687,6 +691,7 @@ public: virtual void commit() { *readonly_records_ = *update_records_; } + virtual void rollback() { // Special hook: if something with a name of "throw.example.org" // has been added, trigger an imaginary unexpected event with an @@ -697,6 +702,7 @@ public: rollbacked_ = true; } + virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) { // Copy the current value to cur_name. If it doesn't exist, // operator[] will create a new one. @@ -718,6 +724,17 @@ public: columns_lastadded_); } + virtual void addRecordToNSEC3Zone( + const string (&columns)[ADD_NSEC3_COLUMN_COUNT]) + { + columns_lastadded_[ADD_NAME] = columns[ADD_NSEC3_HASH]; + columns_lastadded_[ADD_REV_NAME].clear(); + columns_lastadded_[ADD_TTL] = columns[ADD_NSEC3_TTL]; + columns_lastadded_[ADD_TYPE] = columns[ADD_NSEC3_TYPE]; + columns_lastadded_[ADD_SIGTYPE].clear(); + columns_lastadded_[ADD_RDATA] = columns[ADD_NSEC3_RDATA]; + } + // Helper predicate class used in deleteRecordInZone(). struct deleteMatch { deleteMatch(const string& type, const string& rdata) : @@ -2693,6 +2710,42 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) { this->checkLastAdded(rrset_added); } +// TODO: maybe we should share this stuff +// A helper callback of masterLoad() used in InMemoryZoneFinderTest. +void +setRRset(RRsetPtr rrset, vector::iterator& it) { + *(*it) = rrset; + ++it; +} + +ConstRRsetPtr +textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN()) { + stringstream ss(text_rrset); + RRsetPtr rrset; + vector rrsets; + rrsets.push_back(&rrset); + masterLoad(ss, Name::ROOT_NAME(), rrclass, + boost::bind(setRRset, _1, rrsets.begin())); + return (rrset); +} + +// Right now this only works for the mock DB, but the plan is to make it +// a TYPED_TEST to share the case with SQLite3 implementation, too. +TEST_F(MockDatabaseClientTest, addNSEC3ToZone) { + const char* const nsec3_hash = "1BB7SO0452U1QHL98UISNDD9218GELR5"; + const char* const nsec3_rdata = "1 1 12 AABBCCDD " + "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG NSEC3PARAM"; + + this->updater_ = this->client_->getUpdater(this->zname_, true); + this->updater_->addRRset( + *textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " + + string(nsec3_rdata))); + this->updater_->commit(); + const char* const rrset_added[] = { + nsec3_hash, "", "3600", "NSEC3", "", nsec3_rdata }; + this->checkLastAdded(rrset_added); +} + TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) { // Similar to the previous test, but not replacing the existing data. boost::shared_ptr finder(this->getFinder());