Remove dependency on Selectors and refactor codebase.

Change-Id: Ic3024b76ba0eea61f790c91c36090b4aa68702a3
Refs: #4522
diff --git a/src/storage/index.cpp b/src/storage/index.cpp
deleted file mode 100644
index 3eb2cce..0000000
--- a/src/storage/index.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2014-2017, 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.
- *
- * repo-ng is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * repo-ng, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "index.hpp"
-
-#include <ndn-cxx/util/sha256.hpp>
-#include <ndn-cxx/security/signature-sha256-with-rsa.hpp>
-
-namespace repo {
-
-/** @brief determines if entry can satisfy interest
- *  @param hash SHA256 hash of PublisherPublicKeyLocator if exists in interest, otherwise ignored
- */
-static bool
-matchesSimpleSelectors(const Interest& interest, ndn::ConstBufferPtr& hash,
-                       const Index::Entry& entry)
-{
-  const Name& fullName = entry.getName();
-
-  if (!interest.getName().isPrefixOf(fullName))
-    return false;
-
-  size_t nSuffixComponents = fullName.size() - interest.getName().size();
-  if (interest.getMinSuffixComponents() >= 0 &&
-      nSuffixComponents < static_cast<size_t>(interest.getMinSuffixComponents()))
-    return false;
-  if (interest.getMaxSuffixComponents() >= 0 &&
-      nSuffixComponents > static_cast<size_t>(interest.getMaxSuffixComponents()))
-    return false;
-
-  if (!interest.getExclude().empty() &&
-      entry.getName().size() > interest.getName().size() &&
-      interest.getExclude().isExcluded(entry.getName()[interest.getName().size()]))
-    return false;
-  if (!interest.getPublisherPublicKeyLocator().empty())
-    {
-      if (*entry.getKeyLocatorHash() != *hash)
-          return false;
-    }
-  return true;
-}
-
-Index::Index(size_t nMaxPackets)
-  : m_maxPackets(nMaxPackets)
-  , m_size(0)
-{
-}
-
-
-bool
-Index::insert(const Data& data, int64_t id)
-{
-  if (isFull())
-    BOOST_THROW_EXCEPTION(Error("The Index is Full. Cannot Insert Any Data!"));
-  Entry entry(data, id);
-  bool isInserted = m_indexContainer.insert(entry).second;
-  if (isInserted)
-    ++m_size;
-  return isInserted;
-}
-
-bool
-Index::insert(const Name& fullName, int64_t id,
-              const ndn::ConstBufferPtr& keyLocatorHash)
-{
-  if (isFull())
-    BOOST_THROW_EXCEPTION(Error("The Index is Full. Cannot Insert Any Data!"));
-  Entry entry(fullName, keyLocatorHash, id);
-  bool isInserted = m_indexContainer.insert(entry).second;
-  if (isInserted)
-    ++m_size;
-  return isInserted;
-}
-
-std::pair<int64_t,Name>
-Index::find(const Interest& interest) const
-{
-  Name name = interest.getName();
-  IndexContainer::const_iterator result = m_indexContainer.lower_bound(name);
-  if (result != m_indexContainer.end())
-    {
-      return selectChild(interest, result);
-    }
-  else
-    {
-      return std::make_pair(0, Name());
-    }
-}
-
-std::pair<int64_t,Name>
-Index::find(const Name& name) const
-{
-  IndexContainer::const_iterator result = m_indexContainer.lower_bound(name);
-  if (result != m_indexContainer.end())
-    {
-      return findFirstEntry(name, result);
-    }
-  else
-    {
-      return std::make_pair(0, Name());
-    }
-}
-
-bool
-Index::hasData(const Data& data) const
-{
-  Index::Entry entry(data, -1); // the id number is useless
-  IndexContainer::const_iterator result = m_indexContainer.find(entry);
-  return result != m_indexContainer.end();
-
-}
-
-std::pair<int64_t,Name>
-Index::findFirstEntry(const Name& prefix,
-                      IndexContainer::const_iterator startingPoint) const
-{
-  BOOST_ASSERT(startingPoint != m_indexContainer.end());
-  if (prefix.isPrefixOf(startingPoint->getName()))
-    {
-      return std::make_pair(startingPoint->getId(), startingPoint->getName());
-    }
-  else
-    {
-      return std::make_pair(0, Name());
-    }
-}
-
-bool
-Index::erase(const Name& fullName)
-{
-  Entry entry(fullName);
-  IndexContainer::const_iterator findIterator = m_indexContainer.find(entry);
-  if (findIterator != m_indexContainer.end())
-    {
-      m_indexContainer.erase(findIterator);
-      m_size--;
-      return true;
-    }
-  else
-    return false;
-}
-
-const ndn::ConstBufferPtr
-Index::computeKeyLocatorHash(const KeyLocator& keyLocator)
-{
-  const Block& block = keyLocator.wireEncode();
-  ndn::ConstBufferPtr keyLocatorHash = ndn::util::Sha256::computeDigest(block.wire(), block.size());
-  return keyLocatorHash;
-}
-
-std::pair<int64_t,Name>
-Index::selectChild(const Interest& interest,
-                   IndexContainer::const_iterator startingPoint) const
-{
-  BOOST_ASSERT(startingPoint != m_indexContainer.end());
-  bool isLeftmost = (interest.getChildSelector() <= 0);
-  ndn::ConstBufferPtr hash;
-  if (!interest.getPublisherPublicKeyLocator().empty())
-    {
-      KeyLocator keyLocator = interest.getPublisherPublicKeyLocator();
-      const Block& block = keyLocator.wireEncode();
-      hash = ndn::util::Sha256::computeDigest(block.wire(), block.size());
-    }
-
-  if (isLeftmost)
-    {
-      for (IndexContainer::const_iterator it = startingPoint;
-           it != m_indexContainer.end(); ++it)
-        {
-          if (!interest.getName().isPrefixOf(it->getName()))
-            return std::make_pair(0, Name());
-          if (matchesSimpleSelectors(interest, hash, (*it)))
-            return std::make_pair(it->getId(), it->getName());
-        }
-    }
-  else
-    {
-      IndexContainer::const_iterator boundary = m_indexContainer.lower_bound(interest.getName());
-      if (boundary == m_indexContainer.end() || !interest.getName().isPrefixOf(boundary->getName()))
-        return std::make_pair(0, Name());
-      Name successor = interest.getName().getSuccessor();
-      IndexContainer::const_iterator last = interest.getName().size() == 0 ?
-                    m_indexContainer.end() : m_indexContainer.lower_bound(interest.getName().getSuccessor());
-      while (true)
-        {
-          IndexContainer::const_iterator prev = last;
-          --prev;
-          if (prev == boundary)
-            {
-              bool isMatch = matchesSimpleSelectors(interest, hash, (*prev));
-              if (isMatch)
-                {
-                  return std::make_pair(prev->getId(), prev->getName());
-                }
-              else
-                return std::make_pair(0, Name());
-            }
-          IndexContainer::const_iterator first =
-            m_indexContainer.lower_bound(prev->getName().getPrefix(interest.getName().size() + 1));
-          IndexContainer::const_iterator match =
-                     std::find_if(first, last, bind(&matchesSimpleSelectors, interest, hash, _1));
-          if (match != last)
-            {
-              return std::make_pair(match->getId(), match->getName());
-            }
-          last = first;
-        }
-    }
-  return std::make_pair(0, Name());
-}
-
-Index::Entry::Entry(const Data& data, int64_t id)
-  : m_name(data.getFullName())
-  , m_id(id)
-{
-  const ndn::Signature& signature = data.getSignature();
-  if (signature.hasKeyLocator())
-    m_keyLocatorHash = computeKeyLocatorHash(signature.getKeyLocator());
-}
-
-Index::Entry::Entry(const Name& fullName, const KeyLocator& keyLocator, int64_t id)
-  : m_name(fullName)
-  , m_keyLocatorHash(computeKeyLocatorHash(keyLocator))
-  , m_id(id)
-{
-}
-
-Index::Entry::Entry(const Name& fullName,
-                    const ndn::ConstBufferPtr& keyLocatorHash, int64_t id)
-  : m_name(fullName)
-  , m_keyLocatorHash(keyLocatorHash)
-  , m_id(id)
-{
-}
-
-Index::Entry::Entry(const Name& name)
-  : m_name(name)
-{
-}
-
-} // namespace repo
diff --git a/src/storage/index.hpp b/src/storage/index.hpp
deleted file mode 100644
index d60bd37..0000000
--- a/src/storage/index.hpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * 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.
- *
- * repo-ng is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * repo-ng, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef REPO_STORAGE_INDEX_HPP
-#define REPO_STORAGE_INDEX_HPP
-
-#include "../common.hpp"
-
-#include <set>
-
-namespace repo {
-
-class Index : noncopyable
-{
-public:
-  class Error : public std::runtime_error
-  {
-  public:
-    explicit
-    Error(const std::string& what)
-      : std::runtime_error(what)
-    {
-    }
-  };
-
-  class Entry
-  {
-  public:
-    class Error : public std::runtime_error
-    {
-    public:
-      explicit
-      Error(const std::string& what)
-        : std::runtime_error(what)
-      {
-      }
-    };
-
-  public:
-    /**
-     * @brief used by set to construct node
-     */
-    Entry() = default;
-
-    /**
-     * @brief construct Entry by data and id number
-     */
-    Entry(const Data& data, int64_t id);
-
-    /**
-     * @brief construct Entry by fullName, keyLocator and id number
-     */
-    Entry(const Name& fullName, const KeyLocator& keyLocator, int64_t id);
-
-    /**
-     * @brief construct Entry by fullName, keyLocatorHash and id number
-     * @param  fullName        full name with digest computed from data
-     * @param  keyLocatorHash  keyLocator hashed by sha256
-     * @param  id              record ID from database
-     */
-    Entry(const Name& fullName, const ndn::ConstBufferPtr& keyLocatorHash, int64_t id);
-
-    /**
-     *  @brief implicit construct Entry by full name
-     *
-     *  Allow implicit conversion from Name for set lookups by Name
-     */
-    Entry(const Name& name);
-
-    /**
-     *  @brief get the name of entry
-     */
-    const Name&
-    getName() const
-    {
-      return m_name;
-    }
-
-    /**
-     *  @brief get the keyLocator hash value of the entry
-     */
-    const ndn::ConstBufferPtr&
-    getKeyLocatorHash() const
-    {
-      return m_keyLocatorHash;
-    }
-
-    /**
-     *  @brief get record ID from database
-     */
-    int64_t
-    getId() const
-    {
-      return m_id;
-    }
-
-    bool
-    operator>(const Entry& entry) const
-    {
-      return m_name > entry.getName();
-    }
-
-    bool
-    operator<(const Entry& entry) const
-    {
-      return m_name < entry.getName();
-    }
-
-    bool
-    operator==(const Entry& entry) const
-    {
-      return m_name == entry.getName();
-    }
-
-    bool
-    operator!=(const Entry& entry) const
-    {
-      return m_name != entry.getName();
-    }
-
-  private:
-    Name m_name;
-    ndn::ConstBufferPtr m_keyLocatorHash;
-    int64_t m_id;
-  };
-
-private:
-  typedef std::set<Entry> IndexContainer;
-
-public:
-  explicit
-  Index(size_t nMaxPackets);
-
-  /**
-   *  @brief insert entries into index
-   *  @param  data    used to construct entries
-   *  @param  id      obtained from database
-   */
-  bool
-  insert(const Data& data, int64_t id);
-
-  /**
-   *  @brief insert entries into index
-   *  @param  data    used to construct entries
-   *  @param  id      obtained from database
-   */
-  bool
-  insert(const Name& fullName, int64_t id,
-         const ndn::ConstBufferPtr& keyLocatorHash);
-
-  /**
-   *  @brief erase the entry in index by its fullname
-   */
-  bool
-  erase(const Name& fullName);
-
-  /** @brief find the Entry for best match of an Interest
-   * @return ID and fullName of the Entry, or (0,ignored) if not found
-   */
-  std::pair<int64_t, Name>
-  find(const Interest& interest) const;
-
-  /** @brief find the first Entry under a Name prefix
-   * @return ID and fullName of the Entry, or (0,ignored) if not found
-   */
-  std::pair<int64_t, Name>
-  find(const Name& name) const;
-
-  /**
-   *  @brief determine whether same Data is already in the index
-   *  @return true if identical Data exists, false otherwise
-   */
-  bool
-  hasData(const Data& data) const;
-
-  /**
-    *  @brief compute the hash value of keyLocator
-    */
-  static const ndn::ConstBufferPtr
-  computeKeyLocatorHash(const KeyLocator& keyLocator);
-
-  size_t
-  size() const
-  {
-    return m_size;
-  }
-
-private:
-  /**
-   *  @brief select entries which satisfy the selectors in interest and return their name
-   *  @param  interest   used to select entries by comparing the name and checking selectors
-   *  @param  idName    save the id and name of found entries
-   *  @param  startingPoint the entry whose name is equal or larger than the interest name
-   */
-  std::pair<int64_t, Name>
-  selectChild(const Interest& interest,
-              IndexContainer::const_iterator startingPoint) const;
-
-  /**
-   *  @brief check whether the index is full
-   */
-  bool
-  isFull() const
-  {
-    return m_size >= m_maxPackets;
-  }
-
-  /**
-   *  @brief find the first entry with the prefix
-   *  @param  prefix        used to request the entries
-   *  @param  startingPoint the entry whose name is equal or larger than the interest name
-   *  @return int64_t  the id number of found entry
-   *  @return Name     the name of found entry
-   */
-  std::pair<int64_t, Name>
-  findFirstEntry(const Name& prefix,
-                 IndexContainer::const_iterator startingPoint) const;
-
-private:
-  IndexContainer m_indexContainer;
-  size_t m_maxPackets;
-  size_t m_size;
-};
-
-} // namespace repo
-
-#endif // REPO_STORAGE_INDEX_HPP
diff --git a/src/storage/repo-storage.cpp b/src/storage/repo-storage.cpp
index 148116b..334b4d3 100644
--- a/src/storage/repo-storage.cpp
+++ b/src/storage/repo-storage.cpp
@@ -28,62 +28,52 @@
 
 NDN_LOG_INIT(repo.RepoStorage);
 
