Files
OJ2/apps/api/src/db/0019_unify_python_language.sql
T
xuyueandClaude Opus 5 8b4d8899f9
Deploy / deploy (push) Canceled after 0s
feat(判题机): 自建镜像升级工具链,语言收敛到 C / C++ / Python
上游 QingdaoU/JudgeServer 停更在 2024-04(registry 上的 latest 和 1.6.1 是同一份
镜像,编译器停在 gcc-13),没有新版可拉,所以自己重编。docker/judge/ 是只改工具链
的 Dockerfile 分叉,server/ 和 Judger/ 从上游固定 commit b28aa56 拉,一行没动。

镜像 oj2-judge-2(不在任何 registry 上:本机 build.sh --save → scp → docker load)
- gcc/g++ 13 → 14.2,Python 3.12 → 3.13.5,都是 trixie 默认
- Go / JDK / Node 整套删掉:前端的题目语言复选框从来只给 C / C++ / Python / SQL,
  12 万条提交里 Java 44 条、Golang 15、JavaScript 3,全是很早以前的
- 体积 1.1GB → 433MB;默认走清华源,构建 12 分钟 → 40 秒(--no-mirror 换回官方)
- deploy.sh 加一道自检:镜像不在本机就中止,并打印该跑的三条命令

C 的编译参数加三个 -Wno-error(implicit-function-declaration / int-conversion /
incompatible-pointer-types):gcc-14 把它们从 warning 提成了 error,而 -w 压不住。

语言值统一成 Python(迁移 0019 / 0020)
- 0019:Python3(104527 条提交)与 Python2(3 条)并成 Python,一并改掉 937 道题的
  languages、75 个 template 键、15 个 ast_rules 键、257 条 answers、1235 个用户的
  成就指标 _languages(languages_used 重算,总和 1928 → 1925,少的 3 个是同时用过
  两种 Python 的人)
- 0020:把 Java / JavaScript / Golang 从 84 道题的可选语言里摘掉 —— 不摘的话那些题
  的语言下拉还能选 Java,提交必 SYSTEM_ERROR
- 契约新增 normalizeLanguage() 别名表,判题侧一律走 judgeConfigFor():旧客户端
  localStorage 里的 Python3、迁移前排进队列的任务都还能判;协作的语言归一也走它,
  否则上线那一刻学生页面里的 Python3 会静默落到 C
- 回滚要连数据一起回,只滚代码会让所有 Python 提交变 SYSTEM_ERROR

实跑
- 判题冒烟 docker/judge/smoke.ts 13 条全过:三种语言、六种状态码、gcc 宽松度
- 拿备份里的真实代码逐文件比对新旧镜像的编译结果,0 差异:C 提交 1951 份
  (1725 过 / 226 CE)、C++ 882 份、Python 2000 份、20 篇 C 教程的 93 个代码块。
  不加那三个 -Wno-error 的话,C 有 26 份会从能过变成 CE
- 迁移在灌了 12.4 万行真实数据的一次性库里跑过:0 残留、没有题目被清空;
  dev 库用真正的执行器跑通
- check:ast 56 个 target 全过,前后端 typecheck 均 0

顺带记下一个升级之前就有的坑(现在随 Go 一起消失,写在 README 里):GOCACHE 指向
容器的 tmpfs,判题机重启后第一次 Go 提交是冷构建,Go 1.22 要 5.6 秒 CPU、超过 3 秒
的编译预算,于是重启后第一个交 Go 的学生必吃一次 CE,后面的人缓存热了又都正常。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 06:24:05 -06:00

