Remove dependency on Selectors and refactor codebase.

Change-Id: Ic3024b76ba0eea61f790c91c36090b4aa68702a3
Refs: #4522
diff --git a/src/storage/sqlite-storage.cpp b/src/storage/sqlite-storage.cpp
index 42739c3..91016ea 100644
--- a/src/storage/sqlite-storage.cpp
+++ b/src/storage/sqlite-storage.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017, Regents of the University of California.
+ * Copyright (c) 2014-2018, Regents of the University of California.
  *
  * This file is part of NDN repo-ng (Next generation of NDN repository).
  * See AUTHORS.md for complete list of repo-ng authors and contributors.
@@ -19,23 +19,25 @@
 
 #include "sqlite-storage.hpp"
 #include "config.hpp"
-#include "index.hpp"
 
 #include <ndn-cxx/util/sha256.hpp>
+#include <ndn-cxx/util/sqlite3-statement.hpp>
+
 #include <boost/filesystem.hpp>
 #include <istream>
 
+#include <ndn-cxx/util/logger.hpp>
+
 namespace repo {
 
-using std::string;
+NDN_LOG_INIT(repo.SqliteStorage);
 
-SqliteStorage::SqliteStorage(const string& dbPath)
-  : m_size(0)
+SqliteStorage::SqliteStorage(const std::string& dbPath)
 {
   if (dbPath.empty()) {
-    std::cerr << "Create db file in local location [" << dbPath << "]. " << std::endl
-              << "You can assign the path using -d option" << std::endl;
-    m_dbPath = string("ndn_repo.db");
+    m_dbPath = std::string("ndn_repo.db");
+    NDN_LOG_DEBUG("Create db file in local location [" << m_dbPath << "]. " );
+    NDN_LOG_DEBUG("You can assign the path using -d option" );
   }
   else {
     boost::filesystem::path fsPath(dbPath);
@@ -55,32 +57,31 @@
 void
 SqliteStorage::initializeRepo()
 {
-  char* errMsg = 0;
-
+  char* errMsg = nullptr;
   int rc = sqlite3_open_v2(m_dbPath.c_str(), &m_db,
                            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
 #ifdef DISABLE_SQLITE3_FS_LOCKING
                            "unix-dotfile"
 #else
-                           0
+                           nullptr
 #endif
-                           );
+                          );
 
   if (rc == SQLITE_OK) {
-    sqlite3_exec(m_db, "CREATE TABLE NDN_REPO ("
-                      "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
-                      "name BLOB, "
-                      "data BLOB, "
-                      "keylocatorHash BLOB);\n "
-                 , 0, 0, &errMsg);
+    // Create a new table named NDN_REPO_V2, distinguish from the old table name(NDN_REPO)
+    sqlite3_exec(m_db, "CREATE TABLE NDN_REPO_V2 (name BLOB, data BLOB);", nullptr, nullptr, &errMsg);
     // Ignore errors (when database already exists, errors are expected)
+    sqlite3_exec(m_db, "CREATE UNIQUE INDEX index_name ON NDN_REPO_V2 (name);", nullptr, nullptr, &errMsg);
   }
   else {
-    std::cerr << "Database file open failure rc:" << rc << std::endl;
+    NDN_LOG_DEBUG("Database file open failure rc:" << rc);
     BOOST_THROW_EXCEPTION(Error("Database file open failure"));
   }
-  sqlite3_exec(m_db, "PRAGMA synchronous = OFF", 0, 0, &errMsg);
-  sqlite3_exec(m_db, "PRAGMA journal_mode = WAL", 0, 0, &errMsg);
+
+  // SQLite continues without syncing as soon as it has handed data off to the operating system
+  sqlite3_exec(m_db, "PRAGMA synchronous = OFF;", nullptr, nullptr, &errMsg);
+  // Uses a write-ahead log instead of a rollback journal to implement transactions.
+  sqlite3_exec(m_db, "PRAGMA journal_mode = WAL;", nullptr, nullptr, &errMsg);
 }
 
 SqliteStorage::~SqliteStorage()
@@ -88,211 +89,158 @@
   sqlite3_close(m_db);
 }
 
-void
-SqliteStorage::fullEnumerate(const std::function<void(const Storage::ItemMeta)>& f)
-{
-  sqlite3_stmt* m_stmt = 0;
-  int rc = SQLITE_DONE;
-  string sql = string("SELECT id, name, keylocatorHash FROM NDN_REPO;");
-  rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &m_stmt, 0);
-  if (rc != SQLITE_OK)
-    BOOST_THROW_EXCEPTION(Error("Initiation Read Entries from Database Prepare error"));
-  int entryNumber = 0;
-  while (true) {
-    rc = sqlite3_step(m_stmt);
-    if (rc == SQLITE_ROW) {
-
-      ItemMeta item;
-      item.fullName.wireDecode(Block(reinterpret_cast<const uint8_t*>(sqlite3_column_blob(m_stmt, 1)),
-                                     sqlite3_column_bytes(m_stmt, 1)));
-      item.id = sqlite3_column_int(m_stmt, 0);
-      item.keyLocatorHash = make_shared<const ndn::Buffer>(sqlite3_column_blob(m_stmt, 3),
-                                                           sqlite3_column_bytes(m_stmt, 3));
-
-      try {
-        f(item);
-      }
-      catch (...) {
-        sqlite3_finalize(m_stmt);
-        throw;
-      }
-      entryNumber++;
-    }
-    else if (rc == SQLITE_DONE) {
-      sqlite3_finalize(m_stmt);
-      break;
-    }
-    else {
-      std::cerr << "Initiation Read Entries rc:" << rc << std::endl;
-      sqlite3_finalize(m_stmt);
-      BOOST_THROW_EXCEPTION(Error("Initiation Read Entries error"));
-    }
-  }
-  m_size = entryNumber;
-}
-
 int64_t
 SqliteStorage::insert(const Data& data)
 {
-  Name name = data.getName();
+  Name name = data.getFullName(); // store the full name
+  ndn::util::Sqlite3Statement stmt(m_db, "INSERT INTO NDN_REPO_V2 (name, data) VALUES (?, ?);");
 
-  Index::Entry entry(data, 0); //the id is not used
-  int64_t id = -1;
-  if (name.empty()) {
-    std::cerr << "name is empty" << std::endl;
-    return -1;
-  }
-
-  int rc = 0;
-
-  sqlite3_stmt* insertStmt = 0;
-
-  string insertSql = string("INSERT INTO NDN_REPO (id, name, data, keylocatorHash) "
-                            "VALUES (?, ?, ?, ?)");
-
-  if (sqlite3_prepare_v2(m_db, insertSql.c_str(), -1, &insertStmt, 0) != SQLITE_OK) {
-    sqlite3_finalize(insertStmt);
-    std::cerr << "insert sql not prepared" << std::endl;
-  }
   //Insert
-  auto result = sqlite3_bind_null(insertStmt, 1);
+  // Bind NULL to name value in NDN_REPO_V2 when initialize result.
+  auto result = sqlite3_bind_null(stmt, 1);
   if (result == SQLITE_OK) {
-    result = sqlite3_bind_blob(insertStmt, 2,
-                               entry.getName().wireEncode().wire(),
-                               entry.getName().wireEncode().size(), SQLITE_STATIC);
+    result = stmt.bind(1, name.wireEncode().value(),
+                          name.wireEncode().value_size(), SQLITE_STATIC);
   }
   if (result == SQLITE_OK) {
-    result = sqlite3_bind_blob(insertStmt, 3,
-                               data.wireEncode().wire(),
-                               data.wireEncode().size(), SQLITE_STATIC);
-  }
-  if (result == SQLITE_OK) {
-    BOOST_ASSERT(entry.getKeyLocatorHash()->size() == ndn::util::Sha256::DIGEST_SIZE);
-    result = sqlite3_bind_blob(insertStmt, 4,
-                               entry.getKeyLocatorHash()->data(),
-                               entry.getKeyLocatorHash()->size(), SQLITE_STATIC);
+    result = stmt.bind(2, data.wireEncode(), SQLITE_STATIC);
   }
 
+  int id = 0;
   if (result == SQLITE_OK) {
-    rc = sqlite3_step(insertStmt);
+    int rc = 0;
+    rc = stmt.step();
     if (rc == SQLITE_CONSTRAINT) {
-      std::cerr << "Insert failed" << std::endl;
-      sqlite3_finalize(insertStmt);
+      NDN_LOG_DEBUG("Insert failed");
       BOOST_THROW_EXCEPTION(Error("Insert failed"));
-     }
-    sqlite3_reset(insertStmt);
-     m_size++;
-     id = sqlite3_last_insert_rowid(m_db);
+    }
+    sqlite3_reset(stmt);
+    id = sqlite3_last_insert_rowid(m_db);
   }
   else {
     BOOST_THROW_EXCEPTION(Error("Some error with insert"));
   }
