blob: 8f5fcb3221a623b967aacfd649f24d063de9e8dd [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014, Regents of the University of California.
*
* This file is part of NDNS (Named Data Networking Domain Name Service).
* See AUTHORS.md for complete list of NDNS authors and contributors.
*
* NDNS is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NDNS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NDNS, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "db-mgr.hpp"
#include "logger.hpp"
#include "clients/response.hpp"
#include <iostream>
#include <fstream>
namespace ndn {
namespace ndns {
NDNS_LOG_INIT("DbMgr")
static const std::string NDNS_SCHEMA = "\
CREATE TABLE IF NOT EXISTS zones ( \n\
id INTEGER NOT NULL PRIMARY KEY, \n\
name blob NOT NULL UNIQUE, \n\
ttl integer(10) NOT NULL); \n\
\n\
CREATE TABLE IF NOT EXISTS rrsets ( \n\
id INTEGER NOT NULL PRIMARY KEY, \n\
zone_id integer(10) NOT NULL, \n\
label blob NOT NULL, \n\
type blob NOT NULL, \n\
version blob NOT NULL, \n\
ttl integer(10) NOT NULL, \n\
data blob NOT NULL, \n\
FOREIGN KEY(zone_id) REFERENCES zones(id) ON UPDATE Cascade ON DELETE Cascade); \n\
\n\
CREATE UNIQUE INDEX rrsets_zone_id_label_type_version \n\
ON rrsets (zone_id, label, type, version); \n\
";
DbMgr::DbMgr(const std::string& dbFile/* = DEFAULT_CONFIG_PATH "/" "ndns.db"*/)
: m_dbFile(dbFile)
, m_conn(0)
{
if (dbFile.empty())
m_dbFile = DEFAULT_DATABASE_PATH "/" "ndns.db";
this->open();
NDNS_LOG_INFO("open database: " << m_dbFile);
}
DbMgr::~DbMgr()
{
if (m_conn != 0) {
this->close();
}
}
void
DbMgr::open()
{
int res = sqlite3_open_v2(m_dbFile.c_str(), &m_conn,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
#ifdef DISABLE_SQLITE3_FS_LOCKING
"unix-dotfile"
#else
0
#endif
);
if (res != SQLITE_OK) {
NDNS_LOG_FATAL("Cannot open the db file: " << m_dbFile);
throw ConnectError("Cannot open the db file: " + m_dbFile);
}
// ignore any errors from DB creation (command will fail for the existing database, which is ok)
sqlite3_exec(m_conn, NDNS_SCHEMA.c_str(), 0, 0, 0);
}
void
DbMgr::close()
{
if (m_conn == 0)
return;
int ret = sqlite3_close(m_conn);
if (ret != SQLITE_OK) {
NDNS_LOG_FATAL("Cannot close the db: " << m_dbFile);
}
else {
m_conn = 0;
NDNS_LOG_INFO("Close database: " << m_dbFile);
}
}
void
DbMgr::clearAllData()
{
const char* sql = "DELETE FROM zones; DELETE FROM rrsets;";
int rc = sqlite3_exec(m_conn, sql, 0, 0, 0); // sqlite3_step cannot execute multiple SQL statement
if (rc != SQLITE_OK) {
throw ExecuteError(sql);
}
NDNS_LOG_INFO("clear all the data in the database: " << m_dbFile);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Zone
///////////////////////////////////////////////////////////////////////////////////////////////////
void
DbMgr::insert(Zone& zone)
{
if (zone.getId() > 0)
return;
sqlite3_stmt* stmt;
const char* sql = "INSERT INTO zones (name, ttl) VALUES (?, ?)";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
const Block& zoneName = zone.getName().wireEncode();
sqlite3_bind_blob(stmt, 1, zoneName.wire(), zoneName.size(), SQLITE_STATIC);
sqlite3_bind_int(stmt, 2, zone.getTtl().count());
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
sqlite3_finalize(stmt);
throw ExecuteError(sql);
}
zone.setId(sqlite3_last_insert_rowid(m_conn));
sqlite3_finalize(stmt);
}
bool
DbMgr::find(Zone& zone)
{
sqlite3_stmt* stmt;
const char* sql = "SELECT id, ttl FROM zones WHERE name=?";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
const Block& zoneName = zone.getName().wireEncode();
sqlite3_bind_blob(stmt, 1, zoneName.wire(), zoneName.size(), SQLITE_STATIC);
if (sqlite3_step(stmt) == SQLITE_ROW) {
zone.setId(sqlite3_column_int64(stmt, 0));
zone.setTtl(time::seconds(sqlite3_column_int(stmt, 1)));
} else {
zone.setId(0);
}
sqlite3_finalize(stmt);
return zone.getId() != 0;
}
std::vector<Zone>
DbMgr::listZones()
{
sqlite3_stmt* stmt;
const char* sql = "SELECT id, name, ttl FROM zones";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
std::vector<Zone> vec;
while (sqlite3_step(stmt) == SQLITE_ROW) {
vec.emplace_back();
Zone& zone = vec.back();
zone.setId(sqlite3_column_int64(stmt, 0));
zone.setTtl(time::seconds(sqlite3_column_int(stmt, 2)));
zone.setName(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 1)),
sqlite3_column_bytes(stmt, 1))));
}
sqlite3_finalize(stmt);
return vec;
}
void
DbMgr::remove(Zone& zone)
{
if (zone.getId() == 0)
return;
sqlite3_stmt* stmt;
const char* sql = "DELETE FROM zones where id=?";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
sqlite3_bind_int64(stmt, 1, zone.getId());
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
sqlite3_finalize(stmt);
throw ExecuteError(sql);
}
sqlite3_finalize(stmt);
zone = Zone();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Rrset
///////////////////////////////////////////////////////////////////////////////////////////////////
void
DbMgr::insert(Rrset& rrset)
{
if (rrset.getId() != 0)
return;
if (rrset.getZone() == 0) {
throw RrsetError("Rrset has not been assigned to a zone");
}
if (rrset.getZone()->getId() == 0) {
insert(*rrset.getZone());
}
const char* sql =
"INSERT INTO rrsets (zone_id, label, type, version, ttl, data)"
" VALUES (?, ?, ?, ?, ?, ?)";
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
const Block& label = rrset.getLabel().wireEncode();
sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
sqlite3_bind_blob(stmt, 4, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
sqlite3_bind_int64(stmt, 5, rrset.getTtl().count());
sqlite3_bind_blob(stmt, 6, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
sqlite3_finalize(stmt);
throw ExecuteError(sql);
}
rrset.setId(sqlite3_last_insert_rowid(m_conn));
sqlite3_finalize(stmt);
}
bool
DbMgr::find(Rrset& rrset)
{
if (rrset.getZone() == 0) {
throw RrsetError("Rrset has not been assigned to a zone");
}
if (rrset.getZone()->getId() == 0) {
bool isFound = find(*rrset.getZone());
if (!isFound) {
return false;
}
}
sqlite3_stmt* stmt;
const char* sql =
"SELECT id, ttl, version, data FROM rrsets"
" WHERE zone_id=? and label=? and type=?";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
sqlite3_bind_int64(stmt, 1, rrset.getZone()->getId());
const Block& label = rrset.getLabel().wireEncode();
sqlite3_bind_blob(stmt, 2, label.wire(), label.size(), SQLITE_STATIC);
sqlite3_bind_blob(stmt, 3, rrset.getType().wire(), rrset.getType().size(), SQLITE_STATIC);
if (sqlite3_step(stmt) == SQLITE_ROW) {
rrset.setId(sqlite3_column_int64(stmt, 0));
rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
sqlite3_column_bytes(stmt, 2)));
rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
sqlite3_column_bytes(stmt, 3)));
} else {
rrset.setId(0);
}
sqlite3_finalize(stmt);
return rrset.getId() != 0;
}
std::vector<Rrset>
DbMgr::findRrsets(Zone& zone)
{
if (zone.getId() == 0)
find(zone);
if (zone.getId() == 0)
throw RrsetError("Attempting to find all the rrsets with a zone does not in the database");
std::vector<Rrset> vec;
sqlite3_stmt* stmt;
const char* sql = "SELECT id, ttl, version, data, label, type "
"FROM rrsets where zone_id=? ";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
sqlite3_bind_int64(stmt, 1, zone.getId());
while (sqlite3_step(stmt) == SQLITE_ROW) {
vec.emplace_back(&zone);
Rrset& rrset = vec.back();
rrset.setId(sqlite3_column_int64(stmt, 0));
rrset.setTtl(time::seconds(sqlite3_column_int64(stmt, 1)));
rrset.setVersion(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 2)),
sqlite3_column_bytes(stmt, 2)));
rrset.setData(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 3)),
sqlite3_column_bytes(stmt, 3)));
rrset.setLabel(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 4)),
sqlite3_column_bytes(stmt, 4))));
rrset.setType(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 5)),
sqlite3_column_bytes(stmt, 5)));
}
sqlite3_finalize(stmt);
return vec;
}
void
DbMgr::remove(Rrset& rrset)
{
if (rrset.getId() == 0)
throw RrsetError("Attempting to remove Rrset that has no assigned id");
sqlite3_stmt* stmt;
const char* sql = "DELETE FROM rrsets WHERE id=?";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
sqlite3_bind_int64(stmt, 1, rrset.getId());
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
sqlite3_finalize(stmt);
throw ExecuteError(sql);
}
sqlite3_finalize(stmt);
rrset = Rrset(rrset.getZone());
}
void
DbMgr::update(Rrset& rrset)
{
if (rrset.getId() == 0) {
throw RrsetError("Attempting to replace Rrset that has no assigned id");
}
if (rrset.getZone() == 0) {
throw RrsetError("Rrset has not been assigned to a zone");
}
sqlite3_stmt* stmt;
const char* sql = "UPDATE rrsets SET ttl=?, version=?, data=? WHERE id=?";
int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
if (rc != SQLITE_OK) {
throw PrepareError(sql);
}
sqlite3_bind_int64(stmt, 1, rrset.getTtl().count());
sqlite3_bind_blob(stmt, 2, rrset.getVersion().wire(), rrset.getVersion().size(), SQLITE_STATIC);
sqlite3_bind_blob(stmt, 3, rrset.getData().wire(), rrset.getData().size(), SQLITE_STATIC);
sqlite3_bind_int64(stmt, 4, rrset.getId());
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
} // namespace ndns
} // namespace ndn