カスタムクリーンルームテンプレートのリファレンス

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;
Copy

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 }});
Copy

このテンプレートの注意事項:

  • {{ double bracket pairs }} 内の値はカスタム変数です。group_by_colmy_tablesource_tableconsumer_join_colprovider_join_colgroup_by_col はすべて、呼び出し元が設定したカスタム変数です。

  • source_table および my_table は、呼び出し元が設定するSnowflake定義の文字列配列変数です。配列のメンバーは、clean roomにリンクされたプロバイダーとコンシューマーテーブルの完全修飾名です。呼び出し元は、各配列にどのテーブルを含めるかを指定します。

  • Provider tables must be aliased as lowercase p and consumer tables as lowercase c in a template. If you have multiple tables, you can index them as p1, 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_policy and column_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'
  )
);
Copy

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']
));
Copy

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']
));
Copy

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 %};
    $$,
);
Copy

データ保護

テンプレートはプロバイダーとコンシューマーが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_template

  • activation_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
  ...
Copy
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_column or dimensions, the column policy is checked against any columns passed into these variables.

If your template uses a where_clause that 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 }};
Copy

ユーザーは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 to SELECT '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 to SELECT 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 to SELECT 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 }};
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

必須のテーブルエイリアス

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;
Copy

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.

  • identifier JinjaSQL フィルターは、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 }})
Copy

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);
$$
);
Copy

セキュリティに関する考慮事項

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

activation_

cleanroom.activation_data_*

Table name without prefix

Consumer-run provider

No prefix required

cleanroom.activation_data_*

Table name without prefix

Provider-run provider

activation_

cleanroom.temp_result_data is the full table name.

temp_result_data

  • アクティベートされる列は、データをリンクしたプロバイダーまたはコンシューマーの アクティベーションポリシー にリストされ、かつ activation_policy フィルターが適用されている必要があります。列はアクティベーション列の場合もあれば、結合列の場合もあります。

  • テンプレートをクリーンルーム UI から実行する場合、 activation_template_nameenabled_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;
Copy

次のステップ

テンプレートシステムの説明は以上です。以下では、clean roomの実装についてテンプレートタイプ別に説明します:

  • プロバイダーテンプレート は、プロバイダーが作成するテンプレートです。これがデフォルトのユースケースです。

  • コンシューマーテンプレート はコンシューマーが記述したテンプレートです。場合によっては、クリーンルーム作成者は、コンシューマーがクリーンルームに独自のテンプレートを作成、アップロード、実行できるようにしたいと考えています。

  • アクティベーションテンプレート 実行に成功すると結果テーブルが作成されます。アクティベーションテンプレートに応じて、結果テーブルはclean room外のプロバイダーまたはコンシューマーのアカウントに保存されることも、アクティベーションハブにリストされたサードパーティのアクティベーションプロバイダーに送信されることもあります。

  • 連鎖テンプレート 複数のテンプレートを連鎖させて、各テンプレートの出力を次のテンプレートで使用することができます。

詳細情報