Exemplos de manipuladores de UDF em Java

Este tópico inclui exemplos simples de código de manipulador de UDF escrito em Java.

Para saber mais sobre como usar Java para criar um manipulador de UDF, veja Como criar um manipulador de UDF de Java.

Neste tópico:

Como criar e chamar uma UDF de Java inline simples

As seguintes instruções criam e chamam uma UDF de Java 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).

Crie a 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;
  }
}';
Copy

Chame a UDF:

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

Como passar um NULL para uma UDF de Java inline

Isso utiliza a echo_varchar() UDF definida acima. O valor SQL NULL é implicitamente convertido para Java null, e esse null do Java é retornado e implicitamente convertido de volta para SQL NULL:

Chame a UDF:

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

Passagem de valores de matriz

Os métodos de Java podem receber essas matrizes SQL de uma das duas maneiras:

  • Usando o recurso de array do Java.

  • Usando o recurso varargs (número variável de argumentos) do Java.

Em ambos os casos, seu código de SQL deve passar uma ARRAY.

Nota

Certifique-se de usar tipos Java com mapeamentos válidos para tipos SQL. Para obter mais informações, consulte Mapeamentos de tipos de dados SQL-Java.

Passando por uma ARRAY

Declare o parâmetro de Java como uma array. Por exemplo, o terceiro parâmetro no método a seguir é uma array de cadeias de caracteres:

static int myMethod(int fixedArgument1, int fixedArgument2, String[] stringArray)
Copy

Abaixo está um exemplo completo:

Criar e carregar a tabela:

CREATE TABLE string_array_table(id INTEGER, a ARRAY);
INSERT INTO string_array_table (id, a) SELECT
        1, ARRAY_CONSTRUCT('Hello');
INSERT INTO string_array_table (id, a) SELECT
        2, ARRAY_CONSTRUCT('Hello', 'Jay');
INSERT INTO string_array_table (id, a) SELECT
        3, ARRAY_CONSTRUCT('Hello', 'Jay', 'Smith');
Copy

Crie a UDF:

create or replace function concat_varchar_2(a ARRAY)
returns varchar
language java
handler='TestFunc_2.concatVarchar2'
target_path='@~/TestFunc_2.jar'
as
$$
    class TestFunc_2 {
        public static String concatVarchar2(String[] strings) {
            return String.join(" ", strings);
        }
    }
$$;
Copy

Chame a UDF:

SELECT concat_varchar_2(a)
    FROM string_array_table
    ORDER BY id;
+---------------------+
| CONCAT_VARCHAR_2(A) |
|---------------------|
| Hello               |
| Hello Jay           |
| Hello Jay Smith     |
+---------------------+
Copy

Como passar usando o Varargs

O uso do varargs é muito semelhante ao uso de uma array.

Em seu código de Java, use o estilo de declaração do varargs de Java:

static int myMethod(int fixedArgument1, int fixedArgument2, String ... stringArray)
Copy

Abaixo está um exemplo completo. A única diferença significativa entre este exemplo e o exemplo anterior (para arrays) é a declaração dos parâmetros para o método.

Criar e carregar a tabela:

CREATE TABLE string_array_table(id INTEGER, a ARRAY);
INSERT INTO string_array_table (id, a) SELECT
        1, ARRAY_CONSTRUCT('Hello');
INSERT INTO string_array_table (id, a) SELECT
        2, ARRAY_CONSTRUCT('Hello', 'Jay');
INSERT INTO string_array_table (id, a) SELECT
        3, ARRAY_CONSTRUCT('Hello', 'Jay', 'Smith');
Copy

Crie a UDF:

create or replace function concat_varchar(a ARRAY)
returns varchar
language java
handler='TestFunc.concatVarchar'
target_path='@~/TestFunc.jar'
as
$$
    class TestFunc {
        public static String concatVarchar(String ... stringArray) {
            return String.join(" ", stringArray);
        }
    }
$$;
Copy

Chame a UDF:

SELECT concat_varchar(a)
    FROM string_array_table
    ORDER BY id;
+-------------------+
| CONCAT_VARCHAR(A) |
|-------------------|
| Hello             |
| Hello Jay         |
| Hello Jay Smith   |
+-------------------+
Copy

Como retornar NULL explicitamente a partir de uma UDF inline

O código a seguir mostra como retornar um valor NULL explicitamente. O valor Java null é convertido para SQL NULL.

Crie a 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;
  }
}
$$;
Copy

