Parcourir la source

[2541] addZone in SQLite3Accessor

Jelte Jansen il y a 12 ans
Parent
commit
0ece883214

+ 33 - 2
src/lib/datasrc/sqlite3_accessor.cc

@@ -78,7 +78,8 @@ enum StatementID {
     ADD_NSEC3_RECORD = 19,
     DEL_ZONE_NSEC3_RECORDS = 20,
     DEL_NSEC3_RECORD = 21,
-    NUM_STATEMENTS = 22
+    ADD_ZONE = 22,
+    NUM_STATEMENTS = 23
 };
 
 const char* const text_statements[NUM_STATEMENTS] = {
@@ -161,7 +162,10 @@ const char* const text_statements[NUM_STATEMENTS] = {
     "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"
+    "AND rdtype=?3 AND rdata=?4",
+
+    // ADD_ZONE: add a zone to the zones table
+    "INSERT INTO zones (name, rdclass) VALUES (?1, ?2)" // ADD_ZONE
 };
 
 struct SQLite3Parameters {
@@ -612,6 +616,33 @@ SQLite3Accessor::getZone(const std::string& name) const {
     return (std::pair<bool, int>(false, 0));
 }
 
+int
+SQLite3Accessor::addZone(const std::string& zone_name) {
+    // Transaction should have been started by the caller
+    if (!dbparameters_->in_transaction) {
+        isc_throw(DataSourceError, "performing addZone on SQLite3 "
+                  "data source without transaction");
+    }
+
+    // First check if the zone exists, if it does, do nothing and
+    // return false
+    std::pair<bool, int> getzone_result = getZone(zone_name);
+    if (getzone_result.first) {
+        return (getzone_result.second);
+    }
+
+    StatementProcessor proc(*dbparameters_, ADD_ZONE, "add zone");
+    proc.bindText(1, zone_name.c_str(), SQLITE_TRANSIENT);
+    proc.bindText(2, class_.c_str(), SQLITE_TRANSIENT);
+    proc.exec();
+
+    // There are tricks to getting this in one go, but it is safer
+    // to do a new lookup
+    getzone_result = getZone(zone_name);
+    assert(getzone_result.first);
+    return (getzone_result.second);
+}
+
 namespace {
 
 // Conversion to plain char

+ 25 - 0
src/lib/datasrc/sqlite3_accessor.h

@@ -131,6 +131,31 @@ public:
      */
     virtual std::pair<bool, int> getZone(const std::string& name) const;
 
+    /**
+     * \brief Add a zone
+     *
+     * This implements the addZone from DatabaseAccessor and adds an (empty)
+     * zone into the zones table. If the zone exists already, nothing is done,
+     * and the id of the existing zone is returned. Otherwise, the zone is
+     * created, and its id is returned (unless it raises an exception).
+     *
+     * The class of the newly created zone is the class passed at construction
+     * time of the accessor.
+     *
+     * Because this method performs a lookup and probably an assert, it
+     * requires a transaction has been started (with \c beginTransaction)
+     * by the caller.
+     *
+     * \exception DataSourceError if no transaction is active, or if there
+     *                            is an SQLite3 error when performing the
+     *                            queries.
+     *
+     * \param zone_name The origin name of the zone to add
+     * \return the id of the zone (either an existing one if the zone exists
+     *         already, or the id of the newly created zone).
+     */
+    virtual int addZone(const std::string& zone_name);
+
     /** \brief Look up all resource records for a name
      *
      * This implements the getRecords() method from DatabaseAccessor

+ 36 - 0
src/lib/datasrc/tests/sqlite3_accessor_unittest.cc

@@ -667,6 +667,42 @@ TEST_F(SQLite3Create, creationtest) {
     ASSERT_TRUE(isReadable(SQLITE_NEW_DBFILE));
 }
 
+// Test addZone works. This is done on the 'createtest' fixture so we
+// can easily be sure it does not exist yet.
+TEST_F(SQLite3Create, addZone) {
+    // Need shared_ptr for the getAllRecords at the end of the test
+    boost::shared_ptr<SQLite3Accessor> accessor(
+        new SQLite3Accessor(SQLITE_NEW_DBFILE, "IN"));
+
+    const std::string zone_name("example.com");
+    const std::pair<bool, int> zone_info(accessor->getZone(zone_name));
+    ASSERT_FALSE(zone_info.first);
+
+    // Calling addZone without transaction should fail
+    ASSERT_THROW(accessor->addZone(zone_name), DataSourceError);
+
+    // Add the zone. Since it does not exist yet, it should return true
+    accessor->startTransaction();
+    const int new_zone_id = accessor->addZone(zone_name);
+    accessor->commit();
+
+    // Calling addZone again should return the same zone id
+    accessor->startTransaction();
+    ASSERT_EQ(new_zone_id, accessor->addZone(zone_name));
+    accessor->rollback();
+
+    // Check that it exists now, but has no records at this point
+    const std::pair<bool, int> zone_info2(accessor->getZone(zone_name));
+    ASSERT_TRUE(zone_info2.first);
+    ASSERT_EQ(new_zone_id, zone_info2.second);
+
+    DatabaseAccessor::IteratorContextPtr context =
+        accessor->getAllRecords(zone_info2.second);
+    string data[DatabaseAccessor::COLUMN_COUNT];
+    ASSERT_NE(DatabaseAccessor::IteratorContextPtr(), context);
+    EXPECT_FALSE(context->getNext(data));
+}
+
 TEST_F(SQLite3Create, emptytest) {
     ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE));