クリーンルームでカスタム関数をアップロードして実行する

概要

カスタムPython UDFs および UDTFs をクリーンルームにアップロードし、テンプレートから実行して複雑なデータアクションを実行します。これらのアクションには、単一のステップまたは :doc:`複数のステップフロー </user-guide/cleanrooms/multistep-flows>`の一部として、クエリ内での機械学習やカスタマイズされたデータ操作が含まれます。Pythonは、カスタム UDFs でサポートされている唯一のコーディング言語です。

アップロードされたコードは、Pythonパッケージの承認済みバンドル および Snowpark API からパッケージをインポートして、使用できます。

Templates in a clean room can call code uploaded by the account that added the template. Uploaded code can't be viewed or downloaded. Snowflake scans uploaded code for security issues before installing the code.

There are different mechanisms for uploading code into a clean room, depending on your role:

Providers

  • Inline code upload: If you want to upload code using the default compute resources for a clean room, and need to use only the standard bundle of Python packages (including the Snowpark API), you should upload inline code.

  • Snowpark Container Services running within a clean room: If you need more control over the environment, such as specifying additional compute or custom libraries, you can run a container within a clean room.

Consumers

  • Inline upload with template: Consumers can upload and run a template bundled with code. The code is bound to the template, and must be approved by the clean room provider.

このトピックは、カスタムPython UDFs および UDTFs をプロバイダーまたはコンシューマーとしてアップロードし、実行する方法を示しています。

Tip

クリーンルームで独自のPython UDFs を開発する方法に関する背景情報については、以下のトピックをご参照ください。

  • SnowflakeでPython関数を記述する方法に関する一般的な背景情報については、Snowflakeでの UDFs の仕組み

  • 関数からテーブルを返す場合は、Snowflakeで UDTFs を記述する方法

  • :doc:`カスタムテンプレートを作成し、クリーンルームにアップロードする方法。</user-guide/cleanrooms/demo-flows/custom-templates>`UDFs/UDTFs は、カスタムテンプレートから呼び出されます。

  • :doc:`クリーンルームでのSnowparkの使用 </user-guide/cleanrooms/demo-flows/snowpark>`(Snowparkから UDFs を呼び出したい場合)。

Entry points for uploaded code

アップロードされたコードの各バンドルは、お互いを呼び出す複数の関数を定義できますが、バンドルは1つのハンドラー関数のみを公開します。このハンドラー関数は、クリーンルームを使用するユーザーが作成または実行するテンプレートから呼び出すことができます。コードが内部テーブルを作成する場合、マルチステップフローのデザイン で説明されているようにそれらのテーブルにアクセスできます。

たとえば、2つの数値パラメーターを受け取る simple_add という名前の関数をアップロードした場合、ここに示すようにテンプレートから呼び出すことができます。関数は常にスコープ cleanroom を使用して参照されます。たとえば、テンプレートはこのような simple_add を呼び出すことができます。

SELECT cleanroom.simple_add({{ price | sqlsafe | int }}, {{ tax | sqlsafe | int }}) ...
Copy

Tip

プロバイダーが上記のコードを実行したい場合、結果テーブルがバックグラウンドで生成されるため、集計またはカスタム関数を使用するすべての SELECT 列にエイリアスを作成する必要があります。

SELECT
  cleanroom.simple_add(
    {{ price | sqlsafe | int }}, {{ tax | sqlsafe | int }}
    ) AS TOTAL_ITEM_COST
...
Copy

You can upload multiple functions in a single package, and functions within a single package can call each other, but functions can't call functions within other packages. (They can call the handler functions, though.) For example, if you have a clean room where you upload two packages, each with a handler function and two helper functions:

2つのアップロードされたPythonパッケージを持つクリーンルーム

パッケージ1