Chame a UDF:

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

Como passar um OBJECT para uma UDF de Java inline

O exemplo a seguir usa o tipo de dados SQL OBJECT e o tipo de dados de Java 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 Java.

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"} }');
Copy

Crie a 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);
  }
}
$$;
Copy

Chame a 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"        |                                                  |
| }                                   |                                                  |
+-------------------------------------+--------------------------------------------------+
Copy

Como passar um valor GEOGRAPHY para uma UDF de Java inline

O exemplo a seguir utiliza o tipo de dados SQL GEOGRAPHY.

Crie a 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);
  }
}
$$;
Copy

Você pode usar a cláusula PACKAGES para especificar um pacote do sistema Snowflake, como o pacote Snowpark. Quando o fizer, você não precisa incluir também o arquivo JAR do Snowpark como um valor de uma cláusula IMPORTS. Para saber mais sobre PACKAGES, consulte parâmetros opcionais CREATE FUNCTION.

Crie dados e chame a UDF com esses dados:

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;
Copy

A saída é semelhante a:

+----+--------------------------------------------------------+---------------------------------------------------------+--------+
| 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  |
+----+--------------------------------------------------------+---------------------------------------------------------+--------+
Copy

Como passar um valor VARIANT para uma UDF de Java inline

Quando você passa um valor do tipo SQL VARIANT para uma UDF de Java, o Snowflake pode converter o valor para o tipo Variant fornecido com o pacote Snowpark. Note que o Variant é suportado a partir da versão do pacote Snowpark 1.4.0 e posterior.

O tipo Snowpark Variant fornece métodos de conversão de valores entre Variant e outros tipos.

Para utilizar o Snowpark Variant, use a cláusula PACKAGES para especificar o pacote Snowpark ao criar a UDF. Quando o fizer, você não precisa incluir também o arquivo JAR do Snowpark como um valor de uma cláusula IMPORTS. Para obter mais informações sobre PACKAGES, consulte parâmetros opcionais CREATE FUNCTION.

O código no exemplo a seguir recebe dados de JSON armazenados como o tipo VARIANT, depois usa o tipo Variant na biblioteca do Snowpark para recuperar o valor price do JSON. O JSON recebido tem uma estrutura semelhante ao JSON exibido em Amostra de dados usados em exemplos.

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;
  }
}
$$;
Copy

Como ler um arquivo com uma UDF de Java

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. Para obter mais informações sobre o processamento de dados não estruturados, juntamente com o código de exemplo, consulte Processamento de dados não estruturados com manipuladores de procedimento e UDF.

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 cujo caminho de arquivo é especificado estaticamente na cláusula IMPORTS. No tempo de execução, seu código lê o arquivo do diretório base da UDF.

    Isso pode ser útil quando você deseja acessar o arquivo durante a inicialização.

  • 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:

    SnowflakeFile fornece recursos não disponíveis com InputStream, conforme descrito na tabela a seguir.

    Classe

    Entrada

    Notas

    SnowflakeFile

    Formatos de URL:

    • URL com escopo para reduzir o risco de ataques de injeção de arquivo quando o chamador da função não for também seu proprietário.

    • URL do arquivo ou caminho da cadeia de caracteres para arquivos que o proprietário da UDF tenha acesso.

    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.

    InputStream

    Formatos de URL:

    • URL com escopo para reduzir o risco de ataques de injeção de arquivo quando o chamador da função não for também seu proprietário.

    O arquivo deve estar localizado em um estágio interno ou externo.

Pré-requisitos

Antes que seu código de manipulador do Java possa ler um arquivo em um estágio, você deve fazer o seguinte para tornar o arquivo disponível para o código:

  1. Crie um estágio que esteja disponível para seu manipulador.

    Você pode usar um estágio externo ou interno. Se você usar um estágio interno, ele deve ser um usuário ou um estágio nomeado; atualmente, o Snowflake não oferece suporte ao uso de um estágio de tabela para dependências de UDF. Para saber mais sobre a criação de um estágio, consulte CREATE STAGE. Para saber mais sobre a escolha de um tipo de estágio interno, consulte Escolha de um estágio interno para os arquivos locais.

    Tenha em mente que privilégios adequados no estágio devem ser atribuídos às funções que executam ações SQL que leem do estágio. Para obter mais informações, consulte Granting Privileges for User-Defined Functions.

  2. Para o estágio, copie o arquivo que será lido por código.

    Você pode copiar o arquivo de um drive local para um estágio usando o comando PUT. Para obter uma referência do comando, consulte PUT. Para obter mais informações sobre a preparação de arquivos com PUT, consulte Preparação de arquivos de dados de um sistema de arquivo local.

