Exemples de gestionnaires d’UDF Java

Cette rubrique comprend des exemples simples de code de gestionnaire d’UDF écrits en Java.

Pour en savoir plus sur l’utilisation de Java pour créer un gestionnaire d’UDF, voir Création d’un gestionnaire d’UDF Java.

Dans ce chapitre :

Création et appel d’une simple UDF Java en ligne

Les instructions suivantes créent et appellent une UDF Java en ligne. Ce code renvoie le VARCHAR qui lui est transmis.

Cette fonction est déclarée avec la clause facultative CALLED ON NULL INPUT pour indiquer que la fonction est appelée même si la valeur de l’entrée est NULL. (Cette fonction renverrait NULL avec ou sans cette clause, mais vous pourriez modifier le code pour traiter NULL d’une autre manière, par exemple, pour renvoyer une chaîne vide).

Créez l’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

Appelez l’UDF :

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

Transmission d’un NULL à une UDF Java en ligne

Ceci utilise les echo_varchar() UDF définies ci-dessus. La valeur SQL NULL est implicitement convertie en null Java, et cette null Java est retournée et implicitement reconvertie au format SQL NULL :

Appelez l’UDF :

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

Transmission de valeurs de tableaux

Les méthodes Java peuvent recevoir ces tableaux SQL de deux manières différentes :

  • En utilisant la fonctionnalité de tableau Java.

  • En utilisant la fonctionnalité varargs (nombre variable d’arguments) de Java.

Dans les deux cas, votre code SQL doit transmettre un ARRAY.

Note

Veillez à utiliser des types Java avec des mappages valides avec les types SQL. Pour plus d’informations, reportez-vous à Mappages de type de données SQL-Java.

Transmission par un ARRAY

Déclarez le paramètre Java comme un tableau. Par exemple, le troisième paramètre de la méthode suivante est un tableau Chaîne :

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

Vous trouverez ci-dessous un exemple complet :

Créer et charger la table :

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

Créez l’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

Appelez l’UDF :

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

Transmission par des Varargs

L’utilisation de varargs est très similaire à celle d’un tableau.

Dans votre code Java, utilisez le style de déclaration varargs de Java :

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

Vous trouverez ci-dessous un exemple complet. La seule différence significative entre cet exemple et l’exemple précédent (pour les tableaux) est la déclaration des paramètres de la méthode.

Créer et charger la table :

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

Créez l’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

Appelez l’UDF :

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

Renvoyer NULL explicitement à partir d’une UDF en ligne

Le code suivant montre comment retourner une valeur NULL de manière explicite. La valeur Java null est convertie au format SQL NULL.

Créez l’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

Appelez l’UDF :

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

Transmission d’un OBJECT à une UDF Java en ligne

L’exemple suivant utilise le type de données SQL OBJECT et le type de données Java correspondant (Map<chaîne, chaîne>), et extrait une valeur à partir de OBJECT. Cet exemple montre également que vous pouvez transmettre plusieurs paramètres à une UDF Java.

Créer et charger une table qui contient une colonne de type OBJECT :

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

Créez l’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

Appelez l’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

Transmission d’une valeur GEOGRAPHY à une UDF Java en ligne

L’exemple suivant utilise le type de données SQL GEOGRAPHY.

Créez l’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

Vous pouvez utiliser la clause PACKAGES pour spécifier un paquet système Snowflake, comme le package Snowpark. Lorsque vous le faites, il n’est pas nécessaire d’inclure également le fichier JAR de Snowpark comme valeur d’une clause IMPORTS. Pour en savoir plus sur PACKAGES, voir Paramètres facultatifs CREATE FUNCTION.

Créer des données et appeler l’UDF avec ces données :

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

La sortie devrait ressembler à :

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

Transmission d’une valeur VARIANT à une UDF Java en ligne

Lorsque vous passez une valeur de type SQL VARIANT à une UDF Java, Snowflake peut convertir la valeur en type Variant fourni avec le paquet Snowpark. Notez que Variant est pris en charge à partir de la version 1.4.0 du paquet Snowpark et des versions ultérieures.

