Beispiele für Java-UDF-Handler¶
Unter diesem Thema werden einfache Beispiele für in Java geschriebenen UDF-Handler-Code bereitgestellt.
Weitere Informationen zur Verwendung von Java beim Erstellen eines UDF-Handlers finden Sie unter Erstellen eines Java-UDF-Handlers.
Unter diesem Thema:
Erstellen und Aufrufen einer einfachen Inline-Java-UDF¶
Mit den folgenden Anweisungen wird eine Inline-Java-UDF erstellt und aufgerufen. Dieser Code gibt den VARCHAR-Wert zurück, der übergeben wurde.
Diese Funktion wird mit der optionalen CALLED ON NULL INPUT
-Klausel deklariert, um anzugeben, dass die Funktion auch dann aufgerufen wird, wenn der Eingabewert NULL ist. (Diese Funktion würde mit und ohne diese Klausel NULL zurückgeben. Sie könnten den Code aber ändern, um NULL anders zu behandeln, z. B. um eine leere Zeichenfolge zurückzugeben).
Erstellen der 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;
}
}';
Aufrufen der UDF:
SELECT echo_varchar('Hello');
+-----------------------+
| ECHO_VARCHAR('HELLO') |
|-----------------------|
| Hello |
+-----------------------+
Übergeben von NULL an eine Inline-Java-UDF¶
Hier wird die oben definierte echo_varchar()
-UDF verwendet. Der SQL NULL
-Wert wird implizit in einen Java null
-Wert umgewandelt, und dieser Java null
-Wert wird zurückgegeben und implizit wieder in SQL-NULL
umgewandelt:
Aufrufen der UDF:
SELECT echo_varchar(NULL);
+--------------------+
| ECHO_VARCHAR(NULL) |
|--------------------|
| NULL |
+--------------------+
Übergeben von Array-Werten¶
Java-Methoden können SQL-Arrays auf eine von zwei Arten erhalten:
Verwendung der Array-Funktion von Java.
Verwendung der Java-Funktion varargs (variable Anzahl von Argumenten).
In beiden Fällen muss Ihr SQL-Code ein ARRAY übergeben.
Bemerkung
Achten Sie darauf, Java-Typen mit gültigen Zuordnungen zu SQL-Typen zu verwenden. Weitere Informationen dazu finden Sie unter Zuordnung von Datentypen zwischen SQL und Java.
Werteübergabe via ARRAY¶
Deklarieren Sie den Java-Parameter als Array. In der folgenden Methode ist dritte Parameter beispielsweise ein String-Array:
static int myMethod(int fixedArgument1, int fixedArgument2, String[] stringArray)
Nachfolgend finden Sie ein vollständiges Beispiel:
Erstellen und Laden der Tabelle:
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');Erstellen der 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); } } $$;Aufrufen der UDF:
SELECT concat_varchar_2(a) FROM string_array_table ORDER BY id; +---------------------+ | CONCAT_VARCHAR_2(A) | |---------------------| | Hello | | Hello Jay | | Hello Jay Smith | +---------------------+
Werteübergabe via varargs¶
Die Verwendung von varargs ist der Verwendung eines Arrays sehr ähnlich.
Verwenden Sie in Ihrem Java-Code den varargs-Deklarationsstil von Java:
static int myMethod(int fixedArgument1, int fixedArgument2, String ... stringArray)
Nachfolgend finden Sie ein vollständiges Beispiel. Der einzige wesentliche Unterschied zwischen diesem Beispiel und dem vorangegangenen Beispiel (für Arrays) ist die Deklaration der Parameter für die Methode.
Erstellen und Laden der Tabelle:
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');Erstellen der 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); } } $$;Aufrufen der UDF:
SELECT concat_varchar(a) FROM string_array_table ORDER BY id; +-------------------+ | CONCAT_VARCHAR(A) | |-------------------| | Hello | | Hello Jay | | Hello Jay Smith | +-------------------+
Explizite Rückgabe von NULL aus einer Inline-UDF¶
Das folgende Codebeispiel zeigt, wie Sie einen NULL-Wert explizit zurückgeben. Der Java null
-Wert wird in einen SQL NULL
-Wert umgewandelt.
Erstellen der 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;
}
}
$$;
Aufrufen der UDF:
SELECT return_a_null();
+-----------------+
| RETURN_A_NULL() |
|-----------------|
| NULL |
+-----------------+
Übergeben eines OBJECT-Werts an eine Inline-Java-UDF¶
Im folgenden Beispiel werden der Datentyp SQL OBJECT und der entsprechende Java-Datentyp (Map<String, String>
) verwendet, um einen Wert aus OBJECT zu extrahieren. In diesem Beispiel wird auch gezeigt, dass Sie mehrere Parameter an eine Java-UDF übergeben können.
Erstellen und laden Sie eine Tabelle, die eine Spalte vom Typ OBJECT enthält:
CREATE TABLE objectives (o OBJECT);
INSERT INTO objectives SELECT PARSE_JSON('{"outer_key" : {"inner_key" : "inner_value"} }');
Erstellen der 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);
}
}
$$;
Aufrufen der 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" | |
| } | |
+-------------------------------------+--------------------------------------------------+
Übergeben eines GEOGRAPHY-Werts an eine Inline-Java-UDF¶
Im folgenden Beispiel wird der SQL-Datentyp GEOGRAPHY verwendet.
Erstellen der 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);
}
}
$$;
Sie können die PACKAGES-Klausel verwenden, um ein Snowflake-Systempaket wie das Snowpark-Paket anzugeben. Wenn Sie dies tun, müssen Sie die Snowpark-JAR-Datei nicht mehr als Wert in einer IMPORTS-Klausel angeben. Weitere Informationen zu PACKAGES finden Sie unter Optionale CREATE FUNCTION-Parameter.
Erstellen Sie Daten, und rufen Sie mit diesen Daten die UDF auf:
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;
Die Ausgabe sollte ungefähr wir folgt aussehen:
+----+--------------------------------------------------------+---------------------------------------------------------+--------+
| 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 |
+----+--------------------------------------------------------+---------------------------------------------------------+--------+
Übergeben eines VARIANT-Werts an eine Inline-Java-UDF¶
Wenn Sie einen Wert vom SQL-Typ VARIANT an eine Java-UDF übergeben, kann Snowflake den Wert in den Datentyp Variant konvertieren, der mit dem Snowpark-Paket bereitgestellt wird. Beachten Sie, dass Variant
ab Snowpark-Paketversion 1.4.0 unterstützt wird.
Der Snowpark-Typ Variant
bietet Methoden zur Konvertierung von Werten zwischen Variant
und anderen Datentypen.
Für die Verwendung des Snowpark-Typs Variant
muss beim Erstellen der UDF mit der PACKAGES-Klausel das Snowpark-Paket angegeben werden. Wenn Sie dies tun, müssen Sie die Snowpark-JAR-Datei nicht mehr als Wert in einer IMPORTS-Klausel angeben. Weitere Informationen zu PACKAGES finden Sie unter Optionale CREATE FUNCTION-Parameter.
Der Code im folgenden Beispiel empfängt JSON-Daten, die als VARIANT-Typ gespeichert sind, und verwendet dann den Variant
-Typ der Snowpark-Bibliothek, um den Wert price
aus dem JSON-Text abzurufen. Der empfangene JSON-Text hat eine ähnliche Struktur wie der in In Beispielen verwendete Beispieldaten angezeigte JSON-Text.
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;
}
}
$$;
Lesen einer Datei mit einer Java-UDF¶
Sie können den Inhalt einer Datei mit Handler-Code lesen. Sie könnten zum Beispiel eine Datei lesen, um unstrukturierte Daten mit dem Handler zu verarbeiten. Weitere Informationen zum Verarbeiten von unstrukturierten Daten sowie entsprechenden Beispielcode finden Sie unter Verarbeiten von unstrukturierten Daten mit UDF- und Prozedur-Handlern.
Die Datei muss sich in einem Snowflake-Stagingbereich befinden, auf den Ihr Handler zugreifen kann.
Um den Inhalt von Stagingdateien zu lesen, verwenden Sie Ihren Handler wie folgt:
Der Handler liest eine Datei, deren Dateipfad statisch in der IMPORTS-Klausel angegeben ist. Zur Laufzeit liest der Handler die Datei aus dem Basisverzeichnis der UDF.
Dies kann nützlich sein, wenn Sie auf die Datei während der Initialisierung zugreifen möchten.
Der Handler liest eine dynamisch spezifizierte Datei, indem er die Methoden der Klasse
SnowflakeFile
oder der KlasseInputStream
aufruft.Dies kann der Fall sein, wenn Sie auf eine vom Aufrufer angegebene Datei zugreifen müssen. Weitere Informationen dazu finden Sie in den folgenden Abschnitten unter diesem Thema:
SnowflakeFile
bietet Features, die beiInputStream
nicht verfügbar sind, wie in der folgenden Tabelle beschrieben.Klasse
Eingabe
Anmerkungen
SnowflakeFile
URL-Formate:
Bereichs-URL, um das Risiko von Angriffen per Dateieinschleusung zu verringern, wenn der Aufrufer der Funktion nicht deren Eigentümer ist.
Datei-URL oder Dateipfad-Zeichenfolge von Dateien, auf die der Eigentümer der UDF Zugriff hat.
Die Datei muss sich in einem benannten internen Stagingbereich oder in einem externen Stagingbereich befinden.
Einfacher Zugriff auf zusätzliche Dateiattribute, wie z. B. die Dateigröße.
InputStream
URL-Formate:
Bereichs-URL, um das Risiko von Angriffen per Dateieinschleusung zu verringern, wenn der Aufrufer der Funktion nicht deren Eigentümer ist.
Die Datei muss sich in einem internen oder externen Stagingbereich befinden.
Voraussetzungen¶
Bevor der Java-Handler-Code eine in einem Stagingbereich befindliche Datei lesen kann, müssen Sie Folgendes tun, um die Datei für den Code zur Verfügung zu stellen:
Erstellen Sie einen Stagingbereich, auf den Ihr Handler zugreifen kann.
Sie können einen externen Stagingbereich oder eine interne Stagingbereich verwenden. Wenn Sie einen internen Stagingbereich verwenden, muss es sich um einen Benutzer- oder benannten Stagingbereich handeln. Snowflake unterstützt derzeit nicht die Verwendung von Tabellen-Stagingbereichen für UDF-Abhängigkeiten. Weitere Informationen zum Erstellen eines Stagingbereichs finden Sie unter CREATE STAGE. Weitere Informationen zur Auswahl des Typs eines internen Stagingbereichs finden Sie unter Auswahl eines internen Stagingbereichs für lokale Dateien.
Denken Sie daran, dass Rollen, die SQL-Aktionen zum Lesen aus einem Stagingbereich ausführen, entsprechende Berechtigungen für den jeweiligen Stagingbereich zugewiesen sein müssen. Weitere Informationen dazu finden Sie unter Granting Privileges for User-Defined Functions.
Kopieren Sie die Datei, die vom Code gelesen werden soll, in den Stagingbereich.
Sie können die Datei von einem lokalen Laufwerk in einen Stagingbereich kopieren, indem Sie den Befehl PUT verwenden. Weitere Informationen zu diesem Befehl finden Sie unter PUT. Weitere Informationen zum Staging von Dateien mit PUT finden Sie unter Staging von Datendateien aus einem lokalen Dateisystem.
Lesen einer in IMPORTS statisch spezifizierten Datei¶
Ihr Handler kann eine Datei lesen, deren Stagingbereich in der IMPORTS-Klausel des Befehls CREATE FUNCTION angegeben wurde.
Wenn in der IMPORTS-Klausel eine Datei angegeben wird, kopiert Snowflake diese Datei vom Stagingbereich in das Basisverzeichnis der UDF (auch Home-Verzeichnis oder Importverzeichnis genannt), welches das Verzeichnis ist, aus dem die UDF die Datei schließlich ausliest.
Da importierte Dateien in ein einziges Verzeichnis kopiert werden und innerhalb dieses Verzeichnisses eindeutige Namen haben müssen, muss jede Datei in der IMPORTS-Klausel einen eindeutigen Namen haben, auch wenn sich die Dateien in verschiedenen Stagingbereichen oder verschiedenen Unterverzeichnissen innerhalb eines Stagingbereichs befinden.
Im folgenden Beispiel wird eine Java-UDF zum Lesen einer Datei erstellt und aufgerufen.
Der Java-Quellcode erstellt eine Java-Methode mit dem Namen readFile
. Die UDF verwendet diese Methode.
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();
}
}
Mit dem folgenden SQL-Code wird die UDF erstellt. Dieser Code geht davon aus, dass der Java-Quellcode kompiliert und in einer JAR-Datei mit dem Namen TestReadRelativeFile.jar
bereitgestellt wurde, die von der UDF importiert wird. Die zweite und die dritte importierte Datei, my_config_file_1.txt
und my_config_file_2.txt
, sind Konfigurationsdateien, die von der UDF gelesen werden können.
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';
Der folgende Code ruft die UDF auf:
SELECT file_reader('my_config_file_1.txt') ...;
...
SELECT file_reader('my_config_file_2.txt') ...;
Entscheiden, ob Zugriff auf Datei im komprimierten oder nicht komprimierten Format erfolgen soll¶
Dateien können in einem Stagingbereich in komprimiertem oder unkomprimiertem Format gespeichert werden. Der Benutzer kann die Datei komprimieren, bevor er sie in den Stagingbereich kopiert, oder er kann im Befehl PUT anweisen, die Datei zu komprimieren.
Wenn Snowflake eine im GZIP-Format komprimierte Datei von einem Stagingbereich in das Basisverzeichnis der UDF kopiert, kann Snowflake die Datei so kopieren, wie sie ist, oder Snowflake kann den Inhalt vor dem Schreiben der Datei dekomprimieren.
Wenn die Datei im Stagingbereich komprimiert ist und wenn Sie möchten, dass die Kopie im Basisverzeichnis der UDF ebenfalls komprimiert vorliegen soll, dann verwenden Sie bei der Angabe des Dateinamens in der IMPORTS-Klausel einfach den ursprünglichen Dateinamen (z. B. „MyData.txt.gz“). Beispiel:
... imports = ('@MyStage/MyData.txt.gz', ...)
Wenn die Datei im Stagingbereich GZIP-komprimiert ist, Sie aber möchten, dass die Kopie im UDF-Home-Verzeichnis nicht komprimiert ist, dann lassen Sie bei der Angabe des Dateinamens in der IMPORTS-Klausel die Erweiterung „.gz“ weg. Wenn der Stagingbereich beispielsweise „MyData.txt.gz“ enthält, Sie aber möchten, dass Ihre UDF die Datei im nicht komprimierten Format liest, dann geben Sie in der IMPORTS-Klausel „MyData.txt“ an. Wenn es noch keine nicht komprimierte Datei mit dem Namen „MyData.txt“ vorhanden ist, dann sucht Snowflake nach „MyData.txt.gz“ und schreibt automatisch eine dekomprimierte Kopie „MyData.txt“ in das Home-Verzeichnis der UDF. Ihre UDF kann dann die nicht komprimierte Datei „MyData.txt“ öffnen und lesen.
Beachten Sie, dass die intelligente Dekomprimierung nur für die Kopie im UDF-Home-Verzeichnis gilt. Die Originaldatei im Stagingbereich wird nicht verändert.
Folgen Sie den bewährten Verfahren für den Umgang mit komprimierten Dateien:
Befolgen Sie die korrekten Konventionen für die Dateibenennung. Wenn eine Datei im GZIP-komprimierten Format vorliegt, fügen Sie am Ende des Dateinamens die Erweiterung „.gz“ hinzu. Wenn eine Datei nicht im GZIP-komprimierten Format vorliegt, darf der Dateiname nicht mit der Erweiterung „.gz“ enden.
Vermeiden Sie es, Dateien zu erstellen, deren Namen sich nur durch die Endung „.gz“ unterscheiden. Erstellen Sie z. B. nicht gleichzeitig „MyData.txt“ und „MyData.txt.gz“ im selben Stagingbereich und Verzeichnis, und versuchen Sie nicht, sowohl „MyData.txt“ als auch „MyData.txt.gz“ im selben CREATE FUNCTION-Befehl zu importieren.
Komprimieren Sie Dateien nicht doppelt. Wenn Sie z. B. eine Datei manuell komprimieren und dann diese Datei ohne Verwendung von AUTO_COMPRESS = FALSE mit PUT komprimieren, wird die Datei ein zweites Mal komprimiert. Bei der intelligenten Dekomprimierung wird sie nur einmal dekomprimiert, sodass die Datendatei (oder JAR-Datei) immer noch komprimiert ist, wenn sie im Home-Verzeichnis der UDF gespeichert ist.
In Zukunft erweitert Snowflake möglicherweise die intelligente Dekomprimierung auf weitere Komprimierungsalgorithmen als nur GZIP. Um Kompatibilitätsprobleme in Zukunft zu vermeiden, wenden Sie folgende bewährte Verfahren auf Dateien an, die einen beliebigen Typ von Komprimierung verwenden.
Bemerkung
Auch JAR-Dateien können in einem Stagingbereich in komprimiertem oder unkomprimiertem Format gespeichert werden. Snowflake dekomprimiert automatisch alle komprimierten JAR-Dateien, bevor sie der Java-UDF zur Verfügung gestellt werden.
Lesen einer mit SnowflakeFile
dynamisch spezifizierten Datei¶
Die Methoden der SnowflakeFile
-Klasse können im Java-Handler-Code verwendet werden, um Dateien aus einem Stagingbereich zu lesen. Die SnowflakeFile
-Klasse wird in den Klassenpfad aufgenommen, der in Snowflake für Java-UDF-Handler verfügbar ist.
Bemerkung
Um Ihren Code widerstandsfähig gegen Angriffe per Dateieinschleusung zu machen, verwenden Sie immer eine Bereichs-URL, wenn Sie den Speicherort einer Datei an eine UDF übergeben, insbesondere wenn der Aufrufer der Funktion nicht auch ihr Eigentümer ist. Mit der integrierten Funktion BUILD_SCOPED_FILE_URL können Sie eine Bereichs-URL in SQL erstellen. Weitere Informationen zur Funktion BUILD_SCOPED_FILE_URL finden Sie unter Einführung in unstrukturierte Daten.
Um Ihren UDF-Code lokal zu entwickeln, fügen Sie die Snowpark-JAR-Datei, die die SnowflakeFile
-Klasse enthält, zum Klassenpfad Ihres Codes hinzu. Weitere Informationen zur snowpark.jar
-Datei finden Sie unter Einrichten Ihrer Entwicklungsumgebung für Snowpark Java. Beachten Sie, dass Snowpark-Clientanwendungen diese Klasse nicht verwenden können.
Wenn Sie SnowflakeFile
verwenden, ist es beim Erstellen der UDF nicht notwendig, in einer IMPORTS-Klausel die Stagingdatei bzw. die JAR-Datei, die SnowflakeFile
enthält, anzugeben, wie dies in SQL mit einer CREATE FUNCTION-Anweisung erforderlich ist.
Im folgenden Codebeispiel wird SnowflakeFile
verwendet, um eine Datei vom Speicherort des Stagingbereichs zu lesen. Mit InputStream
aus der getInputStream
-Methode wird der Inhalt der Datei in eine String
-Variable gelesen.
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;
}
}
$$;
Rufen Sie die UDF auf, und übergeben Sie den Speicherort der Datei in einer Bereichs-URL, um die Wahrscheinlichkeit von Angriffen mittels Dateieinschleusung zu verringern.
SELECT sum_total_sales(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
Bemerkung
Der Eigentümer der UDF muss Zugriff auf alle Dateien haben, deren Speicherorte keine Bereichs-URLs sind. Sie können diese Stagingdateien lesen, indem Ihr Handler-Code die Methode SnowflakeFile.newInstance
mit einem boolean
-Wert für einen neuen requireScopedUrl
-Parameter aufruft.
Das folgende Beispiel verwendet SnowflakeFile.newInstance
und gibt gleichzeitig an, dass keine Bereichs-URL erforderlich ist.
String filename = "@my_stage/filename.txt";
String sfFile = SnowflakeFile.newInstance(filename, false);
Lesen einer mit InputStream
dynamisch spezifizierten Datei¶
Sie können den Inhalt einer Datei direkt in einen java.io.InputStream
-Eingabestream einlesen, indem Sie das Argument Ihrer Handler-Funktion als InputStream
-Variable behandeln. Dies kann nützlich sein, wenn der Aufrufer der Funktion einen Dateipfad als Argument übergeben möchte.
Bemerkung
Um Ihren Code widerstandsfähig gegen Angriffe per Dateieinschleusung zu machen, verwenden Sie immer eine Bereichs-URL, wenn Sie den Speicherort einer Datei an eine UDF übergeben, insbesondere wenn der Aufrufer der Funktion nicht auch ihr Eigentümer ist. Mit der integrierten Funktion BUILD_SCOPED_FILE_URL können Sie eine Bereichs-URL in SQL erstellen. Weitere Informationen zur Funktion BUILD_SCOPED_FILE_URL finden Sie unter Einführung in unstrukturierte Daten.
Im folgenden Codebeispiel nimmt ein Handler-Funktion sumTotalSales
einen InputStream
-Wert entgegen und gibt einen int
-Wert zurück. Zur Laufzeit weist Snowflake den Inhalt der Datei mit dem in der Variable file
angegebenen Pfad automatisch der Argumentvariablen stream
zu.
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;
}
}
$$;
Rufen Sie die UDF auf, und übergeben Sie den Speicherort der Datei in einer Bereichs-URL, um die Wahrscheinlichkeit von Angriffen mittels Dateieinschleusung zu verringern.
SELECT sum_total_sales(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
Erstellen und Aufrufen einer einfachen Staging-Java-UDF¶
Mit den folgenden Anweisungen wird eine einfache Java-UDF erstellt. Dieses Beispiel folgt im Allgemeinen der unter Organisieren Ihrer Dateien beschriebenen Datei- und Verzeichnisstruktur.
Java-Handler-Code erstellen und kompilieren¶
Erstellen Sie im Stammverzeichnis Ihres Projekts (hier
my_udf
) ein Unterverzeichnissrc
, das die .java-Quelldateien enthält, sowie ein Unterverzeichnisclasses
, das die generierten .class-Dateien enthält.Die Verzeichnishierarchie sollte dann in etwa wie folgt aussehen:
my_udf/ |-- classes/ |-- src/
Erstellen Sie im Verzeichnis
src
ein Unterverzeichnis mit dem Namenmypackage
, um .java-Dateien zu speichern, deren Klassen sich immypackage
-Paket befinden.Erstellen Sie im Verzeichnis
mypackage
eine DateiMyUDFHandler.java
, die Ihren Quellcode enthält.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."); } }
Verwenden Sie in Ihrem Projektstammverzeichnis (hier
my_udf
) den Befehljavac
, um den Quellcode zu kompilieren.Mit dem Befehl
javac
wird im folgenden BeispielMyUDFHandler.java
kompiliert, um eine DateiMyUDFHandler.class
im Verzeichnisclasses
zu generieren.javac -d classes src/mypackage/MyUDFHandler.java
Dieses Beispiel enthält die folgenden Argumente:
-d classes
– Verzeichnis, in das die generierten Klassendateien geschrieben werden sollen.src/mypackage/MyUDFHandler.java
– Pfad zur .java-Datei in der Formsource_directory/package_directory/Java_file_name
.
Kompilierten Code in JAR-Datei packen¶
Optional können Sie im Stammverzeichnis des Projekts eine Manifest-Datei mit dem Namen
my_udf.manifest
erstellen, die die folgenden Attribute enthält:Manifest-Version: 1.0 Main-Class: mypackage.MyUDFHandler
Führen Sie im Stammverzeichnis Ihres Projekts den Befehl
jar
aus, um eine JAR-Datei zu erstellen, die die .class-Datei und die Manifest-Datei enthält.Mit dem
jar
-Befehl im folgenden Beispiel wird die generierte DateiMyUDFHandler.class
im Paketordnermypackage
in einer .jar-Datei namensmy_udf.jar
abgelegt. Das Flag-C ./classes
gibt den Speicherort der .class-Dateien an.jar cmf my_udf.manifest my_udf.jar -C ./classes mypackage/MyUDFHandler.class
Dieses Beispiel enthält die folgenden Argumente:
cmf
– Befehlsargumente:c
zum Erstellen einer JAR-Datei,m
zum Verwenden der angegebenen Manifest-Datei undf
zum Benennung der JAR-Datei mit dem angegebenen Namen.my_udf.manifest
– Manifest-Datei.my_udf.jar
– Name der zu erstellenden JAR-Datei.-C ./classes
– Verzeichnis, das die generierten .class-Dateien enthält.mypackage/MyUDFHandler.class
– Paket und Name der .class-Datei, die in die JAR-Datei eingebunden werden soll.
JAR-Datei mit kompiliertem Handler in Stagingbereich hochladen¶
Erstellen Sie in Snowflake einen Stagingbereich mit dem Namen
jar_stage
, in dem die JAR-Datei mit Ihrem UDF-Handler gespeichert werden soll.Weitere Informationen zum Erstellen eines Stagingbereichs finden Sie unter CREATE STAGE.
Verwenden Sie den
PUT
-Befehl, um die JAR-Datei aus dem lokalen Dateisystem in einen Stagingbereich zu kopieren.
put file:///Users/Me/my_udf/my_udf.jar @jar_stage auto_compress = false overwrite = true ;Sie können den
PUT
-Befehl in einer Skriptdatei speichern und diese Datei dann über SnowSQL ausführen.Der
snowsql
-Befehl sieht ungefähr wie folgt aus:snowsql -a <account_identifier> -w <warehouse> -d <database> -s <schema> -u <user> -f put_command.sqlIn diesem Beispiel wird davon ausgegangen, dass das Kennwort des Benutzers in der Umgebungsvariablen SNOWSQL_PWD angegeben ist.
UDF mit kompiliertem Code als Handler erstellen¶
Erstellen der UDF:
create function decrement_value(i numeric(9, 0))
returns numeric
language java
imports = ('@jar_stage/my_udf.jar')
handler = 'mypackage.MyUDFHandler.decrementValue'
;
Aufrufen der UDF:
select decrement_value(-15);
+----------------------+
| DECREMENT_VALUE(-15) |
|----------------------|
| -16 |
+----------------------+