I tried to verify how the 6 types of data protection policies on the provider side work on the consumer side in Snowflake Data Sharing

I tried to verify how the 6 types of data protection policies on the provider side work on the consumer side in Snowflake Data Sharing

We empirically verified the behavior of six data protection policy types in Snowflake Direct Share from both the provider and consumer sides. We provide a detailed summary of how each policy, including masking, row access, and projection policies, functions across shares, and how context functions within policies are evaluated.
2026.09.01

This page has been translated by machine translation. View original

This is Kawabata.

In this article, I will comprehensively verify how the 6 types of data protection policies defined on the provider side (masking, row access, projection, aggregation, join, and Data Movement Policy) behave on the consumer side.

https://docs.snowflake.com/en/user-guide/data-sharing-policy-protected-data

Conclusion

These are the results measured using two Enterprise Edition accounts in the same organization in the AWS Tokyo region, connected via direct share, as of August 31, 2026.

Policy Does it take effect on the consumer side? Notes
Masking Policy Takes effect even when the tag-based policy body is in a non-shared DB
Row Access Policy Evaluated with owner privileges even when the mapping table is in a non-shared DB
Projection Policy Compile-time error. Can differentiate per share using INVOKER_SHARE()
Aggregation Policy Groups below the minimum are aggregated into a remainder group
Join Policy Requirements can be met by joining with a consumer-owned table
Data Movement Policy Tag-based works / Account-level does not Restricts fetch and unload. However, CTAS passes through

There were two important points.

  1. Policies written to "mask only for specific roles" will expose raw values to all consumers across shares (because CURRENT_ROLE() becomes NULL inside the policy, causing it not to match the masking condition)
-- Fail-closed approach (safe side): only show raw value for allowed roles
CASE WHEN CURRENT_ROLE() IN ('ANALYST_ROLE') THEN val
     ELSE '***MASKED***' END    -- ← Also masked when role cannot be determined

-- Fail-open approach (dangerous side): mask only for specific roles
CASE WHEN CURRENT_ROLE() IN ('UNTRUSTED_ROLE') THEN '***MASKED***'
     ELSE val END               -- ← Raw value is exposed when role cannot be determined
  1. Policies control query results and certain data movements, but cannot prevent CTAS or re-sharing of data that has already been retrieved

Prerequisites

  • Both accounts must be Enterprise Edition or higher
  • Accounts in the same region
  • Verification date: August 31, 2026, Snowflake Enterprise Edition, AWS Tokyo Region
  • Account identifiers in this article have been replaced with sample values (myorg.provider_acct / myorg.consumer_acct)

Verification Perspectives

For each policy type, measurements were taken from the following perspectives.

Perspective What was verified
Whether applied Is it enforced by consumer queries? Is the error at compile time or runtime?
Context functions How CURRENT_ROLE() and similar functions inside the policy body are evaluated on the consumer side
Definition visibility Can the consumer see the policy body and tags?
Application from the receiving side Can the consumer add policies to shared objects?
Change propagation When are provider policy changes and removals reflected?
Protection after extraction Does protection remain after CTAS, re-sharing, or unload?

Preparation

We create a verification DB and two shares on the provider side. The reason for two shares is to demonstrate per-share branching using INVOKER_SHARE() (described later) with a single consumer.

-- Provider side
CREATE DATABASE POLSHARE_DB;       -- Shared target
CREATE DATABASE POLSHARE_LIB_DB;   -- Not shared (centralized policy management)

CREATE SHARE POLSHARE_SHARE_A;
GRANT USAGE ON DATABASE POLSHARE_DB TO SHARE POLSHARE_SHARE_A;
-- (Granting schema USAGE and table SELECT is omitted)
ALTER SHARE POLSHARE_SHARE_A ADD ACCOUNTS = myorg.consumer_acct;