-RepoStorage::RepoStorage(const int64_t& nMaxPackets, Storage& store)
-  : m_index(nMaxPackets)
-  , m_storage(store)
+RepoStorage::RepoStorage(Storage& store)
+  : m_storage(store)
 {
 }
 
-void
-RepoStorage::initialize()
-{
-  NDN_LOG_DEBUG("Initialize");
-  m_storage.fullEnumerate(bind(&RepoStorage::insertItemToIndex, this, _1));
-}
-
-void
-RepoStorage::insertItemToIndex(const Storage::ItemMeta& item)
-{
-  NDN_LOG_DEBUG("Insert data to index " << item.fullName);
-  m_index.insert(item.fullName, item.id, item.keyLocatorHash);
-  afterDataInsertion(item.fullName);
-}
-
 bool
 RepoStorage::insertData(const Data& data)
 {
-   bool isExist = m_index.hasData(data);
-   if (isExist)
-     BOOST_THROW_EXCEPTION(Error("The Entry Has Already In the Skiplist. Cannot be Inserted!"));
-   int64_t id = m_storage.insert(data);
-   if (id == -1)
-     return false;
-   bool didInsert = m_index.insert(data, id);
-   if (didInsert)
-     afterDataInsertion(data.getName());
-   return didInsert;
+  bool isExist = m_storage.has(data.getFullName());
+
+  if (isExist) {
+    NDN_LOG_DEBUG("Data already in storage, regarded as successful data insertion");
+    return true;
+  }
+
+  int64_t id = m_storage.insert(data);
+  NDN_LOG_DEBUG("Insert ID: " << id << ", full name:" << data.getFullName());
+  if (id == NOTFOUND)
+    return false;
+
+  afterDataInsertion(data.getName());
+  return true;
 }
 
 ssize_t
 RepoStorage::deleteData(const Name& name)
 {
+  NDN_LOG_DEBUG("Delete: " << name);
   bool hasError = false;
-  std::pair<int64_t,ndn::Name> idName = m_index.find(name);
-  if (idName.first == 0)
-    return false;
+
   int64_t count = 0;
-  while (idName.first != 0) {
-    bool resultDb = m_storage.erase(idName.first);
-    bool resultIndex = m_index.erase(idName.second); //full name
-    if (resultDb && resultIndex) {
-      afterDataDeletion(idName.second);
+  std::shared_ptr<Data> foundData;
+  Name foundName;
+  while ((foundData = m_storage.find(name))) {
+    foundName = foundData->getFullName();
+    bool resultDb = m_storage.erase(foundName);
+    if (resultDb) {
+      afterDataDeletion(foundName);
       count++;
     }
     else {
       hasError = true;
     }
-    idName = m_index.find(name);
+    NDN_LOG_DEBUG("Delete: " << name << ", found " << foundName << ", count " << count << ", result " << resultDb);
   }
+
   if (hasError)
     return -1;
   else
@@ -93,40 +83,15 @@
 ssize_t
 RepoStorage::deleteData(const Interest& interest)
 {
-  Interest interestDelete = interest;
-  interestDelete.setChildSelector(0);  //to disable the child selector in delete handle
-  int64_t count = 0;
-  bool hasError = false;
-  std::pair<int64_t,ndn::Name> idName = m_index.find(interestDelete);
-  while (idName.first != 0) {
-    bool resultDb = m_storage.erase(idName.first);
-    bool resultIndex = m_index.erase(idName.second); //full name
-    if (resultDb && resultIndex) {
-      afterDataDeletion(idName.second);
-      count++;
-    }
-    else {
-      hasError = true;
-    }
-    idName = m_index.find(interestDelete);
-  }
-  if (hasError)
-    return -1;
-  else
-    return count;
+  return deleteData(interest.getName());
 }
 
-shared_ptr<Data>
+std::shared_ptr<Data>
 RepoStorage::readData(const Interest& interest) const
 {
-  std::pair<int64_t,ndn::Name> idName = m_index.find(interest);
-  if (idName.first != 0) {
-    shared_ptr<Data> data = m_storage.read(idName.first);
-    if (data) {
-      return data;
-    }
-  }
-  return shared_ptr<Data>();
+  NDN_LOG_DEBUG("Reading data for " << interest.getName());
+
+  return m_storage.read(interest.getName());
 }
 
 
diff --git a/src/storage/repo-storage.hpp b/src/storage/repo-storage.hpp
index ec5faa7..39e576d 100644
--- a/src/storage/repo-storage.hpp
+++ b/src/storage/repo-storage.hpp
@@ -22,10 +22,8 @@
 
 #include "../common.hpp"
 #include "storage.hpp"
-#include "index.hpp"
 #include "../repo-command-parameter.hpp"
 
-#include <ndn-cxx/exclude.hpp>
 #include <ndn-cxx/util/signal.hpp>
 
 #include <queue>
@@ -50,13 +48,7 @@
   };
 
 public:
-  RepoStorage(const int64_t& nMaxPackets, Storage& store);
-
-  /**
-   *  @brief  rebuild index from database
-   */
-  void
-  initialize();
+  RepoStorage(Storage& store);
 
   /**
    *  @brief  insert data into repo
@@ -66,7 +58,7 @@
 
   /**
    *  @brief   delete data from repo
-   *  @param   name     used to find entry needed to be erased in repo
+   *  @param   name from interest, use it as a prefix to find entry needed to be erased in repo
    *  @return  if deletion in either index or database fail, return -1,
    *           otherwise return the number of erased entries
    */
@@ -83,24 +75,20 @@
   deleteData(const Interest& interest);
 
   /**
-   *  @brief  read data from repo
+   *  @brief   read data from repo
    *  @param   interest  used to request data
    *  @return  std::shared_ptr<Data>
    */
   std::shared_ptr<Data>
   readData(const Interest& interest) const;
 
-private:
-  void
-  insertItemToIndex(const Storage::ItemMeta& item);
-
 public:
   ndn::util::Signal<RepoStorage, ndn::Name> afterDataInsertion;
   ndn::util::Signal<RepoStorage, ndn::Name> afterDataDeletion;
 
 private:
-  Index m_index;
   Storage& m_storage;
+  const int NOTFOUND = -1;
 };
 
 } // namespace repo
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
diff --git a/src/storage/sqlite-storage.hpp b/src/storage/sqlite-storage.hpp
old mode 100755
new mode 100644
index 168cc41..4d7142b
--- a/src/storage/sqlite-storage.hpp
+++ b/src/storage/sqlite-storage.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California.
+ * Copyright (c) 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.
@@ -21,19 +21,17 @@
 #define REPO_STORAGE_SQLITE_STORAGE_HPP
 
 #include "storage.hpp"
