联接策略¶
联接策略是架构级对象,用于控制在查询时联接表的要求。当联接策略应用于表时,针对该表的查询可能需要联接。当需要联接时,可以将联接限制为某些联接列。
您可以使用这种策略针对某些表和视图的查询强制进行联接,作为跨共享表查找通用信息的手段。被分配联接策略的表或视图称为 受保护 或 受联接约束。
概述¶
Snowflake 的核心功能之一是与其他实体共享数据集的能力。利用联接策略,提供商(数据所有者)可以控制其数据的用途,即使在与使用者共享数据之后也是如此。具体来说,提供商可以要求表的使用者将数据联接到另一个表,而不是从单个表中检索记录。这一要求有利于在拥有共同数据集的不完全信任合作伙伴之间共享数据。从单个表中选择数据通常没用;当一个所有者的表与使用者或合作伙伴拥有的类似表联接时,可获得有意义的数据。
联接策略应用于表或视图后,查询必须:
将该表或视图联接到另一个表或视图。
指定支持的 联接类型。
指定允许联接列的联接条件。
必须至少有一个参与联接的表或视图不受保护。此限制可防止攻击者通过联接由同一组织共享且具有匹配键值的两个受保护表来规避联接策略。
虽然联接策略控制对联接表的访问,但它们不能保证恶意行为者不能使用精心构造的查询从受联接约束的表中获取敏感数据。只要尝试足够多次的查询,恶意行为者就可能设法满足联接要求。联接策略最适合用于已有一定信任度的合作伙伴和客户。此外,提供商应警惕可能存在的数据滥用(例如,查看提供商列表的访问历史记录)。
创建和实施联接策略¶
要创建和实施联接策略,请执行以下步骤:
创建策略本身。
将策略应用于新的或现有的表或视图。
运行一些查询以验证策略的预期行为。
这些步骤将在以下部分中解释。
您可能还需要修改现有策略并验证预期行为变更。
任何时候,您都可以使用 SHOW JOIN POLICIES 和 DESCRIBE JOIN POLICY 命令查看账户中的联接策略。要查看策略的实际定义,请查询 JOIN_POLICIES 视图。要查看附加到策略的表和视图,请查询 POLICY_REFERENCES Information Schema 表函数。
有关管理策略的信息(包括自定义策略管理角色的设置),请参阅 管理联接策略。
创建联接策略¶
最简单的联接策略形式要求用户始终将表或视图联接到其他表或视图。换句话说,不允许对没有联接规范的单个表进行查询。例如,创建名为 jp1
的策略:
CREATE JOIN POLICY jp1
AS () RETURNS JOIN_CONSTRAINT -> JOIN_CONSTRAINT(JOIN_REQUIRED => TRUE);
有关此命令及其 JOIN_CONSTRAINT 函数的完整语法,请参阅 CREATE JOIN POLICY。
将联接策略应用于表或视图¶
创建联接策略后,通过将其分配给特定表或视图来实现:
如果表或视图已存在,请使用 ALTER TABLE 或 ALTER VIEW 命令。
对于新的表或视图,请使用 CREATE TABLE 或 CREATE VIEW 命令。
无论哪种情况,请指定 JOIN POLICY 参数以标识联接策略本身。例如,以下命令将策略 jp
分配给表 join_table
:
CREATE OR REPLACE TABLE join_table (
col1 INT,
col2 VARCHAR,
col3 NUMBER )
JOIN POLICY jp1;
或者,如果要限定联接使用特定的联接列,您还可以指定 ALLOWED JOIN KEYS 参数。请参阅 限制特定列上的联接。
在任何给定时间,只能为表或视图分配一个联接策略。请参阅 替换联接策略。
通过运行一些查询来测试联接策略¶
以下查询演示了 jp1
策略对表 join_table
生效时的预期行为。此表不需要加载;空表足以演示预期行为。
没有联接的查询会返回预期错误:
SELECT * FROM join_table;
506037 (23001): SQL compilation error: Join Policy violation, please contact the policy admin for details
col1
上具有显式内部联接的查询会返回结果:
SELECT * FROM join_table jt1 INNER JOIN join_table_2 jt2 ON jt1.col1=jt2.col1;
+------+------+------+------+------+------+
| COL1 | COL2 | COL3 | COL1 | COL2 | COL3 |
|------+------+------+------+------+------|
+------+------+------+------+------+------+
col2
上具有显式内部联接的查询也会返回结果:
SELECT * FROM join_table jt1 INNER JOIN join_table_2 jt2 ON jt1.col2=jt2.col2;
+------+------+------+------+------+------+
| COL1 | COL2 | COL3 | COL1 | COL2 | COL3 |
|------+------+------+------+------+------|
+------+------+------+------+------+------+
限制特定列上的联接¶
要进一步测试联接策略行为,请从表中分离(取消设置)策略,删除并重新创建联接策略,然后使用包含 ALLOWED JOIN KEYS 参数的 DDL 重新创建 join_table
。
ALTER TABLE join_table UNSET JOIN POLICY;
DROP JOIN POLICY jp1;
CREATE JOIN POLICY jp1
AS () RETURNS JOIN_CONSTRAINT -> JOIN_CONSTRAINT(JOIN_REQUIRED => TRUE);
CREATE OR REPLACE TABLE join_table (
col1 INT,
col2 VARCHAR,
col3 NUMBER )
JOIN POLICY jp1 ALLOWED JOIN KEYS (col1);
现在,重试前面的一个查询,以 col2
作为联接列。查询失败,因为 col2
不是允许的联接键。
SELECT * FROM join_table jt1 INNER JOIN join_table_2 jt2 ON jt1.col2=jt2.col2;
506038 (23001): SQL compilation error: Join Policy violation, invalid join condition with reason: Disallowed join key used.
以 jt1.col1=jt2.col1
作为联接条件的相同查询将成功。这两个表的自然联接将失败,因为它将尝试在 col1
和 col2
上连接。
显示和描述联接策略¶
您可以使用 SHOW JOIN POLICIES 和 DESCRIBE JOIN POLICY 命令获取账户中现有联接策略的基本信息。要返回有关联接策略的更多详细信息,请参阅 监控联接策略。
SHOW JOIN POLICIES;
+-------------------------------+------+---------------+----------------+-------------+--------------+---------+-----------------+---------+
| created_on | name | database_name | schema_name | kind | owner | comment | owner_role_type | options |
|-------------------------------+------+---------------+----------------+-------------+--------------+---------+-----------------+---------|
| 2024-12-04 15:15:49.591 -0800 | JP1 | POLICY1_DB | POLICY1_SCHEMA | JOIN_POLICY | POLICY1_ROLE | | ROLE | |
+-------------------------------+------+---------------+----------------+-------------+--------------+---------+-----------------+---------+
DESCRIBE JOIN POLICY jp1;
+------+-----------+-----------------+----------------------------------------+
| name | signature | return_type | body |
|------+-----------+-----------------+----------------------------------------|
| JP1 | () | JOIN_CONSTRAINT | JOIN_CONSTRAINT(JOIN_REQUIRED => TRUE) |
+------+-----------+-----------------+----------------------------------------+
创建和应用条件联接策略¶
策略管理员可以定义联接策略的 SQL 表达式,以便不同的查询根据执行查询的用户角色等因素有不同的限制。这种策略可以允许一个用户不受限制地查询表,同时要求其他人使用联接。
例如,以下联接策略允许具有 ACCOUNTADMIN
、FINANCE_ROLE
或 HR_ROLE
角色的用户无限制访问表,同时要求所有其他用户指定联接。
CREATE JOIN POLICY my_join_policy AS () RETURNS JOIN_CONSTRAINT -> CASE WHEN CURRENT_ROLE() = 'ACCOUNTADMIN' OR CURRENT_ROLE() = 'FINANCE_ROLE' OR CURRENT_ROLE() = 'HR_ROLE' THEN JOIN_CONSTRAINT(JOIN_REQUIRED => FALSE) ELSE JOIN_CONSTRAINT(JOIN_REQUIRED => TRUE) END;小技巧
在条件策略中使用上下文函数(例如 CURRENT_ROLE)时,可以使用以下策略:
上下文函数返回字符串,因此使用它们进行比较时区分大小写。如果想进行不区分大小写的比较,可以使用 LOWER 将字符串转换为全部小写。
当上下文函数返回某个值时,POLICY_CONTEXT 函数可以帮助您评估策略正文是否返回正确的值。POLICY_CONTEXT 函数根据一个或多个上下文函数的指定值模拟查询结果。
联接查询要求¶
针对受联接约束的表或视图的查询必须符合以下要求,联接策略才能生效:
- 支持的 联接 类型
对于受联接约束的表,支持以下显式联接类型:
INNER JOIN(采用可选的 INNER 关键字;需要 JOIN)
LEFT OUTER JOIN 和 RIGHT OUTER JOIN,其中受联接约束的表是“相反”表。如果受联接约束的表是第一个表或“左侧”表,则外部联接必须是 RIGHT OUTER JOIN。如果受联接约束的表是第二个表或“右侧”表,则外部联接必须是 LEFT OUTER JOIN。
NATURAL JOIN(对具有常用名称的列进行内部联接)
必须在 FROM 子句中明确指定内部和外部联接,并附带 ON 或 USING 联接条件。这些联接不能在 WHERE 子句中指定。
- 不支持的联接类型
不支持以下联接类型:
FULL OUTER JOIN
ASOF JOIN
LATERAL 联接
WHERE 子句中的隐式联接
交叉联接(笛卡尔积)
- 使用多个受联接约束的表的联接
如果联接查询中的两个(或所有)表都被分配了联接策略,则查询失败并出现错误。在两个表的联接中,只能有一个表受联接约束。
针对受联接约束的表的查询不支持 UNION 和 UNION ALL 集操作。
支持 INTERSECT 集操作,因为它们在语义上等同于内部联接。
只有当受联接约束的表位于运算符的筛选侧(即位于第二个查询块中)时,才支持 MINUS 和 EXCEPT 集操作。
- 数据类型转换
在 SELECT 语句中包含数据类型转换函数的查询必须使用该函数的 TRY 版本。例如,允许使用 TRY_CAST 函数,但禁止使用 CAST 函数。数值类型允许使用以下数据类型转换函数:
针对受联接约束的表的查询的预期错误¶
以下示例显示了由于联接策略应用于表或视图而预期查询失败的一些情况。有关背景信息,请参阅 联接查询要求。这些查询中的表不包含任何行,因此查询返回空结果(成功)或错误(失败)。
没有联接的查询失败¶
将联接策略分配给 join_table
时,没有联接的简单查询会失败。以下查询会返回错误。
SELECT col1, col2 FROM join_table;
禁止 WHERE 子句联接¶
如果 join_table``(别名为 ``jt1
)是受联接约束的表,则以下 WHERE 子句联接会返回错误:
SELECT *
FROM join_table jt1, join_table_2 jt2
WHERE jt1.col1=jt2.col1;
左右外部联接取决于表的顺序¶
以下示例显示了外部联接的预期行为,其中 join_table``(别名为 ``jt1
)是受联接约束的表。第一个查询返回错误。
SELECT * FROM join_table jt1
LEFT OUTER JOIN join_table_2 jt2 ON jt1.col1=jt2.col1;
第二个查询返回结果。
SELECT * FROM join_table jt1
RIGHT OUTER JOIN join_table_2 jt2 ON jt1.col1=jt2.col1;
不支持联接两个受联接约束的表¶
如果 join_table
和 join_table_2
都被分配了联接策略,则以下联接查询会返回错误:
SELECT * FROM join_table jt1 JOIN join_table_2 jt2 ON jt1.col1=jt2.col1;
不允许 UNION 集操作,但允许 INTERSECT 操作¶
在这些示例中,join_table
有联接策略,但 join_table_3
没有。UNION 查询失败,但类似的 INTERSECT 查询成功。
SELECT * FROM JOIN_TABLE
UNION
SELECT * FROM JOIN_TABLE_3;
SELECT * FROM JOIN_TABLE
INTERSECT
SELECT * FROM JOIN_TABLE_3;
EXCEPT 行为取决于查询块的顺序¶
EXCEPT 行为取决于查询块的顺序。请注意,第一个查询首先从受联接约束的表中进行选择,返回错误。
SELECT * FROM JOIN_TABLE
EXCEPT
SELECT * FROM JOIN_TABLE_3;
第二个查询成功。
SELECT * FROM JOIN_TABLE_3
EXCEPT
SELECT * FROM JOIN_TABLE;
受联接约束的表上的视图也受到保护¶
假设已为 join_table
分配联接策略 jp1
。在表上创建视图:
CREATE VIEW join_table_view AS
SELECT * FROM join_table;
现在,在不指定联接的情况下查询视图:
SELECT * FROM join_table_view;
查询失败,因为它违反了 join_table
上的策略。视图查询必须包含联接。有关使用视图的联接策略行为的更多信息,请参阅 视图和物化视图。
管理联接策略¶
您可以修改、替换、分离、描述和监控联接策略。以下各部分介绍这些管理任务。
修改联接策略¶
您可以使用 ALTER JOIN POLICY 命令修改联接策略规则。您还可以重命名策略或更改其注释。
在修改联接策略之前,请运行 DESCRIBE JOIN POLICY 命令或 GET_DDL 函数,以查看策略的当前 SQL 表达式。
例如,运行以下命令以更新联接策略 jp3
的 SQL 表达式,以便不需要联接:
ALTER JOIN POLICY jp3 SET BODY -> JOIN_CONSTRAINT(JOIN_REQUIRED => FALSE);
替换联接策略¶
联接策略的推荐替换方法是在 ALTER TABLE 语句中使用 FORCE
参数,分离现有策略并在单个命令中分配新策略。此方法使您能够以原子方式替换旧策略,而不会留下任何保护漏洞。
例如,要为已受联接约束的表分配新的联接策略,请执行以下操作:
ALTER TABLE join_table SET JOIN POLICY jp2 FORCE;
您还可以在单个语句中将策略与表或视图分离(使用 UNSET JOIN POLICY),然后在其他语句中设置新策略(使用 SET JOIN POLICY)。如果您选择此方法,则在两个操作之间的过渡期内,表不受联接策略保护。在此期间,查询可能会访问敏感数据。
分离联接策略¶
使用 ALTER TABLE 或 ALTER VIEW 命令的 UNSET JOIN POLICY 子句将联接策略与表或视图分离。策略的名称不是必需项,因为一个对象不能有多个策略。例如:
ALTER VIEW join_view UNSET JOIN POLICY;
监控联接策略¶
您可以通过以下方式监控联接策略的使用情况:
在共享 SNOWFLAKE 数据库的 Account Usage 架构中查询 JOIN_POLICIES 视图。此视图是 Snowflake 账户中所有联接策略的 目录。
查询 POLICY_REFERENCES Information Schema 表函数以标识联接策略引用,并找出当前哪些表和视图应用了策略。
获取有关联接策略的信息¶
要获取有关现有联接策略的信息,请在共享 SNOWFLAKE 数据库的 Account Usage 架构中查询 JOIN_POLICIES 视图。此视图是 Snowflake 账户中所有联接策略的 目录。例如,您可以返回特定联接策略的策略正文:
SELECT policy_name, policy_body, created
FROM SNOWFLAKE.ACCOUNT_USAGE.JOIN_POLICIES
WHERE policy_name='JP2' AND created LIKE '2024-11-26%';
+-------------+----------------------------------------------------------+-------------------------------+
| POLICY_NAME | POLICY_BODY | CREATED |
|-------------+----------------------------------------------------------+-------------------------------|
| JP2 | CASE | 2024-11-26 11:22:54.848 -0800 |
| | WHEN CURRENT_ROLE() = 'ACCOUNTADMIN' | |
| | THEN JOIN_CONSTRAINT(JOIN_REQUIRED => FALSE) | |
| | ELSE JOIN_CONSTRAINT(JOIN_REQUIRED => TRUE) | |
| | END | |
+-------------+----------------------------------------------------------+-------------------------------+
获取有关附加到联接策略的表和视图的信息¶
POLICY_REFERENCES Information Schema 表函数返回附加到现有联接策略的表和视图的相关信息。您可以使用两种不同的语法选项:
为每个具有指定联接策略集的对象(表或视图)返回一行:
USE DATABASE my_db; USE SCHEMA INFORMATION_SCHEMA; SELECT policy_name, policy_kind, ref_entity_name, ref_entity_domain, ref_column_name, ref_arg_column_names, policy_status FROM TABLE(INFORMATION_SCHEMA.POLICY_REFERENCES(policy_name => 'my_db.my_schema.jp1'));
返回有关分配给
join_table
的策略的信息:USE DATABASE my_db; USE SCHEMA INFORMATION_SCHEMA; SELECT policy_name, policy_kind, ref_entity_name, ref_entity_domain, ref_column_name, ref_arg_column_names, policy_status FROM TABLE(INFORMATION_SCHEMA.POLICY_REFERENCES(ref_entity_name => 'my_db.my_schema.join_table', ref_entity_domain => 'table'));
+-------------+-------------+-----------------+-------------------+-----------------+----------------------+---------------+ | POLICY_NAME | POLICY_KIND | REF_ENTITY_NAME | REF_ENTITY_DOMAIN | REF_COLUMN_NAME | REF_ARG_COLUMN_NAMES | POLICY_STATUS | |-------------+-------------+-----------------+-------------------+-----------------+----------------------+---------------| | JP1 | JOIN_POLICY | JOIN_TABLE | TABLE | NULL | [ "COL1" ] | ACTIVE | +-------------+-------------+-----------------+-------------------+-----------------+----------------------+---------------+
策略管理的最佳实践¶
创建联接策略并向表分配联接策略的一般步骤需要与创建和分配其他策略(如掩码策略、投影策略和聚合策略)的步骤相同:
如果您使用的是集中式管理方法,可创建自定义角色(例如
join_policy_admin
)来管理策略。或者,您可以使用现有的角色。授予此角色创建和分配联接策略的权限。
创建联接策略。
创建或更改表,将策略分配给该表并允许联接列 (ALLOWED JOIN KEYS)。
测试针对表的一些联接和非联接查询。
针对该表的成功查询必须将其数据联接到另一个表或视图,并且必须在允许的列上联接。
- 访问控制管理员任务
创建自定义角色以管理联接策略。您也可以重用现有角色。
USE ROLE USERADMIN; CREATE ROLE join_policy_admin;
向
join_policy_admin
自定义角色授予以下权限:在架构中创建联接策略,并将策略分配给 Snowflake 账户中的表或视图。此步骤假定联接策略将存储在名为
privacy.join_policies
的数据库和架构中,并且此数据库和架构已存在:GRANT USAGE ON DATABASE privacy TO ROLE join_policy_admin; GRANT USAGE ON SCHEMA privacy.join_policies TO ROLE join_policy_admin; GRANT CREATE JOIN POLICY ON SCHEMA privacy.join_policies TO ROLE join_policy_admin; GRANT APPLY JOIN POLICY ON ACCOUNT TO ROLE join_policy_admin;
现在,可以将
join_policy_admin
角色分配给一个或多个用户。有关使用联接策略所需的权限的信息,请参阅 :ref:`label-join_policy_manage`(本主题内容)。
- 联接策略管理员任务
创建联接策略:
USE ROLE join_policy_admin; USE SCHEMA privacy.join_policies; CREATE OR REPLACE JOIN POLICY jp1 AS () RETURNS JOIN_CONSTRAINT -> JOIN_CONSTRAINT(JOIN_REQUIRED => TRUE);
联接策略与其他 Snowflake 功能的交互¶
以下部分总结了联接策略如何与其他 Snowflake 功能和服务交互。
其他策略¶
本部分介绍联接策略如何与其他策略交互,包括 掩码策略、行访问策略、聚合策略 和 投影策略。
您可以将其他策略附加到受联接约束的表。对表的查询必须满足所有策略的要求才能成功。
如果将行访问策略分配给受联接约束的表,则在计算联接的结果时,不会包括基于行访问策略从查询结果中排除的行。
掩码策略、行访问策略、聚合策略或投影策略的正文不能引用受联接约束的表(包括其列)。
视图和物化视图¶
您可以向视图和物化视图分配联接策略。当联接策略应用于视图时,基础表不会变为受联接约束。用户仍然可以不受限制地查询此基表。
是否可以从受联接约束的表创建视图取决于视图的类型:
您可以从一个或多个受联接约束的表创建常规视图,但是,对该视图的查询必须以符合这些基表限制的方式联接数据。不能通过在受保护的表上创建视图来规避该表的联接策略。对于针对视图的查询,将遵从并实施表的策略。有关示例,请参阅 受联接约束的表上的视图也受到保护。
您不能基于受联接约束的表或视图创建物化视图,也不能将联接策略分配给物化视图所基于的表或视图。
克隆对象¶
以下方法有助于保护数据,防止拥有 SELECT 权限的用户访问存储在克隆数据库或架构中的克隆表或视图:
不支持克隆单个联接策略对象。
克隆数据库会导致克隆数据库内的所有联接策略。
克隆架构会导致克隆该架构中的所有联接策略。
克隆的表会映射到与源表相同的联接策略。
当表在其父架构克隆的上下文中进行克隆时,如果源表引用了同一父架构中的联接策略(即本地引用),则克隆的表将引用克隆的联接策略。
如果源表引用了不同架构中的联接策略(即外部引用),则克隆表会保留外部引用。
有关更多信息,请参阅 CREATE <object> ...CLONE。
复制¶
可以使用数据库复制和复制组来复制联接策略及其分配。
对于 数据库复制,如果满足以下任一条件,则复制操作会失败:
主数据库位于 Enterprise(或更高版本)账户中,包含策略,但批准复制的一个或多个账户所属的版本较低。
主数据库中包含的表或视图具有 悬空引用 到其他数据库中的策略。
在 复制组 中复制多个数据库时,可避免数据库复制的悬空引用行为。
权限和命令¶
以下部分提供有助于管理联接策略的信息。
联接策略权限¶
Snowflake 支持对联接策略对象的以下权限。
要对架构中的任何对象执行操作,需要对父数据库和架构的 USAGE 权限。
权限 |
用途 |
---|---|
APPLY |
允许在表上对联接策略执行设置和取消设置操作。 |
OWNERSHIP |
转移联接策略的所有权,从而授予对策略的完全控制权。需要更改联接策略的大多数属性。 |
有关信息,请参阅 DDL 命令、操作和权限总结。
联接策略 DDL 引用¶
Snowflake 支持通过以下 DDL 命令创建和管理联接策略。
DDL 命令、操作和权限总结¶
下表总结了联接策略权限和 DDL 操作之间的关系。
要对架构中的任何对象执行操作,需要对父数据库和架构的 USAGE 权限。
操作 |
所需权限 |
---|---|
创建联接策略。 |
在同一架构中具有 CREATE JOIN POLICY 权限的角色。 |
更改联接策略。 |
对联接策略具有 OWNERSHIP 权限的角色。 |
描述联接策略 |
以下其中一项:
|
删除联接策略。 |
对联接策略具有 OWNERSHIP 权限的角色。 |
显示联接策略。 |
以下其中一项:
|
设置或取消设置表的联接策略。 |
以下其中一项:
|
Snowflake 支持不同的权限,以创建和设置对象的联接策略。
对于集中式联接策略管理方法,即
join_policy_admin
自定义角色在 所有 表上创建和设置联接策略时采用的方法,需要以下权限:USE ROLE securityadmin; GRANT USAGE ON DATABASE mydb TO ROLE join_policy_admin; GRANT USAGE ON SCHEMA mydb.schema TO ROLE join_policy_admin; GRANT CREATE JOIN POLICY ON SCHEMA mydb.schema TO ROLE join_policy_admin; GRANT APPLY ON JOIN POLICY ON ACCOUNT TO ROLE join_policy_admin;
在混合管理方法中,单个角色具有 CREATE JOIN POLICY 权限,以确保联接策略的命名一致,并且各个团队或角色具有特定联接策略的 APPLY 权限。
例如,可以向自定义角色
finance_role
授予在角色拥有的表和视图(即该角色对表或视图具有 OWNERSHIP 权限)上设置联接策略cost_center
的权限:USE ROLE securityadmin; GRANT CREATE JOIN POLICY ON SCHEMA mydb.schema TO ROLE join_policy_admin; GRANT APPLY ON JOIN POLICY cost_center TO ROLE finance_role;