CREATE SHARE POLSHARE_SHARE_B;   -- Also share the same table via a different share
GRANT USAGE ON DATABASE POLSHARE_DB TO SHARE POLSHARE_SHARE_B;
-- (Grant target schema USAGE and table SELECT as with SHARE_A)
ALTER SHARE POLSHARE_SHARE_B ADD ACCOUNTS = myorg.consumer_acct;

Mount the shares on the consumer side.

-- Consumer side
CREATE DATABASE POLSHARE_A_DB FROM SHARE myorg.provider_acct.POLSHARE_SHARE_A;
CREATE DATABASE POLSHARE_B_DB FROM SHARE myorg.provider_acct.POLSHARE_SHARE_B;
GRANT IMPORTED PRIVILEGES ON DATABASE POLSHARE_A_DB TO ROLE POLSHARE_CONSUMER_ADMIN;
GRANT IMPORTED PRIVILEGES ON DATABASE POLSHARE_B_DB TO ROLE POLSHARE_CONSUMER_ADMIN;

Note: For consumer-side verification, ALTER SESSION SET USE_CACHED_RESULT = FALSE; and USE SECONDARY ROLES NONE; were executed for each session. The former prevents false positives in change propagation verification, and the latter blocks privilege bypass via held roles.

Testing

Masking Policy

To examine differences based on how the policy is written, policies with different approaches were applied per column to a single table.

Masking Policy
-- Allowlist type: show raw value only for allowed roles
CREATE MASKING POLICY MP_ALLOWLIST AS (val STRING) RETURNS STRING ->
  CASE WHEN CURRENT_ROLE() IN ('POLSHARE_PROV_ADMIN') THEN val
       ELSE '***MASKED***' END;

-- Blocklist type: mask only for specific roles (raw value shown when undeterminable = fail-open)
CREATE MASKING POLICY MP_BLOCKLIST AS (val STRING) RETURNS STRING ->
  CASE WHEN CURRENT_ROLE() IN ('UNTRUSTED_ROLE') THEN '***MASKED***'
       ELSE val END;

-- Debug policy that returns the evaluated context function values directly
CREATE MASKING POLICY MP_DEBUG AS (val STRING) RETURNS STRING ->
     'ROLE='      || NVL(CURRENT_ROLE(), '<NULL>')
  || '|USER='     || NVL(CURRENT_USER(), '<NULL>')
  || '|ACCTNAME=' || NVL(CURRENT_ACCOUNT_NAME(), '<NULL>')
  || '|SHARE='    || NVL(INVOKER_SHARE(), '<NULL>');

Results of SELECT on the consumer side.

Column (approach) Provider Consumer
Always masked ***ALWAYS*** ***ALWAYS***
Always masked (policy body in non-shared DB) ***ALWAYS_LIB*** ***ALWAYS_LIB***
Allowlist type (MP_ALLOWLIST) Raw value ***MASKED***
Blocklist type (MP_BLOCKLIST) Raw value Raw value (leaked)
Debug column (MP_DEBUG) ROLE=POLSHARE_PROV_ADMIN|... ROLE=<NULL>|USER=<NULL>|ACCTNAME=CONSUMER_ACCT|SHARE=POLSHARE_SHARE_A

2026-09-01_15h49_55

As the debug column shows, CURRENT_ROLE() / CURRENT_USER() become NULL inside a policy on a shared object. This causes the CASE condition to become UNKNOWN, falling through to the ELSE clause.

2026-09-01_15h49_42

  • Allowlist type: ELSE is masked → falls to the safe side
  • Blocklist type: ELSE is raw value → raw value is exposed to all consumers

The "mask only for specific roles" approach works within a single account, but when placed on a share, the role cannot be determined, causing it to fall to the side that shows raw values (fail-open). To safely flip the behavior without drastically changing existing policy designs, you can explicitly detect NULL.

CREATE MASKING POLICY MP_NULLSAFE AS (val STRING) RETURNS STRING ->
  CASE WHEN CURRENT_ROLE() IS NULL THEN '***SHARE_CTX***'   -- Access via share
       WHEN CURRENT_ROLE() IN ('POLSHARE_PROV_ADMIN') THEN val
       ELSE '***MASKED***' END;

