CREATE FUNCTION

CREATE FUNCTION

새 UDF(user-defined function)를 만드는 명령이에요. 구성 방법에 따라 스칼라 결과 또는 테이블 결과를 반환할 수 있어요. UDF를 만들 때 지원되는 언어 중 하나로 작성된 핸들러(handler)를 지정해요.

출처: 문서

본문

핸들러 언어에 따라 핸들러 소스 코드를 CREATE FUNCTION 문에 인라인으로 포함하거나, 사전 컴파일되거나 스테이지에 있는 핸들러 위치를 참조할 수 있어요. 언어별 핸들러 위치:

언어 핸들러 위치
Java 인라인 또는 스테이지
JavaScript 인라인
Python 인라인 또는 스테이지
Scala 인라인 또는 스테이지
SQL 인라인

이 명령은 다음 변형(variant)을 지원해요:

  • CREATE OR ALTER FUNCTION: 함수가 없으면 만들고, 있으면 수정합니다.

구문 (Syntax)

CREATE FUNCTION 구문은 UDF 핸들러로 사용하는 언어에 따라 달라져요.

Java 핸들러 (인라인):

CREATE [ OR REPLACE ] [ { TEMP | TEMPORARY } ] [ SECURE ] FUNCTION [ IF NOT EXISTS ] <name> (
    [ <arg_name> <arg_data_type> [ DEFAULT <default_value> ] ] [ , ... ] )
  [ COPY GRANTS ]
  RETURNS { <result_data_type> | TABLE ( <col_name> <col_data_type> [ , ... ] ) }
  [ [ NOT ] NULL ]
  LANGUAGE JAVA
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ { VOLATILE | IMMUTABLE } ]
  [ RUNTIME_VERSION = <java_jdk_version> ]
  [ COMMENT = '<string_literal>' ]
  [ IMPORTS = ( '<stage_path_and_directory_or_file_name_to_read>' [ , ... ] ) ]
  [ PACKAGES = ( '<package_name_and_version>' [ , ... ] ) ]
  HANDLER = '<path_to_method>'
  [ EXTERNAL_ACCESS_INTEGRATIONS = ( <name_of_integration> [ , ... ] ) ]
  [ SECRETS = ('<secret_variable_name>' = <secret_name> [ , ... ] ) ]
  [ TARGET_PATH = '<stage_path_and_file_name_to_write>' ]
  AS '<function_definition>'

Java 핸들러 (스테이지 참조):

CREATE [ OR REPLACE ] [ { TEMP | TEMPORARY } ] [ SECURE ] FUNCTION [ IF NOT EXISTS ] <name> (
    [ <arg_name> <arg_data_type> [ DEFAULT <default_value> ] ] [ , ... ] )
  [ COPY GRANTS ]
  RETURNS { <result_data_type> | TABLE ( <col_name> <col_data_type> [ , ... ] ) }
  [ [ NOT ] NULL ]
  LANGUAGE JAVA
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ { VOLATILE | IMMUTABLE } ]
  [ RUNTIME_VERSION = <java_jdk_version> ]
  [ COMMENT = '<string_literal>' ]
  IMPORTS = ( '<stage_path_and_directory_or_file_name_to_read>' [ , ... ] )
  HANDLER = '<path_to_method>'
  [ EXTERNAL_ACCESS_INTEGRATIONS = ( <name_of_integration> [ , ... ] ) ]
  [ SECRETS = ('<secret_variable_name>' = <secret_name> [ , ... ] ) ]

JavaScript 핸들러:

CREATE [ OR REPLACE ] [ { TEMP | TEMPORARY } ] [ SECURE ] FUNCTION <name> (
    [ <arg_name> <arg_data_type> [ DEFAULT <default_value> ] ] [ , ... ] )
  [ COPY GRANTS ]
  RETURNS { <result_data_type> | TABLE ( <col_name> <col_data_type> [ , ... ] ) }
  [ [ NOT ] NULL ]
  LANGUAGE JAVASCRIPT
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ { VOLATILE | IMMUTABLE } ]
  [ COMMENT = '<string_literal>' ]
  AS '<function_definition>'

Python 핸들러 (인라인):

