szabadpalya/api/internal/store/migrations/0004_snake_case.sql

194 lines
11 KiB
SQL

-- Every table, column, enum type, index and constraint goes from quoted
-- camelCase to unquoted snake_case.
--
-- The camelCase came from Prisma, which generated the original schema. 0001
-- kept it deliberately ("the cost of keeping them is zero"), and that turned
-- out to be wrong: the cost is that every identifier in every query must stay
-- quoted forever, and any unquoted one folds to lowercase and fails at run
-- time instead of at compile time. This pays that off once.
--
-- Tables are pluralised. That is not only taste: "User" could not become
-- "user" because USER is a reserved word in Postgres, and naming ten tables by
-- one rule and the eleventh by another is worse than renaming all eleven.
--
-- Enum VALUES are left alone. They are already uppercase ('DONATION',
-- 'AKTIV') and they are data, not identifiers — the Go code compares against
-- them as strings and the API sends them over the wire.
--
-- RENAME is a catalogue update: no table is rewritten and no row is touched,
-- so this is instant whatever the database holds. The whole file runs in one
-- transaction (see store.Migrate), so it either lands completely or not at all.
-- ------------------------------------------------------------- enum types ---
ALTER TYPE "TierType" RENAME TO tier_type;
ALTER TYPE "CampaignStatus" RENAME TO campaign_status;
ALTER TYPE "PledgeStatus" RENAME TO pledge_status;
ALTER TYPE "PhysicalTierPolicy" RENAME TO physical_tier_policy;
-- ----------------------------------------------------------------- tables ---
ALTER TABLE "Owner" RENAME TO owners;
ALTER TABLE "User" RENAME TO users;
ALTER TABLE "Session" RENAME TO sessions;
ALTER TABLE "Verification" RENAME TO verifications;
ALTER TABLE "Campaign" RENAME TO campaigns;
ALTER TABLE "StorySection" RENAME TO story_sections;
ALTER TABLE "RewardTier" RENAME TO reward_tiers;
ALTER TABLE "Pledge" RENAME TO pledges;
ALTER TABLE "ShippingAddress" RENAME TO shipping_addresses;
ALTER TABLE "CampaignUpdate" RENAME TO campaign_updates;
ALTER TABLE "Comment" RENAME TO comments;
-- ---------------------------------------------------------------- columns ---
-- Only the mixed-case ones: id, slug, title and the rest are already lowercase
-- and renaming a column to its own name is an error.
ALTER TABLE owners RENAME COLUMN "stripeAccountId" TO stripe_account_id;
ALTER TABLE owners RENAME COLUMN "createdAt" TO created_at;
ALTER TABLE owners RENAME COLUMN "avatarUrl" TO avatar_url;
ALTER TABLE users RENAME COLUMN "emailVerified" TO email_verified;
ALTER TABLE users RENAME COLUMN "isAdmin" TO is_admin;
ALTER TABLE users RENAME COLUMN "createdAt" TO created_at;
ALTER TABLE users RENAME COLUMN "updatedAt" TO updated_at;
ALTER TABLE sessions RENAME COLUMN "expiresAt" TO expires_at;
ALTER TABLE sessions RENAME COLUMN "ipAddress" TO ip_address;
ALTER TABLE sessions RENAME COLUMN "userAgent" TO user_agent;
ALTER TABLE sessions RENAME COLUMN "userId" TO user_id;
ALTER TABLE sessions RENAME COLUMN "createdAt" TO created_at;
ALTER TABLE sessions RENAME COLUMN "updatedAt" TO updated_at;
ALTER TABLE verifications RENAME COLUMN "expiresAt" TO expires_at;
ALTER TABLE verifications RENAME COLUMN "consumedAt" TO consumed_at;
ALTER TABLE verifications RENAME COLUMN "createdAt" TO created_at;
ALTER TABLE verifications RENAME COLUMN "updatedAt" TO updated_at;
ALTER TABLE campaigns RENAME COLUMN "goalAmount" TO goal_amount;
ALTER TABLE campaigns RENAME COLUMN "startsAt" TO starts_at;
ALTER TABLE campaigns RENAME COLUMN "endsAt" TO ends_at;
ALTER TABLE campaigns RENAME COLUMN "physicalTierPolicy" TO physical_tier_policy;
ALTER TABLE campaigns RENAME COLUMN "ownerId" TO owner_id;
ALTER TABLE campaigns RENAME COLUMN "createdAt" TO created_at;
ALTER TABLE campaigns RENAME COLUMN "updatedAt" TO updated_at;
ALTER TABLE story_sections RENAME COLUMN "campaignId" TO campaign_id;
ALTER TABLE reward_tiers RENAME COLUMN "campaignId" TO campaign_id;
ALTER TABLE reward_tiers RENAME COLUMN "listPrice" TO list_price;
ALTER TABLE reward_tiers RENAME COLUMN "inventoryLimit" TO inventory_limit;
ALTER TABLE reward_tiers RENAME COLUMN "estimatedDelivery" TO estimated_delivery;
ALTER TABLE pledges RENAME COLUMN "campaignId" TO campaign_id;
ALTER TABLE pledges RENAME COLUMN "tierId" TO tier_id;
ALTER TABLE pledges RENAME COLUMN "userId" TO user_id;
ALTER TABLE pledges RENAME COLUMN "backerEmail" TO backer_email;
ALTER TABLE pledges RENAME COLUMN "stripePaymentIntentId" TO stripe_payment_intent_id;
ALTER TABLE pledges RENAME COLUMN "createdAt" TO created_at;
ALTER TABLE pledges RENAME COLUMN "capturedAt" TO captured_at;
ALTER TABLE shipping_addresses RENAME COLUMN "pledgeId" TO pledge_id;
ALTER TABLE shipping_addresses RENAME COLUMN "postalCode" TO postal_code;
ALTER TABLE campaign_updates RENAME COLUMN "campaignId" TO campaign_id;
ALTER TABLE campaign_updates RENAME COLUMN "publishedAt" TO published_at;
ALTER TABLE comments RENAME COLUMN "campaignId" TO campaign_id;
ALTER TABLE comments RENAME COLUMN "authorName" TO author_name;
ALTER TABLE comments RENAME COLUMN "createdAt" TO created_at;
-- ---------------------------------------------------------------- indexes ---
-- campaigns_slug_key is load-bearing beyond cosmetics: store.SaveCampaign
-- matches on the constraint name to turn a 23505 into ErrSlugTaken.
ALTER INDEX "Owner_slug_key" RENAME TO owners_slug_key;
ALTER INDEX "Owner_stripeAccountId_key" RENAME TO owners_stripe_account_id_key;
ALTER INDEX "User_email_key" RENAME TO users_email_key;
ALTER INDEX "Session_token_key" RENAME TO sessions_token_key;
ALTER INDEX "Session_userId_idx" RENAME TO sessions_user_id_idx;
ALTER INDEX "Verification_identifier_idx" RENAME TO verifications_identifier_idx;
ALTER INDEX "Verification_value_key" RENAME TO verifications_value_key;
ALTER INDEX "Campaign_slug_key" RENAME TO campaigns_slug_key;
ALTER INDEX "Campaign_status_endsAt_idx" RENAME TO campaigns_status_ends_at_idx;
ALTER INDEX "StorySection_campaignId_position_key" RENAME TO story_sections_campaign_id_position_key;
ALTER INDEX "RewardTier_campaignId_position_key" RENAME TO reward_tiers_campaign_id_position_key;
ALTER INDEX "Pledge_stripePaymentIntentId_key" RENAME TO pledges_stripe_payment_intent_id_key;
ALTER INDEX "Pledge_campaignId_status_idx" RENAME TO pledges_campaign_id_status_idx;
ALTER INDEX "Pledge_tierId_idx" RENAME TO pledges_tier_id_idx;
ALTER INDEX "ShippingAddress_pledgeId_key" RENAME TO shipping_addresses_pledge_id_key;
-- ------------------------------------------------------------ constraints ---
-- Primary and foreign keys are named by Postgres from the table and column, so
-- these names are written down nowhere — not in 0001, not in the Go code. Two
-- of them still say Organization: 0003 renamed the table and the column but
-- not the constraints that were named after them, despite its comment claiming
-- "nothing in the schema still says Organization".
ALTER TABLE owners RENAME CONSTRAINT "Organization_pkey" TO owners_pkey;
ALTER TABLE users RENAME CONSTRAINT "User_pkey" TO users_pkey;
ALTER TABLE sessions RENAME CONSTRAINT "Session_pkey" TO sessions_pkey;
ALTER TABLE verifications RENAME CONSTRAINT "Verification_pkey" TO verifications_pkey;
ALTER TABLE campaigns RENAME CONSTRAINT "Campaign_pkey" TO campaigns_pkey;
ALTER TABLE story_sections RENAME CONSTRAINT "StorySection_pkey" TO story_sections_pkey;
ALTER TABLE reward_tiers RENAME CONSTRAINT "RewardTier_pkey" TO reward_tiers_pkey;
ALTER TABLE pledges RENAME CONSTRAINT "Pledge_pkey" TO pledges_pkey;
ALTER TABLE shipping_addresses RENAME CONSTRAINT "ShippingAddress_pkey" TO shipping_addresses_pkey;
ALTER TABLE campaign_updates RENAME CONSTRAINT "CampaignUpdate_pkey" TO campaign_updates_pkey;
ALTER TABLE comments RENAME CONSTRAINT "Comment_pkey" TO comments_pkey;
ALTER TABLE sessions RENAME CONSTRAINT "Session_userId_fkey" TO sessions_user_id_fkey;
ALTER TABLE campaigns RENAME CONSTRAINT "Campaign_organizationId_fkey" TO campaigns_owner_id_fkey;
ALTER TABLE story_sections RENAME CONSTRAINT "StorySection_campaignId_fkey" TO story_sections_campaign_id_fkey;
ALTER TABLE reward_tiers RENAME CONSTRAINT "RewardTier_campaignId_fkey" TO reward_tiers_campaign_id_fkey;
ALTER TABLE pledges RENAME CONSTRAINT "Pledge_campaignId_fkey" TO pledges_campaign_id_fkey;
ALTER TABLE pledges RENAME CONSTRAINT "Pledge_tierId_fkey" TO pledges_tier_id_fkey;
ALTER TABLE pledges RENAME CONSTRAINT "Pledge_userId_fkey" TO pledges_user_id_fkey;
ALTER TABLE shipping_addresses RENAME CONSTRAINT "ShippingAddress_pledgeId_fkey" TO shipping_addresses_pledge_id_fkey;
ALTER TABLE campaign_updates RENAME CONSTRAINT "CampaignUpdate_campaignId_fkey" TO campaign_updates_campaign_id_fkey;
ALTER TABLE comments RENAME CONSTRAINT "Comment_campaignId_fkey" TO comments_campaign_id_fkey;
-- ------------------------------------------------------------------ guard ---
-- Everything above was written by reading 0001 and 0003. Anything those files
-- do not mention — a constraint added by hand, a name Postgres generated
-- differently than expected — would survive silently and only surface as a
-- quoted identifier someone has to keep quoting. This looks in the catalogue
-- instead of in the migrations and fails the transaction if it finds one.
--
-- lower() rather than a [A-Z] regex: character classes are collation-dependent
-- and this is not.
DO $$
DECLARE
offenders text;
BEGIN
SELECT string_agg(name, ', ' ORDER BY name) INTO offenders FROM (
SELECT c.relname AS name
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relname <> lower(c.relname)
UNION ALL
-- Tables only. An index carries its own copy of the column names it
-- was built on, and a column rename does not update them: after this
-- migration sessions_user_id_idx still has an attribute called
-- "userId". Postgres offers no way to rename it and no SQL can refer
-- to it, so it is not an identifier this migration is about.
SELECT c.relname || '.' || a.attname
FROM pg_attribute a
JOIN pg_class c ON c.oid = a.attrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind IN ('r', 'p', 'v', 'm')
AND a.attnum > 0 AND NOT a.attisdropped
AND a.attname <> lower(a.attname)
UNION ALL
SELECT t.typname
FROM pg_type t
JOIN pg_namespace n ON n.oid = t.typnamespace
WHERE n.nspname = 'public' AND t.typname <> lower(t.typname)
UNION ALL
SELECT con.conname
FROM pg_constraint con
JOIN pg_namespace n ON n.oid = con.connamespace
WHERE n.nspname = 'public' AND con.conname <> lower(con.conname)
) s;
IF offenders IS NOT NULL THEN
RAISE EXCEPTION 'snake_case migration missed: %', offenders;
END IF;
END $$;