void RepoQuery::step() { if (m_done) { throw RepoExc("RepoQuery::%s(repo=%p) error: Query done", __func__, &m_stmt.repo()); } int rc = sqlite3_step(m_stmt.get()); switch (rc) { case SQLITE_DONE: m_row = false; m_done = true; break; case SQLITE_ROW: m_row = true; m_done = false; break; default: m_row = false; m_done = true; std::string errmsg = sqlite3_errmsg(m_stmt.repo().dbc()); if (rc == SQLITE_CORRUPT) { auto repoId = debugComputeRepoIdFromSQL(m_stmt.repo(), m_stmt.sql()); reportDbCorruption(m_stmt.repo(), repoId, "sqlite3_step()"); } throw RepoExc("RepoQuery::%s(repo=%p) error: '%s' --> (%d) %s", __func__, &m_stmt.repo(), m_stmt.sql().c_str(), rc, errmsg.c_str()); } }
void RepoQuery::getInt64(int iCol, int64_t& val) { if (!isInt(iCol)) { throw RepoExc( "RepoQuery::%s(repo=%p) error: Column %d is not an integer in '%s'", __func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str()); } val = sqlite3_column_int64(m_stmt.get(), iCol); }
void RepoQuery::getDouble(int iCol, double& val) { if (!isDouble(iCol)) { throw RepoExc( "RepoQuery::%s(repo=%p) error: Column %d is not a double in '%s'", __func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str()); } val = sqlite3_column_double(m_stmt.get(), iCol); }
void RepoQuery::getText(int iCol, const char*& text) { if (!isText(iCol)) { throw RepoExc( "RepoQuery::%s(repo=%p) error: Column %d is not text in '%s'", __func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str()); } text = (const char*)sqlite3_column_text(m_stmt.get(), iCol); }
void RepoQuery::getBlob(int iCol, const void*& blob, size_t& size) { if (!isBlob(iCol)) { throw RepoExc( "RepoQuery::%s(repo=%p) error: Column %d is not a blob in '%s'", __func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str()); } blob = sqlite3_column_blob(m_stmt.get(), iCol); size = size_t(sqlite3_column_bytes(m_stmt.get(), iCol)); }
void RepoStmt::prepare(const std::string& sql) { finalize(); m_sql = sql; int rc = sqlite3_prepare_v2(m_repo.dbc(), sql.c_str(), sql.size(), &m_stmt, nullptr); if (rc != SQLITE_OK) { throw RepoExc("RepoStmt::%s(repo=%p) error: '%s' --> (%d) %s\n", __func__, &m_repo, sql.c_str(), rc, sqlite3_errmsg(m_repo.dbc())); } }
void RepoQuery::getMd5(int iCol, MD5& md5) { const void* blob; size_t size; getBlob(iCol, blob, size); if (size != 16) { throw RepoExc( "RepoQuery::%s(repo=%p) error: Column %d is the wrong size" " (expected 16, got %zu) in '%s'", __func__, &m_stmt.repo(), iCol, size, m_stmt.sql().c_str()); } new (&md5) MD5(blob); }
void RepoQuery::step() { if (m_done) { throw RepoExc("RepoQuery::%s(repo=%p) error: Query done", __func__, &m_stmt.repo()); } int rc = sqlite3_step(m_stmt.get()); switch (rc) { case SQLITE_DONE: m_row = false; m_done = true; break; case SQLITE_ROW: m_row = true; m_done = false; break; default: m_row = false; m_done = true; throw RepoExc("RepoQuery::%s(repo=%p) error: '%s' --> (%d) %s", __func__, &m_stmt.repo(), m_stmt.sql().c_str(), rc, sqlite3_errmsg(m_stmt.repo().dbc())); } }
void Repo::setTextPragma(int repoId, const char* name, const char* val) { // Pragma writes must be executed outside transactions, since they may change // transaction behavior. std::stringstream ssPragma; ssPragma << "PRAGMA " << dbName(repoId) << "." << name << " = " << val << ";"; exec(ssPragma.str()); if (debug) { // Verify that the pragma had the desired effect. std::string newval = "?"; getTextPragma(repoId, name, newval); if (strcmp(newval.c_str(), val)) { throw RepoExc("Unexpected PRAGMA %s.%s value: %s\n", dbName(repoId), name, newval.c_str()); } } }
void RepoStmt::prepare(const std::string& sql) { finalize(); m_sql = sql; int rc = sqlite3_prepare_v2(m_repo.dbc(), sql.c_str(), sql.size(), &m_stmt, nullptr); if (rc != SQLITE_OK) { std::string errmsg = sqlite3_errmsg(m_repo.dbc()); if (rc == SQLITE_CORRUPT) { auto repoId = debugComputeRepoIdFromSQL(repo(), sql); reportDbCorruption(m_repo, repoId, "sqlite3_prepare_v2()"); } throw RepoExc("RepoStmt::%s(repo=%p) error: '%s' --> (%d) %s\n", __func__, &m_repo, sql.c_str(), rc, errmsg.c_str()); } }
void Repo::loadGlobalData(bool allowFailure /* = false */) { m_lsrp.load(); if (!RuntimeOption::RepoAuthoritative) return; std::vector<std::string> failures; /* * This should probably just go to the Local repo always, except * that our unit test suite is currently running RepoAuthoritative * tests with the compiled repo as the Central repo. */ for (int repoId = RepoIdCount - 1; repoId >= 0; --repoId) { if (repoName(repoId).empty()) { // The repo wasn't loadable continue; } try { RepoStmt stmt(*this); const auto& tbl = table(repoId, "GlobalData"); stmt.prepare( folly::format( "SELECT count(*), data from {};", tbl ).str() ); RepoTxn txn(*this); RepoTxnQuery query(txn, stmt); query.step(); if (!query.row()) { throw RepoExc("Can't find table %s", tbl.c_str()); }; int val; query.getInt(0, val); if (val == 0) { throw RepoExc("No rows in %s. Did you forget to compile that file with " "this HHVM version?", tbl.c_str()); }; BlobDecoder decoder = query.getBlob(1); decoder(s_globalData); txn.commit(); } catch (RepoExc& e) { failures.push_back(repoName(repoId) + ": " + e.msg()); continue; } // TODO: this should probably read out the other elements of the global data // which control Option or RuntimeOption values -- the others are read out // in an inconsistent and ad-hoc manner. But I don't understand their uses // and interactions well enough to feel comfortable fixing now. RuntimeOption::PHP7_IntSemantics = s_globalData.PHP7_IntSemantics; RuntimeOption::PHP7_ScalarTypes = s_globalData.PHP7_ScalarTypes; RuntimeOption::AutoprimeGenerators = s_globalData.AutoprimeGenerators; return; } if (allowFailure) return; if (failures.empty()) { std::fprintf(stderr, "No repo was loadable. Check all the possible repo " "locations (Repo.Central.Path, HHVM_REPO_CENTRAL_PATH, and " "$HOME/.hhvm.hhbc) to make sure one of them is a valid " "sqlite3 HHVM repo built with this exact HHVM version.\n"); } else { // We should always have a global data section in RepoAuthoritative // mode, or the repo is messed up. std::fprintf(stderr, "Failed to load Repo::GlobalData:\n"); for (auto& f : failures) { std::fprintf(stderr, " %s\n", f.c_str()); } } assert(Process::IsInMainThread()); exit(1); }
void Repo::loadGlobalData(bool allowFailure /* = false */, bool readArrayTable /* = true */) { if (readArrayTable) m_lsrp.load(); if (!RuntimeOption::RepoAuthoritative) return; std::vector<std::string> failures; /* * This should probably just go to the Local repo always, except * that our unit test suite is currently running RepoAuthoritative * tests with the compiled repo as the Central repo. */ for (int repoId = RepoIdCount - 1; repoId >= 0; --repoId) { if (repoName(repoId).empty()) { // The repo wasn't loadable continue; } try { RepoStmt stmt(*this); const auto& tbl = table(repoId, "GlobalData"); stmt.prepare( folly::format( "SELECT count(*), data from {};", tbl ).str() ); RepoTxn txn(*this); RepoTxnQuery query(txn, stmt); query.step(); if (!query.row()) { throw RepoExc("Can't find table %s", tbl.c_str()); }; int val; query.getInt(0, val); if (val == 0) { throw RepoExc("No rows in %s. Did you forget to compile that file with " "this HHVM version?", tbl.c_str()); } BlobDecoder decoder = query.getBlob(1); decoder(s_globalData); if (readArrayTable) { auto& arrayTypeTable = globalArrayTypeTable(); decoder(arrayTypeTable); decoder(s_globalData.APCProfile); decoder(s_globalData.ConstantFunctions); decoder.assertDone(); } txn.commit(); } catch (RepoExc& e) { failures.push_back(repoName(repoId) + ": " + e.msg()); continue; } // TODO: this should probably read out the other elements of the global data // which control Option or RuntimeOption values -- the others are read out // in an inconsistent and ad-hoc manner. But I don't understand their uses // and interactions well enough to feel comfortable fixing now. RuntimeOption::EvalPromoteEmptyObject = s_globalData.PromoteEmptyObject; RuntimeOption::EnableIntrinsicsExtension = s_globalData.EnableIntrinsicsExtension; HHBBC::options.ElideAutoloadInvokes = s_globalData.ElideAutoloadInvokes; RuntimeOption::AutoprimeGenerators = s_globalData.AutoprimeGenerators; RuntimeOption::EnableHipHopSyntax = s_globalData.EnableHipHopSyntax; RuntimeOption::EvalHardTypeHints = s_globalData.HardTypeHints; RuntimeOption::EvalUseHHBBC = s_globalData.UsedHHBBC; RuntimeOption::PHP7_Builtins = s_globalData.PHP7_Builtins; RuntimeOption::PHP7_IntSemantics = s_globalData.PHP7_IntSemantics; RuntimeOption::PHP7_NoHexNumerics = s_globalData.PHP7_NoHexNumerics; RuntimeOption::PHP7_ScalarTypes = s_globalData.PHP7_ScalarTypes; RuntimeOption::PHP7_Substr = s_globalData.PHP7_Substr; RuntimeOption::EvalReffinessInvariance = s_globalData.ReffinessInvariance; RuntimeOption::EvalHackArrDVArrs = s_globalData.HackArrDVArrs; RuntimeOption::DisallowDynamicVarEnvFuncs = s_globalData.DisallowDynamicVarEnvFuncs; if (s_globalData.HardReturnTypeHints) { RuntimeOption::EvalCheckReturnTypeHints = 3; } if (s_globalData.ThisTypeHintLevel == 3) { RuntimeOption::EvalThisTypeHintLevel = s_globalData.ThisTypeHintLevel; } RuntimeOption::ConstantFunctions.clear(); for (auto const& elm : s_globalData.ConstantFunctions) { RuntimeOption::ConstantFunctions.insert(elm); } return; } if (allowFailure) return; if (failures.empty()) { std::fprintf(stderr, "No repo was loadable. Check all the possible repo " "locations (Repo.Central.Path, HHVM_REPO_CENTRAL_PATH, and " "$HOME/.hhvm.hhbc) to make sure one of them is a valid " "sqlite3 HHVM repo built with this exact HHVM version.\n"); } else { // We should always have a global data section in RepoAuthoritative // mode, or the repo is messed up. std::fprintf(stderr, "Failed to load Repo::GlobalData:\n"); for (auto& f : failures) { std::fprintf(stderr, " %s\n", f.c_str()); } } assertx(Process::IsInMainThread()); exit(1); }