Row filters and column masks
This page provides guidance for using row filters and column masks to filter sensitive data in your tables.
What are row filters?
Row filters let you control which rows a user can access in a table based on custom logic. At query time, a row filter evaluates a condition and returns only the rows that meet it. This is commonly used to implement row-level security—for example, restricting users to records from a specific region, department, or account.
Row filters are defined as SQL user-defined functions (UDFs), and can also incorporate Python or Scala logic when wrapped in a SQL UDF. You can apply row filters per table, or centrally through ABAC policies using governed tags.
What are column masks?
Column masks control what values users see in specific columns, depending on who they are. At query time, the mask replaces each reference to a column with the result of a masking function. This allows sensitive data, such as SSNs or emails, to be redacted or transformed based on user identity or role.
Each column can have one mask. The mask is defined as a SQL UDF, and can optionally wrap Python or Scala logic, and must return a value of the same type as the column being masked. Column masks can also take other columns as inputs, for example, to vary behavior based on multiple attributes.
Like row filters, column masks can be applied per table or managed centrally through ABAC policies. They operate at query time and integrate seamlessly with standard SQL, notebooks, and dashboards.
When should you use dynamic views vs. filters and masks?
Dynamic views, row filters, and column masks all let you apply filtering or transformation logic at query time — but they differ in how they are managed, scoped, and exposed to users.
Feature | Applies to | Managed using | Naming impact | Best used for… |
---|---|---|---|---|
Dynamic views | Views | SQL logic | Creates a new object name | Sharing filtered data or spanning multiple tables |
Row filters | Tables | ABAC or mapping tables | Table name unchanged | Row-level access control tied to user or data tags |
Column masks | Tables/columns | ABAC or mapping tables | Table name unchanged | Redacting sensitive column data based on identity |
- Use dynamic views when you need a read-only layer across one or more source tables, especially for data sharing or applying logic across multiple inputs.
- Use row filters and column masks when you want to apply logic directly to a table, without changing the table name or introducing new objects. These can be managed using either ABAC policies (recommended) or manually on tables.
For a full comparison, see Access control methods compared.
How to apply filters and masks
You can implement row filters and column masks in two main ways:
-
Using ABAC policies (Beta): Apply filters and masks centrally using governed tags and reusable policies. This approach scales across catalogs and schemas and reduces the need for table-by-table configuration. ABAC policies are more secure than manual table-level policies because they can be defined by higher-level admins and cannot be overridden by table owners, which helps enforce centralized access controls. They are also more performant in many cases, since filtering and masking logic in ABAC policies is evaluated more efficiently than in table-specific UDFs. Databricks recommends using ABAC for most use cases. To apply filters and mask using ABAC see Unity Catalog attribute-based access control (ABAC).
-
Manual assignment per table: Apply filters and masks by assigning functions directly to individual tables and columns. This method can use mapping tables or other custom logic. It allows for fine-grained, table-specific control but is harder to scale and maintain. For more information, see Manually apply row filters and column masks.
Performance recommendations
Row filters and column masks control data visibility by ensuring that users cannot view the contents of values of the base tables before filtering and masking operations. They perform well in response to queries under common use cases. In less common applications, where the query engine must choose between optimizing query performance and protecting against leaking information from the filtered/masked values, it will always make the secure decision at the expense of some impact on query performance. To minimize this performance impact, apply the following recommendations:
- Use simple policy functions: Policy functions with fewer expressions often perform better than more complex expressions. Avoid using mapping tables and expression subqueries in favor of simple CASE functions.
- Reduce the number of function arguments: Databricks cannot optimize away column references to the source table resulting from policy function arguments, even if these columns are not used in the query. Use policy functions with fewer arguments, as the queries from these tables will perform better.
- Avoid adding row filters with too many AND conjuncts: Because each table supports adding at most one row filter, a common approach is to combine multiple desired policy functions with
AND
. However, with each additional conjunct, the chances increase that conjunct(s) include components mentioned elsewhere in this table that could affect performance (such as mapping tables). Use fewer conjuncts to improve performance. - Use deterministic expressions that cannot throw errors in table policies and queries from these tables: Some expressions may throw errors if the provided inputs are not valid, such as ANSI division. In such cases, the SQL compiler must not push operations with those expressions (such as filters) too far down in the query plan to avoid the possibility of errors like “division by zero” that reveal information about values before filtering and/or masking operations. Use deterministic expressions that never throw errors, such as
try_divide
. - Run test queries over your table to gauge performance: Construct realistic queries that represent the workload you expect for your table with row filters or column masks and measure the performance. Make small modifications to the policy functions and observe their effects until you reach a good balance between performance and expressiveness of the filtering and masking logic.
For more best practices and example UDFs, see UDFs for ABAC policies best practices.
Limitations
- Databricks Runtime versions below 12.2 LTS do not support row filters or column masks. These runtimes fail securely, meaning that if you try to access tables from these runtimes, no data is returned.
- Delta Sharing providers cannot share tables with row-level security or column masks. However, Delta Sharing recipients can apply row filters and column masks only to shared tables and foreign tables—not to streaming tables or materialized views.
- You cannot use Iceberg REST catalog or Unity REST APIs to access tables with row filters or column masks.
- You cannot apply row-level security or column masks to a view.
- Time travel does not work with row-level security or column masks.
- Path-based access to files in tables with policies is not supported.
- Row-filter or column-mask policies with circular dependencies back to the original policies are not supported.
- Deep and shallow clones are not supported on tables that have row-level security or column masks.
MERGE
statements do not support tables with row filter or column-mask policies that contain nesting, aggregations, windows, limits, or non-deterministic functions.- Delta Lake APIs are not supported.
Dedicated access mode limitation
You cannot access a table with row filters or column masks from a dedicated access compute resource on Databricks Runtime 15.3 or below. You can use dedicated access mode on Databricks Runtime 15.4 LTS or above if your workspace is enabled for serverless compute. However, only read operations are supported on Databricks Runtime 15.4 through 16.2. Write operations (including INSERT
, UPDATE
, and DELETE
) require DBR 16.3 or above and must use supported patterns such as MERGE INTO
.
For more information, see Fine-grained access control on dedicated compute.