123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744 |
- // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #include <sqlite3.h>
- #include <string>
- #include <vector>
- #include <boost/foreach.hpp>
- #include <datasrc/sqlite3_accessor.h>
- #include <datasrc/logger.h>
- #include <datasrc/data_source.h>
- #include <util/filename.h>
- using namespace std;
- #define SQLITE_SCHEMA_VERSION 1
- namespace isc {
- namespace datasrc {
- // The following enum and char* array define the SQL statements commonly
- // used in this implementation. Corresponding prepared statements (of
- // type sqlite3_stmt*) are maintained in the statements_ array of the
- // SQLite3Parameters structure.
- enum StatementID {
- ZONE = 0,
- ANY = 1,
- ANY_SUB = 2,
- BEGIN = 3,
- COMMIT = 4,
- ROLLBACK = 5,
- DEL_ZONE_RECORDS = 6,
- ADD_RECORD = 7,
- DEL_RECORD = 8,
- ITERATE = 9,
- FIND_PREVIOUS = 10,
- FIND_PREVIOUS_WRAP = 11,
- NUM_STATEMENTS = 12
- };
- const char* const text_statements[NUM_STATEMENTS] = {
- // note for ANY and ITERATE: the order of the SELECT values is
- // specifically chosen to match the enum values in RecordColumns
- "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2", // ZONE
- "SELECT rdtype, ttl, sigtype, rdata FROM records " // ANY
- "WHERE zone_id=?1 AND name=?2",
- "SELECT rdtype, ttl, sigtype, rdata " // ANY_SUB
- "FROM records WHERE zone_id=?1 AND name LIKE (\"%.\" || ?2)",
- "BEGIN", // BEGIN
- "COMMIT", // COMMIT
- "ROLLBACK", // ROLLBACK
- "DELETE FROM records WHERE zone_id=?1", // DEL_ZONE_RECORDS
- "INSERT INTO records " // ADD_RECORD
- "(zone_id, name, rname, ttl, rdtype, sigtype, rdata) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
- "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
- "AND rdtype=?3 AND rdata=?4",
- "SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE
- "WHERE zone_id = ?1 ORDER BY name, rdtype",
- "SELECT name FROM records " // FIND_PREVIOUS
- "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
- "rname < $2 ORDER BY rname DESC LIMIT 1", // FIND_PREVIOUS_WRAP
- "SELECT name FROM records "
- "WHERE zone_id = ?1 AND rdtype = 'NSEC' "
- "ORDER BY rname DESC LIMIT 1"
- };
- struct SQLite3Parameters {
- SQLite3Parameters() :
- db_(NULL), version_(-1), updating_zone(false), updated_zone_id(-1)
- {
- for (int i = 0; i < NUM_STATEMENTS; ++i) {
- statements_[i] = NULL;
- }
- }
- sqlite3* db_;
- int version_;
- sqlite3_stmt* statements_[NUM_STATEMENTS];
- bool updating_zone; // whether or not updating the zone
- int updated_zone_id; // valid only when updating_zone is true
- };
- // This is a helper class to encapsulate the code logic of executing
- // a specific SQLite3 statement, ensuring the corresponding prepared
- // statement is always reset whether the execution is completed successfully
- // or it results in an exception.
- // Note that an object of this class is intended to be used for "ephemeral"
- // statement, which is completed with a single "step" (normally within a
- // single call to an SQLite3Database method). In particular, it cannot be
- // used for "SELECT" variants, which generally expect multiple matching rows.
- class StatementProcessor {
- public:
- // desc will be used on failure in the what() message of the resulting
- // DataSourceError exception.
- StatementProcessor(SQLite3Parameters& dbparameters, StatementID stmt_id,
- const char* desc) :
- dbparameters_(dbparameters), stmt_id_(stmt_id), desc_(desc)
- {
- sqlite3_clear_bindings(dbparameters_.statements_[stmt_id_]);
- }
- ~StatementProcessor() {
- sqlite3_reset(dbparameters_.statements_[stmt_id_]);
- }
- void exec() {
- if (sqlite3_step(dbparameters_.statements_[stmt_id_]) != SQLITE_DONE) {
- sqlite3_reset(dbparameters_.statements_[stmt_id_]);
- isc_throw(DataSourceError, "failed to " << desc_ << ": " <<
- sqlite3_errmsg(dbparameters_.db_));
- }
- }
- private:
- SQLite3Parameters& dbparameters_;
- const StatementID stmt_id_;
- const char* const desc_;
- };
- SQLite3Accessor::SQLite3Accessor(const std::string& filename,
- const isc::dns::RRClass& rrclass) :
- dbparameters_(new SQLite3Parameters),
- filename_(filename),
- class_(rrclass.toText()),
- database_name_("sqlite3_" +
- isc::util::Filename(filename).nameAndExtension())
- {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN);
- open(filename);
- }
- SQLite3Accessor::SQLite3Accessor(const std::string& filename,
- const string& rrclass) :
- dbparameters_(new SQLite3Parameters),
- filename_(filename),
- class_(rrclass),
- database_name_("sqlite3_" +
- isc::util::Filename(filename).nameAndExtension())
- {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN);
- open(filename);
- }
- boost::shared_ptr<DatabaseAccessor>
- SQLite3Accessor::clone() {
- return (boost::shared_ptr<DatabaseAccessor>(new SQLite3Accessor(filename_,
- class_)));
- }
- namespace {
- // This is a helper class to initialize a Sqlite3 DB safely. An object of
- // this class encapsulates all temporary resources that are necessary for
- // the initialization, and release them in the destructor. Once everything
- // is properly initialized, the move() method moves the allocated resources
- // to the main object in an exception free manner. This way, the main code
- // for the initialization can be exception safe, and can provide the strong
- // exception guarantee.
- class Initializer {
- public:
- ~Initializer() {
- for (int i = 0; i < NUM_STATEMENTS; ++i) {
- sqlite3_finalize(params_.statements_[i]);
- }
- if (params_.db_ != NULL) {
- sqlite3_close(params_.db_);
- }
- }
- void move(SQLite3Parameters* dst) {
- *dst = params_;
- params_ = SQLite3Parameters(); // clear everything
- }
- SQLite3Parameters params_;
- };
- const char* const SCHEMA_LIST[] = {
- "CREATE TABLE schema_version (version INTEGER NOT NULL)",
- "INSERT INTO schema_version VALUES (1)",
- "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
- "name STRING NOT NULL COLLATE NOCASE, "
- "rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN', "
- "dnssec BOOLEAN NOT NULL DEFAULT 0)",
- "CREATE INDEX zones_byname ON zones (name)",
- "CREATE TABLE records (id INTEGER PRIMARY KEY, "
- "zone_id INTEGER NOT NULL, name STRING NOT NULL COLLATE NOCASE, "
- "rname STRING NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, "
- "rdtype STRING NOT NULL COLLATE NOCASE, sigtype STRING COLLATE NOCASE, "
- "rdata STRING NOT NULL)",
- "CREATE INDEX records_byname ON records (name)",
- "CREATE INDEX records_byrname ON records (rname)",
- "CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, "
- "hash STRING NOT NULL COLLATE NOCASE, "
- "owner STRING NOT NULL COLLATE NOCASE, "
- "ttl INTEGER NOT NULL, rdtype STRING NOT NULL COLLATE NOCASE, "
- "rdata STRING NOT NULL)",
- "CREATE INDEX nsec3_byhash ON nsec3 (hash)",
- NULL
- };
- sqlite3_stmt*
- prepare(sqlite3* const db, const char* const statement) {
- sqlite3_stmt* prepared = NULL;
- if (sqlite3_prepare_v2(db, statement, -1, &prepared, NULL) != SQLITE_OK) {
- isc_throw(SQLite3Error, "Could not prepare SQLite statement: " <<
- statement);
- }
- return (prepared);
- }
- // small function to sleep for 0.1 seconds, needed when waiting for
- // exclusive database locks (which should only occur on startup, and only
- // when the database has not been created yet)
- void doSleep() {
- struct timespec req;
- req.tv_sec = 0;
- req.tv_nsec = 100000000;
- nanosleep(&req, NULL);
- }
- // returns the schema version if the schema version table exists
- // returns -1 if it does not
- int checkSchemaVersion(sqlite3* db) {
- sqlite3_stmt* prepared = NULL;
- // At this point in time, the database might be exclusively locked, in
- // which case even prepare() will return BUSY, so we may need to try a
- // few times
- for (size_t i = 0; i < 50; ++i) {
- int rc = sqlite3_prepare_v2(db, "SELECT version FROM schema_version",
- -1, &prepared, NULL);
- if (rc == SQLITE_ERROR) {
- // this is the error that is returned when the table does not
- // exist
- return (-1);
- } else if (rc == SQLITE_OK) {
- break;
- } else if (rc != SQLITE_BUSY || i == 50) {
- isc_throw(SQLite3Error, "Unable to prepare version query: "
- << rc << " " << sqlite3_errmsg(db));
- }
- doSleep();
- }
- if (sqlite3_step(prepared) != SQLITE_ROW) {
- isc_throw(SQLite3Error,
- "Unable to query version: " << sqlite3_errmsg(db));
- }
- int version = sqlite3_column_int(prepared, 0);
- sqlite3_finalize(prepared);
- return (version);
- }
- // return db version
- int create_database(sqlite3* db) {
- // try to get an exclusive lock. Once that is obtained, do the version
- // check *again*, just in case this process was racing another
- //
- // try for 5 secs (50*0.1)
- int rc;
- logger.info(DATASRC_SQLITE_SETUP);
- for (size_t i = 0; i < 50; ++i) {
- rc = sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL,
- NULL);
- if (rc == SQLITE_OK) {
- break;
- } else if (rc != SQLITE_BUSY || i == 50) {
- isc_throw(SQLite3Error, "Unable to acquire exclusive lock "
- "for database creation: " << sqlite3_errmsg(db));
- }
- doSleep();
- }
- int schema_version = checkSchemaVersion(db);
- if (schema_version == -1) {
- for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) {
- if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) !=
- SQLITE_OK) {
- isc_throw(SQLite3Error,
- "Failed to set up schema " << SCHEMA_LIST[i]);
- }
- }
- sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL);
- return (SQLITE_SCHEMA_VERSION);
- } else {
- return (schema_version);
- }
- }
- void
- checkAndSetupSchema(Initializer* initializer) {
- sqlite3* const db = initializer->params_.db_;
- int schema_version = checkSchemaVersion(db);
- if (schema_version != SQLITE_SCHEMA_VERSION) {
- schema_version = create_database(db);
- }
- initializer->params_.version_ = schema_version;
- for (int i = 0; i < NUM_STATEMENTS; ++i) {
- initializer->params_.statements_[i] = prepare(db, text_statements[i]);
- }
- }
- }
- void
- SQLite3Accessor::open(const std::string& name) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNOPEN).arg(name);
- if (dbparameters_->db_ != NULL) {
- // There shouldn't be a way to trigger this anyway
- isc_throw(DataSourceError, "Duplicate SQLite open with " << name);
- }
- Initializer initializer;
- if (sqlite3_open(name.c_str(), &initializer.params_.db_) != 0) {
- isc_throw(SQLite3Error, "Cannot open SQLite database file: " << name);
- }
- checkAndSetupSchema(&initializer);
- initializer.move(dbparameters_.get());
- }
- SQLite3Accessor::~SQLite3Accessor() {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN);
- if (dbparameters_->db_ != NULL) {
- close();
- }
- }
- void
- SQLite3Accessor::close(void) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNCLOSE);
- if (dbparameters_->db_ == NULL) {
- isc_throw(DataSourceError,
- "SQLite data source is being closed before open");
- }
- // XXX: sqlite3_finalize() could fail. What should we do in that case?
- for (int i = 0; i < NUM_STATEMENTS; ++i) {
- sqlite3_finalize(dbparameters_->statements_[i]);
- dbparameters_->statements_[i] = NULL;
- }
- sqlite3_close(dbparameters_->db_);
- dbparameters_->db_ = NULL;
- }
- std::pair<bool, int>
- SQLite3Accessor::getZone(const std::string& name) const {
- int rc;
- sqlite3_stmt* const stmt = dbparameters_->statements_[ZONE];
- // Take the statement (simple SELECT id FROM zones WHERE...)
- // and prepare it (bind the parameters to it)
- sqlite3_reset(stmt);
- rc = sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(SQLite3Error, "Could not bind " << name <<
- " to SQL statement (zone)");
- }
- rc = sqlite3_bind_text(stmt, 2, class_.c_str(), -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(SQLite3Error, "Could not bind " << class_ <<
- " to SQL statement (zone)");
- }
- // Get the data there and see if it found anything
- rc = sqlite3_step(stmt);
- if (rc == SQLITE_ROW) {
- const int zone_id = sqlite3_column_int(stmt, 0);
- sqlite3_reset(stmt);
- return (pair<bool, int>(true, zone_id));
- } else if (rc == SQLITE_DONE) {
- // Free resources
- sqlite3_reset(stmt);
- return (pair<bool, int>(false, 0));
- }
- sqlite3_reset(stmt);
- isc_throw(DataSourceError, "Unexpected failure in sqlite3_step: " <<
- sqlite3_errmsg(dbparameters_->db_));
- // Compilers might not realize isc_throw always throws
- return (std::pair<bool, int>(false, 0));
- }
- namespace {
- // Conversion to plain char
- const char*
- convertToPlainCharInternal(const unsigned char* ucp, sqlite3 *db) {
- if (ucp == NULL) {
- // The field can really be NULL, in which case we return an
- // empty string, or sqlite may have run out of memory, in
- // which case we raise an error
- if (sqlite3_errcode(db) == SQLITE_NOMEM) {
- isc_throw(DataSourceError,
- "Sqlite3 backend encountered a memory allocation "
- "error in sqlite3_column_text()");
- } else {
- return ("");
- }
- }
- const void* p = ucp;
- return (static_cast<const char*>(p));
- }
- }
- class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext {
- public:
- // Construct an iterator for all records. When constructed this
- // way, the getNext() call will copy all fields
- Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id) :
- iterator_type_(ITT_ALL),
- accessor_(accessor),
- statement_(NULL),
- name_("")
- {
- // We create the statement now and then just keep getting data from it
- statement_ = prepare(accessor->dbparameters_->db_,
- text_statements[ITERATE]);
- bindZoneId(id);
- }
- // Construct an iterator for records with a specific name. When constructed
- // this way, the getNext() call will copy all fields except name
- Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id,
- const std::string& name, bool subdomains) :
- iterator_type_(ITT_NAME),
- accessor_(accessor),
- statement_(NULL),
- name_(name)
- {
- // We create the statement now and then just keep getting data from it
- statement_ = prepare(accessor->dbparameters_->db_,
- subdomains ? text_statements[ANY_SUB] :
- text_statements[ANY]);
- bindZoneId(id);
- bindName(name_);
- }
- bool getNext(std::string (&data)[COLUMN_COUNT]) {
- // If there's another row, get it
- // If finalize has been called (e.g. when previous getNext() got
- // SQLITE_DONE), directly return false
- if (statement_ == NULL) {
- return false;
- }
- const int rc(sqlite3_step(statement_));
- if (rc == SQLITE_ROW) {
- // For both types, we copy the first four columns
- copyColumn(data, TYPE_COLUMN);
- copyColumn(data, TTL_COLUMN);
- copyColumn(data, SIGTYPE_COLUMN);
- copyColumn(data, RDATA_COLUMN);
- // Only copy Name if we are iterating over every record
- if (iterator_type_ == ITT_ALL) {
- copyColumn(data, NAME_COLUMN);
- }
- return (true);
- } else if (rc != SQLITE_DONE) {
- isc_throw(DataSourceError,
- "Unexpected failure in sqlite3_step: " <<
- sqlite3_errmsg(accessor_->dbparameters_->db_));
- }
- finalize();
- return (false);
- }
- virtual ~Context() {
- finalize();
- }
- private:
- // Depending on which constructor is called, behaviour is slightly
- // different. We keep track of what to do with the iterator type
- // See description of getNext() and the constructors
- enum IteratorType {
- ITT_ALL,
- ITT_NAME
- };
- void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
- data[column] = convertToPlainChar(sqlite3_column_text(statement_,
- column));
- }
- void bindZoneId(const int zone_id) {
- if (sqlite3_bind_int(statement_, 1, zone_id) != SQLITE_OK) {
- finalize();
- isc_throw(SQLite3Error, "Could not bind int " << zone_id <<
- " to SQL statement: " <<
- sqlite3_errmsg(accessor_->dbparameters_->db_));
- }
- }
- void bindName(const std::string& name) {
- if (sqlite3_bind_text(statement_, 2, name.c_str(), -1,
- SQLITE_TRANSIENT) != SQLITE_OK) {
- const char* errmsg = sqlite3_errmsg(accessor_->dbparameters_->db_);
- finalize();
- isc_throw(SQLite3Error, "Could not bind text '" << name <<
- "' to SQL statement: " << errmsg);
- }
- }
- void finalize() {
- sqlite3_finalize(statement_);
- statement_ = NULL;
- }
- // This helper method converts from the unsigned char* type (used by
- // sqlite3) to char* (wanted by std::string). Technically these types
- // might not be directly convertable
- // In case sqlite3_column_text() returns NULL, we just make it an
- // empty string, unless it was caused by a memory error
- const char* convertToPlainChar(const unsigned char* ucp) {
- return (convertToPlainCharInternal(ucp,
- accessor_->dbparameters_->db_));
- }
- const IteratorType iterator_type_;
- boost::shared_ptr<const SQLite3Accessor> accessor_;
- sqlite3_stmt *statement_;
- const std::string name_;
- };
- DatabaseAccessor::IteratorContextPtr
- SQLite3Accessor::getRecords(const std::string& name, int id,
- bool subdomains) const
- {
- return (IteratorContextPtr(new Context(shared_from_this(), id, name,
- subdomains)));
- }
- DatabaseAccessor::IteratorContextPtr
- SQLite3Accessor::getAllRecords(int id) const {
- return (IteratorContextPtr(new Context(shared_from_this(), id)));
- }
- pair<bool, int>
- SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
- if (dbparameters_->updating_zone) {
- isc_throw(DataSourceError,
- "duplicate zone update on SQLite3 data source");
- }
- const pair<bool, int> zone_info(getZone(zone_name));
- if (!zone_info.first) {
- return (zone_info);
- }
- StatementProcessor(*dbparameters_, BEGIN,
- "start an SQLite3 transaction").exec();
- if (replace) {
- try {
- StatementProcessor delzone_exec(*dbparameters_, DEL_ZONE_RECORDS,
- "delete zone records");
- sqlite3_clear_bindings(
- dbparameters_->statements_[DEL_ZONE_RECORDS]);
- if (sqlite3_bind_int(dbparameters_->statements_[DEL_ZONE_RECORDS],
- 1, zone_info.second) != SQLITE_OK) {
- isc_throw(DataSourceError,
- "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparameters_->db_));
- }
- delzone_exec.exec();
- } catch (const DataSourceError&) {
- // Once we start a transaction, if something unexpected happens
- // we need to rollback the transaction so that a subsequent update
- // is still possible with this accessor.
- StatementProcessor(*dbparameters_, ROLLBACK,
- "rollback an SQLite3 transaction").exec();
- throw;
- }
- }
- dbparameters_->updating_zone = true;
- dbparameters_->updated_zone_id = zone_info.second;
- return (zone_info);
- }
- void
- SQLite3Accessor::commitUpdateZone() {
- if (!dbparameters_->updating_zone) {
- isc_throw(DataSourceError, "committing zone update on SQLite3 "
- "data source without transaction");
- }
- StatementProcessor(*dbparameters_, COMMIT,
- "commit an SQLite3 transaction").exec();
- dbparameters_->updating_zone = false;
- dbparameters_->updated_zone_id = -1;
- }
- void
- SQLite3Accessor::rollbackUpdateZone() {
- if (!dbparameters_->updating_zone) {
- isc_throw(DataSourceError, "rolling back zone update on SQLite3 "
- "data source without transaction");
- }
- StatementProcessor(*dbparameters_, ROLLBACK,
- "rollback an SQLite3 transaction").exec();
- dbparameters_->updating_zone = false;
- dbparameters_->updated_zone_id = -1;
- }
- namespace {
- // Commonly used code sequence for adding/deleting record
- template <typename COLUMNS_TYPE>
- void
- doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id,
- COLUMNS_TYPE update_params, const char* exec_desc)
- {
- sqlite3_stmt* const stmt = dbparams.statements_[stmt_id];
- StatementProcessor executer(dbparams, stmt_id, exec_desc);
- int param_id = 0;
- if (sqlite3_bind_int(stmt, ++param_id, dbparams.updated_zone_id)
- != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparams.db_));
- }
- const size_t column_count =
- sizeof(update_params) / sizeof(update_params[0]);
- for (int i = 0; i < column_count; ++i) {
- if (sqlite3_bind_text(stmt, ++param_id, update_params[i].c_str(), -1,
- SQLITE_TRANSIENT) != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparams.db_));
- }
- }
- executer.exec();
- }
- }
- void
- SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
- if (!dbparameters_->updating_zone) {
- isc_throw(DataSourceError, "adding record to SQLite3 "
- "data source without transaction");
- }
- doUpdate<const string (&)[DatabaseAccessor::ADD_COLUMN_COUNT]>(
- *dbparameters_, ADD_RECORD, columns, "add record to zone");
- }
- void
- SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
- if (!dbparameters_->updating_zone) {
- isc_throw(DataSourceError, "deleting record in SQLite3 "
- "data source without transaction");
- }
- doUpdate<const string (&)[DatabaseAccessor::DEL_PARAM_COUNT]>(
- *dbparameters_, DEL_RECORD, params, "delete record from zone");
- }
- std::string
- SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
- const
- {
- sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
- sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS]);
- int rc = sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1,
- zone_id);
- if (rc != SQLITE_OK) {
- isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
- " to SQL statement (find previous)");
- }
- rc = sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2,
- rname.c_str(), -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(SQLite3Error, "Could not bind name " << rname <<
- " to SQL statement (find previous)");
- }
- std::string result;
- rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]);
- if (rc == SQLITE_ROW) {
- // We found it
- result = convertToPlainCharInternal(sqlite3_column_text(dbparameters_->
- statements_[FIND_PREVIOUS], 0), dbparameters_->db_);
- }
- sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
- if (rc == SQLITE_DONE) {
- // Nothing previous, wrap around (is it needed for anything?
- // Well, just for completeness)
- sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
- sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
- int rc = sqlite3_bind_int(
- dbparameters_->statements_[FIND_PREVIOUS_WRAP], 1, zone_id);
- if (rc != SQLITE_OK) {
- isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
- " to SQL statement (find previous wrap)");
- }
- rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
- if (rc == SQLITE_ROW) {
- // We found it
- result =
- convertToPlainCharInternal(sqlite3_column_text(dbparameters_->
- statements_[FIND_PREVIOUS_WRAP], 0), dbparameters_->db_);
- }
- sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS_WRAP]);
- if (rc == SQLITE_DONE) {
- // No NSEC records, this DB doesn't support DNSSEC
- isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC");
- }
- }
- if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
- // Some kind of error
- isc_throw(SQLite3Error, "Could get data for previous name");
- }
- return (result);
- }
- }
- }
|