Commit 672f1297 authored by JINMEI Tatuya's avatar JINMEI Tatuya

[master] Merge branch 'trac1891'

parents f93cd951 bcb62dd7
......@@ -66,14 +66,16 @@ enum StatementID {
ITERATE = 9,
FIND_PREVIOUS = 10,
ADD_RECORD_DIFF = 11,
GET_RECORD_DIFF = 12, // This is temporary for testing "add diff"
LOW_DIFF_ID = 13,
HIGH_DIFF_ID = 14,
DIFF_RECS = 15,
NSEC3 = 16,
NSEC3_PREVIOUS = 17,
NSEC3_LAST = 18,
NUM_STATEMENTS = 19
LOW_DIFF_ID = 12,
HIGH_DIFF_ID = 13,
DIFF_RECS = 14,
NSEC3 = 15,
NSEC3_PREVIOUS = 16,
NSEC3_LAST = 17,
ADD_NSEC3_RECORD = 18,
DEL_ZONE_NSEC3_RECORDS = 19,
DEL_NSEC3_RECORD = 20,
NUM_STATEMENTS = 21
};
const char* const text_statements[NUM_STATEMENTS] = {
......@@ -117,8 +119,6 @@ const char* const text_statements[NUM_STATEMENTS] = {
"INSERT INTO diffs " // ADD_RECORD_DIFF
"(zone_id, version, operation, name, rrtype, ttl, rdata) "
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
"SELECT name, rrtype, ttl, rdata, version, operation " // GET_RECORD_DIFF
"FROM diffs WHERE zone_id = ?1 ORDER BY id, operation",
// Two statements to select the lowest ID and highest ID in a set of
// differences.
......@@ -135,19 +135,27 @@ const char* const text_statements[NUM_STATEMENTS] = {
"WHERE zone_id=?1 AND id>=?2 and id<=?3 "
"ORDER BY id ASC",
// Query to get the NSEC3 records
// NSEC3: Query to get the NSEC3 records
//
// The "1" in SELECT is for positioning the rdata column to the
// expected position, so we can reuse the same code as for other
// lookups.
"SELECT rdtype, ttl, 1, rdata FROM nsec3 WHERE zone_id=?1 AND "
"hash=?2",
// For getting the previous NSEC3 hash
// NSEC3_PREVIOUS: For getting the previous NSEC3 hash
"SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 AND hash < ?2 "
"ORDER BY hash DESC LIMIT 1",
// And for wrap-around
// NSEC3_LAST: And for wrap-around
"SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 "
"ORDER BY hash DESC LIMIT 1",
// ADD_NSEC3_RECORD: Add NSEC3-related (NSEC3 or NSEC3-covering RRSIG) RR
"INSERT INTO nsec3 (zone_id, hash, owner, ttl, rdtype, rdata) "
"VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
// DEL_ZONE_NSEC3_RECORDS: delete all NSEC3-related records from the zone
"DELETE FROM nsec3 WHERE zone_id=?1",
// DEL_NSEC3_RECORD: delete specified NSEC3-related records
"DELETE FROM nsec3 WHERE zone_id=?1 AND hash=?2 "
"AND rdtype=?3 AND rdata=?4"
};
struct SQLite3Parameters {
......@@ -196,6 +204,7 @@ struct SQLite3Parameters {
bool in_transaction; // whether or not a transaction has been started
bool updating_zone; // whether or not updating the zone
int updated_zone_id; // valid only when in_transaction is true
string updated_zone_origin_; // ditto, and only needed to handle NSEC3s
private:
// statements_ are private and must be accessed via getStatement() outside
// of this structure.
......@@ -210,6 +219,10 @@ private:
// statement, which is completed with a single "step" (normally within a
// single call to an SQLite3Database method). In particular, it cannot be
// used for "SELECT" variants, which generally expect multiple matching rows.
//
// The bindXXX methods are straightforward wrappers for the corresponding
// sqlite3_bind_xxx functions that make bindings with the given parameters
// on the statement maintained in this class.
class StatementProcessor {
public:
// desc will be used on failure in the what() message of the resulting
......@@ -226,6 +239,33 @@ public:
sqlite3_reset(stmt_);
}
void bindInt(int index, int val) {
if (sqlite3_bind_int(stmt_, index, val) != SQLITE_OK) {
isc_throw(DataSourceError,
"failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_.db_));
}
}
void bindInt64(int index, sqlite3_int64 val) {
if (sqlite3_bind_int64(stmt_, index, val) != SQLITE_OK) {
isc_throw(DataSourceError,
"failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_.db_));
}
}
// For simplicity, we assume val is a NULL-terminated string, and the
// entire non NUL characters are to be bound. The destructor parameter
// is normally either SQLITE_TRANSIENT or SQLITE_STATIC.
void bindText(int index, const char* val, void(*destructor)(void*)) {
if (sqlite3_bind_text(stmt_, index, val, -1, destructor)
!= SQLITE_OK) {
isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_.db_));
}
}
void exec() {
if (sqlite3_step(stmt_) != SQLITE_DONE) {
sqlite3_reset(stmt_);
......@@ -1021,19 +1061,22 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
"start an SQLite3 update transaction").exec();
if (replace) {
// First, clear all current data from tables.
typedef pair<StatementID, const char* const> StatementSpec;
const StatementSpec delzone_stmts[] =
{ StatementSpec(DEL_ZONE_RECORDS, "delete zone records"),
StatementSpec(DEL_ZONE_NSEC3_RECORDS,
"delete zone NSEC3 records") };
try {
StatementProcessor delzone_exec(*dbparameters_, DEL_ZONE_RECORDS,
"delete zone records");
sqlite3_stmt* stmt = dbparameters_->getStatement(DEL_ZONE_RECORDS);
sqlite3_clear_bindings(stmt);
if (sqlite3_bind_int(stmt, 1, zone_info.second) != SQLITE_OK) {
isc_throw(DataSourceError,
"failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_->db_));
for (size_t i = 0;
i < sizeof(delzone_stmts) / sizeof(delzone_stmts[0]);
++i) {
StatementProcessor delzone_proc(*dbparameters_,
delzone_stmts[i].first,
delzone_stmts[i].second);
delzone_proc.bindInt(1, zone_info.second);
delzone_proc.exec();
}
delzone_exec.exec();
} catch (const DataSourceError&) {
// Once we start a transaction, if something unexpected happens
// we need to rollback the transaction so that a subsequent update
......@@ -1047,6 +1090,7 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
dbparameters_->in_transaction = true;
dbparameters_->updating_zone = true;
dbparameters_->updated_zone_id = zone_info.second;
dbparameters_->updated_zone_origin_ = zone_name;
return (zone_info);
}
......@@ -1073,7 +1117,9 @@ SQLite3Accessor::commit() {
StatementProcessor(*dbparameters_, COMMIT,
"commit an SQLite3 transaction").exec();
dbparameters_->in_transaction = false;
dbparameters_->updating_zone = false;
dbparameters_->updated_zone_id = -1;
dbparameters_->updated_zone_origin_.clear();
}
void
......@@ -1086,7 +1132,9 @@ SQLite3Accessor::rollback() {
StatementProcessor(*dbparameters_, ROLLBACK,
"rollback an SQLite3 transaction").exec();
dbparameters_->in_transaction = false;
dbparameters_->updating_zone = false;
dbparameters_->updated_zone_id = -1;
dbparameters_->updated_zone_origin_.clear();
}
namespace {
......@@ -1096,29 +1144,19 @@ void
doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id,
COLUMNS_TYPE update_params, const char* exec_desc)
{
sqlite3_stmt* const stmt = dbparams.getStatement(stmt_id);
StatementProcessor executer(dbparams, stmt_id, exec_desc);
StatementProcessor proc(dbparams, stmt_id, exec_desc);
int param_id = 0;
if (sqlite3_bind_int(stmt, ++param_id, dbparams.updated_zone_id)
!= SQLITE_OK) {
isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparams.db_));
}
proc.bindInt(++param_id, dbparams.updated_zone_id);
const size_t column_count =
sizeof(update_params) / sizeof(update_params[0]);
for (int i = 0; i < column_count; ++i) {
// The old sqlite3 data source API assumes NULL for an empty column.
// We need to provide compatibility at least for now.
if (sqlite3_bind_text(stmt, ++param_id,
update_params[i].empty() ? NULL :
update_params[i].c_str(),
-1, SQLITE_TRANSIENT) != SQLITE_OK) {
isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparams.db_));
}
proc.bindText(++param_id, update_params[i].empty() ? NULL :
update_params[i].c_str(), SQLITE_TRANSIENT);
}
executer.exec();
proc.exec();
}
}
......@@ -1128,15 +1166,32 @@ SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
isc_throw(DataSourceError, "adding record to SQLite3 "
"data source without transaction");
}
doUpdate<const string (&)[DatabaseAccessor::ADD_COLUMN_COUNT]>(
doUpdate<const string (&)[ADD_COLUMN_COUNT]>(
*dbparameters_, ADD_RECORD, columns, "add record to zone");
}
void
SQLite3Accessor::addNSEC3RecordToZone(
const string (&/*columns*/)[ADD_NSEC3_COLUMN_COUNT])
const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
{
isc_throw(NotImplemented, "not yet implemented");
if (!dbparameters_->updating_zone) {
isc_throw(DataSourceError, "adding NSEC3-related record to SQLite3 "
"data source without transaction");
}
// XXX: the current implementation of SQLite3 schema requires the 'owner'
// column, and the current implementation of getAllRecords() relies on it,
// while the addNSEC3RecordToZone interface doesn't provide it explicitly.
// We should revisit it at the design level, but for now we internally
// convert the given parameter to satisfy the internal requirements.
const string sqlite3_columns[ADD_NSEC3_COLUMN_COUNT + 1] =
{ columns[ADD_NSEC3_HASH],
columns[ADD_NSEC3_HASH] + "." + dbparameters_->updated_zone_origin_,
columns[ADD_NSEC3_TTL],
columns[ADD_NSEC3_TYPE], columns[ADD_NSEC3_RDATA] };
doUpdate<const string (&)[ADD_NSEC3_COLUMN_COUNT + 1]>(
*dbparameters_, ADD_NSEC3_RECORD, sqlite3_columns,
"add NSEC3 record to zone");
}
void
......@@ -1145,15 +1200,21 @@ SQLite3Accessor::deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
isc_throw(DataSourceError, "deleting record in SQLite3 "
"data source without transaction");
}
doUpdate<const string (&)[DatabaseAccessor::DEL_PARAM_COUNT]>(
doUpdate<const string (&)[DEL_PARAM_COUNT]>(
*dbparameters_, DEL_RECORD, params, "delete record from zone");
}
void
SQLite3Accessor::deleteNSEC3RecordInZone(
const string (&/*params*/)[DEL_PARAM_COUNT])
const string (&params)[DEL_PARAM_COUNT])
{
isc_throw(NotImplemented, "not yet implemented");
if (!dbparameters_->updating_zone) {
isc_throw(DataSourceError, "deleting NSEC3-related record in SQLite3 "
"data source without transaction");
}
doUpdate<const string (&)[DEL_PARAM_COUNT]>(
*dbparameters_, DEL_NSEC3_RECORD, params,
"delete NSEC3 record from zone");
}
void
......@@ -1171,33 +1232,16 @@ SQLite3Accessor::addRecordDiff(int zone_id, uint32_t serial,
<< dbparameters_->updated_zone_id);
}
sqlite3_stmt* const stmt = dbparameters_->getStatement(ADD_RECORD_DIFF);
StatementProcessor executer(*dbparameters_, ADD_RECORD_DIFF,
"add record diff");
StatementProcessor proc(*dbparameters_, ADD_RECORD_DIFF,
"add record diff");
int param_id = 0;
if (sqlite3_bind_int(stmt, ++param_id, zone_id)
!= SQLITE_OK) {
isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_->db_));
}
if (sqlite3_bind_int64(stmt, ++param_id, serial)
!= SQLITE_OK) {
isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_->db_));
}
if (sqlite3_bind_int(stmt, ++param_id, operation)
!= SQLITE_OK) {
isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_->db_));
}
proc.bindInt(++param_id, zone_id);
proc.bindInt64(++param_id, serial);
proc.bindInt(++param_id, operation);
for (int i = 0; i < DIFF_PARAM_COUNT; ++i) {
if (sqlite3_bind_text(stmt, ++param_id, params[i].c_str(),
-1, SQLITE_TRANSIENT) != SQLITE_OK) {
isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
sqlite3_errmsg(dbparameters_->db_));
}
proc.bindText(++param_id, params[i].c_str(), SQLITE_TRANSIENT);
}
executer.exec();
proc.exec();
}
std::string
......
......@@ -3002,10 +3002,9 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) {
this->checkLastAdded(rrset_added);
}
// Below we define a set of NSEC3 update tests. Right now this only works
// for the mock DB, but the plan is to make it a TYPED_TEST to share the case
// with SQLite3 implementation, too.
//
// Below we define a set of NSEC3 update tests.
//
// Commonly used data for NSEC3 update tests below.
const char* const nsec3_hash = "1BB7SO0452U1QHL98UISNDD9218GELR5";
const char* const nsec3_rdata = "1 1 12 AABBCCDD "
......@@ -3045,7 +3044,7 @@ nsec3Check(const vector<ConstRRsetPtr>& expected_rrsets,
actual_rrsets.begin(), actual_rrsets.end());
}
TEST_F(MockDatabaseClientTest, addDeleteNSEC3InZone) {
TYPED_TEST(DatabaseClientTest, addDeleteNSEC3InZone) {
// Add one NSEC3 RR to the zone, delete it, and add another one.
this->updater_ = this->client_->getUpdater(this->zname_, true);
const ConstRRsetPtr nsec3_rrset =
......@@ -3066,7 +3065,7 @@ TEST_F(MockDatabaseClientTest, addDeleteNSEC3InZone) {
*this->current_accessor_);
}
TEST_F(MockDatabaseClientTest, addDeleteNSEC3AndRRSIGToZone) {
TYPED_TEST(DatabaseClientTest, addDeleteNSEC3AndRRSIGToZone) {
// Add one NSEC3 RR and its RRSIG to the zone, delete the RRSIG and add
// a new one.
this->updater_ = this->client_->getUpdater(this->zname_, true);
......@@ -3639,10 +3638,7 @@ TYPED_TEST(DatabaseClientTest, journal) {
this->checkJournal(expected);
}
// At the moment this only works for the mock accessor. Once sqlite3
// accessor supports updating NSEC3, this should be merged to the previous
// test
TEST_F(MockDatabaseClientTest, journalForNSEC3) {
TYPED_TEST(DatabaseClientTest, journalForNSEC3) {
// Similar to the previous test, but adding/deleting NSEC3 RRs, just to
// confirm that NSEC3 is not special for managing diffs.
const ConstRRsetPtr nsec3_rrset =
......
......@@ -28,6 +28,19 @@ namespace isc {
namespace datasrc {
namespace test {
// Constant data definitions
const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
"2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
"20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
class TestNSEC3HashCreator::TestNSEC3Hash : public NSEC3Hash {
private:
typedef map<Name, string> NSEC3HashMap;
......
......@@ -31,26 +31,24 @@ namespace test {
//
// Commonly used NSEC3 suffix. It's incorrect to use it for all NSEC3s, but
// doesn't matter for the purpose of our tests.
const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
"2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
extern const char* const nsec3_common;
// Likewise, common RRSIG suffix for NSEC3s.
const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
"20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
extern const char* const nsec3_rrsig_common;
// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
// object.
//
// For apex (example.org)
const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
extern const char* const apex_hash;
extern const char* const apex_hash_lower;
// For ns1.example.org
const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
extern const char* const ns1_hash;
// For w.example.org
const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
extern const char* const w_hash;
// For x.y.w.example.org (lower-cased)
const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
extern const char* const xyw_hash;
// For zzz.example.org.
const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
extern const char* const zzz_hash;
// A simple faked NSEC3 hash calculator with a dedicated creator for it.
//
......@@ -83,4 +81,8 @@ performNSEC3Test(ZoneFinder &finder);
}
}
#endif
#endif // FAKED_NSEC3_H
// Local Variables:
// mode: c++
// End:
......@@ -12,8 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <algorithm>
#include <vector>
#include "faked_nsec3.h"
#include <datasrc/sqlite3_accessor.h>
......@@ -21,14 +20,20 @@
#include <dns/rrclass.h>
#include <sqlite3.h>
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <algorithm>
#include <vector>
#include <fstream>
#include <sqlite3.h>
using namespace std;
using namespace isc::datasrc;
using namespace isc::datasrc::test;
using boost::lexical_cast;
using isc::data::ConstElementPtr;
using isc::data::Element;
......@@ -463,11 +468,11 @@ TEST(SQLite3Open, getDBNameExampleROOT) {
// Simple function to match records
void
checkRecordRow(const std::string columns[],
const std::string& field0,
const std::string& field1,
const std::string& field2,
const std::string& field3,
const std::string& field4)
const std::string& field0, // for type
const std::string& field1, // for TTL
const std::string& field2, // for "sigtype"
const std::string& field3, // for rdata
const std::string& field4) // for name
{
EXPECT_EQ(field0, columns[DatabaseAccessor::TYPE_COLUMN]);
EXPECT_EQ(field1, columns[DatabaseAccessor::TTL_COLUMN]);
......@@ -736,6 +741,23 @@ const char* const deleted_data[] = {
// Existing data to be removed commonly used by some of the tests below
"foo.bar.example.com.", "A", "192.0.2.1"
};
const char* const nsec3_data[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT] = {
// example NSEC3 parameters. Using "apex_hash" just as a convenient
// shortcut; otherwise it has nothing to do with the zone apex for the
// purpose of this test.
apex_hash, "3600", "NSEC3",
"1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA"
};
const char* const nsec3_sig_data[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT] = {
ns1_hash, "3600", "RRSIG",
"NSEC3 5 3 3600 20000101000000 20000201000000 12345 "
"example.com. FAKEFAKEFAKE"
};
const char* const nsec3_deleted_data[] = {
// Delete parameters for nsec3_data
apex_hash, nsec3_data[DatabaseAccessor::ADD_NSEC3_TYPE],
nsec3_data[DatabaseAccessor::ADD_NSEC3_RDATA]
};
class SQLite3Update : public SQLite3AccessorTest {
protected:
......@@ -762,6 +784,7 @@ protected:
int zone_id;
std::string get_columns[DatabaseAccessor::COLUMN_COUNT];
std::string add_columns[DatabaseAccessor::ADD_COLUMN_COUNT];
std::string add_nsec3_columns[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT];
std::string del_params[DatabaseAccessor::DEL_PARAM_COUNT];
std::string diff_params[DatabaseAccessor::DIFF_PARAM_COUNT];
......@@ -789,6 +812,28 @@ checkRecords(SQLite3Accessor& accessor, int zone_id, const std::string& name,
EXPECT_TRUE(it == expected_rows.end());
}
// Similar to the previous one, but checking transactions on the nsec3 table.
void
checkNSEC3Records(SQLite3Accessor& accessor, int zone_id,
const std::string& hash,
vector<const char* const*> expected_rows)
{
DatabaseAccessor::IteratorContextPtr iterator =
accessor.getNSEC3Records(hash, zone_id);
std::string columns[DatabaseAccessor::COLUMN_COUNT];
vector<const char* const*>::const_iterator it = expected_rows.begin();
while (iterator->getNext(columns)) {
ASSERT_TRUE(it != expected_rows.end());
checkRecordRow(columns, (*it)[DatabaseAccessor::ADD_NSEC3_TYPE],
(*it)[DatabaseAccessor::ADD_NSEC3_TTL],
"", // sigtype, should always be empty
(*it)[DatabaseAccessor::ADD_NSEC3_RDATA],
""); // name, always empty
++it;
}
EXPECT_TRUE(it == expected_rows.end());
}
TEST_F(SQLite3Update, emptyUpdate) {
// If we do nothing between start and commit, the zone content
// should be intact.
......@@ -811,6 +856,26 @@ TEST_F(SQLite3Update, flushZone) {
checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
}
TEST_F(SQLite3Update, flushZoneWithNSEC3) {
// Similar to the previous case, but make sure the separate nsec3 table
// is also cleared. We first need to add something to the table.
zone_id = accessor->startUpdateZone("example.com.", false).second;
copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
add_nsec3_columns);
accessor->addNSEC3RecordToZone(add_nsec3_columns);
accessor->commit();
// Confirm it surely exists.
expected_stored.clear();
expected_stored.push_back(nsec3_data);
checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
// Then starting zone replacement. the NSEC3 record should have been
// removed.
zone_id = accessor->startUpdateZone("example.com.", true).second;
checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
}
TEST_F(SQLite3Update, readWhileUpdate) {
zone_id = accessor->startUpdateZone("example.com.", true).second;
checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
......@@ -924,6 +989,62 @@ TEST_F(SQLite3Update, addRecord) {
checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored);
}
TEST_F(SQLite3Update, addNSEC3Record) {
// Similar to the previous test, but for NSEC3-related records
checkRecords(*accessor, zone_id, apex_hash, empty_stored);
checkRecords(*accessor, zone_id, ns1_hash, empty_stored);
zone_id = accessor->startUpdateZone("example.com.", false).second;
// Add an NSEC3
copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
add_nsec3_columns);
accessor->addNSEC3RecordToZone(add_nsec3_columns);
// Add an RRSIG for NSEC3
copy(nsec3_sig_data,
nsec3_sig_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
add_nsec3_columns);
accessor->addNSEC3RecordToZone(add_nsec3_columns);
// Check the stored data, before and after commit().
for (size_t i = 0; i < 2; ++i) {
expected_stored.clear();
expected_stored.push_back(nsec3_data);
checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
expected_stored.clear();
expected_stored.push_back(nsec3_sig_data);
checkNSEC3Records(*accessor, zone_id, ns1_hash, expected_stored);
if (i == 0) { // make sure commit() happens only once
accessor->commit();
}
}
}
TEST_F(SQLite3Update, nsec3IteratorOnAdd) {
// This test checks if an added NSEC3 record will appear in the iterator
// result, meeting the expectation of addNSEC3RecordToZone.
// Specifically, it checks if the name column is filled with the complete
// owner name.
// We'll replace the zone, and add one NSEC3 record, and only that one.
zone_id = accessor->startUpdateZone("example.com.", true).second;
copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
add_nsec3_columns);
accessor->addNSEC3RecordToZone(add_nsec3_columns);
accessor->commit();
// the zone should contain only one record we just added.
DatabaseAccessor::IteratorContextPtr context =
accessor->getAllRecords(zone_id);
string data[DatabaseAccessor::COLUMN_COUNT];
EXPECT_TRUE(context->getNext(data));
EXPECT_EQ(string(apex_hash) + ".example.com.",
data[DatabaseAccessor::NAME_COLUMN]);
EXPECT_FALSE(context->getNext(data));
}
TEST_F(SQLite3Update, addThenRollback) {
zone_id = accessor->startUpdateZone("example.com.", false).second;
copy(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT,
......@@ -934,7 +1055,11 @@ TEST_F(SQLite3Update, addThenRollback) {
expected_stored.push_back(new_data);
checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored);
// Rollback the transaction, and confirm the zone reverts to the previous
// state. We also start another update to check if the accessor can be
// reused for a new update after rollback.
accessor->rollback();
zone_id = accessor->startUpdateZone("example.com.", false).second;
checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored);
}
......@@ -960,6 +1085,12 @@ TEST_F(SQLite3Update, duplicateAdd) {
TEST_F(SQLite3Update, invalidAdd) {
// An attempt of add before an explicit start of transaction
EXPECT_THROW(accessor->addRecordToZone(add_columns), DataSourceError);