-
-  sqlite3_finalize(insertStmt);
   return id;
 }
 
-
 bool
-SqliteStorage::erase(const int64_t id)
+SqliteStorage::erase(const Name& name)
 {
-  sqlite3_stmt* deleteStmt = 0;
+  ndn::util::Sqlite3Statement stmt(m_db, "DELETE FROM NDN_REPO_V2 WHERE name = ?;");
 
-  string deleteSql = string("DELETE from NDN_REPO where id = ?;");
+  auto result = stmt.bind(1,
+                          name.wireEncode().value(),
+                          name.wireEncode().value_size(), SQLITE_STATIC);
 
-  if (sqlite3_prepare_v2(m_db, deleteSql.c_str(), -1, &deleteStmt, 0) != SQLITE_OK) {
-    sqlite3_finalize(deleteStmt);
-    std::cerr << "delete statement prepared failed" << std::endl;
-    BOOST_THROW_EXCEPTION(Error("delete statement prepared failed"));
-  }
-
-  if (sqlite3_bind_int64(deleteStmt, 1, id) == SQLITE_OK) {
-    int rc = sqlite3_step(deleteStmt);
+  if (result == SQLITE_OK) {
+    int rc = stmt.step();
     if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
-      std::cerr << " node delete error rc:" << rc << std::endl;
-      sqlite3_finalize(deleteStmt);
-      BOOST_THROW_EXCEPTION(Error(" node delete error"));
+      NDN_LOG_DEBUG("Node delete error rc:" << rc);
+      BOOST_THROW_EXCEPTION(Error("Node delete error"));
     }
-    if (sqlite3_changes(m_db) != 1)
+    if (sqlite3_changes(m_db) != 1) {
       return false;
-    m_size--;
+    }
   }
   else {
-    std::cerr << "delete bind error" << std::endl;
-    sqlite3_finalize(deleteStmt);
+    NDN_LOG_DEBUG("delete bind error");
     BOOST_THROW_EXCEPTION(Error("delete bind error"));
   }
-  sqlite3_finalize(deleteStmt);
   return true;
 }
 
