Ein normales GRANT SELECT ON ALL TABLES IN SCHEMA app TO reader; wirkt nur auf Tabellen, die in diesem Moment existieren. Sobald die nächste Migration eine neue Tabelle anlegt, fängt der Reader-User dort wieder bei Null an. Genau dafür gibt es ALTER DEFAULT PRIVILEGES: einmal eingerichtet, gilt die Regel automatisch für alle künftigen Objekte.

Das Problem in einem Beispiel

SQL GRANT alleine ist Stichtags-Schnappschuss
SET ROLE app_owner;
CREATE TABLE orders (id serial primary key);
GRANT SELECT ON ALL TABLES IN SCHEMA app TO reader;

-- Spaeter, in einer neuen Migration:
CREATE TABLE customers (id serial primary key);

SET ROLE reader;
SELECT * FROM customers;
-- ERROR: permission denied for table customers

reader hatte SELECT auf alle Tabellen, die zum Zeitpunkt des GRANT existierten — orders. Die später angelegte customers ist davon nicht betroffen.

Die Lösung: ALTER DEFAULT PRIVILEGES

SQL Defaults für kuenftige Objekte
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;

Übersetzt: „Ab jetzt — sobald jemand in Schema app eine neue Tabelle anlegt, soll reader automatisch SELECT bekommen."

Damit greift die Regel auf alle zukünftig erzeugten Tabellen. Die existierenden bleiben unverändert. Für die brauchst du weiterhin den klassischen GRANT SELECT ON ALL TABLES ….

Faustregel: bei einem neuen Read-Only-User immer beides ausführen:

SQL Komplettes Setup
-- 1. Bestehendes:
GRANT USAGE ON SCHEMA app TO reader;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO reader;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA app TO reader;

-- 2. Zukuenftiges:
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON SEQUENCES TO reader;

Worauf wirken Default Privileges?

Du kannst Defaults für die folgenden Objekttypen setzen:

ObjekttypMögliche Privilegien
TABLES (inkl. Views, Foreign Tables)SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
SEQUENCESUSAGE, SELECT, UPDATE
FUNCTIONS (inkl. Procedures)EXECUTE
TYPESUSAGE
SCHEMAS (ab PG 15)USAGE, CREATE

Der wichtigste Stolperstein: GRANTOR

Defaults sind immer an eine bestimmte erzeugende Rolle gebunden — den GRANTOR. In Klartext: die Regel greift nur, wenn die neue Tabelle von derselben Rolle erstellt wird, die das ALTER DEFAULT PRIVILEGES ausgeführt hat.

SQL GRANTOR ist entscheidend
-- Als app_owner:
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;

-- Sind app_owner Migrationen, greift die Regel.

-- Wenn aber eine andere Rolle (z. B. ein Migrations-User
-- "migrator") die Tabellen erstellt:
SET ROLE migrator;
CREATE TABLE app.invoices (...);
-- reader bekommt KEIN SELECT auf invoices.

Lösung: explizit für die richtige Rolle setzen.

SQL
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;

Wer mehrere Rollen Tabellen erzeugen lässt, braucht das ALTER DEFAULT PRIVILEGES FOR ROLE … für jede dieser Rollen. Praktischer ist, das im Migrations-Setup zu vereinheitlichen: eine Owner-Rolle für alle Migrationen, einmal Defaults setzen, fertig.

Pro Schema vs. global

Ohne IN SCHEMA gilt die Regel datenbankweit — für jede zukünftige Tabelle in jedem Schema:

SQL
ALTER DEFAULT PRIVILEGES
    GRANT SELECT ON TABLES TO reader;

Das ist häufig zu großzügig. In den meisten Setups gibt es ein App-Schema, ein paar Werkzeug-Schemas (migrations, tmp) und das public-Schema. Pro-Schema-Defaults sind expliziter und sicherer.

Defaults inspizieren

Im psql:

SQL
myapp=# \ddp
                          Default access privileges
   Owner    | Schema | Type  |     Access privileges
------------+--------+-------+---------------------------
 app_owner  | app    | table | reader=r/app_owner
 app_owner  | app    | seq   | reader=r/app_owner

Oder direkt im Katalog:

SQL
SELECT pg_get_userbyid(defaclrole)   AS grantor,
       nspname                       AS schema,
       defaclobjtype                 AS object_type,
       defaclacl                     AS privileges
FROM pg_default_acl
LEFT JOIN pg_namespace ON pg_namespace.oid = defaclnamespace;

defaclobjtype-Werte: r = relation (Tabelle), S = sequence, f = function, T = type, n = schema.

Zurücknehmen

Symmetrisch:

SQL
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    REVOKE SELECT ON TABLES FROM reader;

Wichtig: das wirkt nur auf die Defaults selbst (für künftige Objekte). Schon vergebene Privilegien auf existierende Tabellen bleiben — die musst du separat mit klassischem REVOKE zurückziehen.

Besonderheiten

Defaults sind keine retroaktive Wirkung.

Sie greifen ab dem Moment, ab dem du sie setzt — und nur auf Objekte, die danach entstehen. Wer nur ALTER DEFAULT PRIVILEGES setzt und das normale GRANT … ON ALL … weglässt, hat existierende Tabellen unangetastet gelassen. Beide gehören zusammen.

Pro GRANTOR — die häufigste „warum greift das nicht?“-Falle.

Wenn deine Migrationen unter migrator laufen, deine Default-Privileges aber als app_owner gesetzt wurden, passiert nichts. \ddp zeigt dir, welcher GRANTOR welche Defaults pflegt — wenn deine erstellende Rolle nicht in der Tabelle steht, ist das die Erklärung.

DROP OWNED BY entfernt auch Defaults der Rolle.

Wenn app_owner gedroppt wird (nach REASSIGN OWNED und DROP OWNED), verschwinden auch alle Default-Privilege-Definitionen, die unter seinem Namen liefen. Beim Aufräumen einer Rolle also vorher dokumentieren, welche Defaults sie pflegte — sonst muss das Setup beim nächsten Owner manuell wiederhergestellt werden.

In CI/Test-DBs Defaults gleich nach dem Anlegen setzen.

Wenn deine Test-DB nach jedem Lauf neu erstellt wird (etwa via docker-compose down -v), sind auch die Defaults weg. Setze sie als Teil des Bootstrap-Skripts (/docker-entrypoint-initdb.d/), sonst sind die ersten Migrationen ohne Reader-Rechte und Tests scheitern erst beim Read-Schritt.

Schema-Defaults gibt es erst seit PG 15.

ALTER DEFAULT PRIVILEGES … GRANT … ON SCHEMAS … ist neu. In früheren Versionen mussten Schemas einzeln per GRANT rechtefiziert werden. Wer noch auf älteren Versionen unterwegs ist, sollte beim Upgrade die Default-Privilege-Definitionen ergänzen.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Server-Administration

Zur Übersicht