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