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
.