On the other hand, CURRENT_ACCOUNT() / CURRENT_ACCOUNT_NAME() / CURRENT_ORGANIZATION_NAME() / INVOKER_SHARE() are correctly evaluated with the consumer-side values. Use these for branching in share-compatible policies.

-- Show raw value for provider only, mask for share recipients
CREATE MASKING POLICY MP_ACCT AS (val STRING) RETURNS STRING ->
  CASE WHEN CURRENT_ACCOUNT_NAME() = 'PROVIDER_ACCT' THEN val
       ELSE '***CONSUMER***' END;

Note: Since CURRENT_ACCOUNT() (locator) may change with account migration, using CURRENT_ACCOUNT_NAME() in combination with CURRENT_ORGANIZATION_NAME() is recommended.

Tag-based masking was also enforced in the same way. Additionally, placing the policy body in a separate DB that is not shared had no effect on enforcement across shares.

Row Access Policy

Since row access policies return only rows evaluated as TRUE, rows where the condition becomes NULL (UNKNOWN) are not returned. Unlike masking policies, this falls to the safe side.

Row Access Policy
-- Role-dependent RAP: CURRENT_ROLE()=NULL across share → all rows excluded (0 rows)
CREATE OR REPLACE ROW ACCESS POLICY POLSHARE_DB.S_RAP.RAP_ROLE
  AS (region STRING) RETURNS BOOLEAN ->
  CURRENT_ROLE() IN ('POLSHARE_PROV_ADMIN', 'ACCOUNTADMIN');

-- Account-branching RAP: show only APAC to consumers (multi-tenant delivery pattern)
CREATE OR REPLACE ROW ACCESS POLICY POLSHARE_DB.S_RAP.RAP_ACCT
  AS (region STRING) RETURNS BOOLEAN ->
  CURRENT_ACCOUNT_NAME() = 'PROVIDER_ACCT' OR region = 'APAC';

-- Mapping table reference RAP (mapping included in share)
CREATE OR REPLACE ROW ACCESS POLICY POLSHARE_DB.S_RAP.RAP_MAP_S
  AS (region STRING) RETURNS BOOLEAN ->
  EXISTS (SELECT 1 FROM POLSHARE_DB.S_RAP.REGION_MAP m
          WHERE m.region = region AND m.account_name = CURRENT_ACCOUNT_NAME());

-- Mapping table reference RAP (mapping in non-shared LIB DB; works because evaluated with owner privileges)
CREATE OR REPLACE ROW ACCESS POLICY POLSHARE_DB.S_RAP.RAP_MAP_L
  AS (region STRING) RETURNS BOOLEAN ->
  EXISTS (SELECT 1 FROM POLSHARE_LIB_DB.MAPS.REGION_MAP_LIB m
          WHERE m.region = region AND m.account_name = CURRENT_ACCOUNT_NAME());

Policy condition Provider Consumer
CURRENT_ROLE() IN (...) 6 rows 0 rows (NULL → all rows excluded)
CURRENT_ACCOUNT_NAME() = '...' OR region = 'APAC' 6 rows 2 rows (APAC only)
Mapping table reference (table is shared) 6 rows 2 rows
Mapping table reference (table in non-shared DB) 6 rows 2 rows

2026-09-01_15h58_16

Even without including the mapping table in the share, the policy is evaluated with owner privileges, so no error occurs. This allows rows to be selectively exposed per tenant while keeping the tenant mapping hidden from consumers.

Projection Policy

I measured the INVOKER_SHARE() branching from the official documentation sample.

Projection Policy
-- Per-share branching (official sample format): projection disabled only via SHARE_A,
-- projection allowed via SHARE_B and by the provider itself
CREATE OR REPLACE PROJECTION POLICY POLSHARE_DB.S_PROJ.PP_BY_SHARE
  AS () RETURNS PROJECTION_CONSTRAINT ->
  CASE WHEN INVOKER_SHARE() = 'POLSHARE_SHARE_A' THEN PROJECTION_CONSTRAINT(ALLOW => false)
       ELSE PROJECTION_CONSTRAINT(ALLOW => true) END;

