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

概要

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

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

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

アップロードされたコードは削除できませんが、更新できます

このページでは、プロバイダーまたはコンシューマーとしてカスタム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 を呼び出したい場合)。

カスタム関数の更新

既存の関数をアップロードしたり、アップロードした既存の関数を上書きしたりすることはできますが、既存の関数を削除することはできません。

以前にアップロードしたものとまったく同じ署名の関数をアップロードすると、既存の関数が上書きされます。署名は、外部ハンドラーの大文字と小文字を区別しない関数名であり、すべてのパラメーターのデータ型(同じ順序)です。パラメーター名は関係ありません。他のアカウントがアップロードした関数を上書きすることはできません。

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

プロバイダー送信コード

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

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

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

Tip

プロバイダー記述コードを更新した後、デフォルトのリリースディレクティブを更新してから provider.create_or_update_cleanroom_listing を呼び出してコンシューマーに変更を伝播させる必要があります。provider.create_or_update_cleanroom_listing を呼び出さない場合、現在クリーンルームを使用しているコンシューマーのデフォルトバージョンは更新されません。

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

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

  2. プロバイダーは provider.load_python_into_cleanroom を呼び出してコードをアップロードします。そのプロシージャ内で直接:ref:コードをインラインでアップロードする<label-dcr_upload_code_inline>`か、:ref:`ステージにコードファイルをアップロード <label-dcr_provider_code_from_stage> して、そのプロシージャにステージの場所を提供できます。

    コードには複数の関数を含めることができますが、アップロードごとに公開されるハンドラーは1つだけです。複数の関数をテンプレートに公開するには、provider.load_python_into_cleanroom を呼び出して各ハンドラーをアップロードします。

  3. コードのアップロードが成功するたびに、クリーンルームの新しいパッチバージョンが生成されます。その後、新しいパッチ番号を使用して provider.set_default_release_directive を呼び出し、デフォルトバージョンを増やす必要があります。クリーンルームが外部に公開されている場合、コードをインストールする前にセキュリティチェックが実行されます。provider.view_cleanroom_scan_status を呼び出して、デフォルトバージョンをインクリメントする前に、セキュリティチェックに合格したことを確認します。

  4. コードを呼び出すカスタムテンプレートを作成し、アップロードします。テンプレートは、cleanroom スコープを使用するハンドラー関数、つまり cleanroom.my_function を呼び出します。たとえば、アップロードしたカスタム simple_add 関数を呼び出すテンプレートは次のようになります。

    SELECT cleanroom.simple_add(1, 2), cleanroom.simple_add({{ price | sqlsafe | int }}, {{ tax | sqlsafe | int }})
    
    Copy
  5. コンシューマーは、他のテンプレートと同じ方法でテンプレートを実行します。

    Tip

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

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

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

プロバイダーが関数をアップロードするたびに、パッチ番号は増えます(パッチ番号99が上限)。そのため、クリーンルームにコードを追加する前に、コードを徹底的にテストし、デバッグを行い、開発中のバージョン更新を減らすようにしてください。

パッチ番号を更新した場合、クリーンルーム 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 を呼び出してこの関数を呼び出します。この プロバイダーの例 は、インラインコードをアップロードする方法を示しています。

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

Pythonファイルをクリーンルームステージにアップロードし、provider.load_python_into_cleanroom を呼び出すときにステージを参照することができます。ステージからコードをロードすると、ローカルシステムのエディターでコードを開発でき、インラインでロードするときのコピー/ペーストのエラーが回避され、バージョン管理も改善されます。1回のプロシージャ呼び出しで複数のファイルをアップロードできますが、アップロードごとに公開されるハンドラー関数は1つだけであることに注意してください。

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

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

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

  2. クリーンルームのステージ名を取得するには、provider.get_stage_for_python_files($cleanroom_name) を呼び出します。このステージはクリーンルームからアクセスできます。自分が作成した任意のステージは使用できません。

  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

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

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

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

以下の例をダウンロードし、Snowflakeアカウントにワークシートファイルとしてアップロードします。プロバイダーとコンシューマーには、それぞれクリーンルームAPIがインストールされた別々のアカウントが必要です。サンプルファイルで示されているように情報を置き換えます。

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

コンシューマーは UDF または UDTF コードを送信し、カスタムテンプレートから呼び出せます。コンシューマーがアップロードしたコードは、カスタムテンプレートとともに単一のプロシージャにバンドルされ、単一のプロシージャ呼び出しでアップロードされます。コンシューマーコードはそのテンプレートに直接結び付けられ、他のテンプレートで呼び出すことはできません。

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

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

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

  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. 結合されたテンプレートでは、一重引用符 \' をエスケープします。これは、複数行のプロシージャ文字列全体の最も外側の区切り文字として一重引用符が使用されるためです。例:

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

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

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

以下の例をダウンロードし、Snowflakeアカウントにワークシートファイルとしてアップロードします。プロバイダーとコンシューマーには、それぞれクリーンルームAPIがインストールされた別々のアカウントが必要です。サンプルファイルで示されているように情報を置き換えます。