BIND 10 trac1062, updated. 5951ef6faaffcff62d9a9963260a932666e3decb [1062] logging in database.cc
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Aug 10 14:04:21 UTC 2011
The branch, trac1062 has been updated
via 5951ef6faaffcff62d9a9963260a932666e3decb (commit)
via f82dc7b09f470f79ed2bf099216fa64c76528d3b (commit)
from 46b961d69aff3a2e4d1cb7f3d0910bfcc66d1e19 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 5951ef6faaffcff62d9a9963260a932666e3decb
Author: Jelte Jansen <jelte at isc.org>
Date: Wed Aug 10 13:36:01 2011 +0200
[1062] logging in database.cc
commit f82dc7b09f470f79ed2bf099216fa64c76528d3b
Author: Jelte Jansen <jelte at isc.org>
Date: Tue Aug 9 11:19:13 2011 +0200
[1062] addressed review comments
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/database.cc | 272 +++++++++++---------
src/lib/datasrc/database.h | 55 ++++-
src/lib/datasrc/datasrc_messages.mes | 35 +++
src/lib/datasrc/sqlite3_connection.cc | 76 ++++--
src/lib/datasrc/sqlite3_connection.h | 26 ++-
src/lib/datasrc/tests/database_unittest.cc | 215 +++++++++++++++-
.../datasrc/tests/sqlite3_connection_unittest.cc | 152 +++++++++---
7 files changed, 634 insertions(+), 197 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index ee25788..b13f3e9 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -23,6 +23,7 @@
#include <dns/rdataclass.h>
#include <datasrc/data_source.h>
+#include <datasrc/logger.h>
#include <boost/foreach.hpp>
@@ -71,93 +72,83 @@ DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseConnection>
{ }
namespace {
- // Adds the given Rdata to the given RRset
- // If the rrset is an empty pointer, a new one is
- // created with the given name, class, type and ttl
- // The type is checked if the rrset exists, but the
- // name is not.
- //
- // Then adds the given rdata to the set
- //
- // Raises a DataSourceError if the type does not
- // match, or if the given rdata string does not
- // parse correctly for the given type and class
- void addOrCreate(isc::dns::RRsetPtr& rrset,
- const isc::dns::Name& name,
- const isc::dns::RRClass& cls,
- const isc::dns::RRType& type,
- const isc::dns::RRTTL& ttl,
- const std::string& rdata_str)
- {
- if (!rrset) {
- rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
- } else {
- if (ttl < rrset->getTTL()) {
- rrset->setTTL(ttl);
- }
- // make sure the type is correct
- if (type != rrset->getType()) {
- isc_throw(DataSourceError,
- "attempt to add multiple types to RRset in find()");
- }
- }
- if (rdata_str != "") {
- try {
- rrset->addRdata(isc::dns::rdata::createRdata(type, cls,
- rdata_str));
- } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
- // at this point, rrset may have been initialised for no reason,
- // and won't be used. But the caller would drop the shared_ptr
- // on such an error anyway, so we don't care.
- isc_throw(DataSourceError,
- "bad rdata in database for " << name.toText() << " "
- << type.toText() << " " << ivrt.what());
- }
+// Adds the given Rdata to the given RRset
+// If the rrset is an empty pointer, a new one is
+// created with the given name, class, type and ttl
+// The type is checked if the rrset exists, but the
+// name is not.
+//
+// Then adds the given rdata to the set
+//
+// Raises a DataSourceError if the type does not
+// match, or if the given rdata string does not
+// parse correctly for the given type and class
+void addOrCreate(isc::dns::RRsetPtr& rrset,
+ const isc::dns::Name& name,
+ const isc::dns::RRClass& cls,
+ const isc::dns::RRType& type,
+ const isc::dns::RRTTL& ttl,
+ const std::string& rdata_str)
+{
+ if (!rrset) {
+ rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
+ } else {
+ if (ttl < rrset->getTTL()) {
+ rrset->setTTL(ttl);
}
+ // This is a check to make sure find() is not messing things up
+ assert(type == rrset->getType());
}
+ try {
+ rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str));
+ } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
+ // at this point, rrset may have been initialised for no reason,
+ // and won't be used. But the caller would drop the shared_ptr
+ // on such an error anyway, so we don't care.
+ isc_throw(DataSourceError,
+ "bad rdata in database for " << name << " "
+ << type << ": " << ivrt.what());
+ }
+}
- // This class keeps a short-lived store of RRSIG records encountered
- // during a call to find(). If the backend happens to return signatures
- // before the actual data, we might not know which signatures we will need
- // So if they may be relevant, we store the in this class.
- //
- // (If this class seems useful in other places, we might want to move
- // it to util. That would also provide an opportunity to add unit tests)
- class RRsigStore {
- public:
- // Adds the given signature Rdata to the store
- // The signature rdata MUST be of the RRSIG rdata type
- // (the caller must make sure of this)
- void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
- const isc::dns::RRType& type_covered =
- static_cast<isc::dns::rdata::generic::RRSIG*>(
- sig_rdata.get())->typeCovered();
- if (!haveSigsFor(type_covered)) {
- sigs[type_covered] = std::vector<isc::dns::rdata::RdataPtr>();
- }
- sigs.find(type_covered)->second.push_back(sig_rdata);
- }
-
- // Returns true if this store contains signatures covering the
- // given type
- bool haveSigsFor(isc::dns::RRType type) const {
- return (sigs.count(type) > 0);
- }
+// This class keeps a short-lived store of RRSIG records encountered
+// during a call to find(). If the backend happens to return signatures
+// before the actual data, we might not know which signatures we will need
+// So if they may be relevant, we store the in this class.
+//
+// (If this class seems useful in other places, we might want to move
+// it to util. That would also provide an opportunity to add unit tests)
+class RRsigStore {
+public:
+ // Adds the given signature Rdata to the store
+ // The signature rdata MUST be of the RRSIG rdata type
+ // (the caller must make sure of this).
+ // NOTE: if we move this class to a public namespace,
+ // we should add a type_covered argument, so as not
+ // to have to do this cast here.
+ void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
+ const isc::dns::RRType& type_covered =
+ static_cast<isc::dns::rdata::generic::RRSIG*>(
+ sig_rdata.get())->typeCovered();
+ sigs[type_covered].push_back(sig_rdata);
+ }
- // If the store contains signatures for the type of the given
- // rrset, they are appended to it.
- void appendSignatures(isc::dns::RRsetPtr& rrset) const {
- if (haveSigsFor(rrset->getType())) {
- BOOST_FOREACH(isc::dns::rdata::RdataPtr sig,
- sigs.find(rrset->getType())->second) {
- rrset->addRRsig(sig);
- }
+ // If the store contains signatures for the type of the given
+ // rrset, they are appended to it.
+ void appendSignatures(isc::dns::RRsetPtr& rrset) const {
+ std::map<isc::dns::RRType,
+ std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
+ found = sigs.find(rrset->getType());
+ if (found != sigs.end()) {
+ BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
+ rrset->addRRsig(sig);
}
}
+ }
- private:
- std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
- };
+private:
+ std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
+};
}
@@ -173,66 +164,101 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
isc::dns::RRsetPtr result_rrset;
ZoneFinder::Result result_status = SUCCESS;
RRsigStore sig_store;
+ logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS).arg(name).arg(type);
- connection_->searchForRecords(zone_id_, name.toText());
+ try {
+ connection_->searchForRecords(zone_id_, name.toText());
- std::vector<std::string> columns;
- while (connection_->getNextRecord(columns)) {
- if (!records_found) {
- records_found = true;
- }
+ std::string columns[DatabaseConnection::RecordColumnCount];
+ while (connection_->getNextRecord(columns,
+ DatabaseConnection::RecordColumnCount)) {
+ if (!records_found) {
+ records_found = true;
+ }
- if (columns.size() != 4) {
- isc_throw(DataSourceError, "Datasource backend did not return 4 "
- "columns in getNextRecord()");
- }
+ try {
+ const isc::dns::RRType cur_type(columns[DatabaseConnection::TYPE_COLUMN]);
+ const isc::dns::RRTTL cur_ttl(columns[DatabaseConnection::TTL_COLUMN]);
+ // Ths sigtype column was an optimization for finding the relevant
+ // RRSIG RRs for a lookup. Currently this column is not used in this
+ // revised datasource implementation. We should either start using it
+ // again, or remove it from use completely (i.e. also remove it from
+ // the schema and the backend implementation).
+ // Note that because we don't use it now, we also won't notice it if
+ // the value is wrong (i.e. if the sigtype column contains an rrtype
+ // that is different from the actual value of the 'type covered' field
+ // in the RRSIG Rdata).
+ //cur_sigtype(columns[SIGTYPE_COLUMN]);
- try {
- const isc::dns::RRType cur_type(columns[0]);
- const isc::dns::RRTTL cur_ttl(columns[1]);
- //cur_sigtype(columns[2]);
-
- if (cur_type == type) {
- addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
- columns[3]);
- } else if (cur_type == isc::dns::RRType::CNAME()) {
- // There should be no other data, so cur_rrset should be empty,
- if (result_rrset) {
- isc_throw(DataSourceError, "CNAME found but it is not "
- "the only record for " + name.toText());
+ if (cur_type == type) {
+ if (result_rrset &&
+ result_rrset->getType() == isc::dns::RRType::CNAME()) {
+ isc_throw(DataSourceError, "CNAME found but it is not "
+ "the only record for " + name.toText());
+ }
+ addOrCreate(result_rrset, name, getClass(), cur_type,
+ cur_ttl, columns[DatabaseConnection::RDATA_COLUMN]);
+ } else if (cur_type == isc::dns::RRType::CNAME()) {
+ // There should be no other data, so result_rrset should be empty.
+ if (result_rrset) {
+ isc_throw(DataSourceError, "CNAME found but it is not "
+ "the only record for " + name.toText());
+ }
+ addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
+ columns[DatabaseConnection::RDATA_COLUMN]);
+ result_status = CNAME;
+ } else if (cur_type == isc::dns::RRType::RRSIG()) {
+ // If we get signatures before we get the actual data, we
+ // can't know which ones to keep and which to drop...
+ // So we keep a separate store of any signature that may be
+ // relevant and add them to the final RRset when we are done.
+ // A possible optimization here is to not store them for types
+ // we are certain we don't need
+ sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
+ getClass(),
+ columns[DatabaseConnection::RDATA_COLUMN]));
}
- addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
- columns[3]);
- result_status = CNAME;
- } else if (cur_type == isc::dns::RRType::RRSIG()) {
- // If we get signatures before we get the actual data, we
- // can't know which ones to keep and which to drop...
- // So we keep a separate store of any signature that may be
- // relevant and add them to the final RRset when we are done.
- // A possible optimization here is to not store them for types
- // we are certain we don't need
- isc::dns::rdata::RdataPtr cur_rrsig(
- isc::dns::rdata::createRdata(cur_type, getClass(),
- columns[3]));
- sig_store.addSig(cur_rrsig);
+ } catch (const isc::dns::InvalidRRType& irt) {
+ isc_throw(DataSourceError, "Invalid RRType in database for " <<
+ name << ": " << columns[DatabaseConnection::TYPE_COLUMN]);
+ } catch (const isc::dns::InvalidRRTTL& irttl) {
+ isc_throw(DataSourceError, "Invalid TTL in database for " <<
+ name << ": " << columns[DatabaseConnection::TTL_COLUMN]);
+ } catch (const isc::dns::rdata::InvalidRdataText& ird) {
+ isc_throw(DataSourceError, "Invalid rdata in database for " <<
+ name << ": " << columns[DatabaseConnection::RDATA_COLUMN]);
}
- } catch (const isc::dns::InvalidRRType& irt) {
- isc_throw(DataSourceError, "Invalid RRType in database for " <<
- name << ": " << columns[0]);
- } catch (const isc::dns::InvalidRRTTL& irttl) {
- isc_throw(DataSourceError, "Invalid TTL in database for " <<
- name << ": " << columns[1]);
}
+ } catch (const DataSourceError& dse) {
+ logger.error(DATASRC_DATABASE_FIND_ERROR).arg(dse.what());
+ // call cleanup and rethrow
+ connection_->resetSearch();
+ throw;
+ } catch (const isc::Exception& isce) {
+ logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR).arg(isce.what());
+ // cleanup, change it to a DataSourceError and rethrow
+ connection_->resetSearch();
+ isc_throw(DataSourceError, isce.what());
+ } catch (const std::exception& ex) {
+ logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR).arg(ex.what());
+ connection_->resetSearch();
+ throw;
}
if (!result_rrset) {
if (records_found) {
+ logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXRRSET)
+ .arg(name).arg(getClass()).arg(type);
result_status = NXRRSET;
} else {
+ logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXDOMAIN)
+ .arg(name).arg(getClass()).arg(type);
result_status = NXDOMAIN;
}
} else {
sig_store.appendSignatures(result_rrset);
+ logger.debug(DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_RRSET).arg(*result_rrset);
}
return (FindResult(result_status, result_rrset));
}
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index d82c86f..4ad3f49 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -92,14 +92,61 @@ public:
* Returns a boolean specifying whether or not there was more data to read.
* In the case of a database error, a DatasourceError is thrown.
*
+ * The columns passed is an array of std::strings consisting of
+ * DatabaseConnection::RecordColumnCount elements, the elements of which
+ * are defined in DatabaseConnection::RecordColumns, in their basic
+ * string representation.
+ *
+ * If you are implementing a derived database connection class, you
+ * should have this method check the column_count value, and fill the
+ * array with strings conforming to their description in RecordColumn.
+ *
* \exception DatasourceError if there was an error reading from the database
*
- * \param columns This vector will be cleared, and the fields of the record will
- * be appended here as strings (in the order rdtype, ttl, sigtype,
- * and rdata). If there was no data, the vector is untouched.
+ * \param columns The elements of this array will be filled with the data
+ * for one record as defined by RecordColumns
+ * If there was no data, the array is untouched.
* \return true if there was a next record, false if there was not
*/
- virtual bool getNextRecord(std::vector<std::string>& columns) = 0;
+ virtual bool getNextRecord(std::string columns[], size_t column_count) = 0;
+
+ /**
+ * \brief Resets the current search initiated with searchForRecords()
+ *
+ * This method will be called when the called of searchForRecords() and
+ * getNextRecord() finds bad data, and aborts the current search.
+ * It should clean up whatever handlers searchForRecords() created, and
+ * any other state modified or needed by getNextRecord()
+ *
+ * Of course, the implementation of getNextRecord may also use it when
+ * it is done with a search. If it does, the implementation of this
+ * method should make sure it can handle being called multiple times.
+ *
+ * The implementation for this method should make sure it never throws.
+ */
+ virtual void resetSearch() = 0;
+
+ /**
+ * Definitions of the fields as they are required to be filled in
+ * by getNextRecord()
+ *
+ * When implementing getNextRecord(), the columns array should
+ * be filled with the values as described in this enumeration,
+ * in this order, i.e. TYPE_COLUMN should be the first element
+ * (index 0) of the array, TTL_COLUMN should be the second element
+ * (index 1), etc.
+ */
+ enum RecordColumns {
+ TYPE_COLUMN = 0, ///< The RRType of the record (A/NS/TXT etc.)
+ TTL_COLUMN = 1, ///< The TTL of the record (a
+ SIGTYPE_COLUMN = 2, ///< For RRSIG records, this contains the RRTYPE
+ ///< the RRSIG covers. In the current implementation,
+ ///< this field is ignored.
+ RDATA_COLUMN = 3 ///< Full text representation of the record's RDATA
+ };
+
+ /// The number of fields the columns array passed to getNextRecord should have
+ static const size_t RecordColumnCount = 4;
};
/**
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index 3fbb24d..af704d9 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -63,6 +63,41 @@ The maximum allowed number of items of the hotspot cache is set to the given
number. If there are too many, some of them will be dropped. The size of 0
means no limit.
+% DATASRC_DATABASE_FIND_ERROR error retrieving data from database datasource: %1
+The was an internal error while reading data from a datasource. This can either
+mean the specific data source implementation is not behaving correctly, or the
+data it provides is invalid. The current search is aborted.
+The error message contains specific information about the error.
+
+% DATASRC_DATABASE_FIND_RECORDS looking for record %1/%2
+Debug information. The database data source is looking up records with the given
+name and type in the database.
+
+% DATASRC_DATABASE_FIND_UNCAUGHT_ERROR uncaught general error retrieving data from database datasource: %1
+There was an uncaught general exception while reading data from a datasource.
+This most likely points to a logic error in the code, and can be considered a
+bug. The current search is aborted. Specific information about the exception is
+printed in this error message.
+
+% DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR uncaught error retrieving data from database datasource: %1
+There was an uncaught ISC exception while reading data from a datasource. This
+most likely points to a logic error in the code, and can be considered a bug.
+The current search is aborted. Specific information about the exception is
+printed in this error message.
+
+% DATASRC_DATABASE_FOUND_NXDOMAIN search in database resulted in NXDOMAIN for %1/%2/%3
+The data returned by the database backend did not contain any data for the given
+domain name, class and type.
+
+% DATASRC_DATABASE_FOUND_NXRRSET search in database resulted in NXRRSET for %1/%2/%3
+The data returned by the database backend contained data for the given domain
+name and class, but not for the given type.
+
+% DATASRC_DATABASE_FOUND_RRSET search in database resulted in RRset %1
+The data returned by the database backend contained data for the given domain
+name, and it either matches the type or has a relevant type. The RRset that is
+returned is printed.
+
% DATASRC_DO_QUERY handling query for '%1/%2'
A debug message indicating that a query for the given name and RR type is being
processed.
diff --git a/src/lib/datasrc/sqlite3_connection.cc b/src/lib/datasrc/sqlite3_connection.cc
index 70adde4..750a62c 100644
--- a/src/lib/datasrc/sqlite3_connection.cc
+++ b/src/lib/datasrc/sqlite3_connection.cc
@@ -321,15 +321,30 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const {
void
SQLite3Connection::searchForRecords(int zone_id, const std::string& name) {
- sqlite3_reset(dbparameters_->q_any_);
- sqlite3_clear_bindings(dbparameters_->q_any_);
- sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id);
+ resetSearch();
+ int result;
+ result = sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id);
+ if (result != SQLITE_OK) {
+ isc_throw(DataSourceError,
+ "Error in sqlite3_bind_int() for zone_id " <<
+ zone_id << ", sqlite3 result code: " << result);
+ }
// use transient since name is a ref and may disappear
- sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1,
- SQLITE_TRANSIENT);
+ result = sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1,
+ SQLITE_TRANSIENT);
+ if (result != SQLITE_OK) {
+ isc_throw(DataSourceError,
+ "Error in sqlite3_bind_text() for name " <<
+ name << ", sqlite3 result code: " << result);
+ }
};
namespace {
+// This helper function converts from the unsigned char* type (used by
+// sqlite3) to char* (wanted by std::string). Technically these types
+// might not be directly convertable
+// In case sqlite3_column_text() returns NULL, we just make it an
+// empty string.
const char*
convertToPlainChar(const unsigned char* ucp) {
if (ucp == NULL) {
@@ -341,31 +356,44 @@ convertToPlainChar(const unsigned char* ucp) {
}
bool
-SQLite3Connection::getNextRecord(std::vector<std::string>& columns) {
- sqlite3_stmt* current_stmt = dbparameters_->q_any_;
- const int rc = sqlite3_step(current_stmt);
+SQLite3Connection::getNextRecord(std::string columns[], size_t column_count) {
+ try {
+ sqlite3_stmt* current_stmt = dbparameters_->q_any_;
+ const int rc = sqlite3_step(current_stmt);
+
+ if (column_count != RecordColumnCount) {
+ isc_throw(DataSourceError,
+ "Datasource backend caller did not pass a column array "
+ "of size " << RecordColumnCount <<
+ " to getNextRecord()");
+ }
- if (rc == SQLITE_ROW) {
- columns.clear();
- for (int column = 0; column < 4; ++column) {
- columns.push_back(convertToPlainChar(sqlite3_column_text(
- current_stmt, column)));
+ if (rc == SQLITE_ROW) {
+ for (int column = 0; column < column_count; ++column) {
+ columns[column] = convertToPlainChar(sqlite3_column_text(
+ current_stmt, column));
+ }
+ return (true);
+ } else if (rc == SQLITE_DONE) {
+ // reached the end of matching rows
+ resetSearch();
+ return (false);
}
- return (true);
- } else if (rc == SQLITE_DONE) {
- // reached the end of matching rows
- sqlite3_reset(current_stmt);
- sqlite3_clear_bindings(current_stmt);
- return (false);
+ resetSearch();
+ isc_throw(DataSourceError,
+ "Unexpected failure in sqlite3_step (sqlite result code " << rc << ")");
+ } catch (std::bad_alloc) {
+ isc_throw(DataSourceError, "bad_alloc in Sqlite3Connection::getNextRecord");
}
- sqlite3_reset(current_stmt);
- sqlite3_clear_bindings(current_stmt);
- isc_throw(DataSourceError,
- "Unexpected failure in sqlite3_step (sqlite result code " << rc << ")");
-
// Compilers might not realize isc_throw always throws
return (false);
}
+void
+SQLite3Connection::resetSearch() {
+ sqlite3_reset(dbparameters_->q_any_);
+ sqlite3_clear_bindings(dbparameters_->q_any_);
+}
+
}
}
diff --git a/src/lib/datasrc/sqlite3_connection.h b/src/lib/datasrc/sqlite3_connection.h
index ffb2470..d41b814 100644
--- a/src/lib/datasrc/sqlite3_connection.h
+++ b/src/lib/datasrc/sqlite3_connection.h
@@ -95,6 +95,9 @@ public:
* This implements the searchForRecords from DatabaseConnection.
* This particular implementation does not raise DataSourceError.
*
+ * \exception DataSourceError when sqlite3_bind_int() or
+ * sqlite3_bind_text() fails
+ *
* \param zone_id The zone to seach in, as returned by getZone()
* \param name The name to find records for
*/
@@ -107,12 +110,31 @@ public:
* This implements the getNextRecord from DatabaseConnection.
* See the documentation there for more information.
*
+ * If this method raises an exception, the contents of columns are undefined.
+ *
+ * \exception DataSourceError if there is an error returned by sqlite_step()
+ * When this exception is raised, the current
+ * search as initialized by searchForRecords() is
+ * NOT reset, and the caller is expected to take
+ * care of that.
* \param columns This vector will be cleared, and the fields of the record will
* be appended here as strings (in the order rdtype, ttl, sigtype,
- * and rdata). If there was no data, the vector is untouched.
+ * and rdata). If there was no data (i.e. if this call returns
+ * false), the vector is untouched.
* \return true if there was a next record, false if there was not
*/
- virtual bool getNextRecord(std::vector<std::string>& columns);
+ virtual bool getNextRecord(std::string columns[], size_t column_count);
+
+ /**
+ * \brief Resets any state created by searchForRecords
+ *
+ * This implements the resetSearch from DatabaseConnection.
+ * See the documentation there for more information.
+ *
+ * This function never throws.
+ */
+ virtual void resetSearch();
+
private:
/// \brief Private database data
SQLite3Parameters* dbparameters_;
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index c315932..69678f0 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -15,6 +15,7 @@
#include <gtest/gtest.h>
#include <dns/name.h>
+#include <dns/rrttl.h>
#include <exceptions/exceptions.h>
#include <datasrc/database.h>
@@ -36,7 +37,7 @@ namespace {
*/
class MockConnection : public DatabaseConnection {
public:
- MockConnection() { fillData(); }
+ MockConnection() : search_running_(false) { fillData(); }
virtual std::pair<bool, int> getZone(const Name& name) const {
if (name == Name("example.org")) {
@@ -47,10 +48,23 @@ public:
}
virtual void searchForRecords(int zone_id, const std::string& name) {
+ search_running_ = true;
+
+ // 'hardcoded' name to trigger exceptions (for testing
+ // the error handling of find() (the other on is below in
+ // if the name is "exceptiononsearch" it'll raise an exception here
+ if (name == "dsexception.in.search.") {
+ isc_throw(DataSourceError, "datasource exception on search");
+ } else if (name == "iscexception.in.search.") {
+ isc_throw(isc::Exception, "isc exception on search");
+ } else if (name == "basicexception.in.search.") {
+ throw std::exception();
+ }
+ searched_name_ = name;
+
// we're not aiming for efficiency in this test, simply
// copy the relevant vector from records
cur_record = 0;
-
if (zone_id == 42) {
if (records.count(name) > 0) {
cur_name = records.find(name)->second;
@@ -62,15 +76,38 @@ public:
}
};
- virtual bool getNextRecord(std::vector<std::string>& columns) {
+ virtual bool getNextRecord(std::string columns[], size_t column_count) {
+ if (searched_name_ == "dsexception.in.getnext.") {
+ isc_throw(DataSourceError, "datasource exception on getnextrecord");
+ } else if (searched_name_ == "iscexception.in.getnext.") {
+ isc_throw(isc::Exception, "isc exception on getnextrecord");
+ } else if (searched_name_ == "basicexception.in.getnext.") {
+ throw std::exception();
+ }
+
+ if (column_count != DatabaseConnection::RecordColumnCount) {
+ isc_throw(DataSourceError, "Wrong column count in getNextRecord");
+ }
if (cur_record < cur_name.size()) {
- columns = cur_name[cur_record++];
+ for (size_t i = 0; i < column_count; ++i) {
+ columns[i] = cur_name[cur_record][i];
+ }
+ cur_record++;
return (true);
} else {
+ resetSearch();
return (false);
}
};
+ virtual void resetSearch() {
+ search_running_ = false;
+ };
+
+ bool searchRunning() const {
+ return (search_running_);
+ }
+
private:
std::map<std::string, std::vector< std::vector<std::string> > > records;
// used as internal index for getNextRecord()
@@ -80,6 +117,14 @@ private:
// fake data
std::vector< std::vector<std::string> > cur_name;
+ // This boolean is used to make sure find() calls resetSearch
+ // when it encounters an error
+ bool search_running_;
+
+ // We store the name passed to searchForRecords, so we can
+ // hardcode some exceptions into getNextRecord
+ std::string searched_name_;
+
// Adds one record to the current name in the database
// The actual data will not be added to 'records' until
// addCurName() is called
@@ -121,6 +166,11 @@ private:
addRecord("AAAA", "3600", "", "2001:db8::2");
addCurName("www.example.org.");
+ addRecord("A", "3600", "", "192.0.2.1");
+ addRecord("AAAA", "3600", "", "2001:db8::1");
+ addRecord("A", "3600", "", "192.0.2.2");
+ addCurName("www2.example.org.");
+
addRecord("CNAME", "3600", "", "www.example.org.");
addCurName("cname.example.org.");
@@ -165,18 +215,42 @@ private:
addRecord("A", "3600", "", "192.0.2.1");
addCurName("acnamesig3.example.org.");
+ addRecord("A", "3600", "", "192.0.2.1");
+ addRecord("A", "360", "", "192.0.2.2");
+ addCurName("ttldiff1.example.org.");
+ addRecord("A", "360", "", "192.0.2.1");
+ addRecord("A", "3600", "", "192.0.2.2");
+ addCurName("ttldiff2.example.org.");
+
// also add some intentionally bad data
- cur_name.push_back(std::vector<std::string>());
- addCurName("emptyvector.example.org.");
addRecord("A", "3600", "", "192.0.2.1");
addRecord("CNAME", "3600", "", "www.example.org.");
- addCurName("badcname.example.org.");
+ addCurName("badcname1.example.org.");
+
+ addRecord("CNAME", "3600", "", "www.example.org.");
+ addRecord("A", "3600", "", "192.0.2.1");
+ addCurName("badcname2.example.org.");
+
+ addRecord("CNAME", "3600", "", "www.example.org.");
+ addRecord("CNAME", "3600", "", "www.example2.org.");
+ addCurName("badcname3.example.org.");
+
addRecord("A", "3600", "", "bad");
addCurName("badrdata.example.org.");
+
addRecord("BAD_TYPE", "3600", "", "192.0.2.1");
addCurName("badtype.example.org.");
+
addRecord("A", "badttl", "", "192.0.2.1");
addCurName("badttl.example.org.");
+
+ addRecord("A", "badttl", "", "192.0.2.1");
+ addRecord("RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+ addCurName("badsig.example.org.");
+
+ addRecord("A", "3600", "", "192.0.2.1");
+ addRecord("RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
+ addCurName("badsigtype.example.org.");
}
};
@@ -241,18 +315,21 @@ doFindTest(shared_ptr<DatabaseClient::Finder> finder,
const isc::dns::Name& name,
const isc::dns::RRType& type,
const isc::dns::RRType& expected_type,
+ const isc::dns::RRTTL expected_ttl,
ZoneFinder::Result expected_result,
unsigned int expected_rdata_count,
unsigned int expected_signature_count)
{
- ZoneFinder::FindResult result = finder->find(name, type,
- NULL, ZoneFinder::FIND_DEFAULT);
- ASSERT_EQ(expected_result, result.code) << name.toText() << " " << type.toText();
+ ZoneFinder::FindResult result =
+ finder->find(name, type, NULL, ZoneFinder::FIND_DEFAULT);
+ ASSERT_EQ(expected_result, result.code) << name << " " << type;
if (expected_rdata_count > 0) {
EXPECT_EQ(expected_rdata_count, result.rrset->getRdataCount());
+ EXPECT_EQ(expected_ttl, result.rrset->getTTL());
EXPECT_EQ(expected_type, result.rrset->getType());
if (expected_signature_count > 0) {
- EXPECT_EQ(expected_signature_count, result.rrset->getRRsig()->getRdataCount());
+ EXPECT_EQ(expected_signature_count,
+ result.rrset->getRRsig()->getRdataCount());
} else {
EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
}
@@ -268,79 +345,189 @@ TEST_F(DatabaseClientTest, find) {
shared_ptr<DatabaseClient::Finder> finder(
dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
EXPECT_EQ(42, finder->zone_id());
- const isc::dns::Name name("www.example.org.");
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("www.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 1, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ doFindTest(finder, isc::dns::Name("www2.example.org."),
+ isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
+ ZoneFinder::SUCCESS, 2, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("www.example.org."),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 2, 0);
doFindTest(finder, isc::dns::Name("www.example.org."),
isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
+ isc::dns::RRTTL(3600),
ZoneFinder::NXRRSET, 0, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("cname.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
+ isc::dns::RRTTL(3600),
ZoneFinder::CNAME, 1, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ doFindTest(finder, isc::dns::Name("cname.example.org."),
+ isc::dns::RRType::CNAME(), isc::dns::RRType::CNAME(),
+ isc::dns::RRTTL(3600),
+ ZoneFinder::SUCCESS, 1, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("doesnotexist.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
ZoneFinder::NXDOMAIN, 0, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signed1.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 1, 2);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signed1.example.org."),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 2, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signed1.example.org."),
isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
+ isc::dns::RRTTL(3600),
ZoneFinder::NXRRSET, 0, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signedcname1.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
+ isc::dns::RRTTL(3600),
ZoneFinder::CNAME, 1, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signed2.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 1, 2);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signed2.example.org."),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 2, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signed2.example.org."),
isc::dns::RRType::TXT(), isc::dns::RRType::TXT(),
+ isc::dns::RRTTL(3600),
ZoneFinder::NXRRSET, 0, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("signedcname2.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::CNAME(),
+ isc::dns::RRTTL(3600),
ZoneFinder::CNAME, 1, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("acnamesig1.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 1, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("acnamesig2.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 1, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
doFindTest(finder, isc::dns::Name("acnamesig3.example.org."),
isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
ZoneFinder::SUCCESS, 1, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
- EXPECT_THROW(finder->find(isc::dns::Name("emptyvector.example.org."),
+ doFindTest(finder, isc::dns::Name("ttldiff1.example.org."),
+ isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(360),
+ ZoneFinder::SUCCESS, 2, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ doFindTest(finder, isc::dns::Name("ttldiff2.example.org."),
+ isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(360),
+ ZoneFinder::SUCCESS, 2, 0);
+ EXPECT_FALSE(current_connection_->searchRunning());
+
+ EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."),
isc::dns::RRType::A(),
NULL, ZoneFinder::FIND_DEFAULT),
DataSourceError);
- EXPECT_THROW(finder->find(isc::dns::Name("badcname.example.org."),
+ EXPECT_FALSE(current_connection_->searchRunning());
+ EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."),
isc::dns::RRType::A(),
NULL, ZoneFinder::FIND_DEFAULT),
DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."),
isc::dns::RRType::A(),
NULL, ZoneFinder::FIND_DEFAULT),
DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."),
isc::dns::RRType::A(),
NULL, ZoneFinder::FIND_DEFAULT),
DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."),
isc::dns::RRType::A(),
NULL, ZoneFinder::FIND_DEFAULT),
DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
+
+ // Trigger the hardcoded exceptions and see if find() has cleaned up
+ /*
+ EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ std::exception);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ */
+ EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ DataSourceError);
+ EXPECT_FALSE(current_connection_->searchRunning());
+ EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."),
+ isc::dns::RRType::A(),
+ NULL, ZoneFinder::FIND_DEFAULT),
+ std::exception);
+ EXPECT_FALSE(current_connection_->searchRunning());
+
+ // This RRSIG has the wrong sigtype field, which should be
+ // an error if we decide to keep using that field
+ // Right now the field is ignored, so it does not error
+ doFindTest(finder, isc::dns::Name("badsigtype.example.org."),
+ isc::dns::RRType::A(), isc::dns::RRType::A(),
+ isc::dns::RRTTL(3600),
+ ZoneFinder::SUCCESS, 1, 1);
+ EXPECT_FALSE(current_connection_->searchRunning());
}
diff --git a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc
index 0d0b8c3..7f70322 100644
--- a/src/lib/datasrc/tests/sqlite3_connection_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_connection_unittest.cc
@@ -11,8 +11,6 @@
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <memory>
-
#include <datasrc/sqlite3_connection.h>
#include <datasrc/data_source.h>
@@ -20,6 +18,7 @@
#include <gtest/gtest.h>
#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
using namespace isc::datasrc;
using isc::data::ConstElementPtr;
@@ -76,7 +75,7 @@ public:
conn.reset(new SQLite3Connection(filename, rrclass));
}
// The tested connection
- boost::shared_ptr<SQLite3Connection> conn;
+ boost::scoped_ptr<SQLite3Connection> conn;
};
// This zone exists in the data, so it should be found
@@ -102,22 +101,19 @@ TEST_F(SQLite3Conn, noClass) {
EXPECT_FALSE(conn->getZone(Name("example.com")).first);
}
-namespace {
- // Simple function to count the number of records for
- // any name
- size_t countRecords(boost::shared_ptr<SQLite3Connection>& conn,
- int zone_id, const std::string& name)
- {
- conn->searchForRecords(zone_id, name);
- size_t count = 0;
- std::vector<std::string> columns;
- while (conn->getNextRecord(columns)) {
- EXPECT_EQ(4, columns.size());
- ++count;
- }
- return (count);
- }
-}
+// Simple function to cound the number of records for
+// any name
+void
+checkRecordRow(const std::string columns[],
+ const std::string& field0,
+ const std::string& field1,
+ const std::string& field2,
+ const std::string& field3)
+{
+ EXPECT_EQ(field0, columns[0]);
+ EXPECT_EQ(field1, columns[1]);
+ EXPECT_EQ(field2, columns[2]);
+ EXPECT_EQ(field3, columns[3]);
}
TEST_F(SQLite3Conn, getRecords) {
@@ -127,16 +123,112 @@ TEST_F(SQLite3Conn, getRecords) {
const int zone_id = zone_info.second;
ASSERT_EQ(1, zone_id);
- // without search, getNext() should return false
- std::vector<std::string> columns;
- EXPECT_FALSE(conn->getNextRecord(columns));
- EXPECT_EQ(0, columns.size());
-
- EXPECT_EQ(4, countRecords(conn, zone_id, "foo.example.com."));
- EXPECT_EQ(15, countRecords(conn, zone_id, "example.com."));
- EXPECT_EQ(0, countRecords(conn, zone_id, "foo.bar."));
- EXPECT_EQ(0, countRecords(conn, zone_id, ""));
+ const size_t column_count = DatabaseConnection::RecordColumnCount;
+ std::string columns[column_count];
- EXPECT_FALSE(conn->getNextRecord(columns));
- EXPECT_EQ(0, columns.size());
+ // without search, getNext() should return false
+ EXPECT_FALSE(conn->getNextRecord(columns,
+ column_count));
+ checkRecordRow(columns, "", "", "", "");
+
+ conn->searchForRecords(zone_id, "foo.bar.");
+ EXPECT_FALSE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "", "", "", "");
+
+ conn->searchForRecords(zone_id, "");
+ EXPECT_FALSE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "", "", "", "");
+
+ // Should error on a bad number of columns
+ EXPECT_THROW(conn->getNextRecord(columns, 3), DataSourceError);
+ EXPECT_THROW(conn->getNextRecord(columns, 5), DataSourceError);
+
+ // now try some real searches
+ conn->searchForRecords(zone_id, "foo.example.com.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "CNAME", "3600", "",
+ "cnametest.example.org.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "3600", "CNAME",
+ "CNAME 5 3 3600 20100322084538 20100220084538 33495 "
+ "example.com. FAKEFAKEFAKEFAKE");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "NSEC", "7200", "",
+ "mail.example.com. CNAME RRSIG NSEC");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "7200", "NSEC",
+ "NSEC 5 3 7200 20100322084538 20100220084538 33495 "
+ "example.com. FAKEFAKEFAKEFAKE");
+ EXPECT_FALSE(conn->getNextRecord(columns, column_count));
+ // with no more records, the array should not have been modified
+ checkRecordRow(columns, "RRSIG", "7200", "NSEC",
+ "NSEC 5 3 7200 20100322084538 20100220084538 33495 "
+ "example.com. FAKEFAKEFAKEFAKE");
+
+ conn->searchForRecords(zone_id, "example.com.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "SOA", "3600", "",
+ "master.example.com. admin.example.com. "
+ "1234 3600 1800 2419200 7200");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "3600", "SOA",
+ "SOA 5 2 3600 20100322084538 20100220084538 "
+ "33495 example.com. FAKEFAKEFAKEFAKE");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "NS", "1200", "", "dns01.example.com.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "NS", "3600", "", "dns02.example.com.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "NS", "1800", "", "dns03.example.com.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "3600", "NS",
+ "NS 5 2 3600 20100322084538 20100220084538 "
+ "33495 example.com. FAKEFAKEFAKEFAKE");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "MX", "3600", "", "10 mail.example.com.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "MX", "3600", "",
+ "20 mail.subzone.example.com.");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "3600", "MX",
+ "MX 5 2 3600 20100322084538 20100220084538 "
+ "33495 example.com. FAKEFAKEFAKEFAKE");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "NSEC", "7200", "",
+ "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "7200", "NSEC",
+ "NSEC 5 2 7200 20100322084538 20100220084538 "
+ "33495 example.com. FAKEFAKEFAKEFAKE");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "DNSKEY", "3600", "",
+ "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q NGV7W"
+ "cTD0WEiuV7IjXgHE36fCmS9QsUxSSOV o1I/FMxI2PJVqTYHkX"
+ "FBS7AzLGsQYMU7UjBZ SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g"
+ "0qcbW YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "DNSKEY", "3600", "",
+ "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ tbvzg"
+ "62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV 4HQZJStJaZ+fHU5AwV"
+ "NT+bBZdtV+NujSikhd THb4FYLg2b3Cx9NyJvAVukHp/91HnWu"
+ "G4T36 CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/i DGd8j6b"
+ "qiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq 23TaOrVTjB7d1a/h31OD"
+ "fiHAxFHrkY3t3D5J R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86"
+ "Acv RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf oIK/aKwEN"
+ "rsjcKZZj660b1M=");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "3600", "DNSKEY",
+ "DNSKEY 5 2 3600 20100322084538 20100220084538 "
+ "4456 example.com. FAKEFAKEFAKEFAKE");
+ ASSERT_TRUE(conn->getNextRecord(columns, column_count));
+ checkRecordRow(columns, "RRSIG", "3600", "DNSKEY",
+ "DNSKEY 5 2 3600 20100322084538 20100220084538 "
+ "33495 example.com. FAKEFAKEFAKEFAKE");
+ EXPECT_FALSE(conn->getNextRecord(columns, column_count));
+ // getnextrecord returning false should mean array is not altered
+ checkRecordRow(columns, "RRSIG", "3600", "DNSKEY",
+ "DNSKEY 5 2 3600 20100322084538 20100220084538 "
+ "33495 example.com. FAKEFAKEFAKEFAKE");
}
+
+} // end anonymous namespace
More information about the bind10-changes
mailing list