Even on the same table with the same consumer, results differed depending on which share was used.

SELECT email FROM POLSHARE_A_DB.S_PROJ.CONTACTS;  -- via SHARE_A

2026-09-01_15h59_47

SELECT email FROM POLSHARE_B_DB.S_PROJ.CONTACTS;  -- via SHARE_B → raw value retrieved

2026-09-01_16h00_30
The same error occurs with EXPLAIN, so enforcement is at compile time. Note that non-projecting references such as WHERE email = '...' are allowed, so it is possible to infer whether a value exists.

Aggregation Policy

Aggregation Policy
-- Provider has no restrictions; all others have minimum group size of 5 (groups below that go to remainder)
CREATE OR REPLACE AGGREGATION POLICY POLSHARE_DB.S_AGG.AP_COND
  AS () RETURNS AGGREGATION_CONSTRAINT ->
  CASE WHEN CURRENT_ACCOUNT_NAME() = 'PROVIDER_ACCT' THEN NO_AGGREGATION_CONSTRAINT()
       ELSE AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 5) END;

A regular SELECT * results in an error.

2026-09-01_16h02_00

Results when aggregating from the consumer on a table with MIN_GROUP_SIZE => 5 set (APAC 12 rows / EMEA 5 rows / US 3 rows).

SELECT region, COUNT(*) AS cnt FROM POLSHARE_A_DB.S_AGG.SALES GROUP BY region;

2026-09-01_16h05_33

Join Policy

Join Policy
-- Consumer side: succeeds by joining with a table in their own DB
SELECT p.diagnosis, COUNT(*) AS cnt
FROM POLSHARE_A_DB.S_JOIN.PATIENTS p
JOIN POLSHARE_LOCAL_DB.WORK.LOCAL_PATIENTS l ON p.patient_id = l.patient_id
GROUP BY p.diagnosis;

A table with JOIN_CONSTRAINT(JOIN_REQUIRED => TRUE) set will have SELECT * blocked with an error. The data could be retrieved when the consumer joined it with their own table.

2026-09-01_16h06_47

2026-09-01_16h09_45

WHERE ... IN (subquery) does not satisfy the join requirement and results in the same error.

2026-09-01_16h10_10

Data Movement Policy

Data Movement Policy (DMP) is a data extraction control feature that went GA in August 2026, and tag-based application is also included in GA.
The article from a previous verification is below.

https://dev.classmethod.jp/articles/snowflake-data-movement-policies-ga/

The official documentation only has a single line saying "Cross-region share protection is not supported," with no mention of behavior for same-region shares. Based on actual measurements, tag-based DMP functioned across shares.

Data Movement Policy
-- Limit CLI/driver fetch to 100 rows (no limit for the verification role itself)
CREATE OR REPLACE DATA MOVEMENT RULE POLSHARE_DB.S_DMP.R_PSH_FETCH_100
  TYPE = 'PROGRAMMATIC_FETCH'
  MAX_ROWS AS () RETURNS INTEGER
  -> (CASE WHEN SYS_CONTEXT('SNOWFLAKE$SESSION', 'ROLE') = 'POLSHARE_PROV_ADMIN' THEN NULL ELSE 100 END)
  COMMENT = 'Fetch limited to 100 rows except provider admin';

-- Limit Snowsight display to 100 rows (additional rule for taking screenshots in Snowsight;
-- the actual error 100168 in the article body is from the PROGRAMMATIC_FETCH side)
CREATE OR REPLACE DATA MOVEMENT RULE POLSHARE_DB.S_DMP.R_PSH_SNOWSIGHT_100
  TYPE = 'SNOWSIGHT_UI'
  MAX_ROWS AS () RETURNS INTEGER
  -> (CASE WHEN SYS_CONTEXT('SNOWFLAKE$SESSION', 'ROLE') = 'POLSHARE_PROV_ADMIN' THEN NULL ELSE 100 END)
  COMMENT = 'Screenshot only: Snowsight display limited to 100 rows';

