-- W2-1 게임잼 엔티티/라이프사이클. 멱등. db/apply-local-ddl.sh 로 실행 DB 비파괴 적용. -- games 변경 없음(연결은 jam_entries 가 보유). 추가만, 파괴 없음. -- =========================================================================== -- 1) jams (게임잼 회차. 회차 독립 = 다중 인스턴스) -- =========================================================================== CREATE SEQUENCE IF NOT EXISTS "jams_id_seq"; CREATE TABLE IF NOT EXISTS "jams" ( "id" bigint DEFAULT nextval('jams_id_seq'::regclass) NOT NULL, "slug" character varying(80) NOT NULL, "title" character varying(200) NOT NULL, "description" text, "status" character varying(20) DEFAULT 'RECRUIT' NOT NULL, "recruit_start_at" timestamp with time zone, "dev_start_at" timestamp with time zone, "eval_start_at" timestamp with time zone, "eval_end_at" timestamp with time zone, "discord_url" character varying(500), "prize_info" text, "sponsor_info" text, "is_visible" boolean DEFAULT true NOT NULL, "created_by" bigint, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, "is_delete" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id") ); ALTER SEQUENCE "jams_id_seq" OWNED BY "jams"."id"; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jams_status_check') THEN ALTER TABLE "jams" ADD CONSTRAINT "jams_status_check" CHECK ("status" IN ('RECRUIT', 'DEV', 'EVAL', 'CLOSED')); END IF; END $$; CREATE UNIQUE INDEX IF NOT EXISTS "ux_jams_slug_active" ON "jams" ("slug") WHERE "is_delete" IS NOT TRUE; CREATE INDEX IF NOT EXISTS "idx_jams_visible_keyset" ON "jams" ("is_visible", "is_delete", "created_at" DESC, "id" DESC); -- =========================================================================== -- 2) jam_teams -- =========================================================================== CREATE SEQUENCE IF NOT EXISTS "jam_teams_id_seq"; CREATE TABLE IF NOT EXISTS "jam_teams" ( "id" bigint DEFAULT nextval('jam_teams_id_seq'::regclass) NOT NULL, "jam_id" bigint NOT NULL, "name" character varying(120) NOT NULL, "owner_user_id" bigint NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "is_delete" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id") ); ALTER SEQUENCE "jam_teams_id_seq" OWNED BY "jam_teams"."id"; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_teams_jam_id_fkey') THEN ALTER TABLE "jam_teams" ADD CONSTRAINT "jam_teams_jam_id_fkey" FOREIGN KEY ("jam_id") REFERENCES "jams" ("id"); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_teams_owner_fkey') THEN ALTER TABLE "jam_teams" ADD CONSTRAINT "jam_teams_owner_fkey" FOREIGN KEY ("owner_user_id") REFERENCES "users" ("id"); END IF; END $$; CREATE INDEX IF NOT EXISTS "idx_jam_teams_jam" ON "jam_teams" ("jam_id"); -- =========================================================================== -- 3) jam_team_members -- =========================================================================== CREATE SEQUENCE IF NOT EXISTS "jam_team_members_id_seq"; CREATE TABLE IF NOT EXISTS "jam_team_members" ( "id" bigint DEFAULT nextval('jam_team_members_id_seq'::regclass) NOT NULL, "jam_team_id" bigint NOT NULL, "user_id" bigint NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, PRIMARY KEY ("id") ); ALTER SEQUENCE "jam_team_members_id_seq" OWNED BY "jam_team_members"."id"; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_team_members_team_fkey') THEN ALTER TABLE "jam_team_members" ADD CONSTRAINT "jam_team_members_team_fkey" FOREIGN KEY ("jam_team_id") REFERENCES "jam_teams" ("id"); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_team_members_user_fkey') THEN ALTER TABLE "jam_team_members" ADD CONSTRAINT "jam_team_members_user_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id"); END IF; END $$; CREATE UNIQUE INDEX IF NOT EXISTS "ux_jam_team_members_team_user" ON "jam_team_members" ("jam_team_id", "user_id"); -- =========================================================================== -- 4) jam_entries (출품작 = 잼-게임 연결 조인. 평가 단위 = (jam_id, game_id) 활성 자연키) -- =========================================================================== CREATE SEQUENCE IF NOT EXISTS "jam_entries_id_seq"; CREATE TABLE IF NOT EXISTS "jam_entries" ( "id" bigint DEFAULT nextval('jam_entries_id_seq'::regclass) NOT NULL, "jam_id" bigint NOT NULL, "game_id" bigint NOT NULL, "entrant_type" character varying(10) NOT NULL, "entrant_user_id" bigint, "jam_team_id" bigint, "submitted_at" timestamp with time zone DEFAULT now() NOT NULL, "is_delete" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id") ); ALTER SEQUENCE "jam_entries_id_seq" OWNED BY "jam_entries"."id"; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_entries_jam_fkey') THEN ALTER TABLE "jam_entries" ADD CONSTRAINT "jam_entries_jam_fkey" FOREIGN KEY ("jam_id") REFERENCES "jams" ("id"); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_entries_game_fkey') THEN ALTER TABLE "jam_entries" ADD CONSTRAINT "jam_entries_game_fkey" FOREIGN KEY ("game_id") REFERENCES "games" ("id"); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_entries_team_fkey') THEN ALTER TABLE "jam_entries" ADD CONSTRAINT "jam_entries_team_fkey" FOREIGN KEY ("jam_team_id") REFERENCES "jam_teams" ("id"); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_entries_user_fkey') THEN ALTER TABLE "jam_entries" ADD CONSTRAINT "jam_entries_user_fkey" FOREIGN KEY ("entrant_user_id") REFERENCES "users" ("id"); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_entries_entrant_type_check') THEN ALTER TABLE "jam_entries" ADD CONSTRAINT "jam_entries_entrant_type_check" CHECK ("entrant_type" IN ('USER', 'TEAM')); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_entries_entrant_xor_check') THEN ALTER TABLE "jam_entries" ADD CONSTRAINT "jam_entries_entrant_xor_check" CHECK ( ("entrant_type" = 'USER' AND "entrant_user_id" IS NOT NULL AND "jam_team_id" IS NULL) OR ("entrant_type" = 'TEAM' AND "jam_team_id" IS NOT NULL AND "entrant_user_id" IS NULL) ); END IF; END $$; CREATE UNIQUE INDEX IF NOT EXISTS "ux_jam_entries_jam_game_active" ON "jam_entries" ("jam_id", "game_id") WHERE "is_delete" IS NOT TRUE; CREATE INDEX IF NOT EXISTS "idx_jam_entries_jam" ON "jam_entries" ("jam_id"); CREATE INDEX IF NOT EXISTS "idx_jam_entries_game" ON "jam_entries" ("game_id"); -- =========================================================================== -- 5) jam_status_log -- =========================================================================== CREATE SEQUENCE IF NOT EXISTS "jam_status_log_id_seq"; CREATE TABLE IF NOT EXISTS "jam_status_log" ( "id" bigint DEFAULT nextval('jam_status_log_id_seq'::regclass) NOT NULL, "jam_id" bigint NOT NULL, "from_status" character varying(20), "to_status" character varying(20) NOT NULL, "actor_id" bigint, "transition_type" character varying(10) DEFAULT 'MANUAL' NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, PRIMARY KEY ("id") ); ALTER SEQUENCE "jam_status_log_id_seq" OWNED BY "jam_status_log"."id"; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_status_log_jam_fkey') THEN ALTER TABLE "jam_status_log" ADD CONSTRAINT "jam_status_log_jam_fkey" FOREIGN KEY ("jam_id") REFERENCES "jams" ("id"); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_status_log_to_status_check') THEN ALTER TABLE "jam_status_log" ADD CONSTRAINT "jam_status_log_to_status_check" CHECK ("to_status" IN ('RECRUIT', 'DEV', 'EVAL', 'CLOSED')); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'jam_status_log_transition_type_check') THEN ALTER TABLE "jam_status_log" ADD CONSTRAINT "jam_status_log_transition_type_check" CHECK ("transition_type" IN ('MANUAL', 'AUTO')); END IF; END $$; CREATE INDEX IF NOT EXISTS "idx_jam_status_log_jam" ON "jam_status_log" ("jam_id", "created_at" DESC);