That is not enough to solve the general case due to locking considerations.
If we have one transaction running delete and then insert concurrently with one transaction doing a normal update then the update will find zero rows to update if it is ran after the delete+insert. The inserted row will be invisible to the update while the old row will be locked. This means the update will wait until the other transaction is committed and then find neither the old nor the new row.
A correct REPLACE implementation would make sure the UPDATE would UPDATE the replaced row.
The above is assuming READ COMMITTED isolation level.
Yep and even if we do account for this, say by asserting a table lock or escalating isolation level, we can still run into problems in the case that there are triggers defined.
There is no direct substitute in PostgreSQL, but there are case by case alternatives AND as far as any use cases I can dream up right now, they have better (As in they are easier to debug, easier to observe and easier to tune - MySQL necessarily hides the update operation from you) semantics.
However MySQL doesn't. I've just tried it, and MySQL blocks the UPDATE until the first transaction (DELETE, INSERT) is finished (like PostgreSQL), but after the commit, the UPDATE statement updates the newly inserted row.
It seems MySQL firstly searches the table for the row and then attempts to acquire a lock on it (thus waits for the DELETE, INSERT to finish, as it's locked the row being DELETEd), and, after the lock has been acquired, searches the table again for rows matching the query in order to perform it.
If we have one transaction running delete and then insert concurrently with one transaction doing a normal update then the update will find zero rows to update if it is ran after the delete+insert. The inserted row will be invisible to the update while the old row will be locked. This means the update will wait until the other transaction is committed and then find neither the old nor the new row.
A correct REPLACE implementation would make sure the UPDATE would UPDATE the replaced row.
The above is assuming READ COMMITTED isolation level.