-- Block all unloads to internal/external stages (for COPY INTO verification)
CREATE OR REPLACE DATA MOVEMENT RULE POLSHARE_DB.S_DMP.R_PSH_COPY_INT_BLOCK
  TYPE = 'COPY_INTO_INTERNAL_STAGE'
  MAX_ROWS AS () RETURNS INTEGER
  -> (CASE WHEN SYS_CONTEXT('SNOWFLAKE$SESSION', 'ROLE') = 'POLSHARE_PROV_ADMIN' THEN NULL ELSE 0 END)
  COMMENT = 'Block unload to internal stage except provider admin';
CREATE OR REPLACE DATA MOVEMENT RULE POLSHARE_DB.S_DMP.R_PSH_COPY_EXT_BLOCK
  TYPE = 'COPY_INTO_EXTERNAL_STAGE'
  MAX_ROWS AS () RETURNS INTEGER
  -> (CASE WHEN SYS_CONTEXT('SNOWFLAKE$SESSION', 'ROLE') = 'POLSHARE_PROV_ADMIN' THEN NULL ELSE 0 END)
  COMMENT = 'Block unload to external stage except provider admin';

-- Policy bundling Rules as ENFORCE (block on exceed)
CREATE OR REPLACE DATA MOVEMENT POLICY POLSHARE_DB.S_DMP.DMP_SHARE_GUARD
  ENFORCE_RULES = (R_PSH_FETCH_100, R_PSH_SNOWSIGHT_100, R_PSH_COPY_INT_BLOCK, R_PSH_COPY_EXT_BLOCK)
  COMMENT = 'Share guard: fetch/display <= 100 rows, block unload';

-- DMP applied via tag (cannot be set on tags with PROPAGATE = NONE)
CREATE OR REPLACE TAG POLSHARE_DB.S_DMP.DMP_TAG
  PROPAGATE = ON_DEPENDENCY_AND_DATA_MOVEMENT;
ALTER TAG POLSHARE_DB.S_DMP.DMP_TAG SET DATA MOVEMENT POLICY POLSHARE_DB.S_DMP.DMP_SHARE_GUARD;
ALTER TABLE POLSHARE_DB.S_DMP.PAYROLL SET TAG POLSHARE_DB.S_DMP.DMP_TAG = 'guarded';

-- Policy for account-level verification (defined here only; applied and removed using ALTER ACCOUNT SET/UNSET for verification)
CREATE OR REPLACE DATA MOVEMENT RULE POLSHARE_DB.S_DMP.R_PSH_BASELINE_FETCH_BLOCK
  TYPE = 'PROGRAMMATIC_FETCH'
  MAX_ROWS AS () RETURNS INTEGER
  -> (CASE WHEN SYS_CONTEXT('SNOWFLAKE$SESSION', 'ROLE') IN ('POLSHARE_PROV_ADMIN', 'ACCOUNTADMIN') THEN NULL ELSE 0 END)
  COMMENT = 'Baseline test: block fetch except provider roles';
CREATE OR REPLACE DATA MOVEMENT POLICY POLSHARE_DB.S_DMP.DMP_TEST_BASELINE
  ENFORCE_RULES = (R_PSH_BASELINE_FETCH_BLOCK)
  COMMENT = 'Temporary account baseline for share verification';

Results of operations from the consumer side, with a fetch limit of 100 rows and unload prohibition applied via tags on the provider side.

Consumer operation Result
SELECT COUNT(*)(1 row result) Success
SELECT * ... LIMIT 50 Success
SELECT * (fetch 500 rows) Runtime error
COPY INTO @own stage Compile-time error
CREATE TABLE ... AS SELECT * (500 rows) Success (passes through)

