All Products
Search
Document Center

AnalyticDB:Row security policies

Last Updated:Mar 28, 2026

By default, any account with read or write privileges on a table can access all rows. Row security policies let you restrict each account to only the rows it is authorized to see or modify.

This feature is available in AnalyticDB for PostgreSQL V7.0. Applying row security policies to views requires V7.0.6.0 or later.

Usage notes

Policy combination

When multiple policies apply to the same table, their USING expressions are combined with OR (permissive by default). If Policy A allows rows 1 and 2, and Policy B allows row 4, the query returns rows 1, 2, and 4.

Limits

  • Row security policies do not apply to privileged accounts or accounts with the BYPASSRLS attribute.

  • ALTER TABLE ... ENABLE ROW LEVEL SECURITY does not apply policies to the table owner. To enforce policies on the table owner, run ALTER TABLE ... FORCE ROW LEVEL SECURITY.

For the full CREATE POLICY parameter reference, see Row Security Policies and CREATE POLICY.

Apply row security policies to a table

Prerequisites

Before you begin, make sure that you have:

Procedure

  1. Create a table named logtest and insert test data.

    CREATE TABLE logtest (
        id serial primary key,
        username text,
        log_event text);
    
    INSERT INTO logtest VALUES (1,'report','DELETE issued'),
                               (2,'messaging', 'Message queue purged'),
                               (3,'messaging','Reset accounts'),
                               (4,'report', 'Reset accounts');
  2. Create two standard accounts named report and messaging, and grant the SELECT privilege on logtest to both.

    CREATE ROLE report WITH LOGIN ENCRYPTED PASSWORD '111111';
    CREATE ROLE messaging WITH LOGIN ENCRYPTED PASSWORD '111111';
    
    GRANT SELECT ON logtest TO report;
    GRANT SELECT ON logtest TO messaging;
  3. Create a row security policy that restricts each account to rows where username matches the current account name.

    CREATE POLICY policy_user_log ON logtest
       FOR ALL
       TO PUBLIC
       USING (username = current_user);
  4. Enable row-level security on logtest. All policies take effect immediately.

    Important

    ALTER TABLE ... ENABLE ROW LEVEL SECURITY does not apply policies to the table owner. To apply policies to the table owner, run ALTER TABLE logtest FORCE ROW LEVEL SECURITY.

    ALTER TABLE logtest ENABLE ROW LEVEL SECURITY;
  5. Verify that the policy is working by querying logtest with each account.

    • `report` account — only rows where username = 'report' are returned:

      SELECT * FROM logtest;
      id| username | log_event
      --+----------+--------------
      1 | report   | DELETE issued
      4 | report   | Reset accounts
      (2 rows)
    • `messaging` account — only rows where username = 'messaging' are returned:

      SELECT * FROM logtest;
      id| username  | log_event
      --+-----------+---------------------
      2 | messaging | Message queue purged
      3 | messaging | Reset accounts
      (2 rows)
    • Privileged account — all rows are returned because row security policies do not apply to privileged accounts:

      SELECT * FROM logtest;
      id| username  | log_event
      --+-----------+---------------------
      1 | report    | DELETE issued
      2 | messaging | Message queue purged
      3 | messaging | Reset accounts
      4 | report    | Reset accounts
      (4 rows)
  6. Add a second policy that restricts results to rows with an even id.

    CREATE POLICY policy_even_ids_only ON logtest
       FOR ALL
       TO PUBLIC
       USING (id % 2 = 0);

    Both policies are now active. Because multiple policies combine with OR, the report account sees rows that satisfy either policy — rows with an even id (rows 2 and 4) or rows where username = 'report' (rows 1 and 4):

    SELECT * FROM logtest;
    id| username  | log_event
    --+-----------+---------------------
    1 | report    | DELETE issued
    2 | messaging | Message queue purged
    4 | report    | Reset accounts
    (3 rows)

Apply row security policies to a view

By default, row security policies on base tables do not carry through to views. When a standard account queries a view, the view runs with the privileges of the view definer (typically a privileged account), bypassing any policies on the underlying tables. The security_invoker parameter changes this behavior so that the view runs with the privileges of the view caller instead.

Prerequisites

  • AnalyticDB for PostgreSQL V7.0.6.0 or later.

  • The view caller must have the SELECT privilege on all data exposed by the view.

Create a secure view

When creating the view, set security_invoker to make all queries run under the caller's privileges:

CREATE VIEW v1 WITH (security_invoker) AS SELECT * FROM logtest;

To understand why this matters, compare the behavior without and with security_invoker.

Without `security_invoker` — the report account sees all rows, because the view runs under the definer's (privileged) account:

CREATE VIEW v1 AS SELECT * FROM logtest;
GRANT SELECT ON v1 TO report;

-- Queried by the report account:
SELECT * FROM v1;
id| username  | log_event
--+-----------+---------------------
1 | report    | DELETE issued
2 | messaging | Message queue purged
3 | messaging | Reset accounts
4 | report    | Reset accounts
(4 rows)

With `security_invoker` — the report account sees only its authorized rows, because the view now runs under the caller's privileges:

-- Queried by the report account:
SELECT * FROM v1;
id| username  | log_event
--+-----------+---------------------
1 | report    | DELETE issued
2 | messaging | Message queue purged
4 | report    | Reset accounts
(3 rows)

Enable or disable security invoker on an existing view

-- Enable row security.
ALTER VIEW v1 SET (security_invoker = on);

-- Disable row security.
ALTER VIEW v1 SET (security_invoker = off);

Apply row security policies to a materialized view

Row security policies can be applied to both standard materialized views and real-time materialized views. The configuration steps are the same as for regular tables.

  1. Create a materialized view mv_logtest based on logtest and grant the SELECT privilege to the report account.

    CREATE MATERIALIZED VIEW mv_logtest
    AS
      SELECT * FROM logtest;
    
    GRANT SELECT ON mv_logtest TO report;
  2. Verify the starting state. The report account currently sees all rows because no policy is active:

    SELECT * FROM mv_logtest;
     id | username  | log_event
    ----+-----------+----------------------
      2 | messaging | Message queue purged
      3 | messaging | Reset accounts
      4 | report    | Reset accounts
      1 | report    | DELETE issued
    (4 rows)
  3. Create a row security policy that restricts each account to rows where username matches the current account name.

    CREATE POLICY policy_user_log ON mv_logtest
       FOR ALL
       TO PUBLIC
       USING (username = current_user);
  4. Enable row-level security on mv_logtest.

    ALTER MATERIALIZED VIEW ... ENABLE ROW LEVEL SECURITY does not apply policies to the view owner. To apply policies to the view owner, run ALTER MATERIALIZED VIEW mv_logtest FORCE ROW LEVEL SECURITY. Multiple policies can be configured for the same materialized view. Policies apply to both standard materialized views and real-time materialized views.
    ALTER MATERIALIZED VIEW mv_logtest ENABLE ROW LEVEL SECURITY;
  5. Verify that the policy is working. The report account now sees only its authorized rows:

    SELECT * FROM mv_logtest;
    id| username | log_event
    --+----------+--------------
    1 | report   | DELETE issued
    4 | report   | Reset accounts
    (2 rows)