Domanda

From this question, I want to use the following method to perform upserts in a PostGreSql table:

UPDATE table SET field='C', field2='Z' WHERE id=3;
INSERT INTO table (id, field, field2)
   SELECT 3, 'C', 'Z'
   WHERE NOT EXISTS (SELECT 1 FROM table WHERE id=3);

I understand I have to perform it in a transaction and that I should use an explicit locking method. I have been reading the postgresql documentation, but I am still hesitating between three types of locks (the difference between each is not crystal clear for me):

ROW EXCLUSIVE
SHARE ROW EXCLUSIVE
EXCLUSIVE

I would like to avoid having to retry the transaction. I am not expecting much concurrency on the rows, though this could happen from time to time. There might be simultaneous attempts at deleting and upserting the same row in rare cases.

È stato utile?

Soluzione

You need self-exclusive lock type that also excludes all DML commands.

The correct lock type for this operation is EXCLUSIVE. It conflicts with all other locks, including its self, except ACCESS SHARE.

ACCESS SHARE is taken by SELECT. All other commands require higher locking levels. So your upsert transaction will then block everything except SELECT, which is what you want.

See the docs on explicit locking.

So:

BEGIN;
LOCK TABLE ... IN EXCLUSIVE MODE;
...

(The historical, and awful, naming of some table level locks using the word ROW does not ease understanding of PostgreSQL's lock types).

BTW, your application should check the rowcount returned from the UPDATE. If it's non-zero, it can skip the INSERT.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top