It very much depends on the details of your setup and requirements.
Note that since Postgres 11, only adding a column with a volatile DEFAULT
still triggers a table rewrite. Unfortunately, this is your case.
If you have sufficient free space on disk (at- at least 110%110 % of pg_size_pretty((pg_total_relation_size(tbl))
) on disk - and can afford a share lock for some time for some time and an exclusive lock for a very short time for a very short time, then create a new table including the uuid
uuid
column using CREATE TABLE AS
. Why?
The below code uses a function from the additional uuid-oss
modulefunction from the additional uuid-oss
module.
Lock the table against concurrent changes in
SHARE
modeLock the table against concurrent changes inSHARE
mode (still allowing concurrent reads). Attempts to write to the table will wait and eventually fail. See below.Copy the whole table while populating the new column on the fly - possibly ordering rows favorably while being at it.
If you are going to reorder rows, be sure to setwork_mem
work_mem
high enough to do the sort in RAM or as high as you can afford (just for your session, not globally).Then add constraints, foreign keys, indices, triggers etc. to the new table. When updating large portions of a table it is much faster to create indices from scratch than to add rows iteratively. Related advice in the manual.
When the new table is ready, drop the old and rename the new to make it a drop-in replacement. Only this last step acquires an exclusive lock on the old table for the rest of the transaction - which should be very short now.
It also requires that you delete any object depending on the table type (views, functions using the table type in the signature, ...) and recreate them afterwards.Do it all in one transaction to avoid incomplete states.
What happens to concurrent writes?
###What happens to concurrent writes?
OtherOther transaction (in other sessions) trying to INSERT
/ UPDATE
/ DELETE
in the same table after your transaction has taken the SHARE
lock, will wait until the lock is released or a timeout kicks in, whichever comes first. They will fail either way, since the table they were trying to write to has been deleted from under them.
Keeping the existing table, alternative 1
###Two alternatives keepingUpdate in place (possibly running the existing tableupdate on small segments at a time) before you add the NOT NULL
constraint. Adding a new column with NULL values and without NOT NULL
constraint is cheap.
Since Postgres 9.2 you can also create a CHECK
constraint with NOT VALID
:
Update in place (possibly running the update on small segments at a time) before you add the
NOT NULL
constraint. Adding a new column with NULL values and withoutNOT NULL
constraint is cheap.
Since Postgres 9.2 you can also create aCHECK
constraint withNOT VALID
:The constraint will still be enforced against subsequent inserts or updates
The constraint will still be enforced against subsequent inserts or updates
ALTER TABLE tbl ADD CONSTRAINT tbl_no_null CHECK (tbl_uuid IS NOT NULL) NOT VALID;
-- update rows in multiple batches in separate transactions
-- possibly run VACUUM between transactions
ALTER TABLE tbl ALTER COLUMN tbl_uuid SET NOT NULL;
ALTER TABLE tbl ALTER DROP CONSTRAINT tbl_no_null;
Related answer discussing NOT VALID
in more detail:
- Disable all constraints and table checks while restoring a dump
- Prepare the new state in a temporary table,
TRUNCATE
the original and refill from the temp table. All in one transaction. You still need to take aSHARE
lock before preparing the new table to prevent losing concurrent writes.
Keeping the existing table, alternative 2
Prepare the new state in a temporary table, TRUNCATE
the original and refill from the temp table. All in one transaction. You still need to take a SHARE
lock before preparing the new table to prevent losing concurrent writes.