2026-09-01_16h14_17

2026-09-01_16h14_54

Error because more than 100 rows

2026-09-01_16h15_50

Error because unloading to own stage
2026-09-01_16h16_23

CTAS passes through successfully as it is outside the movement type

2026-09-01_16h17_17

The last row is important.
CTAS is not included in DMP's control targets (movement types), so all rows retrievable by the query can be copied to the consumer's own table. What gets copied is only the query result, so masking and row access policy results are not reverted to raw values. However, since tags and DMP are not propagated to the copy destination (PROPAGATE settings do not cross account boundaries), fetch and unload from the copy destination are not restricted.

On the other hand, DMP applied at the account level (baseline) without tags did not block consumer operations. Only tag-based DMP functions across shares.

Via Secure View

We also verified the common real-world setup of "share only a secure view that references the base table, without sharing the base table itself."

Via Secure View
CREATE OR REPLACE TABLE POLSHARE_LIB_DB.BASE.CUSTOMERS_BASE (
  id NUMBER, region STRING, ssn STRING, email STRING
);
INSERT INTO POLSHARE_LIB_DB.BASE.CUSTOMERS_BASE VALUES
  (1, 'APAC', 'SSN-0001', 'base1@example.com'),
  (2, 'EMEA', 'SSN-0002', 'base2@example.com');

CREATE OR REPLACE MASKING POLICY POLSHARE_LIB_DB.POLICIES.MP_BASE_ALWAYS
  AS (val STRING) RETURNS STRING -> '***BASE_ALWAYS***';
-- "Mask only for specific roles" type (also verifying that raw values are exposed across shares via a view)
CREATE OR REPLACE MASKING POLICY POLSHARE_LIB_DB.POLICIES.MP_BASE_BLOCKLIST
  AS (val STRING) RETURNS STRING ->
  CASE WHEN CURRENT_ROLE() IN ('UNTRUSTED_ROLE') THEN '***MASKED***' ELSE val END;
CREATE OR REPLACE ROW ACCESS POLICY POLSHARE_LIB_DB.POLICIES.RAP_BASE_ACCT
  AS (region STRING) RETURNS BOOLEAN ->
  CURRENT_ACCOUNT_NAME() = 'PROVIDER_ACCT' OR region = 'APAC';

ALTER TABLE POLSHARE_LIB_DB.BASE.CUSTOMERS_BASE ALTER COLUMN ssn   SET MASKING POLICY POLSHARE_LIB_DB.POLICIES.MP_BASE_ALWAYS;
ALTER TABLE POLSHARE_LIB_DB.BASE.CUSTOMERS_BASE ALTER COLUMN email SET MASKING POLICY POLSHARE_LIB_DB.POLICIES.MP_BASE_BLOCKLIST;
ALTER TABLE POLSHARE_LIB_DB.BASE.CUSTOMERS_BASE ADD ROW ACCESS POLICY POLSHARE_LIB_DB.POLICIES.RAP_BASE_ACCT ON (region);

-- Case 1: Secure view referencing the base table with policies (no policy on the view itself)
CREATE OR REPLACE SECURE VIEW POLSHARE_DB.S_VIEW.V_BASEPOL AS
SELECT id, region, ssn, email FROM POLSHARE_LIB_DB.BASE.CUSTOMERS_BASE;

-- Case 2: Policy directly on view columns (no policy on base table)
CREATE OR REPLACE TABLE POLSHARE_LIB_DB.BASE.CUSTOMERS_PLAIN (id NUMBER, email STRING);
INSERT INTO POLSHARE_LIB_DB.BASE.CUSTOMERS_PLAIN VALUES
  (1, 'plain1@example.com'), (2, 'plain2@example.com');
CREATE OR REPLACE SECURE VIEW POLSHARE_DB.S_VIEW.V_VIEWPOL AS
SELECT id, email FROM POLSHARE_LIB_DB.BASE.CUSTOMERS_PLAIN;
CREATE OR REPLACE MASKING POLICY POLSHARE_DB.S_VIEW.MP_VIEWCOL_ALWAYS
  AS (val STRING) RETURNS STRING -> '***VIEWCOL***';
