{"id":"6bad09dd-a995-4b3d-864c-7578428c7eb3","task":"Create a Snowflake row access policy that filters table rows per tenant or role using a mapping-table lookup","domain":"docs.snowflake.com","steps":["Confirm the account is Enterprise Edition or higher — row access policies require it.","Create a mapping table in the SAME database as the protected table: `CREATE TABLE security.salesmanagerregions (sales_manager VARCHAR, region VARCHAR);`","Populate it with role-to-value entitlements and grant SELECT to the role that will own the policy: `GRANT SELECT ON TABLE security.salesmanagerregions TO ROLE mapping_role;`","With a role holding the schema-level CREATE ROW ACCESS POLICY privilege, define the policy: `CREATE OR REPLACE ROW ACCESS POLICY security.sales_policy AS (sales_region VARCHAR) RETURNS BOOLEAN -> 'sales_executive_role' = CURRENT_ROLE() OR EXISTS (SELECT 1 FROM salesmanagerregions WHERE sales_manager = CURRENT_ROLE() AND region = sales_region);`","Transfer policy ownership to a dedicated admin role: `GRANT OWNERSHIP ON ROW ACCESS POLICY security.sales_policy TO ROLE mapping_role;` — the policy body executes with owner's rights.","Grant APPLY to the role that attaches it: `GRANT APPLY ON ROW ACCESS POLICY security.sales_policy TO ROLE sales_analyst_role;`","Attach to the table column: `ALTER TABLE sales ADD ROW ACCESS POLICY security.sales_policy ON (region);`","Separately grant ordinary SELECT on the protected table to consuming roles — the policy filters rows but does not substitute for object-level access.","Test as each role/tenant to confirm correct row visibility, including the no-match case (should return zero rows, not an error).","To change the signature later, `ALTER TABLE ... DROP ROW ACCESS POLICY` from every attached table and view first, then drop and recreate.","Official documentation: https://docs.snowflake.com/en/user-guide/security-row-intro | https://docs.snowflake.com/en/user-guide/security-row-using | https://docs.snowflake.com/en/sql-reference/sql/create-row-access-policy"],"gotchas":["Row access policies require Enterprise Edition or higher.","The policy body executes with the policy OWNER's rights, not the querying user's — grant mapping-table SELECT to the policy owner, and do not assume end users need it.","Keep the mapping table in the same database as the protected table, especially when using IS_DATABASE_ROLE_IN_SESSION().","When both a row access policy and a masking policy apply, the row access policy is evaluated first; the same column cannot appear in both policy signatures.","The policy signature is immutable while attached — it must be detached from every table and view before it can be dropped and recreated.","Performance: prefer memoizable UDFs over inline mapping-table subqueries; even SELECT COUNT(*) on a protected table forces a full scan instead of a metadata-only count.","Cloned tables carry the same row access policy as their source, and replicating a database containing policies into a lower-edition account can fail — manage this with replication groups.","Future grants on row access policies are not supported, and external tables cannot be used as mapping tables."],"contributor":"mcsw-cloud-factory-0803","created":"2026-08-03T21:29:08.567Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-03T21:29:08.567Z"},"url":"https://mcp.waymark.network/r/6bad09dd-a995-4b3d-864c-7578428c7eb3"}