blob: 0ee3f7c091dfc1fce61f57fe09c2f4fda2cc403c [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/*
Davide Pesavento01a2a8c2024-12-13 14:25:50 -05003 * Copyright (c) 2014-2024, 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"
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040021
Davide Pesavento01a2a8c2024-12-13 14:25:50 -050022#include <ndn-cxx/util/logger.hpp>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040023#include <ndn-cxx/util/sha256.hpp>
weijia yuan3aa8d2b2018-03-06 15:35:57 -080024#include <ndn-cxx/util/sqlite3-statement.hpp>
25
Davide Pesavento01a2a8c2024-12-13 14:25:50 -050026#include <filesystem>
weijia yuan3aa8d2b2018-03-06 15:35:57 -080027
Weiqi Shif0330d52014-07-09 10:54:27 -070028namespace repo {
29
weijia yuan3aa8d2b2018-03-06 15:35:57 -080030NDN_LOG_INIT(repo.SqliteStorage);
Wentao Shanga8f3c402014-10-30 14:03:27 -070031
weijia yuan3aa8d2b2018-03-06 15:35:57 -080032SqliteStorage::SqliteStorage(const std::string& dbPath)
Weiqi Shif0330d52014-07-09 10:54:27 -070033{
34 if (dbPath.empty()) {
Davide Pesavento01a2a8c2024-12-13 14:25:50 -050035 m_dbPath = "ndn_repo.db";
Weiqi Shif0330d52014-07-09 10:54:27 -070036 }
37 else {
Davide Pesavento01a2a8c2024-12-13 14:25:50 -050038 std::filesystem::path fsPath(dbPath);
39 if (!std::filesystem::is_directory(fsPath)) {
40 if (!std::filesystem::create_directory(fsPath)) {
41 NDN_THROW(Error("Directory '" + dbPath + "' does not exists and cannot be created"));
Weiqi Shif0330d52014-07-09 10:54:27 -070042 }
43 }
Weiqi Shif0330d52014-07-09 10:54:27 -070044 m_dbPath = dbPath + "/ndn_repo.db";
45 }
Davide Pesavento01a2a8c2024-12-13 14:25:50 -050046
47 NDN_LOG_DEBUG("Using database file " << m_dbPath);
Weiqi Shif0330d52014-07-09 10:54:27 -070048 initializeRepo();
49}
50
Weiqi Shif0330d52014-07-09 10:54:27 -070051void
52SqliteStorage::initializeRepo()
53{
weijia yuan3aa8d2b2018-03-06 15:35:57 -080054 char* errMsg = nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -070055 int rc = sqlite3_open_v2(m_dbPath.c_str(), &m_db,
56 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
57#ifdef DISABLE_SQLITE3_FS_LOCKING
58 "unix-dotfile"
59#else
weijia yuan3aa8d2b2018-03-06 15:35:57 -080060 nullptr
Weiqi Shif0330d52014-07-09 10:54:27 -070061#endif
weijia yuan3aa8d2b2018-03-06 15:35:57 -080062 );
Weiqi Shif0330d52014-07-09 10:54:27 -070063
64 if (rc == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -080065 // Create a new table named NDN_REPO_V2, distinguish from the old table name(NDN_REPO)
66 sqlite3_exec(m_db, "CREATE TABLE NDN_REPO_V2 (name BLOB, data BLOB);", nullptr, nullptr, &errMsg);
Weiqi Shif0330d52014-07-09 10:54:27 -070067 // Ignore errors (when database already exists, errors are expected)
weijia yuan3aa8d2b2018-03-06 15:35:57 -080068 sqlite3_exec(m_db, "CREATE UNIQUE INDEX index_name ON NDN_REPO_V2 (name);", nullptr, nullptr, &errMsg);
Weiqi Shif0330d52014-07-09 10:54:27 -070069 }
70 else {
weijia yuan3aa8d2b2018-03-06 15:35:57 -080071 NDN_LOG_DEBUG("Database file open failure rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -040072 NDN_THROW(Error("Database file open failure"));
Weiqi Shif0330d52014-07-09 10:54:27 -070073 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -080074
75 // SQLite continues without syncing as soon as it has handed data off to the operating system
76 sqlite3_exec(m_db, "PRAGMA synchronous = OFF;", nullptr, nullptr, &errMsg);
77 // Uses a write-ahead log instead of a rollback journal to implement transactions.
78 sqlite3_exec(m_db, "PRAGMA journal_mode = WAL;", nullptr, nullptr, &errMsg);
Weiqi Shif0330d52014-07-09 10:54:27 -070079}
80
81SqliteStorage::~SqliteStorage()
82{
83 sqlite3_close(m_db);
84}
85
Weiqi Shif0330d52014-07-09 10:54:27 -070086int64_t
87SqliteStorage::insert(const Data& data)
88{
weijia yuan3aa8d2b2018-03-06 15:35:57 -080089 Name name = data.getFullName(); // store the full name
90 ndn::util::Sqlite3Statement stmt(m_db, "INSERT INTO NDN_REPO_V2 (name, data) VALUES (?, ?);");
Weiqi Shif0330d52014-07-09 10:54:27 -070091
Davide Pesavento01a2a8c2024-12-13 14:25:50 -050092 // Insert
weijia yuan3aa8d2b2018-03-06 15:35:57 -080093 // Bind NULL to name value in NDN_REPO_V2 when initialize result.
94 auto result = sqlite3_bind_null(stmt, 1);
Alexander Afanasyevf34a3552017-08-13 21:17:05 -040095 if (result == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -080096 result = stmt.bind(1, name.wireEncode().value(),
97 name.wireEncode().value_size(), SQLITE_STATIC);
Alexander Afanasyevf34a3552017-08-13 21:17:05 -040098 }
99 if (result == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800100 result = stmt.bind(2, data.wireEncode(), SQLITE_STATIC);
Alexander Afanasyevf34a3552017-08-13 21:17:05 -0400101 }
102
103 if (result == SQLITE_OK) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800104 int rc = 0;
105 rc = stmt.step();
Weiqi Shif0330d52014-07-09 10:54:27 -0700106 if (rc == SQLITE_CONSTRAINT) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800107 NDN_LOG_DEBUG("Insert failed");
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400108 NDN_THROW(Error("Insert failed"));
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800109 }
110 sqlite3_reset(stmt);
Davide Pesavento01a2a8c2024-12-13 14:25:50 -0500111 return sqlite3_last_insert_rowid(m_db);
Weiqi Shif0330d52014-07-09 10:54:27 -0700112 }
113 else {
Davide Pesavento01a2a8c2024-12-13 14:25:50 -0500114 NDN_THROW(Error("Database insert failure (code: " + std::to_string(result)));
Weiqi Shif0330d52014-07-09 10:54:27 -0700115 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700116}
117
Weiqi Shif0330d52014-07-09 10:54:27 -0700118bool
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800119SqliteStorage::erase(const Name& name)
Weiqi Shif0330d52014-07-09 10:54:27 -0700120{
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800121 ndn::util::Sqlite3Statement stmt(m_db, "DELETE FROM NDN_REPO_V2 WHERE name = ?;");
Weiqi Shif0330d52014-07-09 10:54:27 -0700122
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800123 auto result = stmt.bind(1,
124 name.wireEncode().value(),
125 name.wireEncode().value_size(), SQLITE_STATIC);
Weiqi Shif0330d52014-07-09 10:54:27 -0700126
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800127 if (result == SQLITE_OK) {
128 int rc = stmt.step();
Weiqi Shif0330d52014-07-09 10:54:27 -0700129 if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800130 NDN_LOG_DEBUG("Node delete error rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400131 NDN_THROW(Error("Node delete error"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700132 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800133 if (sqlite3_changes(m_db) != 1) {
Weiqi Shif0330d52014-07-09 10:54:27 -0700134 return false;
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800135 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700136 }
137 else {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800138 NDN_LOG_DEBUG("delete bind error");
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400139 NDN_THROW(Error("delete bind error"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700140 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700141 return true;
142}
143
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800144std::shared_ptr<Data>
145SqliteStorage::read(const Name& name)
Weiqi Shif0330d52014-07-09 10:54:27 -0700146{
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800147 return find(name);
148}
149
150bool
151SqliteStorage::has(const Name& name)
152{
153 // find exact match
154 return find(name, true) != nullptr;
155}
156
157std::shared_ptr<Data>
158SqliteStorage::find(const Name& name, bool exactMatch)
159{
160 NDN_LOG_DEBUG("Trying to find: " << name);
161 Name nameSuccessor;
162 if (!exactMatch) {
163 nameSuccessor = name.getSuccessor();
164 }
165
166 std::string sql;
167 if (exactMatch)
168 sql = "SELECT * FROM NDN_REPO_V2 WHERE name = ?;";
169 else
170 sql = "SELECT * FROM NDN_REPO_V2 WHERE name >= ? and name < ?;";
171
172 ndn::util::Sqlite3Statement stmt(m_db, sql);
173
174 auto result = stmt.bind(1,
175 name.wireEncode().value(),
176 name.wireEncode().value_size(), SQLITE_STATIC);
177
178 // use getsuccessor to locate prefix match items
179 if (result == SQLITE_OK && !exactMatch) {
180 // use V in TLV for prefix match when there is no exact match
181 result = stmt.bind(2,
182 nameSuccessor.wireEncode().value(),
183 nameSuccessor.wireEncode().value_size(), SQLITE_STATIC);
184 }
185
186 if (result == SQLITE_OK) {
187 int rc = stmt.step();
188 if (rc == SQLITE_ROW) {
189 Name foundName;
190
191 auto data = std::make_shared<Data>();
192 try {
193 data->wireDecode(stmt.getBlock(1));
Weiqi Shif0330d52014-07-09 10:54:27 -0700194 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800195 catch (const ndn::Block::Error& error) {
196 NDN_LOG_DEBUG(error.what());
Davide Pesavento5d669612017-09-22 23:49:37 -0400197 return nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -0700198 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800199 NDN_LOG_DEBUG("Data from db: " << *data);
200
201 foundName = data->getFullName();
202
203 if ((exactMatch && name == foundName) || (!exactMatch && name.isPrefixOf(foundName))) {
204 NDN_LOG_DEBUG("Found: " << foundName << " " << stmt.getInt(0));
205 return data;
Weiqi Shif0330d52014-07-09 10:54:27 -0700206 }
207 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800208 else if (rc == SQLITE_DONE) {
209 return nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -0700210 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800211 else {
212 NDN_LOG_DEBUG("Database query failure rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400213 NDN_THROW(Error("Database query failure"));
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800214 }
Weiqi Shif0330d52014-07-09 10:54:27 -0700215 }
216 else {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800217 NDN_LOG_DEBUG("select bind error");
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400218 NDN_THROW(Error("select bind error"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700219 }
Davide Pesavento5d669612017-09-22 23:49:37 -0400220 return nullptr;
Weiqi Shif0330d52014-07-09 10:54:27 -0700221}
222
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400223void
224SqliteStorage::forEach(const std::function<void(const Name&)>& f)
225{
226 ndn::util::Sqlite3Statement stmt(m_db, "SELECT data FROM NDN_REPO_V2;");
227
228 while (true) {
229 int rc = stmt.step();
230 if (rc == SQLITE_ROW) {
231 Data data;
232 try {
233 data.wireDecode(stmt.getBlock(0));
234 }
235 catch (const ndn::Block::Error& error) {
236 NDN_LOG_DEBUG("Error while decoding data from the database: " << error.what());
237 continue;
238 }
239 f(data.getName());
240 }
241 else if (rc == SQLITE_DONE) {
242 break;
243 }
244 else {
Davide Pesavento01a2a8c2024-12-13 14:25:50 -0500245 NDN_THROW(Error("Database query failure (code: " + std::to_string(rc)));
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400246 }
247 }
248}
249
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800250uint64_t
Weiqi Shif0330d52014-07-09 10:54:27 -0700251SqliteStorage::size()
252{
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800253 ndn::util::Sqlite3Statement stmt(m_db, "SELECT count(*) FROM NDN_REPO_V2;");
Weiqi Shif0330d52014-07-09 10:54:27 -0700254
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800255 int rc = stmt.step();
256 if (rc != SQLITE_ROW) {
257 NDN_LOG_DEBUG("Database query failure rc:" << rc);
Alexander Afanasyev5c16cc22019-04-02 14:17:12 -0400258 NDN_THROW(Error("Database query failure"));
Weiqi Shif0330d52014-07-09 10:54:27 -0700259 }
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800260
Davide Pesavento01a2a8c2024-12-13 14:25:50 -0500261 return stmt.getInt(0);
Weiqi Shif0330d52014-07-09 10:54:27 -0700262}
263
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800264} // namespace repo