ALTER VIEW POLSHARE_DB.S_VIEW.V_VIEWPOL ALTER COLUMN email SET MASKING POLICY POLSHARE_DB.S_VIEW.MP_VIEWCOL_ALWAYS;

Where protection is applied Result via view
Base table always-on masking Enforced (***BASE_ALWAYS***)
Base table row access policy (account branching) Enforced (target rows only)
Base table "mask only for specific roles" type (fail-open) Raw value is leaked
Masking policy applied directly to view column Enforced (***VIEWCOL***)

2026-09-01_16h23_58

Even without sharing the base table, the policy evaluation context remains cross-share (CURRENT_ROLE() = NULL).

Cross-Cutting Verification

Definition visibility: Consumers cannot view the policy definition body or actual names (SHOW command for DMP was not verified).

  • SHOW MASKING POLICIES / SHOW ROW ACCESS POLICIES / SHOW AGGREGATION POLICIES / SHOW PROJECTION POLICIES / SHOW JOIN POLICIES (all with IN DATABASE specified): all return 0 rows
  • DESCRIBE TABLE policy name column: Unknown Policy! (only its existence is visible)
  • GET_DDL('TABLE', ...): operation itself is not supported in shared DBs (error 001131)

2026-09-01_16h26_28

Application from the receiving side: Applying tags and policies to shared tables is blocked with 003001: Provider share does not have sufficient privileges.

2026-09-01_16h27_54

Limitations and Notes

  • This verification is based on a same-organization, same-region, direct share configuration. Cross-organization, cross-region, listing-based, and reader account configurations have not been verified
  • Sharing of Dynamic Tables, materialized views, and external tables is out of scope
  • The behavior where database role branching becomes "per-share evaluation" diverges from the official documentation explanation (activated by granting to a role on the consumer side). Rechecking before incorporating into a design is recommended
  • Projection, aggregation, and join policies are features intended for trusted partners. The official documentation explicitly states that they cannot fully prevent estimation attacks through repeated attempts
  • Tag-based application of row access, projection, aggregation, and join policies is in public preview as of August 2026, so these were verified using direct application (tag-based masking and tag-based DMP application are GA)
  • DMP violation records can be delayed by up to 2 hours. Please factor in this delay when using for auditing purposes

Closing

Masking, row access, projection, aggregation, and join policies are all enforced across shares, and DMP only functioned when applied via tags.
While the premise should be to not share confidential data that doesn't need to be shared in the first place, I think it's an advantage to have data protection policies available when needed.

I hope this article is helpful in some way!


Snowflake World Tour Tokyo 2026に参加しませんか?

Snowflakeの国内最大級イベント「Snowflake World Tour Tokyo」が2026年9月10日(木)・11日(金)にグランドプリンスホテル新高輪にて開催されます。
最新のAI・データ活用事例やライブデモを体感できる無料イベントです。

Snowflake World Tour Tokyoイベントに参加する


Snowflake Community Awards ファイナリストに選出されました

DevelopersIO で Snowflake 記事を執筆している かわばた が、Snowflake Community Awards「RISING COMMUNITY LEADER OF THE YEAR」部門・APJ枠のファイナリストに選ばれました。
最終選考の30%はコミュニティ投票です。記事がお役に立っていたようでしたら、9月15日(火)までにぜひ一票お願いします。フォームの「(4 of 6) RISING COMMUNITY LEADER OF THE YEAR」で Tomohiro Kawabata | Classmethod, Japan を選択、2分ほどで完了します。

投票フォームを開く


Snowflakeの導入支援はクラスメソッドに!

クラスメソッドでは Snowflake の導入を支援しております。
製品の詳細や支援の内容についてお気軽にお問い合わせください。

Snowflakeの詳細を見る

Share this article