CREATE OR REPLACE FUNCTION public.SET_NOT_NULL(_Schema name, _Table name, _Column name)
RETURNS boolean
LANGUAGE plpgsql
AS $$
DECLARE
_TableOID   oid;
_AttNOTNULL boolean;
_NULLExists boolean;
_OK         boolean;
BEGIN

-- Take an ACCESS EXCLUSIVE lock on the entire table,
-- just like normal ALTER TABLE DDL commands do:
EXECUTE format('
LOCK TABLE %I.%I IN ACCESS EXCLUSIVE MODE
', _Schema, _Table);

SELECT
    pg_attribute.attrelid,
    pg_attribute.attnotnull
INTO STRICT
    _TableOID,
    _AttNOTNULL
FROM pg_class
INNER JOIN pg_namespace ON pg_namespace.oid      = pg_class.relnamespace
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid
WHERE pg_namespace.nspname    = _Schema
AND pg_class.relname          = _Table
AND pg_attribute.attname      = _Column
AND pg_class.relkind          = 'r'
AND pg_attribute.attnum       > 0
AND pg_attribute.attisdropped IS FALSE
AND NOT pg_is_other_temp_schema(pg_namespace.oid);

IF _AttNOTNULL THEN
    RAISE NOTICE 'NOT NULL is already set for column %.%.%', _Schema, _Table, _Column;
    RETURN TRUE;
END IF;

-- Verify there are no rows in the table where the column IS NULL:
EXECUTE format('
SELECT EXISTS (SELECT 1 FROM %I.%I WHERE %I IS NULL)
', _Schema, _Table, _Column
) INTO STRICT _NULLExists;

IF _NULLExists THEN
    RAISE 'column "%.%.%" contains null values', _Schema, _Table, _Column USING ERRCODE = 'unique_violation';
END IF;

-- Set NOT NULL for the column by updating the pg_catalog directly:

EXECUTE format('
    UPDATE pg_catalog.pg_attribute SET attnotnull = TRUE
    WHERE attrelid = %L::oid
    AND   attname  = %L
    RETURNING TRUE
', _TableOID, _Column
) INTO STRICT _OK;

IF _OK IS NOT TRUE THEN
    RAISE EXCEPTION 'Failed setting attnotnull to TRUE';
END IF;

RETURN TRUE;
END;
$$