Regular Views

View declarations; query changes emit CREATE OR REPLACE VIEW; output column list changes emit DROP VIEW CASCADE.

The AS query clause is Part 1, terminated with ;. An optional { } block holds grants, comments, and the owner.

Basic view

SCHEMA public {
    VIEW active_users AS
        SELECT id, email, created_at
        FROM users
        WHERE status = 'active' AND deleted_at IS NULL;
}
-- emits
CREATE OR REPLACE VIEW "public"."active_users" AS
    SELECT id, email, created_at
    FROM users
    WHERE status = 'active' AND deleted_at IS NULL;

View with explicit column aliases

VIEW user_summary (user_id, email, order_count) AS
    SELECT u.id, u.email, COUNT(o.id)
    FROM users u
    LEFT JOIN orders o ON o.user_id = u.id
    GROUP BY u.id, u.email;
CREATE OR REPLACE VIEW "public"."user_summary" ("user_id", "email", "order_count") AS
    SELECT u.id, u.email, COUNT(o.id)
    FROM users u
    LEFT JOIN orders o ON o.user_id = u.id
    GROUP BY u.id, u.email;

View with security_barrier

VIEW secure_user_view WITH (security_barrier = true) AS
    SELECT id, email FROM users WHERE tenant_id = current_tenant();
CREATE VIEW "public"."secure_user_view" WITH (security_barrier=true) AS SELECT id, email FROM users WHERE tenant_id = current_tenant();

security_invoker works the same way. An option written without a value, such as WITH (security_barrier), means true.

View with WITH CHECK OPTION

VIEW active_orders AS
    SELECT * FROM orders WHERE status != 'cancelled'
    WITH LOCAL CHECK OPTION;
CREATE VIEW "public"."active_orders" WITH (check_option=local) AS SELECT * FROM orders WHERE status <> 'cancelled';

PostgreSQL stores WITH [LOCAL | CASCADED] CHECK OPTION as the view option check_option, so DPG writes it that way. Writing WITH (check_option = local) yourself means the same thing.

View with grants and comment

VIEW admin_summary AS
    SELECT id, email, created_at FROM users WHERE role = 'admin' {
    COMMENT 'Admin user summary';
    GRANTS  { SELECT TO app_readonly; }
}
CREATE OR REPLACE VIEW "public"."admin_summary" AS
    SELECT id, email, created_at FROM users WHERE role = 'admin';

COMMENT ON VIEW "public"."admin_summary" IS 'Admin user summary';
GRANT SELECT ON "public"."admin_summary" TO "app_readonly";

Diffing behaviour

ChangeSQL emittedSafety
Query body changes; output column list unchangedCREATE OR REPLACE VIEW, restating the view’s options (PostgreSQL resets any it omits)SAFE
View options or check option change; query unchangedALTER VIEW ... SET (...) / RESET (...)CAUTION
Output column list changes in any wayDROP VIEW CASCADE then CREATE VIEWDESTRUCTIVE