diff --git a/CLAUDE.md b/CLAUDE.md index 868de21..e52cb95 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,6 +21,7 @@ OJ2 是判题狗(Online Judge)的后端重写:Django 6 → Bun + TypeScrip | `docs/timezone.md` | 动日历口径、动时间出参格式 | | `docs/contract.md` | 动 zod 契约、想给某个字段加校验 | | `docs/ast-rules.md` | 动 AST 代码规则、升级 tree-sitter | +| `docker/judge/README.md` | 换判题沙箱镜像、升语言版本(gcc / Python / Node …) | | `docs/specs/` | 两份设计文档:后端重写、课堂求助与协作编辑 | ## 仓库结构 @@ -114,6 +115,28 @@ handler。阶段 4 真实发生过一次,两个教师用的分析端点被吃 这些整数是**落库的值**:12 万条历史提交的 `submission.result` 就是它们,判题沙箱回的也是 这套编码,所以只能新增、不能改已有的含义。题目表情 reaction 的语义 key 同理。 +### 判题镜像是自己构建的 + +`compose.*.yml` 里的 `oj2-judge-2` **不在任何 registry 上**:上游 +QingdaoU/JudgeServer 停更在 2024-04(官方镜像的 `latest` 和 `1.6.1` 是同一份, +编译器停在 gcc-13),新工具链只能自己编。`docker/judge/` 里是只改版本的 Dockerfile +分叉 + 构建脚本 + 冒烟测试,判题逻辑一行没动。 + +- 新机器、换镜像:先 `docker/judge/build.sh --save` → scp → `docker load`,再部署。 + **服务器和机房各有各的判题沙箱,两边都要装。** +- 改工具链就把末尾序号 +1(下一版 `oj2-judge-3`)。`up -d` 不带 `--pull`,名字没变会静默用旧镜像。 +- 编译/运行命令在 `apps/api/src/judge/languages.ts`,不在镜像里。gcc-14 把隐式函数 + 声明等提成了 error(`-w` 压不住),那边的 `cLooseErrors` 三个 `-Wno-error=` 就是 + 为此加的 —— 删掉它们等于让一批历史题解和 C 教程示例集体 CE。 +- **判题沙箱只认 C / C++ / Python。** Java / JavaScript / Golang 连同镜像里的 + JDK / Node / Go 在 2026-09 一起砍了(前端本来就没给入口,12 万条提交里它们共 62 条), + 契约 `judgeLanguageSchema` 里的键留着是为了渲染那 62 条历史提交。 + **`Python3` / `Python2` 这两个旧值已经没有了** —— 0019 迁移把 104530 条提交、937 道题、 + 1235 个用户的成就指标并成了一个 `Python`,0020 顺手把那三种语言从题目的可选语言里摘掉 + (不摘的话 84 道题的语言下拉还能选 Java,提交必 SYSTEM_ERROR)。查判题配置走 + `judgeConfigFor()`,它带旧值别名;**回滚要连数据一起回**,只滚代码会让 Python 提交全炸。 +- 换完镜像跑 `bun docker/judge/smoke.ts`:三种语言、六种状态码、gcc 宽松度一起核。 + ### 出参不 `parse`,用 `satisfies` **后端的响应一律 `satisfies XxxType`,不要写 `xxxSchema.parse({...})`。** 出参是后端自己刚 @@ -138,7 +161,7 @@ handler。阶段 4 真实发生过一次,两个教师用的分析端点被吃 bun run --filter '@oj2/api' check:ast # 升级 tree-sitter-* 之后一定要跑 ``` -判题机只认 C / C++ / Python3(`AST_SUPPORTED_LANGUAGES`),别的语言配了规则一条都不会跑, +判题机只认 C / C++ / Python(`AST_SUPPORTED_LANGUAGES`),别的语言配了规则一条都不会跑, 所以后台不给它们开 tab —— **看得见却不检查**比没有更糟。C++ 的调用形态和 C 不一样、 规则的语义校验为什么不挂在 zod 上,见 `docs/ast-rules.md`。 diff --git a/apps/api/src/collab/handler.ts b/apps/api/src/collab/handler.ts index 6aa10ed..2bcac29 100644 --- a/apps/api/src/collab/handler.ts +++ b/apps/api/src/collab/handler.ts @@ -13,7 +13,7 @@ import { getRoom, hasTeacherOnline, listRequests, - normalizeLanguage, + normalizeCollabLanguage, openRoom, queueAheadOf, removeRequest, @@ -307,7 +307,7 @@ async function handleHelpRequest( className: student?.className ?? null, problemId, problemTitle: problem.title, - language: normalizeLanguage(language), + language: normalizeCollabLanguage(language), createdAt: Date.now(), status: "pending", socket: ws, @@ -327,7 +327,7 @@ function handleHelpLanguage(ws: CollabSocket, language: unknown) { const request = getRequest(ws.data.userId) // 比对 socket 归属:同账号的另一个标签页停在别的题上切语言,不该改这条求助 if (!request || request.socket !== ws) return - const next = normalizeLanguage(language) + const next = normalizeCollabLanguage(language) if (request.language === next) return request.language = next diff --git a/apps/api/src/collab/state.ts b/apps/api/src/collab/state.ts index 23e68d0..6814b1f 100644 --- a/apps/api/src/collab/state.ts +++ b/apps/api/src/collab/state.ts @@ -6,6 +6,8 @@ * 所以内存态够用,不需要 Redis 同步。进程重启丢掉全部状态,两端重连后回到干净状态。 */ +import { normalizeLanguage } from "@oj2/contract" + export type CollabSocket = Bun.ServerWebSocket< import("../websocket").SubmissionSocketData > @@ -17,8 +19,7 @@ export type CollabSocket = Bun.ServerWebSocket< export const COLLAB_LANGUAGES = [ "C", "C++", - "Python2", - "Python3", + "Python", "Java", "JavaScript", "Golang", @@ -27,10 +28,16 @@ export const COLLAB_LANGUAGES = [ export type CollabLanguage = (typeof COLLAB_LANGUAGES)[number] -/** 认不出来的一律当 C:老客户端不带这个字段,而它以前就是写死 C 的 */ -export function normalizeLanguage(value: unknown): CollabLanguage { - return (COLLAB_LANGUAGES as readonly string[]).includes(value as string) - ? (value as CollabLanguage) +/** + * 认不出来的一律当 C:老客户端不带这个字段,而它以前就是写死 C 的。 + * + * 先过契约的别名表 —— 上线那一刻学生页面里还揣着 `Python3`,不翻译的话会**静默** + * 落到 C,求助窗口里的代码高亮和同步编辑都按 C 走,没人会报错。 + */ +export function normalizeCollabLanguage(value: unknown): CollabLanguage { + const normalized = normalizeLanguage(value) ?? value + return (COLLAB_LANGUAGES as readonly string[]).includes(normalized as string) + ? (normalized as CollabLanguage) : "C" } diff --git a/apps/api/src/db/0019_unify_python_language.sql b/apps/api/src/db/0019_unify_python_language.sql new file mode 100644 index 0000000..87ed99d --- /dev/null +++ b/apps/api/src/db/0019_unify_python_language.sql @@ -0,0 +1,77 @@ +-- 语言值统一成 `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"; diff --git a/apps/api/src/db/0020_drop_unsupported_languages.sql b/apps/api/src/db/0020_drop_unsupported_languages.sql new file mode 100644 index 0000000..8a38622 --- /dev/null +++ b/apps/api/src/db/0020_drop_unsupported_languages.sql @@ -0,0 +1,41 @@ +-- 把 Java / JavaScript / Golang 从题目的可选语言里摘掉。 +-- +-- 这三种语言的判题配置和判题镜像里的 JDK / Node / Go 已经一起删了(见 +-- judge/languages.ts 和 docker/judge/)。但生产库里有 84 道题的 `languages` 还留着 +-- 它们,而题目页的语言下拉就是按这个数组渲染的 —— 不摘掉的话,学生能在那 84 道题上 +-- 选 Java 提交,判题时 languageConfigs 查不到就抛 Unsupported judge language, +-- 结果是 SYSTEM_ERROR。**这道迁移是那次删语言的收尾,不能只删代码不清数据。** +-- +-- 备份实测:84 道题受影响,其中**没有**任何一道只有这三种语言,所以不会有题目被清空。 +-- 保险起见加了 jsonb_array_length > 0 的条件:真要出现这种题,宁可留着不动、让它 +-- 在后台显形,也不要把语言清空(题目页会渲染出一个空的语言下拉)。 +-- +-- 历史提交里那 62 条 Java/JS/Golang 记录**不动**,语言名留在契约里就是为了渲染它们。 + +UPDATE "problem" p SET "languages" = ( + SELECT jsonb_agg(v ORDER BY ord) + FROM jsonb_array_elements_text(p."languages") WITH ORDINALITY AS t(v, ord) + WHERE v NOT IN ('Java', 'JavaScript', 'Golang') +) +WHERE EXISTS ( + SELECT 1 FROM jsonb_array_elements_text(p."languages") x(v) + WHERE x.v IN ('Java', 'JavaScript', 'Golang') +) AND ( + SELECT count(*) FROM jsonb_array_elements_text(p."languages") y(v) + WHERE y.v NOT IN ('Java', 'JavaScript', 'Golang') +) > 0;--> statement-breakpoint + +-- 预制代码和参考答案里对应的条目一并清掉(生产库里是空的,防后台以后写进去)。 +UPDATE "problem" +SET "template" = "template" - 'Java' - 'JavaScript' - 'Golang' +WHERE jsonb_exists_any("template", ARRAY['Java', 'JavaScript', 'Golang']);--> statement-breakpoint + +UPDATE "problem" p SET "answers" = ( + SELECT COALESCE(jsonb_agg(a ORDER BY ord), '[]'::jsonb) + FROM jsonb_array_elements(p."answers") WITH ORDINALITY AS t(a, ord) + WHERE a ->> 'language' NOT IN ('Java', 'JavaScript', 'Golang') +) +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 ('Java', 'JavaScript', 'Golang') +); diff --git a/apps/api/src/db/meta/0019_snapshot.json b/apps/api/src/db/meta/0019_snapshot.json new file mode 100644 index 0000000..1d16ee3 --- /dev/null +++ b/apps/api/src/db/meta/0019_snapshot.json @@ -0,0 +1,3963 @@ +{ + "id": "f8bcd6f8-5e1b-49b7-990d-8f961dfa5e31", + "prevId": "c1e443a0-71ae-40ba-8484-335584321ac9", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.achievement": { + "name": "achievement", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "achievement_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rarity": { + "name": "rarity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator": { + "name": "operator", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "threshold": { + "name": "threshold", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "unlock_count": { + "name": "unlock_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.acm_contest_rank": { + "name": "acm_contest_rank", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "submission_number": { + "name": "submission_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "accepted_number": { + "name": "accepted_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_time": { + "name": "total_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "submission_info": { + "name": "submission_info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "contest_id": { + "name": "contest_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "acm_rank_contest_user_idx": { + "name": "acm_rank_contest_user_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "acm_rank_order_idx": { + "name": "acm_rank_order_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "accepted_number", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "total_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "acm_contest_rank_contest_id_21030ccd_fk_contest_id": { + "name": "acm_contest_rank_contest_id_21030ccd_fk_contest_id", + "tableFrom": "acm_contest_rank", + "columnsFrom": [ + "contest_id" + ], + "tableTo": "contest", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "acm_contest_rank_user_id_40391ab2_fk_user_id": { + "name": "acm_contest_rank_user_id_40391ab2_fk_user_id", + "tableFrom": "acm_contest_rank", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_acm_rank_user_contest": { + "name": "unique_acm_rank_user_contest", + "columns": [ + "contest_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_analysis": { + "name": "ai_analysis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "ai_analysis_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_prompt": { + "name": "user_prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "analysis": { + "name": "analysis", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_pinned": { + "name": "is_pinned", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "ai_analysis_user_id_3aa23011": { + "name": "ai_analysis_user_id_3aa23011", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "ai_analysis_user_id_3aa23011_fk_user_id": { + "name": "ai_analysis_user_id_3aa23011_fk_user_id", + "tableFrom": "ai_analysis", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_hint": { + "name": "ai_hint", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "ai_hint_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_version": { + "name": "prompt_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "diagnosis": { + "name": "diagnosis", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "diagnosis_error": { + "name": "diagnosis_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "helpful": { + "name": "helpful", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "feedback_time": { + "name": "feedback_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "ai_hint_submission_id_idx": { + "name": "ai_hint_submission_id_idx", + "columns": [ + { + "expression": "submission_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "ai_hint_submission_id_fk_submission_id": { + "name": "ai_hint_submission_id_fk_submission_id", + "tableFrom": "ai_hint", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.announcement": { + "name": "announcement", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "top": { + "name": "top", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "announcement_created_by_id_359ccf50": { + "name": "announcement_created_by_id_359ccf50", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "announcement_list_idx": { + "name": "announcement_list_idx", + "columns": [ + { + "expression": "visible", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "top", + "isExpression": false, + "asc": false, + "nulls": "first" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": false, + "nulls": "first" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "announcement_created_by_id_359ccf50_fk_user_id": { + "name": "announcement_created_by_id_359ccf50_fk_user_id", + "tableFrom": "announcement", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contest": { + "name": "contest", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_time": { + "name": "start_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "contest_created_by_id_a763ca7e": { + "name": "contest_created_by_id_a763ca7e", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "contest_created_by_id_a763ca7e_fk_user_id": { + "name": "contest_created_by_id_a763ca7e_fk_user_id", + "tableFrom": "contest", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exercise": { + "name": "exercise", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "exercise_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "type": { + "name": "type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "tutorial_id": { + "name": "tutorial_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "exercise_tutorial_id_6fd04055": { + "name": "exercise_tutorial_id_6fd04055", + "columns": [ + { + "expression": "tutorial_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "exercise_tutorial_id_6fd04055_fk_tutorial_id": { + "name": "exercise_tutorial_id_6fd04055_fk_tutorial_id", + "tableFrom": "exercise", + "columnsFrom": [ + "tutorial_id" + ], + "tableTo": "tutorial", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exercise_attempt": { + "name": "exercise_attempt", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "exercise_id": { + "name": "exercise_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "wrong_attempts": { + "name": "wrong_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "solved": { + "name": "solved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "attempts_to_solve": { + "name": "attempts_to_solve", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_wrong_answer": { + "name": "last_wrong_answer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_attempt_at": { + "name": "first_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "solved_at": { + "name": "solved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "exercise_attempt_exercise_id_idx": { + "name": "exercise_attempt_exercise_id_idx", + "columns": [ + { + "expression": "exercise_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "exercise_attempt_user_id_fk_user_id": { + "name": "exercise_attempt_user_id_fk_user_id", + "tableFrom": "exercise_attempt", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "exercise_attempt_exercise_id_fk_exercise_id": { + "name": "exercise_attempt_exercise_id_fk_exercise_id", + "tableFrom": "exercise_attempt", + "columnsFrom": [ + "exercise_id" + ], + "tableTo": "exercise", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "exercise_attempt_pkey": { + "name": "exercise_attempt_pkey", + "columns": [ + "user_id", + "exercise_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.flowchart_submission": { + "name": "flowchart_submission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mermaid_code": { + "name": "mermaid_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "flowchart_data": { + "name": "flowchart_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ai_score": { + "name": "ai_score", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "ai_grade": { + "name": "ai_grade", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "ai_feedback": { + "name": "ai_feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ai_suggestions": { + "name": "ai_suggestions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ai_criteria_details": { + "name": "ai_criteria_details", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "ai_provider": { + "name": "ai_provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "ai_model": { + "name": "ai_model", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "processing_time": { + "name": "processing_time", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "evaluation_time": { + "name": "evaluation_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "flowchart_problem_time_idx": { + "name": "flowchart_problem_time_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "flowchart_status_idx": { + "name": "flowchart_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "flowchart_create_time_idx": { + "name": "flowchart_create_time_idx", + "columns": [ + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "flowchart_user_time_idx": { + "name": "flowchart_user_time_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "flowchart_submission_problem_id_8551edbf_fk_problem_id": { + "name": "flowchart_submission_problem_id_8551edbf_fk_problem_id", + "tableFrom": "flowchart_submission", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "flowchart_submission_user_id_225c83e8_fk_user_id": { + "name": "flowchart_submission_user_id_225c83e8_fk_user_id", + "tableFrom": "flowchart_submission", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.judge_server": { + "name": "judge_server", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "judger_version": { + "name": "judger_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cpu_core": { + "name": "cpu_core", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "memory_usage": { + "name": "memory_usage", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "cpu_usage": { + "name": "cpu_usage", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "last_heartbeat": { + "name": "last_heartbeat", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "task_number": { + "name": "task_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "service_url": { + "name": "service_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_disabled": { + "name": "is_disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message": { + "name": "message", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "message_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "recipient_id": { + "name": "recipient_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sender_id": { + "name": "sender_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "message_recipient_time_idx": { + "name": "message_recipient_time_idx", + "columns": [ + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "message_sender_id_a2a2e825": { + "name": "message_sender_id_a2a2e825", + "columns": [ + { + "expression": "sender_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "message_submission_id_2fdf8a47": { + "name": "message_submission_id_2fdf8a47", + "columns": [ + { + "expression": "submission_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "message_recipient_id_2aa5dd76_fk_user_id": { + "name": "message_recipient_id_2aa5dd76_fk_user_id", + "tableFrom": "message", + "columnsFrom": [ + "recipient_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "message_sender_id_a2a2e825_fk_user_id": { + "name": "message_sender_id_a2a2e825_fk_user_id", + "tableFrom": "message", + "columnsFrom": [ + "sender_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "message_submission_id_2fdf8a47_fk_submission_id": { + "name": "message_submission_id_2fdf8a47_fk_submission_id", + "tableFrom": "message", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.options_sysoptions": { + "name": "options_sysoptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "options_sysoptions_key_key": { + "name": "options_sysoptions_key_key", + "columns": [ + "key" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problem": { + "name": "problem", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_description": { + "name": "input_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "output_description": { + "name": "output_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "samples": { + "name": "samples", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "test_case_id": { + "name": "test_case_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "test_case_score": { + "name": "test_case_score", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "hint": { + "name": "hint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "languages": { + "name": "languages", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "template": { + "name": "template", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "time_limit": { + "name": "time_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "memory_limit": { + "name": "memory_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "difficulty": { + "name": "difficulty", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "submission_number": { + "name": "submission_number", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "accepted_number": { + "name": "accepted_number", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_id": { + "name": "_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "statistic_info": { + "name": "statistic_info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "contest_id": { + "name": "contest_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "share_submission": { + "name": "share_submission", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "answers": { + "name": "answers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "allow_flowchart": { + "name": "allow_flowchart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "flowchart_data": { + "name": "flowchart_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "flowchart_hint": { + "name": "flowchart_hint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mermaid_code": { + "name": "mermaid_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "show_flowchart": { + "name": "show_flowchart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "ast_rules": { + "name": "ast_rules", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "sql_config": { + "name": "sql_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "sql_display": { + "name": "sql_display", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "problem_contest_visible_idx": { + "name": "problem_contest_visible_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + }, + { + "expression": "visible", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problem_created_by_id_cb362143": { + "name": "problem_created_by_id_cb362143", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problem_visible_idx": { + "name": "problem_visible_idx", + "columns": [ + { + "expression": "visible", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problem_contest_id_328e013a_fk_contest_id": { + "name": "problem_contest_id_328e013a_fk_contest_id", + "tableFrom": "problem", + "columnsFrom": [ + "contest_id" + ], + "tableTo": "contest", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "problem_created_by_id_cb362143_fk_user_id": { + "name": "problem_created_by_id_cb362143_fk_user_id", + "tableFrom": "problem", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_problem_id_contest": { + "name": "unique_problem_id_contest", + "columns": [ + "_id", + "contest_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problem_tag": { + "name": "problem_tag", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problem_tag_name_ci_unique": { + "name": "problem_tag_name_ci_unique", + "columns": [ + { + "expression": "lower(name)", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problem_tags": { + "name": "problem_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemtag_id": { + "name": "problemtag_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problem_tags_problemtag_id_72d20571": { + "name": "problem_tags_problemtag_id_72d20571", + "columns": [ + { + "expression": "problemtag_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problem_tags_problem_id_866ecb8d_fk_problem_id": { + "name": "problem_tags_problem_id_866ecb8d_fk_problem_id", + "tableFrom": "problem_tags", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problem_tags_problemtag_id_72d20571_fk_problem_tag_id": { + "name": "problem_tags_problemtag_id_72d20571_fk_problem_tag_id", + "tableFrom": "problem_tags", + "columnsFrom": [ + "problemtag_id" + ], + "tableTo": "problem_tag", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "problem_tags_problem_id_problemtag_id_318459d1_uniq": { + "name": "problem_tags_problem_id_problemtag_id_318459d1_uniq", + "columns": [ + "problem_id", + "problemtag_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset": { + "name": "problemset", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "difficulty": { + "name": "difficulty", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "problemset_created_by_id_01b5197f": { + "name": "problemset_created_by_id_01b5197f", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_created_by_id_01b5197f_fk_user_id": { + "name": "problemset_created_by_id_01b5197f_fk_user_id", + "tableFrom": "problemset", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_badge": { + "name": "problemset_badge", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_badge_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "condition_type": { + "name": "condition_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "condition_value": { + "name": "condition_value", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset_badge_problemset_id_6cb6c74f": { + "name": "problemset_badge_problemset_id_6cb6c74f", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_badge_problemset_id_6cb6c74f_fk_problemset_id": { + "name": "problemset_badge_problemset_id_6cb6c74f_fk_problemset_id", + "tableFrom": "problemset_badge", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_problem": { + "name": "problemset_problem", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_problem_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_required": { + "name": "is_required", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "score": { + "name": "score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "hint": { + "name": "hint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset_problem_problem_id_fff2d686": { + "name": "problemset_problem_problem_id_fff2d686", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_problem_problem_id_fff2d686_fk_problem_id": { + "name": "problemset_problem_problem_id_fff2d686_fk_problem_id", + "tableFrom": "problemset_problem", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_problem_problemset_id_350d17fb_fk_problemset_id": { + "name": "problemset_problem_problemset_id_350d17fb_fk_problemset_id", + "tableFrom": "problemset_problem", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_problemset_problem": { + "name": "unique_problemset_problem", + "columns": [ + "problem_id", + "problemset_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_progress": { + "name": "problemset_progress", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_progress_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "join_time": { + "name": "join_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "complete_time": { + "name": "complete_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "is_completed": { + "name": "is_completed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "progress_percentage": { + "name": "progress_percentage", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "completed_problems_count": { + "name": "completed_problems_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_problems_count": { + "name": "total_problems_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_score": { + "name": "total_score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "progress_detail": { + "name": "progress_detail", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset_progress_user_id_c8041a80": { + "name": "problemset_progress_user_id_c8041a80", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_progress_problemset_id_20a9632e_fk_problemset_id": { + "name": "problemset_progress_problemset_id_20a9632e_fk_problemset_id", + "tableFrom": "problemset_progress", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_progress_user_id_c8041a80_fk_user_id": { + "name": "problemset_progress_user_id_c8041a80_fk_user_id", + "tableFrom": "problemset_progress", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_problemset_progress_user": { + "name": "unique_problemset_progress_user", + "columns": [ + "problemset_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_submission": { + "name": "problemset_submission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_submission_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset__problem_1f39fa_idx": { + "name": "problemset__problem_1f39fa_idx", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset__problem_22f053_idx": { + "name": "problemset__problem_22f053_idx", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + }, + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset__user_id_2f1501_idx": { + "name": "problemset__user_id_2f1501_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset_submission_problem_id_5629b105": { + "name": "problemset_submission_problem_id_5629b105", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset_submission_submission_id_78e2b807": { + "name": "problemset_submission_submission_id_78e2b807", + "columns": [ + { + "expression": "submission_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_submission_problem_id_5629b105_fk_problem_id": { + "name": "problemset_submission_problem_id_5629b105_fk_problem_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_submission_problemset_id_85290e17_fk_problemset_id": { + "name": "problemset_submission_problemset_id_85290e17_fk_problemset_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_submission_submission_id_78e2b807_fk_submission_id": { + "name": "problemset_submission_submission_id_78e2b807_fk_submission_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_submission_user_id_915fc9c6_fk_user_id": { + "name": "problemset_submission_user_id_915fc9c6_fk_user_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction": { + "name": "reaction", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "reaction_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "reaction_problem_type_idx": { + "name": "reaction_problem_type_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reaction_user_id_cfa7f469": { + "name": "reaction_user_id_cfa7f469", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "reaction_problem_id_a7f3b9f3_fk_problem_id": { + "name": "reaction_problem_id_a7f3b9f3_fk_problem_id", + "tableFrom": "reaction", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "reaction_user_id_cfa7f469_fk_user_id": { + "name": "reaction_user_id_cfa7f469_fk_user_id", + "tableFrom": "reaction", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "reaction_problem_user_unique": { + "name": "reaction_problem_user_unique", + "columns": [ + "problem_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.submission": { + "name": "submission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "contest_id": { + "name": "contest_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 6 + }, + "info": { + "name": "info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared": { + "name": "shared", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "statistic_info": { + "name": "statistic_info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "contest_create_time_idx": { + "name": "contest_create_time_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": false, + "nulls": "first" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "submission_public_create_time_id_idx": { + "name": "submission_public_create_time_id_idx", + "columns": [ + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "problem_user_idx": { + "name": "problem_user_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "submission_result_37e2f67a": { + "name": "submission_result_37e2f67a", + "columns": [ + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_create_time_idx": { + "name": "user_create_time_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "submission_language_time_idx": { + "name": "submission_language_time_idx", + "columns": [ + { + "expression": "language", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_result_time_idx": { + "name": "submission_result_time_idx", + "columns": [ + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_public_problem_time_idx": { + "name": "submission_public_problem_time_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_public_username_trgm_idx": { + "name": "submission_public_username_trgm_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_public_metrics_idx": { + "name": "submission_public_metrics_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_problemset_id_idx": { + "name": "submission_problemset_id_idx", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"problemset_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "submission_contest_id_775716d5_fk_contest_id": { + "name": "submission_contest_id_775716d5_fk_contest_id", + "tableFrom": "submission", + "columnsFrom": [ + "contest_id" + ], + "tableTo": "contest", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "submission_problem_id_76847b55_fk_problem_id": { + "name": "submission_problem_id_76847b55_fk_problem_id", + "tableFrom": "submission", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "submission_problemset_id_fk_problemset_id": { + "name": "submission_problemset_id_fk_problemset_id", + "tableFrom": "submission", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.submission_trace": { + "name": "submission_trace", + "schema": "", + "columns": { + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "active_ms": { + "name": "active_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "since_open_ms": { + "name": "since_open_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "typed_chars": { + "name": "typed_chars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pasted_chars": { + "name": "pasted_chars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paste_count": { + "name": "paste_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "max_paste": { + "name": "max_paste", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "deleted_chars": { + "name": "deleted_chars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "blur_count": { + "name": "blur_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "initial_len": { + "name": "initial_len", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "collab": { + "name": "collab", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "since_prev_ms": { + "name": "since_prev_ms", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "submission_trace_submission_id_fk_submission_id": { + "name": "submission_trace_submission_id_fk_submission_id", + "tableFrom": "submission_trace", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tutorial": { + "name": "tutorial", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "tutorial_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "title": { + "name": "title", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "tutorial_created_by_id_07973cab": { + "name": "tutorial_created_by_id_07973cab", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "tutorial_created_by_id_07973cab_fk_user_id": { + "name": "tutorial_created_by_id_07973cab_fk_user_id", + "tableFrom": "tutorial", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tutorial_progress": { + "name": "tutorial_progress", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tutorial_id": { + "name": "tutorial_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "view_count": { + "name": "view_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_seconds": { + "name": "total_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_viewed_at": { + "name": "first_viewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_viewed_at": { + "name": "last_viewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "tutorial_progress_tutorial_id_idx": { + "name": "tutorial_progress_tutorial_id_idx", + "columns": [ + { + "expression": "tutorial_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "tutorial_progress_user_id_fk_user_id": { + "name": "tutorial_progress_user_id_fk_user_id", + "tableFrom": "tutorial_progress", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "tutorial_progress_tutorial_id_fk_tutorial_id": { + "name": "tutorial_progress_tutorial_id_fk_tutorial_id", + "tableFrom": "tutorial_progress", + "columnsFrom": [ + "tutorial_id" + ], + "tableTo": "tutorial", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "tutorial_progress_pkey": { + "name": "tutorial_progress_pkey", + "columns": [ + "user_id", + "tutorial_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "last_login": { + "name": "last_login", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "admin_type": { + "name": "admin_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_disabled": { + "name": "is_disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "problem_permission": { + "name": "problem_permission", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "raw_password": { + "name": "raw_password", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "class_name": { + "name": "class_name", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_active_idx": { + "name": "user_active_idx", + "columns": [ + { + "expression": "is_disabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_login", + "isExpression": false, + "asc": false, + "nulls": "first" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_class_name_idx": { + "name": "user_class_name_idx", + "columns": [ + { + "expression": "class_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_username_key": { + "name": "user_username_key", + "columns": [ + "username" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_achievement": { + "name": "user_achievement", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "user_achievement_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "unlock_time": { + "name": "unlock_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "backfilled": { + "name": "backfilled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notified": { + "name": "notified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "achievement_id": { + "name": "achievement_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_achievement_achievement_id_29db600d": { + "name": "user_achievement_achievement_id_29db600d", + "columns": [ + { + "expression": "achievement_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_achv_notified_idx": { + "name": "user_achv_notified_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "notified", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_achv_time_idx": { + "name": "user_achv_time_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "unlock_time", + "isExpression": false, + "asc": false, + "nulls": "first", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "user_achievement_achievement_id_29db600d_fk_achievement_id": { + "name": "user_achievement_achievement_id_29db600d_fk_achievement_id", + "tableFrom": "user_achievement", + "columnsFrom": [ + "achievement_id" + ], + "tableTo": "achievement", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_achievement_user_id_b8ec7d6a_fk_user_id": { + "name": "user_achievement_user_id_b8ec7d6a_fk_user_id", + "tableFrom": "user_achievement", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_user_achievement": { + "name": "unique_user_achievement", + "columns": [ + "achievement_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_badge": { + "name": "user_badge", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "user_badge_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "earned_time": { + "name": "earned_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "badge_id": { + "name": "badge_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_badge_badge_id_92a983e9": { + "name": "user_badge_badge_id_92a983e9", + "columns": [ + { + "expression": "badge_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "user_badge_badge_id_92a983e9_fk_problemset_badge_id": { + "name": "user_badge_badge_id_92a983e9_fk_problemset_badge_id", + "tableFrom": "user_badge", + "columnsFrom": [ + "badge_id" + ], + "tableTo": "problemset_badge", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_badge_user_id_a286d718_fk_user_id": { + "name": "user_badge_user_id_a286d718_fk_user_id", + "tableFrom": "user_badge", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_user_badge": { + "name": "unique_user_badge", + "columns": [ + "badge_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_profile": { + "name": "user_profile", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "acm_problems_status": { + "name": "acm_problems_status", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mood": { + "name": "mood", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accepted_number": { + "name": "accepted_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "submission_number": { + "name": "submission_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "real_name": { + "name": "real_name", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_profile_user_id_8fdce8e2_fk_user_id": { + "name": "user_profile_user_id_8fdce8e2_fk_user_id", + "tableFrom": "user_profile", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_profile_user_id_key": { + "name": "user_profile_user_id_key", + "columns": [ + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_stat": { + "name": "user_stat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "user_stat_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "metrics": { + "name": "metrics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "update_time": { + "name": "update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_stat_user_id_73337fc0_fk_user_id": { + "name": "user_stat_user_id_73337fc0_fk_user_id", + "tableFrom": "user_stat", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_stat_user_id_key": { + "name": "user_stat_user_id_key", + "columns": [ + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/src/db/meta/0020_snapshot.json b/apps/api/src/db/meta/0020_snapshot.json new file mode 100644 index 0000000..397fc65 --- /dev/null +++ b/apps/api/src/db/meta/0020_snapshot.json @@ -0,0 +1,3963 @@ +{ + "id": "6c6c5dc0-dbfd-4a14-9f82-a1cd26041dc1", + "prevId": "f8bcd6f8-5e1b-49b7-990d-8f961dfa5e31", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.achievement": { + "name": "achievement", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "achievement_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rarity": { + "name": "rarity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator": { + "name": "operator", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "threshold": { + "name": "threshold", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "unlock_count": { + "name": "unlock_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.acm_contest_rank": { + "name": "acm_contest_rank", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "submission_number": { + "name": "submission_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "accepted_number": { + "name": "accepted_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_time": { + "name": "total_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "submission_info": { + "name": "submission_info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "contest_id": { + "name": "contest_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "acm_rank_contest_user_idx": { + "name": "acm_rank_contest_user_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "acm_rank_order_idx": { + "name": "acm_rank_order_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "accepted_number", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "total_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "acm_contest_rank_contest_id_21030ccd_fk_contest_id": { + "name": "acm_contest_rank_contest_id_21030ccd_fk_contest_id", + "tableFrom": "acm_contest_rank", + "columnsFrom": [ + "contest_id" + ], + "tableTo": "contest", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "acm_contest_rank_user_id_40391ab2_fk_user_id": { + "name": "acm_contest_rank_user_id_40391ab2_fk_user_id", + "tableFrom": "acm_contest_rank", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_acm_rank_user_contest": { + "name": "unique_acm_rank_user_contest", + "columns": [ + "contest_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_analysis": { + "name": "ai_analysis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "ai_analysis_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_prompt": { + "name": "user_prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "analysis": { + "name": "analysis", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_pinned": { + "name": "is_pinned", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "ai_analysis_user_id_3aa23011": { + "name": "ai_analysis_user_id_3aa23011", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "ai_analysis_user_id_3aa23011_fk_user_id": { + "name": "ai_analysis_user_id_3aa23011_fk_user_id", + "tableFrom": "ai_analysis", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_hint": { + "name": "ai_hint", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "ai_hint_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_version": { + "name": "prompt_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "diagnosis": { + "name": "diagnosis", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "diagnosis_error": { + "name": "diagnosis_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "helpful": { + "name": "helpful", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "feedback_time": { + "name": "feedback_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "ai_hint_submission_id_idx": { + "name": "ai_hint_submission_id_idx", + "columns": [ + { + "expression": "submission_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "ai_hint_submission_id_fk_submission_id": { + "name": "ai_hint_submission_id_fk_submission_id", + "tableFrom": "ai_hint", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.announcement": { + "name": "announcement", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "top": { + "name": "top", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "announcement_created_by_id_359ccf50": { + "name": "announcement_created_by_id_359ccf50", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "announcement_list_idx": { + "name": "announcement_list_idx", + "columns": [ + { + "expression": "visible", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "top", + "isExpression": false, + "asc": false, + "nulls": "first" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": false, + "nulls": "first" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "announcement_created_by_id_359ccf50_fk_user_id": { + "name": "announcement_created_by_id_359ccf50_fk_user_id", + "tableFrom": "announcement", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contest": { + "name": "contest", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_time": { + "name": "start_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "contest_created_by_id_a763ca7e": { + "name": "contest_created_by_id_a763ca7e", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "contest_created_by_id_a763ca7e_fk_user_id": { + "name": "contest_created_by_id_a763ca7e_fk_user_id", + "tableFrom": "contest", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exercise": { + "name": "exercise", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "exercise_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "type": { + "name": "type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "tutorial_id": { + "name": "tutorial_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "exercise_tutorial_id_6fd04055": { + "name": "exercise_tutorial_id_6fd04055", + "columns": [ + { + "expression": "tutorial_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "exercise_tutorial_id_6fd04055_fk_tutorial_id": { + "name": "exercise_tutorial_id_6fd04055_fk_tutorial_id", + "tableFrom": "exercise", + "columnsFrom": [ + "tutorial_id" + ], + "tableTo": "tutorial", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exercise_attempt": { + "name": "exercise_attempt", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "exercise_id": { + "name": "exercise_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "wrong_attempts": { + "name": "wrong_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "solved": { + "name": "solved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "attempts_to_solve": { + "name": "attempts_to_solve", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_wrong_answer": { + "name": "last_wrong_answer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_attempt_at": { + "name": "first_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "solved_at": { + "name": "solved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "exercise_attempt_exercise_id_idx": { + "name": "exercise_attempt_exercise_id_idx", + "columns": [ + { + "expression": "exercise_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "exercise_attempt_user_id_fk_user_id": { + "name": "exercise_attempt_user_id_fk_user_id", + "tableFrom": "exercise_attempt", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "exercise_attempt_exercise_id_fk_exercise_id": { + "name": "exercise_attempt_exercise_id_fk_exercise_id", + "tableFrom": "exercise_attempt", + "columnsFrom": [ + "exercise_id" + ], + "tableTo": "exercise", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "exercise_attempt_pkey": { + "name": "exercise_attempt_pkey", + "columns": [ + "user_id", + "exercise_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.flowchart_submission": { + "name": "flowchart_submission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mermaid_code": { + "name": "mermaid_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "flowchart_data": { + "name": "flowchart_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ai_score": { + "name": "ai_score", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "ai_grade": { + "name": "ai_grade", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "ai_feedback": { + "name": "ai_feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ai_suggestions": { + "name": "ai_suggestions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ai_criteria_details": { + "name": "ai_criteria_details", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "ai_provider": { + "name": "ai_provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "ai_model": { + "name": "ai_model", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "processing_time": { + "name": "processing_time", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "evaluation_time": { + "name": "evaluation_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "flowchart_problem_time_idx": { + "name": "flowchart_problem_time_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "flowchart_status_idx": { + "name": "flowchart_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "flowchart_create_time_idx": { + "name": "flowchart_create_time_idx", + "columns": [ + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "flowchart_user_time_idx": { + "name": "flowchart_user_time_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "flowchart_submission_problem_id_8551edbf_fk_problem_id": { + "name": "flowchart_submission_problem_id_8551edbf_fk_problem_id", + "tableFrom": "flowchart_submission", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "flowchart_submission_user_id_225c83e8_fk_user_id": { + "name": "flowchart_submission_user_id_225c83e8_fk_user_id", + "tableFrom": "flowchart_submission", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.judge_server": { + "name": "judge_server", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "judger_version": { + "name": "judger_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cpu_core": { + "name": "cpu_core", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "memory_usage": { + "name": "memory_usage", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "cpu_usage": { + "name": "cpu_usage", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "last_heartbeat": { + "name": "last_heartbeat", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "task_number": { + "name": "task_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "service_url": { + "name": "service_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_disabled": { + "name": "is_disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message": { + "name": "message", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "message_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "recipient_id": { + "name": "recipient_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sender_id": { + "name": "sender_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "message_recipient_time_idx": { + "name": "message_recipient_time_idx", + "columns": [ + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "message_sender_id_a2a2e825": { + "name": "message_sender_id_a2a2e825", + "columns": [ + { + "expression": "sender_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "message_submission_id_2fdf8a47": { + "name": "message_submission_id_2fdf8a47", + "columns": [ + { + "expression": "submission_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "message_recipient_id_2aa5dd76_fk_user_id": { + "name": "message_recipient_id_2aa5dd76_fk_user_id", + "tableFrom": "message", + "columnsFrom": [ + "recipient_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "message_sender_id_a2a2e825_fk_user_id": { + "name": "message_sender_id_a2a2e825_fk_user_id", + "tableFrom": "message", + "columnsFrom": [ + "sender_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "message_submission_id_2fdf8a47_fk_submission_id": { + "name": "message_submission_id_2fdf8a47_fk_submission_id", + "tableFrom": "message", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.options_sysoptions": { + "name": "options_sysoptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "options_sysoptions_key_key": { + "name": "options_sysoptions_key_key", + "columns": [ + "key" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problem": { + "name": "problem", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_description": { + "name": "input_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "output_description": { + "name": "output_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "samples": { + "name": "samples", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "test_case_id": { + "name": "test_case_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "test_case_score": { + "name": "test_case_score", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "hint": { + "name": "hint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "languages": { + "name": "languages", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "template": { + "name": "template", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "time_limit": { + "name": "time_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "memory_limit": { + "name": "memory_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "difficulty": { + "name": "difficulty", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "submission_number": { + "name": "submission_number", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "accepted_number": { + "name": "accepted_number", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_id": { + "name": "_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "statistic_info": { + "name": "statistic_info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "contest_id": { + "name": "contest_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "share_submission": { + "name": "share_submission", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "answers": { + "name": "answers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "allow_flowchart": { + "name": "allow_flowchart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "flowchart_data": { + "name": "flowchart_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "flowchart_hint": { + "name": "flowchart_hint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mermaid_code": { + "name": "mermaid_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "show_flowchart": { + "name": "show_flowchart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "ast_rules": { + "name": "ast_rules", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "sql_config": { + "name": "sql_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "sql_display": { + "name": "sql_display", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "problem_contest_visible_idx": { + "name": "problem_contest_visible_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + }, + { + "expression": "visible", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problem_created_by_id_cb362143": { + "name": "problem_created_by_id_cb362143", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problem_visible_idx": { + "name": "problem_visible_idx", + "columns": [ + { + "expression": "visible", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problem_contest_id_328e013a_fk_contest_id": { + "name": "problem_contest_id_328e013a_fk_contest_id", + "tableFrom": "problem", + "columnsFrom": [ + "contest_id" + ], + "tableTo": "contest", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "problem_created_by_id_cb362143_fk_user_id": { + "name": "problem_created_by_id_cb362143_fk_user_id", + "tableFrom": "problem", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_problem_id_contest": { + "name": "unique_problem_id_contest", + "columns": [ + "_id", + "contest_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problem_tag": { + "name": "problem_tag", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problem_tag_name_ci_unique": { + "name": "problem_tag_name_ci_unique", + "columns": [ + { + "expression": "lower(name)", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problem_tags": { + "name": "problem_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemtag_id": { + "name": "problemtag_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problem_tags_problemtag_id_72d20571": { + "name": "problem_tags_problemtag_id_72d20571", + "columns": [ + { + "expression": "problemtag_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problem_tags_problem_id_866ecb8d_fk_problem_id": { + "name": "problem_tags_problem_id_866ecb8d_fk_problem_id", + "tableFrom": "problem_tags", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problem_tags_problemtag_id_72d20571_fk_problem_tag_id": { + "name": "problem_tags_problemtag_id_72d20571_fk_problem_tag_id", + "tableFrom": "problem_tags", + "columnsFrom": [ + "problemtag_id" + ], + "tableTo": "problem_tag", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "problem_tags_problem_id_problemtag_id_318459d1_uniq": { + "name": "problem_tags_problem_id_problemtag_id_318459d1_uniq", + "columns": [ + "problem_id", + "problemtag_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset": { + "name": "problemset", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_update_time": { + "name": "last_update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "difficulty": { + "name": "difficulty", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "problemset_created_by_id_01b5197f": { + "name": "problemset_created_by_id_01b5197f", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_created_by_id_01b5197f_fk_user_id": { + "name": "problemset_created_by_id_01b5197f_fk_user_id", + "tableFrom": "problemset", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_badge": { + "name": "problemset_badge", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_badge_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "condition_type": { + "name": "condition_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "condition_value": { + "name": "condition_value", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset_badge_problemset_id_6cb6c74f": { + "name": "problemset_badge_problemset_id_6cb6c74f", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_badge_problemset_id_6cb6c74f_fk_problemset_id": { + "name": "problemset_badge_problemset_id_6cb6c74f_fk_problemset_id", + "tableFrom": "problemset_badge", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_problem": { + "name": "problemset_problem", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_problem_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_required": { + "name": "is_required", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "score": { + "name": "score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "hint": { + "name": "hint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset_problem_problem_id_fff2d686": { + "name": "problemset_problem_problem_id_fff2d686", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_problem_problem_id_fff2d686_fk_problem_id": { + "name": "problemset_problem_problem_id_fff2d686_fk_problem_id", + "tableFrom": "problemset_problem", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_problem_problemset_id_350d17fb_fk_problemset_id": { + "name": "problemset_problem_problemset_id_350d17fb_fk_problemset_id", + "tableFrom": "problemset_problem", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_problemset_problem": { + "name": "unique_problemset_problem", + "columns": [ + "problem_id", + "problemset_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_progress": { + "name": "problemset_progress", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_progress_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "join_time": { + "name": "join_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "complete_time": { + "name": "complete_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "is_completed": { + "name": "is_completed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "progress_percentage": { + "name": "progress_percentage", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "completed_problems_count": { + "name": "completed_problems_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_problems_count": { + "name": "total_problems_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_score": { + "name": "total_score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "progress_detail": { + "name": "progress_detail", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset_progress_user_id_c8041a80": { + "name": "problemset_progress_user_id_c8041a80", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_progress_problemset_id_20a9632e_fk_problemset_id": { + "name": "problemset_progress_problemset_id_20a9632e_fk_problemset_id", + "tableFrom": "problemset_progress", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_progress_user_id_c8041a80_fk_user_id": { + "name": "problemset_progress_user_id_c8041a80_fk_user_id", + "tableFrom": "problemset_progress", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_problemset_progress_user": { + "name": "unique_problemset_progress_user", + "columns": [ + "problemset_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.problemset_submission": { + "name": "problemset_submission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "problemset_submission_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problemset__problem_1f39fa_idx": { + "name": "problemset__problem_1f39fa_idx", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset__problem_22f053_idx": { + "name": "problemset__problem_22f053_idx", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + }, + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset__user_id_2f1501_idx": { + "name": "problemset__user_id_2f1501_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset_submission_problem_id_5629b105": { + "name": "problemset_submission_problem_id_5629b105", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "problemset_submission_submission_id_78e2b807": { + "name": "problemset_submission_submission_id_78e2b807", + "columns": [ + { + "expression": "submission_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "problemset_submission_problem_id_5629b105_fk_problem_id": { + "name": "problemset_submission_problem_id_5629b105_fk_problem_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_submission_problemset_id_85290e17_fk_problemset_id": { + "name": "problemset_submission_problemset_id_85290e17_fk_problemset_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_submission_submission_id_78e2b807_fk_submission_id": { + "name": "problemset_submission_submission_id_78e2b807_fk_submission_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "problemset_submission_user_id_915fc9c6_fk_user_id": { + "name": "problemset_submission_user_id_915fc9c6_fk_user_id", + "tableFrom": "problemset_submission", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction": { + "name": "reaction", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "reaction_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "reaction_problem_type_idx": { + "name": "reaction_problem_type_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reaction_user_id_cfa7f469": { + "name": "reaction_user_id_cfa7f469", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "reaction_problem_id_a7f3b9f3_fk_problem_id": { + "name": "reaction_problem_id_a7f3b9f3_fk_problem_id", + "tableFrom": "reaction", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "reaction_user_id_cfa7f469_fk_user_id": { + "name": "reaction_user_id_cfa7f469_fk_user_id", + "tableFrom": "reaction", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "reaction_problem_user_unique": { + "name": "reaction_problem_user_unique", + "columns": [ + "problem_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.submission": { + "name": "submission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "contest_id": { + "name": "contest_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "problem_id": { + "name": "problem_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 6 + }, + "info": { + "name": "info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared": { + "name": "shared", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "statistic_info": { + "name": "statistic_info", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "problemset_id": { + "name": "problemset_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "contest_create_time_idx": { + "name": "contest_create_time_idx", + "columns": [ + { + "expression": "contest_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": false, + "nulls": "first" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "submission_public_create_time_id_idx": { + "name": "submission_public_create_time_id_idx", + "columns": [ + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "problem_user_idx": { + "name": "problem_user_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "submission_result_37e2f67a": { + "name": "submission_result_37e2f67a", + "columns": [ + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_create_time_idx": { + "name": "user_create_time_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "submission_language_time_idx": { + "name": "submission_language_time_idx", + "columns": [ + { + "expression": "language", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_result_time_idx": { + "name": "submission_result_time_idx", + "columns": [ + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_public_problem_time_idx": { + "name": "submission_public_problem_time_idx", + "columns": [ + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_public_username_trgm_idx": { + "name": "submission_public_username_trgm_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_public_metrics_idx": { + "name": "submission_public_metrics_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "problem_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "create_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"contest_id\" is null", + "concurrently": false + }, + "submission_problemset_id_idx": { + "name": "submission_problemset_id_idx", + "columns": [ + { + "expression": "problemset_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"submission\".\"problemset_id\" is not null", + "concurrently": false + } + }, + "foreignKeys": { + "submission_contest_id_775716d5_fk_contest_id": { + "name": "submission_contest_id_775716d5_fk_contest_id", + "tableFrom": "submission", + "columnsFrom": [ + "contest_id" + ], + "tableTo": "contest", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "submission_problem_id_76847b55_fk_problem_id": { + "name": "submission_problem_id_76847b55_fk_problem_id", + "tableFrom": "submission", + "columnsFrom": [ + "problem_id" + ], + "tableTo": "problem", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "submission_problemset_id_fk_problemset_id": { + "name": "submission_problemset_id_fk_problemset_id", + "tableFrom": "submission", + "columnsFrom": [ + "problemset_id" + ], + "tableTo": "problemset", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.submission_trace": { + "name": "submission_trace", + "schema": "", + "columns": { + "submission_id": { + "name": "submission_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "active_ms": { + "name": "active_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "since_open_ms": { + "name": "since_open_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "typed_chars": { + "name": "typed_chars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pasted_chars": { + "name": "pasted_chars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paste_count": { + "name": "paste_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "max_paste": { + "name": "max_paste", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "deleted_chars": { + "name": "deleted_chars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "blur_count": { + "name": "blur_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "initial_len": { + "name": "initial_len", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "collab": { + "name": "collab", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "since_prev_ms": { + "name": "since_prev_ms", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "submission_trace_submission_id_fk_submission_id": { + "name": "submission_trace_submission_id_fk_submission_id", + "tableFrom": "submission_trace", + "columnsFrom": [ + "submission_id" + ], + "tableTo": "submission", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tutorial": { + "name": "tutorial", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "tutorial_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "title": { + "name": "title", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "tutorial_created_by_id_07973cab": { + "name": "tutorial_created_by_id_07973cab", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "tutorial_created_by_id_07973cab_fk_user_id": { + "name": "tutorial_created_by_id_07973cab_fk_user_id", + "tableFrom": "tutorial", + "columnsFrom": [ + "created_by_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tutorial_progress": { + "name": "tutorial_progress", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tutorial_id": { + "name": "tutorial_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "view_count": { + "name": "view_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_seconds": { + "name": "total_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_viewed_at": { + "name": "first_viewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_viewed_at": { + "name": "last_viewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "tutorial_progress_tutorial_id_idx": { + "name": "tutorial_progress_tutorial_id_idx", + "columns": [ + { + "expression": "tutorial_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "tutorial_progress_user_id_fk_user_id": { + "name": "tutorial_progress_user_id_fk_user_id", + "tableFrom": "tutorial_progress", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "tutorial_progress_tutorial_id_fk_tutorial_id": { + "name": "tutorial_progress_tutorial_id_fk_tutorial_id", + "tableFrom": "tutorial_progress", + "columnsFrom": [ + "tutorial_id" + ], + "tableTo": "tutorial", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "tutorial_progress_pkey": { + "name": "tutorial_progress_pkey", + "columns": [ + "user_id", + "tutorial_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "last_login": { + "name": "last_login", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "create_time": { + "name": "create_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "admin_type": { + "name": "admin_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_disabled": { + "name": "is_disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "problem_permission": { + "name": "problem_permission", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "raw_password": { + "name": "raw_password", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "class_name": { + "name": "class_name", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_active_idx": { + "name": "user_active_idx", + "columns": [ + { + "expression": "is_disabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_login", + "isExpression": false, + "asc": false, + "nulls": "first" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_class_name_idx": { + "name": "user_class_name_idx", + "columns": [ + { + "expression": "class_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_username_key": { + "name": "user_username_key", + "columns": [ + "username" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_achievement": { + "name": "user_achievement", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "user_achievement_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "unlock_time": { + "name": "unlock_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "backfilled": { + "name": "backfilled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notified": { + "name": "notified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "achievement_id": { + "name": "achievement_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_achievement_achievement_id_29db600d": { + "name": "user_achievement_achievement_id_29db600d", + "columns": [ + { + "expression": "achievement_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_achv_notified_idx": { + "name": "user_achv_notified_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "notified", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "user_achv_time_idx": { + "name": "user_achv_time_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + }, + { + "expression": "unlock_time", + "isExpression": false, + "asc": false, + "nulls": "first", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "user_achievement_achievement_id_29db600d_fk_achievement_id": { + "name": "user_achievement_achievement_id_29db600d_fk_achievement_id", + "tableFrom": "user_achievement", + "columnsFrom": [ + "achievement_id" + ], + "tableTo": "achievement", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_achievement_user_id_b8ec7d6a_fk_user_id": { + "name": "user_achievement_user_id_b8ec7d6a_fk_user_id", + "tableFrom": "user_achievement", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_user_achievement": { + "name": "unique_user_achievement", + "columns": [ + "achievement_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_badge": { + "name": "user_badge", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "user_badge_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "earned_time": { + "name": "earned_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "badge_id": { + "name": "badge_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_badge_badge_id_92a983e9": { + "name": "user_badge_badge_id_92a983e9", + "columns": [ + { + "expression": "badge_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int8_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "user_badge_badge_id_92a983e9_fk_problemset_badge_id": { + "name": "user_badge_badge_id_92a983e9_fk_problemset_badge_id", + "tableFrom": "user_badge", + "columnsFrom": [ + "badge_id" + ], + "tableTo": "problemset_badge", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_badge_user_id_a286d718_fk_user_id": { + "name": "user_badge_user_id_a286d718_fk_user_id", + "tableFrom": "user_badge", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_user_badge": { + "name": "unique_user_badge", + "columns": [ + "badge_id", + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_profile": { + "name": "user_profile", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "acm_problems_status": { + "name": "acm_problems_status", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mood": { + "name": "mood", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accepted_number": { + "name": "accepted_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "submission_number": { + "name": "submission_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "real_name": { + "name": "real_name", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_profile_user_id_8fdce8e2_fk_user_id": { + "name": "user_profile_user_id_8fdce8e2_fk_user_id", + "tableFrom": "user_profile", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_profile_user_id_key": { + "name": "user_profile_user_id_key", + "columns": [ + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_stat": { + "name": "user_stat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "user_stat_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "byDefault" + } + }, + "metrics": { + "name": "metrics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "update_time": { + "name": "update_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_stat_user_id_73337fc0_fk_user_id": { + "name": "user_stat_user_id_73337fc0_fk_user_id", + "tableFrom": "user_stat", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_stat_user_id_key": { + "name": "user_stat_user_id_key", + "columns": [ + "user_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/src/db/meta/_journal.json b/apps/api/src/db/meta/_journal.json index 40c708f..2e1f84a 100644 --- a/apps/api/src/db/meta/_journal.json +++ b/apps/api/src/db/meta/_journal.json @@ -134,6 +134,20 @@ "when": 1789822227451, "tag": "0018_ai_hint_diagnosis", "breakpoints": true + }, + { + "idx": 19, + "version": "7", + "when": 1789906464541, + "tag": "0019_unify_python_language", + "breakpoints": true + }, + { + "idx": 20, + "version": "7", + "when": 1789906612433, + "tag": "0020_drop_unsupported_languages", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/api/src/db/schema.ts b/apps/api/src/db/schema.ts index 8801f7b..0c1c626 100644 --- a/apps/api/src/db/schema.ts +++ b/apps/api/src/db/schema.ts @@ -858,11 +858,12 @@ export const submission = pgTable( * 提交列表的「语言」和「结果」两个下拉筛选。原来这两列上要么没索引、要么只有 * 不带 `contest_id IS NULL` 的单列索引,翻页那条靠 submission_public_create_time_id_idx * 边扫边滤还能对付,**count 那条只能全表扫**(快照实测固定 75~82ms / 18448 buffers, - * 筛什么值都一样)。加完:语言 count 80ms → 11ms(Python3,占 8 成)/ 1.6ms(C), + * 筛什么值都一样)。加完:语言 count 80ms → 11ms(Python,占 8 成)/ 1.6ms(C), * 结果 count 75ms → 2.0ms。 * - * 更要命的是冷门语言的**翻页**:Python2 只有 3 条、全是 2022 年的,分页索引得从 - * 最新一路倒扫到底才凑够一页,43ms 全表扫;走这条索引是 0.02ms。 + * 更要命的是冷门语言的**翻页**:JavaScript 只有 3 条、全是很早以前的,分页索引得从 + * 最新一路倒扫到底才凑够一页,43ms 全表扫;走这条索引是 0.02ms。(这个实测当年用的 + * 是 Python2,那 3 条 2026-09 已被 0019 迁移并进 Python,换了个同样冷门的值举例。) * * 两列都 ASC NULLS LAST,理由同上面 submission_public_create_time_id_idx —— * 靠 Index Scan **Backward** 出 `ORDER BY create_time DESC`。这里再实测了一遍: diff --git a/apps/api/src/judge/ast.ts b/apps/api/src/judge/ast.ts index 3f61983..55d3ee9 100644 --- a/apps/api/src/judge/ast.ts +++ b/apps/api/src/judge/ast.ts @@ -236,7 +236,7 @@ function rangePassed(count: number, rule: AstRule) { const CALL_NODE_TYPES: Record = { C: "call_expression", "C++": "call_expression", - Python3: "call", + Python: "call", } function functionCalls(root: Node, target: string, language: string) { @@ -267,7 +267,7 @@ function methodCalls(root: Node, target: string, language: string) { ) }) } - if (language !== "Python3") return [] + if (language !== "Python") return [] return collectNodes(root, "call").filter((call) => { const fn = call.childForFieldName("function") return ( diff --git a/apps/api/src/judge/languages.ts b/apps/api/src/judge/languages.ts index 0237226..625c4f7 100644 --- a/apps/api/src/judge/languages.ts +++ b/apps/api/src/judge/languages.ts @@ -1,9 +1,40 @@ +import { normalizeLanguage } from "@oj2/contract" + +/** + * 判题沙箱认得的语言,**只有 C / C++ / Python 这三种**。 + * + * Java / Golang / JavaScript 在 2026-09 连同镜像里的 JDK、Go、Node 工具链一起砍掉了: + * 前端从来没给过它们入口(后台题目的语言复选框只有 Python / C / C++ / SQL), + * 生产库 12 万条提交里它们一共 62 条,全是很早以前的。砍掉之后判题镜像小了一半多。 + * + * 契约 `judgeLanguageSchema` 里那几个键**故意留着** —— 那是渲染历史提交要用的。 + * 想恢复某种语言,得同时改这里和 `docker/judge/Dockerfile` 的工具链,再重建镜像。 + * + * `Python` 这个键 2026-09 之前叫 `Python3`(库里还有 3 条更老的 `Python2`), + * 0019 迁移把数据并成了一个值。查配置一律走 `judgeConfigFor()`,别直接下标 —— + * 那里带着旧值的别名,迁移之前排进队列的任务、旧客户端传上来的值都还能判。 + * + * SQL 题不走这里,走 `judge/sql/`;流程图题走 AI 评分。 + */ const defaultEnv = [ "LANG=en_US.UTF-8", "LANGUAGE=en_US:en", "LC_ALL=en_US.UTF-8", ] +/** + * gcc-14 起这三类老写法从 warning 提成了 error,而 `-w` 只关警告、压不住 error: + * 隐式函数声明(忘了 `#include ` 就用 printf)、int 与指针互赋、 + * 不兼容的指针类型。判题机镜像 2026-09 从 gcc-13 升到 14(见 docker/judge/), + * 不加这三个开关的话,**一批历史题解和 20 篇 C 教程的示例会突然全部 CE**。 + * + * 只给 C 加:C++ 那边这些本来就是 error,g++ 升版不改判定。 + * 哪天决定「就是要学生写规范」,是删掉这三行,不是改镜像 —— 删之前先拿 + * docs/c-tutorials/verify-code.sh 全量过一遍教程。 + */ +const cLooseErrors = + "-Wno-error=implicit-function-declaration -Wno-error=int-conversion -Wno-error=incompatible-pointer-types" + export const languageConfigs: Record> = { C: { template: "", @@ -13,8 +44,7 @@ export const languageConfigs: Record> = { max_cpu_time: 3000, max_real_time: 10000, max_memory: 256 * 1024 * 1024, - compile_command: - "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c17 {src_path} -lm -o {exe_path}", + compile_command: `/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c17 ${cLooseErrors} {src_path} -lm -o {exe_path}`, }, run: { command: "{exe_path}", @@ -39,24 +69,7 @@ export const languageConfigs: Record> = { env: defaultEnv, }, }, - Java: { - template: "", - compile: { - src_name: "Main.java", - exe_name: "Main", - max_cpu_time: 5000, - max_real_time: 10000, - max_memory: -1, - compile_command: "/usr/bin/javac {src_path} -d {exe_dir}", - }, - run: { - command: "/usr/bin/java -cp {exe_dir} -XX:MaxRAM={max_memory}k Main", - seccomp_rule: null, - env: defaultEnv, - memory_limit_check_only: 1, - }, - }, - Python3: { + Python: { template: "", compile: { src_name: "solution.py", @@ -72,40 +85,16 @@ export const languageConfigs: Record> = { env: defaultEnv, }, }, - Golang: { - template: "", - compile: { - src_name: "main.go", - exe_name: "main", - max_cpu_time: 3000, - max_real_time: 5000, - max_memory: 1024 * 1024 * 1024, - compile_command: "/usr/bin/go build -o {exe_path} {src_path}", - env: ["GOCACHE=/tmp", "GOPATH=/tmp", "GOMAXPROCS=1", ...defaultEnv], - }, - run: { - command: "{exe_path}", - seccomp_rule: "golang", - env: ["GOMAXPROCS=1", ...defaultEnv], - memory_limit_check_only: 1, - }, - }, - JavaScript: { - template: "", - compile: { - src_name: "main.js", - exe_name: "main.js", - max_cpu_time: 3000, - max_real_time: 5000, - max_memory: 1024 * 1024 * 1024, - compile_command: "/usr/bin/node --check {src_path}", - env: defaultEnv, - }, - run: { - command: "/usr/bin/node {exe_path}", - seccomp_rule: "node", - env: defaultEnv, - memory_limit_check_only: 1, - }, - }, +} + +/** + * 按语言取判题配置。**判题侧一律走这个函数**,不要直接 `languageConfigs[x]`: + * 它先过 `normalizeLanguage()`,所以 `Python3` / `Python2` 这类旧值也能命中。 + */ +export function judgeConfigFor(language: string) { + return ( + languageConfigs[language] ?? + languageConfigs[normalizeLanguage(language) ?? ""] ?? + null + ) } diff --git a/apps/api/src/judge/run.ts b/apps/api/src/judge/run.ts index a0777d8..9af9e92 100644 --- a/apps/api/src/judge/run.ts +++ b/apps/api/src/judge/run.ts @@ -14,7 +14,7 @@ import { recordSolvedProblem } from "../services/problemset" import { checkAst, type AstRule } from "./ast" import { publishSubmissionUpdate } from "./events" import type { JudgeJobData } from "./job" -import { languageConfigs } from "./languages" +import { judgeConfigFor } from "./languages" import { isAccepted, JudgeStatus, type JudgeStatusValue } from "./status" import { parseProblemTemplate } from "./template" import { runSqlCase } from "./sql" @@ -77,7 +77,7 @@ async function requestJudge( memoryLimit: number, testCaseId: string, ) { - const languageConfig = languageConfigs[language] + const languageConfig = judgeConfigFor(language) if (!languageConfig) throw new Error(`Unsupported judge language: ${language}`) diff --git a/apps/api/src/scripts/check-ast-targets.ts b/apps/api/src/scripts/check-ast-targets.ts index e4ac2e4..a956ad3 100644 --- a/apps/api/src/scripts/check-ast-targets.ts +++ b/apps/api/src/scripts/check-ast-targets.ts @@ -31,7 +31,7 @@ import treeSitterWasmPath from "web-tree-sitter/web-tree-sitter.wasm" with { typ const WASM_BY_LANGUAGE: Record = { C: cWasmPath, "C++": cppWasmPath, - Python3: pythonWasmPath, + Python: pythonWasmPath, } await Parser.init({ locateFile: () => treeSitterWasmPath }) diff --git a/apps/web/src/admin/problem/components/AstRulesEditor.vue b/apps/web/src/admin/problem/components/AstRulesEditor.vue index df71098..f5e7a29 100644 --- a/apps/web/src/admin/problem/components/AstRulesEditor.vue +++ b/apps/web/src/admin/problem/components/AstRulesEditor.vue @@ -16,7 +16,7 @@ const emit = defineEmits<{ (e: "update:modelValue", value: AstRules | null): void }>() -// 判题机只认 C / Python3,别的语言配了规则也一条都不会跑(judge/ast.ts 的 +// 判题机只认 C / Python,别的语言配了规则也一条都不会跑(judge/ast.ts 的 // loadLanguage 返回 null 就直接放行)。原来这里按题目的全部语言开 tab,老师给 // C++ 配的规则存得下、题目页也照常显示成「要求」,判题却从不检查。 const supportedLanguages = computed(() => @@ -26,7 +26,7 @@ const unsupportedLanguages = computed(() => props.languages.filter((lang) => !AST_SUPPORTED_LANGUAGES.includes(lang)), ) -const activeTab = ref(supportedLanguages.value[0] || "Python3") +const activeTab = ref(supportedLanguages.value[0] || "Python") const ENGINE_OPTIONS: SelectOption[] = [ { diff --git a/apps/web/src/admin/problem/components/TestcaseGenerator.vue b/apps/web/src/admin/problem/components/TestcaseGenerator.vue index b77a365..1122aef 100644 --- a/apps/web/src/admin/problem/components/TestcaseGenerator.vue +++ b/apps/web/src/admin/problem/components/TestcaseGenerator.vue @@ -47,7 +47,7 @@ function makeInitialFiles(): FileEntry[] { const files = ref(makeInitialFiles()) -const selectedLanguage = ref("Python3") +const selectedLanguage = ref("Python") // 始终显示所有语言,不管有没有答案代码 const availableLanguages = computed(() => diff --git a/apps/web/src/admin/problem/detail.vue b/apps/web/src/admin/problem/detail.vue index 0df81c5..7bb40c9 100644 --- a/apps/web/src/admin/problem/detail.vue +++ b/apps/web/src/admin/problem/detail.vue @@ -63,7 +63,7 @@ const problem = useLocalStorage(STORAGE_KEY.ADMIN_PROBLEM, { difficulty: "Low", visible: false, tags: [], - languages: ["Python3", "C"] as LANGUAGE[], + languages: ["Python", "C"] as LANGUAGE[], template: {} as { [key in LANGUAGE]?: string }, samples: [ { input: "", output: "" }, @@ -153,8 +153,8 @@ function validateNewTags(v: string[]) { // 这几个用的少,就不缓存本地了 const [needTemplate, toggleNeedTemplate] = useToggle(false) const template = reactive(JSON.parse(JSON.stringify(CODE_TEMPLATES))) -const currentActiveTemplate = ref("Python3") -const currentActiveAnswer = ref("Python3") +const currentActiveTemplate = ref("Python") +const currentActiveAnswer = ref("Python") // 给 TextEditor 用 const [ready, toggleReady] = useToggle(false) @@ -169,7 +169,7 @@ const difficultyOptions: SelectOption[] = [ ] const languageOptions = [ - { label: LANGUAGE_SHOW_VALUE["Python3"], value: "Python3" }, + { label: LANGUAGE_SHOW_VALUE["Python"], value: "Python" }, { label: LANGUAGE_SHOW_VALUE["C"], value: "C" }, { label: LANGUAGE_SHOW_VALUE["C++"], value: "C++" }, { label: LANGUAGE_SHOW_VALUE["SQL"], value: "SQL" }, @@ -511,7 +511,7 @@ async function generateMermaid() { isAIGenerating.value = true try { const res = await generateFlowchartFromPythonCode( - problem.value.answers.filter((a) => a.language === "Python3")[0].code, + problem.value.answers.filter((a) => a.language === "Python")[0].code, ) problem.value.mermaidCode = res.flowchart message.warning("如果渲染不成功,请复制到外部 AI 网站检查语法") @@ -736,7 +736,7 @@ watch( > diff --git a/apps/web/src/oj/api.ts b/apps/web/src/oj/api.ts index 3385171..f5ed70f 100644 --- a/apps/web/src/oj/api.ts +++ b/apps/web/src/oj/api.ts @@ -131,7 +131,7 @@ export function submitCode(data: SubmitCodePayload) { export function formatCode(data: { code: string; language: string }) { const languages: Record = { - Python3: "python", + Python: "python", C: "c", "C++": "cpp", SQL: "sql", diff --git a/apps/web/src/oj/learn/index.vue b/apps/web/src/oj/learn/index.vue index 52eb5eb..5deef2f 100644 --- a/apps/web/src/oj/learn/index.vue +++ b/apps/web/src/oj/learn/index.vue @@ -137,7 +137,7 @@ const tutorial = ref>({ }) const editorLanguage = computed(() => - tutorial.value.type === "c" ? "C" : "Python3", + tutorial.value.type === "c" ? "C" : "Python", ) const titles = ref<{ id: number; title: string }[]>([]) const progress = ref>({}) diff --git a/apps/web/src/oj/problem/components/Form.vue b/apps/web/src/oj/problem/components/Form.vue index f0be406..143c29f 100644 --- a/apps/web/src/oj/problem/components/Form.vue +++ b/apps/web/src/oj/problem/components/Form.vue @@ -254,8 +254,8 @@ const goEdit = () => { onMounted(() => { if (!languages.value.includes(codeStore.code.language)) { - // 回退到题目支持的第一种语言(如 SQL 题只有 "SQL",硬编码 Python3 会被后端拒绝) - codeStore.code.language = languages.value[0] ?? "Python3" + // 回退到题目支持的第一种语言(如 SQL 题只有 "SQL",硬编码 Python 会被后端拒绝) + codeStore.code.language = languages.value[0] ?? "Python" } }) diff --git a/apps/web/src/oj/problem/components/SubmitCode.vue b/apps/web/src/oj/problem/components/SubmitCode.vue index 4b5e583..4850096 100644 --- a/apps/web/src/oj/problem/components/SubmitCode.vue +++ b/apps/web/src/oj/problem/components/SubmitCode.vue @@ -56,11 +56,11 @@ const isFormatting = ref(false) const isSubmittingRequest = ref(false) // ==================== Python 语法检测器预取 ==================== -// 选中 Python3 时就把 Skulpt 拉下来,避免点提交时才开始下载 +// 选中 Python 时就把 Skulpt 拉下来,避免点提交时才开始下载 watch( () => codeStore.code.language, (language) => { - if (language === "Python3") prefetchPythonSyntaxChecker() + if (language === "Python") prefetchPythonSyntaxChecker() }, { immediate: true }, ) @@ -114,8 +114,8 @@ const buttonState = computed(() => async function submit() { if (buttonState.value.disabled) return - // 0. Python3 语法检测 - if (codeStore.code.language === "Python3") { + // 0. Python 语法检测 + if (codeStore.code.language === "Python") { const syntaxError = await checkPythonSyntax(codeStore.code.value) if (syntaxError) { message.warning(`第 ${syntaxError.line} 行存在语法错误,请修正后再提交`) @@ -123,7 +123,7 @@ async function submit() { } } - // 0.5 提交前自动格式化(Python3 用 ruff,C/C++ 用 clang-format,SQL 用 sqlparse) + // 0.5 提交前自动格式化(Python 用 ruff,C/C++ 用 clang-format,SQL 用 sqlparse) const formatLang = LANGUAGE_FORMAT_VALUE[codeStore.code.language] if (["python", "c", "cpp", "sql"].includes(formatLang)) { isFormatting.value = true @@ -135,7 +135,7 @@ async function submit() { codeStore.setCode(res.code) } catch (e: any) { if (e?.error === "format-error") { - // 仅 Python3 会出现:代码本身存在语法错误 + // 仅 Python 会出现:代码本身存在语法错误 message.warning(`代码格式化失败:${e.data},请检查代码后重试`) return } diff --git a/apps/web/src/oj/problem/utils/pythonSyntaxCheck.ts b/apps/web/src/oj/problem/utils/pythonSyntaxCheck.ts index 3b5bf9d..d7071ac 100644 --- a/apps/web/src/oj/problem/utils/pythonSyntaxCheck.ts +++ b/apps/web/src/oj/problem/utils/pythonSyntaxCheck.ts @@ -5,7 +5,7 @@ export interface PythonSyntaxError { let skulptPromise: Promise | null = null /** - * 按需加载 Skulpt(约 233KB gzip),只在提交 Python3 代码时才下载。 + * 按需加载 Skulpt(约 233KB gzip),只在提交 Python 代码时才下载。 * 结果缓存,同一页面只加载一次。 */ function loadSkulpt(): Promise { diff --git a/apps/web/src/oj/store/code.ts b/apps/web/src/oj/store/code.ts index 1b24ac2..f0405b4 100644 --- a/apps/web/src/oj/store/code.ts +++ b/apps/web/src/oj/store/code.ts @@ -1,3 +1,4 @@ +import { normalizeLanguage } from "@oj2/contract" import { defineStore } from "pinia" import { STORAGE_KEY } from "utils/constants" import storage from "utils/storage" @@ -11,7 +12,9 @@ export const useCodeStore = defineStore("code", () => { // ==================== 状态 ==================== const code = reactive({ value: "", - language: storage.get(STORAGE_KEY.LANGUAGE) || "Python3", + // 过一道 normalizeLanguage:上线那一刻学生浏览器的 localStorage 里存的还是 + // 旧值 Python3,直接拿来用会被后端的契约挡掉(而且报错看不出是这个原因) + language: normalizeLanguage(storage.get(STORAGE_KEY.LANGUAGE)) ?? "Python", }) const input = ref("") diff --git a/apps/web/src/oj/submission/list.vue b/apps/web/src/oj/submission/list.vue index a75e4a0..623ff1b 100644 --- a/apps/web/src/oj/submission/list.vue +++ b/apps/web/src/oj/submission/list.vue @@ -147,7 +147,7 @@ const gradeOptions: SelectOption[] = [ const languageOptions: SelectOption[] = [ { label: "流程图", value: "Flowchart" }, { label: "全部语言", value: "" }, - { label: "Python", value: "Python3" }, + { label: "Python", value: "Python" }, { label: "C语言", value: "C" }, { label: "C++", value: "C++" }, ] diff --git a/apps/web/src/shared/components/CodeEditor.vue b/apps/web/src/shared/components/CodeEditor.vue index 1f234ff..c6db7a2 100644 --- a/apps/web/src/shared/components/CodeEditor.vue +++ b/apps/web/src/shared/components/CodeEditor.vue @@ -27,7 +27,7 @@ interface Props { } const { - language = "Python3", + language = "Python", fontSize = 20, height = "100%", readonly = false, @@ -41,7 +41,7 @@ const isDark = useDark() const langExtension = computed(() => { if (language === "SQL") return sql({ dialect: SQLite, upperCaseKeywords: true }) - return ["Python2", "Python3"].includes(language) ? python() : cpp() + return language === "Python" ? python() : cpp() }) const extensions = computed(() => [ diff --git a/apps/web/src/shared/components/SyncCodeEditor.vue b/apps/web/src/shared/components/SyncCodeEditor.vue index e6198f3..213babb 100644 --- a/apps/web/src/shared/components/SyncCodeEditor.vue +++ b/apps/web/src/shared/components/SyncCodeEditor.vue @@ -41,7 +41,7 @@ interface Props { } const { - language = "Python3", + language = "Python", fontSize = 20, height = "100%", readonly = false, diff --git a/apps/web/src/shared/extensions/language.ts b/apps/web/src/shared/extensions/language.ts index 3a620fd..473532b 100644 --- a/apps/web/src/shared/extensions/language.ts +++ b/apps/web/src/shared/extensions/language.ts @@ -13,5 +13,5 @@ import type { LANGUAGE } from "utils/types" export function languageExtension(language: LANGUAGE): Extension { if (language === "SQL") return sql({ dialect: SQLite, upperCaseKeywords: true }) - return ["Python2", "Python3"].includes(language) ? python() : cpp() + return language === "Python" ? python() : cpp() } diff --git a/apps/web/src/utils/constants.ts b/apps/web/src/utils/constants.ts index 92880fe..bf7ec93 100644 --- a/apps/web/src/utils/constants.ts +++ b/apps/web/src/utils/constants.ts @@ -177,8 +177,7 @@ export const SOURCES = { C: cSource, "C++": cppSource, Java: javaSource, - Python3: pythonSource, - Python2: "", + Python: pythonSource, JavaScript: "", Golang: "", Flowchart: "", @@ -189,8 +188,7 @@ export const LANGUAGE_FORMAT_VALUE = { C: "c", "C++": "cpp", Java: "java", - Python2: "python", - Python3: "python", + Python: "python", JavaScript: "javascript", Golang: "go", Flowchart: "flowchart", @@ -202,8 +200,7 @@ export const LANGUAGE_SHOW_VALUE = { C: "C语言", "C++": "C++", Java: "Java", - Python2: "Python", - Python3: "Python", + Python: "Python", JavaScript: "JS", Golang: "Go", SQL: "SQL", @@ -211,8 +208,7 @@ export const LANGUAGE_SHOW_VALUE = { export const ICON_SET = { Flowchart: "vscode-icons:file-type-drawio", - Python2: "devicon:python", - Python3: "devicon:python", + Python: "devicon:python", C: "devicon:c", "C++": "devicon:cplusplus", Java: "devicon:java", @@ -250,8 +246,7 @@ const blankTemplate = `//PREPEND BEGIN export const CODE_TEMPLATES = { C: cTemplate, "C++": cppTemplate, - Python2: blankTemplate, - Python3: blankTemplate, + Python: blankTemplate, Java: blankTemplate, JavaScript: blankTemplate, Golang: blankTemplate, diff --git a/apps/web/src/utils/judge.ts b/apps/web/src/utils/judge.ts index 614ad85..a86e8c5 100644 --- a/apps/web/src/utils/judge.ts +++ b/apps/web/src/utils/judge.ts @@ -12,8 +12,7 @@ const JUDGE0_LANGUAGE_ID: Partial> = { Java: 62, Golang: 60, JavaScript: 63, - Python2: 70, - Python3: 71, + Python: 71, } export async function createTestSubmission(code: Code, input: string) { diff --git a/docker/compose.debian.yml b/docker/compose.debian.yml index 94ac3ad..4362e7d 100644 --- a/docker/compose.debian.yml +++ b/docker/compose.debian.yml @@ -72,7 +72,11 @@ services: retries: 10 oj-judge: - image: registry.cn-hongkong.aliyuncs.com/oj-image/judge:1.6.1 + # 自己构建的判题沙箱(上游 JudgeServer 1.6.1 + 新工具链,见 docker/judge/)。 + # 上游停更在 2024-04,registry 上的 1.6.1 == latest,没有新版可拉。 + # ⚠️ 这个 tag 不在任何 registry 上:本机 docker/judge/build.sh 构建, + # 服务器/机房用 docker load 装。镜像不在本地时 compose 会去 pull 然后报找不到。 + image: oj2-judge-2 container_name: oj-judge restart: always read_only: true diff --git a/docker/compose.dev.yml b/docker/compose.dev.yml index e4f42f2..b144420 100644 --- a/docker/compose.dev.yml +++ b/docker/compose.dev.yml @@ -57,7 +57,11 @@ services: retries: 10 judge: - image: registry.cn-hongkong.aliyuncs.com/oj-image/judge:1.6.1 + # 自己构建的判题沙箱(上游 JudgeServer 1.6.1 + 新工具链,见 docker/judge/)。 + # 上游停更在 2024-04,registry 上的 1.6.1 == latest,没有新版可拉。 + # ⚠️ 这个 tag 不在任何 registry 上:本机 docker/judge/build.sh 构建, + # 服务器/机房用 docker load 装。镜像不在本地时 compose 会去 pull 然后报找不到。 + image: oj2-judge-2 container_name: oj2-judge restart: unless-stopped read_only: true diff --git a/docker/compose.school.yml b/docker/compose.school.yml index 5f5e606..d1fbd3b 100644 --- a/docker/compose.school.yml +++ b/docker/compose.school.yml @@ -29,7 +29,11 @@ services: retries: 10 oj-judge: - image: registry.cn-hongkong.aliyuncs.com/oj-image/judge:1.6.1 + # 自己构建的判题沙箱(上游 JudgeServer 1.6.1 + 新工具链,见 docker/judge/)。 + # 上游停更在 2024-04,registry 上的 1.6.1 == latest,没有新版可拉。 + # ⚠️ 这个 tag 不在任何 registry 上:本机 docker/judge/build.sh 构建, + # 服务器/机房用 docker load 装。镜像不在本地时 compose 会去 pull 然后报找不到。 + image: oj2-judge-2 container_name: oj-judge restart: always read_only: true diff --git a/docker/deploy.sh b/docker/deploy.sh index 0de37c5..61ceb6e 100755 --- a/docker/deploy.sh +++ b/docker/deploy.sh @@ -152,7 +152,22 @@ judge_dir=$(grep -E '^JUDGE_STATE_DIR=' docker/.env | tail -1 | cut -d= -f2- || [ -n "$judge_dir" ] || die "JUDGE_STATE_DIR 没设 —— 试跑期间新旧两个判题机会共用运行目录" ok "判题机运行目录 $judge_dir" -# ④ 外接形态才需要预检:库不归本栈管,得确认它已经活着。 +# ④ 判题镜像是自建的(上游 JudgeServer 停更,官方镜像的编译器停在 gcc-13), +# registry 上没有这个 tag。忘了 docker load 的话,要到「起栈」那步 compose 去 pull +# 才失败 —— 不如在这里就把该跑的三条命令说清楚。 +# 回滚到官方镜像时这段自动跳过:那个 tag 是 pull 得到的。 +judge_image=$(grep -m1 -E '^[[:space:]]*image: oj2-judge' <<<"$cfg" | awk '{print $2}' || true) +if [ -n "$judge_image" ]; then + docker image inspect "$judge_image" >/dev/null 2>&1 \ + || die "判题镜像 $judge_image 不在这台机器上,而且 registry 上也没有(它是自建的)。 + 本机:docker/judge/build.sh --save + scp dist/${judge_image/:/-}.tar root@这台机器:/root/OJDeploy/ + 这里:docker load -i /root/OJDeploy/${judge_image/:/-}.tar +构建和回滚见 docker/judge/README.md。" + ok "判题镜像 $judge_image 在本机" +fi + +# ⑤ 外接形态才需要预检:库不归本栈管,得确认它已经活着。 # 自带形态下这两个容器就是本栈自己起的,起栈那步会拉起来,这里没什么可查。 if [ "$LOCAL_DATA" -eq 0 ]; then for c in oj-postgres oj-redis; do diff --git a/docker/judge/Dockerfile b/docker/judge/Dockerfile new file mode 100644 index 0000000..87ea8d1 --- /dev/null +++ b/docker/judge/Dockerfile @@ -0,0 +1,148 @@ +# 判题沙箱镜像。**这是 QingdaoU/JudgeServer 官方 Dockerfile 的分叉**, +# 只改工具链版本,server/ 和 Judger/ 的代码一行都没动(构建时从上游仓库 +# 的固定 commit 拉,见 build.sh)。 +# +# 为什么要分叉:上游停更在 2024-04-05(b28aa56,也就是 1.6.1 这个 tag), +# registry 上的 `latest` 和 `1.6.1` 是同一份镜像,没有新版可升。想要新编译器 +# 只能自己构建。 +# +# 相对上游的全部改动: +# gcc/g++ 13 → 14 (trixie 默认) +# Python 3.12 → 3.13 (trixie 默认) +# Go / JDK / Node → **整套删掉**(见下) +# +# 只留 C / C++ / Python3 的工具链:前端的语言复选框从来只给这三种加 SQL, +# 生产库 12 万条提交里 Java/Golang/JavaScript 一共 62 条、全是很早以前的。 +# 删掉 golang-1.24-go、temurin-25-jdk、nodejs 之后镜像从 1.16GB 掉到 ~500MB, +# 构建也少了 NodeSource 那个 39MB 的 deb(它没有可用的国内镜像,最慢的一块)。 +# 想恢复某种语言:这里加回包 + alternatives,同时改 apps/api/src/judge/languages.ts。 +# +# ⚠️ 语言的编译/运行命令**不在这个文件里**,在 apps/api/src/judge/languages.ts。 +# 升 gcc 大版本要同步看那边的开关(gcc-14 把 implicit-function-declaration +# 等提成了 error,languages.ts 里有三个 -Wno-error 把它压回去)。 + +# 镜像源。默认走国内镜像 —— 官方源在这边实测 197 KB/s,清华 3.9 MB/s, +# 整个构建从 12 分钟掉到 2 分钟出头。出国内网络环境用 build.sh --no-mirror 关掉。 +# +# ⚠️ debian 这两个只能用 **http**:改 sources 这一步发生在装 ca-certificates 之前, +# base 镜像里没有 CA 根证书,https 一律 `certificate verify failed`。 +# PyPI 那条是 pip 自己请求的,pip 内置 certifi,不依赖系统 CA。 +ARG APT_MIRROR=http://mirrors.tuna.tsinghua.edu.cn/debian +ARG APT_SECURITY_MIRROR=http://mirrors.tuna.tsinghua.edu.cn/debian-security +ARG PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple + +FROM debian:trixie-slim AS builder +ARG TARGETARCH +ARG TARGETVARIANT +ARG APT_MIRROR +ARG APT_SECURITY_MIRROR +ARG PIP_INDEX_URL + +ENV DEBIAN_FRONTEND=noninteractive +WORKDIR /app + +RUN --mount=type=cache,target=/var/cache/apt,id=apt-cahce-1-$TARGETARCH$TARGETVARIANT-builder,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,id=apt-cahce-2-$TARGETARCH$TARGETVARIANT-builder,sharing=locked \ + < /etc/apt/apt.conf.d/keep-cache +echo 'APT::Install-Recommends "0";' > /etc/apt/apt.conf.d/no-recommends +echo 'APT::AutoRemove::RecommendsImportant "0";' >> /etc/apt/apt.conf.d/no-recommends +# 换镜像源。debian-security 必须先换:它的 URI 以 debian 的 URI 为前缀, +# 反过来的话第二条 sed 会把它改成 <镜像>/debian-security,路径不存在。 +sed -i "s|http://deb.debian.org/debian-security|$APT_SECURITY_MIRROR|; s|http://deb.debian.org/debian|$APT_MIRROR|" /etc/apt/sources.list.d/debian.sources +apt-get update +apt-get install -y libtool make cmake libseccomp-dev gcc python3 python3-venv +EOS + +COPY Judger/ /app/ +RUN < /etc/apt/apt.conf.d/keep-cache +echo 'APT::Install-Recommends "0";' > /etc/apt/apt.conf.d/no-recommends +echo 'APT::AutoRemove::RecommendsImportant "0";' >> /etc/apt/apt.conf.d/no-recommends +needed="python3.13-minimal \ + python3.13-venv \ + libpython3.13-stdlib \ + libpython3.13-dev \ + gcc-14 \ + g++-14 \ + strace" +savedAptMark="$(apt-mark showmanual) $needed" +# 换镜像源。debian-security 必须先换:它的 URI 以 debian 的 URI 为前缀, +# 反过来的话第二条 sed 会把它改成 <镜像>/debian-security,路径不存在。 +sed -i "s|http://deb.debian.org/debian-security|$APT_SECURITY_MIRROR|; s|http://deb.debian.org/debian|$APT_MIRROR|" /etc/apt/sources.list.d/debian.sources +apt-get update +apt-get install -y $needed +# languages.ts 里的编译/运行命令写的是绝对路径(/usr/bin/gcc、/usr/bin/python3 …), +# 全靠下面这几条 alternatives 挂出来。换包名必须同步改,漏一条的表现是 CE 而不是报错。 +update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 14 +update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 14 +update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.13 13 +apt-mark auto '.*' > /dev/null +apt-mark manual $savedAptMark +apt-get purge -y --auto-remove +EOS + +COPY --from=builder --chmod=755 --link /app/output/libjudger.so /usr/lib/judger/libjudger.so +COPY --from=builder /app/bindings/Python/dist/ /app/ +RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cahce-$TARGETARCH$TARGETVARIANT-final \ + <` 之类的隐式函数声明。 +教程那 93 块本身写得规范,加不加都全过。 + +## 换镜像后怎么验 + +```bash +bun docker/judge/smoke.ts # 六种语言 + 六种状态码 + gcc 宽松度 +``` + +它直接打判题机的 `/judge`,不需要起后端、不需要库里有题。用的 `languageConfigs` +就是线上那份,所以配置和镜像对不上会当场暴露。 + +**Go 那条坑记一下**(升级时发现的,升级之前就有):`GOCACHE` 指向容器的 tmpfs +`/tmp`,判题机重启后第一次 Go 提交是冷构建,Go 1.22 要 5.6 秒 CPU、超过 3 秒的编译 +预算 —— 重启后第一个交 Go 的学生必吃一次 CE,后面的人缓存热了又都正常。Go 现在整个 +删掉了,但**以后加回任何需要编译缓存的语言,记得把编译预算放宽**。 + +判题机装好之后,后台「判题机列表」应该能看到它上线(心跳走 +`POST /api/judge-server/heartbeat`,5 秒一次)。 diff --git a/docker/judge/build.sh b/docker/judge/build.sh new file mode 100755 index 0000000..cf8156b --- /dev/null +++ b/docker/judge/build.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# +# 构建判题沙箱镜像(本机构建,产物用 docker save 传到服务器和机房)。 +# +# docker/judge/build.sh # 构建并打 tag +# docker/judge/build.sh --save # 顺便导出 tar(给 scp 用) +# docker/judge/build.sh --no-cache # 不吃构建缓存 +# docker/judge/build.sh --no-mirror # 不走国内镜像源(默认走) +# +# 上游 JudgeServer 停更在 2024-04-05,registry 上的 1.6.1 == latest,没有新版 +# 可拉。这个脚本从上游那个固定 commit 拉源码(server/ 和 Judger/ 一行不改), +# 只把 Dockerfile 换成 docker/judge/Dockerfile —— 新工具链的全部改动都在那里。 +# +# 上传和切换见 docker/judge/README.md。 + +[ -n "${BASH_VERSION:-}" ] || exec bash "$0" "$@" +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/../.." + +# 上游 master HEAD(= tag v1.6.1+judgeserver.1.6.1)。Judger 子模块的版本由这个 +# commit 自己钉住(d19a6dc),不用在这里再写一遍。 +UPSTREAM_REPO=https://github.com/QingdaoU/JudgeServer.git +UPSTREAM_COMMIT=b28aa56d60fed7358a29d9bdeb9d86fcc06e41a7 + +# 我们自己重编的第几版判题镜像(官方那个算第 1 版,这是第 2 版)。 +# **改工具链就把末尾的序号 +1**(下一版叫 oj2-judge-3),别在同一个名字上重建 —— +# compose 的 `up -d` 不带 --pull,名字没变就会静默用机器上的旧镜像。 +IMAGE=oj2-judge-2 + +SAVE=0 +BUILD_ARGS=() +for arg in "$@"; do + case "$arg" in + --save) SAVE=1 ;; + --no-cache) BUILD_ARGS+=(--no-cache) ;; + # 默认用国内镜像源(Dockerfile 顶部的四个 ARG)。在能直连的网络里用这个关掉, + # 换回 deb.debian.org / pypi.org。 + --no-mirror) + BUILD_ARGS+=( + --build-arg APT_MIRROR=http://deb.debian.org/debian + --build-arg APT_SECURITY_MIRROR=http://deb.debian.org/debian-security + --build-arg PIP_INDEX_URL=https://pypi.org/simple + ) ;; + *) echo "未知参数:$arg(可用:--save、--no-cache、--no-mirror)" >&2; exit 2 ;; + esac +done + +say() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; } +ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; } +die() { printf '\n\033[1;31m❌ %s\033[0m\n\n' "$*" >&2; exit 1; } + +command -v docker >/dev/null || die "没装 docker" +[ -f docker/judge/Dockerfile ] || die "docker/judge/Dockerfile 不见了,当前目录:$PWD" + +src=$(mktemp -d) +trap 'rm -rf "$src"' EXIT + +say "拉上游源码 $UPSTREAM_COMMIT" +git -c advice.detachedHead=false clone --quiet "$UPSTREAM_REPO" "$src" +git -C "$src" -c advice.detachedHead=false checkout --quiet "$UPSTREAM_COMMIT" +git -C "$src" submodule update --quiet --init --recursive +ok "server/ 和 Judger/ 就位" + +say "构建 $IMAGE" +# 上下文是上游源码,Dockerfile 用我们自己的那份。 +docker build "${BUILD_ARGS[@]}" -f docker/judge/Dockerfile -t "$IMAGE" "$src" +ok "$IMAGE" + +say "镜像里的工具链" +docker run --rm --entrypoint sh "$IMAGE" -c ' + printf "gcc %s\n" "$(gcc -dumpfullversion)" + printf "g++ %s\n" "$(g++ -dumpfullversion)" + printf "python3 %s\n" "$(python3 -V | cut -d" " -f2)" +' + +if [ "$SAVE" = 1 ]; then + out="dist/${IMAGE/:/-}.tar" + mkdir -p dist + say "导出 $out" + docker save "$IMAGE" -o "$out" + ok "$(du -h "$out" | cut -f1) → scp 到服务器后 docker load -i" +fi diff --git a/docker/judge/smoke.ts b/docker/judge/smoke.ts new file mode 100644 index 0000000..769cbc7 --- /dev/null +++ b/docker/judge/smoke.ts @@ -0,0 +1,266 @@ +/** + * 判题沙箱冒烟测试 —— **换镜像之后跑这个**(项目不写测试,验证一律实跑)。 + * + * bun docker/judge/smoke.ts # 打 .env 里的 JUDGE_SERVER_URL + * JUDGE_SERVER_URL=http://localhost:8082 bun docker/judge/smoke.ts + * + * 直接打判题机的 /judge,不经过 api / worker / 队列,所以不需要起后端,也不需要 + * 库里有题 —— 测试点用 `test_case` 内联传进去,判题机会当场写进临时目录。 + * + * 它核的是三件事: + * 1. 三种语言都能编译、运行、判对(languageConfigs 就是线上那份,不是另抄的) + * 2. 六种结果状态的**整数值**没变(落库的值,见 packages/contract/src/judge-status.ts) + * 3. gcc 的宽松度没变 —— 忘了 #include 的老代码照样能过(gcc-14 默认会把它判 CE) + */ +import { createHash } from "node:crypto" + +import { JudgeStatus } from "@oj2/contract" + +import { languageConfigs } from "../../apps/api/src/judge/languages" + +const url = process.env.JUDGE_SERVER_URL ?? "http://localhost:8081" +const rawToken = process.env.JUDGE_SERVER_TOKEN +if (!rawToken) { + console.error("JUDGE_SERVER_TOKEN 没设 —— 在 OJ2 根目录跑,bun 会自己读 .env") + process.exit(2) +} +const token = createHash("sha256").update(rawToken).digest("hex") + +interface Case { + language: string + name: string + code: string + expect: number + /** 默认 3000ms / 128MB,跑得慢或要撑爆内存的用例自己改 */ + cpu?: number + memory?: number +} + +const sumTestCase = [{ input: "1 2\n", output: "3\n" }] + +const cases: Case[] = [ + // ---------------------------------------------------------------- C + { + language: "C", + name: "C 正常通过", + expect: JudgeStatus.ACCEPTED, + code: `#include +int main(void) { + int a, b; + scanf("%d %d", &a, &b); + printf("%d\\n", a + b); + return 0; +}`, + }, + { + // 这条是升 gcc 的主要风险点:gcc-14 起 implicit-function-declaration 是 + // error,languages.ts 里的三个 -Wno-error 就是为它加的。这条挂了说明那些 + // 开关没生效 —— 后果是一批历史题解和 20 篇 C 教程的示例突然全 CE。 + language: "C", + name: "C 忘了 #include 仍能过(gcc 宽松度)", + expect: JudgeStatus.ACCEPTED, + code: `int main(void) { + int a, b; + scanf("%d %d", &a, &b); + printf("%d\\n", a + b); + return 0; +}`, + }, + { + language: "C", + name: "C 答案错误", + expect: JudgeStatus.WRONG_ANSWER, + code: `#include +int main(void) { + int a, b; + scanf("%d %d", &a, &b); + printf("%d\\n", a + b + 1); + return 0; +}`, + }, + { + language: "C", + name: "C 编译错误", + expect: JudgeStatus.COMPILE_ERROR, + code: `int main(void) { return }`, + }, + { + language: "C", + name: "C 运行超时", + expect: JudgeStatus.CPU_TIME_LIMIT_EXCEEDED, + cpu: 1000, + code: `int main(void) { + volatile long x = 0; + while (1) x++; + return 0; +}`, + }, + { + // RLIMIT_AS 是 max_memory 的两倍,所以 malloc 会先成功一阵子再失败, + // 退出时 ru_maxrss 已经超过 max_memory → judger 判 MLE 而不是 RE。 + language: "C", + name: "C 内存超限", + expect: JudgeStatus.MEMORY_LIMIT_EXCEEDED, + memory: 64 * 1024 * 1024, + code: `#include +#include +int main(void) { + for (;;) { + char *p = malloc(8 * 1024 * 1024); + if (!p) return 1; + memset(p, 1, 8 * 1024 * 1024); + } +}`, + }, + { + language: "C", + name: "C 运行时错误", + expect: JudgeStatus.RUNTIME_ERROR, + code: `int main(void) { + int *p = 0; + *p = 1; + return 0; +}`, + }, + // ---------------------------------------------------------------- Python + { + language: "Python", + name: "Python 正常通过", + expect: JudgeStatus.ACCEPTED, + code: `a, b = map(int, input().split()) +print(a + b)`, + }, + { + language: "Python", + name: "Python 编译错误", + expect: JudgeStatus.COMPILE_ERROR, + code: `def (:`, + }, + { + language: "Python", + name: "Python 运行超时", + expect: JudgeStatus.CPU_TIME_LIMIT_EXCEEDED, + cpu: 1000, + code: `while True: + pass`, + }, + { + language: "Python", + name: "Python 运行时错误", + expect: JudgeStatus.RUNTIME_ERROR, + code: `print(1 / 0)`, + }, + // ---------------------------------------------------------------- C++ + { + language: "C++", + name: "C++ 正常通过", + expect: JudgeStatus.ACCEPTED, + code: `#include +int main() { + int a, b; + std::cin >> a >> b; + std::cout << a + b << std::endl; + return 0; +}`, + }, + { + language: "C++", + name: "C++ 编译错误", + expect: JudgeStatus.COMPILE_ERROR, + code: `int main() { return }`, + }, +] + +const names: Record = { + [JudgeStatus.COMPILE_ERROR]: "CE", + [JudgeStatus.WRONG_ANSWER]: "WA", + [JudgeStatus.ACCEPTED]: "AC", + [JudgeStatus.CPU_TIME_LIMIT_EXCEEDED]: "TLE(cpu)", + [JudgeStatus.REAL_TIME_LIMIT_EXCEEDED]: "TLE(real)", + [JudgeStatus.MEMORY_LIMIT_EXCEEDED]: "MLE", + [JudgeStatus.RUNTIME_ERROR]: "RE", + [JudgeStatus.SYSTEM_ERROR]: "SE", +} +const label = (code: number) => `${names[code] ?? "?"}(${code})` + +async function runCase(item: Case) { + const response = await fetch(new URL("/judge", url), { + method: "POST", + headers: { + "content-type": "application/json", + "X-Judge-Server-Token": token, + }, + body: JSON.stringify({ + language_config: languageConfigs[item.language], + src: item.code, + max_cpu_time: item.cpu ?? 3000, + max_memory: item.memory ?? 128 * 1024 * 1024, + test_case: sumTestCase, + output: false, + io_mode: { + io_mode: "Standard IO", + input: "input.txt", + output: "output.txt", + }, + }), + }) + if (!response.ok) throw new Error(`HTTP ${response.status}`) + + const body = (await response.json()) as { + err: string | null + data: unknown + } + // 编译失败走 err 通道,不会有逐测试点的结果 + if (body.err === "CompileError") return { result: JudgeStatus.COMPILE_ERROR } + if (body.err) throw new Error(`${body.err}: ${JSON.stringify(body.data)}`) + + const results = body.data as { result: number; cpu_time: number }[] + // 多个测试点取最坏的那个,和 run.ts 的口径一致 + const failed = results.find((r) => r.result !== JudgeStatus.ACCEPTED) + return failed ?? results[0]! +} + +/** 判题机刚重建时 gunicorn 还没起来,先等它 —— 否则整屏都是连接被关。 */ +async function waitReady() { + for (let i = 0; i < 60; i++) { + try { + const response = await fetch(new URL("/ping", url), { + method: "POST", + headers: { "X-Judge-Server-Token": token }, + }) + if (response.ok) return + } catch { + // 还没起来,接着等 + } + await Bun.sleep(500) + } + console.error(`连不上判题机 ${url}(等了 30 秒)`) + process.exit(2) +} + +await waitReady() + +let failures = 0 +console.log(`判题机 ${url}\n`) +for (const item of cases) { + try { + const got = await runCase(item) + const pass = got.result === item.expect + if (!pass) failures++ + const time = "cpu_time" in got ? ` ${got.cpu_time}ms` : "" + console.log( + `${pass ? "\x1b[32m✓\x1b[0m" : "\x1b[31m✗\x1b[0m"} ${item.name.padEnd(32)}` + + ` 期望 ${label(item.expect).padEnd(10)} 实得 ${label(got.result)}${time}`, + ) + } catch (error) { + failures++ + console.log(`\x1b[31m✗\x1b[0m ${item.name.padEnd(32)} ${error}`) + } +} + +console.log( + failures === 0 + ? `\n\x1b[32m全部 ${cases.length} 条通过\x1b[0m` + : `\n\x1b[31m${failures} / ${cases.length} 条不对\x1b[0m`, +) +process.exit(failures === 0 ? 0 : 1) diff --git a/docs/ast-rules.md b/docs/ast-rules.md index 05fca65..2803835 100644 --- a/docs/ast-rules.md +++ b/docs/ast-rules.md @@ -27,7 +27,7 @@ bun run --filter '@oj2/api' check:ast # 每个 target 的 node 在语法里 ## 只有三种语言真的会跑 -判题机只认 `AST_SUPPORTED_LANGUAGES`(C / C++ / Python3)。别的语言配了规则一条都不会跑, +判题机只认 `AST_SUPPORTED_LANGUAGES`(C / C++ / Python)。别的语言配了规则一条都不会跑, 所以后台不给它们开 tab,题目页也不把它们的规则展示成「要求」—— **看得见却不检查**比没有更糟。 diff --git a/docs/deploy.md b/docs/deploy.md index 303e001..b59012f 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -64,6 +64,23 @@ docker/deploy.sh --no-build # 只改了 env / compose 时跳过构建 有没有生效、库指向和形态是否自洽、判题机运行目录有没有和旧栈分开、外接的 postgres / redis 是否活着。起完再跑四条冒烟,**题目数是 0 也中止** —— 那意味着连错库了。 +### 判题镜像是自建的,不在 registry 上 + +`compose.*.yml` 里的 `oj2-judge-2` 是本机构建的(上游 JudgeServer 停更在 +2024-04,官方镜像的编译器停在 gcc-13)。**新机器或换镜像之后,先把镜像 load 进去 +再部署**: + +```bash +# 本机 +docker/judge/build.sh --save +scp dist/oj2-judge-2.tar root@服务器:/root/OJDeploy/ +# 服务器 / 机房各来一次(两个站点各有各的判题沙箱) +docker load -i /root/OJDeploy/oj2-judge-2.tar +``` + +忘了这一步,`deploy.sh` 起栈时会去 pull 一个不存在的镜像并失败(响亮地失败, +不会静默降级)。构建、回滚和工具链版本表见 `docker/judge/README.md`。 + ### 迁移在起栈之前跑 `deploy.sh` 在「构建镜像」之后、「起栈」之前跑 `oj2-api migrate`,失败就中止部署 diff --git a/package.json b/package.json index 8ea2303..bbbe54a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "db:generate": "bun run --filter '@oj2/api' db:generate", "db:migrate": "bun run --filter '@oj2/api' db:migrate", "db:down": "docker compose -f docker/compose.dev.yml down", - "fmt": "prettier --write apps/api/src apps/web/src apps/web/tests packages/contract/src apps/api/drizzle.config.ts apps/web/vite.config.ts" + "fmt": "prettier --write apps/api/src apps/web/src apps/web/tests packages/contract/src apps/api/drizzle.config.ts apps/web/vite.config.ts docker/judge/smoke.ts" }, "devDependencies": { "prettier": "^3.9.6", diff --git a/packages/contract/src/language.ts b/packages/contract/src/language.ts index a0df5a2..ea71504 100644 --- a/packages/contract/src/language.ts +++ b/packages/contract/src/language.ts @@ -8,14 +8,24 @@ import { z } from "zod" * 后端那边是 `Record`(判题机要按名字取配置,不能窄化成联合), * 所以这个联合是**手写**的,改 languages.ts 时两边一起改。 * - * 这 6 种是**现在还能提交**的语言。含历史值 `Python2`:判题机已经没有它的 - * 编译配置了,但生产库里有 3 条当年用 Python2 提交的记录,提交列表要能渲染出来。 + * **这里列的是历史上出现过的值,不等于现在能提交的语言。** 现在判题沙箱只认 + * `C` / `C++` / `Python`(`languages.ts` 里就这三块),前端的语言复选框也只给这三个 + * 加 SQL。`Java`(44 条) / `Golang`(15) / `JavaScript`(3) 是历史值:判题机没有它们的 + * 编译配置、镜像里也没有工具链了,但生产库里有这些提交记录,提交列表要能渲染出来, + * 所以键必须留着。 + * + * Java / JavaScript / Golang 是 2026-09 随判题镜像升级一起下掉的,理由见 + * `apps/api/src/judge/languages.ts` 顶部。 + * + * `Python2` / `Python3` 这两个旧值**已经不在这里了** —— 0019 迁移把库里的 + * 104530 条提交、937 道题、1235 个用户的成就指标全并成了 `Python`。万一还有旧值 + * 从别处冒出来(旧客户端的 localStorage、迁移之前排进队列的任务),走下面的 + * `normalizeLanguage()`,别直接 parse。 */ export const judgeLanguageSchema = z.enum([ - "Python2", - "Python3", "C", "C++", + "Python", "Java", "JavaScript", "Golang", @@ -40,3 +50,26 @@ export const problemLanguageSchema = z.enum([ export type JudgeLanguage = z.infer export type ProblemLanguage = z.infer + +/** + * 旧语言名 → 现在的值。**库里已经没有旧值了**(0019 迁移清干净并核对过), + * 这张表挡的是数据之外的三条路: + * + * 1. 学生浏览器 localStorage 里存着上次选的 `Python3`(上线那一刻还在那儿); + * 2. 迁移之前就排进 BullMQ 的判题任务; + * 3. 万一要回滚 —— 旧版后端读 `Python` 会查不到配置,所以**回滚必须连数据一起回**, + * 别只滚代码。 + */ +const LANGUAGE_ALIASES: Record = { + Python2: "Python", + Python3: "Python", +} + +/** 把可能是旧值的语言名归一化;认不出来返回 null,由调用方决定怎么兜底。 */ +export function normalizeLanguage(value: unknown): ProblemLanguage | null { + if (typeof value !== "string") return null + const parsed = problemLanguageSchema.safeParse( + LANGUAGE_ALIASES[value] ?? value, + ) + return parsed.success ? parsed.data : null +} diff --git a/packages/contract/src/problem.ts b/packages/contract/src/problem.ts index 7051643..ad4efb2 100644 --- a/packages/contract/src/problem.ts +++ b/packages/contract/src/problem.ts @@ -106,7 +106,7 @@ export const astRuleSchema = z.object({ max: z.number().int().optional(), }) -/** 按语言分组:`{ Python3: [...], C: [...] }`,键是 languages 里的语言名 */ +/** 按语言分组:`{ Python: [...], C: [...] }`,键是 languages 里的语言名 */ export const astRulesSchema = z.record(z.string(), z.array(astRuleSchema)) /** @@ -183,7 +183,7 @@ export const AST_NODE_TARGETS_BY_LANGUAGE: Record< lambda: { label: "lambda 表达式", node: "lambda_expression" }, using: { label: "using 声明", node: "using_declaration" }, }, - Python3: { + Python: { for_loop: { label: "for 循环", node: "for_statement" }, while_loop: { label: "while 循环", node: "while_statement" }, if_statement: { label: "if 条件", node: "if_statement" }, @@ -260,7 +260,7 @@ export const AST_OPERATOR_TARGETS_BY_LANGUAGE: Record< C: C_OPERATOR_TARGETS, // `<<` / `>>` 对 C++ 主要是 cout/cin 的流运算符(位移是同一个 token) "C++": { ...C_OPERATOR_TARGETS, "<<": "<<", ">>": ">>" }, - Python3: { + Python: { "+": "+", "-": "-", "*": "*",