Le type Variant de Snowpark fournit des méthodes pour convertir des valeurs entre Variant et d’autres types.

Pour utiliser le type Variant Snowpark, utilisez la clause PACKAGES pour spécifier le paquet Snowpark lors de la création de l’UDF. Lorsque vous le faites, il n’est pas nécessaire d’inclure également le fichier JAR de Snowpark comme valeur d’une clause IMPORTS. Pour en savoir plus sur les PACKAGES, voir Paramètres facultatifs CREATE FUNCTION.

Le code de l’exemple suivant reçoit des données JSON stockées sous le type VARIANT, puis utilise le type Variant de la bibliothèque Snowpark pour extraire la valeur price du JSON. Le JSON reçu a une structure similaire au JSON affiché dans Échantillon de données utilisé dans des exemples.

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

Lecture d’un fichier à l’aide d’une UDF Java

Vous pouvez lire le contenu d’un fichier avec le code du gestionnaire. Par exemple, vous pourriez vouloir lire un fichier pour traiter des données non structurées avec le gestionnaire. Pour de plus amples informations sur le traitement des données non structurées, ainsi que des exemples de code, reportez-vous à Traitement des données non structurées avec des gestionnaires d’UDF et de procédures.

Le fichier doit se trouver sur une zone de préparation Snowflake qui est disponible pour votre gestionnaire.

Pour lire le contenu des fichiers en zone de préparation, votre gestionnaire peut :

  • Lire un fichier dont le chemin est spécifié de façon statique dans la clause IMPORTS. Au moment de l’exécution, votre code lit le fichier dans le répertoire personnel de l’UDF.

    Cela peut être utile lorsque vous souhaitez accéder au fichier pendant l’initialisation.

  • Lisez un fichier spécifié de façon dynamique en appelant les méthodes de la classe SnowflakeFile ou de la classe InputStream.

    Vous pouvez procéder ainsi si vous devez accéder à un fichier spécifié par l’appelant. Pour plus d’informations, voir les sections suivantes de ce chapitre :

    SnowflakeFile offre des fonctionnalités non disponibles avec InputStream, comme décrit dans le tableau suivant.

    Classe

    Entrée

    Remarques

    SnowflakeFile

    Formats d’URL :

    • URL scopée qui réduit le risque d’attaques par injection de fichiers lorsque l’appelant de la fonction n’est pas également son propriétaire.

    • URL de fichier ou chemin de chaîne pour les fichiers auxquels le propriétaire de l’UDF a accès.

    Le fichier doit être situé dans une zone de préparation interne ou externe nommée.

    Accédez facilement à des attributs de fichier supplémentaires, tels que la taille du fichier.

    InputStream

    Formats d’URL :

    • URL scopée qui réduit le risque d’attaques par injection de fichiers lorsque l’appelant de la fonction n’est pas également son propriétaire.

    Le fichier doit être situé dans une zone de préparation interne ou externe.

Conditions préalables

Avant que votre code de gestionnaire Java puisse lire un fichier sur une zone de préparation, vous devez effectuer les opérations suivantes pour mettre le fichier à la disposition du code :

  1. Créez une zone de préparation qui est disponible pour votre gestionnaire.

    Vous pouvez utiliser une zone de préparation externe ou une zone de préparation interne. Si vous utilisez une zone de préparation interne, il doit s’agir d’une zone de préparation d’utilisateur ou nommée ; Snowflake ne prend actuellement pas en charge l’utilisation d’une zone de préparation de table pour des dépendances d’UDF. Pour en savoir plus sur la création d’une zone de préparation, voir CREATE STAGE. Pour en savoir plus sur le choix d’un type de zone de préparation interne, voir Choix d’une zone de préparation interne pour les fichiers locaux.

    Gardez à l’esprit que des privilèges adéquats sur la zone de préparation doivent être attribués aux rôles effectuant des actions SQL qui effectuent des lectures à partir de la zone de préparation. Pour plus d’informations, voir Granting Privileges for User-Defined Functions.

  2. Sur la zone de préparation, copiez le fichier qui sera lu par le code.

    Vous pouvez copier le fichier d’un lecteur local vers une zone de préparation en utilisant la commande PUT. Pour la référence de la commande, voir PUT. Pour des informations sur la mise en zone de préparation de fichiers avec PUT, voir Mise en zone de préparation des fichiers de données à partir d’un système de fichiers local.