パッケージ2

  • ハンドラー関数A

  • ヘルパー関数**A1**

  • ヘルパー関数**A2**

  • ハンドラー関数B

  • ヘルパー関数**B1**

  • ヘルパー関数**B2**

  • Code uploaded by either party (provider or consumer) can be run templates submitted by either party.

  • A template can call function A or function B, but not A1, A2, B1, or B2.

  • 関数Aは関数Bを呼び出し、その逆も可能です。

  • 関数AはB1またはB2を呼び出すことができず、関数BはA1またはA2を呼び出すことはできません。

  • A1はA2を呼び出し、その逆も可能です。A1とA2はBを呼び出すことができます。A1とA2はB1またはB2を呼び出すことはできません。

  • B1はB2を呼び出し、その逆も可能です。B1とB2はAを呼び出すことができます。B1とB2はA1またはA2を呼び出すことはできません。

カスタム関数の更新または削除

既存の関数またはアップロードしたテンプレートをアップロードしたり、上書きしたりすることはできますが、既存の関数またはテンプレートを削除することはできません。関数を「削除」する唯一の方法は、常に成功するまったく同じ名前と署名を持つダミー関数を作成することです。

Uploading a function with the same signature as one that you previously uploaded will overwrite the existing function, where a signature means the case-insensitive function name of an external handler, plus the data types of all its arguments, in the same order. Argument names are not part of the signature. You can't overwrite a function uploaded by another account.

関数を更新するときに署名が一致する必要があるため、既存の関数の署名を変更することはできません。関数 foo(name VARIANT age INTEGER) をアップロードしてから、関数 foo(name VARIANT age FLOAT) をアップロードすると、引数の型が異なるため、最初の関数に加えて、2番目の関数がクリーンルームに追加されます。

プロバイダー送信コード

プロバイダー送信関数は、インラインコードとして、またはSnowflakeステージからアップロードできます。両方の手法はここで説明されています。

アップロードされたコードは、Pythonパッケージの承認済みセット からネイティブにパッケージをインポートして、使用できます。デフォルト以外のパッケージが必要な場合は、クリーンルームでのSnowpark Container Services を使用してコードをホストする必要があります。

自分のコードであっても、アップロードされたコードは表示できません。そのため、必ずクリーンルームにアップロードするものの正確なコピーを追加してください。

概要

プロバイダーがクリーンルームにコードを追加する方法の概要は次のとおりです。

  1. プロバイダーは通常の方法でクリーンルームを作成し、構成します。

  2. The provider uploads a bundle by calling provider.load_python_into_cleanroom. You can either upload your code inline directly within that procedure, or upload a code file to a stage, then provide the stage location to that procedure.

    Although each bundle can include multiple functions, only one handler function is exposed for each upload. To expose multiple functions to templates, upload each handler separately or do a bulk upload (described below).

  3. If the clean room is exposed externally, security checks are run before the code is installed in the clean room, and you must call provider.view_cleanroom_scan_status to confirm that security checks have passed before incrementing the default version.

  4. After each successful upload, a new patch version of the clean room is generated. You must then increase the default version by calling provider.set_default_release_directive with the new patch number.

  5. Create and upload a custom template that calls handlers in your code. The template must call the handler function using the cleanroom scope, that is: cleanroom.my_function(...).

  6. コンシューマーは、他のテンプレートと同じ方法でテンプレートを実行します。

    Tip

    コンシューマーがカスタムコードでクリーンルームをインストールする際にマウントエラーが発生した場合、これはコードの構文エラーを示している可能性があります。

このフローを示すコード例は、プロバイダー記述コードの例セクション にあります。

バージョン管理に関する重要な注意事項

Every time the provider uploads a function, it increases the clean room patch number (and there is a limit of 99 patch numbers). Therefore, do your best to test and debug your code thoroughly before adding it to the clean room to reduce version updates during development.

You can upload multiple packages at once in a single bulk upload to reduce the number of patches generated. However, bulk uploads can make it more challenging to debug if the upload has a security scan issue, because the file that caused the problem isn't reported in the error response.

