Commit c35f6b15 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

[1177] Implement previous name for SQLite3

parent 3f2864bf
......@@ -47,7 +47,9 @@ enum StatementID {
ADD_RECORD = 7,
DEL_RECORD = 8,
ITERATE = 9,
NUM_STATEMENTS = 10
FIND_PREVIOUS = 10,
FIND_PREVIOUS_WRAP = 11,
NUM_STATEMENTS = 12
};
const char* const text_statements[NUM_STATEMENTS] = {
......@@ -68,7 +70,13 @@ const char* const text_statements[NUM_STATEMENTS] = {
"DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
"AND rdtype=?3 AND rdata=?4",
"SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE
"WHERE zone_id = ?1 ORDER BY name, rdtype"
"WHERE zone_id = ?1 ORDER BY name, rdtype",
"SELECT name FROM records " // FIND_PREVIOUS
"WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
"rname < $2 ORDER BY rname DESC LIMIT 1", // FIND_PREVIOUS_WRAP
"SELECT name FROM records "
"WHERE zone_id = ?1 AND rdtype = 'NSEC' "
"ORDER BY rname DESC LIMIT 1"
};
struct SQLite3Parameters {
......@@ -391,6 +399,28 @@ SQLite3Accessor::getZone(const std::string& name) const {
return (std::pair<bool, int>(false, 0));
}
namespace {
// Conversion to plain char
const char*
convertToPlainCharInternal(const unsigned char* ucp, sqlite3 *db) {
if (ucp == NULL) {
// The field can really be NULL, in which case we return an
// empty string, or sqlite may have run out of memory, in
// which case we raise an error
if (sqlite3_errcode(db) == SQLITE_NOMEM) {
isc_throw(DataSourceError,
"Sqlite3 backend encountered a memory allocation "
"error in sqlite3_column_text()");
} else {
return ("");
}
}
const void* p = ucp;
return (static_cast<const char*>(p));
}
}
class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext {
public:
// Construct an iterator for all records. When constructed this
......@@ -501,21 +531,8 @@ private:
// In case sqlite3_column_text() returns NULL, we just make it an
// empty string, unless it was caused by a memory error
const char* convertToPlainChar(const unsigned char* ucp) {
if (ucp == NULL) {
// The field can really be NULL, in which case we return an
// empty string, or sqlite may have run out of memory, in
// which case we raise an error
if (sqlite3_errcode(accessor_->dbparameters_->db_)
== SQLITE_NOMEM) {
isc_throw(DataSourceError,
"Sqlite3 backend encountered a memory allocation "
"error in sqlite3_column_text()");
} else {
return ("");
}
}
const void* p = ucp;
return (static_cast<const char*>(p));
return (convertToPlainCharInternal(ucp,
accessor_->dbparameters_->db_));
}
const IteratorType iterator_type_;
......@@ -659,8 +676,68 @@ SQLite3Accessor::deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
}
std::string
SQLite3Accessor::findPreviousName(int , const std::string&) const {
return ("."); // TODO Test and implement
SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
const
{
sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS]);
int rc = sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1,
zone_id);
if (rc != SQLITE_OK) {
isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
" to SQL statement (find previous)");
}
rc = sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2,
rname.c_str(), -1, SQLITE_STATIC);
if (rc != SQLITE_OK) {
isc_throw(SQLite3Error, "Could not bind name " << rname <<
" to SQL statement (find previous)");
}
std::string result;
rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]);
if (rc == SQLITE_ROW) {
// We found it
result = convertToPlainCharInternal(sqlite3_column_text(dbparameters_->
statements_[FIND_PREVIOUS], 0), dbparameters_->db_);
}
sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
if (rc == SQLITE_DONE) {
// Nothing previous, wrap around (is it needed for anything?
// Well, just for completeness)
sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
int rc = sqlite3_bind_int(
dbparameters_->statements_[FIND_PREVIOUS_WRAP], 1, zone_id);
if (rc != SQLITE_OK) {
isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
" to SQL statement (find previous wrap)");
}
rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
if (rc == SQLITE_ROW) {
// We found it
result =
convertToPlainCharInternal(sqlite3_column_text(dbparameters_->
statements_[FIND_PREVIOUS_WRAP], 0), dbparameters_->db_);
}
sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
if (rc == SQLITE_DONE) {
// No NSEC records, this DB doesn't support DNSSEC
isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC");
}
}
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
// Some kind of error
isc_throw(SQLite3Error, "Could get data for previous name");
}
return (result);
}
}
......
......@@ -549,7 +549,8 @@ public:
} else if (id == 42) {
if (rname == "org.example.") {
return ("zzz.example.org.");
} else if (rname == "org.example.www2.") {
} else if (rname == "org.example.www2." ||
rname == "org.example.www1.") {
return ("www.example.org.");
} else {
isc_throw(isc::Unexpected, "Unexpected name");
......@@ -2228,13 +2229,19 @@ TYPED_TEST(DatabaseClientTest, previous) {
// Check wrap around
EXPECT_EQ(Name("zzz.example.org."),
finder->findPreviousName(Name("example.org.")));
// Check it doesn't crash or anything if the underlying DB throws
DataSourceClient::FindResult
zone(this->client_->findZone(Name("bad.example.org")));
finder = dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder);
EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")),
isc::NotImplemented);
// Check a name that doesn't exist there
EXPECT_EQ(Name("www.example.org."),
finder->findPreviousName(Name("www1.example.org.")));
if (this->is_mock_) { // We can't really force the DB to throw
// Check it doesn't crash or anything if the underlying DB throws
DataSourceClient::FindResult
zone(this->client_->findZone(Name("bad.example.org")));
finder =
dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder);
EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")),
isc::NotImplemented);
}
}
}
......@@ -354,6 +354,9 @@ TEST_F(SQLite3AccessorTest, getRecords) {
TEST_F(SQLite3AccessorTest, findPrevious) {
EXPECT_EQ("dns01.example.com.",
accessor->findPreviousName(1, "com.example.dns02."));
// A name that doesn't exist
EXPECT_EQ("dns01.example.com.",
accessor->findPreviousName(1, "com.example.dns01x."));
// Wrap around
EXPECT_EQ("www.example.com.",
accessor->findPreviousName(1, "com.example."));
......
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