create function immutable(schema text, table_ text, columns text[])
  returns void
  language plpgsql
  as $$
declare
  col text;
  qualified_name text := concat(schema, '.', table_);
  do_table_immutable_columns text := concat('do_', table_, '_immutable_columns');
  trigger_table_immutable_columns text := concat('trigger_', table_, '_immutable_columns');
  create_do_table_immutable_columns text;
  create_trigger_table_immutable_columns text;
begin
  create_do_table_immutable_columns := concat( create_do_table_immutable_columns
                                             , 'create function ', schema, '.', do_table_immutable_columns, E'() returns trigger language plpgsql as \$\$\n'
                                             , E'begin\n'
                                             , '  if OLD.', columns[1], ' <> NEW.', columns[1], E' then\n'
                                             , E'    raise exception \'immutable_field\'\n'
                                             , E'    using detail = \'', qualified_name, '.', columns[1], E' is immutable\',\n'
                                             , E'          errcode = \'restrict_violation\';\n'
                                             );
  foreach col in array columns[2:] loop
    create_do_table_immutable_columns := concat( create_do_table_immutable_columns
                                               , E'  elsif OLD.', col, ' <> NEW.', col, E' then\n'
                                               , E'    raise exception \'immutable_field\'\n'
                                               , E'    using detail = \'', qualified_name, '.', col, E' is immutable\',\n'
                                               , E'          errcode = \'restrict_violation\';\n'
                                               );
  end loop;

  create_do_table_immutable_columns := concat( create_do_table_immutable_columns
                                             , E'  end if;\n'
                                             , E'\n'
                                             , E'  return NEW;\n'
                                             , E'end;\n'
                                             , E'\$\$;\n'
                                             );

  create_trigger_table_immutable_columns := concat( create_trigger_table_immutable_columns
                                                  , 'create trigger ', trigger_table_immutable_columns, E'\n'
                                                  , 'before update on ', qualified_name, E'\n'
                                                  , 'for each row execute function ', do_table_immutable_columns, '();'
                                                  );

  execute create_do_table_immutable_columns;
  execute create_trigger_table_immutable_columns;
end;
$$;