Como ler um arquivo especificado estaticamente em IMPORTS

Seu manipulador pode ler um arquivo cujo caminho de estágio foi especificado na cláusula IMPORTS do comando CREATE FUNCTION.

Quando você especifica um arquivo na cláusula IMPORTS, o Snowflake copia esse arquivo do estágio para o diretório inicial do UDF (também chamado de diretório importar), que é o diretório do qual o UDF realmente lê o arquivo.

Como os arquivos importados são copiados para um único diretório e devem ter nomes únicos dentro desse diretório, cada arquivo na cláusula IMPORTS deve ter um nome distinto, mesmo que os arquivos comecem em estágios diferentes ou subdiretórios diferentes dentro de um estágio.

O exemplo seguinte cria e chama um UDF Java que lê um arquivo.

O código fonte Java abaixo cria um método Java chamado readFile. Este UDF usa este método.

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();
    }
}
Copy

O seguinte código SQL cria o UDF. Este código assume que o código fonte Java foi compilado e colocado em um arquivo JAR chamado TestReadRelativeFile.jar, que o UDF importa. O segundo e terceiro arquivos importados, my_config_file_1.txt e my_config_file_2.txt, são arquivos de configuração que o UDF pode ler.

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

Este código chama o UDF:

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

Escolha se deseja acessar um arquivo em formato comprimido ou não comprimido

Os arquivos em um estágio podem ser armazenados em formato comprimido ou não comprimido. Os usuários podem comprimir o arquivo antes de copiá-lo para o estágio, ou podem dizer ao comando PUT para comprimir o arquivo.

Quando o Snowflake copia um arquivo comprimido no formato GZIP de um estágio para o diretório incial do UDF, o Snowflake pode gravar a cópia como está, ou o Snowflake pode descompactar o conteúdo antes de gravar o arquivo.

Se o arquivo no estágio for comprimido, e se você quiser que a cópia no diretório inicial do UDF também seja compactada, então quando você especificar o nome do arquivo na cláusula IMPORTS, simplesmente use o nome do arquivo original (por exemplo “MyData.txt.gz”) na cláusula IMPORTS. Por exemplo:

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

Se o arquivo no estágio for comprimido com GZIP, e se você quiser que a cópia no diretório inicial do UDF seja descomprimida, então quando você especificar o nome do arquivo na cláusula IMPORTS, simplesmente omita a extensão “.gz”. Por exemplo, se seu estágio contiver “MyData.txt.gz”, mas você quiser que seu UDF leia o arquivo em formato não comprimido, então especifique “MyData.txt” na cláusula IMPORTS. Se ainda não houver um arquivo descomprimido chamado “MyData.txt”, então o Snowflake procurará por “MyData.txt.gz” e gravará automaticamente uma cópia descomprimida para “MyData.txt” no diretório inicial do UDF. Seu UDF pode então abrir e ler o arquivo não comprimido “MyData.txt”.

Note que a descompressão inteligente se aplica somente à cópia no diretório inicial do UDF; o arquivo original no estágio não é alterado.

Siga estas práticas recomendadas para o tratamento de arquivos compactados:

  • Siga as convenções de nomenclatura de arquivos adequadas. Se um arquivo estiver no formato comprimido com GZIP, então inclua a extensão “.gz” no fim do nome do arquivo. Se um arquivo não estiver no formato comprimido com GZIP, então não termine o nome do arquivo com a extensão “.gz”.

  • Evite criar arquivos cujos nomes diferem apenas pela extensão “.gz”. Por exemplo, não crie “MyData.txt” e “MyData.txt.gz” no mesmo estágio e diretório, e não tente importar “MyData.txt” e “MyData.txt.gz” no mesmo comando CREATE FUNCTION.

  • Não comprima os arquivos duas vezes. Por exemplo, se você comprimir um arquivo manualmente, e depois PUT esse arquivo sem usar AUTO_COMPRESS=FALSE, o arquivo será comprimido uma segunda vez. A descompressão inteligente descompactará apenas uma vez, portanto o arquivo de dados (ou JAR) ainda será comprimido quando for armazenado no diretório inicial do UDF.

  • No futuro, a Snowflake poderá estender a descompressão inteligente a outros algoritmos de compressão além de GZIP. Para evitar problemas de compatibilidade no futuro, aplique estas práticas recomendadas aos arquivos que utilizam qualquer tipo de compressão.

