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
- Oggetti creati da un ruolo diverso. Le migrazioni girano come
migratoro un admin; l'app si connette comeapp_user; nessuno ha concesso adapp_useralcunché sulle nuove tabelle. - I grant non sono retroattivi in avanti:
GRANT ... ON ALL TABLES IN SCHEMAcopre solo le tabelle esistenti. Le tabelle create la settimana prossima non sono coperte — a questo serveALTER DEFAULT PRIVILEGES. - La trappola di
ALTER DEFAULT PRIVILEGES: si applica solo agli oggetti creati dal ruolo per cui è stato impostato. Impostarlo come te stesso mentre le migrazioni girano come un altro ruolo non fa nulla. - Manca
USAGEsullo schema — i grant sulle tabelle sono inutili se il ruolo non può attraversare lo schema. - PostgreSQL 15+:
CREATEsullo schemapublicnon è più concesso a tutti; strumenti che "funzionavano e basta" prima di un upgrade iniziano a fallire.
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;
- Nota il
FOR ROLE migrator: i default privilege devono essere collegati al ruolo che crea gli oggetti. - Alternativa strutturale che molti team adottano: rendi tutti gli oggetti di proprietà di un ruolo dedicato
app_owner, esegui le migrazioni con esso, e concedi al ruolo di runtime solo ciò che gli serve.
