Column-Level Security
What this covers
Column-level security restricts which columns a persona can see. It works through data tag restrictions: you tag sensitive columns, then configure a persona to be restricted from specific tags. At query time, the persona gate either blocks the query (if a restricted column is explicitly requested) or silently excludes the column (if it would have been auto-included via SELECT *). This article explains how the mechanism works, how to configure it, and how it composes with row-level security.
How it works
Column-level security is a two-layer system:
- Data tags label columns by sensitivity (e.g.
PII,Financial). See Data Tags. - Persona tag restrictions define which tags a persona cannot access.
When a query arrives under a persona:
- If the query explicitly requests a column tagged with a restricted tag → the query is rejected with a generic
OBJECT_NOT_AVAILABLE403 (the error does not name the column, tag, or persona). - If the query uses SELECT * and a restricted column would be included → the column is silently dropped from the result set. If every column the star resolves to is restricted, the query is rejected — there is nothing left to return.
- If the query uses advanced SQL shapes (subqueries, CTEs, set operations, window functions, and similar) → it is rejected with a 403, because such shapes cannot be checked column-by-column. Rewrite it as a plain
SELECTover the model.
Hidden is not column security. A measure or column marked hidden (is_hidden) is dropped from browse lists and from SELECT *. Anyone who types the name can still query it. That is curation. To stop a Sales persona from reading cost, put the measure on a persona allow-list or restrict its column with a data tag. Then both SELECT fee_amount and SELECT SUM(fee_amount) return OBJECT_NOT_AVAILABLE. See Configure Personas.
Restrictions follow a column wherever it is used, not just when it is named directly:
- A calculated measure whose formula reads a restricted column is blocked too.
- A time-variant measure (YTD, prior year, ...) of a measure built on a restricted column is blocked too.
- A computed attribute (user-defined attribute) that reads a restricted column is blocked too.
Relationship to row-level security
Row security and column security are independent axes that compose:
| Axis | Controls | Mechanism |
|---|---|---|
| Row security | Which rows a user sees | WHERE clause filters injected by the security rule compiler |
| Column security | Which columns a user sees | Column exclusion based on persona tag restrictions |
A persona can have both row rules and column restrictions. They apply simultaneously: first the column gate narrows the visible columns, then the row security filter narrows the visible rows. Neither depends on the other.
Everything built from the model inherits this restriction
A restricted column is not just missing from a plain query. It is missing from the model itself, for that persona.
Everything else builds from that same smaller model too. This includes a named set, a saved Named Query, and a KPI. It also includes a summary table, a subtotal, a preview, and an export. It even includes an answer from the assistant. So the restricted column cannot appear in any of them either.
Some measures and KPIs are built from other columns. If one of them uses a restricted column, that measure or KPI is blocked too. You restrict the raw column once. You do not have to find and restrict everything built from it.
A summary table built with the restricted column is not served to that persona at all. Tessallite computes the answer live instead, from the model as the persona sees it. A named set built on a restricted measure is not offered to the persona in the picker. The assistant is grounded on the same restricted catalogue. So it cannot describe, name, or chart a column the persona could not query directly.
Restrict a column once, here. It stays restricted everywhere that column feeds into.
Setting restrictions
- Open the persona in the Personas panel (Toolbelt → Personas → click the persona name).
- Scroll to the Column Restrictions section.
- Toggle the data tags that this persona should NOT see. Each toggled tag restricts all columns assigned to it.
- Click Save.
What happens at query time
Explicit column request
If a persona restricted from PII runs:
SELECT customer_name, email, revenue FROM sales
And email is tagged PII, the query is rejected with:
403 OBJECT_NOT_AVAILABLE
The error does not name the column, the tag, or the persona.
SELECT * (auto-inclusion)
If the same persona runs:
SELECT * FROM sales
The email column is silently excluded from the result. The persona sees customer_name, revenue, and all other non-restricted columns. No error is raised.
Worked example
Scenario: An "External Partner" persona should not see PII data.
- Create the PII tag: In Data Tags, create a tag named
PII. - Assign columns: Tag
customers.email,customers.phone,orders.shipping_addressasPII. - Create the persona: In Personas, create "External Partner".
- Set restrictions: In the persona's Column Restrictions, toggle
PIIto restricted. - Test: Query as the persona.
SELECT *returns all columns except email, phone, and shipping_address.SELECT email FROM ...returnsOBJECT_NOT_AVAILABLE.
Pitfalls
- New columns are visible by default. When you add a column to a table that has tagged columns, the new column is untagged and therefore visible to all personas. Always review new columns after schema changes.
- Calculated measures that depend on restricted columns are blocked. If a calculated measure (or a time variant, or a computed attribute) reads a restricted column anywhere in its formula, restricted personas get
OBJECT_NOT_AVAILABLE. Either remove the dependency or don't restrict the column. - Tag assignment is model-scoped. Restricting
PIIin one model doesn't affect another model, even if they share the same source tables. Each model's tags and restrictions are independent. - Restrictions travel with exports. Data tags and persona restrictions are part of the model snapshot, so project export/import and version restore keep your column security intact.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
SELECT * returns fewer columns than expected | One or more columns carry a tag this persona is restricted from | Check the column's data tags in Data Tags, and the persona's Column Restrictions |
Query returns OBJECT_NOT_AVAILABLE for a column you expect to see | The column, or a column feeding a calculated measure you queried, is tagged with a restricted tag | Remove the restriction, or don't restrict the tag if this persona needs the column |
| A new column shows up for a persona that should not see it | New columns are untagged by default and visible until tagged | Tag the column and confirm the persona's Column Restrictions cover the tag |
| A named set, KPI, or summary table is missing for this persona but present for another | It reads a restricted column somewhere in its definition | Expected. It cannot be built from this persona's smaller model, so it is not offered |
| Restriction disappeared after importing a project | The tag or the restriction was not part of the imported snapshot | Re-check Data Tags and Column Restrictions after import. Both travel with the model snapshot. But they only travel if they existed at export time |