Row Filters
Row filters restrict which rows a developer can access by injecting WHERE clause predicates into queries. Filters are defined per-table within a policy, and the agent rewrites queries transparently — no changes to developer queries or client code are needed. Unlike PostgreSQL RLS, row filters require no database-side configuration and work with ephemeral roles.
Row-level filtering is an Enterprise feature. Contact your organization administrator to enable it.
How It Works
Row filters are structured predicates (column, operator, value) defined on table grants within a policy. When a developer runs a query that touches a filtered table, the agent parses the SQL, injects the appropriate WHERE conditions, and forwards the rewritten query to the database. The developer sees only rows that match all filter conditions.
Filtering is read-and-modify-side: SELECT, UPDATE, and DELETE queries are filtered. INSERT statements are not filtered, consistent with how masking is read-side only.
Creating Row Filters
Row filters are configured as part of a policy in the console. To add a row filter:
- Navigate to the database and open the policy you want to edit.
- In the Row Filters section of a table grant, click Add Filter.
- Select a column, choose an operator, and enter a value.
- Save the policy. The updated filters are pushed to the agent immediately.
The policy editor below shows row filters configured on the public.users and public.orders tables. The public.payments table has no row filters for contrast.
analyst · prod-users (us-east-1)
Version 3 · Updated2026-03-10 14:32:00 UTC
Assignments (3)
| Type | Name | Assigned | |
|---|---|---|---|
| User | [email protected] | 2026-02-20 11:00:00 UTC | |
| User | [email protected] | 2026-02-22 15:30:00 UTC | |
| Group | Data Team | 2026-03-01 09:00:00 UTC |
Operators
Row filters support the following operators. Values are passed as text parameters and PostgreSQL handles type coercion automatically.
| Operator | Example Value | Description |
|---|---|---|
= | active | Column equals the value |
!= | archived | Column does not equal the value |
> | 100 | Column is greater than the value |
< | 1000 | Column is less than the value |
>= | 2024-01-01 | Column is greater than or equal to the value |
<= | 2024-12-31 | Column is less than or equal to the value |
IN | us,eu,apac | Column matches any value in the comma-separated list |
NOT IN | test,staging | Column does not match any value in the list |
BETWEEN | 100,999 | Column is between the two comma-separated bounds (inclusive) |
IS NULL | Column value is NULL (no value needed) | |
IS NOT NULL | Column value is not NULL (no value needed) |
User Variables
Filter values can reference the connected developer's attributes using the double-brace syntax. User variables are resolved at query time, enabling dynamic per-user filtering without creating separate policies for each developer.
| Use Case | Column | Operator | Value |
|---|---|---|---|
| Tenant isolation | tenant_id | = | {{user.email}} |
| Department access | department | IN | {{user.groups}} |
When a user variable is used with the IN or NOT IN operator, the attribute value is split on commas to produce the list. With BETWEEN, both bounds can be user variables.
User variable resolution is fail-closed: when a referenced attribute is missing and has no default value, the query is denied with a permission error. This prevents accidental data exposure from misconfigured attributes.
How Filters Combine
Within a policy: multiple filters on the same table are combined with AND. A row must match all filters to be returned. For example, two filters on the orders table — region = us AND status = active — return only active US orders.
Across policies: per-policy predicates are combined with OR. A user assigned to multiple policies sees the union of rows from each policy, consistent with how table grants use a union model.
Bypass flag: a table grant with row filter bypass enabled disables all row filters for that table across all contributing policies. A policy that grants access to a table but defines no row filters also provides unfiltered access — the unfiltered path wins under the union model.
Example: Policy A filters orders to region = us. Policy B filters orders to region = eu. A user assigned to both policies sees orders from both regions (us OR eu). Adding a third policy with no filters on orders gives the user unfiltered access to all orders.
Limitations
- INSERT statements are not filtered — row filters apply to SELECT, UPDATE, and DELETE only.
- Database functions and stored procedures can bypass row filters. Restrict function access via grants or use PostgreSQL RLS for defense-in-depth.
- Filter values are text-only — complex types (arrays, JSON) work only when PostgreSQL can coerce the text representation.
- Each filter targets a single column. Cross-column comparisons and arbitrary SQL expressions are not supported.
Related
- Policies — policy configuration, table grants, and multi-policy resolution
- Data Masking — column-level masking presets and wildcard patterns