カスタムクリーンルームテンプレートのリファレンス¶
clean roomテンプレートについて¶
Clean room templates are written in JinjaSQL. JinjaSQL is an extension to the Jinja templating language that generates a SQL query as output. This allows templates to use logic statements and run-time variable resolution to let the user specify table names, table columns, and custom values used in the query at run time.
Snowflake provides some pre-designed templates for common use cases. However, most users prefer to create custom query templates for their clean rooms. Custom templates are created using the clean rooms API, but can be run either in code or using the clean rooms UI.
テンプレートには大きく分けて2つのタイプがあります:
Analysis templates, which evaluate to a SELECT statement (or a set of SELECT operations) that show results to the template runner.
Activation templates, which are used to activate results to a Snowflake account or a third-party, rather than showing results in the immediate environment. An activation template is very similar to an analysis template with a few extra requirements.
In the clean rooms UI, an analysis template can be associated with an activation template to enable the caller to run an analysis, see results, and then activate data to themselves or a third party. The activation template does not need to resolve to the same query as the associated analysis template.
カスタムテンプレートの作成と実行¶
In a clean room with default settings, the provider adds a template to a clean room and the consumer runs the template, as described in the custom template usage documentation.
簡単な例¶
以下は、プロバイダーテーブルとコンシューマーテーブルをメールで結合し、重複回数を都市ごとに表示するシンプルな SQL の例です:
SELECT COUNT(*), city FROM consumer_table
INNER consumer_table
ON consumer_table.hashed_email = provider_table.hashed_email
GROUP BY city;
Here is how that query would look as a JinjaSQL template that allows the caller to choose the JOIN and GROUP BY columns, as well as the tables used:
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 }});
このテンプレートの注意事項:
{{ double bracket pairs }} 内の値はカスタム変数です。
group_by_col、my_table、source_table、consumer_join_col、provider_join_col、group_by_colはすべて、呼び出し元が設定したカスタム変数です。source_tableおよびmy_tableは、呼び出し元が設定するSnowflake定義の文字列配列変数です。配列のメンバーは、clean roomにリンクされたプロバイダーとコンシューマーテーブルの完全修飾名です。呼び出し元は、各配列にどのテーブルを含めるかを指定します。Provider tables must be aliased as lowercase
pand consumer tables as lowercasecin a template. If you have multiple tables, you can index them asp1,p2,c1,c2, and so on.IDENTIFIER がすべての列名とテーブル名に必要です。なぜなら、 {{ double brackets }} の変数は文字列リテラルに評価されますが、これは有効な識別子ではないからです。
JinjaSQL filters can be applied to variables to enforce any join or column policies set by either side. Snowflake implements custom filters
join_policyandcolumn_policy, which verify whether a column complies with join or column policies in the clean room respectively, and fail the query if it does not. A filter is applied to a column name as{{ 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'
)
);
To be able to use this template in the clean rooms UI, the provider must create a custom UI form for the template. The UI form has named form elements that correspond to template variable names, and the values provided in the form are passed into the template.
カスタムテンプレートの開発¶
クリーンルームのテンプレートは JinjaSQL テンプレートです。テンプレートを作成するには、次のトピックに精通している必要があります。
Use the consumer.get_jinja_sql procedure to test the validity of your template,
then run the rendered template to see that it produces the results that you expect. Note that this procedure doesn't support clean room filter extensions, such as join_policy, so you must test your template without those filters, and add them later.
例:
-- Template to test
SELECT {{ col1 | sqlsafe }}, {{ col2 | sqlsafe }}
FROM IDENTIFIER({{ source_table[0] }}) AS p
JOIN IDENTIFIER({{ my_table[0] }}) AS c
ON {{ provider_join_col | sqlsafe }} = {{ consumer_join_col | sqlsafe}}
{% if where_phrase %} WHERE {{ where_phrase | sqlsafe}}{% endif %};
-- Render the template.
USE WAREHOUSE app_wh;
USE ROLE SAMOOHA_APP_ROLE;
CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.CONSUMER.GET_SQL_JINJA(
$$
SELECT {{ col1 | sqlsafe }}, {{ col2 | sqlsafe }}
FROM IDENTIFIER({{ source_table[0] }}) AS p
JOIN IDENTIFIER({{ my_table[0] }}) AS c
ON IDENTIFIER({{ provider_join_col }}) = IDENTIFIER({{ consumer_join_col }})
{% if where_phrase %} WHERE {{ where_phrase | sqlsafe }}{% endif %};
$$,
object_construct(
'col1', 'c.status',
'col2', 'c.age_band',
'where_phrase', 'p.household_size > 2',
'consumer_join_col', 'c.age_band',
'provider_join_col', 'p.age_band',
'source_table', ['SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS'],
'my_table', ['SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS']
));
The rendered template looks like this:
SELECT c.status, c.age_band
FROM IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS p
JOIN IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS c
ON p.age_band = c.age_band
WHERE p.household_size > 2;
Try running the SQL statement above in your environment to see if it works, and gets the expected results.
Then test your template without a WHERE clause:
-- Render the template without a WHERE clause
CALL SAMOOHA_BY_SNOWFLAKE_LOCAL_DB.CONSUMER.GET_SQL_JINJA(
$$
SELECT {{ col1 | sqlsafe }}, {{ col2 | sqlsafe }}
FROM IDENTIFIER({{ source_table[0] }}) AS p
JOIN IDENTIFIER({{ my_table[0] }}) AS c
ON {{ provider_join_col | sqlsafe }} = {{ consumer_join_col | sqlsafe}}
{% if where_phrase %} WHERE {{ where_phrase | sqlsafe }}{% endif %};
$$,
object_construct(
'col1', 'c.status',
'col2', 'c.age_band',
'consumer_join_col', 'c.age_band',
'provider_join_col', 'p.age_band',
'source_table', ['SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS'],
'my_table', ['SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS']
));
Rendered template:
SELECT c.status, c.age_band
FROM IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS p
JOIN IDENTIFIER('SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS') AS c
ON p.age_band = c.age_band
;
Add the policy filters to the template, and add the template to your clean room:
CALL samooha_by_snowflake_local_db.provider.add_custom_sql_template(
$cleanroom_name,
'simple_template',
$$
SELECT {{ col1 | sqlsafe | column_policy }}, {{ col2 | sqlsafe | column_policy }}
FROM IDENTIFIER({{ source_table[0] }}) AS p
JOIN IDENTIFIER({{ my_table[0] }}) AS c
ON {{ provider_join_col | sqlsafe | join_policy }} = {{ consumer_join_col | sqlsafe | join_policy }}
{% if where_phrase %} WHERE {{ where_phrase | sqlsafe }}{% endif %};
$$,
);
データ保護¶
テンプレートはプロバイダーとコンシューマーがclean roomにリンクしたデータセットにのみアクセスできます。
Both the provider and consumer can set join, column, and activation policies on their data to protect which columns can be joined on, projected, or activated; however, the template must include the appropriate JinjaSQL policy filter on a column for the policy to be applied.
カスタムテンプレートの構文¶
Snowflake Data Clean Roomsは、いくつか機能が拡張されたV3の JinjaSQL をサポートしています。
テンプレートの命名規則¶
When creating a template, names must be all lowercase letters, numbers, spaces, or underscores.
Activation templates (except for consumer-run provider activation) must have a name beginning with activation_. Template names are
assigned when you call provider.add_custom_sql_template or consumer.create_template_request.
有効な名前の例:
my_templateactivation_template_1
無効な名前の例:
my template- スペースは使用できませんMy_Template- 小文字のみ使用可
テンプレート変数¶
テンプレートの呼び出し元は、テンプレート変数に値を渡すことができます。JinjaSQL 構文を使用すると、 {{ double_brackets }} 内の任意の変数名に対する変数バインディングが可能になりますが、Snowflakeは以下に説明するように、変数名をいくつか予約しており、それらをオーバーライドすることはできません。
注意
Snowflake定義の変数でも、カスタム変数でも、すべての変数をユーザーが入力するため、注意して適切に操作する必要があります。Snowflake Data Clean Roomsテンプレートは必ず、単一の 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;privacy:A set of privacy-related values associated with users and templates. See the list of available child fields. These values can be set explicitly for the user, but you might want to set default values in the template. Access the child fields directly in your template, such as
privacy.threshold.例:
threshold_valueを使用して集約句に最小グループサイズを適用するテンプレートのコード例を以下に示します。SELECT IFF(a.overlap > ( {{ privacy.threshold_value | default(2) | sqlsafe }} ), a.overlap,1 ) AS overlap, c.total_count AS total_count ...
measure_column:dimensions:where_clause:Legacy clean room global variables. They are no longer recommended for use, but are still defined and appear in some legacy templates and documentation, so you should not alias tables or columns using either of these names to avoid naming collisions.
If your template uses
measure_columnordimensions, the column policy is checked against any columns passed into these variables.If your template uses a
where_clausethat has a join condition (for example,table1.column1 = table2.column2), the join policy is checked against any columns named there; otherwise, the column policy is checked against any columns named there.
カスタム変数¶
テンプレート作成者は、呼び出し元が入力できるテンプレートに任意の変数を含めることができます。これらの変数には、Snowflake定義の変数やテーブルエイリアス名以外の、Jinjaに準拠した任意の名前を付けることができます。クリーンルーム UI でテンプレートの使用を可能にしたい場合は、クリーンルーム UI ユーザー用の UI フォームも提供する必要があります。API ユーザーの場合は、必須変数とオプション変数の適切なドキュメントを提供する必要があります。
カスタム変数は、以下に示すカスタム変数 max_income のように、テンプレートからアクセスできます。
SELECT income FROM my_db.my_sch.customers WHERE income < {{ max_income }};
ユーザーは2種類の方法で、変数をテンプレートに渡すことができます。
クリーンルーム UI で、 テンプレート開発者によって作成された UI フォームを介して値を選択または提供します。この UI フォームには、ユーザーがテンプレートの値を指定できるフォーム要素が含まれています。フォーム要素の名前は、変数の名前です。テンプレートは、単純にフォーム要素の名前を使用して値にアクセスします。provider.add_ui_form_customizations を使用して、UI フォームを作成します。
コードの場合は、 コンシューマーが consumer.run_analysis を呼び出し、引数配列のテーブル名、および名前と値のペアであるカスタム変数を、
analysis_arguments引数に渡します。
注釈
If you need to access user-provided values in any custom Python code uploaded to the clean room, you must
explicitly pass variable values in to the code through Python function arguments; template
variables are not directly accessible within the Python code using {{jinja variable binding syntax}}.
変数を正しく解決する¶
文字列値はテンプレートに渡されると、最終的なテンプレートで文字列リテラルに解決されます。バインドされた変数の処理が適切出ない場合、 SQL 解析エラーまたは論理エラーの原因になります:
SELECT {{ my_col }} FROM P;- This resolves toSELECT 'my_col' from P;which simply returns the string "my_col" - probably not what you want.SELECT age FROM {{ my_table[0] }} AS P;- This resolves toSELECT age FROM 'somedb.somesch.my_table' AS P;, which causes a parsing error because a table must be an identifier, not a literal string.SELECT age FROM IDENTIFIER({{ my_table[0] }}) AS P {{ where_clause }};- Passing in "WHERE age < 50" evaluates toSELECT age FROM mytable AS P 'WHERE age < 50';, which is a parsing error because of the literal string WHERE clause.
したがって、必要に応じて変数を解決する必要があります。次では、テンプレートで変数を適切に解決する方法を説明します:
- Resolving table and column names
テーブル名や列名を指定する変数は、以下の2つの方法のどちらかを使用し、テンプレート内で識別子に変換する必要があります:
IDENTIFIER: 例:
SELECT IDENTIFIER({{ my_column }}) FROM P;sqlsafe: この JinjaSQL フィルターは識別子の文字列を SQL テキストに解決します。前項と同等のステートメントは
SELECT {{ my_column | sqlsafe }} FROM P;です
IDENTIFIER と
sqlsafeのどちらを使うかは、状況によって異なります。たとえば、c.{{ my_column | sqlsafe }}は、 IDENTIFIER を使用して簡単に書き換えることはできません。- Resolving dynamic SQL
WHERE 句のように、リテラル SQL として使用する文字列変数がある場合は、
sqlsafeフィルターをテンプレートで使用してください。例:SELECT age FROM IDENTIFIER({{ my_table[0] }}) AS C WHERE {{ where_clause }};
ユーザーが "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 }};
必須のテーブルエイリアス¶
At the top level of your query, all tables or subqueries must be aliased as either p (for provider-tables) or c (for consumer
tables) in order for Snowflake to validate join and column policies correctly in the query. Any column that must be verified against join
or column policies must be qualified with the lowercase p or c table alias. (Specifying p or c tells the back end
whether to validate a column against the provider or the consumer policy respectively.)
If you use multiple provider or consumer tables in your query, add a numeric, sequential 1-based suffix to each table alias after the
first. So: p, p1, p2, and so on for the first, second, and third provider tables, and c, c1, c2, and so on for the
first, second, and third consumer tables. The p or c index should be sequential without gaps (that is, create the aliases p,
p1, and p2, not p, p2, and p4).
例
SELECT p.col1 FROM IDENTIFIER({{ source_table[0] }}) AS P
UNION
SELECT p1.col1 FROM IDENTIFIER({{ source_table[1] }}) AS P1;
Custom clean room template filters¶
Snowflake は、すべての 標準 Jinja フィルター と、ほとんどの標準 JinjaSQL フィルター、およびいくつかの拡張機能をサポートしています:
join_policy: Succeeds if the column is in the join policy of the data owner; fails otherwise.column_policy: Succeeds if the column is in the column policy of the data owner; fails otherwise.activation_policy: Succeeds if the column is in the activation policy of the data owner; fails otherwise.join_and_column_policy: Succeeds if the column is in the join or column policy of the data owner; fails otherwise.identifierJinjaSQL フィルターは、Snowflakeテンプレートでは サポートされていません 。
Tip
JinjaSQL ステートメントは左から右に評価されます。
{{ my_col | column_policy }}正しい{{ my_col | sqlsafe | column_policy }}正しい{{ column_policy | my_col }}正しくない{{ my_col | column_policy | sqlsafe }}正しくない:column_policyは、文字列としてmy_col値をチェックされ、エラーが起こります。
clean roomへのポリシーの適用¶
Clean rooms do not automatically check clean room policies against columns used in a template. If you want to enforce a policy against a column:
You must apply the appropriate policy filter to that column in the template. For example:
JOIN IDENTIFIER({{ source_table[0] }}) AS p
ON IDENTIFIER({{ c_join_col | join_policy }}) = IDENTIFIER({{ p_join_col | join_policy }})
You must alias the table as lowercase p or c. See 必須のテーブルエイリアス.
Policies are checked only against columns owned by other collaborators; policies are not checked for your own data.
Note that column names cannot be ambiguous when testing policies. So if you have columns with the same name in two tables, you must qualify the column name in order to test the policy against that column.
カスタムなPythonコードの実行¶
clean roomにアップロードされたPythonコードを、テンプレートから実行することができます。テンプレートからPython関数を呼び出し、データ行から値を受け取ったり、クエリで使用する値や投影する値を返したりできます。
プロバイダー がカスタムPythonコードをクリーンルームにアップロードすると、テンプレートは構文
cleanroom.function_nameでPython関数を呼び出します。詳細はこちらをご参照ください。コンシューマー がカスタムPythonコードをクリーンルームにアップロードすると、テンプレートはそのままの
function_name値をconsumer.generate_python_request_template(プロバイダーコードのように`クリーンルーム`にスコープされていない)に渡して関数を呼び出します。詳細はこちらをご参照ください。
プロバイダーコードの例:
-- 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);
$$
);
セキュリティに関する考慮事項¶
A clean room template is not executed with the identity of the current user.
ユーザーはclean room内のデータに直接アクセスするのではなく、すべてのアクセスがテンプレート結果を介して、ネイティブアプリケーションで行われます。
Apply a policy filter any time a column is used in your template to ensure that your policies, and the policies of all collaborators, are respected.
Wrap user-provided variables with IDENTIFIER() when possible to strengthen your templates against SQL injection attacks.
アクティベーションテンプレート¶
テンプレートを使用すると、クエリ結果をclean room外のテーブルに保存することもできます。これを アクティベーション と呼びます。現在、カスタムテンプレートでサポートされているアクティベーションは、プロバイダーのアクティベーションと、コンシューマーのアクティベーション (それぞれプロバイダーまたはコンシューマーのSnowflakeアカウントに結果を保存します) のみです。アクティベーションの実装方法についてはこちらをご参照ください。
アクティベーションテンプレートは、以下の要件が追加された分析テンプレートです:
アクティベーションテンプレートは、 SQL スクリプトブロックに評価される JinjaSQL ステートメントで、単純な SELECT ステートメントになる分析テンプレートとは異なります。
Activation templates create a table in the clean room to store results, and return the table name (or a fragment of the name) to the template caller.
スクリプトブロックの最後には RETURN ステートメントを記述します。これは、生成されたテーブルの名前をから
cleanroom.やcleanroom.activation_data_のプレフィックスを除いた名前を返します。The name of the template, the name of the internal table that the template creates, and the table name the template returns follow these patterns:
Activation type |
Template name prefix |
Table name prefix |
Returned table name |
|---|---|---|---|
Consumer-run consumer |
|
|
Table name without prefix |
Consumer-run provider |
No prefix required |
|
Table name without prefix |
Provider-run provider |
|
|
|
アクティベートされる列は、データをリンクしたプロバイダーまたはコンシューマーの アクティベーションポリシー にリストされ、かつ
activation_policyフィルターが適用されている必要があります。列はアクティベーション列の場合もあれば、結合列の場合もあります。テンプレートをクリーンルーム UI から実行する場合、
activation_template_nameとenabled_activationsを含む ウェブフォームを提供 する必要があります。UI で使用するテンプレートには、分析テンプレートと関連するアクティベーションテンプレートの両方が必要です。テーブルが生成されるため、すべての計算列は推測される名前ではなく、明示的にエイリアスされなければなりません。つまり:
SELECT COUNT(*), p.status from T AS P;FAILS, because the COUNT column name is inferred.SELECT COUNT(*) AS COUNT_OF_ITEMS, p.status from T AS P;SUCCEEDS, because it explicitly aliases the COUNT column.
以下に、基本的なアクティベーションテンプレートの例を2つ示します。1つはプロバイダーが実行するサーバーのアクティベーションで使用し、もう1つはその他のアクティベーションタイプで使用します。両者の違いは、結果テーブルの名前を含む強調表示された2行です。
Table must be named cleanroom.temp_result_data:
BEGIN
CREATE OR REPLACE TABLE cleanroom.temp_result_data AS
SELECT COUNT(c.status) AS ITEM_COUNT, c.status, c.age_band
FROM IDENTIFIER({{ my_table[0] }}) AS c
JOIN IDENTIFIER({{ source_table[0] }}) AS p
ON {{ c_join_col | sqlsafe | activation_policy }} = {{ p_join_col | sqlsafe | activation_policy }}
GROUP BY c.status, c.age_band
ORDER BY c.age_band;
RETURN 'temp_result_data';
END;
Table name needs prefix cleanroom.activation_data:
BEGIN
CREATE OR REPLACE TABLE cleanroom.activation_data_analysis_results AS
SELECT COUNT(c.status) AS ITEM_COUNT, c.status, c.age_band
FROM IDENTIFIER({{ my_table[0] }}) AS c
JOIN IDENTIFIER({{ source_table[0] }}) AS p
ON {{ c_join_col | sqlsafe | activation_policy }} = {{ p_join_col | sqlsafe | activation_policy }}
GROUP BY c.status, c.age_band
ORDER BY c.age_band;
RETURN 'analysis_results';
END;
次のステップ¶
テンプレートシステムの説明は以上です。以下では、clean roomの実装についてテンプレートタイプ別に説明します:
プロバイダーテンプレート は、プロバイダーが作成するテンプレートです。これがデフォルトのユースケースです。
コンシューマーテンプレート はコンシューマーが記述したテンプレートです。場合によっては、クリーンルーム作成者は、コンシューマーがクリーンルームに独自のテンプレートを作成、アップロード、実行できるようにしたいと考えています。
アクティベーションテンプレート 実行に成功すると結果テーブルが作成されます。アクティベーションテンプレートに応じて、結果テーブルはclean room外のプロバイダーまたはコンシューマーのアカウントに保存されることも、アクティベーションハブにリストされたサードパーティのアクティベーションプロバイダーに送信されることもあります。
連鎖テンプレート 複数のテンプレートを連鎖させて、各テンプレートの出力を次のテンプレートで使用することができます。