Browse Source

[1217] add argument to getIterator to return each RR in its own RRset

and implement it for the sqlite3 backend
for the memory backend, it may not be necessary, but it might depend on how we add them
Jelte Jansen 13 years ago
parent
commit
e16e284794

+ 6 - 1
src/lib/datasrc/client.h

@@ -215,11 +215,16 @@ public:
     ///
     /// \param name The name of zone apex to be traversed. It doesn't do
     ///     nearest match as findZone.
+    /// \param individual_rrs If true, the iterator will return each RR found
+    ///        in the data as a separate RRset, instead of combining them into
+    ///        RRsets.
     /// \return Pointer to the iterator.
-    virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const {
+    virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
+                                        bool individual_rrs = false) const {
         // This is here to both document the parameter in doxygen (therefore it
         // needs a name) and avoid unused parameter warning.
         static_cast<void>(name);
+        static_cast<void>(individual_rrs);
 
         isc_throw(isc::NotImplemented,
                   "Data source doesn't support iteration");

+ 13 - 4
src/lib/datasrc/database.cc

@@ -705,10 +705,11 @@ namespace {
 class DatabaseIterator : public ZoneIterator {
 public:
     DatabaseIterator(const DatabaseAccessor::IteratorContextPtr& context,
-             const RRClass& rrclass) :
+             const RRClass& rrclass, bool individual_rrs) :
         context_(context),
         class_(rrclass),
-        ready_(true)
+        ready_(true),
+        individual_rrs_(individual_rrs)
     {
         // Prepare data for the next time
         getData();
@@ -740,6 +741,9 @@ public:
             }
             rrset->addRdata(rdata::createRdata(rtype, class_, rdata_));
             getData();
+            if (individual_rrs_) {
+                break;
+            }
         }
         LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
             arg(rrset->getName()).arg(rrset->getType());
@@ -764,12 +768,16 @@ private:
     bool ready_, data_ready_;
     // Data of the next row
     string name_, rtype_, rdata_, ttl_;
+    // Whether to return individual RRsets
+    bool individual_rrs_;
 };
 
 }
 
 ZoneIteratorPtr
-DatabaseClient::getIterator(const isc::dns::Name& name) const {
+DatabaseClient::getIterator(const isc::dns::Name& name,
+                            bool individual_rrs) const
+{
     // Get the zone
     std::pair<bool, int> zone(accessor_->getZone(name.toText()));
     if (!zone.first) {
@@ -793,7 +801,8 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const {
     // it each time)
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE).
         arg(name);
-    return (ZoneIteratorPtr(new DatabaseIterator(context, RRClass::IN())));
+    return (ZoneIteratorPtr(new DatabaseIterator(context, RRClass::IN(),
+                                                 individual_rrs)));
 }
 
 //

+ 5 - 1
src/lib/datasrc/database.h

@@ -741,9 +741,13 @@ public:
      * \exception Anything else the underlying DatabaseConnection might
      *     want to throw.
      * \param name The origin of the zone to iterate.
+     * \param individual_rrs If true, the iterator will return each RR found
+     *        in the data as a separate RRset, instead of combining them into
+     *        RRsets.
      * \return Shared pointer to the iterator (it will never be NULL)
      */
-    virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const;
+    virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
+                                        bool individual_rrs = false) const;
 
     /// This implementation internally clones the accessor from the one
     /// used in the client and starts a separate transaction using the cloned

+ 8 - 4
src/lib/datasrc/memory_datasrc.cc

@@ -730,10 +730,13 @@ private:
     const DomainTree& tree_;
     const DomainNode* node_;
     bool ready_;
+    bool individual_rrs_;
 public:
-    MemoryIterator(const DomainTree& tree, const Name& origin) :
+    MemoryIterator(const DomainTree& tree, const Name& origin,
+                   bool individual_rrs) :
         tree_(tree),
-        ready_(true)
+        ready_(true),
+        individual_rrs_(individual_rrs)
     {
         // Find the first node (origin) and preserve the node chain for future
         // searches
@@ -785,7 +788,7 @@ public:
 } // End of anonymous namespace
 
 ZoneIteratorPtr
-InMemoryClient::getIterator(const Name& name) const {
+InMemoryClient::getIterator(const Name& name, bool individual_rrs) const {
     ZoneTable::FindResult result(impl_->zone_table.findZone(name));
     if (result.code != result::SUCCESS) {
         isc_throw(DataSourceError, "No such zone: " + name.toText());
@@ -803,7 +806,8 @@ InMemoryClient::getIterator(const Name& name) const {
         isc_throw(Unexpected, "The zone at " + name.toText() +
                   " is not InMemoryZoneFinder");
     }
-    return (ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name)));
+    return (ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name,
+                                               individual_rrs)));
 }
 
 ZoneUpdaterPtr

+ 2 - 1
src/lib/datasrc/memory_datasrc.h

@@ -272,7 +272,8 @@ public:
     virtual FindResult findZone(const isc::dns::Name& name) const;
 
     /// \brief Implementation of the getIterator method
-    virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name) const;
+    virtual ZoneIteratorPtr getIterator(const isc::dns::Name& name,
+                                        bool individual_rrs = false) const;
 
     /// In-memory data source is read-only, so this derived method will
     /// result in a NotImplemented exception.

