How I would implement row access policy and aggregation policy in SF
Row access 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
)
;
Snowflake determines whether a row access policy is set on a database object.
- SQL is parsed into AST
- At each FROM node with table or view, look up the policy with the (role, table name) tuple
Snowflake creates a dynamic secure view (i.e. a secure inline view) of the database object.
- Replace the from table table with a select all + from + selection subtree
The values of the columns specified in the ALTER TABLE or ALTER VIEW command (i.e when adding a row access policy to a table or view) are bound to the corresponding parameters in the policy, and the policy expression is evaluated.
- Execute the sql
Aggregation policies
Aggregation policies can be used with or without an entity key. When aggregation policies are used without an entity key, they protect the privacy of individual rows in the data set (that is, row-level privacy). If you use an aggregation policy with an entity key, it protects the privacy of an entity, even if information about that entity appears in multiple rows (that is, entity-level privacy).
Entity-level privacy strengthens the privacy protections provided by aggregation policies. With entity-level privacy, Snowflake can ensure that an aggregation group contains a certain number of entities, not just a certain number of rows.
CREATE AGGREGATION POLICY my_agg_policy
AS () RETURNS AGGREGATION_CONSTRAINT ->
CASE
WHEN CURRENT_ROLE() = 'ADMIN'
THEN NO_AGGREGATION_CONSTRAINT()
ELSE AGGREGATION_CONSTRAINT(MIN_GROUP_SIZE => 3)
END;
ALTER TABLE t1 SET AGGREGATION POLICY my_agg_policy;
- SQL to AST, and then when discovered an aggregation node replace this aggregation node with conditional grouping
SELECT state, AVG(elevation) AS avg_elevation
FROM t1
GROUP BY state;
becomes
--this query is not valid for Postgres
SELECT
CASE
WHEN COUNT(*) >= 3 THEN state
ELSE 'Other'
END AS state_group,
AVG(elevation) AS avg_elevation
FROM t1
GROUP BY
CASE
WHEN COUNT(*) >= 3 THEN state
ELSE 'Other'
END;
-- one Postgres version
SELECT
state,
AVG(elevation) AS avg_elevation
FROM
(
SELECT CASE
WHEN policy.state IS NULL THEN NULL --filtered out
ELSE policy.state
END AS state,
elevation
FROM t1
LEFT JOIN (
SELECT state,
COUNT(*) as count
FROM t1
GROUP BY state
HAVING COUNT(*) >= 3
) policy ON t1.state = policy.state
) t1
GROUP BY
state;