Roles & Permissions

PostgreSQL doesn't actually have a separate concept of "users" — everything is a role, and a role that's allowed to log in is what other databases would call a user.

Creating a role

SQL storefront=#
CREATE ROLE app_reader WITH LOGIN PASSWORD 'a-real-secret';
Output
CREATE ROLE

WITH LOGIN is what makes this role usable as a connection identity — a role created without it can still be useful (for grouping permissions together), but nothing can authenticate as it directly. This is the "roles vs. users" unification: CREATE USER is actually just an alias for CREATE ROLE ... WITH LOGIN.

Granting privileges

A freshly created role can't do anything until you explicitly grant it access:

SQL storefront=#
GRANT SELECT ON products, customers TO app_reader;
Output
GRANT

This role can now read from products and customers, but has no INSERT, UPDATE, or DELETE rights, and can't touch any other table — exactly the least-privilege setup you'd want for, say, a reporting tool that only needs to read data.

Grouping permissions with a role

SQL storefront=#
CREATE ROLE analytics_team;
GRANT SELECT ON orders, order_totals TO analytics_team;
GRANT analytics_team TO app_reader;
Output
CREATE ROLE
GRANT
GRANT

analytics_team here is a role with no LOGIN — nobody connects as it directly. Instead it's a bundle of permissions that gets granted to actual login roles, so adding a new analyst later is one GRANT analytics_team TO new_person; instead of re-granting every individual table.

Revoking access

SQL storefront=#
REVOKE SELECT ON customers FROM app_reader;
Output
REVOKE
Note: app_reader being able to read customers in the earlier example, then losing that access here, is worth noticing — permissions in PostgreSQL are additive and explicit. A brand-new role can do essentially nothing until granted access, which is the safe default; the mistake to avoid is granting broad access "temporarily" and forgetting to revoke it later.