0

I'm trying to insert a row but only if it is not already present in the table. At the moment I'm using code similar to the following:

insert into mentions (project_id, id, data_source, channel)
  select 3, '123456789', 'example.com', 'twitter'
  where not exists
    (select 1 as "one" from mentions where (project_id = 3 and id = '123456789'))
returning reach;

But I sometimes see it fail with the following error in the logs

ERROR: duplicate key value violates unique constraint "mentions_pkey"

DETAIL: Key (project_id, id)=(3, 123456789) already exists.

and the constraint is defined as

Index "public.mentions_pkey"
┌────────────┬─────────┬────────────┐
│  Column    │  Type   │ Definition │
├────────────┼─────────┼────────────┤
│ project_id │ integer │ project_id │
│ id         │ text    │ id         │
└────────────┴─────────┴────────────┘
primary key, btree, for table "public.mentions"

Since I'm only inserting when the exists clause is false, I don't understand how this can ever fail with a constraint violation.

Could it be a concurrency issue? I was under the assumption that individual statements are atomic but maybe this isn't the case?

I know that once 9.5 ships I can use ON CONFLICT DO NOTHING, but I'm running 9.4 at the moment.

Dijkgraaf
  • 11,049
  • 17
  • 42
  • 54
Ian Phillips
  • 567
  • 4
  • 13
  • That question was asked when most recent version of Postgresql hadn't on conflict clause. Notice that op specified version after the first two questions were post, too. – Roberto Nov 27 '15 at 19:02

2 Answers2

1

use ON CONFLICT optional clause on insert:

insert into mentions  (project_id, id, data_source, channel)
  values (3, '123456789', 'example.com', 'twitter')
  ON CONFLICT (project_id, id) DO NOTHING;

look: http://www.postgresql.org/docs/9.5/static/sql-insert.html#SQL-ON-CONFLICT

Roberto
  • 2,115
  • 24
  • 32
0
  1. handle errors in stored procedure
  CREATE OR REPLACE FUNCTION some_insert() RETURNS VOID AS
  $$BEGIN
    BEGIN
      insert into mentions (project_id, id, data_source, channel)
            select 3, '123456789', 'example.com', 'twitter';
      RETURN;
    EXCEPTION WHEN unique_violation THEN
      -- do nothing
    END;
  END;$$
  LANGUAGE plpgsql;
  1. lock table
BEGIN;
LOCK TABLE mentions IN SHARE ROW EXCLUSIVE MODE;
INSERT INTO mentions (project_id, id, data_source, channel)
    SELECT 3, '123456789', 'example.com', 'twitter'
    WHERE NOT EXISTS
        (SELECT 1 AS "one" FROM mentions WHERE (project_id = 3 AND id = '123456789'))
COMMIT;
  1. handle errors on postgres' client side
kwarunek
  • 12,141
  • 4
  • 43
  • 48