Atlas now supports GRANT and REVOKE for PostgreSQL functions, procedures, and materialized views, including column-level grants and WITH GRANT OPTION.
Atlas permission management for PostgreSQL previously covered tables, views, sequences, and columns. This release extends GRANT and REVOKE support to functions, procedures, and materialized views.
Function Permissions
Use the permission block with for = function.<name> to grant EXECUTE on a function:
role "app" {}function "compute_score" {schema = schema.publiclang = SQLarg "x" {type = integer}return = integeras = "SELECT x * 2"}permission {to = role.appfor = function.compute_scoreprivileges = [EXECUTE]}
Procedure Permissions
Procedures work the same way. Reference them with for = procedure.<name>:
procedure "refresh_cache" {schema = schema.publiclang = SQLas = "REFRESH MATERIALIZED VIEW cache"}permission {to = role.appfor = procedure.refresh_cacheprivileges = [EXECUTE]}
WITH GRANT OPTION
Set grantable = true to allow the grantee to re-grant the privilege to other roles. Atlas generates the WITH GRANT OPTION clause automatically:
permission {to = role.appfor = function.compute_scoreprivileges = [EXECUTE]grantable = true}
Generated SQL for Routines
Atlas generates GRANT EXECUTE ON FUNCTION and GRANT EXECUTE ON PROCEDURE statements, including the full argument signature for overloaded routines. All statements are reversible:
-- Grant execute on a functionGRANT EXECUTE ON FUNCTION "public"."compute_score"(integer) TO "app";-- Revoke execute from a functionREVOKE EXECUTE ON FUNCTION "public"."compute_score"(integer) FROM "app";-- Grant with admin option (grantable = true)GRANT EXECUTE ON FUNCTION "public"."compute_score"(integer) TO "app" WITH GRANT OPTION;
-- Grant execute on a procedureGRANT EXECUTE ON PROCEDURE "public"."refresh_cache"() TO "app";-- Revoke execute from a procedureREVOKE EXECUTE ON PROCEDURE "public"."refresh_cache"() FROM "app";
Materialized View Permissions
Materialized views now support both table-level and column-level grants. Reference them with for = materialized.<name>:
materialized "user_stats" {schema = schema.publiccolumn "id" {type = integer}column "total" {type = integer}as = "SELECT id, count(*) AS total FROM users GROUP BY id"}permission {to = role.readerfor = materialized.user_statsprivileges = [SELECT]}
Column-Level Grants
For fine-grained access control, grant privileges on individual columns of a materialized view using for = materialized.<name>.column.<col>:
permission {to = role.readerfor = materialized.user_stats.column.idprivileges = [SELECT]}
Generated SQL for Materialized Views
-- Grant on materialized viewGRANT SELECT ON "public"."user_stats" TO "reader";GRANT DELETE, INSERT, SELECT, UPDATE ON "public"."user_stats" TO "writer";-- Column-level grant on materialized viewGRANT UPDATE ("id") ON TABLE "public"."user_stats" TO "reader";-- Revoke from materialized viewREVOKE SELECT ON "public"."user_stats" FROM "reader";
Setup
To manage permissions, enable the permissions and roles schema modes in your project configuration:
env "local" {schema {src = "file://schema.pg.hcl"mode {permissions = trueroles = true}}}
Atlas handles the full lifecycle: inspecting existing ACLs from the database, computing the diff against your desired state, and generating GRANT/REVOKE statements with correct dependency ordering (e.g., revoking permissions before dropping a role). This works with both the declarative workflow (atlas schema apply) and versioned migrations (atlas migrate diff).