Beispiele für Scala-UDF-Handler¶
Unter diesem Thema werden einfache Beispiele für in Scala geschriebenen UDF-Handler-Code bereitgestellt.
Weitere Informationen zur Verwendung von Scala für das Erstellen eines UDF-Handlers finden Sie unter Schreiben einer skalaren UDF in Scala. Allgemeine Kodierungsrichtlinien finden Sie unter Allgemeine Richtlinien zum Codieren von Scala-UDF-Handlern.
Erstellen und Aufrufen einer einfachen Inline-Scala-UDF¶
Mit den folgenden Anweisungen wird eine Inline-Scala-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 SCALA
CALLED ON NULL INPUT
RUNTIME_VERSION = 2.12
HANDLER='Echo.echoVarchar'
AS
$$
class Echo {
def echoVarchar(x : String): String = {
return x
}
}
$$;
Aufrufen der UDF¶
SELECT echo_varchar('Hello');
Übergeben von NULL an eine Inline-Scala-UDF¶
Hier wird die oben definierte echo_varchar()
-UDF verwendet. Der SQL-NULL
-Wert wird implizit in einen Scala-Null-Wert umgewandelt, und dieser Scala Null
-Wert wird zurückgegeben und implizit wieder in SQL-NULL
umgewandelt:
Aufrufen der UDF:
SELECT echo_varchar(NULL);
Explizite Rückgabe von NULL aus einer Inline-UDF¶
Das folgende Codebeispiel zeigt, wie Sie einen NULL-Wert explizit zurückgeben. Der Scala-Wert Null
wird in den SQL-NULL
-Wert umgewandelt.
Erstellen der UDF¶
CREATE OR REPLACE FUNCTION return_a_null()
RETURNS VARCHAR
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
HANDLER='TemporaryTestLibrary.returnNull'
AS
$$
class TemporaryTestLibrary {
def returnNull(): String = {
return null
}
}
$$;
Aufrufen der UDF¶
SELECT return_a_null();
Übergeben eines OBJECT-Werts an eine Inline-Scala-UDF¶
Im folgenden Beispiel werden der Datentyp SQL OBJECT und der entsprechende Scala-Datentyp (Map[String, String]
) verwendet, um einen Wert aus OBJECT zu extrahieren. In diesem Beispiel wird auch gezeigt, dass Sie mehrere Parameter an eine Scala-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 SCALA
RUNTIME_VERSION = 2.12
HANDLER='VariantLibrary.extract'
AS
$$
import scala.collection.immutable.Map
class VariantLibrary {
def extract(m: Map[String, String], key: String): String = {
return m(key)
}
}
$$;
Aufrufen der UDF¶
SELECT extract_from_object(o, 'outer_key'),
extract_from_object(o, 'outer_key')['inner_key'] FROM OBJECTIVES;
Übergeben eines ARRAY-Werts an eine Inline-Scala-UDF¶
Im folgenden Beispiel wird der SQL-Datentyp ARRAY verwendet.
Erstellen der UDF¶
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
}
}
$$;
Lesen einer Datei mit einer Scala-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.
Die Datei muss sich in einem Snowflake-Stagingbereich befinden, auf den Ihr Handler zugreifen kann.
Um den Inhalt von Stagingdateien zu lesen, kann Ihr Handler eine dynamisch spezifizierte Datei lesen, indem er Methoden entweder der Klasse SnowflakeFile
oder der Klasse InputStream
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 bei InputStream
nicht verfügbar sind, wie in der folgenden Tabelle beschrieben.
Klasse |
Eingabe |
Anmerkungen |
---|---|---|
|
URL-Formate:
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. |
|
URL-Formate:
Die Datei muss sich in einem benannten internen Stagingbereich oder in einem externen Stagingbereich befinden. |
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.
var filename = "@my_stage/filename.txt"
var sfFile = SnowflakeFile.newInstance(filename, false)
Lesen einer mit SnowflakeFile
dynamisch spezifizierten Datei¶
Die Methoden der SnowflakeFile
-Klasse können im Handler-Code verwendet werden, um Dateien aus einem Stagingbereich zu lesen. Die SnowflakeFile
-Klasse wird in den Klassenpfad aufgenommen, der in Snowflake für Scala-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 Scala. 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.
Erstellen der UDF¶
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_snowflake_file(file string)
RETURNS INTEGER
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
PACKAGES=('com.snowflake:snowpark:latest')
HANDLER='SalesSum.sumTotalSales'
AS
$$
import java.io.InputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
import com.snowflake.snowpark_java.types.SnowflakeFile
object SalesSum {
@throws(classOf[IOException])
def sumTotalSales(filePath: String): Int = {
var total = -1
// Use a SnowflakeFile instance to read sales data from a stage.
val file = SnowflakeFile.newInstance(filePath)
val stream = file.getInputStream()
val 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
}
}
$$;
Aufrufen der UDF¶
SELECT sum_total_sales_input_stream(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));
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-URLs, wenn Sie den Speicherort einer Datei an eine UDF übergeben. 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.
Erstellen der UDF¶
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_input_stream(file STRING)
RETURNS NUMBER
LANGUAGE SCALA
RUNTIME_VERSION = 2.12
HANDLER = 'SalesSum.sumTotalSales'
PACKAGES = ('com.snowflake:snowpark:latest')
AS $$
import com.snowflake.snowpark.types.Variant
import java.io.InputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
object SalesSum {
@throws(classOf[IOException])
def sumTotalSales(stream: InputStream): Int = {
val total = -1
val 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
}
}
$$;
Aufrufen der UDF¶
SELECT sum_total_sales_input_stream(BUILD_SCOPED_FILE_URL('@sales_data_stage', '/car_sales.json'));