void Repo::attachLocal(const char* path, bool isWritable) { std::string repoPath = insertSchema(path); if (!isWritable) { // Make sure the repo exists before attaching it, in order to avoid // creating a read-only repo. struct stat buf; if (!strchr(repoPath.c_str(), ':') && stat(repoPath.c_str(), &buf) != 0) { return; } } try { std::stringstream ssAttach; ssAttach << "ATTACH DATABASE '" << repoPath << "' as " << dbName(RepoIdLocal) << ";"; exec(ssAttach.str()); pragmas(RepoIdLocal); } catch (RepoExc& re) { return; } if (initSchema(RepoIdLocal, isWritable)) { return; } m_localRepo = repoPath; m_localReadable = true; m_localWritable = isWritable; TRACE(1, "Local repo: '%s' (read%s)\n", m_localRepo.c_str(), m_localWritable ? "-write" : "-only"); }
bool Repo::openCentral(const char* rawPath, std::string& errorMsg) { std::string repoPath = insertSchema(rawPath); // SQLITE_OPEN_NOMUTEX specifies that the connection be opened such // that no mutexes are used to protect the database connection from other // threads. However, multiple connections can still be used concurrently, // because SQLite as a whole is thread-safe. if (int err = sqlite3_open_v2(repoPath.c_str(), &m_dbc, SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr)) { TRACE(1, "Repo::%s() failed to open candidate central repo '%s'\n", __func__, repoPath.c_str()); errorMsg = folly::format("Failed to open {}: {} - {}", repoPath, err, sqlite3_errmsg(m_dbc)).str(); return false; } // Register a busy handler to avoid spurious SQLITE_BUSY errors. sqlite3_busy_handler(m_dbc, busyHandler, (void*)this); try { m_beginStmt.prepare("BEGIN TRANSACTION;"); m_rollbackStmt.prepare("ROLLBACK;"); m_commitStmt.prepare("COMMIT;"); pragmas(RepoIdCentral); } catch (RepoExc& re) { TRACE(1, "Repo::%s() failed to initialize connection to canditate repo" " '%s': %s\n", __func__, repoPath.c_str(), re.what()); errorMsg = folly::format("Failed to initialize connection to {}: {}", repoPath, re.what()).str(); return false; } // sqlite3_open_v2() will silently open in read-only mode if file permissions // prevent writing, and there is no apparent way to detect this other than to // attempt writing to the database. Therefore, tell initSchema() to verify // that the database is writable. bool centralWritable = true; if (initSchema(RepoIdCentral, centralWritable, errorMsg) || !centralWritable) { TRACE(1, "Repo::initSchema() failed for candidate central repo '%s'\n", repoPath.c_str()); errorMsg = folly::format("Failed to initialize schema in {}: {}", repoPath, errorMsg).str(); return false; } m_centralRepo = repoPath; TRACE(1, "Central repo: '%s'\n", m_centralRepo.c_str()); return true; }