Java UDF 처리기의 예

이 항목에는 Java로 작성된 UDF 처리기 코드의 간단한 예가 나와 있습니다.

Java를 사용하여 UDF 처리기를 만드는 자세한 방법은 Java UDF 만들기 섹션을 참조하십시오.

이 항목의 내용:

간단한 인라인 Java UDF 만들기 및 호출

다음 문은 인라인 Java UDF를 만들고 호출합니다. 이 코드는 이에 전달된 VARCHAR 를 반환합니다.

이 함수는 입력 값이 NULL인 경우에도 함수가 호출됨을 나타내기 위해 선택적 CALLED ON NULL INPUT 절로 선언됩니다. (이 함수는 이 절을 포함하거나 포함하지 않고 NULL을 반환하지만, 사용자는 빈 문자열을 반환하는 등 다른 방식으로 NULL을 처리하도록 코드를 수정할 수 있습니다.)

UDF를 만듭니다.

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;
  }
}';

UDF를 호출합니다.

SELECT echo_varchar('Hello');
+-----------------------+
| ECHO_VARCHAR('HELLO') |
|-----------------------|
| Hello                 |
+-----------------------+

인라인 Java UDF에 NULL 전달

이는 위에서 정의한 echo_varchar() UDF를 사용합니다. SQL NULL 값은 암시적으로 Java null 로 변환되고 해당 Java null 이 반환되어 암시적으로 SQL NULL 로 다시 변환됩니다.

UDF를 호출합니다.

SELECT echo_varchar(NULL);
+--------------------+
| ECHO_VARCHAR(NULL) |
|--------------------|
| NULL               |
+--------------------+

인라인 UDF에서 명시적으로 NULL 반환하기

다음 코드는 명시적으로 NULL 값을 반환하는 방법을 보여줍니다. Java 값 null 은 SQL NULL 로 변환됩니다.

UDF를 만듭니다.

create or replace function return_a_null()
returns varchar
null
language java
handler='TemporaryTestLibrary.returnNull'
target_path='@~/TemporaryTestLibrary.jar'
as
$$
class TemporaryTestLibrary {
  public static String returnNull() {
    return null;
  }
}
$$;

UDF를 호출합니다.

SELECT return_a_null();
+-----------------+
| RETURN_A_NULL() |
|-----------------|
| NULL            |
+-----------------+

인라인 Java UDF에 OBJECT 전달

다음 예에서는 SQL OBJECT 데이터 타입과 해당 Java 데이터 타입(Map<String, String>)을 사용하고 OBJECT 에서 값을 추출합니다. 이 예는 또한 여러 매개 변수를 Java UDF에 전달할 수 있음을 보여줍니다.

다음과 같이 OBJECT 형식의 열이 포함된 테이블을 만들고 로딩하십시오.

CREATE TABLE objectives (o OBJECT);
INSERT INTO objectives SELECT PARSE_JSON('{"outer_key" : {"inner_key" : "inner_value"} }');

UDF를 만듭니다.

create or replace function extract_from_object(x OBJECT, key VARCHAR)
returns variant
language java
handler='VariantLibrary.extract'
target_path='@~/VariantLibrary.jar'
as
$$
import java.util.Map;
class VariantLibrary {
  public static String extract(Map<String, String> m, String key) {
    return m.get(key);
  }
}
$$;

UDF를 호출합니다.

SELECT extract_from_object(o, 'outer_key'), 
       extract_from_object(o, 'outer_key')['inner_key'] FROM objectives;
+-------------------------------------+--------------------------------------------------+
| EXTRACT_FROM_OBJECT(O, 'OUTER_KEY') | EXTRACT_FROM_OBJECT(O, 'OUTER_KEY')['INNER_KEY'] |
|-------------------------------------+--------------------------------------------------|
| {                                   | "inner_value"                                    |
|   "inner_key": "inner_value"        |                                                  |
| }                                   |                                                  |
+-------------------------------------+--------------------------------------------------+

인라인 Java UDF에 ARRAY 전달

다음 예에서는 SQL ARRAY 데이터 타입을 사용합니다.