パッチ番号を更新した場合、クリーンルーム UI を使用しているカスタマーは、変更を確認するためにページの更新が必要になる場合があります。API を使用しているカスタマーはすぐに変更を確認できるはずですが、利用可能なリソースによっては、遅延が発生する可能性があります。クリーンルームのバージョン管理の詳細。

プロバイダー記述のインライン関数のアップロード

provider.load_python_into_cleanroomcode パラメーターにコードをインラインでアップロードできます。単純な関数をインラインでアップロードする例を次に示します。

CALL samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
$cleanroom_name,
'simple_add',                         -- Name used to call the UDF from a template.
['first INTEGER', 'second INTEGER'],  -- Arguments of the UDF, specified as '<variable_name> <SQL type>' pairs.
['numpy', 'pandas'],                  -- Packages imported by the UDF.
'INTEGER',                            -- SQL return type of UDF.
'add_two',                            -- Handler function in your code called when external name is called.
$$
import numpy as np   # Not used, but you can load supported packages.
import pandas as pd

def add_two(first, second):
    return first + second
$$
);
Copy

呼び出しテンプレートは cleanroom.simple_add を呼び出してこの関数を呼び出します。この プロバイダーの例 は、インラインコードをアップロードする方法を示しています。

ステージからのプロバイダー記述関数のアップロード

You can upload Python files to a clean room stage and reference the stage when you call provider.load_python_into_cleanroom. Loading code from a stage allows you to develop the code in your local system in an editor, avoid copy/paste errors when loading it inline, and also have better versioning control of your source code. Note that you can upload multiple files in a single procedure call, but only one handler function is exposed for each upload.

load_python_into_cleanroom を呼び出すと、コードがクリーンルームからステージにロードされます。後からステージ上でコードを変更しても、クリーンルームに伝播しません。

UDF をステージにアップロードするには、以下を実行します。

  1. .pyファイルを作成し、Snowsightステージにアップロードできる場所で利用できるようにします。

  2. To get the name of the stage for your clean room, call provider.get_stage_for_python_files. You must use the specified stage; you cannot use an arbitrary stage that you create.

  3. .pyファイルをクリーンルームのステージにアップロードします。CLI、Snowsight、言語固有のドライバーなど、これを行うには複数の方法 があります。

  4. ステージの場所、ハンドラー、外部名、引数、戻り型で``provider.load_python_into_cleanroom`` を呼び出します。クリーンルームのテンプレートが関数を呼び出せるようになりました。

次のコード例は、ステージからクリーンルームにコードをロードする方法を示しています。

-- Save the following code as reverser.py:
--import numpy as np
--def main(some_string):
--  '''Return the reverse of a string plus a random number 1-10'''
--  return some_string[::-1] + str(np.random.randint(1,10))

-- Get the stage for your clean room.
CALL samooha_by_snowflake_local_db.provider.get_stage_for_python_files($cleanroom_name);

-- Save the file to the stage. Here is how to do it by using the Snowflake CLI
PUT file://~/reverser.py <STAGE_NAME> overwrite=True auto_compress=False;

-- Load the code from the stage into the clean room.
CALL samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
    $cleanroom_name,
    'reverse', -- Name used to call the function
    ['some_string  STRING'], -- Arguments and SQL types
    ['numpy'],               -- Any required packages
    ['/reverser.py'],        -- Relative path to file on stage
    'STRING',                -- Return type
    'reverser.main'          -- <FILE_NAME>.<FUNCTION_NAME>
);

-- Uploading code, even from a stage, increases the patch number.
CALL samooha_by_snowflake_local_db.provider.set_default_release_directive(
  $cleanroom_name, 'V1_0', <NEW_PATCH_NUMBER>);