CREATE [ OR REPLACE ] [ { TEMP | TEMPORARY } ] [ SECURE ] [ AGGREGATE ] FUNCTION [ IF NOT EXISTS ] <name> (
    [ <arg_name> <arg_data_type> [ DEFAULT <default_value> ] ] [ , ... ] )
  [ COPY GRANTS ]
  RETURNS { <result_data_type> | TABLE ( <col_name> <col_data_type> [ , ... ] ) }
  [ [ NOT ] NULL ]
  LANGUAGE PYTHON
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ { VOLATILE | IMMUTABLE } ]
  RUNTIME_VERSION = <python_version>
  [ COMMENT = '<string_literal>' ]
  [ IMPORTS = ( '<stage_path_and_directory_or_file_name_to_read>' [ , ... ] ) ]
  [ PACKAGES = ( '<package_name>[==<version>]' [ , ... ] ) ]
  [ ARTIFACT_REPOSITORY = '<repository_name>' ]
  HANDLER = '<function_name>'
  [ EXTERNAL_ACCESS_INTEGRATIONS = ( <name_of_integration> [ , ... ] ) ]
  [ SECRETS = ('<secret_variable_name>' = <secret_name> [ , ... ] ) ]
  AS '<function_definition>'

Python 핸들러 (스테이지 참조): 스테이지에서 핸들러를 참조할 때는 HANDLER = '<module_file_name>.<function_name>' 형식을 사용해요.

Scala 핸들러 (인라인):

CREATE [ OR REPLACE ] [ { TEMP | TEMPORARY } ] [ SECURE ] FUNCTION [ IF NOT EXISTS ] <name> (
    [ <arg_name> <arg_data_type> [ DEFAULT <default_value> ] ] [ , ... ] )
  [ COPY GRANTS ]
  RETURNS <result_data_type>
  [ [ NOT ] NULL ]
  LANGUAGE SCALA
  [ { CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT } } ]
  [ { VOLATILE | IMMUTABLE } ]
  [ RUNTIME_VERSION = <scala_version> ]
  [ COMMENT = '<string_literal>' ]
  [ IMPORTS = ( '<stage_path_and_directory_or_file_name_to_read>' [ , ... ] ) ]
  [ PACKAGES = ( '<package_name_and_version>' [ , ... ] ) ]
  HANDLER = '<path_to_method>'
  [ TARGET_PATH = '<stage_path_and_file_name_to_write>' ]
  AS '<function_definition>'

SQL 핸들러:

CREATE [ OR REPLACE ] [ { TEMP | TEMPORARY } ] [ SECURE ] FUNCTION <name> (
    [ <arg_name> <arg_data_type> [ DEFAULT <default_value> ] ] [ , ... ] )
  [ COPY GRANTS ]
  RETURNS { <result_data_type> | TABLE ( <col_name> <col_data_type> [ , ... ] ) }
  [ [ NOT ] NULL ]
  [ { VOLATILE | IMMUTABLE } ]
  [ MEMOIZABLE ]
  [ COMMENT = '<string_literal>' ]
  AS '<function_definition>'

CREATE OR ALTER FUNCTION 구문

CREATE [ OR ALTER ] FUNCTION ...

지원되는 수정:

  • 함수 속성/매개변수 변경 (예: SECURE, MAX_BATCH_ROWS, LOG_LEVEL, COMMENT).
  • 함수 정의 변경 (예: RUNTIME_VERSION, ARTIFACT_REPOSITORY(Python), PACKAGES, IMPORTS, 반환 타입, 함수 본문).

이 변형 구문에서는 COPY GRANTS 매개변수가 지원되지 않아요.

필수 매개변수

모든 언어

  • name ( [ arg_name arg_data_type [ DEFAULT default_value ] ] [ , ... ] ): UDF의 식별자(name), 입력 인자, 선택적 인자의 기본값을 지정해요. UDF는 이름과 인자 타입의 조합으로 식별/해석되므로 식별자가 스키마 내에서 고유할 필요는 없어요. 선택적 인자는 필수 인자 뒤에 배치해야 해요.
  • RETURNS ...: UDF가 반환하는 결과를 지정하며, UDF 유형을 결정해요.
    • result_data_type: 지정된 데이터 타입의 단일 값을 반환하는 스칼라 UDF를 만들어요.
    • TABLE ( col_name col_data_type , ... ): 지정된 테이블 컬럼(들)과 컬럼 타입으로 표 형식(tabular) 결과를 반환하는 테이블 UDF를 만들어요. (Scala UDF에서는 TABLE 반환 타입이 지원되지 않아요.)
  • AS function_definition: UDF가 호출될 때 실행되는 핸들러 코드를 정의해요. Java, JavaScript, Python, Scala, 또는 SQL 표현식/Snowflake Scripting 블록일 수 있어요. UDF 핸들러 코드가 IMPORTS 절로 스테이지에서 참조되면 AS 절은 필요 없어요.