UDF를 만듭니다.

create or replace function multiple_functions_in_jar(array1 array)
returns varchar
language java
handler='TemporaryTestLibrary.handleStrings'
target_path='@~/TemporaryTestLibrary.jar'
as
$$
class TemporaryTestLibrary {
  public static String handleStrings(String[] strings) {
    return concatenate(strings);
  }
  public static String concatenate(String[] strings) {
    int numberOfStrings = strings.length;
    String concatenated = "";
    for (int i = 0; i < numberOfStrings; i++)  {
        concatenated = concatenated + " " + strings[i];
    }
    return concatenated;
  }
}
$$;

UDF를 호출합니다.

SELECT multiple_functions_in_jar(ARRAY_CONSTRUCT('Hello', 'world'));
+--------------------------------------------------------------+
| MULTIPLE_FUNCTIONS_IN_JAR(ARRAY_CONSTRUCT('HELLO', 'WORLD')) |
|--------------------------------------------------------------|
|  Hello world                                                 |
+--------------------------------------------------------------+

인라인 Java UDF에 GEOGRAPHY 값 전달

다음 예에서는 SQL GEOGRAPHY 데이터 타입을 사용합니다.

UDF를 만듭니다.

create or replace function geography_equals(x geography, y geography)
returns boolean
language java
packages=('com.snowflake:snowpark:1.2.0')
handler='TestGeography.compute'
as
$$
import com.snowflake.snowpark_java.types.Geography;

class TestGeography {
  public static boolean compute(Geography geo1, Geography geo2) {
    return geo1.equals(geo2);
  }
}
$$;

PACKAGES 절을 사용하여 Snowpark 패키지 와 같은 Snowflake 시스템 패키지를 지정할 수 있습니다. 이때 Snowpark JAR 파일도 IMPORTS 절의 값으로 포함할 필요는 없습니다. PACKAGES 에 대한 자세한 내용은 CREATE FUNCTION 옵션 매개 변수 를 참조하십시오.

데이터를 생성하고 해당 데이터로 UDF를 호출합니다.

create table geocache_table (id INTEGER, g1 GEOGRAPHY, g2 GEOGRAPHY);

insert into geocache_table (id, g1, g2) select
    1, TO_GEOGRAPHY('POINT(-122.35 37.55)'), TO_GEOGRAPHY('POINT(-122.35 37.55)');
insert into geocache_table (id, g1, g2) select
    2, TO_GEOGRAPHY('POINT(-122.35 37.55)'), TO_GEOGRAPHY('POINT(90.0 45.0)');

select id, g1, g2, geography_equals(g1, g2) as "EQUAL?"
    from geocache_table
    order by id;

출력은 다음과 유사합니다.

+----+--------------------------------------------------------+---------------------------------------------------------+--------+
| ID | G1                                                     | G2                                                      | EQUAL? |
+----+--------------------------------------------------------|---------------------------------------------------------+--------+
| 1  | { "coordinates": [ -122.35, 37.55 ], "type": "Point" } | { "coordinates": [ -122.35,  37.55 ], "type": "Point" } | TRUE   |
| 2  | { "coordinates": [ -122.35, 37.55 ], "type": "Point" } | { "coordinates": [   90.0,   45.0  ], "type": "Point" } | FALSE  |
+----+--------------------------------------------------------+---------------------------------------------------------+--------+

인라인 Java UDF에 VARIANT 값 전달

SQL VARIANT 형식의 값을 Java UDF에 전달하면 Snowflake가 Snowpark 패키지 와 함께 제공되는 베리언트 형식으로 값을 변환할 수 있습니다. Variant 는 Snowpark 패키지 버전 1.4.0 이상에서 지원됩니다.

Snowpark Variant 형식은 Variant 와 다른 형식 간에 값을 변환하는 방법을 제공합니다.

Snowpark Variant 형식을 사용하려면 PACKAGES 절을 사용하여 UDF를 만들 때 Snowpark 패키지를 지정하십시오. 이때 Snowpark JAR 파일도 IMPORTS 절의 값으로 포함할 필요는 없습니다. PACKAGES 에 대한 자세한 내용은 CREATE FUNCTION 옵션 매개 변수 를 참조하십시오.

