Errore PostgreSQL: permission denied for table / schema

ERROR:  permission denied for table orders

-- variante a livello di schema:
ERROR:  permission denied for schema app

Al ruolo con cui sei connesso manca il privilegio che serve allo statement: SELECT/INSERT/UPDATE/DELETE su una tabella, USAGE o CREATE su uno schema, USAGE su una sequence. (Le versioni più vecchie di PostgreSQL formulano la prima come "permission denied for relation".) La soluzione è sempre un GRANT — la parte interessante è perché il grant mancava.

Cosa significa questo errore

I privilegi di PostgreSQL sono per-oggetto e non cascano: per leggere app.orders ti servono sia USAGE sullo schema app sia SELECT sulla tabella. I proprietari detengono implicitamente tutti i privilegi sui loro oggetti — che è esattamente il motivo per cui tutto funziona in sviluppo (dove l'app si connette come proprietario) e si rompe in produzione (dove non lo fa).

Due varianti adiacenti da riconoscere: permission denied for sequence orders_id_seq significa che il ruolo può scrivere sulla tabella ma non pescare ID dalla sua sequence; permission denied for schema public ha iniziato a comparire diffusamente con PostgreSQL 15, che ha smesso di consentire a ogni utente di creare oggetti in public di default.

Cause comuni

Come diagnosticarlo

SELECT current_user;                -- chi sei davvero (occhio ai pooler)

\dp orders                          -- privilegi e proprietario della tabella
\dn+                                -- privilegi sugli schemi

SELECT has_table_privilege('app_user', 'app.orders', 'SELECT');

Leggi l'output di \dp senza pietà: una colonna "Access privileges" vuota significa che solo il proprietario ha accesso. E controlla chi è il proprietario — di solito questo spiega come è nata la situazione.

Come risolverlo

-- Il set minimo tipico per un ruolo applicativo:
GRANT USAGE ON SCHEMA app TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_user;

-- E per le tabelle FUTURE create dal ruolo delle migrazioni:
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA app
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA app
  GRANT USAGE, SELECT ON SEQUENCES TO app_user;

-- PostgreSQL 15+, se serve creare oggetti in public:
GRANT CREATE ON SCHEMA public TO app_user;