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
     /// \param name The name of zone apex to be traversed. It doesn't do
     ///     nearest match as findZone.
     ///     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.
     /// \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
         // This is here to both document the parameter in doxygen (therefore it
         // needs a name) and avoid unused parameter warning.
         // needs a name) and avoid unused parameter warning.
         static_cast<void>(name);
         static_cast<void>(name);
+        static_cast<void>(individual_rrs);
 
 
         isc_throw(isc::NotImplemented,
         isc_throw(isc::NotImplemented,
                   "Data source doesn't support iteration");
                   "Data source doesn't support iteration");

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

@@ -705,10 +705,11 @@ namespace {
 class DatabaseIterator : public ZoneIterator {
 class DatabaseIterator : public ZoneIterator {
 public:
 public:
     DatabaseIterator(const DatabaseAccessor::IteratorContextPtr& context,
     DatabaseIterator(const DatabaseAccessor::IteratorContextPtr& context,
-             const RRClass& rrclass) :
+             const RRClass& rrclass, bool individual_rrs) :
         context_(context),
         context_(context),
         class_(rrclass),
         class_(rrclass),
-        ready_(true)
+        ready_(true),
+        individual_rrs_(individual_rrs)
     {
     {
         // Prepare data for the next time
         // Prepare data for the next time
         getData();
         getData();
@@ -740,6 +741,9 @@ public:
             }
             }
             rrset->addRdata(rdata::createRdata(rtype, class_, rdata_));
             rrset->addRdata(rdata::createRdata(rtype, class_, rdata_));
             getData();
             getData();
+            if (individual_rrs_) {
+                break;
+            }
         }
         }
         LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
         LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
             arg(rrset->getName()).arg(rrset->getType());
             arg(rrset->getName()).arg(rrset->getType());
@@ -764,12 +768,16 @@ private:
     bool ready_, data_ready_;
     bool ready_, data_ready_;
     // Data of the next row
     // Data of the next row
     string name_, rtype_, rdata_, ttl_;
     string name_, rtype_, rdata_, ttl_;
+    // Whether to return individual RRsets
+    bool individual_rrs_;
 };
 };
 
 
 }
 }
 
 
 ZoneIteratorPtr
 ZoneIteratorPtr
-DatabaseClient::getIterator(const isc::dns::Name& name) const {
+DatabaseClient::getIterator(const isc::dns::Name& name,
+                            bool individual_rrs) const
+{
     // Get the zone
     // Get the zone
     std::pair<bool, int> zone(accessor_->getZone(name.toText()));
     std::pair<bool, int> zone(accessor_->getZone(name.toText()));
     if (!zone.first) {
     if (!zone.first) {
@@ -793,7 +801,8 @@ DatabaseClient::getIterator(const isc::dns::Name& name) const {
     // it each time)
     // it each time)
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE).
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE).
         arg(name);
         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
      * \exception Anything else the underlying DatabaseConnection might
      *     want to throw.
      *     want to throw.
      * \param name The origin of the zone to iterate.
      * \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)
      * \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
     /// This implementation internally clones the accessor from the one
     /// used in the client and starts a separate transaction using the cloned
     /// 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 DomainTree& tree_;
     const DomainNode* node_;
     const DomainNode* node_;
     bool ready_;
     bool ready_;
+    bool individual_rrs_;
 public:
 public:
-    MemoryIterator(const DomainTree& tree, const Name& origin) :
+    MemoryIterator(const DomainTree& tree, const Name& origin,
+                   bool individual_rrs) :
         tree_(tree),
         tree_(tree),
-        ready_(true)
+        ready_(true),
+        individual_rrs_(individual_rrs)
     {
     {
         // Find the first node (origin) and preserve the node chain for future
         // Find the first node (origin) and preserve the node chain for future
         // searches
         // searches
@@ -785,7 +788,7 @@ public:
 } // End of anonymous namespace
 } // End of anonymous namespace
 
 
 ZoneIteratorPtr
 ZoneIteratorPtr
-InMemoryClient::getIterator(const Name& name) const {
+InMemoryClient::getIterator(const Name& name, bool individual_rrs) const {
     ZoneTable::FindResult result(impl_->zone_table.findZone(name));
     ZoneTable::FindResult result(impl_->zone_table.findZone(name));
     if (result.code != result::SUCCESS) {
     if (result.code != result::SUCCESS) {
         isc_throw(DataSourceError, "No such zone: " + name.toText());
         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() +
         isc_throw(Unexpected, "The zone at " + name.toText() +
                   " is not InMemoryZoneFinder");
                   " is not InMemoryZoneFinder");
     }
     }
-    return (ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name)));
+    return (ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name,
+                                               individual_rrs)));
 }
 }
 
 
 ZoneUpdaterPtr
 ZoneUpdaterPtr

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

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

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

@@ -437,7 +437,6 @@ public:
         accessor_(accessor),
         accessor_(accessor),
         statement_(NULL),
         statement_(NULL),
         name_(name)
         name_(name)
-
     {
     {
         // We create the statement now and then just keep getting data from it
         // We create the statement now and then just keep getting data from it
         statement_ = prepare(accessor->dbparameters_->db_,
         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::TTL_COLUMN] = "300";
                     data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::2";
                     data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::2";
                     return (true);
                     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:
                 default:
                     ADD_FAILURE() <<
                     ADD_FAILURE() <<
                         "Request past the end of iterator context";
                         "Request past the end of iterator context";
-                case 5:
+                case 7:
                     return (false);
                     return (false);
             }
             }
         }
         }
@@ -992,7 +1004,6 @@ TYPED_TEST(DatabaseClientTest, iterator) {
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
     EXPECT_EQ(RRType::AAAA(), rrset->getType());
     EXPECT_EQ(RRType::AAAA(), rrset->getType());
     EXPECT_EQ(RRTTL(300), rrset->getTTL());
     EXPECT_EQ(RRTTL(300), rrset->getTTL());
-    EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset());
     rit = rrset->getRdataIterator();
     rit = rrset->getRdataIterator();
     ASSERT_FALSE(rit->isLast());
     ASSERT_FALSE(rit->isLast());
     EXPECT_EQ("2001:db8::1", rit->getCurrent().toText());
     EXPECT_EQ("2001:db8::1", rit->getCurrent().toText());
@@ -1001,6 +1012,23 @@ TYPED_TEST(DatabaseClientTest, iterator) {
     EXPECT_EQ("2001:db8::2", rit->getCurrent().toText());
     EXPECT_EQ("2001:db8::2", rit->getCurrent().toText());
     rit->next();
     rit->next();
     EXPECT_TRUE(rit->isLast());
     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
 // 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) {
 TYPED_TEST(DatabaseClientTest, find) {
     shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
     shared_ptr<DatabaseClient::Finder> finder(this->getFinder());