Nota

Arquivos JAR também podem ser armazenados em formato comprimido ou não comprimido em um estágio. Snowflake descomprime automaticamente todos os arquivos comprimidos JAR antes de torná-los disponíveis para o UDF Java.

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 em Java. A classe SnowflakeFile está incluída no classpath disponível para os manipuladores de UDF de Java 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 ao carregamento de 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 Java. 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.

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(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;
  }
}
$$;
Copy

Chame a UDF, passando a localização do arquivo em uma URL com escopo para reduzir a probabilidade de ataques de injeção de arquivo.

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

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.

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

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 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 ao carregamento de dados não estruturados.

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(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;
  }
}
$$;
Copy

Chame a UDF, passando a localização do arquivo em uma URL com escopo para reduzir a probabilidade de ataques de injeção de arquivo.

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

Como criar e chamar uma UDF de Java preparada simples

As instruções a seguir criam uma UDF de Java simples. De forma geral, essa amostra segue a estrutura de arquivo e diretório descrita em Organização de seus arquivos.

Crie e compile o código do manipulador em Java

  1. No diretório raiz de seu projeto (aqui, my_udf), crie um subdiretório src para manter os arquivos .java de origem e um subdiretório classes para manter os arquivos .class gerados.

    Você deve ter uma hierarquia de diretórios semelhante à seguinte:

    my_udf/
    |-- classes/
    |-- src/
    
    Copy
  2. No diretório src, crie um diretório chamado mypackage para armazenar arquivos .java cujas classes estão no pacote mypackage.

  3. No diretório mypackage, crie um arquivo MyUDFHandler.java que contenha seu código fonte.

    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.");
      }
    }
    
    Copy
  4. A partir do diretório raiz de seu projeto (aqui, my_udf), use o comando javac para compilar o código fonte.

    O comando javac no seguinte exemplo compila MyUDFHandler.java para gerar um arquivo MyUDFHandler.class no diretório classes.

    javac -d classes src/mypackage/MyUDFHandler.java
    
    Copy

    Este exemplo inclui os seguintes argumentos:

    • -d classes – Diretório no qual os arquivos de classe gerados devem ser gravados.

    • src/mypackage/MyUDFHandler.java – Caminho para o arquivo .java na forma: source_directory/package_directory/Java_file_name.

Empacote o código compilado em um arquivo JAR.

  1. Opcionalmente, no diretório raiz do projeto, crie um arquivo do manifesto chamado my_udf.manifest que contenha os seguintes atributos:

    Manifest-Version: 1.0
    Main-Class: mypackage.MyUDFHandler
    
    Copy
  2. A partir do diretório raiz de seu projeto, execute o comando jar para criar um arquivo JAR contendo o arquivo .class e do manifesto.

    O comando jar no exemplo a seguir coloca o arquivo MyUDFHandler.class gerado em uma pasta de pacotes mypackage em um arquivo .jar chamado my_udf.jar. O sinalizador -C ./classes especifica a localização dos arquivos .class.

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

    Este exemplo inclui os seguintes argumentos:

    • cmf – Argumentos de comando: c para criar um arquivo JAR, m para usar o arquivo .manifest especificado, e f para dar ao arquivo JAR o nome especificado.

    • my_udf.manifest – Arquivo do manifesto.

    • my_udf.jar – Nome do arquivo JAR a ser criado.

    • -C ./classes – Diretório contendo os arquivos .class gerados.

    • mypackage/MyUDFHandler.class – Pacote e nome do arquivo .class a ser incluído no arquivo JAR.

Faça o upload do arquivo JAR com o manipulador compilado para um estágio

  1. No Snowflake, crie um estágio chamado jar_stage para armazenar o arquivo JAR contendo seu manipulador de UDF.

    Para obter mais informações sobre a criação de um estágio, consulte CREATE STAGE.

  2. Use o comando PUT para copiar o arquivo JAR do sistema de arquivo local para um estágio.

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

Você pode armazenar o comando PUT em um arquivo de script e depois executar esse arquivo através do SnowSQL.

O comando snowsql se assemelha ao seguinte:

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

Este exemplo assume que a senha do usuário é especificada na variável de ambiente SNOWSQL_PWD.

Criar a UDF com o código compilado como manipulador

Crie a UDF:

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

Chame a UDF:

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