Редактировать: Это не работает, как ожидалось. В отличие от принятого ответа, это приводит к уникальным нарушениям ключа, когда два процесса неоднократно вызывают upsert_foo
одновременно.
Эврика! Я нашел способ сделать это в одном запросе: используйте UPDATE ... RETURNING
, чтобы проверить, были ли затронуты какие-либо строки:
CREATE TABLE foo (k INT PRIMARY KEY, v TEXT);
CREATE FUNCTION update_foo(k INT, v TEXT)
RETURNS SETOF INT AS $$
UPDATE foo SET v = $2 WHERE k = $1 RETURNING $1
$$ LANGUAGE sql;
CREATE FUNCTION upsert_foo(k INT, v TEXT)
RETURNS VOID AS $$
INSERT INTO foo
SELECT $1, $2
WHERE NOT EXISTS (SELECT update_foo($1, $2))
$$ LANGUAGE sql;
UPDATE
должен быть выполнен в отдельной процедуре, потому что, к сожалению, это синтаксическая ошибка:
... WHERE NOT EXISTS (UPDATE ...)
Теперь все работает как надо:
SELECT upsert_foo(1, 'hi');
SELECT upsert_foo(1, 'bye');
SELECT upsert_foo(3, 'hi');
SELECT upsert_foo(3, 'bye');