다음 예제의 코드는 VARIANT 형식으로 저장된 JSON 데이터를 받은 다음, Snowpark 라이브러리의 Variant 형식을 사용하여 JSON에서 price 값을 검색합니다. 수신된 JSON은 예에서 사용된 샘플 데이터 에 표시된 JSON과 유사한 구조를 갖습니다.

create or replace function retrieve_price(v variant)
returns integer
language java
packages=('com.snowflake:snowpark:1.4.0')
handler='VariantTest.retrievePrice'
as
$$
import java.util.Map;
import com.snowflake.snowpark_java.types.Variant;

public class VariantTest {
  public static Integer retrievePrice(Variant v) throws Exception {
    Map<String, Variant> saleMap = v.asMap();
    int price = saleMap.get("vehicle").asMap().get("price").asInt();
    return price;
  }
}
$$;

Java UDF 처리기로 파일 읽기

Java UDF 처리기 코드로 파일의 내용을 읽을 수 있습니다. 파일은 처리기에 사용할 수 있는 Snowflake 스테이지에 있어야 합니다. 예를 들어 처리기에서 비정형 데이터를 처리하기 위해 파일을 읽을 수 있습니다. 자세한 내용은 Java UDF 또는 UDTF를 사용하여 비정형 데이터 처리하기 섹션을 참조하십시오.

스테이징된 파일의 내용을 읽기 위해 Java UDF 또는 UDTF는 다음을 수행할 수 있습니다.

  • IMPORTS 절로 파일 경로와 이름을 지정한 다음, UDF의 홈 디렉터리에서 읽습니다. 이는 초기화 중과 같이 파일에 정적으로 액세스하려는 할 때 유용할 수 있습니다.

  • SnowflakeFile 클래스 또는 InputStream 클래스의 메서드를 호출합니다. 계산 중에 파일에 동적으로 액세스해야 하는 경우 이 작업을 수행할 수 있습니다. 자세한 내용은 이 항목의 SnowflakeFile 클래스를 사용하여 파일 읽기 또는 InputStream 클래스를 사용하여 파일 읽기 섹션을 참조하십시오.

    SnowflakeFile 은 다음 표에 설명된 것처럼 InputStream 과 함께 사용할 수 없는 기능을 제공합니다.

    클래스

    입력

    참고

    SnowflakeFile

    내부 또는 외부 스테이지에 있는 파일의 범위 지정 URL, 파일 URL 또는 문자열 경로

    파일 크기와 같은 추가 파일 특성에 쉽게 액세스할 수 있습니다.

    InputStream

    내부 또는 외부 스테이지에 있는 파일의 범위 지정 URL, 파일 URL 또는 문자열 경로

전제 조건

다음을 수행하여 코드에 스테이지의 파일을 사용할 수 있도록 해야 Java 처리기 코드가 해당 파일을 읽을 수 있습니다.

  1. 처리기가 사용할 수 있는 스테이지를 만듭니다.

    외부 스테이지 또는 내부 스테이지를 사용할 수 있습니다. 내부 스테이지를 사용하는 경우 사용자 또는 명명된 스테이지여야 합니다. Snowflake는 현재 UDF 종속 항목에 테이블 스테이지를 사용할 수 있도록 지원하지 않습니다. 스테이지 생성에 대한 자세한 내용은 CREATE STAGE 섹션을 참조하십시오. 내부 스테이지 유형 선택에 대한 자세한 내용은 로컬 파일을 위한 내부 스테이지 선택하기 섹션을 참조하십시오.

    스테이지에서 읽는 SQL 작업을 수행하는 역할에 스테이지에 대한 적절한 권한을 할당해야 한다는 점에 유의하십시오. 자세한 내용은 사용자 정의 함수에 대한 권한 부여하기 섹션을 참조하십시오.

  2. 코드로 읽을 파일을 스테이지에 복사합니다.

    PUT 명령을 사용하여 로컬 드라이브에서 스테이지로 파일을 복사할 수 있습니다. 명령 참조는 PUT 섹션을 참조하십시오. PUT으로 파일을 스테이징하는 자세한 방법은 로컬 파일 시스템에서 데이터 파일 스테이징하기 섹션을 참조하십시오.

