blob: 35af5de6fb0af84945d83724df75466428f37c67 [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
177void
178DbMgr::remove(Zone& zone)
179{
180 if (zone.getId() == 0)
181 return;
182
183 sqlite3_stmt* stmt;
184 const char* sql = "DELETE FROM zones where id=?";
185 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
186 if (rc != SQLITE_OK) {
187 throw PrepareError(sql);
188 }
189
190 sqlite3_bind_int64(stmt, 1, zone.getId());
191
192 rc = sqlite3_step(stmt);
193 if (rc != SQLITE_DONE) {
194 sqlite3_finalize(stmt);
195 throw ExecuteError(sql);
196 }
197
198 sqlite3_finalize(stmt);
199
200 zone = Zone();
201}
202
203
204///////////////////////////////////////////////////////////////////////////////////////////////////
205// Rrset
206///////////////////////////////////////////////////////////////////////////////////////////////////
207
208void
209DbMgr::insert(Rrset& rrset)
210{
211 if (rrset.getId() != 0)
212 return;
213
214 if (rrset.getZone() == 0) {
215 throw RrsetError("Rrset has not been assigned to a zone");
216 }
217
218 if (rrset.getZone()->getId() == 0) {
219 insert(*rrset.getZone());
220 }
221
222 const char* sql =
223 "INSERT INTO rrsets (zone_id, label, type, version, ttl, data)"
224 " VALUES (?, ?, ?, ?, ?, ?)";
225
226 sqlite3_stmt* stmt;
227 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
228 if (rc != SQLITE_OK) {
229 throw PrepareError(sql);
230 }
231
232 sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
233
234 const Block& label = rrset.getLabel().wireEncode();
235 sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
236 sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
237 sqlite3_bind_blob(stmt, 4, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
238 sqlite3_bind_int64(stmt, 5, rrset.getTtl().count());
239 sqlite3_bind_blob(stmt, 6, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
240
241 rc = sqlite3_step(stmt);
242 if (rc != SQLITE_DONE) {
243 sqlite3_finalize(stmt);
244 throw ExecuteError(sql);
245 }
246
247 rrset.setId(sqlite3_last_insert_rowid(m_conn));
248 sqlite3_finalize(stmt);
249}
250
251bool
252DbMgr::find(Rrset& rrset)
253{
254 if (rrset.getZone() == 0) {
255 throw RrsetError("Rrset has not been assigned to a zone");
256 }
257
258 if (rrset.getZone()->getId() == 0) {
259 bool isFound = find(*rrset.getZone());
260 if (!isFound) {
261 return false;
262 }
263 }
264
265 sqlite3_stmt* stmt;
266 const char* sql =
267 "SELECT id, ttl, version, data FROM rrsets"
268 " WHERE zone_id=? and label=? and type=?";
269 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
270
271 if (rc != SQLITE_OK) {
272 throw PrepareError(sql);
273 }
274
275 sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
276
277 const Block& label = rrset.getLabel().wireEncode();
278 sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
279 sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
280
281 if (sqlite3_step(stmt) == SQLITE_ROW) {
282 rrset.setId(sqlite3_column_int64(stmt, 0));
283 rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
284 rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
285 sqlite3_column_bytes(stmt, 2)));
286 rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
287 sqlite3_column_bytes(stmt, 3)));
288 } else {
289 rrset.setId(0);
290 }
291 sqlite3_finalize(stmt);
292
293 return rrset.getId() != 0;
294}
295
296std::vector<Rrset>
297DbMgr::findRrsets(Zone& zone)
298{
299 if (zone.getId() == 0)
300 find(zone);
301
302 if (zone.getId() == 0)
303 throw RrsetError("Attempting to find all the rrsets with a zone does not in the database");
304
305 std::vector<Rrset> vec;
306 sqlite3_stmt* stmt;
307 const char* sql = "SELECT id, ttl, version, data, label, type "
308 "FROM rrsets where zone_id=? ";
309
310 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
311 if (rc != SQLITE_OK) {
312 throw PrepareError(sql);
313 }
314 sqlite3_bind_int64(stmt, 1, zone.getId());
315
316 while (sqlite3_step(stmt) == SQLITE_ROW) {
317 vec.emplace_back(&zone);
318 Rrset& rrset = vec.back();
319
320 rrset.setId(sqlite3_column_int64(stmt, 0));
321 rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
322 rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
323 sqlite3_column_bytes(stmt, 2)));
324 rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
325 sqlite3_column_bytes(stmt, 3)));
326 rrset.setLabel(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 4)),
327 sqlite3_column_bytes(stmt, 4))));
328 rrset.setType(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 5)),
329 sqlite3_column_bytes(stmt, 5)));
330 }
331 sqlite3_finalize(stmt);
332
333 return vec;
334}
335
336
337void
338DbMgr::remove(Rrset& rrset)
339{
340 if (rrset.getId() == 0)
341 throw RrsetError("Attempting to remove Rrset that has no assigned id");
342
343 sqlite3_stmt* stmt;
344 const char* sql = "DELETE FROM rrsets WHERE id=?";
345 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
346
347 if (rc != SQLITE_OK) {
348 throw PrepareError(sql);
349 }
350
351 sqlite3_bind_int64(stmt, 1, rrset.getId());
352
353 rc = sqlite3_step(stmt);
354 if (rc != SQLITE_DONE) {
355 sqlite3_finalize(stmt);
356 throw ExecuteError(sql);
357 }
358
359 sqlite3_finalize(stmt);
360
361 rrset = Rrset(rrset.getZone());
362}
363
364void
365DbMgr::update(Rrset& rrset)
366{
367 if (rrset.getId() == 0) {
368 throw RrsetError("Attempting to replace Rrset that has no assigned id");
369 }
370
371 if (rrset.getZone() == 0) {
372 throw RrsetError("Rrset has not been assigned to a zone");
373 }
374
375 sqlite3_stmt* stmt;
376 const char* sql = "UPDATE rrsets SET ttl=?, version=?, data=? WHERE id=?";
377 int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
378
379 if (rc != SQLITE_OK) {
380 throw PrepareError(sql);
381 }
382
383 sqlite3_bind_int64(stmt, 1, rrset.getTtl().count());
384 sqlite3_bind_blob(stmt, 2, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
385 sqlite3_bind_blob(stmt, 3, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
386 sqlite3_bind_int64(stmt, 4, rrset.getId());
387
388 sqlite3_step(stmt);
389 sqlite3_finalize(stmt);
390}
391
392} // namespace ndns
393} // namespace ndn