hpcstat/src/sql.cpp

111 lines
4.7 KiB
C++
Raw Normal View History

2024-05-03 16:15:40 +08:00
# include <filesystem>
# include <set>
# include <hpcstat/sql.hpp>
# include <hpcstat/env.hpp>
# include <range/v3/range.hpp>
# include <range/v3/view.hpp>
2024-05-04 11:57:13 +08:00
# include <nameof.hpp>
# include <fmt/format.h>
2024-05-03 16:15:40 +08:00
namespace hpcstat::sql
{
std::string serialize(auto data)
{
auto [serialized_data_byte, out] = zpp::bits::data_out();
out(data).or_throw();
static_assert(sizeof(char) == sizeof(std::byte));
return { reinterpret_cast<char*>(serialized_data_byte.data()), serialized_data_byte.size() };
}
template std::string serialize(LoginData);
template std::string serialize(SubmitJobData);
template std::string serialize(FinishJobData);
2024-05-04 11:57:13 +08:00
std::optional<zxorm::Connection<LoginTable, LogoutTable, SubmitJobTable, FinishJobTable>> connect
(std::optional<std::string> dbfile = std::nullopt)
2024-05-03 16:15:40 +08:00
{
2024-05-04 11:57:13 +08:00
if (dbfile) return std::make_optional<zxorm::Connection<LoginTable, LogoutTable, SubmitJobTable, FinishJobTable>>
(dbfile->c_str());
else if (auto datadir = env::env("HPCSTAT_DATADIR", true); !datadir)
2024-05-03 16:15:40 +08:00
return std::nullopt;
else
{
auto dbfile = std::filesystem::path(*datadir) / "hpcstat.db";
return std::make_optional<zxorm::Connection<LoginTable, LogoutTable, SubmitJobTable, FinishJobTable>>
(dbfile.c_str());
}
}
bool initdb()
{ if (auto conn = connect(); !conn) return false; else { conn->create_tables(); return true; } }
bool writedb(auto value)
{ if (auto conn = connect(); !conn) return false; else { conn->insert_record(value); return true; } }
template bool writedb(LoginData);
template bool writedb(LogoutData);
template bool writedb(SubmitJobData);
template bool writedb(FinishJobData);
std::optional<std::set<unsigned>> finishjob_remove_existed(std::map<unsigned, std::string> jobid_submit_time)
{
if (auto conn = connect(); !conn) return std::nullopt;
else
{
auto all_job = jobid_submit_time | ranges::views::keys | ranges::to<std::vector<unsigned>>;
auto not_logged_job = all_job | ranges::to<std::set<unsigned>>;
for (auto it : conn->select_query<FinishJobData>()
.order_by<FinishJobTable::field_t<"id">>(zxorm::order_t::DESC)
.where_many(FinishJobTable::field_t<"id">().in(all_job))
.exec())
if (jobid_submit_time[it.JobId] == it.SubmitTime)
not_logged_job.erase(it.JobId);
return not_logged_job;
}
}
2024-05-04 11:57:13 +08:00
std::optional<std::vector<std::tuple<std::string, std::string, std::string>>>
verify(std::string old_db, std::string new_db)
{
auto old_conn = connect(old_db), new_conn = connect(new_db);
if (!old_conn || !new_conn) { std::cerr << "Failed to connect to database.\n"; return std::nullopt; }
else
{
auto check_one = [&]<typename T>()
-> std::optional<std::vector<std::tuple<std::string, std::string, std::string>>>
{
auto old_query = old_conn->select_query<T>().many().exec(),
new_query = new_conn->select_query<T>().many().exec();
auto old_data_it = old_query.begin(), new_data_it = new_query.begin();
for (; old_data_it != old_query.end() && new_data_it != new_query.end(); ++old_data_it, ++new_data_it)
if (*old_data_it != *new_data_it)
{
std::cerr << fmt::format
("Data mismatch: {} {} != {}.\n", nameof::nameof_type<T>(), (*old_data_it).Id, (*new_data_it).Id);
return std::nullopt;
}
if (old_data_it != old_query.end() && new_data_it == new_query.end())
{
std::cerr << fmt::format("Data mismatch in {}.\n", nameof::nameof_type<T>());
return std::nullopt;
}
else if constexpr (requires(T data) { data.Signature; })
{
std::vector<std::tuple<std::string, std::string, std::string>> diff;
for (; old_data_it != old_query.end(); ++old_data_it)
{
auto data = *old_data_it;
data.Signature = "";
data.Id = 0;
diff.push_back({ serialize(data), (*old_data_it).Signature, (*old_data_it).Key });
}
return diff;
}
else return std::vector<std::tuple<std::string, std::string, std::string>>{};
};
auto check_many = [&]<typename T, typename... Ts>(auto&& self)
-> std::optional<std::vector<std::tuple<std::string, std::string, std::string>>>
{
if (auto diff = check_one.operator()<T>(); !diff) return std::nullopt;
else if constexpr (sizeof...(Ts) == 0) return diff;
else if (auto diff2 = self.template operator()<Ts...>(self); !diff2) return std::nullopt;
else { diff->insert(diff->end(), diff2->begin(), diff2->end()); return diff; }
};
return check_many.operator()<LoginData, LogoutData, SubmitJobData, FinishJobData>(check_many);
}
}
2024-05-03 16:15:40 +08:00
}