Row Filters, Column Masks, and ABAC Policies
Introduction
This lesson completes the Governance and Security section of the official exam outline (about 15% of the scored questions). The previous lesson controlled access at the level of whole objects: a principal can or cannot read a table. Real organizations need finer control. A regional sales manager should see only the rows of their region; a support agent should see the last four digits of a card number but not the whole value; an analyst should see the data but never the email addresses.
Unity Catalog offers three tools for this fine-grained control: row filters, column masks and, at scale, attribute-based access control (ABAC) policies driven by governed tags. Row filters and column masks are defined per table. ABAC applies the same logic automatically to every table that carries the right tags, across a whole catalog or schema. Databricks announced ABAC row filtering and column masking policies, governed tags and data classification as generally available in 2026; its documentation recommends ABAC for consistent rules across many tables, and per-table filters and masks when you need logic specific to one table or have not adopted ABAC yet.
Learning objectives
- Explain the difference between object-level privileges, row filters, column masks and ABAC policies.
- Write a row filter function and attach it to a table.
- Write a column mask function and attach it to a column.
- Use
is_account_group_member()to implement identity-based logic. - Explain ABAC: governed tags, policy scope, principals, conditions and the policy functions.
- Choose between a dynamic view, per-table filters/masks and ABAC.
- Name the limits and operational points to remember (privileges, performance, and what consumers see).
1. Why object-level privileges are not enough
Imagine the table prod.sales.orders, with columns order_id, region, customer_email and amount.
| Requirement | Object-level GRANT SELECT |
What is needed |
|---|---|---|
| EU managers see only EU rows | Cannot express it | Row filter |
| Everybody sees the table but the email is hidden except for support | Cannot express it | Column mask |
| All columns tagged as personal data are masked in 400 tables | Would need 400 separate definitions | ABAC policy with governed tags |
Before these features, engineers created dynamic views per table, with a CASE expression or a WHERE clause using is_account_group_member(). That still works, but the base table remains readable by anyone who finds it, and each table needs its own view. Row filters and masks are attached to the table itself, so any query on the table is protected, whatever the interface (notebook, SQL warehouse, dashboard, job).
2. Row filters
A row filter is a SQL user-defined function (UDF) that returns a BOOLEAN. It is attached to a table and receives the values of one or more columns of each row. Unity Catalog evaluates it for every query on the table; rows for which it returns TRUE are returned, and rows for which it returns FALSE (or NULL) are hidden. The reader does not see the filter in the query: the rows are simply absent.
-- 1. The function
CREATE OR REPLACE FUNCTION prod.security.region_filter(region STRING)
RETURN is_account_group_member('global-managers')
OR (is_account_group_member('eu-managers') AND region = 'EU')
OR (is_account_group_member('us-managers') AND region = 'US');
-- 2. Attach it to the table, naming the column(s) passed to the function
ALTER TABLE prod.sales.orders
SET ROW FILTER prod.security.region_filter ON (region);
-- Remove it
ALTER TABLE prod.sales.orders DROP ROW FILTER;
Points to remember:
- The function is a normal Unity Catalog object, so it can be stored in a dedicated security schema with its own, tightly controlled privileges.
is_account_group_member('group')returns true when the current user belongs to the account-level group; it is the preferred check in Unity Catalog.current_user()returns the user's identity and is useful for a mapping table (for exampleuser_regions), but a mapping table must itself be protected.- A table can have one row filter; the function may combine several conditions.
- The filter can also be given other columns, or a constant argument, but it must be deterministic and fast: it runs for every row of every query.
- Users need access to the table (
SELECTand theUSEprivileges) as usual; the row filter then trims what they receive. Setting a filter requires owning the table (orMANAGE) andEXECUTEon the function.
Concrete example. A bank stores loans(loan_id, branch_id, amount, ...). Branch managers must see only their branch. The security team creates branch_filter(branch_id) that compares branch_id with a lookup in a protected mapping table security.user_branches using current_user(). A manager runs SELECT COUNT(*) FROM loans and gets only the loans of their branch; an auditor in the group audit gets everything.
3. Column masks
A column mask is a SQL UDF attached to a column. It takes the column's value (and optionally other columns of the row) and returns the value to display. The return type must match the column's type, so that queries and tools do not break.
CREATE OR REPLACE FUNCTION prod.security.mask_email(email STRING)
RETURN CASE
WHEN is_account_group_member('pii-readers') THEN email
ELSE regexp_replace(email, '^[^@]+', '*****')
END;
ALTER TABLE prod.sales.orders
ALTER COLUMN customer_email SET MASK prod.security.mask_email;
-- Remove it
ALTER TABLE prod.sales.orders
ALTER COLUMN customer_email DROP MASK;
Members of pii-readers see maria@example.com; everybody else sees *****@example.com.
A mask can use additional columns of the same row through USING COLUMNS:
ALTER TABLE prod.sales.orders
ALTER COLUMN card_number SET MASK prod.security.mask_card USING COLUMNS (country);
Properties:
| Aspect | Row filter | Column mask |
|---|---|---|
| Attached to | A table | A column |
| Function returns | BOOLEAN |
Same type as the column |
| Effect | Hides rows | Replaces values |
| Number per object | One per table | One per column |
| Removing | ALTER TABLE ... DROP ROW FILTER |
ALTER COLUMN ... DROP MASK |
Masks are not encryption. They control what a query returns. Anyone with higher privileges (for example the owner of the function logic through group membership) can see the raw value, and the stored files are unchanged.
4. Dynamic views versus filters and masks
| Criterion | Dynamic view | Row filter / column mask | ABAC policy |
|---|---|---|---|
| Protection point | A view, base table still readable by privileged users | The table itself | The table itself, by tags |
| Reuse | One view per table | One function reused by many tables, attached manually | One policy for many tables automatically |
| Effort for 400 tables | Very high | High (attach to each) | Low (tag once, policy applies) |
| Typical use today | Legacy or ad hoc | Table-specific logic | Consistent enterprise rules |
5. Attribute-based access control (ABAC)
ABAC decides access from attributes of the data (tags) and, in policies, of the principals (their groups), rather than from a list of grants per object. In Unity Catalog the building blocks are:
- Governed tags: account-level tags whose name, allowed values and the permission to assign them are controlled centrally. Unlike free tags, a governed tag cannot be created or applied by just anyone; a data steward decides who may assign
pii = email, for example. - Policies: rules that say "for the objects matching this tag condition, apply this row filter or column mask function, to these principals, except those principals".
- Policy functions: the same kind of SQL UDFs as in sections 2 and 3, now referenced by a policy.
A policy is defined on a scope: a catalog, a schema or a table. It is inherited downward, so a policy at catalog level applies to all current and future matching tables below it. The condition uses the tags of the tables and columns, and the principals can be users, service principals or groups, with an EXCEPT list for those not concerned.
An illustrative policy that masks every column tagged as an email for everyone except the privacy team (check the current documentation for the exact syntax):
CREATE POLICY mask_emails
ON CATALOG prod
COLUMN MASK prod.security.mask_email
TO `account users`
EXCEPT `pii-readers`
FOR TABLES
MATCH COLUMNS hasTagValue('pii', 'email') AS email_col
ON COLUMN email_col;
A row filter policy follows the same pattern, with ROW FILTER and a condition on the table, for example tables tagged domain = sales that have a column tagged region.
Operational facts:
- Creating a policy requires the right to manage it on the securable (the
MANAGEprivilege on the catalog, schema or table, or ownership) andEXECUTEon the function used. - Tagging the data is the key step: a column without the tag is not affected. Governance teams often combine ABAC with data classification, which automatically detects and tags sensitive columns.
- Policies are evaluated at query time, and the policy applies whatever the interface used.
- Some ABAC capabilities, such as DENY policies and the identity attribute functions, were still listed in beta in the documentation reviewed for this course. They are not part of the core exam concepts.
Concrete example. A pharmaceutical group has 1,200 tables across 30 schemas. Compliance requires that every column holding a patient identifier is masked for everybody except the clinical-data team. Without ABAC, an engineer would attach a mask to each column, then forget new tables. With ABAC, the data steward tags the identifier columns with pii = patient_id (or data classification does it), one catalog-level policy applies mask_patient_id to every tagged column, now and in the future, and the clinical-data group is the exception.
6. Operational points
- Compute: tables protected by filters, masks or policies are only readable on supported compute (serverless and recent Databricks Runtime versions on standard or dedicated access mode). Older runtimes and unsupported modes are denied rather than leaking data.
- Performance: the function runs on the query path. Keep it simple: no heavy subqueries or external calls. A mapping-table lookup should be small.
- Downstream objects: views built on a protected table respect the protection for the user querying them; materialized views and streaming tables have specific rules that the documentation lists, so check them before relying on them.
- Testing: test with accounts in each group, and check the result of
SELECT *,COUNT(*), joins and aggregations, because a mask applied before an aggregation changes the aggregate. - Auditing: access to protected tables appears in the audit logs and system tables, and lineage keeps working.
Worked example: protecting customer data in a shared orders table
Scenario. prod.sales.orders has order_id, region, customer_email, card_last4 and amount. Requirements:
- Regional managers see only their region's rows; the group
global-financesees everything. - Everybody except
support-tier2seescustomer_emailmasked. - The same email rule must apply to 80 other tables in the catalog
prod.
Step 1: Row filter on this table. Table-specific logic fits a per-table row filter.
CREATE OR REPLACE FUNCTION prod.security.region_filter(region STRING)
RETURN is_account_group_member('global-finance')
OR (is_account_group_member('eu-managers') AND region = 'EU')
OR (is_account_group_member('us-managers') AND region = 'US');
ALTER TABLE prod.sales.orders SET ROW FILTER prod.security.region_filter ON (region);
Step 2: Email masking for 80 tables. Create the mask function mask_email, have the steward tag every email column with pii = email, then create one catalog-level ABAC column-mask policy for all principals except support-tier2.
Step 3: Grants. Make sure the groups hold USE CATALOG, USE SCHEMA and SELECT, because filters and masks restrict what is returned and do not grant access.
Step 4: Test. A user in eu-managers runs SELECT region, COUNT(*) FROM prod.sales.orders GROUP BY region: only EU appears and customer_email is masked. A user in support-tier2 sees the real email but only the rows allowed by the filter.
Why the alternatives are worse. A dynamic view per table protects only the view and requires 80 definitions. Copying a masked version of the table creates stale duplicates. Revoking SELECT from analysts blocks the legitimate use of non-sensitive columns.
Common pitfalls and exam tips
- Filters and masks restrict results; they do not replace
GRANT SELECT. The user still needsUSE CATALOG,USE SCHEMAandSELECT. - A row filter function returns BOOLEAN; a column mask function returns the column's type.
ALTER TABLE ... SET ROW FILTER ... ON (cols)andALTER COLUMN ... SET MASKattach them;DROP ROW FILTERandDROP MASKremove them.- Use
is_account_group_member()for group checks in Unity Catalog, not workspace-local group checks. - One row filter per table, one mask per column.
- ABAC = governed tags + policies; a policy at a catalog or schema is inherited by matching objects below it and applies to future tables too.
- For many tables with the same rule, choose ABAC; for one-off logic, a table filter or mask; dynamic views are the legacy approach.
- Masking is not encryption and does not change stored files.
Exam tip: If a question says "hide the values of a sensitive column from most users while keeping the table readable", the answer is a column mask. If it says "each manager must see only their own region's rows", it is a row filter. If it says "the same protection for every column holding personal data across many tables, including future ones", it is an ABAC policy with governed tags.
Mini case study
Situation. A retailer's table prod.crm.customers contains customer_id, country, email and phone. Data engineers complain that marketing analysts can read emails and phone numbers. The compliance officer wants: (a) analysts see only customers of their own country; (b) email and phone hidden for everybody but the group crm-admins; (c) the phone rule to extend automatically to new tables with a phone column.
Task. Propose the design.
Solution.
- Row filter
country_filter(country)attached withALTER TABLE ... SET ROW FILTER ... ON (country); analysts are in groups per country,crm-adminssees all. - For
email, a column mask attached withALTER COLUMN email SET MASK mask_email, as this table is the only one concerned. - For
phone, the steward tags phone columns with a governed tag such aspii = phone, and a catalog-level ABAC column-mask policy appliesmask_phoneto tagged columns for all principals exceptcrm-admins. New tables with a tagged phone column are covered automatically. - Check that the analysts hold
USE CATALOG,USE SCHEMAandSELECT, and test with an analyst account, an admin account and an account in no group.
Key terms
- Row filter: SQL UDF returning BOOLEAN that decides which rows a user sees.
- Column mask: SQL UDF that returns the displayed value of a column.
- Dynamic view: view that changes its result depending on the reader's identity.
is_account_group_member(): function testing membership of the current user in an account-level group.- ABAC: access control driven by tags and policies instead of per-object grants.
- Governed tag: centrally controlled tag with allowed values and assignment permissions.
- Policy scope: catalog, schema or table at which an ABAC policy is defined and inherited.
- Data classification: automatic detection and tagging of sensitive data.
Summary checklist
- I can write and attach a row filter, and remove it.
- I can write and attach a column mask, and remove it.
- I can explain why filters and masks do not replace
GRANT. - I can describe governed tags, ABAC policies, their scope and inheritance.
- I can pick between a dynamic view, a table-level filter or mask, and ABAC.
- I know the main operational points: supported compute, function performance, testing.
Official sources
- Exam guide: https://www.databricks.com/sites/default/files/2026-05/databricks-certified-data-engineer-associate-exam-guide-may-2026.pdf (opens in a new tab)
- Row filters and column masks: https://docs.databricks.com/aws/en/data-governance/unity-catalog/filters-and-masks/ (opens in a new tab)
- Core concepts for attribute-based access control: https://docs.databricks.com/aws/en/data-governance/unity-catalog/abac/core-concepts (opens in a new tab)
- ABAC GA announcement: https://www.databricks.com/blog/abac-row-filtering-and-column-masking-policies-governed-tags-and-data-classification-are-now (opens in a new tab)
- Access control in Unity Catalog: https://docs.databricks.com/aws/en/data-governance/unity-catalog/access-control/ (opens in a new tab)
