Use cases
Use cases for row-level security include:Restricting access to sensitive data for compliance
In industries like finance or healthcare, organizations are required to ensure that only authorized users access sensitive data. Row-level security (RLS) addresses this requirement directly within the database. For example, RLS allows a financial institution to restrict access to customer records based on roles or departments. In healthcare, RLS can be used to enforce policies ensuring patient records are visible only to the medical staff involved in their care. RLS embeds access control logic directly into the database and eliminates the need for manual filtering in application code. This centralized enforcement prevents inconsistencies, reduces security attack surface, and simplifies compliance with data access regulations. For an example, refer to RLS for Data Security (Fine-Grained Access Control).Designing multi-tenant applications
In multi-tenant applications such as typical Software-as-a-Service (SaaS) deployments, isolating data between tenants within shared tables is a requirement. Row-level Security (RLS) provides a database-level mechanism for enforcing this isolation. SaaS providers can utilize RLS policies to ensure tenants can only access their own data, eliminating the need for complex and potentially insecure application-layer filtering logic based on tenant IDs. For an example, refer to RLS for Multi-Tenant Isolation.How to use row-level security
At a high level, the steps for using row-level security (RLS) are as follows:- Choose which need row-level security. You can re-use existing schemas, or create new ones and . (, )
- & to schema objects by those roles. (, )
- Enable row-level security on the schema objects. ()
- Define row-level security policies on the schema objects which are assigned to specific roles. ()
How row-level security policies are evaluated
Policies function as filters or constraints applied automatically by CockroachDB during . They are based on boolean expressions evaluated in the context of the current , , and the row data itself. When row-level security is enabled on a table:- Existing still determine if a user can access the table at all (e.g.,
SELECT,INSERT). - determine which rows within the table are accessible or modifiable for specific commands.
- All apply to a specific set of . For a policy to be applicable, it must match at least one of the roles assigned to it. If the policy is associated with the
PUBLICrole, it applies to all roles. - If RLS is enabled but no policies apply to a given combination of user and SQL statement, access is denied by default.
- Permissive policies are combined using
ORlogic, while restrictive policies are combined usingANDlogic. The overall policy enforcement is determined by evaluating a logical expression of the form:(permissive policies) AND (restrictive policies). In order for reads or writes to succeed, there must be at least one permissive policy for the user’s role. - The
USINGclause of filters rows during reads; theWITH CHECKclause validates writes, and defaults toUSINGif absent.
Considerations
Performance
Complex evaluated per-row can impact query performance. To limit the performance impacts of row-level security, optimize your policy expressions and consider relevant columns.Security privileges
execute with the of the user invoking the query, unless functions marked are used.Functions marked
SECURITY DEFINER should only be used with extreme caution to ensure expressions do not have unintended side effects.Limitations
SQL language features that bypass row-level security
The following SQL language features bypass row-level security:- (including cascades)
Change data capture (CDC)
messages that are emitted from a table will not be filtered using RLS policies. Furthermore, are not supported on tables using RLS, and will fail with the error message:CDC queries are not supported on tables with row-level security enabled.
Backup and restore
functionality does not take RLS policies into account.Logical data replication (LDR) and Physical cluster replication (PCR)
and do not take RLS policies into account. LDR’s also apply to RLS, since RLS policies amount to a schema change. If you use PCR, the target cluster will have all RLS policies applied to the data because PCR performs byte-for-byte replication.Views
When are accessed, RLS policies on any underlying tables are applied. can only be defined directly on tables, not views. Views will only show rows that the current has access to on the table that the view is referencing.Examples
Create a policy
Refer to .Alter a policy
Refer to .Drop a policy
Refer to .Enable or disable row-level security
For examples, refer to:- .
- .
Detect when row-level security is applied to a query
The controls whether queries in the current should silently honor RLS policies or signal an error when those policies would filter out rows. The variable defaults toon, which applies policies as normal. Setting it to off lets non- users detect when an RLS policy would alter their results by returning an error instead of silently filtering rows. Admin users and table owners remain exempt from RLS by default regardless of this setting. To force table owners to be subject to RLS, you must use .
The following example shows how this session setting works:
RLS for data security (fine-grained access control)
In a fine-grained access control scenario, you will want to restrict access to specific rows within a table based on user , attributes, or relationships defined within the data itself. This goes beyond table-level permissions. Common examples include restricting access to salary information, personal data, or region-specific records. For example, imagine anemployees table containing sensitive salary information. You want to enforce the following rules:
- Employees can view their own record.
- Managers can view the records of their direct reports.
- Members of the
hr_departmentrole can view all records.
Set up fine-grained access control schema
Define thehr_department role and employees table, add some data, and grant basic table access:
Enable row-level security for fine-grained access control
Enable row-level security using the statement. Optionally, you may want to ensure that the table owner is also subject to RLS using .Define row-level security policies for fine-grained access control
Define RLS policies on the table. The following policy allows HR full access to the table:CURRENT_USER special form of the .
Verify fine-grained access control policies
To verify that the RLS settings are working as expected, execute the statements in this section. The following statement is executed by useralice (the manager), and returns: Alice, Bob, Carol, and Edward, but not David, since he works for Carol.
This is expected behavior due to Alice’s manager role having self_access or manager_access policies.
bob (an employee), and returns only Bob’s information.
This is expected behavior due to Bob’s employee role only having the self_access policy.
carol (a manager), and returns: Carol, David.
This is expected behavior due to Carol’s manager role having self_access or manager_access policies.
edward belonging to hr_department, and returns all rows.
This is expected behavior due to Edward’s hr_department role having hr_access or self_access policies.
RLS for multi-tenant isolation
Multi-tenant isolation is used to enforce strict data separation between different tenants (customers, organizations) sharing the same database infrastructure and schema. Each tenant must only be able to see and modify their own data. This is a critical requirement for Software-as-a-Service (SaaS) applications. For example, imagine a SaaS application serving multiple tenants, with all invoice data residing in a singleinvoices table. This table is distinguished by a tenant_id column. The application ensures that each user session is associated with a specific tenant_id.
Create multi-tenant schema
Create the schema and index for thetenants and invoices tables. Next, add an index on tenant_id for increased lookup performance.
Define user roles for app developer
The following statements create an app developer role, and grant it permissions.Define how the application sets the tenant ID for the session
Each application will need to set the tenant context for the session. In this example, you will use theapplication_name session variable to pass in a tenant ID that will later be extracted from the variable.
Specifically, the following the period in application_name is the tenant ID. We will use the current_setting() function in our RLS policies to extract the ID.
For multi-tenancy to work correctly, this setting must be reliably managed by the application layer and passed in the connection string.
Enable row-level security for multi-tenant isolation
To enable row-level security for theinvoices table, issue the following statements.
For multi-tenant isolation to work properly in this example, you must also
FORCE ROW LEVEL SECURITY so that the policies also apply to the table owner.Define tenant isolation policies
The following policy enforces tenant isolation for all operations.AS PERMISSIVE: Necessary because you need at least one permissive policy. The permissive policy above has logic to show results for a default tenant ID if theapplication_nameis omitted or improperly formatted.AS RESTRICTIVE: Makes the policy mandatory. If other policies exist, they must also pass. For simple tenant isolation, this is often the safest default. The restrictive policy above applies to theapp_devrole, so that anyone assigned to theapp_devrole must use the correctly formattedapplication_name, and is not allowed to fallback to the default tenant ID.FOR ALL: Covers all data modification and retrieval.TO PUBLIC: Applies the policy broadly. Roles should primarily manage table-level access usingGRANT, while this policy handles row-level visibility.USING: Ensures queries only see rows matching the session’s tenant ID, which is passed in using theapplication_namesession variable and extracted using thesplit_partfunction.WITH CHECK: Prevents users fromINSERTing rows with a tenant ID column different from their session’s calculated tenant ID, orUPDATEing a row to change itstenant_idcolumn across tenant boundaries. Without this, a user could potentially insert data into another tenant’s space. In this case, theWITH CHECKexpression could have been omitted; it is only necessary if it’s different than theUSINGexpression, or if it applies to a command that doesn’t do reads (e.g., forINSERT).
Verify multi-tenant isolation policies
To verify that the RLS settings are working as expected, execute the statements in this section. They use two tenants with the following IDs:- Tenant A, which has ID
9607a12c-3c2f-407b-ae3c-af903542395b. - Tenant B, which has ID
8177c2fc-3b55-47b7-bf84-38bd3a3e9c0a.
app_dev role which the policy applies to.
invoices which have a tenant_id column matching its ID.
invoices which have a tenant_id column matching its ID.
invoice table for rows that don’t have a tenant_id column matching its ID.
WITH CHECK constraint, and the following error is signaled:
invoice table that have a tenant_id column matching its ID.

