CREATE SCHEMA "groceries"; --> statement-breakpoint CREATE TABLE "groceries"."prices" ( "id" serial PRIMARY KEY, "product_id" integer NOT NULL, "store_id" integer NOT NULL, "price_cents" integer NOT NULL, "unit_price_cents" integer, "promo" boolean DEFAULT false NOT NULL, "observed_at" timestamp with time zone DEFAULT now() NOT NULL, "source" text DEFAULT 'manual' NOT NULL, "store_product_id" text, "url" text, "notes" text, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "prices_price_check" CHECK ("price_cents" >= 0), CONSTRAINT "prices_unit_price_check" CHECK ("unit_price_cents" IS NULL OR "unit_price_cents" >= 0) ); --> statement-breakpoint CREATE TABLE "groceries"."products" ( "id" serial PRIMARY KEY, "name" text NOT NULL, "brand" text, "category" text, "size" text, "unit_amount" numeric(10,3), "unit" text, "favorite" boolean DEFAULT false NOT NULL, "notes" text, "archived_at" timestamp with time zone, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "products_name_check" CHECK (length(btrim("name")) > 0), CONSTRAINT "products_unit_check" CHECK ("unit" IS NULL OR "unit" IN ('piece','kg','l')), CONSTRAINT "products_unit_amount_check" CHECK ("unit_amount" IS NULL OR "unit_amount" > 0) ); --> statement-breakpoint CREATE TABLE "groceries"."stores" ( "id" serial PRIMARY KEY, "slug" text NOT NULL, "name" text NOT NULL, "website" text, "color" text, "sort_order" integer DEFAULT 0 NOT NULL, "archived_at" timestamp with time zone, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "stores_slug_check" CHECK ("slug" ~ '^[a-z0-9][a-z0-9-]{0,62}$'), CONSTRAINT "stores_name_check" CHECK (length(btrim("name")) > 0) ); --> statement-breakpoint CREATE INDEX "prices_product_store_idx" ON "groceries"."prices" ("product_id","store_id","observed_at");--> statement-breakpoint CREATE INDEX "prices_store_idx" ON "groceries"."prices" ("store_id");--> statement-breakpoint CREATE UNIQUE INDEX "products_name_brand_key" ON "groceries"."products" (lower("name"),lower(coalesce("brand", '')));--> statement-breakpoint CREATE INDEX "products_category_idx" ON "groceries"."products" ("category");--> statement-breakpoint CREATE UNIQUE INDEX "stores_slug_key" ON "groceries"."stores" ("slug");--> statement-breakpoint ALTER TABLE "groceries"."prices" ADD CONSTRAINT "prices_product_id_products_id_fkey" FOREIGN KEY ("product_id") REFERENCES "groceries"."products"("id") ON DELETE CASCADE;--> statement-breakpoint ALTER TABLE "groceries"."prices" ADD CONSTRAINT "prices_store_id_stores_id_fkey" FOREIGN KEY ("store_id") REFERENCES "groceries"."stores"("id") ON DELETE CASCADE; --> statement-breakpoint -- Hand-written below this line (not generated by drizzle-kit). -- updated_at triggers, same as every other core table. DO $$ DECLARE t text; BEGIN IF to_regprocedure('meta.touch_updated_at()') IS NOT NULL THEN FOREACH t IN ARRAY ARRAY['stores','products','prices'] LOOP EXECUTE format('CREATE TRIGGER touch_updated_at BEFORE UPDATE ON groceries.%I FOR EACH ROW EXECUTE FUNCTION meta.touch_updated_at()', t); END LOOP; END IF; END $$; --> statement-breakpoint -- Private like tasks/health: app role only, no personal_ro. Grants only run on prod, -- where migrations run as `personal`. REVOKE ALL ON SCHEMA groceries FROM PUBLIC; --> statement-breakpoint DO $$ BEGIN IF current_user = 'personal' AND EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_app') THEN GRANT USAGE ON SCHEMA groceries TO personal_app; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA groceries TO personal_app; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA groceries TO personal_app; ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA groceries GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO personal_app; ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA groceries GRANT USAGE, SELECT ON SEQUENCES TO personal_app; END IF; END $$; --> statement-breakpoint INSERT INTO groceries.stores (slug, name, website, color, sort_order) VALUES ('ah', 'Albert Heijn', 'https://www.ah.nl', '#00a0e2', 10), ('jumbo', 'Jumbo', 'https://www.jumbo.com', '#eeb717', 20), ('lidl', 'Lidl', 'https://www.lidl.nl', '#0050aa', 30), ('plus', 'Plus', 'https://www.plus.nl', '#e2001a', 40) ON CONFLICT DO NOTHING; --> statement-breakpoint -- Registered in meta.tables so the /lists database browser shows them (kind 'core'). DO $$ BEGIN IF to_regclass('meta.tables') IS NOT NULL THEN INSERT INTO meta.tables (schema_name, table_name, slug, display_name, kind, editable, has_done, sort_order) VALUES ('groceries','products','groceries-products','Grocery products','core',true,false,1100), ('groceries','prices','groceries-prices','Grocery prices','core',true,false,1100), ('groceries','stores','groceries-stores','Grocery stores','core',true,false,1100) ON CONFLICT DO NOTHING; -- deploy-remote.sh runs meta.sync() as the app role after migrating. IF has_function_privilege('meta.sync(integer)', 'EXECUTE') THEN PERFORM meta.sync(); END IF; END IF; END $$;