|
@@ -26,6 +26,8 @@
|
|
|
#include <dns/rrset.h>
|
|
|
#include <dns/rrsetlist.h>
|
|
|
|
|
|
+#define SQLITE_SCHEMA_VERSION 1
|
|
|
+
|
|
|
using namespace std;
|
|
|
using namespace isc::dns;
|
|
|
using namespace isc::dns::rdata;
|
|
@@ -77,6 +79,8 @@ const char* const SCHEMA_LIST[] = {
|
|
|
NULL
|
|
|
};
|
|
|
|
|
|
+const char* const q_version_str = "SELECT version FROM schema_version";
|
|
|
+
|
|
|
const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1";
|
|
|
|
|
|
const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata "
|
|
@@ -254,7 +258,7 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
sqlite3_reset(query);
|
|
|
sqlite3_clear_bindings(query);
|
|
|
|
|
@@ -295,7 +299,7 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
|
|
|
//
|
|
|
sqlite3_reset(dbparameters->q_count_);
|
|
|
sqlite3_clear_bindings(dbparameters->q_count_);
|
|
|
-
|
|
|
+
|
|
|
rc = sqlite3_bind_int(dbparameters->q_count_, 1, zone_id);
|
|
|
if (rc != SQLITE_OK) {
|
|
|
isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
|
|
@@ -653,29 +657,90 @@ prepare(sqlite3* const db, const char* const statement) {
|
|
|
return (prepared);
|
|
|
}
|
|
|
|
|
|
-void
|
|
|
-checkAndSetupSchema(Sqlite3Initializer* initializer) {
|
|
|
- sqlite3* const db = initializer->params_.db_;
|
|
|
+// 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 do_sleep() {
|
|
|
+ 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 check_schema_version(sqlite3* db) {
|
|
|
sqlite3_stmt* prepared = NULL;
|
|
|
- if (sqlite3_prepare_v2(db, "SELECT version FROM schema_version", -1,
|
|
|
- &prepared, NULL) == SQLITE_OK &&
|
|
|
- sqlite3_step(prepared) == SQLITE_ROW) {
|
|
|
- initializer->params_.version_ = sqlite3_column_int(prepared, 0);
|
|
|
- sqlite3_finalize(prepared);
|
|
|
- } else {
|
|
|
- logger.info(DATASRC_SQLITE_SETUP);
|
|
|
- if (prepared != NULL) {
|
|
|
- sqlite3_finalize(prepared);
|
|
|
+ // 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, q_version_str, -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));
|
|
|
}
|
|
|
+ do_sleep();
|
|
|
+ }
|
|
|
+ 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));
|
|
|
+ }
|
|
|
+ do_sleep();
|
|
|
+ }
|
|
|
+ int schema_version = check_schema_version(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]);
|
|
|
+ "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(Sqlite3Initializer* initializer) {
|
|
|
+ sqlite3* const db = initializer->params_.db_;
|
|
|
+
|
|
|
+ int schema_version = check_schema_version(db);
|
|
|
+ if (schema_version != SQLITE_SCHEMA_VERSION) {
|
|
|
+ schema_version = create_database(db);
|
|
|
}
|
|
|
+ initializer->params_.version_ = schema_version;
|
|
|
|
|
|
initializer->params_.q_zone_ = prepare(db, q_zone_str);
|
|
|
initializer->params_.q_record_ = prepare(db, q_record_str);
|