IMPORTS를 사용하여 파일 읽기

CREATE FUNCTION 명령의 IMPORTS 절에 파일 이름과 스테이지 이름을 지정하여 파일을 읽을 수 있습니다.

IMPORTS 절에 파일을 지정하면 Snowflake는 해당 파일을 스테이지에서 UDF의 홈 디렉터리 (가져오기 디렉터리 라고도 함)로 복사합니다. 이 디렉터리는 UDF가 실제로 파일을 읽는 디렉터리입니다.

가져온 파일은 단일 디렉터리에 복사되고, 해당 디렉터리 내에서 고유한 이름을 가져야 하기 때문에 IMPORTS 절의 각 파일은 고유한 이름을 가져야 합니다. 해당 파일이 다른 스테이지에서 시작하거나 스테이지 내의 다른 하위 디렉터리에서 시작하더라도 마찬가지입니다.

다음 예에서는 파일을 읽는 Java UDF를 만들고 호출합니다.

아래 Java 소스 코드는 readFile 이라는 Java 메서드를 만듭니다. 이 UDF는 이 메서드를 사용합니다.

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

class TestReadRelativeFile {
    public static String readFile(String fileName) throws IOException {
        StringBuilder contentBuilder = new StringBuilder();
        String importDirectory = System.getProperty("com.snowflake.import_directory");
        String fPath = importDirectory + fileName;
        Stream<String> stream = Files.lines(Paths.get(fPath), StandardCharsets.UTF_8);
        stream.forEach(s -> contentBuilder.append(s).append("\n"));
        return contentBuilder.toString();
    }
}

다음 SQL 코드는 UDF를 만듭니다. 이 코드는 Java 소스 코드가 컴파일되어, UDF가 가져오는 TestReadRelativeFile.jar 이라는 JAR 파일에 저장되었다고 가정합니다. 두 번째 및 세 번째 가져온 파일인 my_config_file_1.txtmy_config_file_2.txt 는 UDF가 읽을 수 있는 구성 파일입니다.

CREATE FUNCTION file_reader(file_name VARCHAR)
RETURNS VARCHAR
LANGUAGE JAVA
IMPORTS = ('@my_stage/my_package/TestReadRelativeFile.jar',
           '@my_stage/my_path/my_config_file_1.txt',
           '@my_stage/my_path/my_config_file_2.txt')
HANDLER = 'my_package.TestReadRelativeFile.readFile';

이 코드는 UDF를 호출합니다.

SELECT file_reader('my_config_file_1.txt') ...;
...
SELECT file_reader('my_config_file_2.txt') ...;

압축 또는 비압축 형식의 파일 액세스 여부 선택

스테이지의 파일은 압축 또는 비압축 형식으로 저장할 수 있습니다. 사용자는 파일을 스테이지에 복사하기 전에 압축하거나, PUT 명령에 파일 압축을 지시할 수 있습니다.

Snowflake가 GZIP 형식으로 압축된 파일을 스테이지에서 UDF 홈 디렉터리로 복사할 때 Snowflake는 복사본을 있는 그대로 쓰거나, 파일을 쓰기 전에 내용의 압축을 풀 수 있습니다.

스테이지의 파일이 압축되어 있고 UDF 홈 디렉터리의 복사본도 압축하려는 경우, IMPORTS 절에 파일 이름을 지정할 때 원래 파일 이름(예: 《MyData.txt.gz》)을 IMPORTS 절에 사용하기만 하면 됩니다. 예:

... imports = ('@MyStage/MyData.txt.gz', ...)

