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 REPLACE와IF 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)
- ALTER FUNCTION
- DROP FUNCTION
- SHOW USER FUNCTIONS
- DESCRIBE FUNCTION
- Java/JavaScript/Python/Scala/SQL UDF 소개