Update
Postgres 15 added NULLS NOT DISTINCT for UNIQUE constraints and indexes. See:
Original answer
My guess is you are missing the fact that NULL values are considered to be distinct, i.e., do not conflict with a UNIQUE constraint. If you enter:
(NULL, 1, 20)
for (name, id, age) multiple times, you get no unique violation. Two NULL values are not considered "the same" (i.e. "distinct").
You can either set all involved columns NOT NULL (after replacing NULL values with dummy values).
Or you can implement additional partial indexes to cover NULLs (after cleaning up "dupes" with NULL). For instance, if you need to cover the name column for NULLs:
CREATE UNIQUE INDEX tbl_id_age_name_null_idx ON my_table (id, age)
WHERE name IS NULL;
Then you can have:
('pete', 1, 20)
, ('jane', 1, 20)
, (NULL , 1, 20)
for (name, id, age) in your table, but none of these a second time. See: