3

I'm trying to insert or update data in a PostgreSQL db. The simplest case is a key-value pairing (the actual data is more complicated, but this is the smallest clear example)

When you set a value, I'd like it to insert if the key is not there, otherwise update. Sadly Postgres does not have an insert or update statement, so I have to emulate it myself.

I've been working with the idea of basically SELECTing whether the key exists, and then running the appropriate INSERT or UPDATE. Now clearly this needs to be be in a transaction or all manner of bad things could happen.

However, this is not working exactly how I'd like it to - I understand that there are limitations to serializable transactions, but I'm not sure how to work around this one.

Here's the situation -

ab: => set transaction isolation level serializable;
a:  => select count(1) from table where id=1; --> 0
b:  => select count(1) from table where id=1; --> 0
a:  => insert into table values(1); --> 1
b:  => insert into table values(1); --> 
    ERROR:  duplicate key value violates unique constraint "serial_test_pkey"

Now I would expect it to throw the usual "couldn't commit due to concurrent update" but I'm guessing since the inserts are different "rows" this does not happen.

Is there an easy way to work around this?

Steven Schlansker
  • 37,580
  • 14
  • 81
  • 100
  • 2
    try this UPSERT approach, check the last code (Example 38-2): http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-UPSERT-EXAMPLE – Michael Buen May 20 '10 at 01:47
  • About merge: I just founded merge specification on postgresql site) Sorry. Why you didn't use triggers or function/procedure? – ksogor Jun 16 '10 at 19:13
  • @ksogor: that's basically what Michael Buen suggested. – Steven Schlansker Jun 17 '10 at 02:15
  • Duplicate of: http://stackoverflow.com/questions/1109061/insert-on-duplicate-update-postgresql – jmz Jun 12 '11 at 17:54

1 Answers1

3

Prior to Postgres 9.1 there were issues with isolation:

asking for serializable isolation guaranteed only that a single MVCC snapshot would be used for the entire transaction, which allowed certain documented anomalies.

Perhaps you're running into these "anomalies".

You can try SELECT … FOR UPDATE when checking if the row exists.

Alternatively, LOCK TABLE yourself.

If you're trying to implement UPSERT, then a slightly more reliable (or rather less unreliable) way is to first attempt UPDATE, check number of affected rows, and then try INSERT if no rows were updated.

Kornel
  • 97,764
  • 37
  • 219
  • 309