Purpose limitation is the data-protection principle most likely to be true on paper and false in the query engine. Everyone agrees marketing-consent data should not train a risk model. Almost nobody can stop it.
GDPR Article 5(1)(b) is short and unambiguous: personal data is collected for specified, explicit and legitimate purposes and not further processed in a manner incompatible with those purposes. In a modern warehouse that sentence has to survive contact with a single analyst who has read access to everything and a deadline. They write a join. The join pulls a consent flag that was gathered for email marketing into a feature table for a credit or fraud model. Nothing in the platform objects, because the platform has no idea what the data was for. The control existed only as a slide, a policy document, and a hope that everyone remembers the rules.
A control that depends on everyone remembering the rules is not a control. It is the same failure mode as a governance tool that ships and then nobody uses it: the intent is sound, the enforcement is absent. The fix is to make lawful purpose a property the query engine evaluates and enforces, at execution, on every query, whether or not the analyst was paying attention.
Free · 4 minutes
When two of your systems disagree, do you know which one to believe?
Fourteen questions on ownership, lineage, and quality — the difference between a number on a dashboard and a number you could defend. Banded finding on screen, full sheet by email.
Why the honour system always loses
The usual answer to purpose limitation is documentation. A record of processing activities lists the purposes. A data catalogue tags a column with a description. A training deck tells analysts not to cross the streams. None of this touches the execution path. The catalogue entry is metadata a query never reads; the training is advisory; the record of processing is written for the regulator, not the compiler. When the join runs, none of that machinery is in the loop.
The second-most-common answer is broad table permissions plus trust: give the risk team access to the risk schema, the marketing team access to the marketing schema, and assume the walls hold. They do not, because analytics is precisely the business of joining across schemas. The moment someone has legitimate read access to two datasets for two different purposes, nothing structural prevents combining them for a third purpose that neither consent covered. Permissions answer “can this identity see this table”. Purpose limitation asks a different question: “is this use of this column compatible with why the data was collected”. Those are not the same question, and access control alone cannot express the second.
Bind purpose to identity, not to a checkbox
There is a tempting design where the analyst declares a purpose per query — a free-text tag, a “reason for access” prompt — and the engine checks it. Resist it. A purpose the engine cannot verify is just another honour system with extra steps; the analyst types “fraud investigation” and proceeds exactly as before. Self-declared purpose is auditable after the fact, which is useful, but it is not enforcement.
Enforceable purpose is purpose bound to identity. The querying context — the role, the workspace, the warehouse the query runs under — carries the set of purposes it is entitled to. Each column of personal data carries the purpose it was lawfully collected for. The engine compares the two at execution and, on a mismatch, masks the value or removes the row. The analyst working in the risk-model workspace is not running under a role that holds the marketing-consent purpose, so the consent column resolves to null for them however they write the join. The rule stops being a thing to remember and becomes a thing the platform does.
This is the same shift that makes secondary-use data platforms defensible. When a health data body builds patient preference into every secondary-use query rather than into a consent form filed elsewhere, the preference is enforced because it sits in the execution path. Purpose limitation deserves the same treatment: not a permit stapled to the front of a project, but a predicate on the data itself.
Tagging columns with their lawful purpose
Both major warehouses give you the primitives. In BigQuery, policy tags in a Data Catalog taxonomy classify columns, and access is granted through the Data Catalog Fine-Grained Reader role; the authorisation check runs at query time and can substitute null, default or hashed values through dynamic data masking. That gets you column-level gating, but the branching logic for “permitted purpose versus this query’s purpose” has to live in how you structure and grant the taxonomy.
Snowflake expresses the logic more directly. A tag carries the column’s permitted purpose as its value; a tag-based masking policy attached to that tag reads the value at query time with SYSTEM$GET_TAG_ON_CURRENT_COLUMN and compares it against what the current role is entitled to. One policy, defined once, then propagates to every column you tag. A row access policy does the equivalent job at the row grain, so a role without the marketing purpose cannot even see marketing-consent rows to join them.
-- Snowflake: purpose limitation enforced in the query engine.
-- A role may only read a personal-data column whose lawful
-- purpose is one that role is entitled to. Everything else masks.
-- 1. The grant of purposes to querying identities. This is the
-- authorisation surface a DPO reviews, not a free-text prompt.
CREATE OR REPLACE TABLE governance.purpose_grants (
role_name STRING,
purpose STRING -- e.g. 'MARKETING', 'RISK_MODELLING'
);
INSERT INTO governance.purpose_grants VALUES
('MARKETING_ANALYST', 'MARKETING'),
('RISK_MODELLER', 'RISK_MODELLING');
-- Note: RISK_MODELLER is deliberately NOT granted 'MARKETING'.
-- 2. The tag that labels each column with why it was collected.
CREATE OR REPLACE TAG governance.permitted_purpose;
-- 3. One masking policy, keyed on the column's own purpose tag
-- versus the querying role's entitlement.
CREATE OR REPLACE MASKING POLICY governance.enforce_purpose
AS (val STRING) RETURNS STRING ->
CASE
WHEN EXISTS (
SELECT 1
FROM governance.purpose_grants g
WHERE g.role_name = CURRENT_ROLE()
AND g.purpose =
SYSTEM$GET_TAG_ON_CURRENT_COLUMN('governance.permitted_purpose')
)
THEN val
ELSE NULL -- non-permitted purpose: value never leaves the engine
END;
-- 4. Attach the policy to the tag once; it follows the tag everywhere.
ALTER TAG governance.permitted_purpose
SET MASKING POLICY governance.enforce_purpose;
-- 5. Label a real column. A RISK_MODELLER joining this now gets NULL.
ALTER TABLE crm.contacts MODIFY COLUMN email_consent
SET TAG governance.permitted_purpose = 'MARKETING';
-- 6. Row-grain equivalent: a role without the purpose sees no rows.
CREATE OR REPLACE ROW ACCESS POLICY governance.consent_purpose_rap
AS (row_purpose STRING) RETURNS BOOLEAN ->
EXISTS (
SELECT 1 FROM governance.purpose_grants g
WHERE g.role_name = CURRENT_ROLE()
AND g.purpose = row_purpose
);
ALTER TABLE consent.records
ADD ROW ACCESS POLICY governance.consent_purpose_rap ON (purpose);
The important line is the ELSE NULL. The non-permitted value is never materialised into the result set, so it cannot be joined, aggregated or exported. The analyst can write any query they like; the engine simply refuses to hand over data outside the purpose the querying identity holds. The rule is enforced by construction rather than by conscience.
What a DPO or supervisor can actually see
The value of moving purpose into the engine is not only that misuse stops. It is that the control becomes evidential. You can show the grants table — a legible map of which identities hold which purposes. You can show the policy definition. You can show, from query history, that a risk-modelling role’s attempts to read marketing-consent columns returned null, on a specific date, against a specific table. That is a far stronger answer to a regulator’s compatibility question than a training register and a promise. It is the difference between a control that was delivered and one that is actually used.
None of this replaces the legal work of defining purposes, documenting them, and deciding what counts as a compatible further use — the same discipline that a well-run secondary-use data permit forces you to do up front. The taxonomy of purposes is a governance decision, not an engineering one. But once those purposes exist, leaving their enforcement to human memory is a choice, and it is the wrong one.
Purpose limitation you cannot enforce in the query engine is not a control. It is a description of the incident you will have to explain later.
Free interactive tool
Website compliance checklist
What your site has to do, based on what it actually does
Answer as much or as little as you like — the list builds as you go. Nothing is stored against your name and no email is required.
Everything that applies
Ordered by what to do first: legal requirements you can close quickly, then larger pieces of work, then what is expected rather than required. Not exhaustive, and not a legal audit.
Dated PDF, yours to keep or circulate.
Free interactive tool
Interactive deadline calculator
Check which regulations apply to you and when
Regulation across the EU, UK, US and Asia-Pacific has moved considerably in the past eighteen months, and several headline dates have shifted more than once. Twelve questions, about three minutes.
Results are shown on screen — no email required. A dated summary is available to download, and can be sent on if that's more useful. What we do with your answers.
Most technology problems are not technology problems. They are control problems.
The systems exist. The investment has been made. The question is whether leadership can understand, direct, evidence, and sustain what those systems produce. Find out where control exists — and where it only appears to.
Full Governance by Sixteen Pillars
Govern your business. Prove your compliance.
A board assurance cockpit for EU-regulated financial firms — tamper-evident, hash-chained proof of governance across DORA, GDPR, NIS2, ISO 27001, the EU AI Act and MiCA. In development.
See what's coming