in lib/Core/SQLiteBuildDB.cpp [111:308]
bool open(std::string *error_out) {
// The db is opened lazily whenever an operation on it occurs. Thus if it is
// already open, we don't need to do any further work.
if (db) return true;
// Configure SQLite3 on first use.
//
// We attempt to set multi-threading mode, but can settle for serialized if
// the library can't be reinitialized (there are only two modes).
static int sqliteConfigureResult = []() -> int {
// We access a single connection from multiple threads.
return sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
}();
if (sqliteConfigureResult != SQLITE_OK) {
if (!sqlite3_threadsafe()) {
*error_out = "unable to configure database: not thread-safe";
return false;
}
}
int result = sqlite3_open(path.c_str(), &db);
if (result != SQLITE_OK) {
*error_out = "unable to open database: " + std::string(
sqlite3_errstr(result));
return false;
}
sqlite3_busy_timeout(db, 5000);
// Create the database schema, if necessary.
char *cError;
int version;
uint32_t clientVersion = 0;
sqlite3_stmt* stmt;
result = sqlite3_prepare_v2(
db, "SELECT version,client_version FROM info LIMIT 1",
-1, &stmt, nullptr);
if (result == SQLITE_ERROR) {
version = -1;
} else {
if (result != SQLITE_OK) {
*error_out = getCurrentErrorMessage();
return false;
}
result = sqlite3_step(stmt);
if (result == SQLITE_DONE) {
version = -1;
} else if (result == SQLITE_ROW) {
assert(sqlite3_column_count(stmt) == 2);
version = sqlite3_column_int(stmt, 0);
clientVersion = sqlite3_column_int(stmt, 1);
} else {
*error_out = getCurrentErrorMessage();
sqlite3_finalize(stmt);
return false;
}
sqlite3_finalize(stmt);
}
if (version != currentSchemaVersion ||
clientVersion != clientSchemaVersion) {
// Close the database before we try to recreate it.
sqlite3_close(db);
db = nullptr;
if (!recreateOnUnmatchedVersion) {
// We don't re-create the database in this case and return an error
*error_out = std::string("Version mismatch. (database-schema: ") + std::to_string(version) + std::string(" requested schema: ") + std::to_string(currentSchemaVersion) + std::string(". database-client: ") + std::to_string(clientVersion) + std::string(" requested client: ") + std::to_string(clientSchemaVersion) + std::string(")");
return false;
}
// Always recreate the database from scratch when the schema changes.
result = basic::sys::unlink(path.c_str());
if (result == -1) {
if (errno != ENOENT) {
*error_out = std::string("unable to unlink existing database: ") +
::strerror(errno);
sqlite3_close(db);
db = nullptr;
return false;
}
} else {
// If the remove was successful, reopen the database.
int result = sqlite3_open(path.c_str(), &db);
if (result != SQLITE_OK) {
*error_out = getCurrentErrorMessage();
return false;
}
}
// Create the schema in a single transaction.
result = sqlite3_exec(db, "BEGIN EXCLUSIVE;", nullptr, nullptr, &cError);
// Create the info table.
if (result == SQLITE_OK) {
result = sqlite3_exec(
db, ("CREATE TABLE info ("
"id INTEGER PRIMARY KEY, "
"version INTEGER, "
"client_version INTEGER, "
"iteration INTEGER);"),
nullptr, nullptr, &cError);
}
if (result == SQLITE_OK) {
char* query = sqlite3_mprintf(
"INSERT INTO info VALUES (0, %d, %d, 0);",
currentSchemaVersion, clientSchemaVersion);
result = sqlite3_exec(db, query, nullptr, nullptr, &cError);
sqlite3_free(query);
}
if (result == SQLITE_OK) {
result = sqlite3_exec(
db, ("CREATE TABLE key_names ("
"id INTEGER PRIMARY KEY, "
"key STRING UNIQUE);"),
nullptr, nullptr, &cError);
}
if (result == SQLITE_OK) {
result = sqlite3_exec(
db, ("CREATE TABLE rule_results ("
"key_id INTEGER PRIMARY KEY, "
"value BLOB, "
"signature INTEGER, "
"built_at INTEGER, "
"computed_at INTEGER, "
"start REAL, "
"end REAL, "
"dependencies BLOB, "
"FOREIGN KEY(key_id) REFERENCES key_names(id));"),
nullptr, nullptr, &cError);
}
// Create the indices on the rule tables.
if (result == SQLITE_OK) {
// Create an index to be used for efficiently looking up rule
// information from a key.
result = sqlite3_exec(
db, "CREATE UNIQUE INDEX rule_results_idx ON rule_results (key_id);",
nullptr, nullptr, &cError);
}
// Sync changes to disk.
if (result == SQLITE_OK) {
result = sqlite3_exec(db, "END;", nullptr, nullptr, &cError);
}
if (result != SQLITE_OK) {
*error_out = (std::string("unable to initialize database (") + cError
+ ")");
sqlite3_free(cError);
sqlite3_close(db);
db = nullptr;
return false;
}
}
// Initialize prepared statements.
result = sqlite3_prepare_v2(
db, findKeyIDForKeyStmtSQL,
-1, &findKeyIDForKeyStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
result = sqlite3_prepare_v2(
db, findKeyNameForKeyIDStmtSQL,
-1, &findKeyNameForKeyIDStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
result = sqlite3_prepare_v2(
db, insertIntoKeysStmtSQL,
-1, &insertIntoKeysStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
result = sqlite3_prepare_v2(
db, insertIntoRuleResultsStmtSQL,
-1, &insertIntoRuleResultsStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
result = sqlite3_prepare_v2(
db, deleteFromKeysStmtSQL,
-1, &deleteFromKeysStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
result = sqlite3_prepare_v2(
db, findRuleResultStmtSQL,
-1, &findRuleResultStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
result = sqlite3_prepare_v2(
db, fastFindRuleResultStmtSQL,
-1, &fastFindRuleResultStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
result = sqlite3_prepare_v2(
db, getKeysWithResultStmtSQL,
-1, &getKeysWithResultStmt, nullptr);
checkSQLiteResultOKReturnFalse(result);
return true;
}