-- Upload a template that calls the function.
CALL samooha_by_snowflake_local_db.provider.add_custom_sql_template(
    $cleanroom_name,
    $udf_template_name,
    $$
    SELECT
      p.status,
      cleanroom.reverse(p.status)
    FROM SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS AS p
    LIMIT 100;
    $$
);

-- Switch to the consumer account and run the template to see the results.
Copy

プロバイダーの例 は、ステージからのコードのアップロードを示しています。

アップロードされたコードの構文エラーやスキャン失敗のトラブルシューティング

構文エラーにより失敗する関数をアップロードした場合や、セキュリティスキャンが失敗した場合は、公開されていないパッチが生成される可能性があります。したがって、アップロード前にコードを徹底的にテストし、構文エラーがないことを確認する必要があります。

次の SQL コマンドを実行すると、パッケージのリストとレビューステータスを確認でき、示された場所にクリーンルーム ID が表示されます。

SHOW VERSIONS IN APPLICATION PACKAGE samooha_cleanroom_cleanroom_id;

セキュリティスキャン

セキュリティスキャンは、プロバイダーがPythonをクリーンルームにアップロードするときなど、外部クリーンルームで新しいパッチバージョンを生成するアクションの後に実行されます。(このページで説明されているコンシューマーが送信するコードは、セキュリティスキャンをトリガーません。)内部クリーンルームはセキュリティスキャンを実行しませんが、内部クリーンルームを外部クリーンルームに変更すると、そのパッチのセキュリティスキャンがトリガーされます。パッチがスキャンされるまで、クリーンルームのパッチを外部で公開することはできません。

Snowflake Data Clean Roomsは:doc:Snowflake Native Appのセキュリティスキャンフレームワーク</developer-guide/native-apps/security-run-scan>`を使用します。:doc:`ネイティブアプリのセキュリティのベストプラクティス </developer-guide/native-apps/security-app-requirements> に従い、セキュリティスキャンエラーを回避します。

最後のセキュリティスキャンが完了する前に、追加のパッチ作成アクションを実行できます。ただし、provider.view_cleanroom_scan_status を待って、クリーンルームの最新バージョンを使用するための、デフォルトのリリースディレクティブを更新する前に成功を示す必要があります。

Uploading multiple Python functions in a single patch (bulk uploading)

If you want to upload multiple Python packages to your clean room, you can call prepare_python_for_cleanroom multiple times, then call load_prepared_python_into_cleanroom once to scan, upload, and generate a single patch for your clean room. The following example demonstrates uploading a UDF and a UDTF using bulk uploading:

---- Add custom inline UDF ----
CALL samooha_by_snowflake_local_db.provider.prepare_python_for_cleanroom(
    $cleanroom_name,
    'get_next_status',  -- Name of the UDF. Can be different from the handler.
    ['status VARCHAR'], -- Arguments of the UDF, specified as (variable name, SQL type).
    ['numpy'],          -- Packages needed by UDF.
    [],                 -- When providing the code inline, this is an empty array.
    'VARCHAR',          -- Return type of UDF.
    'get_next_status',  -- Handler.
    $$
import numpy as np
def get_next_status(status):
  """Return the next higher status, or a random status
  if no matching status found or at the top of the list."""

  statuses = ['MEMBER', 'SILVER', 'GOLD', 'PLATINUM', 'DIAMOND']
  try:
    return statuses[statuses.index(status.upper()) + 1]
  except:
    return 'NO MATCH'
    $$
);