78 lines
4.0 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 语言值统一成 `Python`:库里原来有 `Python3`(104527 条提交)和 `Python2`(3 条,
-- 全是 2022 年的),界面上两个都显示成「Python」,内部却是两个值。判题沙箱早就只剩
-- 一个 Python 了,这里把落库的值也并成一个。
--
-- 语言名不是判题状态码那种「判题机也认得的编码」—— 它只是我们自己的键(语言配置是
-- 整个对象发给判题机的),所以可以改。但它确实是**落库的值**,改完再回滚到旧版后端,
-- 旧代码查 languageConfigs["Python"] 会查不到 → 所有 Python 提交变 SYSTEM_ERROR。
-- 为此后端保留了 Python2/Python3 → Python 的别名(见 judge/languages.ts),
-- 新旧代码读哪一种数据都不会炸。
--
-- 涉及的四张表是全量扫备份确认过的(submission / problem / user_stat /
-- options_sysoptions)。options_sysoptions 里那行 `languages` 是 Django 时代的判题
-- 配置,OJ2 只读 website_* 几个键,不碰它,所以这里**故意不动**。
-- ① 提交记录。12 万条里 8 成是 Python,走一次全表 UPDATE。
UPDATE "submission" SET "language" = 'Python'
WHERE "language" IN ('Python2', 'Python3');--> statement-breakpoint
-- ② 题目的可选语言。用 WITH ORDINALITY 保住原来的顺序 —— 题目页的语言下拉和默认
-- 选中项就是按这个数组的顺序来的,打乱了学生打开题目看到的默认语言会变。
UPDATE "problem" p SET "languages" = (
SELECT COALESCE(jsonb_agg(
CASE WHEN v IN ('Python2', 'Python3') THEN 'Python' ELSE v END ORDER BY ord
), '[]'::jsonb)
FROM jsonb_array_elements_text(p."languages") WITH ORDINALITY AS t(v, ord)
)
WHERE EXISTS (
SELECT 1 FROM jsonb_array_elements_text(p."languages") x(v)
WHERE x.v IN ('Python2', 'Python3')
);--> statement-breakpoint
-- ③ 预制代码,键是语言名(75 道题有 Python3 的模板)。
UPDATE "problem"
SET "template" = ("template" - 'Python3') || jsonb_build_object('Python', "template" -> 'Python3')
WHERE jsonb_exists("template", 'Python3');--> statement-breakpoint
-- ④ AST 代码规则,键就是语言名(15 道题)。
UPDATE "problem"
SET "ast_rules" = ("ast_rules" - 'Python3') || jsonb_build_object('Python', "ast_rules" -> 'Python3')
WHERE "ast_rules" IS NOT NULL AND jsonb_exists("ast_rules", 'Python3');--> statement-breakpoint
-- ⑤ 参考答案,形如 [{"language": "...", "code": "..."}](257 条 Python3 答案)。
UPDATE "problem" p SET "answers" = (
SELECT jsonb_agg(
CASE WHEN a ->> 'language' IN ('Python2', 'Python3')
THEN jsonb_set(a, '{language}', '"Python"')
ELSE a END ORDER BY ord
)
FROM jsonb_array_elements(p."answers") WITH ORDINALITY AS t(a, ord)
)
WHERE p."answers" IS NOT NULL AND jsonb_typeof(p."answers") = 'array' AND EXISTS (
SELECT 1 FROM jsonb_array_elements(p."answers") x(a)
WHERE x.a ->> 'language' IN ('Python2', 'Python3')
);--> statement-breakpoint
-- ⑥ 成就指标里的「用过哪些语言」(1235 个用户)。_languages 去重之后重算
-- languages_used —— 同时用过 Python2 和 Python3 的那 3 个用户,数字会从 n 掉到
-- n-1,这是**对的**:那本来就是同一种语言。已经发出去的成就不回收。
WITH mapped AS (
SELECT s."id", jsonb_agg(d.v ORDER BY d.ord) AS arr
FROM "user_stat" s, LATERAL (
SELECT DISTINCT ON (val) val AS v, ord FROM (
SELECT CASE WHEN e IN ('Python2', 'Python3') THEN 'Python' ELSE e END AS val, ord
FROM jsonb_array_elements_text(s."metrics" -> '_languages') WITH ORDINALITY AS t(e, ord)
) m ORDER BY val, ord
) d
WHERE jsonb_typeof(s."metrics" -> '_languages') = 'array' AND EXISTS (
SELECT 1 FROM jsonb_array_elements_text(s."metrics" -> '_languages') x(e)
WHERE x.e IN ('Python2', 'Python3')
)
GROUP BY s."id"
)
UPDATE "user_stat" s SET "metrics" = jsonb_set(
jsonb_set(s."metrics", '{_languages}', mapped.arr),
'{languages_used}', to_jsonb(jsonb_array_length(mapped.arr))
)
FROM mapped WHERE mapped."id" = s."id";