Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
varqox committed Jun 15, 2024
2 parents ce5187d + 1e73bc1 commit c9468cd
Show file tree
Hide file tree
Showing 44 changed files with 325 additions and 488 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <sim/internal_files/internal_file.hh>
#include <sim/jobs/job.hh>
#include <sim/sql/fields/varbinary.hh>

namespace sim::change_problem_statement_jobs {

struct ChangeProblemStatementJob {
decltype(jobs::Job::id) id;
decltype(internal_files::InternalFile::id) new_statement_file_id;
sql::fields::Varbinary<256> path_for_new_statement;
};

} // namespace sim::change_problem_statement_jobs
1 change: 0 additions & 1 deletion subprojects/sim/include/sim/jobs/job.hh
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ struct Job {
Status status;
std::optional<uint64_t> aux_id;
std::optional<uint64_t> aux_id_2;
sql::fields::Blob info;
sql::fields::Blob data;
};

Expand Down
1 change: 0 additions & 1 deletion subprojects/sim/include/sim/jobs/old_job.hh
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ struct OldJob {
EnumVal<Status> status;
std::optional<uint64_t> aux_id;
std::optional<uint64_t> aux_id_2;
old_sql_fields::Blob<128> info;
old_sql_fields::Blob<0> data;

static constexpr auto primary_key = PrimaryKey{&OldJob::id};
Expand Down
87 changes: 2 additions & 85 deletions subprojects/sim/include/sim/jobs/utils.hh
Original file line number Diff line number Diff line change
@@ -1,93 +1,10 @@
#pragma once

#include <sim/jobs/old_job.hh>
#include <sim/old_mysql/old_mysql.hh>
#include <sim/problems/old_problem.hh>
#include <sim/users/old_user.hh>
#include <utility>
#include <sim/mysql/mysql.hh>
#include <simlib/string_view.hh>

namespace sim::jobs {

/// Append an integer @p x in binary format to the @p buff
template <class Integer>
inline std::enable_if_t<std::is_integral<Integer>::value, void>
append_dumped(std::string& buff, Integer x) {
buff.append(sizeof(x), '\0');
for (uint i = 1, shift = 0; i <= sizeof(x); ++i, shift += 8) {
buff[buff.size() - i] = (x >> shift) & 0xFF;
}
}

template <class Integer>
inline std::enable_if_t<std::is_integral<Integer>::value, Integer>
extract_dumped_int(StringView& dumped_str) {
throw_assert(dumped_str.size() >= sizeof(Integer));
Integer x = 0;
for (int i = sizeof(x) - 1, shift = 0; i >= 0; --i, shift += 8) {
x |= ((static_cast<Integer>(static_cast<uint8_t>(dumped_str[i]))) << shift);
}
dumped_str.remove_prefix(sizeof(x));
return x;
}

template <class Integer>
inline std::enable_if_t<std::is_integral<Integer>::value, void>
extract_dumped(Integer& x, StringView& dumped_str) {
x = extract_dumped_int<Integer>(dumped_str);
}

/// Dumps @p str to binary format XXXXABC... where XXXX code string's size and
/// ABC... is the @p str and appends it to the @p buff
inline void append_dumped(std::string& buff, StringView str) {
append_dumped<uint32_t>(buff, str.size());
buff += str;
}

template <class Rep, class Period>
inline void append_dumped(std::string& buff, const std::chrono::duration<Rep, Period>& dur) {
append_dumped(buff, dur.count());
}

template <class Rep, class Period>
inline void extract_dumped(std::chrono::duration<Rep, Period>& dur, StringView& dumped_str) {
Rep rep;
extract_dumped(rep, dumped_str);
dur = decltype(dur)(rep);
}

template <class T>
inline void append_dumped(std::string& buff, const std::optional<T>& opt) {
if (opt.has_value()) {
append_dumped(buff, true);
append_dumped(buff, opt.value());
} else {
append_dumped(buff, false);
}
}

template <class T>
inline void extract_dumped(std::optional<T>& opt, StringView& dumped_str) {
bool has_val = false;
extract_dumped(has_val, dumped_str);
if (has_val) {
T val{};
extract_dumped(val, dumped_str);
opt = val;
} else {
opt = std::nullopt;
}
}

struct ChangeProblemStatementInfo {
std::string new_statement_path;

ChangeProblemStatementInfo() = default;

explicit ChangeProblemStatementInfo(StringView nsp) : new_statement_path(nsp.to_string()) {}

[[nodiscard]] std::string dump() const { return new_statement_path; }
};

void restart_job(mysql::Connection& mysql, StringView job_id, bool notify_job_server);

// Notifies the Job server that there are jobs to do
Expand Down
1 change: 0 additions & 1 deletion subprojects/sim/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ gmock_dep = simlib_proj.get_variable('gmock_dep')

tests = {
'test/sim/cpp_syntax_highlighter.cc': {'args': [meson.current_source_dir() + '/test/sim/cpp_syntax_highlighter_test_cases/']},
'test/sim/jobs/utils.cc': {},
'test/sim/merging/merge_ids.cc': {'priority': 10},
'test/sim/sql/sql.cc': {},
'test/web_server/http/form_validation.cc': {},
Expand Down
6 changes: 2 additions & 4 deletions subprojects/sim/src/job_server/dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <memory>
#include <sim/jobs/old_job.hh>
#include <sim/old_mysql/old_mysql.hh>
#include <thread>

using sim::jobs::OldJob;
Expand All @@ -28,7 +29,6 @@ void job_dispatcher(
OldJob::Type jtype,
std::optional<uint64_t> file_id,
std::optional<uint64_t> aux_id,
StringView info,
StringView created_at
) {
STACK_UNWINDING_MARK;
Expand Down Expand Up @@ -84,9 +84,7 @@ void job_dispatcher(
break;

case JT::CHANGE_PROBLEM_STATEMENT:
job_handler = make_unique<ChangeProblemStatement>(
job_id, aux_id.value(), file_id.value(), sim::jobs::ChangeProblemStatementInfo(info)
);
job_handler = make_unique<ChangeProblemStatement>(job_id);
break;

case JT::JUDGE_SUBMISSION:
Expand Down
1 change: 0 additions & 1 deletion subprojects/sim/src/job_server/dispatcher.hh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ void job_dispatcher(
sim::jobs::OldJob::Type jtype,
std::optional<uint64_t> file_id,
std::optional<uint64_t> aux_id,
StringView info,
StringView created_at
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ std::vector<FileRemover> submit_solutions(

// Add jobs to judge the solutions
mysql.execute(InsertInto("jobs (created_at, file_id, creator, type, priority, "
"status, aux_id, info, data)")
"status, aux_id, data)")
.select(
"?, NULL, NULL, ?, ?, ?, id, '', ''",
"?, NULL, NULL, ?, ?, ?, id, ''",
curr_datetime,
Job::Type::JUDGE_SUBMISSION,
default_priority(Job::Type::JUDGE_SUBMISSION) + 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,112 +1,135 @@
#include "change_problem_statement.hh"

#include <simlib/file_manip.hh>
#include <sim/change_problem_statement_jobs/change_problem_statement_job.hh>
#include <sim/internal_files/internal_file.hh>
#include <sim/problems/problem.hh>
#include <sim/sql/sql.hh>
#include <simlib/concat_tostr.hh>
#include <simlib/file_remover.hh>
#include <simlib/libzip.hh>
#include <simlib/macros/wont_throw.hh>
#include <simlib/path.hh>
#include <simlib/sim/problem_package.hh>
#include <simlib/sim/simfile.hh>
#include <simlib/time.hh>
#include <zip.h>

using sim::jobs::OldJob;
using sim::change_problem_statement_jobs::ChangeProblemStatementJob;
using sim::jobs::Job;
using sim::problems::Problem;
using sim::sql::InsertInto;
using sim::sql::Select;
using sim::sql::Update;

namespace job_server::job_handlers {

void ChangeProblemStatement::run(sim::mysql::Connection& mysql) {
STACK_UNWINDING_MARK;

auto transaction = mysql.start_repeatable_read_transaction();
auto old_mysql = old_mysql::ConnectionView{mysql};
decltype(Job::aux_id)::value_type problem_id;
decltype(ChangeProblemStatementJob::new_statement_file_id) new_statement_file_id;
decltype(ChangeProblemStatementJob::path_for_new_statement) path_for_new_statement;
{
auto stmt =
mysql.execute(Select("j.aux_id, cpsj.new_statement_file_id, cpsj.path_for_new_statement"
)
.from("jobs j")
.inner_join("change_problem_statement_jobs cpsj")
.on("cpsj.id=j.id")
.where("j.id=?", job_id_));
stmt.res_bind(problem_id, new_statement_file_id, path_for_new_statement);
throw_assert(stmt.next());
}

uint64_t problem_file_id = 0;
std::string problem_package_path;
sim::Simfile simfile;
{
auto stmt = old_mysql.prepare("SELECT file_id, simfile FROM problems"
" WHERE id=?");
stmt.bind_and_execute(problem_id_);
InplaceBuff<0> simfile_str;
stmt.res_bind_all(problem_file_id, simfile_str);
if (not stmt.next()) {
return set_failure("Problem with ID = ", problem_id_, " does not exist");
decltype(Problem::file_id) problem_file_id;
decltype(Problem::simfile) simfile_as_str;
auto stmt =
mysql.execute(Select("file_id, simfile").from("problems").where("id=?", problem_id));
stmt.res_bind(problem_file_id, simfile_as_str);
if (!stmt.next()) {
return set_failure("Problem with ID = ", problem_id, " does not exist");
}

simfile = sim::Simfile(simfile_str.to_string());
problem_package_path = sim::internal_files::path_of(problem_file_id);
simfile = sim::Simfile{simfile_as_str};
simfile.load_all();
}

auto pkg_path = sim::internal_files::old_path_of(problem_file_id);

old_mysql.prepare("INSERT INTO internal_files (created_at) VALUES(?)")
.bind_and_execute(utc_mysql_datetime());
uint64_t new_file_id = old_mysql.insert_id();
auto new_pkg_path = sim::internal_files::old_path_of(new_file_id);
auto new_problem_file_id = sim::internal_files::new_internal_file_id(mysql);
auto new_problem_package_path = sim::internal_files::path_of(new_problem_file_id);

// Replace old statement with new statement

// Escape info.new_statement_path
if (info_.new_statement_path.empty()) {
info_.new_statement_path = WONT_THROW(simfile.statement.value());
// Escape path_for_new_statement
if (path_for_new_statement.empty()) {
path_for_new_statement = WONT_THROW(simfile.statement.value());
} else {
info_.new_statement_path = path_absolute(info_.new_statement_path).erase(0, 1);
path_for_new_statement = path_absolute(path_for_new_statement).erase(0, 1);
}

ZipFile src_zip(pkg_path, ZIP_RDONLY);
// Replace old statement with the new statement
auto src_zip = ZipFile{problem_package_path, ZIP_RDONLY};
auto main_dir = sim::zip_package_main_dir(src_zip);
auto old_statement_path = concat(main_dir, WONT_THROW(simfile.statement.value()));
auto new_statement_path = concat(main_dir, info_.new_statement_path);
auto simfile_path = concat(main_dir, "Simfile");

auto old_statement_path = concat_tostr(main_dir, WONT_THROW(simfile.statement.value()));
auto new_statement_path = concat_tostr(main_dir, path_for_new_statement);
auto simfile_path = concat_tostr(main_dir, "Simfile");
if (new_statement_path == simfile_path) {
return set_failure("Invalid new statement path - it would overwrite Simfile");
}
simfile.statement = path_for_new_statement;

simfile.statement = info_.new_statement_path;
auto simfile_str = simfile.dump();

FileRemover new_pkg_remover(new_pkg_path.to_string());
ZipFile dest_zip(new_pkg_path, ZIP_CREATE | ZIP_TRUNCATE);

// Copy old package contests and update the Simfile
auto simfile_as_str = simfile.dump();
auto new_problem_package_remover = FileRemover{new_problem_package_path};
auto dest_zip = ZipFile{new_problem_package_path, ZIP_CREATE | ZIP_TRUNCATE};
auto eno = src_zip.entries_no();
for (decltype(eno) i = 0; i < eno; ++i) {
auto entry_name = src_zip.get_name(i);
if (entry_name == simfile_path) {
dest_zip.file_add(simfile_path, dest_zip.source_buffer(simfile_str));
} else if (entry_name != old_statement_path) {
dest_zip.file_add(simfile_path, dest_zip.source_buffer(simfile_as_str));
} else if (entry_name != old_statement_path && entry_name != new_statement_path) {
dest_zip.file_add(entry_name, dest_zip.source_zip(src_zip, i));
}
}

// Add new statement file entry
dest_zip.file_add(
new_statement_path, dest_zip.source_file(sim::internal_files::old_path_of(job_file_id_))
new_statement_path,
dest_zip.source_file(sim::internal_files::path_of(new_statement_file_id))
);
// Save all data and close the new problem package
dest_zip.close();

// Add job to delete the old problem file
const auto current_datetime = utc_mysql_datetime();
mysql.execute(
InsertInto("jobs (file_id, creator, type, priority, status, created_at, aux_id, data)")
.select(
"file_id, NULL, ?, ?, ?, ?, NULL, ''",
Job::Type::DELETE_FILE,
default_priority(Job::Type::DELETE_FILE),
Job::Status::PENDING,
current_datetime
)
.from("problems")
.where("id=?", problem_id)
);

dest_zip.close(); // Write all data to the dest_zip

const auto current_date = utc_mysql_datetime();
// Add job to delete old problem file
old_mysql
.prepare("INSERT INTO jobs(file_id, creator, type, priority, status,"
" created_at, aux_id, info, data)"
" SELECT file_id, NULL, ?, ?, ?, ?, NULL, '', '' FROM problems"
" WHERE id=?")
.bind_and_execute(
EnumVal(OldJob::Type::DELETE_FILE),
default_priority(OldJob::Type::DELETE_FILE),
EnumVal(OldJob::Status::PENDING),
current_date,
problem_id_
);

// Use new package as problem file
old_mysql
.prepare("UPDATE problems SET file_id=?, simfile=?, updated_at=?"
" WHERE id=?")
.bind_and_execute(new_file_id, simfile_str, current_date, problem_id_);

job_done(mysql, from_unsafe{info_.dump()});

// Update the problem with new file and simfile
mysql.execute(Update("problems")
.set(
"file_id=?, simfile=?, updated_at=?",
new_problem_file_id,
simfile_as_str,
current_datetime
)
.where("id=?", problem_id));

job_done(mysql);
transaction.commit();
new_pkg_remover.cancel();
new_problem_package_remover.cancel();
}

} // namespace job_server::job_handlers
Loading

0 comments on commit c9468cd

Please sign in to comment.