Commit e16e2847 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[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
parent d07206bb
......@@ -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");
......
......@@ -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)));
}
//
......
......@@ -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
......
......@@ -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
......
......@@ -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.
......
......@@ -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_,
......
......@@ -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());
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment