blob: b925f2d1fcdec09f392109015645126c3c9554da [file] [log] [blame]
Shock Jiang3016c982014-11-11 11:35:17 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Yumin Xia2c509c22017-02-09 14:37:36 -08002/*
3 * Copyright (c) 2014-2017, Regents of the University of California.
Shock Jiang3016c982014-11-11 11:35:17 -08004 *
5 * This file is part of NDNS (Named Data Networking Domain Name Service).
6 * See AUTHORS.md for complete list of NDNS authors and contributors.
7 *
8 * NDNS 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 * NDNS 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 * NDNS, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "db-mgr.hpp"
21#include "logger.hpp"
22#include "clients/response.hpp"
23
24#include <iostream>
25#include <fstream>
26
27namespace ndn {
28namespace ndns {
29
Alexander Afanasyevc7c99002015-10-09 17:27:30 -070030NDNS_LOG_INIT("DbMgr")
Shock Jiang3016c982014-11-11 11:35:17 -080031
Yumin Xia2c509c22017-02-09 14:37:36 -080032static const std::string NDNS_SCHEMA = R"VALUE(
33CREATE TABLE IF NOT EXISTS zones (
34 id INTEGER NOT NULL PRIMARY KEY,
35 name blob NOT NULL UNIQUE,
36 ttl integer(10) NOT NULL);
37
38CREATE TABLE IF NOT EXISTS zone_info (
39 zone_id INTEGER NOT NULL,
40 key VARCHAR(10) NOT NULL,
41 value blob NOT NULL,
42 PRIMARY KEY (zone_id, key),
43 FOREIGN KEY(zone_id) REFERENCES zones(id) ON UPDATE Cascade ON DELETE Cascade);
44
45CREATE TABLE IF NOT EXISTS rrsets (
46 id INTEGER NOT NULL PRIMARY KEY,
47 zone_id integer(10) NOT NULL,
48 label blob NOT NULL,
49 type blob NOT NULL,
50 version blob NOT NULL,
51 ttl integer(10) NOT NULL,
52 data blob NOT NULL,
53 FOREIGN KEY(zone_id) REFERENCES zones(id) ON UPDATE Cascade ON DELETE Cascade);
54
55CREATE UNIQUE INDEX rrsets_zone_id_label_type_version
56 ON rrsets (zone_id, label, type, version);
57)VALUE";
Shock Jiang3016c982014-11-11 11:35:17 -080058
59DbMgr::DbMgr(const std::string& dbFile/* = DEFAULT_CONFIG_PATH "/" "ndns.db"*/)
60 : m_dbFile(dbFile)
61 , m_conn(0)
62{
63 if (dbFile.empty())
64 m_dbFile = DEFAULT_DATABASE_PATH "/" "ndns.db";
65
66 this->open();
67
68 NDNS_LOG_INFO("open database: " << m_dbFile);
69}
70
71
72DbMgr::~DbMgr()
73{
74 if (m_conn != 0) {
75 this->close();
76 }
77}
78
79void
80DbMgr::open()
81{
82 int res = sqlite3_open_v2(m_dbFile.c_str(), &m_conn,
83 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
84#ifdef DISABLE_SQLITE3_FS_LOCKING
85 "unix-dotfile"
86#else
87 0
88#endif
89 );
90
91 if (res != SQLITE_OK) {
92 NDNS_LOG_FATAL("Cannot open the db file: " << m_dbFile);
Yumin Xia2c509c22017-02-09 14:37:36 -080093 BOOST_THROW_EXCEPTION(ConnectError("Cannot open the db file: " + m_dbFile));
Shock Jiang3016c982014-11-11 11:35:17 -080094 }
95 // ignore any errors from DB creation (command will fail for the existing database, which is ok)
96 sqlite3_exec(m_conn, NDNS_SCHEMA.c_str(), 0, 0, 0);
97}
98
99void
100DbMgr::close()
101{
102 if (m_conn == 0)
103 return;
104
105 int ret = sqlite3_close(m_conn);
106 if (ret != SQLITE_OK) {
107 NDNS_LOG_FATAL("Cannot close the db: " << m_dbFile);
108 }
109 else {
110 m_conn = 0;
111 NDNS_LOG_INFO("Close database: " << m_dbFile);
112 }
113}
114
115void
116DbMgr::clearAllData()
117{
118 const char* sql = "DELETE FROM zones; DELETE FROM rrsets;";
119
120 int rc = sqlite3_exec(m_conn, sql, 0, 0, 0); // sqlite3_step cannot execute multiple SQL statement
121 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800122 BOOST_THROW_EXCEPTION(ExecuteError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800123 }
124
125 NDNS_LOG_INFO("clear all the data in the database: " << m_dbFile);
126}
127
128///////////////////////////////////////////////////////////////////////////////////////////////////
129// Zone
130///////////////////////////////////////////////////////////////////////////////////////////////////
131
132void
133DbMgr::insert(Zone& zone)
134{
135 if (zone.getId() > 0)
136 return;
137
138 sqlite3_stmt* stmt;
139 const char* sql = "INSERT INTO zones (name, ttl) VALUES (?, ?)";
140 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
141 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800142 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800143 }
144
145 const Block& zoneName = zone.getName().wireEncode();
146 sqlite3_bind_blob(stmt, 1, zoneName.wire(), zoneName.size(), SQLITE_STATIC);
147 sqlite3_bind_int(stmt, 2, zone.getTtl().count());
148
149 rc = sqlite3_step(stmt);
150 if (rc != SQLITE_DONE) {
151 sqlite3_finalize(stmt);
Yumin Xia2c509c22017-02-09 14:37:36 -0800152 BOOST_THROW_EXCEPTION(ExecuteError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800153 }
154
155 zone.setId(sqlite3_last_insert_rowid(m_conn));
156 sqlite3_finalize(stmt);
157}
158
Yumin Xia2c509c22017-02-09 14:37:36 -0800159void
160DbMgr::setZoneInfo(Zone& zone,
161 const std::string& key,
162 const Block& value)
163{
164 if (zone.getId() == 0) {
165 BOOST_THROW_EXCEPTION(Error("zone has not been initialized"));
166 }
167
168 if (key.length() > 10) {
169 BOOST_THROW_EXCEPTION(Error("key length should not exceed 10"));
170 }
171
172 sqlite3_stmt* stmt;
173 const char* sql = "INSERT OR REPLACE INTO zone_info (zone_id, key, value) VALUES (?, ?, ?)";
174 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
175 if (rc != SQLITE_OK) {
176 BOOST_THROW_EXCEPTION(PrepareError(sql));
177 }
178
179 sqlite3_bind_int(stmt, 1, zone.getId());
180 sqlite3_bind_text(stmt, 2, key.c_str(), key.length(), SQLITE_STATIC);
181 sqlite3_bind_blob(stmt, 3, value.wire(), value.size(), SQLITE_STATIC);
182
183 rc = sqlite3_step(stmt);
184 if (rc != SQLITE_DONE) {
185 sqlite3_finalize(stmt);
186 BOOST_THROW_EXCEPTION(ExecuteError(sql));
187 }
188
189 sqlite3_finalize(stmt);
190}
191
192std::map<std::string, Block>
193DbMgr::getZoneInfo(Zone& zone)
194{
195 using std::string;
196 std::map<string, Block> rtn;
197
198 if (zone.getId() == 0) {
199 find(zone);
200 }
201
202 if (zone.getId() == 0) {
203 BOOST_THROW_EXCEPTION(Error("zone has not been initialized"));
204 }
205
206 sqlite3_stmt* stmt;
207 const char* sql = "SELECT key, value FROM zone_info WHERE zone_id=?";
208 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
209 if (rc != SQLITE_OK) {
210 BOOST_THROW_EXCEPTION(PrepareError(sql));
211 }
212
213 sqlite3_bind_int(stmt, 1, zone.getId());
214
215 while (sqlite3_step(stmt) == SQLITE_ROW) {
216 const char* key = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
217 rtn[string(key)] = Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 1)),
218 sqlite3_column_bytes(stmt, 1));
219 }
220
221 sqlite3_finalize(stmt);
222 return rtn;
223}
224
225
Shock Jiang3016c982014-11-11 11:35:17 -0800226bool
227DbMgr::find(Zone& zone)
228{
229 sqlite3_stmt* stmt;
230 const char* sql = "SELECT id, ttl FROM zones WHERE name=?";
231 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
232 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800233 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800234 }
235
236 const Block& zoneName = zone.getName().wireEncode();
237 sqlite3_bind_blob(stmt, 1, zoneName.wire(), zoneName.size(), SQLITE_STATIC);
238
239 if (sqlite3_step(stmt) == SQLITE_ROW) {
240 zone.setId(sqlite3_column_int64(stmt, 0));
241 zone.setTtl(time::seconds(sqlite3_column_int(stmt, 1)));
Yumin Xia2c509c22017-02-09 14:37:36 -0800242 }
243 else {
Shock Jiang3016c982014-11-11 11:35:17 -0800244 zone.setId(0);
245 }
246
247 sqlite3_finalize(stmt);
248
249 return zone.getId() != 0;
250}
251
Jiewen Tan870b29b2014-11-17 19:09:49 -0800252std::vector<Zone>
253DbMgr::listZones()
254{
255 sqlite3_stmt* stmt;
256 const char* sql = "SELECT id, name, ttl FROM zones";
257 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
258 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800259 BOOST_THROW_EXCEPTION(PrepareError(sql));
Jiewen Tan870b29b2014-11-17 19:09:49 -0800260 }
261
262 std::vector<Zone> vec;
263
264 while (sqlite3_step(stmt) == SQLITE_ROW) {
265 vec.emplace_back();
266 Zone& zone = vec.back();
267 zone.setId(sqlite3_column_int64(stmt, 0));
268 zone.setTtl(time::seconds(sqlite3_column_int(stmt, 2)));
269 zone.setName(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 1)),
270 sqlite3_column_bytes(stmt, 1))));
271 }
272 sqlite3_finalize(stmt);
273
274 return vec;
275}
276
Shock Jiang3016c982014-11-11 11:35:17 -0800277void
278DbMgr::remove(Zone& zone)
279{
280 if (zone.getId() == 0)
281 return;
282
283 sqlite3_stmt* stmt;
284 const char* sql = "DELETE FROM zones where id=?";
285 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
286 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800287 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800288 }
289
290 sqlite3_bind_int64(stmt, 1, zone.getId());
291
292 rc = sqlite3_step(stmt);
293 if (rc != SQLITE_DONE) {
294 sqlite3_finalize(stmt);
Yumin Xia2c509c22017-02-09 14:37:36 -0800295 BOOST_THROW_EXCEPTION(ExecuteError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800296 }
297
298 sqlite3_finalize(stmt);
299
300 zone = Zone();
301}
302
303
304///////////////////////////////////////////////////////////////////////////////////////////////////
305// Rrset
306///////////////////////////////////////////////////////////////////////////////////////////////////
307
308void
309DbMgr::insert(Rrset& rrset)
310{
311 if (rrset.getId() != 0)
312 return;
313
314 if (rrset.getZone() == 0) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800315 BOOST_THROW_EXCEPTION(RrsetError("Rrset has not been assigned to a zone"));
Shock Jiang3016c982014-11-11 11:35:17 -0800316 }
317
318 if (rrset.getZone()->getId() == 0) {
319 insert(*rrset.getZone());
320 }
321
322 const char* sql =
323 "INSERT INTO rrsets (zone_id, label, type, version, ttl, data)"
324 " VALUES (?, ?, ?, ?, ?, ?)";
325
326 sqlite3_stmt* stmt;
327 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
328 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800329 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800330 }
331
332 sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
333
334 const Block& label = rrset.getLabel().wireEncode();
335 sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
336 sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
337 sqlite3_bind_blob(stmt, 4, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
338 sqlite3_bind_int64(stmt, 5, rrset.getTtl().count());
339 sqlite3_bind_blob(stmt, 6, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
340
341 rc = sqlite3_step(stmt);
342 if (rc != SQLITE_DONE) {
343 sqlite3_finalize(stmt);
Yumin Xia2c509c22017-02-09 14:37:36 -0800344 BOOST_THROW_EXCEPTION(ExecuteError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800345 }
346
347 rrset.setId(sqlite3_last_insert_rowid(m_conn));
348 sqlite3_finalize(stmt);
349}
350
351bool
352DbMgr::find(Rrset& rrset)
353{
354 if (rrset.getZone() == 0) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800355 BOOST_THROW_EXCEPTION(RrsetError("Rrset has not been assigned to a zone"));
Shock Jiang3016c982014-11-11 11:35:17 -0800356 }
357
358 if (rrset.getZone()->getId() == 0) {
359 bool isFound = find(*rrset.getZone());
360 if (!isFound) {
361 return false;
362 }
363 }
364
365 sqlite3_stmt* stmt;
366 const char* sql =
367 "SELECT id, ttl, version, data FROM rrsets"
368 " WHERE zone_id=? and label=? and type=?";
369 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
370
371 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800372 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800373 }
374
375 sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
376
377 const Block& label = rrset.getLabel().wireEncode();
378 sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
379 sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
380
381 if (sqlite3_step(stmt) == SQLITE_ROW) {
382 rrset.setId(sqlite3_column_int64(stmt, 0));
383 rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
384 rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
385 sqlite3_column_bytes(stmt, 2)));
386 rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
387 sqlite3_column_bytes(stmt, 3)));
Yumin Xia2c509c22017-02-09 14:37:36 -0800388 }
389 else {
Shock Jiang3016c982014-11-11 11:35:17 -0800390 rrset.setId(0);
391 }
392 sqlite3_finalize(stmt);
393
394 return rrset.getId() != 0;
395}
396
397std::vector<Rrset>
398DbMgr::findRrsets(Zone& zone)
399{
400 if (zone.getId() == 0)
401 find(zone);
402
403 if (zone.getId() == 0)
Yumin Xia2c509c22017-02-09 14:37:36 -0800404 BOOST_THROW_EXCEPTION(RrsetError("Attempting to find all the rrsets with a zone does not in the database"));
Shock Jiang3016c982014-11-11 11:35:17 -0800405
406 std::vector<Rrset> vec;
407 sqlite3_stmt* stmt;
408 const char* sql = "SELECT id, ttl, version, data, label, type "
409 "FROM rrsets where zone_id=? ";
410
411 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
412 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800413 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800414 }
415 sqlite3_bind_int64(stmt, 1, zone.getId());
416
417 while (sqlite3_step(stmt) == SQLITE_ROW) {
418 vec.emplace_back(&zone);
419 Rrset& rrset = vec.back();
420
421 rrset.setId(sqlite3_column_int64(stmt, 0));
422 rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
423 rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
424 sqlite3_column_bytes(stmt, 2)));
425 rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
426 sqlite3_column_bytes(stmt, 3)));
427 rrset.setLabel(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 4)),
428 sqlite3_column_bytes(stmt, 4))));
429 rrset.setType(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 5)),
430 sqlite3_column_bytes(stmt, 5)));
431 }
432 sqlite3_finalize(stmt);
433
434 return vec;
435}
436
437
438void
439DbMgr::remove(Rrset& rrset)
440{
441 if (rrset.getId() == 0)
Yumin Xia2c509c22017-02-09 14:37:36 -0800442 BOOST_THROW_EXCEPTION(RrsetError("Attempting to remove Rrset that has no assigned id"));
Shock Jiang3016c982014-11-11 11:35:17 -0800443
444 sqlite3_stmt* stmt;
445 const char* sql = "DELETE FROM rrsets WHERE id=?";
446 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
447
448 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800449 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800450 }
451
452 sqlite3_bind_int64(stmt, 1, rrset.getId());
453
454 rc = sqlite3_step(stmt);
455 if (rc != SQLITE_DONE) {
456 sqlite3_finalize(stmt);
Yumin Xia2c509c22017-02-09 14:37:36 -0800457 BOOST_THROW_EXCEPTION(ExecuteError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800458 }
459
460 sqlite3_finalize(stmt);
461
462 rrset = Rrset(rrset.getZone());
463}
464
465void
466DbMgr::update(Rrset& rrset)
467{
468 if (rrset.getId() == 0) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800469 BOOST_THROW_EXCEPTION(RrsetError("Attempting to replace Rrset that has no assigned id"));
Shock Jiang3016c982014-11-11 11:35:17 -0800470 }
471
472 if (rrset.getZone() == 0) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800473 BOOST_THROW_EXCEPTION(RrsetError("Rrset has not been assigned to a zone"));
Shock Jiang3016c982014-11-11 11:35:17 -0800474 }
475
476 sqlite3_stmt* stmt;
477 const char* sql = "UPDATE rrsets SET ttl=?, version=?, data=? WHERE id=?";
478 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
479
480 if (rc != SQLITE_OK) {
Yumin Xia2c509c22017-02-09 14:37:36 -0800481 BOOST_THROW_EXCEPTION(PrepareError(sql));
Shock Jiang3016c982014-11-11 11:35:17 -0800482 }
483
484 sqlite3_bind_int64(stmt, 1, rrset.getTtl().count());
485 sqlite3_bind_blob(stmt, 2, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
486 sqlite3_bind_blob(stmt, 3, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
487 sqlite3_bind_int64(stmt, 4, rrset.getId());
488
489 sqlite3_step(stmt);
490 sqlite3_finalize(stmt);
491}
492
493} // namespace ndns
494} // namespace ndn