On Mon, Jun 8, 2015 at 10:15 AM, Dave Johansen <davejohan...@gmail.com>
wrote:

> I'm using Postgres 8.4 on RHEL 6 and I need to support inserting data into
> a table from multiple processes with there occasionally being duplicates
> from the different processes. Here's a simple example table:
> CREATE TABLE test (tutc TIMESTAMP WITHOUT TIME ZONE, id INTEGER, value
> INTEGER, PRIMARY KEY (tutc, id));
> If I do the following query from 2 processes, then it's fine:
> INSERT INTO test (tutc, id, value) SELECT '2015-01-01 01:02:03', 4, 5
> WHERE NOT EXISTS (SELECT 1 FROM test WHERE tutc='2015-01-01 01:02:03' AND
> id=4);
>
> But if I put the operation in a transaction, then the second process will
> block until the transaction of the first is commited (which is fine) but
> then the insert fails with a "duplicate key value violation". I'm guessing
> that this is because the transaction is making it so that the SELECT only
> sees the values from before the transaction of the second process began.
>
> Using an "upsert" type of function, like the one shown in the
> documentation ( see
> http://www.postgresql.org/docs/8.4/static/plpgsql-control-structures.html#PLPGSQL-UPSERT-EXAMPLE
> ) seems like it might work, but I don't need to support updating and was
> hoping to not have to use a custom function. So is there some way to catch
> the unique_violation exception without creating a function? Or some other
> solution to this?
>

For the sake of documentation, here's the function that I used to
accomplish this:
CREATE FUNCTION insert_test_no_dup(tutc_ TIMESTAMP WITHOUT TIME ZONE, id_
INTEGER, value_ INTEGER) RETURNS VOID AS
$$
BEGIN
  BEGIN
    INSERT INTO test(tutc, id, value) VALUES (tutc_, id_, value_);
    RETURN;
  EXCEPTION WHEN unique_violation THEN
    -- do nothing because the record already exists
  END;
END;
$$
LANGUAGE plpgsql;

Both using a rule and using a trigger had the same issue with transactions.
Here's the declarations for documentation:
CREATE RULE ignore_duplicate_inserts AS
  ON INSERT TO test
  WHERE (EXISTS (SELECT 1 FROM test WHERE tutc=new.tutc AND id=new.id)) DO
INSTEAD NOTHING;

CREATE FUNCTION tf_insert_test_ignore_duplicates() RETURNS trigger
AS $$
DECLARE
  found BOOLEAN;
BEGIN
  SELECT 1 INTO found FROM test WHERE tutc=new.tutc AND id=new.id;
  IF found THEN
    RETURN NULL;
  ELSE
    RETURN new;
  END IF;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER insert_test_before BEFORE INSERT ON test
  FOR EACH ROW EXECUTE PROCEDURE tf_insert_test_ignore_duplicates();

Reply via email to