+ 0 - 1
src/lib/datasrc/sqlite3_accessor.cc

@@ -437,7 +437,6 @@ public:
         accessor_(accessor),
         statement_(NULL),
         name_(name)
-
     {
         // We create the statement now and then just keep getting data from it
         statement_ = prepare(accessor->dbparameters_->db_,

+ 81 - 2
src/lib/datasrc/tests/database_unittest.cc

@@ -397,10 +397,22 @@ private:
                     data[DatabaseAccessor::TTL_COLUMN] = "300";
                     data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::2";
                     return (true);
+                case 5:
+                    data[DatabaseAccessor::NAME_COLUMN] = "ttldiff.example.org";
+                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
+                    data[DatabaseAccessor::TTL_COLUMN] = "300";
+                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
+                    return (true);
+                case 6:
+                    data[DatabaseAccessor::NAME_COLUMN] = "ttldiff.example.org";
+                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
+                    data[DatabaseAccessor::TTL_COLUMN] = "600";
+                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2";
+                    return (true);
                 default:
                     ADD_FAILURE() <<
                         "Request past the end of iterator context";
-                case 5:
+                case 7:
                     return (false);
             }
         }
@@ -992,7 +1004,6 @@ TYPED_TEST(DatabaseClientTest, iterator) {
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
     EXPECT_EQ(RRType::AAAA(), rrset->getType());
     EXPECT_EQ(RRTTL(300), rrset->getTTL());
-    EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset());
     rit = rrset->getRdataIterator();
     ASSERT_FALSE(rit->isLast());
     EXPECT_EQ("2001:db8::1", rit->getCurrent().toText());
@@ -1001,6 +1012,23 @@ TYPED_TEST(DatabaseClientTest, iterator) {
     EXPECT_EQ("2001:db8::2", rit->getCurrent().toText());
     rit->next();
     EXPECT_TRUE(rit->isLast());
+
+    rrset = it->getNextRRset();
+    ASSERT_NE(ConstRRsetPtr(), rrset);
+    EXPECT_EQ(Name("ttldiff.example.org"), rrset->getName());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRTTL(300), rrset->getTTL());
+    rit = rrset->getRdataIterator();
+    ASSERT_FALSE(rit->isLast());
+    EXPECT_EQ("192.0.2.1", rit->getCurrent().toText());
+    rit->next();
+    ASSERT_FALSE(rit->isLast());
+    EXPECT_EQ("192.0.2.2", rit->getCurrent().toText());
+    rit->next();
+    EXPECT_TRUE(rit->isLast());
+
+    EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset());
 }
 
 // This has inconsistent TTL in the set (the rest, like nonsense in
@@ -1068,6 +1096,57 @@ doFindTest(ZoneFinder& finder,
     }
 }
 
+// When asking for an RRset where RRs somehow have different TTLs, it should 
+// convert to the lowest one.
+TEST_F(MockDatabaseClientTest, ttldiff) {
+    ZoneIteratorPtr it(this->client_->getIterator(Name("example.org")));
+    // Walk through the full iterator, we should see 1 rrset with name
+    // ttldiff1.example.org., and two rdatas. Same for ttldiff2
+    Name name("ttldiff.example.org.");
+    bool found = false;
+    //bool found2 = false;
+    ConstRRsetPtr rrset = it->getNextRRset();
+    while(rrset != ConstRRsetPtr()) {
+        if (rrset->getName() == name) {
+            ASSERT_FALSE(found);
+            ASSERT_EQ(2, rrset->getRdataCount());
+            ASSERT_EQ(RRTTL(300), rrset->getTTL());
+            found = true;
+        }
+        rrset = it->getNextRRset();
+    }
+    ASSERT_TRUE(found);
+}
+
+// Unless we ask for individual RRs in our iterator request. In that case
+// every RR should go into its own 'rrset'
+TEST_F(MockDatabaseClientTest, ttldiff_individual) {
+    ZoneIteratorPtr it(this->client_->getIterator(Name("example.org"), true));
+
+    // Walk through the full iterator, we should see 1 rrset with name
+    // ttldiff1.example.org., and two rdatas. Same for ttldiff2
+    Name name("ttldiff.example.org.");
+    int found1 = false;
+    int found2 = false;
+    ConstRRsetPtr rrset = it->getNextRRset();
+    while(rrset != ConstRRsetPtr()) {
+        if (rrset->getName() == name) {
+            ASSERT_EQ(1, rrset->getRdataCount());
+            // We should find 1 'rrset' with TTL 300 and one with TTL 600
+            if (rrset->getTTL() == RRTTL(300)) {
+                ASSERT_FALSE(found1);
+                found1 = true;
+            } else if (rrset->getTTL() == RRTTL(600)) {
+                ASSERT_FALSE(found2);
+                found2 = true;
+            }
+        }
+        rrset = it->getNextRRset();
+    }
+    ASSERT_TRUE(found1);
+    ASSERT_TRUE(found2);
+}
+
 TYPED_TEST(DatabaseClientTest, find) {
     shared_ptr<DatabaseClient::Finder> finder(this->getFinder());