81 lines
1.8 KiB
Markdown
81 lines
1.8 KiB
Markdown
---
|
|
title: Add Constraints Safely in Migrations
|
|
impact: HIGH
|
|
impactDescription: Prevents migration failures and enables idempotent schema changes
|
|
tags: constraints, migrations, schema, alter-table
|
|
---
|
|
|
|
## Add Constraints Safely in Migrations
|
|
|
|
PostgreSQL does not support `ADD CONSTRAINT IF NOT EXISTS`. Migrations using this syntax will fail.
|
|
|
|
**Incorrect (causes syntax error):**
|
|
|
|
```sql
|
|
-- ERROR: syntax error at or near "not" (SQLSTATE 42601)
|
|
alter table public.profiles
|
|
add constraint if not exists profiles_birthchart_id_unique unique (birthchart_id);
|
|
```
|
|
|
|
**Correct (idempotent constraint creation):**
|
|
|
|
```sql
|
|
-- Use DO block to check before adding
|
|
do $$
|
|
begin
|
|
if not exists (
|
|
select 1 from pg_constraint
|
|
where conname = 'profiles_birthchart_id_unique'
|
|
and conrelid = 'public.profiles'::regclass
|
|
) then
|
|
alter table public.profiles
|
|
add constraint profiles_birthchart_id_unique unique (birthchart_id);
|
|
end if;
|
|
end $$;
|
|
```
|
|
|
|
For all constraint types:
|
|
|
|
```sql
|
|
-- Check constraints
|
|
do $$
|
|
begin
|
|
if not exists (
|
|
select 1 from pg_constraint
|
|
where conname = 'check_age_positive'
|
|
) then
|
|
alter table users add constraint check_age_positive check (age > 0);
|
|
end if;
|
|
end $$;
|
|
|
|
-- Foreign keys
|
|
do $$
|
|
begin
|
|
if not exists (
|
|
select 1 from pg_constraint
|
|
where conname = 'profiles_birthchart_id_fkey'
|
|
) then
|
|
alter table profiles
|
|
add constraint profiles_birthchart_id_fkey
|
|
foreign key (birthchart_id) references birthcharts(id);
|
|
end if;
|
|
end $$;
|
|
```
|
|
|
|
Check if constraint exists:
|
|
|
|
```sql
|
|
-- Query to check constraint existence
|
|
select conname, contype, pg_get_constraintdef(oid)
|
|
from pg_constraint
|
|
where conrelid = 'public.profiles'::regclass;
|
|
|
|
-- contype values:
|
|
-- 'p' = PRIMARY KEY
|
|
-- 'f' = FOREIGN KEY
|
|
-- 'u' = UNIQUE
|
|
-- 'c' = CHECK
|
|
```
|
|
|
|
Reference: [Constraints](https://www.postgresql.org/docs/current/ddl-constraints.html)
|