CREATE TABLE "games"."osu_goals" ( "id" serial PRIMARY KEY, "name" text NOT NULL, "goal" text, "mods" text[] DEFAULT '{}'::text[] NOT NULL, "notes" text, "sort_order" integer DEFAULT 0 NOT NULL, "list_item_id" integer, "match_status" text DEFAULT 'unmatched' NOT NULL, "match_note" text, "candidates" jsonb DEFAULT '[]' NOT NULL, "beatmapset_id" integer, "beatmap_id" integer, "mode" text, "artist" text, "title" text, "artist_unicode" text, "title_unicode" text, "mapper" text, "mapper_id" integer, "version" text, "diff_mapper_id" integer, "status" text, "ranked_at" timestamp with time zone, "submitted_at" timestamp with time zone, "source" text, "genre" text, "language" text, "tags" text[] DEFAULT '{}'::text[] NOT NULL, "user_tags" text[] DEFAULT '{}'::text[] NOT NULL, "bpm" double precision, "bpm_min" double precision, "bpm_max" double precision, "length_seconds" integer, "drain_seconds" integer, "ar" double precision, "od" double precision, "cs" double precision, "hp" double precision, "max_combo" integer, "circles" integer, "sliders" integer, "spinners" integer, "stars" double precision, "stars_dt" double precision, "stars_hr" double precision, "stars_hd" double precision, "stars_hddt" double precision, "stars_hdhr" double precision, "attributes" jsonb, "longest_stream" integer, "stream_notes" integer, "play_count" integer, "pass_count" integer, "favourite_count" integer, "map_play_count" integer, "cover_url" text, "card_url" text, "list_url" text, "preview_url" text, "focus" text[] DEFAULT '{}'::text[] NOT NULL, "metadata_at" timestamp with time zone, "my_play_count" integer, "best_score_id" bigint, "best_score" bigint, "best_accuracy" double precision, "best_mods" text[] DEFAULT '{}'::text[] NOT NULL, "best_rank" text, "best_pp" double precision, "best_combo" integer, "best_misses" integer, "best_fc" boolean, "best_at" timestamp with time zone, "passed" boolean DEFAULT false NOT NULL, "fc" boolean DEFAULT false NOT NULL, "achieved_at" timestamp with time zone, "scores" jsonb DEFAULT '[]' NOT NULL, "scores_at" timestamp with time zone, "last_error" text, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "osu_goals_goal_check" CHECK ("goal" IS NULL OR "goal" IN ('pass','fc')), CONSTRAINT "osu_goals_match_check" CHECK ("match_status" IN ('matched','ambiguous','unmatched') AND ("match_status" <> 'matched' OR "beatmap_id" IS NOT NULL)), CONSTRAINT "osu_goals_name_check" CHECK (length(btrim("name")) > 0) ); --> statement-breakpoint CREATE INDEX "osu_goals_beatmap_idx" ON "games"."osu_goals" ("beatmap_id");--> statement-breakpoint -- Hand-written: grants (same pattern as the other app tables) and the updated_at trigger. -- Rows come from the retired list lists.osu_maps (imported once, then refreshed by the app). DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_app') THEN GRANT SELECT, INSERT, UPDATE, DELETE ON games.osu_goals TO personal_app; GRANT USAGE, SELECT ON SEQUENCE games.osu_goals_id_seq TO personal_app; END IF; IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_ro') THEN GRANT SELECT ON games.osu_goals TO personal_ro; END IF; IF to_regprocedure('meta.touch_updated_at()') IS NOT NULL THEN CREATE TRIGGER touch_updated_at BEFORE UPDATE ON games.osu_goals FOR EACH ROW EXECUTE FUNCTION meta.touch_updated_at(); END IF; END $$;