Execute Database/BDD_struct.sql in the Supabase SQL Editor:
Navigate to SQL Editor in Supabase dashboard
Click New query
Copy and paste the contents of BDD_struct.sql
Click Run or press Cmd/Ctrl + Enter
This creates:
All tables with proper constraints
Foreign key relationships
Indexes for performance
Check constraints for data integrity
Run this script only once during initial setup. For subsequent changes, use migration files.
2
Apply Row Level Security (RLS)
Execute Database/RLS.sql to enable security policies:
-- Example RLS policy structureALTER TABLE public.items ENABLE ROW LEVEL SECURITY;-- Public read access for active itemsCREATE POLICY "items: public read active" ON public.items FOR SELECT TO anon, authenticated USING (status = 'active');-- Admin full accessCREATE POLICY "items: admin full access" ON public.items FOR ALL TO authenticated USING (public.is_admin(auth.uid()));
Before running migrations, verify which have been applied:
-- Query to check table structureSELECT column_name, data_type FROM information_schema.columnsWHERE table_name = 'items';-- Check if a column existsSELECT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'item_images' AND column_name = 'position');
2
Execute Migration Files
Run migrations in chronological order (oldest first):
-- Example: Add position to item_images-- From: 20250105_add_position_to_item_images.sqlALTER TABLE public.item_imagesADD COLUMN IF NOT EXISTS position INTEGER NOT NULL DEFAULT 0;CREATE INDEX IF NOT EXISTS idx_item_images_position ON public.item_images(item_id, position ASC);
-- Add position column for image orderingALTER TABLE public.item_imagesADD COLUMN IF NOT EXISTS position INTEGER NOT NULL DEFAULT 0;-- Create index for efficient orderingCREATE INDEX IF NOT EXISTS idx_item_images_position ON public.item_images(item_id, position ASC);-- Update existing rows with sequential positionsWITH ranked AS ( SELECT id, ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY created_at) - 1 AS pos FROM public.item_images)UPDATE public.item_imagesSET position = ranked.posFROM rankedWHERE item_images.id = ranked.id;
Migration: 20251222_insert_categories.sql
-- Insert main categoryINSERT INTO public.categories (name, parent_id) VALUES ('Körbe', NULL)ON CONFLICT DO NOTHING;-- Insert subcategoriesINSERT INTO public.categories (name, parent_id)SELECT 'Ovale Körbe', id FROM public.categoriesWHERE name = 'Körbe' AND parent_id IS NULLON CONFLICT DO NOTHING;INSERT INTO public.categories (name, parent_id)SELECT 'Runde Körbe', id FROM public.categoriesWHERE name = 'Körbe' AND parent_id IS NULLON CONFLICT DO NOTHING;
Migration: 20260214_create_customer_photos.sql
-- Create customer photos tableCREATE TABLE IF NOT EXISTS public.customer_photos ( id BIGSERIAL PRIMARY KEY, image_url TEXT NOT NULL, position INTEGER NOT NULL DEFAULT 0, is_visible BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW());-- Create index for visible photosCREATE INDEX IF NOT EXISTS idx_customer_photos_visibleON public.customer_photos(is_visible, position ASC)WHERE is_visible = true;-- Enable RLSALTER TABLE public.customer_photos ENABLE ROW LEVEL SECURITY;-- Public read for visible photosCREATE POLICY "customer_photos: public read visible"ON public.customer_photos FOR SELECTTO anon, authenticatedUSING (is_visible = true);-- Admin full accessCREATE POLICY "customer_photos: admin full access"ON public.customer_photos FOR ALLTO authenticatedUSING (public.is_admin(auth.uid()));
Migration: 20260214_drop_testimonials.sql
-- Drop testimonials table (replaced by customer_photos)DROP TABLE IF EXISTS public.testimonials CASCADE;-- Remove related policies (automatically dropped with CASCADE)-- No additional cleanup needed
-- Migration: Add tags to products-- Created: 2026-03-02CREATE TABLE IF NOT EXISTS public.tags ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL UNIQUE, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW());CREATE TABLE IF NOT EXISTS public.item_tags ( item_id BIGINT NOT NULL REFERENCES public.items(id) ON DELETE CASCADE, tag_id BIGINT NOT NULL REFERENCES public.tags(id) ON DELETE CASCADE, PRIMARY KEY (item_id, tag_id));CREATE INDEX IF NOT EXISTS idx_item_tags_tag ON public.item_tags(tag_id);-- Enable RLSALTER TABLE public.tags ENABLE ROW LEVEL SECURITY;ALTER TABLE public.item_tags ENABLE ROW LEVEL SECURITY;-- Public read, admin writeCREATE POLICY "tags: public read" ON public.tags FOR SELECT TO anon, authenticated USING (true);CREATE POLICY "tags: admin write" ON public.tags FOR ALL TO authenticated USING (public.is_admin(auth.uid()));
3
Test Locally
Run the migration in a development Supabase project first:
# Test in dev environmentsupabase db reset --localsupabase db push
UPDATE items SET status = 'active' WHERE status IS NULL;
Rollback:
-- Store original values before migrationCREATE TABLE items_backup AS SELECT * FROM items;-- RollbackUPDATE items SET status = items_backup.statusFROM items_backup WHERE items.id = items_backup.id;
Error: violates foreign key constraintSolution: Ensure related data exists before creating foreign keys:
-- Check for orphaned records firstSELECT * FROM order_items oiLEFT JOIN orders o ON oi.order_id = o.idWHERE o.id IS NULL;-- Clean up orphaned recordsDELETE FROM order_items WHERE order_id NOT IN (SELECT id FROM orders);
Column already exists error
Error: column "position" of relation "item_images" already existsSolution: Use ADD COLUMN IF NOT EXISTS:
ALTER TABLE item_images ADD COLUMN IF NOT EXISTS position INTEGER DEFAULT 0;
Permission denied on table
Error: permission denied for table itemsSolution: Ensure you’re running migrations with proper privileges:
Use SQL Editor in Supabase dashboard (has full privileges)