-#include "index.hpp"
-#include <string>
-#include <iostream>
-#include <sqlite3.h>
-#include <stdlib.h>
-#include <vector>
-#include <queue>
+
 #include <algorithm>
+#include <iostream>
+#include <queue>
+#include <stdlib.h>
+#include <string>
+#include <sqlite3.h>
+#include <vector>
 
 namespace repo {
 
-using std::queue;
-
 class SqliteStorage : public Storage
 {
 public:
@@ -50,7 +48,6 @@
   explicit
   SqliteStorage(const std::string& dbPath);
 
-  virtual
   ~SqliteStorage();
 
   /**
@@ -58,35 +55,30 @@
    *  @param  data     the data should be inserted into databse
    *  @return int64_t  the id number of each entry in the database
    */
-  virtual int64_t
-  insert(const Data& data);
+  int64_t
+  insert(const Data& data) override;
 
   /**
-   *  @brief  remove the entry in the database by using id
-   *  @param  id   id number of each entry in the database
+   *  @brief  remove the entry in the database by using name as index
+   *  @param  name   name of the data
    */
-  virtual bool
-  erase(const int64_t id);
+  bool
+  erase(const Name& name) override;
 
-  /**
-   *  @brief  get the data from database
-   *  @para   id   id number of each entry in the database, used to find the data
-   */
-  virtual std::shared_ptr<Data>
-  read(const int64_t id);
+  std::shared_ptr<Data>
+  read(const Name& name) override;
+
+  bool
+  has(const Name& name) override;
+
+  std::shared_ptr<Data>
+  find(const Name& name, bool exactMatch = false) override;
 
   /**
    *  @brief  return the size of database
    */
-  virtual int64_t
-  size();
-
-  /**
-   *  @brief enumerate each entry in database and call the function
-   *         insertItemToIndex to reubuild index from database
-   */
-  void
-  fullEnumerate(const std::function<void(const Storage::ItemMeta)>& f);
+  uint64_t
+  size() override;
 
 private:
   void
@@ -95,7 +87,6 @@
 private:
   sqlite3* m_db;
   std::string m_dbPath;
-  int64_t m_size;
 };
 
 
diff --git a/src/storage/storage.hpp b/src/storage/storage.hpp
old mode 100755
new mode 100644
index 5286c8a..146368f
--- a/src/storage/storage.hpp
+++ b/src/storage/storage.hpp
@@ -43,14 +43,6 @@
     }
   };
 
