Pro ยท Lesson 11 of 13
Roles, database roles, masking, row access, tags, access history, and Horizon Catalog.
Sign in to save your place.
A role is a badge. Grants are what the badge opens. A database role is a badge that only works inside one database, which you then pin onto an account role. A masking policy is a stamp that replaces a column when the badge is wrong. A row access policy throws away rows before that stamp is applied. Horizon Catalog is the desk that keeps those rules, including when another engine reads an Iceberg table.
Snowflake access is role-based on every edition. A user has roles. A role has grants. ACCOUNTADMIN, SYSADMIN, and SECURITYADMIN exist so that loading data and writing policy are not the same badge. Secondary roles can be active in a session. Policies often call IS_ROLE_IN_SESSION so a secondary role still counts.
A database role lives in one database. You grant it to an account role. That keeps a schema's privileges from leaking across databases. In a catalog-linked database, database roles are one of the object types you are allowed to create.
CREATE DATABASE ROLE LEARN_DB.READER_DB;
GRANT USAGE ON DATABASE LEARN_DB TO DATABASE ROLE LEARN_DB.READER_DB;
GRANT USAGE ON SCHEMA LEARN_DB.PUBLIC TO DATABASE ROLE LEARN_DB.READER_DB;
GRANT SELECT ON ALL TABLES IN SCHEMA LEARN_DB.PUBLIC TO DATABASE ROLE LEARN_DB.READER_DB;
GRANT DATABASE ROLE LEARN_DB.READER_DB TO ROLE SYSADMIN;
Masking policies and row access policies require Enterprise Edition or higher. A masking policy returns a possibly rewritten value for a column. A row access policy returns true or false for whether the row is visible. When both exist, Snowflake evaluates the row access policy first. The same column cannot appear in both policy signatures.
If a row policy looks up a mapping table, put that table in the same database as the protected table. That placement matters when the policy calls IS_DATABASE_ROLE_IN_SESSION.
Object tags exist on Standard Edition. The edition matrix says some tagging features require Enterprise. Tag-based masking needs the masking feature, so it needs Enterprise or higher. Tags are how you mark a column as an email address once and attach a policy to the tag.
CREATE OR REPLACE MASKING POLICY mask_email AS (val STRING) RETURNS STRING ->
CASE
WHEN IS_ROLE_IN_SESSION('PII_READER') THEN val
ELSE '***'
END;
SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY is an Enterprise feature. It records which user read which columns. Account Usage views are not live. Snowflake documents a latency, so a query you ran a minute ago may be missing. Use Snowsight Query History for that query, and Account Usage for a later audit.
Snowflake Horizon Catalog is the governance surface over those controls. The Horizon docs describe policy enforcement, tags, access history, and Trust Center checks for risky grants. Horizon can also enforce masking and row access when an external engine such as Spark, Trino, or PyIceberg reads an Iceberg table through the Horizon Iceberg REST catalog. The external engine receives a scan plan that already applied the policy. Writes from those engines are limited to policy shapes the Scan Plan API documents, and other shapes can return HTTP 403.
Business Critical adds private connectivity, Tri-Secret Secure, and customer-managed keys. Do not claim those on a Standard trial. Differential privacy, aggregation policies, and projection policies are also Enterprise-and-above features and are separate from a basic mask.
Access flows from user to role to table, and a policy on the column decides what each role sees.
Step 1 of 3Privileges are granted to roles, never straight to people. Asha gets access by being granted the role.
GRANT SELECT ON TABLE customers TO ROLE analyst;
GRANT ROLE analyst TO USER asha;
Step 2 of 3A masking policy is a rule attached to the column. At query time it decides whether this role sees the real email or a masked value.
CREATE MASKING POLICY email_mask AS (v STRING) RETURNS STRING ->
CASE WHEN CURRENT_ROLE() = 'PII_READER' THEN v ELSE '***' END;
Step 3 of 3Horizon is the name for Snowflake's governance features together. The policy follows the column into every query, share and dashboard, and access history records who read it.
ALTER TABLE customers MODIFY COLUMN email SET MASKING POLICY email_mask;
The database role works on Standard. The masking policy fails on Standard with an edition error. That failure is the lesson. Enterprise trials can run the policy and then attach it to a column with ALTER TABLE ... MODIFY COLUMN ... SET MASKING POLICY.
USE DATABASE LEARN_DB;
CREATE DATABASE ROLE IF NOT EXISTS LEARN_DB.READER_DB;
GRANT USAGE ON SCHEMA LEARN_DB.PUBLIC TO DATABASE ROLE LEARN_DB.READER_DB;
GRANT SELECT ON TABLE LEARN_DB.PUBLIC.ORDERS TO DATABASE ROLE LEARN_DB.READER_DB;
CREATE OR REPLACE MASKING POLICY mask_email AS (val STRING) RETURNS STRING ->
CASE
WHEN IS_ROLE_IN_SESSION('PII_READER') THEN val
ELSE '***'
END;
Checked against Snowflake documentation in October 2026. Product behavior still depends on your edition, cloud, and region.