Files
personal-website/scripts/foundation-migration.sql
Stan Runge d0c7b6548b
Some checks failed
Test and deploy staging / staging (push) Has been cancelled
beeeeg update
2026-09-23 04:16:50 +00:00

94 lines
4.5 KiB
PL/PgSQL

-- Apply after auth-catalog-migration.sql and before deploying the foundation release.
-- Additive domain-schema changes; existing records and numeric IDs are retained.
ALTER TABLE blog_articles ADD COLUMN IF NOT EXISTS tags text[] NOT NULL DEFAULT '{}'::text[];
ALTER TABLE lists ADD COLUMN IF NOT EXISTS slug text;
UPDATE lists SET slug = COALESCE(NULLIF(trim(both '-' from regexp_replace(lower(name), '[^a-z0-9]+', '-', 'g')), ''), 'list') || '-' || id
WHERE slug IS NULL;
ALTER TABLE lists ALTER COLUMN slug SET NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS lists_slug_unique ON lists (slug);
ALTER TABLE lists ADD COLUMN IF NOT EXISTS is_progress boolean NOT NULL DEFAULT false;
ALTER TABLE list_items ADD COLUMN IF NOT EXISTS picture_url text;
ALTER TABLE list_items ADD COLUMN IF NOT EXISTS description text;
CREATE TABLE IF NOT EXISTS list_item_completions (
id serial PRIMARY KEY,
item_id integer NOT NULL REFERENCES list_items(id) ON DELETE CASCADE,
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT list_item_completions_item_id_user_id_unique UNIQUE (item_id, user_id)
);
CREATE INDEX IF NOT EXISTS list_item_completions_user_id_idx ON list_item_completions(user_id);
-- Join/setting records gain the same numeric identity as other site-owned rows.
ALTER TABLE tasks_to_topics ADD COLUMN IF NOT EXISTS id serial;
DO $$ BEGIN
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'tasks_to_topics'::regclass AND conname = 'tasks_to_topics_pkey'
AND pg_get_constraintdef(oid) NOT LIKE 'PRIMARY KEY (id)') THEN
ALTER TABLE tasks_to_topics DROP CONSTRAINT tasks_to_topics_pkey;
END IF;
END $$;
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'tasks_to_topics'::regclass AND contype = 'p') THEN
ALTER TABLE tasks_to_topics ADD CONSTRAINT tasks_to_topics_pkey PRIMARY KEY (id);
END IF;
END $$;
CREATE UNIQUE INDEX IF NOT EXISTS tasks_to_topics_task_id_topic_id_unique ON tasks_to_topics(task_id, topic_id);
ALTER TABLE catch_em_all_settings ADD COLUMN IF NOT EXISTS id serial;
DO $$ BEGIN
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'catch_em_all_settings'::regclass AND conname = 'catch_em_all_settings_pkey'
AND pg_get_constraintdef(oid) NOT LIKE 'PRIMARY KEY (id)') THEN
ALTER TABLE catch_em_all_settings DROP CONSTRAINT catch_em_all_settings_pkey;
END IF;
END $$;
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'catch_em_all_settings'::regclass AND contype = 'p') THEN
ALTER TABLE catch_em_all_settings ADD CONSTRAINT catch_em_all_settings_pkey PRIMARY KEY (id);
END IF;
END $$;
CREATE UNIQUE INDEX IF NOT EXISTS catch_em_all_settings_user_id_unique ON catch_em_all_settings(user_id);
-- Every site-owned table records creation and latest update. Domain-specific
-- event times (submitted_at, seen_at, uploaded_at) remain untouched.
DO $$
DECLARE table_name text;
BEGIN
FOREACH table_name IN ARRAY ARRAY[
'blog_articles', 'lists', 'list_items', 'list_item_completions',
'animals', 'sightings', 'photos', 'tasks', 'topics', 'tasks_to_topics',
'pokemon', 'games', 'pokemon_game_entries', 'user_entries', 'user_games',
'catch_em_all_settings'
] LOOP
EXECUTE format('ALTER TABLE %I ADD COLUMN IF NOT EXISTS created_at timestamptz NOT NULL DEFAULT now()', table_name);
EXECUTE format('ALTER TABLE %I ADD COLUMN IF NOT EXISTS updated_at timestamptz NOT NULL DEFAULT now()', table_name);
END LOOP;
END $$;
-- Preserve the best available creation time for older Animaldex rows.
UPDATE animals SET created_at = submitted_at WHERE created_at > submitted_at;
UPDATE sightings SET created_at = submitted_at WHERE created_at > submitted_at;
UPDATE photos SET created_at = uploaded_at WHERE created_at > uploaded_at;
CREATE OR REPLACE FUNCTION site_touch_updated_at() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END $$;
DO $$
DECLARE table_name text;
BEGIN
FOREACH table_name IN ARRAY ARRAY[
'blog_articles', 'lists', 'list_items', 'list_item_completions',
'animals', 'sightings', 'photos', 'tasks', 'topics', 'tasks_to_topics',
'pokemon', 'games', 'pokemon_game_entries', 'user_entries', 'user_games',
'catch_em_all_settings'
] LOOP
EXECUTE format('DROP TRIGGER IF EXISTS site_touch_updated_at ON %I', table_name);
EXECUTE format('CREATE TRIGGER site_touch_updated_at BEFORE UPDATE ON %I FOR EACH ROW EXECUTE FUNCTION site_touch_updated_at()', table_name);
END LOOP;
END $$;