更新时间:2024-11-11 GMT+08:00

CREATE ROW LEVEL SECURITY POLICY

功能描述

对表创建行访问控制策略。

对表创建行访问控制策略时,需打开该表的行访问控制开关(ALTER TABLE ... ENABLE ROW LEVEL SECURITY)策略才能生效,否则不生效。

当前行访问控制会影响数据表的读取操作(SELECT、UPDATE、DELETE),暂不影响数据表的写入操作(INSERT、MERGE INTO)。表所有者或系统管理员可以在USING子句中创建表达式,并在客户端执行数据表读取操作时,数据库后台在查询重写阶段会将满足条件的表达式拼接并应用到执行计划中。针对数据表的每一条元组,当USING表达式返回TRUE时,元组对当前用户可见,当USING表达式返回FALSE或NULL时,元组对当前用户不可见。

行访问控制策略名称是针对表的,同一个数据表上不能有同名的行访问控制策略;对不同的数据表,可以有同名的行访问控制策略。

行访问控制策略可以应用到指定的操作(SELECT、UPDATE、DELETE、ALL),ALL表示会影响SELECT、UPDATE、DELETE三种操作;定义行访问控制策略时,若未指定受影响的相关操作,默认为ALL。

行访问控制策略可以应用到指定的用户(角色),也可应用到全部用户(PUBLIC);定义行访问控制策略时,若未指定受影响的用户,默认为PUBLIC。

注意事项

  • 支持对行存表、行存分区表、列存表、列存分区表、复制表、unlogged表、hash表定义行访问控制策略。
  • 不支持HDFS表、外表、临时表定义行访问控制策略。
  • 不支持对视图定义行访问控制策略。
  • 同一张表上可以创建多个行访问控制策略,一张表最多创建100个行访问控制策略。
  • 具有管理员权限的用户、初始运维用户(Ruby)、表的owner和表的owner角色组成员不受行访问控制影响,可以查看表的全量数据。
  • 通过SQL语句、视图、函数、存储过程查询包含行访问控制策略的表,都会受影响。
  • 不支持对行访问控制策略依赖的列进行类型修改。例如,不支持如下修改:
    1
    ALTER TABLE public.all_data ALTER COLUMN role TYPE text;
    

语法格式

1
2
3
4
5
CREATE [ ROW LEVEL SECURITY ] POLICY policy_name ON table_name
    [ AS { PERMISSIVE | RESTRICTIVE } ]
    [ FOR { ALL | SELECT | UPDATE | DELETE } ]
    [ TO { role_name | PUBLIC } [, ...] ]
    USING ( using_expression )

参数说明

  • policy_name

    行访问控制策略名称,同一个数据表上行访问控制策略名称不能相同。

  • table_name

    行访问控制策略的表名。

  • PERMISSIVE

    指定行访问控制策略的类型为宽容性策略。对于一个给定的查询,将使用“OR”操作符将所有的宽容性策略组合。行访问控制策略的类型默认为宽容性策略。

  • RESTRICTIVE

    指定行访问控制策略的类型为限制性策略。对于一个给定的查询,将使用“AND”操作符将所有的限制性策略组合。

    至少需要一条宽容性策略允许对记录的访问。如果只有限制性策略存在,则不能访问任何记录。当宽容性和限制性策略共存时,只有当记录能通过至少一条宽容性策略以及所有的限制性策略时,该记录才能访问。

  • command

    当前行访问控制影响的SQL操作,可指定操作包括:ALL、SELECT、UPDATE、DELETE。当未指定时,ALL为默认值,涵盖SELECT、UPDATE、DELETE操作。

    当command为SELECT时,SELECT类操作受行访问控制的影响,只能查看到满足条件(using_expression返回值为TRUE)的元组数据,受影响的操作包括SELECT,UPDATE ... RETURNING,DELETE ... RETURNING。

    当command为UPDATE时,UPDATE类操作受行访问控制的影响,只能更新满足条件(using_expression返回值为TRUE)的元组数据,受影响的操作包括UPDATE,UPDATE ... RETURNING,SELECT ... FOR UPDATE/SHARE。

    当command为DELETE时,DELETE类操作受行访问控制的影响,只能删除满足条件(using_expression返回值为TRUE)的元组数据,受影响的操作包括DELETE,DELETE ... RETURNING。

    行访问控制策略与适配的SQL语法关系参见下表:

    表1 ROW LEVEL SECURITY策略与适配SQL语法关系

    Command

    SELECT/ALL policy

    UPDATE/ALL policy

    DELETE/ALL policy

    SELECT

    Existing row

    No

    No

    SELECT FOR UPDATE/SHARE

    Existing row

    Existing row

    No

    UPDATE

    No

    Existing row

    No

    UPDATE RETURNING

    Existing row

    Existing row

    No

    DELETE

    No

    No

    Existing row

    DELETE RETURNING

    Existing row

    No

    Existing row

  • role_name

    行访问控制影响的数据库用户。

    当未指定时,PUBLIC为默认值,PUBLIC表示影响所有数据库用户,可以指定多个受影响的数据库用户。

    系统管理员不受行访问控制特性影响。

  • using_expression

    行访问控制的表达式(返回boolean值)。

    条件表达式中不能包含AGG函数和窗口(WINDOW)函数。在查询重写阶段,如果数据表的行访问控制开关打开,满足条件的表达式会添加到计划树中。针对数据表的每条元组,会进行表达式计算,只有表达式返回值为TRUE时,行数据对用户才可见(SELECT、UPDATE、DELETE);当表达式返回FALSE时,该元组对当前用户不可见,用户无法通过SELECT语句查看此元组,无法通过UPDATE语句更新此元组,无法通过DELETE语句删除此元组。