스테이지에 있는 파일이 GZIP으로 압축되어 있지만, UDF 홈 디렉터리에 있는 복사본의 압축을 풀려는 경우, IMPORTS 절에 파일 이름을 지정할 때 《.gz》 확장자를 생략하십시오. 예를 들어, 스테이지에 《MyData.txt.gz》가 포함되어 있지만, UDF가 압축되지 않은 형식으로 파일을 읽도록 하려면 IMPORTS 절에 《MyData.txt》를 지정하십시오. 《MyData.txt》라는 압축되지 않은 파일이 없는 경우, Snowflake는 《MyData.txt.gz》를 검색하고, 압축을 푼 복사본을 UDF 홈 디렉터리의 《MyData.txt》에 자동으로 씁니다. 그러면 UDF는 압축되지 않은 《MyData.txt》 파일을 열고 읽을 수 있습니다.

스마트 압축 풀기는 UDF 홈 디렉터리의 복사본에만 적용됩니다. 스테이지의 원본 파일은 변경되지 않습니다.

압축 파일 처리에 대한 다음 모범 사례를 따르십시오.

  • 적절한 파일 명명 규칙을 따르십시오. 파일이 GZIP 압축 형식인 경우, 파일 이름 끝에 《.gz》 확장자를 포함하십시오. 파일이 GZIP 압축 형식이 아닌 경우, 파일 이름을 《.gz》 확장자로 끝내지 마십시오.

  • 이름 차이가 《.gz》 확장자일 뿐인 파일을 생성하지 마십시오. 예를 들어, 동일한 스테이지와 디렉터리에서 《MyData.txt》와 《MyData.txt.gz》를 만들지 말고, 동일한 CREATEFUNCTION 명령에서 《MyData.txt》와 《MyData.txt.gz》를 둘 다 가져오려고 하지 마십시오.

  • 파일을 두 번 압축하지 마십시오. 예를 들어, 파일을 수동으로 압축한 다음, AUTO_COMPRESS=FALSE를 사용하지 않고 해당 파일을 PUT 하면 파일이 두 번째로 압축됩니다. 스마트 압축 풀기는 한 번만 압축을 풀기 때문에 데이터(또는 JAR) 파일은 UDF 홈 디렉터리에 저장될 때 여전히 압축됩니다.

  • 앞으로 Snowflake는 스마트 압축 풀기를 GZIP 이외의 압축 알고리즘으로 확장할 수 있습니다. 향후 호환성 문제를 방지하려면 이러한 모범 사례를 모든 형식의 압축 사용 파일에 적용하십시오.

참고

JAR 파일은 스테이지에서 압축 또는 비압축 형식으로 저장할 수도 있습니다. Snowflake는 압축된 모든 JAR 파일을 Java UDF에서 사용할 수 있도록 자동으로 압축을 풉니다.

SnowflakeFile 클래스를 사용하여 파일 읽기

SnowflakeFile 클래스의 메서드를 사용하여 Java 처리기 코드로 스테이지에서 파일을 읽을 수 있습니다. SnowflakeFile 클래스는 Snowflake의 Java UDF 처리기가 사용할 수 있는 클래스 경로에 포함됩니다.

UDF 코드를 로컬에서 개발하려면 SnowflakeFile 이 포함된 Snowpark JAR을 코드의 클래스 경로에 추가하십시오. snowpark.jar 에 대한 정보는 Snowpark Java를 위한 개발 환경 설정하기 섹션을 참조하십시오. Snowpark 클라이언트 애플리케이션은 이 클래스를 사용할 수 없으므로, 이 클래스는 Snowpark 항목에 문서화되어 있지 않습니다.

SnowflakeFile 을 사용할 때는 CREATE FUNCTION 문을 포함한 SQL에서처럼 UDF를 생성할 때 스테이징된 파일이나 SnowflakeFile 이 포함된 JAR을 IMPORTS 절로도 지정할 필요가 없습니다.

SnowflakeFile 클래스에는 다음과 같은 메서드가 있습니다.

메서드

설명

public static native SnowflakeFile newInstance(String url)

url 인자로 지정된 위치의 파일에 대한 SnowflakeFile 오브젝트를 반환합니다.

public synchronized InputStream getInputStream()

파일의 내용을 읽기 위한 InputStream 오브젝트를 반환합니다.

public synchronized Long getSize()

파일의 크기를 반환합니다.

