Commit 5baa7aa7 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge branch #1065

Conflicts:
	src/lib/datasrc/database.cc
	src/lib/datasrc/database.h
	src/lib/datasrc/sqlite3_accessor.cc
	src/lib/datasrc/sqlite3_accessor.h
	src/lib/datasrc/tests/database_unittest.cc
	src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
parents 872bd575 39a0a5c6
......@@ -358,6 +358,22 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
result_rrset->getType() == isc::dns::RRType::CNAME()) {
result_status = CNAME;
}
if (!result_rrset && !records_found) {
// Request the context
DatabaseAccessor::IteratorContextPtr
context(database_->getRecords(name.toText(), zone_id_, true));
// It must not return NULL, that's a bug of the implementation
if (!context) {
isc_throw(isc::Unexpected, "Iterator context null at " +
name.toText());
}
std::string columns[DatabaseAccessor::COLUMN_COUNT];
if (context->getNext(columns)) {
records_found = true;
}
}
}
if (!result_rrset) {
......
......@@ -172,10 +172,14 @@ public:
*
* \param name The name to search for. This should be a FQDN.
* \param id The ID of the zone, returned from getZone().
* \param subdomains If set to true, match subdomains of name instead
* of name itself. It is used to find empty domains and match
* wildcards.
* \return Newly created iterator context. Must not be NULL.
*/
virtual IteratorContextPtr getRecords(const std::string& name,
int id) const = 0;
int id,
bool subdomains = false) const = 0;
/**
* \brief Creates an iterator context for the whole zone.
......
......@@ -28,23 +28,10 @@ struct SQLite3Parameters {
SQLite3Parameters() :
db_(NULL), version_(-1),
q_zone_(NULL)
/*q_record_(NULL), q_addrs_(NULL), q_referral_(NULL),
q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL),
q_prevnsec3_(NULL) */
{}
sqlite3* db_;
int version_;
sqlite3_stmt* q_zone_;
/*
TODO: Yet unneeded statements
sqlite3_stmt* q_record_;
sqlite3_stmt* q_addrs_;
sqlite3_stmt* q_referral_;
sqlite3_stmt* q_count_;
sqlite3_stmt* q_previous_;
sqlite3_stmt* q_nsec3_;
sqlite3_stmt* q_prevnsec3_;
*/
};
SQLite3Database::SQLite3Database(const std::string& filename,
......@@ -74,6 +61,8 @@ public:
if (params_.q_zone_ != NULL) {
sqlite3_finalize(params_.q_zone_);
}
// we do NOT finalize q_current_ - that is just a pointer to one of
// the other statements, not a separate one.
/*
if (params_.q_record_ != NULL) {
sqlite3_finalize(params_.q_record_);
......@@ -139,6 +128,9 @@ const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass =
const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata "
"FROM records WHERE zone_id=?1 AND name=?2";
const char* const q_any_sub_str = "SELECT rdtype, ttl, sigtype, rdata "
"FROM records WHERE zone_id=?1 AND name LIKE (\"%.\" || ?2)";
// note that the order of the SELECT values is specifically chosen to match
// the enum values in RecordColumns
const char* const q_iterate_str = "SELECT rdtype, ttl, sigtype, rdata, name FROM records "
......@@ -328,7 +320,6 @@ SQLite3Database::getZone(const isc::dns::Name& name) const {
return (std::pair<bool, int>(false, 0));
}
class SQLite3Database::Context : public DatabaseAccessor::IteratorContext {
public:
// Construct an iterator for all records. When constructed this
......@@ -347,14 +338,15 @@ public:
// Construct an iterator for records with a specific name. When constructed
// this way, the getNext() call will copy all fields except name
Context(const boost::shared_ptr<const SQLite3Database>& database, int id,
const std::string& name) :
const std::string& name, bool subdomains) :
iterator_type_(ITT_NAME),
database_(database),
statement_(NULL),
name_(name)
{
// We create the statement now and then just keep getting data from it
statement_ = prepare(database->dbparameters_->db_, q_any_str);
statement_ = prepare(database->dbparameters_->db_,
subdomains ? q_any_sub_str : q_any_str);
bindZoneId(id);
bindName(name_);
}
......@@ -459,8 +451,11 @@ private:
};
DatabaseAccessor::IteratorContextPtr
SQLite3Database::getRecords(const std::string& name, int id) const {
return (IteratorContextPtr(new Context(shared_from_this(), id, name)));
SQLite3Database::getRecords(const std::string& name, int id,
bool subdomains) const
{
return (IteratorContextPtr(new Context(shared_from_this(), id, name,
subdomains)));
}
DatabaseAccessor::IteratorContextPtr
......
......@@ -102,10 +102,12 @@ public:
*
* \param name the name to look up
* \param id the zone id, as returned by getZone()
* \param subdomains Match subdomains instead of the name.
* \return Iterator that contains all records with the given name
*/
virtual IteratorContextPtr getRecords(const std::string& name,
int id) const;
int id,
bool subdomains = false) const;
/** \brief Look up all resource records for a zone
*
......
......@@ -62,7 +62,9 @@ public:
return (database_name_);
}
virtual IteratorContextPtr getRecords(const std::string&, int) const {
virtual IteratorContextPtr getRecords(const std::string&, int, bool)
const
{
isc_throw(isc::NotImplemented,
"This database datasource can't be iterated");
};
......@@ -93,7 +95,7 @@ private:
class MockNameIteratorContext : public IteratorContext {
public:
MockNameIteratorContext(const MockAccessor& mock_accessor, int zone_id,
const std::string& name) :
const std::string& name, bool subdomains) :
searched_name_(name), cur_record_(0)
{
// 'hardcoded' names to trigger exceptions
......@@ -107,16 +109,32 @@ private:
throw std::exception();
}
// we're not aiming for efficiency in this test, simply
// copy the relevant vector from records
if (zone_id == 42) {
if (mock_accessor.records.count(searched_name_) > 0) {
cur_name = mock_accessor.records.find(searched_name_)->second;
} else {
if (subdomains) {
cur_name.clear();
// Just walk everything and check if it is a subdomain.
// If it is, just copy all data from there.
for (Domains::const_iterator
i(mock_accessor.records.begin());
i != mock_accessor.records.end(); ++ i) {
Name local(i->first);
if (local.compare(isc::dns::Name(name)).
getRelation() ==
isc::dns::NameComparisonResult::SUBDOMAIN) {
cur_name.insert(cur_name.end(), i->second.begin(),
i->second.end());
}
}
} else {
// we're not aiming for efficiency in this test, simply
// copy the relevant vector from records
if (mock_accessor.records.count(searched_name_) > 0) {
cur_name = mock_accessor.records.find(searched_name_)->
second;
} else {
cur_name.clear();
}
}
} else {
cur_name.clear();
}
}
......@@ -244,17 +262,27 @@ public:
}
}
virtual IteratorContextPtr getRecords(const std::string& name, int id) const {
virtual IteratorContextPtr getRecords(const std::string& name, int id,
bool subdomains) const
{
if (id == 42) {
return (IteratorContextPtr(new MockNameIteratorContext(*this, id, name)));
return (IteratorContextPtr(new MockNameIteratorContext(*this, id,
name, subdomains)));
} else {
isc_throw(isc::Unexpected, "Unknown zone ID");
}
}
private:
std::map<std::string, std::vector< std::vector<std::string> > > records;
typedef std::map<std::string, std::vector< std::vector<std::string> > >
Domains;
// used as internal index for getNextRecord()
size_t cur_record;
// used as temporary storage during the building of the fake data
Domains records;
// used as temporary storage after searchForRecord() and during
// getNextRecord() calls, as well as during the building of the
// fake data
std::vector< std::vector<std::string> > cur_name;
// Adds one record to the current name in the database
......@@ -428,12 +456,16 @@ private:
addRecord("RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
"20000201000000 12345 example.org. FAKEFAKEFAKE");
addCurName("example.org.");
// This is because of empty domain test
addRecord("A", "3600", "", "192.0.2.1");
addCurName("a.b.example.org.");
}
};
// This tests the default getRecords behaviour, throwing NotImplemented
TEST(DatabaseConnectionTest, getRecords) {
EXPECT_THROW(NopAccessor().getRecords(".", 1),
EXPECT_THROW(NopAccessor().getRecords(".", 1, false),
isc::NotImplemented);
}
......@@ -1103,6 +1135,17 @@ TEST_F(DatabaseClientTest, glueOK) {
ZoneFinder::FIND_GLUE_OK);
}
TEST_F(DatabaseClientTest, empty) {
shared_ptr<DatabaseClient::Finder> finder(getFinder());
// Check empty domain
// This domain doesn't exist, but a subdomain of it does.
// Therefore we should pretend the domain is there, but contains no RRsets
doFindTest(finder, isc::dns::Name("b.example.org."), isc::dns::RRType::A(),
isc::dns::RRType::A(), isc::dns::RRTTL(3600),
ZoneFinder::NXRRSET, expected_rdatas_, expected_sig_rdatas_);
}
TEST_F(DatabaseClientTest, getOrigin) {
DataSourceClient::FindResult zone(client_->findZone(Name("example.org")));
ASSERT_EQ(result::SUCCESS, zone.code);
......
......@@ -327,6 +327,16 @@ TEST_F(SQLite3Access, getRecords) {
// check that another getNext does not cause problems
EXPECT_FALSE(context->getNext(columns));
// Try searching for subdomain
// There's foo.bar.example.com in the data
context = db->getRecords("bar.example.com.", zone_id, true);
ASSERT_TRUE(context->getNext(columns));
checkRecordRow(columns, "A", "3600", "", "192.0.2.1", "");
EXPECT_FALSE(context->getNext(columns));
// But we shouldn't match mix.example.com here
context = db->getRecords("ix.example.com.", zone_id, true);
EXPECT_FALSE(context->getNext(columns));
}
} // end anonymous namespace
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