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; $$;