All checks were successful
Deploy production / deploy (push) Successful in 2m40s
113 lines
5.2 KiB
SQL
113 lines
5.2 KiB
SQL
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 $$;
|
|
|