Java

  • LANGUAGE JAVA: 코드가 Java임을 지정해요.
  • RUNTIME_VERSION = java_jdk_version: 사용할 Java JDK 런타임 버전을 지정해요. 지원: 11.x, 17.x, 21.x. 설정하지 않으면 Java JDK 11이 사용돼요.
  • IMPORTS = ( '...' [ , ... ] ): 가져올 디렉터리/파일의 위치(스테이지), 경로, 이름을 지정해요. 파일은 JAR 또는 다른 유형일 수 있어요. 각 파일 이름은 고유해야 해요. 스테이지에 있는 핸들러 UDF에는 필수예요.
  • HANDLER = handler_name: 핸들러 메서드/클래스의 이름을 지정해요. 스칼라 UDF는 메서드 이름(MyClass.myMethod), 표 형식 UDF는 핸들러 클래스 이름이에요.

JavaScript

  • LANGUAGE JAVASCRIPT: 코드가 JavaScript임을 지정해요.

Python

  • LANGUAGE PYTHON: 코드가 Python임을 지정해요.
  • RUNTIME_VERSION = python_version: 사용할 Python 버전을 지정해요. 지원: 3.9(사용 중단), 3.10, 3.11, 3.12, 3.13, 3.14.
  • IMPORTS = ( '...' [ , ... ] ): 가져올 디렉터리/파일의 위치, 경로, 이름을 지정해요. 파일은 .py 또는 다른 유형일 수 있어요. 스테이지의 핸들러 코드에는 필수예요.
  • HANDLER = handler_name: 핸들러 함수/클래스의 이름을 지정해요. 스칼라 UDF는 함수 이름, 표 형식 UDF는 핸들러 클래스 이름이에요.

Scala

  • LANGUAGE SCALA: 코드가 Scala임을 지정해요.
  • RUNTIME_VERSION = scala_version: 사용할 Scala 런타임 버전을 지정해요. 지원: 2.13, 2.12. 설정하지 않으면 Scala 2.12가 사용돼요.
  • IMPORTS = ( '...' [ , ... ] ): 가져올 디렉터리/파일(예: JAR)의 위치, 경로, 이름을 지정해요. 스테이지의 핸들러 UDF에는 필수예요.
  • HANDLER = handler_name: 핸들러 메서드/클래스의 이름을 지정해요.

선택 매개변수

모든 언어

  • SECURE: 함수가 보안(secure) 함수임을 지정해요.
  • { TEMP | TEMPORARY }: 함수가 만든 세션 기간 동안만 유지되도록 지정해요. 임시 함수는 세션 종료 시 드롭돼요. 기본값: 영구.
  • [ [ NOT ] NULL ]: 함수가 NULL을 반환할 수 있는지 또는 NON-NULL만 반환해야 하는지 지정해요. 기본값: NULL 허용. 현재 SQL UDF에서는 NOT NULL이 강제되지 않아요.
  • CALLED ON NULL INPUT 또는 { RETURNS NULL ON NULL INPUT | STRICT }: null 입력 시 UDF 동작을 지정해요. CALLED ON NULL INPUT은 항상 null 입력으로 UDF를 호출하고, RETURNS NULL ON NULL INPUT(STRICT)은 null 입력이 있으면 UDF를 호출하지 않고 null을 반환해요. SQL UDF에서는 STRICT가 지원되지 않아요. 기본값: CALLED ON NULL INPUT.
  • { VOLATILE | IMMUTABLE }: 결과 반환 시 UDF 동작을 지정해요. VOLATILE은 같은 입력에도 다른 값을 반환할 수 있고, IMMUTABLE은 같은 입력에 항상 같은 결과를 반환한다고 가정해요(검사되지 않음). 기본값: VOLATILE. (AGGREGATE 함수에서는 IMMUTABLE이 지원되지 않아요.)
  • COMMENT = 'string_literal': UDF에 대한 주석을 지정해요. 기본값: user-defined function.
  • COPY GRANTS: CREATE OR REPLACE FUNCTION으로 새 함수를 만들 때 원본 함수의 접근 권한을 보존해요. OWNERSHIP을 제외한 모든 권한을 복사해요.

