Skip to content

Instantly share code, notes, and snippets.

@siviae
Last active December 13, 2019 16:33
Show Gist options
  • Save siviae/0d7313d843565f9838f7b099a399ce6c to your computer and use it in GitHub Desktop.
Save siviae/0d7313d843565f9838f7b099a399ce6c to your computer and use it in GitHub Desktop.
PostgreSQL: add NOT NULL column faster with minimal locking
#!/bin/sh
# Inspired by https://medium.com/doctolib/adding-a-not-null-constraint-on-pg-faster-with-minimal-locking-38b2c00c4d1c
if [ "$#" -ne 5 ]; then
echo "This script generates safe not-null column addition SQL script for large tables"
echo "Usage: $0 <table_name> <column_name> <column_type> <id_column_name> <default_value> > script.sql" >&2
exit 1
fi
table_name=$1
column_name=$2
column_type=$3
id_column_name=$4
default_value=$5
cat <<EOF
--create indexes and constraints for fast search and correctness
ALTER TABLE ${table_name}
ADD COLUMN ${column_name} ${column_type} DEFAULT $default_value;
ALTER TABLE ${table_name}
ADD CONSTRAINT chk_${column_name}_not_null CHECK (${column_name} IS NOT NULL) NOT VALID;
CREATE INDEX CONCURRENTLY idx_${table_name}_null_${column_name} ON $table_name (${column_name} NULLS FIRST);
--batch update column with default value
DO
\$\$
BEGIN
WHILE (SELECT exists(select 1 from ${table_name} where ${table_name}.${column_name} IS NULL))
LOOP
UPDATE ${table_name}
SET ${column_name} = ${default_value}
WHERE ${id_column_name} IN (
SELECT ${id_column_name}
FROM ${table_name}
WHERE ${table_name}.${column_name} IS NULL
LIMIT 1000
);
COMMIT;
END LOOP;
END;
\$\$;
--set not null
ALTER TABLE ${table_name}
VALIDATE CONSTRAINT chk_${column_name}_not_null;
ALTER TABLE ${table_name}
ALTER COLUMN ${column_name} SET NOT NULL;
-- clean up
ALTER TABLE ${table_name}
DROP CONSTRAINT chk_${column_name}_not_null;
DROP INDEX CONCURRENTLY idx_${table_name}_null_${column_name};
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment