Exemplos de manipuladores de UDF em Scala¶
Este tópico inclui exemplos simples de código de manipulador de UDF escrito em Scala.
Para obter mais informações sobre como usar o Scala para criar um manipulador escalar de UDF, consulte Como escrever uma UDF escalar em Scala. Para obter diretrizes gerais de codificação, consulte Diretrizes de codificação do manipulador de UDF de Scala.
Como criar e chamar uma UDF de Scala inline simples¶
As seguintes instruções criam e chamam uma UDF de Scala inline. Esse código retorna o VARCHAR passado a ele.
Essa função é declarada com a cláusula opcional CALLED ON NULL INPUT
para indicar que a função é chamada mesmo que o valor da entrada seja NULL. (Essa função retornaria NULL com ou sem a cláusula, mas você poderia modificar o código para tratar NULL de outra forma, por exemplo, para retornar uma cadeia de caracteres vazia).
Criação da 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
}
}
$$;
Chamada da UDF¶
SELECT echo_varchar('Hello');
Como passar um NULL para uma UDF de Scala inline¶
Isso utiliza a echo_varchar()
UDF definida acima. O valor SQL NULL
é convertido implicitamente em nulo Scala, e o Null
Scala é retornado e convertido implicitamente de volta em SQL NULL
:
Chame a UDF:
SELECT echo_varchar(NULL);
Como retornar NULL explicitamente a partir de uma UDF inline¶
O código a seguir mostra como retornar um valor NULL explicitamente. O valor Scala Null
é convertido em SQL NULL
.
Criação da 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
}
}
$$;
Chamada da UDF¶
SELECT return_a_null();
Como passar um OBJECT para uma UDF de Scala inline¶
O exemplo a seguir usa o tipo de dados SQL OBJECT e o tipo de dados de Scala correspondente (Map[String, String]
), e extrai um valor do OBJECT. Esse exemplo também mostra que você pode passar parâmetros múltiplos para uma UDF em Scala.
Como criar e carregar uma tabela que contenha uma coluna do tipo OBJECT:
CREATE TABLE objectives (o OBJECT);
INSERT INTO objectives SELECT PARSE_JSON('{"outer_key" : {"inner_key" : "inner_value"} }');
Criação da 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)
}
}
$$;
Chamada da UDF¶
SELECT extract_from_object(o, 'outer_key'),
extract_from_object(o, 'outer_key')['inner_key'] FROM OBJECTIVES;
Como passar um ARRAY para uma UDF de Scala inline¶
O exemplo a seguir utiliza o tipo de dados SQL ARRAY.
Criação da 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
}
}
$$;
Como ler um arquivo com uma UDF de Scala¶
Você pode ler o conteúdo de um arquivo com o código do manipulador. Por exemplo, talvez você queira ler um arquivo para processar dados não estruturados com o manipulador.
O arquivo deve estar em um estágio do Snowflake que esteja disponível para seu manipulador.
Para ler o conteúdo dos arquivos preparados, seu manipulador pode ler um arquivo especificado dinamicamente chamando métodos da classe SnowflakeFile
ou da classe InputStream
.
Você pode fazer isso se precisar acessar um arquivo especificado pelo chamador. Para obter mais informações, consulte o seguinte neste tópico:
Como ler um arquivo especificado dinamicamente com SnowflakeFile
Como ler um arquivo especificado dinamicamente com InputStream
SnowflakeFile
fornece recursos não disponíveis com InputStream
, conforme descrito na tabela a seguir.
Classe |
Entrada |
Notas |
---|---|---|
|
Formatos de URL:
O arquivo deve estar localizado em um estágio interno ou em um estágio externo nomeado. |
Acesso fácil a atributos adicionais do arquivo, como o tamanho do arquivo. |
|
Formatos de URL:
O arquivo deve estar localizado em um estágio interno ou em um estágio externo nomeado. |
Nota
O proprietário da UDF deve ter acesso a todos os arquivos cujos locais não estão em URLs com escopo. Você pode ler esses arquivos preparados fazendo com que o código do manipulador chame o método SnowflakeFile.newInstance
com um valor boolean
para um novo parâmetro requireScopedUrl
.
O exemplo a seguir usa SnowflakeFile.newInstance
ao especificar que um URL com escopo não é necessário.
var filename = "@my_stage/filename.txt"
var sfFile = SnowflakeFile.newInstance(filename, false)
Como ler um arquivo especificado dinamicamente com SnowflakeFile
¶
Usando métodos da classe SnowflakeFile
, você pode ler arquivos de um estágio com seu código do manipulador. A classe SnowflakeFile
está incluída no classpath disponível para os manipuladores de UDF de Scala no Snowflake.
Nota
Para tornar seu código resistente a ataques de injeção de arquivos, use sempre uma URL com escopo ao passar a localização de um arquivo para uma UDF, especialmente quando o chamador da função não for também seu proprietário. Você pode criar uma URL com escopo em SQL usando a função interna BUILD_SCOPED_FILE_URL. Para obter mais informações sobre o que o BUILD_SCOPED_FILE_URL faz, consulte Introdução aos dados não estruturados.
Para desenvolver seu código da UDF localmente, adicione o JAR do Snowpark contendo SnowflakeFile
ao caminho da classe do seu código. Para obter mais informações sobre snowpark.jar
, consulte Configuração do seu ambiente de desenvolvimento para o Snowpark Scala. Observe que os aplicativos clientes do Snowpark não podem usar essa classe.
Quando você usa SnowflakeFile
, não é necessário especificar também o arquivo preparado ou o JAR contendo SnowflakeFile
com uma cláusula IMPORTS quando você cria a UDF, como no SQL com uma instrução CREATE FUNCTION.
Criação da UDF¶
O código no exemplo a seguir usa SnowflakeFile
para ler um arquivo a partir de um local de estágio especificado. Usando um InputStream
do método getInputStream
, ele lê o conteúdo do arquivo em uma variável 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
}
}
$$;
Chamada da UDF¶
SELECT sum_total_sales_input_stream(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
Como ler um arquivo especificado dinamicamente com InputStream
¶
Você pode ler o conteúdo do arquivo diretamente em um java.io.InputStream
tornando o argumento de sua função de manipulador uma variável InputStream
. Isto pode ser útil quando o executor da função quiser passar um caminho de arquivo como um argumento.
Nota
Para tornar seu código resistente a ataques de injeção de arquivo, URLs com escopo são necessários ao passar a localização de um arquivo para uma UDF. Você pode criar uma URL com escopo em SQL usando a função interna BUILD_SCOPED_FILE_URL. Para obter mais informações sobre o que o BUILD_SCOPED_FILE_URL faz, consulte Introdução aos dados não estruturados.
Criação da UDF¶
O código no exemplo a seguir tem uma função de manipulador sumTotalSales
que obtém um InputStream
e retorna um Int
. Durante a execução, o Snowflake atribui automaticamente o conteúdo do arquivo no caminho da variável file
para a variável de argumento 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
}
}
$$;
Chamada da UDF¶
SELECT sum_total_sales_input_stream(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));