Java / Scala

  • PACKAGES = ( 'package_name_and_version' [ , ... ] ): 의존성으로 필요한 Snowflake 시스템 패키지의 이름과 버전을 지정해요. package_name:version_number 형식이며 latest를 지정할 수 있어요. 예: PACKAGES=('com.snowflake:snowpark:1.2.0').
  • TARGET_PATH = stage_path_and_file_name_to_write: (인라인 Java/Scala) 핸들러 소스 코드 컴파일 결과를 포함한 JAR 파일을 쓸 위치를 지정해요. 생략하면 매번 코드가 필요할 때마다 재컴파일돼요. 기존 파일과 일치하면 오류. UDF를 드롭할 때 JAR 파일도 별도로 제거해야 해요.
  • EXTERNAL_ACCESS_INTEGRATIONS = ( integration_name [ , ... ] ): 핸들러 코드가 외부 네트워크에 접근하는 데 필요한 외부 접근 인티그레이션 이름을 지정해요.
  • SECRETS = ( 'secret_variable_name' = secret_name [ , ... ] ): 핸들러 코드에서 비밀을 참조할 수 있도록 변수에 비밀 이름을 할당해요. 지정된 비밀은 EXTERNAL_ACCESS_INTEGRATIONS에 포함되어야 해요.

Python

  • AGGREGATE: 함수가 집계 함수임을 지정해요.
  • ARTIFACT_REPOSITORY = repository_name: 함수가 사용할 PyPI 패키지를 설치할 저장소 이름을 지정해요.
  • PACKAGES = ( 'package_name_and_version' [ , ... ] ): 의존성으로 필요한 패키지의 이름과 버전을 지정해요. package_name==version_number 형식이며 버전 생략 시 최신 버전을 사용해요. 버전 지정자로 ==, <=, >=, <, >를 지원해요.
  • EXTERNAL_ACCESS_INTEGRATIONS / SECRETS: Java와 동일한 의미.
  • HANDLER = handler_name: 핸들러 함수/클래스 이름을 지정해요.

SQL

  • MEMOIZABLE: 함수가 메모이제이션(memoizable) 가능함을 지정해요.

접근 제어 요구 사항

권한 객체 참고
CREATE FUNCTION 스키마 스키마에서 사용자 정의 함수 생성만 허용.
USAGE 함수 새로 만든 함수에 USAGE 부여 시 다른 곳에서 호출 허용.
USAGE 외부 접근 인티그레이션 EXTERNAL_ACCESS_INTEGRATIONS에 지정된 인티그레이션에 필요.
READ 비밀 SECRETS에 지정된 비밀에 필요.
USAGE 스키마 SECRETS에 지정된 비밀을 포함한 스키마에 필요.

일반 사용법 참고 사항

모든 언어

  • UDF 정의는 1MB로 제한돼요.
  • function_definition 주변 구분자는 작은따옴표 또는 달러 기호 한 쌍($$)일 수 있어요. $$를 사용하면 작은따옴표를 포함한 함수를 더 쉽게 작성할 수 있어요.
  • 마스킹 폴리시에 UDF를 사용하면 컬럼, UDF, 마스킹 폴리시의 데이터 타입이 일치해야 해요.
  • UDF 핸들러 코드에 CURRENT_DATABASE/CURRENT_SCHEMA를 지정하면 세션의 데이터베이스/스키마가 아니라 UDF를 포함하는 데이터베이스/스키마를 반환해요.
  • OR REPLACEIF NOT EXISTS 절은 상호 배타적이에요.
  • CREATE OR REPLACE <object> 문은 원자적이에요.
  • CREATE FUNCTION 문의 속성으로 LOG_LEVEL/TRACE_LEVEL 설정은 지원되지 않아요. ALTER FUNCTION이나 CREATE OR ALTER FUNCTION을 사용하세요.

Java

  • Java의 기본(primitive) 데이터 타입은 NULL을 허용하지 않으므로 그런 타입 인자에 NULL을 전달하면 오류가 발생해요.
  • HANDLER 절의 메서드 이름은 대/소문자를 구분해요. IMPORTS/TARGET_PATH의 패키지, 클래스, 파일 이름은 대/소문자 구분, 스테이지 이름은 구분하지 않아요.
  • Snowflake는 HANDLER JAR 파일 존재, 클래스/메서드 포함, 입출력 타입 호환을 검증해요. 활성 웨어하우스에 연결된 상태면 생성 시 검증하고, 아니면 실행 시 검증하며 Function name created successfully, but could not be validated since there is no active warehouse 메시지가 반환돼요.

JavaScript

  • Snowflake는 UDF 생성 시 JavaScript 코드를 검증하지 않아요. 쿼리 시점에 함수를 호출할 때 오류를 반환해요.

Python

  • HANDLER 함수 이름은 대/소문자를 구분해요. IMPORTS의 파일 이름은 대/소문자 구분, 스테이지 이름은 구분하지 않아요.
  • Snowflake는 HANDLER 함수/클래스 존재와 입출력 타입 호환을 검증해요.

SQL

  • 현재 SQL UDF에서는 NOT NULL 절이 강제되지 않아요.

CREATE OR ALTER FUNCTION 사용법 참고 사항

