From bb4b5825c3255c2d61cc99aecf3374eafd33424c Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Wed, 26 Aug 2026 09:54:31 -0600 Subject: [PATCH] =?UTF-8?q?feat(=E8=BF=81=E7=A7=BB):=200000=20=E6=94=B9?= =?UTF-8?q?=E6=88=90=E5=8F=AF=E6=89=A7=E8=A1=8C=EF=BC=8C=E7=A9=BA=E5=BA=93?= =?UTF-8?q?=E8=83=BD=E8=87=AA=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0000_crazy_gateway.sql 原本是 drizzle-kit pull 的产物,整份被 /* */ 包着、 可执行语句 0 条,所以任何新库的结构都只能先手工 psql 灌一遍 schema.sql 再打基线。 现在它的内容由 docs/specs/schema.sql(2026-08-07 的生产 pg_dump --schema-only) 机械转换而来:去掉 psql 专有指令 12 条、去掉 7 张 Django 遗留表及其索引外键 41 条, 保留 212 条语句、顺序不动。转换脚本入库在 docs/spikes/。 不含 Django 那 7 张表,是因为 meta/0000_snapshot.json 从来就没有它们(pull 当时 tablesFilter 滤掉了),不建它们才和快照一致;0002 那串 DROP ... IF EXISTS 在新库上 空转、在生产库上真删,两边跑同一串迁移落点相同。 改 0000 对生产库没有影响:migrator 只比 created_at、从不校验 hash,而生产库那行 baseline-0000-faked 早把它挡在门外了。 migrate.ts 配套:空库直接从 0000 建起(并自建 drizzle 记账表——原来这步由 drizzle 的 migrate() 顺手做掉);自举时不触发破坏性闸门,因为空库上没有数据可丢,拦下来只会逼 每个新环境都带一次 OJ2_ALLOW_DESTRUCTIVE,把这道闸训练成习惯动作。「有表但没基线」 仍然 exit 3。 验证:空库自举出来的结构,和「灌 schema.sql + 打基线 + 跑迁移」这条老路子跑出来的 结构,pg_dump --schema-only 逐字节一致(734 行,零差异)。 转换时踩到两个坑,都写进注释了:注释里不能出现 statement-breakpoint 的字面量 (readMigrationFiles 纯文本切分,会把注释从中间切开);过滤 Django 对象要看 public.X 形式的对象引用,不能扫语句字样——user 表有一列叫 auth_token。 Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 36 +- apps/api/src/db/0000_crazy_gateway.sql | 1311 ++++++++++++++++------- apps/api/src/db/migrate.ts | 44 +- docs/spikes/build-baseline-migration.ts | 105 ++ 4 files changed, 1074 insertions(+), 422 deletions(-) create mode 100644 docs/spikes/build-baseline-migration.ts diff --git a/CLAUDE.md b/CLAUDE.md index 6eb3092..beead18 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -143,16 +143,38 @@ OJ2_ALLOW_DESTRUCTIVE=1 docker/deploy.sh ``` `DROP INDEX` / `DROP CONSTRAINT` 不算——它们不掉数据,拦了只会让人习惯性带上放行开关。 +**空库自举时这道闸不生效**:没有数据可丢,0002 那串 `DROP ... IF EXISTS` 全是空转, +拦下来只会逼每个新环境都带一次放行开关,把它训练成习惯动作。 -**0000 跑不了,库不能靠迁移自举。** `0000_crazy_gateway.sql` 是 `drizzle-kit pull` -的产物,整份被 `/* */` 包着,可执行语句 0 条。所以任何新库的结构都只能来自 -`docs/specs/schema.sql` 或生产 dump,然后手工做基线。`oj2-api migrate` 会检测这两种 -情况并打印具体该做什么,不会让你撞上 drizzle 那个语焉不详的报错。 +**空库能自举了。** `oj2-api migrate` 指向一个空库时直接从 `0000` 建起: + +```bash +DATABASE_URL=postgres://... oj2-api migrate +# 空库,从 0000 开始自举。 +# 待执行 3 条迁移,开始。 +# ✓ 0000_crazy_gateway +# ✓ 0001_add_submission_public_create_time_idx +# ✓ 0002_drop_django_leftovers +``` + +`0000_crazy_gateway.sql` 原本是 `drizzle-kit pull` 的产物、整份被 `/* */` 包着、可执行 +语句 0 条,所以以前新库只能先手工 `psql -f docs/specs/schema.sql`。现在它的内容由那份 +生产 dump 机械转换而来(去掉 psql 专有指令、去掉 7 张 Django 遗留表及其索引外键, +其余原样保留)。**实测**:空库自举出来的结构,和「灌 schema.sql + 打基线 + 跑迁移」 +这条老路子跑出来的结构,`pg_dump --schema-only` 逐字节一致(734 行,零差异)。 + +改 0000 对生产库没有影响 —— migrator 只比 `created_at`、**从不校验 hash** +(`pg-core/dialect.js` 里就一句 `Number(lastDbMigration.created_at) < migration.folderMillis`), +而生产库那行 `baseline-0000-faked` 早把它挡在门外了。 + +⚠️ **0000 的注释里不要出现 statement-breakpoint 那个分隔标记的字面量。** +`readMigrationFiles` 是纯文本切分,不管它在不在注释里,照切不误 —— 注释被从中间切开, +后半截当成 SQL 发出去,报的是 `syntax error at or near "。"` 这种和真实原因毫不相干的错。 **给一个已经存在的库做基线**:drizzle 没有 `--fake-initial`,`migrate` 见到空的 -`__drizzle_migrations` 会从 `0000` 的完整建表跑起,撞上已存在的表就整个事务回滚 —— -**而且失败时 exit 1 但一个错误都不打印**(只有 NOTICE,实测过)。所以对已有数据的库 -第一次跑之前,先手插一行把 `0000` 标记成已执行: +`__drizzle_migrations`、库里却已经有表,会拒绝执行并 exit 3(裸跑 `drizzle-kit migrate` +的话则是从 `0000` 撞上已存在的表、整个事务回滚,**而且 exit 1 却一个错误都不打印**)。 +对已有数据的库第一次跑之前,先手插一行把 `0000` 标记成已执行: ```sql CREATE SCHEMA IF NOT EXISTS drizzle; diff --git a/apps/api/src/db/0000_crazy_gateway.sql b/apps/api/src/db/0000_crazy_gateway.sql index 6dc477d..9b6ffba 100644 --- a/apps/api/src/db/0000_crazy_gateway.sql +++ b/apps/api/src/db/0000_crazy_gateway.sql @@ -1,453 +1,966 @@ --- Current sql file was generated after introspecting the database --- If you want to run this migration please uncomment this code before executing migrations -/* -CREATE SEQUENCE "public"."auth_group_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1;--> statement-breakpoint -CREATE SEQUENCE "public"."auth_group_permissions_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1;--> statement-breakpoint -CREATE SEQUENCE "public"."auth_permission_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1;--> statement-breakpoint -CREATE SEQUENCE "public"."django_content_type_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1;--> statement-breakpoint -CREATE SEQUENCE "public"."django_migrations_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1;--> statement-breakpoint -CREATE TABLE "ai_analysis" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "ai_analysis_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "provider" text NOT NULL, - "data" jsonb NOT NULL, - "system_prompt" text NOT NULL, - "user_prompt" text NOT NULL, - "analysis" text NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "user_id" integer NOT NULL, - "model" text NOT NULL, - "is_pinned" boolean NOT NULL +-- OJ2 的基线迁移:把一个空库建成新后端要的结构。 +-- +-- 这份文件**不是** `drizzle-kit generate` 的产物,也不该由它重新生成。原本这里是 +-- `drizzle-kit pull` 吐出来的东西,整份被 /* */ 包着、一条可执行语句都没有,于是 +-- 「空库没法靠迁移自举」——新环境、演练、别人接手,都得先手工 psql 灌一遍 schema.sql。 +-- +-- 现在的内容由 `docs/specs/schema.sql`(生产库 2026-08-07 的 pg_dump --schema-only) +-- 机械转换而来:去掉 psql 专有指令(\restrict / SET / set_config)、去掉 7 张 Django +-- 遗留表及其索引与外键,其余原样保留、顺序不动,语句之间插上 drizzle 的分隔标记。 +-- 转换脚本:`docs/spikes/build-baseline-migration.ts`。 +-- +-- 注意:那个分隔标记是纯文本切分,`readMigrationFiles` 不管它出现在哪里 —— 写进注释里 +-- 一样会把文件切开。所以本文件的注释里不要出现它的字面量(我踩过一次,报错是 +-- 「syntax error at or near "。"」,因为注释被从中间切断了)。 +-- +-- 为什么不含 Django 那 7 张表:`meta/0000_snapshot.json` 从来就没有它们(pull 当时用 +-- tablesFilter 滤掉了),所以不建它们才和快照一致。0002 那条 DROP 全带 IF EXISTS, +-- 在新库上是空转,在生产库上才真删——两边跑同一串迁移,落点相同。 +-- +-- **改 schema 不要动这个文件**,走 `bun run db:generate` 生成新的迁移。 +-- 生产库早已把 0000 标记成已执行(migrator 只比 created_at、不校验 hash), +-- 所以这份内容的任何改动都不会在生产库上重放。 + +CREATE TABLE public.achievement ( + id bigint NOT NULL, + name text NOT NULL, + description text NOT NULL, + icon text NOT NULL, + rarity text NOT NULL, + hidden boolean DEFAULT false NOT NULL, + metric text NOT NULL, + operator text NOT NULL, + threshold integer NOT NULL, + visible boolean DEFAULT true NOT NULL, + unlock_count integer DEFAULT 0 NOT NULL, + "order" integer DEFAULT 0 NOT NULL, + create_time timestamp with time zone NOT NULL ); --> statement-breakpoint -CREATE TABLE "announcement" ( - "id" serial PRIMARY KEY NOT NULL, - "title" text NOT NULL, - "content" text NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "last_update_time" timestamp with time zone NOT NULL, - "visible" boolean NOT NULL, - "created_by_id" integer NOT NULL, - "tag" text NOT NULL, - "top" boolean NOT NULL +ALTER TABLE public.achievement ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.achievement_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "achievement" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "achievement_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "name" text NOT NULL, - "description" text NOT NULL, - "icon" text NOT NULL, - "rarity" text NOT NULL, - "hidden" boolean DEFAULT false NOT NULL, - "metric" text NOT NULL, - "operator" text NOT NULL, - "threshold" integer NOT NULL, - "visible" boolean DEFAULT true NOT NULL, - "unlock_count" integer DEFAULT 0 NOT NULL, - "order" integer DEFAULT 0 NOT NULL, - "create_time" timestamp with time zone NOT NULL +CREATE TABLE public.acm_contest_rank ( + id integer NOT NULL, + submission_number integer DEFAULT 0 NOT NULL, + accepted_number integer DEFAULT 0 NOT NULL, + total_time integer DEFAULT 0 NOT NULL, + submission_info jsonb DEFAULT '{}'::jsonb NOT NULL, + contest_id integer NOT NULL, + user_id integer NOT NULL ); --> statement-breakpoint -CREATE TABLE "contest" ( - "id" serial PRIMARY KEY NOT NULL, - "title" text NOT NULL, - "description" text NOT NULL, - "password" text, - "start_time" timestamp with time zone NOT NULL, - "end_time" timestamp with time zone NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "last_update_time" timestamp with time zone NOT NULL, - "visible" boolean NOT NULL, - "created_by_id" integer NOT NULL, - "allowed_ip_ranges" jsonb NOT NULL, - "tag" text NOT NULL +CREATE SEQUENCE public.acm_contest_rank_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.acm_contest_rank_id_seq OWNED BY public.acm_contest_rank.id; +--> statement-breakpoint +CREATE TABLE public.ai_analysis ( + id bigint NOT NULL, + provider text NOT NULL, + data jsonb NOT NULL, + system_prompt text NOT NULL, + user_prompt text NOT NULL, + analysis text NOT NULL, + create_time timestamp with time zone NOT NULL, + user_id integer NOT NULL, + model text NOT NULL, + is_pinned boolean NOT NULL ); --> statement-breakpoint -CREATE TABLE "contest_announcement" ( - "id" serial PRIMARY KEY NOT NULL, - "title" text NOT NULL, - "content" text NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "contest_id" integer NOT NULL, - "created_by_id" integer NOT NULL, - "visible" boolean NOT NULL +ALTER TABLE public.ai_analysis ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.ai_analysis_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "flowchart_submission" ( - "id" text PRIMARY KEY NOT NULL, - "mermaid_code" text NOT NULL, - "flowchart_data" jsonb NOT NULL, - "status" integer NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "ai_score" double precision, - "ai_grade" varchar(10), - "ai_feedback" text, - "ai_suggestions" text, - "ai_criteria_details" jsonb NOT NULL, - "ai_provider" varchar(50) NOT NULL, - "ai_model" varchar(50) NOT NULL, - "processing_time" double precision, - "evaluation_time" timestamp with time zone, - "problem_id" integer NOT NULL, - "user_id" integer NOT NULL +CREATE TABLE public.announcement ( + id integer NOT NULL, + title text NOT NULL, + content text NOT NULL, + create_time timestamp with time zone NOT NULL, + last_update_time timestamp with time zone NOT NULL, + visible boolean NOT NULL, + created_by_id integer NOT NULL, + tag text NOT NULL, + top boolean NOT NULL ); --> statement-breakpoint -CREATE TABLE "message" ( - "id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "message_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), - "message" text NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "recipient_id" integer NOT NULL, - "sender_id" integer NOT NULL, - "submission_id" text NOT NULL +CREATE SEQUENCE public.announcement_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.announcement_id_seq OWNED BY public.announcement.id; +--> statement-breakpoint +CREATE TABLE public.contest ( + id integer NOT NULL, + title text NOT NULL, + description text NOT NULL, + password text, + start_time timestamp with time zone NOT NULL, + end_time timestamp with time zone NOT NULL, + create_time timestamp with time zone NOT NULL, + last_update_time timestamp with time zone NOT NULL, + visible boolean NOT NULL, + created_by_id integer NOT NULL, + allowed_ip_ranges jsonb NOT NULL, + tag text NOT NULL ); --> statement-breakpoint -CREATE TABLE "judge_server" ( - "id" serial PRIMARY KEY NOT NULL, - "hostname" text NOT NULL, - "ip" text, - "judger_version" text NOT NULL, - "cpu_core" integer NOT NULL, - "memory_usage" double precision NOT NULL, - "cpu_usage" double precision NOT NULL, - "last_heartbeat" timestamp with time zone NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "task_number" integer NOT NULL, - "service_url" text, - "is_disabled" boolean NOT NULL +CREATE TABLE public.contest_announcement ( + id integer NOT NULL, + title text NOT NULL, + content text NOT NULL, + create_time timestamp with time zone NOT NULL, + contest_id integer NOT NULL, + created_by_id integer NOT NULL, + visible boolean NOT NULL ); --> statement-breakpoint -CREATE TABLE "exercise" ( - "id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "exercise_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), - "type" varchar(16) NOT NULL, - "data" jsonb NOT NULL, - "order" integer NOT NULL, - "created_at" timestamp with time zone NOT NULL, - "tutorial_id" integer NOT NULL +CREATE SEQUENCE public.contest_announcement_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.contest_announcement_id_seq OWNED BY public.contest_announcement.id; +--> statement-breakpoint +CREATE SEQUENCE public.contest_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.contest_id_seq OWNED BY public.contest.id; +--> statement-breakpoint +CREATE TABLE public.exercise ( + id integer NOT NULL, + type character varying(16) NOT NULL, + data jsonb NOT NULL, + "order" integer NOT NULL, + created_at timestamp with time zone NOT NULL, + tutorial_id integer NOT NULL ); --> statement-breakpoint -CREATE TABLE "options_sysoptions" ( - "id" serial PRIMARY KEY NOT NULL, - "key" text NOT NULL, - "value" jsonb NOT NULL, - CONSTRAINT "options_sysoptions_key_key" UNIQUE("key") +ALTER TABLE public.exercise ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.exercise_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "problemset" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "problemset_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "title" text NOT NULL, - "description" text NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "last_update_time" timestamp with time zone NOT NULL, - "visible" boolean NOT NULL, - "difficulty" text NOT NULL, - "status" text NOT NULL, - "created_by_id" integer NOT NULL, - "end_time" timestamp with time zone +CREATE TABLE public.flowchart_submission ( + id text NOT NULL, + mermaid_code text NOT NULL, + flowchart_data jsonb NOT NULL, + status integer NOT NULL, + create_time timestamp with time zone NOT NULL, + ai_score double precision, + ai_grade character varying(10), + ai_feedback text, + ai_suggestions text, + ai_criteria_details jsonb NOT NULL, + ai_provider character varying(50) NOT NULL, + ai_model character varying(50) NOT NULL, + processing_time double precision, + evaluation_time timestamp with time zone, + problem_id integer NOT NULL, + user_id integer NOT NULL ); --> statement-breakpoint -CREATE TABLE "problemset_problem" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "problemset_problem_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "order" integer NOT NULL, - "is_required" boolean NOT NULL, - "score" integer NOT NULL, - "hint" text, - "problem_id" integer NOT NULL, - "problemset_id" bigint NOT NULL, - CONSTRAINT "unique_problemset_problem" UNIQUE("problem_id","problemset_id") +CREATE TABLE public.judge_server ( + id integer NOT NULL, + hostname text NOT NULL, + ip text, + judger_version text NOT NULL, + cpu_core integer NOT NULL, + memory_usage double precision NOT NULL, + cpu_usage double precision NOT NULL, + last_heartbeat timestamp with time zone NOT NULL, + create_time timestamp with time zone NOT NULL, + task_number integer NOT NULL, + service_url text, + is_disabled boolean NOT NULL ); --> statement-breakpoint -CREATE TABLE "problemset_progress" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "problemset_progress_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "join_time" timestamp with time zone NOT NULL, - "complete_time" timestamp with time zone, - "is_completed" boolean NOT NULL, - "progress_percentage" double precision NOT NULL, - "completed_problems_count" integer NOT NULL, - "total_problems_count" integer NOT NULL, - "total_score" integer NOT NULL, - "progress_detail" jsonb NOT NULL, - "problemset_id" bigint NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "unique_problemset_progress_user" UNIQUE("problemset_id","user_id") +CREATE SEQUENCE public.judge_server_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.judge_server_id_seq OWNED BY public.judge_server.id; +--> statement-breakpoint +CREATE TABLE public.message ( + id integer NOT NULL, + message text NOT NULL, + create_time timestamp with time zone NOT NULL, + recipient_id integer NOT NULL, + sender_id integer NOT NULL, + submission_id text NOT NULL ); --> statement-breakpoint -CREATE TABLE "problemset_submission" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "problemset_submission_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "problem_id" integer NOT NULL, - "problemset_id" bigint NOT NULL, - "submission_id" text NOT NULL, - "user_id" integer NOT NULL +ALTER TABLE public.message ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.message_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "reaction" ( - "id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "reaction_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), - "type" varchar(20) NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "problem_id" integer NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "reaction_problem_user_unique" UNIQUE("problem_id","user_id") +CREATE TABLE public.options_sysoptions ( + id integer NOT NULL, + key text NOT NULL, + value jsonb NOT NULL ); --> statement-breakpoint -CREATE TABLE "problem" ( - "id" serial PRIMARY KEY NOT NULL, - "title" text NOT NULL, - "description" text NOT NULL, - "input_description" text NOT NULL, - "output_description" text NOT NULL, - "samples" jsonb NOT NULL, - "test_case_id" text NOT NULL, - "test_case_score" jsonb NOT NULL, - "hint" text, - "languages" jsonb NOT NULL, - "template" jsonb NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "last_update_time" timestamp with time zone, - "time_limit" integer NOT NULL, - "memory_limit" integer NOT NULL, - "visible" boolean DEFAULT true NOT NULL, - "difficulty" text NOT NULL, - "source" text, - "submission_number" bigint DEFAULT 0 NOT NULL, - "accepted_number" bigint DEFAULT 0 NOT NULL, - "created_by_id" integer NOT NULL, - "_id" text NOT NULL, - "statistic_info" jsonb DEFAULT '{}'::jsonb NOT NULL, - "contest_id" integer, - "is_public" boolean DEFAULT false NOT NULL, - "share_submission" boolean DEFAULT false NOT NULL, - "prompt" text, - "answers" jsonb, - "allow_flowchart" boolean DEFAULT false NOT NULL, - "flowchart_data" jsonb DEFAULT '{}'::jsonb NOT NULL, - "flowchart_hint" text, - "mermaid_code" text, - "show_flowchart" boolean DEFAULT false NOT NULL, - "ast_rules" jsonb, - "sql_config" jsonb, - "sql_display" jsonb, - CONSTRAINT "unique_problem_id_contest" UNIQUE("_id","contest_id") +CREATE SEQUENCE public.options_sysoptions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.options_sysoptions_id_seq OWNED BY public.options_sysoptions.id; +--> statement-breakpoint +CREATE TABLE public.problem ( + id integer NOT NULL, + title text NOT NULL, + description text NOT NULL, + input_description text NOT NULL, + output_description text NOT NULL, + samples jsonb NOT NULL, + test_case_id text NOT NULL, + test_case_score jsonb NOT NULL, + hint text, + languages jsonb NOT NULL, + template jsonb NOT NULL, + create_time timestamp with time zone NOT NULL, + last_update_time timestamp with time zone, + time_limit integer NOT NULL, + memory_limit integer NOT NULL, + visible boolean DEFAULT true NOT NULL, + difficulty text NOT NULL, + source text, + submission_number bigint DEFAULT 0 NOT NULL, + accepted_number bigint DEFAULT 0 NOT NULL, + created_by_id integer NOT NULL, + _id text NOT NULL, + statistic_info jsonb DEFAULT '{}'::jsonb NOT NULL, + contest_id integer, + is_public boolean DEFAULT false NOT NULL, + share_submission boolean DEFAULT false NOT NULL, + prompt text, + answers jsonb, + allow_flowchart boolean DEFAULT false NOT NULL, + flowchart_data jsonb DEFAULT '{}'::jsonb NOT NULL, + flowchart_hint text, + mermaid_code text, + show_flowchart boolean DEFAULT false NOT NULL, + ast_rules jsonb, + sql_config jsonb, + sql_display jsonb ); --> statement-breakpoint -CREATE TABLE "problem_tags" ( - "id" serial PRIMARY KEY NOT NULL, - "problem_id" integer NOT NULL, - "problemtag_id" integer NOT NULL, - CONSTRAINT "problem_tags_problem_id_problemtag_id_318459d1_uniq" UNIQUE("problem_id","problemtag_id") +CREATE SEQUENCE public.problem_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.problem_id_seq OWNED BY public.problem.id; +--> statement-breakpoint +CREATE TABLE public.problem_tag ( + id integer NOT NULL, + name text NOT NULL ); --> statement-breakpoint -CREATE TABLE "problem_tag" ( - "id" serial PRIMARY KEY NOT NULL, - "name" text NOT NULL +CREATE SEQUENCE public.problem_tag_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.problem_tag_id_seq OWNED BY public.problem_tag.id; +--> statement-breakpoint +CREATE TABLE public.problem_tags ( + id integer NOT NULL, + problem_id integer NOT NULL, + problemtag_id integer NOT NULL ); --> statement-breakpoint -CREATE TABLE "submission" ( - "id" text PRIMARY KEY NOT NULL, - "contest_id" integer, - "problem_id" integer NOT NULL, - "create_time" timestamp with time zone NOT NULL, - "user_id" integer NOT NULL, - "code" text NOT NULL, - "result" integer DEFAULT 6 NOT NULL, - "info" jsonb DEFAULT '{}'::jsonb NOT NULL, - "language" text NOT NULL, - "shared" boolean DEFAULT false NOT NULL, - "statistic_info" jsonb DEFAULT '{}'::jsonb NOT NULL, - "username" text NOT NULL, - "ip" text +CREATE SEQUENCE public.problem_tags_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.problem_tags_id_seq OWNED BY public.problem_tags.id; +--> statement-breakpoint +CREATE TABLE public.problemset ( + id bigint NOT NULL, + title text NOT NULL, + description text NOT NULL, + create_time timestamp with time zone NOT NULL, + last_update_time timestamp with time zone NOT NULL, + visible boolean NOT NULL, + difficulty text NOT NULL, + status text NOT NULL, + created_by_id integer NOT NULL, + end_time timestamp with time zone ); --> statement-breakpoint -CREATE TABLE "tutorial" ( - "id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "tutorial_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), - "title" varchar(128) NOT NULL, - "content" text NOT NULL, - "created_at" timestamp with time zone NOT NULL, - "updated_at" timestamp with time zone NOT NULL, - "is_public" boolean NOT NULL, - "order" integer NOT NULL, - "created_by_id" integer NOT NULL, - "code" text, - "type" varchar(10) NOT NULL +CREATE TABLE public.problemset_badge ( + id bigint NOT NULL, + name text NOT NULL, + description text NOT NULL, + icon text NOT NULL, + condition_type text NOT NULL, + condition_value integer NOT NULL, + problemset_id bigint NOT NULL ); --> statement-breakpoint -CREATE TABLE "user_stat" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "user_stat_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "metrics" jsonb DEFAULT '{}'::jsonb NOT NULL, - "update_time" timestamp with time zone NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "user_stat_user_id_key" UNIQUE("user_id") +ALTER TABLE public.problemset_badge ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.problemset_badge_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "user_achievement" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "user_achievement_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "unlock_time" timestamp with time zone NOT NULL, - "backfilled" boolean DEFAULT false NOT NULL, - "notified" boolean DEFAULT false NOT NULL, - "achievement_id" bigint NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "unique_user_achievement" UNIQUE("achievement_id","user_id") +ALTER TABLE public.problemset ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.problemset_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "user_badge" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "user_badge_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "earned_time" timestamp with time zone NOT NULL, - "badge_id" bigint NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "unique_user_badge" UNIQUE("badge_id","user_id") +CREATE TABLE public.problemset_problem ( + id bigint NOT NULL, + "order" integer NOT NULL, + is_required boolean NOT NULL, + score integer NOT NULL, + hint text, + problem_id integer NOT NULL, + problemset_id bigint NOT NULL ); --> statement-breakpoint -CREATE TABLE "user_profile" ( - "id" serial PRIMARY KEY NOT NULL, - "acm_problems_status" jsonb DEFAULT '{}'::jsonb NOT NULL, - "avatar" text NOT NULL, - "blog" varchar(200), - "mood" text, - "accepted_number" integer DEFAULT 0 NOT NULL, - "submission_number" integer DEFAULT 0 NOT NULL, - "github" text, - "school" text, - "major" text, - "user_id" integer NOT NULL, - "real_name" text, - "language" text, - CONSTRAINT "user_profile_user_id_key" UNIQUE("user_id") +ALTER TABLE public.problemset_problem ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.problemset_problem_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "acm_contest_rank" ( - "id" serial PRIMARY KEY NOT NULL, - "submission_number" integer DEFAULT 0 NOT NULL, - "accepted_number" integer DEFAULT 0 NOT NULL, - "total_time" integer DEFAULT 0 NOT NULL, - "submission_info" jsonb DEFAULT '{}'::jsonb NOT NULL, - "contest_id" integer NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "unique_acm_rank_user_contest" UNIQUE("contest_id","user_id") +CREATE TABLE public.problemset_progress ( + id bigint NOT NULL, + join_time timestamp with time zone NOT NULL, + complete_time timestamp with time zone, + is_completed boolean NOT NULL, + progress_percentage double precision NOT NULL, + completed_problems_count integer NOT NULL, + total_problems_count integer NOT NULL, + total_score integer NOT NULL, + progress_detail jsonb NOT NULL, + problemset_id bigint NOT NULL, + user_id integer NOT NULL ); --> statement-breakpoint -CREATE TABLE "user" ( - "id" serial PRIMARY KEY NOT NULL, - "password" varchar(128) NOT NULL, - "last_login" timestamp with time zone, - "username" text NOT NULL, - "email" text, - "create_time" timestamp with time zone, - "admin_type" text NOT NULL, - "auth_token" text, - "open_api" boolean DEFAULT false NOT NULL, - "open_api_appkey" text, - "is_disabled" boolean DEFAULT false NOT NULL, - "problem_permission" text NOT NULL, - "session_keys" jsonb DEFAULT '[]'::jsonb NOT NULL, - "raw_password" varchar(20), - "class_name" text, - CONSTRAINT "user_username_key" UNIQUE("username") +ALTER TABLE public.problemset_progress ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.problemset_progress_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 ); --> statement-breakpoint -CREATE TABLE "problemset_badge" ( - "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "problemset_badge_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), - "name" text NOT NULL, - "description" text NOT NULL, - "icon" text NOT NULL, - "condition_type" text NOT NULL, - "condition_value" integer NOT NULL, - "problemset_id" bigint NOT NULL +CREATE TABLE public.problemset_submission ( + id bigint NOT NULL, + problem_id integer NOT NULL, + problemset_id bigint NOT NULL, + submission_id text NOT NULL, + user_id integer NOT NULL ); --> statement-breakpoint -ALTER TABLE "ai_analysis" ADD CONSTRAINT "ai_analysis_user_id_3aa23011_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "announcement" ADD CONSTRAINT "announcement_created_by_id_359ccf50_fk_user_id" FOREIGN KEY ("created_by_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "contest" ADD CONSTRAINT "contest_created_by_id_a763ca7e_fk_user_id" FOREIGN KEY ("created_by_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "contest_announcement" ADD CONSTRAINT "contest_announcement_contest_id_a8cb419f_fk_contest_id" FOREIGN KEY ("contest_id") REFERENCES "public"."contest"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "contest_announcement" ADD CONSTRAINT "contest_announcement_created_by_id_469a14ce_fk_user_id" FOREIGN KEY ("created_by_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "flowchart_submission" ADD CONSTRAINT "flowchart_submission_problem_id_8551edbf_fk_problem_id" FOREIGN KEY ("problem_id") REFERENCES "public"."problem"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "flowchart_submission" ADD CONSTRAINT "flowchart_submission_user_id_225c83e8_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "message" ADD CONSTRAINT "message_recipient_id_2aa5dd76_fk_user_id" FOREIGN KEY ("recipient_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "message" ADD CONSTRAINT "message_sender_id_a2a2e825_fk_user_id" FOREIGN KEY ("sender_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "message" ADD CONSTRAINT "message_submission_id_2fdf8a47_fk_submission_id" FOREIGN KEY ("submission_id") REFERENCES "public"."submission"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "exercise" ADD CONSTRAINT "exercise_tutorial_id_6fd04055_fk_tutorial_id" FOREIGN KEY ("tutorial_id") REFERENCES "public"."tutorial"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset" ADD CONSTRAINT "problemset_created_by_id_01b5197f_fk_user_id" FOREIGN KEY ("created_by_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_problem" ADD CONSTRAINT "problemset_problem_problem_id_fff2d686_fk_problem_id" FOREIGN KEY ("problem_id") REFERENCES "public"."problem"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_problem" ADD CONSTRAINT "problemset_problem_problemset_id_350d17fb_fk_problemset_id" FOREIGN KEY ("problemset_id") REFERENCES "public"."problemset"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_progress" ADD CONSTRAINT "problemset_progress_problemset_id_20a9632e_fk_problemset_id" FOREIGN KEY ("problemset_id") REFERENCES "public"."problemset"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_progress" ADD CONSTRAINT "problemset_progress_user_id_c8041a80_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_submission" ADD CONSTRAINT "problemset_submission_problem_id_5629b105_fk_problem_id" FOREIGN KEY ("problem_id") REFERENCES "public"."problem"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_submission" ADD CONSTRAINT "problemset_submission_problemset_id_85290e17_fk_problemset_id" FOREIGN KEY ("problemset_id") REFERENCES "public"."problemset"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_submission" ADD CONSTRAINT "problemset_submission_submission_id_78e2b807_fk_submission_id" FOREIGN KEY ("submission_id") REFERENCES "public"."submission"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_submission" ADD CONSTRAINT "problemset_submission_user_id_915fc9c6_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "reaction" ADD CONSTRAINT "reaction_problem_id_a7f3b9f3_fk_problem_id" FOREIGN KEY ("problem_id") REFERENCES "public"."problem"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "reaction" ADD CONSTRAINT "reaction_user_id_cfa7f469_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problem" ADD CONSTRAINT "problem_contest_id_328e013a_fk_contest_id" FOREIGN KEY ("contest_id") REFERENCES "public"."contest"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problem" ADD CONSTRAINT "problem_created_by_id_cb362143_fk_user_id" FOREIGN KEY ("created_by_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problem_tags" ADD CONSTRAINT "problem_tags_problem_id_866ecb8d_fk_problem_id" FOREIGN KEY ("problem_id") REFERENCES "public"."problem"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problem_tags" ADD CONSTRAINT "problem_tags_problemtag_id_72d20571_fk_problem_tag_id" FOREIGN KEY ("problemtag_id") REFERENCES "public"."problem_tag"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "submission" ADD CONSTRAINT "submission_contest_id_775716d5_fk_contest_id" FOREIGN KEY ("contest_id") REFERENCES "public"."contest"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "submission" ADD CONSTRAINT "submission_problem_id_76847b55_fk_problem_id" FOREIGN KEY ("problem_id") REFERENCES "public"."problem"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "tutorial" ADD CONSTRAINT "tutorial_created_by_id_07973cab_fk_user_id" FOREIGN KEY ("created_by_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "user_stat" ADD CONSTRAINT "user_stat_user_id_73337fc0_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "user_achievement" ADD CONSTRAINT "user_achievement_achievement_id_29db600d_fk_achievement_id" FOREIGN KEY ("achievement_id") REFERENCES "public"."achievement"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "user_achievement" ADD CONSTRAINT "user_achievement_user_id_b8ec7d6a_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "user_badge" ADD CONSTRAINT "user_badge_badge_id_92a983e9_fk_problemset_badge_id" FOREIGN KEY ("badge_id") REFERENCES "public"."problemset_badge"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "user_badge" ADD CONSTRAINT "user_badge_user_id_a286d718_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "user_profile" ADD CONSTRAINT "user_profile_user_id_8fdce8e2_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "acm_contest_rank" ADD CONSTRAINT "acm_contest_rank_contest_id_21030ccd_fk_contest_id" FOREIGN KEY ("contest_id") REFERENCES "public"."contest"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "acm_contest_rank" ADD CONSTRAINT "acm_contest_rank_user_id_40391ab2_fk_user_id" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "problemset_badge" ADD CONSTRAINT "problemset_badge_problemset_id_6cb6c74f_fk_problemset_id" FOREIGN KEY ("problemset_id") REFERENCES "public"."problemset"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -CREATE INDEX "ai_analysis_user_id_3aa23011" ON "ai_analysis" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "announcement_created_by_id_359ccf50" ON "announcement" USING btree ("created_by_id" int4_ops);--> statement-breakpoint -CREATE INDEX "announcement_list_idx" ON "announcement" USING btree ("visible" bool_ops,"top" bool_ops,"create_time" bool_ops);--> statement-breakpoint -CREATE INDEX "contest_created_by_id_a763ca7e" ON "contest" USING btree ("created_by_id" int4_ops);--> statement-breakpoint -CREATE INDEX "contest_announcement_contest_id_a8cb419f" ON "contest_announcement" USING btree ("contest_id" int4_ops);--> statement-breakpoint -CREATE INDEX "contest_announcement_created_by_id_469a14ce" ON "contest_announcement" USING btree ("created_by_id" int4_ops);--> statement-breakpoint -CREATE INDEX "flowchart_problem_time_idx" ON "flowchart_submission" USING btree ("problem_id" int4_ops,"create_time" int4_ops);--> statement-breakpoint -CREATE INDEX "flowchart_status_idx" ON "flowchart_submission" USING btree ("status" int4_ops);--> statement-breakpoint -CREATE INDEX "flowchart_submission_id_0dbfc4f9_like" ON "flowchart_submission" USING btree ("id" text_pattern_ops);--> statement-breakpoint -CREATE INDEX "flowchart_submission_problem_id_8551edbf" ON "flowchart_submission" USING btree ("problem_id" int4_ops);--> statement-breakpoint -CREATE INDEX "flowchart_submission_user_id_225c83e8" ON "flowchart_submission" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "flowchart_user_time_idx" ON "flowchart_submission" USING btree ("user_id" int4_ops,"create_time" int4_ops);--> statement-breakpoint -CREATE INDEX "message_recipient_id_2aa5dd76" ON "message" USING btree ("recipient_id" int4_ops);--> statement-breakpoint -CREATE INDEX "message_recipient_time_idx" ON "message" USING btree ("recipient_id" timestamptz_ops,"create_time" int4_ops);--> statement-breakpoint -CREATE INDEX "message_sender_id_a2a2e825" ON "message" USING btree ("sender_id" int4_ops);--> statement-breakpoint -CREATE INDEX "message_submission_id_2fdf8a47" ON "message" USING btree ("submission_id" text_ops);--> statement-breakpoint -CREATE INDEX "message_submission_id_2fdf8a47_like" ON "message" USING btree ("submission_id" text_pattern_ops);--> statement-breakpoint -CREATE INDEX "exercise_tutorial_id_6fd04055" ON "exercise" USING btree ("tutorial_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset_created_by_id_01b5197f" ON "problemset" USING btree ("created_by_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset_problem_problem_id_fff2d686" ON "problemset_problem" USING btree ("problem_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset_problem_problemset_id_350d17fb" ON "problemset_problem" USING btree ("problemset_id" int8_ops);--> statement-breakpoint -CREATE INDEX "problemset_progress_problemset_id_20a9632e" ON "problemset_progress" USING btree ("problemset_id" int8_ops);--> statement-breakpoint -CREATE INDEX "problemset_progress_user_id_c8041a80" ON "problemset_progress" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset__problem_1f39fa_idx" ON "problemset_submission" USING btree ("problemset_id" int4_ops,"user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset__problem_22f053_idx" ON "problemset_submission" USING btree ("problemset_id" int8_ops,"problem_id" int8_ops);--> statement-breakpoint -CREATE INDEX "problemset__user_id_2f1501_idx" ON "problemset_submission" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset_submission_problem_id_5629b105" ON "problemset_submission" USING btree ("problem_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset_submission_problemset_id_85290e17" ON "problemset_submission" USING btree ("problemset_id" int8_ops);--> statement-breakpoint -CREATE INDEX "problemset_submission_submission_id_78e2b807" ON "problemset_submission" USING btree ("submission_id" text_ops);--> statement-breakpoint -CREATE INDEX "problemset_submission_submission_id_78e2b807_like" ON "problemset_submission" USING btree ("submission_id" text_pattern_ops);--> statement-breakpoint -CREATE INDEX "problemset_submission_user_id_915fc9c6" ON "problemset_submission" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "reaction_problem_id_a7f3b9f3" ON "reaction" USING btree ("problem_id" int4_ops);--> statement-breakpoint -CREATE INDEX "reaction_problem_type_idx" ON "reaction" USING btree ("problem_id" int4_ops,"type" int4_ops);--> statement-breakpoint -CREATE INDEX "reaction_user_id_cfa7f469" ON "reaction" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problem__id_919b1d80" ON "problem" USING btree ("_id" text_ops);--> statement-breakpoint -CREATE INDEX "problem_contest_id_328e013a" ON "problem" USING btree ("contest_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problem_contest_visible_idx" ON "problem" USING btree ("contest_id" bool_ops,"visible" int4_ops);--> statement-breakpoint -CREATE INDEX "problem_created_by_id_cb362143" ON "problem" USING btree ("created_by_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problem_visible_idx" ON "problem" USING btree ("visible" bool_ops);--> statement-breakpoint -CREATE INDEX "problem_tags_problem_id_866ecb8d" ON "problem_tags" USING btree ("problem_id" int4_ops);--> statement-breakpoint -CREATE INDEX "problem_tags_problemtag_id_72d20571" ON "problem_tags" USING btree ("problemtag_id" int4_ops);--> statement-breakpoint -CREATE UNIQUE INDEX "problem_tag_name_ci_unique" ON "problem_tag" USING btree (lower(name) text_ops);--> statement-breakpoint -CREATE INDEX "contest_create_time_idx" ON "submission" USING btree ("contest_id" timestamptz_ops,"create_time" int4_ops);--> statement-breakpoint -CREATE INDEX "problem_user_idx" ON "submission" USING btree ("problem_id" int4_ops,"user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "submission_contest_id_775716d5" ON "submission" USING btree ("contest_id" int4_ops);--> statement-breakpoint -CREATE INDEX "submission_problem_id_76847b55" ON "submission" USING btree ("problem_id" int4_ops);--> statement-breakpoint -CREATE INDEX "submission_result_37e2f67a" ON "submission" USING btree ("result" int4_ops);--> statement-breakpoint -CREATE INDEX "submission_user_id_3779a8c1" ON "submission" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "user_create_time_idx" ON "submission" USING btree ("user_id" int4_ops,"create_time" timestamptz_ops);--> statement-breakpoint -CREATE INDEX "tutorial_created_by_id_07973cab" ON "tutorial" USING btree ("created_by_id" int4_ops);--> statement-breakpoint -CREATE INDEX "user_achievement_achievement_id_29db600d" ON "user_achievement" USING btree ("achievement_id" int8_ops);--> statement-breakpoint -CREATE INDEX "user_achievement_user_id_b8ec7d6a" ON "user_achievement" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "user_achv_notified_idx" ON "user_achievement" USING btree ("user_id" int4_ops,"notified" bool_ops);--> statement-breakpoint -CREATE INDEX "user_achv_time_idx" ON "user_achievement" USING btree ("user_id" int4_ops,"unlock_time" timestamptz_ops);--> statement-breakpoint -CREATE INDEX "user_badge_badge_id_92a983e9" ON "user_badge" USING btree ("badge_id" int8_ops);--> statement-breakpoint -CREATE INDEX "user_badge_user_id_a286d718" ON "user_badge" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "acm_contest_rank_contest_id_21030ccd" ON "acm_contest_rank" USING btree ("contest_id" int4_ops);--> statement-breakpoint -CREATE INDEX "acm_contest_rank_user_id_40391ab2" ON "acm_contest_rank" USING btree ("user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "acm_rank_contest_user_idx" ON "acm_contest_rank" USING btree ("contest_id" int4_ops,"user_id" int4_ops);--> statement-breakpoint -CREATE INDEX "acm_rank_order_idx" ON "acm_contest_rank" USING btree ("contest_id" int4_ops,"accepted_number" int4_ops,"total_time" int4_ops);--> statement-breakpoint -CREATE INDEX "problemset_badge_problemset_id_6cb6c74f" ON "problemset_badge" USING btree ("problemset_id" int8_ops); -*/ \ No newline at end of file +ALTER TABLE public.problemset_submission ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.problemset_submission_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); +--> statement-breakpoint +CREATE TABLE public.reaction ( + id integer NOT NULL, + type character varying(20) NOT NULL, + create_time timestamp with time zone NOT NULL, + problem_id integer NOT NULL, + user_id integer NOT NULL +); +--> statement-breakpoint +ALTER TABLE public.reaction ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.reaction_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); +--> statement-breakpoint +CREATE TABLE public.submission ( + id text NOT NULL, + contest_id integer, + problem_id integer NOT NULL, + create_time timestamp with time zone NOT NULL, + user_id integer NOT NULL, + code text NOT NULL, + result integer DEFAULT 6 NOT NULL, + info jsonb DEFAULT '{}'::jsonb NOT NULL, + language text NOT NULL, + shared boolean DEFAULT false NOT NULL, + statistic_info jsonb DEFAULT '{}'::jsonb NOT NULL, + username text NOT NULL, + ip text +); +--> statement-breakpoint +CREATE TABLE public.tutorial ( + id integer NOT NULL, + title character varying(128) NOT NULL, + content text NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + is_public boolean NOT NULL, + "order" integer NOT NULL, + created_by_id integer NOT NULL, + code text, + type character varying(10) NOT NULL +); +--> statement-breakpoint +ALTER TABLE public.tutorial ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.tutorial_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); +--> statement-breakpoint +CREATE TABLE public."user" ( + id integer NOT NULL, + password character varying(128) NOT NULL, + last_login timestamp with time zone, + username text NOT NULL, + email text, + create_time timestamp with time zone, + admin_type text NOT NULL, + auth_token text, + open_api boolean DEFAULT false NOT NULL, + open_api_appkey text, + is_disabled boolean DEFAULT false NOT NULL, + problem_permission text NOT NULL, + session_keys jsonb DEFAULT '[]'::jsonb NOT NULL, + raw_password character varying(20), + class_name text +); +--> statement-breakpoint +CREATE TABLE public.user_achievement ( + id bigint NOT NULL, + unlock_time timestamp with time zone NOT NULL, + backfilled boolean DEFAULT false NOT NULL, + notified boolean DEFAULT false NOT NULL, + achievement_id bigint NOT NULL, + user_id integer NOT NULL +); +--> statement-breakpoint +ALTER TABLE public.user_achievement ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.user_achievement_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); +--> statement-breakpoint +CREATE TABLE public.user_badge ( + id bigint NOT NULL, + earned_time timestamp with time zone NOT NULL, + badge_id bigint NOT NULL, + user_id integer NOT NULL +); +--> statement-breakpoint +ALTER TABLE public.user_badge ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.user_badge_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); +--> statement-breakpoint +CREATE SEQUENCE public.user_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.user_id_seq OWNED BY public."user".id; +--> statement-breakpoint +CREATE TABLE public.user_profile ( + id integer NOT NULL, + acm_problems_status jsonb DEFAULT '{}'::jsonb NOT NULL, + avatar text NOT NULL, + blog character varying(200), + mood text, + accepted_number integer DEFAULT 0 NOT NULL, + submission_number integer DEFAULT 0 NOT NULL, + github text, + school text, + major text, + user_id integer NOT NULL, + real_name text, + language text +); +--> statement-breakpoint +CREATE SEQUENCE public.user_profile_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; +--> statement-breakpoint +ALTER SEQUENCE public.user_profile_id_seq OWNED BY public.user_profile.id; +--> statement-breakpoint +CREATE TABLE public.user_stat ( + id bigint NOT NULL, + metrics jsonb DEFAULT '{}'::jsonb NOT NULL, + update_time timestamp with time zone NOT NULL, + user_id integer NOT NULL +); +--> statement-breakpoint +ALTER TABLE public.user_stat ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.user_stat_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); +--> statement-breakpoint +ALTER TABLE ONLY public.acm_contest_rank ALTER COLUMN id SET DEFAULT nextval('public.acm_contest_rank_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.announcement ALTER COLUMN id SET DEFAULT nextval('public.announcement_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.contest ALTER COLUMN id SET DEFAULT nextval('public.contest_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.contest_announcement ALTER COLUMN id SET DEFAULT nextval('public.contest_announcement_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.judge_server ALTER COLUMN id SET DEFAULT nextval('public.judge_server_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.options_sysoptions ALTER COLUMN id SET DEFAULT nextval('public.options_sysoptions_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.problem ALTER COLUMN id SET DEFAULT nextval('public.problem_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.problem_tag ALTER COLUMN id SET DEFAULT nextval('public.problem_tag_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.problem_tags ALTER COLUMN id SET DEFAULT nextval('public.problem_tags_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public."user" ALTER COLUMN id SET DEFAULT nextval('public.user_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.user_profile ALTER COLUMN id SET DEFAULT nextval('public.user_profile_id_seq'::regclass); +--> statement-breakpoint +ALTER TABLE ONLY public.achievement + ADD CONSTRAINT achievement_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.acm_contest_rank + ADD CONSTRAINT acm_contest_rank_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.ai_analysis + ADD CONSTRAINT ai_analysis_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.announcement + ADD CONSTRAINT announcement_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.contest_announcement + ADD CONSTRAINT contest_announcement_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.contest + ADD CONSTRAINT contest_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.exercise + ADD CONSTRAINT exercise_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.flowchart_submission + ADD CONSTRAINT flowchart_submission_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.judge_server + ADD CONSTRAINT judge_server_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.message + ADD CONSTRAINT message_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.options_sysoptions + ADD CONSTRAINT options_sysoptions_key_key UNIQUE (key); +--> statement-breakpoint +ALTER TABLE ONLY public.options_sysoptions + ADD CONSTRAINT options_sysoptions_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problem + ADD CONSTRAINT problem_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problem_tag + ADD CONSTRAINT problem_tag_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problem_tags + ADD CONSTRAINT problem_tags_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problem_tags + ADD CONSTRAINT problem_tags_problem_id_problemtag_id_318459d1_uniq UNIQUE (problem_id, problemtag_id); +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_badge + ADD CONSTRAINT problemset_badge_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problemset + ADD CONSTRAINT problemset_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_problem + ADD CONSTRAINT problemset_problem_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_progress + ADD CONSTRAINT problemset_progress_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_submission + ADD CONSTRAINT problemset_submission_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.reaction + ADD CONSTRAINT reaction_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.reaction + ADD CONSTRAINT reaction_problem_user_unique UNIQUE (problem_id, user_id); +--> statement-breakpoint +ALTER TABLE ONLY public.submission + ADD CONSTRAINT submission_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.tutorial + ADD CONSTRAINT tutorial_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.acm_contest_rank + ADD CONSTRAINT unique_acm_rank_user_contest UNIQUE (user_id, contest_id); +--> statement-breakpoint +ALTER TABLE ONLY public.problem + ADD CONSTRAINT unique_problem_id_contest UNIQUE (_id, contest_id); +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_problem + ADD CONSTRAINT unique_problemset_problem UNIQUE (problemset_id, problem_id); +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_progress + ADD CONSTRAINT unique_problemset_progress_user UNIQUE (problemset_id, user_id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_achievement + ADD CONSTRAINT unique_user_achievement UNIQUE (user_id, achievement_id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_badge + ADD CONSTRAINT unique_user_badge UNIQUE (user_id, badge_id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_achievement + ADD CONSTRAINT user_achievement_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_badge + ADD CONSTRAINT user_badge_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public."user" + ADD CONSTRAINT user_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_profile + ADD CONSTRAINT user_profile_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_profile + ADD CONSTRAINT user_profile_user_id_key UNIQUE (user_id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_stat + ADD CONSTRAINT user_stat_pkey PRIMARY KEY (id); +--> statement-breakpoint +ALTER TABLE ONLY public.user_stat + ADD CONSTRAINT user_stat_user_id_key UNIQUE (user_id); +--> statement-breakpoint +ALTER TABLE ONLY public."user" + ADD CONSTRAINT user_username_key UNIQUE (username); +--> statement-breakpoint +CREATE INDEX acm_contest_rank_contest_id_21030ccd ON public.acm_contest_rank USING btree (contest_id); +--> statement-breakpoint +CREATE INDEX acm_contest_rank_user_id_40391ab2 ON public.acm_contest_rank USING btree (user_id); +--> statement-breakpoint +CREATE INDEX acm_rank_contest_user_idx ON public.acm_contest_rank USING btree (contest_id, user_id); +--> statement-breakpoint +CREATE INDEX acm_rank_order_idx ON public.acm_contest_rank USING btree (contest_id, accepted_number, total_time); +--> statement-breakpoint +CREATE INDEX ai_analysis_user_id_3aa23011 ON public.ai_analysis USING btree (user_id); +--> statement-breakpoint +CREATE INDEX announcement_created_by_id_359ccf50 ON public.announcement USING btree (created_by_id); +--> statement-breakpoint +CREATE INDEX announcement_list_idx ON public.announcement USING btree (visible, top DESC, create_time DESC); +--> statement-breakpoint +CREATE INDEX contest_announcement_contest_id_a8cb419f ON public.contest_announcement USING btree (contest_id); +--> statement-breakpoint +CREATE INDEX contest_announcement_created_by_id_469a14ce ON public.contest_announcement USING btree (created_by_id); +--> statement-breakpoint +CREATE INDEX contest_create_time_idx ON public.submission USING btree (contest_id, create_time DESC); +--> statement-breakpoint +CREATE INDEX contest_created_by_id_a763ca7e ON public.contest USING btree (created_by_id); +--> statement-breakpoint +CREATE INDEX exercise_tutorial_id_6fd04055 ON public.exercise USING btree (tutorial_id); +--> statement-breakpoint +CREATE INDEX flowchart_problem_time_idx ON public.flowchart_submission USING btree (problem_id, create_time); +--> statement-breakpoint +CREATE INDEX flowchart_status_idx ON public.flowchart_submission USING btree (status); +--> statement-breakpoint +CREATE INDEX flowchart_submission_id_0dbfc4f9_like ON public.flowchart_submission USING btree (id text_pattern_ops); +--> statement-breakpoint +CREATE INDEX flowchart_submission_problem_id_8551edbf ON public.flowchart_submission USING btree (problem_id); +--> statement-breakpoint +CREATE INDEX flowchart_submission_user_id_225c83e8 ON public.flowchart_submission USING btree (user_id); +--> statement-breakpoint +CREATE INDEX flowchart_user_time_idx ON public.flowchart_submission USING btree (user_id, create_time); +--> statement-breakpoint +CREATE INDEX message_recipient_id_2aa5dd76 ON public.message USING btree (recipient_id); +--> statement-breakpoint +CREATE INDEX message_recipient_time_idx ON public.message USING btree (recipient_id, create_time); +--> statement-breakpoint +CREATE INDEX message_sender_id_a2a2e825 ON public.message USING btree (sender_id); +--> statement-breakpoint +CREATE INDEX message_submission_id_2fdf8a47 ON public.message USING btree (submission_id); +--> statement-breakpoint +CREATE INDEX message_submission_id_2fdf8a47_like ON public.message USING btree (submission_id text_pattern_ops); +--> statement-breakpoint +CREATE INDEX problem__id_919b1d80 ON public.problem USING btree (_id); +--> statement-breakpoint +CREATE INDEX problem_contest_id_328e013a ON public.problem USING btree (contest_id); +--> statement-breakpoint +CREATE INDEX problem_contest_visible_idx ON public.problem USING btree (contest_id, visible); +--> statement-breakpoint +CREATE INDEX problem_created_by_id_cb362143 ON public.problem USING btree (created_by_id); +--> statement-breakpoint +CREATE UNIQUE INDEX problem_tag_name_ci_unique ON public.problem_tag USING btree (lower(name)); +--> statement-breakpoint +CREATE INDEX problem_tags_problem_id_866ecb8d ON public.problem_tags USING btree (problem_id); +--> statement-breakpoint +CREATE INDEX problem_tags_problemtag_id_72d20571 ON public.problem_tags USING btree (problemtag_id); +--> statement-breakpoint +CREATE INDEX problem_user_idx ON public.submission USING btree (problem_id, user_id); +--> statement-breakpoint +CREATE INDEX problem_visible_idx ON public.problem USING btree (visible); +--> statement-breakpoint +CREATE INDEX problemset__problem_1f39fa_idx ON public.problemset_submission USING btree (problemset_id, user_id); +--> statement-breakpoint +CREATE INDEX problemset__problem_22f053_idx ON public.problemset_submission USING btree (problemset_id, problem_id); +--> statement-breakpoint +CREATE INDEX problemset__user_id_2f1501_idx ON public.problemset_submission USING btree (user_id); +--> statement-breakpoint +CREATE INDEX problemset_badge_problemset_id_6cb6c74f ON public.problemset_badge USING btree (problemset_id); +--> statement-breakpoint +CREATE INDEX problemset_created_by_id_01b5197f ON public.problemset USING btree (created_by_id); +--> statement-breakpoint +CREATE INDEX problemset_problem_problem_id_fff2d686 ON public.problemset_problem USING btree (problem_id); +--> statement-breakpoint +CREATE INDEX problemset_problem_problemset_id_350d17fb ON public.problemset_problem USING btree (problemset_id); +--> statement-breakpoint +CREATE INDEX problemset_progress_problemset_id_20a9632e ON public.problemset_progress USING btree (problemset_id); +--> statement-breakpoint +CREATE INDEX problemset_progress_user_id_c8041a80 ON public.problemset_progress USING btree (user_id); +--> statement-breakpoint +CREATE INDEX problemset_submission_problem_id_5629b105 ON public.problemset_submission USING btree (problem_id); +--> statement-breakpoint +CREATE INDEX problemset_submission_problemset_id_85290e17 ON public.problemset_submission USING btree (problemset_id); +--> statement-breakpoint +CREATE INDEX problemset_submission_submission_id_78e2b807 ON public.problemset_submission USING btree (submission_id); +--> statement-breakpoint +CREATE INDEX problemset_submission_submission_id_78e2b807_like ON public.problemset_submission USING btree (submission_id text_pattern_ops); +--> statement-breakpoint +CREATE INDEX problemset_submission_user_id_915fc9c6 ON public.problemset_submission USING btree (user_id); +--> statement-breakpoint +CREATE INDEX reaction_problem_id_a7f3b9f3 ON public.reaction USING btree (problem_id); +--> statement-breakpoint +CREATE INDEX reaction_problem_type_idx ON public.reaction USING btree (problem_id, type); +--> statement-breakpoint +CREATE INDEX reaction_user_id_cfa7f469 ON public.reaction USING btree (user_id); +--> statement-breakpoint +CREATE INDEX submission_contest_id_775716d5 ON public.submission USING btree (contest_id); +--> statement-breakpoint +CREATE INDEX submission_problem_id_76847b55 ON public.submission USING btree (problem_id); +--> statement-breakpoint +CREATE INDEX submission_result_37e2f67a ON public.submission USING btree (result); +--> statement-breakpoint +CREATE INDEX submission_user_id_3779a8c1 ON public.submission USING btree (user_id); +--> statement-breakpoint +CREATE INDEX tutorial_created_by_id_07973cab ON public.tutorial USING btree (created_by_id); +--> statement-breakpoint +CREATE INDEX user_achievement_achievement_id_29db600d ON public.user_achievement USING btree (achievement_id); +--> statement-breakpoint +CREATE INDEX user_achievement_user_id_b8ec7d6a ON public.user_achievement USING btree (user_id); +--> statement-breakpoint +CREATE INDEX user_achv_notified_idx ON public.user_achievement USING btree (user_id, notified); +--> statement-breakpoint +CREATE INDEX user_achv_time_idx ON public.user_achievement USING btree (user_id, unlock_time DESC); +--> statement-breakpoint +CREATE INDEX user_badge_badge_id_92a983e9 ON public.user_badge USING btree (badge_id); +--> statement-breakpoint +CREATE INDEX user_badge_user_id_a286d718 ON public.user_badge USING btree (user_id); +--> statement-breakpoint +CREATE INDEX user_create_time_idx ON public.submission USING btree (user_id, create_time); +--> statement-breakpoint +ALTER TABLE ONLY public.acm_contest_rank + ADD CONSTRAINT acm_contest_rank_contest_id_21030ccd_fk_contest_id FOREIGN KEY (contest_id) REFERENCES public.contest(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.acm_contest_rank + ADD CONSTRAINT acm_contest_rank_user_id_40391ab2_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.ai_analysis + ADD CONSTRAINT ai_analysis_user_id_3aa23011_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.announcement + ADD CONSTRAINT announcement_created_by_id_359ccf50_fk_user_id FOREIGN KEY (created_by_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.contest_announcement + ADD CONSTRAINT contest_announcement_contest_id_a8cb419f_fk_contest_id FOREIGN KEY (contest_id) REFERENCES public.contest(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.contest_announcement + ADD CONSTRAINT contest_announcement_created_by_id_469a14ce_fk_user_id FOREIGN KEY (created_by_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.contest + ADD CONSTRAINT contest_created_by_id_a763ca7e_fk_user_id FOREIGN KEY (created_by_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.exercise + ADD CONSTRAINT exercise_tutorial_id_6fd04055_fk_tutorial_id FOREIGN KEY (tutorial_id) REFERENCES public.tutorial(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.flowchart_submission + ADD CONSTRAINT flowchart_submission_problem_id_8551edbf_fk_problem_id FOREIGN KEY (problem_id) REFERENCES public.problem(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.flowchart_submission + ADD CONSTRAINT flowchart_submission_user_id_225c83e8_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.message + ADD CONSTRAINT message_recipient_id_2aa5dd76_fk_user_id FOREIGN KEY (recipient_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.message + ADD CONSTRAINT message_sender_id_a2a2e825_fk_user_id FOREIGN KEY (sender_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.message + ADD CONSTRAINT message_submission_id_2fdf8a47_fk_submission_id FOREIGN KEY (submission_id) REFERENCES public.submission(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problem + ADD CONSTRAINT problem_contest_id_328e013a_fk_contest_id FOREIGN KEY (contest_id) REFERENCES public.contest(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problem + ADD CONSTRAINT problem_created_by_id_cb362143_fk_user_id FOREIGN KEY (created_by_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problem_tags + ADD CONSTRAINT problem_tags_problem_id_866ecb8d_fk_problem_id FOREIGN KEY (problem_id) REFERENCES public.problem(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problem_tags + ADD CONSTRAINT problem_tags_problemtag_id_72d20571_fk_problem_tag_id FOREIGN KEY (problemtag_id) REFERENCES public.problem_tag(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_badge + ADD CONSTRAINT problemset_badge_problemset_id_6cb6c74f_fk_problemset_id FOREIGN KEY (problemset_id) REFERENCES public.problemset(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset + ADD CONSTRAINT problemset_created_by_id_01b5197f_fk_user_id FOREIGN KEY (created_by_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_problem + ADD CONSTRAINT problemset_problem_problem_id_fff2d686_fk_problem_id FOREIGN KEY (problem_id) REFERENCES public.problem(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_problem + ADD CONSTRAINT problemset_problem_problemset_id_350d17fb_fk_problemset_id FOREIGN KEY (problemset_id) REFERENCES public.problemset(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_progress + ADD CONSTRAINT problemset_progress_problemset_id_20a9632e_fk_problemset_id FOREIGN KEY (problemset_id) REFERENCES public.problemset(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_progress + ADD CONSTRAINT problemset_progress_user_id_c8041a80_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_submission + ADD CONSTRAINT problemset_submission_problem_id_5629b105_fk_problem_id FOREIGN KEY (problem_id) REFERENCES public.problem(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_submission + ADD CONSTRAINT problemset_submission_problemset_id_85290e17_fk_problemset_id FOREIGN KEY (problemset_id) REFERENCES public.problemset(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_submission + ADD CONSTRAINT problemset_submission_submission_id_78e2b807_fk_submission_id FOREIGN KEY (submission_id) REFERENCES public.submission(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.problemset_submission + ADD CONSTRAINT problemset_submission_user_id_915fc9c6_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.reaction + ADD CONSTRAINT reaction_problem_id_a7f3b9f3_fk_problem_id FOREIGN KEY (problem_id) REFERENCES public.problem(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.reaction + ADD CONSTRAINT reaction_user_id_cfa7f469_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.submission + ADD CONSTRAINT submission_contest_id_775716d5_fk_contest_id FOREIGN KEY (contest_id) REFERENCES public.contest(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.submission + ADD CONSTRAINT submission_problem_id_76847b55_fk_problem_id FOREIGN KEY (problem_id) REFERENCES public.problem(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.tutorial + ADD CONSTRAINT tutorial_created_by_id_07973cab_fk_user_id FOREIGN KEY (created_by_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.user_achievement + ADD CONSTRAINT user_achievement_achievement_id_29db600d_fk_achievement_id FOREIGN KEY (achievement_id) REFERENCES public.achievement(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.user_achievement + ADD CONSTRAINT user_achievement_user_id_b8ec7d6a_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.user_badge + ADD CONSTRAINT user_badge_badge_id_92a983e9_fk_problemset_badge_id FOREIGN KEY (badge_id) REFERENCES public.problemset_badge(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.user_badge + ADD CONSTRAINT user_badge_user_id_a286d718_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.user_profile + ADD CONSTRAINT user_profile_user_id_8fdce8e2_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; +--> statement-breakpoint +ALTER TABLE ONLY public.user_stat + ADD CONSTRAINT user_stat_user_id_73337fc0_fk_user_id FOREIGN KEY (user_id) REFERENCES public."user"(id) DEFERRABLE INITIALLY DEFERRED; diff --git a/apps/api/src/db/migrate.ts b/apps/api/src/db/migrate.ts index 92d204a..bf5339e 100644 --- a/apps/api/src/db/migrate.ts +++ b/apps/api/src/db/migrate.ts @@ -77,26 +77,35 @@ export async function runMigrations() { from drizzle.__drizzle_migrations `.catch(() => null) - // 基线缺失。这个库没法靠迁移自举 —— 0000 是 `drizzle-kit pull` 的产物, - // 整个文件被块注释包着,一条可执行语句都没有。结构只能来自 docs/specs/schema.sql - // 或生产 dump,然后手工把 0000 标记成已执行。 + // 没有基线记录,两种情况分开处理:空库直接从 0000 建起来,有表的库要人来确认。 const lastApplied = applied === null ? -1 : Number(applied[0]?.last ?? -1) - if (lastApplied < 0) { + const bootstrapping = lastApplied < 0 + if (bootstrapping) { const rows = await client<{ count: number }[]>` select count(*)::int as count from information_schema.tables where table_schema = 'public' ` const tableCount = rows[0]?.count ?? 0 - console.error( - tableCount > 0 - ? `库里已经有 ${tableCount} 张表,但没有迁移基线记录。\n` + - "直接迁移会从 0000 跑起,而 0000 是 introspect 产物、整份被注释掉,跑不了。\n\n" + - BASELINE_HOWTO - : "这是个空库,迁移没法自举建表(0000 是 introspect 产物,整份被注释掉)。\n" + - "先把结构灌进去:\n\n" + - " psql -d <库> -f docs/specs/schema.sql\n\n" + - BASELINE_HOWTO, - ) - process.exit(3) + + // 有表却没有基线记录 —— 这个库不是 OJ2 从 0000 建起来的(多半是从旧后端接管、 + // 或者从生产 dump 恢复出来的)。0000 是完整建表,直接跑必然撞上已存在的表, + // 而且是整个事务回滚。这种情况只能由人确认之后手工打基线。 + if (tableCount > 0) { + console.error( + `库里已经有 ${tableCount} 张表,但没有迁移基线记录。\n` + + "直接迁移会从 0000 跑起,而 0000 是完整建表,撞上已存在的表会整条回滚。\n\n" + + BASELINE_HOWTO, + ) + process.exit(3) + } + + // 空库:drizzle 的记账表还不存在,先建出来。原来这一步由 drizzle 的 migrate() + // 顺手做掉,换成自己的执行器之后得自己建。 + console.log("空库,从 0000 开始自举。") + await client`create schema if not exists drizzle` + await client` + create table if not exists drizzle.__drizzle_migrations ( + id serial primary key, hash text not null, created_at bigint) + ` } const pending = files.filter((f) => f.folderMillis > lastApplied) @@ -124,7 +133,10 @@ export async function runMigrations() { })) .filter(({ reasons }) => reasons.length > 0) - if (blocked.length > 0 && process.env.OJ2_ALLOW_DESTRUCTIVE !== "1") { + // 自举时不拦:空库上没有数据可丢,0002 那串 DROP ... IF EXISTS 全是空转。 + // 拦下来只会逼着每个新环境都带一次 OJ2_ALLOW_DESTRUCTIVE,把这道闸训练成习惯动作 —— + // 那正是它想避免的事。 + if (blocked.length > 0 && !bootstrapping && process.env.OJ2_ALLOW_DESTRUCTIVE !== "1") { console.error( "待执行的迁移里有破坏性语句,已停下:\n" + blocked.map(({ tag, reasons }) => ` · ${tag}:${reasons.join(" / ")}`).join("\n") + diff --git a/docs/spikes/build-baseline-migration.ts b/docs/spikes/build-baseline-migration.ts new file mode 100644 index 0000000..31837e8 --- /dev/null +++ b/docs/spikes/build-baseline-migration.ts @@ -0,0 +1,105 @@ +#!/usr/bin/env bun +// 把 docs/specs/schema.sql(生产库的 pg_dump --schema-only)转成可执行的基线迁移 +// apps/api/src/db/0000_crazy_gateway.sql。 +// +// 这是**一次性**的转换,产物已经入库。留着它是为了说清 0000 的出处、以及日后万一要 +// 从一份新的生产 dump 重做基线时不用从头想一遍规则。日常改 schema 不要碰这里, +// 走 `bun run db:generate`。 +// +// 跑法(仓库根): +// bun docs/spikes/build-baseline-migration.ts +// +// 产物是确定性的:同一份 schema.sql 跑出来的字节完全一样,改完可以直接 git diff 看。 +import { readFileSync, writeFileSync } from "node:fs" + +const SOURCE = "docs/specs/schema.sql" +const TARGET = "apps/api/src/db/0000_crazy_gateway.sql" + +// drizzle 的语句分隔标记。故意不写成字面量常量之外的形式:`readMigrationFiles` 是纯文本 +// 切分,这个串出现在哪里都会切,所以生成出来的文件的**注释里**绝不能带上它。 +const BREAKPOINT = "--> statement-breakpoint" + +// 判断一条语句是不是 Django 遗留物:只看它引用了哪些**对象**(`public.X` 形式), +// 不看语句里有没有出现这些字样。 +// +// 踩过的坑:一开始扫整条语句里的 `auth_` / `django_` 字样,结果把 `user` 表整个滤掉了 —— +// 它有一列叫 `auth_token`。列名不带 `public.` 前缀,按对象引用来判就不会误伤。 +// +// 覆盖到的形式:CREATE TABLE/SEQUENCE public.X、CREATE INDEX ... ON public.X、 +// ALTER TABLE [ONLY] public.X、ALTER SEQUENCE public.X OWNED BY public.Y.id、 +// 以及外键里的 REFERENCES public.Y —— 对象名全都跟在 `public.` 后面。 +const OBJECT_REF = /public\."?([a-z_]+)"?/g +const DJANGO_PREFIX = ["auth_", "django_"] + +function isDjango(stmt: string) { + return [...stmt.matchAll(OBJECT_REF)].some(([, name]) => DJANGO_PREFIX.some((p) => name.startsWith(p))) +} + +const HEADER = `-- OJ2 的基线迁移:把一个空库建成新后端要的结构。 +-- +-- 这份文件**不是** \`drizzle-kit generate\` 的产物,也不该由它重新生成。原本这里是 +-- \`drizzle-kit pull\` 吐出来的东西,整份被 /* */ 包着、一条可执行语句都没有,于是 +-- 「空库没法靠迁移自举」——新环境、演练、别人接手,都得先手工 psql 灌一遍 schema.sql。 +-- +-- 现在的内容由 \`docs/specs/schema.sql\`(生产库 2026-08-07 的 pg_dump --schema-only) +-- 机械转换而来:去掉 psql 专有指令(\\restrict / SET / set_config)、去掉 7 张 Django +-- 遗留表及其索引与外键,其余原样保留、顺序不动,语句之间插上 drizzle 的分隔标记。 +-- 转换脚本:\`docs/spikes/build-baseline-migration.ts\`。 +-- +-- 注意:那个分隔标记是纯文本切分,\`readMigrationFiles\` 不管它出现在哪里 —— 写进注释里 +-- 一样会把文件切开。所以本文件的注释里不要出现它的字面量(我踩过一次,报错是 +-- 「syntax error at or near "。"」,因为注释被从中间切断了)。 +-- +-- 为什么不含 Django 那 7 张表:\`meta/0000_snapshot.json\` 从来就没有它们(pull 当时用 +-- tablesFilter 滤掉了),所以不建它们才和快照一致。0002 那条 DROP 全带 IF EXISTS, +-- 在新库上是空转,在生产库上才真删——两边跑同一串迁移,落点相同。 +-- +-- **改 schema 不要动这个文件**,走 \`bun run db:generate\` 生成新的迁移。 +-- 生产库早已把 0000 标记成已执行(migrator 只比 created_at、不校验 hash), +-- 所以这份内容的任何改动都不会在生产库上重放。 +` + +const statements: string[] = [] +let buffer: string[] = [] +let droppedDjango = 0 +let droppedNoise = 0 + +for (const line of readFileSync(SOURCE, "utf8").split("\n")) { + const trimmed = line.trim() + + // 语句之外的行:空行、注释、psql 元命令(\restrict)直接跳过; + // SET / set_config 是 pg_dump 给自己用的会话设置,迁移里不需要。 + if (buffer.length === 0) { + if (trimmed === "" || trimmed.startsWith("--") || trimmed.startsWith("\\")) continue + if (trimmed.startsWith("SET ") || trimmed.startsWith("SELECT pg_catalog.set_config")) { + droppedNoise++ + continue + } + } + + buffer.push(line) + + // 按行尾分号断句。schema.sql 里句中出现分号的只有注释行,而注释行进不到这儿。 + if (!trimmed.endsWith(";")) continue + + const stmt = buffer.join("\n").trim() + buffer = [] + + if (isDjango(stmt)) { + droppedDjango++ + continue + } + if (/^ALTER TABLE .* OWNER TO /.test(stmt) || stmt.startsWith("COMMENT ON")) { + droppedNoise++ + continue + } + statements.push(stmt) +} + +if (buffer.length > 0) throw new Error(`有没闭合的语句:${buffer[0]}`) + +const body = statements.map((s) => s.replace(/;+$/, "")).join(`;\n${BREAKPOINT}\n`) +writeFileSync(TARGET, `${HEADER}\n${body};\n`) + +console.log(`保留 ${statements.length} 条语句 | 滤掉 Django 相关 ${droppedDjango} 条、噪音 ${droppedNoise} 条`) +console.log(`已写入 ${TARGET}`)