Scala UDF 처리기의 예

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

Scala를 사용하여 스칼라 UDF 처리기를 만드는 방법에 대한 자세한 내용은 Scala로 스칼라 UDF 작성하기 섹션을 참조하십시오. 일반적인 코딩 지침은 일반 Scala UDF 처리기 코딩 지침 섹션을 참조하십시오.

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

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

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

UDF 만들기

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

UDF 호출하기

SELECT echo_varchar('Hello');
Copy

인라인 Scala UDF에 NULL 전달하기

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

UDF를 호출합니다.

SELECT echo_varchar(NULL);
Copy

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

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

UDF 만들기

CREATE OR REPLACE FUNCTION return_a_null()
RETURNS VARCHAR
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
HANDLER='TemporaryTestLibrary.returnNull'
AS
$$
class TemporaryTestLibrary {
  def returnNull(): String = {
    return null
  }
}
$$;
Copy

UDF 호출하기

SELECT return_a_null();
Copy

인라인 Scala UDF에 OBJECT 전달하기

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

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

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

UDF 만들기

CREATE OR REPLACE FUNCTION extract_from_object(x OBJECT, key VARCHAR)
RETURNS VARIANT
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
HANDLER='VariantLibrary.extract'
AS
$$
import scala.collection.immutable.Map

class VariantLibrary {
  def extract(m: Map[String, String], key: String): String = {
    return m(key)
  }
}
$$;
Copy

UDF 호출하기

SELECT extract_from_object(o, 'outer_key'),
  extract_from_object(o, 'outer_key')['inner_key'] FROM OBJECTIVES;
Copy

인라인 Scala UDF에 ARRAY 전달하기

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

UDF 만들기

CREATE OR REPLACE FUNCTION generate_greeting(greeting_words ARRAY)
RETURNS VARCHAR
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
HANDLER='StringHandler.handleStrings'
AS
$$
class StringHandler {
  def handleStrings(strings: Array[String]): String = {
    return concatenate(strings)
  }
  private def concatenate(strings: Array[String]): String = {
    var concatenated : String = ""
    for (newString <- strings)  {
        concatenated = concatenated + " " + newString
    }
    return concatenated
  }
}
$$;
Copy

Scala UDF로 파일 읽기

처리기 코드로 파일의 내용을 읽을 수 있습니다. 예를 들어 처리기로 비정형 데이터를 처리하기 위해 파일을 읽을 수 있습니다.

파일은 처리기에 사용할 수 있는 Snowflake 스테이지에 있어야 합니다.

스테이징된 파일의 내용을 읽기 위해 처리기는 SnowflakeFile 클래스 또는 InputStream 클래스의 메서드를 호출하여 동적으로 지정된 파일을 읽을 수 있습니다.

호출자가 지정한 파일에 액세스해야 하는 경우 이렇게 할 수 있습니다. 자세한 내용은 이 항목의 다음 섹션을 참조하십시오.

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

클래스

입력

참고

SnowflakeFile

URL 형식:

  • 함수의 호출자가 소유자이기도 하지 않은 경우 파일 삽입 공격의 위험을 줄이기 위해 범위가 지정된 URL입니다.

  • UDF 소유자가 액세스할 수 있는 파일의 파일 URL 또는 문자열 경로입니다.

파일은 명명된 내부 스테이지 또는 외부 스테이지에 있어야 합니다.

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

InputStream

URL 형식:

  • 함수의 호출자가 소유자이기도 하지 않은 경우 파일 삽입 공격의 위험을 줄이기 위해 범위가 지정된 URL입니다.

파일은 명명된 내부 스테이지 또는 외부 스테이지에 있어야 합니다.

참고

UDF 소유자는 위치가 범위 지정된 URL이 아닌 모든 파일에 액세스할 수 있어야 합니다. 처리기 코드가 새 requireScopedUrl 매개 변수에 대한 boolean 값으로 SnowflakeFile.newInstance 메서드를 호출하도록 하여 이들 스테이징된 파일을 읽을 수 있습니다.

다음 예에서는 범위가 지정된 URL이 필요하지 않음을 지정하면서 SnowflakeFile.newInstance 를 사용합니다.

var filename = "@my_stage/filename.txt"
var sfFile = SnowflakeFile.newInstance(filename, false)
Copy

SnowflakeFile 을 사용하여 동적으로 지정된 파일 읽기

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

참고

파일 삽입 공격에 대한 코드의 탄력성을 높이려면 파일의 위치를 UDF에 전달할 때, 특히 함수 호출자가 소유자이기도 하지 않을 때는 항상 범위가 지정된 URL을 사용하십시오. 기본 제공 함수 BUILD_SCOPED_FILE_URL 을 사용하여 SQL에서 범위가 지정된 URL을 생성할 수 있습니다. BUILD_SCOPED_FILE_URL 의 기능에 대한 자세한 내용은 반정형 데이터 로딩 소개 섹션을 참조하십시오.

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

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

UDF 만들기

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

CREATE OR REPLACE FUNCTION sum_total_sales_snowflake_file(file string)
RETURNS INTEGER
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
PACKAGES=('com.snowflake:snowpark:latest')
HANDLER='SalesSum.sumTotalSales'
AS
$$
import java.io.InputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
import com.snowflake.snowpark_java.types.SnowflakeFile

object SalesSum {
  @throws(classOf[IOException])
  def sumTotalSales(filePath: String): Int = {
    var total = -1

    // Use a SnowflakeFile instance to read sales data from a stage.
    val file = SnowflakeFile.newInstance(filePath)
    val stream = file.getInputStream()
    val 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
  }
}
$$;
Copy

UDF 호출하기

SELECT sum_total_sales_input_stream(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
Copy

InputStream 을 사용하여 동적으로 지정된 파일 읽기

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

참고

파일 주입 공격에 대한 코드 복원력을 높이려면 파일 위치를 UDF에 전달할 때 범위가 지정된 URL이 필요합니다. 기본 제공 함수 BUILD_SCOPED_FILE_URL 을 사용하여 SQL에서 범위가 지정된 URL을 생성할 수 있습니다. BUILD_SCOPED_FILE_URL 의 기능에 대한 자세한 내용은 반정형 데이터 로딩 소개 섹션을 참조하십시오.

UDF 만들기

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

CREATE OR REPLACE FUNCTION sum_total_sales_input_stream(file STRING)
RETURNS NUMBER
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
HANDLER = 'SalesSum.sumTotalSales'
PACKAGES = ('com.snowflake:snowpark:latest')
AS $$
import com.snowflake.snowpark.types.Variant
import java.io.InputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
object SalesSum {
  @throws(classOf[IOException])
  def sumTotalSales(stream: InputStream): Int = {
    val total = -1
    val 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
  }
}
$$;
Copy

UDF 호출하기

SELECT sum_total_sales_input_stream(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
Copy