Вы столкнулись с каким-то странным поведением, специфичным для Postgresql: если в транзакции происходит ошибка, она вызывает откат всей транзакции. Я считаю это ошибкой дизайна Postgres; в некоторых случаях для его обхода требуется немало SQL-искажения.
Один из обходных путей - сначала сделать ОБНОВЛЕНИЕ. Определите, действительно ли он изменил строку, посмотрев на cursor.rowcount; если он не изменял какие-либо строки, он не существовал, так что вставьте. (Это будет быстрее, если вы обновляете чаще, чем вставляете, конечно.)
Другой обходной путь - использование точек сохранения:
SAVEPOINT a;
INSERT INTO ....;
-- on error:
ROLLBACK TO SAVEPOINT a;
UPDATE ...;
-- on success:
RELEASE SAVEPOINT a;
Это серьезная проблема для кода производственного качества: вы должны точно определить ошибку. Предположительно, вы ожидаете выполнить уникальную проверку ограничения, но вы можете выполнить что-то неожиданное, и может быть почти невозможно надежно отличить ожидаемую ошибку от неожиданной. Если это неправильно соответствует условию ошибки, это приведет к непонятным проблемам, при которых ничего не будет обновлено или вставлено, а ошибки не будет видно. Будьте очень осторожны с этим. Вы можете сузить число ошибок, посмотрев код ошибки Postgresql, чтобы убедиться, что вы ожидаете тип ошибки, но потенциальная проблема все еще существует.
Наконец, если вы действительно хотите выполнить пакетную вставку или обновление, вы на самом деле хотите выполнить многие из них за несколько команд, а не по одному элементу на команду. Для этого требуется более сложный SQL: SELECT, вложенный в INSERT, отфильтровывающий нужные элементы для вставки и обновления.