DAAM
Alpha

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:

  1. Navigate to the database and open the policy you want to edit.
  2. In the Row Filters section of a table grant, click Add Filter.
  3. Select a column, choose an operator, and enter a value.
  4. 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.

Interactive preview
Schema data available (reported2026-03-12 08:15:00 UTC). Table and column dropdowns are populated from the database schema.

analyst · prod-users (us-east-1)

Version 3 · Updated2026-03-10 14:32:00 UTC

Table Grants

Table (schema.table)Selected TableSELECTINSERTUPDATEDELETE
Quick:

Row Filters

Restrict which rows users can access by injecting WHERE conditions. Supports dynamic filtering via user attribute variables.

public.users

ColumnOperatorValue

public.orders

ColumnOperatorValue

public.payments

ColumnOperatorValue

No row filters configured.

Masking Rules

Standard masking transforms query output — developers see masked values but can reference the column freely in SQL.

Strict masking additionally blocks the column from being used in WHERE, GROUP BY, JOIN, HAVING, and other query clauses. This prevents inference attacks where a developer could deduce masked values through query predicates.

Strict-masked columns can only appear in SELECT output, ORDER BY, and RETURNING clauses.

Pattern (schema.table.column)TypeStrict

Test Query

Validates against the grants and masking rules above, including unsaved changes. Save the policy to apply changes to agents.

Checking...

Assignments (3)

TypeNameAssigned
User[email protected]2026-02-20 11:00:00 UTC
User[email protected]2026-02-22 15:30:00 UTC
GroupData Team2026-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.

OperatorExample ValueDescription
=activeColumn equals the value
!=archivedColumn does not equal the value
>100Column is greater than the value
<1000Column is less than the value
>=2024-01-01Column is greater than or equal to the value
<=2024-12-31Column is less than or equal to the value
INus,eu,apacColumn matches any value in the comma-separated list
NOT INtest,stagingColumn does not match any value in the list
BETWEEN100,999Column is between the two comma-separated bounds (inclusive)
IS NULLColumn value is NULL (no value needed)
IS NOT NULLColumn 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 CaseColumnOperatorValue
Tenant isolationtenant_id={{user.email}}
Department accessdepartmentIN{{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.
  • Policies — policy configuration, table grants, and multi-policy resolution
  • Data Masking — column-level masking presets and wildcard patterns