Lecture d’un fichier spécifié statiquement dans IMPORTS

Votre gestionnaire peut lire un fichier dont le chemin de zone de préparation a été spécifié dans la clause IMPORTS de la commande CREATE FUNCTION.

Lorsque vous spécifiez un fichier dans la clause IMPORTS, Snowflake copie ce fichier de la zone de préparation vers le répertoire personnel (également appelé répertoire d’importation) de l’UDF, qui est le répertoire à partir duquel l’UDF lit réellement le fichier.

Étant donné que les fichiers importés sont copiés dans un seul répertoire et qu’ils doivent avoir des noms uniques au sein de ce répertoire, chaque fichier de la clause IMPORTS doit avoir un nom distinct, même si les fichiers commencent dans différentes zones de préparation ou différents sous-répertoires au sein d’une zone de préparation.

L’exemple suivant crée et appelle une UDF Java qui lit un fichier :

Le code source Java ci-dessous crée une méthode Java nommée readFile. Cette UDF utilise cette méthode.

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

Le code SQL suivant crée l” UDF. Ce code suppose que le code source Java a été compilé et placé dans un fichier JAR nommé TestReadRelativeFile.jar, que l’UDF importe. Les deuxième et troisième fichiers importés, my_config_file_1.txt et my_config_file_2.txt, sont des fichiers de configuration que l’UDF peut lire.

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

Ce code appelle l’UDF :

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

Choisir d’accéder à un fichier dans un format compressé ou non compressé

Les fichiers d’une zone de préparation peuvent être stockés en format compressé ou non compressé. Les utilisateurs peuvent compresser le fichier avant de le copier dans la zone de préparation, ou demander à la commande PUT de compresser le fichier.

Lorsque Snowflake copie un fichier compressé au format GZIP d’une zone de préparation vers le répertoire d’accueil de l’UDF, Snowflake peut écrire la copie telle quelle, ou Snowflake peut décompresser le contenu avant d’écrire le fichier.

Si le fichier de la zone de préparation est compressé et si vous souhaitez que la copie dans le répertoire d’accueil de l’UDF soit également compressée, lorsque vous spécifiez le nom du fichier dans la clause IMPORTS, utilisez simplement le nom du fichier original (par ex. « MyData.txt.gz ») dans la clause IMPORTS. Par exemple :

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

Si le fichier de la zone de préparation est compressé au format GZIP et si vous souhaitez que la copie dans le répertoire d’accueil de l’UDF soit également compressée, lorsque vous spécifiez le nom du fichier dans la clause IMPORTS, retirez l’extension « .gz ». Par exemple, si votre zone de préparation contient « MyData.txt.gz », mais que vous voulez que votre UDF lise le fichier au format non compressé, spécifiez « MyData.txt » dans la clause IMPORTS. S’il n’existe pas déjà un fichier non compressé nommé « MyData.txt », alors Snowflake recherche « MyData.txt.gz » et écrit automatiquement une copie décompressée dans « MyData.txt » dans le répertoire personnel de l’UDF. Votre UDF peut alors ouvrir et lire le fichier non compressé « MyData.txt ».

Notez que la décompression intelligente ne s’applique qu’à la copie dans le répertoire d’accueil de l’UDF ; le fichier original dans la zone de préparation n’est pas modifié.

