创建自定义 Clean Room 模板

关于 Clean Room 模板

Clean Room 模板用 JinjaSQL (https://github.com/sripathikrishnan/jinjasql) 编写。JinjaSQL 是 Jinja 模板语言的扩展,生成 SQL 查询作为输出。JinjaSQL 支持逻辑语句和运行时变量解析,让用户在运行时自定义查询。变量通常在模板中使用,以允许用户指定要在查询中使用的表名称、表列和自定义值。

Snowflake 为常见用例提供了一系列预先设计的模板。这些股票模板只能在 Web 应用程序中使用。但是,提供商和使用者都可以为 Clean Room 创建自定义模板。自定义模板只能在代码中创建,但可以在代码中运行或通过 Web 应用程序运行。

创建和运行自定义模板

在默认设置的 Clean Room 中,提供商向 Clean Room 添加模板,使用者可以选择、配置和运行它:

  1. **提供者**设计一个自定义模板,通过调用 provider.add_custom_sql_template 将其添加到 Clean Room。

  2. **使用者**调用 consumer.run_analysis 运行提供商的模板,传递模板所需的任何变量的值。

此流程不需要另一方的许可,但使用者须获得提供商邀请才可进入 Clean Room。此过程存在变体,例如使用者提供的模板和提供商运行的模板,在其他部分有所涵盖

数据保护

模板只能访问提供商和使用者链接到 Clean Room 的数据集。

提供商和使用者都能够对其数据设置联接、列和激活策略,以确保哪些列可以联接或投射到激活的结果中。

快速示例

下面是一个简单的 SQL 示例,它通过电子邮件加入提供商和使用者表,并显示每个城市的重叠计数:

SELECT COUNT(*), city FROM consumer_table
  INNER JOIN provider_table
  ON consumer_table.hashed_email = provider_table.hashed_email
  GROUP BY city;
Copy

下面是该查询的模板,允许调用方选择/分组以及联接列和表:

SELECT COUNT(*), IDENTIFIER({{ group_by_col | column_policy }})
  FROM IDENTIFIER({{ my_table[0] }}) AS C
  INNER JOIN IDENTIFIER({{ source_table[0] }}) AS P
  ON IDENTIFIER({{ consumer_join_col | join_policy }}) = IDENTIFIER({{ provider_join_col | join_policy }})
  GROUP BY IDENTIFIER({{ group_by_col | column_policy }});
Copy

模板备注:

  • {{双括号对}} 内的值是自定义变量。group_by_colmy_tablesource_tableconsumer_join_colprovider_join_colgroup_by_col 都是调用方填充的自定义变量。

  • source_tablemy_table 是由调用方填充的 Snowflake 定义的字符串数组变量。数组成员是链接到 Clean Room 的提供商表和使用者表的完全限定名称。调用方指定每个数组中应该包括哪些表。

  • 在模板中,提供商表别名须为 P,使用者表别名须为 C。如果您有多个表,则可以将它们索引为 P1P2C1C2 等。

  • 所有列名和表名都需要 IDENTIFIER,因为 {{双括号}} 中的变量计算为字符串字面量,而其并非有效标识符。

  • JinjaSQL 筛选器 可以应用于变量。Snowflake 实现了自定义筛选器 join_policycolumn_policy,它们分别验证列是否符合 Clean Room 中的联接或列策略,如果不符合则查询失败。筛选器以 {{ column_name | filter_name }} 格式应用于列名称。

所有这些要点将在后面详细讨论。

下面介绍使用者如何在代码中运行此模板。请注意如何通过模板中声明的表别名来限定列名。

CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.CONSUMER.RUN_ANALYSIS(
  $cleanroom_name,
  $template_name,
  ['my_db.my_sch.consumer_table],       -- Populates the my_table variable
  ['my_db.my_sch.provider_table'],      -- Populates the source_table variable
  OBJECT_CONSTRUCT(                     -- Populates custom named variables
    'consumer_join_col','c.age_band',
    'provider_join_col','p.age_band',
    'group_by_col','p.device_type'
  )
);
Copy

为了在 Web 应用程序中使用此模板,提供商必须 为模板创建自定义 UI 表单。UI 表单具有与模板变量名称对应的命名表单元素,表单中提供的值传递到模板中。

小技巧

在早期开发中,您可以使用 consumer.get_sql_jinja 过程来查看模板渲染时的样子。但请注意,此过程不支持 Clean Room 专用扩展,例如 join_policy,因此,在使用该过程时,需要省略这些筛选器。

自定义模板语法

Snowflake Data Clean Room 支持 V3 JinjaSQL,并提供如前所述的一些扩展。

本部分包括以下主题:

模板命名规则

创建模板时,名称必须全部为小写字母、数字、空格或下划线。激活模板的名称必须以“activation”开头。调用 provider.add_custom_sql_templateconsumer.create_template_request 时分配模板名称。

有效名称示例:

  • my_template

  • activation_template_1

无效名称示例:

  • my template - 不允许存在空格

  • My_Template - 只允许使用小写模板

模板变量

模板调用方可以将值传递给模板变量。JinjaSQL 语法允许对 {{ double_brackets }} 内的任何变量名进行变量绑定,但 Snowflake 保留了一些变量名,不应替换,如下所述。

小心

所有变量,无论是 Snowflake 定义的还是自定义的,都是由用户填充的,因此应谨慎对待。Snowflake Data Clean Room 模板必须解析为单个 SELECT 语句,但您仍应记住所有变量均由调用方传入。

Snowflake 定义的变量

所有 Clean Room 模板都可以访问由 Snowflake 定义但由调用方传入的以下全局变量:

  • source_table:一个从零开始的字符串数组,表示 Clean Room 中可由模板使用的提供商关联表和视图。表名是完全限定的,例如:my_db.my_sch.provider_customers

    例如: SELECT col1 FROM IDENTIFIER({{ source_table[0] }}) AS P;

  • my_table:一个从零开始的字符串数组,表示 Clean Room 中可由模板使用的使用者表和视图。表名是完全限定的,例如:my_db.my_sch.consumer_customers

    例如: SELECT col1 FROM IDENTIFIER({{ my_table[0] }}) AS C;

备注

有两个旧版 Clean Room 全局变量:measure_columnsdimensions。尽管已不再建议使用,但由于其仍存在于部分旧版模板和文档中,因此为避免命名冲突,请勿将其用作表或列的别名。

自定义变量

模板创建者可以在模板中包含可由调用方填充的任意变量。除了 Snowflake 定义的变量或表别名之外,这些变量可以具有任何符合 Jinja 规范的任意名称。如果您希望模板在 Web 应用程序中可用,还必须为 Web 应用程序用户提供 UI 表单。对于代码用户,应为必填变量和可选变量提供完善的文档。

自定义变量可由模板访问,如这里所示的自定义变量 max_income

SELECT income FROM my_db.my_sch.customers WHERE income < {{ max_income }};
Copy

用户可以通过两种不同方式将变量传递给模板:

  • 在 Web 应用程序中, 通过模板开发者创建的 UI 表单选择或提供值。此 UI 表单包含表单元素,用户可以在其中为您的模板提供值。表单元素的名称为变量的名称。模板仅使用表单元素的名称来访问值。使用 provider.add_ui_form_customizations 创建 UI 表单。

  • 在代码中, 使用者调用 consumer.run_analysis 并将表名作为实参数组传入,将自定义变量作为名称-值对传入 analysis_arguments 实参。

备注

如果需要访问上传到 Clean Room 的任何自定义 Python 代码中的用户提供的值,必须通过 Python 函数实参 将变量值显式传递 给代码;不能使用 {{jinja variable binding syntax}} 直接在 Python 代码中访问模板变量。

正确解析变量

传入模板的字符串值解析为最终模板中的字符串字面量。如果绑定变量处理不当,可能会导致 SQL 解析或逻辑错误:

  • SELECT {{ my_col }} FROM P; 解析为 SELECT 'my_col' from P;,仅返回字符串“my_col”,这可能不是您想要的。

  • SELECT age FROM {{ my_table[0] }} AS P; 解析为 SELECT age FROM 'somedb.somesch.my_table' AS P;,这会导致分析错误,因为表必须为标识符,而非字面量字符串。

  • SELECT age FROM IDENTIFIER({{ my_table[0] }}) AS P {{ where_clause }}; 传入“WHERE age < 50”后计算为 SELECT age FROM mytable AS P 'WHERE age < 50';,这是由字面量字符串 WHERE 子句导致的解析错误。

因此,在适当情况下,您必须解析变量。下面介绍如何在模板中正确解析变量:

表和列名称

指定表或列名称的变量必须通过以下两种方式之一转换为模板中的标识符:

  • IDENTIFIER:例如:SELECT IDENTIFIER({{ my_column }}) FROM P;

  • sqlsafe (https://github.com/sripathikrishnan/jinjasql?tab=readme-ov-file#sql-safe-strings):此 JinjaSQL 筛选器将标识符字符串解析为 SQL 文字。与前一个项目符号等效的语句是 SELECT {{ my_column | sqlsafe }} FROM P;

您的特定用法决定了何时使用 IDENTIFIER 或 sqlsafe。例如,c.{{ my_column | sqlsafe }} 不易使用 IDENTIFIER 重写。

动态 SQL

当有字符串变量用作字面量 SQL 时,例如 WHERE 子句,请使用模板中的 sqlsafe 筛选器。例如:

SELECT age FROM IDENTIFIER({{ my_table[0] }}) AS C WHERE {{ where_clause }};
Copy

如果用户将“age < 50”传递给 where_clause,查询将解析为 SELECT age FROM sometable AS C WHERE 'age < 50';,由于字面量字符串 WHERE 条件导致其为无效 SQL。在这种情况下,您应该使用 sqlsafe 筛选器:

SELECT age FROM IDENTIFIER( {{ my_table[0] }} ) as C {{ where_clause | sqlsafe }};
Copy

必填表别名

在查询顶级,所有表或子查询必须使用别名 P``(用于提供商表)或 ``C``(用于使用者表),以便 Snowflake 在查询中正确验证联接和列策略。必须对照联接或列策略验证的任何列都属于别名为 ``PC 的表。(指定 PC 告诉后端是分别针对提供商还是使用者策略验证列。)

如果在查询中使用多个提供商表或使用者表,请在第一个表别名后为每个表别名添加一个从 1 开始的连续后缀。因此,PP1P2 等表示第一、第二和第三个提供商表,CC1C2 等表示第一、第二和第三个使用者表。PC 索引应该是连续的且无间隙(即创建别名 PP1P2,而非 PP2P4)。

示例

SELECT col1 FROM IDENTIFIER({{ source_table[0] }}) AS P;
Copy

模板筛选器

Snowflake 支持大多数标准 JinjaSQL 筛选器 (https://github.com/search?q=repo%3Asripathikrishnan%2Fjinjasql+self.env.filters+path%3Ajinjasql%2Fcore.py&type=code&path+jinjasql%2Fcore.py=),以及一些自定义筛选器:

  • join_policy:验证表的联接策略是否允许列,如果不允许则失败。

  • column_policy:验证模板的列策略是否允许该列(允许投射)。

  • activation_policy:验证 Clean Room 的激活策略(provider.set_activation_policyconsumer.set_activation_policy)是否允许筛选列。

  • join_and_column_policy:验证联接、激活或列策略是否允许列。用于在 Clean Room 中提供更大的灵活性,允许合作者在不更改模板的情况下更新联接和列策略。

  • sql_safe:标准 JinjaSQL 筛选器。用于根据需要将字符串字面量转换为 SQL 语句或标识符。

  • bind:标准 JinjaSQL 筛选器。

  • inclause:标准 JinjaSQL 筛选器。

  • Snowflake 模板 不支持 identifier JinjaSQL 筛选器。

JinjaSQL 筛选器修改其前面的内容:

  • {{ my_col | column_policy }} 正确

  • {{ column_policy | my_col }} 不正确

运行自定义 Python 代码

模板可以运行上传到 Clean Room的 Python 代码。模板可以调用 Python 函数,该函数接受一行数据中的值,并返回值以在查询中使用或投射。

  • 提供商 将自定义的 Python 代码上传到 Clean Room 时,模板会使用语法 cleanroom.function_name 调用 Python 函数。更多详细信息请点击此处。

  • 使用者 将自定义 Python 代码上传到 Clean Room 时,模板会直接使用传递给 consumer.generate_python_request_template 的裸 function_name 值调用该函数(不会像提供商代码那样限定在 Clean Room)。更多详细信息请点击此处。

提供商代码示例:

-- Provider uploads a Python function that takes two numbers and returns the sum.
call samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
  $cleanroom_name,
  'simple_addition',                        -- Function name to use in the template
  ['someval integer', 'added_val integer'], -- Arguments
  [],                                       -- No packages needed
  'integer',                                -- Return type
  'main',                                   -- Handler for function name
  $$

def main(input, added_val):
  return input + int(added_val)
    $$
);

-- Template passes value from each row to the function, along with a
-- caller-supplied argument named 'increment'
call samooha_by_snowflake_local_db.provider.add_custom_sql_template(
    $cleanroom_name,
    'simple_python_example',
$$
    SELECT val, cleanroom.simple_addition(val, {{ increment | sqlsafe }})
    FROM VALUES (5),(8),(12),(39) AS P(val);
$$
);
Copy

安全注意事项

模板必须计算到由 Clean Room 原生应用程序执行的单个 SELECT 查询。模板不以当前用户的身份执行。

用户不能直接访问 Clean Room 内的任何数据;所有访问都是通过模板结果,借助原生应用程序进行的。

在查询中使用列时应用策略筛选器, 即使在模板中明确定义了列名,或列或表是由您提供的情况下也是如此。您以后可能会更改联接或列策略,或更改列,而忘记更新模板。对于用户提供的任何列,应用 join_policycolumn_policyjoin_and_column_policyactivation_policy 筛选器。

后续步骤

掌握模板系统后,请阅读使用模板类型实现 Clean Room 的详细说明:

  • 提供商模板 是提供商编写的模板。这是默认用例。

  • 使用者模板 是使用者编写的模板。在某些情况下,Clean Room 创建者希望让使用者能够创建、运行自己的模板并将其上传至 Clean Room。

  • 激活模板 在成功运行后创建一个结果表。根据激活模板,结果表可以保存到 Clean Room 之外的提供商或使用者账户,也可以发送到 Activation Hub 中所列的第三方激活提供商。

  • 链接模板 允许您将多个模板链接在一起,每个模板的输出由链中的下一个模板使用。

语言: 中文