blob: a77f2a5d5e141d4a55170c53230976cd81ba5267 [file] [log] [blame]
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev1cf5c432017-01-13 23:22:15 -08003 * Copyright (c) 2013-2017, Regents of the University of California.
Alexander Afanasyev68f2a952013-01-08 14:34:16 -08004 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08005 * This file is part of ChronoShare, a decentralized file sharing application over NDN.
Alexander Afanasyev68f2a952013-01-08 14:34:16 -08006 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08007 * ChronoShare is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080010 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -080011 * ChronoShare is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080014 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -080015 * You should have received copies of the GNU General Public License along with
16 * ChronoShare, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ChronoShare authors and contributors.
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080019 */
20
Lijing Wang9f77a8c2016-12-25 14:44:04 -080021#include "object-db.hpp"
22#include "db-helper.hpp"
23#include "core/logging.hpp"
24
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080025#include <iostream>
26#include <sys/stat.h>
Alexander Afanasyeva35756b2013-01-22 16:59:11 -080027
Lijing Wang9f77a8c2016-12-25 14:44:04 -080028#include <ndn-cxx/util/sqlite3-statement.hpp>
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080029
Lijing Wang9f77a8c2016-12-25 14:44:04 -080030namespace ndn {
31namespace chronoshare {
32
33_LOG_INIT(ObjectDb);
34
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080035namespace fs = boost::filesystem;
Lijing Wang9f77a8c2016-12-25 14:44:04 -080036using ndn::util::Sqlite3Statement;
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080037
Lijing Wang9f77a8c2016-12-25 14:44:04 -080038const std::string INIT_DATABASE = R"SQL(
39CREATE TABLE
40 File(
41 device_name BLOB NOT NULL,
42 segment INTEGER,
43 content_object BLOB,
44
45 PRIMARY KEY (device_name, segment)
46 );
47CREATE INDEX device ON File(device_name);
48)SQL";
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080049
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080050ObjectDb::ObjectDb(const fs::path& folder, const std::string& hash)
Lijing Wang9f77a8c2016-12-25 14:44:04 -080051 : m_lastUsed(time::steady_clock::now())
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080052{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080053 fs::path actualFolder = folder / "objects" / hash.substr(0, 2);
54 fs::create_directories(actualFolder);
Alexander Afanasyeva35756b2013-01-22 16:59:11 -080055
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080056 _LOG_DEBUG("Open " << (actualFolder / hash.substr(2, hash.size() - 2)));
Alexander Afanasyev1807e8d2013-01-24 23:37:32 -080057
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080058 int res = sqlite3_open((actualFolder / hash.substr(2, hash.size() - 2)).c_str(), &m_db);
59 if (res != SQLITE_OK) {
Lijing Wang9f77a8c2016-12-25 14:44:04 -080060 BOOST_THROW_EXCEPTION(Error("Cannot open/create database: [" +
61 (actualFolder / hash.substr(2, hash.size() - 2)).string() + "]"));
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080062 }
Alexander Afanasyeva35756b2013-01-22 16:59:11 -080063
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080064 // Alex: determine if tables initialized. if not, initialize... not sure what is the best way to go...
65 // for now, just attempt to create everything
66
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080067 char* errmsg = 0;
Lijing Wang9f77a8c2016-12-25 14:44:04 -080068 res = sqlite3_exec(m_db, INIT_DATABASE.c_str(), nullptr, nullptr, &errmsg);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080069 if (res != SQLITE_OK && errmsg != 0) {
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080070 sqlite3_free(errmsg);
71 }
Alexander Afanasyevb2e608d2013-01-23 20:00:53 -080072
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080073 willStartSave();
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080074}
75
Lijing Wang9f77a8c2016-12-25 14:44:04 -080076ObjectDb::~ObjectDb()
77{
78 didStopSave();
79
80 int res = sqlite3_close(m_db);
81 if (res != SQLITE_OK) {
82 // complain
83 }
84}
85
Alexander Afanasyevdbc06712013-01-08 18:30:28 -080086bool
Lijing Wang9f77a8c2016-12-25 14:44:04 -080087ObjectDb::doesExist(const boost::filesystem::path& folder, const Name& deviceName,
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080088 const std::string& hash)
Alexander Afanasyevdbc06712013-01-08 18:30:28 -080089{
Lijing Wang9f77a8c2016-12-25 14:44:04 -080090 BOOST_ASSERT(hash.size() > 2);
91
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080092 fs::path actualFolder = folder / "objects" / hash.substr(0, 2);
Alexander Afanasyevdbc06712013-01-08 18:30:28 -080093 bool retval = false;
94
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080095 sqlite3* db;
96 int res = sqlite3_open((actualFolder / hash.substr(2, hash.size() - 2)).c_str(), &db);
97 if (res == SQLITE_OK) {
98 sqlite3_stmt* stmt;
99 sqlite3_prepare_v2(db,
100 "SELECT count(*), count(nullif(content_object,0)) FROM File WHERE device_name=?",
101 -1, &stmt, 0);
Alexander Afanasyevdbc06712013-01-08 18:30:28 -0800102
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800103 sqlite3_bind_blob(stmt, 1, deviceName.wireEncode().wire(), deviceName.wireEncode().size(),
104 SQLITE_TRANSIENT);
Alexander Afanasyevdbc06712013-01-08 18:30:28 -0800105
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800106 int res = sqlite3_step(stmt);
107 if (res == SQLITE_ROW) {
108 int countAll = sqlite3_column_int(stmt, 0);
109 int countNonNull = sqlite3_column_int(stmt, 1);
Alexander Afanasyevdbc06712013-01-08 18:30:28 -0800110
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800111 _LOG_TRACE("Total segments: " << countAll << ", non-empty segments: " << countNonNull);
Alexander Afanasyevdbc06712013-01-08 18:30:28 -0800112
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800113 if (countAll > 0 && countAll == countNonNull) {
114 retval = true;
115 }
Alexander Afanasyevdbc06712013-01-08 18:30:28 -0800116 }
117
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800118 sqlite3_finalize(stmt);
119 }
120
121 sqlite3_close(db);
Alexander Afanasyevdbc06712013-01-08 18:30:28 -0800122 return retval;
123}
124
Alexander Afanasyev68f2a952013-01-08 14:34:16 -0800125void
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800126ObjectDb::saveContentObject(const Name& deviceName, sqlite3_int64 segment, const Data& data)
Alexander Afanasyev68f2a952013-01-08 14:34:16 -0800127{
Zhenkai Zhu92bb6952013-02-06 16:43:30 -0800128 // update last used time
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800129 m_lastUsed = time::steady_clock::now();
130
131 Sqlite3Statement stmt(m_db, "INSERT INTO File "
132 "(device_name, segment, content_object) "
133 "VALUES (?, ?, ?)");
134
135 _LOG_DEBUG("Saving content object for [" << deviceName << ", seqno: " << segment << ", size: "
136 << data.wireEncode().size()
137 << "]");
138 stmt.bind(1, deviceName.wireEncode(), SQLITE_STATIC);
139 stmt.bind(2, segment);
140 stmt.bind(3, data.wireEncode(), SQLITE_STATIC);
141 stmt.step();
Alexander Afanasyev68f2a952013-01-08 14:34:16 -0800142}
143
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800144shared_ptr<Data>
145ObjectDb::fetchSegment(const Name& deviceName, sqlite3_int64 segment)
Alexander Afanasyev68f2a952013-01-08 14:34:16 -0800146{
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800147 // update last used time
148 m_lastUsed = time::steady_clock::now();
Alexander Afanasyev68f2a952013-01-08 14:34:16 -0800149
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800150 Sqlite3Statement stmt(m_db, "SELECT content_object FROM File WHERE device_name=? AND segment=?");
151 stmt.bind(1, deviceName.wireEncode(), SQLITE_STATIC);
152 stmt.bind(2, segment);
Alexander Afanasyev68f2a952013-01-08 14:34:16 -0800153
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800154 if (stmt.step() == SQLITE_ROW) {
155 return make_shared<Data>(stmt.getBlock(0));
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800156 }
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800157 else {
158 return nullptr;
159 }
Alexander Afanasyev68f2a952013-01-08 14:34:16 -0800160}
161
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800162const time::steady_clock::TimePoint&
163ObjectDb::getLastUsed() const
Zhenkai Zhu92bb6952013-02-06 16:43:30 -0800164{
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800165 return m_lastUsed;
Zhenkai Zhu92bb6952013-02-06 16:43:30 -0800166}
Alexander Afanasyevdbc06712013-01-08 18:30:28 -0800167
Alexander Afanasyevb2e608d2013-01-23 20:00:53 -0800168void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800169ObjectDb::willStartSave()
Alexander Afanasyevb2e608d2013-01-23 20:00:53 -0800170{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800171 sqlite3_exec(m_db, "BEGIN TRANSACTION;", 0, 0, 0);
Alexander Afanasyevb2e608d2013-01-23 20:00:53 -0800172}
173
174void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800175ObjectDb::didStopSave()
Alexander Afanasyevb2e608d2013-01-23 20:00:53 -0800176{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800177 sqlite3_exec(m_db, "END TRANSACTION;", 0, 0, 0);
Alexander Afanasyevb2e608d2013-01-23 20:00:53 -0800178}
Lijing Wang9f77a8c2016-12-25 14:44:04 -0800179
180} // namespace chronoshare
181} // namespace ndn