Écriture d’une UDF scalaire en Scala

Vous pouvez écrire une fonction scalaire définie par l’utilisateur (UDF) en Scala. Ce code de gestionnaire Scala s’exécute lorsque l’UDF est appelée. Cette rubrique décrit comment écrire un gestionnaire en Scala et créer l’UDF.

Une UDF est une fonction définie par l’utilisateur qui renvoie des résultats scalaires, c’est-à-dire une seule valeur plutôt que plusieurs lignes. Pour plus d’informations générales sur les UDFs, voir Vue d’ensemble des fonctions définies par l’utilisateur.

Lorsque vous créez une UDF, vous effectuez les opérations suivantes :

  1. Écrire un objet ou une classe Scala avec une méthode que Snowflake invoquera lorsque l’UDF est appelé.

    Pour plus d’informations, voir Implémentation d’un gestionnaire dans cette rubrique.

  2. Créez l’UDF en SQL avec la commande CREATE FUNCTION, en spécifiant votre objet ou classe et votre méthode comme gestionnaire. Quand vous créez l’UDF, vous spécifiez :

    • Les types de données des paramètres d’entrée de l’UDF.

    • Le type de données de la valeur renvoyée de l’UDF.

    • Le code à exécuter en tant que gestionnaire lorsque l’UDF est appelée.

    • Le langage dans lequel le gestionnaire est implémenté.

    Pour en savoir plus sur la syntaxe de CREATE FUNCTION, voir Création de l’UDF avec CREATE FUNCTION.

Vous pouvez appeler une UDF comme décrit dans Appel d’une UDF.

Implémentation d’un gestionnaire

Vous mettez en œuvre un objet ou une classe avec une méthode de gestionnaire pour traiter les valeurs d’arguments UDF dans la valeur de retour de l’UDF.

Lorsque vous écrivez un gestionnaire, vous devez :

  • Écrire une classe publique avec une méthode publique à spécifier comme gestionnaire.

    Ce sera la méthode que Snowflake invoquera lorsque l’UDF sera appelée dans SQL.

    Vous pouvez définir plusieurs autres méthodes dans le même objet ou la même classe, puis utiliser chacune d’entre elles comme gestionnaire d’une UDF différente. Par exemple, vous pourriez vouloir procéder ainsi lorsque vous avez l’intention de conserver le code compilé du gestionnaire sur une zone de préparation et d’y faire référence à partir de plusieurs fonctions.

    Pour plus d’informations sur le gestionnaire mis en zone de préparation, voir Conserver le code du gestionnaire en ligne ou dans une zone de préparation.

  • Il est possible d’écrire un constructeur à zéro argument pour que Snowflake l’invoque afin d’initialiser le gestionnaire.

Note

Veillez à écrire votre gestionnaire en respectant les contraintes imposées par Snowflake dans chaque méthode du gestionnaire et dans les méthodes qu’il appelle. Pour en savoir plus sur ces contraintes, voir Concevoir des gestionnaires qui respectent les contraintes imposées par Snowflake.

Exemple de gestionnaire

Le code de l’exemple suivant comprend une méthode de gestionnaire MyHandler.echoVarchar qui reçoit et renvoie une chaîne. La valeur reçue par l’UDF – une VARCHAR – est mise en correspondance par Snowflake avec le type de paramètre de la méthode du gestionnaire, une chaîne de caractères.

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

Appelez l’UDF

SELECT echo_varchar('Hello');
Copy

Initialisation du gestionnaire

Vous pouvez éventuellement initialiser votre gestionnaire en ajoutant un constructeur à zéro argument.

Si le constructeur génère une erreur, celle-ci est signalée comme une erreur de l’utilisateur, avec le message d’exception.

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

Traitement des arguments de la fonction

Pour traiter les données transmises à l’UDF en tant qu’arguments, implémentez une méthode publique que Snowflake invoquera lorsque l’UDF sera appelée dans le code SQL. Lorsque vous créez l’UDF avec une commande CREATE FUNCTION, vous utiliserez la clause HANDLER pour spécifier la méthode en tant que gestionnaire.

Lors de la déclaration d’une méthode de gestionnaire, vous :

  • Déclarez la méthode de gestionnaire comme publique.

    Vous pouvez éventuellement inclure un constructeur à zéro argument pour initialiser le gestionnaire. Pour plus d’informations, reportez-vous à Initialisation du gestionnaire dans cette rubrique.

    Si, au contraire, vous avez l’intention d’empaqueter la classe dans un JAR en tant que gestionnaire en zone de préparation, vous pouvez déclarer plusieurs méthodes de gestionnaire, puis spécifier chacune d’elles en tant que gestionnaire avec la clause HANDLER d’une instruction CREATE FUNCTION. Pour plus d’informations sur le gestionnaire mis en zone de préparation, voir Conserver le code du gestionnaire en ligne ou dans une zone de préparation.

  • Spécifiez les types de paramètres et de retour de la méthode du gestionnaire qui correspondent aux types SQL spécifiés par la déclaration de l’UDF.

    Pour plus d’informations, reportez-vous à Mappages de type de données SQL-Scala.

  • Il est possible de déclarer des méthodes supplémentaires pour soutenir le traitement de la méthode du gestionnaire, telles que des méthodes à appeler à partir de la méthode de gestionnaire.

    Le code de l’exemple suivant comporte une méthode de gestionnaire handleStrings qui appelle une méthode de non gestionnaire concatenate pour aider à traiter le tableau reçu en argument.

    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

    Le texte suivant appelle la fonction generate_greeting.

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

    Le tableau suivant illustre le résultat de l’appel de generate_greeting avec les valeurs ci-dessus.

    Hello world
    

Surcharge des méthodes des gestionnaires

Vous pouvez surcharger les méthodes de gestionnaire dans la même classe ou le même objet, à condition qu’elles aient un nombre différent de paramètres.

Pour les UDFs Scala, Snowflake utilise uniquement le nombre d’arguments de la méthode, et non leurs types, pour différencier les méthodes de gestionnaire. La résolution basée sur les types de données n’est pas pratique, car certains types de données SQL peuvent être mappés à plus d’un type de données Java ou Scala et donc potentiellement à plusieurs signatures de méthode de gestionnaire.

Par exemple, si deux méthodes Scala ont le même nom et le même nombre d’arguments, mais des types de données différents, l’appel d’une UDF utilisant l’une de ces méthodes comme gestionnaire génère une erreur similaire à la suivante :

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

Si un entrepôt est disponible, l’erreur est détectée au moment où l’UDF est créée. Sinon, l’erreur se produit lors de l’appel de l’UDF.