다음 예제의 코드에서는 SnowflakeFile 을 사용하여 지정된 스테이지 위치에서 파일을 읽습니다. getInputStream 메서드의 InputStream 을 사용하여 파일의 내용을 String 변수로 읽습니다.

create or replace function sum_total_sales(file string)
returns INTEGER
language java
handler='SalesSum.sumTotalSales'
target_path='@jar_stage/sales_functions2.jar'
as
$$
import java.io.InputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.snowflake.snowpark_java.types.SnowflakeFile;

public class SalesSum {

  public static int sumTotalSales(String filePath) throws IOException {
    int total = -1;

    // Use a SnowflakeFile instance to read sales data from a stage.
    SnowflakeFile file = SnowflakeFile.newInstance(filePath);
    InputStream stream = file.getInputStream();
    String contents = new String(stream.readAllBytes(), StandardCharsets.UTF_8);

    // Omitted for brevity: code to retrieve sales data from JSON and assign it to the total variable.

    return total;
  }
}
$$;

UDF를 호출하여 처리할 스테이지의 파일을 지정합니다.

select sum_total_sales('@sales_data_stage/car_sales.json');

InputStream 클래스를 사용하여 파일 읽기

처리기 함수의 인자를 InputStream 변수로 만들어 파일 내용을 java.io.InputStream 으로 직접 읽을 수 있습니다. 이것은 함수의 호출자가 파일 경로를 인자로 전달하고 싶을 때 유용할 수 있습니다.

다음 예제의 코드에는 InputStream 을 받아 int 를 반환하는 처리기 함수 sumTotalSales 가 있습니다. 런타임에 Snowflake는 file 변수의 경로에 있는 파일의 내용을 stream 인자 변수에 자동으로 할당합니다.

create or replace function sum_total_sales(file string)
returns INTEGER
language java
handler='SalesSum.sumTotalSales'
target_path='@jar_stage/sales_functions2.jar'
as
$$
import java.io.InputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class SalesSum {

  public static int sumTotalSales(InputStream stream) throws IOException {
    int total = -1;
    String contents = new String(stream.readAllBytes(), StandardCharsets.UTF_8);

    // Omitted for brevity: code to retrieve sales data from JSON and assign it to the total variable.

    return total;
  }
}
$$;

UDF를 호출하여 처리할 스테이지의 파일을 지정합니다.

select sum_total_sales('@sales_data_stage/car_sales.json');

간단한 미리 컴파일된 Java UDF 만들기 및 호출하기

다음 문은 간단한 Java UDF를 만듭니다. 일반적으로 이 샘플은 파일 정리 에 설명된 파일 및 디렉터리 구조를 따릅니다.

Java 처리기 코드 생성 및 컴파일하기

  1. 프로젝트의 루트 디렉터리(이 경우 my_udf)에서 소스 .java 파일을 보관할 src 하위 디렉터리, 그리고 생성된 .class 파일을 보관할 classes 하위 디렉터리를 생성합니다.

    다음과 유사한 디렉터리 계층 구조가 있어야 합니다.

    my_udf/
    |-- classes/
    |-- src/
    
  2. src 디렉터리에서, 클래스가 mypackage 패키지에 있는 .java 파일을 보유할 mypackage 라는 디렉터리를 생성합니다.

  3. mypackage 디렉터리에서, 소스 코드가 포함된 MyUDFHandler.java 파일을 생성합니다.

    package mypackage;
    
    public class MyUDFHandler {
    
      public static int decrementValue(int i)
      {
          return i - 1;
      }
    
      public static void main(String[] argv)
      {
          System.out.println("This main() function won't be called.");
      }
    }
    
  4. 프로젝트 루트 디렉터리(이 경우 my_udf)에서 javac 명령을 사용하여 소스 코드를 컴파일합니다.

    다음 예의 javac 명령은 MyUDFHandler.java 를 컴파일하여 classes 디렉터리에 MyUDFHandler.class 파일을 생성합니다.

    javac -d classes src/mypackage/MyUDFHandler.java
    

    이 예에는 다음 인수가 포함됩니다.

    • -d classes – 생성된 클래스 파일이 기록되어야 하는 디렉터리.

    • src/mypackage/MyUDFHandler.java – 형식의 .java 파일 경로: source_directory/package_directory/Java_file_name.