---- Add custom inline UDTF. ----
CALL samooha_by_snowflake_local_db.provider.prepare_python_for_cleanroom(
    $cleanroom_name,
    'get_info',  -- Name of the UDTF. Can be different from the handler.
    ['hashed_email VARCHAR', 'days_active INT', 'status VARCHAR', 'income VARCHAR'],   -- Name/Type arguments of the UDTF.
    ['numpy'],         -- Packages used by UDTF.
    [],                -- When providing the code inline, this is an empty array.
    'TABLE(hashed_email VARCHAR, months_active INT, level VARCHAR)',  -- Return type of UDTF.
    'GetSomeVals',     -- Handler class name.
$$
class GetSomeVals:
  def __init__(self):
    self.month_days = 30

  def process(self, hashed_email, days_active, status, income):
    '''Change days into rough months, and also return whether we
    think the user's membership status is lower, higher, or equal to
    what is expected, based on their income.'''

    months_active = days_active // self.month_days
    brackets = ['0-50K', '50K-100K', '100K-250K', '250K+']
    statuses = ['MEMBER', 'SILVER', 'GOLD', 'PLATINUM']
    if(statuses.index(status) < brackets.index(income)):
      level = 'low'
    elif(statuses.index(status) > brackets.index(income)):
      level = 'high'
    else:
      level = 'equal'

    yield(hashed_email, months_active, level)
$$
);

-- Upload all stored procedures.
-- Note the new patch number returned by this procedure. Keep this number for later use.
CALL samooha_by_snowflake_local_db.provider.load_prepared_python_into_cleanroom($cleanroom_name);

-- Set the release directive specified by the last load_python_into_cleanroom call.
CALL samooha_by_snowflake_local_db.provider.set_default_release_directive($cleanroom_name, 'V1_0', <PATCH_NUMBER>);
Copy

プロバイダー記述コードの例

次の例は、プロバイダー記述の UDFs および UDTFs のクリーンルームへの追加を示しています。

Download the following examples and then upload them as worksheet files in your Snowflake account. You need separate accounts for the provider and consumer, each with the clean rooms API installed. Replace the information as noted in the sample files. See instructions to upload a SQL worksheet into your Snowflake account.

コンシューマー送信コード

コンシューマーがアップロードしたコードはバンドルされ、コンシューマーのテンプレートアップロードフロー を使用したカスタムテンプレートでアップロードされます。アップロードされたコードは、クリーンルームのどのテンプレートからでも呼び出すことができます。

コンシューマーとしてコードをアップロードするには、カスタムテンプレート構文 を理解する必要があります。

コンシューマーがアップロードするコードは、アップロードの権限をリクエストする際に、プロバイダーに表示される可能性があることに注意してください。コンシューマーコードは、プロバイダーまたはコンシューマーがテンプレートを調べるときにも表示されます。

