LCOV - code coverage report
Current view: top level - src/backend/utils/misc - rls.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 96.9 % 32 31
Test Date: 2026-03-03 14:15:12 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * rls.c
       4              :  *        RLS-related utility functions.
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *        src/backend/utils/misc/rls.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              : */
      15              : #include "postgres.h"
      16              : 
      17              : #include "access/htup.h"
      18              : #include "access/htup_details.h"
      19              : #include "access/transam.h"
      20              : #include "catalog/namespace.h"
      21              : #include "catalog/pg_class.h"
      22              : #include "miscadmin.h"
      23              : #include "utils/acl.h"
      24              : #include "utils/fmgrprotos.h"
      25              : #include "utils/lsyscache.h"
      26              : #include "utils/rls.h"
      27              : #include "utils/syscache.h"
      28              : #include "utils/varlena.h"
      29              : 
      30              : 
      31              : /*
      32              :  * check_enable_rls
      33              :  *
      34              :  * Determine, based on the relation, row_security setting, and current role,
      35              :  * if RLS is applicable to this query.  RLS_NONE_ENV indicates that, while
      36              :  * RLS is not to be added for this query, a change in the environment may change
      37              :  * that.  RLS_NONE means that RLS is not on the relation at all and therefore
      38              :  * we don't need to worry about it.  RLS_ENABLED means RLS should be implemented
      39              :  * for the table and the plan cache needs to be invalidated if the environment
      40              :  * changes.
      41              :  *
      42              :  * Handle checking as another role via checkAsUser (for views, etc).  Pass
      43              :  * InvalidOid to check the current user.
      44              :  *
      45              :  * If noError is set to 'true' then we just return RLS_ENABLED instead of doing
      46              :  * an ereport() if the user has attempted to bypass RLS and they are not
      47              :  * allowed to.  This allows users to check if RLS is enabled without having to
      48              :  * deal with the actual error case (eg: error cases which are trying to decide
      49              :  * if the user should get data from the relation back as part of the error).
      50              :  */
      51              : int
      52       477604 : check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
      53              : {
      54       477604 :     Oid         user_id = OidIsValid(checkAsUser) ? checkAsUser : GetUserId();
      55              :     HeapTuple   tuple;
      56              :     Form_pg_class classform;
      57              :     bool        relrowsecurity;
      58              :     bool        relforcerowsecurity;
      59              :     bool        amowner;
      60              : 
      61              :     /* Nothing to do for built-in relations */
      62       477604 :     if (relid < (Oid) FirstNormalObjectId)
      63       148138 :         return RLS_NONE;
      64              : 
      65              :     /* Fetch relation's relrowsecurity and relforcerowsecurity flags */
      66       329466 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
      67       329466 :     if (!HeapTupleIsValid(tuple))
      68            0 :         return RLS_NONE;
      69       329466 :     classform = (Form_pg_class) GETSTRUCT(tuple);
      70              : 
      71       329466 :     relrowsecurity = classform->relrowsecurity;
      72       329466 :     relforcerowsecurity = classform->relforcerowsecurity;
      73              : 
      74       329466 :     ReleaseSysCache(tuple);
      75              : 
      76              :     /* Nothing to do if the relation does not have RLS */
      77       329466 :     if (!relrowsecurity)
      78       327297 :         return RLS_NONE;
      79              : 
      80              :     /*
      81              :      * BYPASSRLS users always bypass RLS.  Note that superusers are always
      82              :      * considered to have BYPASSRLS.
      83              :      *
      84              :      * Return RLS_NONE_ENV to indicate that this decision depends on the
      85              :      * environment (in this case, the user_id).
      86              :      */
      87         2169 :     if (has_bypassrls_privilege(user_id))
      88          330 :         return RLS_NONE_ENV;
      89              : 
      90              :     /*
      91              :      * Table owners generally bypass RLS, except if the table has been set (by
      92              :      * an owner) to FORCE ROW SECURITY, and this is not a referential
      93              :      * integrity check.
      94              :      *
      95              :      * Return RLS_NONE_ENV to indicate that this decision depends on the
      96              :      * environment (in this case, the user_id).
      97              :      */
      98         1839 :     amowner = object_ownercheck(RelationRelationId, relid, user_id);
      99         1839 :     if (amowner)
     100              :     {
     101              :         /*
     102              :          * If FORCE ROW LEVEL SECURITY has been set on the relation then we
     103              :          * should return RLS_ENABLED to indicate that RLS should be applied.
     104              :          * If not, or if we are in an InNoForceRLSOperation context, we return
     105              :          * RLS_NONE_ENV.
     106              :          *
     107              :          * InNoForceRLSOperation indicates that we should not apply RLS even
     108              :          * if the table has FORCE RLS set - IF the current user is the owner.
     109              :          * This is specifically to ensure that referential integrity checks
     110              :          * are able to still run correctly.
     111              :          *
     112              :          * This is intentionally only done after we have checked that the user
     113              :          * is the table owner, which should always be the case for referential
     114              :          * integrity checks.
     115              :          */
     116          238 :         if (!relforcerowsecurity || InNoForceRLSOperation())
     117          118 :             return RLS_NONE_ENV;
     118              :     }
     119              : 
     120              :     /*
     121              :      * We should apply RLS.  However, the user may turn off the row_security
     122              :      * GUC to get a forced error instead.
     123              :      */
     124         1721 :     if (!row_security && !noError)
     125           39 :         ereport(ERROR,
     126              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     127              :                  errmsg("query would be affected by row-level security policy for table \"%s\"",
     128              :                         get_rel_name(relid)),
     129              :                  amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0));
     130              : 
     131              :     /* RLS should be fully enabled for this relation. */
     132         1682 :     return RLS_ENABLED;
     133              : }
     134              : 
     135              : /*
     136              :  * row_security_active
     137              :  *
     138              :  * check_enable_rls wrapped as a SQL callable function except
     139              :  * RLS_NONE_ENV and RLS_NONE are the same for this purpose.
     140              :  */
     141              : Datum
     142           46 : row_security_active(PG_FUNCTION_ARGS)
     143              : {
     144              :     /* By OID */
     145           46 :     Oid         tableoid = PG_GETARG_OID(0);
     146              :     int         rls_status;
     147              : 
     148           46 :     rls_status = check_enable_rls(tableoid, InvalidOid, true);
     149           46 :     PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     150              : }
     151              : 
     152              : Datum
     153            6 : row_security_active_name(PG_FUNCTION_ARGS)
     154              : {
     155              :     /* By qualified name */
     156            6 :     text       *tablename = PG_GETARG_TEXT_PP(0);
     157              :     RangeVar   *tablerel;
     158              :     Oid         tableoid;
     159              :     int         rls_status;
     160              : 
     161              :     /* Look up table name.  Can't lock it - we might not have privileges. */
     162            6 :     tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
     163            6 :     tableoid = RangeVarGetRelid(tablerel, NoLock, false);
     164              : 
     165            6 :     rls_status = check_enable_rls(tableoid, InvalidOid, true);
     166            6 :     PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     167              : }
        

Generated by: LCOV version 2.0-1