I am attempting to run an "upsert" in postgres like:
INSERT INTO my_table (
id, -- unique key
name,
hash
) VALUES (
'4b544dea-b355-463c-8fba-40c36ac7cb0c',
'example',
'0x0123456789'
) ON CONFLICT (
id
) DO UPDATE SET
name = 'example',
hash = '0x0123456789'
RETURNING
OLD.hash;
I would like to return the previous value of the hash
column (the example above is not a valid query due to OLD
not being a valid table alias). Importantly, I am trying to find a method that does this in a way that won't cause conflicts under load. Is this even possible? Or is the only solution to do a read-before-write in a transaction?
You must have INSERT privilege on a table in order to insert into it. If ON CONFLICT DO UPDATE is present, UPDATE privilege on the table is also required. If a column list is specified, you only need INSERT privilege on the listed columns.
PostgreSQL used the OID internally as a primary key for its system tables. Typically, the INSERT statement returns OID with value 0. The count is the number of rows that the INSERT statement inserted successfully.
First, specify the name of the table that you want to update data after the UPDATE keyword. Second, specify columns and their new values after SET keyword. The columns that do not appear in the SET clause retain their original values. Third, determine which rows to update in the condition of the WHERE clause.
The INSERT ON CONFLICT statement allows you to update an existing row that contains a primary key when you execute the INSERT statement to insert a new row that contains the same primary key. This feature is also known as UPSERT or INSERT OVERWRITE. It is similar to the REPLACE INTO statement of MySQL.
I did end up finding a workaround, though it doesn't strictly guarantee atomicity, but in general could be a good strategy depending on your use case.
INSERT INTO my_table (
id, -- unique key
name,
hash
) VALUES (
'4b544dea-b355-463c-8fba-40c36ac7cb0c',
'example',
'0x0123456789'
) ON CONFLICT (
id
) DO UPDATE SET
name = 'example',
hash = '0x0123456789'
RETURNING
name,
hash, -- will be the new hash
(SELECT hash FROM my_table WHERE my_table.id = '4b544dea-b355-463c-8fba-40c36ac7cb0c') -- will be the old hash
;
Another possible answer, if you are willing to update the schema, would be to store the previous value in another column:
ALTER table my_table add column prev_hash text;
INSERT INTO my_table (
id, -- unique key
hash
) VALUES (
1,
'a'
) ON CONFLICT (
id
) DO UPDATE SET
hash = excluded.hash,
prev_hash = my_table.hash
RETURNING
id,
hash, -- will be the new hash
prev_hash -- will be the old hash
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With