컴파일된 코드를 JAR 파일로 패키징하기

  1. 선택적으로, 프로젝트 루트 디렉터리에서, 다음 속성을 포함하는 my_udf.manifest 라는 매니페스트 파일을 생성합니다.

    Manifest-Version: 1.0
    Main-Class: mypackage.MyUDFHandler
    
  2. 프로젝트 루트 디렉터리에서 jar 명령을 실행하여, .class 파일과 매니페스트가 포함된 JAR 파일을 생성합니다.

    다음 예의 jar 명령은 mypackage 패키지 폴더에 생성된 MyUDFHandler.class 파일을 my_udf.jar 이라는 .jar 파일에 넣습니다. -C ./classes 플래그는 .class 파일의 위치를 지정합니다.

    jar cmf my_udf.manifest my_udf.jar -C ./classes mypackage/MyUDFHandler.class
    

    이 예에는 다음 인수가 포함됩니다.

    • cmf – 명령 인수: c 는 JAR 파일을 생성하고, m 은 지정된 .manifest 파일을 사용하고, f 는 지정된 이름을 JAR 파일에 부여합니다.

    • my_udf.manifest – 매니페스트 파일.

    • my_udf.jar – 생성할 JAR 파일의 이름.

    • -C ./classes – 생성된 .class 파일을 포함하는 디렉터리.

    • mypackage/MyUDFHandler.class – JAR에 포함할 .class 파일의 패키지 및 이름.

컴파일된 처리기가 있는 JAR 파일을 스테이지에 업로드하기

  1. Snowflake에서 jar_stage 라는 스테이지를 생성하여, UDF 핸들러가 포함된 JAR 파일을 저장합니다.

    스테이지 생성에 대한 자세한 내용은 CREATE STAGE 섹션을 참조하십시오.

  2. PUT 명령을 사용하여 JAR 파일을 로컬 파일 시스템에서 스테이지로 복사하십시오.

put
    file:///Users/Me/my_udf/my_udf.jar
    @jar_stage
    auto_compress = false
    overwrite = true
    ;

PUT 명령을 스크립트 파일에 저장한 다음, SnowSQL 을 통해 해당 파일을 실행할 수 있습니다.

snowsql 명령은 다음과 유사합니다.

snowsql -a <account_identifier> -w <warehouse> -d <database> -s <schema> -u <user> -f put_command.sql

이 예에서는 사용자의 비밀번호가 SNOWSQL_PWD 환경 변수에 지정되어 있다고 가정합니다.

컴파일된 코드를 처리기로 사용하여 UDF 만들기

UDF를 만듭니다.

create function decrement_value(i numeric(9, 0))
  returns numeric
  language java
  imports = ('@jar_stage/my_udf.jar')
  handler = 'mypackage.MyUDFHandler.decrementValue'
  ;

UDF를 호출합니다.

select decrement_value(-15);
+----------------------+
| DECREMENT_VALUE(-15) |
|----------------------|
|                  -16 |
+----------------------+

고려 사항

  • 쿼리가 스테이징된 파일에 액세스하는 UDF를 호출하는 경우 뷰의 함수가 스테이징된 파일에 액세스하는지 여부에 관계없이 SQL 문이 모든 UDF 또는 UDTF를 호출하는 뷰도 쿼리하면 사용자 오류가 발생하며 작업이 실패합니다.

  • UDTF는 여러 파일을 병렬로 처리할 수 있지만, UDF는 현재 파일을 직렬로 처리합니다. 해결 방법으로는, GROUP BY 절을 사용하여 하위 쿼리에 행을 그룹화하십시오. 예제는 Java UDTF 예 섹션을 참조하십시오.

  • 현재, 쿼리 실행 중에 쿼리에서 참조되는 스테이징된 파일이 수정되거나 삭제될 경우에는 오류가 발생하며 함수 호출이 실패합니다.

맨 위로 이동