Referência de modelo de sala limpa personalizado

Sobre os modelos de clean rooms

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.

Há dois tipos gerais de modelos:

  • 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.

Criação e execução de um modelo personalizado

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.

Um exemplo rápido

Aqui está um exemplo simples de SQL que une uma tabela de provedor e uma tabela de consumidor por e-mail e mostra a contagem de sobreposições por cidade:

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

Observações sobre o modelo:

  • Os valores em {{ double bracket pairs }} são variáveis personalizadas. group_by_col, my_table, source_table, consumer_join_col, provider_join_col e group_by_col são variáveis personalizadas preenchidas pelo chamador.

  • source_table e my_table são variáveis de matriz de cadeia de caracteres definidas pelo Snowflake e preenchidas pelo chamador. Os membros da matriz são nomes totalmente qualificados de tabelas de provedores e consumidores vinculadas à clean room. O chamador especifica quais tabelas devem ser incluídas em cada matriz.

  • 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 é necessário para todos os nomes de colunas e tabelas, pois as variáveis em {{ double brackets }} são avaliadas como literais de cadeia de caracteres, que não são identificadores válidos.

  • 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 }}.

Todos esses pontos serão discutidos em detalhes posteriormente.

Veja como um consumidor pode executar esse modelo no código. Observe como os nomes de colunas são qualificados pelos aliases de tabela declarados no modelo.

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.

Desenvolvimento de um modelo personalizado

Os modelos de sala limpa são modelos JinjaSQL. Para criar um modelo, você deve estar familiarizado com os seguintes tópicos:

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.

Exemplo:

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

Proteção de dados

Os modelos podem acessar apenas conjuntos de dados vinculados à clean room pelo provedor e pelo consumidor.

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.

Sintaxe de modelo personalizado

O Snowflake Data Clean Rooms é compatível com JinjaSQL V3, com algumas extensões, conforme indicado.

Regras de nomeação de modelos

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.

Exemplo de nomes válidos:

  • my_template

  • activation_template_1

Exemplo de nomes inválidos:

  • my template – Espaços não permitidos

  • My_Template – Somente modelos em letras minúsculas são permitidos

Variáveis de modelo

Os chamadores de modelo podem passar valores para variáveis de modelo. A sintaxe JinjaSQL permite a vinculação de variáveis para qualquer nome de variável em {{ double_brackets }}, mas o Snowflake reserva alguns nomes de variável que você não deve substituir, conforme descrito abaixo.

Cuidado

Todas as variáveis, sejam elas definidas pelo Snowflake ou personalizadas, são preenchidas pelo usuário e devem ser tratadas com a devida cautela. Os modelos do Snowflake Data Clean Rooms devem ser resolvidos em uma única instrução SELECT, mas você ainda deve se lembrar de que todas as variáveis são passadas pelo chamador.

Variáveis definidas pelo Snowflake

Todos os modelos de clean room têm acesso às seguintes variáveis globais definidas pelo Snowflake, mas passadas pelo chamador:

source_table:

Uma matriz de cadeias de caracteres com base zero contendo as tabelas e exibições vinculadas ao provedor na clean room que pode ser usada pelo modelo. Os nomes de tabelas são totalmente qualificados, por exemplo: my_db.my_sch.provider_customers

Exemplo: SELECT col1 FROM IDENTIFIER({{ source_table[0] }}) AS p;

my_table:

Uma matriz de cadeias de caracteres com base zero contendo tabelas e exibições de consumidores na clean room que pode ser usada pelo modelo. Os nomes de tabelas são totalmente qualificados, por exemplo: my_db.my_sch.consumer_customers

Exemplo: 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.

Exemplo: aqui está um exemplo de trecho de código de um modelo que usa threshold_value para aplicar um tamanho mínimo de grupo em uma cláusula de agregação.

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.

Variáveis personalizadas

Os criadores de modelos podem incluir variáveis arbitrárias em um modelo, que pode ser preenchido pelo autor da chamada. Essas variáveis podem ter qualquer nome arbitrário compatível com Jinja, exceto as variáveis definidas pelo Snowflake ou nomes de alias de tabelas. Se você quiser que seu modelo possa ser usado na UI de salas limpas, também deverá fornecer um formulário de UI para os usuários da UI de salas limpas. Para usuários de API, você deverá fornecer uma boa documentação sobre as variáveis obrigatórias e opcionais.

As variáveis personalizadas podem ser acessadas por seu modelo, conforme mostrado aqui para a variável personalizada max_income:

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

Os usuários podem passar variáveis para um modelo de duas maneiras diferentes:

  • Na UI de salas limpas, selecionando ou inserindo valores por meio de um formulário de UI criado pelo desenvolvedor do modelo. Esse formulário de UI contém elementos nos quais o usuário insere os valores do modelo. O nome do elemento de formulário é mesmo da variável. O modelo simplesmente usa o nome do elemento de formulário para acessar o valor. Crie o formulário de UI usando provider.add_ui_form_customizations.

  • No código, um consumidor chama consumer.run_analysis e passa os nomes das tabelas como matrizes de argumentos e as variáveis personalizadas como pares nome-valor ao argumento analysis_arguments.

Nota

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}}.

Resolução correta de variáveis

Os valores de cadeia de caracteres passados para o modelo são resolvidos como literais de cadeia de caracteres no modelo final. Isso pode causar erros SQL de análise ou lógicos se você não tratar adequadamente as variáveis vinculadas:

  • 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.