示例1:创建行访问控制策略,当前用户只能查看用户自身的数据

  1. 创建用户alice和bob:
    1
    2
    CREATE ROLE alice PASSWORD '{password}';
    CREATE ROLE bob PASSWORD '{password}';
    
  2. 创建数据表public.all_data:
    1
    CREATE TABLE public.all_data(id int, role varchar(100), data varchar(100));
    
  3. 向数据表插入数据:
    1
    2
    3
    INSERT INTO all_data VALUES(1, 'alice', 'alice data');
    INSERT INTO all_data VALUES(2, 'bob', 'bob data');
    INSERT INTO all_data VALUES(3, 'peter', 'peter data');
    
  4. 将表all_data的读取权限赋予alice和bob用户:
    1
    GRANT SELECT ON all_data TO alice, bob;
    
  5. 打开行访问控制策略开关:
    1
    ALTER TABLE all_data ENABLE ROW LEVEL SECURITY;
    
  6. 创建行访问控制策略,当前用户只能查看用户自身的数据:
    1
    CREATE ROW LEVEL SECURITY POLICY all_data_rls ON all_data USING(role = CURRENT_USER);
    
  7. 查看表all_data相关信息:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    \d+ all_data
                                   Table "public.all_data"
     Column |          Type          | Modifiers | Storage  | Stats target | Description
    --------+------------------------+-----------+----------+--------------+-------------
     id     | integer                |           | plain    |              |
     role   | character varying(100) |           | extended |              |
     data   | character varying(100) |           | extended |              |
    Row Level Security Policies:
        POLICY "all_data_rls"
          USING (((role)::name = "current_user"()))
    Has OIDs: no
    Distribute By: HASH(id)
    Location Nodes: ALL DATANODES
    Options: orientation=row, compression=no, enable_rowsecurity=true
    
  8. 当前用户执行SELECT操作:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    SELECT * FROM all_data;
     id | role  |    data
    ----+-------+------------
      1 | alice | alice data
      2 | bob   | bob data
      3 | peter | peter data
    (3 rows)
    EXPLAIN(COSTS OFF) SELECT * FROM all_data;
             QUERY PLAN
    ----------------------------
     Streaming (type: GATHER)
       Node/s: All datanodes
       ->  Seq Scan on all_data
    (3 rows)
    
  9. 切换至alice用户:
    1
    set role alice password '{password}';
    
  10. 执行SELECT操作:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    SELECT * FROM all_data;
     id | role  |    data
    ----+-------+------------
      1 | alice | alice data
    (1 row)
    
    EXPLAIN(COSTS OFF) SELECT * FROM all_data;
                               QUERY PLAN
    ----------------------------------------------------------------
     Streaming (type: GATHER)
       Node/s: All datanodes
       ->  Seq Scan on all_data
             Filter: ((role)::name = 'alice'::name)
     Notice: This query is influenced by row level security feature
    (5 rows)
    

示例2:通过行级控制实现分区权限管理

  1. 创建用户alice:
    1
    CREATE ROLE alice PASSWORD '{password1}';
    
  2. 创建范围分区表web_returns_p1,并插入数据:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    CREATE TABLE web_returns_p1
    (
        wr_returned_date_sk       integer,
        wr_returned_time_sk       integer,
        wr_item_sk                integer NOT NULL,
        wr_refunded_customer_sk   integer
    )
    WITH (orientation = column)
    DISTRIBUTE BY HASH (wr_item_sk)
    PARTITION BY RANGE(wr_returned_date_sk)
    (
        PARTITION p2016 START(800) END(830) EVERY(1)
    );
    
    INSERT INTO web_returns_p1 values (801,17,11,102);
    INSERT INTO web_returns_p1 values (802,18,12,103);
    
  3. 将表web_returns_p1的读取权限赋予alice用户:
    1
    GRANT SELECT ON web_returns_p1 TO alice;
    
  4. 打开行访问控制策略开关:
    1
    ALTER TABLE web_returns_p1 ENABLE ROW LEVEL SECURITY;
    
  5. 创建行级访问控制策略web_returns_rsl。其中wr_returned_date_sk为web_returns_p1分区表的分区名,801为分区值:
    1
    CREATE ROW LEVEL SECURITY POLICY web_returns_rsl ON web_returns_p1 USING('wr_returned_date_sk' = '801');
    
  6. 将行级访问控制策略web_returns_rsl的赋予用户alice:
    1
    ALTER ROW LEVEL SECURITY POLICY web_returns_rsl ON web_returns_p1 TO alice;
    
  7. 切换至alice用户:
    1
    set role alice password '{password1}';
    
  8. 查询表web_returns_p1:
    1
    select * from web_returns_p1;