# include # include # include # include # include # include # include # include 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(serialized_data_byte.data()), serialized_data_byte.size() }; } template std::string serialize(LoginData); template std::string serialize(SubmitJobData); template std::string serialize(FinishJobData); std::optional> connect (std::optional dbfile = std::nullopt) { if (dbfile) return std::make_optional> (dbfile->c_str()); else if (auto datadir = env::env("HPCSTAT_DATADIR", true); !datadir) return std::nullopt; else { auto dbfile = std::filesystem::path(*datadir) / "hpcstat.db"; return std::make_optional> (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> finishjob_remove_existed(std::map jobid_submit_time) { if (auto conn = connect(); !conn) return std::nullopt; else { auto all_job = jobid_submit_time | ranges::views::keys | ranges::to>; auto not_logged_job = all_job | ranges::to>; for (auto it : conn->select_query() .order_by>(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; } } std::optional>> 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 = [&]() -> std::optional>> { auto old_query = old_conn->select_query().many().exec(), new_query = new_conn->select_query().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(), (*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()); return std::nullopt; } else if constexpr (requires(T data) { data.Signature; }) { std::vector> 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>{}; }; auto check_many = [&](auto&& self) -> std::optional>> { if (auto diff = check_one.operator()(); !diff) return std::nullopt; else if constexpr (sizeof...(Ts) == 0) return diff; else if (auto diff2 = self.template operator()(self); !diff2) return std::nullopt; else { diff->insert(diff->end(), diff2->begin(), diff2->end()); return diff; } }; return check_many.operator()(check_many); } } }