blob: 53d3822cc7ab71e2ac8aa73fdb5de23c127bba33 [file] [log] [blame]
Shock Jiang3016c982014-11-11 11:35:17 -08001/* -*- 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 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
30NDNS_LOG_INIT("DbMgr");
31
32static const std::string NDNS_SCHEMA = "\
33CREATE TABLE IF NOT EXISTS zones ( \n\
34 id INTEGER NOT NULL PRIMARY KEY, \n\
35 name blob NOT NULL UNIQUE, \n\
36 ttl integer(10) NOT NULL); \n\
37 \n\
38CREATE TABLE IF NOT EXISTS rrsets ( \n\
39 id INTEGER NOT NULL PRIMARY KEY, \n\
40 zone_id integer(10) NOT NULL, \n\
41 label blob NOT NULL, \n\
42 type blob NOT NULL, \n\
43 version blob NOT NULL, \n\
44 ttl integer(10) NOT NULL, \n\
45 data blob NOT NULL, \n\
46 FOREIGN KEY(zone_id) REFERENCES zones(id) ON UPDATE Cascade ON DELETE Cascade); \n\
47 \n\
48CREATE UNIQUE INDEX rrsets_zone_id_label_type_version \n\
49 ON rrsets (zone_id, label, type, version); \n\
50";
51
52DbMgr::DbMgr(const std::string& dbFile/* = DEFAULT_CONFIG_PATH "/" "ndns.db"*/)
53 : m_dbFile(dbFile)
54 , m_conn(0)
55{
56 if (dbFile.empty())
57 m_dbFile = DEFAULT_DATABASE_PATH "/" "ndns.db";
58
59 this->open();
60
61 NDNS_LOG_INFO("open database: " << m_dbFile);
62}
63
64
65DbMgr::~DbMgr()
66{
67 if (m_conn != 0) {
68 this->close();
69 }
70}
71
72void
73DbMgr::open()
74{
75 int res = sqlite3_open_v2(m_dbFile.c_str(), &m_conn,
76 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
77#ifdef DISABLE_SQLITE3_FS_LOCKING
78 "unix-dotfile"
79#else
80 0
81#endif
82 );
83
84 if (res != SQLITE_OK) {
85 NDNS_LOG_FATAL("Cannot open the db file: " << m_dbFile);
86 throw ConnectError("Cannot open the db file: " + m_dbFile);
87 }
88 // ignore any errors from DB creation (command will fail for the existing database, which is ok)
89 sqlite3_exec(m_conn, NDNS_SCHEMA.c_str(), 0, 0, 0);
90}
91
92void
93DbMgr::close()
94{
95 if (m_conn == 0)
96 return;
97
98 int ret = sqlite3_close(m_conn);
99 if (ret != SQLITE_OK) {
100 NDNS_LOG_FATAL("Cannot close the db: " << m_dbFile);
101 }
102 else {
103 m_conn = 0;
104 NDNS_LOG_INFO("Close database: " << m_dbFile);
105 }
106}
107
108void
109DbMgr::clearAllData()
110{
111 const char* sql = "DELETE FROM zones; DELETE FROM rrsets;";
112
113 int rc = sqlite3_exec(m_conn, sql, 0, 0, 0); // sqlite3_step cannot execute multiple SQL statement
114 if (rc != SQLITE_OK) {
115 throw ExecuteError(sql);
116 }
117
118 NDNS_LOG_INFO("clear all the data in the database: " << m_dbFile);
119}
120
121///////////////////////////////////////////////////////////////////////////////////////////////////
122// Zone
123///////////////////////////////////////////////////////////////////////////////////////////////////
124
125void
126DbMgr::insert(Zone& zone)
127{
128 if (zone.getId() > 0)
129 return;
130
131 sqlite3_stmt* stmt;
132 const char* sql = "INSERT INTO zones (name, ttl) VALUES (?, ?)";
133 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
134 if (rc != SQLITE_OK) {
135 throw PrepareError(sql);
136 }
137
138 const Block& zoneName = zone.getName().wireEncode();
139 sqlite3_bind_blob(stmt, 1, zoneName.wire(), zoneName.size(), SQLITE_STATIC);
140 sqlite3_bind_int(stmt, 2, zone.getTtl().count());
141
142 rc = sqlite3_step(stmt);
143 if (rc != SQLITE_DONE) {
144 sqlite3_finalize(stmt);
145 throw ExecuteError(sql);
146 }
147
148 zone.setId(sqlite3_last_insert_rowid(m_conn));
149 sqlite3_finalize(stmt);
150}
151
152bool
153DbMgr::find(Zone& zone)
154{
155 sqlite3_stmt* stmt;
156 const char* sql = "SELECT id, ttl FROM zones WHERE name=?";
157 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
158 if (rc != SQLITE_OK) {
159 throw PrepareError(sql);
160 }
161
162 const Block& zoneName = zone.getName().wireEncode();
163 sqlite3_bind_blob(stmt, 1, zoneName.wire(), zoneName.size(), SQLITE_STATIC);
164
165 if (sqlite3_step(stmt) == SQLITE_ROW) {
166 zone.setId(sqlite3_column_int64(stmt, 0));
167 zone.setTtl(time::seconds(sqlite3_column_int(stmt, 1)));
168 } else {
169 zone.setId(0);
170 }
171
172 sqlite3_finalize(stmt);
173
174 return zone.getId() != 0;
175}
176
Jiewen Tan870b29b2014-11-17 19:09:49 -0800177std::vector<Zone>
178DbMgr::listZones()
179{
180 sqlite3_stmt* stmt;
181 const char* sql = "SELECT id, name, ttl FROM zones";
182 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
183 if (rc != SQLITE_OK) {
184 throw PrepareError(sql);
185 }
186
187 std::vector<Zone> vec;
188
189 while (sqlite3_step(stmt) == SQLITE_ROW) {
190 vec.emplace_back();
191 Zone& zone = vec.back();
192 zone.setId(sqlite3_column_int64(stmt, 0));
193 zone.setTtl(time::seconds(sqlite3_column_int(stmt, 2)));
194 zone.setName(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 1)),
195 sqlite3_column_bytes(stmt, 1))));
196 }
197 sqlite3_finalize(stmt);
198
199 return vec;
200}
201
Shock Jiang3016c982014-11-11 11:35:17 -0800202void
203DbMgr::remove(Zone& zone)
204{
205 if (zone.getId() == 0)
206 return;
207
208 sqlite3_stmt* stmt;
209 const char* sql = "DELETE FROM zones where id=?";
210 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
211 if (rc != SQLITE_OK) {
212 throw PrepareError(sql);
213 }
214
215 sqlite3_bind_int64(stmt, 1, zone.getId());
216
217 rc = sqlite3_step(stmt);
218 if (rc != SQLITE_DONE) {
219 sqlite3_finalize(stmt);
220 throw ExecuteError(sql);
221 }
222
223 sqlite3_finalize(stmt);
224
225 zone = Zone();
226}
227
228
229///////////////////////////////////////////////////////////////////////////////////////////////////
230// Rrset
231///////////////////////////////////////////////////////////////////////////////////////////////////
232
233void
234DbMgr::insert(Rrset& rrset)
235{
236 if (rrset.getId() != 0)
237 return;
238
239 if (rrset.getZone() == 0) {
240 throw RrsetError("Rrset has not been assigned to a zone");
241 }
242
243 if (rrset.getZone()->getId() == 0) {
244 insert(*rrset.getZone());
245 }
246
247 const char* sql =
248 "INSERT INTO rrsets (zone_id, label, type, version, ttl, data)"
249 " VALUES (?, ?, ?, ?, ?, ?)";
250
251 sqlite3_stmt* stmt;
252 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
253 if (rc != SQLITE_OK) {
254 throw PrepareError(sql);
255 }
256
257 sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
258
259 const Block& label = rrset.getLabel().wireEncode();
260 sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
261 sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
262 sqlite3_bind_blob(stmt, 4, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
263 sqlite3_bind_int64(stmt, 5, rrset.getTtl().count());
264 sqlite3_bind_blob(stmt, 6, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
265
266 rc = sqlite3_step(stmt);
267 if (rc != SQLITE_DONE) {
268 sqlite3_finalize(stmt);
269 throw ExecuteError(sql);
270 }
271
272 rrset.setId(sqlite3_last_insert_rowid(m_conn));
273 sqlite3_finalize(stmt);
274}
275
276bool
277DbMgr::find(Rrset& rrset)
278{
279 if (rrset.getZone() == 0) {
280 throw RrsetError("Rrset has not been assigned to a zone");
281 }
282
283 if (rrset.getZone()->getId() == 0) {
284 bool isFound = find(*rrset.getZone());
285 if (!isFound) {
286 return false;
287 }
288 }
289
290 sqlite3_stmt* stmt;
291 const char* sql =
292 "SELECT id, ttl, version, data FROM rrsets"
293 " WHERE zone_id=? and label=? and type=?";
294 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
295
296 if (rc != SQLITE_OK) {
297 throw PrepareError(sql);
298 }
299
300 sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
301
302 const Block& label = rrset.getLabel().wireEncode();
303 sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
304 sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
305
306 if (sqlite3_step(stmt) == SQLITE_ROW) {
307 rrset.setId(sqlite3_column_int64(stmt, 0));
308 rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
309 rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
310 sqlite3_column_bytes(stmt, 2)));
311 rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
312 sqlite3_column_bytes(stmt, 3)));
313 } else {
314 rrset.setId(0);
315 }
316 sqlite3_finalize(stmt);
317
318 return rrset.getId() != 0;
319}
320
321std::vector<Rrset>
322DbMgr::findRrsets(Zone& zone)
323{
324 if (zone.getId() == 0)
325 find(zone);
326
327 if (zone.getId() == 0)
328 throw RrsetError("Attempting to find all the rrsets with a zone does not in the database");
329
330 std::vector<Rrset> vec;
331 sqlite3_stmt* stmt;
332 const char* sql = "SELECT id, ttl, version, data, label, type "
333 "FROM rrsets where zone_id=? ";
334
335 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
336 if (rc != SQLITE_OK) {
337 throw PrepareError(sql);
338 }
339 sqlite3_bind_int64(stmt, 1, zone.getId());
340
341 while (sqlite3_step(stmt) == SQLITE_ROW) {
342 vec.emplace_back(&zone);
343 Rrset& rrset = vec.back();
344
345 rrset.setId(sqlite3_column_int64(stmt, 0));
346 rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
347 rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
348 sqlite3_column_bytes(stmt, 2)));
349 rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
350 sqlite3_column_bytes(stmt, 3)));
351 rrset.setLabel(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 4)),
352 sqlite3_column_bytes(stmt, 4))));
353 rrset.setType(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 5)),
354 sqlite3_column_bytes(stmt, 5)));
355 }
356 sqlite3_finalize(stmt);
357
358 return vec;
359}
360
361
362void
363DbMgr::remove(Rrset& rrset)
364{
365 if (rrset.getId() == 0)
366 throw RrsetError("Attempting to remove Rrset that has no assigned id");
367
368 sqlite3_stmt* stmt;
369 const char* sql = "DELETE FROM rrsets WHERE id=?";
370 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
371
372 if (rc != SQLITE_OK) {
373 throw PrepareError(sql);
374 }
375
376 sqlite3_bind_int64(stmt, 1, rrset.getId());
377
378 rc = sqlite3_step(stmt);
379 if (rc != SQLITE_DONE) {
380 sqlite3_finalize(stmt);
381 throw ExecuteError(sql);
382 }
383
384 sqlite3_finalize(stmt);
385
386 rrset = Rrset(rrset.getZone());
387}
388
389void
390DbMgr::update(Rrset& rrset)
391{
392 if (rrset.getId() == 0) {
393 throw RrsetError("Attempting to replace Rrset that has no assigned id");
394 }
395
396 if (rrset.getZone() == 0) {
397 throw RrsetError("Rrset has not been assigned to a zone");
398 }
399
400 sqlite3_stmt* stmt;
401 const char* sql = "UPDATE rrsets SET ttl=?, version=?, data=? WHERE id=?";
402 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
403
404 if (rc != SQLITE_OK) {
405 throw PrepareError(sql);
406 }
407
408 sqlite3_bind_int64(stmt, 1, rrset.getTtl().count());
409 sqlite3_bind_blob(stmt, 2, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
410 sqlite3_bind_blob(stmt, 3, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
411 sqlite3_bind_int64(stmt, 4, rrset.getId());
412
413 sqlite3_step(stmt);
414 sqlite3_finalize(stmt);
415}
416
417} // namespace ndns
418} // namespace ndn