Another nice feature just made it into PostgreSQL 15: You can now control who is allowed to set specific parameters. This gives you a lot more flexibility, especially when you are hosting PostgreSQL instances for your customers. You can delegate more control to trusted users or roles and of course, you can also revoke it later on when it is not anymore required. One goal of this is to reduce the number of tasks which require superuser privileges. As always, lets do a simple example to highlight how you might use it.

To start with, we create a new user and connect with it:

postgres=# create user u with login password 'u';
CREATE ROLE
postgres=# c postgres u
You are now connected to database "postgres" as user "u".

Which parameters is this user allowed to set? Nothing on the instance and database level:

postgres=> alter system set work_mem='12MB';
ERROR:  permission denied to set parameter "work_mem"
postgres=> alter database postgres set work_mem='12MB';
ERROR:  must be owner of database postgres

Of course the user can set a new value for himself (either permanently or in the session):

postgres=> alter user u set work_mem='12MB';
ALTER ROLE
postgres=> set work_mem='10MB';
SET

This works for all parameters that have a context of “user” in pg_settings:

postgres=> select name,context from pg_settings where context = 'user' order by 1;
                name                 | context 
-------------------------------------+---------
 application_name                    | user
 array_nulls                         | user
 backend_flush_after                 | user
 backslash_quote                     | user
 bytea_output                        | user
 check_function_bodies               | user
 client_connection_check_interval    | user
 client_encoding                     | user
 client_min_messages                 | user
 commit_siblings                     | user
 constraint_exclusion                | user
 cpu_index_tuple_cost                | user
 cpu_operator_cost                   | user
 cpu_tuple_cost                      | user
 cursor_tuple_fraction               | user
 DateStyle                           | user
 debug_pretty_print                  | user
 debug_print_parse                   | user
...
 vacuum_cost_limit                   | user
 vacuum_cost_page_dirty              | user
 vacuum_cost_page_hit                | user
 vacuum_cost_page_miss               | user
 vacuum_failsafe_age                 | user
 vacuum_freeze_min_age               | user
 vacuum_freeze_table_age             | user
 vacuum_multixact_failsafe_age       | user
 vacuum_multixact_freeze_min_age     | user
 vacuum_multixact_freeze_table_age   | user
 wal_sender_timeout                  | user
 wal_skip_threshold                  | user
 work_mem                            | user
 xmlbinary                           | user
 xmloption                           | user
(136 rows)

For all the other parameters it does not work:

postgres=> select name,context from pg_settings where context != 'user' order by 1;
                  name                  |      context      
----------------------------------------+-------------------
 allow_in_place_tablespaces             | superuser
 allow_system_table_mods                | superuser
 archive_cleanup_command                | sighup
 archive_command                        | sighup
 archive_library                        | sighup
 archive_mode                           | postmaster
 archive_timeout                        | sighup
 authentication_timeout                 | sighup
...
 wal_sync_method                        | sighup
 wal_writer_delay                       | sighup
 wal_writer_flush_after                 | sighup
 zero_damaged_pages                     | superuser
(202 rows)
postgres=> set track_counts='on';
ERROR:  permission denied to set parameter "track_counts"
postgres=> 

These times are now over: From PostgreSQL 15 on you can grant setting specific parameters to users or roles:

postgres=# select current_user;
 current_user 
--------------
 postgres
(1 row)
postgres=# grant set on parameter track_counts to u;
GRANT
postgres=# c postgres u
You are now connected to database "postgres" as user "u".
postgres=> set track_counts = 'on';
SET
postgres=> 

This also works on the instance level:

postgres=# grant alter system on parameter shared_buffers to u;
GRANT
postgres=# c postgres u
You are now connected to database "postgres" as user "u".
postgres=> alter system set shared_buffers = '129MB';
ALTER SYSTEM
postgres=> 

A new catalog view comes alongside which lists all the grants for parameters:

postgres=> c postgres
You are now connected to database "postgres" as user "u".
postgres=> select * from pg_parameter_acl;
  oid  |    parname     |               paracl                
-------+----------------+-------------------------------------
 16392 | track_counts   | {postgres=sA/postgres,u=s/postgres}
 16393 | wal_level      | {postgres=sA/postgres,u=s/postgres}
 16394 | shared_buffers | {postgres=sA/postgres,u=A/postgres}
(3 rows)

Nice, thanks to all involved.