클린룸에서 사용자 지정 함수 업로드 및 실행

개요

사용자 지정 Python UDFs 및 UDTFs를 클린룸으로 업로드하고 템플릿에서 실행하여 복잡한 데이터 작업을 수행할 수 있습니다. 이러한 작업으로는 단일 단계 또는 다단계 흐름 중에 쿼리 내에서 머신 러닝 또는 사용자 지정 데이터 조작을 하는 것이 있습니다. Python은 사용자 지정 UDFs에 대해 지원되는 유일한 코딩 언어입니다.

업로드한 코드는 `승인된 Python 패키지 번들<https://repo.anaconda.com/pkgs/snowflake/>`_ 및 :ref:`Snowpark API<label-dcr_snowpark_udf>`에서 패키지를 가져오고 사용할 수 있습니다.

공급자와 컨슈머 모두 사용자 지정 Python 코드를 클린룸에 업로드할 수 있지만, 공급자와 컨슈머의 프로세스는 다릅니다.

이 항목에서는 공급자 또는 컨슈머가 사용자 지정 Python UDFs 및 UDTFs를 업로드하고 실행하는 방법을 보여줍니다.

클린룸에서 Python UDFs를 직접 개발하는 방법에 대한 배경 정보는 다음 항목을 참조하세요.

  • UDFs가 Snowflake에서 작동하는 방식:Snowflake에서 Python 함수를 작성하는 방법에 대한 일반적인 배경 정보가 나와 있습니다.

  • Snowflake에서 UDTFs를 작성하는 방법: 함수에서 테이블을 반환하려는 경우에 참조합니다.

  • :doc:`사용자 지정 템플릿을 만들고 클린룸에 업로드하는 방법: </user-guide/cleanrooms/demo-flows/custom-templates>`UDFs/UDTFs는 사용자 지정 템플릿에서 호출됩니다.

  • :doc:`클린룸에서 Snowpark 사용 </user-guide/cleanrooms/demo-flows/snowpark>`(Snowpark에서 UDFs를 호출하려는 경우)

업로드된 코드 실행하기

업로드된 코드의 각 번들은 서로를 호출하는 여러 함수를 정의할 수 있지만, 번들은 하나의 처리기 함수만 노출합니다. 이 처리기 함수는 클린룸을 사용하는 모든 사용자가 만들거나 실행하는 템플릿으로 호출할 수 있습니다. 코드가 내부 테이블을 생성하는 경우 :doc:`/user-guide/cleanrooms/multistep-flows`에 설명된 대로 이러한 테이블에 액세스할 수 있습니다

예를 들어 두 개의 숫자 매개 변수를 사용하는 simple_add``인 함수를 업로드한 경우 여기에 표시된 대로 템플릿에서 호출할 있습니다. 함수는 항상 ``cleanroom 범위를 사용하여 참조됩니다. 예를 들어, 템플릿은 다음과 같이 ``simple_add``를 호출합니다.

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

공급자가 위의 코드를 실행하려면 결과 테이블이 백그라운드에서 생성되므로 집계 또는 사용자 지정 함수를 사용하는 모든 SELECT 열의 별칭을 지정해야 합니다.

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

단일 패키지에 여러 함수를 업로드할 수 있으며, 단일 패키지 내의 함수는 서로를 호출할 수 있지만, 함수는 다른 패키지 내의 함수를 호출할 수 없습니다. (하지만 처리기 함수는 호출할 수 있습니다.) 예:

업로드된 두 개의 Python 패키지가 있는 클린룸

패키지 1

패키지 2

  • 처리기 함수 A

  • 헬퍼 함수 A1

  • 헬퍼 함수 A2

  • 처리기 함수 B

  • 헬퍼 함수 B1

  • 헬퍼 함수 B2