-
-shared_ptr<Data>
-SqliteStorage::read(const int64_t id)
+std::shared_ptr<Data>
+SqliteStorage::read(const Name& name)
 {
-  sqlite3_stmt* queryStmt = 0;
-  string sql = string("SELECT * FROM NDN_REPO WHERE id = ? ;");
-  int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &queryStmt, 0);
-  if (rc == SQLITE_OK) {
-    if (sqlite3_bind_int64(queryStmt, 1, id) == SQLITE_OK) {
-      rc = sqlite3_step(queryStmt);
-      if (rc == SQLITE_ROW) {
-        auto data = make_shared<Data>();
-        data->wireDecode(Block(reinterpret_cast<const uint8_t*>(sqlite3_column_blob(queryStmt, 2)),
-                               sqlite3_column_bytes(queryStmt, 2)));
-        sqlite3_finalize(queryStmt);
-        return data;
+  return find(name);
+}
+
+bool
+SqliteStorage::has(const Name& name)
+{
+  // find exact match
+  return find(name, true) != nullptr;
+}
+
+std::shared_ptr<Data>
+SqliteStorage::find(const Name& name, bool exactMatch)
+{
+  NDN_LOG_DEBUG("Trying to find: " << name);
+  Name nameSuccessor;
+  if (!exactMatch) {
+    nameSuccessor = name.getSuccessor();
+  }
+
+  std::string sql;
+  if (exactMatch)
+    sql = "SELECT * FROM NDN_REPO_V2 WHERE name = ?;";
+  else
+    sql = "SELECT * FROM NDN_REPO_V2 WHERE name >= ? and name < ?;";
+
+  ndn::util::Sqlite3Statement stmt(m_db, sql);
+
+  auto result = stmt.bind(1,
+                          name.wireEncode().value(),
+                          name.wireEncode().value_size(), SQLITE_STATIC);
+
+  // use getsuccessor to locate prefix match items
+  if (result == SQLITE_OK && !exactMatch) {
+    // use V in TLV for prefix match when there is no exact match
+    result = stmt.bind(2,
+                       nameSuccessor.wireEncode().value(),
+                       nameSuccessor.wireEncode().value_size(), SQLITE_STATIC);
+  }
+
+  if (result == SQLITE_OK) {
+    int rc = stmt.step();
+    if (rc == SQLITE_ROW) {
+      Name foundName;
+
+      auto data = std::make_shared<Data>();
+      try {
+        data->wireDecode(stmt.getBlock(1));
       }
-      else if (rc == SQLITE_DONE) {
+      catch (const ndn::Block::Error& error) {
+        NDN_LOG_DEBUG(error.what());
         return nullptr;
       }
-      else {
-        std::cerr << "Database query failure rc:" << rc << std::endl;
-        sqlite3_finalize(queryStmt);
-        BOOST_THROW_EXCEPTION(Error("Database query failure"));
+      NDN_LOG_DEBUG("Data from db: " << *data);
+
+      foundName = data->getFullName();
+
+      if ((exactMatch && name == foundName) || (!exactMatch && name.isPrefixOf(foundName))) {
+        NDN_LOG_DEBUG("Found: " << foundName << " " << stmt.getInt(0));
+        return data;
       }
     }
-    else {
-      std::cerr << "select bind error" << std::endl;
-      sqlite3_finalize(queryStmt);
-      BOOST_THROW_EXCEPTION(Error("select bind error"));
+    else if (rc == SQLITE_DONE) {
+      return nullptr;
     }
-    sqlite3_finalize(queryStmt);
+    else {
+      NDN_LOG_DEBUG("Database query failure rc:" << rc);
+      BOOST_THROW_EXCEPTION(Error("Database query failure"));
+    }
   }
   else {
-    sqlite3_finalize(queryStmt);
-    std::cerr << "select statement prepared failed" << std::endl;
-    BOOST_THROW_EXCEPTION(Error("select statement prepared failed"));
+    NDN_LOG_DEBUG("select bind error");
+    BOOST_THROW_EXCEPTION(Error("select bind error"));
   }
   return nullptr;
 }
 
-int64_t
+uint64_t
 SqliteStorage::size()
 {
-  sqlite3_stmt* queryStmt = 0;
-  string sql("SELECT count(*) FROM NDN_REPO ");
-  int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &queryStmt, 0);
-  if (rc != SQLITE_OK)
-    {
-      std::cerr << "Database query failure rc:" << rc << std::endl;
-      sqlite3_finalize(queryStmt);
-      BOOST_THROW_EXCEPTION(Error("Database query failure"));
-    }
+  ndn::util::Sqlite3Statement stmt(m_db, "SELECT count(*) FROM NDN_REPO_V2;");
 
-  rc = sqlite3_step(queryStmt);
-  if (rc != SQLITE_ROW)
-    {
-      std::cerr << "Database query failure rc:" << rc << std::endl;
-      sqlite3_finalize(queryStmt);
-      BOOST_THROW_EXCEPTION(Error("Database query failure"));
-    }
-
-  int64_t nDatas = sqlite3_column_int64(queryStmt, 0);
-  if (m_size != nDatas) {
-    std::cerr << "The size of database is not correct! " << std::endl;
+  int rc = stmt.step();
+  if (rc != SQLITE_ROW) {
+    NDN_LOG_DEBUG("Database query failure rc:" << rc);
+    BOOST_THROW_EXCEPTION(Error("Database query failure"));
   }
-  return nDatas;
+
+  uint64_t nData = stmt.getInt(0);
+  return nData;
 }
 
 } // namespace repo