-- Inspect the current session
SELECTcurrent_user,session_user;-- Create ordinary roles (requires CREATEROLE or superuser privileges)
-- CREATE ROLE app_read NOLOGIN; -- a "group" role
-- CREATE ROLE alice LOGIN PASSWORD 'change-me' IN ROLE app_read;
-- CREATE ROLE app_write NOLOGIN;
-- Grant access to a schema and read on tables
-- GRANT USAGE ON SCHEMA public TO app_read;
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_read;
-- Future tables in this schema also get SELECT for app_read
-- ALTER DEFAULT PRIVILEGES IN SCHEMA public
-- GRANT SELECT ON TABLES TO app_read;
-- Write-level
-- GRANT app_read TO app_write; -- inheritance: app_write also reads
-- GRANT INSERT, UPDATE, DELETE ON fruits TO app_write;
-- Revoke
-- REVOKE INSERT ON fruits FROM app_write;
-- Inspect your privileges on a table
SELECTgrantee,privilege_typeFROMinformation_schema.role_table_grantsWHEREtable_name='fruits'ANDgrantee=current_user;-- Row-level security: per-row visibility decided by a policy
-- (demo on a separate table so we don't change `fruits` permissions)
DROPTABLEIFEXISTStenant_docs;CREATETABLEtenant_docs(idintegerPRIMARYKEY,tenanttextNOTNULL,contenttextNOTNULL);INSERTINTOtenant_docsVALUES(1,'acme','hello'),(2,'globex','world');ALTERTABLEtenant_docsENABLEROWLEVELSECURITY;-- Apply the policy to the table owner as well (superusers/BYPASSRLS still bypass it).
ALTERTABLEtenant_docsFORCEROWLEVELSECURITY;CREATEPOLICYtenant_isolationONtenant_docsUSING(tenant=current_setting('app.tenant',true));-- Set the tenant for the session and try a select
SETapp.tenantTO'acme';SELECT*FROMtenant_docs;-- only sees the 'acme' row
SETapp.tenantTO'globex';SELECT*FROMtenant_docs;-- only sees the 'globex' row
-- Cleanup
DROPPOLICYIFEXISTStenant_isolationONtenant_docs;DROPTABLEIFEXISTStenant_docs;