blob: a9627ffc4d8e451cef583b7e0bfe8cd867d08352 [file] [log] [blame]
Weiqi Shif0330d52014-07-09 10:54:27 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyevc0e26582017-08-13 21:16:49 -04002/*
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -04003 * Copyright (c) 2014-2019, Regents of the University of California.
Weiqi Shif0330d52014-07-09 10:54:27 -07004 *
5 * This file is part of NDN repo-ng (Next generation of NDN repository).
6 * See AUTHORS.md for complete list of repo-ng authors and contributors.
7 *
8 * repo-ng is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * repo-ng, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
19
Weiqi Shif0330d52014-07-09 10:54:27 -070020#include "sqlite-storage.hpp"
Davide Pesavento5d669612017-09-22 23:49:37 -040021#include "config.hpp"
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040022
23#include <ndn-cxx/util/sha256.hpp>
weijia yuan3aa8d2b2018-03-06 15:35:57 -080024#include <ndn-cxx/util/sqlite3-statement.hpp>
25
Weiqi Shif0330d52014-07-09 10:54:27 -070026#include <boost/filesystem.hpp>
27#include <istream>
28
weijia yuan3aa8d2b2018-03-06 15:35:57 -080029#include <ndn-cxx/util/logger.hpp>
30
Weiqi Shif0330d52014-07-09 10:54:27 -070031namespace repo {
32
weijia yuan3aa8d2b2018-03-06 15:35:57 -080033NDN_LOG_INIT(repo.SqliteStorage);
Wentao Shanga8f3c402014-10-30 14:03:27 -070034
weijia yuan3aa8d2b2018-03-06 15:35:57 -080035SqliteStorage::SqliteStorage(const std::string& dbPath)
Weiqi Shif0330d52014-07-09 10:54:27 -070036{
37 if (dbPath.empty()) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -080038 m_dbPath = std::string("ndn_repo.db");
39 NDN_LOG_DEBUG("Create db file in local location [" << m_dbPath << "]. " );
40 NDN_LOG_DEBUG("You can assign the path using -d option" );
Weiqi Shif0330d52014-07-09 10:54:27 -070041 }
42 else {
43 boost::filesystem::path fsPath(dbPath);
44 boost::filesystem::file_status fsPathStatus = boost::filesystem::status(fsPath);
45 if (!boost::filesystem::is_directory(fsPathStatus)) {
46 if (!boost::filesystem::create_directory(boost::filesystem::path(fsPath))) {
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -040047 NDN_THROW(Error("Folder '" + dbPath + "' does not exists and cannot be created"));
Weiqi Shif0330d52014-07-09 10:54:27 -070048 }
49 }
50
51 m_dbPath = dbPath + "/ndn_repo.db";
52 }
53 initializeRepo();
54}
55
56
57void
58SqliteStorage::initializeRepo()
59{
weijia yuan3aa8d2b2018-03-06 15:35:57 -080060 char* errMsg = nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -070061 int rc = sqlite3_open_v2(m_dbPath.c_str(), &m_db,
62 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
63#ifdef DISABLE_SQLITE3_FS_LOCKING
64 "unix-dotfile"
65#else
weijia yuan3aa8d2b2018-03-06 15:35:57 -080066 nullptr
Weiqi Shif0330d52014-07-09 10:54:27 -070067#endif
weijia yuan3aa8d2b2018-03-06 15:35:57 -080068 );
Weiqi Shif0330d52014-07-09 10:54:27 -070069
70 if (rc == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -080071 // Create a new table named NDN_REPO_V2, distinguish from the old table name(NDN_REPO)
72 sqlite3_exec(m_db, "CREATE TABLE NDN_REPO_V2 (name BLOB, data BLOB);", nullptr, nullptr, &errMsg);
Weiqi Shif0330d52014-07-09 10:54:27 -070073 // Ignore errors (when database already exists, errors are expected)
weijia yuan3aa8d2b2018-03-06 15:35:57 -080074 sqlite3_exec(m_db, "CREATE UNIQUE INDEX index_name ON NDN_REPO_V2 (name);", nullptr, nullptr, &errMsg);
Weiqi Shif0330d52014-07-09 10:54:27 -070075 }
76 else {
weijia yuan3aa8d2b2018-03-06 15:35:57 -080077 NDN_LOG_DEBUG("Database file open failure rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -040078 NDN_THROW(Error("Database file open failure"));
Weiqi Shif0330d52014-07-09 10:54:27 -070079 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -080080
81 // SQLite continues without syncing as soon as it has handed data off to the operating system
82 sqlite3_exec(m_db, "PRAGMA synchronous = OFF;", nullptr, nullptr, &errMsg);
83 // Uses a write-ahead log instead of a rollback journal to implement transactions.
84 sqlite3_exec(m_db, "PRAGMA journal_mode = WAL;", nullptr, nullptr, &errMsg);
Weiqi Shif0330d52014-07-09 10:54:27 -070085}
86
87SqliteStorage::~SqliteStorage()
88{
89 sqlite3_close(m_db);
90}
91
Weiqi Shif0330d52014-07-09 10:54:27 -070092int64_t
93SqliteStorage::insert(const Data& data)
94{
weijia yuan3aa8d2b2018-03-06 15:35:57 -080095 Name name = data.getFullName(); // store the full name
96 ndn::util::Sqlite3Statement stmt(m_db, "INSERT INTO NDN_REPO_V2 (name, data) VALUES (?, ?);");
Weiqi Shif0330d52014-07-09 10:54:27 -070097
Weiqi Shif0330d52014-07-09 10:54:27 -070098 //Insert
weijia yuan3aa8d2b2018-03-06 15:35:57 -080099 // Bind NULL to name value in NDN_REPO_V2 when initialize result.
100 auto result = sqlite3_bind_null(stmt, 1);
Alexander Afanasyevf34a3552017-08-13 21:17:05 -0400101 if (result == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800102 result = stmt.bind(1, name.wireEncode().value(),
103 name.wireEncode().value_size(), SQLITE_STATIC);
Alexander Afanasyevf34a3552017-08-13 21:17:05 -0400104 }
105 if (result == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800106 result = stmt.bind(2, data.wireEncode(), SQLITE_STATIC);
Alexander Afanasyevf34a3552017-08-13 21:17:05 -0400107 }
108
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800109 int id = 0;
Alexander Afanasyevf34a3552017-08-13 21:17:05 -0400110 if (result == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800111 int rc = 0;
112 rc = stmt.step();
Weiqi Shif0330d52014-07-09 10:54:27 -0700113 if (rc == SQLITE_CONSTRAINT) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800114 NDN_LOG_DEBUG("Insert failed");
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400115 NDN_THROW(Error("Insert failed"));
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800116 }
117 sqlite3_reset(stmt);
118 id = sqlite3_last_insert_rowid(m_db);
Weiqi Shif0330d52014-07-09 10:54:27 -0700119 }
120 else {
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400121 NDN_THROW(Error("Some error with insert"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700122 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700123 return id;
124}
125
Weiqi Shif0330d52014-07-09 10:54:27 -0700126bool
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800127SqliteStorage::erase(const Name& name)
Weiqi Shif0330d52014-07-09 10:54:27 -0700128{
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800129 ndn::util::Sqlite3Statement stmt(m_db, "DELETE FROM NDN_REPO_V2 WHERE name = ?;");
Weiqi Shif0330d52014-07-09 10:54:27 -0700130
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800131 auto result = stmt.bind(1,
132 name.wireEncode().value(),
133 name.wireEncode().value_size(), SQLITE_STATIC);
Weiqi Shif0330d52014-07-09 10:54:27 -0700134
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800135 if (result == SQLITE_OK) {
136 int rc = stmt.step();
Weiqi Shif0330d52014-07-09 10:54:27 -0700137 if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800138 NDN_LOG_DEBUG("Node delete error rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400139 NDN_THROW(Error("Node delete error"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700140 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800141 if (sqlite3_changes(m_db) != 1) {
Weiqi Shif0330d52014-07-09 10:54:27 -0700142 return false;
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800143 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700144 }
145 else {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800146 NDN_LOG_DEBUG("delete bind error");
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400147 NDN_THROW(Error("delete bind error"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700148 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700149 return true;
150}
151
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800152std::shared_ptr<Data>
153SqliteStorage::read(const Name& name)
Weiqi Shif0330d52014-07-09 10:54:27 -0700154{
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800155 return find(name);
156}
157
158bool
159SqliteStorage::has(const Name& name)
160{
161 // find exact match
162 return find(name, true) != nullptr;
163}
164
165std::shared_ptr<Data>
166SqliteStorage::find(const Name& name, bool exactMatch)
167{
168 NDN_LOG_DEBUG("Trying to find: " << name);
169 Name nameSuccessor;
170 if (!exactMatch) {
171 nameSuccessor = name.getSuccessor();
172 }
173
174 std::string sql;
175 if (exactMatch)
176 sql = "SELECT * FROM NDN_REPO_V2 WHERE name = ?;";
177 else
178 sql = "SELECT * FROM NDN_REPO_V2 WHERE name >= ? and name < ?;";
179
180 ndn::util::Sqlite3Statement stmt(m_db, sql);
181
182 auto result = stmt.bind(1,
183 name.wireEncode().value(),
184 name.wireEncode().value_size(), SQLITE_STATIC);
185
186 // use getsuccessor to locate prefix match items
187 if (result == SQLITE_OK && !exactMatch) {
188 // use V in TLV for prefix match when there is no exact match
189 result = stmt.bind(2,
190 nameSuccessor.wireEncode().value(),
191 nameSuccessor.wireEncode().value_size(), SQLITE_STATIC);
192 }
193
194 if (result == SQLITE_OK) {
195 int rc = stmt.step();
196 if (rc == SQLITE_ROW) {
197 Name foundName;
198
199 auto data = std::make_shared<Data>();
200 try {
201 data->wireDecode(stmt.getBlock(1));
Weiqi Shif0330d52014-07-09 10:54:27 -0700202 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800203 catch (const ndn::Block::Error& error) {
204 NDN_LOG_DEBUG(error.what());
Davide Pesavento5d669612017-09-22 23:49:37 -0400205 return nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -0700206 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800207 NDN_LOG_DEBUG("Data from db: " << *data);
208
209 foundName = data->getFullName();
210
211 if ((exactMatch && name == foundName) || (!exactMatch && name.isPrefixOf(foundName))) {
212 NDN_LOG_DEBUG("Found: " << foundName << " " << stmt.getInt(0));
213 return data;
Weiqi Shif0330d52014-07-09 10:54:27 -0700214 }
215 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800216 else if (rc == SQLITE_DONE) {
217 return nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -0700218 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800219 else {
220 NDN_LOG_DEBUG("Database query failure rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400221 NDN_THROW(Error("Database query failure"));
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800222 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700223 }
224 else {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800225 NDN_LOG_DEBUG("select bind error");
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400226 NDN_THROW(Error("select bind error"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700227 }
Davide Pesavento5d669612017-09-22 23:49:37 -0400228 return nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -0700229}
230
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400231void
232SqliteStorage::forEach(const std::function<void(const Name&)>& f)
233{
234 ndn::util::Sqlite3Statement stmt(m_db, "SELECT data FROM NDN_REPO_V2;");
235
236 while (true) {
237 int rc = stmt.step();
238 if (rc == SQLITE_ROW) {
239 Data data;
240 try {
241 data.wireDecode(stmt.getBlock(0));
242 }
243 catch (const ndn::Block::Error& error) {
244 NDN_LOG_DEBUG("Error while decoding data from the database: " << error.what());
245 continue;
246 }
247 f(data.getName());
248 }
249 else if (rc == SQLITE_DONE) {
250 break;
251 }
252 else {
253 NDN_THROW(Error("Database query failure (code: " + ndn::to_string(rc)));
254 }
255 }
256}
257
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800258uint64_t
Weiqi Shif0330d52014-07-09 10:54:27 -0700259SqliteStorage::size()
260{
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800261 ndn::util::Sqlite3Statement stmt(m_db, "SELECT count(*) FROM NDN_REPO_V2;");
Weiqi Shif0330d52014-07-09 10:54:27 -0700262
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800263 int rc = stmt.step();
264 if (rc != SQLITE_ROW) {
265 NDN_LOG_DEBUG("Database query failure rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400266 NDN_THROW(Error("Database query failure"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700267 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800268
269 uint64_t nData = stmt.getInt(0);
270 return nData;
Weiqi Shif0330d52014-07-09 10:54:27 -0700271}
272
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800273} // namespace repo