참고:

  • 양쪽(공급자 및 컨슈머)에서 업로드한 코드는 어느 쪽에서든 실행할 수 있습니다.

  • 템플릿은 함수 A 또는 함수 B를 호출할 수 있지만, 함수 A1, A2, B1, 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를 호출할 수 없습니다.

사용자 지정 함수 업데이트 또는 삭제하기

업로드한 기존 함수 또는 템플릿을 업로드하거나 덮어쓸 수 있지만, 기존 함수 또는 템플릿을 삭제할 수는 없습니다. 함수를 “제거”하는 유일한 방법은 항상 성공 값을 반환하는, 정확히 동일한 이름과 서명을 가진 더미 함수를 생성하는 것입니다.

이전에 업로드한 서명과 정확히 동일한 서명을 가진 함수를 업로드하면 기존 함수를 덮어씁니다. 서명은 외부 처리기의 함수 이름과 모든 매개 변수의 데이터 타입을 동일한 순서대로 나열한 것으로, 대/소문자를 구분하지 않습니다. 매개 변수 이름은 중요하지 않습니다. 다른 계정에서 업로드한 함수는 덮어쓸 수 없습니다.

함수를 업데이트할 때 서명이 일치해야 하므로 기존 함수의 서명을 변경할 수 없습니다. 함수 ``foo(name VARIANT age INTEGER)``를 업로드한 다음, 함수 ``foo(name VARIANT age FLOAT)``를 업로드하면 인자 유형이 다르기 때문에 첫 번째 함수 외에 두 번째 함수가 클린룸에 추가됩니다.

공급자 제출 코드

공급자가 제출한 함수는 인라인 코드로 업로드하거나 Snowflake 스테이지에서 업로드할 수 있습니다. 두 기술 모두 여기에서 살펴봅니다.

업로드한 코드는 기본적으로 `승인된 Python 패키지 세트<https://repo.anaconda.com/pkgs/snowflake/>`_에서 패키지를 가져와 사용할 수 있습니다. 기본 패키지가 아닌 패키지가 필요한 경우 :ref:`클린룸의 Snowpark Container Services<label-dcr_snowpark_spcs>`를 사용하여 코드를 호스트해야 합니다.

업로드된 코드는 물론 자신의 코드도 볼 수 없으므로, 클린룸에 업로드한 코드의 복사본을 그대로 포함해야 합니다.

