The question it answers
Row rules decide which rows. Masking decides which values in those rows - the case where somebody may legitimately work with a dataset but must not read a particular field at full fidelity.
A support analyst needs to match a customer without reading the full document number. A regional manager needs salary distribution without individual salaries. Those are not row problems.
What a mask can express
Masks are declared in a dialect-neutral form and evaluated at execution:
| Mask | Effect | Typical use |
|---|---|---|
| Null | The value is removed | A field that must not be read at all |
| Hash | A stable one-way digest | Joining and counting distinct values without reading them |
| Partial reveal | The first N characters, then a marker | Matching a record against a customer's own reference |
| Round to a bucket | Value rounded to a multiple | Distributions and cohorts without individual figures |
The hash case is worth noting: it preserves what analysis actually needs - equality and cardinality - while removing what it does not need, which is the value itself.
Conflicts resolve by priority
Where more than one mask applies to a column, the highest priority wins. One value per column per caller, not an ambiguous stack.
Applied in the plan
A mask annotates the output column in the query plan, and is applied during execution. It is not a client-side transformation, which means:
- a dashboard, a page of an application, an MCP client and an AI agent are masked identically;
- an export contains masked values, because the export is the query result;
- and no caller can request the unmasked value by asking a different way, since there is no path where the mask is applied by the caller.
Related
- Row-level security - which rows, and how the decision is made.
- Data engine - where the plan is compiled and executed.