Como escrever uma UDF escalar em Scala

É possível escrever uma função escalar definida pelo usuário (UDF) em Scala. Esse código do manipulador Scala é executado quando a UDF é chamada. Este tópico descreve como escrever um manipulador em Scala e criar a UDF.

Uma UDF é uma função definida pelo usuário que retorna resultados escalares, ou seja, um único valor em vez de várias linhas. Para obter informações gerais sobre UDFs, consulte Visão geral das funções definidas pelo usuário.

Ao criar uma UDF, faça o seguinte:

  1. Escreva um objeto ou classe Scala com um método que o Snowflake invocará quando a UDF for chamada.

    Para obter mais informações, consulte Como implementar um manipulador neste tópico.

  2. Crie a UDF em SQL com o comando CREATE FUNCTION, especificando seu objeto ou classe e método como o manipulador. Quando você cria a UDF, você especifica:

    • Tipos de dados de parâmetros de entrada da UDF.

    • O tipo de dados do valor de retorno da UDF.

    • Código a ser executado como manipulador quando a UDF é chamada.

    • A linguagem na qual o manipulador é escrito.

    Para saber mais sobre a sintaxe CREATE FUNCTION, consulte Como criar a UDF com CREATE FUNCTION.

Você pode chamar uma UDF como descrito em Como chamar uma UDF.

Como implementar um manipulador

Você implementa um objeto ou classe com um método do manipulador para processar os valores de argumento da UDF no valor de retorno da UDF.

Ao escrever um manipulador, você:

  • Escreva uma classe pública com um método público para especificar como o manipulador.

    Esse será o método que o Snowflake invocará quando a UDF for chamada em SQL.

    Você pode definir vários outros métodos no mesmo objeto ou classe e, em seguida, usar cada um deles como manipulador de uma UDF diferente. Por exemplo, talvez você queira fazer isso quando pretende manter o código do manipulador compilado em um estágio e fazer referência a ele em várias funções.

    Para obter mais informações sobre um manipulador em estágio, consulte Como manter o código do manipulador inline ou em um estágio.

  • Opcionalmente, escreva um construtor sem argumentos para o Snowflake invocar para inicializar o manipulador.

Nota

Certifique-se de escrever seu manipulador de acordo com as restrições impostas pelo Snowflake em cada método do manipulador e nos métodos que ele chama. Para saber mais sobre estas restrições, consulte Criação de manipuladores que ficam dentro das restrições impostas pelo Snowflake.

Exemplo do manipulador

O código no exemplo a seguir inclui um método do manipulador MyHandler.echoVarchar que recebe e retorna uma cadeia de caracteres. O valor recebido pela UDF – um VARCHAR – é mapeado pelo Snowflake para o tipo de parâmetro do método do manipulador – uma cadeia de caracteres.

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

Chamada da UDF

SELECT echo_varchar('Hello');
Copy

Como inicializar o manipulador

Opcionalmente, você pode inicializar seu manipulador adicionando um construtor sem argumentos.

Se o construtor lança um erro, o erro é lançado como um erro do usuário, juntamente com a mensagem de exceção.

def this() = {
    // Initialize here.
}
Copy

Processamento de argumentos de função

Para processar os dados passados para a UDF como argumentos, implemente um método público que o Snowflake invocará quando a UDF for chamada no código SQL. Ao criar a UDF com um comando CREATE FUNCTION, você usará a cláusula HANDLER para especificar o método como manipulador.

Ao declarar um método do manipulador, você:

  • Declara o método do manipulador como público.

    Opcionalmente, você pode incluir um construtor sem argumentos para inicializar o manipulador. Para obter mais informações, consulte Como inicializar o manipulador neste tópico.

    Se você pretende inserir a classe em um JAR como um manipulador em estágio, você pode declarar vários métodos do manipulador, especificando mais tarde cada um como manipulador com a cláusula HANDLER de uma instrução CREATE FUNCTION. Para obter mais informações sobre um manipulador em estágio, consulte Como manter o código do manipulador inline ou em um estágio.

  • Especifique o parâmetro do método do manipulador e os tipos de retorno que mapeiam para os tipos SQL especificados pela declaração da UDF.

    Para obter mais informações, consulte Mapeamentos de tipos de dados SQL-Scala.

  • Opcionalmente, declare métodos adicionais para dar suporte ao processamento do método do manipulador, como métodos a serem chamados a partir do método do manipulador.

    O código no exemplo a seguir apresenta um método do manipulador handleStrings que chama um método não manipulador concatenate para ajudar a processar a matriz recebida como argumento.

    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

    O seguinte chama a função generate_greeting.

    SELECT generate_greeting(['Hello', 'world']);
    
    Copy

    O exemplo a seguir ilustra o resultado da chamada de generate_greeting com os valores acima.

    Hello world
    

Sobrecarga de métodos do manipulador

Você pode sobrecarregar os métodos do manipulador na mesma classe ou objeto, desde que eles tenham números diferentes de parâmetros.

Para UDFs de Scala, o Snowflake usa apenas o número de argumentos do método, e não seus tipos, para diferenciar os métodos do manipulador. A resolução baseada em tipos de dados não é prática, porque alguns tipos de dados de SQL podem ser mapeados para mais de um tipo de dados de Scala ou Java e, portanto, potencialmente para mais de uma assinatura do método do manipulador.

Por exemplo, se dois métodos de Scala tiverem o mesmo nome e o mesmo número de argumentos, mas tipos de dados diferentes, então chamar uma UDF usando um desses métodos como manipulador gerará um erro semelhante ao seguinte:

Cannot determine which implementation of handler "handler name" to invoke since there are multiple
definitions with <number of args> arguments in function <user defined function name> with
handler <class name>.<handler name>
Copy

Se um warehouse estiver disponível, o erro será detectado no momento em que a UDF for criada. Caso contrário, o erro ocorre quando a UDF é chamada.