blob: d87707f258724a8e03b5f72ba41a19c08b94ac96 [file] [log] [blame]
Yingdi Yu0c3e5912015-03-17 14:22:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyevbe998ac2017-05-06 13:11:42 -07003 * Copyright (c) 2014-2017, Regents of the University of California
Yingdi Yu0c3e5912015-03-17 14:22:38 -07004 *
Alexander Afanasyevbe998ac2017-05-06 13:11:42 -07005 * This file is part of NDN DeLorean, An Authentication System for Data Archives in
6 * Named Data Networking. See AUTHORS.md for complete list of NDN DeLorean authors
7 * and contributors.
Yingdi Yu0c3e5912015-03-17 14:22:38 -07008 *
Alexander Afanasyevbe998ac2017-05-06 13:11:42 -07009 * NDN DeLorean is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
Yingdi Yu0c3e5912015-03-17 14:22:38 -070013 *
Alexander Afanasyevbe998ac2017-05-06 13:11:42 -070014 * NDN DeLorean is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
16 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
Yingdi Yu0c3e5912015-03-17 14:22:38 -070017 *
Alexander Afanasyevbe998ac2017-05-06 13:11:42 -070018 * You should have received a copy of the GNU General Public License along with NDN
19 * DeLorean, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
Yingdi Yu0c3e5912015-03-17 14:22:38 -070020 */
21
22#include "db.hpp"
23
24#include <sqlite3.h>
25#include <string>
26#include <boost/filesystem.hpp>
27
Alexander Afanasyev49e2e4c2017-05-06 13:42:57 -070028namespace ndn {
29namespace delorean {
Yingdi Yu0c3e5912015-03-17 14:22:38 -070030
31static const std::string INITIALIZATION =
32 "CREATE TABLE IF NOT EXISTS \n"
33 " cTrees( \n"
34 " id INTEGER PRIMARY KEY,\n"
35 " level INTEGER NOT NULL, \n"
36 " seqNo INTEGER NOT NULL, \n"
37 " data BLOB NOT NULL \n"
38 " ); \n"
39 "CREATE UNIQUE INDEX IF NOT EXISTS \n"
40 " cTreeIndex ON cTrees(level, seqNo); \n"
41 "CREATE TRIGGER IF NOT EXISTS \n"
42 " cTrees_after_insert_trigger \n"
43 " AFTER INSERT ON cTrees \n"
44 " FOR EACH ROW \n"
45 " BEGIN \n"
46 " DELETE FROM pTrees \n"
47 " WHERE level=NEW.level AND seqNo=NEW.seqNo;\n"
48 " END; \n"
49 " \n"
50 "CREATE TABLE IF NOT EXISTS \n"
51 " pTrees( \n"
52 " id INTEGER PRIMARY KEY,\n"
53 " level INTEGER NOT NULL, \n"
54 " seqNo INTEGER NOT NULL, \n"
55 " nextLeafSeqNo INTEGER NOT NULL, \n"
56 " data BLOB NOT NULL \n"
57 " ); \n"
58 "CREATE UNIQUE INDEX IF NOT EXISTS \n"
59 " pTreeIndex ON pTrees(level, seqNo); \n"
60 " \n"
61 "CREATE TABLE IF NOT EXISTS \n"
62 " leaves( \n"
63 " id INTEGER PRIMARY KEY,\n"
64 " dataSeqNo INTEGER NOT NULL, \n"
65 " dataName BLOB NOT NULL, \n"
66 " signerSeqNo INTEGER NOT NULL, \n"
67 " timestamp INTEGER NOT NULL, \n"
68 " isCert INTEGER DEFAULT 0, \n"
69 " cert BLOB \n"
70 " ); \n"
71 "CREATE UNIQUE INDEX IF NOT EXISTS \n"
72 " leavesIndex ON leaves(dataSeqNo); \n";
73
74
75/**
76 * A utility function to call the normal sqlite3_bind_blob where the value and length are
77 * block.wire() and block.size().
78 */
79static int
80sqlite3_bind_block(sqlite3_stmt* statement,
81 int index,
82 const Block& block,
83 void(*destructor)(void*))
84{
85 return sqlite3_bind_blob(statement, index, block.wire(), block.size(), destructor);
86}
87
88/**
89 * A utility function to generate block by calling the normal sqlite3_column_text.
90 */
91static Block
92sqlite3_column_block(sqlite3_stmt* statement, int column)
93{
94 return Block(sqlite3_column_blob(statement, column), sqlite3_column_bytes(statement, column));
95}
96
97void
98Db::open(const std::string& dbDir)
99{
100 // Determine the path of logger database
101 if (dbDir == "")
102 throw Error("Db: empty db path");
103
104 boost::filesystem::path dir = boost::filesystem::path(dbDir);
105 boost::filesystem::create_directories(dir);
106
107 // Open database
108 int result = sqlite3_open_v2((dir / "sig-logger.db").c_str(), &m_db,
109 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
Alexander Afanasyevbe998ac2017-05-06 13:11:42 -0700110#ifdef NDN_DELOREAN_DISABLE_SQLITE3_FS_LOCKING
Yingdi Yu0c3e5912015-03-17 14:22:38 -0700111 "unix-dotfile"
112#else
113 nullptr
114#endif
115 );
116
117 if (result != SQLITE_OK)
118 throw Error("SigLogger DB cannot be opened/created: " + dbDir);
119
120 // initialize SigLogger specific tables
121 char* errorMessage = nullptr;
122 result = sqlite3_exec(m_db, INITIALIZATION.c_str(), nullptr, nullptr, &errorMessage);
123 if (result != SQLITE_OK && errorMessage != nullptr) {
124 sqlite3_free(errorMessage);
125 throw Error("SigLogger DB cannot be initialized");
126 }
127
128 getMaxLeafSeq();
129}
130
131bool
132Db::insertSubTreeData(size_t level, const NonNegativeInteger& seqNo,
133 const Data& data,
134 bool isFull, const NonNegativeInteger& nextLeafSeqNo)
135{
136 sqlite3_stmt* statement;
137 if (isFull) {
138 sqlite3_prepare_v2(m_db,
139 "INSERT INTO cTrees (level, seqNo, data) VALUES (?, ?, ?)",
140 -1, &statement, nullptr);
141 }
142 else {
143 sqlite3_prepare_v2(m_db,
144 "INSERT OR REPLACE INTO pTrees (level, seqNo, data, nextLeafSeqNo)\
145 VALUES (?, ?, ?, ?)",
146 -1, &statement, nullptr);
147 }
148 sqlite3_bind_int(statement, 1, level);
149 sqlite3_bind_int(statement, 2, seqNo);
150 sqlite3_bind_block(statement, 3, data.wireEncode(), SQLITE_TRANSIENT);
151 if (!isFull)
152 sqlite3_bind_int(statement, 4, nextLeafSeqNo);
153
154 int result = sqlite3_step(statement);
155 sqlite3_finalize(statement);
156
157 if (result == SQLITE_OK)
158 return true;
159 return false;
160}
161
162shared_ptr<Data>
163Db::getSubTreeData(size_t level, const NonNegativeInteger& seqNo)
164{
165 sqlite3_stmt* statement;
166 sqlite3_prepare_v2(m_db,
167 "SELECT data FROM cTrees WHERE level=? AND seqNo=?",
168 -1, &statement, nullptr);
169 sqlite3_bind_int(statement, 1, level);
170 sqlite3_bind_int(statement, 2, seqNo);
171
172 if (sqlite3_step(statement) == SQLITE_ROW) {
173 auto result = make_shared<Data>(sqlite3_column_block(statement, 0));
174 sqlite3_finalize(statement);
175 return result;
176 }
177
178 sqlite3_prepare_v2(m_db,
179 "SELECT data FROM pTrees WHERE level=? AND seqNo=?",
180 -1, &statement, nullptr);
181 sqlite3_bind_int(statement, 1, level);
182 sqlite3_bind_int(statement, 2, seqNo);
183
184 shared_ptr<Data> result;
185 if (sqlite3_step(statement) == SQLITE_ROW)
186 result = make_shared<Data>(sqlite3_column_block(statement, 0));
187
188 sqlite3_finalize(statement);
189 return result;
190}
191
192std::vector<shared_ptr<Data>>
193Db::getPendingSubTrees()
194{
195 sqlite3_stmt* statement;
196 sqlite3_prepare_v2(m_db,
197 "SELECT data FROM pTrees ORDER BY level DESC",
198 -1, &statement, nullptr);
199
200 std::vector<shared_ptr<Data>> datas;
201 while (sqlite3_step(statement) == SQLITE_ROW)
202 datas.push_back(make_shared<Data>(sqlite3_column_block(statement, 0)));
203
204 sqlite3_finalize(statement);
205 return datas;
206}
207
208bool
209Db::insertLeafData(const Leaf& leaf)
210{
211 if (leaf.getDataSeqNo() != m_nextLeafSeqNo)
212 return false;
213
214 sqlite3_stmt* statement;
215 sqlite3_prepare_v2(m_db,
216 "INSERT INTO leaves (dataSeqNo, dataName, signerSeqNo, timestamp, isCert)\
217 VALUES (?, ?, ?, ?, 0)",
218 -1, &statement, nullptr);
219
220 sqlite3_bind_int(statement, 1, leaf.getDataSeqNo());
221 sqlite3_bind_block(statement, 2, leaf.getDataName().wireEncode(), SQLITE_TRANSIENT);
222 sqlite3_bind_int(statement, 3, leaf.getSignerSeqNo());
223 sqlite3_bind_int(statement, 4, leaf.getTimestamp());
224
225 int result = sqlite3_step(statement);
226 sqlite3_finalize(statement);
227
228 if (result == SQLITE_OK || result == SQLITE_DONE) {
229 m_nextLeafSeqNo++;
230 return true;
231 }
232
233 return false;
234}
235
236bool
237Db::insertLeafData(const Leaf& leaf, const Data& data)
238{
239 if (leaf.getDataSeqNo() != m_nextLeafSeqNo)
240 return false;
241
242 sqlite3_stmt* statement;
243 sqlite3_prepare_v2(m_db,
244 "INSERT INTO leaves (dataSeqNo, dataName, signerSeqNo, timestamp, isCert, cert)\
245 VALUES (?, ?, ?, ?, 1, ?)",
246 -1, &statement, nullptr);
247
248 sqlite3_bind_int(statement, 1, leaf.getDataSeqNo());
249 sqlite3_bind_block(statement, 2, leaf.getDataName().wireEncode(), SQLITE_TRANSIENT);
250 sqlite3_bind_int(statement, 3, leaf.getSignerSeqNo());
251 sqlite3_bind_int(statement, 4, leaf.getTimestamp());
252 sqlite3_bind_block(statement, 5, data.wireEncode(), SQLITE_TRANSIENT);
253
254 int result = sqlite3_step(statement);
255 sqlite3_finalize(statement);
256
257 if (result == SQLITE_OK || result == SQLITE_DONE) {
258 m_nextLeafSeqNo++;
259 return true;
260 }
261
262 return false;
263}
264
265std::pair<shared_ptr<Leaf>, shared_ptr<Data>>
266Db::getLeaf(const NonNegativeInteger& seqNo)
267{
268 sqlite3_stmt* statement;
269 sqlite3_prepare_v2(m_db,
270 "SELECT dataName, signerSeqNo, timestamp, cert\
271 FROM leaves WHERE dataSeqNo=?",
272 -1, &statement, nullptr);
273
274 sqlite3_bind_int(statement, 1, seqNo);
275
276 if (sqlite3_step(statement) == SQLITE_ROW) {
277 auto leaf = make_shared<Leaf>(Name(sqlite3_column_block(statement, 0)),
278 sqlite3_column_int(statement, 2),
279 seqNo,
280 sqlite3_column_int(statement, 1));
281
282 shared_ptr<Data> data;
283 if (sqlite3_column_bytes(statement, 3) != 0) {
284 data = make_shared<Data>(sqlite3_column_block(statement, 3));
285 }
286 sqlite3_finalize(statement);
287 return std::make_pair(leaf, data);
288 }
289 else {
290 sqlite3_finalize(statement);
291 return std::make_pair(nullptr, nullptr);
292 }
293}
294
295const NonNegativeInteger&
296Db::getMaxLeafSeq()
297{
298 sqlite3_stmt* statement;
299
300 sqlite3_prepare_v2(m_db, "SELECT count(dataSeqNo) FROM leaves", -1, &statement, nullptr);
301 if (sqlite3_step(statement) == SQLITE_ROW)
302 m_nextLeafSeqNo = sqlite3_column_int(statement, 0);
303 else
304 throw Error("getMaxLeafSeq: db error");
305
306 sqlite3_finalize(statement);
307 return m_nextLeafSeqNo;
308}
309
Alexander Afanasyev49e2e4c2017-05-06 13:42:57 -0700310} // namespace delorean
311} // namespace ndn