Suivez ces meilleures pratiques pour la manipulation des fichiers compressés :

  • Respectez les conventions de dénomination des fichiers. Si un fichier est au format compressé GZIP, incluez l’extension « .gz » à la fin du nom du fichier. Si un fichier n’est pas au format compressé GZIP, il ne faut pas terminer le nom du fichier par l’extension « .gz ».

  • Évitez de créer des fichiers dont les noms ne diffèrent que par l’extension « .gz ». Par exemple, ne créez pas à la fois « MyData.txt » et « MyData.txt.gz » dans la même zone de préparation et le même répertoire, et n’essayez pas d’importer à la fois « MyData.txt » et « MyData.txt.gz » dans la même commande CREATE FUNCTION.

  • Ne compressez pas les fichiers deux fois. Par exemple, si vous compressez un fichier manuellement, puis vous placez (PUT) ce fichier sans utiliser AUTO_COMPRESS=FALSE, le fichier sera compressé une seconde fois. La décompression intelligente ne le décompressera qu’une seule fois, de sorte que le fichier de données (ou JAR) sera toujours compressé lorsqu’il sera stocké dans le répertoire d’accueil de l’UDF.

  • À l’avenir, Snowflake pourrait étendre la décompression intelligente à des algorithmes de compression autres que GZIP. Pour éviter les problèmes de compatibilité à l’avenir, appliquez ces meilleures pratiques aux fichiers qui utilisent tout type de compression.

Note

Les fichiers JAR peuvent également être stockés en format compressé ou non compressé dans une zone de préparation. Snowflake décompresse automatiquement tous les fichiers JAR compressés avant de les mettre à la disposition de l’UDF Java.

Lecture d’un fichier spécifié de façon dynamique avec SnowflakeFile

En utilisant les méthodes de la classe SnowflakeFile, vous pouvez lire les fichiers d’une zone de préparation avec votre code de gestionnaire Java. La classe SnowflakeFile est incluse dans le classpath accessible aux gestionnaires d’UDF Java sur Snowflake.

Note

Pour rendre votre code résistant aux attaques par injection de fichiers, utilisez toujours une URL scopée lorsque vous transmettez l’emplacement d’un fichier à une UDF, en particulier lorsque l’appelant de la fonction n’est pas également son propriétaire. Vous pouvez créer une URL scopée en SQL à l’aide de la fonction intégrée BUILD_SCOPED_FILE_URL. Pour plus d’informations sur le rôle de BUILD_SCOPED_FILE_URL, voir Introduction au chargement de données non structurées.

Pour développer le code de votre UDF localement, ajoutez le fichier JAR Snowpark contenant SnowflakeFile au chemin de classe de votre code. Pour plus d’informations sur snowpark.jar, voir Configuration de votre environnement de développement pour Snowpark Java. Notez que les applications clientes Snowpark ne peuvent pas utiliser cette classe.

Lorsque vous utilisez SnowflakeFile, il n’est pas nécessaire de spécifier également le fichier en zone de préparation ou le fichier JAR contenant SnowflakeFile avec une clause IMPORTS lorsque vous créez l’UDF, comme dans SQL avec une instruction CREATE FUNCTION.

Le code de l’exemple suivant utilise SnowflakeFile pour lire un fichier à partir d’un emplacement de zone de préparation spécifié. En utilisant un InputStream à partir de la méthode getInputStream, il lit le contenu du fichier dans une variable 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

Appelez l’UDF, en transmettant l’emplacement du fichier dans une URL scopée pour réduire la probabilité d’attaques par injection de fichier.

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

Note

Le propriétaire de l’UDF doit avoir accès à tous les fichiers dont les emplacements ne sont pas des URLs scopées. Vous pouvez lire ces fichiers en zone de préparation en demandant au code du gestionnaire d’appeler la méthode SnowflakeFile.newInstance avec une valeur boolean pour un nouveau paramètre requireScopedUrl.

L’exemple suivant utilise SnowflakeFile.newInstance tout en spécifiant qu’une URL scopée n’est pas nécessaire.

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

Lecture d’un fichier spécifié de façon dynamique avec InputStream

Vous pouvez lire le contenu du fichier directement dans un java.io.InputStream en faisant de l’argument de votre fonction de traitement une variable InputStream. Cela peut être utile lorsque l’appelant de la fonction veut passer un chemin de fichier comme argument.

Note

Pour rendre votre code résistant aux attaques par injection de fichiers, utilisez toujours une URL scopée lorsque vous transmettez l’emplacement d’un fichier à une UDF, en particulier lorsque l’appelant de la fonction n’est pas également son propriétaire. Vous pouvez créer une URL scopée en SQL à l’aide de la fonction intégrée BUILD_SCOPED_FILE_URL. Pour plus d’informations sur le rôle de BUILD_SCOPED_FILE_URL, voir Introduction au chargement de données non structurées.

