When to use ABAC vs table-level row filters and column masks
Unity Catalog supports two approaches for row-level and column-level security: ABAC policies and table-level row filters and column masks. Neither approach grants access to data on its own — both add restrictions on top of existing object-level privileges. You must grant base table access separately through object-level permissions (GRANT).
The core difference is where the restrictions are defined. Table-level row filters and column masks apply sensitivity controls directly on individual tables using ALTER TABLE. Table owners manage their own data protection without needing a governed tag system. This is straightforward for a small number of tables, but each table must be configured individually, and table owners can modify or remove their own filters and masks.
ABAC policies attach at the catalog, schema, or table level and match tables and columns dynamically based on governed tags. A policy defined at the catalog level applies to all tables in that catalog, and individual table owners can't remove, modify, or bypass it. The policy lives on the catalog and is evaluated by Unity Catalog before the query reaches the runtime. This lets higher-level administrators enforce organization-wide rules and ensure that lower-level administrators and owners can't circumvent them.
Detailed comparison summary
This table summarizes the differences between ABAC policies and table-level row filters and column masks.
Consideration | ABAC policies | Table-level row filters and column masks |
|---|---|---|
SQL syntax |
|
|
Scope | All tables within the policy scope (catalog, schema, or table) and its descendants. New tagged tables are covered automatically. | A single table where the filter or mask is configured. Each table must be configured individually. |
Dynamic matching | Tables and columns are matched dynamically based on governed tags using | No dynamic matching. Filters and masks are bound to specific tables and columns. |
Targeted principals |
| Identity functions like |
Policy governance | Policies can be set by catalog or schema owners. After being set at a higher level, table owners cannot override, modify, or remove them. | Managed by table owners, who can modify or remove filters and masks on their own tables. |
Unsupported features | Operations like time travel, cloning, and Delta Sharing can be run by principals in the | No |
Effective policies |
| Directly visible on the table definition. |
Auditability |
|
|
In general, use ABAC policies when:
- You need consistent access rules across many tables, schemas, or catalogs.
- Your organization separates duties. For example, policy authors define rules, and data stewards classify data with tags.
- Your data estate is growing and you want new tables covered automatically when they are tagged.
- You need the
EXCEPTclause to allow operations like time travel, Delta Sharing, or full query optimization for specific principals.
In general, use table-level row filters and column masks when:
- Each table has strict, specific logic that doesn't generalize to other tables.
- Table owners should manage their own filters and masks directly, without a centralized tag system.
- You have a small, stable set of tables that change infrequently.
Combining ABAC and table-level row filters and column masks
ABAC and table-level row filters and column masks can co-exist on the same table. At query time, the policies are evaluated independently for the querying user with the following rules:
- Only one distinct row filter can apply.
- Only one distinct column mask can be resolved per column.
Databricks evaluates conflict by comparing the functions applied, not the data output. If both an ABAC policy and a table-level filter or mask apply the same row filter or column mask function for the same user, Databricks allows execution. If they apply different functions, Databricks blocks access and returns an error, even if those functions produce identical data output.
For details on conflict resolution and troubleshooting, see Rules for multiple filters and masks.
Row-level and column-level security with dynamic views
Dynamic views can also implement row-level and column-level security by embedding identity functions like current_user() and is_account_group_member() directly in the view definition. Dynamic views, row filters, and column masks all apply filtering or transformation logic at query time, but they differ in how they are managed, scoped, and exposed to users.
Applies to | How it's managed | Best used for | |
|---|---|---|---|
Dynamic views | Views | SQL logic in the view definition | Fine-grained access control that spans multiple source tables or reshapes data for sharing |
Row filters and column masks | Tables and columns | ABAC policies or table-level assignment | Row-level and column-level access control without introducing new objects |
Use dynamic views when you need fine-grained access control that spans multiple source tables or reshapes data for sharing. Use row filters and column masks when you want to control access on individual tables without introducing new objects.
For example, a dynamic view can mask an email column for non-auditors:
CREATE VIEW sales_redacted AS
SELECT
user_id,
CASE
WHEN is_account_group_member('auditors') THEN email
ELSE regexp_extract(email, '^.*@(.*)$', 1)
END AS email,
country,
product,
total
FROM sales_raw
Dynamic views fully support query optimization and predicate pushdown, so they can offer better query performance than row filters and column masks. They also prevent users from modifying the underlying tables.
However, dynamic views have two drawbacks for data governance:
- Limited auditing: Dynamic views lack semantic metadata such as tags or policy definitions in system tables, which makes them harder to audit at scale.
- Vulnerability to probing: Because they lack a
SecureViewbarrier, they don't protect against probing attacks, where a user crafts a predicate with side effects to infer information about filtered rows. See Understand predicate pushdown on protected tables.