-  class ItemMeta
-  {
-  public:
-    int64_t id;
-    Name fullName;
-    ndn::ConstBufferPtr keyLocatorHash;
-  };
-
 public:
   virtual
   ~Storage() = default;
@@ -63,31 +55,38 @@
   insert(const Data& data) = 0;
 
   /**
-   *  @brief  remove the entry in the database by using id
-   *  @param  id   id number of entry in the database
+   *  @brief  remove the entry in the database by full name
+   *  @param  full name   full name of the data
    */
   virtual bool
-  erase(const int64_t id) = 0;
+  erase(const Name& name) = 0;
 
   /**
    *  @brief  get the data from database
-   *  @param  id   id number of each entry in the database, used to find the data
+   *  @param  full name   full name of the data
    */
   virtual std::shared_ptr<Data>
-  read(const int64_t id) = 0;
+  read(const Name& name) = 0;
+
+  /**
+   *  @brief  check if database already has the data
+   *  @param  full name   full name of the data
+   */
+  virtual bool
+  has(const Name& name) = 0;
+
+  /**
+   *  @brief  find the data in database by full name and return it
+   *  @param  full name   full name of the data
+   */
+  virtual std::shared_ptr<Data>
+  find(const Name& name, bool exactMatch = false) = 0;
 
   /**
    *  @brief  return the size of database
    */
-  virtual int64_t
+  virtual uint64_t
   size() = 0;
-
-  /**
-   *  @brief enumerate each entry in database and call the function
-   *         insertItemToIndex to reubuild index from database
-   */
-  virtual void
-  fullEnumerate(const std::function<void(const Storage::ItemMeta)>& f) = 0;
 };
 
 } // namespace repo