Browse Source

[trac1062] initial addition of searchForRecords and getNextRecord

Jelte Jansen 13 years ago
parent
commit
d23cde8c42

+ 23 - 0
src/lib/datasrc/database.h

@@ -71,6 +71,29 @@ public:
      *     an opaque handle.
      */
     virtual std::pair<bool, int> getZone(const isc::dns::Name& name) const = 0;
+
+    /**
+     * \brief Starts a new search for records of the given name in the given zone
+     *
+     * \param zone_id The zone to search in, as returned by getZone()
+     * \param name The name of the records to find
+     */
+    virtual void searchForRecords(int zone_id, const std::string& name) const = 0;
+
+    /**
+     * \brief Retrieves the next record from the search started with searchForRecords()
+     *
+     * Returns a boolean specifying whether or not there was more data to read.
+     * In the case of a database error, a DatasourceError is thrown.
+     *
+     * \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.
+     * \return true if there was a next record, false if there was not
+     */
+    virtual bool getNextRecord(std::vector<std::string>& columns) const = 0;
 };
 
 /**

+ 60 - 13
src/lib/datasrc/sqlite3_connection.cc

@@ -24,19 +24,20 @@ namespace datasrc {
 struct SQLite3Parameters {
     SQLite3Parameters() :
         db_(NULL), version_(-1),
-        q_zone_(NULL) /*, q_record_(NULL), q_addrs_(NULL), q_referral_(NULL),
-        q_any_(NULL), q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL),
+        q_zone_(NULL), q_any_(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_;
+    sqlite3_stmt* q_any_;
     /*
     TODO: Yet unneeded statements
     sqlite3_stmt* q_record_;
     sqlite3_stmt* q_addrs_;
     sqlite3_stmt* q_referral_;
-    sqlite3_stmt* q_any_;
     sqlite3_stmt* q_count_;
     sqlite3_stmt* q_previous_;
     sqlite3_stmt* q_nsec3_;
@@ -69,6 +70,9 @@ public:
         if (params_.q_zone_ != NULL) {
             sqlite3_finalize(params_.q_zone_);
         }
+        if (params_.q_any_ != NULL) {
+            sqlite3_finalize(params_.q_any_);
+        }
         /*
         if (params_.q_record_ != NULL) {
             sqlite3_finalize(params_.q_record_);
@@ -79,9 +83,6 @@ public:
         if (params_.q_referral_ != NULL) {
             sqlite3_finalize(params_.q_referral_);
         }
-        if (params_.q_any_ != NULL) {
-            sqlite3_finalize(params_.q_any_);
-        }
         if (params_.q_count_ != NULL) {
             sqlite3_finalize(params_.q_count_);
         }
@@ -132,6 +133,9 @@ const char* const SCHEMA_LIST[] = {
 
 const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2";
 
+const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata "
+    "FROM records WHERE zone_id=?1 AND name=?2";
+
 /* TODO: Prune the statements, not everything will be needed maybe?
 const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata "
     "FROM records WHERE zone_id=?1 AND name=?2 AND "
@@ -148,9 +152,6 @@ const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM "
     "(rdtype='NS' OR sigtype='NS' OR rdtype='DS' OR sigtype='DS' OR "
     "rdtype='DNAME' OR sigtype='DNAME')";
 
-const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata "
-    "FROM records WHERE zone_id=?1 AND name=?2";
-
 const char* const q_count_str = "SELECT COUNT(*) FROM records "
     "WHERE zone_id=?1 AND rname LIKE (?2 || '%');";
 
@@ -200,11 +201,11 @@ checkAndSetupSchema(Initializer* initializer) {
     }
 
     initializer->params_.q_zone_ = prepare(db, q_zone_str);
+    initializer->params_.q_any_ = prepare(db, q_any_str);
     /* TODO: Yet unneeded statements
     initializer->params_.q_record_ = prepare(db, q_record_str);
     initializer->params_.q_addrs_ = prepare(db, q_addrs_str);
     initializer->params_.q_referral_ = prepare(db, q_referral_str);
-    initializer->params_.q_any_ = prepare(db, q_any_str);
     initializer->params_.q_count_ = prepare(db, q_count_str);
     initializer->params_.q_previous_ = prepare(db, q_previous_str);
     initializer->params_.q_nsec3_ = prepare(db, q_nsec3_str);
@@ -252,6 +253,9 @@ SQLite3Connection::close(void) {
     sqlite3_finalize(dbparameters_->q_zone_);
     dbparameters_->q_zone_ = NULL;
 
+    sqlite3_finalize(dbparameters_->q_any_);
+    dbparameters_->q_any_ = NULL;
+
     /* TODO: Once they are needed or not, uncomment or drop
     sqlite3_finalize(dbparameters->q_record_);
     dbparameters->q_record_ = NULL;
@@ -262,9 +266,6 @@ SQLite3Connection::close(void) {
     sqlite3_finalize(dbparameters->q_referral_);
     dbparameters->q_referral_ = NULL;
 
-    sqlite3_finalize(dbparameters->q_any_);
-    dbparameters->q_any_ = NULL;
-
     sqlite3_finalize(dbparameters->q_count_);
     dbparameters->q_count_ = NULL;
 
@@ -318,5 +319,51 @@ SQLite3Connection::getZone(const isc::dns::Name& name) const {
     return (result);
 }
 
+void
+SQLite3Connection::searchForRecords(int zone_id, const std::string& name) const {
+    sqlite3_reset(dbparameters_->q_any_);
+    sqlite3_clear_bindings(dbparameters_->q_any_);
+    sqlite3_bind_int(dbparameters_->q_any_, 1, zone_id);
+    // use transient since name is a ref and may disappear
+    sqlite3_bind_text(dbparameters_->q_any_, 2, name.c_str(), -1,
+                      SQLITE_TRANSIENT);
+};
+
+namespace {
+const char*
+convertToPlainChar(const unsigned char* ucp) {
+    if (ucp == NULL) {
+        return ("");
+    }
+    const void* p = ucp;
+    return (static_cast<const char*>(p));
+}
+}
+
+bool
+SQLite3Connection::getNextRecord(std::vector<std::string>& columns) const {
+    sqlite3_stmt* current_stmt = dbparameters_->q_any_;
+    const int rc = sqlite3_step(current_stmt);
+
+    if (rc == SQLITE_ROW) {
+        columns.clear();
+        for (int column = 0; column < 4; ++column) {
+            columns.push_back(convertToPlainChar(sqlite3_column_text(
+                                                 current_stmt, column)));
+        }
+        return true;
+    } else if (rc == SQLITE_DONE) {
+        // reached the end of matching rows
+        sqlite3_reset(current_stmt);
+        sqlite3_clear_bindings(current_stmt);
+        return false;
+    }
+    sqlite3_reset(current_stmt);
+    isc_throw(DataSourceError, "Unexpected failure in sqlite3_step");
+
+    // Compilers might not realize isc_throw always throws
+    return false;
+}
+
 }
 }

+ 2 - 0
src/lib/datasrc/sqlite3_connection.h

@@ -88,6 +88,8 @@ public:
      *     element and the zone id in the second if it was.
      */
     virtual std::pair<bool, int> getZone(const isc::dns::Name& name) const;
+    virtual void searchForRecords(int zone_id, const std::string& name) const;
+    virtual bool getNextRecord(std::vector<std::string>& columns) const;
 private:
     /// \brief Private database data
     SQLite3Parameters* dbparameters_;

+ 2 - 0
src/lib/datasrc/tests/database_unittest.cc

@@ -39,6 +39,8 @@ public:
             return (std::pair<bool, int>(false, 0));
         }
     }
+    virtual void searchForRecords(int, const std::string&) const {};
+    virtual bool getNextRecord(std::vector<std::string>&) const { return false; };
 };
 
 class DatabaseClientTest : public ::testing::Test {