ALTER FUNCTION 명령의 모든 제한이 적용돼요.

  • FUNCTION을 PROCEDURE로, PROCEDURE를 FUNCTION으로 교체/변환할 수 없어요.
  • 임시 FUNCTION과 비-임시 FUNCTION 간 교체/변환할 수 없어요.
  • 일반 FUNCTION과 EXTERNAL FUNCTION 간 교체/변환할 수 없어요.
  • LANGUAGE, HANDLER, VOLATILITY, NULL_HANDLING, TARGET_PATH 속성과 함수 입력 인자 변경은 지원되지 않아요.
  • 태그 설정/해제는 지원되지 않아요. 기존 태그는 변경되지 않고 유지돼요.

예제 (Examples)

Java (인라인):

CREATE OR REPLACE FUNCTION echo_varchar(x VARCHAR)
  RETURNS VARCHAR
  LANGUAGE JAVA
  CALLED ON NULL INPUT
  HANDLER = 'TestFunc.echoVarchar'
  TARGET_PATH = '@~/testfunc.jar'
  AS
  'class TestFunc {
    public static String echoVarchar(String x) {
      return x;
    }
  }';

Java (스테이지 참조):

create function my_decrement_udf(i numeric(9, 0))
    returns numeric
    language java
    imports = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar')
    handler = 'my_decrement_udf_package.my_decrement_udf_class.my_decrement_udf_method'
    ;

JavaScript:

CREATE OR REPLACE FUNCTION js_factorial(d double)
  RETURNS double
  LANGUAGE JAVASCRIPT
  STRICT
  AS '
  if (D <= 0) {
    return 1;
  } else {
    var result = 1;
    for (var i = 2; i <= D; i++) {
      result = result * i;
    }
    return result;
  }
  ';

Python (인라인):

CREATE OR REPLACE FUNCTION py_udf()
  RETURNS VARIANT
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.10'
  PACKAGES = ('numpy','pandas','xgboost==1.5.0')
  HANDLER = 'udf'
AS $$
import numpy as np
import pandas as pd
import xgboost as xgb
def udf():
    return [np.__version__, pd.__version__, xgb.__version__]
$$;

Python (스테이지 참조):

CREATE OR REPLACE FUNCTION dream(i int)
  RETURNS VARIANT
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.10'
  HANDLER = 'sleepy.snore'
  IMPORTS = ('@my_stage/sleepy.py')

Scala (인라인):

CREATE OR REPLACE FUNCTION echo_varchar(x VARCHAR)
  RETURNS VARCHAR
  LANGUAGE SCALA
  RUNTIME_VERSION = 2.12
  HANDLER='Echo.echoVarchar'
  AS
  $$
  class Echo {
    def echoVarchar(x : String): String = {
      return x
    }
  }
  $$;

Scala (스테이지 참조):

CREATE OR REPLACE FUNCTION echo_varchar(x VARCHAR)
  RETURNS VARCHAR
  LANGUAGE SCALA
  RUNTIME_VERSION = 2.12
  IMPORTS = ('@udf_libs/echohandler.jar')
  HANDLER='Echo.echoVarchar';

SQL 스칼라 UDF:

CREATE FUNCTION pi_udf()
  RETURNS FLOAT
  AS '3.141592654::FLOAT'
  ;

SQL 테이블 UDF:

CREATE FUNCTION simple_table_function ()
  RETURNS TABLE (x INTEGER, y INTEGER)
  AS
  $$
    SELECT 1, 2
    UNION ALL
    SELECT 3, 4
  $$
  ;
SELECT * FROM TABLE(simple_table_function());

여러 인자:

CREATE FUNCTION multiply1 (a number, b number)
  RETURNS number
  COMMENT='multiply two numbers'
  AS 'a * b';

쿼리 결과를 반환하는 SQL 테이블 UDF:

CREATE OR REPLACE FUNCTION get_countries_for_user ( id NUMBER )
  RETURNS TABLE (country_code CHAR, country_name VARCHAR)
  AS 'SELECT DISTINCT c.country_code, c.country_name
      FROM user_addresses a, countries c
      WHERE a.user_id = id
      AND c.country_code = a.country_code';

CREATE OR ALTER FUNCTION 사용 예

CREATE OR ALTER FUNCTION multiply(a NUMBER, b NUMBER)
  RETURNS NUMBER
  AS 'a * b';
CREATE OR ALTER SECURE FUNCTION multiply(a NUMBER, b NUMBER)
  RETURNS NUMBER
  COMMENT = 'Multiply two numbers.'
  AS 'a * b';

더 알아보기 (Learn more)