カスタムコンシューマーコードをアップロードする手順の概要は次のとおりです。

  1. プロバイダーは標準的な方法でクリーンルームを作成し、コンシューマーを招待します。

  2. コンシューマーは標準的な方法でクリーンルームをインストールし、構成します。

  3. コンシューマーは、cleanroom 名前空間の中で UDF または UDTF を呼び出すテンプレートを準備します。たとえば、コンシューマー定義の calculate_tax 関数を呼び出す場合、簡潔なテンプレートは次のスニペットのようになります。

    SELECT {{ cleanroom.calculate_tax(p.cost) }} AS Tax FROM my_db.my_sch.sales AS p;
    
    Copy
  4. コンシューマーはPythonコードを準備します。後で余計なエスケープが必要にならないよう、コード内では一重引用符(' ')ではなく二重引用符(" ")を使用することをお勧めします。コードは これらのサポートされているPythonライブラリ を参照できます。

  5. コンシューマーはPythonコードを consumer.generate_python_request_template に渡します。プロシージャは、カスタム JinjaSQL テンプレートのプレースホルダーと一緒にPythonコードをストアドプロシージャとして返します。$$ を複数行の区切り文字として使用するテンプレートには、複数行の文字列がいくつかあります。

  6. コンシューマーは、generate_python_request_template からの出力のテンプレートホルダーを JinjaSQL テンプレートに置き換えます。

  7. 結合されたテンプレートでは、一重引用符 \' をエスケープします。これは、複数行のプロシージャ文字列全体の最も外側の区切り文字として一重引用符が使用されるためです。以下は、コンシューマーPythonコードとカスタムテンプレートを含み、文字エスケープされたストアドプロシージャの例です。

      BEGIN
    
      CREATE OR REPLACE FUNCTION CLEANROOM.custom_compare(min_status STRING, max_status STRING, this_status STRING)
      RETURNS boolean
      LANGUAGE PYTHON
      RUNTIME_VERSION = 3.10
      PACKAGES = (\'numpy\')
    
      HANDLER = \'custom_compare\'
      AS $$
      import numpy as np
    
      def custom_compare(min_status:str, max_status:str, this_status:str):
        statuses = [\'MEMBER\', \'SILVER\', \'GOLD\', \'PLATINUM\']
        return ((statuses.index(this_status) >= statuses.index(min_status)) &
                (statuses.index(this_status) <= statuses.index(max_status)))
      $$;
    
      -- Custom template
      LET SQL_TEXT varchar := $$
      SELECT
        c.status,
        c.hashed_email
      FROM IDENTIFIER( {{ my_table[0] }} ) as c
      WHERE cleanroom.custom_compare({{ min_status }}, {{ max_status }}, c.status);
      $$;
    
      LET RES resultset := (EXECUTE IMMEDIATE :SQL_TEXT);
      RETURN TABLE(RES);
    
      END;
    
    Copy
  8. コンシューマーは、テンプレートを組み合わせた consumer.create_template_request を呼び出します。template_definition 引数のストアドプロシージャに提供するコードに、二重ドル記号区切り文字($$...$$)の代わりに一重引用符(' ')を使用します。例:

    CALL samooha_by_snowflake_local_db.consumer.create_template_request(
      $cleanroom_name,
      $template_name,
      '
    BEGIN
    
    -- First, define the Python UDF.
    CREATE OR REPLACE FUNCTION CLEANROOM.custom_compare(min_status STRING, max_status STRING, this_status STRING)
    RETURNS boolean
    LANGUAGE PYTHON
    RUNTIME_VERSION = 3.10
    PACKAGES = (\'numpy\')
    
    HANDLER = \'custom_compare\'
    AS $$
    import numpy as np
    
    def custom_compare(min_status:str, max_status:str, this_status:str):
      statuses = [\'MEMBER\', \'SILVER\', \'GOLD\', \'PLATINUM\']
      return ((statuses.index(this_status) >= statuses.index(min_status)) &
              (statuses.index(this_status) <= statuses.index(max_status)))
        $$;
    
    -- Then define and execute the SQL query.
    LET SQL_TEXT varchar := $$
    SELECT
      c.status,
      c.hashed_email
    FROM IDENTIFIER( {{ my_table[0] }} ) as c
    WHERE cleanroom.custom_compare({{ min_status }}, {{ max_status }}, c.status);
    $$;
    
    -- Execute the query and then return the result.
    LET RES resultset := (EXECUTE IMMEDIATE :SQL_TEXT);
    RETURN TABLE(RES);
    
    END;
    ');
    
    Copy
  9. コンシューマーとプロバイダーは標準の コンシューマー定義のテンプレートフロー を使用して続行します。

    1. プロバイダーはテンプレートリクエスト(provider.list_pending_template_requests)を表示してから、approve_template_request を呼び出してリクエストを承認します。リクエストで、プロバイダーはテンプレートとバンドルコードを確認できます。

    2. コンシューマーは、リクエストのステータスを確認します(consumer.list_template_requests)。ステータスが APPROVED になったら、テンプレートを実行します(consumer.run_analysis)。

    Consumer code uploads don't trigger a security scan or affect the clean room patch number.

コンシューマー記述コードの例

次の例は、プロバイダー記述の UDFs のクリーンルームへの追加を示しています。

Download the following examples and then upload them as worksheet files in your Snowflake account. You need separate accounts for the provider and consumer, each with the clean rooms API installed. Replace the information as noted in the sample files. See instructions to upload a SQL worksheet into your Snowflake account.