공급자가 작성한 코드를 업데이트한 후 기본 릴리스 지시문을 업데이트한 다음, ``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>`한 다음, 해당 프로시저에 스테이지 위치를 제공할 수 있습니다.

    코드에 여러 함수가 포함될 수 있지만, 각 업로드에 대해 하나의 처리기만 노출됩니다. 여러 함수를 템플릿에 노출하려면 ``provider.load_python_into_cleanroom``을 호출하여 각 처리기를 업로드합니다.

  3. 코드 업로드에 성공할 때마다 클린룸의 새 패치 버전이 생성됩니다. 그러면 새 패치 번호를 사용하여 ``provider.set_default_release_directive``를 호출함으로써 기본 버전을 증분해야 합니다. 클린룸이 외부에 노출되는 경우 코드를 설치하기 전에 보안 검사가 실행되며, 기본 버전을 증분하기 전에 ``provider.view_cleanroom_scan_status``를 호출하여 보안 검사를 통과했는지 확인해야 합니다.

    • 단일 패치에서 여러 함수를 업로드하려는 경우 :ref:`코드 대량 업로드<label-dcr_bulk_upload_python>`를 수행할 수 있습니다. 그러나 업로드에 보안 검사 문제가 있는 경우 문제를 유발한 파일이 오류 응답에 보고되지 않기 때문 대량 업로드를 디버깅하기가 더 어려워질 수 있습니다.

  4. 코드를 호출하는 사용자 지정 템플릿 만들고 업로드합니다. 템플릿은 cleanroom 범위(즉, cleanroom.my_function(...))를 사용하여 처리기 함수를 호출해야 합니다.

  5. 컨슈머는 다른 템플릿과 동일한 방식으로 템플릿을 실행합니다.

    컨슈머가 사용자 지정 코드로 클린룸을 설치할 때 마운트 오류가 발생하는 경우 코드에 구문 오류가 있다는 의미일 수 있습니다.

:ref:`공급자가 작성한 코드 예제 섹션<label-dcr_provider_written_code_examples>`에서 이 흐름을 보여주는 코드 예제를 찾을 수 있습니다.

버전 관리에 대한 중요 참고 사항

공급자는 함수를 업로드할 때마다 패치 번호를 증분합니다(패치 번호는 99로 제한됨). 따라서 개발하는 동안 버전 업데이트를 줄이기 위해 클린룸에 코드를 추가하기 전에 최선을 다해서 코드를 철저히 테스트하고 디버깅해야 합니다.

패치 번호를 업데이트하면 클린룸 UI를 사용하는 고객이 변경 사항을 확인하기 위해 페이지를 새로 고쳐야 할 수도 있습니다. API를 사용하는 고객에게는 변경 사항이 즉시 표시되지만, 사용 가능한 리소스에 따라 지연이 발생할 수 있습니다. 클린룸 버전 관리에 대해 자세히 알아보세요.

공급자가 작성한 인라인 함수 업로드

provider.load_python_into_cleanroom``의 ``code 매개 변수에서 코드를 인라인으로 업로드할 수 있습니다. 다음은 간단한 함수를 인라인으로 업로드하는 예입니다.

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``를 호출하여 이 함수를 호출합니다. :ref:`공급자 예제<label-dcr_provider_written_code_examples>`는 인라인 코드를 업로드하는 방법을 보여줍니다.

스테이지에서 공급자 작성 함수 업로드

Python 파일을 클린룸 스테이지에 업로드하고 ``provider.load_python_into_cleanroom``을 호출할 때 스테이지를 참조할 수 있습니다. 스테이지에서 코드를 로드하면 로컬 시스템의 편집기에서 코드를 개발하고, 인라인으로 로드할 때 복사/붙여넣기 오류를 방지하고, 더 효과적으로 버전 관리를 할 수 있습니다. 단일 프로시저 호출로 여러 파일을 업로드할 수 있지만, 업로드마다 하나의 처리기 함수만 노출됩니다.

``load_python_into_cleanroom``을 호출하면 코드가 스테이지에서 클린룸으로 로드됩니다. 나중에 스테이지의 코드 변경 사항이 클린룸으로 전파되지 않습니다.

스테이지로 UDF를 업로드하려면:

  1. .py 파일을 만들고 Snowsight 스테이지에 업로드할 수 있는 위치에 저장합니다.

  2. 클린룸의 스테이지 이름을 가져오려면 ``provider.get_stage_for_python_files($cleanroom_name)``를 호출합니다. 이 스테이지는 클린룸에서 액세스할 수 있습니다. 하지만 본인이 만든 임의의 스테이지를 사용할 수 없습니다.

  3. .py 파일을 클린룸의 스테이지에 업로드합니다. CLI, Snowsight 또는 언어별 드라이버를 사용하는 것을 포함하여 :doc:`이 작업을 수행하는 여러 가지 방법</user-guide/data-load-local-file-system-stage>`이 있습니다.

  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

:ref:`공급자 예제<label-dcr_provider_written_code_examples>`는 스테이지에서 코드를 업로드하는 방법을 보여줍니다.

업로드된 코드의 구문 오류 또는 검사 실패 문제 해결하기

구문 오류로 인해 실패하는 함수를 업로드하거나 보안 검사에 실패하면 게시할 수 없는 패치가 생성될 수 있습니다. 따라서 업로드하기 전에 코드를 철저히 테스트하여 구문 오류가 없는지 확인해야 합니다.

다음 SQL 명령을 실행하여 패키지 목록과 해당 검토 상태를 볼 수 있으며, 표시된 위치에 클린룸 ID가 제공됩니다.

SHOW VERSIONS IN APPLICATION PACKAGE samooha_cleanroom_cleanroom_id;

보안 검사

보안 검사는 공급자가 Python을 클린룸에 업로드하는 경우와 같이 외부 클린룸에서 새 패치 버전을 생성하는 모든 작업 후에 실행됩니다. (이 페이지에 설명된 컨슈머 제출 코드는 보안 검사를 트리거하지 않습니다.) 내부 클린룸은 보안 검사를 실행하지 않지만, 내부 클린룸을 외부 클린룸으로 변경하면 해당 패치에 대한 보안 검사가 트리거됩니다. 클린룸 패치는 패치를 검사할 때까지 외부에 게시할 수 없습니다.

Snowflake 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``가 성공으로 표시될 때까지 기다려야 합니다.

단일 패치에서 여러 Python 함수 업로드하기

템플릿 호출 가능 Python 패키지를 클린룸에 업로드하는 경우 ``prepare_python_for_cleanroom``을 여러 번 호출한 다음 ``load_prepared_python_into_cleanroom``을 한 번 호출하여 클린룸에 대한 단일 패치를 검사, 업로드, 생성할 수 있습니다. 다음 예제에서는 대량 업로드를 사용하여 UDF 및 UDTF를 업로드하는 방법을 보여줍니다.

---- 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를 클린룸에 추가하는 방법을 보여줍니다.

다음 예제를 다운로드한 후 Snowflake 계정에 워크시트 파일로 업로드합니다. 공급자와 컨슈머에 대해 각각 클린룸 API가 설치된 별도의 계정이 필요합니다. 샘플 파일에 나와 있는 것처럼 이 정보를 바꿉니다.

컨슈머가 제출한 코드

컨슈머 업로드 코드는 번들로 제공되며 :ref:`컨슈머 템플릿 업로드 흐름<label-dcr_consumer_written_templates>`을 사용하여 사용자 지정 템플릿과 함께 업로드됩니다. 업로드된 코드는 클린룸의 모든 템플릿에서 호출할 수 있습니다.

컨슈머로서 코드를 업로드하려면 :doc:`사용자 지정 템플릿 구문</user-guide/cleanrooms/demo-flows/custom-templates>`을 이해해야 합니다.

컨슈머가 업로드한 모든 코드는 공급자가 업로드 권한을 요청할 때 볼 수 있습니다. 공급자 또는 컨슈머가 템플릿을 검사할 때마다 컨슈머 코드도 표시됩니다.

다음은 사용자 지정 컨슈머 코드를 업로드하는 단계의 개요입니다.

  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 라이브러리<https://repo.anaconda.com/pkgs/snowflake/>`_를 참조할 수 있습니다.

  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. 컨슈머와 공급자가 표준 :ref:`컨슈머 정의 템플릿 흐름<label-dcr_consumer_written_templates>`을 계속 유지합니다.

    1. 공급자가 템플릿 요청을 확인한 다음(provider.list_pending_template_requests), ``approve_template_request``를 호출하여 승인합니다. 공급자는 요청에서 템플릿과 번들 코드를 볼 수 있습니다.

    2. 컨슈머가 요청 상태를 확인하고(consumer.list_template_requests) 상태가 APPROVED인 경우 템플릿을 실행합니다(consumer.run_analysis).

컨슈머가 작성한 코드 예제

다음 예제에서는 공급자가 작성한 UDFs를 클린룸에 추가하는 방법을 보여줍니다.

다음 예제를 다운로드한 후 Snowflake 계정에 워크시트 파일로 업로드합니다. 공급자와 컨슈머에 대해 각각 클린룸 API가 설치된 별도의 계정이 필요합니다. 샘플 파일에 나와 있는 것처럼 이 정보를 바꿉니다.