Le code de l’exemple suivant comporte une fonction de gestionnaire sumTotalSales qui prend un InputStream et renvoie un int. Au moment de l’exécution, Snowflake attribue automatiquement le contenu du fichier au chemin d’accès de la variable file à la variable de l’argument 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

Appelez l’UDF, en transmettant l’emplacement du fichier dans une URL scopée pour réduire la probabilité d’attaques par injection de fichier.

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

Création et appel d’une UDF Java simple et mise en zone de préparation

Les instructions suivantes créent une simple UDF Java. Cet échantillon suit généralement la structure des fichiers et des répertoires décrite dans Organiser vos fichiers.

Créer et compiler le code du gestionnaire Java

  1. Dans le répertoire racine de votre projet (ici, my_udf), créez un sous-répertoire src pour contenir les fichiers .java source et un sous-répertoire classes pour contenir les fichiers .class générés.

    Vous devriez avoir une hiérarchie de répertoires semblable à la suivante :

    my_udf/
    |-- classes/
    |-- src/
    
    Copy
  2. Dans le répertoire src , créez un répertoire appelé mypackage pour contenir les fichiers .java dont les classes sont dans le package mypackage.

  3. Dans le répertoire mypackage , créez un fichier MyUDFHandler.java qui contient votre code source.

    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. Depuis le répertoire racine de votre projet (ici, my_udf), utilisez la commande javac pour compiler le code source.

    La commande javac de l’exemple suivant compile MyUDFHandler.java pour générer un fichier MyUDFHandler.class dans le répertoire classes.

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

    Cet exemple comprend les arguments suivants :

    • -d classes – Répertoire dans lequel les fichiers de classe générés doivent être écrits.

    • src/mypackage/MyUDFHandler.java – Chemin du fichier .java sous la forme : source_directory/package_directory/Java_file_name.

Empaqueter le code compilé dans un fichier JAR

  1. En option, dans le répertoire racine du projet, créez un fichier manifeste nommé my_udf.manifest qui contient les attributs suivants :

    Manifest-Version: 1.0
    Main-Class: mypackage.MyUDFHandler
    
    Copy
  2. À partir du répertoire racine de votre projet, exécutez la commande jar pour créer un fichier JAR contenant le fichier .class et le manifeste.

    La commande jar de l’exemple suivant place le fichier MyUDFHandler.class généré dans un dossier de package mypackage dans un fichier .jar appelé my_udf.jar. L’indicateur -C ./classes spécifie l’emplacement des fichiers .class.

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

    Cet exemple comprend les arguments suivants :

    • cmf – Arguments de la commande : c pour créer un fichier JAR, m pour utiliser le fichier .manifest spécifié, et f pour donner au fichier JAR le nom spécifié.

    • my_udf.manifest – Fichier manifeste.

    • my_udf.jar – Nom du fichier JAR à créer.

    • -C ./classes – Répertoire contenant les fichiers .class générés.

    • mypackage/MyUDFHandler.class – Package et nom du fichier .class à inclure dans le JAR.

Charger le fichier JAR avec le gestionnaire compilé vers une zone de préparation

  1. Dans Snowflake, créez une zone de préparation appelée jar_stage pour stocker le fichier JAR contenant votre gestionnaire UDF.

    Pour plus d’informations sur la création d’une zone de préparation, voir CREATE STAGE.

  2. Utilisez la commande PUT pour copier le fichier JAR du système de fichiers local vers une zone de préparation.

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

Vous pouvez stocker la commande PUT dans un fichier de script, puis exécuter ce fichier via SnowSQL.

La commande snowsql doit être similaire à ce qui suit :

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

Cet exemple suppose que le mot de passe de l’utilisateur est spécifié dans la variable d’environnement SNOWSQL_PWD.

Créer l’UDF avec le code compilé comme gestionnaire

Créez l’UDF :

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

Appelez l’UDF :

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