Portanto, quando apropriado, você deve resolver as variáveis. Veja como resolver as variáveis corretamente em seu modelo:

Resolving table and column names

As variáveis que especificam nomes de tabelas ou colunas devem ser convertidas em identificadores em seu modelo de duas maneiras:

  • IDENTIFIER: por exemplo, SELECT IDENTIFIER({{ my_column }}) FROM P;

  • sqlsafe: esse filtro JinjaSQL resolve cadeias de caracteres de identificadores para o texto SQL. Uma instrução equivalente ao item anterior é SELECT {{ my_column | sqlsafe }} FROM P;

Seu uso específico determina quando usar IDENTIFIER ou sqlsafe. Por exemplo, c.{{ my_column | sqlsafe }} não pode ser facilmente reescrito usando IDENTIFIER.

Resolving dynamic SQL

Quando você tiver uma variável de cadeia de caracteres que deva ser usada como SQL literal, como uma cláusula WHERE, use o filtro sqlsafe em seu modelo. Por exemplo:

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

Se um usuário inserir “age < 50” em where_clause, a consulta será resolvida em SELECT age FROM sometable AS C WHERE 'age < 50';, que é SQL inválido devido à condição WHERE da cadeia de caracteres literal. Nesse caso, você deve usar o filtro sqlsafe:

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

Aliases de tabela necessários

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).

Exemplo

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

O Snowflake é compatível com todos os filtros Jinja padrão e com a maioria dos filtros padrão JinjaSQL, além de algumas extensões:

  • 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.

  • O filtro JinjaSQL identifier não é compatível com os modelos Snowflake.

Dica

As instruções JinjaSQL são avaliadas da esquerda para a direita:

  • {{ my_col | column_policy }} Correto

  • {{ my_col | sqlsafe | column_policy }} Correto

  • {{ column_policy | my_col }} Incorreto

  • {{ my_col | column_policy | sqlsafe }} Incorreto: column_policy será verificada com base no valor my_col como cadeia de caracteres, o que é um erro.

Aplicação de políticas de 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.

Execução de código Python personalizado

Os modelos podem executar o código Python carregado na clean room. O modelo pode chamar uma função Python que aceita valores de uma linha de dados e retorna valores para serem usados ou projetados na consulta.

  • Quando o provedor carrega um código Python personalizado em uma sala limpa, o modelo chama funções Python com a sintaxe cleanroom.function_name. Mais detalhes aqui.

  • Quando o consumidor carrega um código Python personalizado em uma sala limpa, o modelo chama a função com o valor básico function_name passado para consumer.generate_python_request_template (sem escopo definido para cleanroom como acontece no código do provedor). Mais detalhes aqui.

Exemplo de código de provedor:

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

Considerações de segurança

A clean room template is not executed with the identity of the current user.

O usuário não tem acesso direto a nenhum dado dentro da clean room; todo o acesso é feito pelo aplicativo nativo por meio dos resultados do modelo.

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.

Modelos de ativação

Um modelo também pode ser usado para salvar os resultados de consulta em uma tabela fora da clean room; isso é chamado de ativação. Atualmente, as únicas formas de ativação compatíveis com modelos personalizados são a ativação do provedor e a ativação do consumidor (armazenando resultados na conta Snowflake do provedor ou do consumidor, respectivamente). Saiba como implementar a ativação.

Um modelo de ativação é um modelo de análise com os seguintes requisitos adicionais:

  • Os modelos de ativação são instruções JinjaSQL que são avaliadas em um bloco de script SQL, diferentemente dos modelos de análise, que podem ser simples instruções 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.

  • O bloco de script deve terminar com uma instrução RETURN que retorna o nome da tabela gerada, menos qualquer prefixo cleanroom. ou 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

  • Todas as colunas que estão sendo ativadas devem estar listadas na política de ativação do provedor ou consumidor que vinculou os dados e devem ter o filtro activation_policy aplicado a elas. Observe que uma coluna pode ser tanto uma coluna de ativação quanto uma coluna de junção.

  • Se o modelo vai ser executado pela UI de salas limpas, você deve fornecer um formulário da Web que inclua os campos activation_template_name e enabled_activations. Os modelos para uso na UI deve ter um modelo tanto de análise quanto de ativação associados.

  • Todas as colunas calculadas devem ter aliases explícitos, em vez de nomes inferidos, porque uma tabela está sendo gerada. Ou seja:

    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.

Aqui estão duas amostras de modelos básicos de ativação. Um é para ativação de servidor executado pelo provedor e o outro é para outros tipos de ativação. Eles diferem nas duas linhas destacadas, que contêm o nome da tabela de resultados.

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

Próximos passos

Depois que você dominar o sistema de modelos, leia os detalhes específicos para implementar uma clean room com seu tipo de modelo:

  • Modelos de provedor são modelos gravados pelo provedor. Esse é o caso de uso padrão.

  • Os modelos de consumidor são escritos pelo consumidor. Em alguns casos, o criador de sala limpa quer permitir que o consumidor crie, carregue e execute os próprios modelos na sala limpa.

  • Modelos de ativação criam uma tabela de resultados após uma execução bem-sucedida. Dependendo do modelo de ativação, a tabela de resultados pode ser salva na conta de provedor ou consumidor fora da clean room ou enviada a um provedor de ativação terceirizado listado no hub de ativação.

  • Modelos encadeados permitem que você encadeie vários modelos em que a saída de cada modelo é usada pelo próximo modelo da cadeia.

Mais informações